diff --git a/Documentation/ABI/testing/sysfs-class-net-batman-adv b/Documentation/ABI/testing/sysfs-class-net-batman-adv index bdc00707c751b955d997aa7ce0c51bd8b0222ca5..7f34a95bb9634c9d8d1d9505d31b68c36bc58f76 100644 --- a/Documentation/ABI/testing/sysfs-class-net-batman-adv +++ b/Documentation/ABI/testing/sysfs-class-net-batman-adv @@ -1,13 +1,13 @@ What: /sys/class/net//batman-adv/iface_status Date: May 2010 -Contact: Marek Lindner +Contact: Marek Lindner Description: Indicates the status of as it is seen by batman. What: /sys/class/net//batman-adv/mesh_iface Date: May 2010 -Contact: Marek Lindner +Contact: Marek Lindner Description: The /sys/class/net//batman-adv/mesh_iface file displays the batman mesh interface this diff --git a/Documentation/ABI/testing/sysfs-class-net-mesh b/Documentation/ABI/testing/sysfs-class-net-mesh index bdcd8b4e38f2dd404acdc36bcd123f72ff5d2ed3..0baa657b18c4719048bf7048affea6fba8f79221 100644 --- a/Documentation/ABI/testing/sysfs-class-net-mesh +++ b/Documentation/ABI/testing/sysfs-class-net-mesh @@ -1,22 +1,23 @@ What: /sys/class/net//mesh/aggregated_ogms Date: May 2010 -Contact: Marek Lindner +Contact: Marek Lindner Description: Indicates whether the batman protocol messages of the mesh shall be aggregated or not. -What: /sys/class/net//mesh/ap_isolation +What: /sys/class/net//mesh//ap_isolation Date: May 2011 -Contact: Antonio Quartulli +Contact: Antonio Quartulli Description: Indicates whether the data traffic going from a wireless client to another wireless client will be - silently dropped. + silently dropped. is empty when referring + to the untagged lan. What: /sys/class/net//mesh/bonding Date: June 2010 -Contact: Simon Wunderlich +Contact: Simon Wunderlich Description: Indicates whether the data traffic going through the mesh will be sent using multiple interfaces at the @@ -24,7 +25,7 @@ Description: What: /sys/class/net//mesh/bridge_loop_avoidance Date: November 2011 -Contact: Simon Wunderlich +Contact: Simon Wunderlich Description: Indicates whether the bridge loop avoidance feature is enabled. This feature detects and avoids loops @@ -41,21 +42,21 @@ Description: What: /sys/class/net//mesh/gw_bandwidth Date: October 2010 -Contact: Marek Lindner +Contact: Marek Lindner Description: Defines the bandwidth which is propagated by this node if gw_mode was set to 'server'. What: /sys/class/net//mesh/gw_mode Date: October 2010 -Contact: Marek Lindner +Contact: Marek Lindner Description: Defines the state of the gateway features. Can be either 'off', 'client' or 'server'. What: /sys/class/net//mesh/gw_sel_class Date: October 2010 -Contact: Marek Lindner +Contact: Marek Lindner Description: Defines the selection criteria this node will use to choose a gateway if gw_mode was set to 'client'. @@ -77,25 +78,14 @@ Description: What: /sys/class/net//mesh/orig_interval Date: May 2010 -Contact: Marek Lindner +Contact: Marek Lindner Description: Defines the interval in milliseconds in which batman sends its protocol messages. What: /sys/class/net//mesh/routing_algo Date: Dec 2011 -Contact: Marek Lindner +Contact: Marek Lindner Description: Defines the routing procotol this mesh instance uses to find the optimal paths through the mesh. - -What: /sys/class/net//mesh/vis_mode -Date: May 2010 -Contact: Marek Lindner -Description: - Each batman node only maintains information about its - own local neighborhood, therefore generating graphs - showing the topology of the entire mesh is not easily - feasible without having a central instance to collect - the local topologies from all nodes. This file allows - to activate the collecting (server) mode. diff --git a/Documentation/DocBook/80211.tmpl b/Documentation/DocBook/80211.tmpl index f403ec3c5c9a4a17cf6c534f2da8590144faf766..46ad6faee9ab73dfae2fa3e2b00bb6fb7f1c63a1 100644 --- a/Documentation/DocBook/80211.tmpl +++ b/Documentation/DocBook/80211.tmpl @@ -152,8 +152,8 @@ !Finclude/net/cfg80211.h cfg80211_scan_request !Finclude/net/cfg80211.h cfg80211_scan_done !Finclude/net/cfg80211.h cfg80211_bss -!Finclude/net/cfg80211.h cfg80211_inform_bss_frame -!Finclude/net/cfg80211.h cfg80211_inform_bss +!Finclude/net/cfg80211.h cfg80211_inform_bss_width_frame +!Finclude/net/cfg80211.h cfg80211_inform_bss_width !Finclude/net/cfg80211.h cfg80211_unlink_bss !Finclude/net/cfg80211.h cfg80211_find_ie !Finclude/net/cfg80211.h ieee80211_bss_get_ie diff --git a/Documentation/devicetree/bindings/net/cpsw-phy-sel.txt b/Documentation/devicetree/bindings/net/cpsw-phy-sel.txt new file mode 100644 index 0000000000000000000000000000000000000000..7ff57a119f81f449cfc2afb5ef988183071a8aa8 --- /dev/null +++ b/Documentation/devicetree/bindings/net/cpsw-phy-sel.txt @@ -0,0 +1,28 @@ +TI CPSW Phy mode Selection Device Tree Bindings +----------------------------------------------- + +Required properties: +- compatible : Should be "ti,am3352-cpsw-phy-sel" +- reg : physical base address and size of the cpsw + registers map +- reg-names : names of the register map given in "reg" node + +Optional properties: +-rmii-clock-ext : If present, the driver will configure the RMII + interface to external clock usage + +Examples: + + phy_sel: cpsw-phy-sel@44e10650 { + compatible = "ti,am3352-cpsw-phy-sel"; + reg= <0x44e10650 0x4>; + reg-names = "gmii-sel"; + }; + +(or) + phy_sel: cpsw-phy-sel@44e10650 { + compatible = "ti,am3352-cpsw-phy-sel"; + reg= <0x44e10650 0x4>; + reg-names = "gmii-sel"; + rmii-clock-ext; + }; diff --git a/Documentation/networking/batman-adv.txt b/Documentation/networking/batman-adv.txt index c1d82047a4b151c35983656e5841e261ff22fd7f..89490beb3c0bf6bd0160c891b51e3cf07497f1ca 100644 --- a/Documentation/networking/batman-adv.txt +++ b/Documentation/networking/batman-adv.txt @@ -69,8 +69,7 @@ folder: # aggregated_ogms gw_bandwidth log_level # ap_isolation gw_mode orig_interval # bonding gw_sel_class routing_algo -# bridge_loop_avoidance hop_penalty vis_mode -# fragmentation +# bridge_loop_avoidance hop_penalty fragmentation There is a special folder for debugging information: @@ -78,7 +77,7 @@ There is a special folder for debugging information: # ls /sys/kernel/debug/batman_adv/bat0/ # bla_backbone_table log transtable_global # bla_claim_table originators transtable_local -# gateways socket vis_data +# gateways socket Some of the files contain all sort of status information regard- ing the mesh network. For example, you can view the table of @@ -127,51 +126,6 @@ ously assigned to interfaces now used by batman advanced, e.g. # ifconfig eth0 0.0.0.0 -VISUALIZATION -------------- - -If you want topology visualization, at least one mesh node must -be configured as VIS-server: - -# echo "server" > /sys/class/net/bat0/mesh/vis_mode - -Each node is either configured as "server" or as "client" (de- -fault: "client"). Clients send their topology data to the server -next to them, and server synchronize with other servers. If there -is no server configured (default) within the mesh, no topology -information will be transmitted. With these "synchronizing -servers", there can be 1 or more vis servers sharing the same (or -at least very similar) data. - -When configured as server, you can get a topology snapshot of -your mesh: - -# cat /sys/kernel/debug/batman_adv/bat0/vis_data - -This raw output is intended to be easily parsable and convertable -with other tools. Have a look at the batctl README if you want a -vis output in dot or json format for instance and how those out- -puts could then be visualised in an image. - -The raw format consists of comma separated values per entry where -each entry is giving information about a certain source inter- -face. Each entry can/has to have the following values: --> "mac" - mac address of an originator's source interface - (each line begins with it) --> "TQ mac value" - src mac's link quality towards mac address - of a neighbor originator's interface which - is being used for routing --> "TT mac" - TT announced by source mac --> "PRIMARY" - this is a primary interface --> "SEC mac" - secondary mac address of source - (requires preceding PRIMARY) - -The TQ value has a range from 4 to 255 with 255 being the best. -The TT entries are showing which hosts are connected to the mesh -via bat0 or being bridged into the mesh network. The PRIMARY/SEC -values are only applied on primary interfaces - - LOGGING/DEBUGGING ----------------- @@ -245,5 +199,5 @@ Mailing-list: b.a.t.m.a.n@open-mesh.org (optional subscription You can also contact the Authors: -Marek Lindner -Simon Wunderlich +Marek Lindner +Simon Wunderlich diff --git a/Documentation/networking/bonding.txt b/Documentation/networking/bonding.txt index 9b28e714831ae35fd0664debe4a1091825701024..2cdb8b66caa934b04f42d4290a297664dcd15251 100644 --- a/Documentation/networking/bonding.txt +++ b/Documentation/networking/bonding.txt @@ -639,6 +639,15 @@ num_unsol_na are generated by the ipv4 and ipv6 code and the numbers of repetitions cannot be set independently. +packets_per_slave + + Specify the number of packets to transmit through a slave before + moving to the next one. When set to 0 then a slave is chosen at + random. + + The valid range is 0 - 65535; the default value is 1. This option + has effect only in balance-rr mode. + primary A string (eth0, eth2, etc) specifying which slave is the @@ -743,21 +752,16 @@ xmit_hash_policy protocol information to generate the hash. Uses XOR of hardware MAC addresses and IP addresses to - generate the hash. The IPv4 formula is - - (((source IP XOR dest IP) AND 0xffff) XOR - ( source MAC XOR destination MAC )) - modulo slave count - - The IPv6 formula is + generate the hash. The formula is - hash = (source ip quad 2 XOR dest IP quad 2) XOR - (source ip quad 3 XOR dest IP quad 3) XOR - (source ip quad 4 XOR dest IP quad 4) + hash = source MAC XOR destination MAC + hash = hash XOR source IP XOR destination IP + hash = hash XOR (hash RSHIFT 16) + hash = hash XOR (hash RSHIFT 8) + And then hash is reduced modulo slave count. - (((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) - XOR (source MAC XOR destination MAC)) - modulo slave count + If the protocol is IPv6 then the source and destination + addresses are first hashed using ipv6_addr_hash. This algorithm will place all traffic to a particular network peer on the same slave. For non-IP traffic, @@ -779,21 +783,16 @@ xmit_hash_policy slaves, although a single connection will not span multiple slaves. - The formula for unfragmented IPv4 TCP and UDP packets is + The formula for unfragmented TCP and UDP packets is - ((source port XOR dest port) XOR - ((source IP XOR dest IP) AND 0xffff) - modulo slave count + hash = source port, destination port (as in the header) + hash = hash XOR source IP XOR destination IP + hash = hash XOR (hash RSHIFT 16) + hash = hash XOR (hash RSHIFT 8) + And then hash is reduced modulo slave count. - The formula for unfragmented IPv6 TCP and UDP packets is - - hash = (source port XOR dest port) XOR - ((source ip quad 2 XOR dest IP quad 2) XOR - (source ip quad 3 XOR dest IP quad 3) XOR - (source ip quad 4 XOR dest IP quad 4)) - - ((hash >> 24) XOR (hash >> 16) XOR (hash >> 8) XOR hash) - modulo slave count + If the protocol is IPv6 then the source and destination + addresses are first hashed using ipv6_addr_hash. For fragmented TCP or UDP packets and all other IPv4 and IPv6 protocol traffic, the source and destination port @@ -801,10 +800,6 @@ xmit_hash_policy formula is the same as for the layer2 transmit hash policy. - The IPv4 policy is intended to mimic the behavior of - certain switches, notably Cisco switches with PFC2 as - well as some Foundry and IBM products. - This algorithm is not fully 802.3ad compliant. A single TCP or UDP conversation containing both fragmented and unfragmented packets will see packets @@ -815,6 +810,26 @@ xmit_hash_policy conversations. Other implementations of 802.3ad may or may not tolerate this noncompliance. + encap2+3 + + This policy uses the same formula as layer2+3 but it + relies on skb_flow_dissect to obtain the header fields + which might result in the use of inner headers if an + encapsulation protocol is used. For example this will + improve the performance for tunnel users because the + packets will be distributed according to the encapsulated + flows. + + encap3+4 + + This policy uses the same formula as layer3+4 but it + relies on skb_flow_dissect to obtain the header fields + which might result in the use of inner headers if an + encapsulation protocol is used. For example this will + improve the performance for tunnel users because the + packets will be distributed according to the encapsulated + flows. + The default value is layer2. This option was added in bonding version 2.6.3. In earlier versions of bonding, this parameter does not exist, and the layer2 policy is the only policy. The diff --git a/Documentation/networking/can.txt b/Documentation/networking/can.txt index 820f55344edc0a035358fa643bbe79c48d4d887f..4c072414eadba20e4a6d85583842608d86284a5c 100644 --- a/Documentation/networking/can.txt +++ b/Documentation/networking/can.txt @@ -25,6 +25,12 @@ This file contains 4.1.5 RAW socket option CAN_RAW_FD_FRAMES 4.1.6 RAW socket returned message flags 4.2 Broadcast Manager protocol sockets (SOCK_DGRAM) + 4.2.1 Broadcast Manager operations + 4.2.2 Broadcast Manager message flags + 4.2.3 Broadcast Manager transmission timers + 4.2.4 Broadcast Manager message sequence transmission + 4.2.5 Broadcast Manager receive filter timers + 4.2.6 Broadcast Manager multiplex message receive filter 4.3 connected transport protocols (SOCK_SEQPACKET) 4.4 unconnected transport protocols (SOCK_DGRAM) @@ -593,6 +599,217 @@ solution for a couple of reasons: In order to receive such messages, CAN_RAW_RECV_OWN_MSGS must be set. 4.2 Broadcast Manager protocol sockets (SOCK_DGRAM) + + The Broadcast Manager protocol provides a command based configuration + interface to filter and send (e.g. cyclic) CAN messages in kernel space. + + Receive filters can be used to down sample frequent messages; detect events + such as message contents changes, packet length changes, and do time-out + monitoring of received messages. + + Periodic transmission tasks of CAN frames or a sequence of CAN frames can be + created and modified at runtime; both the message content and the two + possible transmit intervals can be altered. + + A BCM socket is not intended for sending individual CAN frames using the + struct can_frame as known from the CAN_RAW socket. Instead a special BCM + configuration message is defined. The basic BCM configuration message used + to communicate with the broadcast manager and the available operations are + defined in the linux/can/bcm.h include. The BCM message consists of a + message header with a command ('opcode') followed by zero or more CAN frames. + The broadcast manager sends responses to user space in the same form: + + struct bcm_msg_head { + __u32 opcode; /* command */ + __u32 flags; /* special flags */ + __u32 count; /* run 'count' times with ival1 */ + struct timeval ival1, ival2; /* count and subsequent interval */ + canid_t can_id; /* unique can_id for task */ + __u32 nframes; /* number of can_frames following */ + struct can_frame frames[0]; + }; + + The aligned payload 'frames' uses the same basic CAN frame structure defined + at the beginning of section 4 and in the include/linux/can.h include. All + messages to the broadcast manager from user space have this structure. + + Note a CAN_BCM socket must be connected instead of bound after socket + creation (example without error checking): + + int s; + struct sockaddr_can addr; + struct ifreq ifr; + + s = socket(PF_CAN, SOCK_DGRAM, CAN_BCM); + + strcpy(ifr.ifr_name, "can0"); + ioctl(s, SIOCGIFINDEX, &ifr); + + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + + connect(s, (struct sockaddr *)&addr, sizeof(addr)) + + (..) + + The broadcast manager socket is able to handle any number of in flight + transmissions or receive filters concurrently. The different RX/TX jobs are + distinguished by the unique can_id in each BCM message. However additional + CAN_BCM sockets are recommended to communicate on multiple CAN interfaces. + When the broadcast manager socket is bound to 'any' CAN interface (=> the + interface index is set to zero) the configured receive filters apply to any + CAN interface unless the sendto() syscall is used to overrule the 'any' CAN + interface index. When using recvfrom() instead of read() to retrieve BCM + socket messages the originating CAN interface is provided in can_ifindex. + + 4.2.1 Broadcast Manager operations + + The opcode defines the operation for the broadcast manager to carry out, + or details the broadcast managers response to several events, including + user requests. + + Transmit Operations (user space to broadcast manager): + + TX_SETUP: Create (cyclic) transmission task. + + TX_DELETE: Remove (cyclic) transmission task, requires only can_id. + + TX_READ: Read properties of (cyclic) transmission task for can_id. + + TX_SEND: Send one CAN frame. + + Transmit Responses (broadcast manager to user space): + + TX_STATUS: Reply to TX_READ request (transmission task configuration). + + TX_EXPIRED: Notification when counter finishes sending at initial interval + 'ival1'. Requires the TX_COUNTEVT flag to be set at TX_SETUP. + + Receive Operations (user space to broadcast manager): + + RX_SETUP: Create RX content filter subscription. + + RX_DELETE: Remove RX content filter subscription, requires only can_id. + + RX_READ: Read properties of RX content filter subscription for can_id. + + Receive Responses (broadcast manager to user space): + + RX_STATUS: Reply to RX_READ request (filter task configuration). + + RX_TIMEOUT: Cyclic message is detected to be absent (timer ival1 expired). + + RX_CHANGED: BCM message with updated CAN frame (detected content change). + Sent on first message received or on receipt of revised CAN messages. + + 4.2.2 Broadcast Manager message flags + + When sending a message to the broadcast manager the 'flags' element may + contain the following flag definitions which influence the behaviour: + + SETTIMER: Set the values of ival1, ival2 and count + + STARTTIMER: Start the timer with the actual values of ival1, ival2 + and count. Starting the timer leads simultaneously to emit a CAN frame. + + TX_COUNTEVT: Create the message TX_EXPIRED when count expires + + TX_ANNOUNCE: A change of data by the process is emitted immediately. + + TX_CP_CAN_ID: Copies the can_id from the message header to each + subsequent frame in frames. This is intended as usage simplification. For + TX tasks the unique can_id from the message header may differ from the + can_id(s) stored for transmission in the subsequent struct can_frame(s). + + RX_FILTER_ID: Filter by can_id alone, no frames required (nframes=0). + + RX_CHECK_DLC: A change of the DLC leads to an RX_CHANGED. + + RX_NO_AUTOTIMER: Prevent automatically starting the timeout monitor. + + RX_ANNOUNCE_RESUME: If passed at RX_SETUP and a receive timeout occured, a + RX_CHANGED message will be generated when the (cyclic) receive restarts. + + TX_RESET_MULTI_IDX: Reset the index for the multiple frame transmission. + + RX_RTR_FRAME: Send reply for RTR-request (placed in op->frames[0]). + + 4.2.3 Broadcast Manager transmission timers + + Periodic transmission configurations may use up to two interval timers. + In this case the BCM sends a number of messages ('count') at an interval + 'ival1', then continuing to send at another given interval 'ival2'. When + only one timer is needed 'count' is set to zero and only 'ival2' is used. + When SET_TIMER and START_TIMER flag were set the timers are activated. + The timer values can be altered at runtime when only SET_TIMER is set. + + 4.2.4 Broadcast Manager message sequence transmission + + Up to 256 CAN frames can be transmitted in a sequence in the case of a cyclic + TX task configuration. The number of CAN frames is provided in the 'nframes' + element of the BCM message head. The defined number of CAN frames are added + as array to the TX_SETUP BCM configuration message. + + /* create a struct to set up a sequence of four CAN frames */ + struct { + struct bcm_msg_head msg_head; + struct can_frame frame[4]; + } mytxmsg; + + (..) + mytxmsg.nframes = 4; + (..) + + write(s, &mytxmsg, sizeof(mytxmsg)); + + With every transmission the index in the array of CAN frames is increased + and set to zero at index overflow. + + 4.2.5 Broadcast Manager receive filter timers + + The timer values ival1 or ival2 may be set to non-zero values at RX_SETUP. + When the SET_TIMER flag is set the timers are enabled: + + ival1: Send RX_TIMEOUT when a received message is not received again within + the given time. When START_TIMER is set at RX_SETUP the timeout detection + is activated directly - even without a former CAN frame reception. + + ival2: Throttle the received message rate down to the value of ival2. This + is useful to reduce messages for the application when the signal inside the + CAN frame is stateless as state changes within the ival2 periode may get + lost. + + 4.2.6 Broadcast Manager multiplex message receive filter + + To filter for content changes in multiplex message sequences an array of more + than one CAN frames can be passed in a RX_SETUP configuration message. The + data bytes of the first CAN frame contain the mask of relevant bits that + have to match in the subsequent CAN frames with the received CAN frame. + If one of the subsequent CAN frames is matching the bits in that frame data + mark the relevant content to be compared with the previous received content. + Up to 257 CAN frames (multiplex filter bit mask CAN frame plus 256 CAN + filters) can be added as array to the TX_SETUP BCM configuration message. + + /* usually used to clear CAN frame data[] - beware of endian problems! */ + #define U64_DATA(p) (*(unsigned long long*)(p)->data) + + struct { + struct bcm_msg_head msg_head; + struct can_frame frame[5]; + } msg; + + msg.msg_head.opcode = RX_SETUP; + msg.msg_head.can_id = 0x42; + msg.msg_head.flags = 0; + msg.msg_head.nframes = 5; + U64_DATA(&msg.frame[0]) = 0xFF00000000000000ULL; /* MUX mask */ + U64_DATA(&msg.frame[1]) = 0x01000000000000FFULL; /* data mask (MUX 0x01) */ + U64_DATA(&msg.frame[2]) = 0x0200FFFF000000FFULL; /* data mask (MUX 0x02) */ + U64_DATA(&msg.frame[3]) = 0x330000FFFFFF0003ULL; /* data mask (MUX 0x33) */ + U64_DATA(&msg.frame[4]) = 0x4F07FC0FF0000000ULL; /* data mask (MUX 0x4F) */ + + write(s, &msg, sizeof(msg)); + 4.3 connected transport protocols (SOCK_SEQPACKET) 4.4 unconnected transport protocols (SOCK_DGRAM) diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index a46d78583ae1d9b1a8b49b96066f423f4b29d037..8b8a0578764112c1dacc4a719f454c676c09100c 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -267,17 +267,6 @@ tcp_max_orphans - INTEGER more aggressively. Let me to remind again: each orphan eats up to ~64K of unswappable memory. -tcp_max_ssthresh - INTEGER - Limited Slow-Start for TCP with large congestion windows (cwnd) defined in - RFC3742. Limited slow-start is a mechanism to limit growth of the cwnd - on the region where cwnd is larger than tcp_max_ssthresh. TCP increases cwnd - by at most tcp_max_ssthresh segments, and by at least tcp_max_ssthresh/2 - segments per RTT when the cwnd is above tcp_max_ssthresh. - If TCP connection increased cwnd to thousands (or tens of thousands) segments, - and thousands of packets were being dropped during slow-start, you can set - tcp_max_ssthresh to improve performance for new TCP connection. - Default: 0 (off) - tcp_max_syn_backlog - INTEGER Maximal number of remembered connection requests, which have not received an acknowledgment from connecting client. @@ -451,7 +440,7 @@ tcp_fastopen - INTEGER connect() to perform a TCP handshake automatically. The values (bitmap) are - 1: Enables sending data in the opening SYN on the client. + 1: Enables sending data in the opening SYN on the client w/ MSG_FASTOPEN. 2: Enables TCP Fast Open on the server side, i.e., allowing data in a SYN packet to be accepted and passed to the application before 3-way hand shake finishes. @@ -464,7 +453,7 @@ tcp_fastopen - INTEGER different ways of setting max_qlen without the TCP_FASTOPEN socket option. - Default: 0 + Default: 1 Note that the client & server side Fast Open flags (1 and 2 respectively) must be also enabled before the rest of flags can take diff --git a/Documentation/networking/netdevices.txt b/Documentation/networking/netdevices.txt index c7ecc7080494da43fc469eac5b62b06905c13559..0b1cf6b2a592f4f02f6d7c90f996428ba9e9fead 100644 --- a/Documentation/networking/netdevices.txt +++ b/Documentation/networking/netdevices.txt @@ -10,12 +10,12 @@ network devices. struct net_device allocation rules ================================== Network device structures need to persist even after module is unloaded and -must be allocated with kmalloc. If device has registered successfully, -it will be freed on last use by free_netdev. This is required to handle the -pathologic case cleanly (example: rmmod mydriver sec * 1000000000LL + t->nsec; +} + static void usage(char *progname) { fprintf(stderr, @@ -112,6 +117,8 @@ static void usage(char *progname) " -f val adjust the ptp clock frequency by 'val' ppb\n" " -g get the ptp clock time\n" " -h prints this message\n" + " -k val measure the time offset between system and phc clock\n" + " for 'val' times (Maximum 25)\n" " -p val enable output with a period of 'val' nanoseconds\n" " -P val enable or disable (val=1|0) the system clock PPS\n" " -s set the ptp clock time from the system time\n" @@ -133,8 +140,12 @@ int main(int argc, char *argv[]) struct itimerspec timeout; struct sigevent sigevent; + struct ptp_clock_time *pct; + struct ptp_sys_offset *sysoff; + + char *progname; - int c, cnt, fd; + int i, c, cnt, fd; char *device = DEVICE; clockid_t clkid; @@ -144,14 +155,19 @@ int main(int argc, char *argv[]) int extts = 0; int gettime = 0; int oneshot = 0; + int pct_offset = 0; + int n_samples = 0; int periodic = 0; int perout = -1; int pps = -1; int settime = 0; + int64_t t1, t2, tp; + int64_t interval, offset; + progname = strrchr(argv[0], '/'); progname = progname ? 1+progname : argv[0]; - while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghp:P:sSt:v"))) { + while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghk:p:P:sSt:v"))) { switch (c) { case 'a': oneshot = atoi(optarg); @@ -174,6 +190,10 @@ int main(int argc, char *argv[]) case 'g': gettime = 1; break; + case 'k': + pct_offset = 1; + n_samples = atoi(optarg); + break; case 'p': perout = atoi(optarg); break; @@ -376,6 +396,47 @@ int main(int argc, char *argv[]) } } + if (pct_offset) { + if (n_samples <= 0 || n_samples > 25) { + puts("n_samples should be between 1 and 25"); + usage(progname); + return -1; + } + + sysoff = calloc(1, sizeof(*sysoff)); + if (!sysoff) { + perror("calloc"); + return -1; + } + sysoff->n_samples = n_samples; + + if (ioctl(fd, PTP_SYS_OFFSET, sysoff)) + perror("PTP_SYS_OFFSET"); + else + puts("system and phc clock time offset request okay"); + + pct = &sysoff->ts[0]; + for (i = 0; i < sysoff->n_samples; i++) { + t1 = pctns(pct+2*i); + tp = pctns(pct+2*i+1); + t2 = pctns(pct+2*i+2); + interval = t2 - t1; + offset = (t2 + t1) / 2 - tp; + + printf("system time: %ld.%ld\n", + (pct+2*i)->sec, (pct+2*i)->nsec); + printf("phc time: %ld.%ld\n", + (pct+2*i+1)->sec, (pct+2*i+1)->nsec); + printf("system time: %ld.%ld\n", + (pct+2*i+2)->sec, (pct+2*i+2)->nsec); + printf("system/phc clock time offset is %ld ns\n" + "system clock time delay is %ld ns\n", + offset, interval); + } + + free(sysoff); + } + close(fd); return 0; } diff --git a/MAINTAINERS b/MAINTAINERS index 5cfa91b1974a22cafbda96e263c2a65d2a75b146..67c0068b7a0f65cc9535059b20b03cc62a36c9bf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1667,9 +1667,9 @@ F: drivers/video/backlight/ F: include/linux/backlight.h BATMAN ADVANCED -M: Marek Lindner -M: Simon Wunderlich -M: Antonio Quartulli +M: Marek Lindner +M: Simon Wunderlich +M: Antonio Quartulli L: b.a.t.m.a.n@lists.open-mesh.org W: http://www.open-mesh.org/ S: Maintained @@ -1822,7 +1822,7 @@ F: drivers/net/ethernet/broadcom/bnx2.* F: drivers/net/ethernet/broadcom/bnx2_* BROADCOM BNX2X 10 GIGABIT ETHERNET DRIVER -M: Eilon Greenstein +M: Ariel Elior L: netdev@vger.kernel.org S: Supported F: drivers/net/ethernet/broadcom/bnx2x/ @@ -5374,7 +5374,7 @@ S: Orphan F: drivers/net/wireless/libertas/ MARVELL MV643XX ETHERNET DRIVER -M: Lennert Buytenhek +M: Sebastian Hesselbarth L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/marvell/mv643xx_eth.* @@ -6890,6 +6890,14 @@ L: linux-hexagon@vger.kernel.org S: Supported F: arch/hexagon/ +QUALCOMM WCN36XX WIRELESS DRIVER +M: Eugene Krasnikov +L: wcn36xx@lists.infradead.org +W: http://wireless.kernel.org/en/users/Drivers/wcn36xx +T: git git://github.com/KrasnikovEugene/wcn36xx.git +S: Supported +F: drivers/net/wireless/ath/wcn36xx/ + QUICKCAM PARALLEL PORT WEBCAMS M: Hans Verkuil L: linux-media@vger.kernel.org diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 467de010ea7ee130cdbda0b532f10c885f8d1308..e3a1491d5073a0b59a6b9eb207c4a684ae6c9495 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -81,6 +81,8 @@ #define SO_SELECT_ERR_QUEUE 45 -#define SO_BUSY_POLL 46 +#define SO_BUSY_POLL 46 + +#define SO_MAX_PACING_RATE 47 #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index fcb9c8e42ab79b053f14639886c2d75bd58f2161..f6d8ffe98d0bfa15bc516b8fde2847f3568fcdd2 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -670,6 +670,12 @@ /* Filled in by U-Boot */ mac-address = [ 00 00 00 00 00 00 ]; }; + + phy_sel: cpsw-phy-sel@44e10650 { + compatible = "ti,am3352-cpsw-phy-sel"; + reg= <0x44e10650 0x4>; + reg-names = "gmii-sel"; + }; }; ocmcram: ocmcram@40300000 { diff --git a/arch/avr32/include/uapi/asm/socket.h b/arch/avr32/include/uapi/asm/socket.h index 11c4259c62fb146b8139b7045958f68d6adc3765..4399364214349674999c872ca4779c7089c22160 100644 --- a/arch/avr32/include/uapi/asm/socket.h +++ b/arch/avr32/include/uapi/asm/socket.h @@ -76,4 +76,6 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* __ASM_AVR32_SOCKET_H */ diff --git a/arch/cris/include/uapi/asm/socket.h b/arch/cris/include/uapi/asm/socket.h index eb723e51554e559ef73d1196a455a96583a5cf51..13829aaaeec565b8e53726e95fcf4e7fde638c25 100644 --- a/arch/cris/include/uapi/asm/socket.h +++ b/arch/cris/include/uapi/asm/socket.h @@ -78,6 +78,8 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h index f0cb1c3411638e7b33bee915739881635c59c9bc..5d4299762426b108057a33faf71b85efdfe4f74b 100644 --- a/arch/frv/include/uapi/asm/socket.h +++ b/arch/frv/include/uapi/asm/socket.h @@ -76,5 +76,7 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h index 556d0701a155351e844960aa00b51c55c820a3b9..c25302fb48d95636b59670efa423a59ff2d055c0 100644 --- a/arch/ia64/include/uapi/asm/socket.h +++ b/arch/ia64/include/uapi/asm/socket.h @@ -85,4 +85,6 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h index 24be7c8da86ad3cbbdee6be0933fe302c9adaf02..52966650114f3198df49c109c02d70ae82d8adcb 100644 --- a/arch/m32r/include/uapi/asm/socket.h +++ b/arch/m32r/include/uapi/asm/socket.h @@ -76,4 +76,6 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 61c01f054d1b160f753582b60888e59592b95b21..0df9787cd84d9e4a93e04513f1654b0327b3df0f 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -94,4 +94,6 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h index e2a2b203eb005b5403f2b68f92a841d87137b75c..71dedcae55a69c4ea93c5c71c7edfd93583864a9 100644 --- a/arch/mn10300/include/uapi/asm/socket.h +++ b/arch/mn10300/include/uapi/asm/socket.h @@ -76,4 +76,6 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index 71700e636a8e7b25172cda7f4815b1e46216b3d3..7c614d01f1fa42df36a23b3633f6d6ae7d47aa6a 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -75,6 +75,8 @@ #define SO_BUSY_POLL 0x4027 +#define SO_MAX_PACING_RATE 0x4048 + /* O_NONBLOCK clashes with the bits used for socket types. Therefore we * have to define SOCK_NONBLOCK to a different value here. */ diff --git a/arch/powerpc/include/uapi/asm/socket.h b/arch/powerpc/include/uapi/asm/socket.h index a6d74467c9edb476ad1175e34670848f946528c8..fa698324a1fd13e50ec0d5e01d526b5a00a2b0b8 100644 --- a/arch/powerpc/include/uapi/asm/socket.h +++ b/arch/powerpc/include/uapi/asm/socket.h @@ -83,4 +83,6 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* _ASM_POWERPC_SOCKET_H */ diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h index 92494494692eca965ee1d315d42a800e08c47605..c286c2e868f03f9683481b82689af19f1a222e2d 100644 --- a/arch/s390/include/uapi/asm/socket.h +++ b/arch/s390/include/uapi/asm/socket.h @@ -82,4 +82,6 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index 4e1d66c3ce71b0b89bc2acb86643b3cebd445a06..0f21e9a5ca18879f5d919aa0fa05532204244eba 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -72,6 +72,8 @@ #define SO_BUSY_POLL 0x0030 +#define SO_MAX_PACING_RATE 0x0031 + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/x86/kernel/jump_label.c b/arch/x86/kernel/jump_label.c index ee11b7dfbfbb6676eb94a28360bce88b286ceac1..26d5a55a273610b8d49da975a006f156adb4e482 100644 --- a/arch/x86/kernel/jump_label.c +++ b/arch/x86/kernel/jump_label.c @@ -42,15 +42,27 @@ static void __jump_label_transform(struct jump_entry *entry, int init) { union jump_code_union code; + const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP }; const unsigned char *ideal_nop = ideal_nops[NOP_ATOMIC5]; if (type == JUMP_LABEL_ENABLE) { - /* - * We are enabling this jump label. If it is not a nop - * then something must have gone wrong. - */ - if (unlikely(memcmp((void *)entry->code, ideal_nop, 5) != 0)) - bug_at((void *)entry->code, __LINE__); + if (init) { + /* + * Jump label is enabled for the first time. + * So we expect a default_nop... + */ + if (unlikely(memcmp((void *)entry->code, default_nop, 5) + != 0)) + bug_at((void *)entry->code, __LINE__); + } else { + /* + * ...otherwise expect an ideal_nop. Otherwise + * something went horribly wrong. + */ + if (unlikely(memcmp((void *)entry->code, ideal_nop, 5) + != 0)) + bug_at((void *)entry->code, __LINE__); + } code.jump = 0xe9; code.offset = entry->target - @@ -63,7 +75,6 @@ static void __jump_label_transform(struct jump_entry *entry, * are converting the default nop to the ideal nop. */ if (init) { - const unsigned char default_nop[] = { STATIC_KEY_INIT_NOP }; if (unlikely(memcmp((void *)entry->code, default_nop, 5) != 0)) bug_at((void *)entry->code, __LINE__); } else { diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c index 516593e1ce33b92175195c8de2983258b723493c..26328e800869fc6ac1821c0ab3dcd2baa1506ec1 100644 --- a/arch/x86/net/bpf_jit_comp.c +++ b/arch/x86/net/bpf_jit_comp.c @@ -788,5 +788,7 @@ void bpf_jit_free(struct sk_filter *fp) if (fp->bpf_func != sk_run_filter) { INIT_WORK(&fp->work, bpf_jit_free_deferred); schedule_work(&fp->work); + } else { + kfree(fp); } } diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h index c114483010c13b70caf62b3edea8b46947ca17e8..7db5c22faa68a1803cad4708d9047ad8396b637e 100644 --- a/arch/xtensa/include/uapi/asm/socket.h +++ b/arch/xtensa/include/uapi/asm/socket.h @@ -87,4 +87,6 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* _XTENSA_SOCKET_H */ diff --git a/drivers/atm/firestream.h b/drivers/atm/firestream.h index 49e783e35ee94cf57e24c2e06c7ad8a7b26a29ff..364eded31881fbbf4d83adb0909d614b29692ed9 100644 --- a/drivers/atm/firestream.h +++ b/drivers/atm/firestream.h @@ -420,7 +420,6 @@ struct fs_transmit_config { #define RC_FLAGS_BFPS_BFP27 (0xd << 17) #define RC_FLAGS_BFPS_BFP47 (0xe << 17) -#define RC_FLAGS_BFPS (0x1 << 17) #define RC_FLAGS_BFPP (0x1 << 21) #define RC_FLAGS_TEVC (0x1 << 22) #define RC_FLAGS_TEP (0x1 << 23) diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index a355e63a3838cb7ff7e7ef66194888adc65d6232..6fb98b53533f9d708dfc2a530e1033f3f09008b9 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -188,8 +188,11 @@ static int bcma_host_pci_probe(struct pci_dev *dev, pci_write_config_dword(dev, 0x40, val & 0xffff00ff); /* SSB needed additional powering up, do we have any AMBA PCI cards? */ - if (!pci_is_pcie(dev)) - bcma_err(bus, "PCI card detected, report problems.\n"); + if (!pci_is_pcie(dev)) { + bcma_err(bus, "PCI card detected, they are not supported.\n"); + err = -ENXIO; + goto err_pci_release_regions; + } /* Map MMIO */ err = -ENOMEM; @@ -269,6 +272,7 @@ static SIMPLE_DEV_PM_OPS(bcma_pm_ops, bcma_host_pci_suspend, static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4313) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) }, diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 4afae20df512cb6d88ce61b6d11c489d25eb88b2..9fe8a875a8277b51c069ce682d6ece31c922d426 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -30,3 +30,5 @@ hci_uart-$(CONFIG_BT_HCIUART_LL) += hci_ll.o hci_uart-$(CONFIG_BT_HCIUART_ATH3K) += hci_ath.o hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o hci_uart-objs := $(hci_uart-y) + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 0a327f4154a2b2039ad4ae0e50b7195dfa2cb5f0..6bfc1bb318f6399397ca8f169cc07fd98b46256d 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -57,7 +57,7 @@ struct ath3k_version { unsigned char reserved[0x07]; }; -static struct usb_device_id ath3k_table[] = { +static const struct usb_device_id ath3k_table[] = { /* Atheros AR3011 */ { USB_DEVICE(0x0CF3, 0x3000) }, @@ -112,7 +112,7 @@ MODULE_DEVICE_TABLE(usb, ath3k_table); #define BTUSB_ATH3012 0x80 /* This table is to load patch and sysconfig files * for AR3012 */ -static struct usb_device_id ath3k_blist_tbl[] = { +static const struct usb_device_id ath3k_blist_tbl[] = { /* Atheros AR3012 with sflash firmware*/ { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 995aee9cba22a8d871289004a4cef5ec60cdeaa2..31386998c9a7b4159f04194f2125ee10d73cc920 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -42,7 +42,7 @@ static struct usb_driver bfusb_driver; -static struct usb_device_id bfusb_table[] = { +static const struct usb_device_id bfusb_table[] = { /* AVM BlueFRITZ! USB */ { USB_DEVICE(0x057c, 0x2200) }, @@ -318,7 +318,6 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch return -ENOMEM; } - skb->dev = (void *) data->hdev; bt_cb(skb)->pkt_type = pkt_type; data->reassembly = skb; @@ -333,7 +332,7 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch memcpy(skb_put(data->reassembly, len), buf, len); if (hdr & 0x08) { - hci_recv_frame(data->reassembly); + hci_recv_frame(data->hdev, data->reassembly); data->reassembly = NULL; } @@ -465,26 +464,18 @@ static int bfusb_close(struct hci_dev *hdev) return 0; } -static int bfusb_send_frame(struct sk_buff *skb) +static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct bfusb_data *data; + struct bfusb_data *data = hci_get_drvdata(hdev); struct sk_buff *nskb; unsigned char buf[3]; int sent = 0, size, count; BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len); - if (!hdev) { - BT_ERR("Frame for unknown HCI device (hdev=NULL)"); - return -ENODEV; - } - if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - data = hci_get_drvdata(hdev); - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; @@ -544,11 +535,6 @@ static int bfusb_send_frame(struct sk_buff *skb) return 0; } -static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; -} - static int bfusb_load_firmware(struct bfusb_data *data, const unsigned char *firmware, int count) { @@ -699,11 +685,10 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i hci_set_drvdata(hdev, data); SET_HCIDEV_DEV(hdev, &intf->dev); - hdev->open = bfusb_open; - hdev->close = bfusb_close; - hdev->flush = bfusb_flush; - hdev->send = bfusb_send_frame; - hdev->ioctl = bfusb_ioctl; + hdev->open = bfusb_open; + hdev->close = bfusb_close; + hdev->flush = bfusb_flush; + hdev->send = bfusb_send_frame; if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index 6c3e3d43c718a1d756c9308a8a135bd9e64a2e32..57427de864a657530ec92f1a54fe9e0d5c52938e 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -399,7 +399,6 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset) if (info->rx_state == RECV_WAIT_PACKET_TYPE) { - info->rx_skb->dev = (void *) info->hdev; bt_cb(info->rx_skb)->pkt_type = buf[i]; switch (bt_cb(info->rx_skb)->pkt_type) { @@ -477,7 +476,7 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset) break; case RECV_WAIT_DATA: - hci_recv_frame(info->rx_skb); + hci_recv_frame(info->hdev, info->rx_skb); info->rx_skb = NULL; break; @@ -659,17 +658,9 @@ static int bluecard_hci_close(struct hci_dev *hdev) } -static int bluecard_hci_send_frame(struct sk_buff *skb) +static int bluecard_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - bluecard_info_t *info; - struct hci_dev *hdev = (struct hci_dev *)(skb->dev); - - if (!hdev) { - BT_ERR("Frame for unknown HCI device (hdev=NULL)"); - return -ENODEV; - } - - info = hci_get_drvdata(hdev); + bluecard_info_t *info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -693,12 +684,6 @@ static int bluecard_hci_send_frame(struct sk_buff *skb) } -static int bluecard_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; -} - - /* ======================== Card services HCI interaction ======================== */ @@ -734,11 +719,10 @@ static int bluecard_open(bluecard_info_t *info) hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); - hdev->open = bluecard_hci_open; - hdev->close = bluecard_hci_close; - hdev->flush = bluecard_hci_flush; - hdev->send = bluecard_hci_send_frame; - hdev->ioctl = bluecard_hci_ioctl; + hdev->open = bluecard_hci_open; + hdev->close = bluecard_hci_close; + hdev->flush = bluecard_hci_flush; + hdev->send = bluecard_hci_send_frame; id = inb(iobase + 0x30); diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 2fe4a8031348f0c8b05074eb9889a1d2e02a7a91..8a319913c9a96fd60c6d021c5edd055b83ef0aad 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -37,7 +37,7 @@ #define VERSION "0.10" -static struct usb_device_id bpa10x_table[] = { +static const struct usb_device_id bpa10x_table[] = { /* Tektronix BPA 100/105 (Digianswer) */ { USB_DEVICE(0x08fd, 0x0002) }, @@ -129,8 +129,6 @@ static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count) return -ENOMEM; } - skb->dev = (void *) hdev; - data->rx_skb[queue] = skb; scb = (void *) skb->cb; @@ -155,7 +153,7 @@ static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count) data->rx_skb[queue] = NULL; bt_cb(skb)->pkt_type = scb->type; - hci_recv_frame(skb); + hci_recv_frame(hdev, skb); } count -= len; buf += len; @@ -352,9 +350,8 @@ static int bpa10x_flush(struct hci_dev *hdev) return 0; } -static int bpa10x_send_frame(struct sk_buff *skb) +static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct bpa10x_data *data = hci_get_drvdata(hdev); struct usb_ctrlrequest *dr; struct urb *urb; @@ -366,6 +363,8 @@ static int bpa10x_send_frame(struct sk_buff *skb) if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; + skb->dev = (void *) hdev; + urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) return -ENOMEM; diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index a1aaa3ba2a4bafed0e2c4573b1614e24ddb84306..73d87994d028ad3e64397a61e05a2e8c15f78321 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -247,7 +247,6 @@ static void bt3c_receive(bt3c_info_t *info) if (info->rx_state == RECV_WAIT_PACKET_TYPE) { - info->rx_skb->dev = (void *) info->hdev; bt_cb(info->rx_skb)->pkt_type = inb(iobase + DATA_L); inb(iobase + DATA_H); //printk("bt3c: PACKET_TYPE=%02x\n", bt_cb(info->rx_skb)->pkt_type); @@ -318,7 +317,7 @@ static void bt3c_receive(bt3c_info_t *info) break; case RECV_WAIT_DATA: - hci_recv_frame(info->rx_skb); + hci_recv_frame(info->hdev, info->rx_skb); info->rx_skb = NULL; break; @@ -416,19 +415,11 @@ static int bt3c_hci_close(struct hci_dev *hdev) } -static int bt3c_hci_send_frame(struct sk_buff *skb) +static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - bt3c_info_t *info; - struct hci_dev *hdev = (struct hci_dev *)(skb->dev); + bt3c_info_t *info = hci_get_drvdata(hdev); unsigned long flags; - if (!hdev) { - BT_ERR("Frame for unknown HCI device (hdev=NULL)"); - return -ENODEV; - } - - info = hci_get_drvdata(hdev); - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; @@ -455,12 +446,6 @@ static int bt3c_hci_send_frame(struct sk_buff *skb) } -static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; -} - - /* ======================== Card services HCI interaction ======================== */ @@ -577,11 +562,10 @@ static int bt3c_open(bt3c_info_t *info) hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); - hdev->open = bt3c_hci_open; - hdev->close = bt3c_hci_close; - hdev->flush = bt3c_hci_flush; - hdev->send = bt3c_hci_send_frame; - hdev->ioctl = bt3c_hci_ioctl; + hdev->open = bt3c_hci_open; + hdev->close = bt3c_hci_close; + hdev->flush = bt3c_hci_flush; + hdev->send = bt3c_hci_send_frame; /* Load firmware */ err = request_firmware(&firmware, "BT3CPCC.bin", &info->p_dev->dev); diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 27068d1493808ec3f2acda115fd51773cbc4cfc5..f9d183387f4585b37e0642aef9e269e17c6be1a4 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -23,6 +23,8 @@ #include #include #include +#include +#include #define BTM_HEADER_LEN 4 #define BTM_UPLD_SIZE 2312 @@ -41,6 +43,8 @@ struct btmrvl_thread { struct btmrvl_device { void *card; struct hci_dev *hcidev; + struct device *dev; + const char *cal_data; u8 dev_type; @@ -91,6 +95,7 @@ struct btmrvl_private { #define BT_CMD_HOST_SLEEP_CONFIG 0x59 #define BT_CMD_HOST_SLEEP_ENABLE 0x5A #define BT_CMD_MODULE_CFG_REQ 0x5B +#define BT_CMD_LOAD_CONFIG_DATA 0x61 /* Sub-commands: Module Bringup/Shutdown Request/Response */ #define MODULE_BRINGUP_REQ 0xF1 @@ -116,11 +121,8 @@ struct btmrvl_private { #define PS_SLEEP 0x01 #define PS_AWAKE 0x00 -struct btmrvl_cmd { - __le16 ocf_ogf; - u8 length; - u8 data[4]; -} __packed; +#define BT_CMD_DATA_SIZE 32 +#define BT_CAL_DATA_SIZE 28 struct btmrvl_event { u8 ec; /* event counter */ diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 9a9f51875df5ef7826cea976b6afefa0889e160b..5cf31c4fe6d1cf552c9846c8fe7a79857851c9b6 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -57,8 +57,7 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb) ocf = hci_opcode_ocf(opcode); ogf = hci_opcode_ogf(opcode); - if (ocf == BT_CMD_MODULE_CFG_REQ && - priv->btmrvl_dev.sendcmdflag) { + if (priv->btmrvl_dev.sendcmdflag) { priv->btmrvl_dev.sendcmdflag = false; priv->adapter->cmd_complete = true; wake_up_interruptible(&priv->adapter->cmd_wait_q); @@ -116,7 +115,6 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) adapter->hs_state = HS_ACTIVATED; if (adapter->psmode) adapter->ps_state = PS_SLEEP; - wake_up_interruptible(&adapter->cmd_wait_q); BT_DBG("HS ACTIVATED!"); } else { BT_DBG("HS Enable failed"); @@ -168,45 +166,50 @@ exit: } EXPORT_SYMBOL_GPL(btmrvl_process_event); -int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd) +static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 cmd_no, + const void *param, u8 len) { struct sk_buff *skb; - struct btmrvl_cmd *cmd; - int ret = 0; + struct hci_command_hdr *hdr; - skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); + skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC); if (skb == NULL) { BT_ERR("No free skb"); return -ENOMEM; } - cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); - cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_MODULE_CFG_REQ)); - cmd->length = 1; - cmd->data[0] = subcmd; + hdr = (struct hci_command_hdr *)skb_put(skb, HCI_COMMAND_HDR_SIZE); + hdr->opcode = cpu_to_le16(hci_opcode_pack(OGF, cmd_no)); + hdr->plen = len; + + if (len) + memcpy(skb_put(skb, len), param, len); bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; - skb->dev = (void *) priv->btmrvl_dev.hcidev; skb_queue_head(&priv->adapter->tx_queue, skb); priv->btmrvl_dev.sendcmdflag = true; priv->adapter->cmd_complete = false; - BT_DBG("Queue module cfg Command"); - wake_up_interruptible(&priv->main_thread.wait_q); if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, priv->adapter->cmd_complete, - msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) { - ret = -ETIMEDOUT; - BT_ERR("module_cfg_cmd(%x): timeout: %d", - subcmd, priv->btmrvl_dev.sendcmdflag); - } + msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) + return -ETIMEDOUT; + + return 0; +} - BT_DBG("module cfg Command done"); +int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, int subcmd) +{ + int ret; + + ret = btmrvl_send_sync_cmd(priv, BT_CMD_MODULE_CFG_REQ, &subcmd, 1); + if (ret) + BT_ERR("module_cfg_cmd(%x) failed\n", subcmd); return ret; } @@ -214,61 +217,36 @@ EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd); int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv) { - struct sk_buff *skb; - struct btmrvl_cmd *cmd; - - skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); - if (!skb) { - BT_ERR("No free skb"); - return -ENOMEM; - } - - cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); - cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, - BT_CMD_HOST_SLEEP_CONFIG)); - cmd->length = 2; - cmd->data[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8; - cmd->data[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff); + int ret; + u8 param[2]; - bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; + param[0] = (priv->btmrvl_dev.gpio_gap & 0xff00) >> 8; + param[1] = (u8) (priv->btmrvl_dev.gpio_gap & 0x00ff); - skb->dev = (void *) priv->btmrvl_dev.hcidev; - skb_queue_head(&priv->adapter->tx_queue, skb); + BT_DBG("Sending HSCFG Command, gpio=0x%x, gap=0x%x", + param[0], param[1]); - BT_DBG("Queue HSCFG Command, gpio=0x%x, gap=0x%x", cmd->data[0], - cmd->data[1]); + ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_CONFIG, param, 2); + if (ret) + BT_ERR("HSCFG command failed\n"); - return 0; + return ret; } EXPORT_SYMBOL_GPL(btmrvl_send_hscfg_cmd); int btmrvl_enable_ps(struct btmrvl_private *priv) { - struct sk_buff *skb; - struct btmrvl_cmd *cmd; - - skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); - if (skb == NULL) { - BT_ERR("No free skb"); - return -ENOMEM; - } - - cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); - cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, - BT_CMD_AUTO_SLEEP_MODE)); - cmd->length = 1; + int ret; + u8 param; if (priv->btmrvl_dev.psmode) - cmd->data[0] = BT_PS_ENABLE; + param = BT_PS_ENABLE; else - cmd->data[0] = BT_PS_DISABLE; + param = BT_PS_DISABLE; - bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; - - skb->dev = (void *) priv->btmrvl_dev.hcidev; - skb_queue_head(&priv->adapter->tx_queue, skb); - - BT_DBG("Queue PSMODE Command:%d", cmd->data[0]); + ret = btmrvl_send_sync_cmd(priv, BT_CMD_AUTO_SLEEP_MODE, ¶m, 1); + if (ret) + BT_ERR("PSMODE command failed\n"); return 0; } @@ -276,37 +254,11 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps); int btmrvl_enable_hs(struct btmrvl_private *priv) { - struct sk_buff *skb; - struct btmrvl_cmd *cmd; - int ret = 0; - - skb = bt_skb_alloc(sizeof(*cmd), GFP_ATOMIC); - if (skb == NULL) { - BT_ERR("No free skb"); - return -ENOMEM; - } - - cmd = (struct btmrvl_cmd *) skb_put(skb, sizeof(*cmd)); - cmd->ocf_ogf = cpu_to_le16(hci_opcode_pack(OGF, BT_CMD_HOST_SLEEP_ENABLE)); - cmd->length = 0; - - bt_cb(skb)->pkt_type = MRVL_VENDOR_PKT; - - skb->dev = (void *) priv->btmrvl_dev.hcidev; - skb_queue_head(&priv->adapter->tx_queue, skb); - - BT_DBG("Queue hs enable Command"); - - wake_up_interruptible(&priv->main_thread.wait_q); + int ret; - if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, - priv->adapter->hs_state, - msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED))) { - ret = -ETIMEDOUT; - BT_ERR("timeout: %d, %d,%d", priv->adapter->hs_state, - priv->adapter->ps_state, - priv->adapter->wakeup_tries); - } + ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); + if (ret) + BT_ERR("Host sleep enable command failed\n"); return ret; } @@ -403,26 +355,12 @@ static void btmrvl_free_adapter(struct btmrvl_private *priv) priv->adapter = NULL; } -static int btmrvl_ioctl(struct hci_dev *hdev, - unsigned int cmd, unsigned long arg) +static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - return -ENOIOCTLCMD; -} - -static int btmrvl_send_frame(struct sk_buff *skb) -{ - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - struct btmrvl_private *priv = NULL; + struct btmrvl_private *priv = hci_get_drvdata(hdev); BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len); - if (!hdev) { - BT_ERR("Frame for unknown HCI device"); - return -ENODEV; - } - - priv = hci_get_drvdata(hdev); - if (!test_bit(HCI_RUNNING, &hdev->flags)) { BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags); print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET, @@ -479,6 +417,137 @@ static int btmrvl_open(struct hci_dev *hdev) return 0; } +/* + * This function parses provided calibration data input. It should contain + * hex bytes separated by space or new line character. Here is an example. + * 00 1C 01 37 FF FF FF FF 02 04 7F 01 + * CE BA 00 00 00 2D C6 C0 00 00 00 00 + * 00 F0 00 00 + */ +static int btmrvl_parse_cal_cfg(const u8 *src, u32 len, u8 *dst, u32 dst_size) +{ + const u8 *s = src; + u8 *d = dst; + int ret; + u8 tmp[3]; + + tmp[2] = '\0'; + while ((s - src) <= len - 2) { + if (isspace(*s)) { + s++; + continue; + } + + if (isxdigit(*s)) { + if ((d - dst) >= dst_size) { + BT_ERR("calibration data file too big!!!"); + return -EINVAL; + } + + memcpy(tmp, s, 2); + + ret = kstrtou8(tmp, 16, d++); + if (ret < 0) + return ret; + + s += 2; + } else { + return -EINVAL; + } + } + if (d == dst) + return -EINVAL; + + return 0; +} + +static int btmrvl_load_cal_data(struct btmrvl_private *priv, + u8 *config_data) +{ + int i, ret; + u8 data[BT_CMD_DATA_SIZE]; + + data[0] = 0x00; + data[1] = 0x00; + data[2] = 0x00; + data[3] = BT_CMD_DATA_SIZE - 4; + + /* Swap cal-data bytes. Each four bytes are swapped. Considering 4 + * byte SDIO header offset, mapping of input and output bytes will be + * {3, 2, 1, 0} -> {0+4, 1+4, 2+4, 3+4}, + * {7, 6, 5, 4} -> {4+4, 5+4, 6+4, 7+4} */ + for (i = 4; i < BT_CMD_DATA_SIZE; i++) + data[i] = config_data[(i / 4) * 8 - 1 - i]; + + print_hex_dump_bytes("Calibration data: ", + DUMP_PREFIX_OFFSET, data, BT_CMD_DATA_SIZE); + + ret = btmrvl_send_sync_cmd(priv, BT_CMD_LOAD_CONFIG_DATA, data, + BT_CMD_DATA_SIZE); + if (ret) + BT_ERR("Failed to download caibration data\n"); + + return 0; +} + +static int +btmrvl_process_cal_cfg(struct btmrvl_private *priv, u8 *data, u32 size) +{ + u8 cal_data[BT_CAL_DATA_SIZE]; + int ret; + + ret = btmrvl_parse_cal_cfg(data, size, cal_data, sizeof(cal_data)); + if (ret) + return ret; + + ret = btmrvl_load_cal_data(priv, cal_data); + if (ret) { + BT_ERR("Fail to load calibrate data"); + return ret; + } + + return 0; +} + +static int btmrvl_cal_data_config(struct btmrvl_private *priv) +{ + const struct firmware *cfg; + int ret; + const char *cal_data = priv->btmrvl_dev.cal_data; + + if (!cal_data) + return 0; + + ret = request_firmware(&cfg, cal_data, priv->btmrvl_dev.dev); + if (ret < 0) { + BT_DBG("Failed to get %s file, skipping cal data download", + cal_data); + return 0; + } + + ret = btmrvl_process_cal_cfg(priv, (u8 *)cfg->data, cfg->size); + release_firmware(cfg); + return ret; +} + +static int btmrvl_setup(struct hci_dev *hdev) +{ + struct btmrvl_private *priv = hci_get_drvdata(hdev); + + btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + + if (btmrvl_cal_data_config(priv)) + BT_ERR("Set cal data failed"); + + priv->btmrvl_dev.psmode = 1; + btmrvl_enable_ps(priv); + + priv->btmrvl_dev.gpio_gap = 0xffff; + btmrvl_send_hscfg_cmd(priv); + + return 0; +} + /* * This function handles the event generated by firmware, rx data * received from firmware, and tx data sent from kernel. @@ -566,14 +635,12 @@ int btmrvl_register_hdev(struct btmrvl_private *priv) priv->btmrvl_dev.hcidev = hdev; hci_set_drvdata(hdev, priv); - hdev->bus = HCI_SDIO; - hdev->open = btmrvl_open; + hdev->bus = HCI_SDIO; + hdev->open = btmrvl_open; hdev->close = btmrvl_close; hdev->flush = btmrvl_flush; - hdev->send = btmrvl_send_frame; - hdev->ioctl = btmrvl_ioctl; - - btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + hdev->send = btmrvl_send_frame; + hdev->setup = btmrvl_setup; hdev->dev_type = priv->btmrvl_dev.dev_type; diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 00da6df9f71edfcd6f92acbe70129af5ec62e0bf..fabcf5bb48afbb1c62cf5145d5a12f60eb3a05a8 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -18,7 +18,6 @@ * this warranty disclaimer. **/ -#include #include #include @@ -102,6 +101,7 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_88xx = { static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { .helper = "mrvl/sd8688_helper.bin", .firmware = "mrvl/sd8688.bin", + .cal_data = NULL, .reg = &btmrvl_reg_8688, .sd_blksz_fw_dl = 64, }; @@ -109,6 +109,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { .helper = NULL, .firmware = "mrvl/sd8787_uapsta.bin", + .cal_data = NULL, .reg = &btmrvl_reg_87xx, .sd_blksz_fw_dl = 256, }; @@ -116,6 +117,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { .helper = NULL, .firmware = "mrvl/sd8797_uapsta.bin", + .cal_data = "mrvl/sd8797_caldata.conf", .reg = &btmrvl_reg_87xx, .sd_blksz_fw_dl = 256, }; @@ -123,6 +125,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { .helper = NULL, .firmware = "mrvl/sd8897_uapsta.bin", + .cal_data = NULL, .reg = &btmrvl_reg_88xx, .sd_blksz_fw_dl = 256, }; @@ -597,15 +600,14 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) case HCI_SCODATA_PKT: case HCI_EVENT_PKT: bt_cb(skb)->pkt_type = type; - skb->dev = (void *)hdev; skb_put(skb, buf_len); skb_pull(skb, SDIO_HEADER_LEN); if (type == HCI_EVENT_PKT) { if (btmrvl_check_evtpkt(priv, skb)) - hci_recv_frame(skb); + hci_recv_frame(hdev, skb); } else { - hci_recv_frame(skb); + hci_recv_frame(hdev, skb); } hdev->stat.byte_rx += buf_len; @@ -613,12 +615,11 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv) case MRVL_VENDOR_PKT: bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; - skb->dev = (void *)hdev; skb_put(skb, buf_len); skb_pull(skb, SDIO_HEADER_LEN); if (btmrvl_process_event(priv, skb)) - hci_recv_frame(skb); + hci_recv_frame(hdev, skb); hdev->stat.byte_rx += buf_len; break; @@ -1006,6 +1007,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func, struct btmrvl_sdio_device *data = (void *) id->driver_data; card->helper = data->helper; card->firmware = data->firmware; + card->cal_data = data->cal_data; card->reg = data->reg; card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; } @@ -1034,6 +1036,8 @@ static int btmrvl_sdio_probe(struct sdio_func *func, } card->priv = priv; + priv->btmrvl_dev.dev = &card->func->dev; + priv->btmrvl_dev.cal_data = card->cal_data; /* Initialize the interface specific function pointers */ priv->hw_host_to_card = btmrvl_sdio_host_to_card; @@ -1046,12 +1050,6 @@ static int btmrvl_sdio_probe(struct sdio_func *func, goto disable_host_int; } - priv->btmrvl_dev.psmode = 1; - btmrvl_enable_ps(priv); - - priv->btmrvl_dev.gpio_gap = 0xffff; - btmrvl_send_hscfg_cmd(priv); - return 0; disable_host_int: @@ -1222,4 +1220,5 @@ MODULE_FIRMWARE("mrvl/sd8688_helper.bin"); MODULE_FIRMWARE("mrvl/sd8688.bin"); MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin"); MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin"); +MODULE_FIRMWARE("mrvl/sd8797_caldata.conf"); MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin"); diff --git a/drivers/bluetooth/btmrvl_sdio.h b/drivers/bluetooth/btmrvl_sdio.h index 43d35a609ca9a94795afb731d230fa88ca109bef..6872d9ecac074ba04792c22776b3baee3971c9de 100644 --- a/drivers/bluetooth/btmrvl_sdio.h +++ b/drivers/bluetooth/btmrvl_sdio.h @@ -85,6 +85,7 @@ struct btmrvl_sdio_card { u32 ioport; const char *helper; const char *firmware; + const char *cal_data; const struct btmrvl_sdio_card_reg *reg; u16 sd_blksz_fw_dl; u8 rx_unit; @@ -94,6 +95,7 @@ struct btmrvl_sdio_card { struct btmrvl_sdio_device { const char *helper; const char *firmware; + const char *cal_data; const struct btmrvl_sdio_card_reg *reg; u16 sd_blksz_fw_dl; }; diff --git a/drivers/bluetooth/btsdio.c b/drivers/bluetooth/btsdio.c index 4a9909713874dd03eb52240b148ed83b9a22b9d8..b61440aaee658210143f435a872ed51e649485e6 100644 --- a/drivers/bluetooth/btsdio.c +++ b/drivers/bluetooth/btsdio.c @@ -157,10 +157,9 @@ static int btsdio_rx_packet(struct btsdio_data *data) data->hdev->stat.byte_rx += len; - skb->dev = (void *) data->hdev; bt_cb(skb)->pkt_type = hdr[3]; - err = hci_recv_frame(skb); + err = hci_recv_frame(data->hdev, skb); if (err < 0) return err; @@ -255,9 +254,8 @@ static int btsdio_flush(struct hci_dev *hdev) return 0; } -static int btsdio_send_frame(struct sk_buff *skb) +static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct btsdio_data *data = hci_get_drvdata(hdev); BT_DBG("%s", hdev->name); diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index beb262f2dc4d087432f6e18ab0862888683d0c6d..a03ecc22a561caf4c3d1bdc53489a5b9612745d8 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -198,7 +198,6 @@ static void btuart_receive(btuart_info_t *info) if (info->rx_state == RECV_WAIT_PACKET_TYPE) { - info->rx_skb->dev = (void *) info->hdev; bt_cb(info->rx_skb)->pkt_type = inb(iobase + UART_RX); switch (bt_cb(info->rx_skb)->pkt_type) { @@ -265,7 +264,7 @@ static void btuart_receive(btuart_info_t *info) break; case RECV_WAIT_DATA: - hci_recv_frame(info->rx_skb); + hci_recv_frame(info->hdev, info->rx_skb); info->rx_skb = NULL; break; @@ -424,17 +423,9 @@ static int btuart_hci_close(struct hci_dev *hdev) } -static int btuart_hci_send_frame(struct sk_buff *skb) +static int btuart_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - btuart_info_t *info; - struct hci_dev *hdev = (struct hci_dev *)(skb->dev); - - if (!hdev) { - BT_ERR("Frame for unknown HCI device (hdev=NULL)"); - return -ENODEV; - } - - info = hci_get_drvdata(hdev); + btuart_info_t *info = hci_get_drvdata(hdev); switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: @@ -458,12 +449,6 @@ static int btuart_hci_send_frame(struct sk_buff *skb) } -static int btuart_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; -} - - /* ======================== Card services HCI interaction ======================== */ @@ -495,11 +480,10 @@ static int btuart_open(btuart_info_t *info) hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); - hdev->open = btuart_hci_open; - hdev->close = btuart_hci_close; - hdev->flush = btuart_hci_flush; - hdev->send = btuart_hci_send_frame; - hdev->ioctl = btuart_hci_ioctl; + hdev->open = btuart_hci_open; + hdev->close = btuart_hci_close; + hdev->flush = btuart_hci_flush; + hdev->send = btuart_hci_send_frame; spin_lock_irqsave(&(info->lock), flags); diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index f3dfc0a88fdcb95e25647caa6cfc14a1d9a5a484..c0ff34f2d2df577efffe902562f9578690ea4e39 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -50,7 +50,7 @@ static struct usb_driver btusb_driver; #define BTUSB_ATH3012 0x80 #define BTUSB_INTEL 0x100 -static struct usb_device_id btusb_table[] = { +static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, @@ -121,7 +121,7 @@ static struct usb_device_id btusb_table[] = { MODULE_DEVICE_TABLE(usb, btusb_table); -static struct usb_device_id blacklist_table[] = { +static const struct usb_device_id blacklist_table[] = { /* CSR BlueCore devices */ { USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR }, @@ -716,9 +716,8 @@ static int btusb_flush(struct hci_dev *hdev) return 0; } -static int btusb_send_frame(struct sk_buff *skb) +static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev *hdev = (struct hci_dev *) skb->dev; struct btusb_data *data = hci_get_drvdata(hdev); struct usb_ctrlrequest *dr; struct urb *urb; @@ -730,6 +729,8 @@ static int btusb_send_frame(struct sk_buff *skb) if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; + skb->dev = (void *) hdev; + switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: urb = usb_alloc_urb(0, GFP_ATOMIC); @@ -774,7 +775,7 @@ static int btusb_send_frame(struct sk_buff *skb) break; case HCI_SCODATA_PKT: - if (!data->isoc_tx_ep || hdev->conn_hash.sco_num < 1) + if (!data->isoc_tx_ep || hci_conn_num(hdev, SCO_LINK) < 1) return -ENODEV; urb = usb_alloc_urb(BTUSB_MAX_ISOC_FRAMES, GFP_ATOMIC); @@ -833,8 +834,8 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt) BT_DBG("%s evt %d", hdev->name, evt); - if (hdev->conn_hash.sco_num != data->sco_num) { - data->sco_num = hdev->conn_hash.sco_num; + if (hci_conn_num(hdev, SCO_LINK) != data->sco_num) { + data->sco_num = hci_conn_num(hdev, SCO_LINK); schedule_work(&data->work); } } @@ -889,7 +890,7 @@ static void btusb_work(struct work_struct *work) int new_alts; int err; - if (hdev->conn_hash.sco_num > 0) { + if (data->sco_num > 0) { if (!test_bit(BTUSB_DID_ISO_RESUME, &data->flags)) { err = usb_autopm_get_interface(data->isoc ? data->isoc : data->intf); if (err < 0) { @@ -903,9 +904,9 @@ static void btusb_work(struct work_struct *work) if (hdev->voice_setting & 0x0020) { static const int alts[3] = { 2, 4, 5 }; - new_alts = alts[hdev->conn_hash.sco_num - 1]; + new_alts = alts[data->sco_num - 1]; } else { - new_alts = hdev->conn_hash.sco_num; + new_alts = data->sco_num; } if (data->isoc_altsetting != new_alts) { @@ -1628,7 +1629,6 @@ static struct usb_driver btusb_driver = { #ifdef CONFIG_PM .suspend = btusb_suspend, .resume = btusb_resume, - .reset_resume = btusb_resume, #endif .id_table = btusb_table, .supports_autosuspend = 1, diff --git a/drivers/bluetooth/btwilink.c b/drivers/bluetooth/btwilink.c index 60abf596f60ea21f9354ae1b3d3608bb04e4142e..f038dba19e36b77973da26ee9688ed7791126d2f 100644 --- a/drivers/bluetooth/btwilink.c +++ b/drivers/bluetooth/btwilink.c @@ -108,10 +108,8 @@ static long st_receive(void *priv_data, struct sk_buff *skb) return -EFAULT; } - skb->dev = (void *) lhst->hdev; - /* Forward skb to HCI core layer */ - err = hci_recv_frame(skb); + err = hci_recv_frame(lhst->hdev, skb); if (err < 0) { BT_ERR("Unable to push skb to HCI core(%d)", err); return err; @@ -253,14 +251,11 @@ static int ti_st_close(struct hci_dev *hdev) return err; } -static int ti_st_send_frame(struct sk_buff *skb) +static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev *hdev; struct ti_st *hst; long len; - hdev = (struct hci_dev *)skb->dev; - if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 33f3a6950c0e85e852766d41bc80db4cd986d98f..52eed1f3565dbf5868c094d246cf7dee4dcc86a4 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -256,9 +256,8 @@ static void dtl1_receive(dtl1_info_t *info) case 0x83: case 0x84: /* send frame to the HCI layer */ - info->rx_skb->dev = (void *) info->hdev; bt_cb(info->rx_skb)->pkt_type &= 0x0f; - hci_recv_frame(info->rx_skb); + hci_recv_frame(info->hdev, info->rx_skb); break; default: /* unknown packet */ @@ -383,20 +382,12 @@ static int dtl1_hci_close(struct hci_dev *hdev) } -static int dtl1_hci_send_frame(struct sk_buff *skb) +static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - dtl1_info_t *info; - struct hci_dev *hdev = (struct hci_dev *)(skb->dev); + dtl1_info_t *info = hci_get_drvdata(hdev); struct sk_buff *s; nsh_t nsh; - if (!hdev) { - BT_ERR("Frame for unknown HCI device (hdev=NULL)"); - return -ENODEV; - } - - info = hci_get_drvdata(hdev); - switch (bt_cb(skb)->pkt_type) { case HCI_COMMAND_PKT: hdev->stat.cmd_tx++; @@ -438,12 +429,6 @@ static int dtl1_hci_send_frame(struct sk_buff *skb) } -static int dtl1_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) -{ - return -ENOIOCTLCMD; -} - - /* ======================== Card services HCI interaction ======================== */ @@ -477,11 +462,10 @@ static int dtl1_open(dtl1_info_t *info) hci_set_drvdata(hdev, info); SET_HCIDEV_DEV(hdev, &info->p_dev->dev); - hdev->open = dtl1_hci_open; - hdev->close = dtl1_hci_close; - hdev->flush = dtl1_hci_flush; - hdev->send = dtl1_hci_send_frame; - hdev->ioctl = dtl1_hci_ioctl; + hdev->open = dtl1_hci_open; + hdev->close = dtl1_hci_close; + hdev->flush = dtl1_hci_flush; + hdev->send = dtl1_hci_send_frame; spin_lock_irqsave(&(info->lock), flags); diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index 57e502e0608058ca9943b7cf5c14613339562729..0bc87f7abd958a262028cdd1f0cb7cb0b624c45e 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -522,7 +522,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu) memcpy(skb_push(bcsp->rx_skb, HCI_EVENT_HDR_SIZE), &hdr, HCI_EVENT_HDR_SIZE); bt_cb(bcsp->rx_skb)->pkt_type = HCI_EVENT_PKT; - hci_recv_frame(bcsp->rx_skb); + hci_recv_frame(hu->hdev, bcsp->rx_skb); } else { BT_ERR ("Packet for unknown channel (%u %s)", bcsp->rx_skb->data[1] & 0x0f, @@ -536,7 +536,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu) /* Pull out BCSP hdr */ skb_pull(bcsp->rx_skb, 4); - hci_recv_frame(bcsp->rx_skb); + hci_recv_frame(hu->hdev, bcsp->rx_skb); } bcsp->rx_state = BCSP_W4_PKT_DELIMITER; @@ -655,7 +655,6 @@ static int bcsp_recv(struct hci_uart *hu, void *data, int count) bcsp->rx_count = 0; return 0; } - bcsp->rx_skb->dev = (void *) hu->hdev; break; } break; diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 8ae9f1ea2bb5e59dd40f5c905c3057b5f0e35417..7048a583fe51a695a044ad541a0f894e3bf7c510 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -124,30 +124,6 @@ static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) return 0; } -static inline int h4_check_data_len(struct h4_struct *h4, int len) -{ - int room = skb_tailroom(h4->rx_skb); - - BT_DBG("len %d room %d", len, room); - - if (!len) { - hci_recv_frame(h4->rx_skb); - } else if (len > room) { - BT_ERR("Data length is too large"); - kfree_skb(h4->rx_skb); - } else { - h4->rx_state = H4_W4_DATA; - h4->rx_count = len; - return len; - } - - h4->rx_state = H4_W4_PACKET_TYPE; - h4->rx_skb = NULL; - h4->rx_count = 0; - - return 0; -} - /* Recv data */ static int h4_recv(struct hci_uart *hu, void *data, int count) { diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index b6154d5a07a51cf954b1e34d869cb6d3feb75f53..f6f4974505600a2884f56c01be22bd6a79492f04 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -340,7 +340,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu) /* Remove Three-wire header */ skb_pull(h5->rx_skb, 4); - hci_recv_frame(h5->rx_skb); + hci_recv_frame(hu->hdev, h5->rx_skb); h5->rx_skb = NULL; break; diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index bc68a440d432cd21b2ee10885e7106cfe2852b79..6e06f6f6915296ad08d7da394175da19250364db 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -234,21 +234,13 @@ static int hci_uart_close(struct hci_dev *hdev) } /* Send frames from HCI layer */ -static int hci_uart_send_frame(struct sk_buff *skb) +static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev* hdev = (struct hci_dev *) skb->dev; - struct hci_uart *hu; - - if (!hdev) { - BT_ERR("Frame for unknown device (hdev=NULL)"); - return -ENODEV; - } + struct hci_uart *hu = hci_get_drvdata(hdev); if (!test_bit(HCI_RUNNING, &hdev->flags)) return -EBUSY; - hu = hci_get_drvdata(hdev); - BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); hu->proto->enqueue(hu, skb); diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index cfc7679385890b6e0feaadee1462a1e85330cc59..69a90b1b5ff56131aa7a2e51b7d18aabba28b560 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -110,7 +110,6 @@ static int send_hcill_cmd(u8 cmd, struct hci_uart *hu) /* prepare packet */ hcill_packet = (struct hcill_cmd *) skb_put(skb, 1); hcill_packet->cmd = cmd; - skb->dev = (void *) hu->hdev; /* send packet */ skb_queue_tail(&ll->txq, skb); @@ -346,14 +345,14 @@ static int ll_enqueue(struct hci_uart *hu, struct sk_buff *skb) return 0; } -static inline int ll_check_data_len(struct ll_struct *ll, int len) +static inline int ll_check_data_len(struct hci_dev *hdev, struct ll_struct *ll, int len) { int room = skb_tailroom(ll->rx_skb); BT_DBG("len %d room %d", len, room); if (!len) { - hci_recv_frame(ll->rx_skb); + hci_recv_frame(hdev, ll->rx_skb); } else if (len > room) { BT_ERR("Data length is too large"); kfree_skb(ll->rx_skb); @@ -395,7 +394,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count) switch (ll->rx_state) { case HCILL_W4_DATA: BT_DBG("Complete data"); - hci_recv_frame(ll->rx_skb); + hci_recv_frame(hu->hdev, ll->rx_skb); ll->rx_state = HCILL_W4_PACKET_TYPE; ll->rx_skb = NULL; @@ -406,7 +405,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count) BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); - ll_check_data_len(ll, eh->plen); + ll_check_data_len(hu->hdev, ll, eh->plen); continue; case HCILL_W4_ACL_HDR: @@ -415,7 +414,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count) BT_DBG("ACL header: dlen %d", dlen); - ll_check_data_len(ll, dlen); + ll_check_data_len(hu->hdev, ll, dlen); continue; case HCILL_W4_SCO_HDR: @@ -423,7 +422,7 @@ static int ll_recv(struct hci_uart *hu, void *data, int count) BT_DBG("SCO header: dlen %d", sh->dlen); - ll_check_data_len(ll, sh->dlen); + ll_check_data_len(hu->hdev, ll, sh->dlen); continue; } } @@ -494,7 +493,6 @@ static int ll_recv(struct hci_uart *hu, void *data, int count) return -ENOMEM; } - ll->rx_skb->dev = (void *) hu->hdev; bt_cb(ll->rx_skb)->pkt_type = type; } diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index d8b7aed6e4a96f6d6997ab9fac2a1ac84fbc4700..7b167385a1c4e8bdf7d0974707f0b0b43ea21f43 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -24,6 +24,7 @@ */ #include +#include #include #include @@ -39,17 +40,17 @@ #include #include -#define VERSION "1.3" +#define VERSION "1.4" static bool amp; struct vhci_data { struct hci_dev *hdev; - unsigned long flags; - wait_queue_head_t read_wait; struct sk_buff_head readq; + + struct delayed_work open_timeout; }; static int vhci_open_dev(struct hci_dev *hdev) @@ -80,35 +81,73 @@ static int vhci_flush(struct hci_dev *hdev) return 0; } -static int vhci_send_frame(struct sk_buff *skb) +static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev* hdev = (struct hci_dev *) skb->dev; - struct vhci_data *data; + struct vhci_data *data = hci_get_drvdata(hdev); + + if (!test_bit(HCI_RUNNING, &hdev->flags)) + return -EBUSY; + + memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + skb_queue_tail(&data->readq, skb); + + wake_up_interruptible(&data->read_wait); + return 0; +} +static int vhci_create_device(struct vhci_data *data, __u8 dev_type) +{ + struct hci_dev *hdev; + struct sk_buff *skb; + + skb = bt_skb_alloc(4, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + hdev = hci_alloc_dev(); if (!hdev) { - BT_ERR("Frame for unknown HCI device (hdev=NULL)"); - return -ENODEV; + kfree_skb(skb); + return -ENOMEM; } - if (!test_bit(HCI_RUNNING, &hdev->flags)) + data->hdev = hdev; + + hdev->bus = HCI_VIRTUAL; + hdev->dev_type = dev_type; + hci_set_drvdata(hdev, data); + + hdev->open = vhci_open_dev; + hdev->close = vhci_close_dev; + hdev->flush = vhci_flush; + hdev->send = vhci_send_frame; + + if (hci_register_dev(hdev) < 0) { + BT_ERR("Can't register HCI device"); + hci_free_dev(hdev); + data->hdev = NULL; + kfree_skb(skb); return -EBUSY; + } - data = hci_get_drvdata(hdev); + bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; - memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1); + *skb_put(skb, 1) = 0xff; + *skb_put(skb, 1) = dev_type; + put_unaligned_le16(hdev->id, skb_put(skb, 2)); skb_queue_tail(&data->readq, skb); wake_up_interruptible(&data->read_wait); - return 0; } static inline ssize_t vhci_get_user(struct vhci_data *data, - const char __user *buf, size_t count) + const char __user *buf, size_t count) { struct sk_buff *skb; + __u8 pkt_type, dev_type; + int ret; - if (count > HCI_MAX_FRAME_SIZE) + if (count < 2 || count > HCI_MAX_FRAME_SIZE) return -EINVAL; skb = bt_skb_alloc(count, GFP_KERNEL); @@ -120,27 +159,69 @@ static inline ssize_t vhci_get_user(struct vhci_data *data, return -EFAULT; } - skb->dev = (void *) data->hdev; - bt_cb(skb)->pkt_type = *((__u8 *) skb->data); + pkt_type = *((__u8 *) skb->data); skb_pull(skb, 1); - hci_recv_frame(skb); + switch (pkt_type) { + case HCI_EVENT_PKT: + case HCI_ACLDATA_PKT: + case HCI_SCODATA_PKT: + if (!data->hdev) { + kfree_skb(skb); + return -ENODEV; + } + + bt_cb(skb)->pkt_type = pkt_type; + + ret = hci_recv_frame(data->hdev, skb); + break; - return count; + case HCI_VENDOR_PKT: + if (data->hdev) { + kfree_skb(skb); + return -EBADFD; + } + + cancel_delayed_work_sync(&data->open_timeout); + + dev_type = *((__u8 *) skb->data); + skb_pull(skb, 1); + + if (skb->len > 0) { + kfree_skb(skb); + return -EINVAL; + } + + kfree_skb(skb); + + if (dev_type != HCI_BREDR && dev_type != HCI_AMP) + return -EINVAL; + + ret = vhci_create_device(data, dev_type); + break; + + default: + kfree_skb(skb); + return -EINVAL; + } + + return (ret < 0) ? ret : count; } static inline ssize_t vhci_put_user(struct vhci_data *data, - struct sk_buff *skb, char __user *buf, int count) + struct sk_buff *skb, + char __user *buf, int count) { char __user *ptr = buf; - int len, total = 0; + int len; len = min_t(unsigned int, skb->len, count); if (copy_to_user(ptr, skb->data, len)) return -EFAULT; - total += len; + if (!data->hdev) + return len; data->hdev->stat.byte_tx += len; @@ -148,21 +229,19 @@ static inline ssize_t vhci_put_user(struct vhci_data *data, case HCI_COMMAND_PKT: data->hdev->stat.cmd_tx++; break; - case HCI_ACLDATA_PKT: data->hdev->stat.acl_tx++; break; - case HCI_SCODATA_PKT: data->hdev->stat.sco_tx++; break; } - return total; + return len; } static ssize_t vhci_read(struct file *file, - char __user *buf, size_t count, loff_t *pos) + char __user *buf, size_t count, loff_t *pos) { struct vhci_data *data = file->private_data; struct sk_buff *skb; @@ -185,7 +264,7 @@ static ssize_t vhci_read(struct file *file, } ret = wait_event_interruptible(data->read_wait, - !skb_queue_empty(&data->readq)); + !skb_queue_empty(&data->readq)); if (ret < 0) break; } @@ -194,7 +273,7 @@ static ssize_t vhci_read(struct file *file, } static ssize_t vhci_write(struct file *file, - const char __user *buf, size_t count, loff_t *pos) + const char __user *buf, size_t count, loff_t *pos) { struct vhci_data *data = file->private_data; @@ -213,10 +292,17 @@ static unsigned int vhci_poll(struct file *file, poll_table *wait) return POLLOUT | POLLWRNORM; } +static void vhci_open_timeout(struct work_struct *work) +{ + struct vhci_data *data = container_of(work, struct vhci_data, + open_timeout.work); + + vhci_create_device(data, amp ? HCI_AMP : HCI_BREDR); +} + static int vhci_open(struct inode *inode, struct file *file) { struct vhci_data *data; - struct hci_dev *hdev; data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL); if (!data) @@ -225,35 +311,13 @@ static int vhci_open(struct inode *inode, struct file *file) skb_queue_head_init(&data->readq); init_waitqueue_head(&data->read_wait); - hdev = hci_alloc_dev(); - if (!hdev) { - kfree(data); - return -ENOMEM; - } - - data->hdev = hdev; - - hdev->bus = HCI_VIRTUAL; - hci_set_drvdata(hdev, data); - - if (amp) - hdev->dev_type = HCI_AMP; - - hdev->open = vhci_open_dev; - hdev->close = vhci_close_dev; - hdev->flush = vhci_flush; - hdev->send = vhci_send_frame; - - if (hci_register_dev(hdev) < 0) { - BT_ERR("Can't register HCI device"); - kfree(data); - hci_free_dev(hdev); - return -EBUSY; - } + INIT_DELAYED_WORK(&data->open_timeout, vhci_open_timeout); file->private_data = data; nonseekable_open(inode, file); + schedule_delayed_work(&data->open_timeout, msecs_to_jiffies(1000)); + return 0; } @@ -262,8 +326,12 @@ static int vhci_release(struct inode *inode, struct file *file) struct vhci_data *data = file->private_data; struct hci_dev *hdev = data->hdev; - hci_unregister_dev(hdev); - hci_free_dev(hdev); + cancel_delayed_work_sync(&data->open_timeout); + + if (hdev) { + hci_unregister_dev(hdev); + hci_free_dev(hdev); + } file->private_data = NULL; kfree(data); @@ -309,3 +377,4 @@ MODULE_AUTHOR("Marcel Holtmann "); MODULE_DESCRIPTION("Bluetooth virtual HCI driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); +MODULE_ALIAS("devname:vhci"); diff --git a/drivers/char/random.c b/drivers/char/random.c index 7a744d39175638a381835a8cadce7039f58ee3bf..4fe5609eeb72beb69e31ed1e7832585eafe0c591 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -603,8 +603,11 @@ retry: if (!r->initialized && nbits > 0) { r->entropy_total += nbits; - if (r->entropy_total > 128) + if (r->entropy_total > 128) { r->initialized = 1; + if (r == &nonblocking_pool) + prandom_reseed_late(); + } } trace_credit_entropy_bits(r->name, nbits, entropy_count, diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index dab4b41f1715846bd2a26c03fd90f0175766980e..d2172e71f985fd9f8ec1418c1c719a5491b97a44 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -1848,6 +1848,26 @@ static int cma_resolve_iw_route(struct rdma_id_private *id_priv, int timeout_ms) return 0; } +static int iboe_tos_to_sl(struct net_device *ndev, int tos) +{ + int prio; + struct net_device *dev; + + prio = rt_tos2priority(tos); + dev = ndev->priv_flags & IFF_802_1Q_VLAN ? + vlan_dev_real_dev(ndev) : ndev; + + if (dev->num_tc) + return netdev_get_prio_tc_map(dev, prio); + +#if IS_ENABLED(CONFIG_VLAN_8021Q) + if (ndev->priv_flags & IFF_802_1Q_VLAN) + return (vlan_dev_get_egress_qos_mask(ndev, prio) & + VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; +#endif + return 0; +} + static int cma_resolve_iboe_route(struct rdma_id_private *id_priv) { struct rdma_route *route = &id_priv->id.route; @@ -1888,11 +1908,7 @@ static int cma_resolve_iboe_route(struct rdma_id_private *id_priv) route->path_rec->reversible = 1; route->path_rec->pkey = cpu_to_be16(0xffff); route->path_rec->mtu_selector = IB_SA_EQ; - route->path_rec->sl = netdev_get_prio_tc_map( - ndev->priv_flags & IFF_802_1Q_VLAN ? - vlan_dev_real_dev(ndev) : ndev, - rt_tos2priority(id_priv->tos)); - + route->path_rec->sl = iboe_tos_to_sl(ndev, id_priv->tos); route->path_rec->mtu = iboe_get_mtu(ndev->mtu); route->path_rec->rate_selector = IB_SA_EQ; route->path_rec->rate = iboe_get_rate(ndev); @@ -2294,7 +2310,7 @@ static int cma_alloc_any_port(struct idr *ps, struct rdma_id_private *id_priv) int low, high, remaining; unsigned int rover; - inet_get_local_port_range(&low, &high); + inet_get_local_port_range(&init_net, &low, &high); remaining = (high - low) + 1; rover = net_random() % remaining + low; retry: diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index f0612645de998f6bcba2dfa0ae34a2b51483af38..6a0a0d29660df5fe7d3cd90bf5c74eead9da4936 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -177,18 +177,18 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->max_mr_size = ~0ull; props->page_size_cap = dev->dev->caps.page_size_cap; - props->max_qp = dev->dev->caps.num_qps - dev->dev->caps.reserved_qps; + props->max_qp = dev->dev->quotas.qp; props->max_qp_wr = dev->dev->caps.max_wqes - MLX4_IB_SQ_MAX_SPARE; props->max_sge = min(dev->dev->caps.max_sq_sg, dev->dev->caps.max_rq_sg); - props->max_cq = dev->dev->caps.num_cqs - dev->dev->caps.reserved_cqs; + props->max_cq = dev->dev->quotas.cq; props->max_cqe = dev->dev->caps.max_cqes; - props->max_mr = dev->dev->caps.num_mpts - dev->dev->caps.reserved_mrws; + props->max_mr = dev->dev->quotas.mpt; props->max_pd = dev->dev->caps.num_pds - dev->dev->caps.reserved_pds; props->max_qp_rd_atom = dev->dev->caps.max_qp_dest_rdma; props->max_qp_init_rd_atom = dev->dev->caps.max_qp_init_rdma; props->max_res_rd_atom = props->max_qp_rd_atom * props->max_qp; - props->max_srq = dev->dev->caps.num_srqs - dev->dev->caps.reserved_srqs; + props->max_srq = dev->dev->quotas.srq; props->max_srq_wr = dev->dev->caps.max_srq_wqes - 1; props->max_srq_sge = dev->dev->caps.max_srq_sge; props->max_fast_reg_page_list_len = MLX4_MAX_FAST_REG_PAGES; @@ -526,7 +526,6 @@ static int mlx4_ib_modify_device(struct ib_device *ibdev, int mask, if (IS_ERR(mailbox)) return 0; - memset(mailbox->buf, 0, 256); memcpy(mailbox->buf, props->node_desc, 64); mlx4_cmd(to_mdev(ibdev)->dev, mailbox->dma, 1, 0, MLX4_CMD_SET_NODE, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); @@ -547,8 +546,6 @@ static int mlx4_SET_PORT(struct mlx4_ib_dev *dev, u8 port, int reset_qkey_viols, if (IS_ERR(mailbox)) return PTR_ERR(mailbox); - memset(mailbox->buf, 0, 256); - if (dev->dev->flags & MLX4_FLAG_OLD_PORT_CMDS) { *(u8 *) mailbox->buf = !!reset_qkey_viols << 6; ((__be32 *) mailbox->buf)[2] = cpu_to_be32(cap_mask); @@ -879,8 +876,6 @@ static int __mlx4_ib_create_flow(struct ib_qp *qp, struct ib_flow_attr *flow_att struct mlx4_ib_dev *mdev = to_mdev(qp->device); struct mlx4_cmd_mailbox *mailbox; struct mlx4_net_trans_rule_hw_ctrl *ctrl; - size_t rule_size = sizeof(struct mlx4_net_trans_rule_hw_ctrl) + - (sizeof(struct _rule_hw) * flow_attr->num_of_specs); static const u16 __mlx4_domain[] = { [IB_FLOW_DOMAIN_USER] = MLX4_DOMAIN_UVERBS, @@ -905,7 +900,6 @@ static int __mlx4_ib_create_flow(struct ib_qp *qp, struct ib_flow_attr *flow_att mailbox = mlx4_alloc_cmd_mailbox(mdev->dev); if (IS_ERR(mailbox)) return PTR_ERR(mailbox); - memset(mailbox->buf, 0, rule_size); ctrl = mailbox->buf; ctrl->prio = cpu_to_be16(__mlx4_domain[domain] | diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c index 52377b4bf039e0cbe19249ee3361acbd6ce85014..a2e0ed6c9a4d367db910d9493974bcd116a369e1 100644 --- a/drivers/isdn/hardware/eicon/divasmain.c +++ b/drivers/isdn/hardware/eicon/divasmain.c @@ -481,7 +481,7 @@ void __inline__ outpp(void __iomem *addr, word p) int diva_os_register_irq(void *context, byte irq, const char *name) { int result = request_irq(irq, diva_os_irq_wrapper, - IRQF_DISABLED | IRQF_SHARED, name, context); + IRQF_SHARED, name, context); return (result); } diff --git a/drivers/isdn/hardware/eicon/um_idi.c b/drivers/isdn/hardware/eicon/um_idi.c index 7cab5c3276c2e16f9e91fb97a635e7ccdf6640b5..e1519718ce67e3ed4363078eb3e8207b6b9f1db6 100644 --- a/drivers/isdn/hardware/eicon/um_idi.c +++ b/drivers/isdn/hardware/eicon/um_idi.c @@ -288,9 +288,9 @@ int divas_um_idi_delete_entity(int adapter_nr, void *entity) cleanup_entity(e); diva_os_free(0, e->os_context); memset(e, 0x00, sizeof(*e)); - diva_os_free(0, e); DBG_LOG(("A(%d) remove E:%08x", adapter_nr, e)); + diva_os_free(0, e); return (0); } diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c index e74df7c4658f1b987acf6dec2b855419fb9a174a..53d487f0c79ddf9b4dc605a8c8beebb8dcdd92f3 100644 --- a/drivers/isdn/icn/icn.c +++ b/drivers/isdn/icn/icn.c @@ -1580,8 +1580,7 @@ icn_addcard(int port, char *id1, char *id2) } if (!(card2 = icn_initcard(port, id2))) { printk(KERN_INFO - "icn: (%s) half ICN-4B, port 0x%x added\n", - card2->interface.id, port); + "icn: (%s) half ICN-4B, port 0x%x added\n", id2, port); return 0; } card->doubleS0 = 1; diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c index ca997bd4e818c6c1606ddea03d53d8d5eb51cdd4..92acc81f844d161a7e47d9477c2e49e941929d58 100644 --- a/drivers/isdn/sc/init.c +++ b/drivers/isdn/sc/init.c @@ -336,7 +336,7 @@ static int __init sc_init(void) */ sc_adapter[cinst]->interrupt = irq[b]; if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler, - IRQF_DISABLED, interface->id, + 0, interface->id, (void *)(unsigned long) cinst)) { kfree(sc_adapter[cinst]->channel); diff --git a/drivers/net/bonding/Makefile b/drivers/net/bonding/Makefile index 4c21bf6b8b2f015abfaf77b414d3e5b7670026f2..5a5d720da9292e787b4a2cdb94e33bbe883e8920 100644 --- a/drivers/net/bonding/Makefile +++ b/drivers/net/bonding/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_BONDING) += bonding.o -bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o +bonding-objs := bond_main.o bond_3ad.o bond_alb.o bond_sysfs.o bond_debugfs.o bond_netlink.o bond_options.o proc-$(CONFIG_PROC_FS) += bond_procfs.o bonding-objs += $(proc-y) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 0d8f427ade938c75052d804138cd377b02c34f99..187b1b7772ef1b873303fc46998a591137bec7b7 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -135,41 +135,6 @@ static inline struct bonding *__get_bond_by_port(struct port *port) return bond_get_bond_by_slave(port->slave); } -/** - * __get_first_port - get the first port in the bond - * @bond: the bond we're looking at - * - * Return the port of the first slave in @bond, or %NULL if it can't be found. - */ -static inline struct port *__get_first_port(struct bonding *bond) -{ - struct slave *first_slave = bond_first_slave(bond); - - return first_slave ? &(SLAVE_AD_INFO(first_slave).port) : NULL; -} - -/** - * __get_next_port - get the next port in the bond - * @port: the port we're looking at - * - * Return the port of the slave that is next in line of @port's slave in the - * bond, or %NULL if it can't be found. - */ -static inline struct port *__get_next_port(struct port *port) -{ - struct bonding *bond = __get_bond_by_port(port); - struct slave *slave = port->slave, *slave_next; - - // If there's no bond for this port, or this is the last slave - if (bond == NULL) - return NULL; - slave_next = bond_next_slave(bond, slave); - if (!slave_next || bond_is_first_slave(bond, slave_next)) - return NULL; - - return &(SLAVE_AD_INFO(slave_next).port); -} - /** * __get_first_agg - get the first aggregator in the bond * @bond: the bond we're looking at @@ -190,28 +155,6 @@ static inline struct aggregator *__get_first_agg(struct port *port) return first_slave ? &(SLAVE_AD_INFO(first_slave).aggregator) : NULL; } -/** - * __get_next_agg - get the next aggregator in the bond - * @aggregator: the aggregator we're looking at - * - * Return the aggregator of the slave that is next in line of @aggregator's - * slave in the bond, or %NULL if it can't be found. - */ -static inline struct aggregator *__get_next_agg(struct aggregator *aggregator) -{ - struct slave *slave = aggregator->slave, *slave_next; - struct bonding *bond = bond_get_bond_by_slave(slave); - - // If there's no bond for this aggregator, or this is the last slave - if (bond == NULL) - return NULL; - slave_next = bond_next_slave(bond, slave); - if (!slave_next || bond_is_first_slave(bond, slave_next)) - return NULL; - - return &(SLAVE_AD_INFO(slave_next).aggregator); -} - /* * __agg_has_partner * @@ -755,16 +698,15 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator) */ static struct aggregator *__get_active_agg(struct aggregator *aggregator) { - struct aggregator *retval = NULL; + struct bonding *bond = aggregator->slave->bond; + struct list_head *iter; + struct slave *slave; - for (; aggregator; aggregator = __get_next_agg(aggregator)) { - if (aggregator->is_active) { - retval = aggregator; - break; - } - } + bond_for_each_slave(bond, slave, iter) + if (SLAVE_AD_INFO(slave).aggregator.is_active) + return &(SLAVE_AD_INFO(slave).aggregator); - return retval; + return NULL; } /** @@ -1274,12 +1216,17 @@ static void ad_port_selection_logic(struct port *port) { struct aggregator *aggregator, *free_aggregator = NULL, *temp_aggregator; struct port *last_port = NULL, *curr_port; + struct list_head *iter; + struct bonding *bond; + struct slave *slave; int found = 0; // if the port is already Selected, do nothing if (port->sm_vars & AD_PORT_SELECTED) return; + bond = __get_bond_by_port(port); + // if the port is connected to other aggregator, detach it if (port->aggregator) { // detach the port from its former aggregator @@ -1320,8 +1267,8 @@ static void ad_port_selection_logic(struct port *port) } } // search on all aggregators for a suitable aggregator for this port - for (aggregator = __get_first_agg(port); aggregator; - aggregator = __get_next_agg(aggregator)) { + bond_for_each_slave(bond, slave, iter) { + aggregator = &(SLAVE_AD_INFO(slave).aggregator); // keep a free aggregator for later use(if needed) if (!aggregator->lag_ports) { @@ -1515,19 +1462,23 @@ static int agg_device_up(const struct aggregator *agg) static void ad_agg_selection_logic(struct aggregator *agg) { struct aggregator *best, *active, *origin; + struct bonding *bond = agg->slave->bond; + struct list_head *iter; + struct slave *slave; struct port *port; origin = agg; active = __get_active_agg(agg); best = (active && agg_device_up(active)) ? active : NULL; - do { + bond_for_each_slave(bond, slave, iter) { + agg = &(SLAVE_AD_INFO(slave).aggregator); + agg->is_active = 0; if (agg->num_of_ports && agg_device_up(agg)) best = ad_agg_selection_test(best, agg); - - } while ((agg = __get_next_agg(agg))); + } if (best && __get_agg_selection_mode(best->lag_ports) == BOND_AD_STABLE) { @@ -1565,8 +1516,8 @@ static void ad_agg_selection_logic(struct aggregator *agg) best->lag_ports, best->slave, best->slave ? best->slave->dev->name : "NULL"); - for (agg = __get_first_agg(best->lag_ports); agg; - agg = __get_next_agg(agg)) { + bond_for_each_slave(bond, slave, iter) { + agg = &(SLAVE_AD_INFO(slave).aggregator); pr_debug("Agg=%d; P=%d; a k=%d; p k=%d; Ind=%d; Act=%d\n", agg->aggregator_identifier, agg->num_of_ports, @@ -1614,13 +1565,7 @@ static void ad_agg_selection_logic(struct aggregator *agg) } } - if (origin->slave) { - struct bonding *bond; - - bond = bond_get_bond_by_slave(origin->slave); - if (bond) - bond_3ad_set_carrier(bond); - } + bond_3ad_set_carrier(bond); } /** @@ -1969,6 +1914,9 @@ void bond_3ad_unbind_slave(struct slave *slave) struct port *port, *prev_port, *temp_port; struct aggregator *aggregator, *new_aggregator, *temp_aggregator; int select_new_active_agg = 0; + struct bonding *bond = slave->bond; + struct slave *slave_iter; + struct list_head *iter; // find the aggregator related to this slave aggregator = &(SLAVE_AD_INFO(slave).aggregator); @@ -1998,14 +1946,16 @@ void bond_3ad_unbind_slave(struct slave *slave) // reason to search for new aggregator, and that we will find one if ((aggregator->lag_ports != port) || (aggregator->lag_ports->next_port_in_aggregator)) { // find new aggregator for the related port(s) - new_aggregator = __get_first_agg(port); - for (; new_aggregator; new_aggregator = __get_next_agg(new_aggregator)) { + bond_for_each_slave(bond, slave_iter, iter) { + new_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator); // if the new aggregator is empty, or it is connected to our port only if (!new_aggregator->lag_ports || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator)) break; } + if (!slave_iter) + new_aggregator = NULL; // if new aggregator found, copy the aggregator's parameters // and connect the related lag_ports to the new aggregator if ((new_aggregator) && ((!new_aggregator->lag_ports) || ((new_aggregator->lag_ports == port) && !new_aggregator->lag_ports->next_port_in_aggregator))) { @@ -2056,15 +2006,17 @@ void bond_3ad_unbind_slave(struct slave *slave) pr_info("%s: Removing an active aggregator\n", slave->bond->dev->name); // select new active aggregator - ad_agg_selection_logic(__get_first_agg(port)); + temp_aggregator = __get_first_agg(port); + if (temp_aggregator) + ad_agg_selection_logic(temp_aggregator); } } } pr_debug("Unbinding port %d\n", port->actor_port_number); // find the aggregator that this port is connected to - temp_aggregator = __get_first_agg(port); - for (; temp_aggregator; temp_aggregator = __get_next_agg(temp_aggregator)) { + bond_for_each_slave(bond, slave_iter, iter) { + temp_aggregator = &(SLAVE_AD_INFO(slave_iter).aggregator); prev_port = NULL; // search the port in the aggregator's related ports for (temp_port = temp_aggregator->lag_ports; temp_port; @@ -2111,19 +2063,24 @@ void bond_3ad_state_machine_handler(struct work_struct *work) { struct bonding *bond = container_of(work, struct bonding, ad_work.work); - struct port *port; struct aggregator *aggregator; + struct list_head *iter; + struct slave *slave; + struct port *port; read_lock(&bond->lock); //check if there are any slaves - if (list_empty(&bond->slave_list)) + if (!bond_has_slaves(bond)) goto re_arm; // check if agg_select_timer timer after initialize is timed out if (BOND_AD_INFO(bond).agg_select_timer && !(--BOND_AD_INFO(bond).agg_select_timer)) { + slave = bond_first_slave(bond); + port = slave ? &(SLAVE_AD_INFO(slave).port) : NULL; + // select the active aggregator for the bond - if ((port = __get_first_port(bond))) { + if (port) { if (!port->slave) { pr_warning("%s: Warning: bond's first port is uninitialized\n", bond->dev->name); @@ -2137,7 +2094,8 @@ void bond_3ad_state_machine_handler(struct work_struct *work) } // for each port run the state machines - for (port = __get_first_port(bond); port; port = __get_next_port(port)) { + bond_for_each_slave(bond, slave, iter) { + port = &(SLAVE_AD_INFO(slave).port); if (!port->slave) { pr_warning("%s: Warning: Found an uninitialized port\n", bond->dev->name); @@ -2382,9 +2340,12 @@ int __bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info) { struct aggregator *aggregator = NULL; + struct list_head *iter; + struct slave *slave; struct port *port; - for (port = __get_first_port(bond); port; port = __get_next_port(port)) { + bond_for_each_slave_rcu(bond, slave, iter) { + port = &(SLAVE_AD_INFO(slave).port); if (port->aggregator && port->aggregator->is_active) { aggregator = port->aggregator; break; @@ -2408,25 +2369,25 @@ int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info) { int ret; - read_lock(&bond->lock); + rcu_read_lock(); ret = __bond_3ad_get_active_agg_info(bond, ad_info); - read_unlock(&bond->lock); + rcu_read_unlock(); return ret; } int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) { - struct slave *slave, *start_at; struct bonding *bond = netdev_priv(dev); - int slave_agg_no; - int slaves_in_agg; - int agg_id; - int i; + struct slave *slave, *first_ok_slave; + struct aggregator *agg; struct ad_info ad_info; + struct list_head *iter; + int slaves_in_agg; + int slave_agg_no; int res = 1; + int agg_id; - read_lock(&bond->lock); if (__bond_3ad_get_active_agg_info(bond, &ad_info)) { pr_debug("%s: Error: __bond_3ad_get_active_agg_info failed\n", dev->name); @@ -2437,20 +2398,28 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) agg_id = ad_info.aggregator_id; if (slaves_in_agg == 0) { - /*the aggregator is empty*/ pr_debug("%s: Error: active aggregator is empty\n", dev->name); goto out; } - slave_agg_no = bond->xmit_hash_policy(skb, slaves_in_agg); + slave_agg_no = bond_xmit_hash(bond, skb, slaves_in_agg); + first_ok_slave = NULL; - bond_for_each_slave(bond, slave) { - struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator; + bond_for_each_slave_rcu(bond, slave, iter) { + agg = SLAVE_AD_INFO(slave).port.aggregator; + if (!agg || agg->aggregator_identifier != agg_id) + continue; - if (agg && (agg->aggregator_identifier == agg_id)) { + if (slave_agg_no >= 0) { + if (!first_ok_slave && SLAVE_IS_OK(slave)) + first_ok_slave = slave; slave_agg_no--; - if (slave_agg_no < 0) - break; + continue; + } + + if (SLAVE_IS_OK(slave)) { + res = bond_dev_queue_xmit(bond, skb, slave->dev); + goto out; } } @@ -2460,23 +2429,12 @@ int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev) goto out; } - start_at = slave; - - bond_for_each_slave_from(bond, slave, i, start_at) { - int slave_agg_id = 0; - struct aggregator *agg = SLAVE_AD_INFO(slave).port.aggregator; - - if (agg) - slave_agg_id = agg->aggregator_identifier; - - if (SLAVE_IS_OK(slave) && agg && (slave_agg_id == agg_id)) { - res = bond_dev_queue_xmit(bond, skb, slave->dev); - break; - } - } + /* we couldn't find any suitable slave after the agg_no, so use the + * first suitable found, if found. */ + if (first_ok_slave) + res = bond_dev_queue_xmit(bond, skb, first_ok_slave->dev); out: - read_unlock(&bond->lock); if (res) { /* no suitable interface, frame not sent */ kfree_skb(skb); @@ -2515,11 +2473,12 @@ int bond_3ad_lacpdu_recv(const struct sk_buff *skb, struct bonding *bond, void bond_3ad_update_lacp_rate(struct bonding *bond) { struct port *port = NULL; + struct list_head *iter; struct slave *slave; int lacp_fast; lacp_fast = bond->params.lacp_fast; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { port = &(SLAVE_AD_INFO(slave).port); __get_state_machine_lock(port); if (lacp_fast) diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index f428ef57437279ec4bbf15e1c7e8e9b6a9da7a2c..02872405d35dc4a53d13473b31d1b517d22610d0 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -223,13 +223,14 @@ static long long compute_gap(struct slave *slave) static struct slave *tlb_get_least_loaded_slave(struct bonding *bond) { struct slave *slave, *least_loaded; + struct list_head *iter; long long max_gap; least_loaded = NULL; max_gap = LLONG_MIN; /* Find the slave with the largest gap */ - bond_for_each_slave(bond, slave) { + bond_for_each_slave_rcu(bond, slave, iter) { if (SLAVE_IS_OK(slave)) { long long gap = compute_gap(slave); @@ -382,30 +383,64 @@ out: static struct slave *rlb_next_rx_slave(struct bonding *bond) { struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); - struct slave *rx_slave, *slave, *start_at; - int i = 0; + struct slave *before = NULL, *rx_slave = NULL, *slave; + struct list_head *iter; + bool found = false; - if (bond_info->next_rx_slave) - start_at = bond_info->next_rx_slave; - else - start_at = bond_first_slave(bond); + bond_for_each_slave(bond, slave, iter) { + if (!SLAVE_IS_OK(slave)) + continue; + if (!found) { + if (!before || before->speed < slave->speed) + before = slave; + } else { + if (!rx_slave || rx_slave->speed < slave->speed) + rx_slave = slave; + } + if (slave == bond_info->rx_slave) + found = true; + } + /* we didn't find anything after the current or we have something + * better before and up to the current slave + */ + if (!rx_slave || (before && rx_slave->speed < before->speed)) + rx_slave = before; - rx_slave = NULL; + if (rx_slave) + bond_info->rx_slave = rx_slave; - bond_for_each_slave_from(bond, slave, i, start_at) { - if (SLAVE_IS_OK(slave)) { - if (!rx_slave) { - rx_slave = slave; - } else if (slave->speed > rx_slave->speed) { + return rx_slave; +} + +/* Caller must hold rcu_read_lock() for read */ +static struct slave *__rlb_next_rx_slave(struct bonding *bond) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct slave *before = NULL, *rx_slave = NULL, *slave; + struct list_head *iter; + bool found = false; + + bond_for_each_slave_rcu(bond, slave, iter) { + if (!SLAVE_IS_OK(slave)) + continue; + if (!found) { + if (!before || before->speed < slave->speed) + before = slave; + } else { + if (!rx_slave || rx_slave->speed < slave->speed) rx_slave = slave; - } } + if (slave == bond_info->rx_slave) + found = true; } + /* we didn't find anything after the current or we have something + * better before and up to the current slave + */ + if (!rx_slave || (before && rx_slave->speed < before->speed)) + rx_slave = before; - if (rx_slave) { - slave = bond_next_slave(bond, rx_slave); - bond_info->next_rx_slave = slave; - } + if (rx_slave) + bond_info->rx_slave = rx_slave; return rx_slave; } @@ -626,12 +661,14 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon { struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); struct arp_pkt *arp = arp_pkt(skb); - struct slave *assigned_slave; + struct slave *assigned_slave, *curr_active_slave; struct rlb_client_info *client_info; u32 hash_index = 0; _lock_rx_hashtbl(bond); + curr_active_slave = rcu_dereference(bond->curr_active_slave); + hash_index = _simple_hash((u8 *)&arp->ip_dst, sizeof(arp->ip_dst)); client_info = &(bond_info->rx_hashtbl[hash_index]); @@ -656,14 +693,14 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon * that the new client can be assigned to this entry. */ if (bond->curr_active_slave && - client_info->slave != bond->curr_active_slave) { - client_info->slave = bond->curr_active_slave; + client_info->slave != curr_active_slave) { + client_info->slave = curr_active_slave; rlb_update_client(client_info); } } } /* assign a new slave */ - assigned_slave = rlb_next_rx_slave(bond); + assigned_slave = __rlb_next_rx_slave(bond); if (assigned_slave) { if (!(client_info->assigned && @@ -726,7 +763,7 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) /* Don't modify or load balance ARPs that do not originate locally * (e.g.,arrive via a bridge). */ - if (!bond_slave_has_mac(bond, arp->mac_src)) + if (!bond_slave_has_mac_rcu(bond, arp->mac_src)) return NULL; if (arp->op_code == htons(ARPOP_REPLY)) { @@ -1019,7 +1056,7 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]) /* loop through vlans and send one packet for each */ rcu_read_lock(); - netdev_for_each_upper_dev_rcu(bond->dev, upper, iter) { + netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) { if (upper->priv_flags & IFF_802_1Q_VLAN) alb_send_lp_vid(slave, mac_addr, vlan_dev_vlan_id(upper)); @@ -1172,10 +1209,11 @@ static void alb_change_hw_addr_on_detach(struct bonding *bond, struct slave *sla */ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slave *slave) { - struct slave *tmp_slave1, *free_mac_slave = NULL; struct slave *has_bond_addr = bond->curr_active_slave; + struct slave *tmp_slave1, *free_mac_slave = NULL; + struct list_head *iter; - if (list_empty(&bond->slave_list)) { + if (!bond_has_slaves(bond)) { /* this is the first slave */ return 0; } @@ -1196,7 +1234,7 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav /* The slave's address is equal to the address of the bond. * Search for a spare address in the bond for this slave. */ - bond_for_each_slave(bond, tmp_slave1) { + bond_for_each_slave(bond, tmp_slave1, iter) { if (!bond_slave_has_mac(bond, tmp_slave1->perm_hwaddr)) { /* no slave has tmp_slave1's perm addr * as its curr addr @@ -1246,15 +1284,16 @@ static int alb_handle_addr_collision_on_attach(struct bonding *bond, struct slav */ static int alb_set_mac_address(struct bonding *bond, void *addr) { - char tmp_addr[ETH_ALEN]; - struct slave *slave; + struct slave *slave, *rollback_slave; + struct list_head *iter; struct sockaddr sa; + char tmp_addr[ETH_ALEN]; int res; if (bond->alb_info.rlb_enabled) return 0; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { /* save net_device's current hw address */ memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN); @@ -1274,10 +1313,12 @@ unwind: sa.sa_family = bond->dev->type; /* unwind from head to the slave that failed */ - bond_for_each_slave_continue_reverse(bond, slave) { - memcpy(tmp_addr, slave->dev->dev_addr, ETH_ALEN); - dev_set_mac_address(slave->dev, &sa); - memcpy(slave->dev->dev_addr, tmp_addr, ETH_ALEN); + bond_for_each_slave(bond, rollback_slave, iter) { + if (rollback_slave == slave) + break; + memcpy(tmp_addr, rollback_slave->dev->dev_addr, ETH_ALEN); + dev_set_mac_address(rollback_slave->dev, &sa); + memcpy(rollback_slave->dev->dev_addr, tmp_addr, ETH_ALEN); } return res; @@ -1337,11 +1378,6 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) skb_reset_mac_header(skb); eth_data = eth_hdr(skb); - /* make sure that the curr_active_slave do not change during tx - */ - read_lock(&bond->lock); - read_lock(&bond->curr_slave_lock); - switch (ntohs(skb->protocol)) { case ETH_P_IP: { const struct iphdr *iph = ip_hdr(skb); @@ -1423,12 +1459,12 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) if (!tx_slave) { /* unbalanced or unassigned, send through primary */ - tx_slave = bond->curr_active_slave; + tx_slave = rcu_dereference(bond->curr_active_slave); bond_info->unbalanced_load += skb->len; } if (tx_slave && SLAVE_IS_OK(tx_slave)) { - if (tx_slave != bond->curr_active_slave) { + if (tx_slave != rcu_dereference(bond->curr_active_slave)) { memcpy(eth_data->h_source, tx_slave->dev->dev_addr, ETH_ALEN); @@ -1443,8 +1479,6 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) } } - read_unlock(&bond->curr_slave_lock); - read_unlock(&bond->lock); if (res) { /* no suitable interface, frame not sent */ kfree_skb(skb); @@ -1458,11 +1492,12 @@ void bond_alb_monitor(struct work_struct *work) struct bonding *bond = container_of(work, struct bonding, alb_work.work); struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct list_head *iter; struct slave *slave; read_lock(&bond->lock); - if (list_empty(&bond->slave_list)) { + if (!bond_has_slaves(bond)) { bond_info->tx_rebalance_counter = 0; bond_info->lp_counter = 0; goto re_arm; @@ -1480,7 +1515,7 @@ void bond_alb_monitor(struct work_struct *work) */ read_lock(&bond->curr_slave_lock); - bond_for_each_slave(bond, slave) + bond_for_each_slave(bond, slave, iter) alb_send_learning_packets(slave, slave->dev->dev_addr); read_unlock(&bond->curr_slave_lock); @@ -1493,7 +1528,7 @@ void bond_alb_monitor(struct work_struct *work) read_lock(&bond->curr_slave_lock); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { tlb_clear_slave(bond, slave, 1); if (slave == bond->curr_active_slave) { SLAVE_TLB_INFO(slave).load = @@ -1599,13 +1634,13 @@ int bond_alb_init_slave(struct bonding *bond, struct slave *slave) */ void bond_alb_deinit_slave(struct bonding *bond, struct slave *slave) { - if (!list_empty(&bond->slave_list)) + if (bond_has_slaves(bond)) alb_change_hw_addr_on_detach(bond, slave); tlb_clear_slave(bond, slave, 0); if (bond->alb_info.rlb_enabled) { - bond->alb_info.next_rx_slave = NULL; + bond->alb_info.rx_slave = NULL; rlb_clear_slave(bond, slave); } } @@ -1669,7 +1704,7 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave swap_slave = bond->curr_active_slave; rcu_assign_pointer(bond->curr_active_slave, new_slave); - if (!new_slave || list_empty(&bond->slave_list)) + if (!new_slave || !bond_has_slaves(bond)) return; /* set the new curr_active_slave to the bonds mac address @@ -1692,6 +1727,23 @@ void bond_alb_handle_active_change(struct bonding *bond, struct slave *new_slave ASSERT_RTNL(); + /* in TLB mode, the slave might flip down/up with the old dev_addr, + * and thus filter bond->dev_addr's packets, so force bond's mac + */ + if (bond->params.mode == BOND_MODE_TLB) { + struct sockaddr sa; + u8 tmp_addr[ETH_ALEN]; + + memcpy(tmp_addr, new_slave->dev->dev_addr, ETH_ALEN); + + memcpy(sa.sa_data, bond->dev->dev_addr, bond->dev->addr_len); + sa.sa_family = bond->dev->type; + /* we don't care if it can't change its mac, best effort */ + dev_set_mac_address(new_slave->dev, &sa); + + memcpy(new_slave->dev->dev_addr, tmp_addr, ETH_ALEN); + } + /* curr_active_slave must be set before calling alb_swap_mac_addr */ if (swap_slave) { /* swap mac address */ diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h index c5eff5dafdfeab12ee4849fe75d9117d70c4859f..4226044efd083645db9229c0f88e507eae6d410b 100644 --- a/drivers/net/bonding/bond_alb.h +++ b/drivers/net/bonding/bond_alb.h @@ -154,9 +154,7 @@ struct alb_bond_info { u8 rx_ntt; /* flag - need to transmit * to all rx clients */ - struct slave *next_rx_slave;/* next slave to be assigned - * to a new rx client for - */ + struct slave *rx_slave;/* last slave to xmit from */ u8 primary_is_promisc; /* boolean */ u32 rlb_promisc_timeout_counter;/* counts primary * promiscuity time diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e883bfe2e727aa1bf6fd2fb2cea2375578c8c06d..4dd5ee2a34cc68c4222f4c4115beedaee8b187d2 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -78,6 +78,8 @@ #include #include #include +#include +#include #include "bonding.h" #include "bond_3ad.h" #include "bond_alb.h" @@ -110,6 +112,7 @@ static char *fail_over_mac; static int all_slaves_active; static struct bond_params bonding_defaults; static int resend_igmp = BOND_DEFAULT_RESEND_IGMP; +static int packets_per_slave = 1; module_param(max_bonds, int, 0); MODULE_PARM_DESC(max_bonds, "Max number of bonded devices"); @@ -159,7 +162,8 @@ MODULE_PARM_DESC(min_links, "Minimum number of available links before turning on module_param(xmit_hash_policy, charp, 0); MODULE_PARM_DESC(xmit_hash_policy, "balance-xor and 802.3ad hashing method; " "0 for layer 2 (default), 1 for layer 3+4, " - "2 for layer 2+3"); + "2 for layer 2+3, 3 for encap layer 2+3, " + "4 for encap layer 3+4"); module_param(arp_interval, int, 0); MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds"); module_param_array(arp_ip_target, charp, NULL, 0); @@ -181,6 +185,10 @@ MODULE_PARM_DESC(all_slaves_active, "Keep all frames received on an interface" module_param(resend_igmp, int, 0); MODULE_PARM_DESC(resend_igmp, "Number of IGMP membership reports to send on " "link failure"); +module_param(packets_per_slave, int, 0); +MODULE_PARM_DESC(packets_per_slave, "Packets to send per slave in balance-rr " + "mode; 0 for a random slave, 1 packet per " + "slave (default), >1 packets per slave."); /*----------------------------- Global variables ----------------------------*/ @@ -217,6 +225,8 @@ const struct bond_parm_tbl xmit_hashtype_tbl[] = { { "layer2", BOND_XMIT_POLICY_LAYER2}, { "layer3+4", BOND_XMIT_POLICY_LAYER34}, { "layer2+3", BOND_XMIT_POLICY_LAYER23}, +{ "encap2+3", BOND_XMIT_POLICY_ENCAP23}, +{ "encap3+4", BOND_XMIT_POLICY_ENCAP34}, { NULL, -1}, }; @@ -332,10 +342,11 @@ static int bond_vlan_rx_add_vid(struct net_device *bond_dev, __be16 proto, u16 vid) { struct bonding *bond = netdev_priv(bond_dev); - struct slave *slave; + struct slave *slave, *rollback_slave; + struct list_head *iter; int res; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { res = vlan_vid_add(slave->dev, proto, vid); if (res) goto unwind; @@ -344,9 +355,13 @@ static int bond_vlan_rx_add_vid(struct net_device *bond_dev, return 0; unwind: - /* unwind from the slave that failed */ - bond_for_each_slave_continue_reverse(bond, slave) - vlan_vid_del(slave->dev, proto, vid); + /* unwind to the slave that failed */ + bond_for_each_slave(bond, rollback_slave, iter) { + if (rollback_slave == slave) + break; + + vlan_vid_del(rollback_slave->dev, proto, vid); + } return res; } @@ -360,9 +375,10 @@ static int bond_vlan_rx_kill_vid(struct net_device *bond_dev, __be16 proto, u16 vid) { struct bonding *bond = netdev_priv(bond_dev); + struct list_head *iter; struct slave *slave; - bond_for_each_slave(bond, slave) + bond_for_each_slave(bond, slave, iter) vlan_vid_del(slave->dev, proto, vid); if (bond_is_lb(bond)) @@ -382,15 +398,16 @@ static int bond_vlan_rx_kill_vid(struct net_device *bond_dev, */ static int bond_set_carrier(struct bonding *bond) { + struct list_head *iter; struct slave *slave; - if (list_empty(&bond->slave_list)) + if (!bond_has_slaves(bond)) goto down; if (bond->params.mode == BOND_MODE_8023AD) return bond_3ad_set_carrier(bond); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { if (slave->link == BOND_LINK_UP) { if (!netif_carrier_ok(bond->dev)) { netif_carrier_on(bond->dev); @@ -522,7 +539,9 @@ static int bond_check_dev_link(struct bonding *bond, */ static int bond_set_promiscuity(struct bonding *bond, int inc) { + struct list_head *iter; int err = 0; + if (USES_PRIMARY(bond->params.mode)) { /* write lock already acquired */ if (bond->curr_active_slave) { @@ -532,7 +551,7 @@ static int bond_set_promiscuity(struct bonding *bond, int inc) } else { struct slave *slave; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { err = dev_set_promiscuity(slave->dev, inc); if (err) return err; @@ -546,7 +565,9 @@ static int bond_set_promiscuity(struct bonding *bond, int inc) */ static int bond_set_allmulti(struct bonding *bond, int inc) { + struct list_head *iter; int err = 0; + if (USES_PRIMARY(bond->params.mode)) { /* write lock already acquired */ if (bond->curr_active_slave) { @@ -556,7 +577,7 @@ static int bond_set_allmulti(struct bonding *bond, int inc) } else { struct slave *slave; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { err = dev_set_allmulti(slave->dev, inc); if (err) return err; @@ -774,43 +795,24 @@ static bool bond_should_change_active(struct bonding *bond) /** * find_best_interface - select the best available slave to be the active one * @bond: our bonding struct - * - * Warning: Caller must hold curr_slave_lock for writing. */ static struct slave *bond_find_best_slave(struct bonding *bond) { - struct slave *new_active, *old_active; - struct slave *bestslave = NULL; + struct slave *slave, *bestslave = NULL; + struct list_head *iter; int mintime = bond->params.updelay; - int i; - new_active = bond->curr_active_slave; - - if (!new_active) { /* there were no active slaves left */ - new_active = bond_first_slave(bond); - if (!new_active) - return NULL; /* still no slave, return NULL */ - } - - if ((bond->primary_slave) && - bond->primary_slave->link == BOND_LINK_UP && - bond_should_change_active(bond)) { - new_active = bond->primary_slave; - } - - /* remember where to stop iterating over the slaves */ - old_active = new_active; - - bond_for_each_slave_from(bond, new_active, i, old_active) { - if (new_active->link == BOND_LINK_UP) { - return new_active; - } else if (new_active->link == BOND_LINK_BACK && - IS_UP(new_active->dev)) { - /* link up, but waiting for stabilization */ - if (new_active->delay < mintime) { - mintime = new_active->delay; - bestslave = new_active; - } + if (bond->primary_slave && bond->primary_slave->link == BOND_LINK_UP && + bond_should_change_active(bond)) + return bond->primary_slave; + + bond_for_each_slave(bond, slave, iter) { + if (slave->link == BOND_LINK_UP) + return slave; + if (slave->link == BOND_LINK_BACK && IS_UP(slave->dev) && + slave->delay < mintime) { + mintime = slave->delay; + bestslave = slave; } } @@ -971,35 +973,6 @@ void bond_select_active_slave(struct bonding *bond) } } -/*--------------------------- slave list handling ---------------------------*/ - -/* - * This function attaches the slave to the end of list. - * - * bond->lock held for writing by caller. - */ -static void bond_attach_slave(struct bonding *bond, struct slave *new_slave) -{ - list_add_tail_rcu(&new_slave->list, &bond->slave_list); - bond->slave_cnt++; -} - -/* - * This function detaches the slave from the list. - * WARNING: no check is made to verify if the slave effectively - * belongs to . - * Nothing is freed on return, structures are just unchained. - * If any slave pointer in bond was pointing to , - * it should be changed by the calling function. - * - * bond->lock held for writing by caller. - */ -static void bond_detach_slave(struct bonding *bond, struct slave *slave) -{ - list_del_rcu(&slave->list); - bond->slave_cnt--; -} - #ifdef CONFIG_NET_POLL_CONTROLLER static inline int slave_enable_netpoll(struct slave *slave) { @@ -1046,9 +1019,10 @@ static void bond_poll_controller(struct net_device *bond_dev) static void bond_netpoll_cleanup(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); + struct list_head *iter; struct slave *slave; - bond_for_each_slave(bond, slave) + bond_for_each_slave(bond, slave, iter) if (IS_UP(slave->dev)) slave_disable_netpoll(slave); } @@ -1056,10 +1030,11 @@ static void bond_netpoll_cleanup(struct net_device *bond_dev) static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, gfp_t gfp) { struct bonding *bond = netdev_priv(dev); + struct list_head *iter; struct slave *slave; int err = 0; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { err = slave_enable_netpoll(slave); if (err) { bond_netpoll_cleanup(dev); @@ -1087,10 +1062,11 @@ static netdev_features_t bond_fix_features(struct net_device *dev, netdev_features_t features) { struct bonding *bond = netdev_priv(dev); + struct list_head *iter; netdev_features_t mask; struct slave *slave; - if (list_empty(&bond->slave_list)) { + if (!bond_has_slaves(bond)) { /* Disable adding VLANs to empty bond. But why? --mq */ features |= NETIF_F_VLAN_CHALLENGED; return features; @@ -1100,7 +1076,7 @@ static netdev_features_t bond_fix_features(struct net_device *dev, features &= ~NETIF_F_ONE_FOR_ALL; features |= NETIF_F_ALL_FOR_ALL; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { features = netdev_increment_features(features, slave->dev->features, mask); @@ -1118,16 +1094,17 @@ static void bond_compute_features(struct bonding *bond) { unsigned int flags, dst_release_flag = IFF_XMIT_DST_RELEASE; netdev_features_t vlan_features = BOND_VLAN_FEATURES; + struct net_device *bond_dev = bond->dev; + struct list_head *iter; + struct slave *slave; unsigned short max_hard_header_len = ETH_HLEN; unsigned int gso_max_size = GSO_MAX_SIZE; - struct net_device *bond_dev = bond->dev; u16 gso_max_segs = GSO_MAX_SEGS; - struct slave *slave; - if (list_empty(&bond->slave_list)) + if (!bond_has_slaves(bond)) goto done; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { vlan_features = netdev_increment_features(vlan_features, slave->dev->vlan_features, BOND_VLAN_FEATURES); @@ -1233,15 +1210,16 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) } static int bond_master_upper_dev_link(struct net_device *bond_dev, - struct net_device *slave_dev) + struct net_device *slave_dev, + struct slave *slave) { int err; - err = netdev_master_upper_dev_link(slave_dev, bond_dev); + err = netdev_master_upper_dev_link_private(slave_dev, bond_dev, slave); if (err) return err; slave_dev->flags |= IFF_SLAVE; - rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE); + rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL); return 0; } @@ -1250,7 +1228,7 @@ static void bond_upper_dev_unlink(struct net_device *bond_dev, { netdev_upper_dev_unlink(slave_dev, bond_dev); slave_dev->flags &= ~IFF_SLAVE; - rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE); + rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE, GFP_KERNEL); } /* enslave device to bond device */ @@ -1258,7 +1236,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) { struct bonding *bond = netdev_priv(bond_dev); const struct net_device_ops *slave_ops = slave_dev->netdev_ops; - struct slave *new_slave = NULL; + struct slave *new_slave = NULL, *prev_slave; struct sockaddr addr; int link_reporting; int res = 0, i; @@ -1313,7 +1291,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) * bond ether type mutual exclusion - don't allow slaves of dissimilar * ether type (eg ARPHRD_ETHER and ARPHRD_INFINIBAND) share the same bond */ - if (list_empty(&bond->slave_list)) { + if (!bond_has_slaves(bond)) { if (bond_dev->type != slave_dev->type) { pr_debug("%s: change device type from %d to %d\n", bond_dev->name, @@ -1352,7 +1330,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } if (slave_ops->ndo_set_mac_address == NULL) { - if (list_empty(&bond->slave_list)) { + if (!bond_has_slaves(bond)) { pr_warning("%s: Warning: The first slave device specified does not support setting the MAC address. Setting fail_over_mac to active.", bond_dev->name); bond->params.fail_over_mac = BOND_FOM_ACTIVE; @@ -1368,7 +1346,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) /* If this is the first slave, then we need to set the master's hardware * address to be the same as the slave's. */ - if (list_empty(&bond->slave_list) && + if (!bond_has_slaves(bond) && bond->dev->addr_assign_type == NET_ADDR_RANDOM) bond_set_dev_addr(bond->dev, slave_dev); @@ -1377,7 +1355,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) res = -ENOMEM; goto err_undo_flags; } - INIT_LIST_HEAD(&new_slave->list); /* * Set the new_slave's queue_id to be zero. Queue ID mapping * is set via sysfs or module option if desired. @@ -1413,17 +1390,11 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } } - res = bond_master_upper_dev_link(bond_dev, slave_dev); - if (res) { - pr_debug("Error %d calling bond_master_upper_dev_link\n", res); - goto err_restore_mac; - } - /* open the slave since the application closed it */ res = dev_open(slave_dev); if (res) { pr_debug("Opening slave %s failed\n", slave_dev->name); - goto err_unset_master; + goto err_restore_mac; } new_slave->bond = bond; @@ -1479,21 +1450,13 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_close; } - write_lock_bh(&bond->lock); - - bond_attach_slave(bond, new_slave); + prev_slave = bond_last_slave(bond); new_slave->delay = 0; new_slave->link_failure_count = 0; - write_unlock_bh(&bond->lock); - - bond_compute_features(bond); - bond_update_speed_duplex(new_slave); - read_lock(&bond->lock); - new_slave->last_arp_rx = jiffies - (msecs_to_jiffies(bond->params.arp_interval) + 1); for (i = 0; i < BOND_MAX_ARP_TARGETS; i++) @@ -1554,12 +1517,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } } - write_lock_bh(&bond->curr_slave_lock); - switch (bond->params.mode) { case BOND_MODE_ACTIVEBACKUP: bond_set_slave_inactive_flags(new_slave); - bond_select_active_slave(bond); break; case BOND_MODE_8023AD: /* in 802.3ad mode, the internal mechanism @@ -1568,16 +1528,13 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) */ bond_set_slave_inactive_flags(new_slave); /* if this is the first slave */ - if (bond_first_slave(bond) == new_slave) { + if (!prev_slave) { SLAVE_AD_INFO(new_slave).id = 1; /* Initialize AD with the number of times that the AD timer is called in 1 second * can be called only after the mac address of the bond is set */ bond_3ad_initialize(bond, 1000/AD_TIMER_INTERVAL); } else { - struct slave *prev_slave; - - prev_slave = bond_prev_slave(bond, new_slave); SLAVE_AD_INFO(new_slave).id = SLAVE_AD_INFO(prev_slave).id + 1; } @@ -1588,7 +1545,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) case BOND_MODE_ALB: bond_set_active_slave(new_slave); bond_set_slave_inactive_flags(new_slave); - bond_select_active_slave(bond); break; default: pr_debug("This slave is always active in trunk mode\n"); @@ -1606,10 +1562,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) break; } /* switch(bond_mode) */ - write_unlock_bh(&bond->curr_slave_lock); - - bond_set_carrier(bond); - #ifdef CONFIG_NET_POLL_CONTROLLER slave_dev->npinfo = bond->dev->npinfo; if (slave_dev->npinfo) { @@ -1624,17 +1576,29 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } #endif - read_unlock(&bond->lock); - - res = bond_create_slave_symlinks(bond_dev, slave_dev); - if (res) - goto err_detach; - res = netdev_rx_handler_register(slave_dev, bond_handle_frame, new_slave); if (res) { pr_debug("Error %d calling netdev_rx_handler_register\n", res); - goto err_dest_symlinks; + goto err_detach; + } + + res = bond_master_upper_dev_link(bond_dev, slave_dev, new_slave); + if (res) { + pr_debug("Error %d calling bond_master_upper_dev_link\n", res); + goto err_unregister; + } + + bond->slave_cnt++; + bond_compute_features(bond); + bond_set_carrier(bond); + + if (USES_PRIMARY(bond->params.mode)) { + read_lock(&bond->lock); + write_lock_bh(&bond->curr_slave_lock); + bond_select_active_slave(bond); + write_unlock_bh(&bond->curr_slave_lock); + read_unlock(&bond->lock); } pr_info("%s: enslaving %s as a%s interface with a%s link.\n", @@ -1646,8 +1610,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) return 0; /* Undo stages on error */ -err_dest_symlinks: - bond_destroy_slave_symlinks(bond_dev, slave_dev); +err_unregister: + netdev_rx_handler_unregister(slave_dev); err_detach: if (!USES_PRIMARY(bond->params.mode)) @@ -1655,7 +1619,6 @@ err_detach: vlan_vids_del_by_dev(slave_dev, bond_dev); write_lock_bh(&bond->lock); - bond_detach_slave(bond, new_slave); if (bond->primary_slave == new_slave) bond->primary_slave = NULL; if (bond->curr_active_slave == new_slave) { @@ -1675,9 +1638,6 @@ err_close: slave_dev->priv_flags &= ~IFF_BONDING; dev_close(slave_dev); -err_unset_master: - bond_upper_dev_unlink(bond_dev, slave_dev); - err_restore_mac: if (!bond->params.fail_over_mac) { /* XXX TODO - fom follow mode needs to change master's @@ -1696,9 +1656,8 @@ err_free: kfree(new_slave); err_undo_flags: - bond_compute_features(bond); /* Enslave of first slave has failed and we need to fix master's mac */ - if (list_empty(&bond->slave_list) && + if (!bond_has_slaves(bond) && ether_addr_equal(bond_dev->dev_addr, slave_dev->dev_addr)) eth_hw_addr_random(bond_dev); @@ -1749,6 +1708,11 @@ static int __bond_release_one(struct net_device *bond_dev, } write_unlock_bh(&bond->lock); + + /* release the slave from its bond */ + bond->slave_cnt--; + + bond_upper_dev_unlink(bond_dev, slave_dev); /* unregister rx_handler early so bond_handle_frame wouldn't be called * for this slave anymore. */ @@ -1772,12 +1736,9 @@ static int __bond_release_one(struct net_device *bond_dev, bond->current_arp_slave = NULL; - /* release the slave from its bond */ - bond_detach_slave(bond, slave); - if (!all && !bond->params.fail_over_mac) { if (ether_addr_equal(bond_dev->dev_addr, slave->perm_hwaddr) && - !list_empty(&bond->slave_list)) + bond_has_slaves(bond)) pr_warn("%s: Warning: the permanent HWaddr of %s - %pM - is still in use by %s. Set the HWaddr of %s to a different address to avoid conflicts.\n", bond_dev->name, slave_dev->name, slave->perm_hwaddr, @@ -1820,7 +1781,7 @@ static int __bond_release_one(struct net_device *bond_dev, write_lock_bh(&bond->lock); } - if (list_empty(&bond->slave_list)) { + if (!bond_has_slaves(bond)) { bond_set_carrier(bond); eth_hw_addr_random(bond_dev); @@ -1836,7 +1797,7 @@ static int __bond_release_one(struct net_device *bond_dev, unblock_netpoll_tx(); synchronize_rcu(); - if (list_empty(&bond->slave_list)) { + if (!bond_has_slaves(bond)) { call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev); call_netdevice_notifiers(NETDEV_RELEASE, bond->dev); } @@ -1848,8 +1809,6 @@ static int __bond_release_one(struct net_device *bond_dev, bond_dev->name, slave_dev->name, bond_dev->name); /* must do this from outside any spinlocks */ - bond_destroy_slave_symlinks(bond_dev, slave_dev); - vlan_vids_del_by_dev(slave_dev, bond_dev); /* If the mode USES_PRIMARY, then this cases was handled above by @@ -1873,8 +1832,6 @@ static int __bond_release_one(struct net_device *bond_dev, bond_hw_addr_flush(bond_dev, slave_dev); } - bond_upper_dev_unlink(bond_dev, slave_dev); - slave_disable_netpoll(slave); /* close slave before restoring its mac address */ @@ -1913,7 +1870,7 @@ static int bond_release_and_destroy(struct net_device *bond_dev, int ret; ret = bond_release(bond_dev, slave_dev); - if (ret == 0 && list_empty(&bond->slave_list)) { + if (ret == 0 && !bond_has_slaves(bond)) { bond_dev->priv_flags |= IFF_DISABLE_NETPOLL; pr_info("%s: destroying bond %s.\n", bond_dev->name, bond_dev->name); @@ -1922,61 +1879,6 @@ static int bond_release_and_destroy(struct net_device *bond_dev, return ret; } -/* - * This function changes the active slave to slave . - * It returns -EINVAL in the following cases. - * - is not found in the list. - * - There is not active slave now. - * - is already active. - * - The link state of is not BOND_LINK_UP. - * - is not running. - * In these cases, this function does nothing. - * In the other cases, current_slave pointer is changed and 0 is returned. - */ -static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_device *slave_dev) -{ - struct bonding *bond = netdev_priv(bond_dev); - struct slave *old_active = NULL; - struct slave *new_active = NULL; - int res = 0; - - if (!USES_PRIMARY(bond->params.mode)) - return -EINVAL; - - /* Verify that bond_dev is indeed the master of slave_dev */ - if (!(slave_dev->flags & IFF_SLAVE) || - !netdev_has_upper_dev(slave_dev, bond_dev)) - return -EINVAL; - - read_lock(&bond->lock); - - old_active = bond->curr_active_slave; - new_active = bond_get_slave_by_dev(bond, slave_dev); - /* - * Changing to the current active: do nothing; return success. - */ - if (new_active && new_active == old_active) { - read_unlock(&bond->lock); - return 0; - } - - if (new_active && - old_active && - new_active->link == BOND_LINK_UP && - IS_UP(new_active->dev)) { - block_netpoll_tx(); - write_lock_bh(&bond->curr_slave_lock); - bond_change_active_slave(bond, new_active); - write_unlock_bh(&bond->curr_slave_lock); - unblock_netpoll_tx(); - } else - res = -EINVAL; - - read_unlock(&bond->lock); - - return res; -} - static int bond_info_query(struct net_device *bond_dev, struct ifbond *info) { struct bonding *bond = netdev_priv(bond_dev); @@ -1994,11 +1896,12 @@ static int bond_info_query(struct net_device *bond_dev, struct ifbond *info) static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *info) { struct bonding *bond = netdev_priv(bond_dev); + struct list_head *iter; int i = 0, res = -ENODEV; struct slave *slave; read_lock(&bond->lock); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { if (i++ == (int)info->slave_id) { res = 0; strcpy(info->slave_name, slave->dev->name); @@ -2019,12 +1922,13 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in static int bond_miimon_inspect(struct bonding *bond) { int link_state, commit = 0; + struct list_head *iter; struct slave *slave; bool ignore_updelay; ignore_updelay = !bond->curr_active_slave ? true : false; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { slave->new_link = BOND_LINK_NOCHANGE; link_state = bond_check_dev_link(bond, slave->dev, 0); @@ -2118,9 +2022,10 @@ static int bond_miimon_inspect(struct bonding *bond) static void bond_miimon_commit(struct bonding *bond) { + struct list_head *iter; struct slave *slave; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { switch (slave->new_link) { case BOND_LINK_NOCHANGE: continue; @@ -2225,7 +2130,7 @@ void bond_mii_monitor(struct work_struct *work) delay = msecs_to_jiffies(bond->params.miimon); - if (list_empty(&bond->slave_list)) + if (!bond_has_slaves(bond)) goto re_arm; should_notify_peers = bond_should_notify_peers(bond); @@ -2274,7 +2179,7 @@ static bool bond_has_this_ip(struct bonding *bond, __be32 ip) return true; rcu_read_lock(); - netdev_for_each_upper_dev_rcu(bond->dev, upper, iter) { + netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) { if (ip == bond_confirm_addr(upper, 0, ip)) { ret = true; break; @@ -2349,10 +2254,12 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) * * TODO: QinQ? */ - netdev_for_each_upper_dev_rcu(bond->dev, vlan_upper, vlan_iter) { + netdev_for_each_all_upper_dev_rcu(bond->dev, vlan_upper, + vlan_iter) { if (!is_vlan_dev(vlan_upper)) continue; - netdev_for_each_upper_dev_rcu(vlan_upper, upper, iter) { + netdev_for_each_all_upper_dev_rcu(vlan_upper, upper, + iter) { if (upper == rt->dst.dev) { vlan_id = vlan_dev_vlan_id(vlan_upper); rcu_read_unlock(); @@ -2365,7 +2272,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) * our upper vlans, then just search for any dev that * matches, and in case it's a vlan - save the id */ - netdev_for_each_upper_dev_rcu(bond->dev, upper, iter) { + netdev_for_each_all_upper_dev_rcu(bond->dev, upper, iter) { if (upper == rt->dst.dev) { /* if it's a vlan - get its VID */ if (is_vlan_dev(upper)) @@ -2512,11 +2419,12 @@ void bond_loadbalance_arp_mon(struct work_struct *work) struct bonding *bond = container_of(work, struct bonding, arp_work.work); struct slave *slave, *oldcurrent; + struct list_head *iter; int do_failover = 0; read_lock(&bond->lock); - if (list_empty(&bond->slave_list)) + if (!bond_has_slaves(bond)) goto re_arm; oldcurrent = bond->curr_active_slave; @@ -2528,7 +2436,7 @@ void bond_loadbalance_arp_mon(struct work_struct *work) * TODO: what about up/down delay in arp mode? it wasn't here before * so it can wait */ - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { unsigned long trans_start = dev_trans_start(slave->dev); if (slave->link != BOND_LINK_UP) { @@ -2619,10 +2527,11 @@ re_arm: static int bond_ab_arp_inspect(struct bonding *bond) { unsigned long trans_start, last_rx; + struct list_head *iter; struct slave *slave; int commit = 0; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { slave->new_link = BOND_LINK_NOCHANGE; last_rx = slave_last_rx(bond, slave); @@ -2689,9 +2598,10 @@ static int bond_ab_arp_inspect(struct bonding *bond) static void bond_ab_arp_commit(struct bonding *bond) { unsigned long trans_start; + struct list_head *iter; struct slave *slave; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { switch (slave->new_link) { case BOND_LINK_NOCHANGE: continue; @@ -2762,8 +2672,9 @@ do_failover: */ static void bond_ab_arp_probe(struct bonding *bond) { - struct slave *slave, *next_slave; - int i; + struct slave *slave, *before = NULL, *new_slave = NULL; + struct list_head *iter; + bool found = false; read_lock(&bond->curr_slave_lock); @@ -2793,18 +2704,12 @@ static void bond_ab_arp_probe(struct bonding *bond) bond_set_slave_inactive_flags(bond->current_arp_slave); - /* search for next candidate */ - next_slave = bond_next_slave(bond, bond->current_arp_slave); - bond_for_each_slave_from(bond, slave, i, next_slave) { - if (IS_UP(slave->dev)) { - slave->link = BOND_LINK_BACK; - bond_set_slave_active_flags(slave); - bond_arp_send_all(bond, slave); - slave->jiffies = jiffies; - bond->current_arp_slave = slave; - break; - } + bond_for_each_slave(bond, slave, iter) { + if (!found && !before && IS_UP(slave->dev)) + before = slave; + if (found && !new_slave && IS_UP(slave->dev)) + new_slave = slave; /* if the link state is up at this point, we * mark it down - this can happen if we have * simultaneous link failures and @@ -2812,7 +2717,7 @@ static void bond_ab_arp_probe(struct bonding *bond) * one the current slave so it is still marked * up when it is actually down */ - if (slave->link == BOND_LINK_UP) { + if (!IS_UP(slave->dev) && slave->link == BOND_LINK_UP) { slave->link = BOND_LINK_DOWN; if (slave->link_failure_count < UINT_MAX) slave->link_failure_count++; @@ -2822,7 +2727,22 @@ static void bond_ab_arp_probe(struct bonding *bond) pr_info("%s: backup interface %s is now down.\n", bond->dev->name, slave->dev->name); } + if (slave == bond->current_arp_slave) + found = true; } + + if (!new_slave && before) + new_slave = before; + + if (!new_slave) + return; + + new_slave->link = BOND_LINK_BACK; + bond_set_slave_active_flags(new_slave); + bond_arp_send_all(bond, new_slave); + new_slave->jiffies = jiffies; + bond->current_arp_slave = new_slave; + } void bond_activebackup_arp_mon(struct work_struct *work) @@ -2836,7 +2756,7 @@ void bond_activebackup_arp_mon(struct work_struct *work) delta_in_ticks = msecs_to_jiffies(bond->params.arp_interval); - if (list_empty(&bond->slave_list)) + if (!bond_has_slaves(bond)) goto re_arm; should_notify_peers = bond_should_notify_peers(bond); @@ -3033,99 +2953,85 @@ static struct notifier_block bond_netdev_notifier = { /*---------------------------- Hashing Policies -----------------------------*/ -/* - * Hash for the output device based upon layer 2 data - */ -static int bond_xmit_hash_policy_l2(struct sk_buff *skb, int count) +/* L2 hash helper */ +static inline u32 bond_eth_hash(struct sk_buff *skb) { struct ethhdr *data = (struct ethhdr *)skb->data; if (skb_headlen(skb) >= offsetof(struct ethhdr, h_proto)) - return (data->h_dest[5] ^ data->h_source[5]) % count; + return data->h_dest[5] ^ data->h_source[5]; return 0; } -/* - * Hash for the output device based upon layer 2 and layer 3 data. If - * the packet is not IP, fall back on bond_xmit_hash_policy_l2() - */ -static int bond_xmit_hash_policy_l23(struct sk_buff *skb, int count) +/* Extract the appropriate headers based on bond's xmit policy */ +static bool bond_flow_dissect(struct bonding *bond, struct sk_buff *skb, + struct flow_keys *fk) { - const struct ethhdr *data; + const struct ipv6hdr *iph6; const struct iphdr *iph; - const struct ipv6hdr *ipv6h; - u32 v6hash; - const __be32 *s, *d; + int noff, proto = -1; + + if (bond->params.xmit_policy > BOND_XMIT_POLICY_LAYER23) + return skb_flow_dissect(skb, fk); - if (skb->protocol == htons(ETH_P_IP) && - pskb_network_may_pull(skb, sizeof(*iph))) { + fk->ports = 0; + noff = skb_network_offset(skb); + if (skb->protocol == htons(ETH_P_IP)) { + if (!pskb_may_pull(skb, noff + sizeof(*iph))) + return false; iph = ip_hdr(skb); - data = (struct ethhdr *)skb->data; - return ((ntohl(iph->saddr ^ iph->daddr) & 0xffff) ^ - (data->h_dest[5] ^ data->h_source[5])) % count; - } else if (skb->protocol == htons(ETH_P_IPV6) && - pskb_network_may_pull(skb, sizeof(*ipv6h))) { - ipv6h = ipv6_hdr(skb); - data = (struct ethhdr *)skb->data; - s = &ipv6h->saddr.s6_addr32[0]; - d = &ipv6h->daddr.s6_addr32[0]; - v6hash = (s[1] ^ d[1]) ^ (s[2] ^ d[2]) ^ (s[3] ^ d[3]); - v6hash ^= (v6hash >> 24) ^ (v6hash >> 16) ^ (v6hash >> 8); - return (v6hash ^ data->h_dest[5] ^ data->h_source[5]) % count; - } - - return bond_xmit_hash_policy_l2(skb, count); + fk->src = iph->saddr; + fk->dst = iph->daddr; + noff += iph->ihl << 2; + if (!ip_is_fragment(iph)) + proto = iph->protocol; + } else if (skb->protocol == htons(ETH_P_IPV6)) { + if (!pskb_may_pull(skb, noff + sizeof(*iph6))) + return false; + iph6 = ipv6_hdr(skb); + fk->src = (__force __be32)ipv6_addr_hash(&iph6->saddr); + fk->dst = (__force __be32)ipv6_addr_hash(&iph6->daddr); + noff += sizeof(*iph6); + proto = iph6->nexthdr; + } else { + return false; + } + if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER34 && proto >= 0) + fk->ports = skb_flow_get_ports(skb, noff, proto); + + return true; } -/* - * Hash for the output device based upon layer 3 and layer 4 data. If - * the packet is a frag or not TCP or UDP, just use layer 3 data. If it is - * altogether not IP, fall back on bond_xmit_hash_policy_l2() +/** + * bond_xmit_hash - generate a hash value based on the xmit policy + * @bond: bonding device + * @skb: buffer to use for headers + * @count: modulo value + * + * This function will extract the necessary headers from the skb buffer and use + * them to generate a hash based on the xmit_policy set in the bonding device + * which will be reduced modulo count before returning. */ -static int bond_xmit_hash_policy_l34(struct sk_buff *skb, int count) +int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count) { - u32 layer4_xor = 0; - const struct iphdr *iph; - const struct ipv6hdr *ipv6h; - const __be32 *s, *d; - const __be16 *l4 = NULL; - __be16 _l4[2]; - int noff = skb_network_offset(skb); - int poff; - - if (skb->protocol == htons(ETH_P_IP) && - pskb_may_pull(skb, noff + sizeof(*iph))) { - iph = ip_hdr(skb); - poff = proto_ports_offset(iph->protocol); + struct flow_keys flow; + u32 hash; - if (!ip_is_fragment(iph) && poff >= 0) { - l4 = skb_header_pointer(skb, noff + (iph->ihl << 2) + poff, - sizeof(_l4), &_l4); - if (l4) - layer4_xor = ntohs(l4[0] ^ l4[1]); - } - return (layer4_xor ^ - ((ntohl(iph->saddr ^ iph->daddr)) & 0xffff)) % count; - } else if (skb->protocol == htons(ETH_P_IPV6) && - pskb_may_pull(skb, noff + sizeof(*ipv6h))) { - ipv6h = ipv6_hdr(skb); - poff = proto_ports_offset(ipv6h->nexthdr); - if (poff >= 0) { - l4 = skb_header_pointer(skb, noff + sizeof(*ipv6h) + poff, - sizeof(_l4), &_l4); - if (l4) - layer4_xor = ntohs(l4[0] ^ l4[1]); - } - s = &ipv6h->saddr.s6_addr32[0]; - d = &ipv6h->daddr.s6_addr32[0]; - layer4_xor ^= (s[1] ^ d[1]) ^ (s[2] ^ d[2]) ^ (s[3] ^ d[3]); - layer4_xor ^= (layer4_xor >> 24) ^ (layer4_xor >> 16) ^ - (layer4_xor >> 8); - return layer4_xor % count; - } + if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER2 || + !bond_flow_dissect(bond, skb, &flow)) + return bond_eth_hash(skb) % count; + + if (bond->params.xmit_policy == BOND_XMIT_POLICY_LAYER23 || + bond->params.xmit_policy == BOND_XMIT_POLICY_ENCAP23) + hash = bond_eth_hash(skb); + else + hash = (__force u32)flow.ports; + hash ^= (__force u32)flow.dst ^ (__force u32)flow.src; + hash ^= (hash >> 16); + hash ^= (hash >> 8); - return bond_xmit_hash_policy_l2(skb, count); + return hash % count; } /*-------------------------- Device entry points ----------------------------*/ @@ -3155,13 +3061,14 @@ static void bond_work_cancel_all(struct bonding *bond) static int bond_open(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); + struct list_head *iter; struct slave *slave; /* reset slave->backup and slave->inactive */ read_lock(&bond->lock); - if (!list_empty(&bond->slave_list)) { + if (bond_has_slaves(bond)) { read_lock(&bond->curr_slave_lock); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { if ((bond->params.mode == BOND_MODE_ACTIVEBACKUP) && (slave != bond->curr_active_slave)) { bond_set_slave_inactive_flags(slave); @@ -3221,12 +3128,13 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev, { struct bonding *bond = netdev_priv(bond_dev); struct rtnl_link_stats64 temp; + struct list_head *iter; struct slave *slave; memset(stats, 0, sizeof(*stats)); read_lock_bh(&bond->lock); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { const struct rtnl_link_stats64 *sstats = dev_get_stats(slave->dev, &temp); @@ -3263,6 +3171,7 @@ static struct rtnl_link_stats64 *bond_get_stats(struct net_device *bond_dev, static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd) { + struct bonding *bond = netdev_priv(bond_dev); struct net_device *slave_dev = NULL; struct ifbond k_binfo; struct ifbond __user *u_binfo = NULL; @@ -3293,7 +3202,6 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd if (mii->reg_num == 1) { - struct bonding *bond = netdev_priv(bond_dev); mii->val_out = 0; read_lock(&bond->lock); read_lock(&bond->curr_slave_lock); @@ -3365,7 +3273,7 @@ static int bond_do_ioctl(struct net_device *bond_dev, struct ifreq *ifr, int cmd break; case BOND_CHANGE_ACTIVE_OLD: case SIOCBONDCHANGEACTIVE: - res = bond_ioctl_change_active(bond_dev, slave_dev); + res = bond_option_active_slave_set(bond, slave_dev); break; default: res = -EOPNOTSUPP; @@ -3393,22 +3301,24 @@ static void bond_change_rx_flags(struct net_device *bond_dev, int change) static void bond_set_rx_mode(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); + struct list_head *iter; struct slave *slave; - ASSERT_RTNL(); + rcu_read_lock(); if (USES_PRIMARY(bond->params.mode)) { - slave = rtnl_dereference(bond->curr_active_slave); + slave = rcu_dereference(bond->curr_active_slave); if (slave) { dev_uc_sync(slave->dev, bond_dev); dev_mc_sync(slave->dev, bond_dev); } } else { - bond_for_each_slave(bond, slave) { + bond_for_each_slave_rcu(bond, slave, iter) { dev_uc_sync_multiple(slave->dev, bond_dev); dev_mc_sync_multiple(slave->dev, bond_dev); } } + rcu_read_unlock(); } static int bond_neigh_init(struct neighbour *n) @@ -3471,7 +3381,8 @@ static int bond_neigh_setup(struct net_device *dev, static int bond_change_mtu(struct net_device *bond_dev, int new_mtu) { struct bonding *bond = netdev_priv(bond_dev); - struct slave *slave; + struct slave *slave, *rollback_slave; + struct list_head *iter; int res = 0; pr_debug("bond=%p, name=%s, new_mtu=%d\n", bond, @@ -3492,10 +3403,9 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu) * call to the base driver. */ - bond_for_each_slave(bond, slave) { - pr_debug("s %p s->p %p c_m %p\n", + bond_for_each_slave(bond, slave, iter) { + pr_debug("s %p c_m %p\n", slave, - bond_prev_slave(bond, slave), slave->dev->netdev_ops->ndo_change_mtu); res = dev_set_mtu(slave->dev, new_mtu); @@ -3520,13 +3430,16 @@ static int bond_change_mtu(struct net_device *bond_dev, int new_mtu) unwind: /* unwind from head to the slave that failed */ - bond_for_each_slave_continue_reverse(bond, slave) { + bond_for_each_slave(bond, rollback_slave, iter) { int tmp_res; - tmp_res = dev_set_mtu(slave->dev, bond_dev->mtu); + if (rollback_slave == slave) + break; + + tmp_res = dev_set_mtu(rollback_slave->dev, bond_dev->mtu); if (tmp_res) { pr_debug("unwind err %d dev %s\n", - tmp_res, slave->dev->name); + tmp_res, rollback_slave->dev->name); } } @@ -3543,8 +3456,9 @@ unwind: static int bond_set_mac_address(struct net_device *bond_dev, void *addr) { struct bonding *bond = netdev_priv(bond_dev); + struct slave *slave, *rollback_slave; struct sockaddr *sa = addr, tmp_sa; - struct slave *slave; + struct list_head *iter; int res = 0; if (bond->params.mode == BOND_MODE_ALB) @@ -3578,7 +3492,7 @@ static int bond_set_mac_address(struct net_device *bond_dev, void *addr) * call to the base driver. */ - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { const struct net_device_ops *slave_ops = slave->dev->netdev_ops; pr_debug("slave %p %s\n", slave, slave->dev->name); @@ -3610,13 +3524,16 @@ unwind: tmp_sa.sa_family = bond_dev->type; /* unwind from head to the slave that failed */ - bond_for_each_slave_continue_reverse(bond, slave) { + bond_for_each_slave(bond, rollback_slave, iter) { int tmp_res; - tmp_res = dev_set_mac_address(slave->dev, &tmp_sa); + if (rollback_slave == slave) + break; + + tmp_res = dev_set_mac_address(rollback_slave->dev, &tmp_sa); if (tmp_res) { pr_debug("unwind err %d dev %s\n", - tmp_res, slave->dev->name); + tmp_res, rollback_slave->dev->name); } } @@ -3635,11 +3552,12 @@ unwind: */ void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id) { + struct list_head *iter; struct slave *slave; int i = slave_id; /* Here we start from the slave with slave_id */ - bond_for_each_slave_rcu(bond, slave) { + bond_for_each_slave_rcu(bond, slave, iter) { if (--i < 0) { if (slave_can_tx(slave)) { bond_dev_queue_xmit(bond, skb, slave->dev); @@ -3650,7 +3568,7 @@ void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id) /* Here we start from the first slave up to slave_id */ i = slave_id; - bond_for_each_slave_rcu(bond, slave) { + bond_for_each_slave_rcu(bond, slave, iter) { if (--i < 0) break; if (slave_can_tx(slave)) { @@ -3662,14 +3580,44 @@ void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id) kfree_skb(skb); } +/** + * bond_rr_gen_slave_id - generate slave id based on packets_per_slave + * @bond: bonding device to use + * + * Based on the value of the bonding device's packets_per_slave parameter + * this function generates a slave id, which is usually used as the next + * slave to transmit through. + */ +static u32 bond_rr_gen_slave_id(struct bonding *bond) +{ + int packets_per_slave = bond->params.packets_per_slave; + u32 slave_id; + + switch (packets_per_slave) { + case 0: + slave_id = prandom_u32(); + break; + case 1: + slave_id = bond->rr_tx_counter; + break; + default: + slave_id = reciprocal_divide(bond->rr_tx_counter, + packets_per_slave); + break; + } + bond->rr_tx_counter++; + + return slave_id; +} + static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); struct iphdr *iph = ip_hdr(skb); struct slave *slave; + u32 slave_id; - /* - * Start with the curr_active_slave that joined the bond as the + /* Start with the curr_active_slave that joined the bond as the * default for sending IGMP traffic. For failover purposes one * needs to maintain some consistency for the interface that will * send the join/membership reports. The curr_active_slave found @@ -3682,8 +3630,8 @@ static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *bond_dev else bond_xmit_slave_id(bond, skb, 0); } else { - bond_xmit_slave_id(bond, skb, - bond->rr_tx_counter++ % bond->slave_cnt); + slave_id = bond_rr_gen_slave_id(bond); + bond_xmit_slave_id(bond, skb, slave_id % bond->slave_cnt); } return NETDEV_TX_OK; @@ -3707,8 +3655,7 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d return NETDEV_TX_OK; } -/* - * In bond_xmit_xor() , we determine the output device by using a pre- +/* In bond_xmit_xor() , we determine the output device by using a pre- * determined xmit_hash_policy(), If the selected device is not enabled, * find the next active slave. */ @@ -3716,8 +3663,7 @@ static int bond_xmit_xor(struct sk_buff *skb, struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); - bond_xmit_slave_id(bond, skb, - bond->xmit_hash_policy(skb, bond->slave_cnt)); + bond_xmit_slave_id(bond, skb, bond_xmit_hash(bond, skb, bond->slave_cnt)); return NETDEV_TX_OK; } @@ -3727,8 +3673,9 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); struct slave *slave = NULL; + struct list_head *iter; - bond_for_each_slave_rcu(bond, slave) { + bond_for_each_slave_rcu(bond, slave, iter) { if (bond_is_last_slave(bond, slave)) break; if (IS_UP(slave->dev) && slave->link == BOND_LINK_UP) { @@ -3753,22 +3700,6 @@ static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *bond_dev) /*------------------------- Device initialization ---------------------------*/ -static void bond_set_xmit_hash_policy(struct bonding *bond) -{ - switch (bond->params.xmit_policy) { - case BOND_XMIT_POLICY_LAYER23: - bond->xmit_hash_policy = bond_xmit_hash_policy_l23; - break; - case BOND_XMIT_POLICY_LAYER34: - bond->xmit_hash_policy = bond_xmit_hash_policy_l34; - break; - case BOND_XMIT_POLICY_LAYER2: - default: - bond->xmit_hash_policy = bond_xmit_hash_policy_l2; - break; - } -} - /* * Lookup the slave that corresponds to a qid */ @@ -3777,13 +3708,14 @@ static inline int bond_slave_override(struct bonding *bond, { struct slave *slave = NULL; struct slave *check_slave; + struct list_head *iter; int res = 1; if (!skb->queue_mapping) return 1; /* Find out if any slaves have the same mapping as this skb. */ - bond_for_each_slave_rcu(bond, check_slave) { + bond_for_each_slave_rcu(bond, check_slave, iter) { if (check_slave->queue_id == skb->queue_mapping) { slave = check_slave; break; @@ -3869,7 +3801,7 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; rcu_read_lock(); - if (!list_empty(&bond->slave_list)) + if (bond_has_slaves(bond)) ret = __bond_start_xmit(skb, dev); else kfree_skb(skb); @@ -3878,43 +3810,12 @@ static netdev_tx_t bond_start_xmit(struct sk_buff *skb, struct net_device *dev) return ret; } -/* - * set bond mode specific net device operations - */ -void bond_set_mode_ops(struct bonding *bond, int mode) -{ - struct net_device *bond_dev = bond->dev; - - switch (mode) { - case BOND_MODE_ROUNDROBIN: - break; - case BOND_MODE_ACTIVEBACKUP: - break; - case BOND_MODE_XOR: - bond_set_xmit_hash_policy(bond); - break; - case BOND_MODE_BROADCAST: - break; - case BOND_MODE_8023AD: - bond_set_xmit_hash_policy(bond); - break; - case BOND_MODE_ALB: - /* FALLTHRU */ - case BOND_MODE_TLB: - break; - default: - /* Should never happen, mode already checked */ - pr_err("%s: Error: Unknown bonding mode %d\n", - bond_dev->name, mode); - break; - } -} - static int bond_ethtool_get_settings(struct net_device *bond_dev, struct ethtool_cmd *ecmd) { struct bonding *bond = netdev_priv(bond_dev); unsigned long speed = 0; + struct list_head *iter; struct slave *slave; ecmd->duplex = DUPLEX_UNKNOWN; @@ -3926,7 +3827,7 @@ static int bond_ethtool_get_settings(struct net_device *bond_dev, * this is an accurate maximum. */ read_lock(&bond->lock); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { if (SLAVE_IS_OK(slave)) { if (slave->speed != SPEED_UNKNOWN) speed += slave->speed; @@ -3994,14 +3895,13 @@ static void bond_destructor(struct net_device *bond_dev) free_netdev(bond_dev); } -static void bond_setup(struct net_device *bond_dev) +void bond_setup(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); /* initialize rwlocks */ rwlock_init(&bond->lock); rwlock_init(&bond->curr_slave_lock); - INIT_LIST_HEAD(&bond->slave_list); bond->params = bonding_defaults; /* Initialize pointers */ @@ -4011,7 +3911,6 @@ static void bond_setup(struct net_device *bond_dev) ether_setup(bond_dev); bond_dev->netdev_ops = &bond_netdev_ops; bond_dev->ethtool_ops = &bond_ethtool_ops; - bond_set_mode_ops(bond, bond->params.mode); bond_dev->destructor = bond_destructor; @@ -4057,12 +3956,13 @@ static void bond_setup(struct net_device *bond_dev) static void bond_uninit(struct net_device *bond_dev) { struct bonding *bond = netdev_priv(bond_dev); - struct slave *slave, *tmp_slave; + struct list_head *iter; + struct slave *slave; bond_netpoll_cleanup(bond_dev); /* Release the bonded slaves */ - list_for_each_entry_safe(slave, tmp_slave, &bond->slave_list, list) + bond_for_each_slave(bond, slave, iter) __bond_release_one(bond_dev, slave->dev, true); pr_info("%s: released all slaves\n", bond_dev->name); @@ -4235,6 +4135,12 @@ static int bond_check_params(struct bond_params *params) resend_igmp = BOND_DEFAULT_RESEND_IGMP; } + if (packets_per_slave < 0 || packets_per_slave > USHRT_MAX) { + pr_warn("Warning: packets_per_slave (%d) should be between 0 and %u resetting to 1\n", + packets_per_slave, USHRT_MAX); + packets_per_slave = 1; + } + /* reset values for TLB/ALB */ if ((bond_mode == BOND_MODE_TLB) || (bond_mode == BOND_MODE_ALB)) { @@ -4424,7 +4330,10 @@ static int bond_check_params(struct bond_params *params) params->resend_igmp = resend_igmp; params->min_links = min_links; params->lp_interval = BOND_ALB_DEFAULT_LP_INTERVAL; - + if (packets_per_slave > 1) + params->packets_per_slave = reciprocal_value(packets_per_slave); + else + params->packets_per_slave = packets_per_slave; if (primary) { strncpy(params->primary, primary, IFNAMSIZ); params->primary[IFNAMSIZ - 1] = 0; @@ -4495,32 +4404,11 @@ static int bond_init(struct net_device *bond_dev) return 0; } -static int bond_validate(struct nlattr *tb[], struct nlattr *data[]) -{ - if (tb[IFLA_ADDRESS]) { - if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) - return -EINVAL; - if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) - return -EADDRNOTAVAIL; - } - return 0; -} - -static unsigned int bond_get_num_tx_queues(void) +unsigned int bond_get_num_tx_queues(void) { return tx_queues; } -static struct rtnl_link_ops bond_link_ops __read_mostly = { - .kind = "bond", - .priv_size = sizeof(struct bonding), - .setup = bond_setup, - .validate = bond_validate, - .get_num_tx_queues = bond_get_num_tx_queues, - .get_num_rx_queues = bond_get_num_tx_queues, /* Use the same number - as for TX queues */ -}; - /* Create a new bond based on the specified name and bonding parameters. * If name is NULL, obtain a suitable "bond%d" name for us. * Caller must NOT hold rtnl_lock; we need to release it here before we @@ -4607,7 +4495,7 @@ static int __init bonding_init(void) if (res) goto out; - res = rtnl_link_register(&bond_link_ops); + res = bond_netlink_init(); if (res) goto err_link; @@ -4623,7 +4511,7 @@ static int __init bonding_init(void) out: return res; err: - rtnl_link_unregister(&bond_link_ops); + bond_netlink_fini(); err_link: unregister_pernet_subsys(&bond_net_ops); goto out; @@ -4636,7 +4524,7 @@ static void __exit bonding_exit(void) bond_destroy_debugfs(); - rtnl_link_unregister(&bond_link_ops); + bond_netlink_fini(); unregister_pernet_subsys(&bond_net_ops); #ifdef CONFIG_NET_POLL_CONTROLLER @@ -4653,4 +4541,3 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION(DRV_DESCRIPTION ", v" DRV_VERSION); MODULE_AUTHOR("Thomas Davis, tadavis@lbl.gov and many others"); -MODULE_ALIAS_RTNL_LINK("bond"); diff --git a/drivers/net/bonding/bond_netlink.c b/drivers/net/bonding/bond_netlink.c new file mode 100644 index 0000000000000000000000000000000000000000..40e7b1cb4aeaa622c62b40b455670c2c8fd5b04a --- /dev/null +++ b/drivers/net/bonding/bond_netlink.c @@ -0,0 +1,131 @@ +/* + * drivers/net/bond/bond_netlink.c - Netlink interface for bonding + * Copyright (c) 2013 Jiri Pirko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include "bonding.h" + +static const struct nla_policy bond_policy[IFLA_BOND_MAX + 1] = { + [IFLA_BOND_MODE] = { .type = NLA_U8 }, + [IFLA_BOND_ACTIVE_SLAVE] = { .type = NLA_U32 }, +}; + +static int bond_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + if (tb[IFLA_ADDRESS]) { + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) + return -EINVAL; + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) + return -EADDRNOTAVAIL; + } + return 0; +} + +static int bond_changelink(struct net_device *bond_dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct bonding *bond = netdev_priv(bond_dev); + int err; + + if (data && data[IFLA_BOND_MODE]) { + int mode = nla_get_u8(data[IFLA_BOND_MODE]); + + err = bond_option_mode_set(bond, mode); + if (err) + return err; + } + if (data && data[IFLA_BOND_ACTIVE_SLAVE]) { + int ifindex = nla_get_u32(data[IFLA_BOND_ACTIVE_SLAVE]); + struct net_device *slave_dev; + + if (ifindex == 0) { + slave_dev = NULL; + } else { + slave_dev = __dev_get_by_index(dev_net(bond_dev), + ifindex); + if (!slave_dev) + return -ENODEV; + } + err = bond_option_active_slave_set(bond, slave_dev); + if (err) + return err; + } + return 0; +} + +static int bond_newlink(struct net *src_net, struct net_device *bond_dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + int err; + + err = bond_changelink(bond_dev, tb, data); + if (err < 0) + return err; + + return register_netdevice(bond_dev); +} + +static size_t bond_get_size(const struct net_device *bond_dev) +{ + return nla_total_size(sizeof(u8)) + /* IFLA_BOND_MODE */ + nla_total_size(sizeof(u32)); /* IFLA_BOND_ACTIVE_SLAVE */ +} + +static int bond_fill_info(struct sk_buff *skb, + const struct net_device *bond_dev) +{ + struct bonding *bond = netdev_priv(bond_dev); + struct net_device *slave_dev = bond_option_active_slave_get(bond); + + if (nla_put_u8(skb, IFLA_BOND_MODE, bond->params.mode) || + (slave_dev && + nla_put_u32(skb, IFLA_BOND_ACTIVE_SLAVE, slave_dev->ifindex))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +struct rtnl_link_ops bond_link_ops __read_mostly = { + .kind = "bond", + .priv_size = sizeof(struct bonding), + .setup = bond_setup, + .maxtype = IFLA_BOND_MAX, + .policy = bond_policy, + .validate = bond_validate, + .newlink = bond_newlink, + .changelink = bond_changelink, + .get_size = bond_get_size, + .fill_info = bond_fill_info, + .get_num_tx_queues = bond_get_num_tx_queues, + .get_num_rx_queues = bond_get_num_tx_queues, /* Use the same number + as for TX queues */ +}; + +int __init bond_netlink_init(void) +{ + return rtnl_link_register(&bond_link_ops); +} + +void bond_netlink_fini(void) +{ + rtnl_link_unregister(&bond_link_ops); +} + +MODULE_ALIAS_RTNL_LINK("bond"); diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c new file mode 100644 index 0000000000000000000000000000000000000000..9a5223c7b4d1a14f9b19354cfba6c4ea3dd933d0 --- /dev/null +++ b/drivers/net/bonding/bond_options.c @@ -0,0 +1,142 @@ +/* + * drivers/net/bond/bond_options.c - bonding options + * Copyright (c) 2013 Jiri Pirko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include "bonding.h" + +static bool bond_mode_is_valid(int mode) +{ + int i; + + for (i = 0; bond_mode_tbl[i].modename; i++); + + return mode >= 0 && mode < i; +} + +int bond_option_mode_set(struct bonding *bond, int mode) +{ + if (!bond_mode_is_valid(mode)) { + pr_err("invalid mode value %d.\n", mode); + return -EINVAL; + } + + if (bond->dev->flags & IFF_UP) { + pr_err("%s: unable to update mode because interface is up.\n", + bond->dev->name); + return -EPERM; + } + + if (bond_has_slaves(bond)) { + pr_err("%s: unable to update mode because bond has slaves.\n", + bond->dev->name); + return -EPERM; + } + + if (BOND_MODE_IS_LB(mode) && bond->params.arp_interval) { + pr_err("%s: %s mode is incompatible with arp monitoring.\n", + bond->dev->name, bond_mode_tbl[mode].modename); + return -EINVAL; + } + + /* don't cache arp_validate between modes */ + bond->params.arp_validate = BOND_ARP_VALIDATE_NONE; + bond->params.mode = mode; + return 0; +} + +static struct net_device *__bond_option_active_slave_get(struct bonding *bond, + struct slave *slave) +{ + return USES_PRIMARY(bond->params.mode) && slave ? slave->dev : NULL; +} + +struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond) +{ + struct slave *slave = rcu_dereference(bond->curr_active_slave); + + return __bond_option_active_slave_get(bond, slave); +} + +struct net_device *bond_option_active_slave_get(struct bonding *bond) +{ + return __bond_option_active_slave_get(bond, bond->curr_active_slave); +} + +int bond_option_active_slave_set(struct bonding *bond, + struct net_device *slave_dev) +{ + int ret = 0; + + if (slave_dev) { + if (!netif_is_bond_slave(slave_dev)) { + pr_err("Device %s is not bonding slave.\n", + slave_dev->name); + return -EINVAL; + } + + if (bond->dev != netdev_master_upper_dev_get(slave_dev)) { + pr_err("%s: Device %s is not our slave.\n", + bond->dev->name, slave_dev->name); + return -EINVAL; + } + } + + if (!USES_PRIMARY(bond->params.mode)) { + pr_err("%s: Unable to change active slave; %s is in mode %d\n", + bond->dev->name, bond->dev->name, bond->params.mode); + return -EINVAL; + } + + block_netpoll_tx(); + read_lock(&bond->lock); + write_lock_bh(&bond->curr_slave_lock); + + /* check to see if we are clearing active */ + if (!slave_dev) { + pr_info("%s: Clearing current active slave.\n", + bond->dev->name); + rcu_assign_pointer(bond->curr_active_slave, NULL); + bond_select_active_slave(bond); + } else { + struct slave *old_active = bond->curr_active_slave; + struct slave *new_active = bond_slave_get_rtnl(slave_dev); + + BUG_ON(!new_active); + + if (new_active == old_active) { + /* do nothing */ + pr_info("%s: %s is already the current active slave.\n", + bond->dev->name, new_active->dev->name); + } else { + if (old_active && (new_active->link == BOND_LINK_UP) && + IS_UP(new_active->dev)) { + pr_info("%s: Setting %s as active slave.\n", + bond->dev->name, new_active->dev->name); + bond_change_active_slave(bond, new_active); + } else { + pr_err("%s: Could not set %s as active slave; either %s is down or the link is down.\n", + bond->dev->name, new_active->dev->name, + new_active->dev->name); + ret = -EINVAL; + } + } + } + + write_unlock_bh(&bond->curr_slave_lock); + read_unlock(&bond->lock); + unblock_netpoll_tx(); + return ret; +} diff --git a/drivers/net/bonding/bond_procfs.c b/drivers/net/bonding/bond_procfs.c index 20a6ee25bb63e42cdf89c0273d8e1afa30234f26..fb868d6c22dac5c75ecd156e61089e7f6806b6c9 100644 --- a/drivers/net/bonding/bond_procfs.c +++ b/drivers/net/bonding/bond_procfs.c @@ -10,8 +10,9 @@ static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos) __acquires(&bond->lock) { struct bonding *bond = seq->private; - loff_t off = 0; + struct list_head *iter; struct slave *slave; + loff_t off = 0; /* make sure the bond won't be taken away */ rcu_read_lock(); @@ -20,7 +21,7 @@ static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos) if (*pos == 0) return SEQ_START_TOKEN; - bond_for_each_slave(bond, slave) + bond_for_each_slave(bond, slave, iter) if (++off == *pos) return slave; @@ -30,17 +31,25 @@ static void *bond_info_seq_start(struct seq_file *seq, loff_t *pos) static void *bond_info_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct bonding *bond = seq->private; - struct slave *slave = v; + struct list_head *iter; + struct slave *slave; + bool found = false; ++*pos; if (v == SEQ_START_TOKEN) return bond_first_slave(bond); - if (bond_is_last_slave(bond, slave)) + if (bond_is_last_slave(bond, v)) return NULL; - slave = bond_next_slave(bond, slave); - return slave; + bond_for_each_slave(bond, slave, iter) { + if (found) + return slave; + if (slave == v) + found = true; + } + + return NULL; } static void bond_info_seq_stop(struct seq_file *seq, void *v) diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index ec9b6460a38dd0549b5b69f281b36f6cce0a3e7d..bc8fd362a5aa7624fc01a6c79722f4b57e824aef 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "bonding.h" @@ -159,41 +160,6 @@ static const struct class_attribute class_attr_bonding_masters = { .store = bonding_store_bonds, }; -int bond_create_slave_symlinks(struct net_device *master, - struct net_device *slave) -{ - char linkname[IFNAMSIZ+7]; - int ret = 0; - - /* first, create a link from the slave back to the master */ - ret = sysfs_create_link(&(slave->dev.kobj), &(master->dev.kobj), - "master"); - if (ret) - return ret; - /* next, create a link from the master to the slave */ - sprintf(linkname, "slave_%s", slave->name); - ret = sysfs_create_link(&(master->dev.kobj), &(slave->dev.kobj), - linkname); - - /* free the master link created earlier in case of error */ - if (ret) - sysfs_remove_link(&(slave->dev.kobj), "master"); - - return ret; - -} - -void bond_destroy_slave_symlinks(struct net_device *master, - struct net_device *slave) -{ - char linkname[IFNAMSIZ+7]; - - sysfs_remove_link(&(slave->dev.kobj), "master"); - sprintf(linkname, "slave_%s", slave->name); - sysfs_remove_link(&(master->dev.kobj), linkname); -} - - /* * Show the slaves in the current bond. */ @@ -201,11 +167,14 @@ static ssize_t bonding_show_slaves(struct device *d, struct device_attribute *attr, char *buf) { struct bonding *bond = to_bond(d); + struct list_head *iter; struct slave *slave; int res = 0; - read_lock(&bond->lock); - bond_for_each_slave(bond, slave) { + if (!rtnl_trylock()) + return restart_syscall(); + + bond_for_each_slave(bond, slave, iter) { if (res > (PAGE_SIZE - IFNAMSIZ)) { /* not enough space for another interface name */ if ((PAGE_SIZE - res) > 10) @@ -215,7 +184,9 @@ static ssize_t bonding_show_slaves(struct device *d, } res += sprintf(buf + res, "%s ", slave->dev->name); } - read_unlock(&bond->lock); + + rtnl_unlock(); + if (res) buf[res-1] = '\n'; /* eat the leftover space */ @@ -304,50 +275,26 @@ static ssize_t bonding_store_mode(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - int new_value, ret = count; + int new_value, ret; struct bonding *bond = to_bond(d); - if (!rtnl_trylock()) - return restart_syscall(); - - if (bond->dev->flags & IFF_UP) { - pr_err("unable to update mode of %s because interface is up.\n", - bond->dev->name); - ret = -EPERM; - goto out; - } - - if (!list_empty(&bond->slave_list)) { - pr_err("unable to update mode of %s because it has slaves.\n", - bond->dev->name); - ret = -EPERM; - goto out; - } - new_value = bond_parse_parm(buf, bond_mode_tbl); if (new_value < 0) { pr_err("%s: Ignoring invalid mode value %.*s.\n", bond->dev->name, (int)strlen(buf) - 1, buf); - ret = -EINVAL; - goto out; + return -EINVAL; } - if ((new_value == BOND_MODE_ALB || - new_value == BOND_MODE_TLB) && - bond->params.arp_interval) { - pr_err("%s: %s mode is incompatible with arp monitoring.\n", - bond->dev->name, bond_mode_tbl[new_value].modename); - ret = -EINVAL; - goto out; + if (!rtnl_trylock()) + return restart_syscall(); + + ret = bond_option_mode_set(bond, new_value); + if (!ret) { + pr_info("%s: setting mode to %s (%d).\n", + bond->dev->name, bond_mode_tbl[new_value].modename, + new_value); + ret = count; } - /* don't cache arp_validate between modes */ - bond->params.arp_validate = BOND_ARP_VALIDATE_NONE; - bond->params.mode = new_value; - bond_set_mode_ops(bond, bond->params.mode); - pr_info("%s: setting mode to %s (%d).\n", - bond->dev->name, bond_mode_tbl[new_value].modename, - new_value); -out: rtnl_unlock(); return ret; } @@ -383,7 +330,6 @@ static ssize_t bonding_store_xmit_hash(struct device *d, ret = -EINVAL; } else { bond->params.xmit_policy = new_value; - bond_set_mode_ops(bond, bond->params.mode); pr_info("%s: setting xmit hash policy to %s (%d).\n", bond->dev->name, xmit_hashtype_tbl[new_value].modename, new_value); @@ -513,7 +459,7 @@ static ssize_t bonding_store_fail_over_mac(struct device *d, if (!rtnl_trylock()) return restart_syscall(); - if (!list_empty(&bond->slave_list)) { + if (bond_has_slaves(bond)) { pr_err("%s: Can't alter fail_over_mac with slaves in bond.\n", bond->dev->name); ret = -EPERM; @@ -647,11 +593,15 @@ static ssize_t bonding_store_arp_targets(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); + struct list_head *iter; struct slave *slave; __be32 newtarget, *targets; unsigned long *targets_rx; int ind, i, j, ret = -EINVAL; + if (!rtnl_trylock()) + return restart_syscall(); + targets = bond->params.arp_targets; newtarget = in_aton(buf + 1); /* look for adds */ @@ -679,7 +629,7 @@ static ssize_t bonding_store_arp_targets(struct device *d, &newtarget); /* not to race with bond_arp_rcv */ write_lock_bh(&bond->lock); - bond_for_each_slave(bond, slave) + bond_for_each_slave(bond, slave, iter) slave->target_last_arp_rx[ind] = jiffies; targets[ind] = newtarget; write_unlock_bh(&bond->lock); @@ -705,7 +655,7 @@ static ssize_t bonding_store_arp_targets(struct device *d, &newtarget); write_lock_bh(&bond->lock); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { targets_rx = slave->target_last_arp_rx; j = ind; for (; (j < BOND_MAX_ARP_TARGETS-1) && targets[j+1]; j++) @@ -725,6 +675,7 @@ static ssize_t bonding_store_arp_targets(struct device *d, ret = count; out: + rtnl_unlock(); return ret; } static DEVICE_ATTR(arp_ip_target, S_IRUGO | S_IWUSR , bonding_show_arp_targets, bonding_store_arp_targets); @@ -1102,6 +1053,7 @@ static ssize_t bonding_store_primary(struct device *d, const char *buf, size_t count) { struct bonding *bond = to_bond(d); + struct list_head *iter; char ifname[IFNAMSIZ]; struct slave *slave; @@ -1129,7 +1081,7 @@ static ssize_t bonding_store_primary(struct device *d, goto out; } - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) { pr_info("%s: Setting %s as primary slave.\n", bond->dev->name, slave->dev->name); @@ -1259,13 +1211,13 @@ static ssize_t bonding_show_active_slave(struct device *d, char *buf) { struct bonding *bond = to_bond(d); - struct slave *curr; + struct net_device *slave_dev; int count = 0; rcu_read_lock(); - curr = rcu_dereference(bond->curr_active_slave); - if (USES_PRIMARY(bond->params.mode) && curr) - count = sprintf(buf, "%s\n", curr->dev->name); + slave_dev = bond_option_active_slave_get_rcu(bond); + if (slave_dev) + count = sprintf(buf, "%s\n", slave_dev->name); rcu_read_unlock(); return count; @@ -1275,80 +1227,33 @@ static ssize_t bonding_store_active_slave(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { - struct slave *slave, *old_active, *new_active; + int ret; struct bonding *bond = to_bond(d); char ifname[IFNAMSIZ]; + struct net_device *dev; if (!rtnl_trylock()) return restart_syscall(); - old_active = new_active = NULL; - block_netpoll_tx(); - read_lock(&bond->lock); - write_lock_bh(&bond->curr_slave_lock); - - if (!USES_PRIMARY(bond->params.mode)) { - pr_info("%s: Unable to change active slave; %s is in mode %d\n", - bond->dev->name, bond->dev->name, bond->params.mode); - goto out; - } - sscanf(buf, "%15s", ifname); /* IFNAMSIZ */ - - /* check to see if we are clearing active */ if (!strlen(ifname) || buf[0] == '\n') { - pr_info("%s: Clearing current active slave.\n", - bond->dev->name); - rcu_assign_pointer(bond->curr_active_slave, NULL); - bond_select_active_slave(bond); - goto out; - } - - bond_for_each_slave(bond, slave) { - if (strncmp(slave->dev->name, ifname, IFNAMSIZ) == 0) { - old_active = bond->curr_active_slave; - new_active = slave; - if (new_active == old_active) { - /* do nothing */ - pr_info("%s: %s is already the current" - " active slave.\n", - bond->dev->name, - slave->dev->name); - goto out; - } else { - if ((new_active) && - (old_active) && - (new_active->link == BOND_LINK_UP) && - IS_UP(new_active->dev)) { - pr_info("%s: Setting %s as active" - " slave.\n", - bond->dev->name, - slave->dev->name); - bond_change_active_slave(bond, - new_active); - } else { - pr_info("%s: Could not set %s as" - " active slave; either %s is" - " down or the link is down.\n", - bond->dev->name, - slave->dev->name, - slave->dev->name); - } - goto out; - } + dev = NULL; + } else { + dev = __dev_get_by_name(dev_net(bond->dev), ifname); + if (!dev) { + ret = -ENODEV; + goto out; } } - pr_info("%s: Unable to set %.*s as active slave.\n", - bond->dev->name, (int)strlen(buf) - 1, buf); - out: - write_unlock_bh(&bond->curr_slave_lock); - read_unlock(&bond->lock); - unblock_netpoll_tx(); + ret = bond_option_active_slave_set(bond, dev); + if (!ret) + ret = count; + out: rtnl_unlock(); - return count; + return ret; } static DEVICE_ATTR(active_slave, S_IRUGO | S_IWUSR, @@ -1484,14 +1389,14 @@ static ssize_t bonding_show_queue_id(struct device *d, char *buf) { struct bonding *bond = to_bond(d); + struct list_head *iter; struct slave *slave; int res = 0; if (!rtnl_trylock()) return restart_syscall(); - read_lock(&bond->lock); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { if (res > (PAGE_SIZE - IFNAMSIZ - 6)) { /* not enough space for another interface_name:queue_id pair */ if ((PAGE_SIZE - res) > 10) @@ -1502,9 +1407,9 @@ static ssize_t bonding_show_queue_id(struct device *d, res += sprintf(buf + res, "%s:%d ", slave->dev->name, slave->queue_id); } - read_unlock(&bond->lock); if (res) buf[res-1] = '\n'; /* eat the leftover space */ + rtnl_unlock(); return res; @@ -1520,6 +1425,7 @@ static ssize_t bonding_store_queue_id(struct device *d, { struct slave *slave, *update_slave; struct bonding *bond = to_bond(d); + struct list_head *iter; u16 qid; int ret = count; char *delim; @@ -1552,11 +1458,9 @@ static ssize_t bonding_store_queue_id(struct device *d, if (!sdev) goto err_no_cmd; - read_lock(&bond->lock); - /* Search for thes slave and check for duplicate qids */ update_slave = NULL; - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { if (sdev == slave->dev) /* * We don't need to check the matching @@ -1564,23 +1468,20 @@ static ssize_t bonding_store_queue_id(struct device *d, */ update_slave = slave; else if (qid && qid == slave->queue_id) { - goto err_no_cmd_unlock; + goto err_no_cmd; } } if (!update_slave) - goto err_no_cmd_unlock; + goto err_no_cmd; /* Actually set the qids for the slave */ update_slave->queue_id = qid; - read_unlock(&bond->lock); out: rtnl_unlock(); return ret; -err_no_cmd_unlock: - read_unlock(&bond->lock); err_no_cmd: pr_info("invalid input for queue_id set for %s.\n", bond->dev->name); @@ -1610,8 +1511,12 @@ static ssize_t bonding_store_slaves_active(struct device *d, { struct bonding *bond = to_bond(d); int new_value, ret = count; + struct list_head *iter; struct slave *slave; + if (!rtnl_trylock()) + return restart_syscall(); + if (sscanf(buf, "%d", &new_value) != 1) { pr_err("%s: no all_slaves_active value specified.\n", bond->dev->name); @@ -1631,8 +1536,7 @@ static ssize_t bonding_store_slaves_active(struct device *d, goto out; } - read_lock(&bond->lock); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { if (!bond_is_active_slave(slave)) { if (new_value) slave->inactive = 0; @@ -1640,8 +1544,8 @@ static ssize_t bonding_store_slaves_active(struct device *d, slave->inactive = 1; } } - read_unlock(&bond->lock); out: + rtnl_unlock(); return ret; } static DEVICE_ATTR(all_slaves_active, S_IRUGO | S_IWUSR, @@ -1728,6 +1632,53 @@ out: static DEVICE_ATTR(lp_interval, S_IRUGO | S_IWUSR, bonding_show_lp_interval, bonding_store_lp_interval); +static ssize_t bonding_show_packets_per_slave(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct bonding *bond = to_bond(d); + int packets_per_slave = bond->params.packets_per_slave; + + if (packets_per_slave > 1) + packets_per_slave = reciprocal_value(packets_per_slave); + + return sprintf(buf, "%d\n", packets_per_slave); +} + +static ssize_t bonding_store_packets_per_slave(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bonding *bond = to_bond(d); + int new_value, ret = count; + + if (sscanf(buf, "%d", &new_value) != 1) { + pr_err("%s: no packets_per_slave value specified.\n", + bond->dev->name); + ret = -EINVAL; + goto out; + } + if (new_value < 0 || new_value > USHRT_MAX) { + pr_err("%s: packets_per_slave must be between 0 and %u\n", + bond->dev->name, USHRT_MAX); + ret = -EINVAL; + goto out; + } + if (bond->params.mode != BOND_MODE_ROUNDROBIN) + pr_warn("%s: Warning: packets_per_slave has effect only in balance-rr mode\n", + bond->dev->name); + if (new_value > 1) + bond->params.packets_per_slave = reciprocal_value(new_value); + else + bond->params.packets_per_slave = new_value; +out: + return ret; +} + +static DEVICE_ATTR(packets_per_slave, S_IRUGO | S_IWUSR, + bonding_show_packets_per_slave, + bonding_store_packets_per_slave); + static struct attribute *per_bond_attrs[] = { &dev_attr_slaves.attr, &dev_attr_mode.attr, @@ -1759,6 +1710,7 @@ static struct attribute *per_bond_attrs[] = { &dev_attr_resend_igmp.attr, &dev_attr_min_links.attr, &dev_attr_lp_interval.attr, + &dev_attr_packets_per_slave.attr, NULL, }; diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 03cf3fd14490c4e4dcf8cd2d61f8bca99c9d55db..77a07a12e77fea8e44376e40fd4c0d12caba53af 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -58,6 +58,11 @@ #define TX_QUEUE_OVERRIDE(mode) \ (((mode) == BOND_MODE_ACTIVEBACKUP) || \ ((mode) == BOND_MODE_ROUNDROBIN)) + +#define BOND_MODE_IS_LB(mode) \ + (((mode) == BOND_MODE_TLB) || \ + ((mode) == BOND_MODE_ALB)) + /* * Less bad way to call ioctl from within the kernel; this needs to be * done some other way to get the call out of interrupt context. @@ -72,63 +77,37 @@ res; }) /* slave list primitives */ -#define bond_to_slave(ptr) list_entry(ptr, struct slave, list) +#define bond_slave_list(bond) (&(bond)->dev->adj_list.lower) + +#define bond_has_slaves(bond) !list_empty(bond_slave_list(bond)) /* IMPORTANT: bond_first/last_slave can return NULL in case of an empty list */ #define bond_first_slave(bond) \ - list_first_entry_or_null(&(bond)->slave_list, struct slave, list) + (bond_has_slaves(bond) ? \ + netdev_adjacent_get_private(bond_slave_list(bond)->next) : \ + NULL) #define bond_last_slave(bond) \ - (list_empty(&(bond)->slave_list) ? NULL : \ - bond_to_slave((bond)->slave_list.prev)) + (bond_has_slaves(bond) ? \ + netdev_adjacent_get_private(bond_slave_list(bond)->prev) : \ + NULL) -#define bond_is_first_slave(bond, pos) ((pos)->list.prev == &(bond)->slave_list) -#define bond_is_last_slave(bond, pos) ((pos)->list.next == &(bond)->slave_list) - -/* Since bond_first/last_slave can return NULL, these can return NULL too */ -#define bond_next_slave(bond, pos) \ - (bond_is_last_slave(bond, pos) ? bond_first_slave(bond) : \ - bond_to_slave((pos)->list.next)) - -#define bond_prev_slave(bond, pos) \ - (bond_is_first_slave(bond, pos) ? bond_last_slave(bond) : \ - bond_to_slave((pos)->list.prev)) - -/** - * bond_for_each_slave_from - iterate the slaves list from a starting point - * @bond: the bond holding this list. - * @pos: current slave. - * @cnt: counter for max number of moves - * @start: starting point. - * - * Caller must hold bond->lock - */ -#define bond_for_each_slave_from(bond, pos, cnt, start) \ - for (cnt = 0, pos = start; pos && cnt < (bond)->slave_cnt; \ - cnt++, pos = bond_next_slave(bond, pos)) +#define bond_is_first_slave(bond, pos) (pos == bond_first_slave(bond)) +#define bond_is_last_slave(bond, pos) (pos == bond_last_slave(bond)) /** * bond_for_each_slave - iterate over all slaves * @bond: the bond holding this list * @pos: current slave + * @iter: list_head * iterator * * Caller must hold bond->lock */ -#define bond_for_each_slave(bond, pos) \ - list_for_each_entry(pos, &(bond)->slave_list, list) +#define bond_for_each_slave(bond, pos, iter) \ + netdev_for_each_lower_private((bond)->dev, pos, iter) /* Caller must have rcu_read_lock */ -#define bond_for_each_slave_rcu(bond, pos) \ - list_for_each_entry_rcu(pos, &(bond)->slave_list, list) - -/** - * bond_for_each_slave_reverse - iterate in reverse from a given position - * @bond: the bond holding this list - * @pos: slave to continue from - * - * Caller must hold bond->lock - */ -#define bond_for_each_slave_continue_reverse(bond, pos) \ - list_for_each_entry_continue_reverse(pos, &(bond)->slave_list, list) +#define bond_for_each_slave_rcu(bond, pos, iter) \ + netdev_for_each_lower_private_rcu((bond)->dev, pos, iter) #ifdef CONFIG_NET_POLL_CONTROLLER extern atomic_t netpoll_block_tx; @@ -177,6 +156,7 @@ struct bond_params { int all_slaves_active; int resend_igmp; int lp_interval; + int packets_per_slave; }; struct bond_parm_tbl { @@ -188,7 +168,6 @@ struct bond_parm_tbl { struct slave { struct net_device *dev; /* first - useful for panic debug */ - struct list_head list; struct bonding *bond; /* our master */ int delay; unsigned long jiffies; @@ -228,7 +207,6 @@ struct slave { */ struct bonding { struct net_device *dev; /* first - useful for panic debug */ - struct list_head slave_list; struct slave *curr_active_slave; struct slave *current_arp_slave; struct slave *primary_slave; @@ -245,8 +223,7 @@ struct bonding { char proc_file_name[IFNAMSIZ]; #endif /* CONFIG_PROC_FS */ struct list_head bond_list; - int (*xmit_hash_policy)(struct sk_buff *, int); - u16 rr_tx_counter; + u32 rr_tx_counter; struct ad_bond_info ad_info; struct alb_bond_info alb_info; struct bond_params params; @@ -276,13 +253,7 @@ struct bonding { static inline struct slave *bond_get_slave_by_dev(struct bonding *bond, struct net_device *slave_dev) { - struct slave *slave = NULL; - - bond_for_each_slave(bond, slave) - if (slave->dev == slave_dev) - return slave; - - return NULL; + return netdev_lower_dev_get_private(bond->dev, slave_dev); } static inline struct bonding *bond_get_bond_by_slave(struct slave *slave) @@ -294,8 +265,7 @@ static inline struct bonding *bond_get_bond_by_slave(struct slave *slave) static inline bool bond_is_lb(const struct bonding *bond) { - return (bond->params.mode == BOND_MODE_TLB || - bond->params.mode == BOND_MODE_ALB); + return BOND_MODE_IS_LB(bond->params.mode); } static inline void bond_set_active_slave(struct slave *slave) @@ -432,21 +402,18 @@ static inline bool slave_can_tx(struct slave *slave) struct bond_net; int bond_arp_rcv(const struct sk_buff *skb, struct bonding *bond, struct slave *slave); -struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr); int bond_dev_queue_xmit(struct bonding *bond, struct sk_buff *skb, struct net_device *slave_dev); void bond_xmit_slave_id(struct bonding *bond, struct sk_buff *skb, int slave_id); int bond_create(struct net *net, const char *name); int bond_create_sysfs(struct bond_net *net); void bond_destroy_sysfs(struct bond_net *net); void bond_prepare_sysfs_group(struct bonding *bond); -int bond_create_slave_symlinks(struct net_device *master, struct net_device *slave); -void bond_destroy_slave_symlinks(struct net_device *master, struct net_device *slave); int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev); int bond_release(struct net_device *bond_dev, struct net_device *slave_dev); void bond_mii_monitor(struct work_struct *); void bond_loadbalance_arp_mon(struct work_struct *); void bond_activebackup_arp_mon(struct work_struct *); -void bond_set_mode_ops(struct bonding *bond, int mode); +int bond_xmit_hash(struct bonding *bond, struct sk_buff *skb, int count); int bond_parse_parm(const char *mode_arg, const struct bond_parm_tbl *tbl); void bond_select_active_slave(struct bonding *bond); void bond_change_active_slave(struct bonding *bond, struct slave *new_active); @@ -456,6 +423,14 @@ void bond_debug_register(struct bonding *bond); void bond_debug_unregister(struct bonding *bond); void bond_debug_reregister(struct bonding *bond); const char *bond_mode_name(int mode); +void bond_setup(struct net_device *bond_dev); +unsigned int bond_get_num_tx_queues(void); +int bond_netlink_init(void); +void bond_netlink_fini(void); +int bond_option_mode_set(struct bonding *bond, int mode); +int bond_option_active_slave_set(struct bonding *bond, struct net_device *slave_dev); +struct net_device *bond_option_active_slave_get_rcu(struct bonding *bond); +struct net_device *bond_option_active_slave_get(struct bonding *bond); struct bond_net { struct net * net; /* Associated network namespace */ @@ -492,9 +467,24 @@ static inline void bond_destroy_proc_dir(struct bond_net *bn) static inline struct slave *bond_slave_has_mac(struct bonding *bond, const u8 *mac) { + struct list_head *iter; struct slave *tmp; - bond_for_each_slave(bond, tmp) + bond_for_each_slave(bond, tmp, iter) + if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr)) + return tmp; + + return NULL; +} + +/* Caller must hold rcu_read_lock() for read */ +static inline struct slave *bond_slave_has_mac_rcu(struct bonding *bond, + const u8 *mac) +{ + struct list_head *iter; + struct slave *tmp; + + bond_for_each_slave_rcu(bond, tmp, iter) if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr)) return tmp; @@ -528,4 +518,7 @@ extern const struct bond_parm_tbl fail_over_mac_tbl[]; extern const struct bond_parm_tbl pri_reselect_tbl[]; extern struct bond_parm_tbl ad_select_tbl[]; +/* exported from bond_netlink.c */ +extern struct rtnl_link_ops bond_link_ops; + #endif /* _LINUX_BONDING_H */ diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index 693d8ffe465311b6aab964c25304f4bbdd04cdf0..cf0f63e14e5339d4a6ed324076a4cad7854dfc48 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -1347,7 +1347,7 @@ static int at91_can_probe(struct platform_device *pdev) priv->reg_base = addr; priv->devtype_data = *devtype_data; priv->clk = clk; - priv->pdata = pdev->dev.platform_data; + priv->pdata = dev_get_platdata(&pdev->dev); priv->mb0_id = 0x7ff; netif_napi_add(dev, &priv->napi, at91_poll, get_mb_rx_num(priv)); diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c index a2700d25ff0ed87fb800d4c157aeef911234d26e..8a0b515b33ea57c5804f1c9a82b774be8aadee7f 100644 --- a/drivers/net/can/bfin_can.c +++ b/drivers/net/can/bfin_can.c @@ -539,7 +539,7 @@ static int bfin_can_probe(struct platform_device *pdev) struct resource *res_mem, *rx_irq, *tx_irq, *err_irq; unsigned short *pdata; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "No platform data provided!\n"); err = -EINVAL; diff --git a/drivers/net/can/c_can/c_can_pci.c b/drivers/net/can/c_can/c_can_pci.c index b374be7891a296bde66696e19d2137a47d7ffea3..bce0be54c2f59587a2498d2f37821f2634b886d9 100644 --- a/drivers/net/can/c_can/c_can_pci.c +++ b/drivers/net/can/c_can/c_can_pci.c @@ -160,7 +160,6 @@ static int c_can_pci_probe(struct pci_dev *pdev, return 0; out_free_c_can: - pci_set_drvdata(pdev, NULL); free_c_can_dev(dev); out_iounmap: pci_iounmap(pdev, addr); @@ -181,7 +180,6 @@ static void c_can_pci_remove(struct pci_dev *pdev) unregister_c_can_dev(dev); - pci_set_drvdata(pdev, NULL); free_c_can_dev(dev); pci_iounmap(pdev, priv->base); diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index 294ced3cc227520883c6ebe50ac255634d0f0f7c..d66ac265269c68883070f7857b1cd6d928531f0e 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -322,7 +322,7 @@ static struct platform_driver c_can_plat_driver = { .driver = { .name = KBUILD_MODNAME, .owner = THIS_MODULE, - .of_match_table = of_match_ptr(c_can_of_table), + .of_match_table = c_can_of_table, }, .probe = c_can_plat_probe, .remove = c_can_plat_remove, diff --git a/drivers/net/can/cc770/cc770_platform.c b/drivers/net/can/cc770/cc770_platform.c index 034bdd816a60c74104b00b5b203c69f120f0555d..ad76734b3ecc79556ee4eb59e5c2025a10824b48 100644 --- a/drivers/net/can/cc770/cc770_platform.c +++ b/drivers/net/can/cc770/cc770_platform.c @@ -152,7 +152,7 @@ static int cc770_get_platform_data(struct platform_device *pdev, struct cc770_priv *priv) { - struct cc770_platform_data *pdata = pdev->dev.platform_data; + struct cc770_platform_data *pdata = dev_get_platdata(&pdev->dev); priv->can.clock.freq = pdata->osc_freq; if (priv->cpu_interface & CPUIF_DSC) @@ -203,7 +203,7 @@ static int cc770_platform_probe(struct platform_device *pdev) if (pdev->dev.of_node) err = cc770_get_of_node_data(pdev, priv); - else if (pdev->dev.platform_data) + else if (dev_get_platdata(&pdev->dev)) err = cc770_get_platform_data(pdev, priv); else err = -ENODEV; diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 1870c4731a572d193bef5e6dc0b7364e5f1751ed..bda1888cae9a9baa3034499c222e50cd8778958a 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -645,19 +645,6 @@ static int can_changelink(struct net_device *dev, /* We need synchronization with dev->stop() */ ASSERT_RTNL(); - if (data[IFLA_CAN_CTRLMODE]) { - struct can_ctrlmode *cm; - - /* Do not allow changing controller mode while running */ - if (dev->flags & IFF_UP) - return -EBUSY; - cm = nla_data(data[IFLA_CAN_CTRLMODE]); - if (cm->flags & ~priv->ctrlmode_supported) - return -EOPNOTSUPP; - priv->ctrlmode &= ~cm->mask; - priv->ctrlmode |= cm->flags; - } - if (data[IFLA_CAN_BITTIMING]) { struct can_bittiming bt; @@ -680,6 +667,19 @@ static int can_changelink(struct net_device *dev, } } + if (data[IFLA_CAN_CTRLMODE]) { + struct can_ctrlmode *cm; + + /* Do not allow changing controller mode while running */ + if (dev->flags & IFF_UP) + return -EBUSY; + cm = nla_data(data[IFLA_CAN_CTRLMODE]); + if (cm->flags & ~priv->ctrlmode_supported) + return -EOPNOTSUPP; + priv->ctrlmode &= ~cm->mask; + priv->ctrlmode |= cm->flags; + } + if (data[IFLA_CAN_RESTART_MS]) { /* Do not allow changing restart delay while running */ if (dev->flags & IFF_UP) @@ -702,17 +702,17 @@ static int can_changelink(struct net_device *dev, static size_t can_get_size(const struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); - size_t size; - - size = nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ - size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */ - size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ - size += nla_total_size(sizeof(struct can_bittiming)); /* IFLA_CAN_BITTIMING */ - size += nla_total_size(sizeof(struct can_clock)); /* IFLA_CAN_CLOCK */ - if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */ - size += nla_total_size(sizeof(struct can_berr_counter)); - if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */ + size_t size = 0; + + size += nla_total_size(sizeof(struct can_bittiming)); /* IFLA_CAN_BITTIMING */ + if (priv->bittiming_const) /* IFLA_CAN_BITTIMING_CONST */ size += nla_total_size(sizeof(struct can_bittiming_const)); + size += nla_total_size(sizeof(struct can_clock)); /* IFLA_CAN_CLOCK */ + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_STATE */ + size += nla_total_size(sizeof(struct can_ctrlmode)); /* IFLA_CAN_CTRLMODE */ + size += nla_total_size(sizeof(u32)); /* IFLA_CAN_RESTART_MS */ + if (priv->do_get_berr_counter) /* IFLA_CAN_BERR_COUNTER */ + size += nla_total_size(sizeof(struct can_berr_counter)); return size; } @@ -726,23 +726,20 @@ static int can_fill_info(struct sk_buff *skb, const struct net_device *dev) if (priv->do_get_state) priv->do_get_state(dev, &state); - if (nla_put_u32(skb, IFLA_CAN_STATE, state) || - nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) || - nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) || - nla_put(skb, IFLA_CAN_BITTIMING, + if (nla_put(skb, IFLA_CAN_BITTIMING, sizeof(priv->bittiming), &priv->bittiming) || + (priv->bittiming_const && + nla_put(skb, IFLA_CAN_BITTIMING_CONST, + sizeof(*priv->bittiming_const), priv->bittiming_const)) || nla_put(skb, IFLA_CAN_CLOCK, sizeof(cm), &priv->clock) || + nla_put_u32(skb, IFLA_CAN_STATE, state) || + nla_put(skb, IFLA_CAN_CTRLMODE, sizeof(cm), &cm) || + nla_put_u32(skb, IFLA_CAN_RESTART_MS, priv->restart_ms) || (priv->do_get_berr_counter && !priv->do_get_berr_counter(dev, &bec) && - nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec)) || - (priv->bittiming_const && - nla_put(skb, IFLA_CAN_BITTIMING_CONST, - sizeof(*priv->bittiming_const), priv->bittiming_const))) - goto nla_put_failure; + nla_put(skb, IFLA_CAN_BERR_COUNTER, sizeof(bec), &bec))) + return -EMSGSIZE; return 0; - -nla_put_failure: - return -EMSGSIZE; } static size_t can_get_xstats_size(const struct net_device *dev) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index 8f5ce747feb57a093dc78e4a24bd9742b5ea99fc..ae08cf129ebbb0bda31f4b74893574b9703253f0 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -1068,7 +1068,7 @@ static int flexcan_probe(struct platform_device *pdev) priv->dev = dev; priv->clk_ipg = clk_ipg; priv->clk_per = clk_per; - priv->pdata = pdev->dev.platform_data; + priv->pdata = dev_get_platdata(&pdev->dev); priv->devtype_data = devtype_data; priv->reg_xceiver = devm_regulator_get(&pdev->dev, "xceiver"); diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index 36bd6fa1c7f3e4760b5f69f65b420f8d55b66a5f..ab5909a7bae9efa378c0466936002460205f5cf1 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -1769,7 +1769,7 @@ static int ican3_probe(struct platform_device *pdev) struct device *dev; int ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) return -ENXIO; diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index fe7dd696957ea3d8c4f7e34b21baa866bb797b55..08ac401e0214e091bdf8b851990dd8b45e0bec89 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -999,7 +999,7 @@ static int mcp251x_can_probe(struct spi_device *spi) { struct net_device *net; struct mcp251x_priv *priv; - struct mcp251x_platform_data *pdata = spi->dev.platform_data; + struct mcp251x_platform_data *pdata = dev_get_platdata(&spi->dev); int ret = -ENODEV; if (!pdata) diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h index 9c24d60a23b1dbf459c75d14f7e3ac340350b9d0..e98abb97a0506672ab5cfb2af24fb5484fee61fe 100644 --- a/drivers/net/can/mscan/mscan.h +++ b/drivers/net/can/mscan/mscan.h @@ -297,8 +297,8 @@ struct mscan_priv { struct napi_struct napi; }; -extern struct net_device *alloc_mscandev(void); -extern int register_mscandev(struct net_device *dev, int mscan_clksrc); -extern void unregister_mscandev(struct net_device *dev); +struct net_device *alloc_mscandev(void); +int register_mscandev(struct net_device *dev, int mscan_clksrc); +void unregister_mscandev(struct net_device *dev); #endif /* __MSCAN_H__ */ diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c index 5c314a961970b0041c7776da283979f00c4114f8..5f0e9b3bfa7bb9a8f80f5267058b83c5db53f4c6 100644 --- a/drivers/net/can/pch_can.c +++ b/drivers/net/can/pch_can.c @@ -964,7 +964,6 @@ static void pch_can_remove(struct pci_dev *pdev) pci_disable_msi(priv->dev); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); pch_can_reset(priv); pci_iounmap(pdev, priv->regs); free_candev(priv->ndev); diff --git a/drivers/net/can/sja1000/ems_pci.c b/drivers/net/can/sja1000/ems_pci.c index 3752342a678ac5320e68bf69f747b4f69034758d..835921388e7ba6ab4b6ee98306c629691421d8a7 100644 --- a/drivers/net/can/sja1000/ems_pci.c +++ b/drivers/net/can/sja1000/ems_pci.c @@ -207,7 +207,6 @@ static void ems_pci_del_card(struct pci_dev *pdev) kfree(card); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static void ems_pci_card_reset(struct ems_pci_card *card) diff --git a/drivers/net/can/sja1000/kvaser_pci.c b/drivers/net/can/sja1000/kvaser_pci.c index 217585b97cd3e0851439f8b388c040058b668100..087b13bd300e845a6231d80cfbb29ab0d3c8d41f 100644 --- a/drivers/net/can/sja1000/kvaser_pci.c +++ b/drivers/net/can/sja1000/kvaser_pci.c @@ -387,7 +387,6 @@ static void kvaser_pci_remove_one(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static struct pci_driver kvaser_pci_driver = { diff --git a/drivers/net/can/sja1000/peak_pci.c b/drivers/net/can/sja1000/peak_pci.c index 6b6f0ad75090c4ea463ae49b8e2041dbdaca55df..065ca49eb45e72c48c9d1cc5f8fbda0256fdb195 100644 --- a/drivers/net/can/sja1000/peak_pci.c +++ b/drivers/net/can/sja1000/peak_pci.c @@ -744,8 +744,6 @@ static void peak_pci_remove(struct pci_dev *pdev) pci_iounmap(pdev, cfg_base); pci_release_regions(pdev); pci_disable_device(pdev); - - pci_set_drvdata(pdev, NULL); } static struct pci_driver peak_pci_driver = { diff --git a/drivers/net/can/sja1000/plx_pci.c b/drivers/net/can/sja1000/plx_pci.c index c52c1e96bf90741455eea01add4129fcfc590bad..f9b4f81cd86a4601abc8fbcb5dd7eaf7aaef81a5 100644 --- a/drivers/net/can/sja1000/plx_pci.c +++ b/drivers/net/can/sja1000/plx_pci.c @@ -477,7 +477,6 @@ static void plx_pci_del_card(struct pci_dev *pdev) kfree(card); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } /* diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c index 8e259c541036c575fc181796ebfe21f0c8798a1d..29f9b632118742eea67904bd445922b2773a894f 100644 --- a/drivers/net/can/sja1000/sja1000_platform.c +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -76,7 +76,7 @@ static int sp_probe(struct platform_device *pdev) struct resource *res_mem, *res_irq; struct sja1000_platform_data *pdata; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "No platform data provided!\n"); err = -ENODEV; diff --git a/drivers/net/can/softing/softing.h b/drivers/net/can/softing/softing.h index afd7d85b69155a4259748b2c2648bc2739835d38..35f062282dbdc278fbc9b040395f14935b7a1ac5 100644 --- a/drivers/net/can/softing/softing.h +++ b/drivers/net/can/softing/softing.h @@ -71,34 +71,34 @@ struct softing { } id; }; -extern int softing_default_output(struct net_device *netdev); +int softing_default_output(struct net_device *netdev); -extern ktime_t softing_raw2ktime(struct softing *card, u32 raw); +ktime_t softing_raw2ktime(struct softing *card, u32 raw); -extern int softing_chip_poweron(struct softing *card); +int softing_chip_poweron(struct softing *card); -extern int softing_bootloader_command(struct softing *card, int16_t cmd, - const char *msg); +int softing_bootloader_command(struct softing *card, int16_t cmd, + const char *msg); /* Load firmware after reset */ -extern int softing_load_fw(const char *file, struct softing *card, - __iomem uint8_t *virt, unsigned int size, int offset); +int softing_load_fw(const char *file, struct softing *card, + __iomem uint8_t *virt, unsigned int size, int offset); /* Load final application firmware after bootloader */ -extern int softing_load_app_fw(const char *file, struct softing *card); +int softing_load_app_fw(const char *file, struct softing *card); /* * enable or disable irq * only called with fw.lock locked */ -extern int softing_enable_irq(struct softing *card, int enable); +int softing_enable_irq(struct softing *card, int enable); /* start/stop 1 bus on card */ -extern int softing_startstop(struct net_device *netdev, int up); +int softing_startstop(struct net_device *netdev, int up); /* netif_rx() */ -extern int softing_netdev_rx(struct net_device *netdev, - const struct can_frame *msg, ktime_t ktime); +int softing_netdev_rx(struct net_device *netdev, const struct can_frame *msg, + ktime_t ktime); /* SOFTING DPRAM mappings */ #define DPRAM_RX 0x0000 diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c index 65eef1eea2e2434ca47cfeb086095a415d2b385e..6cd5c01b624d592e6fce2c543075822e5751561b 100644 --- a/drivers/net/can/softing/softing_main.c +++ b/drivers/net/can/softing/softing_main.c @@ -768,7 +768,7 @@ static int softing_pdev_remove(struct platform_device *pdev) static int softing_pdev_probe(struct platform_device *pdev) { - const struct softing_platform_data *pdat = pdev->dev.platform_data; + const struct softing_platform_data *pdat = dev_get_platdata(&pdev->dev); struct softing *card; struct net_device *netdev; struct softing_priv *priv; diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 3a349a22d5bc46eed31bdc32e12d27c25df3bd13..60d95b44d0f70018d5aee7769ae04ff60a4411d3 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -286,15 +286,6 @@ static inline u32 hecc_get_bit(struct ti_hecc_priv *priv, int reg, u32 bit_mask) return (hecc_read(priv, reg) & bit_mask) ? 1 : 0; } -static int ti_hecc_get_state(const struct net_device *ndev, - enum can_state *state) -{ - struct ti_hecc_priv *priv = netdev_priv(ndev); - - *state = priv->can.state; - return 0; -} - static int ti_hecc_set_btc(struct ti_hecc_priv *priv) { struct can_bittiming *bit_timing = &priv->can.bittiming; @@ -894,7 +885,7 @@ static int ti_hecc_probe(struct platform_device *pdev) void __iomem *addr; int err = -ENODEV; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "No platform data\n"); goto probe_exit; @@ -940,7 +931,6 @@ static int ti_hecc_probe(struct platform_device *pdev) priv->can.bittiming_const = &ti_hecc_bittiming_const; priv->can.do_set_mode = ti_hecc_do_set_mode; - priv->can.do_get_state = ti_hecc_get_state; priv->can.do_get_berr_counter = ti_hecc_get_berr_counter; priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig index f00c76377b446cb63999857138e4f0365eccc015..65b735d4a6ad8aa92dc4cea9490f3f14fe9bbab8 100644 --- a/drivers/net/ethernet/3com/Kconfig +++ b/drivers/net/ethernet/3com/Kconfig @@ -35,7 +35,7 @@ config EL3 config 3C515 tristate "3c515 ISA \"Fast EtherLink\"" - depends on (ISA || EISA) && ISA_DMA_API + depends on ISA && ISA_DMA_API ---help--- If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet network card, say Y and read the Ethernet-HOWTO, available from @@ -70,7 +70,7 @@ config VORTEX select MII ---help--- This option enables driver support for a large number of 10Mbps and - 10/100Mbps EISA, PCI and PCMCIA 3Com network cards: + 10/100Mbps EISA, PCI and Cardbus 3Com network cards: "Vortex" (Fast EtherLink 3c590/3c592/3c595/3c597) EISA and PCI "Boomerang" (EtherLink XL 3c900 or 3c905) PCI diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index 144942f6372b68edd1bedc91175b8feab07cbe6e..465cc7108d8a5e5bbb2c34df44f7bd0329d02d6b 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -2525,7 +2525,6 @@ typhoon_remove_one(struct pci_dev *pdev) pci_release_regions(pdev); pci_clear_mwi(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(dev); } diff --git a/drivers/net/ethernet/8390/8390.h b/drivers/net/ethernet/8390/8390.h index ef325ffa1b5ac63dd61dd0c0230c8a63a1a4a6cf..2923c51bb351c38e2cc7d728056bdcb6f5c25a97 100644 --- a/drivers/net/ethernet/8390/8390.h +++ b/drivers/net/ethernet/8390/8390.h @@ -28,42 +28,42 @@ extern int ei_debug; #endif #ifdef CONFIG_NET_POLL_CONTROLLER -extern void ei_poll(struct net_device *dev); -extern void eip_poll(struct net_device *dev); +void ei_poll(struct net_device *dev); +void eip_poll(struct net_device *dev); #endif /* Without I/O delay - non ISA or later chips */ -extern void NS8390_init(struct net_device *dev, int startp); -extern int ei_open(struct net_device *dev); -extern int ei_close(struct net_device *dev); -extern irqreturn_t ei_interrupt(int irq, void *dev_id); -extern void ei_tx_timeout(struct net_device *dev); -extern netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev); -extern void ei_set_multicast_list(struct net_device *dev); -extern struct net_device_stats *ei_get_stats(struct net_device *dev); +void NS8390_init(struct net_device *dev, int startp); +int ei_open(struct net_device *dev); +int ei_close(struct net_device *dev); +irqreturn_t ei_interrupt(int irq, void *dev_id); +void ei_tx_timeout(struct net_device *dev); +netdev_tx_t ei_start_xmit(struct sk_buff *skb, struct net_device *dev); +void ei_set_multicast_list(struct net_device *dev); +struct net_device_stats *ei_get_stats(struct net_device *dev); extern const struct net_device_ops ei_netdev_ops; -extern struct net_device *__alloc_ei_netdev(int size); +struct net_device *__alloc_ei_netdev(int size); static inline struct net_device *alloc_ei_netdev(void) { return __alloc_ei_netdev(0); } /* With I/O delay form */ -extern void NS8390p_init(struct net_device *dev, int startp); -extern int eip_open(struct net_device *dev); -extern int eip_close(struct net_device *dev); -extern irqreturn_t eip_interrupt(int irq, void *dev_id); -extern void eip_tx_timeout(struct net_device *dev); -extern netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev); -extern void eip_set_multicast_list(struct net_device *dev); -extern struct net_device_stats *eip_get_stats(struct net_device *dev); +void NS8390p_init(struct net_device *dev, int startp); +int eip_open(struct net_device *dev); +int eip_close(struct net_device *dev); +irqreturn_t eip_interrupt(int irq, void *dev_id); +void eip_tx_timeout(struct net_device *dev); +netdev_tx_t eip_start_xmit(struct sk_buff *skb, struct net_device *dev); +void eip_set_multicast_list(struct net_device *dev); +struct net_device_stats *eip_get_stats(struct net_device *dev); extern const struct net_device_ops eip_netdev_ops; -extern struct net_device *__alloc_eip_netdev(int size); +struct net_device *__alloc_eip_netdev(int size); static inline struct net_device *alloc_eip_netdev(void) { return __alloc_eip_netdev(0); diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index f92f001551dab53152bf276d189e88b1f984e3b5..36fa577970bbae8741254519c0450d497a286d7e 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -702,7 +702,7 @@ static int ax_init_dev(struct net_device *dev) for (i = 0; i < 16; i++) SA_prom[i] = SA_prom[i+i]; - memcpy(dev->dev_addr, SA_prom, 6); + memcpy(dev->dev_addr, SA_prom, ETH_ALEN); } #ifdef CONFIG_AX88796_93CX6 diff --git a/drivers/net/ethernet/8390/ne2k-pci.c b/drivers/net/ethernet/8390/ne2k-pci.c index 92201080e07a104b4e25d2f308a7b62d8f0099ba..fc14a85e4d5ffdde31d42c40dac5f6af7e80c946 100644 --- a/drivers/net/ethernet/8390/ne2k-pci.c +++ b/drivers/net/ethernet/8390/ne2k-pci.c @@ -389,9 +389,7 @@ err_out_free_netdev: free_netdev (dev); err_out_free_res: release_region (ioaddr, NE_IO_EXTENT); - pci_set_drvdata (pdev, NULL); return -ENODEV; - } /* @@ -655,7 +653,6 @@ static void ne2k_pci_remove_one(struct pci_dev *pdev) release_region(dev->base_addr, NE_IO_EXTENT); free_netdev(dev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM diff --git a/drivers/net/ethernet/adaptec/starfire.c b/drivers/net/ethernet/adaptec/starfire.c index 8b04bfc20cfba3b9c17984471dfccb6163799bdb..171d73c1d3c22de3209ca6c48b7978c35b2a38b0 100644 --- a/drivers/net/ethernet/adaptec/starfire.c +++ b/drivers/net/ethernet/adaptec/starfire.c @@ -835,7 +835,6 @@ static int starfire_init_one(struct pci_dev *pdev, return 0; err_out_cleardev: - pci_set_drvdata(pdev, NULL); iounmap(base); err_out_free_res: pci_release_regions (pdev); @@ -2012,7 +2011,6 @@ static void starfire_remove_one(struct pci_dev *pdev) iounmap(np->base); pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(dev); /* Will also free np!! */ } diff --git a/drivers/net/ethernet/adi/bfin_mac.h b/drivers/net/ethernet/adi/bfin_mac.h index 7a07ee07906b280f2d849ae0959c604ee58d411b..6dec86ac97cdbfb26977c382d73cf6a849582895 100644 --- a/drivers/net/ethernet/adi/bfin_mac.h +++ b/drivers/net/ethernet/adi/bfin_mac.h @@ -104,6 +104,6 @@ struct bfin_mac_local { #endif }; -extern int bfin_get_ether_addr(char *addr); +int bfin_get_ether_addr(char *addr); #endif diff --git a/drivers/net/ethernet/amd/7990.h b/drivers/net/ethernet/amd/7990.h index 0a5837b9642194799bedaf26abf3721bafb94579..ae33a99bf47626695b239cdb78941ea09b723b02 100644 --- a/drivers/net/ethernet/amd/7990.h +++ b/drivers/net/ethernet/amd/7990.h @@ -242,13 +242,13 @@ struct lance_private #define LANCE_ADDR(x) ((int)(x) & ~0xff000000) /* Now the prototypes we export */ -extern int lance_open(struct net_device *dev); -extern int lance_close (struct net_device *dev); -extern int lance_start_xmit (struct sk_buff *skb, struct net_device *dev); -extern void lance_set_multicast (struct net_device *dev); -extern void lance_tx_timeout(struct net_device *dev); +int lance_open(struct net_device *dev); +int lance_close (struct net_device *dev); +int lance_start_xmit (struct sk_buff *skb, struct net_device *dev); +void lance_set_multicast (struct net_device *dev); +void lance_tx_timeout(struct net_device *dev); #ifdef CONFIG_NET_POLL_CONTROLLER -extern void lance_poll(struct net_device *dev); +void lance_poll(struct net_device *dev); #endif #endif /* ndef _7990_H */ diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c index 1b1429d5d5c287f761a3066bc6bbebe6b74995ee..d042511bdc1365882a3bea08626bfce6bcc714da 100644 --- a/drivers/net/ethernet/amd/amd8111e.c +++ b/drivers/net/ethernet/amd/amd8111e.c @@ -1711,7 +1711,6 @@ static void amd8111e_remove_one(struct pci_dev *pdev) free_netdev(dev); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } } static void amd8111e_config_ipg(struct net_device* dev) @@ -1967,7 +1966,6 @@ err_free_reg: err_disable_pdev: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); return err; } diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c index 10ceca523fc0dc9556733d3c1eeded045ab70303..e07ce5ff2d48bf93e2a3daeb102fb4b78d471532 100644 --- a/drivers/net/ethernet/amd/atarilance.c +++ b/drivers/net/ethernet/amd/atarilance.c @@ -586,10 +586,10 @@ static unsigned long __init lance_probe1( struct net_device *dev, switch( lp->cardtype ) { case OLD_RIEBL: /* No ethernet address! (Set some default address) */ - memcpy( dev->dev_addr, OldRieblDefHwaddr, 6 ); + memcpy(dev->dev_addr, OldRieblDefHwaddr, ETH_ALEN); break; case NEW_RIEBL: - lp->memcpy_f( dev->dev_addr, RIEBL_HWADDR_ADDR, 6 ); + lp->memcpy_f(dev->dev_addr, RIEBL_HWADDR_ADDR, ETH_ALEN); break; case PAM_CARD: i = IO->eeprom; diff --git a/drivers/net/ethernet/amd/au1000_eth.c b/drivers/net/ethernet/amd/au1000_eth.c index 91d52b495848a17d4b9bae0de04b7d1f7c798ba9..427c148bb643538c1d2240036a9dd7cad1be9596 100644 --- a/drivers/net/ethernet/amd/au1000_eth.c +++ b/drivers/net/ethernet/amd/au1000_eth.c @@ -1138,7 +1138,7 @@ static int au1000_probe(struct platform_device *pdev) aup->phy1_search_mac0 = 1; } else { if (is_valid_ether_addr(pd->mac)) { - memcpy(dev->dev_addr, pd->mac, 6); + memcpy(dev->dev_addr, pd->mac, ETH_ALEN); } else { /* Set a random MAC since no valid provided by platform_data. */ eth_hw_addr_random(dev); diff --git a/drivers/net/ethernet/amd/declance.c b/drivers/net/ethernet/amd/declance.c index 1b89f1a9127eb42a845822d724ea4fc520a9fb1e..57397295887c964b23f2bbb7cf7d25d908ab2b29 100644 --- a/drivers/net/ethernet/amd/declance.c +++ b/drivers/net/ethernet/amd/declance.c @@ -344,8 +344,8 @@ static void cp_to_buf(const int type, void *to, const void *from, int len) } clen = len & 1; - rtp = tp; - rfp = fp; + rtp = (unsigned char *)tp; + rfp = (const unsigned char *)fp; while (clen--) { *rtp++ = *rfp++; } @@ -372,8 +372,8 @@ static void cp_to_buf(const int type, void *to, const void *from, int len) * do the rest, if any. */ clen = len & 15; - rtp = (unsigned char *) tp; - rfp = (unsigned char *) fp; + rtp = (unsigned char *)tp; + rfp = (const unsigned char *)fp; while (clen--) { *rtp++ = *rfp++; } @@ -403,8 +403,8 @@ static void cp_from_buf(const int type, void *to, const void *from, int len) clen = len & 1; - rtp = tp; - rfp = fp; + rtp = (unsigned char *)tp; + rfp = (const unsigned char *)fp; while (clen--) { *rtp++ = *rfp++; @@ -433,8 +433,8 @@ static void cp_from_buf(const int type, void *to, const void *from, int len) * do the rest, if any. */ clen = len & 15; - rtp = (unsigned char *) tp; - rfp = (unsigned char *) fp; + rtp = (unsigned char *)tp; + rfp = (const unsigned char *)fp; while (clen--) { *rtp++ = *rfp++; } diff --git a/drivers/net/ethernet/amd/lance.c b/drivers/net/ethernet/amd/lance.c index 5c728436b85e7df0cf9421fa6185e45c2b52bd3c..256f590f6bb1a6db167f4375b3ed2719d2ad5073 100644 --- a/drivers/net/ethernet/amd/lance.c +++ b/drivers/net/ethernet/amd/lance.c @@ -754,7 +754,7 @@ lance_open(struct net_device *dev) int i; if (dev->irq == 0 || - request_irq(dev->irq, lance_interrupt, 0, lp->name, dev)) { + request_irq(dev->irq, lance_interrupt, 0, dev->name, dev)) { return -EAGAIN; } diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index 2d8e28819779eed0388109791fe0126f5af763f8..38492e0b704e3aab926ec5fd300b61028223d504 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -1675,7 +1675,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) pr_cont(" warning: CSR address invalid,\n"); pr_info(" using instead PROM address of"); } - memcpy(dev->dev_addr, promaddr, 6); + memcpy(dev->dev_addr, promaddr, ETH_ALEN); } } @@ -2818,7 +2818,6 @@ static void pcnet32_remove_one(struct pci_dev *pdev) lp->init_block, lp->init_dma_addr); free_netdev(dev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } } diff --git a/drivers/net/ethernet/apple/bmac.c b/drivers/net/ethernet/apple/bmac.c index a597b766f0809d3b1e1893e9ec008fcdc813405f..daae0e01625360598194c76fcc4239a3283bc85c 100644 --- a/drivers/net/ethernet/apple/bmac.c +++ b/drivers/net/ethernet/apple/bmac.c @@ -1220,8 +1220,8 @@ static void bmac_reset_and_enable(struct net_device *dev) if (skb != NULL) { data = skb_put(skb, ETHERMINPACKET); memset(data, 0, ETHERMINPACKET); - memcpy(data, dev->dev_addr, 6); - memcpy(data+6, dev->dev_addr, 6); + memcpy(data, dev->dev_addr, ETH_ALEN); + memcpy(data + ETH_ALEN, dev->dev_addr, ETH_ALEN); bmac_transmit_packet(skb, dev); } spin_unlock_irqrestore(&bp->lock, flags); diff --git a/drivers/net/ethernet/apple/macmace.c b/drivers/net/ethernet/apple/macmace.c index 4ce8ceb6220584c1e1af51217f91cfa97f4e5783..58a200df4c35ad4745125260db3d38a5ccb381b1 100644 --- a/drivers/net/ethernet/apple/macmace.c +++ b/drivers/net/ethernet/apple/macmace.c @@ -211,6 +211,7 @@ static int mace_probe(struct platform_device *pdev) mp = netdev_priv(dev); mp->device = &pdev->dev; + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); dev->base_addr = (u32)MACE_BASE; diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c index d0878526c0c86c95d9c238f079c6564ca16c48bc..b2ffad1304d221ef54e60cd1a82138a4d8344975 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -671,6 +671,7 @@ static int arc_emac_probe(struct platform_device *pdev) if (!ndev) return -ENOMEM; + platform_set_drvdata(pdev, ndev); SET_NETDEV_DEV(ndev, &pdev->dev); ndev->netdev_ops = &arc_emac_netdev_ops; @@ -725,10 +726,10 @@ static int arc_emac_probe(struct platform_device *pdev) /* Get MAC address from device tree */ mac_addr = of_get_mac_address(pdev->dev.of_node); - if (!mac_addr || !is_valid_ether_addr(mac_addr)) - eth_hw_addr_random(ndev); - else + if (mac_addr) memcpy(ndev->dev_addr, mac_addr, ETH_ALEN); + else + eth_hw_addr_random(ndev); dev_info(&pdev->dev, "MAC address is now %pM\n", ndev->dev_addr); diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index fc95b235e210d058545efd889537c2a8dea08a45..5aa5e8146496ba807ece60fe5b287d70a79a8ad4 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -1367,7 +1367,6 @@ static void alx_remove(struct pci_dev *pdev) pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(alx->dev); } diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c.h b/drivers/net/ethernet/atheros/atl1c/atl1c.h index 0f0556526ba90bff7f764c4005d6f6e850220dd7..7f9369a3b378320762c89cc9369964d265fda6c9 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c.h +++ b/drivers/net/ethernet/atheros/atl1c/atl1c.h @@ -600,7 +600,7 @@ struct atl1c_adapter { extern char atl1c_driver_name[]; extern char atl1c_driver_version[]; -extern void atl1c_reinit_locked(struct atl1c_adapter *adapter); -extern s32 atl1c_reset_hw(struct atl1c_hw *hw); -extern void atl1c_set_ethtool_ops(struct net_device *netdev); +void atl1c_reinit_locked(struct atl1c_adapter *adapter); +s32 atl1c_reset_hw(struct atl1c_hw *hw); +void atl1c_set_ethtool_ops(struct net_device *netdev); #endif /* _ATL1C_H_ */ diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c index 3ef7092e3f1c5ff0fd31cb53a0253a0c7532d711..1cda49a28f7f0e9a672ae775a832eba31ad76fb3 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c @@ -153,7 +153,7 @@ static int atl1c_get_permanent_address(struct atl1c_hw *hw) bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value) { int i; - int ret = false; + bool ret = false; u32 otp_ctrl_data; u32 control; u32 data; diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e.h b/drivers/net/ethernet/atheros/atl1e/atl1e.h index b5fd934585e9f9f14595f60a2ec2e069cd2bed35..1b0fe2d04a0e88fcb8ac5e250e33384d66c0c68f 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e.h +++ b/drivers/net/ethernet/atheros/atl1e/atl1e.h @@ -499,10 +499,10 @@ struct atl1e_adapter { extern char atl1e_driver_name[]; extern char atl1e_driver_version[]; -extern void atl1e_check_options(struct atl1e_adapter *adapter); -extern int atl1e_up(struct atl1e_adapter *adapter); -extern void atl1e_down(struct atl1e_adapter *adapter); -extern void atl1e_reinit_locked(struct atl1e_adapter *adapter); -extern s32 atl1e_reset_hw(struct atl1e_hw *hw); -extern void atl1e_set_ethtool_ops(struct net_device *netdev); +void atl1e_check_options(struct atl1e_adapter *adapter); +int atl1e_up(struct atl1e_adapter *adapter); +void atl1e_down(struct atl1e_adapter *adapter); +void atl1e_reinit_locked(struct atl1e_adapter *adapter); +s32 atl1e_reset_hw(struct atl1e_hw *hw); +void atl1e_set_ethtool_ops(struct net_device *netdev); #endif /* _ATL1_E_H_ */ diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index 1966444590f6192e862a713216182ab09052ebef..7a73f3a9fcb5e2a5b0ffbac1ed1d0bc0163769cb 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -313,6 +313,34 @@ static void atl1e_set_multi(struct net_device *netdev) } } +static void __atl1e_rx_mode(netdev_features_t features, u32 *mac_ctrl_data) +{ + + if (features & NETIF_F_RXALL) { + /* enable RX of ALL frames */ + *mac_ctrl_data |= MAC_CTRL_DBG; + } else { + /* disable RX of ALL frames */ + *mac_ctrl_data &= ~MAC_CTRL_DBG; + } +} + +static void atl1e_rx_mode(struct net_device *netdev, + netdev_features_t features) +{ + struct atl1e_adapter *adapter = netdev_priv(netdev); + u32 mac_ctrl_data = 0; + + netdev_dbg(adapter->netdev, "%s\n", __func__); + + atl1e_irq_disable(adapter); + mac_ctrl_data = AT_READ_REG(&adapter->hw, REG_MAC_CTRL); + __atl1e_rx_mode(features, &mac_ctrl_data); + AT_WRITE_REG(&adapter->hw, REG_MAC_CTRL, mac_ctrl_data); + atl1e_irq_enable(adapter); +} + + static void __atl1e_vlan_mode(netdev_features_t features, u32 *mac_ctrl_data) { if (features & NETIF_F_HW_VLAN_CTAG_RX) { @@ -394,6 +422,10 @@ static int atl1e_set_features(struct net_device *netdev, if (changed & NETIF_F_HW_VLAN_CTAG_RX) atl1e_vlan_mode(netdev, features); + if (changed & NETIF_F_RXALL) + atl1e_rx_mode(netdev, features); + + return 0; } @@ -1057,7 +1089,8 @@ static void atl1e_setup_mac_ctrl(struct atl1e_adapter *adapter) value |= MAC_CTRL_PROMIS_EN; if (netdev->flags & IFF_ALLMULTI) value |= MAC_CTRL_MC_ALL_EN; - + if (netdev->features & NETIF_F_RXALL) + value |= MAC_CTRL_DBG; AT_WRITE_REG(hw, REG_MAC_CTRL, value); } @@ -1405,7 +1438,8 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que, rx_page_desc[que].rx_nxseq++; /* error packet */ - if (prrs->pkt_flag & RRS_IS_ERR_FRAME) { + if ((prrs->pkt_flag & RRS_IS_ERR_FRAME) && + !(netdev->features & NETIF_F_RXALL)) { if (prrs->err_flag & (RRS_ERR_BAD_CRC | RRS_ERR_DRIBBLE | RRS_ERR_CODE | RRS_ERR_TRUNC)) { @@ -1418,7 +1452,10 @@ static void atl1e_clean_rx_irq(struct atl1e_adapter *adapter, u8 que, } packet_size = ((prrs->word1 >> RRS_PKT_SIZE_SHIFT) & - RRS_PKT_SIZE_MASK) - 4; /* CRC */ + RRS_PKT_SIZE_MASK); + if (likely(!(netdev->features & NETIF_F_RXFCS))) + packet_size -= 4; /* CRC */ + skb = netdev_alloc_skb_ip_align(netdev, packet_size); if (skb == NULL) goto skip_pkt; @@ -2245,7 +2282,8 @@ static int atl1e_init_netdev(struct net_device *netdev, struct pci_dev *pdev) NETIF_F_HW_VLAN_CTAG_RX; netdev->features = netdev->hw_features | NETIF_F_LLTX | NETIF_F_HW_VLAN_CTAG_TX; - + /* not enabled by default */ + netdev->hw_features |= NETIF_F_RXALL | NETIF_F_RXFCS; return 0; } diff --git a/drivers/net/ethernet/atheros/atlx/atl2.h b/drivers/net/ethernet/atheros/atlx/atl2.h index 3ebe19f7242b6da9cd5d3290674f85cb9e164013..2f27d4c4c3ad4ae5b026983dba7d9478cd1c5430 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.h +++ b/drivers/net/ethernet/atheros/atlx/atl2.h @@ -42,7 +42,7 @@ #include "atlx.h" #ifdef ETHTOOL_OPS_COMPAT -extern int ethtool_ioctl(struct ifreq *ifr); +int ethtool_ioctl(struct ifreq *ifr); #endif #define PCI_COMMAND_REGISTER PCI_COMMAND diff --git a/drivers/net/ethernet/broadcom/b44.c b/drivers/net/ethernet/broadcom/b44.c index 9b017d9c58e94e5ab671aeda2cfcc969cc561c6e..079a597fa20cd95216947263115437af86fef0f0 100644 --- a/drivers/net/ethernet/broadcom/b44.c +++ b/drivers/net/ethernet/broadcom/b44.c @@ -596,6 +596,7 @@ static void b44_timer(unsigned long __opaque) static void b44_tx(struct b44 *bp) { u32 cur, cons; + unsigned bytes_compl = 0, pkts_compl = 0; cur = br32(bp, B44_DMATX_STAT) & DMATX_STAT_CDMASK; cur /= sizeof(struct dma_desc); @@ -612,9 +613,14 @@ static void b44_tx(struct b44 *bp) skb->len, DMA_TO_DEVICE); rp->skb = NULL; + + bytes_compl += skb->len; + pkts_compl++; + dev_kfree_skb_irq(skb); } + netdev_completed_queue(bp->dev, pkts_compl, bytes_compl); bp->tx_cons = cons; if (netif_queue_stopped(bp->dev) && TX_BUFFS_AVAIL(bp) > B44_TX_WAKEUP_THRESH) @@ -1018,6 +1024,8 @@ static netdev_tx_t b44_start_xmit(struct sk_buff *skb, struct net_device *dev) if (bp->flags & B44_FLAG_REORDER_BUG) br32(bp, B44_DMATX_PTR); + netdev_sent_queue(dev, skb->len); + if (TX_BUFFS_AVAIL(bp) < 1) netif_stop_queue(dev); @@ -1416,6 +1424,8 @@ static void b44_init_hw(struct b44 *bp, int reset_kind) val = br32(bp, B44_ENET_CTRL); bw32(bp, B44_ENET_CTRL, (val | ENET_CTRL_ENABLE)); + + netdev_reset_queue(bp->dev); } static int b44_open(struct net_device *dev) @@ -2101,7 +2111,7 @@ static int b44_get_invariants(struct b44 *bp) * valid PHY address. */ bp->phy_addr &= 0x1F; - memcpy(bp->dev->dev_addr, addr, 6); + memcpy(bp->dev->dev_addr, addr, ETH_ALEN); if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){ pr_err("Invalid MAC address found in EEPROM\n"); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 9e8a3e024e0150e71b77057c83c6aa3bdf1aabcf..e2aa09ce6af7702d6d8a9063e9841f3c4f50c830 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -149,6 +149,8 @@ static netdev_tx_t bgmac_dma_tx_add(struct bgmac *bgmac, dma_desc->ctl0 = cpu_to_le32(ctl0); dma_desc->ctl1 = cpu_to_le32(ctl1); + netdev_sent_queue(net_dev, skb->len); + wmb(); /* Increase ring->end to point empty slot. We tell hardware the first @@ -178,6 +180,7 @@ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring) struct device *dma_dev = bgmac->core->dma_dev; int empty_slot; bool freed = false; + unsigned bytes_compl = 0, pkts_compl = 0; /* The last slot that hardware didn't consume yet */ empty_slot = bgmac_read(bgmac, ring->mmio_base + BGMAC_DMA_TX_STATUS); @@ -195,6 +198,9 @@ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring) slot->skb->len, DMA_TO_DEVICE); slot->dma_addr = 0; + bytes_compl += slot->skb->len; + pkts_compl++; + /* Free memory! :) */ dev_kfree_skb(slot->skb); slot->skb = NULL; @@ -208,6 +214,8 @@ static void bgmac_dma_tx_free(struct bgmac *bgmac, struct bgmac_dma_ring *ring) freed = true; } + netdev_completed_queue(bgmac->net_dev, pkts_compl, bytes_compl); + if (freed && netif_queue_stopped(bgmac->net_dev)) netif_wake_queue(bgmac->net_dev); } @@ -277,6 +285,26 @@ static int bgmac_dma_rx_skb_for_slot(struct bgmac *bgmac, return 0; } +static void bgmac_dma_rx_setup_desc(struct bgmac *bgmac, + struct bgmac_dma_ring *ring, int desc_idx) +{ + struct bgmac_dma_desc *dma_desc = ring->cpu_base + desc_idx; + u32 ctl0 = 0, ctl1 = 0; + + if (desc_idx == ring->num_slots - 1) + ctl0 |= BGMAC_DESC_CTL0_EOT; + ctl1 |= BGMAC_RX_BUF_SIZE & BGMAC_DESC_CTL1_LEN; + /* Is there any BGMAC device that requires extension? */ + /* ctl1 |= (addrext << B43_DMA64_DCTL1_ADDREXT_SHIFT) & + * B43_DMA64_DCTL1_ADDREXT_MASK; + */ + + dma_desc->addr_low = cpu_to_le32(lower_32_bits(ring->slots[desc_idx].dma_addr)); + dma_desc->addr_high = cpu_to_le32(upper_32_bits(ring->slots[desc_idx].dma_addr)); + dma_desc->ctl0 = cpu_to_le32(ctl0); + dma_desc->ctl1 = cpu_to_le32(ctl1); +} + static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring, int weight) { @@ -295,7 +323,6 @@ static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring, struct device *dma_dev = bgmac->core->dma_dev; struct bgmac_slot_info *slot = &ring->slots[ring->start]; struct sk_buff *skb = slot->skb; - struct sk_buff *new_skb; struct bgmac_rx_header *rx; u16 len, flags; @@ -308,38 +335,51 @@ static int bgmac_dma_rx_read(struct bgmac *bgmac, struct bgmac_dma_ring *ring, len = le16_to_cpu(rx->len); flags = le16_to_cpu(rx->flags); - /* Check for poison and drop or pass the packet */ - if (len == 0xdead && flags == 0xbeef) { - bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n", - ring->start); - } else { + do { + dma_addr_t old_dma_addr = slot->dma_addr; + int err; + + /* Check for poison and drop or pass the packet */ + if (len == 0xdead && flags == 0xbeef) { + bgmac_err(bgmac, "Found poisoned packet at slot %d, DMA issue!\n", + ring->start); + dma_sync_single_for_device(dma_dev, + slot->dma_addr, + BGMAC_RX_BUF_SIZE, + DMA_FROM_DEVICE); + break; + } + /* Omit CRC. */ len -= ETH_FCS_LEN; - new_skb = netdev_alloc_skb_ip_align(bgmac->net_dev, len); - if (new_skb) { - skb_put(new_skb, len); - skb_copy_from_linear_data_offset(skb, BGMAC_RX_FRAME_OFFSET, - new_skb->data, - len); - skb_checksum_none_assert(skb); - new_skb->protocol = - eth_type_trans(new_skb, bgmac->net_dev); - netif_receive_skb(new_skb); - handled++; - } else { - bgmac->net_dev->stats.rx_dropped++; - bgmac_err(bgmac, "Allocation of skb for copying packet failed!\n"); + /* Prepare new skb as replacement */ + err = bgmac_dma_rx_skb_for_slot(bgmac, slot); + if (err) { + /* Poison the old skb */ + rx->len = cpu_to_le16(0xdead); + rx->flags = cpu_to_le16(0xbeef); + + dma_sync_single_for_device(dma_dev, + slot->dma_addr, + BGMAC_RX_BUF_SIZE, + DMA_FROM_DEVICE); + break; } + bgmac_dma_rx_setup_desc(bgmac, ring, ring->start); - /* Poison the old skb */ - rx->len = cpu_to_le16(0xdead); - rx->flags = cpu_to_le16(0xbeef); - } + /* Unmap old skb, we'll pass it to the netfif */ + dma_unmap_single(dma_dev, old_dma_addr, + BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE); - /* Make it back accessible to the hardware */ - dma_sync_single_for_device(dma_dev, slot->dma_addr, - BGMAC_RX_BUF_SIZE, DMA_FROM_DEVICE); + skb_put(skb, BGMAC_RX_FRAME_OFFSET + len); + skb_pull(skb, BGMAC_RX_FRAME_OFFSET); + + skb_checksum_none_assert(skb); + skb->protocol = eth_type_trans(skb, bgmac->net_dev); + netif_receive_skb(skb); + handled++; + } while (0); if (++ring->start >= BGMAC_RX_RING_SLOTS) ring->start = 0; @@ -503,8 +543,6 @@ err_dma_free: static void bgmac_dma_init(struct bgmac *bgmac) { struct bgmac_dma_ring *ring; - struct bgmac_dma_desc *dma_desc; - u32 ctl0, ctl1; int i; for (i = 0; i < BGMAC_MAX_TX_RINGS; i++) { @@ -537,23 +575,8 @@ static void bgmac_dma_init(struct bgmac *bgmac) if (ring->unaligned) bgmac_dma_rx_enable(bgmac, ring); - for (j = 0, dma_desc = ring->cpu_base; j < ring->num_slots; - j++, dma_desc++) { - ctl0 = ctl1 = 0; - - if (j == ring->num_slots - 1) - ctl0 |= BGMAC_DESC_CTL0_EOT; - ctl1 |= BGMAC_RX_BUF_SIZE & BGMAC_DESC_CTL1_LEN; - /* Is there any BGMAC device that requires extension? */ - /* ctl1 |= (addrext << B43_DMA64_DCTL1_ADDREXT_SHIFT) & - * B43_DMA64_DCTL1_ADDREXT_MASK; - */ - - dma_desc->addr_low = cpu_to_le32(lower_32_bits(ring->slots[j].dma_addr)); - dma_desc->addr_high = cpu_to_le32(upper_32_bits(ring->slots[j].dma_addr)); - dma_desc->ctl0 = cpu_to_le32(ctl0); - dma_desc->ctl1 = cpu_to_le32(ctl1); - } + for (j = 0; j < ring->num_slots; j++) + bgmac_dma_rx_setup_desc(bgmac, ring, j); bgmac_write(bgmac, ring->mmio_base + BGMAC_DMA_RX_INDEX, ring->index_base + @@ -996,6 +1019,8 @@ static void bgmac_chip_reset(struct bgmac *bgmac) bgmac_miiconfig(bgmac); bgmac_phy_init(bgmac); + netdev_reset_queue(bgmac->net_dev); + bgmac->int_status = 0; } diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index e838a3f74b696dd208680498f3350a3349a65392..d9980ad00b4b8d65bc23061fd71d28e936d48cb1 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -5761,8 +5761,8 @@ bnx2_run_loopback(struct bnx2 *bp, int loopback_mode) if (!skb) return -ENOMEM; packet = skb_put(skb, pkt_size); - memcpy(packet, bp->dev->dev_addr, 6); - memset(packet + 6, 0x0, 8); + memcpy(packet, bp->dev->dev_addr, ETH_ALEN); + memset(packet + ETH_ALEN, 0x0, 8); for (i = 14; i < pkt_size; i++) packet[i] = (unsigned char) (i & 0xff); @@ -8413,7 +8413,6 @@ err_out_release: err_out_disable: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); err_out: return rc; @@ -8514,7 +8513,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_drvdata(pdev, dev); - memcpy(dev->dev_addr, bp->mac_addr, 6); + memcpy(dev->dev_addr, bp->mac_addr, ETH_ALEN); dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO_ECN | @@ -8546,7 +8545,6 @@ error: pci_iounmap(pdev, bp->regview); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); err_free: free_netdev(dev); return rc; @@ -8578,7 +8576,6 @@ bnx2_remove_one(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static int diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index c5e375ddd6c09c137232ecfc8264439bc614e08e..4e01c57d8c8de349da61e80cb0e4cb0f4aece3fd 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -1546,6 +1546,7 @@ struct bnx2x { #define IS_VF_FLAG (1 << 22) #define INTERRUPTS_ENABLED_FLAG (1 << 23) #define BC_SUPPORTS_RMMOD_CMD (1 << 24) +#define HAS_PHYS_PORT_ID (1 << 25) #define BP_NOMCP(bp) ((bp)->flags & NO_MCP_FLAG) @@ -1876,6 +1877,8 @@ struct bnx2x { u32 dump_preset_idx; bool stats_started; struct semaphore stats_sema; + + u8 phys_port_id[ETH_ALEN]; }; /* Tx queues may be less or equal to Rx queues */ @@ -2232,7 +2235,7 @@ void bnx2x_igu_clear_sb_gen(struct bnx2x *bp, u8 func, u8 idu_sb_id, #define BNX2X_NUM_TESTS_SF 7 #define BNX2X_NUM_TESTS_MF 3 #define BNX2X_NUM_TESTS(bp) (IS_MF(bp) ? BNX2X_NUM_TESTS_MF : \ - BNX2X_NUM_TESTS_SF) + IS_VF(bp) ? 0 : BNX2X_NUM_TESTS_SF) #define BNX2X_PHY_LOOPBACK 0 #define BNX2X_MAC_LOOPBACK 1 @@ -2492,12 +2495,6 @@ enum { #define NUM_MACS 8 -enum bnx2x_pci_bus_speed { - BNX2X_PCI_LINK_SPEED_2500 = 2500, - BNX2X_PCI_LINK_SPEED_5000 = 5000, - BNX2X_PCI_LINK_SPEED_8000 = 8000 -}; - void bnx2x_set_local_cmng(struct bnx2x *bp); #define MCPR_SCRATCH_BASE(bp) \ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 74d6486fccfde2cba3128410a9d8b59c79a98918..dcafbda3e5be1035034d61c486f4eefceb6887f6 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -3256,14 +3256,16 @@ static u32 bnx2x_xmit_type(struct bnx2x *bp, struct sk_buff *skb) if (prot == IPPROTO_TCP) rc |= XMIT_CSUM_TCP; - if (skb_is_gso_v6(skb)) { - rc |= (XMIT_GSO_V6 | XMIT_CSUM_TCP); - if (rc & XMIT_CSUM_ENC) - rc |= XMIT_GSO_ENC_V6; - } else if (skb_is_gso(skb)) { - rc |= (XMIT_GSO_V4 | XMIT_CSUM_TCP); - if (rc & XMIT_CSUM_ENC) - rc |= XMIT_GSO_ENC_V4; + if (skb_is_gso(skb)) { + if (skb_is_gso_v6(skb)) { + rc |= (XMIT_GSO_V6 | XMIT_CSUM_TCP); + if (rc & XMIT_CSUM_ENC) + rc |= XMIT_GSO_ENC_V6; + } else { + rc |= (XMIT_GSO_V4 | XMIT_CSUM_TCP); + if (rc & XMIT_CSUM_ENC) + rc |= XMIT_GSO_ENC_V4; + } } return rc; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index e8efa1c93ffecdefde5183e91cbf67d7286928af..32d0f1435fb410b54c3105236c14a211f35bc0b2 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -639,6 +639,9 @@ static int bnx2x_get_regs_len(struct net_device *dev) struct bnx2x *bp = netdev_priv(dev); int regdump_len = 0; + if (IS_VF(bp)) + return 0; + regdump_len = __bnx2x_get_regs_len(bp); regdump_len *= 4; regdump_len += sizeof(struct dump_header); @@ -2864,9 +2867,16 @@ static void bnx2x_self_test(struct net_device *dev, memset(buf, 0, sizeof(u64) * BNX2X_NUM_TESTS(bp)); + if (bnx2x_test_nvram(bp) != 0) { + if (!IS_MF(bp)) + buf[4] = 1; + else + buf[0] = 1; + etest->flags |= ETH_TEST_FL_FAILED; + } + if (!netif_running(dev)) { - DP(BNX2X_MSG_ETHTOOL, - "Can't perform self-test when interface is down\n"); + DP(BNX2X_MSG_ETHTOOL, "Interface is down\n"); return; } @@ -2928,13 +2938,7 @@ static void bnx2x_self_test(struct net_device *dev, /* wait until link state is restored */ bnx2x_wait_for_link(bp, link_up, is_serdes); } - if (bnx2x_test_nvram(bp) != 0) { - if (!IS_MF(bp)) - buf[4] = 1; - else - buf[0] = 1; - etest->flags |= ETH_TEST_FL_FAILED; - } + if (bnx2x_test_intr(bp) != 0) { if (!IS_MF(bp)) buf[5] = 1; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index 32767f6aa33f473a126259e4877fa608ed51b3bf..cf1df8b62e2c2785c0560b77ac5ed4b5fc8ae8b3 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -172,6 +172,7 @@ struct shared_hw_cfg { /* NVRAM Offset */ #define SHARED_HW_CFG_LED_MAC4 0x000c0000 #define SHARED_HW_CFG_LED_PHY8 0x000d0000 #define SHARED_HW_CFG_LED_EXTPHY1 0x000e0000 + #define SHARED_HW_CFG_LED_EXTPHY2 0x000f0000 #define SHARED_HW_CFG_AN_ENABLE_MASK 0x3f000000 diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index 51468227bf3b75fc17d2abafb8b3bd9294d8494c..20dcc02431cac441a8bf3076a7fe6db1abfb04e9 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -3122,7 +3122,7 @@ static void bnx2x_bsc_module_sel(struct link_params *params) } static int bnx2x_bsc_read(struct link_params *params, - struct bnx2x_phy *phy, + struct bnx2x *bp, u8 sl_devid, u16 sl_addr, u8 lc_addr, @@ -3131,7 +3131,6 @@ static int bnx2x_bsc_read(struct link_params *params, { u32 val, i; int rc = 0; - struct bnx2x *bp = params->bp; if (xfer_cnt > 16) { DP(NETIF_MSG_LINK, "invalid xfer_cnt %d. Max is 16 bytes\n", @@ -6371,9 +6370,15 @@ int bnx2x_set_led(struct link_params *params, * intended override. */ break; - } else + } else { + u32 nig_led_mode = ((params->hw_led_mode << + SHARED_HW_CFG_LED_MODE_SHIFT) == + SHARED_HW_CFG_LED_EXTPHY2) ? + (SHARED_HW_CFG_LED_PHY1 >> + SHARED_HW_CFG_LED_MODE_SHIFT) : hw_led_mode; REG_WR(bp, NIG_REG_LED_MODE_P0 + port*4, - hw_led_mode); + nig_led_mode); + } REG_WR(bp, NIG_REG_LED_CONTROL_OVERRIDE_TRAFFIC_P0 + port*4, 0); /* Set blinking rate to ~15.9Hz */ @@ -7917,7 +7922,7 @@ static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy, usleep_range(1000, 2000); bnx2x_warpcore_power_module(params, 1); } - rc = bnx2x_bsc_read(params, phy, dev_addr, addr32, 0, byte_cnt, + rc = bnx2x_bsc_read(params, bp, dev_addr, addr32, 0, byte_cnt, data_array); } while ((rc != 0) && (++cnt < I2C_WA_RETRY_CNT)); @@ -10653,10 +10658,18 @@ static void bnx2x_848xx_set_link_led(struct bnx2x_phy *phy, 0x40); } else { + /* EXTPHY2 LED mode indicate that the 100M/1G/10G LED + * sources are all wired through LED1, rather than only + * 10G in other modes. + */ + val = ((params->hw_led_mode << + SHARED_HW_CFG_LED_MODE_SHIFT) == + SHARED_HW_CFG_LED_EXTPHY2) ? 0x98 : 0x80; + bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED1_MASK, - 0x80); + val); /* Tell LED3 to blink on source */ bnx2x_cl45_read(bp, phy, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index b42f89ce02ef5997fa46e57a9521b0873968d7b7..bb2f20291509392192545f495a290cec93f60e1d 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -9916,7 +9916,7 @@ static int bnx2x_prev_path_mark_eeh(struct bnx2x *bp) static bool bnx2x_prev_is_path_marked(struct bnx2x *bp) { struct bnx2x_prev_path_list *tmp_list; - int rc = false; + bool rc = false; if (down_trylock(&bnx2x_prev_sem)) return false; @@ -11186,6 +11186,14 @@ static void bnx2x_get_mac_hwinfo(struct bnx2x *bp) bnx2x_get_cnic_mac_hwinfo(bp); } + if (!BP_NOMCP(bp)) { + /* Read physical port identifier from shmem */ + val2 = SHMEM_RD(bp, dev_info.port_hw_config[port].mac_upper); + val = SHMEM_RD(bp, dev_info.port_hw_config[port].mac_lower); + bnx2x_set_mac_buf(bp->phys_port_id, val, val2); + bp->flags |= HAS_PHYS_PORT_ID; + } + memcpy(bp->link_params.mac_addr, bp->dev->dev_addr, ETH_ALEN); if (!bnx2x_is_valid_ether_addr(bp, bp->dev->dev_addr)) @@ -11784,7 +11792,7 @@ static int bnx2x_open(struct net_device *dev) rc = bnx2x_nic_load(bp, LOAD_OPEN); if (rc) return rc; - return bnx2x_open_epilog(bp); + return 0; } /* called with rtnl_lock */ @@ -12082,6 +12090,20 @@ static int bnx2x_validate_addr(struct net_device *dev) return 0; } +static int bnx2x_get_phys_port_id(struct net_device *netdev, + struct netdev_phys_port_id *ppid) +{ + struct bnx2x *bp = netdev_priv(netdev); + + if (!(bp->flags & HAS_PHYS_PORT_ID)) + return -EOPNOTSUPP; + + ppid->id_len = sizeof(bp->phys_port_id); + memcpy(ppid->id, bp->phys_port_id, ppid->id_len); + + return 0; +} + static const struct net_device_ops bnx2x_netdev_ops = { .ndo_open = bnx2x_open, .ndo_stop = bnx2x_close, @@ -12111,6 +12133,7 @@ static const struct net_device_ops bnx2x_netdev_ops = { #ifdef CONFIG_NET_RX_BUSY_POLL .ndo_busy_poll = bnx2x_low_latency_recv, #endif + .ndo_get_phys_port_id = bnx2x_get_phys_port_id, }; static int bnx2x_set_coherency_mask(struct bnx2x *bp) @@ -12274,10 +12297,13 @@ static int bnx2x_init_dev(struct bnx2x *bp, struct pci_dev *pdev, NETIF_F_RXCSUM | NETIF_F_LRO | NETIF_F_GRO | NETIF_F_RXHASH | NETIF_F_HW_VLAN_CTAG_TX; if (!CHIP_IS_E1x(bp)) { - dev->hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL; + dev->hw_features |= NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT; dev->hw_enc_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO_ECN | NETIF_F_TSO6 | + NETIF_F_GSO_IPIP | + NETIF_F_GSO_SIT | NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL; } @@ -12310,34 +12336,11 @@ err_out_release: err_out_disable: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); err_out: return rc; } -static void bnx2x_get_pcie_width_speed(struct bnx2x *bp, int *width, - enum bnx2x_pci_bus_speed *speed) -{ - u32 link_speed, val = 0; - - pci_read_config_dword(bp->pdev, PCICFG_LINK_CONTROL, &val); - *width = (val & PCICFG_LINK_WIDTH) >> PCICFG_LINK_WIDTH_SHIFT; - - link_speed = (val & PCICFG_LINK_SPEED) >> PCICFG_LINK_SPEED_SHIFT; - - switch (link_speed) { - case 3: - *speed = BNX2X_PCI_LINK_SPEED_8000; - break; - case 2: - *speed = BNX2X_PCI_LINK_SPEED_5000; - break; - default: - *speed = BNX2X_PCI_LINK_SPEED_2500; - } -} - static int bnx2x_check_firmware(struct bnx2x *bp) { const struct firmware *firmware = bp->firmware; @@ -12694,8 +12697,8 @@ static int bnx2x_init_one(struct pci_dev *pdev, { struct net_device *dev = NULL; struct bnx2x *bp; - int pcie_width; - enum bnx2x_pci_bus_speed pcie_speed; + enum pcie_link_width pcie_width; + enum pci_bus_speed pcie_speed; int rc, max_non_def_sbs; int rx_count, tx_count, rss_count, doorbell_size; int max_cos_est; @@ -12844,18 +12847,19 @@ static int bnx2x_init_one(struct pci_dev *pdev, dev_addr_add(bp->dev, bp->fip_mac, NETDEV_HW_ADDR_T_SAN); rtnl_unlock(); } - - bnx2x_get_pcie_width_speed(bp, &pcie_width, &pcie_speed); - BNX2X_DEV_INFO("got pcie width %d and speed %d\n", - pcie_width, pcie_speed); - - BNX2X_DEV_INFO("%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n", + if (pcie_get_minimum_link(bp->pdev, &pcie_speed, &pcie_width) || + pcie_speed == PCI_SPEED_UNKNOWN || + pcie_width == PCIE_LNK_WIDTH_UNKNOWN) + BNX2X_DEV_INFO("Failed to determine PCI Express Bandwidth\n"); + else + BNX2X_DEV_INFO( + "%s (%c%d) PCI-E x%d %s found at mem %lx, IRQ %d, node addr %pM\n", board_info[ent->driver_data].name, (CHIP_REV(bp) >> 12) + 'A', (CHIP_METAL(bp) >> 4), pcie_width, - pcie_speed == BNX2X_PCI_LINK_SPEED_2500 ? "2.5GHz" : - pcie_speed == BNX2X_PCI_LINK_SPEED_5000 ? "5.0GHz" : - pcie_speed == BNX2X_PCI_LINK_SPEED_8000 ? "8.0GHz" : + pcie_speed == PCIE_SPEED_2_5GT ? "2.5GHz" : + pcie_speed == PCIE_SPEED_5_0GT ? "5.0GHz" : + pcie_speed == PCIE_SPEED_8_0GT ? "8.0GHz" : "Unknown", dev->base_addr, bp->pdev->irq, dev->dev_addr); @@ -12874,7 +12878,6 @@ init_one_exit: pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); return rc; } @@ -12957,7 +12960,6 @@ static void __bnx2x_remove(struct pci_dev *pdev, pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static void bnx2x_remove_one(struct pci_dev *pdev) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c index 5e07efb6ec1366bcef6b020d228a9b408e9b2000..0216d592d0cee4b080a9d33d7776f632bf639453 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c @@ -2816,7 +2816,7 @@ struct set_vf_state_cookie { u8 state; }; -void bnx2x_set_vf_state(void *cookie) +static void bnx2x_set_vf_state(void *cookie) { struct set_vf_state_cookie *p = (struct set_vf_state_cookie *)cookie; @@ -3239,8 +3239,9 @@ void bnx2x_disable_sriov(struct bnx2x *bp) pci_disable_sriov(bp->pdev); } -int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx, struct bnx2x_virtf **vf, - struct pf_vf_bulletin_content **bulletin) +static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx, + struct bnx2x_virtf **vf, + struct pf_vf_bulletin_content **bulletin) { if (bp->state != BNX2X_STATE_OPEN) { BNX2X_ERR("vf ndo called though PF is down\n"); @@ -3656,29 +3657,6 @@ alloc_mem_err: return -ENOMEM; } -int bnx2x_open_epilog(struct bnx2x *bp) -{ - /* Enable sriov via delayed work. This must be done via delayed work - * because it causes the probe of the vf devices to be run, which invoke - * register_netdevice which must have rtnl lock taken. As we are holding - * the lock right now, that could only work if the probe would not take - * the lock. However, as the probe of the vf may be called from other - * contexts as well (such as passthrough to vm fails) it can't assume - * the lock is being held for it. Using delayed work here allows the - * probe code to simply take the lock (i.e. wait for it to be released - * if it is being held). We only want to do this if the number of VFs - * was set before PF driver was loaded. - */ - if (IS_SRIOV(bp) && BNX2X_NR_VIRTFN(bp)) { - smp_mb__before_clear_bit(); - set_bit(BNX2X_SP_RTNL_ENABLE_SRIOV, &bp->sp_rtnl_state); - smp_mb__after_clear_bit(); - schedule_delayed_work(&bp->sp_rtnl_task, 0); - } - - return 0; -} - void bnx2x_iov_channel_down(struct bnx2x *bp) { int vf_idx; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h index 059f0d460af2a249e631a913ec98da290d78f323..1ff6a9366629ed88fe79a079391c92e95d1e9baf 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h @@ -782,7 +782,6 @@ static inline int bnx2x_vf_headroom(struct bnx2x *bp) void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp); int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs); void bnx2x_iov_channel_down(struct bnx2x *bp); -int bnx2x_open_epilog(struct bnx2x *bp); #else /* CONFIG_BNX2X_SRIOV */ @@ -842,7 +841,6 @@ static inline int bnx2x_vf_pci_alloc(struct bnx2x *bp) {return 0; } static inline void bnx2x_pf_set_vfs_vlan(struct bnx2x *bp) {} static inline int bnx2x_sriov_configure(struct pci_dev *dev, int num_vfs) {return 0; } static inline void bnx2x_iov_channel_down(struct bnx2x *bp) {} -static inline int bnx2x_open_epilog(struct bnx2x *bp) {return 0; } #endif /* CONFIG_BNX2X_SRIOV */ #endif /* bnx2x_sriov.h */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c index 28757dfacf0da0107576e0c8f7db3222fd5f7a1e..9199adf32d33c1639dfa2354e609ef92b638ef91 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c @@ -60,6 +60,30 @@ void bnx2x_vfpf_finalize(struct bnx2x *bp, struct vfpf_first_tlv *first_tlv) mutex_unlock(&bp->vf2pf_mutex); } +/* Finds a TLV by type in a TLV buffer; If found, returns pointer to the TLV */ +static void *bnx2x_search_tlv_list(struct bnx2x *bp, void *tlvs_list, + enum channel_tlvs req_tlv) +{ + struct channel_tlv *tlv = (struct channel_tlv *)tlvs_list; + + do { + if (tlv->type == req_tlv) + return tlv; + + if (!tlv->length) { + BNX2X_ERR("Found TLV with length 0\n"); + return NULL; + } + + tlvs_list += tlv->length; + tlv = (struct channel_tlv *)tlvs_list; + } while (tlv->type != CHANNEL_TLV_LIST_END); + + DP(BNX2X_MSG_IOV, "TLV list does not contain %d TLV\n", req_tlv); + + return NULL; +} + /* list the types and lengths of the tlvs on the buffer */ void bnx2x_dp_tlv_list(struct bnx2x *bp, void *tlvs_list) { @@ -196,6 +220,7 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count) int rc = 0, attempts = 0; struct vfpf_acquire_tlv *req = &bp->vf2pf_mbox->req.acquire; struct pfvf_acquire_resp_tlv *resp = &bp->vf2pf_mbox->resp.acquire_resp; + struct vfpf_port_phys_id_resp_tlv *phys_port_resp; u32 vf_id; bool resources_acquired = false; @@ -219,8 +244,14 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count) /* pf 2 vf bulletin board address */ req->bulletin_addr = bp->pf2vf_bulletin_mapping; + /* Request physical port identifier */ + bnx2x_add_tlv(bp, req, req->first_tlv.tl.length, + CHANNEL_TLV_PHYS_PORT_ID, sizeof(struct channel_tlv)); + /* add list termination tlv */ - bnx2x_add_tlv(bp, req, req->first_tlv.tl.length, CHANNEL_TLV_LIST_END, + bnx2x_add_tlv(bp, req, + req->first_tlv.tl.length + sizeof(struct channel_tlv), + CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); /* output tlvs list */ @@ -287,6 +318,15 @@ int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count) } } + /* Retrieve physical port id (if possible) */ + phys_port_resp = (struct vfpf_port_phys_id_resp_tlv *) + bnx2x_search_tlv_list(bp, resp, + CHANNEL_TLV_PHYS_PORT_ID); + if (phys_port_resp) { + memcpy(bp->phys_port_id, phys_port_resp->id, ETH_ALEN); + bp->flags |= HAS_PHYS_PORT_ID; + } + /* get HW info */ bp->common.chip_id |= (bp->acquire_resp.pfdev_info.chip_num & 0xffff); bp->link_params.chip_id = bp->common.chip_id; @@ -983,53 +1023,59 @@ static int bnx2x_copy32_vf_dmae(struct bnx2x *bp, u8 from_vf, return bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp)); } -static void bnx2x_vf_mbx_resp(struct bnx2x *bp, struct bnx2x_virtf *vf) +static void bnx2x_vf_mbx_resp_single_tlv(struct bnx2x *bp, + struct bnx2x_virtf *vf) { struct bnx2x_vf_mbx *mbx = BP_VF_MBX(bp, vf->index); - u64 vf_addr; - dma_addr_t pf_addr; u16 length, type; - int rc; - struct pfvf_general_resp_tlv *resp = &mbx->msg->resp.general_resp; /* prepare response */ type = mbx->first_tlv.tl.type; length = type == CHANNEL_TLV_ACQUIRE ? sizeof(struct pfvf_acquire_resp_tlv) : sizeof(struct pfvf_general_resp_tlv); - bnx2x_add_tlv(bp, resp, 0, type, length); - resp->hdr.status = bnx2x_pfvf_status_codes(vf->op_rc); - bnx2x_add_tlv(bp, resp, length, CHANNEL_TLV_LIST_END, + bnx2x_add_tlv(bp, &mbx->msg->resp, 0, type, length); + bnx2x_add_tlv(bp, &mbx->msg->resp, length, CHANNEL_TLV_LIST_END, sizeof(struct channel_list_end_tlv)); +} + +static void bnx2x_vf_mbx_resp_send_msg(struct bnx2x *bp, + struct bnx2x_virtf *vf) +{ + struct bnx2x_vf_mbx *mbx = BP_VF_MBX(bp, vf->index); + struct pfvf_general_resp_tlv *resp = &mbx->msg->resp.general_resp; + dma_addr_t pf_addr; + u64 vf_addr; + int rc; + bnx2x_dp_tlv_list(bp, resp); DP(BNX2X_MSG_IOV, "mailbox vf address hi 0x%x, lo 0x%x, offset 0x%x\n", mbx->vf_addr_hi, mbx->vf_addr_lo, mbx->first_tlv.resp_msg_offset); + resp->hdr.status = bnx2x_pfvf_status_codes(vf->op_rc); + /* send response */ vf_addr = HILO_U64(mbx->vf_addr_hi, mbx->vf_addr_lo) + mbx->first_tlv.resp_msg_offset; pf_addr = mbx->msg_mapping + offsetof(struct bnx2x_vf_mbx_msg, resp); - /* copy the response body, if there is one, before the header, as the vf - * is sensitive to the header being written + /* Copy the response buffer. The first u64 is written afterwards, as + * the vf is sensitive to the header being written */ - if (resp->hdr.tl.length > sizeof(u64)) { - length = resp->hdr.tl.length - sizeof(u64); - vf_addr += sizeof(u64); - pf_addr += sizeof(u64); - rc = bnx2x_copy32_vf_dmae(bp, false, pf_addr, vf->abs_vfid, - U64_HI(vf_addr), - U64_LO(vf_addr), - length/4); - if (rc) { - BNX2X_ERR("Failed to copy response body to VF %d\n", - vf->abs_vfid); - goto mbx_error; - } - vf_addr -= sizeof(u64); - pf_addr -= sizeof(u64); + vf_addr += sizeof(u64); + pf_addr += sizeof(u64); + rc = bnx2x_copy32_vf_dmae(bp, false, pf_addr, vf->abs_vfid, + U64_HI(vf_addr), + U64_LO(vf_addr), + (sizeof(union pfvf_tlvs) - sizeof(u64))/4); + if (rc) { + BNX2X_ERR("Failed to copy response body to VF %d\n", + vf->abs_vfid); + goto mbx_error; } + vf_addr -= sizeof(u64); + pf_addr -= sizeof(u64); /* ack the FW */ storm_memset_vf_mbx_ack(bp, vf->abs_vfid); @@ -1060,6 +1106,36 @@ mbx_error: bnx2x_vf_release(bp, vf, false); /* non blocking */ } +static void bnx2x_vf_mbx_resp(struct bnx2x *bp, + struct bnx2x_virtf *vf) +{ + bnx2x_vf_mbx_resp_single_tlv(bp, vf); + bnx2x_vf_mbx_resp_send_msg(bp, vf); +} + +static void bnx2x_vf_mbx_resp_phys_port(struct bnx2x *bp, + struct bnx2x_virtf *vf, + void *buffer, + u16 *offset) +{ + struct vfpf_port_phys_id_resp_tlv *port_id; + + if (!(bp->flags & HAS_PHYS_PORT_ID)) + return; + + bnx2x_add_tlv(bp, buffer, *offset, CHANNEL_TLV_PHYS_PORT_ID, + sizeof(struct vfpf_port_phys_id_resp_tlv)); + + port_id = (struct vfpf_port_phys_id_resp_tlv *) + (((u8 *)buffer) + *offset); + memcpy(port_id->id, bp->phys_port_id, ETH_ALEN); + + /* Offset should continue representing the offset to the tail + * of TLV data (outside this function scope) + */ + *offset += sizeof(struct vfpf_port_phys_id_resp_tlv); +} + static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf, struct bnx2x_vf_mbx *mbx, int vfop_status) { @@ -1067,6 +1143,7 @@ static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf, struct pfvf_acquire_resp_tlv *resp = &mbx->msg->resp.acquire_resp; struct pf_vf_resc *resc = &resp->resc; u8 status = bnx2x_pfvf_status_codes(vfop_status); + u16 length; memset(resp, 0, sizeof(*resp)); @@ -1140,9 +1217,24 @@ static void bnx2x_vf_mbx_acquire_resp(struct bnx2x *bp, struct bnx2x_virtf *vf, resc->hw_sbs[i].sb_qid); DP_CONT(BNX2X_MSG_IOV, "]\n"); + /* prepare response */ + length = sizeof(struct pfvf_acquire_resp_tlv); + bnx2x_add_tlv(bp, &mbx->msg->resp, 0, CHANNEL_TLV_ACQUIRE, length); + + /* Handle possible VF requests for physical port identifiers. + * 'length' should continue to indicate the offset of the first empty + * place in the buffer (i.e., where next TLV should be inserted) + */ + if (bnx2x_search_tlv_list(bp, &mbx->msg->req, + CHANNEL_TLV_PHYS_PORT_ID)) + bnx2x_vf_mbx_resp_phys_port(bp, vf, &mbx->msg->resp, &length); + + bnx2x_add_tlv(bp, &mbx->msg->resp, length, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + /* send the response */ vf->op_rc = vfop_status; - bnx2x_vf_mbx_resp(bp, vf); + bnx2x_vf_mbx_resp_send_msg(bp, vf); } static void bnx2x_vf_mbx_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf, @@ -1874,6 +1966,9 @@ void bnx2x_vf_mbx(struct bnx2x *bp, struct vf_pf_event_data *vfpf_event) /* process the VF message header */ mbx->first_tlv = mbx->msg->req.first_tlv; + /* Clean response buffer to refrain from falsely seeing chains */ + memset(&mbx->msg->resp, 0, sizeof(union pfvf_tlvs)); + /* dispatch the request (will prepare the response) */ bnx2x_vf_mbx_request(bp, vf, mbx); goto mbx_done; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h index 1179fe06d0c7ff010b7a06102a68b601c747bf9c..208568bc7a71d72d8e19aea70f02485e17c0e8ee 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.h @@ -188,6 +188,12 @@ struct pfvf_acquire_resp_tlv { } resc; }; +struct vfpf_port_phys_id_resp_tlv { + struct channel_tlv tl; + u8 id[ETH_ALEN]; + u8 padding[2]; +}; + #define VFPF_INIT_FLG_STATS_COALESCE (1 << 0) /* when set the VFs queues * stats will be coalesced on * the leading RSS queue @@ -398,6 +404,7 @@ enum channel_tlvs { CHANNEL_TLV_PF_SET_MAC, CHANNEL_TLV_PF_SET_VLAN, CHANNEL_TLV_UPDATE_RSS, + CHANNEL_TLV_PHYS_PORT_ID, CHANNEL_TLV_MAX }; diff --git a/drivers/net/ethernet/broadcom/cnic.c b/drivers/net/ethernet/broadcom/cnic.c index 99394bd49a139414776df9ff776c02b2fe6f3c26..f58a8b80302d9b2088ce139688b76e3a9181bb42 100644 --- a/drivers/net/ethernet/broadcom/cnic.c +++ b/drivers/net/ethernet/broadcom/cnic.c @@ -393,7 +393,7 @@ static int cnic_iscsi_nl_msg_recv(struct cnic_dev *dev, u32 msg_type, csk->vlan_id = path_resp->vlan_id; - memcpy(csk->ha, path_resp->mac_addr, 6); + memcpy(csk->ha, path_resp->mac_addr, ETH_ALEN); if (test_bit(SK_F_IPV6, &csk->flags)) memcpy(&csk->src_ip[0], &path_resp->src.v6_addr, sizeof(struct in6_addr)); @@ -5572,7 +5572,7 @@ static struct cnic_dev *init_bnx2x_cnic(struct net_device *dev) if (cdev->max_fcoe_conn > BNX2X_FCOE_NUM_CONNECTIONS) cdev->max_fcoe_conn = BNX2X_FCOE_NUM_CONNECTIONS; - memcpy(cdev->mac_addr, ethdev->iscsi_mac, 6); + memcpy(cdev->mac_addr, ethdev->iscsi_mac, ETH_ALEN); cp->cnic_ops = &cnic_bnx2x_ops; cp->start_hw = cnic_start_bnx2x_hw; diff --git a/drivers/net/ethernet/broadcom/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h index 0658b43e148c1c45ecb2013696534f2ff1d2ffb0..ebbfe25acaa6e0fc927938bd54150435a1feedb5 100644 --- a/drivers/net/ethernet/broadcom/cnic_if.h +++ b/drivers/net/ethernet/broadcom/cnic_if.h @@ -353,8 +353,8 @@ struct cnic_ulp_ops { atomic_t ref_count; }; -extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops); +int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops); -extern int cnic_unregister_driver(int ulp_type); +int cnic_unregister_driver(int ulp_type); #endif diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 12d961c4ebcaf704e36b83c8624633c2fbd48dda..00c5be8c55b8ff7186a670645cb654dc46779399 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -94,10 +94,10 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits) #define DRV_MODULE_NAME "tg3" #define TG3_MAJ_NUM 3 -#define TG3_MIN_NUM 133 +#define TG3_MIN_NUM 134 #define DRV_MODULE_VERSION \ __stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM) -#define DRV_MODULE_RELDATE "Jul 29, 2013" +#define DRV_MODULE_RELDATE "Sep 16, 2013" #define RESET_KIND_SHUTDOWN 0 #define RESET_KIND_INIT 1 @@ -337,6 +337,11 @@ static DEFINE_PCI_DEVICE_TABLE(tg3_pci_tbl) = { {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5762)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5725)}, {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5727)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57764)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57767)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57787)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57782)}, + {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57786)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)}, {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)}, {PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)}, @@ -1326,6 +1331,12 @@ static int tg3_phy_toggle_auxctl_smdsp(struct tg3 *tp, bool enable) return err; } +static int tg3_phy_shdw_write(struct tg3 *tp, int reg, u32 val) +{ + return tg3_writephy(tp, MII_TG3_MISC_SHDW, + reg | val | MII_TG3_MISC_SHDW_WREN); +} + static int tg3_bmcr_reset(struct tg3 *tp) { u32 phy_control; @@ -1364,7 +1375,7 @@ static int tg3_mdio_read(struct mii_bus *bp, int mii_id, int reg) spin_lock_bh(&tp->lock); - if (tg3_readphy(tp, reg, &val)) + if (__tg3_readphy(tp, mii_id, reg, &val)) val = -EIO; spin_unlock_bh(&tp->lock); @@ -1379,7 +1390,7 @@ static int tg3_mdio_write(struct mii_bus *bp, int mii_id, int reg, u16 val) spin_lock_bh(&tp->lock); - if (tg3_writephy(tp, reg, val)) + if (__tg3_writephy(tp, mii_id, reg, val)) ret = -EIO; spin_unlock_bh(&tp->lock); @@ -1397,7 +1408,7 @@ static void tg3_mdio_config_5785(struct tg3 *tp) u32 val; struct phy_device *phydev; - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; switch (phydev->drv->phy_id & phydev->drv->phy_id_mask) { case PHY_ID_BCM50610: case PHY_ID_BCM50610M: @@ -1502,6 +1513,13 @@ static int tg3_mdio_init(struct tg3 *tp) TG3_CPMU_PHY_STRAP_IS_SERDES; if (is_serdes) tp->phy_addr += 7; + } else if (tg3_flag(tp, IS_SSB_CORE) && tg3_flag(tp, ROBOSWITCH)) { + int addr; + + addr = ssb_gige_get_phyaddr(tp->pdev); + if (addr < 0) + return addr; + tp->phy_addr = addr; } else tp->phy_addr = TG3_PHY_MII_ADDR; @@ -1522,7 +1540,7 @@ static int tg3_mdio_init(struct tg3 *tp) tp->mdio_bus->read = &tg3_mdio_read; tp->mdio_bus->write = &tg3_mdio_write; tp->mdio_bus->reset = &tg3_mdio_reset; - tp->mdio_bus->phy_mask = ~(1 << TG3_PHY_MII_ADDR); + tp->mdio_bus->phy_mask = ~(1 << tp->phy_addr); tp->mdio_bus->irq = &tp->mdio_irq[0]; for (i = 0; i < PHY_MAX_ADDR; i++) @@ -1543,7 +1561,7 @@ static int tg3_mdio_init(struct tg3 *tp) return i; } - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; if (!phydev || !phydev->drv) { dev_warn(&tp->pdev->dev, "No PHY devices\n"); @@ -1953,7 +1971,7 @@ static void tg3_setup_flow_control(struct tg3 *tp, u32 lcladv, u32 rmtadv) u32 old_tx_mode = tp->tx_mode; if (tg3_flag(tp, USE_PHYLIB)) - autoneg = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]->autoneg; + autoneg = tp->mdio_bus->phy_map[tp->phy_addr]->autoneg; else autoneg = tp->link_config.autoneg; @@ -1989,7 +2007,7 @@ static void tg3_adjust_link(struct net_device *dev) u8 oldflowctrl, linkmesg = 0; u32 mac_mode, lcl_adv, rmt_adv; struct tg3 *tp = netdev_priv(dev); - struct phy_device *phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + struct phy_device *phydev = tp->mdio_bus->phy_map[tp->phy_addr]; spin_lock_bh(&tp->lock); @@ -2078,7 +2096,7 @@ static int tg3_phy_init(struct tg3 *tp) /* Bring the PHY back to a known state. */ tg3_bmcr_reset(tp); - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; /* Attach the MAC to the PHY. */ phydev = phy_connect(tp->dev, dev_name(&phydev->dev), @@ -2105,7 +2123,7 @@ static int tg3_phy_init(struct tg3 *tp) SUPPORTED_Asym_Pause); break; default: - phy_disconnect(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]); + phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]); return -EINVAL; } @@ -2123,7 +2141,7 @@ static void tg3_phy_start(struct tg3 *tp) if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return; - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; if (tp->phy_flags & TG3_PHYFLG_IS_LOW_POWER) { tp->phy_flags &= ~TG3_PHYFLG_IS_LOW_POWER; @@ -2143,13 +2161,13 @@ static void tg3_phy_stop(struct tg3 *tp) if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return; - phy_stop(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]); + phy_stop(tp->mdio_bus->phy_map[tp->phy_addr]); } static void tg3_phy_fini(struct tg3 *tp) { if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) { - phy_disconnect(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]); + phy_disconnect(tp->mdio_bus->phy_map[tp->phy_addr]); tp->phy_flags &= ~TG3_PHYFLG_IS_CONNECTED; } } @@ -2218,25 +2236,21 @@ static void tg3_phy_toggle_apd(struct tg3 *tp, bool enable) return; } - reg = MII_TG3_MISC_SHDW_WREN | - MII_TG3_MISC_SHDW_SCR5_SEL | - MII_TG3_MISC_SHDW_SCR5_LPED | + reg = MII_TG3_MISC_SHDW_SCR5_LPED | MII_TG3_MISC_SHDW_SCR5_DLPTLM | MII_TG3_MISC_SHDW_SCR5_SDTL | MII_TG3_MISC_SHDW_SCR5_C125OE; if (tg3_asic_rev(tp) != ASIC_REV_5784 || !enable) reg |= MII_TG3_MISC_SHDW_SCR5_DLLAPD; - tg3_writephy(tp, MII_TG3_MISC_SHDW, reg); + tg3_phy_shdw_write(tp, MII_TG3_MISC_SHDW_SCR5_SEL, reg); - reg = MII_TG3_MISC_SHDW_WREN | - MII_TG3_MISC_SHDW_APD_SEL | - MII_TG3_MISC_SHDW_APD_WKTM_84MS; + reg = MII_TG3_MISC_SHDW_APD_WKTM_84MS; if (enable) reg |= MII_TG3_MISC_SHDW_APD_ENABLE; - tg3_writephy(tp, MII_TG3_MISC_SHDW, reg); + tg3_phy_shdw_write(tp, MII_TG3_MISC_SHDW_APD_SEL, reg); } static void tg3_phy_toggle_automdix(struct tg3 *tp, bool enable) @@ -4027,7 +4041,7 @@ static int tg3_power_down_prepare(struct tg3 *tp) struct phy_device *phydev; u32 phyid, advertising; - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; tp->phy_flags |= TG3_PHYFLG_IS_LOW_POWER; @@ -6848,12 +6862,6 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) pci_unmap_single(tp->pdev, dma_addr, skb_size, PCI_DMA_FROMDEVICE); - skb = build_skb(data, frag_size); - if (!skb) { - tg3_frag_free(frag_size != 0, data); - goto drop_it_no_recycle; - } - skb_reserve(skb, TG3_RX_OFFSET(tp)); /* Ensure that the update to the data happens * after the usage of the old DMA mapping. */ @@ -6861,6 +6869,12 @@ static int tg3_rx(struct tg3_napi *tnapi, int budget) ri->data = NULL; + skb = build_skb(data, frag_size); + if (!skb) { + tg3_frag_free(frag_size != 0, data); + goto drop_it_no_recycle; + } + skb_reserve(skb, TG3_RX_OFFSET(tp)); } else { tg3_recycle_rx(tnapi, tpr, opaque_key, desc_idx, *post_ptr); @@ -9196,10 +9210,7 @@ static int tg3_halt(struct tg3 *tp, int kind, bool silent) memset(tp->hw_stats, 0, sizeof(struct tg3_hw_stats)); } - if (err) - return err; - - return 0; + return err; } static int tg3_set_mac_addr(struct net_device *dev, void *p) @@ -11035,7 +11046,18 @@ static int tg3_request_irq(struct tg3 *tp, int irq_num) name = tp->dev->name; else { name = &tnapi->irq_lbl[0]; - snprintf(name, IFNAMSIZ, "%s-%d", tp->dev->name, irq_num); + if (tnapi->tx_buffers && tnapi->rx_rcb) + snprintf(name, IFNAMSIZ, + "%s-txrx-%d", tp->dev->name, irq_num); + else if (tnapi->tx_buffers) + snprintf(name, IFNAMSIZ, + "%s-tx-%d", tp->dev->name, irq_num); + else if (tnapi->rx_rcb) + snprintf(name, IFNAMSIZ, + "%s-rx-%d", tp->dev->name, irq_num); + else + snprintf(name, IFNAMSIZ, + "%s-%d", tp->dev->name, irq_num); name[IFNAMSIZ-1] = 0; } @@ -11907,7 +11929,7 @@ static int tg3_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) struct phy_device *phydev; if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return -EAGAIN; - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; return phy_ethtool_gset(phydev, cmd); } @@ -11974,7 +11996,7 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) struct phy_device *phydev; if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return -EAGAIN; - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; return phy_ethtool_sset(phydev, cmd); } @@ -12093,12 +12115,10 @@ static int tg3_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) device_set_wakeup_enable(dp, wol->wolopts & WAKE_MAGIC); - spin_lock_bh(&tp->lock); if (device_may_wakeup(dp)) tg3_flag_set(tp, WOL_ENABLE); else tg3_flag_clear(tp, WOL_ENABLE); - spin_unlock_bh(&tp->lock); return 0; } @@ -12131,7 +12151,7 @@ static int tg3_nway_reset(struct net_device *dev) if (tg3_flag(tp, USE_PHYLIB)) { if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return -EAGAIN; - r = phy_start_aneg(tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]); + r = phy_start_aneg(tp->mdio_bus->phy_map[tp->phy_addr]); } else { u32 bmcr; @@ -12247,7 +12267,7 @@ static int tg3_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam u32 newadv; struct phy_device *phydev; - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; if (!(phydev->supported & SUPPORTED_Pause) || (!(phydev->supported & SUPPORTED_Asym_Pause) && @@ -13194,8 +13214,8 @@ static int tg3_run_loopback(struct tg3 *tp, u32 pktsz, bool tso_loopback) return -ENOMEM; tx_data = skb_put(skb, tx_len); - memcpy(tx_data, tp->dev->dev_addr, 6); - memset(tx_data + 6, 0x0, 8); + memcpy(tx_data, tp->dev->dev_addr, ETH_ALEN); + memset(tx_data + ETH_ALEN, 0x0, 8); tw32(MAC_RX_MTU_SIZE, tx_len + ETH_FCS_LEN); @@ -13683,7 +13703,7 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) struct phy_device *phydev; if (!(tp->phy_flags & TG3_PHYFLG_IS_CONNECTED)) return -EAGAIN; - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; return phy_mii_ioctl(phydev, ifr, cmd); } @@ -14921,6 +14941,12 @@ static void tg3_get_eeprom_hw_cfg(struct tg3 *tp) tg3_chip_rev_id(tp) != CHIPREV_ID_5750_A1) tp->led_ctrl |= (LED_CTRL_MODE_PHY_1 | LED_CTRL_MODE_PHY_2); + + if (tg3_flag(tp, 5717_PLUS) || + tg3_asic_rev(tp) == ASIC_REV_5762) + tp->led_ctrl |= LED_CTRL_BLINK_RATE_OVERRIDE | + LED_CTRL_BLINK_RATE_MASK; + break; case SHASTA_EXT_LED_MAC: @@ -15759,9 +15785,12 @@ static void tg3_detect_asic_rev(struct tg3 *tp, u32 misc_ctrl_reg) tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_5719 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_5720 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57767 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57764 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_5762 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_5725 || - tp->pdev->device == TG3PCI_DEVICE_TIGON3_5727) + tp->pdev->device == TG3PCI_DEVICE_TIGON3_5727 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57787) reg = TG3PCI_GEN2_PRODID_ASICREV; else if (tp->pdev->device == TG3PCI_DEVICE_TIGON3_57781 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_57785 || @@ -16632,8 +16661,8 @@ static int tg3_get_macaddr_sparc(struct tg3 *tp) int len; addr = of_get_property(dp, "local-mac-address", &len); - if (addr && len == 6) { - memcpy(dev->dev_addr, addr, 6); + if (addr && len == ETH_ALEN) { + memcpy(dev->dev_addr, addr, ETH_ALEN); return 0; } return -ENODEV; @@ -16643,7 +16672,7 @@ static int tg3_get_default_macaddr_sparc(struct tg3 *tp) { struct net_device *dev = tp->dev; - memcpy(dev->dev_addr, idprom->id_ethaddr, 6); + memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN); return 0; } #endif @@ -17052,10 +17081,6 @@ static int tg3_test_dma(struct tg3 *tp) tw32(TG3PCI_DMA_RW_CTRL, tp->dma_rwctrl); -#if 0 - /* Unneeded, already done by tg3_get_invariants. */ - tg3_switch_clocks(tp); -#endif if (tg3_asic_rev(tp) != ASIC_REV_5700 && tg3_asic_rev(tp) != ASIC_REV_5701) @@ -17083,20 +17108,6 @@ static int tg3_test_dma(struct tg3 *tp) break; } -#if 0 - /* validate data reached card RAM correctly. */ - for (i = 0; i < TEST_BUFFER_SIZE / sizeof(u32); i++) { - u32 val; - tg3_read_mem(tp, 0x2100 + (i*4), &val); - if (le32_to_cpu(val) != p[i]) { - dev_err(&tp->pdev->dev, - "%s: Buffer corrupted on device! " - "(%d != %d)\n", __func__, val, i); - /* ret = -ENODEV here? */ - } - p[i] = 0; - } -#endif /* Now read it back. */ ret = tg3_do_test_dma(tp, buf, buf_dma, TEST_BUFFER_SIZE, false); if (ret) { @@ -17362,8 +17373,10 @@ static int tg3_init_one(struct pci_dev *pdev, tg3_flag_set(tp, FLUSH_POSTED_WRITES); if (ssb_gige_one_dma_at_once(pdev)) tg3_flag_set(tp, ONE_DMA_AT_ONCE); - if (ssb_gige_have_roboswitch(pdev)) + if (ssb_gige_have_roboswitch(pdev)) { + tg3_flag_set(tp, USE_PHYLIB); tg3_flag_set(tp, ROBOSWITCH); + } if (ssb_gige_is_rgmii(pdev)) tg3_flag_set(tp, RGMII_MODE); } @@ -17409,9 +17422,12 @@ static int tg3_init_one(struct pci_dev *pdev, tp->pdev->device == TG3PCI_DEVICE_TIGON3_5718 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_5719 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_5720 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57767 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57764 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_5762 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_5725 || - tp->pdev->device == TG3PCI_DEVICE_TIGON3_5727) { + tp->pdev->device == TG3PCI_DEVICE_TIGON3_5727 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57787) { tg3_flag_set(tp, ENABLE_APE); tp->aperegs = pci_ioremap_bar(pdev, BAR_2); if (!tp->aperegs) { @@ -17628,7 +17644,7 @@ static int tg3_init_one(struct pci_dev *pdev, if (tp->phy_flags & TG3_PHYFLG_IS_CONNECTED) { struct phy_device *phydev; - phydev = tp->mdio_bus->phy_map[TG3_PHY_MII_ADDR]; + phydev = tp->mdio_bus->phy_map[tp->phy_addr]; netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s)\n", phydev->drv->name, dev_name(&phydev->dev)); @@ -17685,7 +17701,6 @@ err_out_free_res: err_out_disable_pdev: if (pci_is_enabled(pdev)) pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); return err; } @@ -17717,7 +17732,6 @@ static void tg3_remove_one(struct pci_dev *pdev) free_netdev(dev); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } } diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index 70257808aa37deb3c2ff0511a16a6966a8d5f9c0..5c3835aa1e1b0702d602102031a260644129390e 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h @@ -68,6 +68,9 @@ #define TG3PCI_DEVICE_TIGON3_5762 0x1687 #define TG3PCI_DEVICE_TIGON3_5725 0x1643 #define TG3PCI_DEVICE_TIGON3_5727 0x16f3 +#define TG3PCI_DEVICE_TIGON3_57764 0x1642 +#define TG3PCI_DEVICE_TIGON3_57767 0x1683 +#define TG3PCI_DEVICE_TIGON3_57787 0x1641 /* 0x04 --> 0x2c unused */ #define TG3PCI_SUBVENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM #define TG3PCI_SUBDEVICE_ID_BROADCOM_95700A6 0x1644 diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index b78e69e0e52a291047e72c222530a1500e4ac003..f276433d37cee0b0a4abe22836ec354bdb9ef899 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -3212,7 +3212,6 @@ bnad_init(struct bnad *bnad, bnad->bar0 = ioremap_nocache(bnad->mmio_start, bnad->mmio_len); if (!bnad->bar0) { dev_err(&pdev->dev, "ioremap for bar0 failed\n"); - pci_set_drvdata(pdev, NULL); return -ENOMEM; } pr_info("bar0 mapped to %p, len %llu\n", bnad->bar0, diff --git a/drivers/net/ethernet/brocade/bna/bnad.h b/drivers/net/ethernet/brocade/bna/bnad.h index aefee77523f2fbc52c621883ebe8fd56a5ffbfce..f7e033f8a00e30f0ed33e0f9889d14bafbf512a6 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.h +++ b/drivers/net/ethernet/brocade/bna/bnad.h @@ -372,38 +372,37 @@ extern u32 bnad_rxqs_per_cq; /* * EXTERN PROTOTYPES */ -extern u32 *cna_get_firmware_buf(struct pci_dev *pdev); +u32 *cna_get_firmware_buf(struct pci_dev *pdev); /* Netdev entry point prototypes */ -extern void bnad_set_rx_mode(struct net_device *netdev); -extern struct net_device_stats *bnad_get_netdev_stats( - struct net_device *netdev); -extern int bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr); -extern int bnad_enable_default_bcast(struct bnad *bnad); -extern void bnad_restore_vlans(struct bnad *bnad, u32 rx_id); -extern void bnad_set_ethtool_ops(struct net_device *netdev); -extern void bnad_cb_completion(void *arg, enum bfa_status status); +void bnad_set_rx_mode(struct net_device *netdev); +struct net_device_stats *bnad_get_netdev_stats(struct net_device *netdev); +int bnad_mac_addr_set_locked(struct bnad *bnad, u8 *mac_addr); +int bnad_enable_default_bcast(struct bnad *bnad); +void bnad_restore_vlans(struct bnad *bnad, u32 rx_id); +void bnad_set_ethtool_ops(struct net_device *netdev); +void bnad_cb_completion(void *arg, enum bfa_status status); /* Configuration & setup */ -extern void bnad_tx_coalescing_timeo_set(struct bnad *bnad); -extern void bnad_rx_coalescing_timeo_set(struct bnad *bnad); +void bnad_tx_coalescing_timeo_set(struct bnad *bnad); +void bnad_rx_coalescing_timeo_set(struct bnad *bnad); -extern int bnad_setup_rx(struct bnad *bnad, u32 rx_id); -extern int bnad_setup_tx(struct bnad *bnad, u32 tx_id); -extern void bnad_destroy_tx(struct bnad *bnad, u32 tx_id); -extern void bnad_destroy_rx(struct bnad *bnad, u32 rx_id); +int bnad_setup_rx(struct bnad *bnad, u32 rx_id); +int bnad_setup_tx(struct bnad *bnad, u32 tx_id); +void bnad_destroy_tx(struct bnad *bnad, u32 tx_id); +void bnad_destroy_rx(struct bnad *bnad, u32 rx_id); /* Timer start/stop protos */ -extern void bnad_dim_timer_start(struct bnad *bnad); +void bnad_dim_timer_start(struct bnad *bnad); /* Statistics */ -extern void bnad_netdev_qstats_fill(struct bnad *bnad, - struct rtnl_link_stats64 *stats); -extern void bnad_netdev_hwstats_fill(struct bnad *bnad, - struct rtnl_link_stats64 *stats); +void bnad_netdev_qstats_fill(struct bnad *bnad, + struct rtnl_link_stats64 *stats); +void bnad_netdev_hwstats_fill(struct bnad *bnad, + struct rtnl_link_stats64 *stats); /* Debugfs */ -void bnad_debugfs_init(struct bnad *bnad); -void bnad_debugfs_uninit(struct bnad *bnad); +void bnad_debugfs_init(struct bnad *bnad); +void bnad_debugfs_uninit(struct bnad *bnad); /* MACROS */ /* To set & get the stats counters */ diff --git a/drivers/net/ethernet/calxeda/xgmac.c b/drivers/net/ethernet/calxeda/xgmac.c index 48f52882a22b07414b7fed808fe72ccd52650300..4fc5c8ef51211abeeb6a576a6ecf88c84fb98177 100644 --- a/drivers/net/ethernet/calxeda/xgmac.c +++ b/drivers/net/ethernet/calxeda/xgmac.c @@ -1060,13 +1060,13 @@ static int xgmac_stop(struct net_device *dev) { struct xgmac_priv *priv = netdev_priv(dev); - netif_stop_queue(dev); - if (readl(priv->base + XGMAC_DMA_INTR_ENA)) napi_disable(&priv->napi); writel(0, priv->base + XGMAC_DMA_INTR_ENA); + netif_tx_disable(dev); + /* Disable the MAC core */ xgmac_mac_disable(priv->base); @@ -1370,11 +1370,8 @@ static int xgmac_change_mtu(struct net_device *dev, int new_mtu) } old_mtu = dev->mtu; - dev->mtu = new_mtu; /* return early if the buffer sizes will not change */ - if (old_mtu <= ETH_DATA_LEN && new_mtu <= ETH_DATA_LEN) - return 0; if (old_mtu == new_mtu) return 0; @@ -1382,8 +1379,9 @@ static int xgmac_change_mtu(struct net_device *dev, int new_mtu) if (!netif_running(dev)) return 0; - /* Bring the interface down and then back up */ + /* Bring interface down, change mtu and bring interface back up */ xgmac_stop(dev); + dev->mtu = new_mtu; return xgmac_open(dev); } diff --git a/drivers/net/ethernet/chelsio/cxgb/common.h b/drivers/net/ethernet/chelsio/cxgb/common.h index 5ccbed1784d2020b4683881f26a1a99ab472ad9a..8abb46b39032df4bc13fe3ad4b0f227f9d2411e1 100644 --- a/drivers/net/ethernet/chelsio/cxgb/common.h +++ b/drivers/net/ethernet/chelsio/cxgb/common.h @@ -324,30 +324,30 @@ static inline unsigned int core_ticks_per_usec(const adapter_t *adap) return board_info(adap)->clock_core / 1000000; } -extern int __t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp); -extern int __t1_tpi_write(adapter_t *adapter, u32 addr, u32 value); -extern int t1_tpi_write(adapter_t *adapter, u32 addr, u32 value); -extern int t1_tpi_read(adapter_t *adapter, u32 addr, u32 *value); - -extern void t1_interrupts_enable(adapter_t *adapter); -extern void t1_interrupts_disable(adapter_t *adapter); -extern void t1_interrupts_clear(adapter_t *adapter); -extern int t1_elmer0_ext_intr_handler(adapter_t *adapter); -extern void t1_elmer0_ext_intr(adapter_t *adapter); -extern int t1_slow_intr_handler(adapter_t *adapter); - -extern int t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc); -extern const struct board_info *t1_get_board_info(unsigned int board_id); -extern const struct board_info *t1_get_board_info_from_ids(unsigned int devid, +int __t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp); +int __t1_tpi_write(adapter_t *adapter, u32 addr, u32 value); +int t1_tpi_write(adapter_t *adapter, u32 addr, u32 value); +int t1_tpi_read(adapter_t *adapter, u32 addr, u32 *value); + +void t1_interrupts_enable(adapter_t *adapter); +void t1_interrupts_disable(adapter_t *adapter); +void t1_interrupts_clear(adapter_t *adapter); +int t1_elmer0_ext_intr_handler(adapter_t *adapter); +void t1_elmer0_ext_intr(adapter_t *adapter); +int t1_slow_intr_handler(adapter_t *adapter); + +int t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc); +const struct board_info *t1_get_board_info(unsigned int board_id); +const struct board_info *t1_get_board_info_from_ids(unsigned int devid, unsigned short ssid); -extern int t1_seeprom_read(adapter_t *adapter, u32 addr, __le32 *data); -extern int t1_get_board_rev(adapter_t *adapter, const struct board_info *bi, +int t1_seeprom_read(adapter_t *adapter, u32 addr, __le32 *data); +int t1_get_board_rev(adapter_t *adapter, const struct board_info *bi, struct adapter_params *p); -extern int t1_init_hw_modules(adapter_t *adapter); -extern int t1_init_sw_modules(adapter_t *adapter, const struct board_info *bi); -extern void t1_free_sw_modules(adapter_t *adapter); -extern void t1_fatal_err(adapter_t *adapter); -extern void t1_link_changed(adapter_t *adapter, int port_id); -extern void t1_link_negotiated(adapter_t *adapter, int port_id, int link_stat, +int t1_init_hw_modules(adapter_t *adapter); +int t1_init_sw_modules(adapter_t *adapter, const struct board_info *bi); +void t1_free_sw_modules(adapter_t *adapter); +void t1_fatal_err(adapter_t *adapter); +void t1_link_changed(adapter_t *adapter, int port_id); +void t1_link_negotiated(adapter_t *adapter, int port_id, int link_stat, int speed, int duplex, int pause); #endif /* _CXGB_COMMON_H_ */ diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index d7048db9863d905a57b23b9bc985a4c3d27f9bc9..1d021059f097579b9306e458e2c8a5cd92a41948 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -1168,7 +1168,6 @@ out_free_dev: pci_release_regions(pdev); out_disable_pdev: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); return err; } @@ -1347,7 +1346,6 @@ static void remove_one(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); t1_sw_reset(pdev); } diff --git a/drivers/net/ethernet/chelsio/cxgb/pm3393.c b/drivers/net/ethernet/chelsio/cxgb/pm3393.c index 40c7b93ababc3d39d09e6f0ef9ba8e61af241955..eb33a31b08a01487dc17465ffbc7a4505030b9e2 100644 --- a/drivers/net/ethernet/chelsio/cxgb/pm3393.c +++ b/drivers/net/ethernet/chelsio/cxgb/pm3393.c @@ -499,7 +499,7 @@ static const struct cmac_statistics *pm3393_update_statistics(struct cmac *mac, static int pm3393_macaddress_get(struct cmac *cmac, u8 mac_addr[6]) { - memcpy(mac_addr, cmac->instance->mac_addr, 6); + memcpy(mac_addr, cmac->instance->mac_addr, ETH_ALEN); return 0; } @@ -526,7 +526,7 @@ static int pm3393_macaddress_set(struct cmac *cmac, u8 ma[6]) */ /* Store local copy */ - memcpy(cmac->instance->mac_addr, ma, 6); + memcpy(cmac->instance->mac_addr, ma, ETH_ALEN); lo = ((u32) ma[1] << 8) | (u32) ma[0]; mid = ((u32) ma[3] << 8) | (u32) ma[2]; diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index b650951791dd46e81c35e972354ba133d48ee865..45d77334d7d9a6c1a053be81599edcbf8b74c3b7 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -3374,7 +3374,6 @@ out_release_regions: pci_release_regions(pdev); out_disable_device: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); out: return err; } @@ -3415,7 +3414,6 @@ static void remove_one(struct pci_dev *pdev) kfree(adapter); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } } diff --git a/drivers/net/ethernet/chelsio/cxgb3/regs.h b/drivers/net/ethernet/chelsio/cxgb3/regs.h index 6990f6c6522164a7e602903554f3c8e3ccde83fc..81029b872bdd432a0d95f00048fa8f61143ceaa7 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/regs.h +++ b/drivers/net/ethernet/chelsio/cxgb3/regs.h @@ -685,10 +685,6 @@ #define V_BUSY(x) ((x) << S_BUSY) #define F_BUSY V_BUSY(1U) -#define S_BUSY 31 -#define V_BUSY(x) ((x) << S_BUSY) -#define F_BUSY V_BUSY(1U) - #define A_MC7_EXT_MODE1 0x108 #define A_MC7_EXT_MODE2 0x10c @@ -749,14 +745,6 @@ #define A_MC7_CAL 0x128 -#define S_BUSY 31 -#define V_BUSY(x) ((x) << S_BUSY) -#define F_BUSY V_BUSY(1U) - -#define S_BUSY 31 -#define V_BUSY(x) ((x) << S_BUSY) -#define F_BUSY V_BUSY(1U) - #define S_CAL_FAULT 30 #define V_CAL_FAULT(x) ((x) << S_CAL_FAULT) #define F_CAL_FAULT V_CAL_FAULT(1U) @@ -815,9 +803,6 @@ #define V_OP(x) ((x) << S_OP) #define F_OP V_OP(1U) -#define F_OP V_OP(1U) -#define A_SF_OP 0x6dc - #define A_MC7_BIST_ADDR_BEG 0x168 #define A_MC7_BIST_ADDR_END 0x16c @@ -830,8 +815,6 @@ #define V_CONT(x) ((x) << S_CONT) #define F_CONT V_CONT(1U) -#define F_CONT V_CONT(1U) - #define A_MC7_INT_ENABLE 0x178 #define S_AE 17 @@ -1017,8 +1000,6 @@ #define V_NICMODE(x) ((x) << S_NICMODE) #define F_NICMODE V_NICMODE(1U) -#define F_NICMODE V_NICMODE(1U) - #define S_IPV6ENABLE 15 #define V_IPV6ENABLE(x) ((x) << S_IPV6ENABLE) #define F_IPV6ENABLE V_IPV6ENABLE(1U) @@ -1561,28 +1542,16 @@ #define A_ULPRX_STAG_ULIMIT 0x530 -#define A_ULPRX_RQ_LLIMIT 0x534 #define A_ULPRX_RQ_LLIMIT 0x534 -#define A_ULPRX_RQ_ULIMIT 0x538 #define A_ULPRX_RQ_ULIMIT 0x538 #define A_ULPRX_PBL_LLIMIT 0x53c -#define A_ULPRX_PBL_ULIMIT 0x540 #define A_ULPRX_PBL_ULIMIT 0x540 #define A_ULPRX_TDDP_TAGMASK 0x524 -#define A_ULPRX_RQ_LLIMIT 0x534 -#define A_ULPRX_RQ_LLIMIT 0x534 - -#define A_ULPRX_RQ_ULIMIT 0x538 -#define A_ULPRX_RQ_ULIMIT 0x538 - -#define A_ULPRX_PBL_ULIMIT 0x540 -#define A_ULPRX_PBL_ULIMIT 0x540 - #define A_ULPTX_CONFIG 0x580 #define S_CFG_CQE_SOP_MASK 1 @@ -2053,8 +2022,6 @@ #define V_TMMODE(x) ((x) << S_TMMODE) #define F_TMMODE V_TMMODE(1U) -#define F_TMMODE V_TMMODE(1U) - #define A_MC5_DB_ROUTING_TABLE_INDEX 0x70c #define A_MC5_DB_FILTER_TABLE 0x710 @@ -2454,8 +2421,6 @@ #define V_TXACTENABLE(x) ((x) << S_TXACTENABLE) #define F_TXACTENABLE V_TXACTENABLE(1U) -#define A_XGM_SERDES_CTRL0 0x8e0 - #define S_RESET3 23 #define V_RESET3(x) ((x) << S_RESET3) #define F_RESET3 V_RESET3(1U) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index dfd1e36f57531f0e9e9cfeed14048460d96f6e30..ecd2fb3ef69596146a7cf155323e624e397b6834 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -48,7 +48,6 @@ #include #include #include "cxgb4_uld.h" -#include "t4_hw.h" #define FW_VERSION_MAJOR 1 #define FW_VERSION_MINOR 4 diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index c73cabdbd4c08a22fd506eef8a219f02833fb4b3..8b929eeecd2d37cd3b41e32d57bd1fa50402a230 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -3983,6 +3983,7 @@ static int cxgb4_inet6addr_handler(struct notifier_block *this, struct net_device *event_dev; int ret = NOTIFY_DONE; struct bonding *bond = netdev_priv(ifa->idev->dev); + struct list_head *iter; struct slave *slave; struct pci_dev *first_pdev = NULL; @@ -3995,7 +3996,7 @@ static int cxgb4_inet6addr_handler(struct notifier_block *this, * in all of them only once. */ read_lock(&bond->lock); - bond_for_each_slave(bond, slave) { + bond_for_each_slave(bond, slave, iter) { if (!first_pdev) { ret = clip_add(slave->dev, ifa, event); /* If clip_add is success then only initialize @@ -6074,7 +6075,6 @@ sriov: pci_disable_device(pdev); out_release_regions: pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); return err; } @@ -6122,7 +6122,6 @@ static void remove_one(struct pci_dev *pdev) pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); } else pci_release_regions(pdev); } diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 40c22e7de15c2be52482b990354274bbc1fc3175..5f90ec5f7519a4ac6e7916396cff31446f5c614b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -2782,11 +2782,9 @@ err_unmap_bar: err_free_adapter: kfree(adapter); - pci_set_drvdata(pdev, NULL); err_release_regions: pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); pci_clear_master(pdev); err_disable_device: @@ -2851,7 +2849,6 @@ static void cxgb4vf_pci_remove(struct pci_dev *pdev) } iounmap(adapter->regs); kfree(adapter); - pci_set_drvdata(pdev, NULL); } /* @@ -2908,7 +2905,7 @@ static void cxgb4vf_pci_shutdown(struct pci_dev *pdev) #define CH_DEVICE(devid, idx) \ { PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, PCI_ANY_ID, 0, 0, idx } -static struct pci_device_id cxgb4vf_pci_tbl[] = { +static DEFINE_PCI_DEVICE_TABLE(cxgb4vf_pci_tbl) = { CH_DEVICE(0xb000, 0), /* PE10K FPGA */ CH_DEVICE(0x4800, 0), /* T440-dbg */ CH_DEVICE(0x4801, 0), /* T420-cr */ diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index df296af20bd52d1052a1c05200841d55dc4acde1..8475c4cda9e4ca72ef8a2b309788620e82fb67d3 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -1396,8 +1396,9 @@ static inline void copy_frags(struct sk_buff *skb, * Builds an sk_buff from the given packet gather list. Returns the * sk_buff or %NULL if sk_buff allocation failed. */ -struct sk_buff *t4vf_pktgl_to_skb(const struct pkt_gl *gl, - unsigned int skb_len, unsigned int pull_len) +static struct sk_buff *t4vf_pktgl_to_skb(const struct pkt_gl *gl, + unsigned int skb_len, + unsigned int pull_len) { struct sk_buff *skb; @@ -1443,7 +1444,7 @@ out: * Releases the pages of a packet gather list. We do not own the last * page on the list and do not free it. */ -void t4vf_pktgl_free(const struct pkt_gl *gl) +static void t4vf_pktgl_free(const struct pkt_gl *gl) { int frag; @@ -1640,7 +1641,7 @@ static inline void rspq_next(struct sge_rspq *rspq) * on this queue. If the system is under memory shortage use a fairly * long delay to help recovery. */ -int process_responses(struct sge_rspq *rspq, int budget) +static int process_responses(struct sge_rspq *rspq, int budget) { struct sge_eth_rxq *rxq = container_of(rspq, struct sge_eth_rxq, rspq); int budget_left = budget; @@ -1893,7 +1894,7 @@ static unsigned int process_intrq(struct adapter *adapter) * The MSI interrupt handler handles data events from SGE response queues as * well as error and other async events as they all use the same MSI vector. */ -irqreturn_t t4vf_intr_msi(int irq, void *cookie) +static irqreturn_t t4vf_intr_msi(int irq, void *cookie) { struct adapter *adapter = cookie; diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 7b756cf9474a90497de9987591c57cabfcc09f2d..ff78dfaec5087184021b863174f84adfeea59476 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -2309,7 +2309,6 @@ err_out_release_regions: err_out_disable_device: pci_disable_device(pdev); err_out_free_netdev: - pci_set_drvdata(pdev, NULL); free_netdev(netdev); return err; @@ -2338,7 +2337,6 @@ static void enic_remove(struct pci_dev *pdev) enic_iounmap(enic); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(netdev); } } diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index a7a941b1a655a3bbf82baa949c1fe81d17458701..7080ad6c401409199b091021aeed398dda61062c 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -1623,7 +1623,7 @@ dm9000_probe(struct platform_device *pdev) if (!is_valid_ether_addr(ndev->dev_addr) && pdata != NULL) { mac_src = "platform data"; - memcpy(ndev->dev_addr, pdata->dev_addr, 6); + memcpy(ndev->dev_addr, pdata->dev_addr, ETH_ALEN); } if (!is_valid_ether_addr(ndev->dev_addr)) { diff --git a/drivers/net/ethernet/dec/tulip/de2104x.c b/drivers/net/ethernet/dec/tulip/de2104x.c index eaab73cf27caffbbabb2904c47be6fc7941f5303..38148b0e3a952a348977de0b2d459b9b4436eee6 100644 --- a/drivers/net/ethernet/dec/tulip/de2104x.c +++ b/drivers/net/ethernet/dec/tulip/de2104x.c @@ -2110,7 +2110,6 @@ static void de_remove_one(struct pci_dev *pdev) iounmap(de->regs); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(dev); } diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c index 263b92c00cbfb34dbc3b1904cd2ddacafdeb48cc..c05b66dfcc30bc9c8e6f300f3d1c47300cd73808 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.c +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -2328,7 +2328,7 @@ static void de4x5_pci_remove(struct pci_dev *pdev) pci_disable_device (pdev); } -static struct pci_device_id de4x5_pci_tbl[] = { +static DEFINE_PCI_DEVICE_TABLE(de4x5_pci_tbl) = { { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_PLUS, diff --git a/drivers/net/ethernet/dec/tulip/dmfe.c b/drivers/net/ethernet/dec/tulip/dmfe.c index 83139307861cd720edfd78d71d7abbe6b2265ff5..5ad9e3e3c0b8a5095564ae4c4369c3ac531c9591 100644 --- a/drivers/net/ethernet/dec/tulip/dmfe.c +++ b/drivers/net/ethernet/dec/tulip/dmfe.c @@ -523,7 +523,6 @@ err_out_res: err_out_disable: pci_disable_device(pdev); err_out_free: - pci_set_drvdata(pdev, NULL); free_netdev(dev); return err; @@ -548,8 +547,6 @@ static void dmfe_remove_one(struct pci_dev *pdev) db->buf_pool_ptr, db->buf_pool_dma_ptr); pci_release_regions(pdev); free_netdev(dev); /* free board information */ - - pci_set_drvdata(pdev, NULL); } DMFE_DBUG(0, "dmfe_remove_one() exit", 0); diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 4e8cfa2ac803abb13c9c692ef971618cc26d664b..add05f14b38be17311f3344bd23860064fac8ce1 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -1939,7 +1939,6 @@ static void tulip_remove_one(struct pci_dev *pdev) pci_iounmap(pdev, tp->base_addr); free_netdev (dev); pci_release_regions (pdev); - pci_set_drvdata (pdev, NULL); /* pci_power_off (pdev, -1); */ } diff --git a/drivers/net/ethernet/dec/tulip/uli526x.c b/drivers/net/ethernet/dec/tulip/uli526x.c index 93845afe1cea105f21751c4bea54100405799b42..a5397b130724faa9bb4fb9d54881226eb03695bd 100644 --- a/drivers/net/ethernet/dec/tulip/uli526x.c +++ b/drivers/net/ethernet/dec/tulip/uli526x.c @@ -429,7 +429,6 @@ err_out_release: err_out_disable: pci_disable_device(pdev); err_out_free: - pci_set_drvdata(pdev, NULL); free_netdev(dev); return err; @@ -450,7 +449,6 @@ static void uli526x_remove_one(struct pci_dev *pdev) db->buf_pool_ptr, db->buf_pool_dma_ptr); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(dev); } diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c index c7b04ecf5b497ea26f969ce52eeaffccfc0ee5ef..62fe512bb2167fb682f63e26e2678e6a5df0cb6d 100644 --- a/drivers/net/ethernet/dec/tulip/winbond-840.c +++ b/drivers/net/ethernet/dec/tulip/winbond-840.c @@ -468,7 +468,6 @@ static int w840_probe1(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; err_out_cleardev: - pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, ioaddr); err_out_free_res: pci_release_regions(pdev); @@ -1542,8 +1541,6 @@ static void w840_remove1(struct pci_dev *pdev) pci_iounmap(pdev, np->base_addr); free_netdev(dev); } - - pci_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM diff --git a/drivers/net/ethernet/dec/tulip/xircom_cb.c b/drivers/net/ethernet/dec/tulip/xircom_cb.c index 9b84cb04fe5fccded9f26fe010cf16ea6bb36c8a..ab7ebac6fbea0e6fe579b29d398b2c73fc369d4b 100644 --- a/drivers/net/ethernet/dec/tulip/xircom_cb.c +++ b/drivers/net/ethernet/dec/tulip/xircom_cb.c @@ -289,7 +289,6 @@ out: err_unmap: pci_iounmap(pdev, private->ioaddr); reg_fail: - pci_set_drvdata(pdev, NULL); dma_free_coherent(d, 8192, private->tx_buffer, private->tx_dma_handle); tx_buf_fail: dma_free_coherent(d, 8192, private->rx_buffer, private->rx_dma_handle); @@ -317,7 +316,6 @@ static void xircom_remove(struct pci_dev *pdev) unregister_netdev(dev); pci_iounmap(pdev, card->ioaddr); - pci_set_drvdata(pdev, NULL); dma_free_coherent(d, 8192, card->tx_buffer, card->tx_dma_handle); dma_free_coherent(d, 8192, card->rx_buffer, card->rx_dma_handle); free_netdev(dev); diff --git a/drivers/net/ethernet/dlink/dl2k.c b/drivers/net/ethernet/dlink/dl2k.c index afa8e3af2c4d6840a9eee8c9c46d0e3f6df22a02..4fb756d219f700bfb82a37d62e9cb078fca22024 100644 --- a/drivers/net/ethernet/dlink/dl2k.c +++ b/drivers/net/ethernet/dlink/dl2k.c @@ -1746,7 +1746,6 @@ rio_remove1 (struct pci_dev *pdev) pci_release_regions (pdev); pci_disable_device (pdev); } - pci_set_drvdata (pdev, NULL); } static struct pci_driver rio_driver = { diff --git a/drivers/net/ethernet/dlink/sundance.c b/drivers/net/ethernet/dlink/sundance.c index bf3bf6f22c998b7c7ef2563be2041737a15959fe..113cd799a131f925fcbd3b5946d4f185249d376f 100644 --- a/drivers/net/ethernet/dlink/sundance.c +++ b/drivers/net/ethernet/dlink/sundance.c @@ -703,7 +703,6 @@ err_out_unmap_tx: dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring, np->tx_ring_dma); err_out_cleardev: - pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, ioaddr); err_out_res: pci_release_regions(pdev); @@ -1941,7 +1940,6 @@ static void sundance_remove1(struct pci_dev *pdev) pci_iounmap(pdev, np->base); pci_release_regions(pdev); free_netdev(dev); - pci_set_drvdata(pdev, NULL); } } diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index c99dac6a9ddf22131ed9f68e1fe22e018a0c8fcc..f4825db5d1792419924bab4f9d08e17abbfd6128 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -34,7 +34,7 @@ #include "be_hw.h" #include "be_roce.h" -#define DRV_VER "4.9.134.0u" +#define DRV_VER "4.9.224.0u" #define DRV_NAME "be2net" #define BE_NAME "Emulex BladeEngine2" #define BE3_NAME "Emulex BladeEngine3" @@ -89,7 +89,7 @@ static inline char *nic_name(struct pci_dev *pdev) #define BE_NUM_VLANS_SUPPORTED 64 #define BE_UMC_NUM_VLANS_SUPPORTED 15 -#define BE_MAX_EQD 96u +#define BE_MAX_EQD 128u #define BE_MAX_TX_FRAG_COUNT 30 #define EVNT_Q_LEN 1024 @@ -199,8 +199,37 @@ struct be_eq_obj { u16 spurious_intr; struct napi_struct napi; struct be_adapter *adapter; + +#ifdef CONFIG_NET_RX_BUSY_POLL +#define BE_EQ_IDLE 0 +#define BE_EQ_NAPI 1 /* napi owns this EQ */ +#define BE_EQ_POLL 2 /* poll owns this EQ */ +#define BE_EQ_LOCKED (BE_EQ_NAPI | BE_EQ_POLL) +#define BE_EQ_NAPI_YIELD 4 /* napi yielded this EQ */ +#define BE_EQ_POLL_YIELD 8 /* poll yielded this EQ */ +#define BE_EQ_YIELD (BE_EQ_NAPI_YIELD | BE_EQ_POLL_YIELD) +#define BE_EQ_USER_PEND (BE_EQ_POLL | BE_EQ_POLL_YIELD) + unsigned int state; + spinlock_t lock; /* lock to serialize napi and busy-poll */ +#endif /* CONFIG_NET_RX_BUSY_POLL */ } ____cacheline_aligned_in_smp; +struct be_aic_obj { /* Adaptive interrupt coalescing (AIC) info */ + bool enable; + u32 min_eqd; /* in usecs */ + u32 max_eqd; /* in usecs */ + u32 prev_eqd; /* in usecs */ + u32 et_eqd; /* configured val when aic is off */ + ulong jiffies; + u64 rx_pkts_prev; /* Used to calculate RX pps */ + u64 tx_reqs_prev; /* Used to calculate TX pps */ +}; + +enum { + NAPI_POLLING, + BUSY_POLLING +}; + struct be_mcc_obj { struct be_queue_info q; struct be_queue_info cq; @@ -215,6 +244,7 @@ struct be_tx_stats { u64 tx_compl; ulong tx_jiffies; u32 tx_stops; + u32 tx_drv_drops; /* pkts dropped by driver */ struct u64_stats_sync sync; struct u64_stats_sync sync_compl; }; @@ -239,15 +269,12 @@ struct be_rx_page_info { struct be_rx_stats { u64 rx_bytes; u64 rx_pkts; - u64 rx_pkts_prev; - ulong rx_jiffies; u32 rx_drops_no_skbs; /* skb allocation errors */ u32 rx_drops_no_frags; /* HW has no fetched frags */ u32 rx_post_fail; /* page post alloc failures */ u32 rx_compl; u32 rx_mcast_pkts; u32 rx_compl_err; /* completions with err set */ - u32 rx_pps; /* pkts per second */ struct u64_stats_sync sync; }; @@ -316,6 +343,11 @@ struct be_drv_stats { u32 rx_input_fifo_overflow_drop; u32 pmem_fifo_overflow_drop; u32 jabber_events; + u32 rx_roce_bytes_lsd; + u32 rx_roce_bytes_msd; + u32 rx_roce_frames; + u32 roce_drops_payload_len; + u32 roce_drops_crc; }; struct be_vf_cfg { @@ -405,6 +437,7 @@ struct be_adapter { u32 big_page_size; /* Compounded page size shared by rx wrbs */ struct be_drv_stats drv_stats; + struct be_aic_obj aic_obj[MAX_EVT_QS]; u16 vlans_added; u8 vlan_tag[VLAN_N_VID]; u8 vlan_prio_bmap; /* Available Priority BitMap */ @@ -437,7 +470,6 @@ struct be_adapter { u32 rx_fc; /* Rx flow control */ u32 tx_fc; /* Tx flow control */ bool stats_cmd_sent; - u32 if_type; struct { u32 size; u32 total_size; @@ -472,8 +504,8 @@ struct be_adapter { #define be_physfn(adapter) (!adapter->virtfn) #define sriov_enabled(adapter) (adapter->num_vfs > 0) -#define sriov_want(adapter) (be_max_vfs(adapter) && num_vfs && \ - be_physfn(adapter)) +#define sriov_want(adapter) (be_physfn(adapter) && \ + (num_vfs || pci_num_vf(adapter->pdev))) #define for_all_vfs(adapter, vf_cfg, i) \ for (i = 0, vf_cfg = &adapter->vf_cfg[i]; i < adapter->num_vfs; \ i++, vf_cfg++) @@ -546,6 +578,10 @@ extern const struct ethtool_ops be_ethtool_ops; for (i = 0, eqo = &adapter->eq_obj[i]; i < adapter->num_evt_qs; \ i++, eqo++) +#define for_all_rx_queues_on_eq(adapter, eqo, rxo, i) \ + for (i = eqo->idx, rxo = &adapter->rx_obj[i]; i < adapter->num_rx_qs;\ + i += adapter->num_evt_qs, rxo += adapter->num_evt_qs) + #define is_mcc_eqo(eqo) (eqo->idx == 0) #define mcc_eqo(adapter) (&adapter->eq_obj[0]) @@ -696,6 +732,115 @@ static inline int qnq_async_evt_rcvd(struct be_adapter *adapter) return adapter->flags & BE_FLAGS_QNQ_ASYNC_EVT_RCVD; } +#ifdef CONFIG_NET_RX_BUSY_POLL +static inline bool be_lock_napi(struct be_eq_obj *eqo) +{ + bool status = true; + + spin_lock(&eqo->lock); /* BH is already disabled */ + if (eqo->state & BE_EQ_LOCKED) { + WARN_ON(eqo->state & BE_EQ_NAPI); + eqo->state |= BE_EQ_NAPI_YIELD; + status = false; + } else { + eqo->state = BE_EQ_NAPI; + } + spin_unlock(&eqo->lock); + return status; +} + +static inline void be_unlock_napi(struct be_eq_obj *eqo) +{ + spin_lock(&eqo->lock); /* BH is already disabled */ + + WARN_ON(eqo->state & (BE_EQ_POLL | BE_EQ_NAPI_YIELD)); + eqo->state = BE_EQ_IDLE; + + spin_unlock(&eqo->lock); +} + +static inline bool be_lock_busy_poll(struct be_eq_obj *eqo) +{ + bool status = true; + + spin_lock_bh(&eqo->lock); + if (eqo->state & BE_EQ_LOCKED) { + eqo->state |= BE_EQ_POLL_YIELD; + status = false; + } else { + eqo->state |= BE_EQ_POLL; + } + spin_unlock_bh(&eqo->lock); + return status; +} + +static inline void be_unlock_busy_poll(struct be_eq_obj *eqo) +{ + spin_lock_bh(&eqo->lock); + + WARN_ON(eqo->state & (BE_EQ_NAPI)); + eqo->state = BE_EQ_IDLE; + + spin_unlock_bh(&eqo->lock); +} + +static inline void be_enable_busy_poll(struct be_eq_obj *eqo) +{ + spin_lock_init(&eqo->lock); + eqo->state = BE_EQ_IDLE; +} + +static inline void be_disable_busy_poll(struct be_eq_obj *eqo) +{ + local_bh_disable(); + + /* It's enough to just acquire napi lock on the eqo to stop + * be_busy_poll() from processing any queueus. + */ + while (!be_lock_napi(eqo)) + mdelay(1); + + local_bh_enable(); +} + +#else /* CONFIG_NET_RX_BUSY_POLL */ + +static inline bool be_lock_napi(struct be_eq_obj *eqo) +{ + return true; +} + +static inline void be_unlock_napi(struct be_eq_obj *eqo) +{ +} + +static inline bool be_lock_busy_poll(struct be_eq_obj *eqo) +{ + return false; +} + +static inline void be_unlock_busy_poll(struct be_eq_obj *eqo) +{ +} + +static inline void be_enable_busy_poll(struct be_eq_obj *eqo) +{ +} + +static inline void be_disable_busy_poll(struct be_eq_obj *eqo) +{ +} +#endif /* CONFIG_NET_RX_BUSY_POLL */ + +void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm, + u16 num_popped); +void be_link_status_update(struct be_adapter *adapter, u8 link_status); +void be_parse_stats(struct be_adapter *adapter); +int be_load_fw(struct be_adapter *adapter, u8 *func); +bool be_is_wol_supported(struct be_adapter *adapter); +bool be_pause_supported(struct be_adapter *adapter); +u32 be_get_fw_log_level(struct be_adapter *adapter); + static inline int fw_major_num(const char *fw_ver) { int fw_major = 0; @@ -705,27 +850,19 @@ static inline int fw_major_num(const char *fw_ver) return fw_major; } -extern void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm, - u16 num_popped); -extern void be_link_status_update(struct be_adapter *adapter, u8 link_status); -extern void be_parse_stats(struct be_adapter *adapter); -extern int be_load_fw(struct be_adapter *adapter, u8 *func); -extern bool be_is_wol_supported(struct be_adapter *adapter); -extern bool be_pause_supported(struct be_adapter *adapter); -extern u32 be_get_fw_log_level(struct be_adapter *adapter); int be_update_queues(struct be_adapter *adapter); int be_poll(struct napi_struct *napi, int budget); /* * internal function to initialize-cleanup roce device. */ -extern void be_roce_dev_add(struct be_adapter *); -extern void be_roce_dev_remove(struct be_adapter *); +void be_roce_dev_add(struct be_adapter *); +void be_roce_dev_remove(struct be_adapter *); /* * internal function to open-close roce device during ifup-ifdown. */ -extern void be_roce_dev_open(struct be_adapter *); -extern void be_roce_dev_close(struct be_adapter *); +void be_roce_dev_open(struct be_adapter *); +void be_roce_dev_close(struct be_adapter *); #endif /* BE_H */ diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index c08fd32bb8e50abd5ba37a7029641df6bfcb80d9..7fb0edfe3d2482514f32f48afef1a5a1c46e9805 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -522,7 +522,7 @@ static u16 be_POST_stage_get(struct be_adapter *adapter) return sem & POST_STAGE_MASK; } -int lancer_wait_ready(struct be_adapter *adapter) +static int lancer_wait_ready(struct be_adapter *adapter) { #define SLIPORT_READY_TIMEOUT 30 u32 sliport_status; @@ -1436,8 +1436,12 @@ int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd) OPCODE_ETH_GET_STATISTICS, nonemb_cmd->size, wrb, nonemb_cmd); /* version 1 of the cmd is not supported only by BE2 */ - if (!BE2_chip(adapter)) + if (BE2_chip(adapter)) + hdr->version = 0; + if (BE3_chip(adapter) || lancer_chip(adapter)) hdr->version = 1; + else + hdr->version = 2; be_mcc_notify(adapter); adapter->stats_cmd_sent = true; @@ -1719,11 +1723,12 @@ err: /* set the EQ delay interval of an EQ to specified value * Uses async mcc */ -int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd) +int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *set_eqd, + int num) { struct be_mcc_wrb *wrb; struct be_cmd_req_modify_eq_delay *req; - int status = 0; + int status = 0, i; spin_lock_bh(&adapter->mcc_lock); @@ -1737,13 +1742,15 @@ int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd) be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_MODIFY_EQ_DELAY, sizeof(*req), wrb, NULL); - req->num_eq = cpu_to_le32(1); - req->delay[0].eq_id = cpu_to_le32(eq_id); - req->delay[0].phase = 0; - req->delay[0].delay_multiplier = cpu_to_le32(eqd); + req->num_eq = cpu_to_le32(num); + for (i = 0; i < num; i++) { + req->set_eqd[i].eq_id = cpu_to_le32(set_eqd[i].eq_id); + req->set_eqd[i].phase = 0; + req->set_eqd[i].delay_multiplier = + cpu_to_le32(set_eqd[i].delay_multiplier); + } be_mcc_notify(adapter); - err: spin_unlock_bh(&adapter->mcc_lock); return status; @@ -3520,7 +3527,7 @@ int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain) struct be_cmd_enable_disable_vf *req; int status; - if (!lancer_chip(adapter)) + if (BEx_chip(adapter)) return 0; spin_lock_bh(&adapter->mcc_lock); diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 108ca8abf0af2321a6f5db5fe289af658b0bfcdf..edf3e8a0ff839c069bc05154f85c1e68e89544f2 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -1057,14 +1057,16 @@ struct be_cmd_resp_get_flow_control { } __packed; /******************** Modify EQ Delay *******************/ +struct be_set_eqd { + u32 eq_id; + u32 phase; + u32 delay_multiplier; +}; + struct be_cmd_req_modify_eq_delay { struct be_cmd_req_hdr hdr; u32 num_eq; - struct { - u32 eq_id; - u32 phase; - u32 delay_multiplier; - } delay[8]; + struct be_set_eqd set_eqd[MAX_EVT_QS]; } __packed; struct be_cmd_resp_modify_eq_delay { @@ -1660,6 +1662,67 @@ struct be_erx_stats_v1 { u32 rsvd[4]; }; +struct be_port_rxf_stats_v2 { + u32 rsvd0[10]; + u32 roce_bytes_received_lsd; + u32 roce_bytes_received_msd; + u32 rsvd1[5]; + u32 roce_frames_received; + u32 rx_crc_errors; + u32 rx_alignment_symbol_errors; + u32 rx_pause_frames; + u32 rx_priority_pause_frames; + u32 rx_control_frames; + u32 rx_in_range_errors; + u32 rx_out_range_errors; + u32 rx_frame_too_long; + u32 rx_address_filtered; + u32 rx_dropped_too_small; + u32 rx_dropped_too_short; + u32 rx_dropped_header_too_small; + u32 rx_dropped_tcp_length; + u32 rx_dropped_runt; + u32 rsvd2[10]; + u32 rx_ip_checksum_errs; + u32 rx_tcp_checksum_errs; + u32 rx_udp_checksum_errs; + u32 rsvd3[7]; + u32 rx_switched_unicast_packets; + u32 rx_switched_multicast_packets; + u32 rx_switched_broadcast_packets; + u32 rsvd4[3]; + u32 tx_pauseframes; + u32 tx_priority_pauseframes; + u32 tx_controlframes; + u32 rsvd5[10]; + u32 rxpp_fifo_overflow_drop; + u32 rx_input_fifo_overflow_drop; + u32 pmem_fifo_overflow_drop; + u32 jabber_events; + u32 rsvd6[3]; + u32 rx_drops_payload_size; + u32 rx_drops_clipped_header; + u32 rx_drops_crc; + u32 roce_drops_payload_len; + u32 roce_drops_crc; + u32 rsvd7[19]; +}; + +struct be_rxf_stats_v2 { + struct be_port_rxf_stats_v2 port[4]; + u32 rsvd0[2]; + u32 rx_drops_no_pbuf; + u32 rx_drops_no_txpb; + u32 rx_drops_no_erx_descr; + u32 rx_drops_no_tpre_descr; + u32 rsvd1[6]; + u32 rx_drops_too_many_frags; + u32 rx_drops_invalid_ring; + u32 forwarded_packets; + u32 rx_drops_mtu; + u32 rsvd2[35]; +}; + struct be_hw_stats_v1 { struct be_rxf_stats_v1 rxf; u32 rsvd0[BE_TXP_SW_SZ]; @@ -1678,6 +1741,29 @@ struct be_cmd_resp_get_stats_v1 { struct be_hw_stats_v1 hw_stats; }; +struct be_erx_stats_v2 { + u32 rx_drops_no_fragments[136]; /* dwordS 0 to 135*/ + u32 rsvd[3]; +}; + +struct be_hw_stats_v2 { + struct be_rxf_stats_v2 rxf; + u32 rsvd0[BE_TXP_SW_SZ]; + struct be_erx_stats_v2 erx; + struct be_pmem_stats pmem; + u32 rsvd1[18]; +}; + +struct be_cmd_req_get_stats_v2 { + struct be_cmd_req_hdr hdr; + u8 rsvd[sizeof(struct be_hw_stats_v2)]; +}; + +struct be_cmd_resp_get_stats_v2 { + struct be_cmd_resp_hdr hdr; + struct be_hw_stats_v2 hw_stats; +}; + /************** get fat capabilites *******************/ #define MAX_MODULES 27 #define MAX_MODES 4 @@ -1865,137 +1951,119 @@ struct be_cmd_resp_get_iface_list { struct be_if_desc if_desc; }; -extern int be_pci_fnum_get(struct be_adapter *adapter); -extern int be_fw_wait_ready(struct be_adapter *adapter); -extern int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr, - bool permanent, u32 if_handle, u32 pmac_id); -extern int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr, - u32 if_id, u32 *pmac_id, u32 domain); -extern int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, - int pmac_id, u32 domain); -extern int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, - u32 en_flags, u32 *if_handle, u32 domain); -extern int be_cmd_if_destroy(struct be_adapter *adapter, int if_handle, - u32 domain); -extern int be_cmd_eq_create(struct be_adapter *adapter, struct be_eq_obj *eqo); -extern int be_cmd_cq_create(struct be_adapter *adapter, - struct be_queue_info *cq, struct be_queue_info *eq, - bool no_delay, int num_cqe_dma_coalesce); -extern int be_cmd_mccq_create(struct be_adapter *adapter, - struct be_queue_info *mccq, - struct be_queue_info *cq); -extern int be_cmd_txq_create(struct be_adapter *adapter, - struct be_tx_obj *txo); -extern int be_cmd_rxq_create(struct be_adapter *adapter, - struct be_queue_info *rxq, u16 cq_id, - u16 frag_size, u32 if_id, u32 rss, u8 *rss_id); -extern int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q, - int type); -extern int be_cmd_rxq_destroy(struct be_adapter *adapter, - struct be_queue_info *q); -extern int be_cmd_link_status_query(struct be_adapter *adapter, u16 *link_speed, - u8 *link_status, u32 dom); -extern int be_cmd_reset(struct be_adapter *adapter); -extern int be_cmd_get_stats(struct be_adapter *adapter, - struct be_dma_mem *nonemb_cmd); -extern int lancer_cmd_get_pport_stats(struct be_adapter *adapter, - struct be_dma_mem *nonemb_cmd); -extern int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver, - char *fw_on_flash); - -extern int be_cmd_modify_eqd(struct be_adapter *adapter, u32 eq_id, u32 eqd); -extern int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, - u16 *vtag_array, u32 num, bool untagged, - bool promiscuous); -extern int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 status); -extern int be_cmd_set_flow_control(struct be_adapter *adapter, - u32 tx_fc, u32 rx_fc); -extern int be_cmd_get_flow_control(struct be_adapter *adapter, - u32 *tx_fc, u32 *rx_fc); -extern int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num, +int be_pci_fnum_get(struct be_adapter *adapter); +int be_fw_wait_ready(struct be_adapter *adapter); +int be_cmd_mac_addr_query(struct be_adapter *adapter, u8 *mac_addr, + bool permanent, u32 if_handle, u32 pmac_id); +int be_cmd_pmac_add(struct be_adapter *adapter, u8 *mac_addr, u32 if_id, + u32 *pmac_id, u32 domain); +int be_cmd_pmac_del(struct be_adapter *adapter, u32 if_id, int pmac_id, + u32 domain); +int be_cmd_if_create(struct be_adapter *adapter, u32 cap_flags, u32 en_flags, + u32 *if_handle, u32 domain); +int be_cmd_if_destroy(struct be_adapter *adapter, int if_handle, u32 domain); +int be_cmd_eq_create(struct be_adapter *adapter, struct be_eq_obj *eqo); +int be_cmd_cq_create(struct be_adapter *adapter, struct be_queue_info *cq, + struct be_queue_info *eq, bool no_delay, + int num_cqe_dma_coalesce); +int be_cmd_mccq_create(struct be_adapter *adapter, struct be_queue_info *mccq, + struct be_queue_info *cq); +int be_cmd_txq_create(struct be_adapter *adapter, struct be_tx_obj *txo); +int be_cmd_rxq_create(struct be_adapter *adapter, struct be_queue_info *rxq, + u16 cq_id, u16 frag_size, u32 if_id, u32 rss, u8 *rss_id); +int be_cmd_q_destroy(struct be_adapter *adapter, struct be_queue_info *q, + int type); +int be_cmd_rxq_destroy(struct be_adapter *adapter, struct be_queue_info *q); +int be_cmd_link_status_query(struct be_adapter *adapter, u16 *link_speed, + u8 *link_status, u32 dom); +int be_cmd_reset(struct be_adapter *adapter); +int be_cmd_get_stats(struct be_adapter *adapter, struct be_dma_mem *nonemb_cmd); +int lancer_cmd_get_pport_stats(struct be_adapter *adapter, + struct be_dma_mem *nonemb_cmd); +int be_cmd_get_fw_ver(struct be_adapter *adapter, char *fw_ver, + char *fw_on_flash); +int be_cmd_modify_eqd(struct be_adapter *adapter, struct be_set_eqd *, int num); +int be_cmd_vlan_config(struct be_adapter *adapter, u32 if_id, u16 *vtag_array, + u32 num, bool untagged, bool promiscuous); +int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 status); +int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc); +int be_cmd_get_flow_control(struct be_adapter *adapter, u32 *tx_fc, u32 *rx_fc); +int be_cmd_query_fw_cfg(struct be_adapter *adapter, u32 *port_num, u32 *function_mode, u32 *function_caps, u16 *asic_rev); -extern int be_cmd_reset_function(struct be_adapter *adapter); -extern int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable, - u32 rss_hash_opts, u16 table_size); -extern int be_process_mcc(struct be_adapter *adapter); -extern int be_cmd_set_beacon_state(struct be_adapter *adapter, - u8 port_num, u8 beacon, u8 status, u8 state); -extern int be_cmd_get_beacon_state(struct be_adapter *adapter, - u8 port_num, u32 *state); -extern int be_cmd_write_flashrom(struct be_adapter *adapter, - struct be_dma_mem *cmd, u32 flash_oper, - u32 flash_opcode, u32 buf_size); -extern int lancer_cmd_write_object(struct be_adapter *adapter, - struct be_dma_mem *cmd, - u32 data_size, u32 data_offset, - const char *obj_name, - u32 *data_written, u8 *change_status, - u8 *addn_status); +int be_cmd_reset_function(struct be_adapter *adapter); +int be_cmd_rss_config(struct be_adapter *adapter, u8 *rsstable, + u32 rss_hash_opts, u16 table_size); +int be_process_mcc(struct be_adapter *adapter); +int be_cmd_set_beacon_state(struct be_adapter *adapter, u8 port_num, u8 beacon, + u8 status, u8 state); +int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, + u32 *state); +int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, + u32 flash_oper, u32 flash_opcode, u32 buf_size); +int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd, + u32 data_size, u32 data_offset, + const char *obj_name, u32 *data_written, + u8 *change_status, u8 *addn_status); int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd, - u32 data_size, u32 data_offset, const char *obj_name, - u32 *data_read, u32 *eof, u8 *addn_status); + u32 data_size, u32 data_offset, const char *obj_name, + u32 *data_read, u32 *eof, u8 *addn_status); int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, - int offset); -extern int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac, - struct be_dma_mem *nonemb_cmd); -extern int be_cmd_fw_init(struct be_adapter *adapter); -extern int be_cmd_fw_clean(struct be_adapter *adapter); -extern void be_async_mcc_enable(struct be_adapter *adapter); -extern void be_async_mcc_disable(struct be_adapter *adapter); -extern int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num, - u32 loopback_type, u32 pkt_size, - u32 num_pkts, u64 pattern); -extern int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern, - u32 byte_cnt, struct be_dma_mem *cmd); -extern int be_cmd_get_seeprom_data(struct be_adapter *adapter, - struct be_dma_mem *nonemb_cmd); -extern int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num, - u8 loopback_type, u8 enable); -extern int be_cmd_get_phy_info(struct be_adapter *adapter); -extern int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain); -extern void be_detect_error(struct be_adapter *adapter); -extern int be_cmd_get_die_temperature(struct be_adapter *adapter); -extern int be_cmd_get_cntl_attributes(struct be_adapter *adapter); -extern int be_cmd_req_native_mode(struct be_adapter *adapter); -extern int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size); -extern void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf); -extern int be_cmd_get_fn_privileges(struct be_adapter *adapter, - u32 *privilege, u32 domain); -extern int be_cmd_set_fn_privileges(struct be_adapter *adapter, - u32 privileges, u32 vf_num); -extern int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac, - bool *pmac_id_active, u32 *pmac_id, - u8 domain); -extern int be_cmd_get_active_mac(struct be_adapter *adapter, u32 pmac_id, - u8 *mac); -extern int be_cmd_get_perm_mac(struct be_adapter *adapter, u8 *mac); -extern int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array, - u8 mac_count, u32 domain); -extern int be_cmd_set_mac(struct be_adapter *adapter, u8 *mac, int if_id, - u32 dom); -extern int be_cmd_set_hsw_config(struct be_adapter *adapter, u16 pvid, - u32 domain, u16 intf_id, u16 hsw_mode); -extern int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid, - u32 domain, u16 intf_id, u8 *mode); -extern int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter); -extern int be_cmd_get_ext_fat_capabilites(struct be_adapter *adapter, - struct be_dma_mem *cmd); -extern int be_cmd_set_ext_fat_capabilites(struct be_adapter *adapter, - struct be_dma_mem *cmd, - struct be_fat_conf_params *cfgs); -extern int lancer_wait_ready(struct be_adapter *adapter); -extern int lancer_physdev_ctrl(struct be_adapter *adapter, u32 mask); -extern int lancer_initiate_dump(struct be_adapter *adapter); -extern bool dump_present(struct be_adapter *adapter); -extern int lancer_test_and_set_rdy_state(struct be_adapter *adapter); -extern int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name); + int offset); +int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac, + struct be_dma_mem *nonemb_cmd); +int be_cmd_fw_init(struct be_adapter *adapter); +int be_cmd_fw_clean(struct be_adapter *adapter); +void be_async_mcc_enable(struct be_adapter *adapter); +void be_async_mcc_disable(struct be_adapter *adapter); +int be_cmd_loopback_test(struct be_adapter *adapter, u32 port_num, + u32 loopback_type, u32 pkt_size, u32 num_pkts, + u64 pattern); +int be_cmd_ddr_dma_test(struct be_adapter *adapter, u64 pattern, u32 byte_cnt, + struct be_dma_mem *cmd); +int be_cmd_get_seeprom_data(struct be_adapter *adapter, + struct be_dma_mem *nonemb_cmd); +int be_cmd_set_loopback(struct be_adapter *adapter, u8 port_num, + u8 loopback_type, u8 enable); +int be_cmd_get_phy_info(struct be_adapter *adapter); +int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain); +void be_detect_error(struct be_adapter *adapter); +int be_cmd_get_die_temperature(struct be_adapter *adapter); +int be_cmd_get_cntl_attributes(struct be_adapter *adapter); +int be_cmd_req_native_mode(struct be_adapter *adapter); +int be_cmd_get_reg_len(struct be_adapter *adapter, u32 *log_size); +void be_cmd_get_regs(struct be_adapter *adapter, u32 buf_len, void *buf); +int be_cmd_get_fn_privileges(struct be_adapter *adapter, u32 *privilege, + u32 domain); +int be_cmd_set_fn_privileges(struct be_adapter *adapter, u32 privileges, + u32 vf_num); +int be_cmd_get_mac_from_list(struct be_adapter *adapter, u8 *mac, + bool *pmac_id_active, u32 *pmac_id, u8 domain); +int be_cmd_get_active_mac(struct be_adapter *adapter, u32 pmac_id, u8 *mac); +int be_cmd_get_perm_mac(struct be_adapter *adapter, u8 *mac); +int be_cmd_set_mac_list(struct be_adapter *adapter, u8 *mac_array, u8 mac_count, + u32 domain); +int be_cmd_set_mac(struct be_adapter *adapter, u8 *mac, int if_id, u32 dom); +int be_cmd_set_hsw_config(struct be_adapter *adapter, u16 pvid, u32 domain, + u16 intf_id, u16 hsw_mode); +int be_cmd_get_hsw_config(struct be_adapter *adapter, u16 *pvid, u32 domain, + u16 intf_id, u8 *mode); +int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter); +int be_cmd_get_ext_fat_capabilites(struct be_adapter *adapter, + struct be_dma_mem *cmd); +int be_cmd_set_ext_fat_capabilites(struct be_adapter *adapter, + struct be_dma_mem *cmd, + struct be_fat_conf_params *cfgs); +int lancer_physdev_ctrl(struct be_adapter *adapter, u32 mask); +int lancer_initiate_dump(struct be_adapter *adapter); +bool dump_present(struct be_adapter *adapter); +int lancer_test_and_set_rdy_state(struct be_adapter *adapter); +int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name); int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res); int be_cmd_get_profile_config(struct be_adapter *adapter, struct be_resources *res, u8 domain); -extern int be_cmd_set_profile_config(struct be_adapter *adapter, u32 bps, - u8 domain); -extern int be_cmd_get_if_id(struct be_adapter *adapter, - struct be_vf_cfg *vf_cfg, int vf_num); -extern int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain); -extern int be_cmd_intr_set(struct be_adapter *adapter, bool intr_enable); +int be_cmd_set_profile_config(struct be_adapter *adapter, u32 bps, u8 domain); +int be_cmd_get_if_id(struct be_adapter *adapter, struct be_vf_cfg *vf_cfg, + int vf_num); +int be_cmd_enable_vf(struct be_adapter *adapter, u8 domain); +int be_cmd_intr_set(struct be_adapter *adapter, bool intr_enable); diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index b440a1fac77b2883b3417eeab76861cc02ad42b9..08330034d9efab6f303025e95a17775caf657f50 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -116,7 +116,12 @@ static const struct be_ethtool_stat et_stats[] = { {DRVSTAT_INFO(rx_drops_mtu)}, /* Number of packets dropped due to random early drop function */ {DRVSTAT_INFO(eth_red_drops)}, - {DRVSTAT_INFO(be_on_die_temperature)} + {DRVSTAT_INFO(be_on_die_temperature)}, + {DRVSTAT_INFO(rx_roce_bytes_lsd)}, + {DRVSTAT_INFO(rx_roce_bytes_msd)}, + {DRVSTAT_INFO(rx_roce_frames)}, + {DRVSTAT_INFO(roce_drops_payload_len)}, + {DRVSTAT_INFO(roce_drops_crc)} }; #define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats) @@ -155,7 +160,9 @@ static const struct be_ethtool_stat et_tx_stats[] = { /* Number of times the TX queue was stopped due to lack * of spaces in the TXQ. */ - {DRVSTAT_TX_INFO(tx_stops)} + {DRVSTAT_TX_INFO(tx_stops)}, + /* Pkts dropped in the driver's transmit path */ + {DRVSTAT_TX_INFO(tx_drv_drops)} }; #define ETHTOOL_TXSTATS_NUM (ARRAY_SIZE(et_tx_stats)) @@ -290,19 +297,19 @@ static int be_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *et) { struct be_adapter *adapter = netdev_priv(netdev); - struct be_eq_obj *eqo = &adapter->eq_obj[0]; + struct be_aic_obj *aic = &adapter->aic_obj[0]; - et->rx_coalesce_usecs = eqo->cur_eqd; - et->rx_coalesce_usecs_high = eqo->max_eqd; - et->rx_coalesce_usecs_low = eqo->min_eqd; + et->rx_coalesce_usecs = aic->prev_eqd; + et->rx_coalesce_usecs_high = aic->max_eqd; + et->rx_coalesce_usecs_low = aic->min_eqd; - et->tx_coalesce_usecs = eqo->cur_eqd; - et->tx_coalesce_usecs_high = eqo->max_eqd; - et->tx_coalesce_usecs_low = eqo->min_eqd; + et->tx_coalesce_usecs = aic->prev_eqd; + et->tx_coalesce_usecs_high = aic->max_eqd; + et->tx_coalesce_usecs_low = aic->min_eqd; - et->use_adaptive_rx_coalesce = eqo->enable_aic; - et->use_adaptive_tx_coalesce = eqo->enable_aic; + et->use_adaptive_rx_coalesce = aic->enable; + et->use_adaptive_tx_coalesce = aic->enable; return 0; } @@ -314,14 +321,17 @@ static int be_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *et) { struct be_adapter *adapter = netdev_priv(netdev); + struct be_aic_obj *aic = &adapter->aic_obj[0]; struct be_eq_obj *eqo; int i; for_all_evt_queues(adapter, eqo, i) { - eqo->enable_aic = et->use_adaptive_rx_coalesce; - eqo->max_eqd = min(et->rx_coalesce_usecs_high, BE_MAX_EQD); - eqo->min_eqd = min(et->rx_coalesce_usecs_low, eqo->max_eqd); - eqo->eqd = et->rx_coalesce_usecs; + aic->enable = et->use_adaptive_rx_coalesce; + aic->max_eqd = min(et->rx_coalesce_usecs_high, BE_MAX_EQD); + aic->min_eqd = min(et->rx_coalesce_usecs_low, aic->max_eqd); + aic->et_eqd = min(et->rx_coalesce_usecs, aic->max_eqd); + aic->et_eqd = max(aic->et_eqd, aic->min_eqd); + aic++; } return 0; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 53ed58b492c8fb5a7372e68e64eeee5eaaee0e09..741d3bff5ae7e6eff93a512d221053718121be78 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -22,6 +22,7 @@ #include #include #include +#include MODULE_VERSION(DRV_VER); MODULE_DEVICE_TABLE(pci, be_dev_ids); @@ -306,9 +307,13 @@ static void *hw_stats_from_cmd(struct be_adapter *adapter) struct be_cmd_resp_get_stats_v0 *cmd = adapter->stats_cmd.va; return &cmd->hw_stats; - } else { + } else if (BE3_chip(adapter)) { struct be_cmd_resp_get_stats_v1 *cmd = adapter->stats_cmd.va; + return &cmd->hw_stats; + } else { + struct be_cmd_resp_get_stats_v2 *cmd = adapter->stats_cmd.va; + return &cmd->hw_stats; } } @@ -320,9 +325,13 @@ static void *be_erx_stats_from_cmd(struct be_adapter *adapter) struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter); return &hw_stats->erx; - } else { + } else if (BE3_chip(adapter)) { struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter); + return &hw_stats->erx; + } else { + struct be_hw_stats_v2 *hw_stats = hw_stats_from_cmd(adapter); + return &hw_stats->erx; } } @@ -422,6 +431,60 @@ static void populate_be_v1_stats(struct be_adapter *adapter) adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops; } +static void populate_be_v2_stats(struct be_adapter *adapter) +{ + struct be_hw_stats_v2 *hw_stats = hw_stats_from_cmd(adapter); + struct be_pmem_stats *pmem_sts = &hw_stats->pmem; + struct be_rxf_stats_v2 *rxf_stats = &hw_stats->rxf; + struct be_port_rxf_stats_v2 *port_stats = + &rxf_stats->port[adapter->port_num]; + struct be_drv_stats *drvs = &adapter->drv_stats; + + be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats)); + drvs->pmem_fifo_overflow_drop = port_stats->pmem_fifo_overflow_drop; + drvs->rx_priority_pause_frames = port_stats->rx_priority_pause_frames; + drvs->rx_pause_frames = port_stats->rx_pause_frames; + drvs->rx_crc_errors = port_stats->rx_crc_errors; + drvs->rx_control_frames = port_stats->rx_control_frames; + drvs->rx_in_range_errors = port_stats->rx_in_range_errors; + drvs->rx_frame_too_long = port_stats->rx_frame_too_long; + drvs->rx_dropped_runt = port_stats->rx_dropped_runt; + drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs; + drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs; + drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs; + drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length; + drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small; + drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short; + drvs->rx_out_range_errors = port_stats->rx_out_range_errors; + drvs->rx_dropped_header_too_small = + port_stats->rx_dropped_header_too_small; + drvs->rx_input_fifo_overflow_drop = + port_stats->rx_input_fifo_overflow_drop; + drvs->rx_address_filtered = port_stats->rx_address_filtered; + drvs->rx_alignment_symbol_errors = + port_stats->rx_alignment_symbol_errors; + drvs->rxpp_fifo_overflow_drop = port_stats->rxpp_fifo_overflow_drop; + drvs->tx_pauseframes = port_stats->tx_pauseframes; + drvs->tx_controlframes = port_stats->tx_controlframes; + drvs->tx_priority_pauseframes = port_stats->tx_priority_pauseframes; + drvs->jabber_events = port_stats->jabber_events; + drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf; + drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr; + drvs->forwarded_packets = rxf_stats->forwarded_packets; + drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu; + drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr; + drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags; + adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops; + if (be_roce_supported(adapter)) { + drvs->rx_roce_bytes_lsd = port_stats->roce_bytes_received_lsd; + drvs->rx_roce_bytes_msd = port_stats->roce_bytes_received_msd; + drvs->rx_roce_frames = port_stats->roce_frames_received; + drvs->roce_drops_crc = port_stats->roce_drops_crc; + drvs->roce_drops_payload_len = + port_stats->roce_drops_payload_len; + } +} + static void populate_lancer_stats(struct be_adapter *adapter) { @@ -489,7 +552,7 @@ static void populate_erx_stats(struct be_adapter *adapter, void be_parse_stats(struct be_adapter *adapter) { - struct be_erx_stats_v1 *erx = be_erx_stats_from_cmd(adapter); + struct be_erx_stats_v2 *erx = be_erx_stats_from_cmd(adapter); struct be_rx_obj *rxo; int i; u32 erx_stat; @@ -499,11 +562,13 @@ void be_parse_stats(struct be_adapter *adapter) } else { if (BE2_chip(adapter)) populate_be_v0_stats(adapter); - else - /* for BE3 and Skyhawk */ + else if (BE3_chip(adapter)) + /* for BE3 */ populate_be_v1_stats(adapter); + else + populate_be_v2_stats(adapter); - /* as erx_v1 is longer than v0, ok to use v1 for v0 access */ + /* erx_v2 is longer than v0, v1. use v2 for v0, v1 access */ for_all_rx_queues(adapter, rxo, i) { erx_stat = erx->rx_drops_no_fragments[rxo->q.id]; populate_erx_stats(adapter, rxo, erx_stat); @@ -935,8 +1000,10 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) u32 start = txq->head; skb = be_xmit_workarounds(adapter, skb, &skip_hw_vlan); - if (!skb) + if (!skb) { + tx_stats(txo)->tx_drv_drops++; return NETDEV_TX_OK; + } wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); @@ -965,6 +1032,7 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) be_tx_stats_update(txo, wrb_cnt, copied, gso_segs, stopped); } else { txq->head = start; + tx_stats(txo)->tx_drv_drops++; dev_kfree_skb_any(skb); } return NETDEV_TX_OK; @@ -1275,53 +1343,79 @@ static int be_set_vf_tx_rate(struct net_device *netdev, return status; } -static void be_eqd_update(struct be_adapter *adapter, struct be_eq_obj *eqo) +static void be_aic_update(struct be_aic_obj *aic, u64 rx_pkts, u64 tx_pkts, + ulong now) { - struct be_rx_stats *stats = rx_stats(&adapter->rx_obj[eqo->idx]); - ulong now = jiffies; - ulong delta = now - stats->rx_jiffies; - u64 pkts; - unsigned int start, eqd; + aic->rx_pkts_prev = rx_pkts; + aic->tx_reqs_prev = tx_pkts; + aic->jiffies = now; +} - if (!eqo->enable_aic) { - eqd = eqo->eqd; - goto modify_eqd; - } +static void be_eqd_update(struct be_adapter *adapter) +{ + struct be_set_eqd set_eqd[MAX_EVT_QS]; + int eqd, i, num = 0, start; + struct be_aic_obj *aic; + struct be_eq_obj *eqo; + struct be_rx_obj *rxo; + struct be_tx_obj *txo; + u64 rx_pkts, tx_pkts; + ulong now; + u32 pps, delta; - if (eqo->idx >= adapter->num_rx_qs) - return; + for_all_evt_queues(adapter, eqo, i) { + aic = &adapter->aic_obj[eqo->idx]; + if (!aic->enable) { + if (aic->jiffies) + aic->jiffies = 0; + eqd = aic->et_eqd; + goto modify_eqd; + } - stats = rx_stats(&adapter->rx_obj[eqo->idx]); + rxo = &adapter->rx_obj[eqo->idx]; + do { + start = u64_stats_fetch_begin_bh(&rxo->stats.sync); + rx_pkts = rxo->stats.rx_pkts; + } while (u64_stats_fetch_retry_bh(&rxo->stats.sync, start)); - /* Wrapped around */ - if (time_before(now, stats->rx_jiffies)) { - stats->rx_jiffies = now; - return; - } + txo = &adapter->tx_obj[eqo->idx]; + do { + start = u64_stats_fetch_begin_bh(&txo->stats.sync); + tx_pkts = txo->stats.tx_reqs; + } while (u64_stats_fetch_retry_bh(&txo->stats.sync, start)); - /* Update once a second */ - if (delta < HZ) - return; - do { - start = u64_stats_fetch_begin_bh(&stats->sync); - pkts = stats->rx_pkts; - } while (u64_stats_fetch_retry_bh(&stats->sync, start)); - - stats->rx_pps = (unsigned long)(pkts - stats->rx_pkts_prev) / (delta / HZ); - stats->rx_pkts_prev = pkts; - stats->rx_jiffies = now; - eqd = (stats->rx_pps / 110000) << 3; - eqd = min(eqd, eqo->max_eqd); - eqd = max(eqd, eqo->min_eqd); - if (eqd < 10) - eqd = 0; + /* Skip, if wrapped around or first calculation */ + now = jiffies; + if (!aic->jiffies || time_before(now, aic->jiffies) || + rx_pkts < aic->rx_pkts_prev || + tx_pkts < aic->tx_reqs_prev) { + be_aic_update(aic, rx_pkts, tx_pkts, now); + continue; + } + + delta = jiffies_to_msecs(now - aic->jiffies); + pps = (((u32)(rx_pkts - aic->rx_pkts_prev) * 1000) / delta) + + (((u32)(tx_pkts - aic->tx_reqs_prev) * 1000) / delta); + eqd = (pps / 15000) << 2; + + if (eqd < 8) + eqd = 0; + eqd = min_t(u32, eqd, aic->max_eqd); + eqd = max_t(u32, eqd, aic->min_eqd); + be_aic_update(aic, rx_pkts, tx_pkts, now); modify_eqd: - if (eqd != eqo->cur_eqd) { - be_cmd_modify_eqd(adapter, eqo->q.id, eqd); - eqo->cur_eqd = eqd; + if (eqd != aic->prev_eqd) { + set_eqd[num].delay_multiplier = (eqd * 65)/100; + set_eqd[num].eq_id = eqo->q.id; + aic->prev_eqd = eqd; + num++; + } } + + if (num) + be_cmd_modify_eqd(adapter, set_eqd, num); } static void be_rx_stats_update(struct be_rx_obj *rxo, @@ -1463,7 +1557,7 @@ static void skb_fill_rx_data(struct be_rx_obj *rxo, struct sk_buff *skb, } /* Process the RX completion indicated by rxcp when GRO is disabled */ -static void be_rx_compl_process(struct be_rx_obj *rxo, +static void be_rx_compl_process(struct be_rx_obj *rxo, struct napi_struct *napi, struct be_rx_compl_info *rxcp) { struct be_adapter *adapter = rxo->adapter; @@ -1488,7 +1582,7 @@ static void be_rx_compl_process(struct be_rx_obj *rxo, skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]); if (netdev->features & NETIF_F_RXHASH) skb->rxhash = rxcp->rss_hash; - + skb_mark_napi_id(skb, napi); if (rxcp->vlanf) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag); @@ -1546,6 +1640,7 @@ static void be_rx_compl_process_gro(struct be_rx_obj *rxo, skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]); if (adapter->netdev->features & NETIF_F_RXHASH) skb->rxhash = rxcp->rss_hash; + skb_mark_napi_id(skb, napi); if (rxcp->vlanf) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag); @@ -1726,6 +1821,8 @@ static void be_post_rx_frags(struct be_rx_obj *rxo, gfp_t gfp) if (posted) { atomic_add(posted, &rxq->used); + if (rxo->rx_post_starved) + rxo->rx_post_starved = false; be_rxq_notify(adapter, rxq->id, posted); } else if (atomic_read(&rxq->used) == 0) { /* Let be_worker replenish when memory is available */ @@ -1928,6 +2025,7 @@ static void be_evt_queues_destroy(struct be_adapter *adapter) if (eqo->q.created) { be_eq_clean(eqo); be_cmd_q_destroy(adapter, &eqo->q, QTYPE_EQ); + napi_hash_del(&eqo->napi); netif_napi_del(&eqo->napi); } be_queue_free(adapter, &eqo->q); @@ -1938,6 +2036,7 @@ static int be_evt_queues_create(struct be_adapter *adapter) { struct be_queue_info *eq; struct be_eq_obj *eqo; + struct be_aic_obj *aic; int i, rc; adapter->num_evt_qs = min_t(u16, num_irqs(adapter), @@ -1946,11 +2045,13 @@ static int be_evt_queues_create(struct be_adapter *adapter) for_all_evt_queues(adapter, eqo, i) { netif_napi_add(adapter->netdev, &eqo->napi, be_poll, BE_NAPI_WEIGHT); + napi_hash_add(&eqo->napi); + aic = &adapter->aic_obj[i]; eqo->adapter = adapter; eqo->tx_budget = BE_TX_BUDGET; eqo->idx = i; - eqo->max_eqd = BE_MAX_EQD; - eqo->enable_aic = true; + aic->max_eqd = BE_MAX_EQD; + aic->enable = true; eq = &eqo->q; rc = be_queue_alloc(adapter, eq, EVNT_Q_LEN, @@ -2167,7 +2268,7 @@ static inline bool do_gro(struct be_rx_compl_info *rxcp) } static int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi, - int budget) + int budget, int polling) { struct be_adapter *adapter = rxo->adapter; struct be_queue_info *rx_cq = &rxo->cq; @@ -2198,10 +2299,12 @@ static int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi, goto loop_continue; } - if (do_gro(rxcp)) + /* Don't do gro when we're busy_polling */ + if (do_gro(rxcp) && polling != BUSY_POLLING) be_rx_compl_process_gro(rxo, napi, rxcp); else - be_rx_compl_process(rxo, rxcp); + be_rx_compl_process(rxo, napi, rxcp); + loop_continue: be_rx_stats_update(rxo, rxcp); } @@ -2209,7 +2312,11 @@ loop_continue: if (work_done) { be_cq_notify(adapter, rx_cq->id, true, work_done); - if (atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM) + /* When an rx-obj gets into post_starved state, just + * let be_worker do the posting. + */ + if (atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM && + !rxo->rx_post_starved) be_post_rx_frags(rxo, GFP_ATOMIC); } @@ -2254,6 +2361,7 @@ int be_poll(struct napi_struct *napi, int budget) struct be_eq_obj *eqo = container_of(napi, struct be_eq_obj, napi); struct be_adapter *adapter = eqo->adapter; int max_work = 0, work, i, num_evts; + struct be_rx_obj *rxo; bool tx_done; num_evts = events_get(eqo); @@ -2266,13 +2374,18 @@ int be_poll(struct napi_struct *napi, int budget) max_work = budget; } - /* This loop will iterate twice for EQ0 in which - * completions of the last RXQ (default one) are also processed - * For other EQs the loop iterates only once - */ - for (i = eqo->idx; i < adapter->num_rx_qs; i += adapter->num_evt_qs) { - work = be_process_rx(&adapter->rx_obj[i], napi, budget); - max_work = max(work, max_work); + if (be_lock_napi(eqo)) { + /* This loop will iterate twice for EQ0 in which + * completions of the last RXQ (default one) are also processed + * For other EQs the loop iterates only once + */ + for_all_rx_queues_on_eq(adapter, eqo, rxo, i) { + work = be_process_rx(rxo, napi, budget, NAPI_POLLING); + max_work = max(work, max_work); + } + be_unlock_napi(eqo); + } else { + max_work = budget; } if (is_mcc_eqo(eqo)) @@ -2288,6 +2401,28 @@ int be_poll(struct napi_struct *napi, int budget) return max_work; } +#ifdef CONFIG_NET_RX_BUSY_POLL +static int be_busy_poll(struct napi_struct *napi) +{ + struct be_eq_obj *eqo = container_of(napi, struct be_eq_obj, napi); + struct be_adapter *adapter = eqo->adapter; + struct be_rx_obj *rxo; + int i, work = 0; + + if (!be_lock_busy_poll(eqo)) + return LL_FLUSH_BUSY; + + for_all_rx_queues_on_eq(adapter, eqo, rxo, i) { + work = be_process_rx(rxo, napi, 4, BUSY_POLLING); + if (work) + break; + } + + be_unlock_busy_poll(eqo); + return work; +} +#endif + void be_detect_error(struct be_adapter *adapter) { u32 ue_lo = 0, ue_hi = 0, ue_lo_mask = 0, ue_hi_mask = 0; @@ -2519,9 +2654,11 @@ static int be_close(struct net_device *netdev) be_roce_dev_close(adapter); - if (adapter->flags & BE_FLAGS_NAPI_ENABLED) { - for_all_evt_queues(adapter, eqo, i) + for_all_evt_queues(adapter, eqo, i) { + if (adapter->flags & BE_FLAGS_NAPI_ENABLED) { napi_disable(&eqo->napi); + be_disable_busy_poll(eqo); + } adapter->flags &= ~BE_FLAGS_NAPI_ENABLED; } @@ -2632,6 +2769,7 @@ static int be_open(struct net_device *netdev) for_all_evt_queues(adapter, eqo, i) { napi_enable(&eqo->napi); + be_enable_busy_poll(eqo); be_eq_notify(adapter, eqo->q.id, true, false, 0); } adapter->flags |= BE_FLAGS_NAPI_ENABLED; @@ -2937,7 +3075,8 @@ static int be_vf_setup(struct be_adapter *adapter) goto err; vf_cfg->def_vid = def_vlan; - be_cmd_enable_vf(adapter, vf + 1); + if (!old_vfs) + be_cmd_enable_vf(adapter, vf + 1); } if (!old_vfs) { @@ -2962,12 +3101,12 @@ static void BEx_get_resources(struct be_adapter *adapter, struct pci_dev *pdev = adapter->pdev; bool use_sriov = false; - if (BE3_chip(adapter) && be_physfn(adapter)) { + if (BE3_chip(adapter) && sriov_want(adapter)) { int max_vfs; max_vfs = pci_sriov_get_totalvfs(pdev); res->max_vfs = max_vfs > 0 ? min(MAX_VFS, max_vfs) : 0; - use_sriov = res->max_vfs && num_vfs; + use_sriov = res->max_vfs; } if (be_physfn(adapter)) @@ -2983,8 +3122,9 @@ static void BEx_get_resources(struct be_adapter *adapter, res->max_vlans = BE_NUM_VLANS_SUPPORTED; res->max_mcast_mac = BE_MAX_MC; + /* For BE3 1Gb ports, F/W does not properly support multiple TXQs */ if (BE2_chip(adapter) || use_sriov || be_is_mc(adapter) || - !be_physfn(adapter)) + !be_physfn(adapter) || (adapter->port_num > 1)) res->max_tx_qs = 1; else res->max_tx_qs = BE3_MAX_TX_QS; @@ -3026,14 +3166,6 @@ static int be_get_resources(struct be_adapter *adapter) adapter->res = res; } - /* For BE3 only check if FW suggests a different max-txqs value */ - if (BE3_chip(adapter)) { - status = be_cmd_get_profile_config(adapter, &res, 0); - if (!status && res.max_tx_qs) - adapter->res.max_tx_qs = - min(adapter->res.max_tx_qs, res.max_tx_qs); - } - /* For Lancer, SH etc read per-function resource limits from FW. * GET_FUNC_CONFIG returns per function guaranteed limits. * GET_PROFILE_CONFIG returns PCI-E related limits PF-pool limits @@ -3264,7 +3396,7 @@ static int be_setup(struct be_adapter *adapter) be_cmd_set_flow_control(adapter, adapter->tx_fc, adapter->rx_fc); - if (be_physfn(adapter) && num_vfs) { + if (sriov_want(adapter)) { if (be_max_vfs(adapter)) be_vf_setup(adapter); else @@ -3906,6 +4038,9 @@ static const struct net_device_ops be_netdev_ops = { #endif .ndo_bridge_setlink = be_ndo_bridge_setlink, .ndo_bridge_getlink = be_ndo_bridge_getlink, +#ifdef CONFIG_NET_RX_BUSY_POLL + .ndo_busy_poll = be_busy_poll +#endif }; static void be_netdev_init(struct net_device *netdev) @@ -3966,11 +4101,6 @@ static int be_roce_map_pci_bars(struct be_adapter *adapter) static int be_map_pci_bars(struct be_adapter *adapter) { u8 __iomem *addr; - u32 sli_intf; - - pci_read_config_dword(adapter->pdev, SLI_INTF_REG_OFFSET, &sli_intf); - adapter->if_type = (sli_intf & SLI_INTF_IF_TYPE_MASK) >> - SLI_INTF_IF_TYPE_SHIFT; if (BEx_chip(adapter) && be_physfn(adapter)) { adapter->csr = pci_iomap(adapter->pdev, 2, 0); @@ -4083,9 +4213,11 @@ static int be_stats_init(struct be_adapter *adapter) cmd->size = sizeof(struct lancer_cmd_req_pport_stats); else if (BE2_chip(adapter)) cmd->size = sizeof(struct be_cmd_req_get_stats_v0); - else - /* BE3 and Skyhawk */ + else if (BE3_chip(adapter)) cmd->size = sizeof(struct be_cmd_req_get_stats_v1); + else + /* ALL non-BE ASICs */ + cmd->size = sizeof(struct be_cmd_req_get_stats_v2); cmd->va = dma_zalloc_coherent(&adapter->pdev->dev, cmd->size, &cmd->dma, GFP_KERNEL); @@ -4119,7 +4251,6 @@ static void be_remove(struct pci_dev *pdev) pci_disable_pcie_error_reporting(pdev); - pci_set_drvdata(pdev, NULL); pci_release_regions(pdev); pci_disable_device(pdev); @@ -4268,7 +4399,6 @@ static void be_worker(struct work_struct *work) struct be_adapter *adapter = container_of(work, struct be_adapter, work.work); struct be_rx_obj *rxo; - struct be_eq_obj *eqo; int i; /* when interrupts are not yet enabled, just reap any pending @@ -4293,14 +4423,14 @@ static void be_worker(struct work_struct *work) be_cmd_get_die_temperature(adapter); for_all_rx_queues(adapter, rxo, i) { - if (rxo->rx_post_starved) { - rxo->rx_post_starved = false; + /* Replenish RX-queues starved due to memory + * allocation failures. + */ + if (rxo->rx_post_starved) be_post_rx_frags(rxo, GFP_KERNEL); - } } - for_all_evt_queues(adapter, eqo, i) - be_eqd_update(adapter, eqo); + be_eqd_update(adapter); reschedule: adapter->work_counter++; @@ -4376,9 +4506,11 @@ static int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id) } } - status = pci_enable_pcie_error_reporting(pdev); - if (status) - dev_info(&pdev->dev, "Could not use PCIe error reporting\n"); + if (be_physfn(adapter)) { + status = pci_enable_pcie_error_reporting(pdev); + if (!status) + dev_info(&pdev->dev, "PCIe error reporting enabled\n"); + } status = be_ctrl_init(adapter); if (status) @@ -4449,7 +4581,6 @@ ctrl_clean: be_ctrl_cleanup(adapter); free_netdev: free_netdev(netdev); - pci_set_drvdata(pdev, NULL); rel_reg: pci_release_regions(pdev); disable_dev: diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c index c706b7a9397ed6f13dedfee142f99ae50a98ff98..4b22a9579f859e7e9d61da22d5175632d6441d2d 100644 --- a/drivers/net/ethernet/fealnx.c +++ b/drivers/net/ethernet/fealnx.c @@ -699,7 +699,6 @@ static void fealnx_remove_one(struct pci_dev *pdev) pci_iounmap(pdev, np->mem); free_netdev(dev); pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); } else printk(KERN_ERR "fealnx: remove for unknown device\n"); } diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index 6b60582ce8cf1c7a82dd3da1d69acb64981014f4..56f2f608a9f43aa27a32b038d21daf83ffe7f682 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -1083,7 +1083,7 @@ static int fs_enet_probe(struct platform_device *ofdev) mac_addr = of_get_mac_address(ofdev->dev.of_node); if (mac_addr) - memcpy(ndev->dev_addr, mac_addr, 6); + memcpy(ndev->dev_addr, mac_addr, ETH_ALEN); ret = fep->ops->allocate_bd(ndev); if (ret) diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index ef95c3ae53e6bbab3ef5243e99aab6d12e27f066..b14d7904a075e69779e4a2847dee1bedaba791d2 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -2920,7 +2920,7 @@ static int gfar_poll(struct napi_struct *napi, int budget) struct gfar_priv_rx_q *rx_queue = NULL; int work_done = 0, work_done_per_q = 0; int i, budget_per_q = 0; - int has_tx_work; + int has_tx_work = 0; unsigned long rstat_rxf; int num_act_queues; @@ -2935,62 +2935,51 @@ static int gfar_poll(struct napi_struct *napi, int budget) if (num_act_queues) budget_per_q = budget/num_act_queues; - while (1) { - has_tx_work = 0; - for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) { - tx_queue = priv->tx_queue[i]; - /* run Tx cleanup to completion */ - if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) { - gfar_clean_tx_ring(tx_queue); - has_tx_work = 1; - } + for_each_set_bit(i, &gfargrp->tx_bit_map, priv->num_tx_queues) { + tx_queue = priv->tx_queue[i]; + /* run Tx cleanup to completion */ + if (tx_queue->tx_skbuff[tx_queue->skb_dirtytx]) { + gfar_clean_tx_ring(tx_queue); + has_tx_work = 1; } + } - for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) { - /* skip queue if not active */ - if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i))) - continue; - - rx_queue = priv->rx_queue[i]; - work_done_per_q = - gfar_clean_rx_ring(rx_queue, budget_per_q); - work_done += work_done_per_q; - - /* finished processing this queue */ - if (work_done_per_q < budget_per_q) { - /* clear active queue hw indication */ - gfar_write(®s->rstat, - RSTAT_CLEAR_RXF0 >> i); - rstat_rxf &= ~(RSTAT_CLEAR_RXF0 >> i); - num_act_queues--; - - if (!num_act_queues) - break; - /* recompute budget per Rx queue */ - budget_per_q = - (budget - work_done) / num_act_queues; - } - } + for_each_set_bit(i, &gfargrp->rx_bit_map, priv->num_rx_queues) { + /* skip queue if not active */ + if (!(rstat_rxf & (RSTAT_CLEAR_RXF0 >> i))) + continue; - if (work_done >= budget) - break; + rx_queue = priv->rx_queue[i]; + work_done_per_q = + gfar_clean_rx_ring(rx_queue, budget_per_q); + work_done += work_done_per_q; + + /* finished processing this queue */ + if (work_done_per_q < budget_per_q) { + /* clear active queue hw indication */ + gfar_write(®s->rstat, + RSTAT_CLEAR_RXF0 >> i); + num_act_queues--; + + if (!num_act_queues) + break; + } + } - if (!num_act_queues && !has_tx_work) { + if (!num_act_queues && !has_tx_work) { - napi_complete(napi); + napi_complete(napi); - /* Clear the halt bit in RSTAT */ - gfar_write(®s->rstat, gfargrp->rstat); + /* Clear the halt bit in RSTAT */ + gfar_write(®s->rstat, gfargrp->rstat); - gfar_write(®s->imask, IMASK_DEFAULT); + gfar_write(®s->imask, IMASK_DEFAULT); - /* If we are coalescing interrupts, update the timer - * Otherwise, clear it - */ - gfar_configure_coalescing(priv, gfargrp->rx_bit_map, - gfargrp->tx_bit_map); - break; - } + /* If we are coalescing interrupts, update the timer + * Otherwise, clear it + */ + gfar_configure_coalescing(priv, gfargrp->rx_bit_map, + gfargrp->tx_bit_map); } return work_done; diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index 04112b98ff5d9231f1ed60e4df438fa88b6b7947..114c58f9d8d25a83d235e0cafa976a93db3c7426 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -1177,21 +1177,21 @@ static inline void gfar_read_filer(struct gfar_private *priv, *fpr = gfar_read(®s->rqfpr); } -extern void lock_rx_qs(struct gfar_private *priv); -extern void lock_tx_qs(struct gfar_private *priv); -extern void unlock_rx_qs(struct gfar_private *priv); -extern void unlock_tx_qs(struct gfar_private *priv); -extern irqreturn_t gfar_receive(int irq, void *dev_id); -extern int startup_gfar(struct net_device *dev); -extern void stop_gfar(struct net_device *dev); -extern void gfar_halt(struct net_device *dev); -extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, - int enable, u32 regnum, u32 read); -extern void gfar_configure_coalescing_all(struct gfar_private *priv); +void lock_rx_qs(struct gfar_private *priv); +void lock_tx_qs(struct gfar_private *priv); +void unlock_rx_qs(struct gfar_private *priv); +void unlock_tx_qs(struct gfar_private *priv); +irqreturn_t gfar_receive(int irq, void *dev_id); +int startup_gfar(struct net_device *dev); +void stop_gfar(struct net_device *dev); +void gfar_halt(struct net_device *dev); +void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev, int enable, + u32 regnum, u32 read); +void gfar_configure_coalescing_all(struct gfar_private *priv); void gfar_init_sysfs(struct net_device *dev); int gfar_set_features(struct net_device *dev, netdev_features_t features); -extern void gfar_check_rx_parser_mode(struct gfar_private *priv); -extern void gfar_vlan_mode(struct net_device *dev, netdev_features_t features); +void gfar_check_rx_parser_mode(struct gfar_private *priv); +void gfar_vlan_mode(struct net_device *dev, netdev_features_t features); extern const struct ethtool_ops gfar_ethtool_ops; diff --git a/drivers/net/ethernet/freescale/ucc_geth.c b/drivers/net/ethernet/freescale/ucc_geth.c index 64b329fecf3d9e7ab2484ae7e704914ba3f02bc6..5548b6d00c31fe6282f108aa48bdbf45fc0c5eba 100644 --- a/drivers/net/ethernet/freescale/ucc_geth.c +++ b/drivers/net/ethernet/freescale/ucc_geth.c @@ -3901,7 +3901,7 @@ static int ucc_geth_probe(struct platform_device* ofdev) mac_addr = of_get_mac_address(np); if (mac_addr) - memcpy(dev->dev_addr, mac_addr, 6); + memcpy(dev->dev_addr, mac_addr, ETH_ALEN); ugeth->ug_info = ug_info; ugeth->dev = device; diff --git a/drivers/net/ethernet/fujitsu/Kconfig b/drivers/net/ethernet/fujitsu/Kconfig index 6231bc02b9648f9c178ef984c22c362bc08c10cb..1085257385d2c2d1312b0a31c5272eec484a8d4b 100644 --- a/drivers/net/ethernet/fujitsu/Kconfig +++ b/drivers/net/ethernet/fujitsu/Kconfig @@ -5,7 +5,7 @@ config NET_VENDOR_FUJITSU bool "Fujitsu devices" default y - depends on ISA || PCMCIA + depends on PCMCIA ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from diff --git a/drivers/net/ethernet/hp/hp100.c b/drivers/net/ethernet/hp/hp100.c index 91227d03274e02685d5e96fe6096e56e6af999eb..37860096f744005a17c7dc805f890e396ac8933e 100644 --- a/drivers/net/ethernet/hp/hp100.c +++ b/drivers/net/ethernet/hp/hp100.c @@ -1098,7 +1098,7 @@ static int hp100_open(struct net_device *dev) if (request_irq(dev->irq, hp100_interrupt, lp->bus == HP100_BUS_PCI || lp->bus == HP100_BUS_EISA ? IRQF_SHARED : 0, - "hp100", dev)) { + dev->name, dev)) { printk("hp100: %s: unable to get IRQ %d\n", dev->name, dev->irq); return -EAGAIN; } diff --git a/drivers/net/ethernet/i825xx/82596.c b/drivers/net/ethernet/i825xx/82596.c index e38816145395c9e2b08416934e0b10b6352b4507..7ce6379fd1a362cfbad846f7881cdae0d03e6982 100644 --- a/drivers/net/ethernet/i825xx/82596.c +++ b/drivers/net/ethernet/i825xx/82596.c @@ -711,7 +711,7 @@ static int init_i596_mem(struct net_device *dev) i596_add_cmd(dev, &lp->cf_cmd.cmd); DEB(DEB_INIT,printk(KERN_DEBUG "%s: queuing CmdSASetup\n", dev->name)); - memcpy(lp->sa_cmd.eth_addr, dev->dev_addr, 6); + memcpy(lp->sa_cmd.eth_addr, dev->dev_addr, ETH_ALEN); lp->sa_cmd.cmd.command = CmdSASetup; i596_add_cmd(dev, &lp->sa_cmd.cmd); @@ -1155,7 +1155,7 @@ struct net_device * __init i82596_probe(int unit) err = -ENODEV; goto out; } - memcpy(eth_addr, (void *) 0xfffc1f2c, 6); /* YUCK! Get addr from NOVRAM */ + memcpy(eth_addr, (void *) 0xfffc1f2c, ETH_ALEN); /* YUCK! Get addr from NOVRAM */ dev->base_addr = MVME_I596_BASE; dev->irq = (unsigned) MVME16x_IRQ_I596; goto found; @@ -1527,9 +1527,7 @@ int __init init_module(void) if (debug >= 0) i596_debug = debug; dev_82596 = i82596_probe(-1); - if (IS_ERR(dev_82596)) - return PTR_ERR(dev_82596); - return 0; + return PTR_ERR_OR_ZERO(dev_82596); } void __exit cleanup_module(void) diff --git a/drivers/net/ethernet/i825xx/lib82596.c b/drivers/net/ethernet/i825xx/lib82596.c index d653bac4cfc4e2be49d1ab1ff9a1247eae5460aa..861fa15e1e81b11e31f0d8a2f03907056689ce9d 100644 --- a/drivers/net/ethernet/i825xx/lib82596.c +++ b/drivers/net/ethernet/i825xx/lib82596.c @@ -607,7 +607,7 @@ static int init_i596_mem(struct net_device *dev) i596_add_cmd(dev, &dma->cf_cmd.cmd); DEB(DEB_INIT, printk(KERN_DEBUG "%s: queuing CmdSASetup\n", dev->name)); - memcpy(dma->sa_cmd.eth_addr, dev->dev_addr, 6); + memcpy(dma->sa_cmd.eth_addr, dev->dev_addr, ETH_ALEN); dma->sa_cmd.cmd.command = SWAP16(CmdSASetup); DMA_WBACK(dev, &(dma->sa_cmd), sizeof(struct sa_cmd)); i596_add_cmd(dev, &dma->sa_cmd.cmd); @@ -1396,13 +1396,13 @@ static void set_multicast_list(struct net_device *dev) netdev_for_each_mc_addr(ha, dev) { if (!cnt--) break; - memcpy(cp, ha->addr, 6); + memcpy(cp, ha->addr, ETH_ALEN); if (i596_debug > 1) DEB(DEB_MULTI, printk(KERN_DEBUG "%s: Adding address %pM\n", dev->name, cp)); - cp += 6; + cp += ETH_ALEN; } DMA_WBACK_INV(dev, &dma->mc_cmd, sizeof(struct mc_cmd)); i596_add_cmd(dev, &cmd->cmd); diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index cdf2321f1f0f0dbb0f14ba8b745ab361055ebfe1..ae342fdb42c8e79853507007ad2ea6c8d3681fad 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -2678,7 +2678,7 @@ static int emac_init_config(struct emac_instance *dev) np->full_name); return -ENXIO; } - memcpy(dev->ndev->dev_addr, p, 6); + memcpy(dev->ndev->dev_addr, p, ETH_ALEN); /* IAHT and GAHT filter parameterization */ if (emac_has_feature(dev, EMAC_FTR_EMAC4SYNC)) { diff --git a/drivers/net/ethernet/ibm/emac/debug.h b/drivers/net/ethernet/ibm/emac/debug.h index 59a92d5870b5535826a6fec333f27d1796e062db..9c45efe4c8fecfdc0e16371ee67a622e4bfcbdbe 100644 --- a/drivers/net/ethernet/ibm/emac/debug.h +++ b/drivers/net/ethernet/ibm/emac/debug.h @@ -29,13 +29,13 @@ struct emac_instance; struct mal_instance; -extern void emac_dbg_register(struct emac_instance *dev); -extern void emac_dbg_unregister(struct emac_instance *dev); -extern void mal_dbg_register(struct mal_instance *mal); -extern void mal_dbg_unregister(struct mal_instance *mal); -extern int emac_init_debug(void) __init; -extern void emac_fini_debug(void) __exit; -extern void emac_dbg_dump_all(void); +void emac_dbg_register(struct emac_instance *dev); +void emac_dbg_unregister(struct emac_instance *dev); +void mal_dbg_register(struct mal_instance *mal); +void mal_dbg_unregister(struct mal_instance *mal); +int emac_init_debug(void) __init; +void emac_fini_debug(void) __exit; +void emac_dbg_dump_all(void); # define DBG_LEVEL 1 diff --git a/drivers/net/ethernet/ibm/emac/rgmii.h b/drivers/net/ethernet/ibm/emac/rgmii.h index 668bceeff4a2112eb98eece3405c9594dd8accef..d4f1374d19000ea0562d9c4c738e1ea56caffb4b 100644 --- a/drivers/net/ethernet/ibm/emac/rgmii.h +++ b/drivers/net/ethernet/ibm/emac/rgmii.h @@ -56,15 +56,15 @@ struct rgmii_instance { #ifdef CONFIG_IBM_EMAC_RGMII -extern int rgmii_init(void); -extern void rgmii_exit(void); -extern int rgmii_attach(struct platform_device *ofdev, int input, int mode); -extern void rgmii_detach(struct platform_device *ofdev, int input); -extern void rgmii_get_mdio(struct platform_device *ofdev, int input); -extern void rgmii_put_mdio(struct platform_device *ofdev, int input); -extern void rgmii_set_speed(struct platform_device *ofdev, int input, int speed); -extern int rgmii_get_regs_len(struct platform_device *ofdev); -extern void *rgmii_dump_regs(struct platform_device *ofdev, void *buf); +int rgmii_init(void); +void rgmii_exit(void); +int rgmii_attach(struct platform_device *ofdev, int input, int mode); +void rgmii_detach(struct platform_device *ofdev, int input); +void rgmii_get_mdio(struct platform_device *ofdev, int input); +void rgmii_put_mdio(struct platform_device *ofdev, int input); +void rgmii_set_speed(struct platform_device *ofdev, int input, int speed); +int rgmii_get_regs_len(struct platform_device *ofdev); +void *rgmii_dump_regs(struct platform_device *ofdev, void *buf); #else diff --git a/drivers/net/ethernet/ibm/emac/tah.h b/drivers/net/ethernet/ibm/emac/tah.h index 350b7096a041a310f620911f7f2e448bc7d4e8ca..4d5f336f07b3669c15c916ea79ce8e90bc9efb47 100644 --- a/drivers/net/ethernet/ibm/emac/tah.h +++ b/drivers/net/ethernet/ibm/emac/tah.h @@ -72,13 +72,13 @@ struct tah_instance { #ifdef CONFIG_IBM_EMAC_TAH -extern int tah_init(void); -extern void tah_exit(void); -extern int tah_attach(struct platform_device *ofdev, int channel); -extern void tah_detach(struct platform_device *ofdev, int channel); -extern void tah_reset(struct platform_device *ofdev); -extern int tah_get_regs_len(struct platform_device *ofdev); -extern void *tah_dump_regs(struct platform_device *ofdev, void *buf); +int tah_init(void); +void tah_exit(void); +int tah_attach(struct platform_device *ofdev, int channel); +void tah_detach(struct platform_device *ofdev, int channel); +void tah_reset(struct platform_device *ofdev); +int tah_get_regs_len(struct platform_device *ofdev); +void *tah_dump_regs(struct platform_device *ofdev, void *buf); #else diff --git a/drivers/net/ethernet/ibm/emac/zmii.h b/drivers/net/ethernet/ibm/emac/zmii.h index 455bfb0854934d684164d5105faa45ae89df03bb..0959c55b14591dc7f2d58b1946585eeb0da04d68 100644 --- a/drivers/net/ethernet/ibm/emac/zmii.h +++ b/drivers/net/ethernet/ibm/emac/zmii.h @@ -53,15 +53,15 @@ struct zmii_instance { #ifdef CONFIG_IBM_EMAC_ZMII -extern int zmii_init(void); -extern void zmii_exit(void); -extern int zmii_attach(struct platform_device *ofdev, int input, int *mode); -extern void zmii_detach(struct platform_device *ofdev, int input); -extern void zmii_get_mdio(struct platform_device *ofdev, int input); -extern void zmii_put_mdio(struct platform_device *ofdev, int input); -extern void zmii_set_speed(struct platform_device *ofdev, int input, int speed); -extern int zmii_get_regs_len(struct platform_device *ocpdev); -extern void *zmii_dump_regs(struct platform_device *ofdev, void *buf); +int zmii_init(void); +void zmii_exit(void); +int zmii_attach(struct platform_device *ofdev, int input, int *mode); +void zmii_detach(struct platform_device *ofdev, int input); +void zmii_get_mdio(struct platform_device *ofdev, int input); +void zmii_put_mdio(struct platform_device *ofdev, int input); +void zmii_set_speed(struct platform_device *ofdev, int input, int speed); +int zmii_get_regs_len(struct platform_device *ocpdev); +void *zmii_dump_regs(struct platform_device *ofdev, void *buf); #else # define zmii_init() 0 diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index 5d41aee69d1646e3b42a1d91b8873825344043cc..952d795230a479c79c0684ee0849a05e5e0ff631 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1185,7 +1185,7 @@ static void ibmveth_set_multicast_list(struct net_device *netdev) netdev_for_each_mc_addr(ha, netdev) { /* add the multicast address to the filter table */ unsigned long mcast_addr = 0; - memcpy(((char *)&mcast_addr)+2, ha->addr, 6); + memcpy(((char *)&mcast_addr)+2, ha->addr, ETH_ALEN); lpar_rc = h_multicast_ctrl(adapter->vdev->unit_address, IbmVethMcastAddFilter, mcast_addr); @@ -1370,7 +1370,7 @@ static int ibmveth_probe(struct vio_dev *dev, const struct vio_device_id *id) netif_napi_add(netdev, &adapter->napi, ibmveth_poll, 16); adapter->mac_addr = 0; - memcpy(&adapter->mac_addr, mac_addr_p, 6); + memcpy(&adapter->mac_addr, mac_addr_p, ETH_ALEN); netdev->irq = dev->irq; netdev->netdev_ops = &ibmveth_netdev_ops; diff --git a/drivers/net/ethernet/icplus/ipg.c b/drivers/net/ethernet/icplus/ipg.c index bdf5023724e768945f8ec71ceb32a27d897dba6e..25045ae071711f03cee9ccabf9898cf2abebbc21 100644 --- a/drivers/net/ethernet/icplus/ipg.c +++ b/drivers/net/ethernet/icplus/ipg.c @@ -2183,7 +2183,6 @@ static void ipg_remove(struct pci_dev *pdev) free_netdev(dev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static const struct net_device_ops ipg_netdev_ops = { diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c index ada6e210279f3750e26ea5720ca3bbe5057abf2c..cbaba4442d4b226d18691059a4e122029040ff0a 100644 --- a/drivers/net/ethernet/intel/e100.c +++ b/drivers/net/ethernet/intel/e100.c @@ -2985,7 +2985,6 @@ err_out_free_res: err_out_disable_pdev: pci_disable_device(pdev); err_out_free_dev: - pci_set_drvdata(pdev, NULL); free_netdev(netdev); return err; } @@ -3003,7 +3002,6 @@ static void e100_remove(struct pci_dev *pdev) free_netdev(netdev); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } } diff --git a/drivers/net/ethernet/intel/e1000/e1000.h b/drivers/net/ethernet/intel/e1000/e1000.h index 26d9cd59ec75a25451185a8cb933c318f9068912..58c147271a362e68914d55d97b71b327b194e86b 100644 --- a/drivers/net/ethernet/intel/e1000/e1000.h +++ b/drivers/net/ethernet/intel/e1000/e1000.h @@ -325,7 +325,7 @@ enum e1000_state_t { #undef pr_fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -extern struct net_device *e1000_get_hw_dev(struct e1000_hw *hw); +struct net_device *e1000_get_hw_dev(struct e1000_hw *hw); #define e_dbg(format, arg...) \ netdev_dbg(e1000_get_hw_dev(hw), format, ## arg) #define e_err(msglvl, format, arg...) \ @@ -346,20 +346,20 @@ extern struct net_device *e1000_get_hw_dev(struct e1000_hw *hw); extern char e1000_driver_name[]; extern const char e1000_driver_version[]; -extern int e1000_up(struct e1000_adapter *adapter); -extern void e1000_down(struct e1000_adapter *adapter); -extern void e1000_reinit_locked(struct e1000_adapter *adapter); -extern void e1000_reset(struct e1000_adapter *adapter); -extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx); -extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); -extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); -extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter); -extern void e1000_free_all_tx_resources(struct e1000_adapter *adapter); -extern void e1000_update_stats(struct e1000_adapter *adapter); -extern bool e1000_has_link(struct e1000_adapter *adapter); -extern void e1000_power_up_phy(struct e1000_adapter *); -extern void e1000_set_ethtool_ops(struct net_device *netdev); -extern void e1000_check_options(struct e1000_adapter *adapter); -extern char *e1000_get_hw_dev_name(struct e1000_hw *hw); +int e1000_up(struct e1000_adapter *adapter); +void e1000_down(struct e1000_adapter *adapter); +void e1000_reinit_locked(struct e1000_adapter *adapter); +void e1000_reset(struct e1000_adapter *adapter); +int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx); +int e1000_setup_all_rx_resources(struct e1000_adapter *adapter); +int e1000_setup_all_tx_resources(struct e1000_adapter *adapter); +void e1000_free_all_rx_resources(struct e1000_adapter *adapter); +void e1000_free_all_tx_resources(struct e1000_adapter *adapter); +void e1000_update_stats(struct e1000_adapter *adapter); +bool e1000_has_link(struct e1000_adapter *adapter); +void e1000_power_up_phy(struct e1000_adapter *); +void e1000_set_ethtool_ops(struct net_device *netdev); +void e1000_check_options(struct e1000_adapter *adapter); +char *e1000_get_hw_dev_name(struct e1000_hw *hw); #endif /* _E1000_H_ */ diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 59ad007dd5aa09a6123cbd25db7c7b8f4e06f1b7..ad6800ad1bfcb41a66a387fd077b96a59140a25d 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -3917,8 +3917,7 @@ static bool e1000_clean_tx_irq(struct e1000_adapter *adapter, " next_to_watch <%x>\n" " jiffies <%lx>\n" " next_to_watch.status <%x>\n", - (unsigned long)((tx_ring - adapter->tx_ring) / - sizeof(struct e1000_tx_ring)), + (unsigned long)(tx_ring - adapter->tx_ring), readl(hw->hw_addr + tx_ring->tdh), readl(hw->hw_addr + tx_ring->tdt), tx_ring->next_to_use, diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index ad0edd11015d7b40a14d79f06afdab8ecec76cef..0150f7fc893d4ae6985096f17e5a4678f39fc3b3 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -472,26 +472,25 @@ enum latency_range { extern char e1000e_driver_name[]; extern const char e1000e_driver_version[]; -extern void e1000e_check_options(struct e1000_adapter *adapter); -extern void e1000e_set_ethtool_ops(struct net_device *netdev); - -extern int e1000e_up(struct e1000_adapter *adapter); -extern void e1000e_down(struct e1000_adapter *adapter); -extern void e1000e_reinit_locked(struct e1000_adapter *adapter); -extern void e1000e_reset(struct e1000_adapter *adapter); -extern void e1000e_power_up_phy(struct e1000_adapter *adapter); -extern int e1000e_setup_rx_resources(struct e1000_ring *ring); -extern int e1000e_setup_tx_resources(struct e1000_ring *ring); -extern void e1000e_free_rx_resources(struct e1000_ring *ring); -extern void e1000e_free_tx_resources(struct e1000_ring *ring); -extern struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev, - struct rtnl_link_stats64 - *stats); -extern void e1000e_set_interrupt_capability(struct e1000_adapter *adapter); -extern void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter); -extern void e1000e_get_hw_control(struct e1000_adapter *adapter); -extern void e1000e_release_hw_control(struct e1000_adapter *adapter); -extern void e1000e_write_itr(struct e1000_adapter *adapter, u32 itr); +void e1000e_check_options(struct e1000_adapter *adapter); +void e1000e_set_ethtool_ops(struct net_device *netdev); + +int e1000e_up(struct e1000_adapter *adapter); +void e1000e_down(struct e1000_adapter *adapter); +void e1000e_reinit_locked(struct e1000_adapter *adapter); +void e1000e_reset(struct e1000_adapter *adapter); +void e1000e_power_up_phy(struct e1000_adapter *adapter); +int e1000e_setup_rx_resources(struct e1000_ring *ring); +int e1000e_setup_tx_resources(struct e1000_ring *ring); +void e1000e_free_rx_resources(struct e1000_ring *ring); +void e1000e_free_tx_resources(struct e1000_ring *ring); +struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats); +void e1000e_set_interrupt_capability(struct e1000_adapter *adapter); +void e1000e_reset_interrupt_capability(struct e1000_adapter *adapter); +void e1000e_get_hw_control(struct e1000_adapter *adapter); +void e1000e_release_hw_control(struct e1000_adapter *adapter); +void e1000e_write_itr(struct e1000_adapter *adapter, u32 itr); extern unsigned int copybreak; @@ -508,8 +507,8 @@ extern const struct e1000_info e1000_pch2_info; extern const struct e1000_info e1000_pch_lpt_info; extern const struct e1000_info e1000_es2_info; -extern void e1000e_ptp_init(struct e1000_adapter *adapter); -extern void e1000e_ptp_remove(struct e1000_adapter *adapter); +void e1000e_ptp_init(struct e1000_adapter *adapter); +void e1000e_ptp_remove(struct e1000_adapter *adapter); static inline s32 e1000_phy_hw_reset(struct e1000_hw *hw) { @@ -536,7 +535,7 @@ static inline s32 e1e_wphy_locked(struct e1000_hw *hw, u32 offset, u16 data) return hw->phy.ops.write_reg_locked(hw, offset, data); } -extern void e1000e_reload_nvm_generic(struct e1000_hw *hw); +void e1000e_reload_nvm_generic(struct e1000_hw *hw); static inline s32 e1000e_read_mac_addr(struct e1000_hw *hw) { diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index b5252eb8a6c757861ff2c22a3efea81400c3934d..1ca9834cdfda58411c5a43bd3ec344382500382f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -46,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -347,9 +346,9 @@ struct i40e_vsi { u32 rx_buf_failed; u32 rx_page_failed; - /* These are arrays of rings, allocated at run-time */ - struct i40e_ring *rx_rings; - struct i40e_ring *tx_rings; + /* These are containers of ring pointers, allocated at run-time */ + struct i40e_ring **rx_rings; + struct i40e_ring **tx_rings; u16 work_limit; /* high bit set means dynamic, use accessor routines to read/write. @@ -366,7 +365,7 @@ struct i40e_vsi { u8 dtype; /* List of q_vectors allocated to this VSI */ - struct i40e_q_vector *q_vectors; + struct i40e_q_vector **q_vectors; int num_q_vectors; int base_vector; @@ -422,8 +421,9 @@ struct i40e_q_vector { u8 num_ringpairs; /* total number of ring pairs in vector */ - char name[IFNAMSIZ + 9]; cpumask_t affinity_mask; + struct rcu_head rcu; /* to avoid race with update stats on free */ + char name[IFNAMSIZ + 9]; } ____cacheline_internodealigned_in_smp; /* lan device */ @@ -544,6 +544,7 @@ static inline void i40e_dbg_init(void) {} static inline void i40e_dbg_exit(void) {} #endif /* CONFIG_DEBUG_FS*/ void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector); +void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf); int i40e_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd); void i40e_vlan_stripping_disable(struct i40e_vsi *vsi); int i40e_vsi_add_vlan(struct i40e_vsi *vsi, s16 vid); diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 8dbd91f64b74d815fff682c6fe07570182e3182c..ef4cb1cf31f2d392a4ed4e1ae2e0059919423317 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -151,9 +151,7 @@ static ssize_t i40e_dbg_dump_write(struct file *filp, size_t count, loff_t *ppos) { struct i40e_pf *pf = filp->private_data; - char dump_request_buf[16]; bool seid_found = false; - int bytes_not_copied; long seid = -1; int buflen = 0; int i, ret; @@ -163,21 +161,12 @@ static ssize_t i40e_dbg_dump_write(struct file *filp, /* don't allow partial writes */ if (*ppos != 0) return 0; - if (count >= sizeof(dump_request_buf)) - return -ENOSPC; - - bytes_not_copied = copy_from_user(dump_request_buf, buffer, count); - if (bytes_not_copied < 0) - return bytes_not_copied; - if (bytes_not_copied > 0) - count -= bytes_not_copied; - dump_request_buf[count] = '\0'; /* decode the SEID given to be dumped */ - ret = kstrtol(dump_request_buf, 0, &seid); - if (ret < 0) { - dev_info(&pf->pdev->dev, "bad seid value '%s'\n", - dump_request_buf); + ret = kstrtol_from_user(buffer, count, 0, &seid); + + if (ret) { + dev_info(&pf->pdev->dev, "bad seid value\n"); } else if (seid == 0) { seid_found = true; @@ -245,26 +234,33 @@ static ssize_t i40e_dbg_dump_write(struct file *filp, memcpy(p, vsi, len); p += len; - len = (sizeof(struct i40e_q_vector) - * vsi->num_q_vectors); - memcpy(p, vsi->q_vectors, len); - p += len; - - len = (sizeof(struct i40e_ring) * vsi->num_queue_pairs); - memcpy(p, vsi->tx_rings, len); - p += len; - memcpy(p, vsi->rx_rings, len); - p += len; + if (vsi->num_q_vectors) { + len = (sizeof(struct i40e_q_vector) + * vsi->num_q_vectors); + memcpy(p, vsi->q_vectors, len); + p += len; + } - for (i = 0; i < vsi->num_queue_pairs; i++) { - len = sizeof(struct i40e_tx_buffer); - memcpy(p, vsi->tx_rings[i].tx_bi, len); + if (vsi->num_queue_pairs) { + len = (sizeof(struct i40e_ring) * + vsi->num_queue_pairs); + memcpy(p, vsi->tx_rings, len); + p += len; + memcpy(p, vsi->rx_rings, len); p += len; } - for (i = 0; i < vsi->num_queue_pairs; i++) { + + if (vsi->tx_rings[0]) { + len = sizeof(struct i40e_tx_buffer); + for (i = 0; i < vsi->num_queue_pairs; i++) { + memcpy(p, vsi->tx_rings[i]->tx_bi, len); + p += len; + } len = sizeof(struct i40e_rx_buffer); - memcpy(p, vsi->rx_rings[i].rx_bi, len); - p += len; + for (i = 0; i < vsi->num_queue_pairs; i++) { + memcpy(p, vsi->rx_rings[i]->rx_bi, len); + p += len; + } } /* macvlan filter list */ @@ -484,100 +480,104 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) " tx_restart = %d, tx_busy = %d, rx_buf_failed = %d, rx_page_failed = %d\n", vsi->tx_restart, vsi->tx_busy, vsi->rx_buf_failed, vsi->rx_page_failed); - if (vsi->rx_rings) { - for (i = 0; i < vsi->num_queue_pairs; i++) { - dev_info(&pf->pdev->dev, - " rx_rings[%i]: desc = %p\n", - i, vsi->rx_rings[i].desc); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: dev = %p, netdev = %p, rx_bi = %p\n", - i, vsi->rx_rings[i].dev, - vsi->rx_rings[i].netdev, - vsi->rx_rings[i].rx_bi); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n", - i, vsi->rx_rings[i].state, - vsi->rx_rings[i].queue_index, - vsi->rx_rings[i].reg_idx); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: rx_hdr_len = %d, rx_buf_len = %d, dtype = %d\n", - i, vsi->rx_rings[i].rx_hdr_len, - vsi->rx_rings[i].rx_buf_len, - vsi->rx_rings[i].dtype); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: hsplit = %d, next_to_use = %d, next_to_clean = %d, ring_active = %i\n", - i, vsi->rx_rings[i].hsplit, - vsi->rx_rings[i].next_to_use, - vsi->rx_rings[i].next_to_clean, - vsi->rx_rings[i].ring_active); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: rx_stats: packets = %lld, bytes = %lld, non_eop_descs = %lld\n", - i, vsi->rx_rings[i].rx_stats.packets, - vsi->rx_rings[i].rx_stats.bytes, - vsi->rx_rings[i].rx_stats.non_eop_descs); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: rx_stats: alloc_rx_page_failed = %lld, alloc_rx_buff_failed = %lld\n", - i, - vsi->rx_rings[i].rx_stats.alloc_rx_page_failed, - vsi->rx_rings[i].rx_stats.alloc_rx_buff_failed); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: size = %i, dma = 0x%08lx\n", - i, vsi->rx_rings[i].size, - (long unsigned int)vsi->rx_rings[i].dma); - dev_info(&pf->pdev->dev, - " rx_rings[%i]: vsi = %p, q_vector = %p\n", - i, vsi->rx_rings[i].vsi, - vsi->rx_rings[i].q_vector); - } + rcu_read_lock(); + for (i = 0; i < vsi->num_queue_pairs; i++) { + struct i40e_ring *rx_ring = ACCESS_ONCE(vsi->rx_rings[i]); + if (!rx_ring) + continue; + + dev_info(&pf->pdev->dev, + " rx_rings[%i]: desc = %p\n", + i, rx_ring->desc); + dev_info(&pf->pdev->dev, + " rx_rings[%i]: dev = %p, netdev = %p, rx_bi = %p\n", + i, rx_ring->dev, + rx_ring->netdev, + rx_ring->rx_bi); + dev_info(&pf->pdev->dev, + " rx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n", + i, rx_ring->state, + rx_ring->queue_index, + rx_ring->reg_idx); + dev_info(&pf->pdev->dev, + " rx_rings[%i]: rx_hdr_len = %d, rx_buf_len = %d, dtype = %d\n", + i, rx_ring->rx_hdr_len, + rx_ring->rx_buf_len, + rx_ring->dtype); + dev_info(&pf->pdev->dev, + " rx_rings[%i]: hsplit = %d, next_to_use = %d, next_to_clean = %d, ring_active = %i\n", + i, rx_ring->hsplit, + rx_ring->next_to_use, + rx_ring->next_to_clean, + rx_ring->ring_active); + dev_info(&pf->pdev->dev, + " rx_rings[%i]: rx_stats: packets = %lld, bytes = %lld, non_eop_descs = %lld\n", + i, rx_ring->stats.packets, + rx_ring->stats.bytes, + rx_ring->rx_stats.non_eop_descs); + dev_info(&pf->pdev->dev, + " rx_rings[%i]: rx_stats: alloc_rx_page_failed = %lld, alloc_rx_buff_failed = %lld\n", + i, + rx_ring->rx_stats.alloc_rx_page_failed, + rx_ring->rx_stats.alloc_rx_buff_failed); + dev_info(&pf->pdev->dev, + " rx_rings[%i]: size = %i, dma = 0x%08lx\n", + i, rx_ring->size, + (long unsigned int)rx_ring->dma); + dev_info(&pf->pdev->dev, + " rx_rings[%i]: vsi = %p, q_vector = %p\n", + i, rx_ring->vsi, + rx_ring->q_vector); } - if (vsi->tx_rings) { - for (i = 0; i < vsi->num_queue_pairs; i++) { - dev_info(&pf->pdev->dev, - " tx_rings[%i]: desc = %p\n", - i, vsi->tx_rings[i].desc); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: dev = %p, netdev = %p, tx_bi = %p\n", - i, vsi->tx_rings[i].dev, - vsi->tx_rings[i].netdev, - vsi->tx_rings[i].tx_bi); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n", - i, vsi->tx_rings[i].state, - vsi->tx_rings[i].queue_index, - vsi->tx_rings[i].reg_idx); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: dtype = %d\n", - i, vsi->tx_rings[i].dtype); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: hsplit = %d, next_to_use = %d, next_to_clean = %d, ring_active = %i\n", - i, vsi->tx_rings[i].hsplit, - vsi->tx_rings[i].next_to_use, - vsi->tx_rings[i].next_to_clean, - vsi->tx_rings[i].ring_active); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: tx_stats: packets = %lld, bytes = %lld, restart_queue = %lld\n", - i, vsi->tx_rings[i].tx_stats.packets, - vsi->tx_rings[i].tx_stats.bytes, - vsi->tx_rings[i].tx_stats.restart_queue); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: tx_stats: tx_busy = %lld, completed = %lld, tx_done_old = %lld\n", - i, - vsi->tx_rings[i].tx_stats.tx_busy, - vsi->tx_rings[i].tx_stats.completed, - vsi->tx_rings[i].tx_stats.tx_done_old); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: size = %i, dma = 0x%08lx\n", - i, vsi->tx_rings[i].size, - (long unsigned int)vsi->tx_rings[i].dma); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: vsi = %p, q_vector = %p\n", - i, vsi->tx_rings[i].vsi, - vsi->tx_rings[i].q_vector); - dev_info(&pf->pdev->dev, - " tx_rings[%i]: DCB tc = %d\n", - i, vsi->tx_rings[i].dcb_tc); - } + for (i = 0; i < vsi->num_queue_pairs; i++) { + struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[i]); + if (!tx_ring) + continue; + dev_info(&pf->pdev->dev, + " tx_rings[%i]: desc = %p\n", + i, tx_ring->desc); + dev_info(&pf->pdev->dev, + " tx_rings[%i]: dev = %p, netdev = %p, tx_bi = %p\n", + i, tx_ring->dev, + tx_ring->netdev, + tx_ring->tx_bi); + dev_info(&pf->pdev->dev, + " tx_rings[%i]: state = %li, queue_index = %d, reg_idx = %d\n", + i, tx_ring->state, + tx_ring->queue_index, + tx_ring->reg_idx); + dev_info(&pf->pdev->dev, + " tx_rings[%i]: dtype = %d\n", + i, tx_ring->dtype); + dev_info(&pf->pdev->dev, + " tx_rings[%i]: hsplit = %d, next_to_use = %d, next_to_clean = %d, ring_active = %i\n", + i, tx_ring->hsplit, + tx_ring->next_to_use, + tx_ring->next_to_clean, + tx_ring->ring_active); + dev_info(&pf->pdev->dev, + " tx_rings[%i]: tx_stats: packets = %lld, bytes = %lld, restart_queue = %lld\n", + i, tx_ring->stats.packets, + tx_ring->stats.bytes, + tx_ring->tx_stats.restart_queue); + dev_info(&pf->pdev->dev, + " tx_rings[%i]: tx_stats: tx_busy = %lld, tx_done_old = %lld\n", + i, + tx_ring->tx_stats.tx_busy, + tx_ring->tx_stats.tx_done_old); + dev_info(&pf->pdev->dev, + " tx_rings[%i]: size = %i, dma = 0x%08lx\n", + i, tx_ring->size, + (long unsigned int)tx_ring->dma); + dev_info(&pf->pdev->dev, + " tx_rings[%i]: vsi = %p, q_vector = %p\n", + i, tx_ring->vsi, + tx_ring->q_vector); + dev_info(&pf->pdev->dev, + " tx_rings[%i]: DCB tc = %d\n", + i, tx_ring->dcb_tc); } + rcu_read_unlock(); dev_info(&pf->pdev->dev, " work_limit = %d, rx_itr_setting = %d (%s), tx_itr_setting = %d (%s)\n", vsi->work_limit, vsi->rx_itr_setting, @@ -587,15 +587,6 @@ static void i40e_dbg_dump_vsi_seid(struct i40e_pf *pf, int seid) dev_info(&pf->pdev->dev, " max_frame = %d, rx_hdr_len = %d, rx_buf_len = %d dtype = %d\n", vsi->max_frame, vsi->rx_hdr_len, vsi->rx_buf_len, vsi->dtype); - if (vsi->q_vectors) { - for (i = 0; i < vsi->num_q_vectors; i++) { - dev_info(&pf->pdev->dev, - " q_vectors[%i]: base index = %ld\n", - i, ((long int)*vsi->q_vectors[i].rx.ring- - (long int)*vsi->q_vectors[0].rx.ring)/ - sizeof(struct i40e_ring)); - } - } dev_info(&pf->pdev->dev, " num_q_vectors = %i, base_vector = %i\n", vsi->num_q_vectors, vsi->base_vector); @@ -792,9 +783,9 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n, return; } if (is_rx_ring) - ring = vsi->rx_rings[ring_id]; + ring = *vsi->rx_rings[ring_id]; else - ring = vsi->tx_rings[ring_id]; + ring = *vsi->tx_rings[ring_id]; if (cnt == 2) { dev_info(&pf->pdev->dev, "vsi = %02i %s ring = %02i\n", vsi_seid, is_rx_ring ? "rx" : "tx", ring_id); @@ -1028,11 +1019,11 @@ static ssize_t i40e_dbg_command_write(struct file *filp, size_t count, loff_t *ppos) { struct i40e_pf *pf = filp->private_data; + char *cmd_buf, *cmd_buf_tmp; int bytes_not_copied; struct i40e_vsi *vsi; u8 *print_buf_start; u8 *print_buf; - char *cmd_buf; int vsi_seid; int veb_seid; int cnt; @@ -1051,6 +1042,12 @@ static ssize_t i40e_dbg_command_write(struct file *filp, count -= bytes_not_copied; cmd_buf[count] = '\0'; + cmd_buf_tmp = strchr(cmd_buf, '\n'); + if (cmd_buf_tmp) { + *cmd_buf_tmp = '\0'; + count = cmd_buf_tmp - cmd_buf + 1; + } + print_buf_start = kzalloc(I40E_MAX_DEBUG_OUT_BUFFER, GFP_KERNEL); if (!print_buf_start) goto command_write_done; @@ -1157,9 +1154,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, i40e_veb_release(pf->veb[i]); } else if (strncmp(cmd_buf, "add macaddr", 11) == 0) { - u8 ma[6]; - int vlan = 0; struct i40e_mac_filter *f; + int vlan = 0; + u8 ma[6]; int ret; cnt = sscanf(&cmd_buf[11], @@ -1195,8 +1192,8 @@ static ssize_t i40e_dbg_command_write(struct file *filp, ma, vlan, vsi_seid, f, ret); } else if (strncmp(cmd_buf, "del macaddr", 11) == 0) { - u8 ma[6]; int vlan = 0; + u8 ma[6]; int ret; cnt = sscanf(&cmd_buf[11], @@ -1232,9 +1229,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, ma, vlan, vsi_seid, ret); } else if (strncmp(cmd_buf, "add pvid", 8) == 0) { - int v; - u16 vid; i40e_status ret; + u16 vid; + int v; cnt = sscanf(&cmd_buf[8], "%i %u", &vsi_seid, &v); if (cnt != 2) { @@ -1545,10 +1542,10 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } else if ((strncmp(cmd_buf, "add fd_filter", 13) == 0) || (strncmp(cmd_buf, "rem fd_filter", 13) == 0)) { struct i40e_fdir_data fd_data; - int ret; u16 packet_len, i, j = 0; char *asc_packet; bool add = false; + int ret; asc_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP, GFP_KERNEL); @@ -1636,9 +1633,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, } } else if (strncmp(&cmd_buf[5], "get local", 9) == 0) { + u16 llen, rlen; int ret, i; u8 *buff; - u16 llen, rlen; buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL); if (!buff) goto command_write_done; @@ -1669,9 +1666,9 @@ static ssize_t i40e_dbg_command_write(struct file *filp, kfree(buff); buff = NULL; } else if (strncmp(&cmd_buf[5], "get remote", 10) == 0) { + u16 llen, rlen; int ret, i; u8 *buff; - u16 llen, rlen; buff = kzalloc(I40E_LLDPDU_SIZE, GFP_KERNEL); if (!buff) goto command_write_done; @@ -1747,11 +1744,13 @@ static ssize_t i40e_dbg_command_write(struct file *filp, goto command_write_done; } - /* Read at least 512 words */ - if (buffer_len == 0) - buffer_len = 512; + /* set the max length */ + buffer_len = min_t(u16, buffer_len, I40E_MAX_AQ_BUF_SIZE/2); bytes = 2 * buffer_len; + + /* read at least 1k bytes, no more than 4kB */ + bytes = clamp(bytes, (u16)1024, (u16)I40E_MAX_AQ_BUF_SIZE); buff = kzalloc(bytes, GFP_KERNEL); if (!buff) goto command_write_done; @@ -1903,6 +1902,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, struct i40e_pf *pf = filp->private_data; int bytes_not_copied; struct i40e_vsi *vsi; + char *buf_tmp; int vsi_seid; int i, cnt; @@ -1921,6 +1921,12 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, count -= bytes_not_copied; i40e_dbg_netdev_ops_buf[count] = '\0'; + buf_tmp = strchr(i40e_dbg_netdev_ops_buf, '\n'); + if (buf_tmp) { + *buf_tmp = '\0'; + count = buf_tmp - i40e_dbg_netdev_ops_buf + 1; + } + if (strncmp(i40e_dbg_netdev_ops_buf, "tx_timeout", 10) == 0) { cnt = sscanf(&i40e_dbg_netdev_ops_buf[11], "%i", &vsi_seid); if (cnt != 1) { @@ -1996,7 +2002,7 @@ static ssize_t i40e_dbg_netdev_ops_write(struct file *filp, goto netdev_ops_write_done; } for (i = 0; i < vsi->num_q_vectors; i++) - napi_schedule(&vsi->q_vectors[i].napi); + napi_schedule(&vsi->q_vectors[i]->napi); dev_info(&pf->pdev->dev, "napi called\n"); } else { dev_info(&pf->pdev->dev, "unknown command '%s'\n", @@ -2024,21 +2030,35 @@ static const struct file_operations i40e_dbg_netdev_ops_fops = { **/ void i40e_dbg_pf_init(struct i40e_pf *pf) { - struct dentry *pfile __attribute__((unused)); + struct dentry *pfile; const char *name = pci_name(pf->pdev); + const struct device *dev = &pf->pdev->dev; pf->i40e_dbg_pf = debugfs_create_dir(name, i40e_dbg_root); - if (pf->i40e_dbg_pf) { - pfile = debugfs_create_file("command", 0600, pf->i40e_dbg_pf, - pf, &i40e_dbg_command_fops); - pfile = debugfs_create_file("dump", 0600, pf->i40e_dbg_pf, pf, - &i40e_dbg_dump_fops); - pfile = debugfs_create_file("netdev_ops", 0600, pf->i40e_dbg_pf, - pf, &i40e_dbg_netdev_ops_fops); - } else { - dev_info(&pf->pdev->dev, - "debugfs entry for %s failed\n", name); - } + if (!pf->i40e_dbg_pf) + return; + + pfile = debugfs_create_file("command", 0600, pf->i40e_dbg_pf, pf, + &i40e_dbg_command_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("dump", 0600, pf->i40e_dbg_pf, pf, + &i40e_dbg_dump_fops); + if (!pfile) + goto create_failed; + + pfile = debugfs_create_file("netdev_ops", 0600, pf->i40e_dbg_pf, pf, + &i40e_dbg_netdev_ops_fops); + if (!pfile) + goto create_failed; + + return; + +create_failed: + dev_info(dev, "debugfs dir/file for %s failed\n", name); + debugfs_remove_recursive(pf->i40e_dbg_pf); + return; } /** diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 9a76b8cec76c0b9ef700c8fb6c68c75fd4e09e05..1b86138fa9e19fb0a87510db8d05c5dafd347a61 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -399,8 +399,8 @@ static void i40e_get_ringparam(struct net_device *netdev, ring->tx_max_pending = I40E_MAX_NUM_DESCRIPTORS; ring->rx_mini_max_pending = 0; ring->rx_jumbo_max_pending = 0; - ring->rx_pending = vsi->rx_rings[0].count; - ring->tx_pending = vsi->tx_rings[0].count; + ring->rx_pending = vsi->rx_rings[0]->count; + ring->tx_pending = vsi->tx_rings[0]->count; ring->rx_mini_pending = 0; ring->rx_jumbo_pending = 0; } @@ -429,8 +429,8 @@ static int i40e_set_ringparam(struct net_device *netdev, new_rx_count = ALIGN(new_rx_count, I40E_REQ_DESCRIPTOR_MULTIPLE); /* if nothing to do return success */ - if ((new_tx_count == vsi->tx_rings[0].count) && - (new_rx_count == vsi->rx_rings[0].count)) + if ((new_tx_count == vsi->tx_rings[0]->count) && + (new_rx_count == vsi->rx_rings[0]->count)) return 0; while (test_and_set_bit(__I40E_CONFIG_BUSY, &pf->state)) @@ -439,8 +439,8 @@ static int i40e_set_ringparam(struct net_device *netdev, if (!netif_running(vsi->netdev)) { /* simple case - set for the next time the netdev is started */ for (i = 0; i < vsi->num_queue_pairs; i++) { - vsi->tx_rings[i].count = new_tx_count; - vsi->rx_rings[i].count = new_rx_count; + vsi->tx_rings[i]->count = new_tx_count; + vsi->rx_rings[i]->count = new_rx_count; } goto done; } @@ -451,10 +451,10 @@ static int i40e_set_ringparam(struct net_device *netdev, */ /* alloc updated Tx resources */ - if (new_tx_count != vsi->tx_rings[0].count) { + if (new_tx_count != vsi->tx_rings[0]->count) { netdev_info(netdev, "Changing Tx descriptor count from %d to %d.\n", - vsi->tx_rings[0].count, new_tx_count); + vsi->tx_rings[0]->count, new_tx_count); tx_rings = kcalloc(vsi->alloc_queue_pairs, sizeof(struct i40e_ring), GFP_KERNEL); if (!tx_rings) { @@ -464,7 +464,7 @@ static int i40e_set_ringparam(struct net_device *netdev, for (i = 0; i < vsi->num_queue_pairs; i++) { /* clone ring and setup updated count */ - tx_rings[i] = vsi->tx_rings[i]; + tx_rings[i] = *vsi->tx_rings[i]; tx_rings[i].count = new_tx_count; err = i40e_setup_tx_descriptors(&tx_rings[i]); if (err) { @@ -481,10 +481,10 @@ static int i40e_set_ringparam(struct net_device *netdev, } /* alloc updated Rx resources */ - if (new_rx_count != vsi->rx_rings[0].count) { + if (new_rx_count != vsi->rx_rings[0]->count) { netdev_info(netdev, "Changing Rx descriptor count from %d to %d\n", - vsi->rx_rings[0].count, new_rx_count); + vsi->rx_rings[0]->count, new_rx_count); rx_rings = kcalloc(vsi->alloc_queue_pairs, sizeof(struct i40e_ring), GFP_KERNEL); if (!rx_rings) { @@ -494,7 +494,7 @@ static int i40e_set_ringparam(struct net_device *netdev, for (i = 0; i < vsi->num_queue_pairs; i++) { /* clone ring and setup updated count */ - rx_rings[i] = vsi->rx_rings[i]; + rx_rings[i] = *vsi->rx_rings[i]; rx_rings[i].count = new_rx_count; err = i40e_setup_rx_descriptors(&rx_rings[i]); if (err) { @@ -517,8 +517,8 @@ static int i40e_set_ringparam(struct net_device *netdev, if (tx_rings) { for (i = 0; i < vsi->num_queue_pairs; i++) { - i40e_free_tx_resources(&vsi->tx_rings[i]); - vsi->tx_rings[i] = tx_rings[i]; + i40e_free_tx_resources(vsi->tx_rings[i]); + *vsi->tx_rings[i] = tx_rings[i]; } kfree(tx_rings); tx_rings = NULL; @@ -526,8 +526,8 @@ static int i40e_set_ringparam(struct net_device *netdev, if (rx_rings) { for (i = 0; i < vsi->num_queue_pairs; i++) { - i40e_free_rx_resources(&vsi->rx_rings[i]); - vsi->rx_rings[i] = rx_rings[i]; + i40e_free_rx_resources(vsi->rx_rings[i]); + *vsi->rx_rings[i] = rx_rings[i]; } kfree(rx_rings); rx_rings = NULL; @@ -579,6 +579,7 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, char *p; int j; struct rtnl_link_stats64 *net_stats = i40e_get_vsi_stats_struct(vsi); + unsigned int start; i40e_update_stats(vsi); @@ -587,14 +588,30 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, data[i++] = (i40e_gstrings_net_stats[j].sizeof_stat == sizeof(u64)) ? *(u64 *)p : *(u32 *)p; } - for (j = 0; j < vsi->num_queue_pairs; j++) { - data[i++] = vsi->tx_rings[j].tx_stats.packets; - data[i++] = vsi->tx_rings[j].tx_stats.bytes; - } - for (j = 0; j < vsi->num_queue_pairs; j++) { - data[i++] = vsi->rx_rings[j].rx_stats.packets; - data[i++] = vsi->rx_rings[j].rx_stats.bytes; + rcu_read_lock(); + for (j = 0; j < vsi->num_queue_pairs; j++, i += 4) { + struct i40e_ring *tx_ring = ACCESS_ONCE(vsi->tx_rings[j]); + struct i40e_ring *rx_ring; + + if (!tx_ring) + continue; + + /* process Tx ring statistics */ + do { + start = u64_stats_fetch_begin_bh(&tx_ring->syncp); + data[i] = tx_ring->stats.packets; + data[i + 1] = tx_ring->stats.bytes; + } while (u64_stats_fetch_retry_bh(&tx_ring->syncp, start)); + + /* Rx ring is the 2nd half of the queue pair */ + rx_ring = &tx_ring[1]; + do { + start = u64_stats_fetch_begin_bh(&rx_ring->syncp); + data[i + 2] = rx_ring->stats.packets; + data[i + 3] = rx_ring->stats.bytes; + } while (u64_stats_fetch_retry_bh(&rx_ring->syncp, start)); } + rcu_read_unlock(); if (vsi == pf->vsi[pf->lan_vsi]) { for (j = 0; j < I40E_GLOBAL_STATS_LEN; j++) { p = (char *)pf + i40e_gstrings_stats[j].stat_offset; @@ -641,8 +658,6 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, p += ETH_GSTRING_LEN; snprintf(p, ETH_GSTRING_LEN, "tx-%u.tx_bytes", i); p += ETH_GSTRING_LEN; - } - for (i = 0; i < vsi->num_queue_pairs; i++) { snprintf(p, ETH_GSTRING_LEN, "rx-%u.rx_packets", i); p += ETH_GSTRING_LEN; snprintf(p, ETH_GSTRING_LEN, "rx-%u.rx_bytes", i); @@ -910,8 +925,8 @@ static int i40e_set_coalesce(struct net_device *netdev, } vector = vsi->base_vector; - q_vector = vsi->q_vectors; - for (i = 0; i < vsi->num_q_vectors; i++, vector++, q_vector++) { + for (i = 0; i < vsi->num_q_vectors; i++, vector++) { + q_vector = vsi->q_vectors[i]; q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); wr32(hw, I40E_PFINT_ITRN(0, vector - 1), q_vector->rx.itr); q_vector->tx.itr = ITR_TO_REG(vsi->tx_itr_setting); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 221aa4795017649ccdd57accb01520c4bccb42e8..be15938ba2130372192276670e1621c3e470505c 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -36,7 +36,7 @@ static const char i40e_driver_string[] = #define DRV_VERSION_MAJOR 0 #define DRV_VERSION_MINOR 3 -#define DRV_VERSION_BUILD 9 +#define DRV_VERSION_BUILD 11 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN @@ -347,14 +347,53 @@ struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi) **/ static struct rtnl_link_stats64 *i40e_get_netdev_stats_struct( struct net_device *netdev, - struct rtnl_link_stats64 *storage) + struct rtnl_link_stats64 *stats) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; + struct rtnl_link_stats64 *vsi_stats = i40e_get_vsi_stats_struct(vsi); + int i; + + rcu_read_lock(); + for (i = 0; i < vsi->num_queue_pairs; i++) { + struct i40e_ring *tx_ring, *rx_ring; + u64 bytes, packets; + unsigned int start; + + tx_ring = ACCESS_ONCE(vsi->tx_rings[i]); + if (!tx_ring) + continue; - *storage = *i40e_get_vsi_stats_struct(vsi); + do { + start = u64_stats_fetch_begin_bh(&tx_ring->syncp); + packets = tx_ring->stats.packets; + bytes = tx_ring->stats.bytes; + } while (u64_stats_fetch_retry_bh(&tx_ring->syncp, start)); + + stats->tx_packets += packets; + stats->tx_bytes += bytes; + rx_ring = &tx_ring[1]; - return storage; + do { + start = u64_stats_fetch_begin_bh(&rx_ring->syncp); + packets = rx_ring->stats.packets; + bytes = rx_ring->stats.bytes; + } while (u64_stats_fetch_retry_bh(&rx_ring->syncp, start)); + + stats->rx_packets += packets; + stats->rx_bytes += bytes; + } + rcu_read_unlock(); + + /* following stats updated by ixgbe_watchdog_task() */ + stats->multicast = vsi_stats->multicast; + stats->tx_errors = vsi_stats->tx_errors; + stats->tx_dropped = vsi_stats->tx_dropped; + stats->rx_errors = vsi_stats->rx_errors; + stats->rx_crc_errors = vsi_stats->rx_crc_errors; + stats->rx_length_errors = vsi_stats->rx_length_errors; + + return stats; } /** @@ -376,10 +415,14 @@ void i40e_vsi_reset_stats(struct i40e_vsi *vsi) memset(&vsi->eth_stats_offsets, 0, sizeof(vsi->eth_stats_offsets)); if (vsi->rx_rings) for (i = 0; i < vsi->num_queue_pairs; i++) { - memset(&vsi->rx_rings[i].rx_stats, 0 , - sizeof(vsi->rx_rings[i].rx_stats)); - memset(&vsi->tx_rings[i].tx_stats, 0, - sizeof(vsi->tx_rings[i].tx_stats)); + memset(&vsi->rx_rings[i]->stats, 0 , + sizeof(vsi->rx_rings[i]->stats)); + memset(&vsi->rx_rings[i]->rx_stats, 0 , + sizeof(vsi->rx_rings[i]->rx_stats)); + memset(&vsi->tx_rings[i]->stats, 0 , + sizeof(vsi->tx_rings[i]->stats)); + memset(&vsi->tx_rings[i]->tx_stats, 0, + sizeof(vsi->tx_rings[i]->tx_stats)); } vsi->stat_offsets_loaded = false; } @@ -598,7 +641,7 @@ static void i40e_update_link_xoff_rx(struct i40e_pf *pf) continue; for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *ring = &vsi->tx_rings[i]; + struct i40e_ring *ring = vsi->tx_rings[i]; clear_bit(__I40E_HANG_CHECK_ARMED, &ring->state); } } @@ -652,7 +695,7 @@ static void i40e_update_prio_xoff_rx(struct i40e_pf *pf) continue; for (i = 0; i < vsi->num_queue_pairs; i++) { - struct i40e_ring *ring = &vsi->tx_rings[i]; + struct i40e_ring *ring = vsi->tx_rings[i]; tc = ring->dcb_tc; if (xoff[tc]) @@ -704,21 +747,38 @@ void i40e_update_stats(struct i40e_vsi *vsi) tx_restart = tx_busy = 0; rx_page = 0; rx_buf = 0; + rcu_read_lock(); for (q = 0; q < vsi->num_queue_pairs; q++) { struct i40e_ring *p; + u64 bytes, packets; + unsigned int start; - p = &vsi->rx_rings[q]; - rx_b += p->rx_stats.bytes; - rx_p += p->rx_stats.packets; - rx_buf += p->rx_stats.alloc_rx_buff_failed; - rx_page += p->rx_stats.alloc_rx_page_failed; + /* locate Tx ring */ + p = ACCESS_ONCE(vsi->tx_rings[q]); - p = &vsi->tx_rings[q]; - tx_b += p->tx_stats.bytes; - tx_p += p->tx_stats.packets; + do { + start = u64_stats_fetch_begin_bh(&p->syncp); + packets = p->stats.packets; + bytes = p->stats.bytes; + } while (u64_stats_fetch_retry_bh(&p->syncp, start)); + tx_b += bytes; + tx_p += packets; tx_restart += p->tx_stats.restart_queue; tx_busy += p->tx_stats.tx_busy; + + /* Rx queue is part of the same block as Tx queue */ + p = &p[1]; + do { + start = u64_stats_fetch_begin_bh(&p->syncp); + packets = p->stats.packets; + bytes = p->stats.bytes; + } while (u64_stats_fetch_retry_bh(&p->syncp, start)); + rx_b += bytes; + rx_p += packets; + rx_buf += p->rx_stats.alloc_rx_buff_failed; + rx_page += p->rx_stats.alloc_rx_page_failed; } + rcu_read_unlock(); vsi->tx_restart = tx_restart; vsi->tx_busy = tx_busy; vsi->rx_page_failed = rx_page; @@ -1988,7 +2048,7 @@ static int i40e_vsi_setup_tx_resources(struct i40e_vsi *vsi) int i, err = 0; for (i = 0; i < vsi->num_queue_pairs && !err; i++) - err = i40e_setup_tx_descriptors(&vsi->tx_rings[i]); + err = i40e_setup_tx_descriptors(vsi->tx_rings[i]); return err; } @@ -2004,8 +2064,8 @@ static void i40e_vsi_free_tx_resources(struct i40e_vsi *vsi) int i; for (i = 0; i < vsi->num_queue_pairs; i++) - if (vsi->tx_rings[i].desc) - i40e_free_tx_resources(&vsi->tx_rings[i]); + if (vsi->tx_rings[i]->desc) + i40e_free_tx_resources(vsi->tx_rings[i]); } /** @@ -2023,7 +2083,7 @@ static int i40e_vsi_setup_rx_resources(struct i40e_vsi *vsi) int i, err = 0; for (i = 0; i < vsi->num_queue_pairs && !err; i++) - err = i40e_setup_rx_descriptors(&vsi->rx_rings[i]); + err = i40e_setup_rx_descriptors(vsi->rx_rings[i]); return err; } @@ -2038,8 +2098,8 @@ static void i40e_vsi_free_rx_resources(struct i40e_vsi *vsi) int i; for (i = 0; i < vsi->num_queue_pairs; i++) - if (vsi->rx_rings[i].desc) - i40e_free_rx_resources(&vsi->rx_rings[i]); + if (vsi->rx_rings[i]->desc) + i40e_free_rx_resources(vsi->rx_rings[i]); } /** @@ -2114,8 +2174,8 @@ static int i40e_configure_tx_ring(struct i40e_ring *ring) /* Now associate this queue with this PCI function */ qtx_ctl = I40E_QTX_CTL_PF_QUEUE; - qtx_ctl |= ((hw->hmc.hmc_fn_id << I40E_QTX_CTL_PF_INDX_SHIFT) - & I40E_QTX_CTL_PF_INDX_MASK); + qtx_ctl |= ((hw->pf_id << I40E_QTX_CTL_PF_INDX_SHIFT) & + I40E_QTX_CTL_PF_INDX_MASK); wr32(hw, I40E_QTX_CTL(pf_q), qtx_ctl); i40e_flush(hw); @@ -2223,8 +2283,8 @@ static int i40e_vsi_configure_tx(struct i40e_vsi *vsi) int err = 0; u16 i; - for (i = 0; (i < vsi->num_queue_pairs) && (!err); i++) - err = i40e_configure_tx_ring(&vsi->tx_rings[i]); + for (i = 0; (i < vsi->num_queue_pairs) && !err; i++) + err = i40e_configure_tx_ring(vsi->tx_rings[i]); return err; } @@ -2274,7 +2334,7 @@ static int i40e_vsi_configure_rx(struct i40e_vsi *vsi) /* set up individual rings */ for (i = 0; i < vsi->num_queue_pairs && !err; i++) - err = i40e_configure_rx_ring(&vsi->rx_rings[i]); + err = i40e_configure_rx_ring(vsi->rx_rings[i]); return err; } @@ -2298,8 +2358,8 @@ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi) qoffset = vsi->tc_config.tc_info[n].qoffset; qcount = vsi->tc_config.tc_info[n].qcount; for (i = qoffset; i < (qoffset + qcount); i++) { - struct i40e_ring *rx_ring = &vsi->rx_rings[i]; - struct i40e_ring *tx_ring = &vsi->tx_rings[i]; + struct i40e_ring *rx_ring = vsi->rx_rings[i]; + struct i40e_ring *tx_ring = vsi->tx_rings[i]; rx_ring->dcb_tc = n; tx_ring->dcb_tc = n; } @@ -2354,8 +2414,8 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) */ qp = vsi->base_queue; vector = vsi->base_vector; - q_vector = vsi->q_vectors; - for (i = 0; i < vsi->num_q_vectors; i++, q_vector++, vector++) { + for (i = 0; i < vsi->num_q_vectors; i++, vector++) { + q_vector = vsi->q_vectors[i]; q_vector->rx.itr = ITR_TO_REG(vsi->rx_itr_setting); q_vector->rx.latency_range = I40E_LOW_LATENCY; wr32(hw, I40E_PFINT_ITRN(I40E_RX_ITR, vector - 1), @@ -2435,7 +2495,7 @@ static void i40e_enable_misc_int_causes(struct i40e_hw *hw) **/ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi) { - struct i40e_q_vector *q_vector = vsi->q_vectors; + struct i40e_q_vector *q_vector = vsi->q_vectors[0]; struct i40e_pf *pf = vsi->back; struct i40e_hw *hw = &pf->hw; u32 val; @@ -2472,7 +2532,7 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi) * i40e_irq_dynamic_enable_icr0 - Enable default interrupt generation for icr0 * @pf: board private structure **/ -static void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf) +void i40e_irq_dynamic_enable_icr0(struct i40e_pf *pf) { struct i40e_hw *hw = &pf->hw; u32 val; @@ -2500,7 +2560,7 @@ void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | (I40E_ITR_NONE << I40E_PFINT_DYN_CTLN_ITR_INDX_SHIFT); wr32(hw, I40E_PFINT_DYN_CTLN(vector - 1), val); - i40e_flush(hw); + /* skip the flush */ } /** @@ -2512,7 +2572,7 @@ static irqreturn_t i40e_msix_clean_rings(int irq, void *data) { struct i40e_q_vector *q_vector = data; - if (!q_vector->tx.ring[0] && !q_vector->rx.ring[0]) + if (!q_vector->tx.ring && !q_vector->rx.ring) return IRQ_HANDLED; napi_schedule(&q_vector->napi); @@ -2529,7 +2589,7 @@ static irqreturn_t i40e_fdir_clean_rings(int irq, void *data) { struct i40e_q_vector *q_vector = data; - if (!q_vector->tx.ring[0] && !q_vector->rx.ring[0]) + if (!q_vector->tx.ring && !q_vector->rx.ring) return IRQ_HANDLED; pr_info("fdir ring cleaning needed\n"); @@ -2554,16 +2614,16 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) int vector, err; for (vector = 0; vector < q_vectors; vector++) { - struct i40e_q_vector *q_vector = &(vsi->q_vectors[vector]); + struct i40e_q_vector *q_vector = vsi->q_vectors[vector]; - if (q_vector->tx.ring[0] && q_vector->rx.ring[0]) { + if (q_vector->tx.ring && q_vector->rx.ring) { snprintf(q_vector->name, sizeof(q_vector->name) - 1, "%s-%s-%d", basename, "TxRx", rx_int_idx++); tx_int_idx++; - } else if (q_vector->rx.ring[0]) { + } else if (q_vector->rx.ring) { snprintf(q_vector->name, sizeof(q_vector->name) - 1, "%s-%s-%d", basename, "rx", rx_int_idx++); - } else if (q_vector->tx.ring[0]) { + } else if (q_vector->tx.ring) { snprintf(q_vector->name, sizeof(q_vector->name) - 1, "%s-%s-%d", basename, "tx", tx_int_idx++); } else { @@ -2611,8 +2671,8 @@ static void i40e_vsi_disable_irq(struct i40e_vsi *vsi) int i; for (i = 0; i < vsi->num_queue_pairs; i++) { - wr32(hw, I40E_QINT_TQCTL(vsi->tx_rings[i].reg_idx), 0); - wr32(hw, I40E_QINT_RQCTL(vsi->rx_rings[i].reg_idx), 0); + wr32(hw, I40E_QINT_TQCTL(vsi->tx_rings[i]->reg_idx), 0); + wr32(hw, I40E_QINT_RQCTL(vsi->rx_rings[i]->reg_idx), 0); } if (pf->flags & I40E_FLAG_MSIX_ENABLED) { @@ -2649,6 +2709,7 @@ static int i40e_vsi_enable_irq(struct i40e_vsi *vsi) i40e_irq_dynamic_enable_icr0(pf); } + i40e_flush(&pf->hw); return 0; } @@ -2681,14 +2742,14 @@ static irqreturn_t i40e_intr(int irq, void *data) icr0 = rd32(hw, I40E_PFINT_ICR0); - /* if sharing a legacy IRQ, we might get called w/o an intr pending */ - if ((icr0 & I40E_PFINT_ICR0_INTEVENT_MASK) == 0) - return IRQ_NONE; - val = rd32(hw, I40E_PFINT_DYN_CTL0); val = val | I40E_PFINT_DYN_CTL0_CLEARPBA_MASK; wr32(hw, I40E_PFINT_DYN_CTL0, val); + /* if sharing a legacy IRQ, we might get called w/o an intr pending */ + if ((icr0 & I40E_PFINT_ICR0_INTEVENT_MASK) == 0) + return IRQ_NONE; + ena_mask = rd32(hw, I40E_PFINT_ICR0_ENA); /* only q0 is used in MSI/Legacy mode, and none are used in MSIX */ @@ -2702,10 +2763,9 @@ static irqreturn_t i40e_intr(int irq, void *data) qval = rd32(hw, I40E_QINT_TQCTL(0)); qval &= ~I40E_QINT_TQCTL_CAUSE_ENA_MASK; wr32(hw, I40E_QINT_TQCTL(0), qval); - i40e_flush(hw); if (!test_bit(__I40E_DOWN, &pf->state)) - napi_schedule(&pf->vsi[pf->lan_vsi]->q_vectors[0].napi); + napi_schedule(&pf->vsi[pf->lan_vsi]->q_vectors[0]->napi); } if (icr0 & I40E_PFINT_ICR0_ADMINQ_MASK) { @@ -2764,7 +2824,6 @@ static irqreturn_t i40e_intr(int irq, void *data) /* re-enable interrupt causes */ wr32(hw, I40E_PFINT_ICR0_ENA, ena_mask); - i40e_flush(hw); if (!test_bit(__I40E_DOWN, &pf->state)) { i40e_service_event_schedule(pf); i40e_irq_dynamic_enable_icr0(pf); @@ -2774,40 +2833,26 @@ static irqreturn_t i40e_intr(int irq, void *data) } /** - * i40e_map_vector_to_rxq - Assigns the Rx queue to the vector + * i40e_map_vector_to_qp - Assigns the queue pair to the vector * @vsi: the VSI being configured * @v_idx: vector index - * @r_idx: rx queue index + * @qp_idx: queue pair index **/ -static void map_vector_to_rxq(struct i40e_vsi *vsi, int v_idx, int r_idx) +static void map_vector_to_qp(struct i40e_vsi *vsi, int v_idx, int qp_idx) { - struct i40e_q_vector *q_vector = &(vsi->q_vectors[v_idx]); - struct i40e_ring *rx_ring = &(vsi->rx_rings[r_idx]); - - rx_ring->q_vector = q_vector; - q_vector->rx.ring[q_vector->rx.count] = rx_ring; - q_vector->rx.count++; - q_vector->rx.latency_range = I40E_LOW_LATENCY; - q_vector->vsi = vsi; -} - -/** - * i40e_map_vector_to_txq - Assigns the Tx queue to the vector - * @vsi: the VSI being configured - * @v_idx: vector index - * @t_idx: tx queue index - **/ -static void map_vector_to_txq(struct i40e_vsi *vsi, int v_idx, int t_idx) -{ - struct i40e_q_vector *q_vector = &(vsi->q_vectors[v_idx]); - struct i40e_ring *tx_ring = &(vsi->tx_rings[t_idx]); + struct i40e_q_vector *q_vector = vsi->q_vectors[v_idx]; + struct i40e_ring *tx_ring = vsi->tx_rings[qp_idx]; + struct i40e_ring *rx_ring = vsi->rx_rings[qp_idx]; tx_ring->q_vector = q_vector; - q_vector->tx.ring[q_vector->tx.count] = tx_ring; + tx_ring->next = q_vector->tx.ring; + q_vector->tx.ring = tx_ring; q_vector->tx.count++; - q_vector->tx.latency_range = I40E_LOW_LATENCY; - q_vector->num_ringpairs++; - q_vector->vsi = vsi; + + rx_ring->q_vector = q_vector; + rx_ring->next = q_vector->rx.ring; + q_vector->rx.ring = rx_ring; + q_vector->rx.count++; } /** @@ -2823,7 +2868,7 @@ static void i40e_vsi_map_rings_to_vectors(struct i40e_vsi *vsi) { int qp_remaining = vsi->num_queue_pairs; int q_vectors = vsi->num_q_vectors; - int qp_per_vector; + int num_ringpairs; int v_start = 0; int qp_idx = 0; @@ -2831,11 +2876,21 @@ static void i40e_vsi_map_rings_to_vectors(struct i40e_vsi *vsi) * group them so there are multiple queues per vector. */ for (; v_start < q_vectors && qp_remaining; v_start++) { - qp_per_vector = DIV_ROUND_UP(qp_remaining, q_vectors - v_start); - for (; qp_per_vector; - qp_per_vector--, qp_idx++, qp_remaining--) { - map_vector_to_rxq(vsi, v_start, qp_idx); - map_vector_to_txq(vsi, v_start, qp_idx); + struct i40e_q_vector *q_vector = vsi->q_vectors[v_start]; + + num_ringpairs = DIV_ROUND_UP(qp_remaining, q_vectors - v_start); + + q_vector->num_ringpairs = num_ringpairs; + + q_vector->rx.count = 0; + q_vector->tx.count = 0; + q_vector->rx.ring = NULL; + q_vector->tx.ring = NULL; + + while (num_ringpairs--) { + map_vector_to_qp(vsi, v_start, qp_idx); + qp_idx++; + qp_remaining--; } } } @@ -2887,7 +2942,7 @@ static void i40e_netpoll(struct net_device *netdev) pf->flags |= I40E_FLAG_IN_NETPOLL; if (pf->flags & I40E_FLAG_MSIX_ENABLED) { for (i = 0; i < vsi->num_q_vectors; i++) - i40e_msix_clean_rings(0, &vsi->q_vectors[i]); + i40e_msix_clean_rings(0, vsi->q_vectors[i]); } else { i40e_intr(pf->pdev->irq, netdev); } @@ -3073,14 +3128,14 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi) u16 vector = i + base; /* free only the irqs that were actually requested */ - if (vsi->q_vectors[i].num_ringpairs == 0) + if (vsi->q_vectors[i]->num_ringpairs == 0) continue; /* clear the affinity_mask in the IRQ descriptor */ irq_set_affinity_hint(pf->msix_entries[vector].vector, NULL); free_irq(pf->msix_entries[vector].vector, - &vsi->q_vectors[i]); + vsi->q_vectors[i]); /* Tear down the interrupt queue link list * @@ -3163,6 +3218,39 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi) } } +/** + * i40e_free_q_vector - Free memory allocated for specific interrupt vector + * @vsi: the VSI being configured + * @v_idx: Index of vector to be freed + * + * This function frees the memory allocated to the q_vector. In addition if + * NAPI is enabled it will delete any references to the NAPI struct prior + * to freeing the q_vector. + **/ +static void i40e_free_q_vector(struct i40e_vsi *vsi, int v_idx) +{ + struct i40e_q_vector *q_vector = vsi->q_vectors[v_idx]; + struct i40e_ring *ring; + + if (!q_vector) + return; + + /* disassociate q_vector from rings */ + i40e_for_each_ring(ring, q_vector->tx) + ring->q_vector = NULL; + + i40e_for_each_ring(ring, q_vector->rx) + ring->q_vector = NULL; + + /* only VSI w/ an associated netdev is set up w/ NAPI */ + if (vsi->netdev) + netif_napi_del(&q_vector->napi); + + vsi->q_vectors[v_idx] = NULL; + + kfree_rcu(q_vector, rcu); +} + /** * i40e_vsi_free_q_vectors - Free memory allocated for interrupt vectors * @vsi: the VSI being un-configured @@ -3174,24 +3262,8 @@ static void i40e_vsi_free_q_vectors(struct i40e_vsi *vsi) { int v_idx; - for (v_idx = 0; v_idx < vsi->num_q_vectors; v_idx++) { - struct i40e_q_vector *q_vector = &vsi->q_vectors[v_idx]; - int r_idx; - - if (!q_vector) - continue; - - /* disassociate q_vector from rings */ - for (r_idx = 0; r_idx < q_vector->tx.count; r_idx++) - q_vector->tx.ring[r_idx]->q_vector = NULL; - for (r_idx = 0; r_idx < q_vector->rx.count; r_idx++) - q_vector->rx.ring[r_idx]->q_vector = NULL; - - /* only VSI w/ an associated netdev is set up w/ NAPI */ - if (vsi->netdev) - netif_napi_del(&q_vector->napi); - } - kfree(vsi->q_vectors); + for (v_idx = 0; v_idx < vsi->num_q_vectors; v_idx++) + i40e_free_q_vector(vsi, v_idx); } /** @@ -3241,7 +3313,7 @@ static void i40e_napi_enable_all(struct i40e_vsi *vsi) return; for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) - napi_enable(&vsi->q_vectors[q_idx].napi); + napi_enable(&vsi->q_vectors[q_idx]->napi); } /** @@ -3256,7 +3328,7 @@ static void i40e_napi_disable_all(struct i40e_vsi *vsi) return; for (q_idx = 0; q_idx < vsi->num_q_vectors; q_idx++) - napi_disable(&vsi->q_vectors[q_idx].napi); + napi_disable(&vsi->q_vectors[q_idx]->napi); } /** @@ -3703,8 +3775,11 @@ static int i40e_up_complete(struct i40e_vsi *vsi) if ((pf->hw.phy.link_info.link_info & I40E_AQ_LINK_UP) && (vsi->netdev)) { + netdev_info(vsi->netdev, "NIC Link is Up\n"); netif_tx_start_all_queues(vsi->netdev); netif_carrier_on(vsi->netdev); + } else if (vsi->netdev) { + netdev_info(vsi->netdev, "NIC Link is Down\n"); } i40e_service_event_schedule(pf); @@ -3772,8 +3847,8 @@ void i40e_down(struct i40e_vsi *vsi) i40e_napi_disable_all(vsi); for (i = 0; i < vsi->num_queue_pairs; i++) { - i40e_clean_tx_ring(&vsi->tx_rings[i]); - i40e_clean_rx_ring(&vsi->rx_rings[i]); + i40e_clean_tx_ring(vsi->tx_rings[i]); + i40e_clean_rx_ring(vsi->rx_rings[i]); } } @@ -4153,8 +4228,9 @@ static void i40e_link_event(struct i40e_pf *pf) if (new_link == old_link) return; - netdev_info(pf->vsi[pf->lan_vsi]->netdev, - "NIC Link is %s\n", (new_link ? "Up" : "Down")); + if (!test_bit(__I40E_DOWN, &pf->vsi[pf->lan_vsi]->state)) + netdev_info(pf->vsi[pf->lan_vsi]->netdev, + "NIC Link is %s\n", (new_link ? "Up" : "Down")); /* Notify the base of the switch tree connected to * the link. Floating VEBs are not notified. @@ -4199,9 +4275,9 @@ static void i40e_check_hang_subtask(struct i40e_pf *pf) continue; for (i = 0; i < vsi->num_queue_pairs; i++) { - set_check_for_tx_hang(&vsi->tx_rings[i]); + set_check_for_tx_hang(vsi->tx_rings[i]); if (test_bit(__I40E_HANG_CHECK_ARMED, - &vsi->tx_rings[i].state)) + &vsi->tx_rings[i]->state)) armed++; } @@ -4537,7 +4613,8 @@ static void i40e_fdir_setup(struct i40e_pf *pf) bool new_vsi = false; int err, i; - if (!(pf->flags & (I40E_FLAG_FDIR_ENABLED|I40E_FLAG_FDIR_ATR_ENABLED))) + if (!(pf->flags & (I40E_FLAG_FDIR_ENABLED | + I40E_FLAG_FDIR_ATR_ENABLED))) return; pf->atr_sample_rate = I40E_DEFAULT_ATR_SAMPLE_RATE; @@ -4937,6 +5014,8 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) { int ret = -ENODEV; struct i40e_vsi *vsi; + int sz_vectors; + int sz_rings; int vsi_idx; int i; @@ -4962,14 +5041,14 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) vsi_idx = i; /* Found one! */ } else { ret = -ENODEV; - goto err_alloc_vsi; /* out of VSI slots! */ + goto unlock_pf; /* out of VSI slots! */ } pf->next_vsi = ++i; vsi = kzalloc(sizeof(*vsi), GFP_KERNEL); if (!vsi) { ret = -ENOMEM; - goto err_alloc_vsi; + goto unlock_pf; } vsi->type = type; vsi->back = pf; @@ -4982,14 +5061,40 @@ static int i40e_vsi_mem_alloc(struct i40e_pf *pf, enum i40e_vsi_type type) vsi->work_limit = I40E_DEFAULT_IRQ_WORK; INIT_LIST_HEAD(&vsi->mac_filter_list); - i40e_set_num_rings_in_vsi(vsi); + ret = i40e_set_num_rings_in_vsi(vsi); + if (ret) + goto err_rings; + + /* allocate memory for ring pointers */ + sz_rings = sizeof(struct i40e_ring *) * vsi->alloc_queue_pairs * 2; + vsi->tx_rings = kzalloc(sz_rings, GFP_KERNEL); + if (!vsi->tx_rings) { + ret = -ENOMEM; + goto err_rings; + } + vsi->rx_rings = &vsi->tx_rings[vsi->alloc_queue_pairs]; + + /* allocate memory for q_vector pointers */ + sz_vectors = sizeof(struct i40e_q_vectors *) * vsi->num_q_vectors; + vsi->q_vectors = kzalloc(sz_vectors, GFP_KERNEL); + if (!vsi->q_vectors) { + ret = -ENOMEM; + goto err_vectors; + } /* Setup default MSIX irq handler for VSI */ i40e_vsi_setup_irqhandler(vsi, i40e_msix_clean_rings); pf->vsi[vsi_idx] = vsi; ret = vsi_idx; -err_alloc_vsi: + goto unlock_pf; + +err_vectors: + kfree(vsi->tx_rings); +err_rings: + pf->next_vsi = i - 1; + kfree(vsi); +unlock_pf: mutex_unlock(&pf->switch_mutex); return ret; } @@ -5030,6 +5135,10 @@ static int i40e_vsi_clear(struct i40e_vsi *vsi) i40e_put_lump(pf->qp_pile, vsi->base_queue, vsi->idx); i40e_put_lump(pf->irq_pile, vsi->base_vector, vsi->idx); + /* free the ring and vector containers */ + kfree(vsi->q_vectors); + kfree(vsi->tx_rings); + pf->vsi[vsi->idx] = NULL; if (vsi->idx < pf->next_vsi) pf->next_vsi = vsi->idx; @@ -5042,6 +5151,24 @@ free_vsi: return 0; } +/** + * i40e_vsi_clear_rings - Deallocates the Rx and Tx rings for the provided VSI + * @vsi: the VSI being cleaned + **/ +static s32 i40e_vsi_clear_rings(struct i40e_vsi *vsi) +{ + int i; + + if (vsi->tx_rings[0]) + for (i = 0; i < vsi->alloc_queue_pairs; i++) { + kfree_rcu(vsi->tx_rings[i], rcu); + vsi->tx_rings[i] = NULL; + vsi->rx_rings[i] = NULL; + } + + return 0; +} + /** * i40e_alloc_rings - Allocates the Rx and Tx rings for the provided VSI * @vsi: the VSI being configured @@ -5049,28 +5176,16 @@ free_vsi: static int i40e_alloc_rings(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; - int ret = 0; int i; - vsi->rx_rings = kcalloc(vsi->alloc_queue_pairs, - sizeof(struct i40e_ring), GFP_KERNEL); - if (!vsi->rx_rings) { - ret = -ENOMEM; - goto err_alloc_rings; - } - - vsi->tx_rings = kcalloc(vsi->alloc_queue_pairs, - sizeof(struct i40e_ring), GFP_KERNEL); - if (!vsi->tx_rings) { - ret = -ENOMEM; - kfree(vsi->rx_rings); - goto err_alloc_rings; - } - /* Set basic values in the rings to be used later during open() */ for (i = 0; i < vsi->alloc_queue_pairs; i++) { - struct i40e_ring *rx_ring = &vsi->rx_rings[i]; - struct i40e_ring *tx_ring = &vsi->tx_rings[i]; + struct i40e_ring *tx_ring; + struct i40e_ring *rx_ring; + + tx_ring = kzalloc(sizeof(struct i40e_ring) * 2, GFP_KERNEL); + if (!tx_ring) + goto err_out; tx_ring->queue_index = i; tx_ring->reg_idx = vsi->base_queue + i; @@ -5081,7 +5196,9 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi) tx_ring->count = vsi->num_desc; tx_ring->size = 0; tx_ring->dcb_tc = 0; + vsi->tx_rings[i] = tx_ring; + rx_ring = &tx_ring[1]; rx_ring->queue_index = i; rx_ring->reg_idx = vsi->base_queue + i; rx_ring->ring_active = false; @@ -5095,24 +5212,14 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi) set_ring_16byte_desc_enabled(rx_ring); else clear_ring_16byte_desc_enabled(rx_ring); - } - -err_alloc_rings: - return ret; -} - -/** - * i40e_vsi_clear_rings - Deallocates the Rx and Tx rings for the provided VSI - * @vsi: the VSI being cleaned - **/ -static int i40e_vsi_clear_rings(struct i40e_vsi *vsi) -{ - if (vsi) { - kfree(vsi->rx_rings); - kfree(vsi->tx_rings); + vsi->rx_rings[i] = rx_ring; } return 0; + +err_out: + i40e_vsi_clear_rings(vsi); + return -ENOMEM; } /** @@ -5248,6 +5355,38 @@ static int i40e_init_msix(struct i40e_pf *pf) return err; } +/** + * i40e_alloc_q_vector - Allocate memory for a single interrupt vector + * @vsi: the VSI being configured + * @v_idx: index of the vector in the vsi struct + * + * We allocate one q_vector. If allocation fails we return -ENOMEM. + **/ +static int i40e_alloc_q_vector(struct i40e_vsi *vsi, int v_idx) +{ + struct i40e_q_vector *q_vector; + + /* allocate q_vector */ + q_vector = kzalloc(sizeof(struct i40e_q_vector), GFP_KERNEL); + if (!q_vector) + return -ENOMEM; + + q_vector->vsi = vsi; + q_vector->v_idx = v_idx; + cpumask_set_cpu(v_idx, &q_vector->affinity_mask); + if (vsi->netdev) + netif_napi_add(vsi->netdev, &q_vector->napi, + i40e_napi_poll, vsi->work_limit); + + q_vector->rx.latency_range = I40E_LOW_LATENCY; + q_vector->tx.latency_range = I40E_LOW_LATENCY; + + /* tie q_vector and vsi together */ + vsi->q_vectors[v_idx] = q_vector; + + return 0; +} + /** * i40e_alloc_q_vectors - Allocate memory for interrupt vectors * @vsi: the VSI being configured @@ -5259,6 +5398,7 @@ static int i40e_alloc_q_vectors(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; int v_idx, num_q_vectors; + int err; /* if not MSIX, give the one vector only to the LAN VSI */ if (pf->flags & I40E_FLAG_MSIX_ENABLED) @@ -5268,22 +5408,19 @@ static int i40e_alloc_q_vectors(struct i40e_vsi *vsi) else return -EINVAL; - vsi->q_vectors = kcalloc(num_q_vectors, - sizeof(struct i40e_q_vector), - GFP_KERNEL); - if (!vsi->q_vectors) - return -ENOMEM; - for (v_idx = 0; v_idx < num_q_vectors; v_idx++) { - vsi->q_vectors[v_idx].vsi = vsi; - vsi->q_vectors[v_idx].v_idx = v_idx; - cpumask_set_cpu(v_idx, &vsi->q_vectors[v_idx].affinity_mask); - if (vsi->netdev) - netif_napi_add(vsi->netdev, &vsi->q_vectors[v_idx].napi, - i40e_napi_poll, vsi->work_limit); + err = i40e_alloc_q_vector(vsi, v_idx); + if (err) + goto err_out; } return 0; + +err_out: + while (v_idx--) + i40e_free_q_vector(vsi, v_idx); + + return err; } /** @@ -5297,7 +5434,8 @@ static void i40e_init_interrupt_scheme(struct i40e_pf *pf) if (pf->flags & I40E_FLAG_MSIX_ENABLED) { err = i40e_init_msix(pf); if (err) { - pf->flags &= ~(I40E_FLAG_RSS_ENABLED | + pf->flags &= ~(I40E_FLAG_MSIX_ENABLED | + I40E_FLAG_RSS_ENABLED | I40E_FLAG_MQ_ENABLED | I40E_FLAG_DCB_ENABLED | I40E_FLAG_SRIOV_ENABLED | @@ -5312,14 +5450,17 @@ static void i40e_init_interrupt_scheme(struct i40e_pf *pf) if (!(pf->flags & I40E_FLAG_MSIX_ENABLED) && (pf->flags & I40E_FLAG_MSI_ENABLED)) { + dev_info(&pf->pdev->dev, "MSIX not available, trying MSI\n"); err = pci_enable_msi(pf->pdev); if (err) { - dev_info(&pf->pdev->dev, - "MSI init failed (%d), trying legacy.\n", err); + dev_info(&pf->pdev->dev, "MSI init failed - %d\n", err); pf->flags &= ~I40E_FLAG_MSI_ENABLED; } } + if (!(pf->flags & (I40E_FLAG_MSIX_ENABLED | I40E_FLAG_MSI_ENABLED))) + dev_info(&pf->pdev->dev, "MSIX and MSI not available, falling back to Legacy IRQ\n"); + /* track first vector for misc interrupts */ err = i40e_get_lump(pf, pf->irq_pile, 1, I40E_PILE_VALID_BIT-1); } @@ -5950,7 +6091,7 @@ static int i40e_vsi_setup_vectors(struct i40e_vsi *vsi) int ret = -ENOENT; struct i40e_pf *pf = vsi->back; - if (vsi->q_vectors) { + if (vsi->q_vectors[0]) { dev_info(&pf->pdev->dev, "VSI %d has existing q_vectors\n", vsi->seid); return -EEXIST; @@ -5972,8 +6113,9 @@ static int i40e_vsi_setup_vectors(struct i40e_vsi *vsi) goto vector_setup_out; } - vsi->base_vector = i40e_get_lump(pf, pf->irq_pile, - vsi->num_q_vectors, vsi->idx); + if (vsi->num_q_vectors) + vsi->base_vector = i40e_get_lump(pf, pf->irq_pile, + vsi->num_q_vectors, vsi->idx); if (vsi->base_vector < 0) { dev_info(&pf->pdev->dev, "failed to get q tracking for VSI %d, err=%d\n", @@ -7062,8 +7204,10 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) */ len = sizeof(struct i40e_vsi *) * pf->hw.func_caps.num_vsis; pf->vsi = kzalloc(len, GFP_KERNEL); - if (!pf->vsi) + if (!pf->vsi) { + err = -ENOMEM; goto err_switch_setup; + } err = i40e_setup_pf_switch(pf); if (err) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 49d2cfa9b0cceb20c5b5f3f6d64cac1968398b60..f1f03bc5c729131bacdb13e243213b18b5b43132 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -37,6 +37,7 @@ static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size, ((u64)td_tag << I40E_TXD_QW1_L2TAG1_SHIFT)); } +#define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) /** * i40e_program_fdir_filter - Program a Flow Director filter * @fdir_input: Packet data that will be filter parameters @@ -50,6 +51,7 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data, struct i40e_tx_buffer *tx_buf; struct i40e_tx_desc *tx_desc; struct i40e_ring *tx_ring; + unsigned int fpt, dcc; struct i40e_vsi *vsi; struct device *dev; dma_addr_t dma; @@ -64,93 +66,78 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data, if (!vsi) return -ENOENT; - tx_ring = &vsi->tx_rings[0]; + tx_ring = vsi->tx_rings[0]; dev = tx_ring->dev; dma = dma_map_single(dev, fdir_data->raw_packet, - I40E_FDIR_MAX_RAW_PACKET_LOOKUP, DMA_TO_DEVICE); + I40E_FDIR_MAX_RAW_PACKET_LOOKUP, DMA_TO_DEVICE); if (dma_mapping_error(dev, dma)) goto dma_fail; /* grab the next descriptor */ - fdir_desc = I40E_TX_FDIRDESC(tx_ring, tx_ring->next_to_use); - tx_buf = &tx_ring->tx_bi[tx_ring->next_to_use]; - tx_ring->next_to_use++; - if (tx_ring->next_to_use == tx_ring->count) - tx_ring->next_to_use = 0; + i = tx_ring->next_to_use; + fdir_desc = I40E_TX_FDIRDESC(tx_ring, i); + tx_buf = &tx_ring->tx_bi[i]; + + tx_ring->next_to_use = (i + 1 < tx_ring->count) ? i + 1 : 0; - fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32((fdir_data->q_index - << I40E_TXD_FLTR_QW0_QINDEX_SHIFT) - & I40E_TXD_FLTR_QW0_QINDEX_MASK); + fpt = (fdir_data->q_index << I40E_TXD_FLTR_QW0_QINDEX_SHIFT) & + I40E_TXD_FLTR_QW0_QINDEX_MASK; - fdir_desc->qindex_flex_ptype_vsi |= cpu_to_le32((fdir_data->flex_off - << I40E_TXD_FLTR_QW0_FLEXOFF_SHIFT) - & I40E_TXD_FLTR_QW0_FLEXOFF_MASK); + fpt |= (fdir_data->flex_off << I40E_TXD_FLTR_QW0_FLEXOFF_SHIFT) & + I40E_TXD_FLTR_QW0_FLEXOFF_MASK; - fdir_desc->qindex_flex_ptype_vsi |= cpu_to_le32((fdir_data->pctype - << I40E_TXD_FLTR_QW0_PCTYPE_SHIFT) - & I40E_TXD_FLTR_QW0_PCTYPE_MASK); + fpt |= (fdir_data->pctype << I40E_TXD_FLTR_QW0_PCTYPE_SHIFT) & + I40E_TXD_FLTR_QW0_PCTYPE_MASK; /* Use LAN VSI Id if not programmed by user */ if (fdir_data->dest_vsi == 0) - fdir_desc->qindex_flex_ptype_vsi |= - cpu_to_le32((pf->vsi[pf->lan_vsi]->id) - << I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT); + fpt |= (pf->vsi[pf->lan_vsi]->id) << + I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT; else - fdir_desc->qindex_flex_ptype_vsi |= - cpu_to_le32((fdir_data->dest_vsi - << I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT) - & I40E_TXD_FLTR_QW0_DEST_VSI_MASK); + fpt |= ((u32)fdir_data->dest_vsi << + I40E_TXD_FLTR_QW0_DEST_VSI_SHIFT) & + I40E_TXD_FLTR_QW0_DEST_VSI_MASK; - fdir_desc->dtype_cmd_cntindex = - cpu_to_le32(I40E_TX_DESC_DTYPE_FILTER_PROG); + fdir_desc->qindex_flex_ptype_vsi = cpu_to_le32(fpt); + + dcc = I40E_TX_DESC_DTYPE_FILTER_PROG; if (add) - fdir_desc->dtype_cmd_cntindex |= cpu_to_le32( - I40E_FILTER_PROGRAM_DESC_PCMD_ADD_UPDATE - << I40E_TXD_FLTR_QW1_PCMD_SHIFT); + dcc |= I40E_FILTER_PROGRAM_DESC_PCMD_ADD_UPDATE << + I40E_TXD_FLTR_QW1_PCMD_SHIFT; else - fdir_desc->dtype_cmd_cntindex |= cpu_to_le32( - I40E_FILTER_PROGRAM_DESC_PCMD_REMOVE - << I40E_TXD_FLTR_QW1_PCMD_SHIFT); + dcc |= I40E_FILTER_PROGRAM_DESC_PCMD_REMOVE << + I40E_TXD_FLTR_QW1_PCMD_SHIFT; - fdir_desc->dtype_cmd_cntindex |= cpu_to_le32((fdir_data->dest_ctl - << I40E_TXD_FLTR_QW1_DEST_SHIFT) - & I40E_TXD_FLTR_QW1_DEST_MASK); + dcc |= (fdir_data->dest_ctl << I40E_TXD_FLTR_QW1_DEST_SHIFT) & + I40E_TXD_FLTR_QW1_DEST_MASK; - fdir_desc->dtype_cmd_cntindex |= cpu_to_le32( - (fdir_data->fd_status << I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT) - & I40E_TXD_FLTR_QW1_FD_STATUS_MASK); + dcc |= (fdir_data->fd_status << I40E_TXD_FLTR_QW1_FD_STATUS_SHIFT) & + I40E_TXD_FLTR_QW1_FD_STATUS_MASK; if (fdir_data->cnt_index != 0) { - fdir_desc->dtype_cmd_cntindex |= - cpu_to_le32(I40E_TXD_FLTR_QW1_CNT_ENA_MASK); - fdir_desc->dtype_cmd_cntindex |= - cpu_to_le32((fdir_data->cnt_index - << I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) - & I40E_TXD_FLTR_QW1_CNTINDEX_MASK); + dcc |= I40E_TXD_FLTR_QW1_CNT_ENA_MASK; + dcc |= ((u32)fdir_data->cnt_index << + I40E_TXD_FLTR_QW1_CNTINDEX_SHIFT) & + I40E_TXD_FLTR_QW1_CNTINDEX_MASK; } + fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dcc); fdir_desc->fd_id = cpu_to_le32(fdir_data->fd_id); /* Now program a dummy descriptor */ - tx_desc = I40E_TX_DESC(tx_ring, tx_ring->next_to_use); - tx_buf = &tx_ring->tx_bi[tx_ring->next_to_use]; - tx_ring->next_to_use++; - if (tx_ring->next_to_use == tx_ring->count) - tx_ring->next_to_use = 0; + i = tx_ring->next_to_use; + tx_desc = I40E_TX_DESC(tx_ring, i); + + tx_ring->next_to_use = (i + 1 < tx_ring->count) ? i + 1 : 0; tx_desc->buffer_addr = cpu_to_le64(dma); - td_cmd = I40E_TX_DESC_CMD_EOP | - I40E_TX_DESC_CMD_RS | - I40E_TX_DESC_CMD_DUMMY; + td_cmd = I40E_TXD_CMD | I40E_TX_DESC_CMD_DUMMY; tx_desc->cmd_type_offset_bsz = build_ctob(td_cmd, 0, I40E_FDIR_MAX_RAW_PACKET_LOOKUP, 0); - /* Mark the data descriptor to be watched */ - tx_buf->next_to_watch = tx_desc; - /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, @@ -158,6 +145,9 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data, */ wmb(); + /* Mark the data descriptor to be watched */ + tx_buf->next_to_watch = tx_desc; + writel(tx_ring->next_to_use, tx_ring->tail); return 0; @@ -188,27 +178,30 @@ static void i40e_fd_handle_status(struct i40e_ring *rx_ring, u32 qw, u8 prog_id) } /** - * i40e_unmap_tx_resource - Release a Tx buffer + * i40e_unmap_and_free_tx_resource - Release a Tx buffer * @ring: the ring that owns the buffer * @tx_buffer: the buffer to free **/ -static inline void i40e_unmap_tx_resource(struct i40e_ring *ring, - struct i40e_tx_buffer *tx_buffer) +static void i40e_unmap_and_free_tx_resource(struct i40e_ring *ring, + struct i40e_tx_buffer *tx_buffer) { - if (tx_buffer->dma) { - if (tx_buffer->tx_flags & I40E_TX_FLAGS_MAPPED_AS_PAGE) - dma_unmap_page(ring->dev, - tx_buffer->dma, - tx_buffer->length, - DMA_TO_DEVICE); - else + if (tx_buffer->skb) { + dev_kfree_skb_any(tx_buffer->skb); + if (dma_unmap_len(tx_buffer, len)) dma_unmap_single(ring->dev, - tx_buffer->dma, - tx_buffer->length, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), DMA_TO_DEVICE); + } else if (dma_unmap_len(tx_buffer, len)) { + dma_unmap_page(ring->dev, + dma_unmap_addr(tx_buffer, dma), + dma_unmap_len(tx_buffer, len), + DMA_TO_DEVICE); } - tx_buffer->dma = 0; - tx_buffer->time_stamp = 0; + tx_buffer->next_to_watch = NULL; + tx_buffer->skb = NULL; + dma_unmap_len_set(tx_buffer, len, 0); + /* tx_buffer must be completely set up in the transmit path */ } /** @@ -217,7 +210,6 @@ static inline void i40e_unmap_tx_resource(struct i40e_ring *ring, **/ void i40e_clean_tx_ring(struct i40e_ring *tx_ring) { - struct i40e_tx_buffer *tx_buffer; unsigned long bi_size; u16 i; @@ -226,13 +218,8 @@ void i40e_clean_tx_ring(struct i40e_ring *tx_ring) return; /* Free all the Tx ring sk_buffs */ - for (i = 0; i < tx_ring->count; i++) { - tx_buffer = &tx_ring->tx_bi[i]; - i40e_unmap_tx_resource(tx_ring, tx_buffer); - if (tx_buffer->skb) - dev_kfree_skb_any(tx_buffer->skb); - tx_buffer->skb = NULL; - } + for (i = 0; i < tx_ring->count; i++) + i40e_unmap_and_free_tx_resource(tx_ring, &tx_ring->tx_bi[i]); bi_size = sizeof(struct i40e_tx_buffer) * tx_ring->count; memset(tx_ring->tx_bi, 0, bi_size); @@ -242,6 +229,13 @@ void i40e_clean_tx_ring(struct i40e_ring *tx_ring) tx_ring->next_to_use = 0; tx_ring->next_to_clean = 0; + + if (!tx_ring->netdev) + return; + + /* cleanup Tx queue statistics */ + netdev_tx_reset_queue(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index)); } /** @@ -300,14 +294,14 @@ static bool i40e_check_tx_hang(struct i40e_ring *tx_ring) * run the check_tx_hang logic with a transmit completion * pending but without time to complete it yet. */ - if ((tx_ring->tx_stats.tx_done_old == tx_ring->tx_stats.packets) && + if ((tx_ring->tx_stats.tx_done_old == tx_ring->stats.packets) && tx_pending) { /* make sure it is true for two checks in a row */ ret = test_and_set_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); } else { /* update completed stats and disarm the hang check */ - tx_ring->tx_stats.tx_done_old = tx_ring->tx_stats.packets; + tx_ring->tx_stats.tx_done_old = tx_ring->stats.packets; clear_bit(__I40E_HANG_CHECK_ARMED, &tx_ring->state); } @@ -331,62 +325,88 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) tx_buf = &tx_ring->tx_bi[i]; tx_desc = I40E_TX_DESC(tx_ring, i); + i -= tx_ring->count; - for (; budget; budget--) { - struct i40e_tx_desc *eop_desc; - - eop_desc = tx_buf->next_to_watch; + do { + struct i40e_tx_desc *eop_desc = tx_buf->next_to_watch; /* if next_to_watch is not set then there is no work pending */ if (!eop_desc) break; + /* prevent any other reads prior to eop_desc */ + read_barrier_depends(); + /* if the descriptor isn't done, no work yet to do */ if (!(eop_desc->cmd_type_offset_bsz & cpu_to_le64(I40E_TX_DESC_DTYPE_DESC_DONE))) break; - /* count the packet as being completed */ - tx_ring->tx_stats.completed++; + /* clear next_to_watch to prevent false hangs */ tx_buf->next_to_watch = NULL; - tx_buf->time_stamp = 0; - - /* set memory barrier before eop_desc is verified */ - rmb(); - do { - i40e_unmap_tx_resource(tx_ring, tx_buf); + /* update the statistics for this packet */ + total_bytes += tx_buf->bytecount; + total_packets += tx_buf->gso_segs; - /* clear dtype status */ - tx_desc->cmd_type_offset_bsz &= - ~cpu_to_le64(I40E_TXD_QW1_DTYPE_MASK); + /* free the skb */ + dev_kfree_skb_any(tx_buf->skb); - if (likely(tx_desc == eop_desc)) { - eop_desc = NULL; + /* unmap skb header data */ + dma_unmap_single(tx_ring->dev, + dma_unmap_addr(tx_buf, dma), + dma_unmap_len(tx_buf, len), + DMA_TO_DEVICE); - dev_kfree_skb_any(tx_buf->skb); - tx_buf->skb = NULL; + /* clear tx_buffer data */ + tx_buf->skb = NULL; + dma_unmap_len_set(tx_buf, len, 0); - total_bytes += tx_buf->bytecount; - total_packets += tx_buf->gso_segs; - } + /* unmap remaining buffers */ + while (tx_desc != eop_desc) { tx_buf++; tx_desc++; i++; - if (unlikely(i == tx_ring->count)) { - i = 0; + if (unlikely(!i)) { + i -= tx_ring->count; tx_buf = tx_ring->tx_bi; tx_desc = I40E_TX_DESC(tx_ring, 0); } - } while (eop_desc); - } + /* unmap any remaining paged data */ + if (dma_unmap_len(tx_buf, len)) { + dma_unmap_page(tx_ring->dev, + dma_unmap_addr(tx_buf, dma), + dma_unmap_len(tx_buf, len), + DMA_TO_DEVICE); + dma_unmap_len_set(tx_buf, len, 0); + } + } + + /* move us one more past the eop_desc for start of next pkt */ + tx_buf++; + tx_desc++; + i++; + if (unlikely(!i)) { + i -= tx_ring->count; + tx_buf = tx_ring->tx_bi; + tx_desc = I40E_TX_DESC(tx_ring, 0); + } + + /* update budget accounting */ + budget--; + } while (likely(budget)); + + i += tx_ring->count; tx_ring->next_to_clean = i; - tx_ring->tx_stats.bytes += total_bytes; - tx_ring->tx_stats.packets += total_packets; + u64_stats_update_begin(&tx_ring->syncp); + tx_ring->stats.bytes += total_bytes; + tx_ring->stats.packets += total_packets; + u64_stats_update_end(&tx_ring->syncp); tx_ring->q_vector->tx.total_bytes += total_bytes; tx_ring->q_vector->tx.total_packets += total_packets; + if (check_for_tx_hang(tx_ring) && i40e_check_tx_hang(tx_ring)) { /* schedule immediate reset if we believe we hung */ dev_info(tx_ring->dev, "Detected Tx Unit Hang\n" @@ -414,6 +434,10 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) return true; } + netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index), + total_packets, total_bytes); + #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) && (I40E_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) { @@ -524,8 +548,6 @@ static void i40e_update_dynamic_itr(struct i40e_q_vector *q_vector) i40e_set_new_dynamic_itr(&q_vector->tx); if (old_itr != q_vector->tx.itr) wr32(hw, reg_addr, q_vector->tx.itr); - - i40e_flush(hw); } /** @@ -1042,8 +1064,10 @@ next_desc: } rx_ring->next_to_clean = i; - rx_ring->rx_stats.packets += total_rx_packets; - rx_ring->rx_stats.bytes += total_rx_bytes; + u64_stats_update_begin(&rx_ring->syncp); + rx_ring->stats.packets += total_rx_packets; + rx_ring->stats.bytes += total_rx_bytes; + u64_stats_update_end(&rx_ring->syncp); rx_ring->q_vector->rx.total_packets += total_rx_packets; rx_ring->q_vector->rx.total_bytes += total_rx_bytes; @@ -1067,27 +1091,28 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) struct i40e_q_vector *q_vector = container_of(napi, struct i40e_q_vector, napi); struct i40e_vsi *vsi = q_vector->vsi; + struct i40e_ring *ring; bool clean_complete = true; int budget_per_ring; - int i; if (test_bit(__I40E_DOWN, &vsi->state)) { napi_complete(napi); return 0; } + /* Since the actual Tx work is minimal, we can give the Tx a larger + * budget and be more aggressive about cleaning up the Tx descriptors. + */ + i40e_for_each_ring(ring, q_vector->tx) + clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit); + /* We attempt to distribute budget to each Rx queue fairly, but don't * allow the budget to go below 1 because that would exit polling early. - * Since the actual Tx work is minimal, we can give the Tx a larger - * budget and be more aggressive about cleaning up the Tx descriptors. */ budget_per_ring = max(budget/q_vector->num_ringpairs, 1); - for (i = 0; i < q_vector->num_ringpairs; i++) { - clean_complete &= i40e_clean_tx_irq(q_vector->tx.ring[i], - vsi->work_limit); - clean_complete &= i40e_clean_rx_irq(q_vector->rx.ring[i], - budget_per_ring); - } + + i40e_for_each_ring(ring, q_vector->rx) + clean_complete &= i40e_clean_rx_irq(ring, budget_per_ring); /* If work not completed, return budget and polling will return */ if (!clean_complete) @@ -1117,7 +1142,8 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) qval = rd32(hw, I40E_QINT_TQCTL(0)); qval |= I40E_QINT_TQCTL_CAUSE_ENA_MASK; wr32(hw, I40E_QINT_TQCTL(0), qval); - i40e_flush(hw); + + i40e_irq_dynamic_enable_icr0(vsi->back); } } @@ -1144,6 +1170,7 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, struct tcphdr *th; unsigned int hlen; u32 flex_ptype, dtype_cmd; + u16 i; /* make sure ATR is enabled */ if (!(pf->flags & I40E_FLAG_FDIR_ATR_ENABLED)) @@ -1183,10 +1210,11 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, tx_ring->atr_count = 0; /* grab the next descriptor */ - fdir_desc = I40E_TX_FDIRDESC(tx_ring, tx_ring->next_to_use); - tx_ring->next_to_use++; - if (tx_ring->next_to_use == tx_ring->count) - tx_ring->next_to_use = 0; + i = tx_ring->next_to_use; + fdir_desc = I40E_TX_FDIRDESC(tx_ring, i); + + i++; + tx_ring->next_to_use = (i < tx_ring->count) ? i : 0; flex_ptype = (tx_ring->queue_index << I40E_TXD_FLTR_QW0_QINDEX_SHIFT) & I40E_TXD_FLTR_QW0_QINDEX_MASK; @@ -1216,7 +1244,6 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, fdir_desc->dtype_cmd_cntindex = cpu_to_le32(dtype_cmd); } -#define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS) /** * i40e_tx_prepare_vlan_flags - prepare generic TX VLAN tagging flags for HW * @skb: send buffer @@ -1275,27 +1302,6 @@ static int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, return 0; } -/** - * i40e_tx_csum - is checksum offload requested - * @tx_ring: ptr to the ring to send - * @skb: ptr to the skb we're sending - * @tx_flags: the collected send information - * @protocol: the send protocol - * - * Returns true if checksum offload is requested - **/ -static bool i40e_tx_csum(struct i40e_ring *tx_ring, struct sk_buff *skb, - u32 tx_flags, __be16 protocol) -{ - if ((skb->ip_summed != CHECKSUM_PARTIAL) && - !(tx_flags & I40E_TX_FLAGS_TXSW)) { - if (!(tx_flags & I40E_TX_FLAGS_HW_VLAN)) - return false; - } - - return skb->ip_summed == CHECKSUM_PARTIAL; -} - /** * i40e_tso - set up the tso context descriptor * @tx_ring: ptr to the ring to send @@ -1482,15 +1488,16 @@ static void i40e_create_tx_ctx(struct i40e_ring *tx_ring, const u32 cd_tunneling, const u32 cd_l2tag2) { struct i40e_tx_context_desc *context_desc; + int i = tx_ring->next_to_use; if (!cd_type_cmd_tso_mss && !cd_tunneling && !cd_l2tag2) return; /* grab the next descriptor */ - context_desc = I40E_TX_CTXTDESC(tx_ring, tx_ring->next_to_use); - tx_ring->next_to_use++; - if (tx_ring->next_to_use == tx_ring->count) - tx_ring->next_to_use = 0; + context_desc = I40E_TX_CTXTDESC(tx_ring, i); + + i++; + tx_ring->next_to_use = (i < tx_ring->count) ? i : 0; /* cpu_to_le32 and assign to struct fields */ context_desc->tunneling_params = cpu_to_le32(cd_tunneling); @@ -1512,68 +1519,71 @@ static void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, struct i40e_tx_buffer *first, u32 tx_flags, const u8 hdr_len, u32 td_cmd, u32 td_offset) { - struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[0]; unsigned int data_len = skb->data_len; unsigned int size = skb_headlen(skb); - struct device *dev = tx_ring->dev; - u32 paylen = skb->len - hdr_len; - u16 i = tx_ring->next_to_use; + struct skb_frag_struct *frag; struct i40e_tx_buffer *tx_bi; struct i40e_tx_desc *tx_desc; - u32 buf_offset = 0; + u16 i = tx_ring->next_to_use; u32 td_tag = 0; dma_addr_t dma; u16 gso_segs; - dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE); - if (dma_mapping_error(dev, dma)) - goto dma_error; - if (tx_flags & I40E_TX_FLAGS_HW_VLAN) { td_cmd |= I40E_TX_DESC_CMD_IL2TAG1; td_tag = (tx_flags & I40E_TX_FLAGS_VLAN_MASK) >> I40E_TX_FLAGS_VLAN_SHIFT; } + if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) + gso_segs = skb_shinfo(skb)->gso_segs; + else + gso_segs = 1; + + /* multiply data chunks by size of headers */ + first->bytecount = skb->len - hdr_len + (gso_segs * hdr_len); + first->gso_segs = gso_segs; + first->skb = skb; + first->tx_flags = tx_flags; + + dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE); + tx_desc = I40E_TX_DESC(tx_ring, i); - for (;;) { - while (size > I40E_MAX_DATA_PER_TXD) { - tx_desc->buffer_addr = cpu_to_le64(dma + buf_offset); + tx_bi = first; + + for (frag = &skb_shinfo(skb)->frags[0];; frag++) { + if (dma_mapping_error(tx_ring->dev, dma)) + goto dma_error; + + /* record length, and DMA address */ + dma_unmap_len_set(tx_bi, len, size); + dma_unmap_addr_set(tx_bi, dma, dma); + + tx_desc->buffer_addr = cpu_to_le64(dma); + + while (unlikely(size > I40E_MAX_DATA_PER_TXD)) { tx_desc->cmd_type_offset_bsz = build_ctob(td_cmd, td_offset, I40E_MAX_DATA_PER_TXD, td_tag); - buf_offset += I40E_MAX_DATA_PER_TXD; - size -= I40E_MAX_DATA_PER_TXD; - tx_desc++; i++; if (i == tx_ring->count) { tx_desc = I40E_TX_DESC(tx_ring, 0); i = 0; } - } - tx_bi = &tx_ring->tx_bi[i]; - tx_bi->length = buf_offset + size; - tx_bi->tx_flags = tx_flags; - tx_bi->dma = dma; + dma += I40E_MAX_DATA_PER_TXD; + size -= I40E_MAX_DATA_PER_TXD; - tx_desc->buffer_addr = cpu_to_le64(dma + buf_offset); - tx_desc->cmd_type_offset_bsz = build_ctob(td_cmd, td_offset, - size, td_tag); + tx_desc->buffer_addr = cpu_to_le64(dma); + } if (likely(!data_len)) break; - size = skb_frag_size(frag); - data_len -= size; - buf_offset = 0; - tx_flags |= I40E_TX_FLAGS_MAPPED_AS_PAGE; - - dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE); - if (dma_mapping_error(dev, dma)) - goto dma_error; + tx_desc->cmd_type_offset_bsz = build_ctob(td_cmd, td_offset, + size, td_tag); tx_desc++; i++; @@ -1582,31 +1592,25 @@ static void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, i = 0; } - frag++; - } - - tx_desc->cmd_type_offset_bsz |= - cpu_to_le64((u64)I40E_TXD_CMD << I40E_TXD_QW1_CMD_SHIFT); + size = skb_frag_size(frag); + data_len -= size; - i++; - if (i == tx_ring->count) - i = 0; + dma = skb_frag_dma_map(tx_ring->dev, frag, 0, size, + DMA_TO_DEVICE); - tx_ring->next_to_use = i; + tx_bi = &tx_ring->tx_bi[i]; + } - if (tx_flags & (I40E_TX_FLAGS_TSO | I40E_TX_FLAGS_FSO)) - gso_segs = skb_shinfo(skb)->gso_segs; - else - gso_segs = 1; + tx_desc->cmd_type_offset_bsz = + build_ctob(td_cmd, td_offset, size, td_tag) | + cpu_to_le64((u64)I40E_TXD_CMD << I40E_TXD_QW1_CMD_SHIFT); - /* multiply data chunks by size of headers */ - tx_bi->bytecount = paylen + (gso_segs * hdr_len); - tx_bi->gso_segs = gso_segs; - tx_bi->skb = skb; + netdev_tx_sent_queue(netdev_get_tx_queue(tx_ring->netdev, + tx_ring->queue_index), + first->bytecount); - /* set the timestamp and next to watch values */ + /* set the timestamp */ first->time_stamp = jiffies; - first->next_to_watch = tx_desc; /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only @@ -1615,16 +1619,27 @@ static void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, */ wmb(); + /* set next_to_watch value indicating a packet is present */ + first->next_to_watch = tx_desc; + + i++; + if (i == tx_ring->count) + i = 0; + + tx_ring->next_to_use = i; + + /* notify HW of packet */ writel(i, tx_ring->tail); + return; dma_error: - dev_info(dev, "TX DMA map failed\n"); + dev_info(tx_ring->dev, "TX DMA map failed\n"); /* clear dma mappings for failed tx_bi map */ for (;;) { tx_bi = &tx_ring->tx_bi[i]; - i40e_unmap_tx_resource(tx_ring, tx_bi); + i40e_unmap_and_free_tx_resource(tx_ring, tx_bi); if (tx_bi == first) break; if (i == 0) @@ -1632,8 +1647,6 @@ dma_error: i--; } - dev_kfree_skb_any(skb); - tx_ring->next_to_use = i; } @@ -1758,16 +1771,16 @@ static netdev_tx_t i40e_xmit_frame_ring(struct sk_buff *skb, skb_tx_timestamp(skb); + /* always enable CRC insertion offload */ + td_cmd |= I40E_TX_DESC_CMD_ICRC; + /* Always offload the checksum, since it's in the data descriptor */ - if (i40e_tx_csum(tx_ring, skb, tx_flags, protocol)) + if (skb->ip_summed == CHECKSUM_PARTIAL) { tx_flags |= I40E_TX_FLAGS_CSUM; - /* always enable offload insertion */ - td_cmd |= I40E_TX_DESC_CMD_ICRC; - - if (tx_flags & I40E_TX_FLAGS_CSUM) i40e_tx_enable_csum(skb, tx_flags, &td_cmd, &td_offset, tx_ring, &cd_tunneling); + } i40e_create_tx_ctx(tx_ring, cd_type_cmd_tso_mss, cd_tunneling, cd_l2tag2); @@ -1801,7 +1814,7 @@ netdev_tx_t i40e_lan_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_vsi *vsi = np->vsi; - struct i40e_ring *tx_ring = &vsi->tx_rings[skb->queue_mapping]; + struct i40e_ring *tx_ring = vsi->tx_rings[skb->queue_mapping]; /* hardware can't handle really short frames, hardware padding works * beyond this point diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index b1d7722d98a7fc4cd217d43cae627895f5baa5f6..db55d9947f151cf19c17f2f55e8ee24e69364cbd 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -102,23 +102,20 @@ #define I40E_TX_FLAGS_IPV6 (u32)(1 << 5) #define I40E_TX_FLAGS_FCCRC (u32)(1 << 6) #define I40E_TX_FLAGS_FSO (u32)(1 << 7) -#define I40E_TX_FLAGS_TXSW (u32)(1 << 8) -#define I40E_TX_FLAGS_MAPPED_AS_PAGE (u32)(1 << 9) #define I40E_TX_FLAGS_VLAN_MASK 0xffff0000 #define I40E_TX_FLAGS_VLAN_PRIO_MASK 0xe0000000 #define I40E_TX_FLAGS_VLAN_PRIO_SHIFT 29 #define I40E_TX_FLAGS_VLAN_SHIFT 16 struct i40e_tx_buffer { - struct sk_buff *skb; - dma_addr_t dma; - unsigned long time_stamp; - u16 length; - u32 tx_flags; struct i40e_tx_desc *next_to_watch; + unsigned long time_stamp; + struct sk_buff *skb; unsigned int bytecount; - u16 gso_segs; - u8 mapped_as_page; + unsigned short gso_segs; + DEFINE_DMA_UNMAP_ADDR(dma); + DEFINE_DMA_UNMAP_LEN(len); + u32 tx_flags; }; struct i40e_rx_buffer { @@ -129,18 +126,18 @@ struct i40e_rx_buffer { unsigned int page_offset; }; -struct i40e_tx_queue_stats { +struct i40e_queue_stats { u64 packets; u64 bytes; +}; + +struct i40e_tx_queue_stats { u64 restart_queue; u64 tx_busy; - u64 completed; u64 tx_done_old; }; struct i40e_rx_queue_stats { - u64 packets; - u64 bytes; u64 non_eop_descs; u64 alloc_rx_page_failed; u64 alloc_rx_buff_failed; @@ -183,6 +180,7 @@ enum i40e_ring_state_t { /* struct that defines a descriptor ring, associated with a VSI */ struct i40e_ring { + struct i40e_ring *next; /* pointer to next ring in q_vector */ void *desc; /* Descriptor ring memory */ struct device *dev; /* Used for DMA mapping */ struct net_device *netdev; /* netdev ring maps to */ @@ -219,6 +217,8 @@ struct i40e_ring { bool ring_active; /* is ring online or not */ /* stats structs */ + struct i40e_queue_stats stats; + struct u64_stats_sync syncp; union { struct i40e_tx_queue_stats tx_stats; struct i40e_rx_queue_stats rx_stats; @@ -229,6 +229,8 @@ struct i40e_ring { struct i40e_vsi *vsi; /* Backreference to associated VSI */ struct i40e_q_vector *q_vector; /* Backreference to associated vector */ + + struct rcu_head rcu; /* to avoid race on free */ } ____cacheline_internodealigned_in_smp; enum i40e_latency_range { @@ -238,9 +240,8 @@ enum i40e_latency_range { }; struct i40e_ring_container { -#define I40E_MAX_RINGPAIR_PER_VECTOR 8 /* array of pointers to rings */ - struct i40e_ring *ring[I40E_MAX_RINGPAIR_PER_VECTOR]; + struct i40e_ring *ring; unsigned int total_bytes; /* total bytes processed this int */ unsigned int total_packets; /* total packets processed this int */ u16 count; @@ -248,6 +249,10 @@ struct i40e_ring_container { u16 itr; }; +/* iterator for handling rings in ring container */ +#define i40e_for_each_ring(pos, head) \ + for (pos = (head).ring; pos != NULL; pos = pos->next) + void i40e_alloc_rx_buffers(struct i40e_ring *rxr, u16 cleaned_count); netdev_tx_t i40e_lan_xmit_frame(struct sk_buff *skb, struct net_device *netdev); void i40e_clean_tx_ring(struct i40e_ring *tx_ring); diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 8967e58e2408a4930507091bfa85bd26a3862b6f..07596982a4773fc942a5a0c3b8a458bdaa837768 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -251,7 +251,7 @@ static void i40e_config_irq_link_list(struct i40e_vf *vf, u16 vsi_idx, reg_idx = I40E_VPINT_LNKLST0(vf->vf_id); else reg_idx = I40E_VPINT_LNKLSTN( - ((pf->hw.func_caps.num_msix_vectors_vf - 1) + (pf->hw.func_caps.num_msix_vectors_vf * vf->vf_id) + (vector_id - 1)); if (vecmap->rxq_map == 0 && vecmap->txq_map == 0) { @@ -383,7 +383,7 @@ static int i40e_config_vsi_tx_queue(struct i40e_vf *vf, u16 vsi_idx, /* associate this queue with the PCI VF function */ qtx_ctl = I40E_QTX_CTL_VF_QUEUE; - qtx_ctl |= ((hw->hmc.hmc_fn_id << I40E_QTX_CTL_PF_INDX_SHIFT) + qtx_ctl |= ((hw->pf_id << I40E_QTX_CTL_PF_INDX_SHIFT) & I40E_QTX_CTL_PF_INDX_MASK); qtx_ctl |= (((vf->vf_id + hw->func_caps.vf_base_id) << I40E_QTX_CTL_VFVM_INDX_SHIFT) diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.h b/drivers/net/ethernet/intel/igb/e1000_82575.h index 74a1506b42359d02305ea9d78cda7e536633559f..8c2437722aad2b32fec48bfc3f17bdf71db5c164 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.h +++ b/drivers/net/ethernet/intel/igb/e1000_82575.h @@ -28,14 +28,14 @@ #ifndef _E1000_82575_H_ #define _E1000_82575_H_ -extern void igb_shutdown_serdes_link_82575(struct e1000_hw *hw); -extern void igb_power_up_serdes_link_82575(struct e1000_hw *hw); -extern void igb_power_down_phy_copper_82575(struct e1000_hw *hw); -extern void igb_rx_fifo_flush_82575(struct e1000_hw *hw); -extern s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset, - u8 dev_addr, u8 *data); -extern s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset, - u8 dev_addr, u8 data); +void igb_shutdown_serdes_link_82575(struct e1000_hw *hw); +void igb_power_up_serdes_link_82575(struct e1000_hw *hw); +void igb_power_down_phy_copper_82575(struct e1000_hw *hw); +void igb_rx_fifo_flush_82575(struct e1000_hw *hw); +s32 igb_read_i2c_byte(struct e1000_hw *hw, u8 byte_offset, u8 dev_addr, + u8 *data); +s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset, u8 dev_addr, + u8 data); #define ID_LED_DEFAULT_82575_SERDES ((ID_LED_DEF1_DEF2 << 12) | \ (ID_LED_DEF1_DEF2 << 8) | \ diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index 37a9c06a6c6816bf3f87c82986851504d79d7b71..2e166b22d52b6374e3814a9526d6cad9decfa5a3 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -562,11 +562,11 @@ struct e1000_hw { u8 revision_id; }; -extern struct net_device *igb_get_hw_dev(struct e1000_hw *hw); +struct net_device *igb_get_hw_dev(struct e1000_hw *hw); #define hw_dbg(format, arg...) \ netdev_dbg(igb_get_hw_dev(hw), format, ##arg) /* These functions must be implemented by drivers */ -s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); -s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); +s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); +s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value); #endif /* _E1000_HW_H_ */ diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h index dde3c4b7ea9971db46981bc319faeb5fe56e452c..2d913716573a29a830610de841164e30e505cdfb 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.h +++ b/drivers/net/ethernet/intel/igb/e1000_i210.h @@ -28,26 +28,24 @@ #ifndef _E1000_I210_H_ #define _E1000_I210_H_ -extern s32 igb_update_flash_i210(struct e1000_hw *hw); -extern s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw); -extern s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw); -extern s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, - u16 words, u16 *data); -extern s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, - u16 words, u16 *data); -extern s32 igb_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask); -extern void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask); -extern s32 igb_acquire_nvm_i210(struct e1000_hw *hw); -extern void igb_release_nvm_i210(struct e1000_hw *hw); -extern s32 igb_valid_led_default_i210(struct e1000_hw *hw, u16 *data); -extern s32 igb_read_invm_version(struct e1000_hw *hw, - struct e1000_fw_version *invm_ver); -extern s32 igb_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, - u16 *data); -extern s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, - u16 data); -extern s32 igb_init_nvm_params_i210(struct e1000_hw *hw); -extern bool igb_get_flash_presence_i210(struct e1000_hw *hw); +s32 igb_update_flash_i210(struct e1000_hw *hw); +s32 igb_update_nvm_checksum_i210(struct e1000_hw *hw); +s32 igb_validate_nvm_checksum_i210(struct e1000_hw *hw); +s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data); +s32 igb_acquire_swfw_sync_i210(struct e1000_hw *hw, u16 mask); +void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask); +s32 igb_acquire_nvm_i210(struct e1000_hw *hw); +void igb_release_nvm_i210(struct e1000_hw *hw); +s32 igb_valid_led_default_i210(struct e1000_hw *hw, u16 *data); +s32 igb_read_invm_version(struct e1000_hw *hw, + struct e1000_fw_version *invm_ver); +s32 igb_read_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 *data); +s32 igb_write_xmdio_reg(struct e1000_hw *hw, u16 addr, u8 dev_addr, u16 data); +s32 igb_init_nvm_params_i210(struct e1000_hw *hw); +bool igb_get_flash_presence_i210(struct e1000_hw *hw); #define E1000_STM_OPCODE 0xDB00 #define E1000_EEPROM_FLASH_SIZE_WORD 0x11 diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.h b/drivers/net/ethernet/intel/igb/e1000_mac.h index 5e13e83cc608358273d3e7dadb6bf488e426127b..e4cbe8ef67b3094b32b73cbccda9be5673243d1f 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.h +++ b/drivers/net/ethernet/intel/igb/e1000_mac.h @@ -86,6 +86,6 @@ enum e1000_mng_mode { #define E1000_MNG_DHCP_COOKIE_STATUS_VLAN 0x2 -extern void e1000_init_function_pointers_82575(struct e1000_hw *hw); +void e1000_init_function_pointers_82575(struct e1000_hw *hw); #endif diff --git a/drivers/net/ethernet/intel/igb/e1000_phy.c b/drivers/net/ethernet/intel/igb/e1000_phy.c index e7266759a10bb116c1da8d8b7c4c9e5aa399c65c..c4c4fe332c7ee06af09696cc8e5ba027d6d9ba15 100644 --- a/drivers/net/ethernet/intel/igb/e1000_phy.c +++ b/drivers/net/ethernet/intel/igb/e1000_phy.c @@ -708,11 +708,6 @@ s32 igb_copper_link_setup_m88(struct e1000_hw *hw) hw_dbg("Error committing the PHY changes\n"); goto out; } - if (phy->type == e1000_phy_i210) { - ret_val = igb_set_master_slave_mode(hw); - if (ret_val) - return ret_val; - } out: return ret_val; @@ -806,6 +801,9 @@ s32 igb_copper_link_setup_m88_gen2(struct e1000_hw *hw) hw_dbg("Error committing the PHY changes\n"); return ret_val; } + ret_val = igb_set_master_slave_mode(hw); + if (ret_val) + return ret_val; return 0; } diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 6807b098edaee27c0343f3903a080c08604ea3d5..5e9ed89403aa45adb9238330e4937e8aa1f4cd09 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -483,40 +483,38 @@ enum igb_boards { extern char igb_driver_name[]; extern char igb_driver_version[]; -extern int igb_up(struct igb_adapter *); -extern void igb_down(struct igb_adapter *); -extern void igb_reinit_locked(struct igb_adapter *); -extern void igb_reset(struct igb_adapter *); -extern void igb_write_rss_indir_tbl(struct igb_adapter *); -extern int igb_set_spd_dplx(struct igb_adapter *, u32, u8); -extern int igb_setup_tx_resources(struct igb_ring *); -extern int igb_setup_rx_resources(struct igb_ring *); -extern void igb_free_tx_resources(struct igb_ring *); -extern void igb_free_rx_resources(struct igb_ring *); -extern void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *); -extern void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *); -extern void igb_setup_tctl(struct igb_adapter *); -extern void igb_setup_rctl(struct igb_adapter *); -extern netdev_tx_t igb_xmit_frame_ring(struct sk_buff *, struct igb_ring *); -extern void igb_unmap_and_free_tx_resource(struct igb_ring *, - struct igb_tx_buffer *); -extern void igb_alloc_rx_buffers(struct igb_ring *, u16); -extern void igb_update_stats(struct igb_adapter *, struct rtnl_link_stats64 *); -extern bool igb_has_link(struct igb_adapter *adapter); -extern void igb_set_ethtool_ops(struct net_device *); -extern void igb_power_up_link(struct igb_adapter *); -extern void igb_set_fw_version(struct igb_adapter *); -extern void igb_ptp_init(struct igb_adapter *adapter); -extern void igb_ptp_stop(struct igb_adapter *adapter); -extern void igb_ptp_reset(struct igb_adapter *adapter); -extern void igb_ptp_tx_work(struct work_struct *work); -extern void igb_ptp_rx_hang(struct igb_adapter *adapter); -extern void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter); -extern void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, - struct sk_buff *skb); -extern void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, - unsigned char *va, - struct sk_buff *skb); +int igb_up(struct igb_adapter *); +void igb_down(struct igb_adapter *); +void igb_reinit_locked(struct igb_adapter *); +void igb_reset(struct igb_adapter *); +int igb_reinit_queues(struct igb_adapter *); +void igb_write_rss_indir_tbl(struct igb_adapter *); +int igb_set_spd_dplx(struct igb_adapter *, u32, u8); +int igb_setup_tx_resources(struct igb_ring *); +int igb_setup_rx_resources(struct igb_ring *); +void igb_free_tx_resources(struct igb_ring *); +void igb_free_rx_resources(struct igb_ring *); +void igb_configure_tx_ring(struct igb_adapter *, struct igb_ring *); +void igb_configure_rx_ring(struct igb_adapter *, struct igb_ring *); +void igb_setup_tctl(struct igb_adapter *); +void igb_setup_rctl(struct igb_adapter *); +netdev_tx_t igb_xmit_frame_ring(struct sk_buff *, struct igb_ring *); +void igb_unmap_and_free_tx_resource(struct igb_ring *, struct igb_tx_buffer *); +void igb_alloc_rx_buffers(struct igb_ring *, u16); +void igb_update_stats(struct igb_adapter *, struct rtnl_link_stats64 *); +bool igb_has_link(struct igb_adapter *adapter); +void igb_set_ethtool_ops(struct net_device *); +void igb_power_up_link(struct igb_adapter *); +void igb_set_fw_version(struct igb_adapter *); +void igb_ptp_init(struct igb_adapter *adapter); +void igb_ptp_stop(struct igb_adapter *adapter); +void igb_ptp_reset(struct igb_adapter *adapter); +void igb_ptp_tx_work(struct work_struct *work); +void igb_ptp_rx_hang(struct igb_adapter *adapter); +void igb_ptp_tx_hwtstamp(struct igb_adapter *adapter); +void igb_ptp_rx_rgtstamp(struct igb_q_vector *q_vector, struct sk_buff *skb); +void igb_ptp_rx_pktstamp(struct igb_q_vector *q_vector, unsigned char *va, + struct sk_buff *skb); static inline void igb_ptp_rx_hwtstamp(struct igb_ring *rx_ring, union e1000_adv_rx_desc *rx_desc, struct sk_buff *skb) @@ -531,11 +529,11 @@ static inline void igb_ptp_rx_hwtstamp(struct igb_ring *rx_ring, rx_ring->last_rx_timestamp = jiffies; } -extern int igb_ptp_hwtstamp_ioctl(struct net_device *netdev, - struct ifreq *ifr, int cmd); +int igb_ptp_hwtstamp_ioctl(struct net_device *netdev, struct ifreq *ifr, + int cmd); #ifdef CONFIG_IGB_HWMON -extern void igb_sysfs_exit(struct igb_adapter *adapter); -extern int igb_sysfs_init(struct igb_adapter *adapter); +void igb_sysfs_exit(struct igb_adapter *adapter); +int igb_sysfs_init(struct igb_adapter *adapter); #endif static inline s32 igb_reset_phy(struct e1000_hw *hw) { diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 151e00cad113f5d7c42249e091a844d56955ba49..b0f3666b1d7f1c669181a9f62534aa8096baeccc 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -146,6 +146,7 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) struct e1000_sfp_flags *eth_flags = &dev_spec->eth_flags; u32 status; + status = rd32(E1000_STATUS); if (hw->phy.media_type == e1000_media_type_copper) { ecmd->supported = (SUPPORTED_10baseT_Half | @@ -169,13 +170,22 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->transceiver = XCVR_INTERNAL; } else { ecmd->supported = (SUPPORTED_FIBRE | + SUPPORTED_1000baseKX_Full | SUPPORTED_Autoneg | SUPPORTED_Pause); - ecmd->advertising = ADVERTISED_FIBRE; - - if ((eth_flags->e1000_base_lx) || (eth_flags->e1000_base_sx)) { - ecmd->supported |= SUPPORTED_1000baseT_Full; - ecmd->advertising |= ADVERTISED_1000baseT_Full; + ecmd->advertising = (ADVERTISED_FIBRE | + ADVERTISED_1000baseKX_Full); + if (hw->mac.type == e1000_i354) { + if ((hw->device_id == + E1000_DEV_ID_I354_BACKPLANE_2_5GBPS) && + !(status & E1000_STATUS_2P5_SKU_OVER)) { + ecmd->supported |= SUPPORTED_2500baseX_Full; + ecmd->supported &= + ~SUPPORTED_1000baseKX_Full; + ecmd->advertising |= ADVERTISED_2500baseX_Full; + ecmd->advertising &= + ~ADVERTISED_1000baseKX_Full; + } } if (eth_flags->e100_base_fx) { ecmd->supported |= SUPPORTED_100baseT_Full; @@ -187,35 +197,29 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->port = PORT_FIBRE; ecmd->transceiver = XCVR_EXTERNAL; } - if (hw->mac.autoneg != 1) ecmd->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); - if (hw->fc.requested_mode == e1000_fc_full) + switch (hw->fc.requested_mode) { + case e1000_fc_full: ecmd->advertising |= ADVERTISED_Pause; - else if (hw->fc.requested_mode == e1000_fc_rx_pause) + break; + case e1000_fc_rx_pause: ecmd->advertising |= (ADVERTISED_Pause | ADVERTISED_Asym_Pause); - else if (hw->fc.requested_mode == e1000_fc_tx_pause) + break; + case e1000_fc_tx_pause: ecmd->advertising |= ADVERTISED_Asym_Pause; - else + break; + default: ecmd->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause); - - status = rd32(E1000_STATUS); - + } if (status & E1000_STATUS_LU) { - if (hw->mac.type == e1000_i354) { - if ((status & E1000_STATUS_2P5_SKU) && - !(status & E1000_STATUS_2P5_SKU_OVER)) { - ecmd->supported = SUPPORTED_2500baseX_Full; - ecmd->advertising = ADVERTISED_2500baseX_Full; - ecmd->speed = SPEED_2500; - } else { - ecmd->supported = SUPPORTED_1000baseT_Full; - ecmd->advertising = ADVERTISED_1000baseT_Full; - } + if ((status & E1000_STATUS_2P5_SKU) && + !(status & E1000_STATUS_2P5_SKU_OVER)) { + ecmd->speed = SPEED_2500; } else if (status & E1000_STATUS_SPEED_1000) { ecmd->speed = SPEED_1000; } else if (status & E1000_STATUS_SPEED_100) { @@ -232,7 +236,6 @@ static int igb_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->speed = -1; ecmd->duplex = -1; } - if ((hw->phy.media_type == e1000_media_type_fiber) || hw->mac.autoneg) ecmd->autoneg = AUTONEG_ENABLE; @@ -771,8 +774,10 @@ static int igb_set_eeprom(struct net_device *netdev, if (eeprom->len == 0) return -EOPNOTSUPP; - if (hw->mac.type == e1000_i211) + if ((hw->mac.type >= e1000_i210) && + !igb_get_flash_presence_i210(hw)) { return -EOPNOTSUPP; + } if (eeprom->magic != (hw->vendor_id | (hw->device_id << 16))) return -EFAULT; @@ -1659,7 +1664,8 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter) if ((hw->device_id == E1000_DEV_ID_DH89XXCC_SGMII) || (hw->device_id == E1000_DEV_ID_DH89XXCC_SERDES) || (hw->device_id == E1000_DEV_ID_DH89XXCC_BACKPLANE) || - (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP)) { + (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP) || + (hw->device_id == E1000_DEV_ID_I354_SGMII)) { /* Enable DH89xxCC MPHY for near end loopback */ reg = rd32(E1000_MPHY_ADDR_CTL); @@ -1725,7 +1731,8 @@ static void igb_loopback_cleanup(struct igb_adapter *adapter) if ((hw->device_id == E1000_DEV_ID_DH89XXCC_SGMII) || (hw->device_id == E1000_DEV_ID_DH89XXCC_SERDES) || (hw->device_id == E1000_DEV_ID_DH89XXCC_BACKPLANE) || - (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP)) { + (hw->device_id == E1000_DEV_ID_DH89XXCC_SFP) || + (hw->device_id == E1000_DEV_ID_I354_SGMII)) { u32 reg; /* Disable near end loopback on DH89xxCC */ @@ -2877,6 +2884,88 @@ static int igb_set_rxfh_indir(struct net_device *netdev, const u32 *indir) return 0; } +static unsigned int igb_max_channels(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + unsigned int max_combined = 0; + + switch (hw->mac.type) { + case e1000_i211: + max_combined = IGB_MAX_RX_QUEUES_I211; + break; + case e1000_82575: + case e1000_i210: + max_combined = IGB_MAX_RX_QUEUES_82575; + break; + case e1000_i350: + if (!!adapter->vfs_allocated_count) { + max_combined = 1; + break; + } + /* fall through */ + case e1000_82576: + if (!!adapter->vfs_allocated_count) { + max_combined = 2; + break; + } + /* fall through */ + case e1000_82580: + case e1000_i354: + default: + max_combined = IGB_MAX_RX_QUEUES; + break; + } + + return max_combined; +} + +static void igb_get_channels(struct net_device *netdev, + struct ethtool_channels *ch) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + + /* Report maximum channels */ + ch->max_combined = igb_max_channels(adapter); + + /* Report info for other vector */ + if (adapter->msix_entries) { + ch->max_other = NON_Q_VECTORS; + ch->other_count = NON_Q_VECTORS; + } + + ch->combined_count = adapter->rss_queues; +} + +static int igb_set_channels(struct net_device *netdev, + struct ethtool_channels *ch) +{ + struct igb_adapter *adapter = netdev_priv(netdev); + unsigned int count = ch->combined_count; + + /* Verify they are not requesting separate vectors */ + if (!count || ch->rx_count || ch->tx_count) + return -EINVAL; + + /* Verify other_count is valid and has not been changed */ + if (ch->other_count != NON_Q_VECTORS) + return -EINVAL; + + /* Verify the number of channels doesn't exceed hw limits */ + if (count > igb_max_channels(adapter)) + return -EINVAL; + + if (count != adapter->rss_queues) { + adapter->rss_queues = count; + + /* Hardware has to reinitialize queues and interrupts to + * match the new configuration. + */ + return igb_reinit_queues(adapter); + } + + return 0; +} + static const struct ethtool_ops igb_ethtool_ops = { .get_settings = igb_get_settings, .set_settings = igb_set_settings, @@ -2913,6 +3002,8 @@ static const struct ethtool_ops igb_ethtool_ops = { .get_rxfh_indir_size = igb_get_rxfh_indir_size, .get_rxfh_indir = igb_get_rxfh_indir, .set_rxfh_indir = igb_set_rxfh_indir, + .get_channels = igb_get_channels, + .set_channels = igb_set_channels, .begin = igb_ethtool_begin, .complete = igb_ethtool_complete, }; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 8cf44f2a8ccd5b531f42fa0dfb357a19d6efa4f6..ebe6370c4b185bf85cf931d8b93e28a14d58b7c6 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -182,6 +182,7 @@ static void igb_check_vf_rate_limit(struct igb_adapter *); #ifdef CONFIG_PCI_IOV static int igb_vf_configure(struct igb_adapter *adapter, int vf); +static int igb_pci_enable_sriov(struct pci_dev *dev, int num_vfs); #endif #ifdef CONFIG_PM @@ -2429,7 +2430,7 @@ err_dma: } #ifdef CONFIG_PCI_IOV -static int igb_disable_sriov(struct pci_dev *pdev) +static int igb_disable_sriov(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct igb_adapter *adapter = netdev_priv(netdev); @@ -2470,27 +2471,19 @@ static int igb_enable_sriov(struct pci_dev *pdev, int num_vfs) int err = 0; int i; - if (!adapter->msix_entries) { + if (!adapter->msix_entries || num_vfs > 7) { err = -EPERM; goto out; } - if (!num_vfs) goto out; - else if (old_vfs && old_vfs == num_vfs) - goto out; - else if (old_vfs && old_vfs != num_vfs) - err = igb_disable_sriov(pdev); - - if (err) - goto out; - - if (num_vfs > 7) { - err = -EPERM; - goto out; - } - adapter->vfs_allocated_count = num_vfs; + if (old_vfs) { + dev_info(&pdev->dev, "%d pre-allocated VFs found - override max_vfs setting of %d\n", + old_vfs, max_vfs); + adapter->vfs_allocated_count = old_vfs; + } else + adapter->vfs_allocated_count = num_vfs; adapter->vf_data = kcalloc(adapter->vfs_allocated_count, sizeof(struct vf_data_storage), GFP_KERNEL); @@ -2504,10 +2497,12 @@ static int igb_enable_sriov(struct pci_dev *pdev, int num_vfs) goto out; } - err = pci_enable_sriov(pdev, adapter->vfs_allocated_count); - if (err) - goto err_out; - + /* only call pci_enable_sriov() if no VFs are allocated already */ + if (!old_vfs) { + err = pci_enable_sriov(pdev, adapter->vfs_allocated_count); + if (err) + goto err_out; + } dev_info(&pdev->dev, "%d VFs allocated\n", adapter->vfs_allocated_count); for (i = 0; i < adapter->vfs_allocated_count; i++) @@ -2623,7 +2618,7 @@ static void igb_probe_vfs(struct igb_adapter *adapter) return; pci_sriov_set_totalvfs(pdev, 7); - igb_enable_sriov(pdev, max_vfs); + igb_pci_enable_sriov(pdev, max_vfs); #endif /* CONFIG_PCI_IOV */ } @@ -5708,7 +5703,7 @@ static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) /* reply to reset with ack and vf mac address */ msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_ACK; - memcpy(addr, vf_mac, 6); + memcpy(addr, vf_mac, ETH_ALEN); igb_write_mbx(hw, msgbuf, 3, vf); } @@ -7838,4 +7833,26 @@ s32 igb_write_i2c_byte(struct e1000_hw *hw, u8 byte_offset, return E1000_SUCCESS; } + +int igb_reinit_queues(struct igb_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + struct pci_dev *pdev = adapter->pdev; + int err = 0; + + if (netif_running(netdev)) + igb_close(netdev); + + igb_clear_interrupt_scheme(adapter); + + if (igb_init_interrupt_scheme(adapter, true)) { + dev_err(&pdev->dev, "Unable to allocate memory for queues\n"); + return -ENOMEM; + } + + if (netif_running(netdev)) + err = igb_open(netdev); + + return err; +} /* igb_main.c */ diff --git a/drivers/net/ethernet/intel/igbvf/igbvf.h b/drivers/net/ethernet/intel/igbvf/igbvf.h index a1463e3d14c0522511c75c424ffa16bd3162411a..7d6a25c8f889efd0eb69ef749dd0bb13f18302ad 100644 --- a/drivers/net/ethernet/intel/igbvf/igbvf.h +++ b/drivers/net/ethernet/intel/igbvf/igbvf.h @@ -312,17 +312,17 @@ enum igbvf_state_t { extern char igbvf_driver_name[]; extern const char igbvf_driver_version[]; -extern void igbvf_check_options(struct igbvf_adapter *); -extern void igbvf_set_ethtool_ops(struct net_device *); - -extern int igbvf_up(struct igbvf_adapter *); -extern void igbvf_down(struct igbvf_adapter *); -extern void igbvf_reinit_locked(struct igbvf_adapter *); -extern int igbvf_setup_rx_resources(struct igbvf_adapter *, struct igbvf_ring *); -extern int igbvf_setup_tx_resources(struct igbvf_adapter *, struct igbvf_ring *); -extern void igbvf_free_rx_resources(struct igbvf_ring *); -extern void igbvf_free_tx_resources(struct igbvf_ring *); -extern void igbvf_update_stats(struct igbvf_adapter *); +void igbvf_check_options(struct igbvf_adapter *); +void igbvf_set_ethtool_ops(struct net_device *); + +int igbvf_up(struct igbvf_adapter *); +void igbvf_down(struct igbvf_adapter *); +void igbvf_reinit_locked(struct igbvf_adapter *); +int igbvf_setup_rx_resources(struct igbvf_adapter *, struct igbvf_ring *); +int igbvf_setup_tx_resources(struct igbvf_adapter *, struct igbvf_ring *); +void igbvf_free_rx_resources(struct igbvf_ring *); +void igbvf_free_tx_resources(struct igbvf_ring *); +void igbvf_update_stats(struct igbvf_adapter *); extern unsigned int copybreak; diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 93eb7ee06d3e0aa2159fcccc61e797eb44fd79bd..9fadbb28cf08c8ae213a248b921ec9af5f0a08b8 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -2343,10 +2343,9 @@ static int igbvf_change_mtu(struct net_device *netdev, int new_mtu) struct igbvf_adapter *adapter = netdev_priv(netdev); int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; - if ((new_mtu < 68) || (max_frame > MAX_JUMBO_FRAME_SIZE)) { - dev_err(&adapter->pdev->dev, "Invalid MTU setting\n"); + if (new_mtu < 68 || new_mtu > INT_MAX - ETH_HLEN - ETH_FCS_LEN || + max_frame > MAX_JUMBO_FRAME_SIZE) return -EINVAL; - } #define MAX_STD_JUMBO_FRAME_SIZE 9234 if (max_frame > MAX_STD_JUMBO_FRAME_SIZE) { @@ -2699,7 +2698,7 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ei->get_variants) { err = ei->get_variants(adapter); if (err) - goto err_ioremap; + goto err_get_variants; } /* setup adapter struct */ @@ -2796,6 +2795,7 @@ err_hw_init: kfree(adapter->rx_ring); err_sw_init: igbvf_reset_interrupt_capability(adapter); +err_get_variants: iounmap(adapter->hw.hw_addr); err_ioremap: free_netdev(netdev); diff --git a/drivers/net/ethernet/intel/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c index eea0e10ce12f95d47ad884d48170495913f8db70..955ad8c2c53456a2bb9ce28515a00d52264ed3ee 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.c +++ b/drivers/net/ethernet/intel/igbvf/vf.c @@ -154,7 +154,7 @@ static s32 e1000_reset_hw_vf(struct e1000_hw *hw) ret_val = mbx->ops.read_posted(hw, msgbuf, 3); if (!ret_val) { if (msgbuf[0] == (E1000_VF_RESET | E1000_VT_MSGTYPE_ACK)) - memcpy(hw->mac.perm_addr, addr, 6); + memcpy(hw->mac.perm_addr, addr, ETH_ALEN); else ret_val = -E1000_ERR_MAC_INIT; } @@ -314,7 +314,7 @@ static void e1000_rar_set_vf(struct e1000_hw *hw, u8 * addr, u32 index) memset(msgbuf, 0, 12); msgbuf[0] = E1000_VF_SET_MAC_ADDR; - memcpy(msg_addr, addr, 6); + memcpy(msg_addr, addr, ETH_ALEN); ret_val = mbx->ops.write_posted(hw, msgbuf, 3); if (!ret_val) diff --git a/drivers/net/ethernet/intel/ixgb/ixgb.h b/drivers/net/ethernet/intel/ixgb/ixgb.h index 4d2ae97ff1b3d7380de5c2ea4653f33fda330880..2224cc2edf1396d338b9786a2dd23af8b77aa581 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb.h +++ b/drivers/net/ethernet/intel/ixgb/ixgb.h @@ -187,21 +187,21 @@ enum ixgb_state_t { }; /* Exported from other modules */ -extern void ixgb_check_options(struct ixgb_adapter *adapter); -extern void ixgb_set_ethtool_ops(struct net_device *netdev); +void ixgb_check_options(struct ixgb_adapter *adapter); +void ixgb_set_ethtool_ops(struct net_device *netdev); extern char ixgb_driver_name[]; extern const char ixgb_driver_version[]; -extern void ixgb_set_speed_duplex(struct net_device *netdev); +void ixgb_set_speed_duplex(struct net_device *netdev); -extern int ixgb_up(struct ixgb_adapter *adapter); -extern void ixgb_down(struct ixgb_adapter *adapter, bool kill_watchdog); -extern void ixgb_reset(struct ixgb_adapter *adapter); -extern int ixgb_setup_rx_resources(struct ixgb_adapter *adapter); -extern int ixgb_setup_tx_resources(struct ixgb_adapter *adapter); -extern void ixgb_free_rx_resources(struct ixgb_adapter *adapter); -extern void ixgb_free_tx_resources(struct ixgb_adapter *adapter); -extern void ixgb_update_stats(struct ixgb_adapter *adapter); +int ixgb_up(struct ixgb_adapter *adapter); +void ixgb_down(struct ixgb_adapter *adapter, bool kill_watchdog); +void ixgb_reset(struct ixgb_adapter *adapter); +int ixgb_setup_rx_resources(struct ixgb_adapter *adapter); +int ixgb_setup_tx_resources(struct ixgb_adapter *adapter); +void ixgb_free_rx_resources(struct ixgb_adapter *adapter); +void ixgb_free_tx_resources(struct ixgb_adapter *adapter); +void ixgb_update_stats(struct ixgb_adapter *adapter); #endif /* _IXGB_H_ */ diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_hw.h b/drivers/net/ethernet/intel/ixgb/ixgb_hw.h index 2a99a35c33aa24c30a61837ae880ea373b908c14..0bd5d72e1af5b6be4873baf31431e7f5ad09a347 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_hw.h +++ b/drivers/net/ethernet/intel/ixgb/ixgb_hw.h @@ -759,27 +759,20 @@ struct ixgb_hw_stats { }; /* Function Prototypes */ -extern bool ixgb_adapter_stop(struct ixgb_hw *hw); -extern bool ixgb_init_hw(struct ixgb_hw *hw); -extern bool ixgb_adapter_start(struct ixgb_hw *hw); -extern void ixgb_check_for_link(struct ixgb_hw *hw); -extern bool ixgb_check_for_bad_link(struct ixgb_hw *hw); - -extern void ixgb_rar_set(struct ixgb_hw *hw, - u8 *addr, - u32 index); +bool ixgb_adapter_stop(struct ixgb_hw *hw); +bool ixgb_init_hw(struct ixgb_hw *hw); +bool ixgb_adapter_start(struct ixgb_hw *hw); +void ixgb_check_for_link(struct ixgb_hw *hw); +bool ixgb_check_for_bad_link(struct ixgb_hw *hw); +void ixgb_rar_set(struct ixgb_hw *hw, u8 *addr, u32 index); /* Filters (multicast, vlan, receive) */ -extern void ixgb_mc_addr_list_update(struct ixgb_hw *hw, - u8 *mc_addr_list, - u32 mc_addr_count, - u32 pad); +void ixgb_mc_addr_list_update(struct ixgb_hw *hw, u8 *mc_addr_list, + u32 mc_addr_count, u32 pad); /* Vfta functions */ -extern void ixgb_write_vfta(struct ixgb_hw *hw, - u32 offset, - u32 value); +void ixgb_write_vfta(struct ixgb_hw *hw, u32 offset, u32 value); /* Access functions to eeprom data */ void ixgb_get_ee_mac_addr(struct ixgb_hw *hw, u8 *mac_addr); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 0ac6b11c6e4ec323aaa50db976e2a996b1b02d19..f38fc0a343a28623a6191657afbc356bbebee3b5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -55,7 +55,7 @@ #include #ifdef CONFIG_NET_RX_BUSY_POLL -#define LL_EXTENDED_STATS +#define BP_EXTENDED_STATS #endif /* common prefix used by pr_<> macros */ #undef pr_fmt @@ -67,7 +67,11 @@ #define IXGBE_MAX_TXD 4096 #define IXGBE_MIN_TXD 64 +#if (PAGE_SIZE < 8192) #define IXGBE_DEFAULT_RXD 512 +#else +#define IXGBE_DEFAULT_RXD 128 +#endif #define IXGBE_MAX_RXD 4096 #define IXGBE_MIN_RXD 64 @@ -187,11 +191,11 @@ struct ixgbe_rx_buffer { struct ixgbe_queue_stats { u64 packets; u64 bytes; -#ifdef LL_EXTENDED_STATS +#ifdef BP_EXTENDED_STATS u64 yields; u64 misses; u64 cleaned; -#endif /* LL_EXTENDED_STATS */ +#endif /* BP_EXTENDED_STATS */ }; struct ixgbe_tx_queue_stats { @@ -219,6 +223,15 @@ enum ixgbe_ring_state_t { __IXGBE_RX_FCOE, }; +struct ixgbe_fwd_adapter { + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + struct net_device *netdev; + struct ixgbe_adapter *real_adapter; + unsigned int tx_base_queue; + unsigned int rx_base_queue; + int pool; +}; + #define check_for_tx_hang(ring) \ test_bit(__IXGBE_TX_DETECT_HANG, &(ring)->state) #define set_check_for_tx_hang(ring) \ @@ -236,6 +249,7 @@ struct ixgbe_ring { struct ixgbe_q_vector *q_vector; /* backpointer to host q_vector */ struct net_device *netdev; /* netdev ring belongs to */ struct device *dev; /* device for DMA mapping */ + struct ixgbe_fwd_adapter *l2_accel_priv; void *desc; /* descriptor ring memory */ union { struct ixgbe_tx_buffer *tx_buffer_info; @@ -293,6 +307,12 @@ enum ixgbe_ring_f_enum { #define IXGBE_MAX_FCOE_INDICES 8 #define MAX_RX_QUEUES (IXGBE_MAX_FDIR_INDICES + 1) #define MAX_TX_QUEUES (IXGBE_MAX_FDIR_INDICES + 1) +#define IXGBE_MAX_L2A_QUEUES 4 +#define IXGBE_MAX_L2A_QUEUES 4 +#define IXGBE_BAD_L2A_QUEUE 3 +#define IXGBE_MAX_MACVLANS 31 +#define IXGBE_MAX_DCBMACVLANS 8 + struct ixgbe_ring_feature { u16 limit; /* upper limit on feature indices */ u16 indices; /* current value of indices */ @@ -369,11 +389,13 @@ struct ixgbe_q_vector { #ifdef CONFIG_NET_RX_BUSY_POLL unsigned int state; #define IXGBE_QV_STATE_IDLE 0 -#define IXGBE_QV_STATE_NAPI 1 /* NAPI owns this QV */ -#define IXGBE_QV_STATE_POLL 2 /* poll owns this QV */ -#define IXGBE_QV_LOCKED (IXGBE_QV_STATE_NAPI | IXGBE_QV_STATE_POLL) -#define IXGBE_QV_STATE_NAPI_YIELD 4 /* NAPI yielded this QV */ -#define IXGBE_QV_STATE_POLL_YIELD 8 /* poll yielded this QV */ +#define IXGBE_QV_STATE_NAPI 1 /* NAPI owns this QV */ +#define IXGBE_QV_STATE_POLL 2 /* poll owns this QV */ +#define IXGBE_QV_STATE_DISABLED 4 /* QV is disabled */ +#define IXGBE_QV_OWNED (IXGBE_QV_STATE_NAPI | IXGBE_QV_STATE_POLL) +#define IXGBE_QV_LOCKED (IXGBE_QV_OWNED | IXGBE_QV_STATE_DISABLED) +#define IXGBE_QV_STATE_NAPI_YIELD 8 /* NAPI yielded this QV */ +#define IXGBE_QV_STATE_POLL_YIELD 16 /* poll yielded this QV */ #define IXGBE_QV_YIELD (IXGBE_QV_STATE_NAPI_YIELD | IXGBE_QV_STATE_POLL_YIELD) #define IXGBE_QV_USER_PEND (IXGBE_QV_STATE_POLL | IXGBE_QV_STATE_POLL_YIELD) spinlock_t lock; @@ -394,18 +416,18 @@ static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector) static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector) { int rc = true; - spin_lock(&q_vector->lock); + spin_lock_bh(&q_vector->lock); if (q_vector->state & IXGBE_QV_LOCKED) { WARN_ON(q_vector->state & IXGBE_QV_STATE_NAPI); q_vector->state |= IXGBE_QV_STATE_NAPI_YIELD; rc = false; -#ifdef LL_EXTENDED_STATS +#ifdef BP_EXTENDED_STATS q_vector->tx.ring->stats.yields++; #endif } else /* we don't care if someone yielded */ q_vector->state = IXGBE_QV_STATE_NAPI; - spin_unlock(&q_vector->lock); + spin_unlock_bh(&q_vector->lock); return rc; } @@ -413,14 +435,15 @@ static inline bool ixgbe_qv_lock_napi(struct ixgbe_q_vector *q_vector) static inline bool ixgbe_qv_unlock_napi(struct ixgbe_q_vector *q_vector) { int rc = false; - spin_lock(&q_vector->lock); + spin_lock_bh(&q_vector->lock); WARN_ON(q_vector->state & (IXGBE_QV_STATE_POLL | IXGBE_QV_STATE_NAPI_YIELD)); if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD) rc = true; - q_vector->state = IXGBE_QV_STATE_IDLE; - spin_unlock(&q_vector->lock); + /* will reset state to idle, unless QV is disabled */ + q_vector->state &= IXGBE_QV_STATE_DISABLED; + spin_unlock_bh(&q_vector->lock); return rc; } @@ -432,7 +455,7 @@ static inline bool ixgbe_qv_lock_poll(struct ixgbe_q_vector *q_vector) if ((q_vector->state & IXGBE_QV_LOCKED)) { q_vector->state |= IXGBE_QV_STATE_POLL_YIELD; rc = false; -#ifdef LL_EXTENDED_STATS +#ifdef BP_EXTENDED_STATS q_vector->rx.ring->stats.yields++; #endif } else @@ -451,17 +474,32 @@ static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector) if (q_vector->state & IXGBE_QV_STATE_POLL_YIELD) rc = true; - q_vector->state = IXGBE_QV_STATE_IDLE; + /* will reset state to idle, unless QV is disabled */ + q_vector->state &= IXGBE_QV_STATE_DISABLED; spin_unlock_bh(&q_vector->lock); return rc; } /* true if a socket is polling, even if it did not get the lock */ -static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector) +static inline bool ixgbe_qv_busy_polling(struct ixgbe_q_vector *q_vector) { - WARN_ON(!(q_vector->state & IXGBE_QV_LOCKED)); + WARN_ON(!(q_vector->state & IXGBE_QV_OWNED)); return q_vector->state & IXGBE_QV_USER_PEND; } + +/* false if QV is currently owned */ +static inline bool ixgbe_qv_disable(struct ixgbe_q_vector *q_vector) +{ + int rc = true; + spin_lock_bh(&q_vector->lock); + if (q_vector->state & IXGBE_QV_OWNED) + rc = false; + q_vector->state |= IXGBE_QV_STATE_DISABLED; + spin_unlock_bh(&q_vector->lock); + + return rc; +} + #else /* CONFIG_NET_RX_BUSY_POLL */ static inline void ixgbe_qv_init_lock(struct ixgbe_q_vector *q_vector) { @@ -487,10 +525,16 @@ static inline bool ixgbe_qv_unlock_poll(struct ixgbe_q_vector *q_vector) return false; } -static inline bool ixgbe_qv_ll_polling(struct ixgbe_q_vector *q_vector) +static inline bool ixgbe_qv_busy_polling(struct ixgbe_q_vector *q_vector) { return false; } + +static inline bool ixgbe_qv_disable(struct ixgbe_q_vector *q_vector) +{ + return true; +} + #endif /* CONFIG_NET_RX_BUSY_POLL */ #ifdef CONFIG_IXGBE_HWMON @@ -738,6 +782,7 @@ struct ixgbe_adapter { #endif /*CONFIG_DEBUG_FS*/ u8 default_up; + unsigned long fwd_bitmask; /* Bitmask indicating in use pools */ }; struct ixgbe_fdir_filter { @@ -786,93 +831,89 @@ extern const char ixgbe_driver_version[]; extern char ixgbe_default_device_descr[]; #endif /* IXGBE_FCOE */ -extern void ixgbe_up(struct ixgbe_adapter *adapter); -extern void ixgbe_down(struct ixgbe_adapter *adapter); -extern void ixgbe_reinit_locked(struct ixgbe_adapter *adapter); -extern void ixgbe_reset(struct ixgbe_adapter *adapter); -extern void ixgbe_set_ethtool_ops(struct net_device *netdev); -extern int ixgbe_setup_rx_resources(struct ixgbe_ring *); -extern int ixgbe_setup_tx_resources(struct ixgbe_ring *); -extern void ixgbe_free_rx_resources(struct ixgbe_ring *); -extern void ixgbe_free_tx_resources(struct ixgbe_ring *); -extern void ixgbe_configure_rx_ring(struct ixgbe_adapter *,struct ixgbe_ring *); -extern void ixgbe_configure_tx_ring(struct ixgbe_adapter *,struct ixgbe_ring *); -extern void ixgbe_disable_rx_queue(struct ixgbe_adapter *adapter, - struct ixgbe_ring *); -extern void ixgbe_update_stats(struct ixgbe_adapter *adapter); -extern int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter); -extern int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, +void ixgbe_up(struct ixgbe_adapter *adapter); +void ixgbe_down(struct ixgbe_adapter *adapter); +void ixgbe_reinit_locked(struct ixgbe_adapter *adapter); +void ixgbe_reset(struct ixgbe_adapter *adapter); +void ixgbe_set_ethtool_ops(struct net_device *netdev); +int ixgbe_setup_rx_resources(struct ixgbe_ring *); +int ixgbe_setup_tx_resources(struct ixgbe_ring *); +void ixgbe_free_rx_resources(struct ixgbe_ring *); +void ixgbe_free_tx_resources(struct ixgbe_ring *); +void ixgbe_configure_rx_ring(struct ixgbe_adapter *, struct ixgbe_ring *); +void ixgbe_configure_tx_ring(struct ixgbe_adapter *, struct ixgbe_ring *); +void ixgbe_disable_rx_queue(struct ixgbe_adapter *adapter, struct ixgbe_ring *); +void ixgbe_update_stats(struct ixgbe_adapter *adapter); +int ixgbe_init_interrupt_scheme(struct ixgbe_adapter *adapter); +int ixgbe_wol_supported(struct ixgbe_adapter *adapter, u16 device_id, u16 subdevice_id); -extern void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter); -extern netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, - struct ixgbe_adapter *, - struct ixgbe_ring *); -extern void ixgbe_unmap_and_free_tx_resource(struct ixgbe_ring *, - struct ixgbe_tx_buffer *); -extern void ixgbe_alloc_rx_buffers(struct ixgbe_ring *, u16); -extern void ixgbe_write_eitr(struct ixgbe_q_vector *); -extern int ixgbe_poll(struct napi_struct *napi, int budget); -extern int ethtool_ioctl(struct ifreq *ifr); -extern s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw); -extern s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl); -extern s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl); -extern s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, - union ixgbe_atr_hash_dword input, - union ixgbe_atr_hash_dword common, - u8 queue); -extern s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw, - union ixgbe_atr_input *input_mask); -extern s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw, - union ixgbe_atr_input *input, - u16 soft_id, u8 queue); -extern s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw, - union ixgbe_atr_input *input, - u16 soft_id); -extern void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input, - union ixgbe_atr_input *mask); -extern bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw); -extern void ixgbe_set_rx_mode(struct net_device *netdev); +void ixgbe_clear_interrupt_scheme(struct ixgbe_adapter *adapter); +netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *, struct ixgbe_adapter *, + struct ixgbe_ring *); +void ixgbe_unmap_and_free_tx_resource(struct ixgbe_ring *, + struct ixgbe_tx_buffer *); +void ixgbe_alloc_rx_buffers(struct ixgbe_ring *, u16); +void ixgbe_write_eitr(struct ixgbe_q_vector *); +int ixgbe_poll(struct napi_struct *napi, int budget); +int ethtool_ioctl(struct ifreq *ifr); +s32 ixgbe_reinit_fdir_tables_82599(struct ixgbe_hw *hw); +s32 ixgbe_init_fdir_signature_82599(struct ixgbe_hw *hw, u32 fdirctrl); +s32 ixgbe_init_fdir_perfect_82599(struct ixgbe_hw *hw, u32 fdirctrl); +s32 ixgbe_fdir_add_signature_filter_82599(struct ixgbe_hw *hw, + union ixgbe_atr_hash_dword input, + union ixgbe_atr_hash_dword common, + u8 queue); +s32 ixgbe_fdir_set_input_mask_82599(struct ixgbe_hw *hw, + union ixgbe_atr_input *input_mask); +s32 ixgbe_fdir_write_perfect_filter_82599(struct ixgbe_hw *hw, + union ixgbe_atr_input *input, + u16 soft_id, u8 queue); +s32 ixgbe_fdir_erase_perfect_filter_82599(struct ixgbe_hw *hw, + union ixgbe_atr_input *input, + u16 soft_id); +void ixgbe_atr_compute_perfect_hash_82599(union ixgbe_atr_input *input, + union ixgbe_atr_input *mask); +bool ixgbe_verify_lesm_fw_enabled_82599(struct ixgbe_hw *hw); +void ixgbe_set_rx_mode(struct net_device *netdev); #ifdef CONFIG_IXGBE_DCB -extern void ixgbe_set_rx_drop_en(struct ixgbe_adapter *adapter); +void ixgbe_set_rx_drop_en(struct ixgbe_adapter *adapter); #endif -extern int ixgbe_setup_tc(struct net_device *dev, u8 tc); -extern void ixgbe_tx_ctxtdesc(struct ixgbe_ring *, u32, u32, u32, u32); -extern void ixgbe_do_reset(struct net_device *netdev); +int ixgbe_setup_tc(struct net_device *dev, u8 tc); +void ixgbe_tx_ctxtdesc(struct ixgbe_ring *, u32, u32, u32, u32); +void ixgbe_do_reset(struct net_device *netdev); #ifdef CONFIG_IXGBE_HWMON -extern void ixgbe_sysfs_exit(struct ixgbe_adapter *adapter); -extern int ixgbe_sysfs_init(struct ixgbe_adapter *adapter); +void ixgbe_sysfs_exit(struct ixgbe_adapter *adapter); +int ixgbe_sysfs_init(struct ixgbe_adapter *adapter); #endif /* CONFIG_IXGBE_HWMON */ #ifdef IXGBE_FCOE -extern void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter); -extern int ixgbe_fso(struct ixgbe_ring *tx_ring, - struct ixgbe_tx_buffer *first, - u8 *hdr_len); -extern int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter, - union ixgbe_adv_rx_desc *rx_desc, - struct sk_buff *skb); -extern int ixgbe_fcoe_ddp_get(struct net_device *netdev, u16 xid, - struct scatterlist *sgl, unsigned int sgc); -extern int ixgbe_fcoe_ddp_target(struct net_device *netdev, u16 xid, - struct scatterlist *sgl, unsigned int sgc); -extern int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid); -extern int ixgbe_setup_fcoe_ddp_resources(struct ixgbe_adapter *adapter); -extern void ixgbe_free_fcoe_ddp_resources(struct ixgbe_adapter *adapter); -extern int ixgbe_fcoe_enable(struct net_device *netdev); -extern int ixgbe_fcoe_disable(struct net_device *netdev); +void ixgbe_configure_fcoe(struct ixgbe_adapter *adapter); +int ixgbe_fso(struct ixgbe_ring *tx_ring, struct ixgbe_tx_buffer *first, + u8 *hdr_len); +int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter, + union ixgbe_adv_rx_desc *rx_desc, struct sk_buff *skb); +int ixgbe_fcoe_ddp_get(struct net_device *netdev, u16 xid, + struct scatterlist *sgl, unsigned int sgc); +int ixgbe_fcoe_ddp_target(struct net_device *netdev, u16 xid, + struct scatterlist *sgl, unsigned int sgc); +int ixgbe_fcoe_ddp_put(struct net_device *netdev, u16 xid); +int ixgbe_setup_fcoe_ddp_resources(struct ixgbe_adapter *adapter); +void ixgbe_free_fcoe_ddp_resources(struct ixgbe_adapter *adapter); +int ixgbe_fcoe_enable(struct net_device *netdev); +int ixgbe_fcoe_disable(struct net_device *netdev); #ifdef CONFIG_IXGBE_DCB -extern u8 ixgbe_fcoe_getapp(struct ixgbe_adapter *adapter); -extern u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up); +u8 ixgbe_fcoe_getapp(struct ixgbe_adapter *adapter); +u8 ixgbe_fcoe_setapp(struct ixgbe_adapter *adapter, u8 up); #endif /* CONFIG_IXGBE_DCB */ -extern int ixgbe_fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type); -extern int ixgbe_fcoe_get_hbainfo(struct net_device *netdev, - struct netdev_fcoe_hbainfo *info); -extern u8 ixgbe_fcoe_get_tc(struct ixgbe_adapter *adapter); +int ixgbe_fcoe_get_wwn(struct net_device *netdev, u64 *wwn, int type); +int ixgbe_fcoe_get_hbainfo(struct net_device *netdev, + struct netdev_fcoe_hbainfo *info); +u8 ixgbe_fcoe_get_tc(struct ixgbe_adapter *adapter); #endif /* IXGBE_FCOE */ #ifdef CONFIG_DEBUG_FS -extern void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter); -extern void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter); -extern void ixgbe_dbg_init(void); -extern void ixgbe_dbg_exit(void); +void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter); +void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter); +void ixgbe_dbg_init(void); +void ixgbe_dbg_exit(void); #else static inline void ixgbe_dbg_adapter_init(struct ixgbe_adapter *adapter) {} static inline void ixgbe_dbg_adapter_exit(struct ixgbe_adapter *adapter) {} @@ -884,12 +925,12 @@ static inline struct netdev_queue *txring_txq(const struct ixgbe_ring *ring) return netdev_get_tx_queue(ring->netdev, ring->queue_index); } -extern void ixgbe_ptp_init(struct ixgbe_adapter *adapter); -extern void ixgbe_ptp_stop(struct ixgbe_adapter *adapter); -extern void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter); -extern void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter); -extern void __ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector, - struct sk_buff *skb); +void ixgbe_ptp_init(struct ixgbe_adapter *adapter); +void ixgbe_ptp_stop(struct ixgbe_adapter *adapter); +void ixgbe_ptp_overflow_check(struct ixgbe_adapter *adapter); +void ixgbe_ptp_rx_hang(struct ixgbe_adapter *adapter); +void __ixgbe_ptp_rx_hwtstamp(struct ixgbe_q_vector *q_vector, + struct sk_buff *skb); static inline void ixgbe_ptp_rx_hwtstamp(struct ixgbe_ring *rx_ring, union ixgbe_adv_rx_desc *rx_desc, struct sk_buff *skb) @@ -906,13 +947,16 @@ static inline void ixgbe_ptp_rx_hwtstamp(struct ixgbe_ring *rx_ring, rx_ring->last_rx_timestamp = jiffies; } -extern int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, - struct ifreq *ifr, int cmd); -extern void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter); -extern void ixgbe_ptp_reset(struct ixgbe_adapter *adapter); -extern void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr); +int ixgbe_ptp_hwtstamp_ioctl(struct ixgbe_adapter *adapter, struct ifreq *ifr, + int cmd); +void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter); +void ixgbe_ptp_reset(struct ixgbe_adapter *adapter); +void ixgbe_ptp_check_pps_event(struct ixgbe_adapter *adapter, u32 eicr); #ifdef CONFIG_PCI_IOV void ixgbe_sriov_reinit(struct ixgbe_adapter *adapter); #endif +netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb, + struct ixgbe_adapter *adapter, + struct ixgbe_ring *tx_ring); #endif /* _IXGBE_H_ */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index e8649abf97c0dd93152d7037bae870eaf3532954..4e7c9b098b58f51441fe23a46f7a158834ed93c6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -442,7 +442,7 @@ static void ixgbe_set_msglevel(struct net_device *netdev, u32 data) static int ixgbe_get_regs_len(struct net_device *netdev) { -#define IXGBE_REGS_LEN 1129 +#define IXGBE_REGS_LEN 1139 return IXGBE_REGS_LEN * sizeof(u32); } @@ -602,22 +602,53 @@ static void ixgbe_get_regs(struct net_device *netdev, regs_buff[828] = IXGBE_READ_REG(hw, IXGBE_FHFT(0)); /* DCB */ - regs_buff[829] = IXGBE_READ_REG(hw, IXGBE_RMCS); - regs_buff[830] = IXGBE_READ_REG(hw, IXGBE_DPMCS); - regs_buff[831] = IXGBE_READ_REG(hw, IXGBE_PDPMCS); - regs_buff[832] = IXGBE_READ_REG(hw, IXGBE_RUPPBMR); - for (i = 0; i < 8; i++) - regs_buff[833 + i] = IXGBE_READ_REG(hw, IXGBE_RT2CR(i)); - for (i = 0; i < 8; i++) - regs_buff[841 + i] = IXGBE_READ_REG(hw, IXGBE_RT2SR(i)); - for (i = 0; i < 8; i++) - regs_buff[849 + i] = IXGBE_READ_REG(hw, IXGBE_TDTQ2TCCR(i)); - for (i = 0; i < 8; i++) - regs_buff[857 + i] = IXGBE_READ_REG(hw, IXGBE_TDTQ2TCSR(i)); + regs_buff[829] = IXGBE_READ_REG(hw, IXGBE_RMCS); /* same as FCCFG */ + regs_buff[831] = IXGBE_READ_REG(hw, IXGBE_PDPMCS); /* same as RTTPCS */ + + switch (hw->mac.type) { + case ixgbe_mac_82598EB: + regs_buff[830] = IXGBE_READ_REG(hw, IXGBE_DPMCS); + regs_buff[832] = IXGBE_READ_REG(hw, IXGBE_RUPPBMR); + for (i = 0; i < 8; i++) + regs_buff[833 + i] = + IXGBE_READ_REG(hw, IXGBE_RT2CR(i)); + for (i = 0; i < 8; i++) + regs_buff[841 + i] = + IXGBE_READ_REG(hw, IXGBE_RT2SR(i)); + for (i = 0; i < 8; i++) + regs_buff[849 + i] = + IXGBE_READ_REG(hw, IXGBE_TDTQ2TCCR(i)); + for (i = 0; i < 8; i++) + regs_buff[857 + i] = + IXGBE_READ_REG(hw, IXGBE_TDTQ2TCSR(i)); + break; + case ixgbe_mac_82599EB: + case ixgbe_mac_X540: + regs_buff[830] = IXGBE_READ_REG(hw, IXGBE_RTTDCS); + regs_buff[832] = IXGBE_READ_REG(hw, IXGBE_RTRPCS); + for (i = 0; i < 8; i++) + regs_buff[833 + i] = + IXGBE_READ_REG(hw, IXGBE_RTRPT4C(i)); + for (i = 0; i < 8; i++) + regs_buff[841 + i] = + IXGBE_READ_REG(hw, IXGBE_RTRPT4S(i)); + for (i = 0; i < 8; i++) + regs_buff[849 + i] = + IXGBE_READ_REG(hw, IXGBE_RTTDT2C(i)); + for (i = 0; i < 8; i++) + regs_buff[857 + i] = + IXGBE_READ_REG(hw, IXGBE_RTTDT2S(i)); + break; + default: + break; + } + for (i = 0; i < 8; i++) - regs_buff[865 + i] = IXGBE_READ_REG(hw, IXGBE_TDPT2TCCR(i)); + regs_buff[865 + i] = + IXGBE_READ_REG(hw, IXGBE_TDPT2TCCR(i)); /* same as RTTPT2C */ for (i = 0; i < 8; i++) - regs_buff[873 + i] = IXGBE_READ_REG(hw, IXGBE_TDPT2TCSR(i)); + regs_buff[873 + i] = + IXGBE_READ_REG(hw, IXGBE_TDPT2TCSR(i)); /* same as RTTPT2S */ /* Statistics */ regs_buff[881] = IXGBE_GET_STAT(adapter, crcerrs); @@ -757,6 +788,20 @@ static void ixgbe_get_regs(struct net_device *netdev, /* 82599 X540 specific registers */ regs_buff[1128] = IXGBE_READ_REG(hw, IXGBE_MFLCN); + + /* 82599 X540 specific DCB registers */ + regs_buff[1129] = IXGBE_READ_REG(hw, IXGBE_RTRUP2TC); + regs_buff[1130] = IXGBE_READ_REG(hw, IXGBE_RTTUP2TC); + for (i = 0; i < 4; i++) + regs_buff[1131 + i] = IXGBE_READ_REG(hw, IXGBE_TXLLQ(i)); + regs_buff[1135] = IXGBE_READ_REG(hw, IXGBE_RTTBCNRM); + /* same as RTTQCNRM */ + regs_buff[1136] = IXGBE_READ_REG(hw, IXGBE_RTTBCNRD); + /* same as RTTQCNRR */ + + /* X540 specific DCB registers */ + regs_buff[1137] = IXGBE_READ_REG(hw, IXGBE_RTTQCNCR); + regs_buff[1138] = IXGBE_READ_REG(hw, IXGBE_RTTQCNTG); } static int ixgbe_get_eeprom_len(struct net_device *netdev) @@ -1072,7 +1117,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i] = 0; data[i+1] = 0; i += 2; -#ifdef LL_EXTENDED_STATS +#ifdef BP_EXTENDED_STATS data[i] = 0; data[i+1] = 0; data[i+2] = 0; @@ -1087,7 +1132,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i+1] = ring->stats.bytes; } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; -#ifdef LL_EXTENDED_STATS +#ifdef BP_EXTENDED_STATS data[i] = ring->stats.yields; data[i+1] = ring->stats.misses; data[i+2] = ring->stats.cleaned; @@ -1100,7 +1145,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i] = 0; data[i+1] = 0; i += 2; -#ifdef LL_EXTENDED_STATS +#ifdef BP_EXTENDED_STATS data[i] = 0; data[i+1] = 0; data[i+2] = 0; @@ -1115,7 +1160,7 @@ static void ixgbe_get_ethtool_stats(struct net_device *netdev, data[i+1] = ring->stats.bytes; } while (u64_stats_fetch_retry_bh(&ring->syncp, start)); i += 2; -#ifdef LL_EXTENDED_STATS +#ifdef BP_EXTENDED_STATS data[i] = ring->stats.yields; data[i+1] = ring->stats.misses; data[i+2] = ring->stats.cleaned; @@ -1157,28 +1202,28 @@ static void ixgbe_get_strings(struct net_device *netdev, u32 stringset, p += ETH_GSTRING_LEN; sprintf(p, "tx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; -#ifdef LL_EXTENDED_STATS - sprintf(p, "tx_queue_%u_ll_napi_yield", i); +#ifdef BP_EXTENDED_STATS + sprintf(p, "tx_queue_%u_bp_napi_yield", i); p += ETH_GSTRING_LEN; - sprintf(p, "tx_queue_%u_ll_misses", i); + sprintf(p, "tx_queue_%u_bp_misses", i); p += ETH_GSTRING_LEN; - sprintf(p, "tx_queue_%u_ll_cleaned", i); + sprintf(p, "tx_queue_%u_bp_cleaned", i); p += ETH_GSTRING_LEN; -#endif /* LL_EXTENDED_STATS */ +#endif /* BP_EXTENDED_STATS */ } for (i = 0; i < IXGBE_NUM_RX_QUEUES; i++) { sprintf(p, "rx_queue_%u_packets", i); p += ETH_GSTRING_LEN; sprintf(p, "rx_queue_%u_bytes", i); p += ETH_GSTRING_LEN; -#ifdef LL_EXTENDED_STATS - sprintf(p, "rx_queue_%u_ll_poll_yield", i); +#ifdef BP_EXTENDED_STATS + sprintf(p, "rx_queue_%u_bp_poll_yield", i); p += ETH_GSTRING_LEN; - sprintf(p, "rx_queue_%u_ll_misses", i); + sprintf(p, "rx_queue_%u_bp_misses", i); p += ETH_GSTRING_LEN; - sprintf(p, "rx_queue_%u_ll_cleaned", i); + sprintf(p, "rx_queue_%u_bp_cleaned", i); p += ETH_GSTRING_LEN; -#endif /* LL_EXTENDED_STATS */ +#endif /* BP_EXTENDED_STATS */ } for (i = 0; i < IXGBE_MAX_PACKET_BUFFERS; i++) { sprintf(p, "tx_pb_%u_pxon", i); @@ -2212,13 +2257,13 @@ static int ixgbe_set_coalesce(struct net_device *netdev, #if IS_ENABLED(CONFIG_BQL) /* detect ITR changes that require update of TXDCTL.WTHRESH */ - if ((adapter->tx_itr_setting > 1) && + if ((adapter->tx_itr_setting != 1) && (adapter->tx_itr_setting < IXGBE_100K_ITR)) { if ((tx_itr_prev == 1) || - (tx_itr_prev > IXGBE_100K_ITR)) + (tx_itr_prev >= IXGBE_100K_ITR)) need_reset = true; } else { - if ((tx_itr_prev > 1) && + if ((tx_itr_prev != 1) && (tx_itr_prev < IXGBE_100K_ITR)) need_reset = true; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index 90b4e1089eccae8c794dc10185a550662ae77e57..32e3eaaa160ac4c4864cae2e4e9025f56f3604ef 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -498,6 +498,7 @@ static bool ixgbe_set_sriov_queues(struct ixgbe_adapter *adapter) #ifdef IXGBE_FCOE u16 fcoe_i = 0; #endif + bool pools = (find_first_zero_bit(&adapter->fwd_bitmask, 32) > 1); /* only proceed if SR-IOV is enabled */ if (!(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)) @@ -510,7 +511,7 @@ static bool ixgbe_set_sriov_queues(struct ixgbe_adapter *adapter) vmdq_i = min_t(u16, IXGBE_MAX_VMDQ_INDICES, vmdq_i); /* 64 pool mode with 2 queues per pool */ - if ((vmdq_i > 32) || (rss_i < 4)) { + if ((vmdq_i > 32) || (rss_i < 4) || (vmdq_i > 16 && pools)) { vmdq_m = IXGBE_82599_VMDQ_2Q_MASK; rss_m = IXGBE_RSS_2Q_MASK; rss_i = min_t(u16, rss_i, 2); @@ -852,7 +853,11 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, /* apply Tx specific ring traits */ ring->count = adapter->tx_ring_count; - ring->queue_index = txr_idx; + if (adapter->num_rx_pools > 1) + ring->queue_index = + txr_idx % adapter->num_rx_queues_per_pool; + else + ring->queue_index = txr_idx; /* assign ring to adapter */ adapter->tx_ring[txr_idx] = ring; @@ -895,7 +900,11 @@ static int ixgbe_alloc_q_vector(struct ixgbe_adapter *adapter, #endif /* IXGBE_FCOE */ /* apply Rx specific ring traits */ ring->count = adapter->rx_ring_count; - ring->queue_index = rxr_idx; + if (adapter->num_rx_pools > 1) + ring->queue_index = + rxr_idx % adapter->num_rx_queues_per_pool; + else + ring->queue_index = rxr_idx; /* assign ring to adapter */ adapter->rx_ring[rxr_idx] = ring; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 0ade0cd5ef53ffab28b3fd34136374bfe9f4b51e..bd8f5239dfe66cab2eac86f24a3ea214cfb2f21d 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -132,7 +133,7 @@ static struct notifier_block dca_notifier = { static unsigned int max_vfs; module_param(max_vfs, uint, 0); MODULE_PARM_DESC(max_vfs, - "Maximum number of virtual functions to allocate per physical function - default is zero and maximum value is 63"); + "Maximum number of virtual functions to allocate per physical function - default is zero and maximum value is 63. (Deprecated)"); #endif /* CONFIG_PCI_IOV */ static unsigned int allow_unsupported_sfp; @@ -153,7 +154,6 @@ MODULE_VERSION(DRV_VERSION); static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter, u32 reg, u16 *value) { - int pos = 0; struct pci_dev *parent_dev; struct pci_bus *parent_bus; @@ -165,11 +165,10 @@ static int ixgbe_read_pci_cfg_word_parent(struct ixgbe_adapter *adapter, if (!parent_dev) return -1; - pos = pci_find_capability(parent_dev, PCI_CAP_ID_EXP); - if (!pos) + if (!pci_is_pcie(parent_dev)) return -1; - pci_read_config_word(parent_dev, pos + reg, value); + pcie_capability_read_word(parent_dev, reg, value); return 0; } @@ -247,7 +246,7 @@ static void ixgbe_check_minimum_link(struct ixgbe_adapter *adapter, max_gts = 4 * width; break; case PCIE_SPEED_8_0GT: - /* 128b/130b encoding only reduces throughput by 1% */ + /* 128b/130b encoding reduces throughput by less than 2% */ max_gts = 8 * width; break; default: @@ -265,7 +264,7 @@ static void ixgbe_check_minimum_link(struct ixgbe_adapter *adapter, width, (speed == PCIE_SPEED_2_5GT ? "20%" : speed == PCIE_SPEED_5_0GT ? "20%" : - speed == PCIE_SPEED_8_0GT ? "N/a" : + speed == PCIE_SPEED_8_0GT ? "<2%" : "Unknown")); if (max_gts < expected_gts) { @@ -872,11 +871,18 @@ static u64 ixgbe_get_tx_completed(struct ixgbe_ring *ring) static u64 ixgbe_get_tx_pending(struct ixgbe_ring *ring) { - struct ixgbe_adapter *adapter = netdev_priv(ring->netdev); - struct ixgbe_hw *hw = &adapter->hw; + struct ixgbe_adapter *adapter; + struct ixgbe_hw *hw; + u32 head, tail; - u32 head = IXGBE_READ_REG(hw, IXGBE_TDH(ring->reg_idx)); - u32 tail = IXGBE_READ_REG(hw, IXGBE_TDT(ring->reg_idx)); + if (ring->l2_accel_priv) + adapter = ring->l2_accel_priv->real_adapter; + else + adapter = netdev_priv(ring->netdev); + + hw = &adapter->hw; + head = IXGBE_READ_REG(hw, IXGBE_TDH(ring->reg_idx)); + tail = IXGBE_READ_REG(hw, IXGBE_TDT(ring->reg_idx)); if (head != tail) return (head < tail) ? @@ -1585,7 +1591,7 @@ static void ixgbe_rx_skb(struct ixgbe_q_vector *q_vector, { struct ixgbe_adapter *adapter = q_vector->adapter; - if (ixgbe_qv_ll_polling(q_vector)) + if (ixgbe_qv_busy_polling(q_vector)) netif_receive_skb(skb); else if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) napi_gro_receive(&q_vector->napi, skb); @@ -2097,7 +2103,7 @@ static int ixgbe_low_latency_recv(struct napi_struct *napi) ixgbe_for_each_ring(ring, q_vector->rx) { found = ixgbe_clean_rx_irq(q_vector, ring, 4); -#ifdef LL_EXTENDED_STATS +#ifdef BP_EXTENDED_STATS if (found) ring->stats.cleaned += found; else @@ -3005,7 +3011,7 @@ void ixgbe_configure_tx_ring(struct ixgbe_adapter *adapter, struct ixgbe_q_vector *q_vector = ring->q_vector; if (q_vector) - netif_set_xps_queue(adapter->netdev, + netif_set_xps_queue(ring->netdev, &q_vector->affinity_mask, ring->queue_index); } @@ -3395,7 +3401,7 @@ static void ixgbe_setup_psrtype(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; int rss_i = adapter->ring_feature[RING_F_RSS].indices; - int p; + u16 pool; /* PSRTYPE must be initialized in non 82598 adapters */ u32 psrtype = IXGBE_PSRTYPE_TCPHDR | @@ -3412,9 +3418,8 @@ static void ixgbe_setup_psrtype(struct ixgbe_adapter *adapter) else if (rss_i > 1) psrtype |= 1 << 29; - for (p = 0; p < adapter->num_rx_pools; p++) - IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(VMDQ_P(p)), - psrtype); + for_each_set_bit(pool, &adapter->fwd_bitmask, 32) + IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(VMDQ_P(pool)), psrtype); } static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter) @@ -3683,7 +3688,11 @@ static void ixgbe_vlan_strip_disable(struct ixgbe_adapter *adapter) case ixgbe_mac_82599EB: case ixgbe_mac_X540: for (i = 0; i < adapter->num_rx_queues; i++) { - j = adapter->rx_ring[i]->reg_idx; + struct ixgbe_ring *ring = adapter->rx_ring[i]; + + if (ring->l2_accel_priv) + continue; + j = ring->reg_idx; vlnctrl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(j)); vlnctrl &= ~IXGBE_RXDCTL_VME; IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(j), vlnctrl); @@ -3713,7 +3722,11 @@ static void ixgbe_vlan_strip_enable(struct ixgbe_adapter *adapter) case ixgbe_mac_82599EB: case ixgbe_mac_X540: for (i = 0; i < adapter->num_rx_queues; i++) { - j = adapter->rx_ring[i]->reg_idx; + struct ixgbe_ring *ring = adapter->rx_ring[i]; + + if (ring->l2_accel_priv) + continue; + j = ring->reg_idx; vlnctrl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(j)); vlnctrl |= IXGBE_RXDCTL_VME; IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(j), vlnctrl); @@ -3750,7 +3763,7 @@ static int ixgbe_write_uc_addr_list(struct net_device *netdev) unsigned int rar_entries = hw->mac.num_rar_entries - 1; int count = 0; - /* In SR-IOV mode significantly less RAR entries are available */ + /* In SR-IOV/VMDQ modes significantly less RAR entries are available */ if (adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) rar_entries = IXGBE_MAX_PF_MACVLANS - 1; @@ -3825,14 +3838,6 @@ void ixgbe_set_rx_mode(struct net_device *netdev) if (netdev->flags & IFF_ALLMULTI) { fctrl |= IXGBE_FCTRL_MPE; vmolr |= IXGBE_VMOLR_MPE; - } else { - /* - * Write addresses to the MTA, if the attempt fails - * then we should just turn on promiscuous mode so - * that we can at least receive multicast traffic - */ - hw->mac.ops.update_mc_addr_list(hw, netdev); - vmolr |= IXGBE_VMOLR_ROMPE; } ixgbe_vlan_filter_enable(adapter); hw->addr_ctrl.user_set_promisc = false; @@ -3849,6 +3854,13 @@ void ixgbe_set_rx_mode(struct net_device *netdev) vmolr |= IXGBE_VMOLR_ROPE; } + /* Write addresses to the MTA, if the attempt fails + * then we should just turn on promiscuous mode so + * that we can at least receive multicast traffic + */ + hw->mac.ops.update_mc_addr_list(hw, netdev); + vmolr |= IXGBE_VMOLR_ROMPE; + if (adapter->num_vfs) ixgbe_restore_vf_multicasts(adapter); @@ -3893,15 +3905,13 @@ static void ixgbe_napi_disable_all(struct ixgbe_adapter *adapter) { int q_idx; - local_bh_disable(); /* for ixgbe_qv_lock_napi() */ for (q_idx = 0; q_idx < adapter->num_q_vectors; q_idx++) { napi_disable(&adapter->q_vector[q_idx]->napi); - while (!ixgbe_qv_lock_napi(adapter->q_vector[q_idx])) { + while (!ixgbe_qv_disable(adapter->q_vector[q_idx])) { pr_info("QV %d locked\n", q_idx); - mdelay(1); + usleep_range(1000, 20000); } } - local_bh_enable(); } #ifdef CONFIG_IXGBE_DCB @@ -4118,6 +4128,228 @@ static void ixgbe_fdir_filter_restore(struct ixgbe_adapter *adapter) spin_unlock(&adapter->fdir_perfect_lock); } +static void ixgbe_macvlan_set_rx_mode(struct net_device *dev, unsigned int pool, + struct ixgbe_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + u32 vmolr; + + /* No unicast promiscuous support for VMDQ devices. */ + vmolr = IXGBE_READ_REG(hw, IXGBE_VMOLR(pool)); + vmolr |= (IXGBE_VMOLR_ROMPE | IXGBE_VMOLR_BAM | IXGBE_VMOLR_AUPE); + + /* clear the affected bit */ + vmolr &= ~IXGBE_VMOLR_MPE; + + if (dev->flags & IFF_ALLMULTI) { + vmolr |= IXGBE_VMOLR_MPE; + } else { + vmolr |= IXGBE_VMOLR_ROMPE; + hw->mac.ops.update_mc_addr_list(hw, dev); + } + ixgbe_write_uc_addr_list(adapter->netdev); + IXGBE_WRITE_REG(hw, IXGBE_VMOLR(pool), vmolr); +} + +static void ixgbe_add_mac_filter(struct ixgbe_adapter *adapter, + u8 *addr, u16 pool) +{ + struct ixgbe_hw *hw = &adapter->hw; + unsigned int entry; + + entry = hw->mac.num_rar_entries - pool; + hw->mac.ops.set_rar(hw, entry, addr, VMDQ_P(pool), IXGBE_RAH_AV); +} + +static void ixgbe_fwd_psrtype(struct ixgbe_fwd_adapter *vadapter) +{ + struct ixgbe_adapter *adapter = vadapter->real_adapter; + int rss_i = adapter->num_rx_queues_per_pool; + struct ixgbe_hw *hw = &adapter->hw; + u16 pool = vadapter->pool; + u32 psrtype = IXGBE_PSRTYPE_TCPHDR | + IXGBE_PSRTYPE_UDPHDR | + IXGBE_PSRTYPE_IPV4HDR | + IXGBE_PSRTYPE_L2HDR | + IXGBE_PSRTYPE_IPV6HDR; + + if (hw->mac.type == ixgbe_mac_82598EB) + return; + + if (rss_i > 3) + psrtype |= 2 << 29; + else if (rss_i > 1) + psrtype |= 1 << 29; + + IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(VMDQ_P(pool)), psrtype); +} + +/** + * ixgbe_clean_rx_ring - Free Rx Buffers per Queue + * @rx_ring: ring to free buffers from + **/ +static void ixgbe_clean_rx_ring(struct ixgbe_ring *rx_ring) +{ + struct device *dev = rx_ring->dev; + unsigned long size; + u16 i; + + /* ring already cleared, nothing to do */ + if (!rx_ring->rx_buffer_info) + return; + + /* Free all the Rx ring sk_buffs */ + for (i = 0; i < rx_ring->count; i++) { + struct ixgbe_rx_buffer *rx_buffer; + + rx_buffer = &rx_ring->rx_buffer_info[i]; + if (rx_buffer->skb) { + struct sk_buff *skb = rx_buffer->skb; + if (IXGBE_CB(skb)->page_released) { + dma_unmap_page(dev, + IXGBE_CB(skb)->dma, + ixgbe_rx_bufsz(rx_ring), + DMA_FROM_DEVICE); + IXGBE_CB(skb)->page_released = false; + } + dev_kfree_skb(skb); + } + rx_buffer->skb = NULL; + if (rx_buffer->dma) + dma_unmap_page(dev, rx_buffer->dma, + ixgbe_rx_pg_size(rx_ring), + DMA_FROM_DEVICE); + rx_buffer->dma = 0; + if (rx_buffer->page) + __free_pages(rx_buffer->page, + ixgbe_rx_pg_order(rx_ring)); + rx_buffer->page = NULL; + } + + size = sizeof(struct ixgbe_rx_buffer) * rx_ring->count; + memset(rx_ring->rx_buffer_info, 0, size); + + /* Zero out the descriptor ring */ + memset(rx_ring->desc, 0, rx_ring->size); + + rx_ring->next_to_alloc = 0; + rx_ring->next_to_clean = 0; + rx_ring->next_to_use = 0; +} + +static void ixgbe_disable_fwd_ring(struct ixgbe_fwd_adapter *vadapter, + struct ixgbe_ring *rx_ring) +{ + struct ixgbe_adapter *adapter = vadapter->real_adapter; + int index = rx_ring->queue_index + vadapter->rx_base_queue; + + /* shutdown specific queue receive and wait for dma to settle */ + ixgbe_disable_rx_queue(adapter, rx_ring); + usleep_range(10000, 20000); + ixgbe_irq_disable_queues(adapter, ((u64)1 << index)); + ixgbe_clean_rx_ring(rx_ring); + rx_ring->l2_accel_priv = NULL; +} + +int ixgbe_fwd_ring_down(struct net_device *vdev, + struct ixgbe_fwd_adapter *accel) +{ + struct ixgbe_adapter *adapter = accel->real_adapter; + unsigned int rxbase = accel->rx_base_queue; + unsigned int txbase = accel->tx_base_queue; + int i; + + netif_tx_stop_all_queues(vdev); + + for (i = 0; i < adapter->num_rx_queues_per_pool; i++) { + ixgbe_disable_fwd_ring(accel, adapter->rx_ring[rxbase + i]); + adapter->rx_ring[rxbase + i]->netdev = adapter->netdev; + } + + for (i = 0; i < adapter->num_rx_queues_per_pool; i++) { + adapter->tx_ring[txbase + i]->l2_accel_priv = NULL; + adapter->tx_ring[txbase + i]->netdev = adapter->netdev; + } + + + return 0; +} + +static int ixgbe_fwd_ring_up(struct net_device *vdev, + struct ixgbe_fwd_adapter *accel) +{ + struct ixgbe_adapter *adapter = accel->real_adapter; + unsigned int rxbase, txbase, queues; + int i, baseq, err = 0; + + if (!test_bit(accel->pool, &adapter->fwd_bitmask)) + return 0; + + baseq = accel->pool * adapter->num_rx_queues_per_pool; + netdev_dbg(vdev, "pool %i:%i queues %i:%i VSI bitmask %lx\n", + accel->pool, adapter->num_rx_pools, + baseq, baseq + adapter->num_rx_queues_per_pool, + adapter->fwd_bitmask); + + accel->netdev = vdev; + accel->rx_base_queue = rxbase = baseq; + accel->tx_base_queue = txbase = baseq; + + for (i = 0; i < adapter->num_rx_queues_per_pool; i++) + ixgbe_disable_fwd_ring(accel, adapter->rx_ring[rxbase + i]); + + for (i = 0; i < adapter->num_rx_queues_per_pool; i++) { + adapter->rx_ring[rxbase + i]->netdev = vdev; + adapter->rx_ring[rxbase + i]->l2_accel_priv = accel; + ixgbe_configure_rx_ring(adapter, adapter->rx_ring[rxbase + i]); + } + + for (i = 0; i < adapter->num_rx_queues_per_pool; i++) { + adapter->tx_ring[txbase + i]->netdev = vdev; + adapter->tx_ring[txbase + i]->l2_accel_priv = accel; + } + + queues = min_t(unsigned int, + adapter->num_rx_queues_per_pool, vdev->num_tx_queues); + err = netif_set_real_num_tx_queues(vdev, queues); + if (err) + goto fwd_queue_err; + + err = netif_set_real_num_rx_queues(vdev, queues); + if (err) + goto fwd_queue_err; + + if (is_valid_ether_addr(vdev->dev_addr)) + ixgbe_add_mac_filter(adapter, vdev->dev_addr, accel->pool); + + ixgbe_fwd_psrtype(accel); + ixgbe_macvlan_set_rx_mode(vdev, accel->pool, adapter); + return err; +fwd_queue_err: + ixgbe_fwd_ring_down(vdev, accel); + return err; +} + +static void ixgbe_configure_dfwd(struct ixgbe_adapter *adapter) +{ + struct net_device *upper; + struct list_head *iter; + int err; + + netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) { + if (netif_is_macvlan(upper)) { + struct macvlan_dev *dfwd = netdev_priv(upper); + struct ixgbe_fwd_adapter *vadapter = dfwd->fwd_priv; + + if (dfwd->fwd_priv) { + err = ixgbe_fwd_ring_up(upper, vadapter); + if (err) + continue; + } + } + } +} + static void ixgbe_configure(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; @@ -4169,6 +4401,7 @@ static void ixgbe_configure(struct ixgbe_adapter *adapter) #endif /* IXGBE_FCOE */ ixgbe_configure_tx(adapter); ixgbe_configure_rx(adapter); + ixgbe_configure_dfwd(adapter); } static inline bool ixgbe_is_sfp(struct ixgbe_hw *hw) @@ -4322,6 +4555,8 @@ static void ixgbe_setup_gpie(struct ixgbe_adapter *adapter) static void ixgbe_up_complete(struct ixgbe_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; + struct net_device *upper; + struct list_head *iter; int err; u32 ctrl_ext; @@ -4365,6 +4600,16 @@ static void ixgbe_up_complete(struct ixgbe_adapter *adapter) /* enable transmits */ netif_tx_start_all_queues(adapter->netdev); + /* enable any upper devices */ + netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) { + if (netif_is_macvlan(upper)) { + struct macvlan_dev *vlan = netdev_priv(upper); + + if (vlan->fwd_priv) + netif_tx_start_all_queues(upper); + } + } + /* bring the link up in the watchdog, this could race with our first * link up interrupt but shouldn't be a problem */ adapter->flags |= IXGBE_FLAG_NEED_LINK_UPDATE; @@ -4455,59 +4700,6 @@ void ixgbe_reset(struct ixgbe_adapter *adapter) ixgbe_ptp_reset(adapter); } -/** - * ixgbe_clean_rx_ring - Free Rx Buffers per Queue - * @rx_ring: ring to free buffers from - **/ -static void ixgbe_clean_rx_ring(struct ixgbe_ring *rx_ring) -{ - struct device *dev = rx_ring->dev; - unsigned long size; - u16 i; - - /* ring already cleared, nothing to do */ - if (!rx_ring->rx_buffer_info) - return; - - /* Free all the Rx ring sk_buffs */ - for (i = 0; i < rx_ring->count; i++) { - struct ixgbe_rx_buffer *rx_buffer; - - rx_buffer = &rx_ring->rx_buffer_info[i]; - if (rx_buffer->skb) { - struct sk_buff *skb = rx_buffer->skb; - if (IXGBE_CB(skb)->page_released) { - dma_unmap_page(dev, - IXGBE_CB(skb)->dma, - ixgbe_rx_bufsz(rx_ring), - DMA_FROM_DEVICE); - IXGBE_CB(skb)->page_released = false; - } - dev_kfree_skb(skb); - } - rx_buffer->skb = NULL; - if (rx_buffer->dma) - dma_unmap_page(dev, rx_buffer->dma, - ixgbe_rx_pg_size(rx_ring), - DMA_FROM_DEVICE); - rx_buffer->dma = 0; - if (rx_buffer->page) - __free_pages(rx_buffer->page, - ixgbe_rx_pg_order(rx_ring)); - rx_buffer->page = NULL; - } - - size = sizeof(struct ixgbe_rx_buffer) * rx_ring->count; - memset(rx_ring->rx_buffer_info, 0, size); - - /* Zero out the descriptor ring */ - memset(rx_ring->desc, 0, rx_ring->size); - - rx_ring->next_to_alloc = 0; - rx_ring->next_to_clean = 0; - rx_ring->next_to_use = 0; -} - /** * ixgbe_clean_tx_ring - Free Tx Buffers * @tx_ring: ring to be cleaned @@ -4585,6 +4777,8 @@ void ixgbe_down(struct ixgbe_adapter *adapter) { struct net_device *netdev = adapter->netdev; struct ixgbe_hw *hw = &adapter->hw; + struct net_device *upper; + struct list_head *iter; u32 rxctrl; int i; @@ -4608,6 +4802,19 @@ void ixgbe_down(struct ixgbe_adapter *adapter) netif_carrier_off(netdev); netif_tx_disable(netdev); + /* disable any upper devices */ + netdev_for_each_all_upper_dev_rcu(adapter->netdev, upper, iter) { + if (netif_is_macvlan(upper)) { + struct macvlan_dev *vlan = netdev_priv(upper); + + if (vlan->fwd_priv) { + netif_tx_stop_all_queues(upper); + netif_carrier_off(upper); + netif_tx_disable(upper); + } + } + } + ixgbe_irq_disable(adapter); ixgbe_napi_disable_all(adapter); @@ -4816,11 +5023,20 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) hw->fc.disable_fc_autoneg = ixgbe_device_supports_autoneg_fc(hw); #ifdef CONFIG_PCI_IOV + if (max_vfs > 0) + e_dev_warn("Enabling SR-IOV VFs using the max_vfs module parameter is deprecated - please use the pci sysfs interface instead.\n"); + /* assign number of SR-IOV VFs */ - if (hw->mac.type != ixgbe_mac_82598EB) - adapter->num_vfs = (max_vfs > 63) ? 0 : max_vfs; + if (hw->mac.type != ixgbe_mac_82598EB) { + if (max_vfs > 63) { + adapter->num_vfs = 0; + e_dev_warn("max_vfs parameter out of range. Not assigning any SR-IOV VFs\n"); + } else { + adapter->num_vfs = max_vfs; + } + } +#endif /* CONFIG_PCI_IOV */ -#endif /* enable itr by default in dynamic mode */ adapter->rx_itr_setting = 1; adapter->tx_itr_setting = 1; @@ -4838,6 +5054,8 @@ static int ixgbe_sw_init(struct ixgbe_adapter *adapter) return -EIO; } + /* PF holds first pool slot */ + set_bit(0, &adapter->fwd_bitmask); set_bit(__IXGBE_DOWN, &adapter->state); return 0; @@ -5143,7 +5361,7 @@ static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu) static int ixgbe_open(struct net_device *netdev) { struct ixgbe_adapter *adapter = netdev_priv(netdev); - int err; + int err, queues; /* disallow open during test */ if (test_bit(__IXGBE_TESTING, &adapter->state)) @@ -5168,16 +5386,21 @@ static int ixgbe_open(struct net_device *netdev) goto err_req_irq; /* Notify the stack of the actual queue counts. */ - err = netif_set_real_num_tx_queues(netdev, - adapter->num_rx_pools > 1 ? 1 : - adapter->num_tx_queues); + if (adapter->num_rx_pools > 1) + queues = adapter->num_rx_queues_per_pool; + else + queues = adapter->num_tx_queues; + + err = netif_set_real_num_tx_queues(netdev, queues); if (err) goto err_set_queues; - - err = netif_set_real_num_rx_queues(netdev, - adapter->num_rx_pools > 1 ? 1 : - adapter->num_rx_queues); + if (adapter->num_rx_pools > 1 && + adapter->num_rx_queues > IXGBE_MAX_L2A_QUEUES) + queues = IXGBE_MAX_L2A_QUEUES; + else + queues = adapter->num_rx_queues; + err = netif_set_real_num_rx_queues(netdev, queues); if (err) goto err_set_queues; @@ -6767,8 +6990,9 @@ out_drop: return NETDEV_TX_OK; } -static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, - struct net_device *netdev) +static netdev_tx_t __ixgbe_xmit_frame(struct sk_buff *skb, + struct net_device *netdev, + struct ixgbe_ring *ring) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_ring *tx_ring; @@ -6784,10 +7008,17 @@ static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, skb_set_tail_pointer(skb, 17); } - tx_ring = adapter->tx_ring[skb->queue_mapping]; + tx_ring = ring ? ring : adapter->tx_ring[skb->queue_mapping]; + return ixgbe_xmit_frame_ring(skb, adapter, tx_ring); } +static netdev_tx_t ixgbe_xmit_frame(struct sk_buff *skb, + struct net_device *netdev) +{ + return __ixgbe_xmit_frame(skb, netdev, NULL); +} + /** * ixgbe_set_mac - Change the Ethernet Address of the NIC * @netdev: network interface device structure @@ -7044,6 +7275,7 @@ int ixgbe_setup_tc(struct net_device *dev, u8 tc) { struct ixgbe_adapter *adapter = netdev_priv(dev); struct ixgbe_hw *hw = &adapter->hw; + bool pools; /* Hardware supports up to 8 traffic classes */ if (tc > adapter->dcb_cfg.num_tcs.pg_tcs || @@ -7051,6 +7283,10 @@ int ixgbe_setup_tc(struct net_device *dev, u8 tc) tc < MAX_TRAFFIC_CLASS)) return -EINVAL; + pools = (find_first_zero_bit(&adapter->fwd_bitmask, 32) > 1); + if (tc && pools && adapter->num_rx_pools > IXGBE_MAX_DCBMACVLANS) + return -EBUSY; + /* Hardware has to reinitialize queues and interrupts to * match packet buffer alignment. Unfortunately, the * hardware is not flexible enough to do this dynamically. @@ -7305,6 +7541,104 @@ static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode); } +static void *ixgbe_fwd_add(struct net_device *pdev, struct net_device *vdev) +{ + struct ixgbe_fwd_adapter *fwd_adapter = NULL; + struct ixgbe_adapter *adapter = netdev_priv(pdev); + unsigned int limit; + int pool, err; + +#ifdef CONFIG_RPS + if (vdev->num_rx_queues != vdev->num_tx_queues) { + netdev_info(pdev, "%s: Only supports a single queue count for TX and RX\n", + vdev->name); + return ERR_PTR(-EINVAL); + } +#endif + /* Check for hardware restriction on number of rx/tx queues */ + if (vdev->num_tx_queues > IXGBE_MAX_L2A_QUEUES || + vdev->num_tx_queues == IXGBE_BAD_L2A_QUEUE) { + netdev_info(pdev, + "%s: Supports RX/TX Queue counts 1,2, and 4\n", + pdev->name); + return ERR_PTR(-EINVAL); + } + + if (((adapter->flags & IXGBE_FLAG_DCB_ENABLED) && + adapter->num_rx_pools > IXGBE_MAX_DCBMACVLANS - 1) || + (adapter->num_rx_pools > IXGBE_MAX_MACVLANS)) + return ERR_PTR(-EBUSY); + + fwd_adapter = kcalloc(1, sizeof(struct ixgbe_fwd_adapter), GFP_KERNEL); + if (!fwd_adapter) + return ERR_PTR(-ENOMEM); + + pool = find_first_zero_bit(&adapter->fwd_bitmask, 32); + adapter->num_rx_pools++; + set_bit(pool, &adapter->fwd_bitmask); + limit = find_last_bit(&adapter->fwd_bitmask, 32); + + /* Enable VMDq flag so device will be set in VM mode */ + adapter->flags |= IXGBE_FLAG_VMDQ_ENABLED | IXGBE_FLAG_SRIOV_ENABLED; + adapter->ring_feature[RING_F_VMDQ].limit = limit + 1; + adapter->ring_feature[RING_F_RSS].limit = vdev->num_tx_queues; + + /* Force reinit of ring allocation with VMDQ enabled */ + err = ixgbe_setup_tc(pdev, netdev_get_num_tc(pdev)); + if (err) + goto fwd_add_err; + fwd_adapter->pool = pool; + fwd_adapter->real_adapter = adapter; + err = ixgbe_fwd_ring_up(vdev, fwd_adapter); + if (err) + goto fwd_add_err; + netif_tx_start_all_queues(vdev); + return fwd_adapter; +fwd_add_err: + /* unwind counter and free adapter struct */ + netdev_info(pdev, + "%s: dfwd hardware acceleration failed\n", vdev->name); + clear_bit(pool, &adapter->fwd_bitmask); + adapter->num_rx_pools--; + kfree(fwd_adapter); + return ERR_PTR(err); +} + +static void ixgbe_fwd_del(struct net_device *pdev, void *priv) +{ + struct ixgbe_fwd_adapter *fwd_adapter = priv; + struct ixgbe_adapter *adapter = fwd_adapter->real_adapter; + unsigned int limit; + + clear_bit(fwd_adapter->pool, &adapter->fwd_bitmask); + adapter->num_rx_pools--; + + limit = find_last_bit(&adapter->fwd_bitmask, 32); + adapter->ring_feature[RING_F_VMDQ].limit = limit + 1; + ixgbe_fwd_ring_down(fwd_adapter->netdev, fwd_adapter); + ixgbe_setup_tc(pdev, netdev_get_num_tc(pdev)); + netdev_dbg(pdev, "pool %i:%i queues %i:%i VSI bitmask %lx\n", + fwd_adapter->pool, adapter->num_rx_pools, + fwd_adapter->rx_base_queue, + fwd_adapter->rx_base_queue + adapter->num_rx_queues_per_pool, + adapter->fwd_bitmask); + kfree(fwd_adapter); +} + +static netdev_tx_t ixgbe_fwd_xmit(struct sk_buff *skb, + struct net_device *dev, + void *priv) +{ + struct ixgbe_fwd_adapter *fwd_adapter = priv; + unsigned int queue; + struct ixgbe_ring *tx_ring; + + queue = skb->queue_mapping + fwd_adapter->tx_base_queue; + tx_ring = fwd_adapter->real_adapter->tx_ring[queue]; + + return __ixgbe_xmit_frame(skb, dev, tx_ring); +} + static const struct net_device_ops ixgbe_netdev_ops = { .ndo_open = ixgbe_open, .ndo_stop = ixgbe_close, @@ -7349,6 +7683,9 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_fdb_add = ixgbe_ndo_fdb_add, .ndo_bridge_setlink = ixgbe_ndo_bridge_setlink, .ndo_bridge_getlink = ixgbe_ndo_bridge_getlink, + .ndo_dfwd_add_station = ixgbe_fwd_add, + .ndo_dfwd_del_station = ixgbe_fwd_del, + .ndo_dfwd_start_xmit = ixgbe_fwd_xmit, }; /** @@ -7362,19 +7699,16 @@ static const struct net_device_ops ixgbe_netdev_ops = { **/ static inline int ixgbe_enumerate_functions(struct ixgbe_adapter *adapter) { - struct ixgbe_hw *hw = &adapter->hw; struct list_head *entry; int physfns = 0; - /* Some cards can not use the generic count PCIe functions method, and - * so must be hardcoded to the correct value. + /* Some cards can not use the generic count PCIe functions method, + * because they are behind a parent switch, so we hardcode these with + * the correct number of functions. */ - switch (hw->device_id) { - case IXGBE_DEV_ID_82599_SFP_SF_QP: - case IXGBE_DEV_ID_82599_QSFP_SF_QP: + if (ixgbe_pcie_from_parent(&adapter->hw)) { physfns = 4; - break; - default: + } else { list_for_each(entry, &adapter->pdev->bus_list) { struct pci_dev *pdev = list_entry(entry, struct pci_dev, bus_list); @@ -7653,7 +7987,8 @@ skip_sriov: NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_RXHASH | - NETIF_F_RXCSUM; + NETIF_F_RXCSUM | + NETIF_F_HW_L2FW_DOFFLOAD; netdev->hw_features = netdev->features; @@ -7759,29 +8094,6 @@ skip_sriov: if (ixgbe_pcie_from_parent(hw)) ixgbe_get_parent_bus_info(adapter); - /* print bus type/speed/width info */ - e_dev_info("(PCI Express:%s:%s) %pM\n", - (hw->bus.speed == ixgbe_bus_speed_8000 ? "8.0GT/s" : - hw->bus.speed == ixgbe_bus_speed_5000 ? "5.0GT/s" : - hw->bus.speed == ixgbe_bus_speed_2500 ? "2.5GT/s" : - "Unknown"), - (hw->bus.width == ixgbe_bus_width_pcie_x8 ? "Width x8" : - hw->bus.width == ixgbe_bus_width_pcie_x4 ? "Width x4" : - hw->bus.width == ixgbe_bus_width_pcie_x1 ? "Width x1" : - "Unknown"), - netdev->dev_addr); - - err = ixgbe_read_pba_string_generic(hw, part_str, IXGBE_PBANUM_LENGTH); - if (err) - strncpy(part_str, "Unknown", IXGBE_PBANUM_LENGTH); - if (ixgbe_is_sfp(hw) && hw->phy.sfp_type != ixgbe_sfp_type_not_present) - e_dev_info("MAC: %d, PHY: %d, SFP+: %d, PBA No: %s\n", - hw->mac.type, hw->phy.type, hw->phy.sfp_type, - part_str); - else - e_dev_info("MAC: %d, PHY: %d, PBA No: %s\n", - hw->mac.type, hw->phy.type, part_str); - /* calculate the expected PCIe bandwidth required for optimal * performance. Note that some older parts will never have enough * bandwidth due to being older generation PCIe parts. We clamp these @@ -7797,6 +8109,19 @@ skip_sriov: } ixgbe_check_minimum_link(adapter, expected_gts); + err = ixgbe_read_pba_string_generic(hw, part_str, IXGBE_PBANUM_LENGTH); + if (err) + strncpy(part_str, "Unknown", IXGBE_PBANUM_LENGTH); + if (ixgbe_is_sfp(hw) && hw->phy.sfp_type != ixgbe_sfp_type_not_present) + e_dev_info("MAC: %d, PHY: %d, SFP+: %d, PBA No: %s\n", + hw->mac.type, hw->phy.type, hw->phy.sfp_type, + part_str); + else + e_dev_info("MAC: %d, PHY: %d, PBA No: %s\n", + hw->mac.type, hw->phy.type, part_str); + + e_dev_info("%pM\n", netdev->dev_addr); + /* reset the hardware with the new settings */ err = hw->mac.ops.start_hw(hw); if (err == IXGBE_ERR_EEPROM_VERSION) { diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h index 24af12e3719e00c8345ba750399226a8522105de..aae900a256da98601a501a121eed1f14347d3997 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_phy.h @@ -57,28 +57,28 @@ #define IXGBE_SFF_QSFP_DEVICE_TECH 0x93 /* Bitmasks */ -#define IXGBE_SFF_DA_PASSIVE_CABLE 0x4 -#define IXGBE_SFF_DA_ACTIVE_CABLE 0x8 -#define IXGBE_SFF_DA_SPEC_ACTIVE_LIMITING 0x4 -#define IXGBE_SFF_1GBASESX_CAPABLE 0x1 -#define IXGBE_SFF_1GBASELX_CAPABLE 0x2 -#define IXGBE_SFF_1GBASET_CAPABLE 0x8 -#define IXGBE_SFF_10GBASESR_CAPABLE 0x10 -#define IXGBE_SFF_10GBASELR_CAPABLE 0x20 -#define IXGBE_SFF_SOFT_RS_SELECT_MASK 0x8 -#define IXGBE_SFF_SOFT_RS_SELECT_10G 0x8 -#define IXGBE_SFF_SOFT_RS_SELECT_1G 0x0 -#define IXGBE_SFF_ADDRESSING_MODE 0x4 -#define IXGBE_SFF_QSFP_DA_ACTIVE_CABLE 0x1 -#define IXGBE_SFF_QSFP_DA_PASSIVE_CABLE 0x8 +#define IXGBE_SFF_DA_PASSIVE_CABLE 0x4 +#define IXGBE_SFF_DA_ACTIVE_CABLE 0x8 +#define IXGBE_SFF_DA_SPEC_ACTIVE_LIMITING 0x4 +#define IXGBE_SFF_1GBASESX_CAPABLE 0x1 +#define IXGBE_SFF_1GBASELX_CAPABLE 0x2 +#define IXGBE_SFF_1GBASET_CAPABLE 0x8 +#define IXGBE_SFF_10GBASESR_CAPABLE 0x10 +#define IXGBE_SFF_10GBASELR_CAPABLE 0x20 +#define IXGBE_SFF_SOFT_RS_SELECT_MASK 0x8 +#define IXGBE_SFF_SOFT_RS_SELECT_10G 0x8 +#define IXGBE_SFF_SOFT_RS_SELECT_1G 0x0 +#define IXGBE_SFF_ADDRESSING_MODE 0x4 +#define IXGBE_SFF_QSFP_DA_ACTIVE_CABLE 0x1 +#define IXGBE_SFF_QSFP_DA_PASSIVE_CABLE 0x8 #define IXGBE_SFF_QSFP_CONNECTOR_NOT_SEPARABLE 0x23 #define IXGBE_SFF_QSFP_TRANSMITER_850NM_VCSEL 0x0 -#define IXGBE_I2C_EEPROM_READ_MASK 0x100 -#define IXGBE_I2C_EEPROM_STATUS_MASK 0x3 -#define IXGBE_I2C_EEPROM_STATUS_NO_OPERATION 0x0 -#define IXGBE_I2C_EEPROM_STATUS_PASS 0x1 -#define IXGBE_I2C_EEPROM_STATUS_FAIL 0x2 -#define IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS 0x3 +#define IXGBE_I2C_EEPROM_READ_MASK 0x100 +#define IXGBE_I2C_EEPROM_STATUS_MASK 0x3 +#define IXGBE_I2C_EEPROM_STATUS_NO_OPERATION 0x0 +#define IXGBE_I2C_EEPROM_STATUS_PASS 0x1 +#define IXGBE_I2C_EEPROM_STATUS_FAIL 0x2 +#define IXGBE_I2C_EEPROM_STATUS_IN_PROGRESS 0x3 /* Flow control defines */ #define IXGBE_TAF_SYM_PAUSE 0x400 diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 276d7b135332c1c1c0ec4415f0594f15ffd9f1d2..d6f0c0d8cf11ddb395617e2749bbf25329cae42c 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -129,10 +129,6 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter) if (!pre_existing_vfs && !adapter->num_vfs) return; - if (!pre_existing_vfs) - dev_warn(&adapter->pdev->dev, - "Enabling SR-IOV VFs using the module parameter is deprecated - please use the pci sysfs interface.\n"); - /* If there are pre-existing VFs then we have to force * use of that many - over ride any module parameter value. * This may result from the user unloading the PF driver @@ -223,17 +219,19 @@ int ixgbe_disable_sriov(struct ixgbe_adapter *adapter) IXGBE_WRITE_FLUSH(hw); /* Disable VMDq flag so device will be set in VM mode */ - if (adapter->ring_feature[RING_F_VMDQ].limit == 1) + if (adapter->ring_feature[RING_F_VMDQ].limit == 1) { adapter->flags &= ~IXGBE_FLAG_VMDQ_ENABLED; - adapter->ring_feature[RING_F_VMDQ].offset = 0; + adapter->flags &= ~IXGBE_FLAG_SRIOV_ENABLED; + rss = min_t(int, IXGBE_MAX_RSS_INDICES, num_online_cpus()); + } else { + rss = min_t(int, IXGBE_MAX_L2A_QUEUES, num_online_cpus()); + } - rss = min_t(int, IXGBE_MAX_RSS_INDICES, num_online_cpus()); + adapter->ring_feature[RING_F_VMDQ].offset = 0; adapter->ring_feature[RING_F_RSS].limit = rss; /* take a breather then clean up driver data */ msleep(100); - - adapter->flags &= ~IXGBE_FLAG_SRIOV_ENABLED; return 0; } @@ -298,13 +296,10 @@ static int ixgbe_pci_sriov_disable(struct pci_dev *dev) err = ixgbe_disable_sriov(adapter); /* Only reinit if no error and state changed */ - if (!err && current_flags != adapter->flags) { - /* ixgbe_disable_sriov() doesn't clear VMDQ flag */ - adapter->flags &= ~IXGBE_FLAG_VMDQ_ENABLED; #ifdef CONFIG_PCI_IOV + if (!err && current_flags != adapter->flags) ixgbe_sriov_reinit(adapter); #endif - } return err; } @@ -558,7 +553,7 @@ static int ixgbe_set_vf_mac(struct ixgbe_adapter *adapter, struct ixgbe_hw *hw = &adapter->hw; int rar_entry = hw->mac.num_rar_entries - (vf + 1); - memcpy(adapter->vfinfo[vf].vf_mac_addresses, mac_addr, 6); + memcpy(adapter->vfinfo[vf].vf_mac_addresses, mac_addr, ETH_ALEN); hw->mac.ops.set_rar(hw, rar_entry, mac_addr, vf, IXGBE_RAH_AV); return 0; @@ -621,16 +616,13 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter, int ixgbe_vf_configuration(struct pci_dev *pdev, unsigned int event_mask) { - unsigned char vf_mac_addr[6]; struct ixgbe_adapter *adapter = pci_get_drvdata(pdev); unsigned int vfn = (event_mask & 0x3f); bool enable = ((event_mask & 0x10000000U) != 0); - if (enable) { - eth_zero_addr(vf_mac_addr); - memcpy(adapter->vfinfo[vfn].vf_mac_addresses, vf_mac_addr, 6); - } + if (enable) + eth_zero_addr(adapter->vfinfo[vfn].vf_mac_addresses); return 0; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 10775cb9b6d84c066263c51881257bf8995e037a..7c19e969576f60160649f3ea95b6033582ed9552 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -561,6 +561,10 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_RTTDQSEL 0x04904 #define IXGBE_RTTDT1C 0x04908 #define IXGBE_RTTDT1S 0x0490C +#define IXGBE_RTTQCNCR 0x08B00 +#define IXGBE_RTTQCNTG 0x04A90 +#define IXGBE_RTTBCNRD 0x0498C +#define IXGBE_RTTQCNRR 0x0498C #define IXGBE_RTTDTECC 0x04990 #define IXGBE_RTTDTECC_NO_BCN 0x00000100 #define IXGBE_RTTBCNRC 0x04984 @@ -570,6 +574,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_RTTBCNRC_RF_INT_MASK \ (IXGBE_RTTBCNRC_RF_DEC_MASK << IXGBE_RTTBCNRC_RF_INT_SHIFT) #define IXGBE_RTTBCNRM 0x04980 +#define IXGBE_RTTQCNRM 0x04980 /* FCoE DMA Context Registers */ #define IXGBE_FCPTRL 0x02410 /* FC User Desc. PTR Low */ diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index 389324f5929a5ef9fd16bac01b9a6d799943747a..24b80a6cfca4ec593f7bed4bf2e1423213125d90 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -32,12 +32,12 @@ #include "ixgbe.h" #include "ixgbe_phy.h" -#define IXGBE_X540_MAX_TX_QUEUES 128 -#define IXGBE_X540_MAX_RX_QUEUES 128 -#define IXGBE_X540_RAR_ENTRIES 128 -#define IXGBE_X540_MC_TBL_SIZE 128 -#define IXGBE_X540_VFT_TBL_SIZE 128 -#define IXGBE_X540_RX_PB_SIZE 384 +#define IXGBE_X540_MAX_TX_QUEUES 128 +#define IXGBE_X540_MAX_RX_QUEUES 128 +#define IXGBE_X540_RAR_ENTRIES 128 +#define IXGBE_X540_MC_TBL_SIZE 128 +#define IXGBE_X540_VFT_TBL_SIZE 128 +#define IXGBE_X540_RX_PB_SIZE 384 static s32 ixgbe_update_flash_X540(struct ixgbe_hw *hw); static s32 ixgbe_poll_flash_update_done_X540(struct ixgbe_hw *hw); diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index c9d0c12d6f04156e19ec0240c19d64c0f23ebc8a..54d9acef9c4e8dab993d7a810a1fe896e22070cb 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -45,16 +45,27 @@ struct ixgbe_stats { char stat_string[ETH_GSTRING_LEN]; - int sizeof_stat; - int stat_offset; - int base_stat_offset; - int saved_reset_offset; + struct { + int sizeof_stat; + int stat_offset; + int base_stat_offset; + int saved_reset_offset; + }; }; -#define IXGBEVF_STAT(m, b, r) sizeof(((struct ixgbevf_adapter *)0)->m), \ - offsetof(struct ixgbevf_adapter, m), \ - offsetof(struct ixgbevf_adapter, b), \ - offsetof(struct ixgbevf_adapter, r) +#define IXGBEVF_STAT(m, b, r) { \ + .sizeof_stat = FIELD_SIZEOF(struct ixgbevf_adapter, m), \ + .stat_offset = offsetof(struct ixgbevf_adapter, m), \ + .base_stat_offset = offsetof(struct ixgbevf_adapter, b), \ + .saved_reset_offset = offsetof(struct ixgbevf_adapter, r) \ +} + +#define IXGBEVF_ZSTAT(m) { \ + .sizeof_stat = FIELD_SIZEOF(struct ixgbevf_adapter, m), \ + .stat_offset = offsetof(struct ixgbevf_adapter, m), \ + .base_stat_offset = -1, \ + .saved_reset_offset = -1 \ +} static const struct ixgbe_stats ixgbe_gstrings_stats[] = { {"rx_packets", IXGBEVF_STAT(stats.vfgprc, stats.base_vfgprc, @@ -65,15 +76,20 @@ static const struct ixgbe_stats ixgbe_gstrings_stats[] = { stats.saved_reset_vfgorc)}, {"tx_bytes", IXGBEVF_STAT(stats.vfgotc, stats.base_vfgotc, stats.saved_reset_vfgotc)}, - {"tx_busy", IXGBEVF_STAT(tx_busy, zero_base, zero_base)}, + {"tx_busy", IXGBEVF_ZSTAT(tx_busy)}, {"multicast", IXGBEVF_STAT(stats.vfmprc, stats.base_vfmprc, stats.saved_reset_vfmprc)}, - {"rx_csum_offload_good", IXGBEVF_STAT(hw_csum_rx_good, zero_base, - zero_base)}, - {"rx_csum_offload_errors", IXGBEVF_STAT(hw_csum_rx_error, zero_base, - zero_base)}, - {"tx_csum_offload_ctxt", IXGBEVF_STAT(hw_csum_tx_good, zero_base, - zero_base)}, + {"rx_csum_offload_good", IXGBEVF_ZSTAT(hw_csum_rx_good)}, + {"rx_csum_offload_errors", IXGBEVF_ZSTAT(hw_csum_rx_error)}, + {"tx_csum_offload_ctxt", IXGBEVF_ZSTAT(hw_csum_tx_good)}, +#ifdef BP_EXTENDED_STATS + {"rx_bp_poll_yield", IXGBEVF_ZSTAT(bp_rx_yields)}, + {"rx_bp_cleaned", IXGBEVF_ZSTAT(bp_rx_cleaned)}, + {"rx_bp_misses", IXGBEVF_ZSTAT(bp_rx_missed)}, + {"tx_bp_napi_yield", IXGBEVF_ZSTAT(bp_tx_yields)}, + {"tx_bp_cleaned", IXGBEVF_ZSTAT(bp_tx_cleaned)}, + {"tx_bp_misses", IXGBEVF_ZSTAT(bp_tx_missed)}, +#endif }; #define IXGBE_QUEUE_STATS_LEN 0 @@ -140,58 +156,10 @@ static void ixgbevf_set_msglevel(struct net_device *netdev, u32 data) #define IXGBE_GET_STAT(_A_, _R_) (_A_->stats._R_) -static char *ixgbevf_reg_names[] = { - "IXGBE_VFCTRL", - "IXGBE_VFSTATUS", - "IXGBE_VFLINKS", - "IXGBE_VFRXMEMWRAP", - "IXGBE_VFFRTIMER", - "IXGBE_VTEICR", - "IXGBE_VTEICS", - "IXGBE_VTEIMS", - "IXGBE_VTEIMC", - "IXGBE_VTEIAC", - "IXGBE_VTEIAM", - "IXGBE_VTEITR", - "IXGBE_VTIVAR", - "IXGBE_VTIVAR_MISC", - "IXGBE_VFRDBAL0", - "IXGBE_VFRDBAL1", - "IXGBE_VFRDBAH0", - "IXGBE_VFRDBAH1", - "IXGBE_VFRDLEN0", - "IXGBE_VFRDLEN1", - "IXGBE_VFRDH0", - "IXGBE_VFRDH1", - "IXGBE_VFRDT0", - "IXGBE_VFRDT1", - "IXGBE_VFRXDCTL0", - "IXGBE_VFRXDCTL1", - "IXGBE_VFSRRCTL0", - "IXGBE_VFSRRCTL1", - "IXGBE_VFPSRTYPE", - "IXGBE_VFTDBAL0", - "IXGBE_VFTDBAL1", - "IXGBE_VFTDBAH0", - "IXGBE_VFTDBAH1", - "IXGBE_VFTDLEN0", - "IXGBE_VFTDLEN1", - "IXGBE_VFTDH0", - "IXGBE_VFTDH1", - "IXGBE_VFTDT0", - "IXGBE_VFTDT1", - "IXGBE_VFTXDCTL0", - "IXGBE_VFTXDCTL1", - "IXGBE_VFTDWBAL0", - "IXGBE_VFTDWBAL1", - "IXGBE_VFTDWBAH0", - "IXGBE_VFTDWBAH1" -}; - - static int ixgbevf_get_regs_len(struct net_device *netdev) { - return (ARRAY_SIZE(ixgbevf_reg_names)) * sizeof(u32); +#define IXGBE_REGS_LEN 45 + return IXGBE_REGS_LEN * sizeof(u32); } static void ixgbevf_get_regs(struct net_device *netdev, @@ -264,9 +232,6 @@ static void ixgbevf_get_regs(struct net_device *netdev, regs_buff[41 + i] = IXGBE_READ_REG(hw, IXGBE_VFTDWBAL(i)); for (i = 0; i < 2; i++) regs_buff[43 + i] = IXGBE_READ_REG(hw, IXGBE_VFTDWBAH(i)); - - for (i = 0; i < ARRAY_SIZE(ixgbevf_reg_names); i++) - hw_dbg(hw, "%s\t%8.8x\n", ixgbevf_reg_names[i], regs_buff[i]); } static void ixgbevf_get_drvinfo(struct net_device *netdev, @@ -441,22 +406,50 @@ static void ixgbevf_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct ixgbevf_adapter *adapter = netdev_priv(netdev); + char *base = (char *) adapter; int i; +#ifdef BP_EXTENDED_STATS + u64 rx_yields = 0, rx_cleaned = 0, rx_missed = 0, + tx_yields = 0, tx_cleaned = 0, tx_missed = 0; + + for (i = 0; i < adapter->num_rx_queues; i++) { + rx_yields += adapter->rx_ring[i].bp_yields; + rx_cleaned += adapter->rx_ring[i].bp_cleaned; + rx_yields += adapter->rx_ring[i].bp_yields; + } + + for (i = 0; i < adapter->num_tx_queues; i++) { + tx_yields += adapter->tx_ring[i].bp_yields; + tx_cleaned += adapter->tx_ring[i].bp_cleaned; + tx_yields += adapter->tx_ring[i].bp_yields; + } + + adapter->bp_rx_yields = rx_yields; + adapter->bp_rx_cleaned = rx_cleaned; + adapter->bp_rx_missed = rx_missed; + + adapter->bp_tx_yields = tx_yields; + adapter->bp_tx_cleaned = tx_cleaned; + adapter->bp_tx_missed = tx_missed; +#endif ixgbevf_update_stats(adapter); for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) { - char *p = (char *)adapter + - ixgbe_gstrings_stats[i].stat_offset; - char *b = (char *)adapter + - ixgbe_gstrings_stats[i].base_stat_offset; - char *r = (char *)adapter + - ixgbe_gstrings_stats[i].saved_reset_offset; - data[i] = ((ixgbe_gstrings_stats[i].sizeof_stat == - sizeof(u64)) ? *(u64 *)p : *(u32 *)p) - - ((ixgbe_gstrings_stats[i].sizeof_stat == - sizeof(u64)) ? *(u64 *)b : *(u32 *)b) + - ((ixgbe_gstrings_stats[i].sizeof_stat == - sizeof(u64)) ? *(u64 *)r : *(u32 *)r); + char *p = base + ixgbe_gstrings_stats[i].stat_offset; + char *b = base + ixgbe_gstrings_stats[i].base_stat_offset; + char *r = base + ixgbe_gstrings_stats[i].saved_reset_offset; + + if (ixgbe_gstrings_stats[i].sizeof_stat == sizeof(u64)) { + if (ixgbe_gstrings_stats[i].base_stat_offset >= 0) + data[i] = *(u64 *)p - *(u64 *)b + *(u64 *)r; + else + data[i] = *(u64 *)p; + } else { + if (ixgbe_gstrings_stats[i].base_stat_offset >= 0) + data[i] = *(u32 *)p - *(u32 *)b + *(u32 *)r; + else + data[i] = *(u32 *)p; + } } } @@ -685,6 +678,85 @@ static int ixgbevf_nway_reset(struct net_device *netdev) return 0; } +static int ixgbevf_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct ixgbevf_adapter *adapter = netdev_priv(netdev); + + /* only valid if in constant ITR mode */ + if (adapter->rx_itr_setting <= 1) + ec->rx_coalesce_usecs = adapter->rx_itr_setting; + else + ec->rx_coalesce_usecs = adapter->rx_itr_setting >> 2; + + /* if in mixed tx/rx queues per vector mode, report only rx settings */ + if (adapter->q_vector[0]->tx.count && adapter->q_vector[0]->rx.count) + return 0; + + /* only valid if in constant ITR mode */ + if (adapter->tx_itr_setting <= 1) + ec->tx_coalesce_usecs = adapter->tx_itr_setting; + else + ec->tx_coalesce_usecs = adapter->tx_itr_setting >> 2; + + return 0; +} + +static int ixgbevf_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct ixgbevf_adapter *adapter = netdev_priv(netdev); + struct ixgbevf_q_vector *q_vector; + int num_vectors, i; + u16 tx_itr_param, rx_itr_param; + + /* don't accept tx specific changes if we've got mixed RxTx vectors */ + if (adapter->q_vector[0]->tx.count && adapter->q_vector[0]->rx.count + && ec->tx_coalesce_usecs) + return -EINVAL; + + + if ((ec->rx_coalesce_usecs > (IXGBE_MAX_EITR >> 2)) || + (ec->tx_coalesce_usecs > (IXGBE_MAX_EITR >> 2))) + return -EINVAL; + + if (ec->rx_coalesce_usecs > 1) + adapter->rx_itr_setting = ec->rx_coalesce_usecs << 2; + else + adapter->rx_itr_setting = ec->rx_coalesce_usecs; + + if (adapter->rx_itr_setting == 1) + rx_itr_param = IXGBE_20K_ITR; + else + rx_itr_param = adapter->rx_itr_setting; + + + if (ec->tx_coalesce_usecs > 1) + adapter->tx_itr_setting = ec->tx_coalesce_usecs << 2; + else + adapter->tx_itr_setting = ec->tx_coalesce_usecs; + + if (adapter->tx_itr_setting == 1) + tx_itr_param = IXGBE_10K_ITR; + else + tx_itr_param = adapter->tx_itr_setting; + + num_vectors = adapter->num_msix_vectors - NON_Q_VECTORS; + + for (i = 0; i < num_vectors; i++) { + q_vector = adapter->q_vector[i]; + if (q_vector->tx.count && !q_vector->rx.count) + /* tx only */ + q_vector->itr = tx_itr_param; + else + /* rx only or mixed */ + q_vector->itr = rx_itr_param; + ixgbevf_write_eitr(q_vector); + } + + return 0; +} + static const struct ethtool_ops ixgbevf_ethtool_ops = { .get_settings = ixgbevf_get_settings, .get_drvinfo = ixgbevf_get_drvinfo, @@ -700,6 +772,8 @@ static const struct ethtool_ops ixgbevf_ethtool_ops = { .get_sset_count = ixgbevf_get_sset_count, .get_strings = ixgbevf_get_strings, .get_ethtool_stats = ixgbevf_get_ethtool_stats, + .get_coalesce = ixgbevf_get_coalesce, + .set_coalesce = ixgbevf_set_coalesce, }; void ixgbevf_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index fff0d98675295182fd15b7bbba36820274286ea2..8971e2d0a984d74055b788742190f9654fb5ac13 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -38,6 +38,11 @@ #include "vf.h" +#ifdef CONFIG_NET_RX_BUSY_POLL +#include +#define BP_EXTENDED_STATS +#endif + /* wrapper around a pointer to a socket buffer, * so a DMA handle can be stored along with the buffer */ struct ixgbevf_tx_buffer { @@ -76,6 +81,11 @@ struct ixgbevf_ring { struct u64_stats_sync syncp; u64 hw_csum_rx_error; u64 hw_csum_rx_good; +#ifdef BP_EXTENDED_STATS + u64 bp_yields; + u64 bp_misses; + u64 bp_cleaned; +#endif u16 head; u16 tail; @@ -145,7 +155,118 @@ struct ixgbevf_q_vector { struct napi_struct napi; struct ixgbevf_ring_container rx, tx; char name[IFNAMSIZ + 9]; +#ifdef CONFIG_NET_RX_BUSY_POLL + unsigned int state; +#define IXGBEVF_QV_STATE_IDLE 0 +#define IXGBEVF_QV_STATE_NAPI 1 /* NAPI owns this QV */ +#define IXGBEVF_QV_STATE_POLL 2 /* poll owns this QV */ +#define IXGBEVF_QV_STATE_DISABLED 4 /* QV is disabled */ +#define IXGBEVF_QV_OWNED (IXGBEVF_QV_STATE_NAPI | IXGBEVF_QV_STATE_POLL) +#define IXGBEVF_QV_LOCKED (IXGBEVF_QV_OWNED | IXGBEVF_QV_STATE_DISABLED) +#define IXGBEVF_QV_STATE_NAPI_YIELD 8 /* NAPI yielded this QV */ +#define IXGBEVF_QV_STATE_POLL_YIELD 16 /* poll yielded this QV */ +#define IXGBEVF_QV_YIELD (IXGBEVF_QV_STATE_NAPI_YIELD | IXGBEVF_QV_STATE_POLL_YIELD) +#define IXGBEVF_QV_USER_PEND (IXGBEVF_QV_STATE_POLL | IXGBEVF_QV_STATE_POLL_YIELD) + spinlock_t lock; +#endif /* CONFIG_NET_RX_BUSY_POLL */ }; +#ifdef CONFIG_NET_RX_BUSY_POLL +static inline void ixgbevf_qv_init_lock(struct ixgbevf_q_vector *q_vector) +{ + + spin_lock_init(&q_vector->lock); + q_vector->state = IXGBEVF_QV_STATE_IDLE; +} + +/* called from the device poll routine to get ownership of a q_vector */ +static inline bool ixgbevf_qv_lock_napi(struct ixgbevf_q_vector *q_vector) +{ + int rc = true; + spin_lock_bh(&q_vector->lock); + if (q_vector->state & IXGBEVF_QV_LOCKED) { + WARN_ON(q_vector->state & IXGBEVF_QV_STATE_NAPI); + q_vector->state |= IXGBEVF_QV_STATE_NAPI_YIELD; + rc = false; +#ifdef BP_EXTENDED_STATS + q_vector->tx.ring->bp_yields++; +#endif + } else { + /* we don't care if someone yielded */ + q_vector->state = IXGBEVF_QV_STATE_NAPI; + } + spin_unlock_bh(&q_vector->lock); + return rc; +} + +/* returns true is someone tried to get the qv while napi had it */ +static inline bool ixgbevf_qv_unlock_napi(struct ixgbevf_q_vector *q_vector) +{ + int rc = false; + spin_lock_bh(&q_vector->lock); + WARN_ON(q_vector->state & (IXGBEVF_QV_STATE_POLL | + IXGBEVF_QV_STATE_NAPI_YIELD)); + + if (q_vector->state & IXGBEVF_QV_STATE_POLL_YIELD) + rc = true; + /* reset state to idle, unless QV is disabled */ + q_vector->state &= IXGBEVF_QV_STATE_DISABLED; + spin_unlock_bh(&q_vector->lock); + return rc; +} + +/* called from ixgbevf_low_latency_poll() */ +static inline bool ixgbevf_qv_lock_poll(struct ixgbevf_q_vector *q_vector) +{ + int rc = true; + spin_lock_bh(&q_vector->lock); + if ((q_vector->state & IXGBEVF_QV_LOCKED)) { + q_vector->state |= IXGBEVF_QV_STATE_POLL_YIELD; + rc = false; +#ifdef BP_EXTENDED_STATS + q_vector->rx.ring->bp_yields++; +#endif + } else { + /* preserve yield marks */ + q_vector->state |= IXGBEVF_QV_STATE_POLL; + } + spin_unlock_bh(&q_vector->lock); + return rc; +} + +/* returns true if someone tried to get the qv while it was locked */ +static inline bool ixgbevf_qv_unlock_poll(struct ixgbevf_q_vector *q_vector) +{ + int rc = false; + spin_lock_bh(&q_vector->lock); + WARN_ON(q_vector->state & (IXGBEVF_QV_STATE_NAPI)); + + if (q_vector->state & IXGBEVF_QV_STATE_POLL_YIELD) + rc = true; + /* reset state to idle, unless QV is disabled */ + q_vector->state &= IXGBEVF_QV_STATE_DISABLED; + spin_unlock_bh(&q_vector->lock); + return rc; +} + +/* true if a socket is polling, even if it did not get the lock */ +static inline bool ixgbevf_qv_busy_polling(struct ixgbevf_q_vector *q_vector) +{ + WARN_ON(!(q_vector->state & IXGBEVF_QV_OWNED)); + return q_vector->state & IXGBEVF_QV_USER_PEND; +} + +/* false if QV is currently owned */ +static inline bool ixgbevf_qv_disable(struct ixgbevf_q_vector *q_vector) +{ + int rc = true; + spin_lock_bh(&q_vector->lock); + if (q_vector->state & IXGBEVF_QV_OWNED) + rc = false; + spin_unlock_bh(&q_vector->lock); + return rc; +} + +#endif /* CONFIG_NET_RX_BUSY_POLL */ /* * microsecond values for various ITR rates shifted by 2 to fit itr register @@ -165,9 +286,13 @@ struct ixgbevf_q_vector { ((_eitr) ? (1000000000 / ((_eitr) * 256)) : 8) #define EITR_REG_TO_INTS_PER_SEC EITR_INTS_PER_SEC_TO_REG -#define IXGBE_DESC_UNUSED(R) \ - ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \ - (R)->next_to_clean - (R)->next_to_use - 1) +static inline u16 ixgbevf_desc_unused(struct ixgbevf_ring *ring) +{ + u16 ntc = ring->next_to_clean; + u16 ntu = ring->next_to_use; + + return ((ntc > ntu) ? 0 : ring->count) + ntc - ntu - 1; +} #define IXGBEVF_RX_DESC(R, i) \ (&(((union ixgbe_adv_rx_desc *)((R)->desc))[i])) @@ -240,7 +365,6 @@ struct ixgbevf_adapter { struct ixgbe_hw hw; u16 msg_enable; struct ixgbevf_hw_stats stats; - u64 zero_base; /* Interrupt Throttle Rate */ u32 eitr_param; @@ -249,6 +373,16 @@ struct ixgbevf_adapter { unsigned int tx_ring_count; unsigned int rx_ring_count; +#ifdef BP_EXTENDED_STATS + u64 bp_rx_yields; + u64 bp_rx_cleaned; + u64 bp_rx_missed; + + u64 bp_tx_yields; + u64 bp_tx_cleaned; + u64 bp_tx_missed; +#endif + u32 link_speed; bool link_up; @@ -281,27 +415,25 @@ extern const struct ixgbe_mbx_operations ixgbevf_mbx_ops; extern const char ixgbevf_driver_name[]; extern const char ixgbevf_driver_version[]; -extern void ixgbevf_up(struct ixgbevf_adapter *adapter); -extern void ixgbevf_down(struct ixgbevf_adapter *adapter); -extern void ixgbevf_reinit_locked(struct ixgbevf_adapter *adapter); -extern void ixgbevf_reset(struct ixgbevf_adapter *adapter); -extern void ixgbevf_set_ethtool_ops(struct net_device *netdev); -extern int ixgbevf_setup_rx_resources(struct ixgbevf_adapter *, - struct ixgbevf_ring *); -extern int ixgbevf_setup_tx_resources(struct ixgbevf_adapter *, - struct ixgbevf_ring *); -extern void ixgbevf_free_rx_resources(struct ixgbevf_adapter *, - struct ixgbevf_ring *); -extern void ixgbevf_free_tx_resources(struct ixgbevf_adapter *, - struct ixgbevf_ring *); -extern void ixgbevf_update_stats(struct ixgbevf_adapter *adapter); -extern int ethtool_ioctl(struct ifreq *ifr); - -extern void ixgbe_napi_add_all(struct ixgbevf_adapter *adapter); -extern void ixgbe_napi_del_all(struct ixgbevf_adapter *adapter); +void ixgbevf_up(struct ixgbevf_adapter *adapter); +void ixgbevf_down(struct ixgbevf_adapter *adapter); +void ixgbevf_reinit_locked(struct ixgbevf_adapter *adapter); +void ixgbevf_reset(struct ixgbevf_adapter *adapter); +void ixgbevf_set_ethtool_ops(struct net_device *netdev); +int ixgbevf_setup_rx_resources(struct ixgbevf_adapter *, struct ixgbevf_ring *); +int ixgbevf_setup_tx_resources(struct ixgbevf_adapter *, struct ixgbevf_ring *); +void ixgbevf_free_rx_resources(struct ixgbevf_adapter *, struct ixgbevf_ring *); +void ixgbevf_free_tx_resources(struct ixgbevf_adapter *, struct ixgbevf_ring *); +void ixgbevf_update_stats(struct ixgbevf_adapter *adapter); +int ethtool_ioctl(struct ifreq *ifr); + +extern void ixgbevf_write_eitr(struct ixgbevf_q_vector *q_vector); + +void ixgbe_napi_add_all(struct ixgbevf_adapter *adapter); +void ixgbe_napi_del_all(struct ixgbevf_adapter *adapter); #ifdef DEBUG -extern char *ixgbevf_get_hw_dev_name(struct ixgbe_hw *hw); +char *ixgbevf_get_hw_dev_name(struct ixgbe_hw *hw); #define hw_dbg(hw, format, arg...) \ printk(KERN_DEBUG "%s: " format, ixgbevf_get_hw_dev_name(hw), ##arg) #else diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 59a62bbfb3714a55683bec8fcb49e9adcb5cb279..038bfc8b76163049bf0f8c2e7b0fc4956ea6bce4 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -58,7 +58,7 @@ const char ixgbevf_driver_name[] = "ixgbevf"; static const char ixgbevf_driver_string[] = "Intel(R) 10 Gigabit PCI Express Virtual Function Network Driver"; -#define DRV_VERSION "2.7.12-k" +#define DRV_VERSION "2.11.3-k" const char ixgbevf_driver_version[] = DRV_VERSION; static char ixgbevf_copyright[] = "Copyright (c) 2009 - 2012 Intel Corporation."; @@ -251,7 +251,7 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) if (unlikely(count && netif_carrier_ok(tx_ring->netdev) && - (IXGBE_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) { + (ixgbevf_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) { /* Make sure that anybody stopping the queue after this * sees the new next_to_clean. */ @@ -299,6 +299,30 @@ static void ixgbevf_receive_skb(struct ixgbevf_q_vector *q_vector, netif_rx(skb); } +/** + * ixgbevf_rx_skb - Helper function to determine proper Rx method + * @q_vector: structure containing interrupt and ring information + * @skb: packet to send up + * @status: hardware indication of status of receive + * @rx_desc: rx descriptor + **/ +static void ixgbevf_rx_skb(struct ixgbevf_q_vector *q_vector, + struct sk_buff *skb, u8 status, + union ixgbe_adv_rx_desc *rx_desc) +{ +#ifdef CONFIG_NET_RX_BUSY_POLL + skb_mark_napi_id(skb, &q_vector->napi); + + if (ixgbevf_qv_busy_polling(q_vector)) { + netif_receive_skb(skb); + /* exit early if we busy polled */ + return; + } +#endif /* CONFIG_NET_RX_BUSY_POLL */ + + ixgbevf_receive_skb(q_vector, skb, status, rx_desc); +} + /** * ixgbevf_rx_checksum - indicate in skb if hw indicated a good cksum * @ring: pointer to Rx descriptor ring structure @@ -396,9 +420,9 @@ static inline void ixgbevf_irq_enable_queues(struct ixgbevf_adapter *adapter, IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, qmask); } -static bool ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, - struct ixgbevf_ring *rx_ring, - int budget) +static int ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, + struct ixgbevf_ring *rx_ring, + int budget) { struct ixgbevf_adapter *adapter = q_vector->adapter; struct pci_dev *pdev = adapter->pdev; @@ -473,15 +497,6 @@ static bool ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, total_rx_bytes += skb->len; total_rx_packets++; - /* - * Work around issue of some types of VM to VM loop back - * packets not getting split correctly - */ - if (staterr & IXGBE_RXD_STAT_LB) { - u32 header_fixup_len = skb_headlen(skb); - if (header_fixup_len < 14) - skb_push(skb, header_fixup_len); - } skb->protocol = eth_type_trans(skb, rx_ring->netdev); /* Workaround hardware that can't do proper VEPA multicast @@ -494,7 +509,7 @@ static bool ixgbevf_clean_rx_irq(struct ixgbevf_q_vector *q_vector, goto next_desc; } - ixgbevf_receive_skb(q_vector, skb, staterr, rx_desc); + ixgbevf_rx_skb(q_vector, skb, staterr, rx_desc); next_desc: rx_desc->wb.upper.status_error = 0; @@ -514,7 +529,7 @@ next_desc: } rx_ring->next_to_clean = i; - cleaned_count = IXGBE_DESC_UNUSED(rx_ring); + cleaned_count = ixgbevf_desc_unused(rx_ring); if (cleaned_count) ixgbevf_alloc_rx_buffers(adapter, rx_ring, cleaned_count); @@ -526,7 +541,7 @@ next_desc: q_vector->rx.total_packets += total_rx_packets; q_vector->rx.total_bytes += total_rx_bytes; - return !!budget; + return total_rx_packets; } /** @@ -549,6 +564,11 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) ixgbevf_for_each_ring(ring, q_vector->tx) clean_complete &= ixgbevf_clean_tx_irq(q_vector, ring); +#ifdef CONFIG_NET_RX_BUSY_POLL + if (!ixgbevf_qv_lock_napi(q_vector)) + return budget; +#endif + /* attempt to distribute budget to each queue fairly, but don't allow * the budget to go below 1 because we'll exit polling */ if (q_vector->rx.count > 1) @@ -558,10 +578,15 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) adapter->flags |= IXGBE_FLAG_IN_NETPOLL; ixgbevf_for_each_ring(ring, q_vector->rx) - clean_complete &= ixgbevf_clean_rx_irq(q_vector, ring, - per_ring_budget); + clean_complete &= (ixgbevf_clean_rx_irq(q_vector, ring, + per_ring_budget) + < per_ring_budget); adapter->flags &= ~IXGBE_FLAG_IN_NETPOLL; +#ifdef CONFIG_NET_RX_BUSY_POLL + ixgbevf_qv_unlock_napi(q_vector); +#endif + /* If all work not completed, return budget and keep polling */ if (!clean_complete) return budget; @@ -580,7 +605,7 @@ static int ixgbevf_poll(struct napi_struct *napi, int budget) * ixgbevf_write_eitr - write VTEITR register in hardware specific way * @q_vector: structure containing interrupt and ring information */ -static void ixgbevf_write_eitr(struct ixgbevf_q_vector *q_vector) +void ixgbevf_write_eitr(struct ixgbevf_q_vector *q_vector) { struct ixgbevf_adapter *adapter = q_vector->adapter; struct ixgbe_hw *hw = &adapter->hw; @@ -596,6 +621,40 @@ static void ixgbevf_write_eitr(struct ixgbevf_q_vector *q_vector) IXGBE_WRITE_REG(hw, IXGBE_VTEITR(v_idx), itr_reg); } +#ifdef CONFIG_NET_RX_BUSY_POLL +/* must be called with local_bh_disable()d */ +static int ixgbevf_busy_poll_recv(struct napi_struct *napi) +{ + struct ixgbevf_q_vector *q_vector = + container_of(napi, struct ixgbevf_q_vector, napi); + struct ixgbevf_adapter *adapter = q_vector->adapter; + struct ixgbevf_ring *ring; + int found = 0; + + if (test_bit(__IXGBEVF_DOWN, &adapter->state)) + return LL_FLUSH_FAILED; + + if (!ixgbevf_qv_lock_poll(q_vector)) + return LL_FLUSH_BUSY; + + ixgbevf_for_each_ring(ring, q_vector->rx) { + found = ixgbevf_clean_rx_irq(q_vector, ring, 4); +#ifdef BP_EXTENDED_STATS + if (found) + ring->bp_cleaned += found; + else + ring->bp_misses++; +#endif + if (found) + break; + } + + ixgbevf_qv_unlock_poll(q_vector); + + return found; +} +#endif /* CONFIG_NET_RX_BUSY_POLL */ + /** * ixgbevf_configure_msix - Configure MSI-X hardware * @adapter: board private structure @@ -756,37 +815,12 @@ static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector) static irqreturn_t ixgbevf_msix_other(int irq, void *data) { struct ixgbevf_adapter *adapter = data; - struct pci_dev *pdev = adapter->pdev; struct ixgbe_hw *hw = &adapter->hw; - u32 msg; - bool got_ack = false; hw->mac.get_link_status = 1; - if (!hw->mbx.ops.check_for_ack(hw)) - got_ack = true; - - if (!hw->mbx.ops.check_for_msg(hw)) { - hw->mbx.ops.read(hw, &msg, 1); - - if ((msg & IXGBE_MBVFICR_VFREQ_MASK) == IXGBE_PF_CONTROL_MSG) { - mod_timer(&adapter->watchdog_timer, - round_jiffies(jiffies + 1)); - adapter->link_up = false; - } - if (msg & IXGBE_VT_MSGTYPE_NACK) - dev_info(&pdev->dev, - "Last Request of type %2.2x to PF Nacked\n", - msg & 0xFF); - hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFSTS; - } - - /* checking for the ack clears the PFACK bit. Place - * it back in the v2p_mailbox cache so that anyone - * polling for an ack will not miss it - */ - if (got_ack) - hw->mbx.v2p_mailbox |= IXGBE_VFMAILBOX_PFACK; + if (!test_bit(__IXGBEVF_DOWN, &adapter->state)) + mod_timer(&adapter->watchdog_timer, jiffies); IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, adapter->eims_other); @@ -1107,6 +1141,21 @@ static void ixgbevf_configure_srrctl(struct ixgbevf_adapter *adapter, int index) IXGBE_WRITE_REG(hw, IXGBE_VFSRRCTL(index), srrctl); } +static void ixgbevf_setup_psrtype(struct ixgbevf_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + + /* PSRTYPE must be initialized in 82599 */ + u32 psrtype = IXGBE_PSRTYPE_TCPHDR | IXGBE_PSRTYPE_UDPHDR | + IXGBE_PSRTYPE_IPV4HDR | IXGBE_PSRTYPE_IPV6HDR | + IXGBE_PSRTYPE_L2HDR; + + if (adapter->num_rx_queues > 1) + psrtype |= 1 << 29; + + IXGBE_WRITE_REG(hw, IXGBE_VFPSRTYPE, psrtype); +} + static void ixgbevf_set_rx_buffer_len(struct ixgbevf_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; @@ -1154,8 +1203,7 @@ static void ixgbevf_configure_rx(struct ixgbevf_adapter *adapter) int i, j; u32 rdlen; - /* PSRTYPE must be initialized in 82599 */ - IXGBE_WRITE_REG(hw, IXGBE_VFPSRTYPE, 0); + ixgbevf_setup_psrtype(adapter); /* set_rx_buffer_len must be called before ring initialization */ ixgbevf_set_rx_buffer_len(adapter); @@ -1293,6 +1341,9 @@ static void ixgbevf_napi_enable_all(struct ixgbevf_adapter *adapter) for (q_idx = 0; q_idx < q_vectors; q_idx++) { q_vector = adapter->q_vector[q_idx]; +#ifdef CONFIG_NET_RX_BUSY_POLL + ixgbevf_qv_init_lock(adapter->q_vector[q_idx]); +#endif napi_enable(&q_vector->napi); } } @@ -1306,6 +1357,12 @@ static void ixgbevf_napi_disable_all(struct ixgbevf_adapter *adapter) for (q_idx = 0; q_idx < q_vectors; q_idx++) { q_vector = adapter->q_vector[q_idx]; napi_disable(&q_vector->napi); +#ifdef CONFIG_NET_RX_BUSY_POLL + while (!ixgbevf_qv_disable(adapter->q_vector[q_idx])) { + pr_info("QV %d locked\n", q_idx); + usleep_range(1000, 20000); + } +#endif /* CONFIG_NET_RX_BUSY_POLL */ } } @@ -1323,31 +1380,55 @@ static void ixgbevf_configure(struct ixgbevf_adapter *adapter) for (i = 0; i < adapter->num_rx_queues; i++) { struct ixgbevf_ring *ring = &adapter->rx_ring[i]; ixgbevf_alloc_rx_buffers(adapter, ring, - IXGBE_DESC_UNUSED(ring)); + ixgbevf_desc_unused(ring)); } } -#define IXGBE_MAX_RX_DESC_POLL 10 -static inline void ixgbevf_rx_desc_queue_enable(struct ixgbevf_adapter *adapter, - int rxr) +#define IXGBEVF_MAX_RX_DESC_POLL 10 +static void ixgbevf_rx_desc_queue_enable(struct ixgbevf_adapter *adapter, + int rxr) { struct ixgbe_hw *hw = &adapter->hw; + int wait_loop = IXGBEVF_MAX_RX_DESC_POLL; + u32 rxdctl; int j = adapter->rx_ring[rxr].reg_idx; - int k; - for (k = 0; k < IXGBE_MAX_RX_DESC_POLL; k++) { - if (IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(j)) & IXGBE_RXDCTL_ENABLE) - break; - else - msleep(1); - } - if (k >= IXGBE_MAX_RX_DESC_POLL) { - hw_dbg(hw, "RXDCTL.ENABLE on Rx queue %d " - "not set within the polling period\n", rxr); - } + do { + usleep_range(1000, 2000); + rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(j)); + } while (--wait_loop && !(rxdctl & IXGBE_RXDCTL_ENABLE)); + + if (!wait_loop) + hw_dbg(hw, "RXDCTL.ENABLE queue %d not set while polling\n", + rxr); + + ixgbevf_release_rx_desc(&adapter->hw, &adapter->rx_ring[rxr], + (adapter->rx_ring[rxr].count - 1)); +} + +static void ixgbevf_disable_rx_queue(struct ixgbevf_adapter *adapter, + struct ixgbevf_ring *ring) +{ + struct ixgbe_hw *hw = &adapter->hw; + int wait_loop = IXGBEVF_MAX_RX_DESC_POLL; + u32 rxdctl; + u8 reg_idx = ring->reg_idx; + + rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(reg_idx)); + rxdctl &= ~IXGBE_RXDCTL_ENABLE; + + /* write value back with RXDCTL.ENABLE bit cleared */ + IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(reg_idx), rxdctl); - ixgbevf_release_rx_desc(hw, &adapter->rx_ring[rxr], - adapter->rx_ring[rxr].count - 1); + /* the hardware may take up to 100us to really disable the rx queue */ + do { + udelay(10); + rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(reg_idx)); + } while (--wait_loop && (rxdctl & IXGBE_RXDCTL_ENABLE)); + + if (!wait_loop) + hw_dbg(hw, "RXDCTL.ENABLE queue %d not cleared while polling\n", + reg_idx); } static void ixgbevf_save_reset_stats(struct ixgbevf_adapter *adapter) @@ -1545,8 +1626,6 @@ void ixgbevf_up(struct ixgbevf_adapter *adapter) { struct ixgbe_hw *hw = &adapter->hw; - ixgbevf_negotiate_api(adapter); - ixgbevf_reset_queues(adapter); ixgbevf_configure(adapter); @@ -1679,7 +1758,10 @@ void ixgbevf_down(struct ixgbevf_adapter *adapter) /* signal that we are down to the interrupt handler */ set_bit(__IXGBEVF_DOWN, &adapter->state); - /* disable receives */ + + /* disable all enabled rx queues */ + for (i = 0; i < adapter->num_rx_queues; i++) + ixgbevf_disable_rx_queue(adapter, &adapter->rx_ring[i]); netif_tx_disable(netdev); @@ -1733,10 +1815,12 @@ void ixgbevf_reset(struct ixgbevf_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; - if (hw->mac.ops.reset_hw(hw)) + if (hw->mac.ops.reset_hw(hw)) { hw_dbg(hw, "PF still resetting\n"); - else + } else { hw->mac.ops.init_hw(hw); + ixgbevf_negotiate_api(adapter); + } if (is_valid_ether_addr(adapter->hw.mac.addr)) { memcpy(netdev->dev_addr, adapter->hw.mac.addr, @@ -1929,6 +2013,9 @@ static int ixgbevf_alloc_q_vectors(struct ixgbevf_adapter *adapter) q_vector->v_idx = q_idx; netif_napi_add(adapter->netdev, &q_vector->napi, ixgbevf_poll, 64); +#ifdef CONFIG_NET_RX_BUSY_POLL + napi_hash_add(&q_vector->napi); +#endif adapter->q_vector[q_idx] = q_vector; } @@ -1938,6 +2025,9 @@ err_out: while (q_idx) { q_idx--; q_vector = adapter->q_vector[q_idx]; +#ifdef CONFIG_NET_RX_BUSY_POLL + napi_hash_del(&q_vector->napi); +#endif netif_napi_del(&q_vector->napi); kfree(q_vector); adapter->q_vector[q_idx] = NULL; @@ -1961,6 +2051,9 @@ static void ixgbevf_free_q_vectors(struct ixgbevf_adapter *adapter) struct ixgbevf_q_vector *q_vector = adapter->q_vector[q_idx]; adapter->q_vector[q_idx] = NULL; +#ifdef CONFIG_NET_RX_BUSY_POLL + napi_hash_del(&q_vector->napi); +#endif netif_napi_del(&q_vector->napi); kfree(q_vector); } @@ -2072,6 +2165,9 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter) hw->mac.max_tx_queues = 2; hw->mac.max_rx_queues = 2; + /* lock to protect mailbox accesses */ + spin_lock_init(&adapter->mbx_lock); + err = hw->mac.ops.reset_hw(hw); if (err) { dev_info(&pdev->dev, @@ -2082,6 +2178,7 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter) pr_err("init_shared_code failed: %d\n", err); goto out; } + ixgbevf_negotiate_api(adapter); err = hw->mac.ops.get_mac_addr(hw, hw->mac.addr); if (err) dev_info(&pdev->dev, "Error reading MAC address\n"); @@ -2097,9 +2194,6 @@ static int ixgbevf_sw_init(struct ixgbevf_adapter *adapter) memcpy(hw->mac.addr, netdev->dev_addr, netdev->addr_len); } - /* lock to protect mailbox accesses */ - spin_lock_init(&adapter->mbx_lock); - /* Enable dynamic interrupt throttling rates */ adapter->rx_itr_setting = 1; adapter->tx_itr_setting = 1; @@ -2620,8 +2714,6 @@ static int ixgbevf_open(struct net_device *netdev) } } - ixgbevf_negotiate_api(adapter); - /* setup queue reg_idx and Rx queue count */ err = ixgbevf_setup_queues(adapter); if (err) @@ -3010,7 +3102,7 @@ static int __ixgbevf_maybe_stop_tx(struct ixgbevf_ring *tx_ring, int size) /* We need to check again in a case another CPU has just * made room available. */ - if (likely(IXGBE_DESC_UNUSED(tx_ring) < size)) + if (likely(ixgbevf_desc_unused(tx_ring) < size)) return -EBUSY; /* A reprieve! - use start_queue because it doesn't call schedule */ @@ -3021,7 +3113,7 @@ static int __ixgbevf_maybe_stop_tx(struct ixgbevf_ring *tx_ring, int size) static int ixgbevf_maybe_stop_tx(struct ixgbevf_ring *tx_ring, int size) { - if (likely(IXGBE_DESC_UNUSED(tx_ring) >= size)) + if (likely(ixgbevf_desc_unused(tx_ring) >= size)) return 0; return __ixgbevf_maybe_stop_tx(tx_ring, size); } @@ -3216,6 +3308,8 @@ static int ixgbevf_resume(struct pci_dev *pdev) } pci_set_master(pdev); + ixgbevf_reset(adapter); + rtnl_lock(); err = ixgbevf_init_interrupt_scheme(adapter); rtnl_unlock(); @@ -3224,8 +3318,6 @@ static int ixgbevf_resume(struct pci_dev *pdev) return err; } - ixgbevf_reset(adapter); - if (netif_running(netdev)) { err = ixgbevf_open(netdev); if (err) @@ -3293,6 +3385,9 @@ static const struct net_device_ops ixgbevf_netdev_ops = { .ndo_tx_timeout = ixgbevf_tx_timeout, .ndo_vlan_rx_add_vid = ixgbevf_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = ixgbevf_vlan_rx_kill_vid, +#ifdef CONFIG_NET_RX_BUSY_POLL + .ndo_busy_poll = ixgbevf_busy_poll_recv, +#endif }; static void ixgbevf_assign_netdev_ops(struct net_device *dev) diff --git a/drivers/net/ethernet/intel/ixgbevf/vf.c b/drivers/net/ethernet/intel/ixgbevf/vf.c index 387b52635bc051259a5e5b99d9740c69bcca687a..4d44d64ae3870c42dd62bb4d1cdfeb96fe9122b9 100644 --- a/drivers/net/ethernet/intel/ixgbevf/vf.c +++ b/drivers/net/ethernet/intel/ixgbevf/vf.c @@ -242,7 +242,7 @@ static s32 ixgbevf_set_uc_addr_vf(struct ixgbe_hw *hw, u32 index, u8 *addr) msgbuf[0] |= index << IXGBE_VT_MSGINFO_SHIFT; msgbuf[0] |= IXGBE_VF_SET_MACVLAN; if (addr) - memcpy(msg_addr, addr, 6); + memcpy(msg_addr, addr, ETH_ALEN); ret_val = mbx->ops.write_posted(hw, msgbuf, 3); if (!ret_val) @@ -275,7 +275,7 @@ static s32 ixgbevf_set_rar_vf(struct ixgbe_hw *hw, u32 index, u8 *addr, memset(msgbuf, 0, sizeof(msgbuf)); msgbuf[0] = IXGBE_VF_SET_MAC_ADDR; - memcpy(msg_addr, addr, 6); + memcpy(msg_addr, addr, ETH_ALEN); ret_val = mbx->ops.write_posted(hw, msgbuf, 3); if (!ret_val) diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 23de82a9da82ff408181759505450adfd439e88d..f5685c0d057911c6c2f59dd982f21f869d1b978c 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -309,7 +309,7 @@ static void jme_load_macaddr(struct net_device *netdev) { struct jme_adapter *jme = netdev_priv(netdev); - unsigned char macaddr[6]; + unsigned char macaddr[ETH_ALEN]; u32 val; spin_lock_bh(&jme->macaddr_lock); @@ -321,7 +321,7 @@ jme_load_macaddr(struct net_device *netdev) val = jread32(jme, JME_RXUMA_HI); macaddr[4] = (val >> 0) & 0xFF; macaddr[5] = (val >> 8) & 0xFF; - memcpy(netdev->dev_addr, macaddr, 6); + memcpy(netdev->dev_addr, macaddr, ETH_ALEN); spin_unlock_bh(&jme->macaddr_lock); } @@ -3192,7 +3192,6 @@ jme_init_one(struct pci_dev *pdev, err_out_unmap: iounmap(jme->regs); err_out_free_netdev: - pci_set_drvdata(pdev, NULL); free_netdev(netdev); err_out_release_regions: pci_release_regions(pdev); @@ -3210,7 +3209,6 @@ jme_remove_one(struct pci_dev *pdev) unregister_netdev(netdev); iounmap(jme->regs); - pci_set_drvdata(pdev, NULL); free_netdev(netdev); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/jme.h b/drivers/net/ethernet/jme.h index 3efc897c991310c637353aaed01c9516575a02d9..58cd67c0c8e42527504719cc7c2c39bf6ee34de0 100644 --- a/drivers/net/ethernet/jme.h +++ b/drivers/net/ethernet/jme.h @@ -28,7 +28,6 @@ #define DRV_NAME "jme" #define DRV_VERSION "1.0.8" -#define PFX DRV_NAME ": " #define PCI_DEVICE_ID_JMICRON_JMC250 0x0250 #define PCI_DEVICE_ID_JMICRON_JMC260 0x0260 diff --git a/drivers/net/ethernet/korina.c b/drivers/net/ethernet/korina.c index a36fa80968eb1a0a42e9051b8ca2ea91ece4840c..4a5e3b0f712e82e7253f92c7ef19ebd282f5dd4d 100644 --- a/drivers/net/ethernet/korina.c +++ b/drivers/net/ethernet/korina.c @@ -1110,7 +1110,7 @@ static int korina_probe(struct platform_device *pdev) lp = netdev_priv(dev); bif->dev = dev; - memcpy(dev->dev_addr, bif->mac, 6); + memcpy(dev->dev_addr, bif->mac, ETH_ALEN); lp->rx_irq = platform_get_irq_byname(pdev, "korina_rx"); lp->tx_irq = platform_get_irq_byname(pdev, "korina_tx"); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 2c210ec35d59da72e0a55257665538d184bc04ef..00cd36e0860105aaa577d089e8a169e7d7b1fd97 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -2513,7 +2513,7 @@ static int mv643xx_eth_shared_of_add_port(struct platform_device *pdev, mac_addr = of_get_mac_address(pnp); if (mac_addr) - memcpy(ppd.mac_addr, mac_addr, 6); + memcpy(ppd.mac_addr, mac_addr, ETH_ALEN); mv643xx_eth_property(pnp, "tx-queue-size", ppd.tx_queue_size); mv643xx_eth_property(pnp, "tx-sram-addr", ppd.tx_sram_addr); @@ -2696,7 +2696,7 @@ static void set_params(struct mv643xx_eth_private *mp, struct net_device *dev = mp->dev; if (is_valid_ether_addr(pd->mac_addr)) - memcpy(dev->dev_addr, pd->mac_addr, 6); + memcpy(dev->dev_addr, pd->mac_addr, ETH_ALEN); else uc_addr_get(mp, dev->dev_addr); @@ -2890,6 +2890,7 @@ static int mv643xx_eth_probe(struct platform_device *pdev) PHY_INTERFACE_MODE_GMII); if (!mp->phy) err = -ENODEV; + phy_addr_set(mp, mp->phy->addr); } else if (pd->phy_addr != MV643XX_ETH_PHY_NONE) { mp->phy = phy_scan(mp, pd->phy_addr); diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index e2f6626603138e7e7d4c7cc01e4853e83a0920a9..7354960b583bc83b45a8aadd001c03c456912f81 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -4,11 +4,9 @@ * Since the MDIO interface of Marvell network interfaces is shared * between all network interfaces, having a single driver allows to * handle concurrent accesses properly (you may have four Ethernet - * ports, but they in fact share the same SMI interface to access the - * MDIO bus). Moreover, this MDIO interface code is similar between - * the mv643xx_eth driver and the mvneta driver. For now, it is only - * used by the mvneta driver, but it could later be used by the - * mv643xx_eth driver as well. + * ports, but they in fact share the same SMI interface to access + * the MDIO bus). This driver is currently used by the mvneta and + * mv643xx_eth drivers. * * Copyright (C) 2012 Marvell * @@ -44,6 +42,15 @@ #define MVMDIO_ERR_INT_SMI_DONE 0x00000010 #define MVMDIO_ERR_INT_MASK 0x0080 +/* + * SMI Timeout measurements: + * - Kirkwood 88F6281 (Globalscale Dreamplug): 45us to 95us (Interrupt) + * - Armada 370 (Globalscale Mirabox): 41us to 43us (Polled) + */ +#define MVMDIO_SMI_TIMEOUT 1000 /* 1000us = 1ms */ +#define MVMDIO_SMI_POLL_INTERVAL_MIN 45 +#define MVMDIO_SMI_POLL_INTERVAL_MAX 55 + struct orion_mdio_dev { struct mutex lock; void __iomem *regs; @@ -68,77 +75,68 @@ static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) static int orion_mdio_wait_ready(struct mii_bus *bus) { struct orion_mdio_dev *dev = bus->priv; - int count; - - if (dev->err_interrupt <= 0) { - count = 0; - while (1) { - if (orion_mdio_smi_is_done(dev)) - break; - - if (count > 100) { - dev_err(bus->parent, - "Timeout: SMI busy for too long\n"); - return -ETIMEDOUT; - } - - udelay(10); - count++; - } - } else { - if (!orion_mdio_smi_is_done(dev)) { + unsigned long timeout = usecs_to_jiffies(MVMDIO_SMI_TIMEOUT); + unsigned long end = jiffies + timeout; + int timedout = 0; + + while (1) { + if (orion_mdio_smi_is_done(dev)) + return 0; + else if (timedout) + break; + + if (dev->err_interrupt <= 0) { + usleep_range(MVMDIO_SMI_POLL_INTERVAL_MIN, + MVMDIO_SMI_POLL_INTERVAL_MAX); + + if (time_is_before_jiffies(end)) + ++timedout; + } else { wait_event_timeout(dev->smi_busy_wait, - orion_mdio_smi_is_done(dev), - msecs_to_jiffies(100)); - if (!orion_mdio_smi_is_done(dev)) - return -ETIMEDOUT; - } + orion_mdio_smi_is_done(dev), + timeout); + + ++timedout; + } } - return 0; + dev_err(bus->parent, "Timeout: SMI busy for too long\n"); + return -ETIMEDOUT; } static int orion_mdio_read(struct mii_bus *bus, int mii_id, int regnum) { struct orion_mdio_dev *dev = bus->priv; - int count; u32 val; int ret; mutex_lock(&dev->lock); ret = orion_mdio_wait_ready(bus); - if (ret < 0) { - mutex_unlock(&dev->lock); - return ret; - } + if (ret < 0) + goto out; writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | MVMDIO_SMI_READ_OPERATION), dev->regs); - /* Wait for the value to become available */ - count = 0; - while (1) { - val = readl(dev->regs); - if (val & MVMDIO_SMI_READ_VALID) - break; - - if (count > 100) { - dev_err(bus->parent, "Timeout when reading PHY\n"); - mutex_unlock(&dev->lock); - return -ETIMEDOUT; - } + ret = orion_mdio_wait_ready(bus); + if (ret < 0) + goto out; - udelay(10); - count++; + val = readl(dev->regs); + if (!(val & MVMDIO_SMI_READ_VALID)) { + dev_err(bus->parent, "SMI bus read not valid\n"); + ret = -ENODEV; + goto out; } + ret = val & 0xFFFF; +out: mutex_unlock(&dev->lock); - - return val & 0xFFFF; + return ret; } static int orion_mdio_write(struct mii_bus *bus, int mii_id, @@ -150,10 +148,8 @@ static int orion_mdio_write(struct mii_bus *bus, int mii_id, mutex_lock(&dev->lock); ret = orion_mdio_wait_ready(bus); - if (ret < 0) { - mutex_unlock(&dev->lock); - return ret; - } + if (ret < 0) + goto out; writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | @@ -161,9 +157,9 @@ static int orion_mdio_write(struct mii_bus *bus, int mii_id, (value << MVMDIO_SMI_DATA_SHIFT)), dev->regs); +out: mutex_unlock(&dev->lock); - - return 0; + return ret; } static int orion_mdio_reset(struct mii_bus *bus) diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index e35bac7cfdf14dc85d2bb55b49a864c29f2ae2c6..7d99e695a1106ff1ab74f00c4edd8363b4b7e2c6 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2811,7 +2811,7 @@ static int mvneta_probe(struct platform_device *pdev) } dt_mac_addr = of_get_mac_address(dn); - if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { + if (dt_mac_addr) { mac_from = "device tree"; memcpy(dev->dev_addr, dt_mac_addr, ETH_ALEN); } else { diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index ecc7f7b696b89a122b80318a12d5f6488a190126..5978461938699f098f7927c7d439cd9134fbc6a7 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -4046,7 +4046,6 @@ err_out_free_regions: pci_release_regions(pdev); err_out_disable_pdev: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); err_out: return err; } @@ -4090,7 +4089,6 @@ static void skge_remove(struct pci_dev *pdev) iounmap(hw->regs); kfree(hw); - pci_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index e09a8c6f85366c4c1251f5365549e154f743f08c..a7df981d2123a9d0c73180fbc4fc70834adacd98 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -5081,7 +5081,6 @@ err_out_free_regions: err_out_disable: pci_disable_device(pdev); err_out: - pci_set_drvdata(pdev, NULL); return err; } @@ -5124,8 +5123,6 @@ static void sky2_remove(struct pci_dev *pdev) iounmap(hw->regs); kfree(hw); - - pci_set_drvdata(pdev, NULL); } static int sky2_suspend(struct device *dev) diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index bb11624a1f3948bb81c38238b89422389b43c61b..1e9970d2f0f38c733ef100db4e91e762eb68da5e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1539,11 +1539,6 @@ out: return ret; } -static int calculate_transition(u16 oper_vlan, u16 admin_vlan) -{ - return (2 * (oper_vlan == MLX4_VGT) + (admin_vlan == MLX4_VGT)); -} - static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, int slave, int port) { @@ -1553,7 +1548,6 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, struct mlx4_dev *dev = &(priv->dev); int err; int admin_vlan_ix = NO_INDX; - enum mlx4_vlan_transition vlan_trans; vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port]; vp_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; @@ -1563,12 +1557,8 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, vp_oper->state.link_state == vp_admin->link_state) return 0; - vlan_trans = calculate_transition(vp_oper->state.default_vlan, - vp_admin->default_vlan); - if (!(priv->mfunc.master.slave_state[slave].active && - dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP && - vlan_trans == MLX4_VLAN_TRANSITION_VST_VST)) { + dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_UPDATE_QP)) { /* even if the UPDATE_QP command isn't supported, we still want * to set this VF link according to the admin directive */ @@ -1586,15 +1576,19 @@ static int mlx4_master_immediate_activate_vlan_qos(struct mlx4_priv *priv, return -ENOMEM; if (vp_oper->state.default_vlan != vp_admin->default_vlan) { - err = __mlx4_register_vlan(&priv->dev, port, - vp_admin->default_vlan, - &admin_vlan_ix); - if (err) { - kfree(work); - mlx4_warn((&priv->dev), - "No vlan resources slave %d, port %d\n", - slave, port); - return err; + if (MLX4_VGT != vp_admin->default_vlan) { + err = __mlx4_register_vlan(&priv->dev, port, + vp_admin->default_vlan, + &admin_vlan_ix); + if (err) { + kfree(work); + mlx4_warn((&priv->dev), + "No vlan resources slave %d, port %d\n", + slave, port); + return err; + } + } else { + admin_vlan_ix = NO_INDX; } work->flags |= MLX4_VF_IMMED_VLAN_FLAG_VLAN; mlx4_dbg((&(priv->dev)), @@ -1687,7 +1681,7 @@ static void mlx4_master_deactivate_admin_state(struct mlx4_priv *priv, int slave vp_oper = &priv->mfunc.master.vf_oper[slave].vport[port]; if (NO_INDX != vp_oper->vlan_idx) { __mlx4_unregister_vlan(&priv->dev, - port, vp_oper->vlan_idx); + port, vp_oper->state.default_vlan); vp_oper->vlan_idx = NO_INDX; } if (NO_INDX != vp_oper->mac_idx) { @@ -1718,6 +1712,7 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, if (cmd == MLX4_COMM_CMD_RESET) { mlx4_warn(dev, "Received reset from slave:%d\n", slave); slave_state[slave].active = false; + slave_state[slave].old_vlan_api = false; mlx4_master_deactivate_admin_state(priv, slave); for (i = 0; i < MLX4_EVENT_TYPES_NUM; ++i) { slave_state[slave].event_eq[i].eqn = -1; @@ -2198,6 +2193,8 @@ struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev) return ERR_PTR(-ENOMEM); } + memset(mailbox->buf, 0, MLX4_MAILBOX_SIZE); + return mailbox; } EXPORT_SYMBOL_GPL(mlx4_alloc_cmd_mailbox); @@ -2253,7 +2250,6 @@ EXPORT_SYMBOL_GPL(mlx4_set_vf_mac); int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) { struct mlx4_priv *priv = mlx4_priv(dev); - struct mlx4_vport_oper_state *vf_oper; struct mlx4_vport_state *vf_admin; int slave; @@ -2269,7 +2265,6 @@ int mlx4_set_vf_vlan(struct mlx4_dev *dev, int port, int vf, u16 vlan, u8 qos) return -EINVAL; vf_admin = &priv->mfunc.master.vf_admin[slave].vport[port]; - vf_oper = &priv->mfunc.master.vf_oper[slave].vport[port]; if ((0 == vlan) && (0 == qos)) vf_admin->default_vlan = MLX4_VGT; diff --git a/drivers/net/ethernet/mellanox/mlx4/cq.c b/drivers/net/ethernet/mellanox/mlx4/cq.c index 004e4231af67b97f5b97ff2e93304c8b5472fb62..22fcbe78311ca50892d5ae6a0a69c4bbeff75196 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/cq.c @@ -128,8 +128,6 @@ int mlx4_cq_modify(struct mlx4_dev *dev, struct mlx4_cq *cq, return PTR_ERR(mailbox); cq_context = mailbox->buf; - memset(cq_context, 0, sizeof *cq_context); - cq_context->cq_max_count = cpu_to_be16(count); cq_context->cq_period = cpu_to_be16(period); @@ -153,8 +151,6 @@ int mlx4_cq_resize(struct mlx4_dev *dev, struct mlx4_cq *cq, return PTR_ERR(mailbox); cq_context = mailbox->buf; - memset(cq_context, 0, sizeof *cq_context); - cq_context->logsize_usrpage = cpu_to_be32(ilog2(entries) << 24); cq_context->log_page_size = mtt->page_shift - 12; mtt_addr = mlx4_mtt_addr(dev, mtt); @@ -274,8 +270,6 @@ int mlx4_cq_alloc(struct mlx4_dev *dev, int nent, } cq_context = mailbox->buf; - memset(cq_context, 0, sizeof *cq_context); - cq_context->flags = cpu_to_be32(!!collapsed << 18); if (timestamp_en) cq_context->flags |= cpu_to_be32(1 << 19); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 3e2d5047cdb39321a35cd27359dbdeb6f277391b..3a098cc4d349a1a35282416fe078d71a2b2c8fb4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -44,12 +44,23 @@ static void mlx4_en_cq_event(struct mlx4_cq *cq, enum mlx4_event event) int mlx4_en_create_cq(struct mlx4_en_priv *priv, - struct mlx4_en_cq *cq, - int entries, int ring, enum cq_type mode) + struct mlx4_en_cq **pcq, + int entries, int ring, enum cq_type mode, + int node) { struct mlx4_en_dev *mdev = priv->mdev; + struct mlx4_en_cq *cq; int err; + cq = kzalloc_node(sizeof(*cq), GFP_KERNEL, node); + if (!cq) { + cq = kzalloc(sizeof(*cq), GFP_KERNEL); + if (!cq) { + en_err(priv, "Failed to allocate CQ structure\n"); + return -ENOMEM; + } + } + cq->size = entries; cq->buf_size = cq->size * mdev->dev->caps.cqe_size; @@ -57,17 +68,30 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv, cq->is_tx = mode; spin_lock_init(&cq->lock); + /* Allocate HW buffers on provided NUMA node. + * dev->numa_node is used in mtt range allocation flow. + */ + set_dev_node(&mdev->dev->pdev->dev, node); err = mlx4_alloc_hwq_res(mdev->dev, &cq->wqres, cq->buf_size, 2 * PAGE_SIZE); + set_dev_node(&mdev->dev->pdev->dev, mdev->dev->numa_node); if (err) - return err; + goto err_cq; err = mlx4_en_map_buffer(&cq->wqres.buf); if (err) - mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size); - else - cq->buf = (struct mlx4_cqe *) cq->wqres.buf.direct.buf; + goto err_res; + cq->buf = (struct mlx4_cqe *)cq->wqres.buf.direct.buf; + *pcq = cq; + + return 0; + +err_res: + mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size); +err_cq: + kfree(cq); + *pcq = NULL; return err; } @@ -117,12 +141,12 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, struct mlx4_en_cq *rx_cq; cq_idx = cq_idx % priv->rx_ring_num; - rx_cq = &priv->rx_cq[cq_idx]; + rx_cq = priv->rx_cq[cq_idx]; cq->vector = rx_cq->vector; } if (!cq->is_tx) - cq->size = priv->rx_ring[cq->ring].actual_size; + cq->size = priv->rx_ring[cq->ring]->actual_size; if ((cq->is_tx && priv->hwtstamp_config.tx_type) || (!cq->is_tx && priv->hwtstamp_config.rx_filter)) @@ -146,9 +170,10 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, return 0; } -void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) +void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq) { struct mlx4_en_dev *mdev = priv->mdev; + struct mlx4_en_cq *cq = *pcq; mlx4_en_unmap_buffer(&cq->wqres.buf); mlx4_free_hwq_res(mdev->dev, &cq->wqres, cq->buf_size); @@ -157,6 +182,8 @@ void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) cq->vector = 0; cq->buf_size = 0; cq->buf = NULL; + kfree(cq); + *pcq = NULL; } void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 0c750985f47e61f06bd402e390978800206b27f4..0596f9f85a0efe120ce9841390ff58a5743cf30e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -51,10 +51,10 @@ static int mlx4_en_moderation_update(struct mlx4_en_priv *priv) int err = 0; for (i = 0; i < priv->tx_ring_num; i++) { - priv->tx_cq[i].moder_cnt = priv->tx_frames; - priv->tx_cq[i].moder_time = priv->tx_usecs; + priv->tx_cq[i]->moder_cnt = priv->tx_frames; + priv->tx_cq[i]->moder_time = priv->tx_usecs; if (priv->port_up) { - err = mlx4_en_set_cq_moder(priv, &priv->tx_cq[i]); + err = mlx4_en_set_cq_moder(priv, priv->tx_cq[i]); if (err) return err; } @@ -64,11 +64,11 @@ static int mlx4_en_moderation_update(struct mlx4_en_priv *priv) return 0; for (i = 0; i < priv->rx_ring_num; i++) { - priv->rx_cq[i].moder_cnt = priv->rx_frames; - priv->rx_cq[i].moder_time = priv->rx_usecs; + priv->rx_cq[i]->moder_cnt = priv->rx_frames; + priv->rx_cq[i]->moder_time = priv->rx_usecs; priv->last_moder_time[i] = MLX4_EN_AUTO_CONF; if (priv->port_up) { - err = mlx4_en_set_cq_moder(priv, &priv->rx_cq[i]); + err = mlx4_en_set_cq_moder(priv, priv->rx_cq[i]); if (err) return err; } @@ -274,16 +274,16 @@ static void mlx4_en_get_ethtool_stats(struct net_device *dev, } } for (i = 0; i < priv->tx_ring_num; i++) { - data[index++] = priv->tx_ring[i].packets; - data[index++] = priv->tx_ring[i].bytes; + data[index++] = priv->tx_ring[i]->packets; + data[index++] = priv->tx_ring[i]->bytes; } for (i = 0; i < priv->rx_ring_num; i++) { - data[index++] = priv->rx_ring[i].packets; - data[index++] = priv->rx_ring[i].bytes; + data[index++] = priv->rx_ring[i]->packets; + data[index++] = priv->rx_ring[i]->bytes; #ifdef CONFIG_NET_RX_BUSY_POLL - data[index++] = priv->rx_ring[i].yields; - data[index++] = priv->rx_ring[i].misses; - data[index++] = priv->rx_ring[i].cleaned; + data[index++] = priv->rx_ring[i]->yields; + data[index++] = priv->rx_ring[i]->misses; + data[index++] = priv->rx_ring[i]->cleaned; #endif } spin_unlock_bh(&priv->stats_lock); @@ -510,9 +510,9 @@ static int mlx4_en_set_ringparam(struct net_device *dev, tx_size = max_t(u32, tx_size, MLX4_EN_MIN_TX_SIZE); tx_size = min_t(u32, tx_size, MLX4_EN_MAX_TX_SIZE); - if (rx_size == (priv->port_up ? priv->rx_ring[0].actual_size : - priv->rx_ring[0].size) && - tx_size == priv->tx_ring[0].size) + if (rx_size == (priv->port_up ? priv->rx_ring[0]->actual_size : + priv->rx_ring[0]->size) && + tx_size == priv->tx_ring[0]->size) return 0; mutex_lock(&mdev->state_lock); @@ -553,8 +553,8 @@ static void mlx4_en_get_ringparam(struct net_device *dev, param->rx_max_pending = MLX4_EN_MAX_RX_SIZE; param->tx_max_pending = MLX4_EN_MAX_TX_SIZE; param->rx_pending = priv->port_up ? - priv->rx_ring[0].actual_size : priv->rx_ring[0].size; - param->tx_pending = priv->tx_ring[0].size; + priv->rx_ring[0]->actual_size : priv->rx_ring[0]->size; + param->tx_pending = priv->tx_ring[0]->size; } static u32 mlx4_en_get_rxfh_indir_size(struct net_device *dev) diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index a071cda2dd04ad880ce3862d7c4011839524dd13..0d087b03a7b09db29f170c712e8f36c71c8f9db6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -264,6 +264,10 @@ static void *mlx4_en_add(struct mlx4_dev *dev) mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) mdev->port_cnt++; + /* Initialize time stamp mechanism */ + if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS) + mlx4_en_init_timestamp(mdev); + mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) { if (!dev->caps.comp_pool) { mdev->profile.prof[i].rx_ring_num = @@ -301,10 +305,6 @@ static void *mlx4_en_add(struct mlx4_dev *dev) mdev->pndev[i] = NULL; } - /* Initialize time stamp mechanism */ - if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_TS) - mlx4_en_init_timestamp(mdev); - return mdev; err_mr: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index fa37b7a612133c739263346a5d6f372af0fd9dda..e72d8a112a6bdc03e3d1f33763e8134b19f92d99 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -75,7 +75,7 @@ static int mlx4_en_low_latency_recv(struct napi_struct *napi) struct mlx4_en_cq *cq = container_of(napi, struct mlx4_en_cq, napi); struct net_device *dev = cq->dev; struct mlx4_en_priv *priv = netdev_priv(dev); - struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring]; + struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring]; int done; if (!priv->port_up) @@ -102,6 +102,7 @@ struct mlx4_en_filter { struct list_head next; struct work_struct work; + u8 ip_proto; __be32 src_ip; __be32 dst_ip; __be16 src_port; @@ -120,14 +121,26 @@ struct mlx4_en_filter { static void mlx4_en_filter_rfs_expire(struct mlx4_en_priv *priv); +static enum mlx4_net_trans_rule_id mlx4_ip_proto_to_trans_rule_id(u8 ip_proto) +{ + switch (ip_proto) { + case IPPROTO_UDP: + return MLX4_NET_TRANS_RULE_ID_UDP; + case IPPROTO_TCP: + return MLX4_NET_TRANS_RULE_ID_TCP; + default: + return -EPROTONOSUPPORT; + } +}; + static void mlx4_en_filter_work(struct work_struct *work) { struct mlx4_en_filter *filter = container_of(work, struct mlx4_en_filter, work); struct mlx4_en_priv *priv = filter->priv; - struct mlx4_spec_list spec_tcp = { - .id = MLX4_NET_TRANS_RULE_ID_TCP, + struct mlx4_spec_list spec_tcp_udp = { + .id = mlx4_ip_proto_to_trans_rule_id(filter->ip_proto), { .tcp_udp = { .dst_port = filter->dst_port, @@ -163,9 +176,14 @@ static void mlx4_en_filter_work(struct work_struct *work) int rc; __be64 mac_mask = cpu_to_be64(MLX4_MAC_MASK << 16); + if (spec_tcp_udp.id < 0) { + en_warn(priv, "RFS: ignoring unsupported ip protocol (%d)\n", + filter->ip_proto); + goto ignore; + } list_add_tail(&spec_eth.list, &rule.list); list_add_tail(&spec_ip.list, &rule.list); - list_add_tail(&spec_tcp.list, &rule.list); + list_add_tail(&spec_tcp_udp.list, &rule.list); rule.qpn = priv->rss_map.qps[filter->rxq_index].qpn; memcpy(spec_eth.eth.dst_mac, priv->dev->dev_addr, ETH_ALEN); @@ -183,6 +201,7 @@ static void mlx4_en_filter_work(struct work_struct *work) if (rc) en_err(priv, "Error attaching flow. err = %d\n", rc); +ignore: mlx4_en_filter_rfs_expire(priv); filter->activated = 1; @@ -206,8 +225,8 @@ filter_hash_bucket(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip, static struct mlx4_en_filter * mlx4_en_filter_alloc(struct mlx4_en_priv *priv, int rxq_index, __be32 src_ip, - __be32 dst_ip, __be16 src_port, __be16 dst_port, - u32 flow_id) + __be32 dst_ip, u8 ip_proto, __be16 src_port, + __be16 dst_port, u32 flow_id) { struct mlx4_en_filter *filter = NULL; @@ -221,6 +240,7 @@ mlx4_en_filter_alloc(struct mlx4_en_priv *priv, int rxq_index, __be32 src_ip, filter->src_ip = src_ip; filter->dst_ip = dst_ip; + filter->ip_proto = ip_proto; filter->src_port = src_port; filter->dst_port = dst_port; @@ -252,7 +272,7 @@ static void mlx4_en_filter_free(struct mlx4_en_filter *filter) static inline struct mlx4_en_filter * mlx4_en_filter_find(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip, - __be16 src_port, __be16 dst_port) + u8 ip_proto, __be16 src_port, __be16 dst_port) { struct mlx4_en_filter *filter; struct mlx4_en_filter *ret = NULL; @@ -263,6 +283,7 @@ mlx4_en_filter_find(struct mlx4_en_priv *priv, __be32 src_ip, __be32 dst_ip, filter_chain) { if (filter->src_ip == src_ip && filter->dst_ip == dst_ip && + filter->ip_proto == ip_proto && filter->src_port == src_port && filter->dst_port == dst_port) { ret = filter; @@ -281,6 +302,7 @@ mlx4_en_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, struct mlx4_en_filter *filter; const struct iphdr *ip; const __be16 *ports; + u8 ip_proto; __be32 src_ip; __be32 dst_ip; __be16 src_port; @@ -295,18 +317,19 @@ mlx4_en_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, if (ip_is_fragment(ip)) return -EPROTONOSUPPORT; + if ((ip->protocol != IPPROTO_TCP) && (ip->protocol != IPPROTO_UDP)) + return -EPROTONOSUPPORT; ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl); + ip_proto = ip->protocol; src_ip = ip->saddr; dst_ip = ip->daddr; src_port = ports[0]; dst_port = ports[1]; - if (ip->protocol != IPPROTO_TCP) - return -EPROTONOSUPPORT; - spin_lock_bh(&priv->filters_lock); - filter = mlx4_en_filter_find(priv, src_ip, dst_ip, src_port, dst_port); + filter = mlx4_en_filter_find(priv, src_ip, dst_ip, ip_proto, + src_port, dst_port); if (filter) { if (filter->rxq_index == rxq_index) goto out; @@ -314,7 +337,7 @@ mlx4_en_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, filter->rxq_index = rxq_index; } else { filter = mlx4_en_filter_alloc(priv, rxq_index, - src_ip, dst_ip, + src_ip, dst_ip, ip_proto, src_port, dst_port, flow_id); if (!filter) { ret = -ENOMEM; @@ -332,8 +355,7 @@ err: return ret; } -void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *rx_ring) +void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv) { struct mlx4_en_filter *filter, *tmp; LIST_HEAD(del_list); @@ -417,7 +439,6 @@ static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev, struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; int err; - int idx; en_dbg(HW, priv, "Killing VID:%d\n", vid); @@ -425,10 +446,7 @@ static int mlx4_en_vlan_rx_kill_vid(struct net_device *dev, /* Remove VID from port VLAN filter */ mutex_lock(&mdev->state_lock); - if (!mlx4_find_cached_vlan(mdev->dev, priv->port, vid, &idx)) - mlx4_unregister_vlan(mdev->dev, priv->port, idx); - else - en_dbg(HW, priv, "could not find vid %d in cache\n", vid); + mlx4_unregister_vlan(mdev->dev, priv->port, vid); if (mdev->device_up && priv->port_up) { err = mlx4_SET_VLAN_FLTR(mdev->dev, priv); @@ -1223,7 +1241,7 @@ static void mlx4_en_netpoll(struct net_device *dev) int i; for (i = 0; i < priv->rx_ring_num; i++) { - cq = &priv->rx_cq[i]; + cq = priv->rx_cq[i]; spin_lock_irqsave(&cq->lock, flags); napi_synchronize(&cq->napi); mlx4_en_process_rx_cq(dev, cq, 0); @@ -1245,8 +1263,8 @@ static void mlx4_en_tx_timeout(struct net_device *dev) if (!netif_tx_queue_stopped(netdev_get_tx_queue(dev, i))) continue; en_warn(priv, "TX timeout on queue: %d, QP: 0x%x, CQ: 0x%x, Cons: 0x%x, Prod: 0x%x\n", - i, priv->tx_ring[i].qpn, priv->tx_ring[i].cqn, - priv->tx_ring[i].cons, priv->tx_ring[i].prod); + i, priv->tx_ring[i]->qpn, priv->tx_ring[i]->cqn, + priv->tx_ring[i]->cons, priv->tx_ring[i]->prod); } priv->port_stats.tx_timeout++; @@ -1286,7 +1304,7 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv) /* Setup cq moderation params */ for (i = 0; i < priv->rx_ring_num; i++) { - cq = &priv->rx_cq[i]; + cq = priv->rx_cq[i]; cq->moder_cnt = priv->rx_frames; cq->moder_time = priv->rx_usecs; priv->last_moder_time[i] = MLX4_EN_AUTO_CONF; @@ -1295,7 +1313,7 @@ static void mlx4_en_set_default_moderation(struct mlx4_en_priv *priv) } for (i = 0; i < priv->tx_ring_num; i++) { - cq = &priv->tx_cq[i]; + cq = priv->tx_cq[i]; cq->moder_cnt = priv->tx_frames; cq->moder_time = priv->tx_usecs; } @@ -1329,8 +1347,8 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv) for (ring = 0; ring < priv->rx_ring_num; ring++) { spin_lock_bh(&priv->stats_lock); - rx_packets = priv->rx_ring[ring].packets; - rx_bytes = priv->rx_ring[ring].bytes; + rx_packets = priv->rx_ring[ring]->packets; + rx_bytes = priv->rx_ring[ring]->bytes; spin_unlock_bh(&priv->stats_lock); rx_pkt_diff = ((unsigned long) (rx_packets - @@ -1359,7 +1377,7 @@ static void mlx4_en_auto_moderation(struct mlx4_en_priv *priv) if (moder_time != priv->last_moder_time[ring]) { priv->last_moder_time[ring] = moder_time; - cq = &priv->rx_cq[ring]; + cq = priv->rx_cq[ring]; cq->moder_time = moder_time; cq->moder_cnt = priv->rx_frames; err = mlx4_en_set_cq_moder(priv, cq); @@ -1482,7 +1500,7 @@ int mlx4_en_start_port(struct net_device *dev) return err; } for (i = 0; i < priv->rx_ring_num; i++) { - cq = &priv->rx_cq[i]; + cq = priv->rx_cq[i]; mlx4_en_cq_init_lock(cq); @@ -1500,7 +1518,7 @@ int mlx4_en_start_port(struct net_device *dev) goto cq_err; } mlx4_en_arm_cq(priv, cq); - priv->rx_ring[i].cqn = cq->mcq.cqn; + priv->rx_ring[i]->cqn = cq->mcq.cqn; ++rx_index; } @@ -1526,7 +1544,7 @@ int mlx4_en_start_port(struct net_device *dev) /* Configure tx cq's and rings */ for (i = 0; i < priv->tx_ring_num; i++) { /* Configure cq */ - cq = &priv->tx_cq[i]; + cq = priv->tx_cq[i]; err = mlx4_en_activate_cq(priv, cq, i); if (err) { en_err(priv, "Failed allocating Tx CQ\n"); @@ -1542,7 +1560,7 @@ int mlx4_en_start_port(struct net_device *dev) cq->buf->wqe_index = cpu_to_be16(0xffff); /* Configure ring */ - tx_ring = &priv->tx_ring[i]; + tx_ring = priv->tx_ring[i]; err = mlx4_en_activate_tx_ring(priv, tx_ring, cq->mcq.cqn, i / priv->num_tx_rings_p_up); if (err) { @@ -1612,8 +1630,8 @@ int mlx4_en_start_port(struct net_device *dev) tx_err: while (tx_index--) { - mlx4_en_deactivate_tx_ring(priv, &priv->tx_ring[tx_index]); - mlx4_en_deactivate_cq(priv, &priv->tx_cq[tx_index]); + mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[tx_index]); + mlx4_en_deactivate_cq(priv, priv->tx_cq[tx_index]); } mlx4_en_destroy_drop_qp(priv); rss_err: @@ -1622,9 +1640,9 @@ mac_err: mlx4_en_put_qp(priv); cq_err: while (rx_index--) - mlx4_en_deactivate_cq(priv, &priv->rx_cq[rx_index]); + mlx4_en_deactivate_cq(priv, priv->rx_cq[rx_index]); for (i = 0; i < priv->rx_ring_num; i++) - mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]); + mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]); return err; /* need to close devices */ } @@ -1720,25 +1738,25 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) /* Free TX Rings */ for (i = 0; i < priv->tx_ring_num; i++) { - mlx4_en_deactivate_tx_ring(priv, &priv->tx_ring[i]); - mlx4_en_deactivate_cq(priv, &priv->tx_cq[i]); + mlx4_en_deactivate_tx_ring(priv, priv->tx_ring[i]); + mlx4_en_deactivate_cq(priv, priv->tx_cq[i]); } msleep(10); for (i = 0; i < priv->tx_ring_num; i++) - mlx4_en_free_tx_buf(dev, &priv->tx_ring[i]); + mlx4_en_free_tx_buf(dev, priv->tx_ring[i]); /* Free RSS qps */ mlx4_en_release_rss_steer(priv); /* Unregister Mac address for the port */ mlx4_en_put_qp(priv); - if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN)) + if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_REASSIGN_MAC_EN)) mdev->mac_removed[priv->port] = 1; /* Free RX Rings */ for (i = 0; i < priv->rx_ring_num; i++) { - struct mlx4_en_cq *cq = &priv->rx_cq[i]; + struct mlx4_en_cq *cq = priv->rx_cq[i]; local_bh_disable(); while (!mlx4_en_cq_lock_napi(cq)) { @@ -1749,7 +1767,7 @@ void mlx4_en_stop_port(struct net_device *dev, int detach) while (test_bit(NAPI_STATE_SCHED, &cq->napi.state)) msleep(1); - mlx4_en_deactivate_rx_ring(priv, &priv->rx_ring[i]); + mlx4_en_deactivate_rx_ring(priv, priv->rx_ring[i]); mlx4_en_deactivate_cq(priv, cq); } } @@ -1787,15 +1805,15 @@ static void mlx4_en_clear_stats(struct net_device *dev) memset(&priv->port_stats, 0, sizeof(priv->port_stats)); for (i = 0; i < priv->tx_ring_num; i++) { - priv->tx_ring[i].bytes = 0; - priv->tx_ring[i].packets = 0; - priv->tx_ring[i].tx_csum = 0; + priv->tx_ring[i]->bytes = 0; + priv->tx_ring[i]->packets = 0; + priv->tx_ring[i]->tx_csum = 0; } for (i = 0; i < priv->rx_ring_num; i++) { - priv->rx_ring[i].bytes = 0; - priv->rx_ring[i].packets = 0; - priv->rx_ring[i].csum_ok = 0; - priv->rx_ring[i].csum_none = 0; + priv->rx_ring[i]->bytes = 0; + priv->rx_ring[i]->packets = 0; + priv->rx_ring[i]->csum_ok = 0; + priv->rx_ring[i]->csum_none = 0; } } @@ -1852,17 +1870,17 @@ void mlx4_en_free_resources(struct mlx4_en_priv *priv) #endif for (i = 0; i < priv->tx_ring_num; i++) { - if (priv->tx_ring[i].tx_info) + if (priv->tx_ring && priv->tx_ring[i]) mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]); - if (priv->tx_cq[i].buf) + if (priv->tx_cq && priv->tx_cq[i]) mlx4_en_destroy_cq(priv, &priv->tx_cq[i]); } for (i = 0; i < priv->rx_ring_num; i++) { - if (priv->rx_ring[i].rx_info) + if (priv->rx_ring[i]) mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i], priv->prof->rx_ring_size, priv->stride); - if (priv->rx_cq[i].buf) + if (priv->rx_cq[i]) mlx4_en_destroy_cq(priv, &priv->rx_cq[i]); } @@ -1877,6 +1895,7 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv) struct mlx4_en_port_profile *prof = priv->prof; int i; int err; + int node; err = mlx4_qp_reserve_range(priv->mdev->dev, priv->tx_ring_num, 256, &priv->base_tx_qpn); if (err) { @@ -1886,23 +1905,26 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv) /* Create tx Rings */ for (i = 0; i < priv->tx_ring_num; i++) { + node = cpu_to_node(i % num_online_cpus()); if (mlx4_en_create_cq(priv, &priv->tx_cq[i], - prof->tx_ring_size, i, TX)) + prof->tx_ring_size, i, TX, node)) goto err; if (mlx4_en_create_tx_ring(priv, &priv->tx_ring[i], priv->base_tx_qpn + i, - prof->tx_ring_size, TXBB_SIZE)) + prof->tx_ring_size, TXBB_SIZE, node)) goto err; } /* Create rx Rings */ for (i = 0; i < priv->rx_ring_num; i++) { + node = cpu_to_node(i % num_online_cpus()); if (mlx4_en_create_cq(priv, &priv->rx_cq[i], - prof->rx_ring_size, i, RX)) + prof->rx_ring_size, i, RX, node)) goto err; if (mlx4_en_create_rx_ring(priv, &priv->rx_ring[i], - prof->rx_ring_size, priv->stride)) + prof->rx_ring_size, priv->stride, + node)) goto err; } @@ -1918,6 +1940,20 @@ int mlx4_en_alloc_resources(struct mlx4_en_priv *priv) err: en_err(priv, "Failed to allocate NIC resources\n"); + for (i = 0; i < priv->rx_ring_num; i++) { + if (priv->rx_ring[i]) + mlx4_en_destroy_rx_ring(priv, &priv->rx_ring[i], + prof->rx_ring_size, + priv->stride); + if (priv->rx_cq[i]) + mlx4_en_destroy_cq(priv, &priv->rx_cq[i]); + } + for (i = 0; i < priv->tx_ring_num; i++) { + if (priv->tx_ring[i]) + mlx4_en_destroy_tx_ring(priv, &priv->tx_ring[i]); + if (priv->tx_cq[i]) + mlx4_en_destroy_cq(priv, &priv->tx_cq[i]); + } return -ENOMEM; } @@ -2211,13 +2247,13 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, priv->num_tx_rings_p_up = mdev->profile.num_tx_rings_p_up; priv->tx_ring_num = prof->tx_ring_num; - priv->tx_ring = kzalloc(sizeof(struct mlx4_en_tx_ring) * MAX_TX_RINGS, + priv->tx_ring = kzalloc(sizeof(struct mlx4_en_tx_ring *) * MAX_TX_RINGS, GFP_KERNEL); if (!priv->tx_ring) { err = -ENOMEM; goto out; } - priv->tx_cq = kzalloc(sizeof(struct mlx4_en_cq) * MAX_TX_RINGS, + priv->tx_cq = kzalloc(sizeof(struct mlx4_en_cq *) * MAX_TX_RINGS, GFP_KERNEL); if (!priv->tx_cq) { err = -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_port.c b/drivers/net/ethernet/mellanox/mlx4/en_port.c index 331791467a221c327104dbd9ae1ac53a2a950a88..dae1a1f4ae55e38287e6bcb5ff1bc5cb73435808 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_port.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_port.c @@ -56,7 +56,6 @@ int mlx4_SET_VLAN_FLTR(struct mlx4_dev *dev, struct mlx4_en_priv *priv) return PTR_ERR(mailbox); filter = mailbox->buf; - memset(filter, 0, sizeof(*filter)); for (i = VLAN_FLTR_SIZE - 1; i >= 0; i--) { entry = 0; for (j = 0; j < 32; j++) @@ -81,7 +80,6 @@ int mlx4_en_QUERY_PORT(struct mlx4_en_dev *mdev, u8 port) mailbox = mlx4_alloc_cmd_mailbox(mdev->dev); if (IS_ERR(mailbox)) return PTR_ERR(mailbox); - memset(mailbox->buf, 0, sizeof(*qport_context)); err = mlx4_cmd_box(mdev->dev, 0, mailbox->dma, port, 0, MLX4_CMD_QUERY_PORT, MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); @@ -127,7 +125,6 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) mailbox = mlx4_alloc_cmd_mailbox(mdev->dev); if (IS_ERR(mailbox)) return PTR_ERR(mailbox); - memset(mailbox->buf, 0, sizeof(*mlx4_en_stats)); err = mlx4_cmd_box(mdev->dev, 0, mailbox->dma, in_mod, 0, MLX4_CMD_DUMP_ETH_STATS, MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); @@ -143,18 +140,18 @@ int mlx4_en_DUMP_ETH_STATS(struct mlx4_en_dev *mdev, u8 port, u8 reset) priv->port_stats.rx_chksum_good = 0; priv->port_stats.rx_chksum_none = 0; for (i = 0; i < priv->rx_ring_num; i++) { - stats->rx_packets += priv->rx_ring[i].packets; - stats->rx_bytes += priv->rx_ring[i].bytes; - priv->port_stats.rx_chksum_good += priv->rx_ring[i].csum_ok; - priv->port_stats.rx_chksum_none += priv->rx_ring[i].csum_none; + stats->rx_packets += priv->rx_ring[i]->packets; + stats->rx_bytes += priv->rx_ring[i]->bytes; + priv->port_stats.rx_chksum_good += priv->rx_ring[i]->csum_ok; + priv->port_stats.rx_chksum_none += priv->rx_ring[i]->csum_none; } stats->tx_packets = 0; stats->tx_bytes = 0; priv->port_stats.tx_chksum_offload = 0; for (i = 0; i < priv->tx_ring_num; i++) { - stats->tx_packets += priv->tx_ring[i].packets; - stats->tx_bytes += priv->tx_ring[i].bytes; - priv->port_stats.tx_chksum_offload += priv->tx_ring[i].tx_csum; + stats->tx_packets += priv->tx_ring[i]->packets; + stats->tx_bytes += priv->tx_ring[i]->bytes; + priv->port_stats.tx_chksum_offload += priv->tx_ring[i]->tx_csum; } stats->rx_errors = be64_to_cpu(mlx4_en_stats->PCS) + diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index afe2efa69c8683647766a8d5dc7d561f3f76c2df..07a1d0fbae4706a6718406158c2358a8eb62da9b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -264,7 +264,7 @@ static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv) for (buf_ind = 0; buf_ind < priv->prof->rx_ring_size; buf_ind++) { for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) { - ring = &priv->rx_ring[ring_ind]; + ring = priv->rx_ring[ring_ind]; if (mlx4_en_prepare_rx_desc(priv, ring, ring->actual_size, @@ -289,7 +289,7 @@ static int mlx4_en_fill_rx_buffers(struct mlx4_en_priv *priv) reduce_rings: for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) { - ring = &priv->rx_ring[ring_ind]; + ring = priv->rx_ring[ring_ind]; while (ring->actual_size > new_size) { ring->actual_size--; ring->prod--; @@ -319,12 +319,23 @@ static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv, } int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring, u32 size, u16 stride) + struct mlx4_en_rx_ring **pring, + u32 size, u16 stride, int node) { struct mlx4_en_dev *mdev = priv->mdev; + struct mlx4_en_rx_ring *ring; int err = -ENOMEM; int tmp; + ring = kzalloc_node(sizeof(*ring), GFP_KERNEL, node); + if (!ring) { + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) { + en_err(priv, "Failed to allocate RX ring structure\n"); + return -ENOMEM; + } + } + ring->prod = 0; ring->cons = 0; ring->size = size; @@ -335,17 +346,25 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, tmp = size * roundup_pow_of_two(MLX4_EN_MAX_RX_FRAGS * sizeof(struct mlx4_en_rx_alloc)); - ring->rx_info = vmalloc(tmp); - if (!ring->rx_info) - return -ENOMEM; + ring->rx_info = vmalloc_node(tmp, node); + if (!ring->rx_info) { + ring->rx_info = vmalloc(tmp); + if (!ring->rx_info) { + err = -ENOMEM; + goto err_ring; + } + } en_dbg(DRV, priv, "Allocated rx_info ring at addr:%p size:%d\n", ring->rx_info, tmp); + /* Allocate HW buffers on provided NUMA node */ + set_dev_node(&mdev->dev->pdev->dev, node); err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size, 2 * PAGE_SIZE); + set_dev_node(&mdev->dev->pdev->dev, mdev->dev->numa_node); if (err) - goto err_ring; + goto err_info; err = mlx4_en_map_buffer(&ring->wqres.buf); if (err) { @@ -356,13 +375,18 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, ring->hwtstamp_rx_filter = priv->hwtstamp_config.rx_filter; + *pring = ring; return 0; err_hwq: mlx4_free_hwq_res(mdev->dev, &ring->wqres, ring->buf_size); -err_ring: +err_info: vfree(ring->rx_info); ring->rx_info = NULL; +err_ring: + kfree(ring); + *pring = NULL; + return err; } @@ -376,12 +400,12 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv) DS_SIZE * priv->num_frags); for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) { - ring = &priv->rx_ring[ring_ind]; + ring = priv->rx_ring[ring_ind]; ring->prod = 0; ring->cons = 0; ring->actual_size = 0; - ring->cqn = priv->rx_cq[ring_ind].mcq.cqn; + ring->cqn = priv->rx_cq[ring_ind]->mcq.cqn; ring->stride = stride; if (ring->stride <= TXBB_SIZE) @@ -412,7 +436,7 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv) goto err_buffers; for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) { - ring = &priv->rx_ring[ring_ind]; + ring = priv->rx_ring[ring_ind]; ring->size_mask = ring->actual_size - 1; mlx4_en_update_rx_prod_db(ring); @@ -422,30 +446,34 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv) err_buffers: for (ring_ind = 0; ring_ind < priv->rx_ring_num; ring_ind++) - mlx4_en_free_rx_buf(priv, &priv->rx_ring[ring_ind]); + mlx4_en_free_rx_buf(priv, priv->rx_ring[ring_ind]); ring_ind = priv->rx_ring_num - 1; err_allocator: while (ring_ind >= 0) { - if (priv->rx_ring[ring_ind].stride <= TXBB_SIZE) - priv->rx_ring[ring_ind].buf -= TXBB_SIZE; - mlx4_en_destroy_allocator(priv, &priv->rx_ring[ring_ind]); + if (priv->rx_ring[ring_ind]->stride <= TXBB_SIZE) + priv->rx_ring[ring_ind]->buf -= TXBB_SIZE; + mlx4_en_destroy_allocator(priv, priv->rx_ring[ring_ind]); ring_ind--; } return err; } void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring, u32 size, u16 stride) + struct mlx4_en_rx_ring **pring, + u32 size, u16 stride) { struct mlx4_en_dev *mdev = priv->mdev; + struct mlx4_en_rx_ring *ring = *pring; mlx4_en_unmap_buffer(&ring->wqres.buf); mlx4_free_hwq_res(mdev->dev, &ring->wqres, size * stride + TXBB_SIZE); vfree(ring->rx_info); ring->rx_info = NULL; + kfree(ring); + *pring = NULL; #ifdef CONFIG_RFS_ACCEL - mlx4_en_cleanup_filters(priv, ring); + mlx4_en_cleanup_filters(priv); #endif } @@ -592,7 +620,7 @@ int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int bud struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_en_dev *mdev = priv->mdev; struct mlx4_cqe *cqe; - struct mlx4_en_rx_ring *ring = &priv->rx_ring[cq->ring]; + struct mlx4_en_rx_ring *ring = priv->rx_ring[cq->ring]; struct mlx4_en_rx_alloc *frags; struct mlx4_en_rx_desc *rx_desc; struct sk_buff *skb; @@ -991,7 +1019,7 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) for (i = 0; i < priv->rx_ring_num; i++) { qpn = rss_map->base_qpn + i; - err = mlx4_en_config_rss_qp(priv, qpn, &priv->rx_ring[i], + err = mlx4_en_config_rss_qp(priv, qpn, priv->rx_ring[i], &rss_map->state[i], &rss_map->qps[i]); if (err) @@ -1008,7 +1036,7 @@ int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv) } rss_map->indir_qp.event = mlx4_en_sqp_event; mlx4_en_fill_qp_context(priv, 0, 0, 0, 1, priv->base_qpn, - priv->rx_ring[0].cqn, -1, &context); + priv->rx_ring[0]->cqn, -1, &context); if (!priv->prof->rss_rings || priv->prof->rss_rings > priv->rx_ring_num) rss_rings = priv->rx_ring_num; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c index 2448f0d669e6cadd19ca06b5d820097ca6cf80fe..40626690e8a8679d9812d4edee4ede897ec55a2a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_selftest.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_selftest.c @@ -156,7 +156,7 @@ retry_tx: * since we turned the carrier off */ msleep(200); for (i = 0; i < priv->tx_ring_num && carrier_ok; i++) { - tx_ring = &priv->tx_ring[i]; + tx_ring = priv->tx_ring[i]; if (tx_ring->prod != (tx_ring->cons + tx_ring->last_nr_txbb)) goto retry_tx; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index 0698c82d6ff1bf94598b4ed577b446289c4178be..f54ebd5a1702457524bf5fc6918fbdf191a5d73f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -54,13 +54,23 @@ module_param_named(inline_thold, inline_thold, int, 0444); MODULE_PARM_DESC(inline_thold, "threshold for using inline data"); int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_tx_ring *ring, int qpn, u32 size, - u16 stride) + struct mlx4_en_tx_ring **pring, int qpn, u32 size, + u16 stride, int node) { struct mlx4_en_dev *mdev = priv->mdev; + struct mlx4_en_tx_ring *ring; int tmp; int err; + ring = kzalloc_node(sizeof(*ring), GFP_KERNEL, node); + if (!ring) { + ring = kzalloc(sizeof(*ring), GFP_KERNEL); + if (!ring) { + en_err(priv, "Failed allocating TX ring\n"); + return -ENOMEM; + } + } + ring->size = size; ring->size_mask = size - 1; ring->stride = stride; @@ -68,22 +78,33 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, inline_thold = min(inline_thold, MAX_INLINE); tmp = size * sizeof(struct mlx4_en_tx_info); - ring->tx_info = vmalloc(tmp); - if (!ring->tx_info) - return -ENOMEM; + ring->tx_info = vmalloc_node(tmp, node); + if (!ring->tx_info) { + ring->tx_info = vmalloc(tmp); + if (!ring->tx_info) { + err = -ENOMEM; + goto err_ring; + } + } en_dbg(DRV, priv, "Allocated tx_info ring at addr:%p size:%d\n", ring->tx_info, tmp); - ring->bounce_buf = kmalloc(MAX_DESC_SIZE, GFP_KERNEL); + ring->bounce_buf = kmalloc_node(MAX_DESC_SIZE, GFP_KERNEL, node); if (!ring->bounce_buf) { - err = -ENOMEM; - goto err_tx; + ring->bounce_buf = kmalloc(MAX_DESC_SIZE, GFP_KERNEL); + if (!ring->bounce_buf) { + err = -ENOMEM; + goto err_info; + } } ring->buf_size = ALIGN(size * ring->stride, MLX4_EN_PAGE_SIZE); + /* Allocate HW buffers on provided NUMA node */ + set_dev_node(&mdev->dev->pdev->dev, node); err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size, 2 * PAGE_SIZE); + set_dev_node(&mdev->dev->pdev->dev, mdev->dev->numa_node); if (err) { en_err(priv, "Failed allocating hwq resources\n"); goto err_bounce; @@ -109,7 +130,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, } ring->qp.event = mlx4_en_sqp_event; - err = mlx4_bf_alloc(mdev->dev, &ring->bf); + err = mlx4_bf_alloc(mdev->dev, &ring->bf, node); if (err) { en_dbg(DRV, priv, "working without blueflame (%d)", err); ring->bf.uar = &mdev->priv_uar; @@ -120,6 +141,7 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, ring->hwtstamp_tx_type = priv->hwtstamp_config.tx_type; + *pring = ring; return 0; err_map: @@ -129,16 +151,20 @@ err_hwq_res: err_bounce: kfree(ring->bounce_buf); ring->bounce_buf = NULL; -err_tx: +err_info: vfree(ring->tx_info); ring->tx_info = NULL; +err_ring: + kfree(ring); + *pring = NULL; return err; } void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_tx_ring *ring) + struct mlx4_en_tx_ring **pring) { struct mlx4_en_dev *mdev = priv->mdev; + struct mlx4_en_tx_ring *ring = *pring; en_dbg(DRV, priv, "Destroying tx ring, qpn: %d\n", ring->qpn); if (ring->bf_enabled) @@ -151,6 +177,8 @@ void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, ring->bounce_buf = NULL; vfree(ring->tx_info); ring->tx_info = NULL; + kfree(ring); + *pring = NULL; } int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, @@ -330,7 +358,7 @@ static void mlx4_en_process_tx_cq(struct net_device *dev, struct mlx4_en_cq *cq) { struct mlx4_en_priv *priv = netdev_priv(dev); struct mlx4_cq *mcq = &cq->mcq; - struct mlx4_en_tx_ring *ring = &priv->tx_ring[cq->ring]; + struct mlx4_en_tx_ring *ring = priv->tx_ring[cq->ring]; struct mlx4_cqe *cqe; u16 index; u16 new_index, ring_index, stamp_index; @@ -622,7 +650,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) } tx_ind = skb->queue_mapping; - ring = &priv->tx_ring[tx_ind]; + ring = priv->tx_ring[tx_ind]; if (vlan_tx_tag_present(skb)) vlan_tag = vlan_tx_tag_get(skb); diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 0416c5b3b35cc721c9e312e6b7b2ca0c0917cbf7..c9cdb2a2c596e889ec5f2ae67a3b8912e0f687eb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -936,7 +936,6 @@ static int mlx4_create_eq(struct mlx4_dev *dev, int nent, if (err) goto err_out_free_mtt; - memset(eq_context, 0, sizeof *eq_context); eq_context->flags = cpu_to_be32(MLX4_EQ_STATUS_OK | MLX4_EQ_STATE_ARMED); eq_context->log_eq_size = ilog2(eq->nent); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 0d63daa2f422e082d85fd627a892c9fb5f5c1945..fda26679f7d5c8cac8780c55a470357a2f16ec01 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -159,8 +159,6 @@ int mlx4_MOD_STAT_CFG(struct mlx4_dev *dev, struct mlx4_mod_stat_cfg *cfg) return PTR_ERR(mailbox); inbox = mailbox->buf; - memset(inbox, 0, MOD_STAT_CFG_IN_SIZE); - MLX4_PUT(inbox, cfg->log_pg_sz, MOD_STAT_CFG_PG_SZ_OFFSET); MLX4_PUT(inbox, cfg->log_pg_sz_m, MOD_STAT_CFG_PG_SZ_M_OFFSET); @@ -177,6 +175,7 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, struct mlx4_cmd_mailbox *outbox, struct mlx4_cmd_info *cmd) { + struct mlx4_priv *priv = mlx4_priv(dev); u8 field; u32 size; int err = 0; @@ -185,18 +184,26 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, #define QUERY_FUNC_CAP_NUM_PORTS_OFFSET 0x1 #define QUERY_FUNC_CAP_PF_BHVR_OFFSET 0x4 #define QUERY_FUNC_CAP_FMR_OFFSET 0x8 -#define QUERY_FUNC_CAP_QP_QUOTA_OFFSET 0x10 -#define QUERY_FUNC_CAP_CQ_QUOTA_OFFSET 0x14 -#define QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET 0x18 -#define QUERY_FUNC_CAP_MPT_QUOTA_OFFSET 0x20 -#define QUERY_FUNC_CAP_MTT_QUOTA_OFFSET 0x24 -#define QUERY_FUNC_CAP_MCG_QUOTA_OFFSET 0x28 +#define QUERY_FUNC_CAP_QP_QUOTA_OFFSET_DEP 0x10 +#define QUERY_FUNC_CAP_CQ_QUOTA_OFFSET_DEP 0x14 +#define QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET_DEP 0x18 +#define QUERY_FUNC_CAP_MPT_QUOTA_OFFSET_DEP 0x20 +#define QUERY_FUNC_CAP_MTT_QUOTA_OFFSET_DEP 0x24 +#define QUERY_FUNC_CAP_MCG_QUOTA_OFFSET_DEP 0x28 #define QUERY_FUNC_CAP_MAX_EQ_OFFSET 0x2c #define QUERY_FUNC_CAP_RESERVED_EQ_OFFSET 0x30 +#define QUERY_FUNC_CAP_QP_QUOTA_OFFSET 0x50 +#define QUERY_FUNC_CAP_CQ_QUOTA_OFFSET 0x54 +#define QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET 0x58 +#define QUERY_FUNC_CAP_MPT_QUOTA_OFFSET 0x60 +#define QUERY_FUNC_CAP_MTT_QUOTA_OFFSET 0x64 +#define QUERY_FUNC_CAP_MCG_QUOTA_OFFSET 0x68 + #define QUERY_FUNC_CAP_FMR_FLAG 0x80 #define QUERY_FUNC_CAP_FLAG_RDMA 0x40 #define QUERY_FUNC_CAP_FLAG_ETH 0x80 +#define QUERY_FUNC_CAP_FLAG_QUOTAS 0x10 /* when opcode modifier = 1 */ #define QUERY_FUNC_CAP_PHYS_PORT_OFFSET 0x3 @@ -237,8 +244,9 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_QP1_PROXY); } else if (vhcr->op_modifier == 0) { - /* enable rdma and ethernet interfaces */ - field = (QUERY_FUNC_CAP_FLAG_ETH | QUERY_FUNC_CAP_FLAG_RDMA); + /* enable rdma and ethernet interfaces, and new quota locations */ + field = (QUERY_FUNC_CAP_FLAG_ETH | QUERY_FUNC_CAP_FLAG_RDMA | + QUERY_FUNC_CAP_FLAG_QUOTAS); MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_FLAGS_OFFSET); field = dev->caps.num_ports; @@ -250,14 +258,20 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, field = 0; /* protected FMR support not available as yet */ MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_FMR_OFFSET); - size = dev->caps.num_qps; + size = priv->mfunc.master.res_tracker.res_alloc[RES_QP].quota[slave]; MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_QP_QUOTA_OFFSET); + size = dev->caps.num_qps; + MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_QP_QUOTA_OFFSET_DEP); - size = dev->caps.num_srqs; + size = priv->mfunc.master.res_tracker.res_alloc[RES_SRQ].quota[slave]; MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET); + size = dev->caps.num_srqs; + MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET_DEP); - size = dev->caps.num_cqs; + size = priv->mfunc.master.res_tracker.res_alloc[RES_CQ].quota[slave]; MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET); + size = dev->caps.num_cqs; + MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET_DEP); size = dev->caps.num_eqs; MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MAX_EQ_OFFSET); @@ -265,14 +279,19 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, size = dev->caps.reserved_eqs; MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_RESERVED_EQ_OFFSET); - size = dev->caps.num_mpts; + size = priv->mfunc.master.res_tracker.res_alloc[RES_MPT].quota[slave]; MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET); + size = dev->caps.num_mpts; + MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET_DEP); - size = dev->caps.num_mtts; + size = priv->mfunc.master.res_tracker.res_alloc[RES_MTT].quota[slave]; MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET); + size = dev->caps.num_mtts; + MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET_DEP); size = dev->caps.num_mgms + dev->caps.num_amgms; MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET); + MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET_DEP); } else err = -EINVAL; @@ -287,7 +306,7 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u32 gen_or_port, u32 *outbox; u8 field, op_modifier; u32 size; - int err = 0; + int err = 0, quotas = 0; op_modifier = !!gen_or_port; /* 0 = general, 1 = logical port */ @@ -311,6 +330,7 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u32 gen_or_port, goto out; } func_cap->flags = field; + quotas = !!(func_cap->flags & QUERY_FUNC_CAP_FLAG_QUOTAS); MLX4_GET(field, outbox, QUERY_FUNC_CAP_NUM_PORTS_OFFSET); func_cap->num_ports = field; @@ -318,29 +338,50 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u32 gen_or_port, MLX4_GET(size, outbox, QUERY_FUNC_CAP_PF_BHVR_OFFSET); func_cap->pf_context_behaviour = size; - MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP_QUOTA_OFFSET); - func_cap->qp_quota = size & 0xFFFFFF; + if (quotas) { + MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP_QUOTA_OFFSET); + func_cap->qp_quota = size & 0xFFFFFF; - MLX4_GET(size, outbox, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET); - func_cap->srq_quota = size & 0xFFFFFF; + MLX4_GET(size, outbox, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET); + func_cap->srq_quota = size & 0xFFFFFF; - MLX4_GET(size, outbox, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET); - func_cap->cq_quota = size & 0xFFFFFF; + MLX4_GET(size, outbox, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET); + func_cap->cq_quota = size & 0xFFFFFF; + MLX4_GET(size, outbox, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET); + func_cap->mpt_quota = size & 0xFFFFFF; + + MLX4_GET(size, outbox, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET); + func_cap->mtt_quota = size & 0xFFFFFF; + + MLX4_GET(size, outbox, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET); + func_cap->mcg_quota = size & 0xFFFFFF; + + } else { + MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP_QUOTA_OFFSET_DEP); + func_cap->qp_quota = size & 0xFFFFFF; + + MLX4_GET(size, outbox, QUERY_FUNC_CAP_SRQ_QUOTA_OFFSET_DEP); + func_cap->srq_quota = size & 0xFFFFFF; + + MLX4_GET(size, outbox, QUERY_FUNC_CAP_CQ_QUOTA_OFFSET_DEP); + func_cap->cq_quota = size & 0xFFFFFF; + + MLX4_GET(size, outbox, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET_DEP); + func_cap->mpt_quota = size & 0xFFFFFF; + + MLX4_GET(size, outbox, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET_DEP); + func_cap->mtt_quota = size & 0xFFFFFF; + + MLX4_GET(size, outbox, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET_DEP); + func_cap->mcg_quota = size & 0xFFFFFF; + } MLX4_GET(size, outbox, QUERY_FUNC_CAP_MAX_EQ_OFFSET); func_cap->max_eq = size & 0xFFFFFF; MLX4_GET(size, outbox, QUERY_FUNC_CAP_RESERVED_EQ_OFFSET); func_cap->reserved_eq = size & 0xFFFFFF; - MLX4_GET(size, outbox, QUERY_FUNC_CAP_MPT_QUOTA_OFFSET); - func_cap->mpt_quota = size & 0xFFFFFF; - - MLX4_GET(size, outbox, QUERY_FUNC_CAP_MTT_QUOTA_OFFSET); - func_cap->mtt_quota = size & 0xFFFFFF; - - MLX4_GET(size, outbox, QUERY_FUNC_CAP_MCG_QUOTA_OFFSET); - func_cap->mcg_quota = size & 0xFFFFFF; goto out; } @@ -652,7 +693,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) QUERY_DEV_CAP_RSVD_LKEY_OFFSET); MLX4_GET(field, outbox, QUERY_DEV_CAP_FW_REASSIGN_MAC); if (field & 1<<6) - dev_cap->flags2 |= MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN; + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_REASSIGN_MAC_EN; MLX4_GET(dev_cap->max_icm_sz, outbox, QUERY_DEV_CAP_MAX_ICM_SZ_OFFSET); if (dev_cap->flags & MLX4_DEV_CAP_FLAG_COUNTERS) @@ -924,7 +965,6 @@ int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt) mailbox = mlx4_alloc_cmd_mailbox(dev); if (IS_ERR(mailbox)) return PTR_ERR(mailbox); - memset(mailbox->buf, 0, MLX4_MAILBOX_SIZE); pages = mailbox->buf; for (mlx4_icm_first(icm, &iter); @@ -1273,8 +1313,6 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) return PTR_ERR(mailbox); inbox = mailbox->buf; - memset(inbox, 0, INIT_HCA_IN_SIZE); - *((u8 *) mailbox->buf + INIT_HCA_VERSION_OFFSET) = INIT_HCA_VERSION; *((u8 *) mailbox->buf + INIT_HCA_CACHELINE_SZ_OFFSET) = @@ -1573,8 +1611,6 @@ int mlx4_INIT_PORT(struct mlx4_dev *dev, int port) return PTR_ERR(mailbox); inbox = mailbox->buf; - memset(inbox, 0, INIT_PORT_IN_SIZE); - flags = 0; flags |= (dev->caps.vl_cap[port] & 0xf) << INIT_PORT_VL_SHIFT; flags |= (dev->caps.port_width_cap[port] & 0xf) << INIT_PORT_PORT_WIDTH_SHIFT; @@ -1713,7 +1749,6 @@ void mlx4_opreq_action(struct work_struct *work) u32 *outbox; u32 modifier; u16 token; - u16 type_m; u16 type; int err; u32 num_qps; @@ -1746,7 +1781,6 @@ void mlx4_opreq_action(struct work_struct *work) MLX4_GET(modifier, outbox, GET_OP_REQ_MODIFIER_OFFSET); MLX4_GET(token, outbox, GET_OP_REQ_TOKEN_OFFSET); MLX4_GET(type, outbox, GET_OP_REQ_TYPE_OFFSET); - type_m = type >> 12; type &= 0xfff; switch (type) { diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c index 31d02649be41f2317d16f178f88bc1f19153352a..5fbf4924c2729e0dd3666a59ab64386ddf76b4c9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.c +++ b/drivers/net/ethernet/mellanox/mlx4/icm.c @@ -93,13 +93,17 @@ void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent) kfree(icm); } -static int mlx4_alloc_icm_pages(struct scatterlist *mem, int order, gfp_t gfp_mask) +static int mlx4_alloc_icm_pages(struct scatterlist *mem, int order, + gfp_t gfp_mask, int node) { struct page *page; - page = alloc_pages(gfp_mask, order); - if (!page) - return -ENOMEM; + page = alloc_pages_node(node, gfp_mask, order); + if (!page) { + page = alloc_pages(gfp_mask, order); + if (!page) + return -ENOMEM; + } sg_set_page(mem, page, PAGE_SIZE << order, 0); return 0; @@ -130,9 +134,15 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, /* We use sg_set_buf for coherent allocs, which assumes low memory */ BUG_ON(coherent && (gfp_mask & __GFP_HIGHMEM)); - icm = kmalloc(sizeof *icm, gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); - if (!icm) - return NULL; + icm = kmalloc_node(sizeof(*icm), + gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN), + dev->numa_node); + if (!icm) { + icm = kmalloc(sizeof(*icm), + gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); + if (!icm) + return NULL; + } icm->refcount = 0; INIT_LIST_HEAD(&icm->chunk_list); @@ -141,10 +151,17 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, while (npages > 0) { if (!chunk) { - chunk = kmalloc(sizeof *chunk, - gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); - if (!chunk) - goto fail; + chunk = kmalloc_node(sizeof(*chunk), + gfp_mask & ~(__GFP_HIGHMEM | + __GFP_NOWARN), + dev->numa_node); + if (!chunk) { + chunk = kmalloc(sizeof(*chunk), + gfp_mask & ~(__GFP_HIGHMEM | + __GFP_NOWARN)); + if (!chunk) + goto fail; + } sg_init_table(chunk->mem, MLX4_ICM_CHUNK_LEN); chunk->npages = 0; @@ -161,7 +178,8 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, cur_order, gfp_mask); else ret = mlx4_alloc_icm_pages(&chunk->mem[chunk->npages], - cur_order, gfp_mask); + cur_order, gfp_mask, + dev->numa_node); if (ret) { if (--cur_order < 0) diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 60c9f4f103fce1a2c7d815ccc303b7b94805068c..5789ea2c934d4aaa15218bdaea2d565f42a82b41 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -561,13 +562,17 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) } dev->caps.num_ports = func_cap.num_ports; - dev->caps.num_qps = func_cap.qp_quota; - dev->caps.num_srqs = func_cap.srq_quota; - dev->caps.num_cqs = func_cap.cq_quota; - dev->caps.num_eqs = func_cap.max_eq; - dev->caps.reserved_eqs = func_cap.reserved_eq; - dev->caps.num_mpts = func_cap.mpt_quota; - dev->caps.num_mtts = func_cap.mtt_quota; + dev->quotas.qp = func_cap.qp_quota; + dev->quotas.srq = func_cap.srq_quota; + dev->quotas.cq = func_cap.cq_quota; + dev->quotas.mpt = func_cap.mpt_quota; + dev->quotas.mtt = func_cap.mtt_quota; + dev->caps.num_qps = 1 << hca_param.log_num_qps; + dev->caps.num_srqs = 1 << hca_param.log_num_srqs; + dev->caps.num_cqs = 1 << hca_param.log_num_cqs; + dev->caps.num_mpts = 1 << hca_param.log_mpt_sz; + dev->caps.num_eqs = func_cap.max_eq; + dev->caps.reserved_eqs = func_cap.reserved_eq; dev->caps.num_pds = MLX4_NUM_PDS; dev->caps.num_mgms = 0; dev->caps.num_amgms = 0; @@ -650,6 +655,27 @@ err_mem: return err; } +static void mlx4_request_modules(struct mlx4_dev *dev) +{ + int port; + int has_ib_port = false; + int has_eth_port = false; +#define EN_DRV_NAME "mlx4_en" +#define IB_DRV_NAME "mlx4_ib" + + for (port = 1; port <= dev->caps.num_ports; port++) { + if (dev->caps.port_type[port] == MLX4_PORT_TYPE_IB) + has_ib_port = true; + else if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) + has_eth_port = true; + } + + if (has_ib_port) + request_module_nowait(IB_DRV_NAME); + if (has_eth_port) + request_module_nowait(EN_DRV_NAME); +} + /* * Change the port configuration of the device. * Every user of this function must hold the port mutex. @@ -681,6 +707,11 @@ int mlx4_change_port_types(struct mlx4_dev *dev, } mlx4_set_port_mask(dev); err = mlx4_register_device(dev); + if (err) { + mlx4_err(dev, "Failed to register device\n"); + goto out; + } + mlx4_request_modules(dev); } out: @@ -2075,9 +2106,15 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data) "aborting.\n"); return err; } - if (num_vfs > MLX4_MAX_NUM_VF) { - printk(KERN_ERR "There are more VF's (%d) than allowed(%d)\n", - num_vfs, MLX4_MAX_NUM_VF); + + /* Due to requirement that all VFs and the PF are *guaranteed* 2 MACS + * per port, we must limit the number of VFs to 63 (since their are + * 128 MACs) + */ + if (num_vfs >= MLX4_MAX_NUM_VF) { + dev_err(&pdev->dev, + "Requested more VF's (%d) than allowed (%d)\n", + num_vfs, MLX4_MAX_NUM_VF - 1); return -EINVAL; } @@ -2154,6 +2191,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data) mutex_init(&priv->bf_mutex); dev->rev_id = pdev->revision; + dev->numa_node = dev_to_node(&pdev->dev); /* Detect if this device is a virtual function */ if (pci_dev_data & MLX4_PCI_DEV_IS_VF) { /* When acting as pf, we normally skip vfs unless explicitly @@ -2295,6 +2333,8 @@ slave_start: if (err) goto err_steer; + mlx4_init_quotas(dev); + for (port = 1; port <= dev->caps.num_ports; port++) { err = mlx4_init_port_info(dev, port); if (err) @@ -2305,6 +2345,8 @@ slave_start: if (err) goto err_port; + mlx4_request_modules(dev); + mlx4_sense_init(dev); mlx4_start_sense(dev); diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 55f6245efb6cd250f2efda4e6941bdb81b1fd9f3..acf9d5f1f9223fbfa0e4efa1b9d886186a12c0cf 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -506,7 +506,6 @@ static int remove_promisc_qp(struct mlx4_dev *dev, u8 port, goto out_list; } mgm = mailbox->buf; - memset(mgm, 0, sizeof *mgm); members_count = 0; list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list) mgm->qp[members_count++] = cpu_to_be32(dqp->qpn & MGM_QPN_MASK); @@ -645,7 +644,7 @@ static const u8 __promisc_mode[] = { int mlx4_map_sw_to_hw_steering_mode(struct mlx4_dev *dev, enum mlx4_net_trans_promisc_mode flow_type) { - if (flow_type >= MLX4_FS_MODE_NUM || flow_type < 0) { + if (flow_type >= MLX4_FS_MODE_NUM) { mlx4_err(dev, "Invalid flow type. type = %d\n", flow_type); return -EINVAL; } @@ -681,7 +680,7 @@ const u16 __sw_id_hw[] = { int mlx4_map_sw_to_hw_steering_id(struct mlx4_dev *dev, enum mlx4_net_trans_rule_id id) { - if (id >= MLX4_NET_TRANS_RULE_NUM || id < 0) { + if (id >= MLX4_NET_TRANS_RULE_NUM) { mlx4_err(dev, "Invalid network rule id. id = %d\n", id); return -EINVAL; } @@ -706,7 +705,7 @@ static const int __rule_hw_sz[] = { int mlx4_hw_rule_sz(struct mlx4_dev *dev, enum mlx4_net_trans_rule_id id) { - if (id >= MLX4_NET_TRANS_RULE_NUM || id < 0) { + if (id >= MLX4_NET_TRANS_RULE_NUM) { mlx4_err(dev, "Invalid network rule id. id = %d\n", id); return -EINVAL; } @@ -857,7 +856,6 @@ int mlx4_flow_attach(struct mlx4_dev *dev, if (IS_ERR(mailbox)) return PTR_ERR(mailbox); - memset(mailbox->buf, 0, sizeof(struct mlx4_net_trans_rule_hw_ctrl)); trans_rule_ctrl_to_hw(rule, mailbox->buf); size += sizeof(struct mlx4_net_trans_rule_hw_ctrl); diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 348bb8c7d9a70bc58d1c51c4e7aca6ec495dcb8f..e582a41a802baeddba0aa9c68dd79113a1cd236e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -455,6 +455,7 @@ struct mlx4_slave_state { u8 last_cmd; u8 init_port_mask; bool active; + bool old_vlan_api; u8 function; dma_addr_t vhcr_dma; u16 mtu[MLX4_MAX_PORTS + 1]; @@ -503,12 +504,28 @@ struct slave_list { struct list_head res_list[MLX4_NUM_OF_RESOURCE_TYPE]; }; +struct resource_allocator { + spinlock_t alloc_lock; /* protect quotas */ + union { + int res_reserved; + int res_port_rsvd[MLX4_MAX_PORTS]; + }; + union { + int res_free; + int res_port_free[MLX4_MAX_PORTS]; + }; + int *quota; + int *allocated; + int *guaranteed; +}; + struct mlx4_resource_tracker { spinlock_t lock; /* tree for each resources */ struct rb_root res_tree[MLX4_NUM_OF_RESOURCE_TYPE]; /* num_of_slave's lists, one per slave */ struct slave_list *slave_list; + struct resource_allocator res_alloc[MLX4_NUM_OF_RESOURCE_TYPE]; }; #define SLAVE_EVENT_EQ_SIZE 128 @@ -1111,7 +1128,7 @@ int mlx4_change_port_types(struct mlx4_dev *dev, void mlx4_init_mac_table(struct mlx4_dev *dev, struct mlx4_mac_table *table); void mlx4_init_vlan_table(struct mlx4_dev *dev, struct mlx4_vlan_table *table); -void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index); +void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan); int __mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index); int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz); @@ -1252,4 +1269,6 @@ static inline spinlock_t *mlx4_tlock(struct mlx4_dev *dev) void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work); +void mlx4_init_quotas(struct mlx4_dev *dev); + #endif /* MLX4_H */ diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index bf06e3610d27787ced2f43b9f9aa9fef105aa3df..f3758de59c05f5adeb195661814ff4041b405c4a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -530,10 +530,10 @@ struct mlx4_en_priv { u16 num_frags; u16 log_rx_info; - struct mlx4_en_tx_ring *tx_ring; - struct mlx4_en_rx_ring rx_ring[MAX_RX_RINGS]; - struct mlx4_en_cq *tx_cq; - struct mlx4_en_cq rx_cq[MAX_RX_RINGS]; + struct mlx4_en_tx_ring **tx_ring; + struct mlx4_en_rx_ring *rx_ring[MAX_RX_RINGS]; + struct mlx4_en_cq **tx_cq; + struct mlx4_en_cq *rx_cq[MAX_RX_RINGS]; struct mlx4_qp drop_qp; struct work_struct rx_mode_task; struct work_struct watchdog_task; @@ -626,7 +626,7 @@ static inline bool mlx4_en_cq_lock_poll(struct mlx4_en_cq *cq) if ((cq->state & MLX4_CQ_LOCKED)) { struct net_device *dev = cq->dev; struct mlx4_en_priv *priv = netdev_priv(dev); - struct mlx4_en_rx_ring *rx_ring = &priv->rx_ring[cq->ring]; + struct mlx4_en_rx_ring *rx_ring = priv->rx_ring[cq->ring]; cq->state |= MLX4_EN_CQ_STATE_POLL_YIELD; rc = false; @@ -704,9 +704,9 @@ void mlx4_en_stop_port(struct net_device *dev, int detach); void mlx4_en_free_resources(struct mlx4_en_priv *priv); int mlx4_en_alloc_resources(struct mlx4_en_priv *priv); -int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, - int entries, int ring, enum cq_type mode); -void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); +int mlx4_en_create_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq, + int entries, int ring, enum cq_type mode, int node); +void mlx4_en_destroy_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq **pcq); int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, int cq_idx); void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); @@ -717,9 +717,11 @@ void mlx4_en_tx_irq(struct mlx4_cq *mcq); u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb); netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev); -int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, - int qpn, u32 size, u16 stride); -void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring); +int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, + struct mlx4_en_tx_ring **pring, + int qpn, u32 size, u16 stride, int node); +void mlx4_en_destroy_tx_ring(struct mlx4_en_priv *priv, + struct mlx4_en_tx_ring **pring); int mlx4_en_activate_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring, int cq, int user_prio); @@ -727,10 +729,10 @@ void mlx4_en_deactivate_tx_ring(struct mlx4_en_priv *priv, struct mlx4_en_tx_ring *ring); int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring, - u32 size, u16 stride); + struct mlx4_en_rx_ring **pring, + u32 size, u16 stride, int node); void mlx4_en_destroy_rx_ring(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring, + struct mlx4_en_rx_ring **pring, u32 size, u16 stride); int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv); void mlx4_en_deactivate_rx_ring(struct mlx4_en_priv *priv, @@ -768,8 +770,7 @@ extern const struct dcbnl_rtnl_ops mlx4_en_dcbnl_pfc_ops; int mlx4_en_setup_tc(struct net_device *dev, u8 up); #ifdef CONFIG_RFS_ACCEL -void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *rx_ring); +void mlx4_en_cleanup_filters(struct mlx4_en_priv *priv); #endif #define MLX4_EN_NUM_SELF_TEST 5 diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index f91719a08cbac000dc34f68f7d40252aa652ac58..b3ee9bafff5ecdb9f48a6ff526bd5a2d1912a37d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -480,9 +480,6 @@ int mlx4_mr_enable(struct mlx4_dev *dev, struct mlx4_mr *mr) goto err_table; } mpt_entry = mailbox->buf; - - memset(mpt_entry, 0, sizeof *mpt_entry); - mpt_entry->flags = cpu_to_be32(MLX4_MPT_FLAG_MIO | MLX4_MPT_FLAG_REGION | mr->access); @@ -695,8 +692,6 @@ int mlx4_mw_enable(struct mlx4_dev *dev, struct mlx4_mw *mw) } mpt_entry = mailbox->buf; - memset(mpt_entry, 0, sizeof(*mpt_entry)); - /* Note that the MLX4_MPT_FLAG_REGION bit in mpt_entry->flags is turned * off, thus creating a memory window and not a memory region. */ @@ -755,14 +750,14 @@ int mlx4_init_mr_table(struct mlx4_dev *dev) struct mlx4_mr_table *mr_table = &priv->mr_table; int err; - if (!is_power_of_2(dev->caps.num_mpts)) - return -EINVAL; - /* Nothing to do for slaves - all MR handling is forwarded * to the master */ if (mlx4_is_slave(dev)) return 0; + if (!is_power_of_2(dev->caps.num_mpts)) + return -EINVAL; + err = mlx4_bitmap_init(&mr_table->mpt_bitmap, dev->caps.num_mpts, ~0, dev->caps.reserved_mrws, 0); if (err) diff --git a/drivers/net/ethernet/mellanox/mlx4/pd.c b/drivers/net/ethernet/mellanox/mlx4/pd.c index 00f223acada79a8f0bf6b52ae21e75afbfb60ce0..84cfb40bf4513495d9997625e61d580a25c56d1f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/pd.c +++ b/drivers/net/ethernet/mellanox/mlx4/pd.c @@ -168,7 +168,7 @@ void mlx4_uar_free(struct mlx4_dev *dev, struct mlx4_uar *uar) } EXPORT_SYMBOL_GPL(mlx4_uar_free); -int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf) +int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf, int node) { struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_uar *uar; @@ -186,10 +186,13 @@ int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf) err = -ENOMEM; goto out; } - uar = kmalloc(sizeof *uar, GFP_KERNEL); + uar = kmalloc_node(sizeof(*uar), GFP_KERNEL, node); if (!uar) { - err = -ENOMEM; - goto out; + uar = kmalloc(sizeof(*uar), GFP_KERNEL); + if (!uar) { + err = -ENOMEM; + goto out; + } } err = mlx4_uar_alloc(dev, uar); if (err) diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 946e0af5faef28b2e89bc677510e1032c24e9eef..97d342fa5032e0ed1a90207bea0a01d2fc203b5c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -178,13 +178,24 @@ EXPORT_SYMBOL_GPL(__mlx4_register_mac); int mlx4_register_mac(struct mlx4_dev *dev, u8 port, u64 mac) { u64 out_param = 0; - int err; + int err = -EINVAL; if (mlx4_is_mfunc(dev)) { - set_param_l(&out_param, port); - err = mlx4_cmd_imm(dev, mac, &out_param, RES_MAC, - RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES, - MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); + if (!(dev->flags & MLX4_FLAG_OLD_REG_MAC)) { + err = mlx4_cmd_imm(dev, mac, &out_param, + ((u32) port) << 8 | (u32) RES_MAC, + RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES, + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); + } + if (err && err == -EINVAL && mlx4_is_slave(dev)) { + /* retry using old REG_MAC format */ + set_param_l(&out_param, port); + err = mlx4_cmd_imm(dev, mac, &out_param, RES_MAC, + RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES, + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); + if (!err) + dev->flags |= MLX4_FLAG_OLD_REG_MAC; + } if (err) return err; @@ -231,10 +242,18 @@ void mlx4_unregister_mac(struct mlx4_dev *dev, u8 port, u64 mac) u64 out_param = 0; if (mlx4_is_mfunc(dev)) { - set_param_l(&out_param, port); - (void) mlx4_cmd_imm(dev, mac, &out_param, RES_MAC, - RES_OP_RESERVE_AND_MAP, MLX4_CMD_FREE_RES, - MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); + if (!(dev->flags & MLX4_FLAG_OLD_REG_MAC)) { + (void) mlx4_cmd_imm(dev, mac, &out_param, + ((u32) port) << 8 | (u32) RES_MAC, + RES_OP_RESERVE_AND_MAP, MLX4_CMD_FREE_RES, + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); + } else { + /* use old unregister mac format */ + set_param_l(&out_param, port); + (void) mlx4_cmd_imm(dev, mac, &out_param, RES_MAC, + RES_OP_RESERVE_AND_MAP, MLX4_CMD_FREE_RES, + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); + } return; } __mlx4_unregister_mac(dev, port, mac); @@ -284,7 +303,7 @@ static int mlx4_set_port_vlan_table(struct mlx4_dev *dev, u8 port, memcpy(mailbox->buf, entries, MLX4_VLAN_TABLE_SIZE); in_mod = MLX4_SET_PORT_VLAN_TABLE << 8 | port; err = mlx4_cmd(dev, mailbox->dma, in_mod, 1, MLX4_CMD_SET_PORT, - MLX4_CMD_TIME_CLASS_B, MLX4_CMD_WRAPPED); + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); mlx4_free_cmd_mailbox(dev, mailbox); @@ -370,9 +389,12 @@ int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index) u64 out_param = 0; int err; + if (vlan > 4095) + return -EINVAL; + if (mlx4_is_mfunc(dev)) { - set_param_l(&out_param, port); - err = mlx4_cmd_imm(dev, vlan, &out_param, RES_VLAN, + err = mlx4_cmd_imm(dev, vlan, &out_param, + ((u32) port) << 8 | (u32) RES_VLAN, RES_OP_RESERVE_AND_MAP, MLX4_CMD_ALLOC_RES, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); if (!err) @@ -384,23 +406,26 @@ int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index) } EXPORT_SYMBOL_GPL(mlx4_register_vlan); -void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index) +void __mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan) { struct mlx4_vlan_table *table = &mlx4_priv(dev)->port[port].vlan_table; + int index; - if (index < MLX4_VLAN_REGULAR) { - mlx4_warn(dev, "Trying to free special vlan index %d\n", index); - return; + mutex_lock(&table->mutex); + if (mlx4_find_cached_vlan(dev, port, vlan, &index)) { + mlx4_warn(dev, "vlan 0x%x is not in the vlan table\n", vlan); + goto out; } - mutex_lock(&table->mutex); - if (!table->refs[index]) { - mlx4_warn(dev, "No vlan entry for index %d\n", index); + if (index < MLX4_VLAN_REGULAR) { + mlx4_warn(dev, "Trying to free special vlan index %d\n", index); goto out; } + if (--table->refs[index]) { - mlx4_dbg(dev, "Have more references for index %d," - "no need to modify vlan table\n", index); + mlx4_dbg(dev, "Have %d more references for index %d," + "no need to modify vlan table\n", table->refs[index], + index); goto out; } table->entries[index] = 0; @@ -410,23 +435,19 @@ out: mutex_unlock(&table->mutex); } -void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index) +void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan) { - u64 in_param = 0; - int err; + u64 out_param = 0; if (mlx4_is_mfunc(dev)) { - set_param_l(&in_param, port); - err = mlx4_cmd(dev, in_param, RES_VLAN, RES_OP_RESERVE_AND_MAP, - MLX4_CMD_FREE_RES, MLX4_CMD_TIME_CLASS_A, - MLX4_CMD_WRAPPED); - if (!err) - mlx4_warn(dev, "Failed freeing vlan at index:%d\n", - index); - + (void) mlx4_cmd_imm(dev, vlan, &out_param, + ((u32) port) << 8 | (u32) RES_VLAN, + RES_OP_RESERVE_AND_MAP, + MLX4_CMD_FREE_RES, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_WRAPPED); return; } - __mlx4_unregister_vlan(dev, port, index); + __mlx4_unregister_vlan(dev, port, vlan); } EXPORT_SYMBOL_GPL(mlx4_unregister_vlan); @@ -448,8 +469,6 @@ int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps) inbuf = inmailbox->buf; outbuf = outmailbox->buf; - memset(inbuf, 0, 256); - memset(outbuf, 0, 256); inbuf[0] = 1; inbuf[1] = 1; inbuf[2] = 1; @@ -632,8 +651,6 @@ int mlx4_SET_PORT(struct mlx4_dev *dev, u8 port, int pkey_tbl_sz) if (IS_ERR(mailbox)) return PTR_ERR(mailbox); - memset(mailbox->buf, 0, 256); - ((__be32 *) mailbox->buf)[1] = dev->caps.ib_port_def_cap[port]; if (pkey_tbl_sz >= 0 && mlx4_is_master(dev)) { @@ -671,8 +688,6 @@ int mlx4_SET_PORT_general(struct mlx4_dev *dev, u8 port, int mtu, if (IS_ERR(mailbox)) return PTR_ERR(mailbox); context = mailbox->buf; - memset(context, 0, sizeof *context); - context->flags = SET_PORT_GEN_ALL_VALID; context->mtu = cpu_to_be16(mtu); context->pptx = (pptx * (!pfctx)) << 7; @@ -706,8 +721,6 @@ int mlx4_SET_PORT_qpn_calc(struct mlx4_dev *dev, u8 port, u32 base_qpn, if (IS_ERR(mailbox)) return PTR_ERR(mailbox); context = mailbox->buf; - memset(context, 0, sizeof *context); - context->base_qpn = cpu_to_be32(base_qpn); context->n_mac = dev->caps.log_num_macs; context->promisc = cpu_to_be32(promisc << SET_PORT_PROMISC_SHIFT | @@ -740,8 +753,6 @@ int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc) if (IS_ERR(mailbox)) return PTR_ERR(mailbox); context = mailbox->buf; - memset(context, 0, sizeof *context); - for (i = 0; i < MLX4_NUM_UP; i += 2) context->prio2tc[i >> 1] = prio2tc[i] << 4 | prio2tc[i + 1]; @@ -767,7 +778,6 @@ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, if (IS_ERR(mailbox)) return PTR_ERR(mailbox); context = mailbox->buf; - memset(context, 0, sizeof *context); for (i = 0; i < MLX4_NUM_TC; i++) { struct mlx4_port_scheduler_tc_cfg_be *tc = &context->tc[i]; diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index e891b058c1befdc09927ea1ab49aa70e282d4278..2715e61dbb74613371b13e324c7f76c7482cb3d3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -480,8 +480,7 @@ int mlx4_init_qp_table(struct mlx4_dev *dev) */ err = mlx4_bitmap_init(&qp_table->bitmap, dev->caps.num_qps, - (1 << 23) - 1, dev->phys_caps.base_sqpn + 8 + - 16 * MLX4_MFUNC_MAX * !!mlx4_is_master(dev), + (1 << 23) - 1, mlx4_num_reserved_sqps(dev), reserved_from_top); if (err) return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index dd6876321116a0bbad26a5fbea55aca3731e024f..2f3f2bc7f2837e2d7eef7c42ced3a2ffad8f0e56 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -55,6 +55,14 @@ struct mac_res { u8 port; }; +struct vlan_res { + struct list_head list; + u16 vlan; + int ref_count; + int vlan_index; + u8 port; +}; + struct res_common { struct list_head list; struct rb_node node; @@ -102,7 +110,14 @@ struct res_qp { int local_qpn; atomic_t ref_count; u32 qpc_flags; + /* saved qp params before VST enforcement in order to restore on VGT */ u8 sched_queue; + __be32 param3; + u8 vlan_control; + u8 fvl_rx; + u8 pri_path_fl; + u8 vlan_index; + u8 feup; }; enum res_mtt_states { @@ -266,6 +281,7 @@ static const char *ResourceType(enum mlx4_resource rt) case RES_MPT: return "RES_MPT"; case RES_MTT: return "RES_MTT"; case RES_MAC: return "RES_MAC"; + case RES_VLAN: return "RES_VLAN"; case RES_EQ: return "RES_EQ"; case RES_COUNTER: return "RES_COUNTER"; case RES_FS_RULE: return "RES_FS_RULE"; @@ -274,10 +290,139 @@ static const char *ResourceType(enum mlx4_resource rt) }; } +static void rem_slave_vlans(struct mlx4_dev *dev, int slave); +static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave, + enum mlx4_resource res_type, int count, + int port) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct resource_allocator *res_alloc = + &priv->mfunc.master.res_tracker.res_alloc[res_type]; + int err = -EINVAL; + int allocated, free, reserved, guaranteed, from_free; + + if (slave > dev->num_vfs) + return -EINVAL; + + spin_lock(&res_alloc->alloc_lock); + allocated = (port > 0) ? + res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] : + res_alloc->allocated[slave]; + free = (port > 0) ? res_alloc->res_port_free[port - 1] : + res_alloc->res_free; + reserved = (port > 0) ? res_alloc->res_port_rsvd[port - 1] : + res_alloc->res_reserved; + guaranteed = res_alloc->guaranteed[slave]; + + if (allocated + count > res_alloc->quota[slave]) + goto out; + + if (allocated + count <= guaranteed) { + err = 0; + } else { + /* portion may need to be obtained from free area */ + if (guaranteed - allocated > 0) + from_free = count - (guaranteed - allocated); + else + from_free = count; + + if (free - from_free > reserved) + err = 0; + } + + if (!err) { + /* grant the request */ + if (port > 0) { + res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] += count; + res_alloc->res_port_free[port - 1] -= count; + } else { + res_alloc->allocated[slave] += count; + res_alloc->res_free -= count; + } + } + +out: + spin_unlock(&res_alloc->alloc_lock); + return err; +} + +static inline void mlx4_release_resource(struct mlx4_dev *dev, int slave, + enum mlx4_resource res_type, int count, + int port) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct resource_allocator *res_alloc = + &priv->mfunc.master.res_tracker.res_alloc[res_type]; + + if (slave > dev->num_vfs) + return; + + spin_lock(&res_alloc->alloc_lock); + if (port > 0) { + res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] -= count; + res_alloc->res_port_free[port - 1] += count; + } else { + res_alloc->allocated[slave] -= count; + res_alloc->res_free += count; + } + + spin_unlock(&res_alloc->alloc_lock); + return; +} + +static inline void initialize_res_quotas(struct mlx4_dev *dev, + struct resource_allocator *res_alloc, + enum mlx4_resource res_type, + int vf, int num_instances) +{ + res_alloc->guaranteed[vf] = num_instances / (2 * (dev->num_vfs + 1)); + res_alloc->quota[vf] = (num_instances / 2) + res_alloc->guaranteed[vf]; + if (vf == mlx4_master_func_num(dev)) { + res_alloc->res_free = num_instances; + if (res_type == RES_MTT) { + /* reserved mtts will be taken out of the PF allocation */ + res_alloc->res_free += dev->caps.reserved_mtts; + res_alloc->guaranteed[vf] += dev->caps.reserved_mtts; + res_alloc->quota[vf] += dev->caps.reserved_mtts; + } + } +} + +void mlx4_init_quotas(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int pf; + + /* quotas for VFs are initialized in mlx4_slave_cap */ + if (mlx4_is_slave(dev)) + return; + + if (!mlx4_is_mfunc(dev)) { + dev->quotas.qp = dev->caps.num_qps - dev->caps.reserved_qps - + mlx4_num_reserved_sqps(dev); + dev->quotas.cq = dev->caps.num_cqs - dev->caps.reserved_cqs; + dev->quotas.srq = dev->caps.num_srqs - dev->caps.reserved_srqs; + dev->quotas.mtt = dev->caps.num_mtts - dev->caps.reserved_mtts; + dev->quotas.mpt = dev->caps.num_mpts - dev->caps.reserved_mrws; + return; + } + + pf = mlx4_master_func_num(dev); + dev->quotas.qp = + priv->mfunc.master.res_tracker.res_alloc[RES_QP].quota[pf]; + dev->quotas.cq = + priv->mfunc.master.res_tracker.res_alloc[RES_CQ].quota[pf]; + dev->quotas.srq = + priv->mfunc.master.res_tracker.res_alloc[RES_SRQ].quota[pf]; + dev->quotas.mtt = + priv->mfunc.master.res_tracker.res_alloc[RES_MTT].quota[pf]; + dev->quotas.mpt = + priv->mfunc.master.res_tracker.res_alloc[RES_MPT].quota[pf]; +} int mlx4_init_resource_tracker(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); - int i; + int i, j; int t; priv->mfunc.master.res_tracker.slave_list = @@ -298,8 +443,105 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev) for (i = 0 ; i < MLX4_NUM_OF_RESOURCE_TYPE; i++) priv->mfunc.master.res_tracker.res_tree[i] = RB_ROOT; + for (i = 0; i < MLX4_NUM_OF_RESOURCE_TYPE; i++) { + struct resource_allocator *res_alloc = + &priv->mfunc.master.res_tracker.res_alloc[i]; + res_alloc->quota = kmalloc((dev->num_vfs + 1) * sizeof(int), GFP_KERNEL); + res_alloc->guaranteed = kmalloc((dev->num_vfs + 1) * sizeof(int), GFP_KERNEL); + if (i == RES_MAC || i == RES_VLAN) + res_alloc->allocated = kzalloc(MLX4_MAX_PORTS * + (dev->num_vfs + 1) * sizeof(int), + GFP_KERNEL); + else + res_alloc->allocated = kzalloc((dev->num_vfs + 1) * sizeof(int), GFP_KERNEL); + + if (!res_alloc->quota || !res_alloc->guaranteed || + !res_alloc->allocated) + goto no_mem_err; + + spin_lock_init(&res_alloc->alloc_lock); + for (t = 0; t < dev->num_vfs + 1; t++) { + switch (i) { + case RES_QP: + initialize_res_quotas(dev, res_alloc, RES_QP, + t, dev->caps.num_qps - + dev->caps.reserved_qps - + mlx4_num_reserved_sqps(dev)); + break; + case RES_CQ: + initialize_res_quotas(dev, res_alloc, RES_CQ, + t, dev->caps.num_cqs - + dev->caps.reserved_cqs); + break; + case RES_SRQ: + initialize_res_quotas(dev, res_alloc, RES_SRQ, + t, dev->caps.num_srqs - + dev->caps.reserved_srqs); + break; + case RES_MPT: + initialize_res_quotas(dev, res_alloc, RES_MPT, + t, dev->caps.num_mpts - + dev->caps.reserved_mrws); + break; + case RES_MTT: + initialize_res_quotas(dev, res_alloc, RES_MTT, + t, dev->caps.num_mtts - + dev->caps.reserved_mtts); + break; + case RES_MAC: + if (t == mlx4_master_func_num(dev)) { + res_alloc->quota[t] = MLX4_MAX_MAC_NUM; + res_alloc->guaranteed[t] = 2; + for (j = 0; j < MLX4_MAX_PORTS; j++) + res_alloc->res_port_free[j] = MLX4_MAX_MAC_NUM; + } else { + res_alloc->quota[t] = MLX4_MAX_MAC_NUM; + res_alloc->guaranteed[t] = 2; + } + break; + case RES_VLAN: + if (t == mlx4_master_func_num(dev)) { + res_alloc->quota[t] = MLX4_MAX_VLAN_NUM; + res_alloc->guaranteed[t] = MLX4_MAX_VLAN_NUM / 2; + for (j = 0; j < MLX4_MAX_PORTS; j++) + res_alloc->res_port_free[j] = + res_alloc->quota[t]; + } else { + res_alloc->quota[t] = MLX4_MAX_VLAN_NUM / 2; + res_alloc->guaranteed[t] = 0; + } + break; + case RES_COUNTER: + res_alloc->quota[t] = dev->caps.max_counters; + res_alloc->guaranteed[t] = 0; + if (t == mlx4_master_func_num(dev)) + res_alloc->res_free = res_alloc->quota[t]; + break; + default: + break; + } + if (i == RES_MAC || i == RES_VLAN) { + for (j = 0; j < MLX4_MAX_PORTS; j++) + res_alloc->res_port_rsvd[j] += + res_alloc->guaranteed[t]; + } else { + res_alloc->res_reserved += res_alloc->guaranteed[t]; + } + } + } spin_lock_init(&priv->mfunc.master.res_tracker.lock); - return 0 ; + return 0; + +no_mem_err: + for (i = 0; i < MLX4_NUM_OF_RESOURCE_TYPE; i++) { + kfree(priv->mfunc.master.res_tracker.res_alloc[i].allocated); + priv->mfunc.master.res_tracker.res_alloc[i].allocated = NULL; + kfree(priv->mfunc.master.res_tracker.res_alloc[i].guaranteed); + priv->mfunc.master.res_tracker.res_alloc[i].guaranteed = NULL; + kfree(priv->mfunc.master.res_tracker.res_alloc[i].quota); + priv->mfunc.master.res_tracker.res_alloc[i].quota = NULL; + } + return -ENOMEM; } void mlx4_free_resource_tracker(struct mlx4_dev *dev, @@ -309,13 +551,28 @@ void mlx4_free_resource_tracker(struct mlx4_dev *dev, int i; if (priv->mfunc.master.res_tracker.slave_list) { - if (type != RES_TR_FREE_STRUCTS_ONLY) - for (i = 0 ; i < dev->num_slaves; i++) + if (type != RES_TR_FREE_STRUCTS_ONLY) { + for (i = 0; i < dev->num_slaves; i++) { if (type == RES_TR_FREE_ALL || dev->caps.function != i) mlx4_delete_all_resources_for_slave(dev, i); + } + /* free master's vlans */ + i = dev->caps.function; + mutex_lock(&priv->mfunc.master.res_tracker.slave_list[i].mutex); + rem_slave_vlans(dev, i); + mutex_unlock(&priv->mfunc.master.res_tracker.slave_list[i].mutex); + } if (type != RES_TR_FREE_SLAVES_ONLY) { + for (i = 0; i < MLX4_NUM_OF_RESOURCE_TYPE; i++) { + kfree(priv->mfunc.master.res_tracker.res_alloc[i].allocated); + priv->mfunc.master.res_tracker.res_alloc[i].allocated = NULL; + kfree(priv->mfunc.master.res_tracker.res_alloc[i].guaranteed); + priv->mfunc.master.res_tracker.res_alloc[i].guaranteed = NULL; + kfree(priv->mfunc.master.res_tracker.res_alloc[i].quota); + priv->mfunc.master.res_tracker.res_alloc[i].quota = NULL; + } kfree(priv->mfunc.master.res_tracker.slave_list); priv->mfunc.master.res_tracker.slave_list = NULL; } @@ -1229,12 +1486,19 @@ static int qp_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, case RES_OP_RESERVE: count = get_param_l(&in_param); align = get_param_h(&in_param); - err = __mlx4_qp_reserve_range(dev, count, align, &base); + err = mlx4_grant_resource(dev, slave, RES_QP, count, 0); if (err) return err; + err = __mlx4_qp_reserve_range(dev, count, align, &base); + if (err) { + mlx4_release_resource(dev, slave, RES_QP, count, 0); + return err; + } + err = add_res_range(dev, slave, base, count, RES_QP, 0); if (err) { + mlx4_release_resource(dev, slave, RES_QP, count, 0); __mlx4_qp_release_range(dev, base, count); return err; } @@ -1282,15 +1546,24 @@ static int mtt_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, return err; order = get_param_l(&in_param); + + err = mlx4_grant_resource(dev, slave, RES_MTT, 1 << order, 0); + if (err) + return err; + base = __mlx4_alloc_mtt_range(dev, order); - if (base == -1) + if (base == -1) { + mlx4_release_resource(dev, slave, RES_MTT, 1 << order, 0); return -ENOMEM; + } err = add_res_range(dev, slave, base, 1, RES_MTT, order); - if (err) + if (err) { + mlx4_release_resource(dev, slave, RES_MTT, 1 << order, 0); __mlx4_free_mtt_range(dev, base, order); - else + } else { set_param_l(out_param, base); + } return err; } @@ -1305,13 +1578,20 @@ static int mpt_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, switch (op) { case RES_OP_RESERVE: + err = mlx4_grant_resource(dev, slave, RES_MPT, 1, 0); + if (err) + break; + index = __mlx4_mpt_reserve(dev); - if (index == -1) + if (index == -1) { + mlx4_release_resource(dev, slave, RES_MPT, 1, 0); break; + } id = index & mpt_mask(dev); err = add_res_range(dev, slave, id, 1, RES_MPT, index); if (err) { + mlx4_release_resource(dev, slave, RES_MPT, 1, 0); __mlx4_mpt_release(dev, index); break; } @@ -1345,12 +1625,19 @@ static int cq_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, switch (op) { case RES_OP_RESERVE_AND_MAP: - err = __mlx4_cq_alloc_icm(dev, &cqn); + err = mlx4_grant_resource(dev, slave, RES_CQ, 1, 0); if (err) break; + err = __mlx4_cq_alloc_icm(dev, &cqn); + if (err) { + mlx4_release_resource(dev, slave, RES_CQ, 1, 0); + break; + } + err = add_res_range(dev, slave, cqn, 1, RES_CQ, 0); if (err) { + mlx4_release_resource(dev, slave, RES_CQ, 1, 0); __mlx4_cq_free_icm(dev, cqn); break; } @@ -1373,12 +1660,19 @@ static int srq_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, switch (op) { case RES_OP_RESERVE_AND_MAP: - err = __mlx4_srq_alloc_icm(dev, &srqn); + err = mlx4_grant_resource(dev, slave, RES_SRQ, 1, 0); if (err) break; + err = __mlx4_srq_alloc_icm(dev, &srqn); + if (err) { + mlx4_release_resource(dev, slave, RES_SRQ, 1, 0); + break; + } + err = add_res_range(dev, slave, srqn, 1, RES_SRQ, 0); if (err) { + mlx4_release_resource(dev, slave, RES_SRQ, 1, 0); __mlx4_srq_free_icm(dev, srqn); break; } @@ -1399,9 +1693,13 @@ static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port) struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker; struct mac_res *res; + if (mlx4_grant_resource(dev, slave, RES_MAC, 1, port)) + return -EINVAL; res = kzalloc(sizeof *res, GFP_KERNEL); - if (!res) + if (!res) { + mlx4_release_resource(dev, slave, RES_MAC, 1, port); return -ENOMEM; + } res->mac = mac; res->port = (u8) port; list_add_tail(&res->list, @@ -1421,6 +1719,7 @@ static void mac_del_from_slave(struct mlx4_dev *dev, int slave, u64 mac, list_for_each_entry_safe(res, tmp, mac_list, list) { if (res->mac == mac && res->port == (u8) port) { list_del(&res->list); + mlx4_release_resource(dev, slave, RES_MAC, 1, port); kfree(res); break; } @@ -1438,12 +1737,13 @@ static void rem_slave_macs(struct mlx4_dev *dev, int slave) list_for_each_entry_safe(res, tmp, mac_list, list) { list_del(&res->list); __mlx4_unregister_mac(dev, res->port, res->mac); + mlx4_release_resource(dev, slave, RES_MAC, 1, res->port); kfree(res); } } static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, - u64 in_param, u64 *out_param) + u64 in_param, u64 *out_param, int in_port) { int err = -EINVAL; int port; @@ -1452,7 +1752,7 @@ static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, if (op != RES_OP_RESERVE_AND_MAP) return err; - port = get_param_l(out_param); + port = !in_port ? get_param_l(out_param) : in_port; mac = in_param; err = __mlx4_register_mac(dev, port, mac); @@ -1469,12 +1769,114 @@ static int mac_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, return err; } -static int vlan_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, - u64 in_param, u64 *out_param) +static int vlan_add_to_slave(struct mlx4_dev *dev, int slave, u16 vlan, + int port, int vlan_index) { + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker; + struct list_head *vlan_list = + &tracker->slave_list[slave].res_list[RES_VLAN]; + struct vlan_res *res, *tmp; + + list_for_each_entry_safe(res, tmp, vlan_list, list) { + if (res->vlan == vlan && res->port == (u8) port) { + /* vlan found. update ref count */ + ++res->ref_count; + return 0; + } + } + + if (mlx4_grant_resource(dev, slave, RES_VLAN, 1, port)) + return -EINVAL; + res = kzalloc(sizeof(*res), GFP_KERNEL); + if (!res) { + mlx4_release_resource(dev, slave, RES_VLAN, 1, port); + return -ENOMEM; + } + res->vlan = vlan; + res->port = (u8) port; + res->vlan_index = vlan_index; + res->ref_count = 1; + list_add_tail(&res->list, + &tracker->slave_list[slave].res_list[RES_VLAN]); return 0; } + +static void vlan_del_from_slave(struct mlx4_dev *dev, int slave, u16 vlan, + int port) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker; + struct list_head *vlan_list = + &tracker->slave_list[slave].res_list[RES_VLAN]; + struct vlan_res *res, *tmp; + + list_for_each_entry_safe(res, tmp, vlan_list, list) { + if (res->vlan == vlan && res->port == (u8) port) { + if (!--res->ref_count) { + list_del(&res->list); + mlx4_release_resource(dev, slave, RES_VLAN, + 1, port); + kfree(res); + } + break; + } + } +} + +static void rem_slave_vlans(struct mlx4_dev *dev, int slave) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker; + struct list_head *vlan_list = + &tracker->slave_list[slave].res_list[RES_VLAN]; + struct vlan_res *res, *tmp; + int i; + + list_for_each_entry_safe(res, tmp, vlan_list, list) { + list_del(&res->list); + /* dereference the vlan the num times the slave referenced it */ + for (i = 0; i < res->ref_count; i++) + __mlx4_unregister_vlan(dev, res->port, res->vlan); + mlx4_release_resource(dev, slave, RES_VLAN, 1, res->port); + kfree(res); + } +} + +static int vlan_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, + u64 in_param, u64 *out_param, int in_port) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_slave_state *slave_state = priv->mfunc.master.slave_state; + int err; + u16 vlan; + int vlan_index; + int port; + + port = !in_port ? get_param_l(out_param) : in_port; + + if (!port || op != RES_OP_RESERVE_AND_MAP) + return -EINVAL; + + /* upstream kernels had NOP for reg/unreg vlan. Continue this. */ + if (!in_port && port > 0 && port <= dev->caps.num_ports) { + slave_state[slave].old_vlan_api = true; + return 0; + } + + vlan = (u16) in_param; + + err = __mlx4_register_vlan(dev, port, vlan, &vlan_index); + if (!err) { + set_param_l(out_param, (u32) vlan_index); + err = vlan_add_to_slave(dev, slave, vlan, port, vlan_index); + if (err) + __mlx4_unregister_vlan(dev, port, vlan); + } + return err; +} + static int counter_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, u64 in_param, u64 *out_param) { @@ -1484,15 +1886,23 @@ static int counter_alloc_res(struct mlx4_dev *dev, int slave, int op, int cmd, if (op != RES_OP_RESERVE) return -EINVAL; - err = __mlx4_counter_alloc(dev, &index); + err = mlx4_grant_resource(dev, slave, RES_COUNTER, 1, 0); if (err) return err; + err = __mlx4_counter_alloc(dev, &index); + if (err) { + mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0); + return err; + } + err = add_res_range(dev, slave, index, 1, RES_COUNTER, 0); - if (err) + if (err) { __mlx4_counter_free(dev, index); - else + mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0); + } else { set_param_l(out_param, index); + } return err; } @@ -1528,7 +1938,7 @@ int mlx4_ALLOC_RES_wrapper(struct mlx4_dev *dev, int slave, int err; int alop = vhcr->op_modifier; - switch (vhcr->in_modifier) { + switch (vhcr->in_modifier & 0xFF) { case RES_QP: err = qp_alloc_res(dev, slave, vhcr->op_modifier, alop, vhcr->in_param, &vhcr->out_param); @@ -1556,12 +1966,14 @@ int mlx4_ALLOC_RES_wrapper(struct mlx4_dev *dev, int slave, case RES_MAC: err = mac_alloc_res(dev, slave, vhcr->op_modifier, alop, - vhcr->in_param, &vhcr->out_param); + vhcr->in_param, &vhcr->out_param, + (vhcr->in_modifier >> 8) & 0xFF); break; case RES_VLAN: err = vlan_alloc_res(dev, slave, vhcr->op_modifier, alop, - vhcr->in_param, &vhcr->out_param); + vhcr->in_param, &vhcr->out_param, + (vhcr->in_modifier >> 8) & 0xFF); break; case RES_COUNTER: @@ -1597,6 +2009,7 @@ static int qp_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, err = rem_res_range(dev, slave, base, count, RES_QP, 0); if (err) break; + mlx4_release_resource(dev, slave, RES_QP, count, 0); __mlx4_qp_release_range(dev, base, count); break; case RES_OP_MAP_ICM: @@ -1634,8 +2047,10 @@ static int mtt_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, base = get_param_l(&in_param); order = get_param_h(&in_param); err = rem_res_range(dev, slave, base, 1, RES_MTT, order); - if (!err) + if (!err) { + mlx4_release_resource(dev, slave, RES_MTT, 1 << order, 0); __mlx4_free_mtt_range(dev, base, order); + } return err; } @@ -1660,6 +2075,7 @@ static int mpt_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, err = rem_res_range(dev, slave, id, 1, RES_MPT, 0); if (err) break; + mlx4_release_resource(dev, slave, RES_MPT, 1, 0); __mlx4_mpt_release(dev, index); break; case RES_OP_MAP_ICM: @@ -1694,6 +2110,7 @@ static int cq_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, if (err) break; + mlx4_release_resource(dev, slave, RES_CQ, 1, 0); __mlx4_cq_free_icm(dev, cqn); break; @@ -1718,6 +2135,7 @@ static int srq_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, if (err) break; + mlx4_release_resource(dev, slave, RES_SRQ, 1, 0); __mlx4_srq_free_icm(dev, srqn); break; @@ -1730,14 +2148,14 @@ static int srq_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, } static int mac_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, - u64 in_param, u64 *out_param) + u64 in_param, u64 *out_param, int in_port) { int port; int err = 0; switch (op) { case RES_OP_RESERVE_AND_MAP: - port = get_param_l(out_param); + port = !in_port ? get_param_l(out_param) : in_port; mac_del_from_slave(dev, slave, in_param, port); __mlx4_unregister_mac(dev, port, in_param); break; @@ -1751,9 +2169,27 @@ static int mac_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, } static int vlan_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, - u64 in_param, u64 *out_param) + u64 in_param, u64 *out_param, int port) { - return 0; + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_slave_state *slave_state = priv->mfunc.master.slave_state; + int err = 0; + + switch (op) { + case RES_OP_RESERVE_AND_MAP: + if (slave_state[slave].old_vlan_api) + return 0; + if (!port) + return -EINVAL; + vlan_del_from_slave(dev, slave, in_param, port); + __mlx4_unregister_vlan(dev, port, in_param); + break; + default: + err = -EINVAL; + break; + } + + return err; } static int counter_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, @@ -1771,6 +2207,7 @@ static int counter_free_res(struct mlx4_dev *dev, int slave, int op, int cmd, return err; __mlx4_counter_free(dev, index); + mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0); return err; } @@ -1803,7 +2240,7 @@ int mlx4_FREE_RES_wrapper(struct mlx4_dev *dev, int slave, int err = -EINVAL; int alop = vhcr->op_modifier; - switch (vhcr->in_modifier) { + switch (vhcr->in_modifier & 0xFF) { case RES_QP: err = qp_free_res(dev, slave, vhcr->op_modifier, alop, vhcr->in_param); @@ -1831,12 +2268,14 @@ int mlx4_FREE_RES_wrapper(struct mlx4_dev *dev, int slave, case RES_MAC: err = mac_free_res(dev, slave, vhcr->op_modifier, alop, - vhcr->in_param, &vhcr->out_param); + vhcr->in_param, &vhcr->out_param, + (vhcr->in_modifier >> 8) & 0xFF); break; case RES_VLAN: err = vlan_free_res(dev, slave, vhcr->op_modifier, alop, - vhcr->in_param, &vhcr->out_param); + vhcr->in_param, &vhcr->out_param, + (vhcr->in_modifier >> 8) & 0xFF); break; case RES_COUNTER: @@ -2136,6 +2575,12 @@ int mlx4_RST2INIT_QP_wrapper(struct mlx4_dev *dev, int slave, return err; qp->local_qpn = local_qpn; qp->sched_queue = 0; + qp->param3 = 0; + qp->vlan_control = 0; + qp->fvl_rx = 0; + qp->pri_path_fl = 0; + qp->vlan_index = 0; + qp->feup = 0; qp->qpc_flags = be32_to_cpu(qpc->flags); err = get_res(dev, slave, mtt_base, RES_MTT, &mtt); @@ -2862,6 +3307,12 @@ int mlx4_INIT2RTR_QP_wrapper(struct mlx4_dev *dev, int slave, int qpn = vhcr->in_modifier & 0x7fffff; struct res_qp *qp; u8 orig_sched_queue; + __be32 orig_param3 = qpc->param3; + u8 orig_vlan_control = qpc->pri_path.vlan_control; + u8 orig_fvl_rx = qpc->pri_path.fvl_rx; + u8 orig_pri_path_fl = qpc->pri_path.fl; + u8 orig_vlan_index = qpc->pri_path.vlan_index; + u8 orig_feup = qpc->pri_path.feup; err = verify_qp_parameters(dev, inbox, QP_TRANS_INIT2RTR, slave); if (err) @@ -2889,9 +3340,15 @@ out: * essentially the QOS value provided by the VF. This will be useful * if we allow dynamic changes from VST back to VGT */ - if (!err) + if (!err) { qp->sched_queue = orig_sched_queue; - + qp->param3 = orig_param3; + qp->vlan_control = orig_vlan_control; + qp->fvl_rx = orig_fvl_rx; + qp->pri_path_fl = orig_pri_path_fl; + qp->vlan_index = orig_vlan_index; + qp->feup = orig_feup; + } put_res(dev, slave, qpn, RES_QP); return err; } @@ -3498,6 +3955,11 @@ static void rem_slave_qps(struct mlx4_dev *dev, int slave) &tracker->res_tree[RES_QP]); list_del(&qp->com.list); spin_unlock_irq(mlx4_tlock(dev)); + if (!valid_reserved(dev, slave, qpn)) { + __mlx4_qp_release_range(dev, qpn, 1); + mlx4_release_resource(dev, slave, + RES_QP, 1, 0); + } kfree(qp); state = 0; break; @@ -3569,6 +4031,8 @@ static void rem_slave_srqs(struct mlx4_dev *dev, int slave) &tracker->res_tree[RES_SRQ]); list_del(&srq->com.list); spin_unlock_irq(mlx4_tlock(dev)); + mlx4_release_resource(dev, slave, + RES_SRQ, 1, 0); kfree(srq); state = 0; break; @@ -3635,6 +4099,8 @@ static void rem_slave_cqs(struct mlx4_dev *dev, int slave) &tracker->res_tree[RES_CQ]); list_del(&cq->com.list); spin_unlock_irq(mlx4_tlock(dev)); + mlx4_release_resource(dev, slave, + RES_CQ, 1, 0); kfree(cq); state = 0; break; @@ -3698,6 +4164,8 @@ static void rem_slave_mrs(struct mlx4_dev *dev, int slave) &tracker->res_tree[RES_MPT]); list_del(&mpt->com.list); spin_unlock_irq(mlx4_tlock(dev)); + mlx4_release_resource(dev, slave, + RES_MPT, 1, 0); kfree(mpt); state = 0; break; @@ -3767,6 +4235,8 @@ static void rem_slave_mtts(struct mlx4_dev *dev, int slave) &tracker->res_tree[RES_MTT]); list_del(&mtt->com.list); spin_unlock_irq(mlx4_tlock(dev)); + mlx4_release_resource(dev, slave, RES_MTT, + 1 << mtt->order, 0); kfree(mtt); state = 0; break; @@ -3925,6 +4395,7 @@ static void rem_slave_counters(struct mlx4_dev *dev, int slave) list_del(&counter->com.list); kfree(counter); __mlx4_counter_free(dev, index); + mlx4_release_resource(dev, slave, RES_COUNTER, 1, 0); } } spin_unlock_irq(mlx4_tlock(dev)); @@ -3964,7 +4435,7 @@ void mlx4_delete_all_resources_for_slave(struct mlx4_dev *dev, int slave) struct mlx4_priv *priv = mlx4_priv(dev); mutex_lock(&priv->mfunc.master.res_tracker.slave_list[slave].mutex); - /*VLAN*/ + rem_slave_vlans(dev, slave); rem_slave_macs(dev, slave); rem_slave_fs_rule(dev, slave); rem_slave_qps(dev, slave); @@ -3991,13 +4462,20 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) &tracker->slave_list[work->slave].res_list[RES_QP]; struct res_qp *qp; struct res_qp *tmp; - u64 qp_mask = ((1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_UNTAGGED) | + u64 qp_path_mask_vlan_ctrl = + ((1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_UNTAGGED) | (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_1P) | (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_TX_BLOCK_TAGGED) | (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_UNTAGGED) | (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_1P) | - (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_TAGGED) | - (1ULL << MLX4_UPD_QP_PATH_MASK_VLAN_INDEX) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_RX_BLOCK_TAGGED)); + + u64 qp_path_mask = ((1ULL << MLX4_UPD_QP_PATH_MASK_VLAN_INDEX) | + (1ULL << MLX4_UPD_QP_PATH_MASK_FVL) | + (1ULL << MLX4_UPD_QP_PATH_MASK_CV) | + (1ULL << MLX4_UPD_QP_PATH_MASK_ETH_HIDE_CQE_VLAN) | + (1ULL << MLX4_UPD_QP_PATH_MASK_FEUP) | + (1ULL << MLX4_UPD_QP_PATH_MASK_FVL_RX) | (1ULL << MLX4_UPD_QP_PATH_MASK_SCHED_QUEUE)); int err; @@ -4029,9 +4507,7 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) MLX4_VLAN_CTRL_ETH_RX_BLOCK_UNTAGGED; upd_context = mailbox->buf; - upd_context->primary_addr_path_mask = cpu_to_be64(qp_mask); - upd_context->qp_context.pri_path.vlan_control = vlan_control; - upd_context->qp_context.pri_path.vlan_index = work->vlan_ix; + upd_context->qp_mask = cpu_to_be64(MLX4_UPD_QP_MASK_VSD); spin_lock_irq(mlx4_tlock(dev)); list_for_each_entry_safe(qp, tmp, qp_list, com.list) { @@ -4049,10 +4525,35 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) spin_lock_irq(mlx4_tlock(dev)); continue; } - upd_context->qp_context.pri_path.sched_queue = - qp->sched_queue & 0xC7; - upd_context->qp_context.pri_path.sched_queue |= - ((work->qos & 0x7) << 3); + if (MLX4_QP_ST_RC == ((qp->qpc_flags >> 16) & 0xff)) + upd_context->primary_addr_path_mask = cpu_to_be64(qp_path_mask); + else + upd_context->primary_addr_path_mask = + cpu_to_be64(qp_path_mask | qp_path_mask_vlan_ctrl); + if (work->vlan_id == MLX4_VGT) { + upd_context->qp_context.param3 = qp->param3; + upd_context->qp_context.pri_path.vlan_control = qp->vlan_control; + upd_context->qp_context.pri_path.fvl_rx = qp->fvl_rx; + upd_context->qp_context.pri_path.vlan_index = qp->vlan_index; + upd_context->qp_context.pri_path.fl = qp->pri_path_fl; + upd_context->qp_context.pri_path.feup = qp->feup; + upd_context->qp_context.pri_path.sched_queue = + qp->sched_queue; + } else { + upd_context->qp_context.param3 = qp->param3 & ~cpu_to_be32(MLX4_STRIP_VLAN); + upd_context->qp_context.pri_path.vlan_control = vlan_control; + upd_context->qp_context.pri_path.vlan_index = work->vlan_ix; + upd_context->qp_context.pri_path.fvl_rx = + qp->fvl_rx | MLX4_FVL_RX_FORCE_ETH_VLAN; + upd_context->qp_context.pri_path.fl = + qp->pri_path_fl | MLX4_FL_CV | MLX4_FL_ETH_HIDE_CQE_VLAN; + upd_context->qp_context.pri_path.feup = + qp->feup | MLX4_FEUP_FORCE_ETH_UP | MLX4_FVL_FORCE_ETH_VLAN; + upd_context->qp_context.pri_path.sched_queue = + qp->sched_queue & 0xC7; + upd_context->qp_context.pri_path.sched_queue |= + ((work->qos & 0x7) << 3); + } err = mlx4_cmd(dev, mailbox->dma, qp->local_qpn & 0xffffff, @@ -4081,7 +4582,7 @@ void mlx4_vf_immed_vlan_work_handler(struct work_struct *_work) if (work->flags & MLX4_VF_IMMED_VLAN_FLAG_VLAN && !errors && NO_INDX != work->orig_vlan_ix) __mlx4_unregister_vlan(&work->priv->dev, work->port, - work->orig_vlan_ix); + work->orig_vlan_id); out: kfree(work); return; diff --git a/drivers/net/ethernet/mellanox/mlx4/srq.c b/drivers/net/ethernet/mellanox/mlx4/srq.c index 79fd269e2c54b3e0a043ab072a4e2b0f888e0088..8fdf237537791da5568fc7be38ca36bed3fc57a3 100644 --- a/drivers/net/ethernet/mellanox/mlx4/srq.c +++ b/drivers/net/ethernet/mellanox/mlx4/srq.c @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -188,8 +189,6 @@ int mlx4_srq_alloc(struct mlx4_dev *dev, u32 pdn, u32 cqn, u16 xrcd, } srq_context = mailbox->buf; - memset(srq_context, 0, sizeof *srq_context); - srq_context->state_logsize_srqn = cpu_to_be32((ilog2(srq->max) << 24) | srq->srqn); srq_context->logstride = srq->wqe_shift - 4; diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c index 075f4e21d33df6f4f2b749dcd4aeb9fee1405077..c83d16dc7cd56034de4bfbc7deafcad6ddb42439 100644 --- a/drivers/net/ethernet/micrel/ks8851_mll.c +++ b/drivers/net/ethernet/micrel/ks8851_mll.c @@ -1248,7 +1248,7 @@ static void ks_set_mac(struct ks_net *ks, u8 *data) w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF); ks_wrreg16(ks, KS_MARL, w); - memcpy(ks->mac_addr, data, 6); + memcpy(ks->mac_addr, data, ETH_ALEN); if (ks->enabled) ks_start_rx(ks); @@ -1651,7 +1651,7 @@ static int ks8851_probe(struct platform_device *pdev) } netdev_info(netdev, "Mac address is: %pM\n", ks->mac_addr); - memcpy(netdev->dev_addr, ks->mac_addr, 6); + memcpy(netdev->dev_addr, ks->mac_addr, ETH_ALEN); ks_set_mac(ks, netdev->dev_addr); diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 8ebc352bcbe6b817912e9d055202c4c077779924..ddd252a3da9c023794e0bde418d689d87cb4b8c4 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -7150,8 +7150,6 @@ static void pcidev_exit(struct pci_dev *pdev) struct platform_info *info = pci_get_drvdata(pdev); struct dev_info *hw_priv = &info->dev_info; - pci_set_drvdata(pdev, NULL); - release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); for (i = 0; i < hw_priv->hw.dev_count; i++) { @@ -7227,7 +7225,7 @@ static int pcidev_suspend(struct pci_dev *pdev, pm_message_t state) static char pcidev_name[] = "ksz884xp"; -static struct pci_device_id pcidev_table[] = { +static DEFINE_PCI_DEVICE_TABLE(pcidev_table) = { { PCI_VENDOR_ID_MICREL_KS, 0x8841, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { PCI_VENDOR_ID_MICREL_KS, 0x8842, diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index ea54d95e5b9f9caa250974b050b5de82a2231e31..cbd013379252a158322aa5c4f508d0a9d1413020 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -26,7 +26,6 @@ #include #include #include -#include #include "moxart_ether.h" diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 149355b52ad0c8f2d4aac4f6cfe83041bc59f45b..68026f7e8ba308d62c66570fb5ac7c19eb9f8994 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -934,7 +934,7 @@ static inline void myri10ge_ss_init_lock(struct myri10ge_slice_state *ss) static inline bool myri10ge_ss_lock_napi(struct myri10ge_slice_state *ss) { - int rc = true; + bool rc = true; spin_lock(&ss->lock); if ((ss->state & SLICE_LOCKED)) { WARN_ON((ss->state & SLICE_STATE_NAPI)); @@ -957,7 +957,7 @@ static inline void myri10ge_ss_unlock_napi(struct myri10ge_slice_state *ss) static inline bool myri10ge_ss_lock_poll(struct myri10ge_slice_state *ss) { - int rc = true; + bool rc = true; spin_lock_bh(&ss->lock); if ((ss->state & SLICE_LOCKED)) { ss->state |= SLICE_STATE_POLL_YIELD; @@ -3164,7 +3164,7 @@ static void myri10ge_set_multicast_list(struct net_device *dev) /* Walk the multicast list, and add each address */ netdev_for_each_mc_addr(ha, dev) { - memcpy(data, &ha->addr, 6); + memcpy(data, &ha->addr, ETH_ALEN); cmd.data0 = ntohl(data[0]); cmd.data1 = ntohl(data[1]); err = myri10ge_send_cmd(mgp, MXGEFW_JOIN_MULTICAST_GROUP, @@ -3207,7 +3207,7 @@ static int myri10ge_set_mac_address(struct net_device *dev, void *addr) } /* change the dev structure */ - memcpy(dev->dev_addr, sa->sa_data, 6); + memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN); return 0; } @@ -4208,7 +4208,6 @@ static void myri10ge_remove(struct pci_dev *pdev) set_fw_name(mgp, NULL, false); free_netdev(netdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } #define PCI_DEVICE_ID_MYRICOM_MYRI10GE_Z8E 0x0008 diff --git a/drivers/net/ethernet/natsemi/natsemi.c b/drivers/net/ethernet/natsemi/natsemi.c index 7a5e295588b0740f38c288364de1a51be6898fd9..64ec2a437f46a3280e9377c55d8226a19f82d089 100644 --- a/drivers/net/ethernet/natsemi/natsemi.c +++ b/drivers/net/ethernet/natsemi/natsemi.c @@ -970,7 +970,6 @@ static int natsemi_probe1(struct pci_dev *pdev, const struct pci_device_id *ent) err_ioremap: pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); err_pci_request_regions: free_netdev(dev); @@ -3220,7 +3219,6 @@ static void natsemi_remove1(struct pci_dev *pdev) pci_release_regions (pdev); iounmap(ioaddr); free_netdev (dev); - pci_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM diff --git a/drivers/net/ethernet/natsemi/xtsonic.c b/drivers/net/ethernet/natsemi/xtsonic.c index 4da172ac55991bd69b50037501c3fc886728dc08..7007d212f3e4ef6c574fb4d39b59f349d85bc3e7 100644 --- a/drivers/net/ethernet/natsemi/xtsonic.c +++ b/drivers/net/ethernet/natsemi/xtsonic.c @@ -264,6 +264,7 @@ int xtsonic_probe(struct platform_device *pdev) lp = netdev_priv(dev); lp->device = &pdev->dev; + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); netdev_boot_setup_check(dev); diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index 51b00941302c8d7e7eea3dcad6daa17c7702c65e..9eeddbd0b2c7c749de1c863e4b142c25f35c4fb9 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -8185,7 +8185,6 @@ mem_alloc_failed: free_shared_mem(sp); pci_disable_device(pdev); pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(dev); return ret; @@ -8221,7 +8220,6 @@ static void s2io_rem_nic(struct pci_dev *pdev) iounmap(sp->bar0); iounmap(sp->bar1); pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(dev); pci_disable_device(pdev); } diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index 5a20eaf903dd8b8833262166e4be4e65abd6f674..8614eeb7de8140114d256ae05c62ce95d5844a18 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -4739,7 +4739,6 @@ _exit6: _exit5: vxge_device_unregister(hldev); _exit4: - pci_set_drvdata(pdev, NULL); vxge_hw_device_terminate(hldev); pci_disable_sriov(pdev); _exit3: @@ -4782,7 +4781,6 @@ static void vxge_remove(struct pci_dev *pdev) vxge_free_mac_add_list(&vdev->vpaths[i]); vxge_device_unregister(hldev); - pci_set_drvdata(pdev, NULL); /* Do not call pci_disable_sriov here, as it will break child devices */ vxge_hw_device_terminate(hldev); iounmap(vdev->bar0); diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c index 622aa75904c4ee07c1b666466925e08ce0df25d3..1b326cbcd34b1bb9334635ff832b2cfd560ea905 100644 --- a/drivers/net/ethernet/octeon/octeon_mgmt.c +++ b/drivers/net/ethernet/octeon/octeon_mgmt.c @@ -1545,7 +1545,7 @@ static int octeon_mgmt_probe(struct platform_device *pdev) mac = of_get_mac_address(pdev->dev.of_node); - if (mac && is_valid_ether_addr(mac)) + if (mac) memcpy(netdev->dev_addr, mac, ETH_ALEN); else eth_hw_addr_random(netdev); diff --git a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h index 6797b1075874ae26cca6756bf450559ed7c7e6be..2a9003071d51a985be9b133fea76a585dabdf2d4 100644 --- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h +++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe.h @@ -653,38 +653,38 @@ struct pch_gbe_adapter { extern const char pch_driver_version[]; /* pch_gbe_main.c */ -extern int pch_gbe_up(struct pch_gbe_adapter *adapter); -extern void pch_gbe_down(struct pch_gbe_adapter *adapter); -extern void pch_gbe_reinit_locked(struct pch_gbe_adapter *adapter); -extern void pch_gbe_reset(struct pch_gbe_adapter *adapter); -extern int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter, - struct pch_gbe_tx_ring *txdr); -extern int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter, - struct pch_gbe_rx_ring *rxdr); -extern void pch_gbe_free_tx_resources(struct pch_gbe_adapter *adapter, - struct pch_gbe_tx_ring *tx_ring); -extern void pch_gbe_free_rx_resources(struct pch_gbe_adapter *adapter, - struct pch_gbe_rx_ring *rx_ring); -extern void pch_gbe_update_stats(struct pch_gbe_adapter *adapter); -extern u32 pch_ch_control_read(struct pci_dev *pdev); -extern void pch_ch_control_write(struct pci_dev *pdev, u32 val); -extern u32 pch_ch_event_read(struct pci_dev *pdev); -extern void pch_ch_event_write(struct pci_dev *pdev, u32 val); -extern u32 pch_src_uuid_lo_read(struct pci_dev *pdev); -extern u32 pch_src_uuid_hi_read(struct pci_dev *pdev); -extern u64 pch_rx_snap_read(struct pci_dev *pdev); -extern u64 pch_tx_snap_read(struct pci_dev *pdev); -extern int pch_set_station_address(u8 *addr, struct pci_dev *pdev); +int pch_gbe_up(struct pch_gbe_adapter *adapter); +void pch_gbe_down(struct pch_gbe_adapter *adapter); +void pch_gbe_reinit_locked(struct pch_gbe_adapter *adapter); +void pch_gbe_reset(struct pch_gbe_adapter *adapter); +int pch_gbe_setup_tx_resources(struct pch_gbe_adapter *adapter, + struct pch_gbe_tx_ring *txdr); +int pch_gbe_setup_rx_resources(struct pch_gbe_adapter *adapter, + struct pch_gbe_rx_ring *rxdr); +void pch_gbe_free_tx_resources(struct pch_gbe_adapter *adapter, + struct pch_gbe_tx_ring *tx_ring); +void pch_gbe_free_rx_resources(struct pch_gbe_adapter *adapter, + struct pch_gbe_rx_ring *rx_ring); +void pch_gbe_update_stats(struct pch_gbe_adapter *adapter); +u32 pch_ch_control_read(struct pci_dev *pdev); +void pch_ch_control_write(struct pci_dev *pdev, u32 val); +u32 pch_ch_event_read(struct pci_dev *pdev); +void pch_ch_event_write(struct pci_dev *pdev, u32 val); +u32 pch_src_uuid_lo_read(struct pci_dev *pdev); +u32 pch_src_uuid_hi_read(struct pci_dev *pdev); +u64 pch_rx_snap_read(struct pci_dev *pdev); +u64 pch_tx_snap_read(struct pci_dev *pdev); +int pch_set_station_address(u8 *addr, struct pci_dev *pdev); /* pch_gbe_param.c */ -extern void pch_gbe_check_options(struct pch_gbe_adapter *adapter); +void pch_gbe_check_options(struct pch_gbe_adapter *adapter); /* pch_gbe_ethtool.c */ -extern void pch_gbe_set_ethtool_ops(struct net_device *netdev); +void pch_gbe_set_ethtool_ops(struct net_device *netdev); /* pch_gbe_mac.c */ -extern s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw); -extern s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw); -extern u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, - u32 addr, u32 dir, u32 reg, u16 data); +s32 pch_gbe_mac_force_mac_fc(struct pch_gbe_hw *hw); +s32 pch_gbe_mac_read_mac_addr(struct pch_gbe_hw *hw); +u16 pch_gbe_mac_ctrl_miim(struct pch_gbe_hw *hw, u32 addr, u32 dir, u32 reg, + u16 data); #endif /* _PCH_GBE_H_ */ diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c index cac33e5f9bc2635cd2a4fd9b38c1af6f155164d8..b6bdeb3c19711ac960646dfdbeef2207b86f366d 100644 --- a/drivers/net/ethernet/packetengines/hamachi.c +++ b/drivers/net/ethernet/packetengines/hamachi.c @@ -1910,7 +1910,6 @@ static void hamachi_remove_one(struct pci_dev *pdev) iounmap(hmp->base); free_netdev(dev); pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); } } diff --git a/drivers/net/ethernet/packetengines/yellowfin.c b/drivers/net/ethernet/packetengines/yellowfin.c index d28593b1fc3e8664b1f6aad8e6d53dba4fdd1a5c..07a890eb72ad8677a47782f84f79c6f02ee1140e 100644 --- a/drivers/net/ethernet/packetengines/yellowfin.c +++ b/drivers/net/ethernet/packetengines/yellowfin.c @@ -513,7 +513,6 @@ err_out_unmap_rx: err_out_unmap_tx: pci_free_consistent(pdev, TX_TOTAL_SIZE, np->tx_ring, np->tx_ring_dma); err_out_cleardev: - pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, ioaddr); err_out_free_res: pci_release_regions(pdev); @@ -1392,7 +1391,6 @@ static void yellowfin_remove_one(struct pci_dev *pdev) pci_release_regions (pdev); free_netdev (dev); - pci_set_drvdata(pdev, NULL); } diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c index 5b65356e7568b28e0d2030c7436863db10a44b32..dbaa49e58b0cd8610c71a83c8170c7f10255453e 100644 --- a/drivers/net/ethernet/pasemi/pasemi_mac.c +++ b/drivers/net/ethernet/pasemi/pasemi_mac.c @@ -1870,7 +1870,6 @@ static void pasemi_mac_remove(struct pci_dev *pdev) pasemi_dma_free_chan(&mac->tx->chan); pasemi_dma_free_chan(&mac->rx->chan); - pci_set_drvdata(pdev, NULL); free_netdev(netdev); } diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index 32675e16021e8aafd2a5598473cc2ad54dae7668..9adcdbb49476e53face68f353dd7261ffea356b3 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -53,8 +53,8 @@ #define _NETXEN_NIC_LINUX_MAJOR 4 #define _NETXEN_NIC_LINUX_MINOR 0 -#define _NETXEN_NIC_LINUX_SUBVERSION 81 -#define NETXEN_NIC_LINUX_VERSIONID "4.0.81" +#define _NETXEN_NIC_LINUX_SUBVERSION 82 +#define NETXEN_NIC_LINUX_VERSIONID "4.0.82" #define NETXEN_VERSION_CODE(a, b, c) (((a) << 24) + ((b) << 16) + (c)) #define _major(v) (((v) >> 24) & 0xff) @@ -1883,9 +1883,8 @@ static inline u32 netxen_tx_avail(struct nx_host_tx_ring *tx_ring) int netxen_get_flash_mac_addr(struct netxen_adapter *adapter, u64 *mac); int netxen_p3_get_mac_addr(struct netxen_adapter *adapter, u64 *mac); -extern void netxen_change_ringparam(struct netxen_adapter *adapter); -extern int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, - int *valp); +void netxen_change_ringparam(struct netxen_adapter *adapter); +int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp); extern const struct ethtool_ops netxen_nic_ethtool_ops; diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h index 32c790659f9c1ba0410614c30818eb9cf20a46ee..0c64c82b9acffdcfd58b9471f971d0eebe95a9cc 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hdr.h @@ -958,6 +958,7 @@ enum { #define NETXEN_PEG_HALT_STATUS2 (NETXEN_CAM_RAM(0xac)) #define NX_CRB_DEV_REF_COUNT (NETXEN_CAM_RAM(0x138)) #define NX_CRB_DEV_STATE (NETXEN_CAM_RAM(0x140)) +#define NETXEN_ULA_KEY (NETXEN_CAM_RAM(0x178)) /* MiniDIMM related macros */ #define NETXEN_DIMM_CAPABILITY (NETXEN_CAM_RAM(0x258)) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c index 8375cbde996976047475b9c5e6c16f33fd365df4..67efe754367de10d78e6bb1cbacd65caa6f1524c 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_hw.c @@ -648,7 +648,7 @@ nx_p3_sre_macaddr_change(struct netxen_adapter *adapter, u8 *addr, unsigned op) mac_req = (nx_mac_req_t *)&req.words[0]; mac_req->op = op; - memcpy(mac_req->mac_addr, addr, 6); + memcpy(mac_req->mac_addr, addr, ETH_ALEN); return netxen_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1); } diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index cbd75f97ffb3c6d32624e15b81adcb05d5d4b1d9..3bec8cfebf99299ef9c3733b47033a26fe8ba7b1 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -1415,6 +1415,32 @@ netxen_setup_netdev(struct netxen_adapter *adapter, return 0; } +#define NETXEN_ULA_ADAPTER_KEY (0xdaddad01) +#define NETXEN_NON_ULA_ADAPTER_KEY (0xdaddad00) + +static void netxen_read_ula_info(struct netxen_adapter *adapter) +{ + u32 temp; + + /* Print ULA info only once for an adapter */ + if (adapter->portnum != 0) + return; + + temp = NXRD32(adapter, NETXEN_ULA_KEY); + switch (temp) { + case NETXEN_ULA_ADAPTER_KEY: + dev_info(&adapter->pdev->dev, "ULA adapter"); + break; + case NETXEN_NON_ULA_ADAPTER_KEY: + dev_info(&adapter->pdev->dev, "non ULA adapter"); + break; + default: + break; + } + + return; +} + #ifdef CONFIG_PCIEAER static void netxen_mask_aer_correctable(struct netxen_adapter *adapter) { @@ -1561,6 +1587,8 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_disable_msi; } + netxen_read_ula_info(adapter); + err = netxen_setup_netdev(adapter, netdev); if (err) goto err_out_disable_msi; @@ -1602,7 +1630,6 @@ err_out_free_res: pci_release_regions(pdev); err_out_disable_pdev: - pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); return err; } @@ -1661,7 +1688,6 @@ static void netxen_nic_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(netdev); } diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index 91a8fcd6c24654e52d94aa6a96f68bf02e32a4ee..0758b9435358b9f4012e703a2605ad64ef4aff7c 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -3916,7 +3916,6 @@ err_out_free_regions: pci_release_regions(pdev); err_out_disable_pdev: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); err_out: return err; } @@ -3939,7 +3938,6 @@ static void ql3xxx_remove(struct pci_dev *pdev) iounmap(qdev->mem_map_registers); pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(ndev); } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 81bf83604c4fb1c04aa0a5dfe361d1ffaefd160d..631ea0ac1cd89c47bddb863c56f04c28469bd745 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -38,8 +38,8 @@ #define _QLCNIC_LINUX_MAJOR 5 #define _QLCNIC_LINUX_MINOR 3 -#define _QLCNIC_LINUX_SUBVERSION 50 -#define QLCNIC_LINUX_VERSIONID "5.3.50" +#define _QLCNIC_LINUX_SUBVERSION 52 +#define QLCNIC_LINUX_VERSIONID "5.3.52" #define QLCNIC_DRV_IDC_VER 0x01 #define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\ (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) @@ -98,8 +98,22 @@ #define TX_STOP_THRESH ((MAX_SKB_FRAGS >> 2) + MAX_TSO_HEADER_DESC \ + MGMT_CMD_DESC_RESV) #define QLCNIC_MAX_TX_TIMEOUTS 2 -#define QLCNIC_MAX_TX_RINGS 8 -#define QLCNIC_MAX_SDS_RINGS 8 + +/* Driver will use 1 Tx ring in INT-x/MSI/SRIOV mode. */ +#define QLCNIC_SINGLE_RING 1 +#define QLCNIC_DEF_SDS_RINGS 4 +#define QLCNIC_DEF_TX_RINGS 4 +#define QLCNIC_MAX_VNIC_TX_RINGS 4 +#define QLCNIC_MAX_VNIC_SDS_RINGS 4 + +enum qlcnic_queue_type { + QLCNIC_TX_QUEUE = 1, + QLCNIC_RX_QUEUE, +}; + +/* Operational mode for driver */ +#define QLCNIC_VNIC_MODE 0xFF +#define QLCNIC_DEFAULT_MODE 0x0 /* * Following are the states of the Phantom. Phantom will set them and @@ -533,6 +547,14 @@ struct qlcnic_host_sds_ring { char name[IFNAMSIZ + 12]; } ____cacheline_internodealigned_in_smp; +struct qlcnic_tx_queue_stats { + u64 xmit_on; + u64 xmit_off; + u64 xmit_called; + u64 xmit_finished; + u64 tx_bytes; +}; + struct qlcnic_host_tx_ring { int irq; void __iomem *crb_intr_mask; @@ -544,10 +566,7 @@ struct qlcnic_host_tx_ring { u32 sw_consumer; u32 num_desc; - u64 xmit_on; - u64 xmit_off; - u64 xmit_called; - u64 xmit_finished; + struct qlcnic_tx_queue_stats tx_stats; void __iomem *crb_cmd_producer; struct cmd_desc_type0 *desc_head; @@ -940,8 +959,6 @@ struct qlcnic_ipaddr { #define QLCNIC_BEACON_EANBLE 0xC #define QLCNIC_BEACON_DISABLE 0xD -#define QLCNIC_DEF_NUM_STS_DESC_RINGS 4 -#define QLCNIC_DEF_NUM_TX_RINGS 4 #define QLCNIC_MSIX_TBL_SPACE 8192 #define QLCNIC_PCI_REG_MSIX_TBL 0x44 #define QLCNIC_MSIX_TBL_PGSIZE 4096 @@ -961,8 +978,7 @@ struct qlcnic_ipaddr { #define __QLCNIC_SRIOV_CAPABLE 11 #define __QLCNIC_MBX_POLL_ENABLE 12 #define __QLCNIC_DIAG_MODE 13 -#define __QLCNIC_DCB_STATE 14 -#define __QLCNIC_DCB_IN_AEN 15 +#define __QLCNIC_MAINTENANCE_MODE 16 #define QLCNIC_INTERRUPT_TEST 1 #define QLCNIC_LOOPBACK_TEST 2 @@ -1013,7 +1029,6 @@ struct qlcnic_adapter { unsigned long state; u32 flags; - int max_drv_tx_rings; u16 num_txd; u16 num_rxd; u16 num_jumbo_rxd; @@ -1021,7 +1036,13 @@ struct qlcnic_adapter { u16 max_jumbo_rxd; u8 max_rds_rings; - u8 max_sds_rings; + + u8 max_sds_rings; /* max sds rings supported by adapter */ + u8 max_tx_rings; /* max tx rings supported by adapter */ + + u8 drv_tx_rings; /* max tx rings supported by driver */ + u8 drv_sds_rings; /* max sds rings supported by driver */ + u8 rx_csum; u8 portnum; @@ -1199,6 +1220,7 @@ struct qlcnic_npar_info { u8 promisc_mode; u8 offload_flags; u8 pci_func; + u8 mac[ETH_ALEN]; }; struct qlcnic_eswitch { @@ -1543,12 +1565,13 @@ int qlcnic_loopback_test(struct net_device *, u8); /* Functions from qlcnic_main.c */ int qlcnic_reset_context(struct qlcnic_adapter *); -void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings); -int qlcnic_diag_alloc_res(struct net_device *netdev, int test); -netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); -int qlcnic_set_max_rss(struct qlcnic_adapter *, u8, int); -int qlcnic_validate_max_rss(struct qlcnic_adapter *, __u32); -int qlcnic_validate_max_tx_rings(struct qlcnic_adapter *, u32 txq); +void qlcnic_diag_free_res(struct net_device *netdev, int); +int qlcnic_diag_alloc_res(struct net_device *netdev, int); +netdev_tx_t qlcnic_xmit_frame(struct sk_buff *, struct net_device *); +void qlcnic_set_tx_ring_count(struct qlcnic_adapter *, u8); +void qlcnic_set_sds_ring_count(struct qlcnic_adapter *, u8); +int qlcnic_setup_rings(struct qlcnic_adapter *, u8, u8); +int qlcnic_validate_rings(struct qlcnic_adapter *, __u32, int); void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter); void qlcnic_82xx_set_mac_filter_count(struct qlcnic_adapter *); int qlcnic_enable_msix(struct qlcnic_adapter *, u32); @@ -1641,19 +1664,18 @@ static inline u32 qlcnic_tx_avail(struct qlcnic_host_tx_ring *tx_ring) static inline int qlcnic_set_real_num_queues(struct qlcnic_adapter *adapter, struct net_device *netdev) { - int err, tx_q; - - tx_q = adapter->max_drv_tx_rings; + int err; - netdev->num_tx_queues = tx_q; - netdev->real_num_tx_queues = tx_q; + netdev->num_tx_queues = adapter->drv_tx_rings; + netdev->real_num_tx_queues = adapter->drv_tx_rings; - err = netif_set_real_num_tx_queues(netdev, tx_q); + err = netif_set_real_num_tx_queues(netdev, adapter->drv_tx_rings); if (err) dev_err(&adapter->pdev->dev, "failed to set %d Tx queues\n", - tx_q); + adapter->drv_tx_rings); else - dev_info(&adapter->pdev->dev, "set %d Tx queues\n", tx_q); + dev_info(&adapter->pdev->dev, "Set %d Tx queues\n", + adapter->drv_tx_rings); return err; } @@ -1695,7 +1717,7 @@ struct qlcnic_hardware_ops { int (*write_reg) (struct qlcnic_adapter *, ulong, u32); void (*get_ocm_win) (struct qlcnic_hardware_context *); int (*get_mac_address) (struct qlcnic_adapter *, u8 *, u8); - int (*setup_intr) (struct qlcnic_adapter *, u8, int); + int (*setup_intr) (struct qlcnic_adapter *); int (*alloc_mbx_args)(struct qlcnic_cmd_args *, struct qlcnic_adapter *, u32); int (*mbx_cmd) (struct qlcnic_adapter *, struct qlcnic_cmd_args *); @@ -1766,10 +1788,9 @@ static inline int qlcnic_get_mac_address(struct qlcnic_adapter *adapter, return adapter->ahw->hw_ops->get_mac_address(adapter, mac, function); } -static inline int qlcnic_setup_intr(struct qlcnic_adapter *adapter, - u8 num_intr, int txq) +static inline int qlcnic_setup_intr(struct qlcnic_adapter *adapter) { - return adapter->ahw->hw_ops->setup_intr(adapter, num_intr, txq); + return adapter->ahw->hw_ops->setup_intr(adapter); } static inline int qlcnic_alloc_mbx_args(struct qlcnic_cmd_args *mbx, @@ -2005,7 +2026,7 @@ static inline bool qlcnic_check_multi_tx(struct qlcnic_adapter *adapter) static inline void qlcnic_disable_multi_tx(struct qlcnic_adapter *adapter) { test_and_clear_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state); - adapter->max_drv_tx_rings = 1; + adapter->drv_tx_rings = QLCNIC_SINGLE_RING; } /* When operating in a muti tx mode, driver needs to write 0x1 @@ -2115,98 +2136,4 @@ static inline bool qlcnic_sriov_vf_check(struct qlcnic_adapter *adapter) return status; } - -static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_adapter *adapter) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->get_hw_capability) - return dcb->ops->get_hw_capability(adapter); - - return 0; -} - -static inline void qlcnic_dcb_free(struct qlcnic_adapter *adapter) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->free) - dcb->ops->free(adapter); -} - -static inline int qlcnic_dcb_attach(struct qlcnic_adapter *adapter) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->attach) - return dcb->ops->attach(adapter); - - return 0; -} - -static inline int -qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter, char *buf) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->query_hw_capability) - return dcb->ops->query_hw_capability(adapter, buf); - - return 0; -} - -static inline void qlcnic_dcb_get_info(struct qlcnic_adapter *adapter) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->get_info) - dcb->ops->get_info(adapter); -} - -static inline int -qlcnic_dcb_query_cee_param(struct qlcnic_adapter *adapter, char *buf, u8 type) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->query_cee_param) - return dcb->ops->query_cee_param(adapter, buf, type); - - return 0; -} - -static inline int qlcnic_dcb_get_cee_cfg(struct qlcnic_adapter *adapter) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->get_cee_cfg) - return dcb->ops->get_cee_cfg(adapter); - - return 0; -} - -static inline void -qlcnic_dcb_register_aen(struct qlcnic_adapter *adapter, u8 flag) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->register_aen) - dcb->ops->register_aen(adapter, flag); -} - -static inline void qlcnic_dcb_handle_aen(struct qlcnic_adapter *adapter, - void *msg) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->handle_aen) - dcb->ops->handle_aen(adapter, msg); -} - -static inline void qlcnic_dcb_init_dcbnl_ops(struct qlcnic_adapter *adapter) -{ - struct qlcnic_dcb *dcb = adapter->dcb; - - if (dcb && dcb->ops->init_dcbnl_ops) - dcb->ops->init_dcbnl_ops(adapter); -} #endif /* __QLCNIC_H_ */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index ace217c447ddd182d0143bb2977b2e62b23ba883..09810ddd11ec3231bb34c6e79e3235c24bbcc546 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -13,7 +13,6 @@ #include #include -#define QLCNIC_MAX_TX_QUEUES 1 #define RSS_HASHTYPE_IP_TCP 0x3 #define QLC_83XX_FW_MBX_CMD 0 @@ -268,20 +267,18 @@ int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *adapter, ulong addr, } } -int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr, int txq) +int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter) { int err, i, num_msix; struct qlcnic_hardware_context *ahw = adapter->ahw; - if (!num_intr) - num_intr = QLCNIC_DEF_NUM_STS_DESC_RINGS; - num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(), - num_intr)); + num_msix = adapter->drv_sds_rings; + /* account for AEN interrupt MSI-X based interrupts */ num_msix += 1; if (!(adapter->flags & QLCNIC_TX_INTR_SHARED)) - num_msix += adapter->max_drv_tx_rings; + num_msix += adapter->drv_tx_rings; err = qlcnic_enable_msix(adapter, num_msix); if (err == -ENOMEM) @@ -325,7 +322,8 @@ inline void qlcnic_83xx_clear_legacy_intr_mask(struct qlcnic_adapter *adapter) inline void qlcnic_83xx_set_legacy_intr_mask(struct qlcnic_adapter *adapter) { - writel(1, adapter->tgt_mask_reg); + if (adapter->tgt_mask_reg) + writel(1, adapter->tgt_mask_reg); } /* Enable MSI-x and INT-x interrupts */ @@ -498,8 +496,11 @@ void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter) num_msix = 0; msleep(20); - synchronize_irq(adapter->msix_entries[num_msix].vector); - free_irq(adapter->msix_entries[num_msix].vector, adapter); + + if (adapter->msix_entries) { + synchronize_irq(adapter->msix_entries[num_msix].vector); + free_irq(adapter->msix_entries[num_msix].vector, adapter); + } } int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter) @@ -760,6 +761,9 @@ int qlcnic_83xx_issue_cmd(struct qlcnic_adapter *adapter, int cmd_type, err, opcode; unsigned long timeout; + if (!mbx) + return -EIO; + opcode = LSW(cmd->req.arg[0]); cmd_type = cmd->type; err = mbx->ops->enqueue_cmd(adapter, cmd, &timeout); @@ -902,7 +906,7 @@ void __qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter) QLCNIC_MBX_RSP(event[0])); break; case QLCNIC_MBX_DCBX_CONFIG_CHANGE_EVENT: - qlcnic_dcb_handle_aen(adapter, (void *)&event[1]); + qlcnic_dcb_aen_handler(adapter->dcb, (void *)&event[1]); break; default: dev_dbg(&adapter->pdev->dev, "Unsupported AEN:0x%x.\n", @@ -979,14 +983,14 @@ static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter) sds_mbx_size = sizeof(struct qlcnic_sds_mbx); context_id = recv_ctx->context_id; - num_sds = (adapter->max_sds_rings - QLCNIC_MAX_RING_SETS); + num_sds = adapter->drv_sds_rings - QLCNIC_MAX_SDS_RINGS; ahw->hw_ops->alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_ADD_RCV_RINGS); cmd.req.arg[1] = 0 | (num_sds << 8) | (context_id << 16); /* set up status rings, mbx 2-81 */ index = 2; - for (i = 8; i < adapter->max_sds_rings; i++) { + for (i = 8; i < adapter->drv_sds_rings; i++) { memset(&sds_mbx, 0, sds_mbx_size); sds = &recv_ctx->sds_rings[i]; sds->consumer = 0; @@ -1021,7 +1025,7 @@ static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter) mbx_out = (struct qlcnic_add_rings_mbx_out *)&cmd.rsp.arg[1]; index = 0; /* status descriptor ring */ - for (i = 8; i < adapter->max_sds_rings; i++) { + for (i = 8; i < adapter->drv_sds_rings; i++) { sds = &recv_ctx->sds_rings[i]; sds->crb_sts_consumer = ahw->pci_base0 + mbx_out->host_csmr[index]; @@ -1079,10 +1083,10 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter) struct qlcnic_hardware_context *ahw = adapter->ahw; num_rds = adapter->max_rds_rings; - if (adapter->max_sds_rings <= QLCNIC_MAX_RING_SETS) - num_sds = adapter->max_sds_rings; + if (adapter->drv_sds_rings <= QLCNIC_MAX_SDS_RINGS) + num_sds = adapter->drv_sds_rings; else - num_sds = QLCNIC_MAX_RING_SETS; + num_sds = QLCNIC_MAX_SDS_RINGS; sds_mbx_size = sizeof(struct qlcnic_sds_mbx); rds_mbx_size = sizeof(struct qlcnic_rds_mbx); @@ -1183,7 +1187,7 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter) sds->crb_intr_mask = ahw->pci_base0 + intr_mask; } - if (adapter->max_sds_rings > QLCNIC_MAX_RING_SETS) + if (adapter->drv_sds_rings > QLCNIC_MAX_SDS_RINGS) err = qlcnic_83xx_add_rings(adapter); out: qlcnic_free_mbx_args(&cmd); @@ -1239,9 +1243,9 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter, mbx.size = tx->num_desc; if (adapter->flags & QLCNIC_MSIX_ENABLED) { if (!(adapter->flags & QLCNIC_TX_INTR_SHARED)) - msix_vector = adapter->max_sds_rings + ring; + msix_vector = adapter->drv_sds_rings + ring; else - msix_vector = adapter->max_sds_rings - 1; + msix_vector = adapter->drv_sds_rings - 1; msix_id = ahw->intr_tbl[msix_vector].id; } else { msix_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID); @@ -1264,7 +1268,8 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter, qlcnic_pf_set_interface_id_create_tx_ctx(adapter, &temp); cmd.req.arg[1] = QLCNIC_CAP0_LEGACY_CONTEXT; - cmd.req.arg[5] = QLCNIC_MAX_TX_QUEUES | temp; + cmd.req.arg[5] = QLCNIC_SINGLE_RING | temp; + buf = &cmd.req.arg[6]; memcpy(buf, &mbx, sizeof(struct qlcnic_tx_mbx)); /* send the mailbox command*/ @@ -1279,7 +1284,7 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter, tx->ctx_id = mbx_out->ctx_id; if ((adapter->flags & QLCNIC_MSIX_ENABLED) && !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { - intr_mask = ahw->intr_tbl[adapter->max_sds_rings + ring].src; + intr_mask = ahw->intr_tbl[adapter->drv_sds_rings + ring].src; tx->crb_intr_mask = ahw->pci_base0 + intr_mask; } dev_info(&adapter->pdev->dev, "Tx Context[0x%x] Created, state:0x%x\n", @@ -1290,7 +1295,7 @@ out: } static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test, - int num_sds_ring) + u8 num_sds_ring) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_host_sds_ring *sds_ring; @@ -1306,7 +1311,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test, qlcnic_detach(adapter); - adapter->max_sds_rings = 1; + adapter->drv_sds_rings = QLCNIC_SINGLE_RING; adapter->ahw->diag_test = test; adapter->ahw->linkup = 0; @@ -1320,7 +1325,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test, if (ret) { qlcnic_detach(adapter); if (adapter_state == QLCNIC_ADAPTER_UP_MAGIC) { - adapter->max_sds_rings = num_sds_ring; + adapter->drv_sds_rings = num_sds_ring; qlcnic_attach(adapter); } netif_device_attach(netdev); @@ -1333,7 +1338,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test, } if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) { - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &adapter->recv_ctx->sds_rings[ring]; qlcnic_83xx_enable_intr(adapter, sds_ring); } @@ -1354,7 +1359,7 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test, } static void qlcnic_83xx_diag_free_res(struct net_device *netdev, - int max_sds_rings) + u8 drv_sds_rings) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_host_sds_ring *sds_ring; @@ -1362,7 +1367,7 @@ static void qlcnic_83xx_diag_free_res(struct net_device *netdev, clear_bit(__QLCNIC_DEV_UP, &adapter->state); if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) { - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &adapter->recv_ctx->sds_rings[ring]; qlcnic_83xx_disable_intr(adapter, sds_ring); if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) @@ -1386,7 +1391,7 @@ static void qlcnic_83xx_diag_free_res(struct net_device *netdev, } } adapter->ahw->diag_test = 0; - adapter->max_sds_rings = max_sds_rings; + adapter->drv_sds_rings = drv_sds_rings; if (qlcnic_attach(adapter)) goto out; @@ -1648,7 +1653,9 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_hardware_context *ahw = adapter->ahw; - int ret = 0, loop = 0, max_sds_rings = adapter->max_sds_rings; + u8 drv_sds_rings = adapter->drv_sds_rings; + u8 drv_tx_rings = adapter->drv_tx_rings; + int ret = 0, loop = 0; if (ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { netdev_warn(netdev, @@ -1670,7 +1677,7 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode) mode == QLCNIC_ILB_MODE ? "internal" : "external"); ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_LOOPBACK_TEST, - max_sds_rings); + drv_sds_rings); if (ret) goto fail_diag_alloc; @@ -1708,10 +1715,11 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode) qlcnic_83xx_clear_lb_mode(adapter, mode); free_diag_res: - qlcnic_83xx_diag_free_res(netdev, max_sds_rings); + qlcnic_83xx_diag_free_res(netdev, drv_sds_rings); fail_diag_alloc: - adapter->max_sds_rings = max_sds_rings; + adapter->drv_sds_rings = drv_sds_rings; + adapter->drv_tx_rings = drv_tx_rings; qlcnic_release_diag_lock(adapter); return ret; } @@ -2321,19 +2329,7 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter, i++; memcpy(pci_info->mac + sizeof(u32), &cmd.rsp.arg[i], 2); i = i + 3; - if (ahw->op_mode == QLCNIC_MGMT_FUNC) - dev_info(dev, "id = %d active = %d type = %d\n" - "\tport = %d min bw = %d max bw = %d\n" - "\tmac_addr = %pM\n", pci_info->id, - pci_info->active, pci_info->type, - pci_info->default_port, - pci_info->tx_min_bw, - pci_info->tx_max_bw, pci_info->mac); } - if (ahw->op_mode == QLCNIC_MGMT_FUNC) - dev_info(dev, "Max functions = %d, active functions = %d\n", - ahw->max_pci_func, ahw->act_pci_func); - } else { dev_err(dev, "Failed to get PCI Info, error = %d\n", err); err = -EIO; @@ -3061,11 +3057,14 @@ int qlcnic_83xx_get_settings(struct qlcnic_adapter *adapter, int status = 0; struct qlcnic_hardware_context *ahw = adapter->ahw; - /* Get port configuration info */ - status = qlcnic_83xx_get_port_info(adapter); - /* Get Link Status related info */ - config = qlcnic_83xx_test_link(adapter); - ahw->module_type = QLC_83XX_SFP_MODULE_TYPE(config); + if (!test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) { + /* Get port configuration info */ + status = qlcnic_83xx_get_port_info(adapter); + /* Get Link Status related info */ + config = qlcnic_83xx_test_link(adapter); + ahw->module_type = QLC_83XX_SFP_MODULE_TYPE(config); + } + /* hard code until there is a way to get it from flash */ ahw->board_type = QLCNIC_BRDTYPE_83XX_10G; @@ -3279,12 +3278,12 @@ int qlcnic_83xx_reg_test(struct qlcnic_adapter *adapter) return 0; } -int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *adapter) +inline int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *adapter) { return (ARRAY_SIZE(qlcnic_83xx_ext_reg_tbl) * - sizeof(adapter->ahw->ext_reg_tbl)) + - (ARRAY_SIZE(qlcnic_83xx_reg_tbl) + - sizeof(adapter->ahw->reg_tbl)); + sizeof(*adapter->ahw->ext_reg_tbl)) + + (ARRAY_SIZE(qlcnic_83xx_reg_tbl) * + sizeof(*adapter->ahw->reg_tbl)); } int qlcnic_83xx_get_registers(struct qlcnic_adapter *adapter, u32 *regs_buff) @@ -3305,10 +3304,11 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev) struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_hardware_context *ahw = adapter->ahw; struct qlcnic_cmd_args cmd; + u8 val, drv_sds_rings = adapter->drv_sds_rings; + u8 drv_tx_rings = adapter->drv_tx_rings; u32 data; u16 intrpt_id, id; - u8 val; - int ret, max_sds_rings = adapter->max_sds_rings; + int ret; if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { netdev_info(netdev, "Device is resetting\n"); @@ -3321,7 +3321,7 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev) } ret = qlcnic_83xx_diag_alloc_res(netdev, QLCNIC_INTERRUPT_TEST, - max_sds_rings); + drv_sds_rings); if (ret) goto fail_diag_irq; @@ -3358,10 +3358,11 @@ int qlcnic_83xx_interrupt_test(struct net_device *netdev) done: qlcnic_free_mbx_args(&cmd); - qlcnic_83xx_diag_free_res(netdev, max_sds_rings); + qlcnic_83xx_diag_free_res(netdev, drv_sds_rings); fail_diag_irq: - adapter->max_sds_rings = max_sds_rings; + adapter->drv_sds_rings = drv_sds_rings; + adapter->drv_tx_rings = drv_tx_rings; qlcnic_release_diag_lock(adapter); return ret; } @@ -3381,10 +3382,21 @@ void qlcnic_83xx_get_pauseparam(struct qlcnic_adapter *adapter, } config = ahw->port_config; if (config & QLC_83XX_CFG_STD_PAUSE) { - if (config & QLC_83XX_CFG_STD_TX_PAUSE) + switch (MSW(config)) { + case QLC_83XX_TX_PAUSE: + pause->tx_pause = 1; + break; + case QLC_83XX_RX_PAUSE: + pause->rx_pause = 1; + break; + case QLC_83XX_TX_RX_PAUSE: + default: + /* Backward compatibility for existing + * flash definitions + */ pause->tx_pause = 1; - if (config & QLC_83XX_CFG_STD_RX_PAUSE) pause->rx_pause = 1; + } } if (QLC_83XX_AUTONEG(config)) @@ -3427,7 +3439,8 @@ int qlcnic_83xx_set_pauseparam(struct qlcnic_adapter *adapter, ahw->port_config &= ~QLC_83XX_CFG_STD_RX_PAUSE; ahw->port_config |= QLC_83XX_CFG_STD_TX_PAUSE; } else if (!pause->rx_pause && !pause->tx_pause) { - ahw->port_config &= ~QLC_83XX_CFG_STD_TX_RX_PAUSE; + ahw->port_config &= ~(QLC_83XX_CFG_STD_TX_RX_PAUSE | + QLC_83XX_CFG_STD_PAUSE); } status = qlcnic_83xx_set_port_config(adapter); if (status) { @@ -3503,7 +3516,7 @@ int qlcnic_83xx_resume(struct qlcnic_adapter *adapter) if (err) return err; - if (ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) { + if (ahw->nic_mode == QLCNIC_VNIC_MODE) { if (ahw->op_mode == QLCNIC_MGMT_FUNC) { qlcnic_83xx_set_vnic_opmode(adapter); } else { @@ -3530,6 +3543,9 @@ void qlcnic_83xx_reinit_mbx_work(struct qlcnic_mailbox *mbx) void qlcnic_83xx_free_mailbox(struct qlcnic_mailbox *mbx) { + if (!mbx) + return; + destroy_workqueue(mbx->work_q); kfree(mbx); } @@ -3650,6 +3666,9 @@ void qlcnic_83xx_detach_mailbox_work(struct qlcnic_adapter *adapter) { struct qlcnic_mailbox *mbx = adapter->ahw->mailbox; + if (!mbx) + return; + clear_bit(QLC_83XX_MBX_READY, &mbx->status); complete(&mbx->completion); cancel_work_sync(&mbx->work); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h index 533e150503afc06fb5db107d1fa4bdbcb894abb8..4cae6caa6bfa42476ff79b881784dd2f42a36db6 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h @@ -61,7 +61,6 @@ #define QLC_83XX_HOST_SDS_MBX_IDX 8 #define QLCNIC_HOST_RDS_MBX_IDX 88 -#define QLCNIC_MAX_RING_SETS 8 /* Pause control registers */ #define QLC_83XX_SRE_SHIM_REG 0x0D200284 @@ -183,8 +182,8 @@ struct qlcnic_rcv_mbx_out { u8 num_pci_func; u8 state; #endif - u32 host_csmr[QLCNIC_MAX_RING_SETS]; - struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS]; + u32 host_csmr[QLCNIC_MAX_SDS_RINGS]; + struct __host_producer_mbx host_prod[QLCNIC_MAX_SDS_RINGS]; } __packed; struct qlcnic_add_rings_mbx_out { @@ -197,8 +196,8 @@ struct qlcnic_add_rings_mbx_out { u8 sts_num; u8 rcv_num; #endif - u32 host_csmr[QLCNIC_MAX_RING_SETS]; - struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS]; + u32 host_csmr[QLCNIC_MAX_SDS_RINGS]; + struct __host_producer_mbx host_prod[QLCNIC_MAX_SDS_RINGS]; } __packed; /* Transmit context mailbox inbox registers @@ -363,6 +362,9 @@ enum qlcnic_83xx_states { #define QLC_83XX_LINK_EEE(data) ((data) & BIT_13) #define QLC_83XX_DCBX(data) (((data) >> 28) & 7) #define QLC_83XX_AUTONEG(data) ((data) & BIT_15) +#define QLC_83XX_TX_PAUSE 0x10 +#define QLC_83XX_RX_PAUSE 0x20 +#define QLC_83XX_TX_RX_PAUSE 0x30 #define QLC_83XX_CFG_STD_PAUSE (1 << 5) #define QLC_83XX_CFG_STD_TX_PAUSE (1 << 20) #define QLC_83XX_CFG_STD_RX_PAUSE (2 << 20) @@ -412,8 +414,6 @@ enum qlcnic_83xx_states { #define QLC_83XX_GET_VLAN_ALIGN_CAPABILITY(val) (val & 0x4000) #define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val) (val & 0x20000) #define QLC_83XX_ESWITCH_CAPABILITY BIT_23 -#define QLC_83XX_VIRTUAL_NIC_MODE 0xFF -#define QLC_83XX_DEFAULT_MODE 0x0 #define QLC_83XX_SRIOV_MODE 0x1 #define QLCNIC_BRDTYPE_83XX_10G 0x0083 @@ -521,7 +521,7 @@ enum qlc_83xx_ext_regs { /* 83xx funcitons */ int qlcnic_83xx_get_fw_version(struct qlcnic_adapter *); int qlcnic_83xx_issue_cmd(struct qlcnic_adapter *, struct qlcnic_cmd_args *); -int qlcnic_83xx_setup_intr(struct qlcnic_adapter *, u8, int); +int qlcnic_83xx_setup_intr(struct qlcnic_adapter *); void qlcnic_83xx_get_func_no(struct qlcnic_adapter *); int qlcnic_83xx_cam_lock(struct qlcnic_adapter *); void qlcnic_83xx_cam_unlock(struct qlcnic_adapter *); @@ -626,7 +626,7 @@ int qlcnic_83xx_config_vnic_opmode(struct qlcnic_adapter *); int qlcnic_83xx_get_vnic_vport_info(struct qlcnic_adapter *, struct qlcnic_info *, u8); int qlcnic_83xx_get_vnic_pf_info(struct qlcnic_adapter *, struct qlcnic_info *); -int qlcnic_83xx_enable_port_eswitch(struct qlcnic_adapter *, int); +int qlcnic_83xx_set_port_eswitch_status(struct qlcnic_adapter *, int, int *); void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *); void qlcnic_83xx_get_stats(struct qlcnic_adapter *adapter, u64 *data); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c index f09e787af0b25ae9d523f531b47b67805c7bc8e0..89208e5b25d6a1bc5c829a52ea2d79edcb5d110e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c @@ -636,7 +636,7 @@ int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter) if (adapter->portnum == 0) qlcnic_set_drv_version(adapter); - qlcnic_dcb_get_info(adapter); + qlcnic_dcb_get_info(adapter->dcb); qlcnic_83xx_idc_attach_driver(adapter); return 0; @@ -818,6 +818,7 @@ static int qlcnic_83xx_idc_ready_state(struct qlcnic_adapter *adapter) struct qlcnic_hardware_context *ahw = adapter->ahw; struct qlcnic_mailbox *mbx = ahw->mailbox; int ret = 0; + u32 owner; u32 val; /* Perform NIC configuration based ready state entry actions */ @@ -846,6 +847,10 @@ static int qlcnic_83xx_idc_ready_state(struct qlcnic_adapter *adapter) clear_bit(QLC_83XX_MBX_READY, &mbx->status); set_bit(__QLCNIC_RESETTING, &adapter->state); qlcnic_83xx_idc_enter_need_reset_state(adapter, 1); + } else { + owner = qlcnic_83xx_idc_find_reset_owner_id(adapter); + if (ahw->pci_func == owner) + qlcnic_dump_fw(adapter); } return -EIO; } @@ -897,7 +902,7 @@ static int qlcnic_83xx_idc_need_reset_state(struct qlcnic_adapter *adapter) qlcnic_83xx_idc_update_audit_reg(adapter, 0, 1); set_bit(__QLCNIC_RESETTING, &adapter->state); clear_bit(QLC_83XX_MBX_READY, &mbx->status); - if (adapter->ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) + if (adapter->ahw->nic_mode == QLCNIC_VNIC_MODE) qlcnic_83xx_disable_vnic_mode(adapter, 1); if (qlcnic_check_diag_status(adapter)) { @@ -1058,6 +1063,12 @@ void qlcnic_83xx_idc_poll_dev_state(struct work_struct *work) adapter->ahw->idc.prev_state = adapter->ahw->idc.curr_state; qlcnic_83xx_periodic_tasks(adapter); + /* Do not reschedule if firmaware is in hanged state and auto + * recovery is disabled + */ + if ((adapter->flags & QLCNIC_FW_HANG) && !qlcnic_auto_fw_reset) + return; + /* Re-schedule the function */ if (test_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status)) qlcnic_schedule_work(adapter, qlcnic_83xx_idc_poll_dev_state, @@ -2022,6 +2033,8 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter) ahw->max_mac_filters = nic_info.max_mac_filters; ahw->max_mtu = nic_info.max_mtu; + adapter->max_tx_rings = ahw->max_tx_ques; + adapter->max_sds_rings = ahw->max_rx_ques; /* eSwitch capability indicates vNIC mode. * vNIC and SRIOV are mutually exclusive operational modes. * If SR-IOV capability is detected, SR-IOV physical function @@ -2034,7 +2047,7 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter) return QLC_83XX_DEFAULT_OPMODE; if (ahw->capabilities & QLC_83XX_ESWITCH_CAPABILITY) - return QLC_83XX_VIRTUAL_NIC_MODE; + return QLCNIC_VNIC_MODE; return QLC_83XX_DEFAULT_OPMODE; } @@ -2048,15 +2061,20 @@ int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter) if (ret == -EIO) return -EIO; - if (ret == QLC_83XX_VIRTUAL_NIC_MODE) { - ahw->nic_mode = QLC_83XX_VIRTUAL_NIC_MODE; + if (ret == QLCNIC_VNIC_MODE) { + ahw->nic_mode = QLCNIC_VNIC_MODE; + if (qlcnic_83xx_config_vnic_opmode(adapter)) return -EIO; + adapter->max_sds_rings = QLCNIC_MAX_VNIC_SDS_RINGS; + adapter->max_tx_rings = QLCNIC_MAX_VNIC_TX_RINGS; } else if (ret == QLC_83XX_DEFAULT_OPMODE) { - ahw->nic_mode = QLC_83XX_DEFAULT_MODE; + ahw->nic_mode = QLCNIC_DEFAULT_MODE; adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver; ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry; + adapter->max_sds_rings = ahw->max_rx_ques; + adapter->max_tx_rings = ahw->max_tx_ques; } else { return -EIO; } @@ -2159,13 +2177,34 @@ static int qlcnic_83xx_get_fw_info(struct qlcnic_adapter *adapter) return err; } +static void qlcnic_83xx_init_rings(struct qlcnic_adapter *adapter) +{ + u8 rx_cnt = QLCNIC_DEF_SDS_RINGS; + u8 tx_cnt = QLCNIC_DEF_TX_RINGS; + + adapter->max_tx_rings = QLCNIC_MAX_TX_RINGS; + adapter->max_sds_rings = QLCNIC_MAX_SDS_RINGS; + + if (!adapter->ahw->msix_supported) { + rx_cnt = QLCNIC_SINGLE_RING; + tx_cnt = QLCNIC_SINGLE_RING; + } + + /* compute and set drv sds rings */ + qlcnic_set_tx_ring_count(adapter, tx_cnt); + qlcnic_set_sds_ring_count(adapter, rx_cnt); +} int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac) { struct qlcnic_hardware_context *ahw = adapter->ahw; + struct qlcnic_dcb *dcb; int err = 0; ahw->msix_supported = !!qlcnic_use_msi_x; + + qlcnic_83xx_init_rings(adapter); + err = qlcnic_83xx_init_mailbox_work(adapter); if (err) goto exit; @@ -2178,22 +2217,26 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac) return err; } + if (qlcnic_83xx_read_flash_descriptor_table(adapter) || + qlcnic_83xx_read_flash_mfg_id(adapter)) { + dev_err(&adapter->pdev->dev, "Failed reading flash mfg id\n"); + err = -ENOTRECOVERABLE; + goto detach_mbx; + } + err = qlcnic_83xx_check_hw_status(adapter); if (err) goto detach_mbx; - if (!qlcnic_83xx_read_flash_descriptor_table(adapter)) - qlcnic_83xx_read_flash_mfg_id(adapter); - err = qlcnic_83xx_get_fw_info(adapter); if (err) goto detach_mbx; err = qlcnic_83xx_idc_init(adapter); if (err) - goto clear_fw_info; + goto detach_mbx; - err = qlcnic_setup_intr(adapter, 0, 0); + err = qlcnic_setup_intr(adapter); if (err) { dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n"); goto disable_intr; @@ -2215,13 +2258,16 @@ int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac) if (err) goto disable_mbx_intr; + /* Perform operating mode specific initialization */ err = adapter->nic_ops->init_driver(adapter); if (err) goto disable_mbx_intr; - if (adapter->dcb && qlcnic_dcb_attach(adapter)) - qlcnic_clear_dcb_ops(adapter); + dcb = adapter->dcb; + + if (dcb && qlcnic_dcb_attach(dcb)) + qlcnic_clear_dcb_ops(dcb); /* Periodically monitor device status */ qlcnic_83xx_idc_poll_dev_state(&adapter->fw_work.work); @@ -2233,12 +2279,10 @@ disable_mbx_intr: disable_intr: qlcnic_teardown_intr(adapter); -clear_fw_info: - kfree(ahw->fw_info); - detach_mbx: qlcnic_83xx_detach_mailbox_work(adapter); qlcnic_83xx_free_mailbox(ahw->mailbox); + ahw->mailbox = NULL; exit: return err; } @@ -2251,7 +2295,7 @@ void qlcnic_83xx_aer_stop_poll_work(struct qlcnic_adapter *adapter) clear_bit(QLC_83XX_MBX_READY, &idc->status); cancel_delayed_work_sync(&adapter->fw_work); - if (ahw->nic_mode == QLC_83XX_VIRTUAL_NIC_MODE) + if (ahw->nic_mode == QLCNIC_VNIC_MODE) qlcnic_83xx_disable_vnic_mode(adapter, 1); qlcnic_83xx_idc_detach_driver(adapter); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c index 0248a4c2f5dd15fb1f12827b90bf1af0b1e73ba1..734d28602ac3f335214ba9cc5f1c9d4408d6d554 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_vnic.c @@ -94,13 +94,29 @@ qlcnic_83xx_config_vnic_buff_descriptors(struct qlcnic_adapter *adapter) **/ static int qlcnic_83xx_init_mgmt_vnic(struct qlcnic_adapter *adapter) { - int err = -EIO; + struct qlcnic_hardware_context *ahw = adapter->ahw; + struct device *dev = &adapter->pdev->dev; + struct qlcnic_npar_info *npar; + int i, err = -EIO; qlcnic_83xx_get_minidump_template(adapter); + if (!(adapter->flags & QLCNIC_ADAPTER_INITIALIZED)) { if (qlcnic_init_pci_info(adapter)) return err; + npar = adapter->npars; + + for (i = 0; i < ahw->act_pci_func; i++, npar++) { + dev_info(dev, "id:%d active:%d type:%d port:%d min_bw:%d max_bw:%d mac_addr:%pM\n", + npar->pci_func, npar->active, npar->type, + npar->phy_port, npar->min_bw, npar->max_bw, + npar->mac); + } + + dev_info(dev, "Max functions = %d, active functions = %d\n", + ahw->max_pci_func, ahw->act_pci_func); + if (qlcnic_83xx_set_vnic_opmode(adapter)) return err; @@ -115,12 +131,12 @@ static int qlcnic_83xx_init_mgmt_vnic(struct qlcnic_adapter *adapter) return err; qlcnic_83xx_config_vnic_buff_descriptors(adapter); - adapter->ahw->msix_supported = !!qlcnic_use_msi_x; + ahw->msix_supported = qlcnic_use_msi_x ? 1 : 0; adapter->flags |= QLCNIC_ADAPTER_INITIALIZED; qlcnic_83xx_enable_vnic_mode(adapter, 1); - dev_info(&adapter->pdev->dev, "HAL Version: %d, Management function\n", - adapter->ahw->fw_hal_version); + dev_info(dev, "HAL Version: %d, Management function\n", + ahw->fw_hal_version); return 0; } @@ -240,8 +256,8 @@ int qlcnic_83xx_check_vnic_state(struct qlcnic_adapter *adapter) return 0; } -static int qlcnic_83xx_get_eswitch_port_info(struct qlcnic_adapter *adapter, - int func, int *port_id) +int qlcnic_83xx_set_port_eswitch_status(struct qlcnic_adapter *adapter, + int func, int *port_id) { struct qlcnic_info nic_info; int err = 0; @@ -257,23 +273,8 @@ static int qlcnic_83xx_get_eswitch_port_info(struct qlcnic_adapter *adapter, else err = -EIO; - return err; -} - -int qlcnic_83xx_enable_port_eswitch(struct qlcnic_adapter *adapter, int func) -{ - int id, err = 0; - - err = qlcnic_83xx_get_eswitch_port_info(adapter, func, &id); - if (err) - return err; - - if (!(adapter->eswitch[id].flags & QLCNIC_SWITCH_ENABLE)) { - if (!qlcnic_enable_eswitch(adapter, id, 1)) - adapter->eswitch[id].flags |= QLCNIC_SWITCH_ENABLE; - else - err = -EIO; - } + if (!err) + adapter->eswitch[*port_id].flags |= QLCNIC_SWITCH_ENABLE; return err; } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index 86850dd633a17169e51141287ff8b69462bbe412..859cb161fc63c44b514e7e4d5d624f4a57c6f17d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -270,7 +270,7 @@ int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter) int err; nrds_rings = adapter->max_rds_rings; - nsds_rings = adapter->max_sds_rings; + nsds_rings = adapter->drv_sds_rings; rq_size = SIZEOF_HOSTRQ_RX(struct qlcnic_hostrq_rx_ctx, nrds_rings, nsds_rings); @@ -475,7 +475,7 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter, if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) { - temp_nsds_rings = adapter->max_sds_rings; + temp_nsds_rings = adapter->drv_sds_rings; index = temp_nsds_rings + ring; msix_id = ahw->intr_tbl[index].id; prq->msi_index = cpu_to_le16(msix_id); @@ -512,7 +512,7 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter, if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test && (adapter->flags & QLCNIC_MSIX_ENABLED)) { - index = adapter->max_sds_rings + ring; + index = adapter->drv_sds_rings + ring; intr_mask = ahw->intr_tbl[index].src; tx_ring->crb_intr_mask = ahw->pci_base0 + intr_mask; } @@ -582,7 +582,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter) recv_ctx = adapter->recv_ctx; - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; ptr = (__le32 *)dma_alloc_coherent(&pdev->dev, sizeof(u32), &tx_ring->hw_cons_phys_addr, @@ -616,7 +616,7 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter) } - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; addr = dma_alloc_coherent(&adapter->pdev->dev, @@ -664,7 +664,7 @@ int qlcnic_fw_create_ctx(struct qlcnic_adapter *dev) if (err) goto err_out; - for (ring = 0; ring < dev->max_drv_tx_rings; ring++) { + for (ring = 0; ring < dev->drv_tx_rings; ring++) { err = qlcnic_fw_cmd_create_tx_ctx(dev, &dev->tx_ring[ring], ring); @@ -703,7 +703,7 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter) if (test_and_clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) { qlcnic_fw_cmd_del_rx_ctx(adapter); - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) + for (ring = 0; ring < adapter->drv_tx_rings; ring++) qlcnic_fw_cmd_del_tx_ctx(adapter, &adapter->tx_ring[ring]); @@ -733,7 +733,7 @@ void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter) recv_ctx = adapter->recv_ctx; - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; if (tx_ring->hw_consumer != NULL) { dma_free_coherent(&adapter->pdev->dev, sizeof(u32), @@ -764,7 +764,7 @@ void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter) } } - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; if (sds_ring->desc_head != NULL) { @@ -895,6 +895,8 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter, npar_info->max_rx_ques = le16_to_cpu(nic_info->max_rx_ques); npar_info->capabilities = le32_to_cpu(nic_info->capabilities); npar_info->max_mtu = le16_to_cpu(nic_info->max_mtu); + adapter->max_tx_rings = npar_info->max_tx_ques; + adapter->max_sds_rings = npar_info->max_rx_ques; } qlcnic_free_mbx_args(&cmd); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c index d62d5ce432ec7525ca3f504b790d050797f343d2..86bca7c14f99245a07e65a7385e8b67796afb3a9 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.c @@ -57,22 +57,22 @@ static const struct dcbnl_rtnl_ops qlcnic_dcbnl_ops; static void qlcnic_dcb_aen_work(struct work_struct *); static void qlcnic_dcb_data_cee_param_map(struct qlcnic_adapter *); -static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_adapter *); -static void __qlcnic_dcb_free(struct qlcnic_adapter *); -static int __qlcnic_dcb_attach(struct qlcnic_adapter *); -static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *, char *); -static void __qlcnic_dcb_get_info(struct qlcnic_adapter *); - -static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *); -static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_adapter *, char *, u8); -static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_adapter *); -static void qlcnic_82xx_dcb_handle_aen(struct qlcnic_adapter *, void *); - -static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *); -static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_adapter *, char *, u8); -static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_adapter *); -static int qlcnic_83xx_dcb_register_aen(struct qlcnic_adapter *, bool); -static void qlcnic_83xx_dcb_handle_aen(struct qlcnic_adapter *, void *); +static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_dcb *); +static void __qlcnic_dcb_free(struct qlcnic_dcb *); +static int __qlcnic_dcb_attach(struct qlcnic_dcb *); +static int __qlcnic_dcb_query_hw_capability(struct qlcnic_dcb *, char *); +static void __qlcnic_dcb_get_info(struct qlcnic_dcb *); + +static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_dcb *); +static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_dcb *, char *, u8); +static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_dcb *); +static void qlcnic_82xx_dcb_aen_handler(struct qlcnic_dcb *, void *); + +static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_dcb *); +static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_dcb *, char *, u8); +static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_dcb *); +static int qlcnic_83xx_dcb_register_aen(struct qlcnic_dcb *, bool); +static void qlcnic_83xx_dcb_aen_handler(struct qlcnic_dcb *, void *); struct qlcnic_dcb_capability { bool tsa_capability; @@ -180,7 +180,7 @@ static struct qlcnic_dcb_ops qlcnic_83xx_dcb_ops = { .query_cee_param = qlcnic_83xx_dcb_query_cee_param, .get_cee_cfg = qlcnic_83xx_dcb_get_cee_cfg, .register_aen = qlcnic_83xx_dcb_register_aen, - .handle_aen = qlcnic_83xx_dcb_handle_aen, + .aen_handler = qlcnic_83xx_dcb_aen_handler, }; static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = { @@ -193,7 +193,7 @@ static struct qlcnic_dcb_ops qlcnic_82xx_dcb_ops = { .get_hw_capability = qlcnic_82xx_dcb_get_hw_capability, .query_cee_param = qlcnic_82xx_dcb_query_cee_param, .get_cee_cfg = qlcnic_82xx_dcb_get_cee_cfg, - .handle_aen = qlcnic_82xx_dcb_handle_aen, + .aen_handler = qlcnic_82xx_dcb_aen_handler, }; static u8 qlcnic_dcb_get_num_app(struct qlcnic_adapter *adapter, u32 val) @@ -242,10 +242,10 @@ static int qlcnic_dcb_prio_count(u8 up_tc_map) return j; } -static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_adapter *adapter) +static inline void __qlcnic_init_dcbnl_ops(struct qlcnic_dcb *dcb) { - if (test_bit(__QLCNIC_DCB_STATE, &adapter->state)) - adapter->netdev->dcbnl_ops = &qlcnic_dcbnl_ops; + if (test_bit(QLCNIC_DCB_STATE, &dcb->state)) + dcb->adapter->netdev->dcbnl_ops = &qlcnic_dcbnl_ops; } static void qlcnic_set_dcb_ops(struct qlcnic_adapter *adapter) @@ -256,7 +256,7 @@ static void qlcnic_set_dcb_ops(struct qlcnic_adapter *adapter) adapter->dcb->ops = &qlcnic_83xx_dcb_ops; } -int __qlcnic_register_dcb(struct qlcnic_adapter *adapter) +int qlcnic_register_dcb(struct qlcnic_adapter *adapter) { struct qlcnic_dcb *dcb; @@ -267,20 +267,22 @@ int __qlcnic_register_dcb(struct qlcnic_adapter *adapter) adapter->dcb = dcb; dcb->adapter = adapter; qlcnic_set_dcb_ops(adapter); + dcb->state = 0; return 0; } -static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter) +static void __qlcnic_dcb_free(struct qlcnic_dcb *dcb) { - struct qlcnic_dcb *dcb = adapter->dcb; + struct qlcnic_adapter *adapter; if (!dcb) return; - qlcnic_dcb_register_aen(adapter, 0); + adapter = dcb->adapter; + qlcnic_dcb_register_aen(dcb, 0); - while (test_bit(__QLCNIC_DCB_IN_AEN, &adapter->state)) + while (test_bit(QLCNIC_DCB_AEN_MODE, &dcb->state)) usleep_range(10000, 11000); cancel_delayed_work_sync(&dcb->aen_work); @@ -298,23 +300,22 @@ static void __qlcnic_dcb_free(struct qlcnic_adapter *adapter) adapter->dcb = NULL; } -static void __qlcnic_dcb_get_info(struct qlcnic_adapter *adapter) +static void __qlcnic_dcb_get_info(struct qlcnic_dcb *dcb) { - qlcnic_dcb_get_hw_capability(adapter); - qlcnic_dcb_get_cee_cfg(adapter); - qlcnic_dcb_register_aen(adapter, 1); + qlcnic_dcb_get_hw_capability(dcb); + qlcnic_dcb_get_cee_cfg(dcb); + qlcnic_dcb_register_aen(dcb, 1); } -static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter) +static int __qlcnic_dcb_attach(struct qlcnic_dcb *dcb) { - struct qlcnic_dcb *dcb = adapter->dcb; int err = 0; INIT_DELAYED_WORK(&dcb->aen_work, qlcnic_dcb_aen_work); dcb->wq = create_singlethread_workqueue("qlcnic-dcb"); if (!dcb->wq) { - dev_err(&adapter->pdev->dev, + dev_err(&dcb->adapter->pdev->dev, "DCB workqueue allocation failed. DCB will be disabled\n"); return -1; } @@ -331,7 +332,7 @@ static int __qlcnic_dcb_attach(struct qlcnic_adapter *adapter) goto out_free_cfg; } - qlcnic_dcb_get_info(adapter); + qlcnic_dcb_get_info(dcb); return 0; out_free_cfg: @@ -345,9 +346,9 @@ out_free_wq: return err; } -static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter, - char *buf) +static int __qlcnic_dcb_query_hw_capability(struct qlcnic_dcb *dcb, char *buf) { + struct qlcnic_adapter *adapter = dcb->adapter; struct qlcnic_cmd_args cmd; u32 mbx_out; int err; @@ -371,15 +372,15 @@ static int __qlcnic_dcb_query_hw_capability(struct qlcnic_adapter *adapter, return err; } -static int __qlcnic_dcb_get_capability(struct qlcnic_adapter *adapter, u32 *val) +static int __qlcnic_dcb_get_capability(struct qlcnic_dcb *dcb, u32 *val) { - struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability; + struct qlcnic_dcb_capability *cap = &dcb->cfg->capability; u32 mbx_out; int err; memset(cap, 0, sizeof(struct qlcnic_dcb_capability)); - err = qlcnic_dcb_query_hw_capability(adapter, (char *)val); + err = qlcnic_dcb_query_hw_capability(dcb, (char *)val); if (err) return err; @@ -397,21 +398,21 @@ static int __qlcnic_dcb_get_capability(struct qlcnic_adapter *adapter, u32 *val) if (cap->max_num_tc > QLC_DCB_MAX_TC || cap->max_ets_tc > cap->max_num_tc || cap->max_pfc_tc > cap->max_num_tc) { - dev_err(&adapter->pdev->dev, "Invalid DCB configuration\n"); + dev_err(&dcb->adapter->pdev->dev, "Invalid DCB configuration\n"); return -EINVAL; } return err; } -static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter) +static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_dcb *dcb) { - struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg; + struct qlcnic_dcb_cfg *cfg = dcb->cfg; struct qlcnic_dcb_capability *cap; u32 mbx_out; int err; - err = __qlcnic_dcb_get_capability(adapter, &mbx_out); + err = __qlcnic_dcb_get_capability(dcb, &mbx_out); if (err) return err; @@ -419,15 +420,16 @@ static int qlcnic_82xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter) cap->dcb_capability = DCB_CAP_DCBX_VER_CEE | DCB_CAP_DCBX_LLD_MANAGED; if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability) - set_bit(__QLCNIC_DCB_STATE, &adapter->state); + set_bit(QLCNIC_DCB_STATE, &dcb->state); return err; } -static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_adapter *adapter, +static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_dcb *dcb, char *buf, u8 type) { u16 size = sizeof(struct qlcnic_82xx_dcb_param_mbx_le); + struct qlcnic_adapter *adapter = dcb->adapter; struct qlcnic_82xx_dcb_param_mbx_le *prsp_le; struct device *dev = &adapter->pdev->dev; dma_addr_t cardrsp_phys_addr; @@ -447,8 +449,7 @@ static int qlcnic_82xx_dcb_query_cee_param(struct qlcnic_adapter *adapter, return -EINVAL; } - addr = dma_alloc_coherent(&adapter->pdev->dev, size, &cardrsp_phys_addr, - GFP_KERNEL); + addr = dma_alloc_coherent(dev, size, &cardrsp_phys_addr, GFP_KERNEL); if (addr == NULL) return -ENOMEM; @@ -488,72 +489,67 @@ out: qlcnic_free_mbx_args(&cmd); out_free_rsp: - dma_free_coherent(&adapter->pdev->dev, size, addr, cardrsp_phys_addr); + dma_free_coherent(dev, size, addr, cardrsp_phys_addr); return err; } -static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter) +static int qlcnic_82xx_dcb_get_cee_cfg(struct qlcnic_dcb *dcb) { struct qlcnic_dcb_mbx_params *mbx; int err; - mbx = adapter->dcb->param; + mbx = dcb->param; if (!mbx) return 0; - err = qlcnic_dcb_query_cee_param(adapter, (char *)&mbx->type[0], + err = qlcnic_dcb_query_cee_param(dcb, (char *)&mbx->type[0], QLC_DCB_LOCAL_PARAM_FWID); if (err) return err; - err = qlcnic_dcb_query_cee_param(adapter, (char *)&mbx->type[1], + err = qlcnic_dcb_query_cee_param(dcb, (char *)&mbx->type[1], QLC_DCB_OPER_PARAM_FWID); if (err) return err; - err = qlcnic_dcb_query_cee_param(adapter, (char *)&mbx->type[2], + err = qlcnic_dcb_query_cee_param(dcb, (char *)&mbx->type[2], QLC_DCB_PEER_PARAM_FWID); if (err) return err; mbx->prio_tc_map = QLC_82XX_DCB_PRIO_TC_MAP; - qlcnic_dcb_data_cee_param_map(adapter); + qlcnic_dcb_data_cee_param_map(dcb->adapter); return err; } static void qlcnic_dcb_aen_work(struct work_struct *work) { - struct qlcnic_adapter *adapter; struct qlcnic_dcb *dcb; dcb = container_of(work, struct qlcnic_dcb, aen_work.work); - adapter = dcb->adapter; - qlcnic_dcb_get_cee_cfg(adapter); - clear_bit(__QLCNIC_DCB_IN_AEN, &adapter->state); + qlcnic_dcb_get_cee_cfg(dcb); + clear_bit(QLCNIC_DCB_AEN_MODE, &dcb->state); } -static void qlcnic_82xx_dcb_handle_aen(struct qlcnic_adapter *adapter, - void *data) +static void qlcnic_82xx_dcb_aen_handler(struct qlcnic_dcb *dcb, void *data) { - struct qlcnic_dcb *dcb = adapter->dcb; - - if (test_and_set_bit(__QLCNIC_DCB_IN_AEN, &adapter->state)) + if (test_and_set_bit(QLCNIC_DCB_AEN_MODE, &dcb->state)) return; queue_delayed_work(dcb->wq, &dcb->aen_work, 0); } -static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter) +static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_dcb *dcb) { - struct qlcnic_dcb_capability *cap = &adapter->dcb->cfg->capability; + struct qlcnic_dcb_capability *cap = &dcb->cfg->capability; u32 mbx_out; int err; - err = __qlcnic_dcb_get_capability(adapter, &mbx_out); + err = __qlcnic_dcb_get_capability(dcb, &mbx_out); if (err) return err; @@ -565,14 +561,15 @@ static int qlcnic_83xx_dcb_get_hw_capability(struct qlcnic_adapter *adapter) cap->dcb_capability |= DCB_CAP_DCBX_LLD_MANAGED; if (cap->dcb_capability && cap->tsa_capability && cap->ets_capability) - set_bit(__QLCNIC_DCB_STATE, &adapter->state); + set_bit(QLCNIC_DCB_STATE, &dcb->state); return err; } -static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_adapter *adapter, +static int qlcnic_83xx_dcb_query_cee_param(struct qlcnic_dcb *dcb, char *buf, u8 idx) { + struct qlcnic_adapter *adapter = dcb->adapter; struct qlcnic_dcb_mbx_params mbx_out; int err, i, j, k, max_app, size; struct qlcnic_dcb_param *each; @@ -632,24 +629,23 @@ out: return err; } -static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_adapter *adapter) +static int qlcnic_83xx_dcb_get_cee_cfg(struct qlcnic_dcb *dcb) { - struct qlcnic_dcb *dcb = adapter->dcb; int err; - err = qlcnic_dcb_query_cee_param(adapter, (char *)dcb->param, 0); + err = qlcnic_dcb_query_cee_param(dcb, (char *)dcb->param, 0); if (err) return err; - qlcnic_dcb_data_cee_param_map(adapter); + qlcnic_dcb_data_cee_param_map(dcb->adapter); return err; } -static int qlcnic_83xx_dcb_register_aen(struct qlcnic_adapter *adapter, - bool flag) +static int qlcnic_83xx_dcb_register_aen(struct qlcnic_dcb *dcb, bool flag) { u8 val = (flag ? QLCNIC_CMD_INIT_NIC_FUNC : QLCNIC_CMD_STOP_NIC_FUNC); + struct qlcnic_adapter *adapter = dcb->adapter; struct qlcnic_cmd_args cmd; int err; @@ -669,19 +665,17 @@ static int qlcnic_83xx_dcb_register_aen(struct qlcnic_adapter *adapter, return err; } -static void qlcnic_83xx_dcb_handle_aen(struct qlcnic_adapter *adapter, - void *data) +static void qlcnic_83xx_dcb_aen_handler(struct qlcnic_dcb *dcb, void *data) { - struct qlcnic_dcb *dcb = adapter->dcb; u32 *val = data; - if (test_and_set_bit(__QLCNIC_DCB_IN_AEN, &adapter->state)) + if (test_and_set_bit(QLCNIC_DCB_AEN_MODE, &dcb->state)) return; if (*val & BIT_8) - set_bit(__QLCNIC_DCB_STATE, &adapter->state); + set_bit(QLCNIC_DCB_STATE, &dcb->state); else - clear_bit(__QLCNIC_DCB_STATE, &adapter->state); + clear_bit(QLCNIC_DCB_STATE, &dcb->state); queue_delayed_work(dcb->wq, &dcb->aen_work, 0); } @@ -814,12 +808,12 @@ static u8 qlcnic_dcb_get_state(struct net_device *netdev) { struct qlcnic_adapter *adapter = netdev_priv(netdev); - return test_bit(__QLCNIC_DCB_STATE, &adapter->state); + return test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state); } static void qlcnic_dcb_get_perm_hw_addr(struct net_device *netdev, u8 *addr) { - memcpy(addr, netdev->dev_addr, netdev->addr_len); + memcpy(addr, netdev->perm_addr, netdev->addr_len); } static void @@ -834,7 +828,7 @@ qlcnic_dcb_get_pg_tc_cfg_tx(struct net_device *netdev, int tc, u8 *prio, type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX]; *prio = *pgid = *bw_per = *up_tc_map = 0; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state) || + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state) || !type->tc_param_valid) return; @@ -870,7 +864,7 @@ static void qlcnic_dcb_get_pg_bwg_cfg_tx(struct net_device *netdev, int pgid, *bw_pct = 0; type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX]; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state) || + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state) || !type->tc_param_valid) return; @@ -896,7 +890,7 @@ static void qlcnic_dcb_get_pfc_cfg(struct net_device *netdev, int prio, *setting = 0; type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX]; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state) || + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state) || !type->pfc_mode_enable) return; @@ -915,7 +909,7 @@ static u8 qlcnic_dcb_get_capability(struct net_device *netdev, int capid, { struct qlcnic_adapter *adapter = netdev_priv(netdev); - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) return 0; switch (capid) { @@ -944,7 +938,7 @@ static int qlcnic_dcb_get_num_tcs(struct net_device *netdev, int attr, u8 *num) struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) return -EINVAL; switch (attr) { @@ -967,7 +961,7 @@ static u8 qlcnic_dcb_get_app(struct net_device *netdev, u8 idtype, u16 id) .protocol = id, }; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) return 0; return dcb_getapp(netdev, &app); @@ -978,7 +972,7 @@ static u8 qlcnic_dcb_get_pfc_state(struct net_device *netdev) struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_dcb *dcb = adapter->dcb; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &dcb->state)) return 0; return dcb->cfg->type[QLC_DCB_OPER_IDX].pfc_mode_enable; @@ -989,7 +983,7 @@ static u8 qlcnic_dcb_get_dcbx(struct net_device *netdev) struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_dcb_cfg *cfg = adapter->dcb->cfg; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) return 0; return cfg->capability.dcb_capability; @@ -1000,7 +994,7 @@ static u8 qlcnic_dcb_get_feat_cfg(struct net_device *netdev, int fid, u8 *flag) struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_dcb_cee *type; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) return 1; type = &adapter->dcb->cfg->type[QLC_DCB_OPER_IDX]; @@ -1055,7 +1049,7 @@ static int qlcnic_dcb_peer_app_info(struct net_device *netdev, *app_count = 0; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) return 0; peer = &adapter->dcb->cfg->type[QLC_DCB_PEER_IDX]; @@ -1076,7 +1070,7 @@ static int qlcnic_dcb_peer_app_table(struct net_device *netdev, struct qlcnic_dcb_app *app; int i, j; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) return 0; peer = &adapter->dcb->cfg->type[QLC_DCB_PEER_IDX]; @@ -1101,7 +1095,7 @@ static int qlcnic_dcb_cee_peer_get_pg(struct net_device *netdev, struct qlcnic_dcb_cee *peer; u8 i, j, k, map; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) return 0; peer = &adapter->dcb->cfg->type[QLC_DCB_PEER_IDX]; @@ -1136,7 +1130,7 @@ static int qlcnic_dcb_cee_peer_get_pfc(struct net_device *netdev, pfc->pfc_en = 0; - if (!test_bit(__QLCNIC_DCB_STATE, &adapter->state)) + if (!test_bit(QLCNIC_DCB_STATE, &adapter->dcb->state)) return 0; peer = &cfg->type[QLC_DCB_PEER_IDX]; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h index b87ce9fb503e48298ea9e96e0490047fd3bb488b..c04ae0cdc108c98eb57d8a860d772535379178af 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_dcb.h @@ -8,26 +8,29 @@ #ifndef __QLCNIC_DCBX_H #define __QLCNIC_DCBX_H -void qlcnic_clear_dcb_ops(struct qlcnic_adapter *); +#define QLCNIC_DCB_STATE 0 +#define QLCNIC_DCB_AEN_MODE 1 #ifdef CONFIG_QLCNIC_DCB -int __qlcnic_register_dcb(struct qlcnic_adapter *); +int qlcnic_register_dcb(struct qlcnic_adapter *); #else -static inline int __qlcnic_register_dcb(struct qlcnic_adapter *adapter) +static inline int qlcnic_register_dcb(struct qlcnic_adapter *adapter) { return 0; } #endif +struct qlcnic_dcb; + struct qlcnic_dcb_ops { - void (*init_dcbnl_ops) (struct qlcnic_adapter *); - void (*free) (struct qlcnic_adapter *); - int (*attach) (struct qlcnic_adapter *); - int (*query_hw_capability) (struct qlcnic_adapter *, char *); - int (*get_hw_capability) (struct qlcnic_adapter *); - void (*get_info) (struct qlcnic_adapter *); - int (*query_cee_param) (struct qlcnic_adapter *, char *, u8); - int (*get_cee_cfg) (struct qlcnic_adapter *); - int (*register_aen) (struct qlcnic_adapter *, bool); - void (*handle_aen) (struct qlcnic_adapter *, void *); + int (*query_hw_capability) (struct qlcnic_dcb *, char *); + int (*get_hw_capability) (struct qlcnic_dcb *); + int (*query_cee_param) (struct qlcnic_dcb *, char *, u8); + void (*init_dcbnl_ops) (struct qlcnic_dcb *); + int (*register_aen) (struct qlcnic_dcb *, bool); + void (*aen_handler) (struct qlcnic_dcb *, void *); + int (*get_cee_cfg) (struct qlcnic_dcb *); + void (*get_info) (struct qlcnic_dcb *); + int (*attach) (struct qlcnic_dcb *); + void (*free) (struct qlcnic_dcb *); }; struct qlcnic_dcb { @@ -37,5 +40,85 @@ struct qlcnic_dcb { struct workqueue_struct *wq; struct qlcnic_dcb_ops *ops; struct qlcnic_dcb_cfg *cfg; + unsigned long state; }; + +static inline void qlcnic_clear_dcb_ops(struct qlcnic_dcb *dcb) +{ + kfree(dcb); + dcb = NULL; +} + +static inline int qlcnic_dcb_get_hw_capability(struct qlcnic_dcb *dcb) +{ + if (dcb && dcb->ops->get_hw_capability) + return dcb->ops->get_hw_capability(dcb); + + return 0; +} + +static inline void qlcnic_dcb_free(struct qlcnic_dcb *dcb) +{ + if (dcb && dcb->ops->free) + dcb->ops->free(dcb); +} + +static inline int qlcnic_dcb_attach(struct qlcnic_dcb *dcb) +{ + if (dcb && dcb->ops->attach) + return dcb->ops->attach(dcb); + + return 0; +} + +static inline int +qlcnic_dcb_query_hw_capability(struct qlcnic_dcb *dcb, char *buf) +{ + if (dcb && dcb->ops->query_hw_capability) + return dcb->ops->query_hw_capability(dcb, buf); + + return 0; +} + +static inline void qlcnic_dcb_get_info(struct qlcnic_dcb *dcb) +{ + if (dcb && dcb->ops->get_info) + dcb->ops->get_info(dcb); +} + +static inline int +qlcnic_dcb_query_cee_param(struct qlcnic_dcb *dcb, char *buf, u8 type) +{ + if (dcb && dcb->ops->query_cee_param) + return dcb->ops->query_cee_param(dcb, buf, type); + + return 0; +} + +static inline int qlcnic_dcb_get_cee_cfg(struct qlcnic_dcb *dcb) +{ + if (dcb && dcb->ops->get_cee_cfg) + return dcb->ops->get_cee_cfg(dcb); + + return 0; +} + +static inline void +qlcnic_dcb_register_aen(struct qlcnic_dcb *dcb, u8 flag) +{ + if (dcb && dcb->ops->register_aen) + dcb->ops->register_aen(dcb, flag); +} + +static inline void qlcnic_dcb_aen_handler(struct qlcnic_dcb *dcb, void *msg) +{ + if (dcb && dcb->ops->aen_handler) + dcb->ops->aen_handler(dcb, msg); +} + +static inline void qlcnic_dcb_init_dcbnl_ops(struct qlcnic_dcb *dcb) +{ + if (dcb && dcb->ops->init_dcbnl_ops) + dcb->ops->init_dcbnl_ops(dcb); +} #endif diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index ff83a9fcd4c5a844955d221c9daef96772402b7a..b36c02fafcfd1ebe6f6ce8da49e73f73364f64b5 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -27,43 +27,36 @@ static const u32 qlcnic_fw_dump_level[] = { }; static const struct qlcnic_stats qlcnic_gstrings_stats[] = { + {"xmit_on", QLC_SIZEOF(stats.xmit_on), QLC_OFF(stats.xmit_on)}, + {"xmit_off", QLC_SIZEOF(stats.xmit_off), QLC_OFF(stats.xmit_off)}, {"xmit_called", QLC_SIZEOF(stats.xmitcalled), - QLC_OFF(stats.xmitcalled)}, + QLC_OFF(stats.xmitcalled)}, {"xmit_finished", QLC_SIZEOF(stats.xmitfinished), - QLC_OFF(stats.xmitfinished)}, - {"rx_dropped", QLC_SIZEOF(stats.rxdropped), QLC_OFF(stats.rxdropped)}, + QLC_OFF(stats.xmitfinished)}, + {"tx dma map error", QLC_SIZEOF(stats.tx_dma_map_error), + QLC_OFF(stats.tx_dma_map_error)}, + {"tx_bytes", QLC_SIZEOF(stats.txbytes), QLC_OFF(stats.txbytes)}, {"tx_dropped", QLC_SIZEOF(stats.txdropped), QLC_OFF(stats.txdropped)}, - {"csummed", QLC_SIZEOF(stats.csummed), QLC_OFF(stats.csummed)}, + {"rx dma map error", QLC_SIZEOF(stats.rx_dma_map_error), + QLC_OFF(stats.rx_dma_map_error)}, {"rx_pkts", QLC_SIZEOF(stats.rx_pkts), QLC_OFF(stats.rx_pkts)}, - {"lro_pkts", QLC_SIZEOF(stats.lro_pkts), QLC_OFF(stats.lro_pkts)}, {"rx_bytes", QLC_SIZEOF(stats.rxbytes), QLC_OFF(stats.rxbytes)}, - {"tx_bytes", QLC_SIZEOF(stats.txbytes), QLC_OFF(stats.txbytes)}, + {"rx_dropped", QLC_SIZEOF(stats.rxdropped), QLC_OFF(stats.rxdropped)}, + {"null rxbuf", QLC_SIZEOF(stats.null_rxbuf), QLC_OFF(stats.null_rxbuf)}, + {"csummed", QLC_SIZEOF(stats.csummed), QLC_OFF(stats.csummed)}, + {"lro_pkts", QLC_SIZEOF(stats.lro_pkts), QLC_OFF(stats.lro_pkts)}, {"lrobytes", QLC_SIZEOF(stats.lrobytes), QLC_OFF(stats.lrobytes)}, {"lso_frames", QLC_SIZEOF(stats.lso_frames), QLC_OFF(stats.lso_frames)}, - {"xmit_on", QLC_SIZEOF(stats.xmit_on), QLC_OFF(stats.xmit_on)}, - {"xmit_off", QLC_SIZEOF(stats.xmit_off), QLC_OFF(stats.xmit_off)}, {"skb_alloc_failure", QLC_SIZEOF(stats.skb_alloc_failure), QLC_OFF(stats.skb_alloc_failure)}, - {"null rxbuf", QLC_SIZEOF(stats.null_rxbuf), QLC_OFF(stats.null_rxbuf)}, - {"rx dma map error", QLC_SIZEOF(stats.rx_dma_map_error), - QLC_OFF(stats.rx_dma_map_error)}, - {"tx dma map error", QLC_SIZEOF(stats.tx_dma_map_error), - QLC_OFF(stats.tx_dma_map_error)}, {"mac_filter_limit_overrun", QLC_SIZEOF(stats.mac_filter_limit_overrun), - QLC_OFF(stats.mac_filter_limit_overrun)}, + QLC_OFF(stats.mac_filter_limit_overrun)}, {"spurious intr", QLC_SIZEOF(stats.spurious_intr), QLC_OFF(stats.spurious_intr)}, }; static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = { - "rx unicast frames", - "rx multicast frames", - "rx broadcast frames", - "rx dropped frames", - "rx errors", - "rx local frames", - "rx numbytes", "tx unicast frames", "tx multicast frames", "tx broadcast frames", @@ -71,6 +64,13 @@ static const char qlcnic_device_gstrings_stats[][ETH_GSTRING_LEN] = { "tx errors", "tx local frames", "tx numbytes", + "rx unicast frames", + "rx multicast frames", + "rx broadcast frames", + "rx dropped frames", + "rx errors", + "rx local frames", + "rx numbytes", }; static const char qlcnic_83xx_tx_stats_strings[][ETH_GSTRING_LEN] = { @@ -126,13 +126,16 @@ static const char qlcnic_83xx_mac_stats_strings[][ETH_GSTRING_LEN] = { #define QLCNIC_STATS_LEN ARRAY_SIZE(qlcnic_gstrings_stats) -static const char qlcnic_tx_ring_stats_strings[][ETH_GSTRING_LEN] = { +static const char qlcnic_tx_queue_stats_strings[][ETH_GSTRING_LEN] = { "xmit_on", "xmit_off", "xmit_called", "xmit_finished", + "tx_bytes", }; +#define QLCNIC_TX_STATS_LEN ARRAY_SIZE(qlcnic_tx_queue_stats_strings) + static const char qlcnic_83xx_rx_stats_strings[][ETH_GSTRING_LEN] = { "ctx_rx_bytes", "ctx_rx_pkts", @@ -187,8 +190,8 @@ static int qlcnic_dev_statistics_len(struct qlcnic_adapter *adapter) return -1; } -#define QLCNIC_RING_REGS_COUNT 20 -#define QLCNIC_RING_REGS_LEN (QLCNIC_RING_REGS_COUNT * sizeof(u32)) +#define QLCNIC_TX_INTR_NOT_CONFIGURED 0X78563412 + #define QLCNIC_MAX_EEPROM_LEN 1024 static const u32 diag_registers[] = { @@ -219,7 +222,15 @@ static const u32 ext_diag_registers[] = { }; #define QLCNIC_MGMT_API_VERSION 2 -#define QLCNIC_ETHTOOL_REGS_VER 3 +#define QLCNIC_ETHTOOL_REGS_VER 4 + +static inline int qlcnic_get_ring_regs_len(struct qlcnic_adapter *adapter) +{ + int ring_regs_cnt = (adapter->drv_tx_rings * 5) + + (adapter->max_rds_rings * 2) + + (adapter->drv_sds_rings * 3) + 5; + return ring_regs_cnt * sizeof(u32); +} static int qlcnic_get_regs_len(struct net_device *dev) { @@ -231,7 +242,9 @@ static int qlcnic_get_regs_len(struct net_device *dev) else len = sizeof(ext_diag_registers) + sizeof(diag_registers); - return QLCNIC_RING_REGS_LEN + len + QLCNIC_DEV_INFO_SIZE + 1; + len += ((QLCNIC_DEV_INFO_SIZE + 2) * sizeof(u32)); + len += qlcnic_get_ring_regs_len(adapter); + return len; } static int qlcnic_get_eeprom_len(struct net_device *dev) @@ -493,6 +506,8 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) struct qlcnic_adapter *adapter = netdev_priv(dev); struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; struct qlcnic_host_sds_ring *sds_ring; + struct qlcnic_host_rds_ring *rds_rings; + struct qlcnic_host_tx_ring *tx_ring; u32 *regs_buff = p; int ring, i = 0; @@ -512,21 +527,35 @@ qlcnic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) return; - regs_buff[i++] = 0xFFEFCDAB; /* Marker btw regs and ring count*/ - - regs_buff[i++] = 1; /* No. of tx ring */ - regs_buff[i++] = le32_to_cpu(*(adapter->tx_ring->hw_consumer)); - regs_buff[i++] = readl(adapter->tx_ring->crb_cmd_producer); - - regs_buff[i++] = 2; /* No. of rx ring */ - regs_buff[i++] = readl(recv_ctx->rds_rings[0].crb_rcv_producer); - regs_buff[i++] = readl(recv_ctx->rds_rings[1].crb_rcv_producer); + /* Marker btw regs and TX ring count */ + regs_buff[i++] = 0xFFEFCDAB; + + regs_buff[i++] = adapter->drv_tx_rings; /* No. of TX ring */ + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { + tx_ring = &adapter->tx_ring[ring]; + regs_buff[i++] = le32_to_cpu(*(tx_ring->hw_consumer)); + regs_buff[i++] = tx_ring->sw_consumer; + regs_buff[i++] = readl(tx_ring->crb_cmd_producer); + regs_buff[i++] = tx_ring->producer; + if (tx_ring->crb_intr_mask) + regs_buff[i++] = readl(tx_ring->crb_intr_mask); + else + regs_buff[i++] = QLCNIC_TX_INTR_NOT_CONFIGURED; + } - regs_buff[i++] = adapter->max_sds_rings; + regs_buff[i++] = adapter->max_rds_rings; /* No. of RX ring */ + for (ring = 0; ring < adapter->max_rds_rings; ring++) { + rds_rings = &recv_ctx->rds_rings[ring]; + regs_buff[i++] = readl(rds_rings->crb_rcv_producer); + regs_buff[i++] = rds_rings->producer; + } - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + regs_buff[i++] = adapter->drv_sds_rings; /* No. of SDS ring */ + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &(recv_ctx->sds_rings[ring]); regs_buff[i++] = readl(sds_ring->crb_sts_consumer); + regs_buff[i++] = sds_ring->consumer; + regs_buff[i++] = readl(sds_ring->crb_intr_mask); } } @@ -635,46 +664,88 @@ qlcnic_set_ringparam(struct net_device *dev, return qlcnic_reset_context(adapter); } +static int qlcnic_validate_ring_count(struct qlcnic_adapter *adapter, + u8 rx_ring, u8 tx_ring) +{ + if (rx_ring != 0) { + if (rx_ring > adapter->max_sds_rings) { + netdev_err(adapter->netdev, "Invalid ring count, SDS ring count %d should not be greater than max %d driver sds rings.\n", + rx_ring, adapter->max_sds_rings); + return -EINVAL; + } + } + + if (tx_ring != 0) { + if (qlcnic_82xx_check(adapter) && + (tx_ring > adapter->max_tx_rings)) { + netdev_err(adapter->netdev, + "Invalid ring count, Tx ring count %d should not be greater than max %d driver Tx rings.\n", + tx_ring, adapter->max_tx_rings); + return -EINVAL; + } + + if (qlcnic_83xx_check(adapter) && + (tx_ring > QLCNIC_SINGLE_RING)) { + netdev_err(adapter->netdev, + "Invalid ring count, Tx ring count %d should not be greater than %d driver Tx rings.\n", + tx_ring, QLCNIC_SINGLE_RING); + return -EINVAL; + } + } + + return 0; +} + static void qlcnic_get_channels(struct net_device *dev, struct ethtool_channels *channel) { struct qlcnic_adapter *adapter = netdev_priv(dev); - int min; - - min = min_t(int, adapter->ahw->max_rx_ques, num_online_cpus()); - channel->max_rx = rounddown_pow_of_two(min); - channel->max_tx = min_t(int, QLCNIC_MAX_TX_RINGS, num_online_cpus()); - channel->rx_count = adapter->max_sds_rings; - channel->tx_count = adapter->max_drv_tx_rings; + channel->max_rx = adapter->max_sds_rings; + channel->max_tx = adapter->max_tx_rings; + channel->rx_count = adapter->drv_sds_rings; + channel->tx_count = adapter->drv_tx_rings; } static int qlcnic_set_channels(struct net_device *dev, - struct ethtool_channels *channel) + struct ethtool_channels *channel) { struct qlcnic_adapter *adapter = netdev_priv(dev); int err; - int txq = 0; if (channel->other_count || channel->combined_count) return -EINVAL; + err = qlcnic_validate_ring_count(adapter, channel->rx_count, + channel->tx_count); + if (err) + return err; + if (channel->rx_count) { - err = qlcnic_validate_max_rss(adapter, channel->rx_count); - if (err) + err = qlcnic_validate_rings(adapter, channel->rx_count, + QLCNIC_RX_QUEUE); + if (err) { + netdev_err(dev, "Unable to configure %u SDS rings\n", + channel->rx_count); return err; + } } - if (qlcnic_82xx_check(adapter) && channel->tx_count) { - err = qlcnic_validate_max_tx_rings(adapter, channel->tx_count); - if (err) + if (channel->tx_count) { + err = qlcnic_validate_rings(adapter, channel->tx_count, + QLCNIC_TX_QUEUE); + if (err) { + netdev_err(dev, "Unable to configure %u Tx rings\n", + channel->tx_count); return err; - txq = channel->tx_count; + } } - err = qlcnic_set_max_rss(adapter, channel->rx_count, txq); - netdev_info(dev, "allocated 0x%x sds rings and 0x%x tx rings\n", - adapter->max_sds_rings, adapter->max_drv_tx_rings); + err = qlcnic_setup_rings(adapter, channel->rx_count, + channel->tx_count); + netdev_info(dev, "Allocated %d SDS rings and %d Tx rings\n", + adapter->drv_sds_rings, adapter->drv_tx_rings); + return err; } @@ -876,7 +947,7 @@ static int qlcnic_irq_test(struct net_device *netdev) struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_hardware_context *ahw = adapter->ahw; struct qlcnic_cmd_args cmd; - int ret, max_sds_rings = adapter->max_sds_rings; + int ret, drv_sds_rings = adapter->drv_sds_rings; if (qlcnic_83xx_check(adapter)) return qlcnic_83xx_interrupt_test(netdev); @@ -905,10 +976,10 @@ done: qlcnic_free_mbx_args(&cmd); free_diag_res: - qlcnic_diag_free_res(netdev, max_sds_rings); + qlcnic_diag_free_res(netdev, drv_sds_rings); clear_diag_irq: - adapter->max_sds_rings = max_sds_rings; + adapter->drv_sds_rings = drv_sds_rings; clear_bit(__QLCNIC_RESETTING, &adapter->state); return ret; @@ -984,8 +1055,8 @@ int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode) int qlcnic_loopback_test(struct net_device *netdev, u8 mode) { struct qlcnic_adapter *adapter = netdev_priv(netdev); - int max_drv_tx_rings = adapter->max_drv_tx_rings; - int max_sds_rings = adapter->max_sds_rings; + int drv_tx_rings = adapter->drv_tx_rings; + int drv_sds_rings = adapter->drv_sds_rings; struct qlcnic_host_sds_ring *sds_ring; struct qlcnic_hardware_context *ahw = adapter->ahw; int loop = 0; @@ -1040,11 +1111,11 @@ int qlcnic_loopback_test(struct net_device *netdev, u8 mode) qlcnic_clear_lb_mode(adapter, mode); free_res: - qlcnic_diag_free_res(netdev, max_sds_rings); + qlcnic_diag_free_res(netdev, drv_sds_rings); clear_it: - adapter->max_sds_rings = max_sds_rings; - adapter->max_drv_tx_rings = max_drv_tx_rings; + adapter->drv_sds_rings = drv_sds_rings; + adapter->drv_tx_rings = drv_tx_rings; clear_bit(__QLCNIC_RESETTING, &adapter->state); return ret; } @@ -1097,11 +1168,11 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) QLCNIC_TEST_LEN * ETH_GSTRING_LEN); break; case ETH_SS_STATS: - num_stats = ARRAY_SIZE(qlcnic_tx_ring_stats_strings); - for (i = 0; i < adapter->max_drv_tx_rings; i++) { + num_stats = ARRAY_SIZE(qlcnic_tx_queue_stats_strings); + for (i = 0; i < adapter->drv_tx_rings; i++) { for (index = 0; index < num_stats; index++) { - sprintf(data, "tx_ring_%d %s", i, - qlcnic_tx_ring_stats_strings[index]); + sprintf(data, "tx_queue_%d %s", i, + qlcnic_tx_queue_stats_strings[index]); data += ETH_GSTRING_LEN; } } @@ -1199,6 +1270,36 @@ static u64 *qlcnic_fill_stats(u64 *data, void *stats, int type) return data; } +static void qlcnic_update_stats(struct qlcnic_adapter *adapter) +{ + struct qlcnic_host_tx_ring *tx_ring; + int ring; + + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { + tx_ring = &adapter->tx_ring[ring]; + adapter->stats.xmit_on += tx_ring->tx_stats.xmit_on; + adapter->stats.xmit_off += tx_ring->tx_stats.xmit_off; + adapter->stats.xmitcalled += tx_ring->tx_stats.xmit_called; + adapter->stats.xmitfinished += tx_ring->tx_stats.xmit_finished; + adapter->stats.txbytes += tx_ring->tx_stats.tx_bytes; + } +} + +static u64 *qlcnic_fill_tx_queue_stats(u64 *data, void *stats) +{ + struct qlcnic_host_tx_ring *tx_ring; + + tx_ring = (struct qlcnic_host_tx_ring *)stats; + + *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_on); + *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_off); + *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_called); + *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.xmit_finished); + *data++ = QLCNIC_FILL_STATS(tx_ring->tx_stats.tx_bytes); + + return data; +} + static void qlcnic_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { @@ -1206,19 +1307,20 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev, struct qlcnic_host_tx_ring *tx_ring; struct qlcnic_esw_statistics port_stats; struct qlcnic_mac_statistics mac_stats; - int index, ret, length, size, ring; + int index, ret, length, size, tx_size, ring; char *p; - memset(data, 0, adapter->max_drv_tx_rings * 4 * sizeof(u64)); - for (ring = 0, index = 0; ring < adapter->max_drv_tx_rings; ring++) { + tx_size = adapter->drv_tx_rings * QLCNIC_TX_STATS_LEN; + + memset(data, 0, tx_size * sizeof(u64)); + for (ring = 0, index = 0; ring < adapter->drv_tx_rings; ring++) { if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) { tx_ring = &adapter->tx_ring[ring]; - *data++ = tx_ring->xmit_on; - *data++ = tx_ring->xmit_off; - *data++ = tx_ring->xmit_called; - *data++ = tx_ring->xmit_finished; + data = qlcnic_fill_tx_queue_stats(data, tx_ring); + qlcnic_update_stats(adapter); } } + memset(data, 0, stats->n_stats * sizeof(u64)); length = QLCNIC_STATS_LEN; for (index = 0; index < length; index++) { @@ -1260,7 +1362,7 @@ static int qlcnic_set_led(struct net_device *dev, enum ethtool_phys_id_state state) { struct qlcnic_adapter *adapter = netdev_priv(dev); - int max_sds_rings = adapter->max_sds_rings; + int drv_sds_rings = adapter->drv_sds_rings; int err = -EIO, active = 1; if (qlcnic_83xx_check(adapter)) @@ -1318,7 +1420,7 @@ static int qlcnic_set_led(struct net_device *dev, } if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) - qlcnic_diag_free_res(dev, max_sds_rings); + qlcnic_diag_free_res(dev, drv_sds_rings); if (!active || err) clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); @@ -1659,7 +1761,6 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; bool valid_mask = false; int i, ret = 0; - u32 state; switch (val->flag) { case QLCNIC_FORCE_FW_DUMP_KEY: @@ -1712,9 +1813,8 @@ qlcnic_set_dump(struct net_device *netdev, struct ethtool_dump *val) case QLCNIC_SET_QUIESCENT: case QLCNIC_RESET_QUIESCENT: - state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE); - if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) - netdev_info(netdev, "Device in FAILED state\n"); + if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) + netdev_info(netdev, "Device is in non-operational state\n"); break; default: diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index b64e2bef94287cea63a5f452a7e97add7ee73986..6f7f60c09f071f0e84dce4c51e75c0a47cb6d9e7 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -445,7 +445,7 @@ int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr, mac_req = (struct qlcnic_mac_req *)&req.words[0]; mac_req->op = op; - memcpy(mac_req->mac_addr, addr, 6); + memcpy(mac_req->mac_addr, addr, ETH_ALEN); vlan_req = (struct qlcnic_vlan_req *)&req.words[1]; vlan_req->vlan_id = cpu_to_le16(vlan_id); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h index 272c356cf9b2a860beab0ec327f4b27bb47744bc..13303e7d1ed7afaec0c5fd41bd530f435cbb62e9 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h @@ -146,6 +146,12 @@ struct qlcnic_mailbox_metadata { #define QLCNIC_MBX_PORT_RSP_OK 0x1a #define QLCNIC_MBX_ASYNC_EVENT BIT_15 +/* Set HW Tx ring limit for 82xx adapter. */ +#define QLCNIC_MAX_HW_TX_RINGS 8 +#define QLCNIC_MAX_HW_VNIC_TX_RINGS 4 +#define QLCNIC_MAX_TX_RINGS 8 +#define QLCNIC_MAX_SDS_RINGS 8 + struct qlcnic_pci_info; struct qlcnic_info; struct qlcnic_cmd_args; @@ -176,7 +182,7 @@ int qlcnic_82xx_set_lb_mode(struct qlcnic_adapter *, u8); void qlcnic_82xx_write_crb(struct qlcnic_adapter *, char *, loff_t, size_t); void qlcnic_82xx_read_crb(struct qlcnic_adapter *, char *, loff_t, size_t); void qlcnic_82xx_dev_request_reset(struct qlcnic_adapter *, u32); -int qlcnic_82xx_setup_intr(struct qlcnic_adapter *, u8, int); +int qlcnic_82xx_setup_intr(struct qlcnic_adapter *); irqreturn_t qlcnic_82xx_clear_legacy_intr(struct qlcnic_adapter *); int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter, struct qlcnic_cmd_args *); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c index 66c26cf7a2b89290524a2d85005ee847f43e925b..e9c21e5d0ca95c7cc08df0c5f29cca2f7b473ba4 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c @@ -236,7 +236,7 @@ int qlcnic_alloc_sw_resources(struct qlcnic_adapter *adapter) spin_lock_init(&rds_ring->lock); } - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; sds_ring->irq = adapter->msix_entries[ring].vector; sds_ring->adapter = adapter; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index 11b4bb83b9308b2c4a9331ea737ee7766c074794..0149c94953474e6ad0ffd56b93cc440c67e77687 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -581,10 +581,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) goto drop_packet; } - if (qlcnic_check_multi_tx(adapter)) - tx_ring = &adapter->tx_ring[skb_get_queue_mapping(skb)]; - else - tx_ring = &adapter->tx_ring[0]; + tx_ring = &adapter->tx_ring[skb_get_queue_mapping(skb)]; num_txd = tx_ring->num_desc; frag_count = skb_shinfo(skb)->nr_frags + 1; @@ -607,8 +604,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) { netif_tx_start_queue(tx_ring->txq); } else { - adapter->stats.xmit_off++; - tx_ring->xmit_off++; + tx_ring->tx_stats.xmit_off++; return NETDEV_TX_BUSY; } } @@ -669,9 +665,8 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if (adapter->drv_mac_learn) qlcnic_send_filter(adapter, first_desc, skb); - adapter->stats.txbytes += skb->len; - adapter->stats.xmitcalled++; - tx_ring->xmit_called++; + tx_ring->tx_stats.tx_bytes += skb->len; + tx_ring->tx_stats.xmit_called++; qlcnic_update_cmd_producer(tx_ring); @@ -789,6 +784,9 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter, struct net_device *netdev = adapter->netdev; struct qlcnic_skb_frag *frag; + if (!spin_trylock(&adapter->tx_clean_lock)) + return 1; + sw_consumer = tx_ring->sw_consumer; hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); @@ -805,8 +803,7 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter, PCI_DMA_TODEVICE); frag->dma = 0ULL; } - adapter->stats.xmitfinished++; - tx_ring->xmit_finished++; + tx_ring->tx_stats.xmit_finished++; dev_kfree_skb_any(buffer->skb); buffer->skb = NULL; } @@ -823,8 +820,7 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter, netif_carrier_ok(netdev)) { if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) { netif_tx_wake_queue(tx_ring->txq); - adapter->stats.xmit_on++; - tx_ring->xmit_on++; + tx_ring->tx_stats.xmit_on++; } } adapter->tx_timeo_cnt = 0; @@ -844,6 +840,7 @@ static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter, */ hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); done = (sw_consumer == hw_consumer); + spin_unlock(&adapter->tx_clean_lock); return done; } @@ -1011,7 +1008,7 @@ static void qlcnic_handle_fw_message(int desc_cnt, int index, } break; case QLCNIC_C2H_OPCODE_GET_DCB_AEN: - qlcnic_dcb_handle_aen(adapter, (void *)&msg); + qlcnic_dcb_aen_handler(adapter->dcb, (void *)&msg); break; default: break; @@ -1463,18 +1460,18 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter, struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; struct qlcnic_host_tx_ring *tx_ring; - if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings)) + if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings)) return -ENOMEM; - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test && - (adapter->max_drv_tx_rings > 1)) { + (adapter->drv_tx_rings > QLCNIC_SINGLE_RING)) { netif_napi_add(netdev, &sds_ring->napi, qlcnic_rx_poll, NAPI_POLL_WEIGHT); } else { - if (ring == (adapter->max_sds_rings - 1)) + if (ring == (adapter->drv_sds_rings - 1)) netif_napi_add(netdev, &sds_ring->napi, qlcnic_poll, NAPI_POLL_WEIGHT); @@ -1491,7 +1488,7 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter, } if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) { - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; netif_napi_add(netdev, &tx_ring->napi, qlcnic_tx_poll, NAPI_POLL_WEIGHT); @@ -1508,7 +1505,7 @@ void qlcnic_82xx_napi_del(struct qlcnic_adapter *adapter) struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; struct qlcnic_host_tx_ring *tx_ring; - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; netif_napi_del(&sds_ring->napi); } @@ -1516,7 +1513,7 @@ void qlcnic_82xx_napi_del(struct qlcnic_adapter *adapter) qlcnic_free_sds_rings(adapter->recv_ctx); if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test) { - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; netif_napi_del(&tx_ring->napi); } @@ -1535,7 +1532,7 @@ void qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter) if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) return; - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; napi_enable(&sds_ring->napi); qlcnic_enable_int(sds_ring); @@ -1544,8 +1541,8 @@ void qlcnic_82xx_napi_enable(struct qlcnic_adapter *adapter) if (qlcnic_check_multi_tx(adapter) && (adapter->flags & QLCNIC_MSIX_ENABLED) && !adapter->ahw->diag_test && - (adapter->max_drv_tx_rings > 1)) { - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + (adapter->drv_tx_rings > QLCNIC_SINGLE_RING)) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; napi_enable(&tx_ring->napi); qlcnic_enable_tx_intr(adapter, tx_ring); @@ -1563,7 +1560,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter) if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) return; - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; qlcnic_disable_int(sds_ring); napi_synchronize(&sds_ring->napi); @@ -1573,7 +1570,7 @@ void qlcnic_82xx_napi_disable(struct qlcnic_adapter *adapter) if ((adapter->flags & QLCNIC_MSIX_ENABLED) && !adapter->ahw->diag_test && qlcnic_check_multi_tx(adapter)) { - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; qlcnic_disable_tx_int(adapter, tx_ring); napi_synchronize(&tx_ring->napi); @@ -1911,7 +1908,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter) if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) return; - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; napi_enable(&sds_ring->napi); if (adapter->flags & QLCNIC_MSIX_ENABLED) @@ -1920,7 +1917,7 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter) if ((adapter->flags & QLCNIC_MSIX_ENABLED) && !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; napi_enable(&tx_ring->napi); qlcnic_83xx_enable_tx_intr(adapter, tx_ring); @@ -1938,7 +1935,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter) if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) return; - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; if (adapter->flags & QLCNIC_MSIX_ENABLED) qlcnic_83xx_disable_intr(adapter, sds_ring); @@ -1948,7 +1945,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter) if ((adapter->flags & QLCNIC_MSIX_ENABLED) && !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; qlcnic_83xx_disable_tx_intr(adapter, tx_ring); napi_synchronize(&tx_ring->napi); @@ -1965,10 +1962,10 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter, struct qlcnic_host_tx_ring *tx_ring; struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings)) + if (qlcnic_alloc_sds_rings(recv_ctx, adapter->drv_sds_rings)) return -ENOMEM; - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; if (adapter->flags & QLCNIC_MSIX_ENABLED) { if (!(adapter->flags & QLCNIC_TX_INTR_SHARED)) @@ -1994,7 +1991,7 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter, if ((adapter->flags & QLCNIC_MSIX_ENABLED) && !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; netif_napi_add(netdev, &tx_ring->napi, qlcnic_83xx_msix_tx_poll, @@ -2012,7 +2009,7 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter) struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; struct qlcnic_host_tx_ring *tx_ring; - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; netif_napi_del(&sds_ring->napi); } @@ -2021,7 +2018,7 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter) if ((adapter->flags & QLCNIC_MSIX_ENABLED) && !(adapter->flags & QLCNIC_TX_INTR_SHARED)) { - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; netif_napi_del(&tx_ring->napi); } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index d8f4897e9e820ebef4469c2c744e75910f6ebf2c..05c1eef8df1325359ef1bf73df8344492a95876d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -548,36 +548,75 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = { .io_resume = qlcnic_82xx_io_resume, }; -static void qlcnic_get_multiq_capability(struct qlcnic_adapter *adapter) +static int qlcnic_check_multi_tx_capability(struct qlcnic_adapter *adapter) { struct qlcnic_hardware_context *ahw = adapter->ahw; - int num_tx_q; - if (ahw->msix_supported && + if (qlcnic_82xx_check(adapter) && (ahw->extra_capability[0] & QLCNIC_FW_CAPABILITY_2_MULTI_TX)) { - num_tx_q = min_t(int, QLCNIC_DEF_NUM_TX_RINGS, - num_online_cpus()); - if (num_tx_q > 1) { - test_and_set_bit(__QLCNIC_MULTI_TX_UNIQUE, - &adapter->state); - adapter->max_drv_tx_rings = num_tx_q; - } + test_and_set_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state); + return 0; } else { - adapter->max_drv_tx_rings = 1; + return 1; } } +static int qlcnic_max_rings(struct qlcnic_adapter *adapter, u8 ring_cnt, + int queue_type) +{ + int num_rings, max_rings = QLCNIC_MAX_SDS_RINGS; + + if (queue_type == QLCNIC_RX_QUEUE) + max_rings = adapter->max_sds_rings; + else if (queue_type == QLCNIC_TX_QUEUE) + max_rings = adapter->max_tx_rings; + + num_rings = rounddown_pow_of_two(min_t(int, num_online_cpus(), + max_rings)); + + if (ring_cnt > num_rings) + return num_rings; + else + return ring_cnt; +} + +void qlcnic_set_tx_ring_count(struct qlcnic_adapter *adapter, u8 tx_cnt) +{ + /* 83xx adapter does not have max_tx_rings intialized in probe */ + if (adapter->max_tx_rings) + adapter->drv_tx_rings = qlcnic_max_rings(adapter, tx_cnt, + QLCNIC_TX_QUEUE); + else + adapter->drv_tx_rings = tx_cnt; + + dev_info(&adapter->pdev->dev, "Set %d Tx rings\n", + adapter->drv_tx_rings); +} + +void qlcnic_set_sds_ring_count(struct qlcnic_adapter *adapter, u8 rx_cnt) +{ + /* 83xx adapter does not have max_sds_rings intialized in probe */ + if (adapter->max_sds_rings) + adapter->drv_sds_rings = qlcnic_max_rings(adapter, rx_cnt, + QLCNIC_RX_QUEUE); + else + adapter->drv_sds_rings = rx_cnt; + + dev_info(&adapter->pdev->dev, "Set %d SDS rings\n", + adapter->drv_sds_rings); +} + int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix) { struct pci_dev *pdev = adapter->pdev; - int max_tx_rings, max_sds_rings, tx_vector; + int drv_tx_rings, drv_sds_rings, tx_vector; int err = -1, i; if (adapter->flags & QLCNIC_TX_INTR_SHARED) { - max_tx_rings = 0; + drv_tx_rings = 0; tx_vector = 0; } else { - max_tx_rings = adapter->max_drv_tx_rings; + drv_tx_rings = adapter->drv_tx_rings; tx_vector = 1; } @@ -589,7 +628,7 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix) return -ENOMEM; } - adapter->max_sds_rings = 1; + adapter->drv_sds_rings = QLCNIC_SINGLE_RING; adapter->flags &= ~(QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED); if (adapter->ahw->msix_supported) { @@ -602,18 +641,18 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix) if (qlcnic_83xx_check(adapter)) { adapter->ahw->num_msix = num_msix; /* subtract mail box and tx ring vectors */ - adapter->max_sds_rings = num_msix - - max_tx_rings - 1; + adapter->drv_sds_rings = num_msix - + drv_tx_rings - 1; } else { adapter->ahw->num_msix = num_msix; if (qlcnic_check_multi_tx(adapter) && !adapter->ahw->diag_test && - (adapter->max_drv_tx_rings > 1)) - max_sds_rings = num_msix - max_tx_rings; + (adapter->drv_tx_rings > 1)) + drv_sds_rings = num_msix - drv_tx_rings; else - max_sds_rings = num_msix; + drv_sds_rings = num_msix; - adapter->max_sds_rings = max_sds_rings; + adapter->drv_sds_rings = drv_sds_rings; } dev_info(&pdev->dev, "using msi-x interrupts\n"); return err; @@ -624,13 +663,13 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix) if (qlcnic_83xx_check(adapter)) { if (err < (QLC_83XX_MINIMUM_VECTOR - tx_vector)) return err; - err -= (max_tx_rings + 1); + err -= drv_tx_rings + 1; num_msix = rounddown_pow_of_two(err); - num_msix += (max_tx_rings + 1); + num_msix += drv_tx_rings + 1; } else { num_msix = rounddown_pow_of_two(err); if (qlcnic_check_multi_tx(adapter)) - num_msix += max_tx_rings; + num_msix += drv_tx_rings; } if (num_msix) { @@ -683,25 +722,14 @@ static int qlcnic_enable_msi_legacy(struct qlcnic_adapter *adapter) return err; } -int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr, int txq) +int qlcnic_82xx_setup_intr(struct qlcnic_adapter *adapter) { - struct qlcnic_hardware_context *ahw = adapter->ahw; int num_msix, err = 0; - if (!num_intr) - num_intr = QLCNIC_DEF_NUM_STS_DESC_RINGS; + num_msix = adapter->drv_sds_rings; - if (ahw->msix_supported) { - num_msix = rounddown_pow_of_two(min_t(int, num_online_cpus(), - num_intr)); - if (qlcnic_check_multi_tx(adapter)) { - if (txq) - adapter->max_drv_tx_rings = txq; - num_msix += adapter->max_drv_tx_rings; - } - } else { - num_msix = 1; - } + if (qlcnic_check_multi_tx(adapter)) + num_msix += adapter->drv_tx_rings; err = qlcnic_enable_msix(adapter, num_msix); if (err == -ENOMEM) @@ -819,7 +847,7 @@ static bool qlcnic_port_eswitch_cfg_capability(struct qlcnic_adapter *adapter) int qlcnic_init_pci_info(struct qlcnic_adapter *adapter) { struct qlcnic_pci_info *pci_info; - int i, ret = 0, j = 0; + int i, id = 0, ret = 0, j = 0; u16 act_pci_func; u8 pfn; @@ -860,7 +888,8 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter) continue; if (qlcnic_port_eswitch_cfg_capability(adapter)) { - if (!qlcnic_83xx_enable_port_eswitch(adapter, pfn)) + if (!qlcnic_83xx_set_port_eswitch_status(adapter, pfn, + &id)) adapter->npars[j].eswitch_status = true; else continue; @@ -875,15 +904,16 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *adapter) adapter->npars[j].min_bw = pci_info[i].tx_min_bw; adapter->npars[j].max_bw = pci_info[i].tx_max_bw; + memcpy(&adapter->npars[j].mac, &pci_info[i].mac, ETH_ALEN); j++; } - if (qlcnic_82xx_check(adapter)) { + /* Update eSwitch status for adapters without per port eSwitch + * configuration capability + */ + if (!qlcnic_port_eswitch_cfg_capability(adapter)) { for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++) adapter->eswitch[i].flags |= QLCNIC_SWITCH_ENABLE; - } else if (!qlcnic_port_eswitch_cfg_capability(adapter)) { - for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++) - qlcnic_enable_eswitch(adapter, i, 1); } kfree(pci_info); @@ -1138,14 +1168,18 @@ qlcnic_initialize_nic(struct qlcnic_adapter *adapter) adapter->ahw->max_mac_filters = nic_info.max_mac_filters; adapter->ahw->max_mtu = nic_info.max_mtu; - /* Disable NPAR for 83XX */ - if (qlcnic_83xx_check(adapter)) - return err; - - if (adapter->ahw->capabilities & BIT_6) + if (adapter->ahw->capabilities & BIT_6) { adapter->flags |= QLCNIC_ESWITCH_ENABLED; - else + adapter->ahw->nic_mode = QLCNIC_VNIC_MODE; + adapter->max_tx_rings = QLCNIC_MAX_HW_VNIC_TX_RINGS; + adapter->max_sds_rings = QLCNIC_MAX_VNIC_SDS_RINGS; + + dev_info(&adapter->pdev->dev, "vNIC mode enabled.\n"); + } else { + adapter->ahw->nic_mode = QLCNIC_DEFAULT_MODE; + adapter->max_tx_rings = QLCNIC_MAX_HW_TX_RINGS; adapter->flags &= ~QLCNIC_ESWITCH_ENABLED; + } return err; } @@ -1293,6 +1327,8 @@ qlcnic_check_eswitch_mode(struct qlcnic_adapter *adapter) "HAL Version: %d, Privileged function\n", adapter->ahw->fw_hal_version); } + } else { + adapter->ahw->nic_mode = QLCNIC_DEFAULT_MODE; } adapter->flags |= QLCNIC_ADAPTER_INITIALIZED; @@ -1552,7 +1588,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter) if (qlcnic_82xx_check(adapter) || (qlcnic_83xx_check(adapter) && (adapter->flags & QLCNIC_MSIX_ENABLED))) { - num_sds_rings = adapter->max_sds_rings; + num_sds_rings = adapter->drv_sds_rings; for (ring = 0; ring < num_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; if (qlcnic_82xx_check(adapter) && @@ -1586,7 +1622,7 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter) (adapter->flags & QLCNIC_MSIX_ENABLED) && !(adapter->flags & QLCNIC_TX_INTR_SHARED))) { handler = qlcnic_msix_tx_intr; - for (ring = 0; ring < adapter->max_drv_tx_rings; + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; snprintf(tx_ring->name, sizeof(tx_ring->name), @@ -1614,7 +1650,7 @@ qlcnic_free_irq(struct qlcnic_adapter *adapter) if (qlcnic_82xx_check(adapter) || (qlcnic_83xx_check(adapter) && (adapter->flags & QLCNIC_MSIX_ENABLED))) { - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; free_irq(sds_ring->irq, sds_ring); } @@ -1623,7 +1659,7 @@ qlcnic_free_irq(struct qlcnic_adapter *adapter) !(adapter->flags & QLCNIC_TX_INTR_SHARED)) || (qlcnic_82xx_check(adapter) && qlcnic_check_multi_tx(adapter))) { - for (ring = 0; ring < adapter->max_drv_tx_rings; + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; if (tx_ring->irq) @@ -1677,7 +1713,7 @@ int __qlcnic_up(struct qlcnic_adapter *adapter, struct net_device *netdev) adapter->ahw->linkup = 0; - if (adapter->max_sds_rings > 1) + if (adapter->drv_sds_rings > 1) qlcnic_config_rss(adapter, 1); qlcnic_config_intr_coalesce(adapter); @@ -1719,6 +1755,7 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev) if (qlcnic_sriov_vf_check(adapter)) qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc); smp_mb(); + spin_lock(&adapter->tx_clean_lock); netif_carrier_off(netdev); adapter->ahw->linkup = 0; netif_tx_disable(netdev); @@ -1737,8 +1774,9 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev) qlcnic_reset_rx_buffers_list(adapter); - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) + for (ring = 0; ring < adapter->drv_tx_rings; ring++) qlcnic_release_tx_buffers(adapter, &adapter->tx_ring[ring]); + spin_unlock(&adapter->tx_clean_lock); } /* Usage: During suspend and firmware recovery module */ @@ -1814,16 +1852,16 @@ void qlcnic_detach(struct qlcnic_adapter *adapter) adapter->is_up = 0; } -void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings) +void qlcnic_diag_free_res(struct net_device *netdev, int drv_sds_rings) { struct qlcnic_adapter *adapter = netdev_priv(netdev); struct qlcnic_host_sds_ring *sds_ring; - int max_tx_rings = adapter->max_drv_tx_rings; + int drv_tx_rings = adapter->drv_tx_rings; int ring; clear_bit(__QLCNIC_DEV_UP, &adapter->state); if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) { - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &adapter->recv_ctx->sds_rings[ring]; qlcnic_disable_int(sds_ring); } @@ -1834,8 +1872,8 @@ void qlcnic_diag_free_res(struct net_device *netdev, int max_sds_rings) qlcnic_detach(adapter); adapter->ahw->diag_test = 0; - adapter->max_sds_rings = max_sds_rings; - adapter->max_drv_tx_rings = max_tx_rings; + adapter->drv_sds_rings = drv_sds_rings; + adapter->drv_tx_rings = drv_tx_rings; if (qlcnic_attach(adapter)) goto out; @@ -1901,10 +1939,10 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test) qlcnic_detach(adapter); - adapter->max_sds_rings = 1; + adapter->drv_sds_rings = QLCNIC_SINGLE_RING; + adapter->drv_tx_rings = QLCNIC_SINGLE_RING; adapter->ahw->diag_test = test; adapter->ahw->linkup = 0; - adapter->max_drv_tx_rings = 1; ret = qlcnic_attach(adapter); if (ret) { @@ -1925,7 +1963,7 @@ int qlcnic_diag_alloc_res(struct net_device *netdev, int test) } if (adapter->ahw->diag_test == QLCNIC_INTERRUPT_TEST) { - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &adapter->recv_ctx->sds_rings[ring]; qlcnic_enable_int(sds_ring); } @@ -2072,7 +2110,7 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev, return err; } - qlcnic_dcb_init_dcbnl_ops(adapter); + qlcnic_dcb_init_dcbnl_ops(adapter->dcb); return 0; } @@ -2098,7 +2136,7 @@ void qlcnic_free_tx_rings(struct qlcnic_adapter *adapter) int ring; struct qlcnic_host_tx_ring *tx_ring; - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; if (tx_ring && tx_ring->cmd_buf_arr != NULL) { vfree(tx_ring->cmd_buf_arr); @@ -2116,14 +2154,14 @@ int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter, struct qlcnic_host_tx_ring *tx_ring; struct qlcnic_cmd_buffer *cmd_buf_arr; - tx_ring = kcalloc(adapter->max_drv_tx_rings, + tx_ring = kcalloc(adapter->drv_tx_rings, sizeof(struct qlcnic_host_tx_ring), GFP_KERNEL); if (tx_ring == NULL) return -ENOMEM; adapter->tx_ring = tx_ring; - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; tx_ring->num_desc = adapter->num_txd; tx_ring->txq = netdev_get_tx_queue(netdev, ring); @@ -2138,11 +2176,11 @@ int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter, if (qlcnic_83xx_check(adapter) || (qlcnic_82xx_check(adapter) && qlcnic_check_multi_tx(adapter))) { - for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) { + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; tx_ring->adapter = adapter; if (adapter->flags & QLCNIC_MSIX_ENABLED) { - index = adapter->max_sds_rings + ring; + index = adapter->drv_sds_rings + ring; vector = adapter->msix_entries[index].vector; tx_ring->irq = vector; } @@ -2166,17 +2204,6 @@ void qlcnic_set_drv_version(struct qlcnic_adapter *adapter) qlcnic_fw_cmd_set_drv_version(adapter, fw_cmd); } -static int qlcnic_register_dcb(struct qlcnic_adapter *adapter) -{ - return __qlcnic_register_dcb(adapter); -} - -void qlcnic_clear_dcb_ops(struct qlcnic_adapter *adapter) -{ - kfree(adapter->dcb); - adapter->dcb = NULL; -} - static int qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -2185,6 +2212,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) struct qlcnic_hardware_context *ahw; int err, pci_using_dac = -1; char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */ + struct qlcnic_dcb *dcb; if (pdev->is_virtfn) return -ENODEV; @@ -2271,6 +2299,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) rwlock_init(&adapter->ahw->crb_lock); mutex_init(&adapter->ahw->mem_lock); + spin_lock_init(&adapter->tx_clean_lock); INIT_LIST_HEAD(&adapter->mac_list); qlcnic_register_dcb(adapter); @@ -2285,38 +2314,51 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_maintenance_mode; } - qlcnic_get_multiq_capability(adapter); - - if ((adapter->ahw->act_pci_func > 2) && - qlcnic_check_multi_tx(adapter)) { - adapter->max_drv_tx_rings = QLCNIC_DEF_NUM_TX_RINGS; - dev_info(&adapter->pdev->dev, - "vNIC mode enabled, Set max TX rings = %d\n", - adapter->max_drv_tx_rings); + /* compute and set default and max tx/sds rings */ + if (adapter->ahw->msix_supported) { + if (qlcnic_check_multi_tx_capability(adapter) == 1) + qlcnic_set_tx_ring_count(adapter, + QLCNIC_SINGLE_RING); + else + qlcnic_set_tx_ring_count(adapter, + QLCNIC_DEF_TX_RINGS); + qlcnic_set_sds_ring_count(adapter, + QLCNIC_DEF_SDS_RINGS); + } else { + qlcnic_set_tx_ring_count(adapter, QLCNIC_SINGLE_RING); + qlcnic_set_sds_ring_count(adapter, QLCNIC_SINGLE_RING); } - if (!qlcnic_check_multi_tx(adapter)) { - clear_bit(__QLCNIC_MULTI_TX_UNIQUE, &adapter->state); - adapter->max_drv_tx_rings = 1; - } err = qlcnic_setup_idc_param(adapter); if (err) goto err_out_free_hw; adapter->flags |= QLCNIC_NEED_FLR; - if (adapter->dcb && qlcnic_dcb_attach(adapter)) - qlcnic_clear_dcb_ops(adapter); + dcb = adapter->dcb; + if (dcb && qlcnic_dcb_attach(dcb)) + qlcnic_clear_dcb_ops(dcb); } else if (qlcnic_83xx_check(adapter)) { - adapter->max_drv_tx_rings = 1; qlcnic_83xx_check_vf(adapter, ent); adapter->portnum = adapter->ahw->pci_func; err = qlcnic_83xx_init(adapter, pci_using_dac); if (err) { - dev_err(&pdev->dev, "%s: failed\n", __func__); - goto err_out_free_hw; + switch (err) { + case -ENOTRECOVERABLE: + dev_err(&pdev->dev, "Adapter initialization failed due to a faulty hardware. Please reboot\n"); + dev_err(&pdev->dev, "If reboot doesn't help, please replace the adapter with new one and return the faulty adapter for repair\n"); + goto err_out_free_hw; + case -ENOMEM: + dev_err(&pdev->dev, "Adapter initialization failed. Please reboot\n"); + goto err_out_free_hw; + default: + dev_err(&pdev->dev, "Adapter initialization failed. A reboot may be required to recover from this failure\n"); + dev_err(&pdev->dev, "If reboot does not help to recover from this failure, try a flash update of the adapter\n"); + goto err_out_maintenance_mode; + } } + if (qlcnic_sriov_vf_check(adapter)) return 0; } else { @@ -2344,7 +2386,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) "Device does not support MSI interrupts\n"); if (qlcnic_82xx_check(adapter)) { - err = qlcnic_setup_intr(adapter, 0, 0); + err = qlcnic_setup_intr(adapter); if (err) { dev_err(&pdev->dev, "Failed to setup interrupt\n"); goto err_out_disable_msi; @@ -2414,13 +2456,20 @@ err_out_free_res: pci_release_regions(pdev); err_out_disable_pdev: - pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); return err; err_out_maintenance_mode: + set_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state); netdev->netdev_ops = &qlcnic_netdev_failed_ops; SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_failed_ops); + ahw->port_type = QLCNIC_XGBE; + + if (qlcnic_83xx_check(adapter)) + adapter->tgt_status_reg = NULL; + else + ahw->board_type = QLCNIC_BRDTYPE_P3P_10G_SFP_PLUS; + err = register_netdev(netdev); if (err) { @@ -2451,7 +2500,7 @@ static void qlcnic_remove(struct pci_dev *pdev) qlcnic_cancel_idc_work(adapter); ahw = adapter->ahw; - qlcnic_dcb_free(adapter); + qlcnic_dcb_free(adapter->dcb); unregister_netdev(netdev); qlcnic_sriov_cleanup(adapter); @@ -2490,7 +2539,6 @@ static void qlcnic_remove(struct pci_dev *pdev) pci_disable_pcie_error_reporting(pdev); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); if (adapter->qlcnic_wq) { destroy_workqueue(adapter->qlcnic_wq); @@ -2543,12 +2591,11 @@ static int qlcnic_resume(struct pci_dev *pdev) static int qlcnic_open(struct net_device *netdev) { struct qlcnic_adapter *adapter = netdev_priv(netdev); - u32 state; int err; - state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE); - if (state == QLCNIC_DEV_FAILED || state == QLCNIC_DEV_BADBAD) { - netdev_err(netdev, "%s: Device is in FAILED state\n", __func__); + if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) { + netdev_err(netdev, "%s: Device is in non-operational state\n", + __func__); return -EIO; } @@ -2710,24 +2757,21 @@ static void qlcnic_tx_timeout(struct net_device *netdev) QLCNIC_FORCE_FW_DUMP_KEY); } else { netdev_info(netdev, "Tx timeout, reset adapter context.\n"); - if (qlcnic_82xx_check(adapter)) { - for (ring = 0; ring < adapter->max_drv_tx_rings; - ring++) { - tx_ring = &adapter->tx_ring[ring]; - dev_info(&netdev->dev, "ring=%d\n", ring); - dev_info(&netdev->dev, "crb_intr_mask=%d\n", - readl(tx_ring->crb_intr_mask)); - dev_info(&netdev->dev, "producer=%d\n", - readl(tx_ring->crb_cmd_producer)); - dev_info(&netdev->dev, "sw_consumer = %d\n", - tx_ring->sw_consumer); - dev_info(&netdev->dev, "hw_consumer = %d\n", - le32_to_cpu(*(tx_ring->hw_consumer))); - dev_info(&netdev->dev, "xmit-on=%llu\n", - tx_ring->xmit_on); - dev_info(&netdev->dev, "xmit-off=%llu\n", - tx_ring->xmit_off); - } + for (ring = 0; ring < adapter->drv_tx_rings; ring++) { + tx_ring = &adapter->tx_ring[ring]; + netdev_info(netdev, "Tx ring=%d\n", ring); + netdev_info(netdev, + "crb_intr_mask=%d, producer=%d, sw_consumer=%d, hw_consumer=%d\n", + readl(tx_ring->crb_intr_mask), + readl(tx_ring->crb_cmd_producer), + tx_ring->sw_consumer, + le32_to_cpu(*(tx_ring->hw_consumer))); + netdev_info(netdev, + "xmit_finished=%llu, xmit_called=%llu, xmit_on=%llu, xmit_off=%llu\n", + tx_ring->tx_stats.xmit_finished, + tx_ring->tx_stats.xmit_called, + tx_ring->tx_stats.xmit_on, + tx_ring->tx_stats.xmit_off); } adapter->ahw->reset_context = 1; } @@ -2841,7 +2885,7 @@ static void qlcnic_poll_controller(struct net_device *netdev) struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; disable_irq(adapter->irq); - for (ring = 0; ring < adapter->max_sds_rings; ring++) { + for (ring = 0; ring < adapter->drv_sds_rings; ring++) { sds_ring = &recv_ctx->sds_rings[ring]; qlcnic_intr(adapter->irq, sds_ring); } @@ -3261,8 +3305,9 @@ void qlcnic_82xx_dev_request_reset(struct qlcnic_adapter *adapter, u32 key) return; state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE); - if (state == QLCNIC_DEV_FAILED || state == QLCNIC_DEV_BADBAD) { - netdev_err(adapter->netdev, "%s: Device is in FAILED state\n", + + if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) { + netdev_err(adapter->netdev, "%s: Device is in non-operational state\n", __func__); qlcnic_api_unlock(adapter); @@ -3329,7 +3374,7 @@ qlcnic_attach_work(struct work_struct *work) return; } attach: - qlcnic_dcb_get_info(adapter); + qlcnic_dcb_get_info(adapter->dcb); if (netif_running(netdev)) { if (qlcnic_up(adapter, netdev)) @@ -3354,6 +3399,8 @@ done: static int qlcnic_check_health(struct qlcnic_adapter *adapter) { + struct qlcnic_hardware_context *ahw = adapter->ahw; + struct qlcnic_fw_dump *fw_dump = &ahw->fw_dump; u32 state = 0, heartbeat; u32 peg_status; int err = 0; @@ -3378,7 +3425,7 @@ qlcnic_check_health(struct qlcnic_adapter *adapter) if (adapter->need_fw_reset) goto detach; - if (adapter->ahw->reset_context && qlcnic_auto_fw_reset) + if (ahw->reset_context && qlcnic_auto_fw_reset) qlcnic_reset_hw_context(adapter); return 0; @@ -3421,6 +3468,9 @@ detach: qlcnic_schedule_work(adapter, qlcnic_detach_work, 0); QLCDB(adapter, DRV, "fw recovery scheduled.\n"); + } else if (!qlcnic_auto_fw_reset && fw_dump->enable && + adapter->flags & QLCNIC_FW_RESET_OWNER) { + qlcnic_dump_fw(adapter); } return 1; @@ -3502,7 +3552,7 @@ static int qlcnic_attach_func(struct pci_dev *pdev) qlcnic_clr_drv_state(adapter); kfree(adapter->msix_entries); adapter->msix_entries = NULL; - err = qlcnic_setup_intr(adapter, 0, 0); + err = qlcnic_setup_intr(adapter); if (err) { kfree(adapter->msix_entries); @@ -3647,130 +3697,90 @@ qlcnicvf_start_firmware(struct qlcnic_adapter *adapter) return err; } -int qlcnic_validate_max_tx_rings(struct qlcnic_adapter *adapter, u32 txq) +int qlcnic_validate_rings(struct qlcnic_adapter *adapter, __u32 ring_cnt, + int queue_type) { struct net_device *netdev = adapter->netdev; - u8 max_hw = QLCNIC_MAX_TX_RINGS; - u32 max_allowed; + u8 max_hw_rings = 0; + char buf[8]; + int cur_rings; - if (!qlcnic_use_msi_x && !qlcnic_use_msi) { - netdev_err(netdev, "No Multi TX-Q support in INT-x mode\n"); - return -EINVAL; + if (queue_type == QLCNIC_RX_QUEUE) { + max_hw_rings = adapter->max_sds_rings; + cur_rings = adapter->drv_sds_rings; + strcpy(buf, "SDS"); + } else if (queue_type == QLCNIC_TX_QUEUE) { + max_hw_rings = adapter->max_tx_rings; + cur_rings = adapter->drv_tx_rings; + strcpy(buf, "Tx"); } - if (!qlcnic_check_multi_tx(adapter)) { - netdev_err(netdev, "No Multi TX-Q support\n"); + if (!qlcnic_use_msi_x && !qlcnic_use_msi) { + netdev_err(netdev, "No RSS/TSS support in INT-x mode\n"); return -EINVAL; } - if (txq > QLCNIC_MAX_TX_RINGS) { - netdev_err(netdev, "Invalid ring count\n"); + if (adapter->flags & QLCNIC_MSI_ENABLED) { + netdev_err(netdev, "No RSS/TSS support in MSI mode\n"); return -EINVAL; } - max_allowed = rounddown_pow_of_two(min_t(int, max_hw, - num_online_cpus())); - if ((txq > max_allowed) || !is_power_of_2(txq)) { - if (!is_power_of_2(txq)) - netdev_err(netdev, - "TX queue should be a power of 2\n"); - if (txq > num_online_cpus()) - netdev_err(netdev, - "Tx queue should not be higher than [%u], number of online CPUs in the system\n", - num_online_cpus()); - netdev_err(netdev, "Unable to configure %u Tx rings\n", txq); + if (ring_cnt < 2) { + netdev_err(netdev, + "%s rings value should not be lower than 2\n", buf); return -EINVAL; } - return 0; -} - -int qlcnic_validate_max_rss(struct qlcnic_adapter *adapter, - __u32 val) -{ - struct net_device *netdev = adapter->netdev; - u8 max_hw = adapter->ahw->max_rx_ques; - u32 max_allowed; - - if (!qlcnic_use_msi_x && !qlcnic_use_msi) { - netdev_err(netdev, "No RSS support in INT-x mode\n"); + if (!is_power_of_2(ring_cnt)) { + netdev_err(netdev, "%s rings value should be a power of 2\n", + buf); return -EINVAL; } - if (val > QLCNIC_MAX_SDS_RINGS) { - netdev_err(netdev, "RSS value should not be higher than %u\n", - QLCNIC_MAX_SDS_RINGS); - return -EINVAL; + if (qlcnic_82xx_check(adapter) && (queue_type == QLCNIC_TX_QUEUE) && + !qlcnic_check_multi_tx(adapter)) { + netdev_err(netdev, "No Multi Tx queue support\n"); + return -EINVAL; } - max_allowed = rounddown_pow_of_two(min_t(int, max_hw, - num_online_cpus())); - if ((val > max_allowed) || (val < 2) || !is_power_of_2(val)) { - if (!is_power_of_2(val)) - netdev_err(netdev, "RSS value should be a power of 2\n"); - - if (val < 2) - netdev_err(netdev, "RSS value should not be lower than 2\n"); - - if (val > max_hw) - netdev_err(netdev, - "RSS value should not be higher than[%u], the max RSS rings supported by the adapter\n", - max_hw); - - if (val > num_online_cpus()) - netdev_err(netdev, - "RSS value should not be higher than[%u], number of online CPUs in the system\n", - num_online_cpus()); - - netdev_err(netdev, "Unable to configure %u RSS rings\n", val); - + if (ring_cnt > num_online_cpus()) { + netdev_err(netdev, + "%s value[%u] should not be higher than, number of online CPUs\n", + buf, num_online_cpus()); return -EINVAL; } + return 0; } -int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, int txq) +int qlcnic_setup_rings(struct qlcnic_adapter *adapter, u8 rx_cnt, u8 tx_cnt) { - int err; struct net_device *netdev = adapter->netdev; - int num_msix; + int err; if (test_bit(__QLCNIC_RESETTING, &adapter->state)) return -EBUSY; - if (qlcnic_82xx_check(adapter) && !qlcnic_use_msi_x && - !qlcnic_use_msi) { - netdev_err(netdev, "No RSS support in INT-x mode\n"); - return -EINVAL; - } - netif_device_detach(netdev); if (netif_running(netdev)) __qlcnic_down(adapter, netdev); qlcnic_detach(adapter); - if (qlcnic_82xx_check(adapter)) { - if (txq != 0) - adapter->max_drv_tx_rings = txq; - - if (qlcnic_check_multi_tx(adapter) && - (txq > adapter->max_drv_tx_rings)) - num_msix = adapter->max_drv_tx_rings; - else - num_msix = data; - } - if (qlcnic_83xx_check(adapter)) { qlcnic_83xx_free_mbx_intr(adapter); qlcnic_83xx_enable_mbx_poll(adapter); } - netif_set_real_num_tx_queues(netdev, adapter->max_drv_tx_rings); - qlcnic_teardown_intr(adapter); - err = qlcnic_setup_intr(adapter, data, txq); + /* compute and set default and max tx/sds rings */ + qlcnic_set_tx_ring_count(adapter, tx_cnt); + qlcnic_set_sds_ring_count(adapter, rx_cnt); + + netif_set_real_num_tx_queues(netdev, adapter->drv_tx_rings); + + err = qlcnic_setup_intr(adapter); if (err) { kfree(adapter->msix_entries); netdev_err(netdev, "failed to setup interrupt\n"); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index 15513608d4808851917f1b5294e7733a5c4ea5cd..7763962e2ec4e9128ae22cf7b8ee694248d8058d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -1187,41 +1187,38 @@ int qlcnic_dump_fw(struct qlcnic_adapter *adapter) } if (ops_index == ops_cnt) { - dev_info(&adapter->pdev->dev, - "Invalid entry type %d, exiting dump\n", + dev_info(dev, "Skipping unknown entry opcode %d\n", entry->hdr.type); - goto error; + entry->hdr.flags |= QLCNIC_DUMP_SKIP; + entry_offset += entry->hdr.offset; + continue; } /* Collect dump for this entry */ dump = fw_dump_ops[ops_index].handler(adapter, entry, buffer); - if (!qlcnic_valid_dump_entry(&adapter->pdev->dev, entry, dump)) + if (!qlcnic_valid_dump_entry(dev, entry, dump)) { entry->hdr.flags |= QLCNIC_DUMP_SKIP; + entry_offset += entry->hdr.offset; + continue; + } + buf_offset += entry->hdr.cap_size; entry_offset += entry->hdr.offset; buffer = fw_dump->data + buf_offset; } - if (dump_size != buf_offset) { - dev_info(&adapter->pdev->dev, - "Captured(%d) and expected size(%d) do not match\n", - buf_offset, dump_size); - goto error; - } else { - fw_dump->clr = 1; - snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", - adapter->netdev->name); - dev_info(&adapter->pdev->dev, "%s: Dump data, %d bytes captured\n", - adapter->netdev->name, fw_dump->size); - /* Send a udev event to notify availability of FW dump */ - kobject_uevent_env(&adapter->pdev->dev.kobj, KOBJ_CHANGE, msg); - return 0; - } -error: + + fw_dump->clr = 1; + snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", adapter->netdev->name); + dev_info(dev, "%s: Dump data %d bytes captured, template header size %d bytes\n", + adapter->netdev->name, fw_dump->size, tmpl_hdr->size); + /* Send a udev event to notify availability of FW dump */ + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, msg); + if (fw_dump->use_pex_dma) dma_free_coherent(dev, QLC_PEX_DMA_READ_SIZE, fw_dump->dma_buffer, fw_dump->phys_addr); - vfree(fw_dump->data); - return -EINVAL; + + return 0; } void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *adapter) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c index 392b9bd12b4fb67585501f817defd1fb0194b9ca..21a4b274d2e40d898e97958a170ae93696549604 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -500,6 +500,7 @@ static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter) static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, int pci_using_dac) { + struct qlcnic_dcb *dcb; int err; INIT_LIST_HEAD(&adapter->vf_mc_list); @@ -507,7 +508,11 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, dev_warn(&adapter->pdev->dev, "Device does not support MSI interrupts\n"); - err = qlcnic_setup_intr(adapter, 1, 0); + /* compute and set default and max tx/sds rings */ + qlcnic_set_tx_ring_count(adapter, QLCNIC_SINGLE_RING); + qlcnic_set_sds_ring_count(adapter, QLCNIC_SINGLE_RING); + + err = qlcnic_setup_intr(adapter); if (err) { dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n"); goto err_out_disable_msi; @@ -533,8 +538,10 @@ static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, if (err) goto err_out_send_channel_term; - if (adapter->dcb && qlcnic_dcb_attach(adapter)) - qlcnic_clear_dcb_ops(adapter); + dcb = adapter->dcb; + + if (dcb && qlcnic_dcb_attach(dcb)) + qlcnic_clear_dcb_ops(dcb); err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac); if (err) @@ -1577,7 +1584,7 @@ static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter) if (err) goto err_out_term_channel; - qlcnic_dcb_get_info(adapter); + qlcnic_dcb_get_info(adapter->dcb); return 0; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 019f4377307f025df8d7f93d3e473e7811f4101a..1a9f8a400e50a889491eba90f30f363c1d566dd9 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -156,7 +156,7 @@ static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter, const char *buf, size_t len) { struct qlcnic_hardware_context *ahw = adapter->ahw; - int err, max_sds_rings = adapter->max_sds_rings; + int err, drv_sds_rings = adapter->drv_sds_rings; u16 beacon; u8 h_beacon_state, b_state, b_rate; @@ -211,7 +211,7 @@ static int qlcnic_82xx_store_beacon(struct qlcnic_adapter *adapter, } if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) - qlcnic_diag_free_res(adapter->netdev, max_sds_rings); + qlcnic_diag_free_res(adapter->netdev, drv_sds_rings); out: if (!ahw->beacon_state) @@ -1272,7 +1272,6 @@ void qlcnic_remove_sysfs_entries(struct qlcnic_adapter *adapter) void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter) { struct device *dev = &adapter->pdev->dev; - u32 state; if (device_create_bin_file(dev, &bin_attr_port_stats)) dev_info(dev, "failed to create port stats sysfs entry"); @@ -1286,8 +1285,7 @@ void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter) if (device_create_bin_file(dev, &bin_attr_mem)) dev_info(dev, "failed to create mem sysfs entry\n"); - state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE); - if (state == QLCNIC_DEV_FAILED || state == QLCNIC_DEV_BADBAD) + if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) return; if (device_create_bin_file(dev, &bin_attr_pci_config)) @@ -1313,7 +1311,6 @@ void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter) void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter) { struct device *dev = &adapter->pdev->dev; - u32 state; device_remove_bin_file(dev, &bin_attr_port_stats); @@ -1323,8 +1320,7 @@ void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter) device_remove_bin_file(dev, &bin_attr_crb); device_remove_bin_file(dev, &bin_attr_mem); - state = QLC_SHARED_REG_RD32(adapter, QLCNIC_CRB_DEV_STATE); - if (state == QLCNIC_DEV_FAILED || state == QLCNIC_DEV_BADBAD) + if (test_bit(__QLCNIC_MAINTENANCE_MODE, &adapter->state)) return; device_remove_bin_file(dev, &bin_attr_pci_config); diff --git a/drivers/net/ethernet/qlogic/qlge/qlge.h b/drivers/net/ethernet/qlogic/qlge/qlge.h index 89943377846699e1e2719be8b2da16b07b144d09..0c9c4e89559524d78aa789dfb65e5e32211b8bc1 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge.h +++ b/drivers/net/ethernet/qlogic/qlge/qlge.h @@ -18,7 +18,7 @@ */ #define DRV_NAME "qlge" #define DRV_STRING "QLogic 10 Gigabit PCI-E Ethernet Driver " -#define DRV_VERSION "v1.00.00.32" +#define DRV_VERSION "1.00.00.33" #define WQ_ADDR_ALIGN 0x3 /* 4 byte alignment */ @@ -2206,14 +2206,14 @@ extern char qlge_driver_name[]; extern const char qlge_driver_version[]; extern const struct ethtool_ops qlge_ethtool_ops; -extern int ql_sem_spinlock(struct ql_adapter *qdev, u32 sem_mask); -extern void ql_sem_unlock(struct ql_adapter *qdev, u32 sem_mask); -extern int ql_read_xgmac_reg(struct ql_adapter *qdev, u32 reg, u32 *data); -extern int ql_get_mac_addr_reg(struct ql_adapter *qdev, u32 type, u16 index, - u32 *value); -extern int ql_get_routing_reg(struct ql_adapter *qdev, u32 index, u32 *value); -extern int ql_write_cfg(struct ql_adapter *qdev, void *ptr, int size, u32 bit, - u16 q_id); +int ql_sem_spinlock(struct ql_adapter *qdev, u32 sem_mask); +void ql_sem_unlock(struct ql_adapter *qdev, u32 sem_mask); +int ql_read_xgmac_reg(struct ql_adapter *qdev, u32 reg, u32 *data); +int ql_get_mac_addr_reg(struct ql_adapter *qdev, u32 type, u16 index, + u32 *value); +int ql_get_routing_reg(struct ql_adapter *qdev, u32 index, u32 *value); +int ql_write_cfg(struct ql_adapter *qdev, void *ptr, int size, u32 bit, + u16 q_id); void ql_queue_fw_error(struct ql_adapter *qdev); void ql_mpi_work(struct work_struct *work); void ql_mpi_reset_work(struct work_struct *work); @@ -2233,10 +2233,9 @@ int ql_unpause_mpi_risc(struct ql_adapter *qdev); int ql_pause_mpi_risc(struct ql_adapter *qdev); int ql_hard_reset_mpi_risc(struct ql_adapter *qdev); int ql_soft_reset_mpi_risc(struct ql_adapter *qdev); -int ql_dump_risc_ram_area(struct ql_adapter *qdev, void *buf, - u32 ram_addr, int word_count); -int ql_core_dump(struct ql_adapter *qdev, - struct ql_mpi_coredump *mpi_coredump); +int ql_dump_risc_ram_area(struct ql_adapter *qdev, void *buf, u32 ram_addr, + int word_count); +int ql_core_dump(struct ql_adapter *qdev, struct ql_mpi_coredump *mpi_coredump); int ql_mb_about_fw(struct ql_adapter *qdev); int ql_mb_wol_set_magic(struct ql_adapter *qdev, u32 enable_wol); int ql_mb_wol_mode(struct ql_adapter *qdev, u32 wol); @@ -2249,8 +2248,7 @@ int ql_mb_get_port_cfg(struct ql_adapter *qdev); int ql_mb_set_port_cfg(struct ql_adapter *qdev); int ql_wait_fifo_empty(struct ql_adapter *qdev); void ql_get_dump(struct ql_adapter *qdev, void *buff); -void ql_gen_reg_dump(struct ql_adapter *qdev, - struct ql_reg_dump *mpi_coredump); +void ql_gen_reg_dump(struct ql_adapter *qdev, struct ql_reg_dump *mpi_coredump); netdev_tx_t ql_lb_send(struct sk_buff *skb, struct net_device *ndev); void ql_check_lb_frame(struct ql_adapter *, struct sk_buff *); int ql_own_firmware(struct ql_adapter *qdev); @@ -2264,9 +2262,9 @@ int ql_clean_lb_rx_ring(struct rx_ring *rx_ring, int budget); /* #define QL_OB_DUMP */ #ifdef QL_REG_DUMP -extern void ql_dump_xgmac_control_regs(struct ql_adapter *qdev); -extern void ql_dump_routing_entries(struct ql_adapter *qdev); -extern void ql_dump_regs(struct ql_adapter *qdev); +void ql_dump_xgmac_control_regs(struct ql_adapter *qdev); +void ql_dump_routing_entries(struct ql_adapter *qdev); +void ql_dump_regs(struct ql_adapter *qdev); #define QL_DUMP_REGS(qdev) ql_dump_regs(qdev) #define QL_DUMP_ROUTE(qdev) ql_dump_routing_entries(qdev) #define QL_DUMP_XGMAC_CONTROL_REGS(qdev) ql_dump_xgmac_control_regs(qdev) @@ -2277,26 +2275,26 @@ extern void ql_dump_regs(struct ql_adapter *qdev); #endif #ifdef QL_STAT_DUMP -extern void ql_dump_stat(struct ql_adapter *qdev); +void ql_dump_stat(struct ql_adapter *qdev); #define QL_DUMP_STAT(qdev) ql_dump_stat(qdev) #else #define QL_DUMP_STAT(qdev) #endif #ifdef QL_DEV_DUMP -extern void ql_dump_qdev(struct ql_adapter *qdev); +void ql_dump_qdev(struct ql_adapter *qdev); #define QL_DUMP_QDEV(qdev) ql_dump_qdev(qdev) #else #define QL_DUMP_QDEV(qdev) #endif #ifdef QL_CB_DUMP -extern void ql_dump_wqicb(struct wqicb *wqicb); -extern void ql_dump_tx_ring(struct tx_ring *tx_ring); -extern void ql_dump_ricb(struct ricb *ricb); -extern void ql_dump_cqicb(struct cqicb *cqicb); -extern void ql_dump_rx_ring(struct rx_ring *rx_ring); -extern void ql_dump_hw_cb(struct ql_adapter *qdev, int size, u32 bit, u16 q_id); +void ql_dump_wqicb(struct wqicb *wqicb); +void ql_dump_tx_ring(struct tx_ring *tx_ring); +void ql_dump_ricb(struct ricb *ricb); +void ql_dump_cqicb(struct cqicb *cqicb); +void ql_dump_rx_ring(struct rx_ring *rx_ring); +void ql_dump_hw_cb(struct ql_adapter *qdev, int size, u32 bit, u16 q_id); #define QL_DUMP_RICB(ricb) ql_dump_ricb(ricb) #define QL_DUMP_WQICB(wqicb) ql_dump_wqicb(wqicb) #define QL_DUMP_TX_RING(tx_ring) ql_dump_tx_ring(tx_ring) @@ -2314,9 +2312,9 @@ extern void ql_dump_hw_cb(struct ql_adapter *qdev, int size, u32 bit, u16 q_id); #endif #ifdef QL_OB_DUMP -extern void ql_dump_tx_desc(struct tx_buf_desc *tbd); -extern void ql_dump_ob_mac_iocb(struct ob_mac_iocb_req *ob_mac_iocb); -extern void ql_dump_ob_mac_rsp(struct ob_mac_iocb_rsp *ob_mac_rsp); +void ql_dump_tx_desc(struct tx_buf_desc *tbd); +void ql_dump_ob_mac_iocb(struct ob_mac_iocb_req *ob_mac_iocb); +void ql_dump_ob_mac_rsp(struct ob_mac_iocb_rsp *ob_mac_rsp); #define QL_DUMP_OB_MAC_IOCB(ob_mac_iocb) ql_dump_ob_mac_iocb(ob_mac_iocb) #define QL_DUMP_OB_MAC_RSP(ob_mac_rsp) ql_dump_ob_mac_rsp(ob_mac_rsp) #else @@ -2325,14 +2323,14 @@ extern void ql_dump_ob_mac_rsp(struct ob_mac_iocb_rsp *ob_mac_rsp); #endif #ifdef QL_IB_DUMP -extern void ql_dump_ib_mac_rsp(struct ib_mac_iocb_rsp *ib_mac_rsp); +void ql_dump_ib_mac_rsp(struct ib_mac_iocb_rsp *ib_mac_rsp); #define QL_DUMP_IB_MAC_RSP(ib_mac_rsp) ql_dump_ib_mac_rsp(ib_mac_rsp) #else #define QL_DUMP_IB_MAC_RSP(ib_mac_rsp) #endif #ifdef QL_ALL_DUMP -extern void ql_dump_all(struct ql_adapter *qdev); +void ql_dump_all(struct ql_adapter *qdev); #define QL_DUMP_ALL(qdev) ql_dump_all(qdev) #else #define QL_DUMP_ALL(qdev) diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index 2553cf4503b9f83996bb72bd291fb756558644f9..a245dc18d769241bcf23d607458e33538c7ee99c 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -96,8 +96,10 @@ static DEFINE_PCI_DEVICE_TABLE(qlge_pci_tbl) = { MODULE_DEVICE_TABLE(pci, qlge_pci_tbl); -static int ql_wol(struct ql_adapter *qdev); -static void qlge_set_multicast_list(struct net_device *ndev); +static int ql_wol(struct ql_adapter *); +static void qlge_set_multicast_list(struct net_device *); +static int ql_adapter_down(struct ql_adapter *); +static int ql_adapter_up(struct ql_adapter *); /* This hardware semaphore causes exclusive access to * resources shared between the NIC driver, MPI firmware, @@ -1464,6 +1466,29 @@ static void ql_categorize_rx_err(struct ql_adapter *qdev, u8 rx_err, } } +/** + * ql_update_mac_hdr_len - helper routine to update the mac header length + * based on vlan tags if present + */ +static void ql_update_mac_hdr_len(struct ql_adapter *qdev, + struct ib_mac_iocb_rsp *ib_mac_rsp, + void *page, size_t *len) +{ + u16 *tags; + + if (qdev->ndev->features & NETIF_F_HW_VLAN_CTAG_RX) + return; + if (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) { + tags = (u16 *)page; + /* Look for stacked vlan tags in ethertype field */ + if (tags[6] == ETH_P_8021Q && + tags[8] == ETH_P_8021Q) + *len += 2 * VLAN_HLEN; + else + *len += VLAN_HLEN; + } +} + /* Process an inbound completion from an rx ring. */ static void ql_process_mac_rx_gro_page(struct ql_adapter *qdev, struct rx_ring *rx_ring, @@ -1523,6 +1548,7 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev, void *addr; struct bq_desc *lbq_desc = ql_get_curr_lchunk(qdev, rx_ring); struct napi_struct *napi = &rx_ring->napi; + size_t hlen = ETH_HLEN; skb = netdev_alloc_skb(ndev, length); if (!skb) { @@ -1540,25 +1566,28 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev, goto err_out; } + /* Update the MAC header length*/ + ql_update_mac_hdr_len(qdev, ib_mac_rsp, addr, &hlen); + /* The max framesize filter on this chip is set higher than * MTU since FCoE uses 2k frames. */ - if (skb->len > ndev->mtu + ETH_HLEN) { + if (skb->len > ndev->mtu + hlen) { netif_err(qdev, drv, qdev->ndev, "Segment too small, dropping.\n"); rx_ring->rx_dropped++; goto err_out; } - memcpy(skb_put(skb, ETH_HLEN), addr, ETH_HLEN); + memcpy(skb_put(skb, hlen), addr, hlen); netif_printk(qdev, rx_status, KERN_DEBUG, qdev->ndev, "%d bytes of headers and data in large. Chain page to new skb and pull tail.\n", length); skb_fill_page_desc(skb, 0, lbq_desc->p.pg_chunk.page, - lbq_desc->p.pg_chunk.offset+ETH_HLEN, - length-ETH_HLEN); - skb->len += length-ETH_HLEN; - skb->data_len += length-ETH_HLEN; - skb->truesize += length-ETH_HLEN; + lbq_desc->p.pg_chunk.offset + hlen, + length - hlen); + skb->len += length - hlen; + skb->data_len += length - hlen; + skb->truesize += length - hlen; rx_ring->rx_packets++; rx_ring->rx_bytes += skb->len; @@ -1576,7 +1605,7 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev, (ib_mac_rsp->flags3 & IB_MAC_IOCB_RSP_V4)) { /* Unfragmented ipv4 UDP frame. */ struct iphdr *iph = - (struct iphdr *) ((u8 *)addr + ETH_HLEN); + (struct iphdr *)((u8 *)addr + hlen); if (!(iph->frag_off & htons(IP_MF|IP_OFFSET))) { skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -1726,7 +1755,8 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, struct bq_desc *sbq_desc; struct sk_buff *skb = NULL; u32 length = le32_to_cpu(ib_mac_rsp->data_len); - u32 hdr_len = le32_to_cpu(ib_mac_rsp->hdr_len); + u32 hdr_len = le32_to_cpu(ib_mac_rsp->hdr_len); + size_t hlen = ETH_HLEN; /* * Handle the header buffer if present. @@ -1853,9 +1883,10 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, skb->data_len += length; skb->truesize += length; length -= length; - __pskb_pull_tail(skb, - (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ? - VLAN_ETH_HLEN : ETH_HLEN); + ql_update_mac_hdr_len(qdev, ib_mac_rsp, + lbq_desc->p.pg_chunk.va, + &hlen); + __pskb_pull_tail(skb, hlen); } } else { /* @@ -1910,8 +1941,9 @@ static struct sk_buff *ql_build_rx_skb(struct ql_adapter *qdev, length -= size; i++; } - __pskb_pull_tail(skb, (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ? - VLAN_ETH_HLEN : ETH_HLEN); + ql_update_mac_hdr_len(qdev, ib_mac_rsp, lbq_desc->p.pg_chunk.va, + &hlen); + __pskb_pull_tail(skb, hlen); } return skb; } @@ -2003,7 +2035,7 @@ static void ql_process_mac_split_rx_intr(struct ql_adapter *qdev, rx_ring->rx_packets++; rx_ring->rx_bytes += skb->len; skb_record_rx_queue(skb, rx_ring->cq_id); - if ((ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) && (vlan_id != 0)) + if (vlan_id != 0xffff) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id); if (skb->ip_summed == CHECKSUM_UNNECESSARY) napi_gro_receive(&rx_ring->napi, skb); @@ -2017,7 +2049,8 @@ static unsigned long ql_process_mac_rx_intr(struct ql_adapter *qdev, struct ib_mac_iocb_rsp *ib_mac_rsp) { u32 length = le32_to_cpu(ib_mac_rsp->data_len); - u16 vlan_id = (ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) ? + u16 vlan_id = ((ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) && + (qdev->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)) ? ((le16_to_cpu(ib_mac_rsp->vlan_id) & IB_MAC_IOCB_RSP_VLAN_MASK)) : 0xffff; @@ -2310,9 +2343,39 @@ static void qlge_vlan_mode(struct net_device *ndev, netdev_features_t features) } } +/** + * qlge_update_hw_vlan_features - helper routine to reinitialize the adapter + * based on the features to enable/disable hardware vlan accel + */ +static int qlge_update_hw_vlan_features(struct net_device *ndev, + netdev_features_t features) +{ + struct ql_adapter *qdev = netdev_priv(ndev); + int status = 0; + + status = ql_adapter_down(qdev); + if (status) { + netif_err(qdev, link, qdev->ndev, + "Failed to bring down the adapter\n"); + return status; + } + + /* update the features with resent change */ + ndev->features = features; + + status = ql_adapter_up(qdev); + if (status) { + netif_err(qdev, link, qdev->ndev, + "Failed to bring up the adapter\n"); + return status; + } + return status; +} + static netdev_features_t qlge_fix_features(struct net_device *ndev, netdev_features_t features) { + int err; /* * Since there is no support for separate rx/tx vlan accel * enable/disable make sure tx flag is always in same state as rx. @@ -2322,6 +2385,11 @@ static netdev_features_t qlge_fix_features(struct net_device *ndev, else features &= ~NETIF_F_HW_VLAN_CTAG_TX; + /* Update the behavior of vlan accel in the adapter */ + err = qlge_update_hw_vlan_features(ndev, features); + if (err) + return err; + return features; } @@ -3704,8 +3772,12 @@ static int ql_adapter_initialize(struct ql_adapter *qdev) ql_write32(qdev, SYS, mask | value); /* Set the default queue, and VLAN behavior. */ - value = NIC_RCV_CFG_DFQ | NIC_RCV_CFG_RV; - mask = NIC_RCV_CFG_DFQ_MASK | (NIC_RCV_CFG_RV << 16); + value = NIC_RCV_CFG_DFQ; + mask = NIC_RCV_CFG_DFQ_MASK; + if (qdev->ndev->features & NETIF_F_HW_VLAN_CTAG_RX) { + value |= NIC_RCV_CFG_RV; + mask |= (NIC_RCV_CFG_RV << 16); + } ql_write32(qdev, NIC_RCV_CFG, (mask | value)); /* Set the MPI interrupt to enabled. */ @@ -4505,7 +4577,6 @@ static void ql_release_all(struct pci_dev *pdev) iounmap(qdev->doorbell_area); vfree(qdev->mpi_coredump); pci_release_regions(pdev); - pci_set_drvdata(pdev, NULL); } static int ql_init_device(struct pci_dev *pdev, struct net_device *ndev, @@ -4692,11 +4763,15 @@ static int qlge_probe(struct pci_dev *pdev, qdev = netdev_priv(ndev); SET_NETDEV_DEV(ndev, &pdev->dev); - ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | - NETIF_F_TSO | NETIF_F_TSO_ECN | - NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_RXCSUM; - ndev->features = ndev->hw_features | - NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; + ndev->hw_features = NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_TSO | + NETIF_F_TSO_ECN | + NETIF_F_HW_VLAN_CTAG_TX | + NETIF_F_HW_VLAN_CTAG_RX | + NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_RXCSUM; + ndev->features = ndev->hw_features; ndev->vlan_features = ndev->hw_features; if (test_bit(QL_DMA64, &qdev->flags)) diff --git a/drivers/net/ethernet/rdc/r6040.c b/drivers/net/ethernet/rdc/r6040.c index e9dc84943cfcb5fa39a8031e5a28051f5963da90..1e49ec5b22324815005bcefcfcbd9eef6e46facc 100644 --- a/drivers/net/ethernet/rdc/r6040.c +++ b/drivers/net/ethernet/rdc/r6040.c @@ -1231,7 +1231,6 @@ err_out_mdio: mdiobus_free(lp->mii_bus); err_out_unmap: netif_napi_del(&lp->napi); - pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, ioaddr); err_out_free_res: pci_release_regions(pdev); @@ -1257,7 +1256,6 @@ static void r6040_remove_one(struct pci_dev *pdev) pci_release_regions(pdev); free_netdev(dev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index d2e591955bdde3fd03b40dac1c9c22e3d013c23d..f2a2128165dd98b4c41598fb481c4e4f40417614 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -2052,7 +2052,6 @@ static void cp_remove_one (struct pci_dev *pdev) pci_release_regions(pdev); pci_clear_mwi(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); free_netdev(dev); } diff --git a/drivers/net/ethernet/realtek/8139too.c b/drivers/net/ethernet/realtek/8139too.c index 3ccedeb8aba03703bcdff318dff2ff888eec4bfa..50a92104dd0a65399f118b6c6505bc04e04329af 100644 --- a/drivers/net/ethernet/realtek/8139too.c +++ b/drivers/net/ethernet/realtek/8139too.c @@ -727,7 +727,6 @@ static void __rtl8139_cleanup_dev (struct net_device *dev) pci_release_regions (pdev); free_netdev(dev); - pci_set_drvdata (pdev, NULL); } diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 3397cee89777ba4be6c888fe9711e928ef209e06..799387570766b6642ad4fdbc13f917acd358887a 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -6811,7 +6811,6 @@ static void rtl_remove_one(struct pci_dev *pdev) rtl_disable_msi(pdev, tp); rtl8169_release_board(pdev, dev, tp->mmio_addr); - pci_set_drvdata(pdev, NULL); } static const struct net_device_ops rtl_netdev_ops = { diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index b57c278d3b46b2eeb1d9b56b3cc9ca312ba67f61..d256ce19d4de1cf6e93b51700b3ed8f5d813a3e0 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -483,7 +483,7 @@ static struct sh_eth_cpu_data sh7757_data = { .register_type = SH_ETH_REG_FAST_SH4, .eesipr_value = DMAC_M_RFRMER | DMAC_M_ECI | 0x003fffff, - .rmcr_value = 0x00000001, + .rmcr_value = RMCR_RNC, .tx_check = EESR_FTC | EESR_CND | EESR_DLC | EESR_CD | EESR_RTO, .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | @@ -561,7 +561,7 @@ static struct sh_eth_cpu_data sh7757_data_giga = { EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, .fdr_value = 0x0000072f, - .rmcr_value = 0x00000001, + .rmcr_value = RMCR_RNC, .irq_flags = IRQF_SHARED, .apr = 1, @@ -689,7 +689,7 @@ static struct sh_eth_cpu_data r8a7740_data = { EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, .fdr_value = 0x0000070f, - .rmcr_value = 0x00000001, + .rmcr_value = RMCR_RNC, .apr = 1, .mpr = 1, @@ -872,7 +872,7 @@ static void update_mac_address(struct net_device *ndev) static void read_mac_address(struct net_device *ndev, unsigned char *mac) { if (mac[0] || mac[1] || mac[2] || mac[3] || mac[4] || mac[5]) { - memcpy(ndev->dev_addr, mac, 6); + memcpy(ndev->dev_addr, mac, ETH_ALEN); } else { ndev->dev_addr[0] = (sh_eth_read(ndev, MAHR) >> 24); ndev->dev_addr[1] = (sh_eth_read(ndev, MAHR) >> 16) & 0xFF; @@ -2663,6 +2663,12 @@ static int sh_eth_drv_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); pm_runtime_resume(&pdev->dev); + if (!pd) { + dev_err(&pdev->dev, "no platform data\n"); + ret = -EINVAL; + goto out_release; + } + /* get PHY ID */ mdp->phy_id = pd->phy; mdp->phy_interface = pd->phy_interface; diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index a0db02c63b1157930ca0fca070dd733aa39b7c54..f32c1692d31017186e0e33aaf408eddf7d16a3fa 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -321,6 +321,9 @@ enum TD_STS_BIT { #define TD_TFP (TD_TFP1|TD_TFP0) /* RMCR */ +enum RMCR_BIT { + RMCR_RNC = 0x00000001, +}; #define DEFAULT_RMCR_VALUE 0x00000000 /* ECMR */ diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 21f9ad6392e9e2d547817c91e5260558eefcbcbb..676c3c057bfba69e69b6116e212e2d03fdff330c 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -285,6 +285,181 @@ static int efx_ef10_free_vis(struct efx_nic *efx) return rc; } +#ifdef EFX_USE_PIO + +static void efx_ef10_free_piobufs(struct efx_nic *efx) +{ + struct efx_ef10_nic_data *nic_data = efx->nic_data; + MCDI_DECLARE_BUF(inbuf, MC_CMD_FREE_PIOBUF_IN_LEN); + unsigned int i; + int rc; + + BUILD_BUG_ON(MC_CMD_FREE_PIOBUF_OUT_LEN != 0); + + for (i = 0; i < nic_data->n_piobufs; i++) { + MCDI_SET_DWORD(inbuf, FREE_PIOBUF_IN_PIOBUF_HANDLE, + nic_data->piobuf_handle[i]); + rc = efx_mcdi_rpc(efx, MC_CMD_FREE_PIOBUF, inbuf, sizeof(inbuf), + NULL, 0, NULL); + WARN_ON(rc); + } + + nic_data->n_piobufs = 0; +} + +static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n) +{ + struct efx_ef10_nic_data *nic_data = efx->nic_data; + MCDI_DECLARE_BUF(outbuf, MC_CMD_ALLOC_PIOBUF_OUT_LEN); + unsigned int i; + size_t outlen; + int rc = 0; + + BUILD_BUG_ON(MC_CMD_ALLOC_PIOBUF_IN_LEN != 0); + + for (i = 0; i < n; i++) { + rc = efx_mcdi_rpc(efx, MC_CMD_ALLOC_PIOBUF, NULL, 0, + outbuf, sizeof(outbuf), &outlen); + if (rc) + break; + if (outlen < MC_CMD_ALLOC_PIOBUF_OUT_LEN) { + rc = -EIO; + break; + } + nic_data->piobuf_handle[i] = + MCDI_DWORD(outbuf, ALLOC_PIOBUF_OUT_PIOBUF_HANDLE); + netif_dbg(efx, probe, efx->net_dev, + "allocated PIO buffer %u handle %x\n", i, + nic_data->piobuf_handle[i]); + } + + nic_data->n_piobufs = i; + if (rc) + efx_ef10_free_piobufs(efx); + return rc; +} + +static int efx_ef10_link_piobufs(struct efx_nic *efx) +{ + struct efx_ef10_nic_data *nic_data = efx->nic_data; + MCDI_DECLARE_BUF(inbuf, + max(MC_CMD_LINK_PIOBUF_IN_LEN, + MC_CMD_UNLINK_PIOBUF_IN_LEN)); + struct efx_channel *channel; + struct efx_tx_queue *tx_queue; + unsigned int offset, index; + int rc; + + BUILD_BUG_ON(MC_CMD_LINK_PIOBUF_OUT_LEN != 0); + BUILD_BUG_ON(MC_CMD_UNLINK_PIOBUF_OUT_LEN != 0); + + /* Link a buffer to each VI in the write-combining mapping */ + for (index = 0; index < nic_data->n_piobufs; ++index) { + MCDI_SET_DWORD(inbuf, LINK_PIOBUF_IN_PIOBUF_HANDLE, + nic_data->piobuf_handle[index]); + MCDI_SET_DWORD(inbuf, LINK_PIOBUF_IN_TXQ_INSTANCE, + nic_data->pio_write_vi_base + index); + rc = efx_mcdi_rpc(efx, MC_CMD_LINK_PIOBUF, + inbuf, MC_CMD_LINK_PIOBUF_IN_LEN, + NULL, 0, NULL); + if (rc) { + netif_err(efx, drv, efx->net_dev, + "failed to link VI %u to PIO buffer %u (%d)\n", + nic_data->pio_write_vi_base + index, index, + rc); + goto fail; + } + netif_dbg(efx, probe, efx->net_dev, + "linked VI %u to PIO buffer %u\n", + nic_data->pio_write_vi_base + index, index); + } + + /* Link a buffer to each TX queue */ + efx_for_each_channel(channel, efx) { + efx_for_each_channel_tx_queue(tx_queue, channel) { + /* We assign the PIO buffers to queues in + * reverse order to allow for the following + * special case. + */ + offset = ((efx->tx_channel_offset + efx->n_tx_channels - + tx_queue->channel->channel - 1) * + efx_piobuf_size); + index = offset / ER_DZ_TX_PIOBUF_SIZE; + offset = offset % ER_DZ_TX_PIOBUF_SIZE; + + /* When the host page size is 4K, the first + * host page in the WC mapping may be within + * the same VI page as the last TX queue. We + * can only link one buffer to each VI. + */ + if (tx_queue->queue == nic_data->pio_write_vi_base) { + BUG_ON(index != 0); + rc = 0; + } else { + MCDI_SET_DWORD(inbuf, + LINK_PIOBUF_IN_PIOBUF_HANDLE, + nic_data->piobuf_handle[index]); + MCDI_SET_DWORD(inbuf, + LINK_PIOBUF_IN_TXQ_INSTANCE, + tx_queue->queue); + rc = efx_mcdi_rpc(efx, MC_CMD_LINK_PIOBUF, + inbuf, MC_CMD_LINK_PIOBUF_IN_LEN, + NULL, 0, NULL); + } + + if (rc) { + /* This is non-fatal; the TX path just + * won't use PIO for this queue + */ + netif_err(efx, drv, efx->net_dev, + "failed to link VI %u to PIO buffer %u (%d)\n", + tx_queue->queue, index, rc); + tx_queue->piobuf = NULL; + } else { + tx_queue->piobuf = + nic_data->pio_write_base + + index * EFX_VI_PAGE_SIZE + offset; + tx_queue->piobuf_offset = offset; + netif_dbg(efx, probe, efx->net_dev, + "linked VI %u to PIO buffer %u offset %x addr %p\n", + tx_queue->queue, index, + tx_queue->piobuf_offset, + tx_queue->piobuf); + } + } + } + + return 0; + +fail: + while (index--) { + MCDI_SET_DWORD(inbuf, UNLINK_PIOBUF_IN_TXQ_INSTANCE, + nic_data->pio_write_vi_base + index); + efx_mcdi_rpc(efx, MC_CMD_UNLINK_PIOBUF, + inbuf, MC_CMD_UNLINK_PIOBUF_IN_LEN, + NULL, 0, NULL); + } + return rc; +} + +#else /* !EFX_USE_PIO */ + +static int efx_ef10_alloc_piobufs(struct efx_nic *efx, unsigned int n) +{ + return n == 0 ? 0 : -ENOBUFS; +} + +static int efx_ef10_link_piobufs(struct efx_nic *efx) +{ + return 0; +} + +static void efx_ef10_free_piobufs(struct efx_nic *efx) +{ +} + +#endif /* EFX_USE_PIO */ + static void efx_ef10_remove(struct efx_nic *efx) { struct efx_ef10_nic_data *nic_data = efx->nic_data; @@ -295,9 +470,15 @@ static void efx_ef10_remove(struct efx_nic *efx) /* This needs to be after efx_ptp_remove_channel() with no filters */ efx_ef10_rx_free_indir_table(efx); + if (nic_data->wc_membase) + iounmap(nic_data->wc_membase); + rc = efx_ef10_free_vis(efx); WARN_ON(rc != 0); + if (!nic_data->must_restore_piobufs) + efx_ef10_free_piobufs(efx); + efx_mcdi_fini(efx); efx_nic_free_buffer(efx, &nic_data->mcdi_buf); kfree(nic_data); @@ -330,12 +511,126 @@ static int efx_ef10_alloc_vis(struct efx_nic *efx, return 0; } +/* Note that the failure path of this function does not free + * resources, as this will be done by efx_ef10_remove(). + */ static int efx_ef10_dimension_resources(struct efx_nic *efx) { - unsigned int n_vis = - max(efx->n_channels, efx->n_tx_channels * EFX_TXQ_TYPES); + struct efx_ef10_nic_data *nic_data = efx->nic_data; + unsigned int uc_mem_map_size, wc_mem_map_size; + unsigned int min_vis, pio_write_vi_base, max_vis; + void __iomem *membase; + int rc; + + min_vis = max(efx->n_channels, efx->n_tx_channels * EFX_TXQ_TYPES); + +#ifdef EFX_USE_PIO + /* Try to allocate PIO buffers if wanted and if the full + * number of PIO buffers would be sufficient to allocate one + * copy-buffer per TX channel. Failure is non-fatal, as there + * are only a small number of PIO buffers shared between all + * functions of the controller. + */ + if (efx_piobuf_size != 0 && + ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size * EF10_TX_PIOBUF_COUNT >= + efx->n_tx_channels) { + unsigned int n_piobufs = + DIV_ROUND_UP(efx->n_tx_channels, + ER_DZ_TX_PIOBUF_SIZE / efx_piobuf_size); + + rc = efx_ef10_alloc_piobufs(efx, n_piobufs); + if (rc) + netif_err(efx, probe, efx->net_dev, + "failed to allocate PIO buffers (%d)\n", rc); + else + netif_dbg(efx, probe, efx->net_dev, + "allocated %u PIO buffers\n", n_piobufs); + } +#else + nic_data->n_piobufs = 0; +#endif - return efx_ef10_alloc_vis(efx, n_vis, n_vis); + /* PIO buffers should be mapped with write-combining enabled, + * and we want to make single UC and WC mappings rather than + * several of each (in fact that's the only option if host + * page size is >4K). So we may allocate some extra VIs just + * for writing PIO buffers through. + */ + uc_mem_map_size = PAGE_ALIGN((min_vis - 1) * EFX_VI_PAGE_SIZE + + ER_DZ_TX_PIOBUF); + if (nic_data->n_piobufs) { + pio_write_vi_base = uc_mem_map_size / EFX_VI_PAGE_SIZE; + wc_mem_map_size = (PAGE_ALIGN((pio_write_vi_base + + nic_data->n_piobufs) * + EFX_VI_PAGE_SIZE) - + uc_mem_map_size); + max_vis = pio_write_vi_base + nic_data->n_piobufs; + } else { + pio_write_vi_base = 0; + wc_mem_map_size = 0; + max_vis = min_vis; + } + + /* In case the last attached driver failed to free VIs, do it now */ + rc = efx_ef10_free_vis(efx); + if (rc != 0) + return rc; + + rc = efx_ef10_alloc_vis(efx, min_vis, max_vis); + if (rc != 0) + return rc; + + /* If we didn't get enough VIs to map all the PIO buffers, free the + * PIO buffers + */ + if (nic_data->n_piobufs && + nic_data->n_allocated_vis < + pio_write_vi_base + nic_data->n_piobufs) { + netif_dbg(efx, probe, efx->net_dev, + "%u VIs are not sufficient to map %u PIO buffers\n", + nic_data->n_allocated_vis, nic_data->n_piobufs); + efx_ef10_free_piobufs(efx); + } + + /* Shrink the original UC mapping of the memory BAR */ + membase = ioremap_nocache(efx->membase_phys, uc_mem_map_size); + if (!membase) { + netif_err(efx, probe, efx->net_dev, + "could not shrink memory BAR to %x\n", + uc_mem_map_size); + return -ENOMEM; + } + iounmap(efx->membase); + efx->membase = membase; + + /* Set up the WC mapping if needed */ + if (wc_mem_map_size) { + nic_data->wc_membase = ioremap_wc(efx->membase_phys + + uc_mem_map_size, + wc_mem_map_size); + if (!nic_data->wc_membase) { + netif_err(efx, probe, efx->net_dev, + "could not allocate WC mapping of size %x\n", + wc_mem_map_size); + return -ENOMEM; + } + nic_data->pio_write_vi_base = pio_write_vi_base; + nic_data->pio_write_base = + nic_data->wc_membase + + (pio_write_vi_base * EFX_VI_PAGE_SIZE + ER_DZ_TX_PIOBUF - + uc_mem_map_size); + + rc = efx_ef10_link_piobufs(efx); + if (rc) + efx_ef10_free_piobufs(efx); + } + + netif_dbg(efx, probe, efx->net_dev, + "memory BAR at %pa (virtual %p+%x UC, %p+%x WC)\n", + &efx->membase_phys, efx->membase, uc_mem_map_size, + nic_data->wc_membase, wc_mem_map_size); + + return 0; } static int efx_ef10_init_nic(struct efx_nic *efx) @@ -359,6 +654,21 @@ static int efx_ef10_init_nic(struct efx_nic *efx) nic_data->must_realloc_vis = false; } + if (nic_data->must_restore_piobufs && nic_data->n_piobufs) { + rc = efx_ef10_alloc_piobufs(efx, nic_data->n_piobufs); + if (rc == 0) { + rc = efx_ef10_link_piobufs(efx); + if (rc) + efx_ef10_free_piobufs(efx); + } + + /* Log an error on failure, but this is non-fatal */ + if (rc) + netif_err(efx, drv, efx->net_dev, + "failed to restore PIO buffers (%d)\n", rc); + nic_data->must_restore_piobufs = false; + } + efx_ef10_rx_push_indir_table(efx); return 0; } @@ -759,6 +1069,7 @@ static int efx_ef10_mcdi_poll_reboot(struct efx_nic *efx) /* All our allocations have been reset */ nic_data->must_realloc_vis = true; nic_data->must_restore_filters = true; + nic_data->must_restore_piobufs = true; nic_data->rx_rss_context = EFX_EF10_RSS_CONTEXT_INVALID; /* The datapath firmware might have been changed */ @@ -2180,7 +2491,7 @@ out_unlock: return rc; } -void efx_ef10_filter_update_rx_scatter(struct efx_nic *efx) +static void efx_ef10_filter_update_rx_scatter(struct efx_nic *efx) { /* no need to do anything here on EF10 */ } diff --git a/drivers/net/ethernet/sfc/ef10_regs.h b/drivers/net/ethernet/sfc/ef10_regs.h index b3f4e3755fd97cd7c38130597d54e22c20b591d6..207ac9a1e3de989d0f5e4d588c27b6035eec31ec 100644 --- a/drivers/net/ethernet/sfc/ef10_regs.h +++ b/drivers/net/ethernet/sfc/ef10_regs.h @@ -315,6 +315,7 @@ #define ESF_DZ_TX_PIO_TYPE_WIDTH 1 #define ESF_DZ_TX_PIO_OPT_LBN 60 #define ESF_DZ_TX_PIO_OPT_WIDTH 3 +#define ESE_DZ_TX_OPTION_DESC_PIO 1 #define ESF_DZ_TX_PIO_CONT_LBN 59 #define ESF_DZ_TX_PIO_CONT_WIDTH 1 #define ESF_DZ_TX_PIO_BYTE_CNT_LBN 32 diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index 34d00f5771fe4c521177f17061acb9abac846c50..b8235ee5d7d739ae879aaba810620740951af8ff 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -18,37 +18,36 @@ #define EFX_MEM_BAR 2 /* TX */ -extern int efx_probe_tx_queue(struct efx_tx_queue *tx_queue); -extern void efx_remove_tx_queue(struct efx_tx_queue *tx_queue); -extern void efx_init_tx_queue(struct efx_tx_queue *tx_queue); -extern void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue); -extern void efx_fini_tx_queue(struct efx_tx_queue *tx_queue); -extern netdev_tx_t -efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); -extern netdev_tx_t -efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); -extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); -extern int efx_setup_tc(struct net_device *net_dev, u8 num_tc); -extern unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); +int efx_probe_tx_queue(struct efx_tx_queue *tx_queue); +void efx_remove_tx_queue(struct efx_tx_queue *tx_queue); +void efx_init_tx_queue(struct efx_tx_queue *tx_queue); +void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue); +void efx_fini_tx_queue(struct efx_tx_queue *tx_queue); +netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, + struct net_device *net_dev); +netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); +void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); +int efx_setup_tc(struct net_device *net_dev, u8 num_tc); +unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); +extern unsigned int efx_piobuf_size; /* RX */ -extern void efx_rx_config_page_split(struct efx_nic *efx); -extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue); -extern void efx_remove_rx_queue(struct efx_rx_queue *rx_queue); -extern void efx_init_rx_queue(struct efx_rx_queue *rx_queue); -extern void efx_fini_rx_queue(struct efx_rx_queue *rx_queue); -extern void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue); -extern void efx_rx_slow_fill(unsigned long context); -extern void __efx_rx_packet(struct efx_channel *channel); -extern void efx_rx_packet(struct efx_rx_queue *rx_queue, - unsigned int index, unsigned int n_frags, - unsigned int len, u16 flags); +void efx_rx_config_page_split(struct efx_nic *efx); +int efx_probe_rx_queue(struct efx_rx_queue *rx_queue); +void efx_remove_rx_queue(struct efx_rx_queue *rx_queue); +void efx_init_rx_queue(struct efx_rx_queue *rx_queue); +void efx_fini_rx_queue(struct efx_rx_queue *rx_queue); +void efx_fast_push_rx_descriptors(struct efx_rx_queue *rx_queue); +void efx_rx_slow_fill(unsigned long context); +void __efx_rx_packet(struct efx_channel *channel); +void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, + unsigned int n_frags, unsigned int len, u16 flags); static inline void efx_rx_flush_packet(struct efx_channel *channel) { if (channel->rx_pkt_n_frags) __efx_rx_packet(channel); } -extern void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue); +void efx_schedule_slow_fill(struct efx_rx_queue *rx_queue); #define EFX_MAX_DMAQ_SIZE 4096UL #define EFX_DEFAULT_DMAQ_SIZE 1024UL @@ -162,9 +161,9 @@ static inline s32 efx_filter_get_rx_ids(struct efx_nic *efx, return efx->type->filter_get_rx_ids(efx, priority, buf, size); } #ifdef CONFIG_RFS_ACCEL -extern int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, - u16 rxq_index, u32 flow_id); -extern bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned quota); +int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, + u16 rxq_index, u32 flow_id); +bool __efx_filter_rfs_expire(struct efx_nic *efx, unsigned quota); static inline void efx_filter_rfs_expire(struct efx_channel *channel) { if (channel->rfs_filters_added >= 60 && @@ -176,50 +175,48 @@ static inline void efx_filter_rfs_expire(struct efx_channel *channel) static inline void efx_filter_rfs_expire(struct efx_channel *channel) {} #define efx_filter_rfs_enabled() 0 #endif -extern bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec); +bool efx_filter_is_mc_recipient(const struct efx_filter_spec *spec); /* Channels */ -extern int efx_channel_dummy_op_int(struct efx_channel *channel); -extern void efx_channel_dummy_op_void(struct efx_channel *channel); -extern int -efx_realloc_channels(struct efx_nic *efx, u32 rxq_entries, u32 txq_entries); +int efx_channel_dummy_op_int(struct efx_channel *channel); +void efx_channel_dummy_op_void(struct efx_channel *channel); +int efx_realloc_channels(struct efx_nic *efx, u32 rxq_entries, u32 txq_entries); /* Ports */ -extern int efx_reconfigure_port(struct efx_nic *efx); -extern int __efx_reconfigure_port(struct efx_nic *efx); +int efx_reconfigure_port(struct efx_nic *efx); +int __efx_reconfigure_port(struct efx_nic *efx); /* Ethtool support */ extern const struct ethtool_ops efx_ethtool_ops; /* Reset handling */ -extern int efx_reset(struct efx_nic *efx, enum reset_type method); -extern void efx_reset_down(struct efx_nic *efx, enum reset_type method); -extern int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok); -extern int efx_try_recovery(struct efx_nic *efx); +int efx_reset(struct efx_nic *efx, enum reset_type method); +void efx_reset_down(struct efx_nic *efx, enum reset_type method); +int efx_reset_up(struct efx_nic *efx, enum reset_type method, bool ok); +int efx_try_recovery(struct efx_nic *efx); /* Global */ -extern void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); -extern int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs, - unsigned int rx_usecs, bool rx_adaptive, - bool rx_may_override_tx); -extern void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs, - unsigned int *rx_usecs, bool *rx_adaptive); +void efx_schedule_reset(struct efx_nic *efx, enum reset_type type); +int efx_init_irq_moderation(struct efx_nic *efx, unsigned int tx_usecs, + unsigned int rx_usecs, bool rx_adaptive, + bool rx_may_override_tx); +void efx_get_irq_moderation(struct efx_nic *efx, unsigned int *tx_usecs, + unsigned int *rx_usecs, bool *rx_adaptive); /* Dummy PHY ops for PHY drivers */ -extern int efx_port_dummy_op_int(struct efx_nic *efx); -extern void efx_port_dummy_op_void(struct efx_nic *efx); - +int efx_port_dummy_op_int(struct efx_nic *efx); +void efx_port_dummy_op_void(struct efx_nic *efx); /* MTD */ #ifdef CONFIG_SFC_MTD -extern int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts, - size_t n_parts, size_t sizeof_part); +int efx_mtd_add(struct efx_nic *efx, struct efx_mtd_partition *parts, + size_t n_parts, size_t sizeof_part); static inline int efx_mtd_probe(struct efx_nic *efx) { return efx->type->mtd_probe(efx); } -extern void efx_mtd_rename(struct efx_nic *efx); -extern void efx_mtd_remove(struct efx_nic *efx); +void efx_mtd_rename(struct efx_nic *efx); +void efx_mtd_remove(struct efx_nic *efx); #else static inline int efx_mtd_probe(struct efx_nic *efx) { return 0; } static inline void efx_mtd_rename(struct efx_nic *efx) {} @@ -241,9 +238,9 @@ static inline void efx_schedule_channel_irq(struct efx_channel *channel) efx_schedule_channel(channel); } -extern void efx_link_status_changed(struct efx_nic *efx); -extern void efx_link_set_advertising(struct efx_nic *efx, u32); -extern void efx_link_set_wanted_fc(struct efx_nic *efx, u8); +void efx_link_status_changed(struct efx_nic *efx); +void efx_link_set_advertising(struct efx_nic *efx, u32); +void efx_link_set_wanted_fc(struct efx_nic *efx, u8); static inline void efx_device_detach_sync(struct efx_nic *efx) { diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index 5b471cf5c323a2aabae6ae3c55404194e6f90c42..1f529fa2edb10008a5c03944bea2fb0d995e1e61 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -70,6 +70,7 @@ static const struct efx_sw_stat_desc efx_sw_stat_desc[] = { EFX_ETHTOOL_UINT_TXQ_STAT(tso_long_headers), EFX_ETHTOOL_UINT_TXQ_STAT(tso_packets), EFX_ETHTOOL_UINT_TXQ_STAT(pushes), + EFX_ETHTOOL_UINT_TXQ_STAT(pio_packets), EFX_ETHTOOL_ATOMIC_NIC_ERROR_STAT(rx_reset), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_tobe_disc), EFX_ETHTOOL_UINT_CHANNEL_STAT(rx_ip_hdr_chksum_err), @@ -1035,8 +1036,8 @@ static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev, return 0; } -int efx_ethtool_get_ts_info(struct net_device *net_dev, - struct ethtool_ts_info *ts_info) +static int efx_ethtool_get_ts_info(struct net_device *net_dev, + struct ethtool_ts_info *ts_info) { struct efx_nic *efx = netdev_priv(net_dev); diff --git a/drivers/net/ethernet/sfc/io.h b/drivers/net/ethernet/sfc/io.h index 96ce507d8602eb4a8f40a7cfa034f5cf3dede3c3..4d3f119b67b38ec35719ebac40d59f49ea9b844f 100644 --- a/drivers/net/ethernet/sfc/io.h +++ b/drivers/net/ethernet/sfc/io.h @@ -66,6 +66,11 @@ #define EFX_USE_QWORD_IO 1 #endif +/* PIO is a win only if write-combining is possible */ +#ifdef ARCH_HAS_IOREMAP_WC +#define EFX_USE_PIO 1 +#endif + #ifdef EFX_USE_QWORD_IO static inline void _efx_writeq(struct efx_nic *efx, __le64 value, unsigned int reg) diff --git a/drivers/net/ethernet/sfc/mcdi.h b/drivers/net/ethernet/sfc/mcdi.h index c34d0d4e10ee2e0d453b3f192acaed86c5d75301..656a3277c2b210e69ffd028d059ce10b809e8db8 100644 --- a/drivers/net/ethernet/sfc/mcdi.h +++ b/drivers/net/ethernet/sfc/mcdi.h @@ -108,38 +108,35 @@ static inline struct efx_mcdi_mon *efx_mcdi_mon(struct efx_nic *efx) } #endif -extern int efx_mcdi_init(struct efx_nic *efx); -extern void efx_mcdi_fini(struct efx_nic *efx); +int efx_mcdi_init(struct efx_nic *efx); +void efx_mcdi_fini(struct efx_nic *efx); -extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, - const efx_dword_t *inbuf, size_t inlen, +int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const efx_dword_t *inbuf, + size_t inlen, efx_dword_t *outbuf, size_t outlen, + size_t *outlen_actual); + +int efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, + const efx_dword_t *inbuf, size_t inlen); +int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, efx_dword_t *outbuf, size_t outlen, size_t *outlen_actual); -extern int efx_mcdi_rpc_start(struct efx_nic *efx, unsigned cmd, - const efx_dword_t *inbuf, size_t inlen); -extern int efx_mcdi_rpc_finish(struct efx_nic *efx, unsigned cmd, size_t inlen, - efx_dword_t *outbuf, size_t outlen, - size_t *outlen_actual); - typedef void efx_mcdi_async_completer(struct efx_nic *efx, unsigned long cookie, int rc, efx_dword_t *outbuf, size_t outlen_actual); -extern int efx_mcdi_rpc_async(struct efx_nic *efx, unsigned int cmd, - const efx_dword_t *inbuf, size_t inlen, - size_t outlen, - efx_mcdi_async_completer *complete, - unsigned long cookie); +int efx_mcdi_rpc_async(struct efx_nic *efx, unsigned int cmd, + const efx_dword_t *inbuf, size_t inlen, size_t outlen, + efx_mcdi_async_completer *complete, + unsigned long cookie); -extern int efx_mcdi_poll_reboot(struct efx_nic *efx); -extern void efx_mcdi_mode_poll(struct efx_nic *efx); -extern void efx_mcdi_mode_event(struct efx_nic *efx); -extern void efx_mcdi_flush_async(struct efx_nic *efx); +int efx_mcdi_poll_reboot(struct efx_nic *efx); +void efx_mcdi_mode_poll(struct efx_nic *efx); +void efx_mcdi_mode_event(struct efx_nic *efx); +void efx_mcdi_flush_async(struct efx_nic *efx); -extern void efx_mcdi_process_event(struct efx_channel *channel, - efx_qword_t *event); -extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); +void efx_mcdi_process_event(struct efx_channel *channel, efx_qword_t *event); +void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); /* We expect that 16- and 32-bit fields in MCDI requests and responses * are appropriately aligned, but 64-bit fields are only @@ -275,55 +272,54 @@ extern void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev); #define MCDI_EVENT_FIELD(_ev, _field) \ EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) -extern void efx_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len); -extern int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, - u16 *fw_subtype_list, u32 *capabilities); -extern int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, - u32 dest_evq); -extern int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out); -extern int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, - size_t *size_out, size_t *erase_size_out, - bool *protected_out); -extern int efx_mcdi_nvram_test_all(struct efx_nic *efx); -extern int efx_mcdi_handle_assertion(struct efx_nic *efx); -extern void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); -extern int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, - const u8 *mac, int *id_out); -extern int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out); -extern int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id); -extern int efx_mcdi_wol_filter_reset(struct efx_nic *efx); -extern int efx_mcdi_flush_rxqs(struct efx_nic *efx); -extern int efx_mcdi_port_probe(struct efx_nic *efx); -extern void efx_mcdi_port_remove(struct efx_nic *efx); -extern int efx_mcdi_port_reconfigure(struct efx_nic *efx); -extern int efx_mcdi_port_get_number(struct efx_nic *efx); -extern u32 efx_mcdi_phy_get_caps(struct efx_nic *efx); -extern void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev); -extern int efx_mcdi_set_mac(struct efx_nic *efx); +void efx_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len); +int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, + u16 *fw_subtype_list, u32 *capabilities); +int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq); +int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out); +int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, + size_t *size_out, size_t *erase_size_out, + bool *protected_out); +int efx_mcdi_nvram_test_all(struct efx_nic *efx); +int efx_mcdi_handle_assertion(struct efx_nic *efx); +void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); +int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, const u8 *mac, + int *id_out); +int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out); +int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id); +int efx_mcdi_wol_filter_reset(struct efx_nic *efx); +int efx_mcdi_flush_rxqs(struct efx_nic *efx); +int efx_mcdi_port_probe(struct efx_nic *efx); +void efx_mcdi_port_remove(struct efx_nic *efx); +int efx_mcdi_port_reconfigure(struct efx_nic *efx); +int efx_mcdi_port_get_number(struct efx_nic *efx); +u32 efx_mcdi_phy_get_caps(struct efx_nic *efx); +void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev); +int efx_mcdi_set_mac(struct efx_nic *efx); #define EFX_MC_STATS_GENERATION_INVALID ((__force __le64)(-1)) -extern void efx_mcdi_mac_start_stats(struct efx_nic *efx); -extern void efx_mcdi_mac_stop_stats(struct efx_nic *efx); -extern bool efx_mcdi_mac_check_fault(struct efx_nic *efx); -extern enum reset_type efx_mcdi_map_reset_reason(enum reset_type reason); -extern int efx_mcdi_reset(struct efx_nic *efx, enum reset_type method); -extern int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled); +void efx_mcdi_mac_start_stats(struct efx_nic *efx); +void efx_mcdi_mac_stop_stats(struct efx_nic *efx); +bool efx_mcdi_mac_check_fault(struct efx_nic *efx); +enum reset_type efx_mcdi_map_reset_reason(enum reset_type reason); +int efx_mcdi_reset(struct efx_nic *efx, enum reset_type method); +int efx_mcdi_set_workaround(struct efx_nic *efx, u32 type, bool enabled); #ifdef CONFIG_SFC_MCDI_MON -extern int efx_mcdi_mon_probe(struct efx_nic *efx); -extern void efx_mcdi_mon_remove(struct efx_nic *efx); +int efx_mcdi_mon_probe(struct efx_nic *efx); +void efx_mcdi_mon_remove(struct efx_nic *efx); #else static inline int efx_mcdi_mon_probe(struct efx_nic *efx) { return 0; } static inline void efx_mcdi_mon_remove(struct efx_nic *efx) {} #endif #ifdef CONFIG_SFC_MTD -extern int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start, - size_t len, size_t *retlen, u8 *buffer); -extern int efx_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len); -extern int efx_mcdi_mtd_write(struct mtd_info *mtd, loff_t start, - size_t len, size_t *retlen, const u8 *buffer); -extern int efx_mcdi_mtd_sync(struct mtd_info *mtd); -extern void efx_mcdi_mtd_rename(struct efx_mtd_partition *part); +int efx_mcdi_mtd_read(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, u8 *buffer); +int efx_mcdi_mtd_erase(struct mtd_info *mtd, loff_t start, size_t len); +int efx_mcdi_mtd_write(struct mtd_info *mtd, loff_t start, size_t len, + size_t *retlen, const u8 *buffer); +int efx_mcdi_mtd_sync(struct mtd_info *mtd); +void efx_mcdi_mtd_rename(struct efx_mtd_partition *part); #endif #endif /* EFX_MCDI_H */ diff --git a/drivers/net/ethernet/sfc/mdio_10g.h b/drivers/net/ethernet/sfc/mdio_10g.h index 16824fecc5ee5c652a34179aeba4130c93fbc5fb..4a2dc4c281b730fd3b415c4714657c8ead4bdad7 100644 --- a/drivers/net/ethernet/sfc/mdio_10g.h +++ b/drivers/net/ethernet/sfc/mdio_10g.h @@ -20,7 +20,7 @@ static inline unsigned efx_mdio_id_rev(u32 id) { return id & 0xf; } static inline unsigned efx_mdio_id_model(u32 id) { return (id >> 4) & 0x3f; } -extern unsigned efx_mdio_id_oui(u32 id); +unsigned efx_mdio_id_oui(u32 id); static inline int efx_mdio_read(struct efx_nic *efx, int devad, int addr) { @@ -56,7 +56,7 @@ static inline bool efx_mdio_phyxgxs_lane_sync(struct efx_nic *efx) return sync; } -extern const char *efx_mdio_mmd_name(int mmd); +const char *efx_mdio_mmd_name(int mmd); /* * Reset a specific MMD and wait for reset to clear. @@ -64,30 +64,29 @@ extern const char *efx_mdio_mmd_name(int mmd); * * This function will sleep */ -extern int efx_mdio_reset_mmd(struct efx_nic *efx, int mmd, - int spins, int spintime); +int efx_mdio_reset_mmd(struct efx_nic *efx, int mmd, int spins, int spintime); /* As efx_mdio_check_mmd but for multiple MMDs */ int efx_mdio_check_mmds(struct efx_nic *efx, unsigned int mmd_mask); /* Check the link status of specified mmds in bit mask */ -extern bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask); +bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask); /* Generic transmit disable support though PMAPMD */ -extern void efx_mdio_transmit_disable(struct efx_nic *efx); +void efx_mdio_transmit_disable(struct efx_nic *efx); /* Generic part of reconfigure: set/clear loopback bits */ -extern void efx_mdio_phy_reconfigure(struct efx_nic *efx); +void efx_mdio_phy_reconfigure(struct efx_nic *efx); /* Set the power state of the specified MMDs */ -extern void efx_mdio_set_mmds_lpower(struct efx_nic *efx, - int low_power, unsigned int mmd_mask); +void efx_mdio_set_mmds_lpower(struct efx_nic *efx, int low_power, + unsigned int mmd_mask); /* Set (some of) the PHY settings over MDIO */ -extern int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); +int efx_mdio_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd); /* Push advertising flags and restart autonegotiation */ -extern void efx_mdio_an_reconfigure(struct efx_nic *efx); +void efx_mdio_an_reconfigure(struct efx_nic *efx); /* Get pause parameters from AN if available (otherwise return * requested pause parameters) @@ -95,8 +94,7 @@ extern void efx_mdio_an_reconfigure(struct efx_nic *efx); u8 efx_mdio_get_pause(struct efx_nic *efx); /* Wait for specified MMDs to exit reset within a timeout */ -extern int efx_mdio_wait_reset_mmds(struct efx_nic *efx, - unsigned int mmd_mask); +int efx_mdio_wait_reset_mmds(struct efx_nic *efx, unsigned int mmd_mask); /* Set or clear flag, debouncing */ static inline void @@ -107,6 +105,6 @@ efx_mdio_set_flag(struct efx_nic *efx, int devad, int addr, } /* Liveness self-test for MDIO PHYs */ -extern int efx_mdio_test_alive(struct efx_nic *efx); +int efx_mdio_test_alive(struct efx_nic *efx); #endif /* EFX_MDIO_10G_H */ diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index b172ed13305554cf6009fc445ac4c09123b97550..b14a717ac3e8d95099b5d2648be590e138bc9e61 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -141,6 +141,8 @@ struct efx_special_buffer { * @len: Length of this fragment. * This field is zero when the queue slot is empty. * @unmap_len: Length of this fragment to unmap + * @dma_offset: Offset of @dma_addr from the address of the backing DMA mapping. + * Only valid if @unmap_len != 0. */ struct efx_tx_buffer { union { @@ -154,6 +156,7 @@ struct efx_tx_buffer { unsigned short flags; unsigned short len; unsigned short unmap_len; + unsigned short dma_offset; }; #define EFX_TX_BUF_CONT 1 /* not last descriptor of packet */ #define EFX_TX_BUF_SKB 2 /* buffer is last part of skb */ @@ -182,6 +185,9 @@ struct efx_tx_buffer { * @tsoh_page: Array of pages of TSO header buffers * @txd: The hardware descriptor ring * @ptr_mask: The size of the ring minus 1. + * @piobuf: PIO buffer region for this TX queue (shared with its partner). + * Size of the region is efx_piobuf_size. + * @piobuf_offset: Buffer offset to be specified in PIO descriptors * @initialised: Has hardware queue been initialised? * @read_count: Current read pointer. * This is the number of buffers that have been removed from both rings. @@ -209,6 +215,7 @@ struct efx_tx_buffer { * blocks * @tso_packets: Number of packets via the TSO xmit path * @pushes: Number of times the TX push feature has been used + * @pio_packets: Number of times the TX PIO feature has been used * @empty_read_count: If the completion path has seen the queue as empty * and the transmission path has not yet checked this, the value of * @read_count bitwise-added to %EFX_EMPTY_COUNT_VALID; otherwise 0. @@ -223,6 +230,8 @@ struct efx_tx_queue { struct efx_buffer *tsoh_page; struct efx_special_buffer txd; unsigned int ptr_mask; + void __iomem *piobuf; + unsigned int piobuf_offset; bool initialised; /* Members used mainly on the completion path */ @@ -238,6 +247,7 @@ struct efx_tx_queue { unsigned int tso_long_headers; unsigned int tso_packets; unsigned int pushes; + unsigned int pio_packets; /* Members shared between paths and sometimes updated */ unsigned int empty_read_count ____cacheline_aligned_in_smp; diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index 9826594c8a48fa21ca75040fd1513db024092a6e..9c90bf56090f604b4b4df77fb12b0353c922b6e2 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -19,6 +19,7 @@ #include "bitfield.h" #include "efx.h" #include "nic.h" +#include "ef10_regs.h" #include "farch_regs.h" #include "io.h" #include "workarounds.h" @@ -166,26 +167,30 @@ void efx_nic_fini_interrupt(struct efx_nic *efx) /* Register dump */ -#define REGISTER_REVISION_A 1 -#define REGISTER_REVISION_B 2 -#define REGISTER_REVISION_C 3 -#define REGISTER_REVISION_Z 3 /* latest revision */ +#define REGISTER_REVISION_FA 1 +#define REGISTER_REVISION_FB 2 +#define REGISTER_REVISION_FC 3 +#define REGISTER_REVISION_FZ 3 /* last Falcon arch revision */ +#define REGISTER_REVISION_ED 4 +#define REGISTER_REVISION_EZ 4 /* latest EF10 revision */ struct efx_nic_reg { u32 offset:24; - u32 min_revision:2, max_revision:2; + u32 min_revision:3, max_revision:3; }; -#define REGISTER(name, min_rev, max_rev) { \ - FR_ ## min_rev ## max_rev ## _ ## name, \ - REGISTER_REVISION_ ## min_rev, REGISTER_REVISION_ ## max_rev \ +#define REGISTER(name, arch, min_rev, max_rev) { \ + arch ## R_ ## min_rev ## max_rev ## _ ## name, \ + REGISTER_REVISION_ ## arch ## min_rev, \ + REGISTER_REVISION_ ## arch ## max_rev \ } -#define REGISTER_AA(name) REGISTER(name, A, A) -#define REGISTER_AB(name) REGISTER(name, A, B) -#define REGISTER_AZ(name) REGISTER(name, A, Z) -#define REGISTER_BB(name) REGISTER(name, B, B) -#define REGISTER_BZ(name) REGISTER(name, B, Z) -#define REGISTER_CZ(name) REGISTER(name, C, Z) +#define REGISTER_AA(name) REGISTER(name, F, A, A) +#define REGISTER_AB(name) REGISTER(name, F, A, B) +#define REGISTER_AZ(name) REGISTER(name, F, A, Z) +#define REGISTER_BB(name) REGISTER(name, F, B, B) +#define REGISTER_BZ(name) REGISTER(name, F, B, Z) +#define REGISTER_CZ(name) REGISTER(name, F, C, Z) +#define REGISTER_DZ(name) REGISTER(name, E, D, Z) static const struct efx_nic_reg efx_nic_regs[] = { REGISTER_AZ(ADR_REGION), @@ -292,37 +297,42 @@ static const struct efx_nic_reg efx_nic_regs[] = { REGISTER_AB(XX_TXDRV_CTL), /* XX_PRBS_CTL, XX_PRBS_CHK and XX_PRBS_ERR are not used */ /* XX_CORE_STAT is partly RC */ + REGISTER_DZ(BIU_HW_REV_ID), + REGISTER_DZ(MC_DB_LWRD), + REGISTER_DZ(MC_DB_HWRD), }; struct efx_nic_reg_table { u32 offset:24; - u32 min_revision:2, max_revision:2; + u32 min_revision:3, max_revision:3; u32 step:6, rows:21; }; -#define REGISTER_TABLE_DIMENSIONS(_, offset, min_rev, max_rev, step, rows) { \ +#define REGISTER_TABLE_DIMENSIONS(_, offset, arch, min_rev, max_rev, step, rows) { \ offset, \ - REGISTER_REVISION_ ## min_rev, REGISTER_REVISION_ ## max_rev, \ + REGISTER_REVISION_ ## arch ## min_rev, \ + REGISTER_REVISION_ ## arch ## max_rev, \ step, rows \ } -#define REGISTER_TABLE(name, min_rev, max_rev) \ +#define REGISTER_TABLE(name, arch, min_rev, max_rev) \ REGISTER_TABLE_DIMENSIONS( \ - name, FR_ ## min_rev ## max_rev ## _ ## name, \ - min_rev, max_rev, \ - FR_ ## min_rev ## max_rev ## _ ## name ## _STEP, \ - FR_ ## min_rev ## max_rev ## _ ## name ## _ROWS) -#define REGISTER_TABLE_AA(name) REGISTER_TABLE(name, A, A) -#define REGISTER_TABLE_AZ(name) REGISTER_TABLE(name, A, Z) -#define REGISTER_TABLE_BB(name) REGISTER_TABLE(name, B, B) -#define REGISTER_TABLE_BZ(name) REGISTER_TABLE(name, B, Z) + name, arch ## R_ ## min_rev ## max_rev ## _ ## name, \ + arch, min_rev, max_rev, \ + arch ## R_ ## min_rev ## max_rev ## _ ## name ## _STEP, \ + arch ## R_ ## min_rev ## max_rev ## _ ## name ## _ROWS) +#define REGISTER_TABLE_AA(name) REGISTER_TABLE(name, F, A, A) +#define REGISTER_TABLE_AZ(name) REGISTER_TABLE(name, F, A, Z) +#define REGISTER_TABLE_BB(name) REGISTER_TABLE(name, F, B, B) +#define REGISTER_TABLE_BZ(name) REGISTER_TABLE(name, F, B, Z) #define REGISTER_TABLE_BB_CZ(name) \ - REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, B, B, \ + REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, F, B, B, \ FR_BZ_ ## name ## _STEP, \ FR_BB_ ## name ## _ROWS), \ - REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, C, Z, \ + REGISTER_TABLE_DIMENSIONS(name, FR_BZ_ ## name, F, C, Z, \ FR_BZ_ ## name ## _STEP, \ FR_CZ_ ## name ## _ROWS) -#define REGISTER_TABLE_CZ(name) REGISTER_TABLE(name, C, Z) +#define REGISTER_TABLE_CZ(name) REGISTER_TABLE(name, F, C, Z) +#define REGISTER_TABLE_DZ(name) REGISTER_TABLE(name, E, D, Z) static const struct efx_nic_reg_table efx_nic_reg_tables[] = { /* DRIVER is not used */ @@ -340,9 +350,9 @@ static const struct efx_nic_reg_table efx_nic_reg_tables[] = { * 1K entries allows for some expansion of queue count and * size before we need to change the version. */ REGISTER_TABLE_DIMENSIONS(BUF_FULL_TBL_KER, FR_AA_BUF_FULL_TBL_KER, - A, A, 8, 1024), + F, A, A, 8, 1024), REGISTER_TABLE_DIMENSIONS(BUF_FULL_TBL, FR_BZ_BUF_FULL_TBL, - B, Z, 8, 1024), + F, B, Z, 8, 1024), REGISTER_TABLE_CZ(RX_MAC_FILTER_TBL0), REGISTER_TABLE_BB_CZ(TIMER_TBL), REGISTER_TABLE_BB_CZ(TX_PACE_TBL), @@ -353,6 +363,7 @@ static const struct efx_nic_reg_table efx_nic_reg_tables[] = { /* MSIX_PBA_TABLE is not mapped */ /* SRM_DBG is not mapped (and is redundant with BUF_FLL_TBL) */ REGISTER_TABLE_BZ(RX_FILTER_TBL0), + REGISTER_TABLE_DZ(BIU_MC_SFT_STATUS), }; size_t efx_nic_get_regs_len(struct efx_nic *efx) diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index 890bbbe8320ee247ba1ee37965334ed1828d226c..11b6112d9249a734701eebe66414bd039702bcdd 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -30,7 +30,7 @@ static inline int efx_nic_rev(struct efx_nic *efx) return efx->type->revision; } -extern u32 efx_farch_fpga_ver(struct efx_nic *efx); +u32 efx_farch_fpga_ver(struct efx_nic *efx); /* NIC has two interlinked PCI functions for the same port. */ static inline bool efx_nic_is_dual_func(struct efx_nic *efx) @@ -71,6 +71,26 @@ efx_tx_desc(struct efx_tx_queue *tx_queue, unsigned int index) return ((efx_qword_t *) (tx_queue->txd.buf.addr)) + index; } +/* Report whether the NIC considers this TX queue empty, given the + * write_count used for the last doorbell push. May return false + * negative. + */ +static inline bool __efx_nic_tx_is_empty(struct efx_tx_queue *tx_queue, + unsigned int write_count) +{ + unsigned int empty_read_count = ACCESS_ONCE(tx_queue->empty_read_count); + + if (empty_read_count == 0) + return false; + + return ((empty_read_count ^ write_count) & ~EFX_EMPTY_COUNT_VALID) == 0; +} + +static inline bool efx_nic_tx_is_empty(struct efx_tx_queue *tx_queue) +{ + return __efx_nic_tx_is_empty(tx_queue, tx_queue->write_count); +} + /* Decide whether to push a TX descriptor to the NIC vs merely writing * the doorbell. This can reduce latency when we are adding a single * descriptor to an empty queue, but is otherwise pointless. Further, @@ -80,14 +100,10 @@ efx_tx_desc(struct efx_tx_queue *tx_queue, unsigned int index) static inline bool efx_nic_may_push_tx_desc(struct efx_tx_queue *tx_queue, unsigned int write_count) { - unsigned empty_read_count = ACCESS_ONCE(tx_queue->empty_read_count); - - if (empty_read_count == 0) - return false; + bool was_empty = __efx_nic_tx_is_empty(tx_queue, write_count); tx_queue->empty_read_count = 0; - return ((empty_read_count ^ write_count) & ~EFX_EMPTY_COUNT_VALID) == 0 - && tx_queue->write_count - write_count == 1; + return was_empty && tx_queue->write_count - write_count == 1; } /* Returns a pointer to the specified descriptor in the RX descriptor queue */ @@ -401,6 +417,12 @@ enum { EF10_STAT_COUNT }; +/* Maximum number of TX PIO buffers we may allocate to a function. + * This matches the total number of buffers on each SFC9100-family + * controller. + */ +#define EF10_TX_PIOBUF_COUNT 16 + /** * struct efx_ef10_nic_data - EF10 architecture NIC state * @mcdi_buf: DMA buffer for MCDI @@ -409,6 +431,13 @@ enum { * @n_allocated_vis: Number of VIs allocated to this function * @must_realloc_vis: Flag: VIs have yet to be reallocated after MC reboot * @must_restore_filters: Flag: filters have yet to be restored after MC reboot + * @n_piobufs: Number of PIO buffers allocated to this function + * @wc_membase: Base address of write-combining mapping of the memory BAR + * @pio_write_base: Base address for writing PIO buffers + * @pio_write_vi_base: Relative VI number for @pio_write_base + * @piobuf_handle: Handle of each PIO buffer allocated + * @must_restore_piobufs: Flag: PIO buffers have yet to be restored after MC + * reboot * @rx_rss_context: Firmware handle for our RSS context * @stats: Hardware statistics * @workaround_35388: Flag: firmware supports workaround for bug 35388 @@ -424,6 +453,11 @@ struct efx_ef10_nic_data { unsigned int n_allocated_vis; bool must_realloc_vis; bool must_restore_filters; + unsigned int n_piobufs; + void __iomem *wc_membase, *pio_write_base; + unsigned int pio_write_vi_base; + unsigned int piobuf_handle[EF10_TX_PIOBUF_COUNT]; + bool must_restore_piobufs; u32 rx_rss_context; u64 stats[EF10_STAT_COUNT]; bool workaround_35388; @@ -475,18 +509,18 @@ static inline unsigned int efx_vf_size(struct efx_nic *efx) return 1 << efx->vi_scale; } -extern int efx_init_sriov(void); -extern void efx_sriov_probe(struct efx_nic *efx); -extern int efx_sriov_init(struct efx_nic *efx); -extern void efx_sriov_mac_address_changed(struct efx_nic *efx); -extern void efx_sriov_tx_flush_done(struct efx_nic *efx, efx_qword_t *event); -extern void efx_sriov_rx_flush_done(struct efx_nic *efx, efx_qword_t *event); -extern void efx_sriov_event(struct efx_channel *channel, efx_qword_t *event); -extern void efx_sriov_desc_fetch_err(struct efx_nic *efx, unsigned dmaq); -extern void efx_sriov_flr(struct efx_nic *efx, unsigned flr); -extern void efx_sriov_reset(struct efx_nic *efx); -extern void efx_sriov_fini(struct efx_nic *efx); -extern void efx_fini_sriov(void); +int efx_init_sriov(void); +void efx_sriov_probe(struct efx_nic *efx); +int efx_sriov_init(struct efx_nic *efx); +void efx_sriov_mac_address_changed(struct efx_nic *efx); +void efx_sriov_tx_flush_done(struct efx_nic *efx, efx_qword_t *event); +void efx_sriov_rx_flush_done(struct efx_nic *efx, efx_qword_t *event); +void efx_sriov_event(struct efx_channel *channel, efx_qword_t *event); +void efx_sriov_desc_fetch_err(struct efx_nic *efx, unsigned dmaq); +void efx_sriov_flr(struct efx_nic *efx, unsigned flr); +void efx_sriov_reset(struct efx_nic *efx); +void efx_sriov_fini(struct efx_nic *efx); +void efx_fini_sriov(void); #else @@ -512,22 +546,20 @@ static inline void efx_fini_sriov(void) {} #endif -extern int efx_sriov_set_vf_mac(struct net_device *dev, int vf, u8 *mac); -extern int efx_sriov_set_vf_vlan(struct net_device *dev, int vf, - u16 vlan, u8 qos); -extern int efx_sriov_get_vf_config(struct net_device *dev, int vf, - struct ifla_vf_info *ivf); -extern int efx_sriov_set_vf_spoofchk(struct net_device *net_dev, int vf, - bool spoofchk); +int efx_sriov_set_vf_mac(struct net_device *dev, int vf, u8 *mac); +int efx_sriov_set_vf_vlan(struct net_device *dev, int vf, u16 vlan, u8 qos); +int efx_sriov_get_vf_config(struct net_device *dev, int vf, + struct ifla_vf_info *ivf); +int efx_sriov_set_vf_spoofchk(struct net_device *net_dev, int vf, + bool spoofchk); struct ethtool_ts_info; -extern void efx_ptp_probe(struct efx_nic *efx); -extern int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd); -extern void efx_ptp_get_ts_info(struct efx_nic *efx, - struct ethtool_ts_info *ts_info); -extern bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb); -extern int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb); -extern void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev); +void efx_ptp_probe(struct efx_nic *efx); +int efx_ptp_ioctl(struct efx_nic *efx, struct ifreq *ifr, int cmd); +void efx_ptp_get_ts_info(struct efx_nic *efx, struct ethtool_ts_info *ts_info); +bool efx_ptp_is_ptp_tx(struct efx_nic *efx, struct sk_buff *skb); +int efx_ptp_tx(struct efx_nic *efx, struct sk_buff *skb); +void efx_ptp_event(struct efx_nic *efx, efx_qword_t *ev); extern const struct efx_nic_type falcon_a1_nic_type; extern const struct efx_nic_type falcon_b0_nic_type; @@ -541,7 +573,7 @@ extern const struct efx_nic_type efx_hunt_a0_nic_type; ************************************************************************** */ -extern int falcon_probe_board(struct efx_nic *efx, u16 revision_info); +int falcon_probe_board(struct efx_nic *efx, u16 revision_info); /* TX data path */ static inline int efx_nic_probe_tx(struct efx_tx_queue *tx_queue) @@ -609,58 +641,58 @@ static inline void efx_nic_eventq_read_ack(struct efx_channel *channel) { channel->efx->type->ev_read_ack(channel); } -extern void efx_nic_event_test_start(struct efx_channel *channel); +void efx_nic_event_test_start(struct efx_channel *channel); /* Falcon/Siena queue operations */ -extern int efx_farch_tx_probe(struct efx_tx_queue *tx_queue); -extern void efx_farch_tx_init(struct efx_tx_queue *tx_queue); -extern void efx_farch_tx_fini(struct efx_tx_queue *tx_queue); -extern void efx_farch_tx_remove(struct efx_tx_queue *tx_queue); -extern void efx_farch_tx_write(struct efx_tx_queue *tx_queue); -extern int efx_farch_rx_probe(struct efx_rx_queue *rx_queue); -extern void efx_farch_rx_init(struct efx_rx_queue *rx_queue); -extern void efx_farch_rx_fini(struct efx_rx_queue *rx_queue); -extern void efx_farch_rx_remove(struct efx_rx_queue *rx_queue); -extern void efx_farch_rx_write(struct efx_rx_queue *rx_queue); -extern void efx_farch_rx_defer_refill(struct efx_rx_queue *rx_queue); -extern int efx_farch_ev_probe(struct efx_channel *channel); -extern int efx_farch_ev_init(struct efx_channel *channel); -extern void efx_farch_ev_fini(struct efx_channel *channel); -extern void efx_farch_ev_remove(struct efx_channel *channel); -extern int efx_farch_ev_process(struct efx_channel *channel, int quota); -extern void efx_farch_ev_read_ack(struct efx_channel *channel); -extern void efx_farch_ev_test_generate(struct efx_channel *channel); +int efx_farch_tx_probe(struct efx_tx_queue *tx_queue); +void efx_farch_tx_init(struct efx_tx_queue *tx_queue); +void efx_farch_tx_fini(struct efx_tx_queue *tx_queue); +void efx_farch_tx_remove(struct efx_tx_queue *tx_queue); +void efx_farch_tx_write(struct efx_tx_queue *tx_queue); +int efx_farch_rx_probe(struct efx_rx_queue *rx_queue); +void efx_farch_rx_init(struct efx_rx_queue *rx_queue); +void efx_farch_rx_fini(struct efx_rx_queue *rx_queue); +void efx_farch_rx_remove(struct efx_rx_queue *rx_queue); +void efx_farch_rx_write(struct efx_rx_queue *rx_queue); +void efx_farch_rx_defer_refill(struct efx_rx_queue *rx_queue); +int efx_farch_ev_probe(struct efx_channel *channel); +int efx_farch_ev_init(struct efx_channel *channel); +void efx_farch_ev_fini(struct efx_channel *channel); +void efx_farch_ev_remove(struct efx_channel *channel); +int efx_farch_ev_process(struct efx_channel *channel, int quota); +void efx_farch_ev_read_ack(struct efx_channel *channel); +void efx_farch_ev_test_generate(struct efx_channel *channel); /* Falcon/Siena filter operations */ -extern int efx_farch_filter_table_probe(struct efx_nic *efx); -extern void efx_farch_filter_table_restore(struct efx_nic *efx); -extern void efx_farch_filter_table_remove(struct efx_nic *efx); -extern void efx_farch_filter_update_rx_scatter(struct efx_nic *efx); -extern s32 efx_farch_filter_insert(struct efx_nic *efx, - struct efx_filter_spec *spec, bool replace); -extern int efx_farch_filter_remove_safe(struct efx_nic *efx, - enum efx_filter_priority priority, - u32 filter_id); -extern int efx_farch_filter_get_safe(struct efx_nic *efx, - enum efx_filter_priority priority, - u32 filter_id, struct efx_filter_spec *); -extern void efx_farch_filter_clear_rx(struct efx_nic *efx, - enum efx_filter_priority priority); -extern u32 efx_farch_filter_count_rx_used(struct efx_nic *efx, - enum efx_filter_priority priority); -extern u32 efx_farch_filter_get_rx_id_limit(struct efx_nic *efx); -extern s32 efx_farch_filter_get_rx_ids(struct efx_nic *efx, - enum efx_filter_priority priority, - u32 *buf, u32 size); +int efx_farch_filter_table_probe(struct efx_nic *efx); +void efx_farch_filter_table_restore(struct efx_nic *efx); +void efx_farch_filter_table_remove(struct efx_nic *efx); +void efx_farch_filter_update_rx_scatter(struct efx_nic *efx); +s32 efx_farch_filter_insert(struct efx_nic *efx, struct efx_filter_spec *spec, + bool replace); +int efx_farch_filter_remove_safe(struct efx_nic *efx, + enum efx_filter_priority priority, + u32 filter_id); +int efx_farch_filter_get_safe(struct efx_nic *efx, + enum efx_filter_priority priority, u32 filter_id, + struct efx_filter_spec *); +void efx_farch_filter_clear_rx(struct efx_nic *efx, + enum efx_filter_priority priority); +u32 efx_farch_filter_count_rx_used(struct efx_nic *efx, + enum efx_filter_priority priority); +u32 efx_farch_filter_get_rx_id_limit(struct efx_nic *efx); +s32 efx_farch_filter_get_rx_ids(struct efx_nic *efx, + enum efx_filter_priority priority, u32 *buf, + u32 size); #ifdef CONFIG_RFS_ACCEL -extern s32 efx_farch_filter_rfs_insert(struct efx_nic *efx, - struct efx_filter_spec *spec); -extern bool efx_farch_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id, - unsigned int index); +s32 efx_farch_filter_rfs_insert(struct efx_nic *efx, + struct efx_filter_spec *spec); +bool efx_farch_filter_rfs_expire_one(struct efx_nic *efx, u32 flow_id, + unsigned int index); #endif -extern void efx_farch_filter_sync_rx_mode(struct efx_nic *efx); +void efx_farch_filter_sync_rx_mode(struct efx_nic *efx); -extern bool efx_nic_event_present(struct efx_channel *channel); +bool efx_nic_event_present(struct efx_channel *channel); /* Some statistics are computed as A - B where A and B each increase * linearly with some hardware counter(s) and the counters are read @@ -681,17 +713,17 @@ static inline void efx_update_diff_stat(u64 *stat, u64 diff) } /* Interrupts */ -extern int efx_nic_init_interrupt(struct efx_nic *efx); -extern void efx_nic_irq_test_start(struct efx_nic *efx); -extern void efx_nic_fini_interrupt(struct efx_nic *efx); +int efx_nic_init_interrupt(struct efx_nic *efx); +void efx_nic_irq_test_start(struct efx_nic *efx); +void efx_nic_fini_interrupt(struct efx_nic *efx); /* Falcon/Siena interrupts */ -extern void efx_farch_irq_enable_master(struct efx_nic *efx); -extern void efx_farch_irq_test_generate(struct efx_nic *efx); -extern void efx_farch_irq_disable_master(struct efx_nic *efx); -extern irqreturn_t efx_farch_msi_interrupt(int irq, void *dev_id); -extern irqreturn_t efx_farch_legacy_interrupt(int irq, void *dev_id); -extern irqreturn_t efx_farch_fatal_interrupt(struct efx_nic *efx); +void efx_farch_irq_enable_master(struct efx_nic *efx); +void efx_farch_irq_test_generate(struct efx_nic *efx); +void efx_farch_irq_disable_master(struct efx_nic *efx); +irqreturn_t efx_farch_msi_interrupt(int irq, void *dev_id); +irqreturn_t efx_farch_legacy_interrupt(int irq, void *dev_id); +irqreturn_t efx_farch_fatal_interrupt(struct efx_nic *efx); static inline int efx_nic_event_test_irq_cpu(struct efx_channel *channel) { @@ -703,21 +735,21 @@ static inline int efx_nic_irq_test_irq_cpu(struct efx_nic *efx) } /* Global Resources */ -extern int efx_nic_flush_queues(struct efx_nic *efx); -extern void siena_prepare_flush(struct efx_nic *efx); -extern int efx_farch_fini_dmaq(struct efx_nic *efx); -extern void siena_finish_flush(struct efx_nic *efx); -extern void falcon_start_nic_stats(struct efx_nic *efx); -extern void falcon_stop_nic_stats(struct efx_nic *efx); -extern int falcon_reset_xaui(struct efx_nic *efx); -extern void efx_farch_dimension_resources(struct efx_nic *efx, unsigned sram_lim_qw); -extern void efx_farch_init_common(struct efx_nic *efx); -extern void efx_ef10_handle_drain_event(struct efx_nic *efx); +int efx_nic_flush_queues(struct efx_nic *efx); +void siena_prepare_flush(struct efx_nic *efx); +int efx_farch_fini_dmaq(struct efx_nic *efx); +void siena_finish_flush(struct efx_nic *efx); +void falcon_start_nic_stats(struct efx_nic *efx); +void falcon_stop_nic_stats(struct efx_nic *efx); +int falcon_reset_xaui(struct efx_nic *efx); +void efx_farch_dimension_resources(struct efx_nic *efx, unsigned sram_lim_qw); +void efx_farch_init_common(struct efx_nic *efx); +void efx_ef10_handle_drain_event(struct efx_nic *efx); static inline void efx_nic_push_rx_indir_table(struct efx_nic *efx) { efx->type->rx_push_indir_table(efx); } -extern void efx_farch_rx_push_indir_table(struct efx_nic *efx); +void efx_farch_rx_push_indir_table(struct efx_nic *efx); int efx_nic_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buffer, unsigned int len, gfp_t gfp_flags); @@ -728,24 +760,22 @@ struct efx_farch_register_test { unsigned address; efx_oword_t mask; }; -extern int efx_farch_test_registers(struct efx_nic *efx, - const struct efx_farch_register_test *regs, - size_t n_regs); +int efx_farch_test_registers(struct efx_nic *efx, + const struct efx_farch_register_test *regs, + size_t n_regs); -extern size_t efx_nic_get_regs_len(struct efx_nic *efx); -extern void efx_nic_get_regs(struct efx_nic *efx, void *buf); +size_t efx_nic_get_regs_len(struct efx_nic *efx); +void efx_nic_get_regs(struct efx_nic *efx, void *buf); -extern size_t -efx_nic_describe_stats(const struct efx_hw_stat_desc *desc, size_t count, - const unsigned long *mask, u8 *names); -extern void -efx_nic_update_stats(const struct efx_hw_stat_desc *desc, size_t count, - const unsigned long *mask, - u64 *stats, const void *dma_buf, bool accumulate); +size_t efx_nic_describe_stats(const struct efx_hw_stat_desc *desc, size_t count, + const unsigned long *mask, u8 *names); +void efx_nic_update_stats(const struct efx_hw_stat_desc *desc, size_t count, + const unsigned long *mask, u64 *stats, + const void *dma_buf, bool accumulate); #define EFX_MAX_FLUSH_TIME 5000 -extern void efx_farch_generate_event(struct efx_nic *efx, unsigned int evq, - efx_qword_t *event); +void efx_farch_generate_event(struct efx_nic *efx, unsigned int evq, + efx_qword_t *event); #endif /* EFX_NIC_H */ diff --git a/drivers/net/ethernet/sfc/phy.h b/drivers/net/ethernet/sfc/phy.h index 45eeb70751562651b3a361e7b61f4ea4469b381b..803bf445c08e22df119c8274816cb26cf0b51a2b 100644 --- a/drivers/net/ethernet/sfc/phy.h +++ b/drivers/net/ethernet/sfc/phy.h @@ -15,7 +15,7 @@ */ extern const struct efx_phy_operations falcon_sfx7101_phy_ops; -extern void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); +void tenxpress_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); /**************************************************************************** * AMCC/Quake QT202x PHYs @@ -34,7 +34,7 @@ extern const struct efx_phy_operations falcon_qt202x_phy_ops; #define QUAKE_LED_TXLINK (0) #define QUAKE_LED_RXLINK (8) -extern void falcon_qt202x_set_led(struct efx_nic *p, int led, int state); +void falcon_qt202x_set_led(struct efx_nic *p, int led, int state); /**************************************************************************** * Transwitch CX4 retimer @@ -44,7 +44,7 @@ extern const struct efx_phy_operations falcon_txc_phy_ops; #define TXC_GPIO_DIR_INPUT 0 #define TXC_GPIO_DIR_OUTPUT 1 -extern void falcon_txc_set_gpio_dir(struct efx_nic *efx, int pin, int dir); -extern void falcon_txc_set_gpio_val(struct efx_nic *efx, int pin, int val); +void falcon_txc_set_gpio_dir(struct efx_nic *efx, int pin, int dir); +void falcon_txc_set_gpio_val(struct efx_nic *efx, int pin, int val); #endif diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 4a596725023f07284ef66b3bb2cc337e92a0d69e..8f09e686fc2392a80f56610c78a61c6374b4a410 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -818,44 +819,70 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, struct efx_nic *efx = netdev_priv(net_dev); struct efx_channel *channel; struct efx_filter_spec spec; - const struct iphdr *ip; const __be16 *ports; + __be16 ether_type; int nhoff; int rc; - nhoff = skb_network_offset(skb); + /* The core RPS/RFS code has already parsed and validated + * VLAN, IP and transport headers. We assume they are in the + * header area. + */ if (skb->protocol == htons(ETH_P_8021Q)) { - EFX_BUG_ON_PARANOID(skb_headlen(skb) < - nhoff + sizeof(struct vlan_hdr)); - if (((const struct vlan_hdr *)skb->data + nhoff)-> - h_vlan_encapsulated_proto != htons(ETH_P_IP)) - return -EPROTONOSUPPORT; + const struct vlan_hdr *vh = + (const struct vlan_hdr *)skb->data; - /* This is IP over 802.1q VLAN. We can't filter on the - * IP 5-tuple and the vlan together, so just strip the - * vlan header and filter on the IP part. + /* We can't filter on the IP 5-tuple and the vlan + * together, so just strip the vlan header and filter + * on the IP part. */ - nhoff += sizeof(struct vlan_hdr); - } else if (skb->protocol != htons(ETH_P_IP)) { - return -EPROTONOSUPPORT; + EFX_BUG_ON_PARANOID(skb_headlen(skb) < sizeof(*vh)); + ether_type = vh->h_vlan_encapsulated_proto; + nhoff = sizeof(struct vlan_hdr); + } else { + ether_type = skb->protocol; + nhoff = 0; } - /* RFS must validate the IP header length before calling us */ - EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip)); - ip = (const struct iphdr *)(skb->data + nhoff); - if (ip_is_fragment(ip)) + if (ether_type != htons(ETH_P_IP) && ether_type != htons(ETH_P_IPV6)) return -EPROTONOSUPPORT; - EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4); - ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl); efx_filter_init_rx(&spec, EFX_FILTER_PRI_HINT, efx->rx_scatter ? EFX_FILTER_FLAG_RX_SCATTER : 0, rxq_index); - rc = efx_filter_set_ipv4_full(&spec, ip->protocol, - ip->daddr, ports[1], ip->saddr, ports[0]); - if (rc) - return rc; + spec.match_flags = + EFX_FILTER_MATCH_ETHER_TYPE | EFX_FILTER_MATCH_IP_PROTO | + EFX_FILTER_MATCH_LOC_HOST | EFX_FILTER_MATCH_LOC_PORT | + EFX_FILTER_MATCH_REM_HOST | EFX_FILTER_MATCH_REM_PORT; + spec.ether_type = ether_type; + + if (ether_type == htons(ETH_P_IP)) { + const struct iphdr *ip = + (const struct iphdr *)(skb->data + nhoff); + + EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + sizeof(*ip)); + if (ip_is_fragment(ip)) + return -EPROTONOSUPPORT; + spec.ip_proto = ip->protocol; + spec.rem_host[0] = ip->saddr; + spec.loc_host[0] = ip->daddr; + EFX_BUG_ON_PARANOID(skb_headlen(skb) < nhoff + 4 * ip->ihl + 4); + ports = (const __be16 *)(skb->data + nhoff + 4 * ip->ihl); + } else { + const struct ipv6hdr *ip6 = + (const struct ipv6hdr *)(skb->data + nhoff); + + EFX_BUG_ON_PARANOID(skb_headlen(skb) < + nhoff + sizeof(*ip6) + 4); + spec.ip_proto = ip6->nexthdr; + memcpy(spec.rem_host, &ip6->saddr, sizeof(ip6->saddr)); + memcpy(spec.loc_host, &ip6->daddr, sizeof(ip6->daddr)); + ports = (const __be16 *)(ip6 + 1); + } + + spec.rem_port = ports[0]; + spec.loc_port = ports[1]; rc = efx->type->filter_rfs_insert(efx, &spec); if (rc < 0) @@ -866,11 +893,18 @@ int efx_filter_rfs(struct net_device *net_dev, const struct sk_buff *skb, channel = efx_get_channel(efx, skb_get_rx_queue(skb)); ++channel->rfs_filters_added; - netif_info(efx, rx_status, efx->net_dev, - "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n", - (ip->protocol == IPPROTO_TCP) ? "TCP" : "UDP", - &ip->saddr, ntohs(ports[0]), &ip->daddr, ntohs(ports[1]), - rxq_index, flow_id, rc); + if (ether_type == htons(ETH_P_IP)) + netif_info(efx, rx_status, efx->net_dev, + "steering %s %pI4:%u:%pI4:%u to queue %u [flow %u filter %d]\n", + (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", + spec.rem_host, ntohs(ports[0]), spec.loc_host, + ntohs(ports[1]), rxq_index, flow_id, rc); + else + netif_info(efx, rx_status, efx->net_dev, + "steering %s [%pI6]:%u:[%pI6]:%u to queue %u [flow %u filter %d]\n", + (spec.ip_proto == IPPROTO_TCP) ? "TCP" : "UDP", + spec.rem_host, ntohs(ports[0]), spec.loc_host, + ntohs(ports[1]), rxq_index, flow_id, rc); return rc; } diff --git a/drivers/net/ethernet/sfc/selftest.h b/drivers/net/ethernet/sfc/selftest.h index 87698ae0bf75f7dab78ca093ea2c3f3b3f1774d5..a2f4a06ffa4e4d8a6cb6829b45b8397ea10d4283 100644 --- a/drivers/net/ethernet/sfc/selftest.h +++ b/drivers/net/ethernet/sfc/selftest.h @@ -43,13 +43,12 @@ struct efx_self_tests { struct efx_loopback_self_tests loopback[LOOPBACK_TEST_MAX + 1]; }; -extern void efx_loopback_rx_packet(struct efx_nic *efx, - const char *buf_ptr, int pkt_len); -extern int efx_selftest(struct efx_nic *efx, - struct efx_self_tests *tests, - unsigned flags); -extern void efx_selftest_async_start(struct efx_nic *efx); -extern void efx_selftest_async_cancel(struct efx_nic *efx); -extern void efx_selftest_async_work(struct work_struct *data); +void efx_loopback_rx_packet(struct efx_nic *efx, const char *buf_ptr, + int pkt_len); +int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, + unsigned flags); +void efx_selftest_async_start(struct efx_nic *efx); +void efx_selftest_async_cancel(struct efx_nic *efx); +void efx_selftest_async_work(struct work_struct *data); #endif /* EFX_SELFTEST_H */ diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 2ac91c5b5eeae4e7a210e5b3693c9f01a04c9f9c..c49d1fb169652199ebccc42db45b0b73323d6f28 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -17,10 +17,46 @@ #include #include #include +#include #include "net_driver.h" #include "efx.h" +#include "io.h" #include "nic.h" #include "workarounds.h" +#include "ef10_regs.h" + +#ifdef EFX_USE_PIO + +#define EFX_PIOBUF_SIZE_MAX ER_DZ_TX_PIOBUF_SIZE +#define EFX_PIOBUF_SIZE_DEF ALIGN(256, L1_CACHE_BYTES) +unsigned int efx_piobuf_size __read_mostly = EFX_PIOBUF_SIZE_DEF; + +#endif /* EFX_USE_PIO */ + +static inline unsigned int +efx_tx_queue_get_insert_index(const struct efx_tx_queue *tx_queue) +{ + return tx_queue->insert_count & tx_queue->ptr_mask; +} + +static inline struct efx_tx_buffer * +__efx_tx_queue_get_insert_buffer(const struct efx_tx_queue *tx_queue) +{ + return &tx_queue->buffer[efx_tx_queue_get_insert_index(tx_queue)]; +} + +static inline struct efx_tx_buffer * +efx_tx_queue_get_insert_buffer(const struct efx_tx_queue *tx_queue) +{ + struct efx_tx_buffer *buffer = + __efx_tx_queue_get_insert_buffer(tx_queue); + + EFX_BUG_ON_PARANOID(buffer->len); + EFX_BUG_ON_PARANOID(buffer->flags); + EFX_BUG_ON_PARANOID(buffer->unmap_len); + + return buffer; +} static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, struct efx_tx_buffer *buffer, @@ -29,8 +65,7 @@ static void efx_dequeue_buffer(struct efx_tx_queue *tx_queue, { if (buffer->unmap_len) { struct device *dma_dev = &tx_queue->efx->pci_dev->dev; - dma_addr_t unmap_addr = (buffer->dma_addr + buffer->len - - buffer->unmap_len); + dma_addr_t unmap_addr = buffer->dma_addr - buffer->dma_offset; if (buffer->flags & EFX_TX_BUF_MAP_SINGLE) dma_unmap_single(dma_dev, unmap_addr, buffer->unmap_len, DMA_TO_DEVICE); @@ -83,8 +118,10 @@ unsigned int efx_tx_max_skb_descs(struct efx_nic *efx) */ unsigned int max_descs = EFX_TSO_MAX_SEGS * 2 + MAX_SKB_FRAGS; - /* Possibly one more per segment for the alignment workaround */ - if (EFX_WORKAROUND_5391(efx)) + /* Possibly one more per segment for the alignment workaround, + * or for option descriptors + */ + if (EFX_WORKAROUND_5391(efx) || efx_nic_rev(efx) >= EFX_REV_HUNT_A0) max_descs += EFX_TSO_MAX_SEGS; /* Possibly more for PCIe page boundaries within input fragments */ @@ -145,6 +182,145 @@ static void efx_tx_maybe_stop_queue(struct efx_tx_queue *txq1) } } +#ifdef EFX_USE_PIO + +struct efx_short_copy_buffer { + int used; + u8 buf[L1_CACHE_BYTES]; +}; + +/* Copy to PIO, respecting that writes to PIO buffers must be dword aligned. + * Advances piobuf pointer. Leaves additional data in the copy buffer. + */ +static void efx_memcpy_toio_aligned(struct efx_nic *efx, u8 __iomem **piobuf, + u8 *data, int len, + struct efx_short_copy_buffer *copy_buf) +{ + int block_len = len & ~(sizeof(copy_buf->buf) - 1); + + memcpy_toio(*piobuf, data, block_len); + *piobuf += block_len; + len -= block_len; + + if (len) { + data += block_len; + BUG_ON(copy_buf->used); + BUG_ON(len > sizeof(copy_buf->buf)); + memcpy(copy_buf->buf, data, len); + copy_buf->used = len; + } +} + +/* Copy to PIO, respecting dword alignment, popping data from copy buffer first. + * Advances piobuf pointer. Leaves additional data in the copy buffer. + */ +static void efx_memcpy_toio_aligned_cb(struct efx_nic *efx, u8 __iomem **piobuf, + u8 *data, int len, + struct efx_short_copy_buffer *copy_buf) +{ + if (copy_buf->used) { + /* if the copy buffer is partially full, fill it up and write */ + int copy_to_buf = + min_t(int, sizeof(copy_buf->buf) - copy_buf->used, len); + + memcpy(copy_buf->buf + copy_buf->used, data, copy_to_buf); + copy_buf->used += copy_to_buf; + + /* if we didn't fill it up then we're done for now */ + if (copy_buf->used < sizeof(copy_buf->buf)) + return; + + memcpy_toio(*piobuf, copy_buf->buf, sizeof(copy_buf->buf)); + *piobuf += sizeof(copy_buf->buf); + data += copy_to_buf; + len -= copy_to_buf; + copy_buf->used = 0; + } + + efx_memcpy_toio_aligned(efx, piobuf, data, len, copy_buf); +} + +static void efx_flush_copy_buffer(struct efx_nic *efx, u8 __iomem *piobuf, + struct efx_short_copy_buffer *copy_buf) +{ + /* if there's anything in it, write the whole buffer, including junk */ + if (copy_buf->used) + memcpy_toio(piobuf, copy_buf->buf, sizeof(copy_buf->buf)); +} + +/* Traverse skb structure and copy fragments in to PIO buffer. + * Advances piobuf pointer. + */ +static void efx_skb_copy_bits_to_pio(struct efx_nic *efx, struct sk_buff *skb, + u8 __iomem **piobuf, + struct efx_short_copy_buffer *copy_buf) +{ + int i; + + efx_memcpy_toio_aligned(efx, piobuf, skb->data, skb_headlen(skb), + copy_buf); + + for (i = 0; i < skb_shinfo(skb)->nr_frags; ++i) { + skb_frag_t *f = &skb_shinfo(skb)->frags[i]; + u8 *vaddr; + + vaddr = kmap_atomic(skb_frag_page(f)); + + efx_memcpy_toio_aligned_cb(efx, piobuf, vaddr + f->page_offset, + skb_frag_size(f), copy_buf); + kunmap_atomic(vaddr); + } + + EFX_BUG_ON_PARANOID(skb_shinfo(skb)->frag_list); +} + +static struct efx_tx_buffer * +efx_enqueue_skb_pio(struct efx_tx_queue *tx_queue, struct sk_buff *skb) +{ + struct efx_tx_buffer *buffer = + efx_tx_queue_get_insert_buffer(tx_queue); + u8 __iomem *piobuf = tx_queue->piobuf; + + /* Copy to PIO buffer. Ensure the writes are padded to the end + * of a cache line, as this is required for write-combining to be + * effective on at least x86. + */ + + if (skb_shinfo(skb)->nr_frags) { + /* The size of the copy buffer will ensure all writes + * are the size of a cache line. + */ + struct efx_short_copy_buffer copy_buf; + + copy_buf.used = 0; + + efx_skb_copy_bits_to_pio(tx_queue->efx, skb, + &piobuf, ©_buf); + efx_flush_copy_buffer(tx_queue->efx, piobuf, ©_buf); + } else { + /* Pad the write to the size of a cache line. + * We can do this because we know the skb_shared_info sruct is + * after the source, and the destination buffer is big enough. + */ + BUILD_BUG_ON(L1_CACHE_BYTES > + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); + memcpy_toio(tx_queue->piobuf, skb->data, + ALIGN(skb->len, L1_CACHE_BYTES)); + } + + EFX_POPULATE_QWORD_5(buffer->option, + ESF_DZ_TX_DESC_IS_OPT, 1, + ESF_DZ_TX_OPTION_TYPE, ESE_DZ_TX_OPTION_DESC_PIO, + ESF_DZ_TX_PIO_CONT, 0, + ESF_DZ_TX_PIO_BYTE_CNT, skb->len, + ESF_DZ_TX_PIO_BUF_ADDR, + tx_queue->piobuf_offset); + ++tx_queue->pio_packets; + ++tx_queue->insert_count; + return buffer; +} +#endif /* EFX_USE_PIO */ + /* * Add a socket buffer to a TX queue * @@ -167,7 +343,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) struct device *dma_dev = &efx->pci_dev->dev; struct efx_tx_buffer *buffer; skb_frag_t *fragment; - unsigned int len, unmap_len = 0, insert_ptr; + unsigned int len, unmap_len = 0; dma_addr_t dma_addr, unmap_addr = 0; unsigned int dma_len; unsigned short dma_flags; @@ -189,6 +365,17 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) return NETDEV_TX_OK; } + /* Consider using PIO for short packets */ +#ifdef EFX_USE_PIO + if (skb->len <= efx_piobuf_size && tx_queue->piobuf && + efx_nic_tx_is_empty(tx_queue) && + efx_nic_tx_is_empty(efx_tx_queue_partner(tx_queue))) { + buffer = efx_enqueue_skb_pio(tx_queue, skb); + dma_flags = EFX_TX_BUF_OPTION; + goto finish_packet; + } +#endif + /* Map for DMA. Use dma_map_single rather than dma_map_page * since this is more efficient on machines with sparse * memory. @@ -208,11 +395,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) /* Add to TX queue, splitting across DMA boundaries */ do { - insert_ptr = tx_queue->insert_count & tx_queue->ptr_mask; - buffer = &tx_queue->buffer[insert_ptr]; - EFX_BUG_ON_PARANOID(buffer->flags); - EFX_BUG_ON_PARANOID(buffer->len); - EFX_BUG_ON_PARANOID(buffer->unmap_len); + buffer = efx_tx_queue_get_insert_buffer(tx_queue); dma_len = efx_max_tx_len(efx, dma_addr); if (likely(dma_len >= len)) @@ -230,6 +413,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) /* Transfer ownership of the unmapping to the final buffer */ buffer->flags = EFX_TX_BUF_CONT | dma_flags; buffer->unmap_len = unmap_len; + buffer->dma_offset = buffer->dma_addr - unmap_addr; unmap_len = 0; /* Get address and size of next fragment */ @@ -245,6 +429,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) } /* Transfer ownership of the skb to the final buffer */ +finish_packet: buffer->skb = skb; buffer->flags = EFX_TX_BUF_SKB | dma_flags; @@ -270,8 +455,7 @@ netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb) while (tx_queue->insert_count != tx_queue->write_count) { unsigned int pkts_compl = 0, bytes_compl = 0; --tx_queue->insert_count; - insert_ptr = tx_queue->insert_count & tx_queue->ptr_mask; - buffer = &tx_queue->buffer[insert_ptr]; + buffer = __efx_tx_queue_get_insert_buffer(tx_queue); efx_dequeue_buffer(tx_queue, buffer, &pkts_compl, &bytes_compl); } @@ -628,6 +812,9 @@ void efx_remove_tx_queue(struct efx_tx_queue *tx_queue) * @tcp_off: Offset of TCP header * @header_len: Number of bytes of header * @ip_base_len: IPv4 tot_len or IPv6 payload_len, before TCP payload + * @header_dma_addr: Header DMA address, when using option descriptors + * @header_unmap_len: Header DMA mapped length, or 0 if not using option + * descriptors * * The state used during segmentation. It is put into this data structure * just to make it easy to pass into inline functions. @@ -636,7 +823,7 @@ struct tso_state { /* Output position */ unsigned out_len; unsigned seqnum; - unsigned ipv4_id; + u16 ipv4_id; unsigned packet_space; /* Input position */ @@ -651,6 +838,8 @@ struct tso_state { unsigned int tcp_off; unsigned header_len; unsigned int ip_base_len; + dma_addr_t header_dma_addr; + unsigned int header_unmap_len; }; @@ -737,23 +926,18 @@ static void efx_tx_queue_insert(struct efx_tx_queue *tx_queue, { struct efx_tx_buffer *buffer; struct efx_nic *efx = tx_queue->efx; - unsigned dma_len, insert_ptr; + unsigned dma_len; EFX_BUG_ON_PARANOID(len <= 0); while (1) { - insert_ptr = tx_queue->insert_count & tx_queue->ptr_mask; - buffer = &tx_queue->buffer[insert_ptr]; + buffer = efx_tx_queue_get_insert_buffer(tx_queue); ++tx_queue->insert_count; EFX_BUG_ON_PARANOID(tx_queue->insert_count - tx_queue->read_count >= efx->txq_entries); - EFX_BUG_ON_PARANOID(buffer->len); - EFX_BUG_ON_PARANOID(buffer->unmap_len); - EFX_BUG_ON_PARANOID(buffer->flags); - buffer->dma_addr = dma_addr; dma_len = efx_max_tx_len(efx, dma_addr); @@ -796,6 +980,7 @@ static int efx_tso_put_header(struct efx_tx_queue *tx_queue, return -ENOMEM; } buffer->unmap_len = buffer->len; + buffer->dma_offset = 0; buffer->flags |= EFX_TX_BUF_MAP_SINGLE; } @@ -814,19 +999,27 @@ static void efx_enqueue_unwind(struct efx_tx_queue *tx_queue) /* Work backwards until we hit the original insert pointer value */ while (tx_queue->insert_count != tx_queue->write_count) { --tx_queue->insert_count; - buffer = &tx_queue->buffer[tx_queue->insert_count & - tx_queue->ptr_mask]; + buffer = __efx_tx_queue_get_insert_buffer(tx_queue); efx_dequeue_buffer(tx_queue, buffer, NULL, NULL); } } /* Parse the SKB header and initialise state. */ -static void tso_start(struct tso_state *st, const struct sk_buff *skb) +static int tso_start(struct tso_state *st, struct efx_nic *efx, + const struct sk_buff *skb) { + bool use_options = efx_nic_rev(efx) >= EFX_REV_HUNT_A0; + struct device *dma_dev = &efx->pci_dev->dev; + unsigned int header_len, in_len; + dma_addr_t dma_addr; + st->ip_off = skb_network_header(skb) - skb->data; st->tcp_off = skb_transport_header(skb) - skb->data; - st->header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u); + header_len = st->tcp_off + (tcp_hdr(skb)->doff << 2u); + in_len = skb_headlen(skb) - header_len; + st->header_len = header_len; + st->in_len = in_len; if (st->protocol == htons(ETH_P_IP)) { st->ip_base_len = st->header_len - st->ip_off; st->ipv4_id = ntohs(ip_hdr(skb)->id); @@ -840,9 +1033,34 @@ static void tso_start(struct tso_state *st, const struct sk_buff *skb) EFX_BUG_ON_PARANOID(tcp_hdr(skb)->syn); EFX_BUG_ON_PARANOID(tcp_hdr(skb)->rst); - st->out_len = skb->len - st->header_len; - st->unmap_len = 0; - st->dma_flags = 0; + st->out_len = skb->len - header_len; + + if (!use_options) { + st->header_unmap_len = 0; + + if (likely(in_len == 0)) { + st->dma_flags = 0; + st->unmap_len = 0; + return 0; + } + + dma_addr = dma_map_single(dma_dev, skb->data + header_len, + in_len, DMA_TO_DEVICE); + st->dma_flags = EFX_TX_BUF_MAP_SINGLE; + st->dma_addr = dma_addr; + st->unmap_addr = dma_addr; + st->unmap_len = in_len; + } else { + dma_addr = dma_map_single(dma_dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); + st->header_dma_addr = dma_addr; + st->header_unmap_len = skb_headlen(skb); + st->dma_flags = 0; + st->dma_addr = dma_addr + header_len; + st->unmap_len = 0; + } + + return unlikely(dma_mapping_error(dma_dev, dma_addr)) ? -ENOMEM : 0; } static int tso_get_fragment(struct tso_state *st, struct efx_nic *efx, @@ -860,24 +1078,6 @@ static int tso_get_fragment(struct tso_state *st, struct efx_nic *efx, return -ENOMEM; } -static int tso_get_head_fragment(struct tso_state *st, struct efx_nic *efx, - const struct sk_buff *skb) -{ - int hl = st->header_len; - int len = skb_headlen(skb) - hl; - - st->unmap_addr = dma_map_single(&efx->pci_dev->dev, skb->data + hl, - len, DMA_TO_DEVICE); - if (likely(!dma_mapping_error(&efx->pci_dev->dev, st->unmap_addr))) { - st->dma_flags = EFX_TX_BUF_MAP_SINGLE; - st->unmap_len = len; - st->in_len = len; - st->dma_addr = st->unmap_addr; - return 0; - } - return -ENOMEM; -} - /** * tso_fill_packet_with_fragment - form descriptors for the current fragment @@ -922,6 +1122,7 @@ static void tso_fill_packet_with_fragment(struct efx_tx_queue *tx_queue, if (st->in_len == 0) { /* Transfer ownership of the DMA mapping */ buffer->unmap_len = st->unmap_len; + buffer->dma_offset = buffer->unmap_len - buffer->len; buffer->flags |= st->dma_flags; st->unmap_len = 0; } @@ -944,55 +1145,98 @@ static int tso_start_new_packet(struct efx_tx_queue *tx_queue, struct tso_state *st) { struct efx_tx_buffer *buffer = - &tx_queue->buffer[tx_queue->insert_count & tx_queue->ptr_mask]; - struct tcphdr *tsoh_th; - unsigned ip_length; - u8 *header; - int rc; + efx_tx_queue_get_insert_buffer(tx_queue); + bool is_last = st->out_len <= skb_shinfo(skb)->gso_size; + u8 tcp_flags_clear; - /* Allocate and insert a DMA-mapped header buffer. */ - header = efx_tsoh_get_buffer(tx_queue, buffer, st->header_len); - if (!header) - return -ENOMEM; - - tsoh_th = (struct tcphdr *)(header + st->tcp_off); - - /* Copy and update the headers. */ - memcpy(header, skb->data, st->header_len); - - tsoh_th->seq = htonl(st->seqnum); - st->seqnum += skb_shinfo(skb)->gso_size; - if (st->out_len > skb_shinfo(skb)->gso_size) { - /* This packet will not finish the TSO burst. */ + if (!is_last) { st->packet_space = skb_shinfo(skb)->gso_size; - tsoh_th->fin = 0; - tsoh_th->psh = 0; + tcp_flags_clear = 0x09; /* mask out FIN and PSH */ } else { - /* This packet will be the last in the TSO burst. */ st->packet_space = st->out_len; - tsoh_th->fin = tcp_hdr(skb)->fin; - tsoh_th->psh = tcp_hdr(skb)->psh; + tcp_flags_clear = 0x00; } - ip_length = st->ip_base_len + st->packet_space; - if (st->protocol == htons(ETH_P_IP)) { - struct iphdr *tsoh_iph = (struct iphdr *)(header + st->ip_off); + if (!st->header_unmap_len) { + /* Allocate and insert a DMA-mapped header buffer. */ + struct tcphdr *tsoh_th; + unsigned ip_length; + u8 *header; + int rc; + + header = efx_tsoh_get_buffer(tx_queue, buffer, st->header_len); + if (!header) + return -ENOMEM; - tsoh_iph->tot_len = htons(ip_length); + tsoh_th = (struct tcphdr *)(header + st->tcp_off); + + /* Copy and update the headers. */ + memcpy(header, skb->data, st->header_len); + + tsoh_th->seq = htonl(st->seqnum); + ((u8 *)tsoh_th)[13] &= ~tcp_flags_clear; + + ip_length = st->ip_base_len + st->packet_space; + + if (st->protocol == htons(ETH_P_IP)) { + struct iphdr *tsoh_iph = + (struct iphdr *)(header + st->ip_off); + + tsoh_iph->tot_len = htons(ip_length); + tsoh_iph->id = htons(st->ipv4_id); + } else { + struct ipv6hdr *tsoh_iph = + (struct ipv6hdr *)(header + st->ip_off); + + tsoh_iph->payload_len = htons(ip_length); + } - /* Linux leaves suitable gaps in the IP ID space for us to fill. */ - tsoh_iph->id = htons(st->ipv4_id); - st->ipv4_id++; + rc = efx_tso_put_header(tx_queue, buffer, header); + if (unlikely(rc)) + return rc; } else { - struct ipv6hdr *tsoh_iph = - (struct ipv6hdr *)(header + st->ip_off); + /* Send the original headers with a TSO option descriptor + * in front + */ + u8 tcp_flags = ((u8 *)tcp_hdr(skb))[13] & ~tcp_flags_clear; - tsoh_iph->payload_len = htons(ip_length); + buffer->flags = EFX_TX_BUF_OPTION; + buffer->len = 0; + buffer->unmap_len = 0; + EFX_POPULATE_QWORD_5(buffer->option, + ESF_DZ_TX_DESC_IS_OPT, 1, + ESF_DZ_TX_OPTION_TYPE, + ESE_DZ_TX_OPTION_DESC_TSO, + ESF_DZ_TX_TSO_TCP_FLAGS, tcp_flags, + ESF_DZ_TX_TSO_IP_ID, st->ipv4_id, + ESF_DZ_TX_TSO_TCP_SEQNO, st->seqnum); + ++tx_queue->insert_count; + + /* We mapped the headers in tso_start(). Unmap them + * when the last segment is completed. + */ + buffer = efx_tx_queue_get_insert_buffer(tx_queue); + buffer->dma_addr = st->header_dma_addr; + buffer->len = st->header_len; + if (is_last) { + buffer->flags = EFX_TX_BUF_CONT | EFX_TX_BUF_MAP_SINGLE; + buffer->unmap_len = st->header_unmap_len; + buffer->dma_offset = 0; + /* Ensure we only unmap them once in case of a + * later DMA mapping error and rollback + */ + st->header_unmap_len = 0; + } else { + buffer->flags = EFX_TX_BUF_CONT; + buffer->unmap_len = 0; + } + ++tx_queue->insert_count; } - rc = efx_tso_put_header(tx_queue, buffer, header); - if (unlikely(rc)) - return rc; + st->seqnum += skb_shinfo(skb)->gso_size; + + /* Linux leaves suitable gaps in the IP ID space for us to fill. */ + ++st->ipv4_id; ++tx_queue->tso_packets; @@ -1023,12 +1267,11 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, EFX_BUG_ON_PARANOID(tx_queue->write_count != tx_queue->insert_count); - tso_start(&state, skb); + rc = tso_start(&state, efx, skb); + if (rc) + goto mem_err; - /* Assume that skb header area contains exactly the headers, and - * all payload is in the frag list. - */ - if (skb_headlen(skb) == state.header_len) { + if (likely(state.in_len == 0)) { /* Grab the first payload fragment. */ EFX_BUG_ON_PARANOID(skb_shinfo(skb)->nr_frags < 1); frag_i = 0; @@ -1037,9 +1280,7 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, if (rc) goto mem_err; } else { - rc = tso_get_head_fragment(&state, efx, skb); - if (rc) - goto mem_err; + /* Payload starts in the header area. */ frag_i = -1; } @@ -1091,6 +1332,11 @@ static int efx_enqueue_skb_tso(struct efx_tx_queue *tx_queue, state.unmap_len, DMA_TO_DEVICE); } + /* Free the header DMA mapping, if using option descriptors */ + if (state.header_unmap_len) + dma_unmap_single(&efx->pci_dev->dev, state.header_dma_addr, + state.header_unmap_len, DMA_TO_DEVICE); + efx_enqueue_unwind(tx_queue); return NETDEV_TX_OK; } diff --git a/drivers/net/ethernet/sgi/meth.c b/drivers/net/ethernet/sgi/meth.c index 770036bc2d87c9fde8335241a43b52a5df581a6f..513ed8b1ba582add37602ab4c93faaf462c7f3a3 100644 --- a/drivers/net/ethernet/sgi/meth.c +++ b/drivers/net/ethernet/sgi/meth.c @@ -839,7 +839,7 @@ static int meth_probe(struct platform_device *pdev) dev->watchdog_timeo = timeout; dev->irq = MACE_ETHERNET_IRQ; dev->base_addr = (unsigned long)&mace->eth; - memcpy(dev->dev_addr, o2meth_eaddr, 6); + memcpy(dev->dev_addr, o2meth_eaddr, ETH_ALEN); priv = netdev_priv(dev); spin_lock_init(&priv->meth_lock); diff --git a/drivers/net/ethernet/sis/sis190.c b/drivers/net/ethernet/sis/sis190.c index ee18e6f7b4fe19d9b8e3bbec69fd5e2cd098cb81..acbbe48a519c0c673ff8de55c31b1690c3976cb5 100644 --- a/drivers/net/ethernet/sis/sis190.c +++ b/drivers/net/ethernet/sis/sis190.c @@ -1921,7 +1921,6 @@ static void sis190_remove_one(struct pci_dev *pdev) cancel_work_sync(&tp->phy_task); unregister_netdev(dev); sis190_release_board(pdev); - pci_set_drvdata(pdev, NULL); } static struct pci_driver sis190_pci_driver = { diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig index 068fc44d37e1d0723849da40ade4d77671be0917..753630f5d3d35d15ad0d596ccff5c082bcc7bbbe 100644 --- a/drivers/net/ethernet/smsc/Kconfig +++ b/drivers/net/ethernet/smsc/Kconfig @@ -6,7 +6,7 @@ config NET_VENDOR_SMSC bool "SMC (SMSC)/Western Digital devices" default y depends on ARM || ISA || MAC || ARM64 || MIPS || M32R || SUPERH || \ - BLACKFIN || MN10300 || COLDFIRE || PCI || PCMCIA + BLACKFIN || MN10300 || COLDFIRE || XTENSA || PCI || PCMCIA ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -39,7 +39,7 @@ config SMC91X select CRC32 select MII depends on (ARM || M32R || SUPERH || MIPS || BLACKFIN || \ - MN10300 || COLDFIRE || ARM64) + MN10300 || COLDFIRE || ARM64 || XTENSA) ---help--- This is a driver for SMC's 91x series of Ethernet chipsets, including the SMC91C94 and the SMC91C111. Say Y if you want it diff --git a/drivers/net/ethernet/smsc/epic100.c b/drivers/net/ethernet/smsc/epic100.c index 03b256af7ed5f8c68a75a47cea63cf8f6bc7bb2e..8ae1f8a7bf38f34d032ba717478f863f4aa6a267 100644 --- a/drivers/net/ethernet/smsc/epic100.c +++ b/drivers/net/ethernet/smsc/epic100.c @@ -91,9 +91,9 @@ static int rx_copybreak; /* These identify the driver base version and may not be removed. */ static char version[] = -DRV_NAME ".c:v1.11 1/7/2001 Written by Donald Becker \n"; +DRV_NAME ".c:v1.11 1/7/2001 Written by Donald Becker "; static char version2[] = -" (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE ")\n"; +" (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE ")"; MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("SMC 83c170 EPIC series Ethernet driver"); @@ -332,9 +332,7 @@ static int epic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) /* when built into the kernel, we only print version if device is found */ #ifndef MODULE - static int printed_version; - if (!printed_version++) - printk(KERN_INFO "%s%s", version, version2); + pr_info_once("%s%s\n", version, version2); #endif card_idx++; @@ -423,9 +421,9 @@ static int epic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) ((__le16 *)dev->dev_addr)[i] = cpu_to_le16(er16(LAN0 + i*4)); if (debug > 2) { - dev_printk(KERN_DEBUG, &pdev->dev, "EEPROM contents:\n"); + dev_dbg(&pdev->dev, "EEPROM contents:\n"); for (i = 0; i < 64; i++) - printk(" %4.4x%s", read_eeprom(ep, i), + pr_cont(" %4.4x%s", read_eeprom(ep, i), i % 16 == 15 ? "\n" : ""); } @@ -490,10 +488,10 @@ static int epic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret < 0) goto err_out_unmap_rx; - printk(KERN_INFO "%s: %s at %lx, IRQ %d, %pM\n", - dev->name, pci_id_tbl[chip_idx].name, - (long)pci_resource_start(pdev, EPIC_BAR), pdev->irq, - dev->dev_addr); + netdev_info(dev, "%s at %lx, IRQ %d, %pM\n", + pci_id_tbl[chip_idx].name, + (long)pci_resource_start(pdev, EPIC_BAR), pdev->irq, + dev->dev_addr); out: return ret; @@ -703,9 +701,8 @@ static int epic_open(struct net_device *dev) mdio_write(dev, ep->phys[0], MII_BMCR, media2miictl[dev->if_port&15]); if (dev->if_port == 1) { if (debug > 1) - printk(KERN_INFO "%s: Using the 10base2 transceiver, MII " - "status %4.4x.\n", - dev->name, mdio_read(dev, ep->phys[0], MII_BMSR)); + netdev_info(dev, "Using the 10base2 transceiver, MII status %4.4x.\n", + mdio_read(dev, ep->phys[0], MII_BMSR)); } } else { int mii_lpa = mdio_read(dev, ep->phys[0], MII_LPA); @@ -715,10 +712,10 @@ static int epic_open(struct net_device *dev) else if (! (mii_lpa & LPA_LPACK)) mdio_write(dev, ep->phys[0], MII_BMCR, BMCR_ANENABLE|BMCR_ANRESTART); if (debug > 1) - printk(KERN_INFO "%s: Setting %s-duplex based on MII xcvr %d" - " register read of %4.4x.\n", dev->name, - ep->mii.full_duplex ? "full" : "half", - ep->phys[0], mii_lpa); + netdev_info(dev, "Setting %s-duplex based on MII xcvr %d register read of %4.4x.\n", + ep->mii.full_duplex ? "full" + : "half", + ep->phys[0], mii_lpa); } } @@ -738,10 +735,9 @@ static int epic_open(struct net_device *dev) TxUnderrun); if (debug > 1) { - printk(KERN_DEBUG "%s: epic_open() ioaddr %p IRQ %d " - "status %4.4x %s-duplex.\n", - dev->name, ioaddr, irq, er32(GENCTL), - ep->mii.full_duplex ? "full" : "half"); + netdev_dbg(dev, "epic_open() ioaddr %p IRQ %d status %4.4x %s-duplex.\n", + ioaddr, irq, er32(GENCTL), + ep->mii.full_duplex ? "full" : "half"); } /* Set the timer to switch to check for link beat and perhaps switch @@ -790,8 +786,8 @@ static void epic_restart(struct net_device *dev) /* Soft reset the chip. */ ew32(GENCTL, 0x4001); - printk(KERN_DEBUG "%s: Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n", - dev->name, ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx); + netdev_dbg(dev, "Restarting the EPIC chip, Rx %d/%d Tx %d/%d.\n", + ep->cur_rx, ep->dirty_rx, ep->dirty_tx, ep->cur_tx); udelay(1); /* This magic is documented in SMSC app note 7.15 */ @@ -827,9 +823,8 @@ static void epic_restart(struct net_device *dev) ((ep->chip_flags & TYPE2_INTR) ? PCIBusErr175 : PCIBusErr170) | TxUnderrun); - printk(KERN_DEBUG "%s: epic_restart() done, cmd status %4.4x, ctl %4.4x" - " interrupt %4.4x.\n", - dev->name, er32(COMMAND), er32(GENCTL), er32(INTSTAT)); + netdev_dbg(dev, "epic_restart() done, cmd status %4.4x, ctl %4.4x interrupt %4.4x.\n", + er32(COMMAND), er32(GENCTL), er32(INTSTAT)); } static void check_media(struct net_device *dev) @@ -846,9 +841,9 @@ static void check_media(struct net_device *dev) return; if (ep->mii.full_duplex != duplex) { ep->mii.full_duplex = duplex; - printk(KERN_INFO "%s: Setting %s-duplex based on MII #%d link" - " partner capability of %4.4x.\n", dev->name, - ep->mii.full_duplex ? "full" : "half", ep->phys[0], mii_lpa); + netdev_info(dev, "Setting %s-duplex based on MII #%d link partner capability of %4.4x.\n", + ep->mii.full_duplex ? "full" : "half", + ep->phys[0], mii_lpa); ew32(TxCtrl, ep->mii.full_duplex ? 0x7F : 0x79); } } @@ -861,11 +856,10 @@ static void epic_timer(unsigned long data) int next_tick = 5*HZ; if (debug > 3) { - printk(KERN_DEBUG "%s: Media monitor tick, Tx status %8.8x.\n", - dev->name, er32(TxSTAT)); - printk(KERN_DEBUG "%s: Other registers are IntMask %4.4x " - "IntStatus %4.4x RxStatus %4.4x.\n", dev->name, - er32(INTMASK), er32(INTSTAT), er32(RxSTAT)); + netdev_dbg(dev, "Media monitor tick, Tx status %8.8x.\n", + er32(TxSTAT)); + netdev_dbg(dev, "Other registers are IntMask %4.4x IntStatus %4.4x RxStatus %4.4x.\n", + er32(INTMASK), er32(INTSTAT), er32(RxSTAT)); } check_media(dev); @@ -880,11 +874,11 @@ static void epic_tx_timeout(struct net_device *dev) void __iomem *ioaddr = ep->ioaddr; if (debug > 0) { - printk(KERN_WARNING "%s: Transmit timeout using MII device, " - "Tx status %4.4x.\n", dev->name, er16(TxSTAT)); + netdev_warn(dev, "Transmit timeout using MII device, Tx status %4.4x.\n", + er16(TxSTAT)); if (debug > 1) { - printk(KERN_DEBUG "%s: Tx indices: dirty_tx %d, cur_tx %d.\n", - dev->name, ep->dirty_tx, ep->cur_tx); + netdev_dbg(dev, "Tx indices: dirty_tx %d, cur_tx %d.\n", + ep->dirty_tx, ep->cur_tx); } } if (er16(TxSTAT) & 0x10) { /* Tx FIFO underflow. */ @@ -994,9 +988,8 @@ static netdev_tx_t epic_start_xmit(struct sk_buff *skb, struct net_device *dev) ew32(COMMAND, TxQueued); if (debug > 4) - printk(KERN_DEBUG "%s: Queued Tx packet size %d to slot %d, " - "flag %2.2x Tx status %8.8x.\n", dev->name, skb->len, - entry, ctrl_word, er32(TxSTAT)); + netdev_dbg(dev, "Queued Tx packet size %d to slot %d, flag %2.2x Tx status %8.8x.\n", + skb->len, entry, ctrl_word, er32(TxSTAT)); return NETDEV_TX_OK; } @@ -1009,8 +1002,8 @@ static void epic_tx_error(struct net_device *dev, struct epic_private *ep, #ifndef final_version /* There was an major error, log it. */ if (debug > 1) - printk(KERN_DEBUG "%s: Transmit error, Tx status %8.8x.\n", - dev->name, status); + netdev_dbg(dev, "Transmit error, Tx status %8.8x.\n", + status); #endif stats->tx_errors++; if (status & 0x1050) @@ -1057,9 +1050,8 @@ static void epic_tx(struct net_device *dev, struct epic_private *ep) #ifndef final_version if (cur_tx - dirty_tx > TX_RING_SIZE) { - printk(KERN_WARNING - "%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", - dev->name, dirty_tx, cur_tx, ep->tx_full); + netdev_warn(dev, "Out-of-sync dirty pointer, %d vs. %d, full=%d.\n", + dirty_tx, cur_tx, ep->tx_full); dirty_tx += TX_RING_SIZE; } #endif @@ -1086,8 +1078,8 @@ static irqreturn_t epic_interrupt(int irq, void *dev_instance) ew32(INTSTAT, status & EpicNormalEvent); if (debug > 4) { - printk(KERN_DEBUG "%s: Interrupt, status=%#8.8x new " - "intstat=%#8.8x.\n", dev->name, status, er32(INTSTAT)); + netdev_dbg(dev, "Interrupt, status=%#8.8x new intstat=%#8.8x.\n", + status, er32(INTSTAT)); } if ((status & IntrSummary) == 0) @@ -1125,8 +1117,8 @@ static irqreturn_t epic_interrupt(int irq, void *dev_instance) ew32(COMMAND, RestartTx); } if (status & PCIBusErr170) { - printk(KERN_ERR "%s: PCI Bus Error! status %4.4x.\n", - dev->name, status); + netdev_err(dev, "PCI Bus Error! status %4.4x.\n", + status); epic_pause(dev); epic_restart(dev); } @@ -1136,8 +1128,8 @@ static irqreturn_t epic_interrupt(int irq, void *dev_instance) out: if (debug > 3) { - printk(KERN_DEBUG "%s: exit interrupt, intr_status=%#4.4x.\n", - dev->name, status); + netdev_dbg(dev, "exit interrupt, intr_status=%#4.4x.\n", + status); } return IRQ_RETVAL(handled); @@ -1151,7 +1143,7 @@ static int epic_rx(struct net_device *dev, int budget) int work_done = 0; if (debug > 4) - printk(KERN_DEBUG " In epic_rx(), entry %d %8.8x.\n", entry, + netdev_dbg(dev, " In epic_rx(), entry %d %8.8x.\n", entry, ep->rx_ring[entry].rxstatus); if (rx_work_limit > budget) @@ -1162,16 +1154,17 @@ static int epic_rx(struct net_device *dev, int budget) int status = ep->rx_ring[entry].rxstatus; if (debug > 4) - printk(KERN_DEBUG " epic_rx() status was %8.8x.\n", status); + netdev_dbg(dev, " epic_rx() status was %8.8x.\n", + status); if (--rx_work_limit < 0) break; if (status & 0x2006) { if (debug > 2) - printk(KERN_DEBUG "%s: epic_rx() error status was %8.8x.\n", - dev->name, status); + netdev_dbg(dev, "epic_rx() error status was %8.8x.\n", + status); if (status & 0x2000) { - printk(KERN_WARNING "%s: Oversized Ethernet frame spanned " - "multiple buffers, status %4.4x!\n", dev->name, status); + netdev_warn(dev, "Oversized Ethernet frame spanned multiple buffers, status %4.4x!\n", + status); dev->stats.rx_length_errors++; } else if (status & 0x0006) /* Rx Frame errors are counted in hardware. */ @@ -1183,9 +1176,8 @@ static int epic_rx(struct net_device *dev, int budget) struct sk_buff *skb; if (pkt_len > PKT_BUF_SZ - 4) { - printk(KERN_ERR "%s: Oversized Ethernet frame, status %x " - "%d bytes.\n", - dev->name, status, pkt_len); + netdev_err(dev, "Oversized Ethernet frame, status %x %d bytes.\n", + status, pkt_len); pkt_len = 1514; } /* Check if the packet is long enough to accept without copying @@ -1305,8 +1297,8 @@ static int epic_close(struct net_device *dev) napi_disable(&ep->napi); if (debug > 1) - printk(KERN_DEBUG "%s: Shutting down ethercard, status was %2.2x.\n", - dev->name, er32(INTSTAT)); + netdev_dbg(dev, "Shutting down ethercard, status was %2.2x.\n", + er32(INTSTAT)); del_timer_sync(&ep->timer); @@ -1324,7 +1316,7 @@ static int epic_close(struct net_device *dev) ep->rx_ring[i].buflength = 0; if (skb) { pci_unmap_single(pdev, ep->rx_ring[i].bufaddr, - ep->rx_buf_sz, PCI_DMA_FROMDEVICE); + ep->rx_buf_sz, PCI_DMA_FROMDEVICE); dev_kfree_skb(skb); } ep->rx_ring[i].bufaddr = 0xBADF00D0; /* An invalid address. */ @@ -1535,7 +1527,6 @@ static void epic_remove_one(struct pci_dev *pdev) pci_release_regions(pdev); free_netdev(dev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); /* pci_power_off(pdev, -1); */ } @@ -1588,8 +1579,7 @@ static int __init epic_init (void) { /* when a module, this is printed whether or not devices are found in probe */ #ifdef MODULE - printk (KERN_INFO "%s%s", - version, version2); + pr_info("%s%s\n", version, version2); #endif return pci_register_driver(&epic_driver); diff --git a/drivers/net/ethernet/smsc/smc911x.c b/drivers/net/ethernet/smsc/smc911x.c index afe01c4088a314aad26e73d5cda7a1f129700ca2..0f096a890059255170976ee78a3f169cdafb7c90 100644 --- a/drivers/net/ethernet/smsc/smc911x.c +++ b/drivers/net/ethernet/smsc/smc911x.c @@ -106,16 +106,16 @@ MODULE_ALIAS("platform:smc911x"); #define POWER_DOWN 1 #if SMC_DEBUG > 0 -#define DBG(n, args...) \ +#define DBG(n, dev, args...) \ do { \ if (SMC_DEBUG & (n)) \ - printk(args); \ + netdev_dbg(dev, args); \ } while (0) -#define PRINTK(args...) printk(args) +#define PRINTK(dev, args...) netdev_info(dev, args) #else -#define DBG(n, args...) do { } while (0) -#define PRINTK(args...) printk(KERN_DEBUG args) +#define DBG(n, dev, args...) do { } while (0) +#define PRINTK(dev, args...) netdev_dbg(dev, args) #endif #if SMC_DEBUG_PKTS > 0 @@ -130,21 +130,23 @@ static void PRINT_PKT(u_char *buf, int length) for (i = 0; i < lines ; i ++) { int cur; + printk(KERN_DEBUG); for (cur = 0; cur < 8; cur++) { u_char a, b; a = *buf++; b = *buf++; - printk("%02x%02x ", a, b); + pr_cont("%02x%02x ", a, b); } - printk("\n"); + pr_cont("\n"); } + printk(KERN_DEBUG); for (i = 0; i < remainder/2 ; i++) { u_char a, b; a = *buf++; b = *buf++; - printk("%02x%02x ", a, b); + pr_cont("%02x%02x ", a, b); } - printk("\n"); + pr_cont("\n"); } #else #define PRINT_PKT(x...) do { } while (0) @@ -176,7 +178,7 @@ static void smc911x_reset(struct net_device *dev) unsigned int reg, timeout=0, resets=1, irq_cfg; unsigned long flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); /* Take out of PM setting first */ if ((SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_) == 0) { @@ -188,7 +190,7 @@ static void smc911x_reset(struct net_device *dev) reg = SMC_GET_PMT_CTRL(lp) & PMT_CTRL_READY_; } while (--timeout && !reg); if (timeout == 0) { - PRINTK("%s: smc911x_reset timeout waiting for PM restore\n", dev->name); + PRINTK(dev, "smc911x_reset timeout waiting for PM restore\n"); return; } } @@ -206,14 +208,14 @@ static void smc911x_reset(struct net_device *dev) reg = SMC_GET_HW_CFG(lp); /* If chip indicates reset timeout then try again */ if (reg & HW_CFG_SRST_TO_) { - PRINTK("%s: chip reset timeout, retrying...\n", dev->name); + PRINTK(dev, "chip reset timeout, retrying...\n"); resets++; break; } } while (--timeout && (reg & HW_CFG_SRST_)); } if (timeout == 0) { - PRINTK("%s: smc911x_reset timeout waiting for reset\n", dev->name); + PRINTK(dev, "smc911x_reset timeout waiting for reset\n"); return; } @@ -223,7 +225,7 @@ static void smc911x_reset(struct net_device *dev) udelay(10); if (timeout == 0){ - PRINTK("%s: smc911x_reset timeout waiting for EEPROM busy\n", dev->name); + PRINTK(dev, "smc911x_reset timeout waiting for EEPROM busy\n"); return; } @@ -270,7 +272,7 @@ static void smc911x_enable(struct net_device *dev) unsigned mask, cfg, cr; unsigned long flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); spin_lock_irqsave(&lp->lock, flags); @@ -296,7 +298,7 @@ static void smc911x_enable(struct net_device *dev) /* Turn on receiver and enable RX */ if (cr & MAC_CR_RXEN_) - DBG(SMC_DEBUG_RX, "%s: Receiver already enabled\n", dev->name); + DBG(SMC_DEBUG_RX, dev, "Receiver already enabled\n"); SMC_SET_MAC_CR(lp, cr | MAC_CR_RXEN_); @@ -327,7 +329,7 @@ static void smc911x_shutdown(struct net_device *dev) unsigned cr; unsigned long flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", CARDNAME, __func__); + DBG(SMC_DEBUG_FUNC, dev, "%s: --> %s\n", CARDNAME, __func__); /* Disable IRQ's */ SMC_SET_INT_EN(lp, 0); @@ -346,7 +348,8 @@ static inline void smc911x_drop_pkt(struct net_device *dev) struct smc911x_local *lp = netdev_priv(dev); unsigned int fifo_count, timeout, reg; - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n", CARDNAME, __func__); + DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, dev, "%s: --> %s\n", + CARDNAME, __func__); fifo_count = SMC_GET_RX_FIFO_INF(lp) & 0xFFFF; if (fifo_count <= 4) { /* Manually dump the packet data */ @@ -361,7 +364,7 @@ static inline void smc911x_drop_pkt(struct net_device *dev) reg = SMC_GET_RX_DP_CTRL(lp) & RX_DP_CTRL_FFWD_BUSY_; } while (--timeout && reg); if (timeout == 0) { - PRINTK("%s: timeout waiting for RX fast forward\n", dev->name); + PRINTK(dev, "timeout waiting for RX fast forward\n"); } } } @@ -379,11 +382,11 @@ static inline void smc911x_rcv(struct net_device *dev) struct sk_buff *skb; unsigned char *data; - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, "%s: --> %s\n", - dev->name, __func__); + DBG(SMC_DEBUG_FUNC | SMC_DEBUG_RX, dev, "--> %s\n", + __func__); status = SMC_GET_RX_STS_FIFO(lp); - DBG(SMC_DEBUG_RX, "%s: Rx pkt len %d status 0x%08x\n", - dev->name, (status & 0x3fff0000) >> 16, status & 0xc000ffff); + DBG(SMC_DEBUG_RX, dev, "Rx pkt len %d status 0x%08x\n", + (status & 0x3fff0000) >> 16, status & 0xc000ffff); pkt_len = (status & RX_STS_PKT_LEN_) >> 16; if (status & RX_STS_ES_) { /* Deal with a bad packet */ @@ -403,8 +406,7 @@ static inline void smc911x_rcv(struct net_device *dev) /* Alloc a buffer with extra room for DMA alignment */ skb = netdev_alloc_skb(dev, pkt_len+32); if (unlikely(skb == NULL)) { - PRINTK( "%s: Low memory, rcvd packet dropped.\n", - dev->name); + PRINTK(dev, "Low memory, rcvd packet dropped.\n"); dev->stats.rx_dropped++; smc911x_drop_pkt(dev); return; @@ -422,8 +424,8 @@ static inline void smc911x_rcv(struct net_device *dev) /* Lower the FIFO threshold if possible */ fifo = SMC_GET_FIFO_INT(lp); if (fifo & 0xFF) fifo--; - DBG(SMC_DEBUG_RX, "%s: Setting RX stat FIFO threshold to %d\n", - dev->name, fifo & 0xff); + DBG(SMC_DEBUG_RX, dev, "Setting RX stat FIFO threshold to %d\n", + fifo & 0xff); SMC_SET_FIFO_INT(lp, fifo); /* Setup RX DMA */ SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN16_ | ((2<<8) & RX_CFG_RXDOFF_)); @@ -436,7 +438,7 @@ static inline void smc911x_rcv(struct net_device *dev) SMC_SET_RX_CFG(lp, RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_)); SMC_PULL_DATA(lp, data, pkt_len+2+3); - DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name); + DBG(SMC_DEBUG_PKTS, dev, "Received packet\n"); PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); @@ -456,7 +458,7 @@ static void smc911x_hardware_send_pkt(struct net_device *dev) unsigned int cmdA, cmdB, len; unsigned char *buf; - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, dev, "--> %s\n", __func__); BUG_ON(lp->pending_tx_skb == NULL); skb = lp->pending_tx_skb; @@ -481,12 +483,12 @@ static void smc911x_hardware_send_pkt(struct net_device *dev) /* tag is packet length so we can use this in stats update later */ cmdB = (skb->len << 16) | (skb->len & 0x7FF); - DBG(SMC_DEBUG_TX, "%s: TX PKT LENGTH 0x%04x (%d) BUF 0x%p CMDA 0x%08x CMDB 0x%08x\n", - dev->name, len, len, buf, cmdA, cmdB); + DBG(SMC_DEBUG_TX, dev, "TX PKT LENGTH 0x%04x (%d) BUF 0x%p CMDA 0x%08x CMDB 0x%08x\n", + len, len, buf, cmdA, cmdB); SMC_SET_TX_FIFO(lp, cmdA); SMC_SET_TX_FIFO(lp, cmdB); - DBG(SMC_DEBUG_PKTS, "%s: Transmitted packet\n", dev->name); + DBG(SMC_DEBUG_PKTS, dev, "Transmitted packet\n"); PRINT_PKT(buf, len <= 64 ? len : 64); /* Send pkt via PIO or DMA */ @@ -517,20 +519,20 @@ static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) unsigned int free; unsigned long flags; - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", - dev->name, __func__); + DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, dev, "--> %s\n", + __func__); spin_lock_irqsave(&lp->lock, flags); BUG_ON(lp->pending_tx_skb != NULL); free = SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TDFREE_; - DBG(SMC_DEBUG_TX, "%s: TX free space %d\n", dev->name, free); + DBG(SMC_DEBUG_TX, dev, "TX free space %d\n", free); /* Turn off the flow when running out of space in FIFO */ if (free <= SMC911X_TX_FIFO_LOW_THRESHOLD) { - DBG(SMC_DEBUG_TX, "%s: Disabling data flow due to low FIFO space (%d)\n", - dev->name, free); + DBG(SMC_DEBUG_TX, dev, "Disabling data flow due to low FIFO space (%d)\n", + free); /* Reenable when at least 1 packet of size MTU present */ SMC_SET_FIFO_TDA(lp, (SMC911X_TX_FIFO_LOW_THRESHOLD)/64); lp->tx_throttle = 1; @@ -545,8 +547,8 @@ static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) * End padding 15 bytes */ if (unlikely(free < (skb->len + 8 + 15 + 15))) { - printk("%s: No Tx free space %d < %d\n", - dev->name, free, skb->len); + netdev_warn(dev, "No Tx free space %d < %d\n", + free, skb->len); lp->pending_tx_skb = NULL; dev->stats.tx_errors++; dev->stats.tx_dropped++; @@ -561,13 +563,13 @@ static int smc911x_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) * the DMA IRQ starts it */ if (lp->txdma_active) { - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Tx DMA running, deferring packet\n", dev->name); + DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "Tx DMA running, deferring packet\n"); lp->pending_tx_skb = skb; netif_stop_queue(dev); spin_unlock_irqrestore(&lp->lock, flags); return NETDEV_TX_OK; } else { - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: Activating Tx DMA\n", dev->name); + DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "Activating Tx DMA\n"); lp->txdma_active = 1; } } @@ -589,20 +591,19 @@ static void smc911x_tx(struct net_device *dev) struct smc911x_local *lp = netdev_priv(dev); unsigned int tx_status; - DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, "%s: --> %s\n", - dev->name, __func__); + DBG(SMC_DEBUG_FUNC | SMC_DEBUG_TX, dev, "--> %s\n", + __func__); /* Collect the TX status */ while (((SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16) != 0) { - DBG(SMC_DEBUG_TX, "%s: Tx stat FIFO used 0x%04x\n", - dev->name, - (SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16); + DBG(SMC_DEBUG_TX, dev, "Tx stat FIFO used 0x%04x\n", + (SMC_GET_TX_FIFO_INF(lp) & TX_FIFO_INF_TSUSED_) >> 16); tx_status = SMC_GET_TX_STS_FIFO(lp); dev->stats.tx_packets++; dev->stats.tx_bytes+=tx_status>>16; - DBG(SMC_DEBUG_TX, "%s: Tx FIFO tag 0x%04x status 0x%04x\n", - dev->name, (tx_status & 0xffff0000) >> 16, - tx_status & 0x0000ffff); + DBG(SMC_DEBUG_TX, dev, "Tx FIFO tag 0x%04x status 0x%04x\n", + (tx_status & 0xffff0000) >> 16, + tx_status & 0x0000ffff); /* count Tx errors, but ignore lost carrier errors when in * full-duplex mode */ if ((tx_status & TX_STS_ES_) && !(lp->ctl_rfduplx && @@ -640,8 +641,8 @@ static int smc911x_phy_read(struct net_device *dev, int phyaddr, int phyreg) SMC_GET_MII(lp, phyreg, phyaddr, phydata); - DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n", - __func__, phyaddr, phyreg, phydata); + DBG(SMC_DEBUG_MISC, dev, "%s: phyaddr=0x%x, phyreg=0x%02x, phydata=0x%04x\n", + __func__, phyaddr, phyreg, phydata); return phydata; } @@ -654,8 +655,8 @@ static void smc911x_phy_write(struct net_device *dev, int phyaddr, int phyreg, { struct smc911x_local *lp = netdev_priv(dev); - DBG(SMC_DEBUG_MISC, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", - __func__, phyaddr, phyreg, phydata); + DBG(SMC_DEBUG_MISC, dev, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", + __func__, phyaddr, phyreg, phydata); SMC_SET_MII(lp, phyreg, phyaddr, phydata); } @@ -670,7 +671,7 @@ static void smc911x_phy_detect(struct net_device *dev) int phyaddr; unsigned int cfg, id1, id2; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); lp->phy_type = 0; @@ -731,8 +732,8 @@ static void smc911x_phy_detect(struct net_device *dev) lp->phy_type = id1 << 16 | id2; } - DBG(SMC_DEBUG_MISC, "%s: phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%d\n", - dev->name, id1, id2, lp->mii.phy_id); + DBG(SMC_DEBUG_MISC, dev, "phy_id1=0x%x, phy_id2=0x%x phyaddr=0x%d\n", + id1, id2, lp->mii.phy_id); } /* @@ -745,7 +746,7 @@ static int smc911x_phy_fixed(struct net_device *dev) int phyaddr = lp->mii.phy_id; int bmcr; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); /* Enter Link Disable state */ SMC_GET_PHY_BMCR(lp, phyaddr, bmcr); @@ -792,7 +793,7 @@ static int smc911x_phy_reset(struct net_device *dev, int phy) unsigned long flags; unsigned int reg; - DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s()\n", __func__); spin_lock_irqsave(&lp->lock, flags); reg = SMC_GET_PMT_CTRL(lp); @@ -851,18 +852,18 @@ static void smc911x_phy_check_media(struct net_device *dev, int init) int phyaddr = lp->mii.phy_id; unsigned int bmcr, cr; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); if (mii_check_media(&lp->mii, netif_msg_link(lp), init)) { /* duplex state has changed */ SMC_GET_PHY_BMCR(lp, phyaddr, bmcr); SMC_GET_MAC_CR(lp, cr); if (lp->mii.full_duplex) { - DBG(SMC_DEBUG_MISC, "%s: Configuring for full-duplex mode\n", dev->name); + DBG(SMC_DEBUG_MISC, dev, "Configuring for full-duplex mode\n"); bmcr |= BMCR_FULLDPLX; cr |= MAC_CR_RCVOWN_; } else { - DBG(SMC_DEBUG_MISC, "%s: Configuring for half-duplex mode\n", dev->name); + DBG(SMC_DEBUG_MISC, dev, "Configuring for half-duplex mode\n"); bmcr &= ~BMCR_FULLDPLX; cr &= ~MAC_CR_RCVOWN_; } @@ -891,7 +892,7 @@ static void smc911x_phy_configure(struct work_struct *work) int status; unsigned long flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s()\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s()\n", __func__); /* * We should not be called if phy_type is zero. @@ -900,7 +901,7 @@ static void smc911x_phy_configure(struct work_struct *work) return; if (smc911x_phy_reset(dev, phyaddr)) { - printk("%s: PHY reset timed out\n", dev->name); + netdev_info(dev, "PHY reset timed out\n"); return; } spin_lock_irqsave(&lp->lock, flags); @@ -922,7 +923,7 @@ static void smc911x_phy_configure(struct work_struct *work) /* Copy our capabilities from MII_BMSR to MII_ADVERTISE */ SMC_GET_PHY_BMSR(lp, phyaddr, my_phy_caps); if (!(my_phy_caps & BMSR_ANEGCAPABLE)) { - printk(KERN_INFO "Auto negotiation NOT supported\n"); + netdev_info(dev, "Auto negotiation NOT supported\n"); smc911x_phy_fixed(dev); goto smc911x_phy_configure_exit; } @@ -960,8 +961,8 @@ static void smc911x_phy_configure(struct work_struct *work) udelay(10); SMC_GET_PHY_MII_ADV(lp, phyaddr, status); - DBG(SMC_DEBUG_MISC, "%s: phy caps=0x%04x\n", dev->name, my_phy_caps); - DBG(SMC_DEBUG_MISC, "%s: phy advertised caps=0x%04x\n", dev->name, my_ad_caps); + DBG(SMC_DEBUG_MISC, dev, "phy caps=0x%04x\n", my_phy_caps); + DBG(SMC_DEBUG_MISC, dev, "phy advertised caps=0x%04x\n", my_ad_caps); /* Restart auto-negotiation process in order to advertise my caps */ SMC_SET_PHY_BMCR(lp, phyaddr, BMCR_ANENABLE | BMCR_ANRESTART); @@ -984,7 +985,7 @@ static void smc911x_phy_interrupt(struct net_device *dev) int phyaddr = lp->mii.phy_id; int status; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); if (lp->phy_type == 0) return; @@ -992,10 +993,10 @@ static void smc911x_phy_interrupt(struct net_device *dev) smc911x_phy_check_media(dev, 0); /* read to clear status bits */ SMC_GET_PHY_INT_SRC(lp, phyaddr,status); - DBG(SMC_DEBUG_MISC, "%s: PHY interrupt status 0x%04x\n", - dev->name, status & 0xffff); - DBG(SMC_DEBUG_MISC, "%s: AFC_CFG 0x%08x\n", - dev->name, SMC_GET_AFC_CFG(lp)); + DBG(SMC_DEBUG_MISC, dev, "PHY interrupt status 0x%04x\n", + status & 0xffff); + DBG(SMC_DEBUG_MISC, dev, "AFC_CFG 0x%08x\n", + SMC_GET_AFC_CFG(lp)); } /*--- END PHY CONTROL AND CONFIGURATION-------------------------------------*/ @@ -1012,7 +1013,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) unsigned int rx_overrun=0, cr, pkts; unsigned long flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); spin_lock_irqsave(&lp->lock, flags); @@ -1033,8 +1034,8 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) do { status = SMC_GET_INT(lp); - DBG(SMC_DEBUG_MISC, "%s: INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n", - dev->name, status, mask, status & ~mask); + DBG(SMC_DEBUG_MISC, dev, "INT 0x%08x MASK 0x%08x OUTSIDE MASK 0x%08x\n", + status, mask, status & ~mask); status &= mask; if (!status) @@ -1066,7 +1067,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) SMC_GET_MAC_CR(lp, cr); cr &= ~MAC_CR_RXEN_; SMC_SET_MAC_CR(lp, cr); - DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name); + DBG(SMC_DEBUG_RX, dev, "RX overrun\n"); dev->stats.rx_errors++; dev->stats.rx_fifo_errors++; } @@ -1078,7 +1079,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) cr &= ~MAC_CR_RXEN_; SMC_SET_MAC_CR(lp, cr); rx_overrun=1; - DBG(SMC_DEBUG_RX, "%s: RX overrun\n", dev->name); + DBG(SMC_DEBUG_RX, dev, "RX overrun\n"); dev->stats.rx_errors++; dev->stats.rx_fifo_errors++; } @@ -1087,23 +1088,23 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) /* Handle receive condition */ if ((status & INT_STS_RSFL_) || rx_overrun) { unsigned int fifo; - DBG(SMC_DEBUG_RX, "%s: RX irq\n", dev->name); + DBG(SMC_DEBUG_RX, dev, "RX irq\n"); fifo = SMC_GET_RX_FIFO_INF(lp); pkts = (fifo & RX_FIFO_INF_RXSUSED_) >> 16; - DBG(SMC_DEBUG_RX, "%s: Rx FIFO pkts %d, bytes %d\n", - dev->name, pkts, fifo & 0xFFFF ); + DBG(SMC_DEBUG_RX, dev, "Rx FIFO pkts %d, bytes %d\n", + pkts, fifo & 0xFFFF); if (pkts != 0) { #ifdef SMC_USE_DMA unsigned int fifo; if (lp->rxdma_active){ - DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, - "%s: RX DMA active\n", dev->name); + DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev, + "RX DMA active\n"); /* The DMA is already running so up the IRQ threshold */ fifo = SMC_GET_FIFO_INT(lp) & ~0xFF; fifo |= pkts & 0xFF; - DBG(SMC_DEBUG_RX, - "%s: Setting RX stat FIFO threshold to %d\n", - dev->name, fifo & 0xff); + DBG(SMC_DEBUG_RX, dev, + "Setting RX stat FIFO threshold to %d\n", + fifo & 0xff); SMC_SET_FIFO_INT(lp, fifo); } else #endif @@ -1113,7 +1114,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) } /* Handle transmit FIFO available */ if (status & INT_STS_TDFA_) { - DBG(SMC_DEBUG_TX, "%s: TX data FIFO space available irq\n", dev->name); + DBG(SMC_DEBUG_TX, dev, "TX data FIFO space available irq\n"); SMC_SET_FIFO_TDA(lp, 0xFF); lp->tx_throttle = 0; #ifdef SMC_USE_DMA @@ -1125,9 +1126,9 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) /* Handle transmit done condition */ #if 1 if (status & (INT_STS_TSFL_ | INT_STS_GPT_INT_)) { - DBG(SMC_DEBUG_TX | SMC_DEBUG_MISC, - "%s: Tx stat FIFO limit (%d) /GPT irq\n", - dev->name, (SMC_GET_FIFO_INT(lp) & 0x00ff0000) >> 16); + DBG(SMC_DEBUG_TX | SMC_DEBUG_MISC, dev, + "Tx stat FIFO limit (%d) /GPT irq\n", + (SMC_GET_FIFO_INT(lp) & 0x00ff0000) >> 16); smc911x_tx(dev); SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000); SMC_ACK_INT(lp, INT_STS_TSFL_); @@ -1135,23 +1136,20 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) } #else if (status & INT_STS_TSFL_) { - DBG(SMC_DEBUG_TX, "%s: TX status FIFO limit (%d) irq\n", dev->name, ); + DBG(SMC_DEBUG_TX, dev, "TX status FIFO limit (%d) irq\n", ?); smc911x_tx(dev); SMC_ACK_INT(lp, INT_STS_TSFL_); } if (status & INT_STS_GPT_INT_) { - DBG(SMC_DEBUG_RX, "%s: IRQ_CFG 0x%08x FIFO_INT 0x%08x RX_CFG 0x%08x\n", - dev->name, - SMC_GET_IRQ_CFG(lp), - SMC_GET_FIFO_INT(lp), - SMC_GET_RX_CFG(lp)); - DBG(SMC_DEBUG_RX, "%s: Rx Stat FIFO Used 0x%02x " - "Data FIFO Used 0x%04x Stat FIFO 0x%08x\n", - dev->name, - (SMC_GET_RX_FIFO_INF(lp) & 0x00ff0000) >> 16, - SMC_GET_RX_FIFO_INF(lp) & 0xffff, - SMC_GET_RX_STS_FIFO_PEEK(lp)); + DBG(SMC_DEBUG_RX, dev, "IRQ_CFG 0x%08x FIFO_INT 0x%08x RX_CFG 0x%08x\n", + SMC_GET_IRQ_CFG(lp), + SMC_GET_FIFO_INT(lp), + SMC_GET_RX_CFG(lp)); + DBG(SMC_DEBUG_RX, dev, "Rx Stat FIFO Used 0x%02x Data FIFO Used 0x%04x Stat FIFO 0x%08x\n", + (SMC_GET_RX_FIFO_INF(lp) & 0x00ff0000) >> 16, + SMC_GET_RX_FIFO_INF(lp) & 0xffff, + SMC_GET_RX_STS_FIFO_PEEK(lp)); SMC_SET_GPT_CFG(lp, GPT_CFG_TIMER_EN_ | 10000); SMC_ACK_INT(lp, INT_STS_GPT_INT_); } @@ -1159,7 +1157,7 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) /* Handle PHY interrupt condition */ if (status & INT_STS_PHY_INT_) { - DBG(SMC_DEBUG_MISC, "%s: PHY irq\n", dev->name); + DBG(SMC_DEBUG_MISC, dev, "PHY irq\n"); smc911x_phy_interrupt(dev); SMC_ACK_INT(lp, INT_STS_PHY_INT_); } @@ -1168,8 +1166,8 @@ static irqreturn_t smc911x_interrupt(int irq, void *dev_id) /* restore mask state */ SMC_SET_INT_EN(lp, mask); - DBG(SMC_DEBUG_MISC, "%s: Interrupt done (%d loops)\n", - dev->name, 8-timeout); + DBG(SMC_DEBUG_MISC, dev, "Interrupt done (%d loops)\n", + 8-timeout); spin_unlock_irqrestore(&lp->lock, flags); @@ -1185,9 +1183,9 @@ smc911x_tx_dma_irq(int dma, void *data) struct sk_buff *skb = lp->current_tx_skb; unsigned long flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, "%s: TX DMA irq handler\n", dev->name); + DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, "TX DMA irq handler\n"); /* Clear the DMA interrupt sources */ SMC_DMA_ACK_IRQ(dev, dma); BUG_ON(skb == NULL); @@ -1198,8 +1196,8 @@ smc911x_tx_dma_irq(int dma, void *data) if (lp->pending_tx_skb != NULL) smc911x_hardware_send_pkt(dev); else { - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, - "%s: No pending Tx packets. DMA disabled\n", dev->name); + DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, + "No pending Tx packets. DMA disabled\n"); spin_lock_irqsave(&lp->lock, flags); lp->txdma_active = 0; if (!lp->tx_throttle) { @@ -1208,8 +1206,8 @@ smc911x_tx_dma_irq(int dma, void *data) spin_unlock_irqrestore(&lp->lock, flags); } - DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, - "%s: TX DMA irq completed\n", dev->name); + DBG(SMC_DEBUG_TX | SMC_DEBUG_DMA, dev, + "TX DMA irq completed\n"); } static void smc911x_rx_dma_irq(int dma, void *data) @@ -1221,8 +1219,8 @@ smc911x_rx_dma_irq(int dma, void *data) unsigned long flags; unsigned int pkts; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); - DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, "%s: RX DMA irq handler\n", dev->name); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); + DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev, "RX DMA irq handler\n"); /* Clear the DMA interrupt sources */ SMC_DMA_ACK_IRQ(dev, dma); dma_unmap_single(NULL, rx_dmabuf, rx_dmalen, DMA_FROM_DEVICE); @@ -1242,9 +1240,9 @@ smc911x_rx_dma_irq(int dma, void *data) lp->rxdma_active = 0; } spin_unlock_irqrestore(&lp->lock, flags); - DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, - "%s: RX DMA irq completed. DMA RX FIFO PKTS %d\n", - dev->name, pkts); + DBG(SMC_DEBUG_RX | SMC_DEBUG_DMA, dev, + "RX DMA irq completed. DMA RX FIFO PKTS %d\n", + pkts); } #endif /* SMC_USE_DMA */ @@ -1268,14 +1266,14 @@ static void smc911x_timeout(struct net_device *dev) int status, mask; unsigned long flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); spin_lock_irqsave(&lp->lock, flags); status = SMC_GET_INT(lp); mask = SMC_GET_INT_EN(lp); spin_unlock_irqrestore(&lp->lock, flags); - DBG(SMC_DEBUG_MISC, "%s: INT 0x%02x MASK 0x%02x\n", - dev->name, status, mask); + DBG(SMC_DEBUG_MISC, dev, "INT 0x%02x MASK 0x%02x\n", + status, mask); /* Dump the current TX FIFO contents and restart */ mask = SMC_GET_TX_CFG(lp); @@ -1306,7 +1304,7 @@ static void smc911x_set_multicast_list(struct net_device *dev) unsigned int mcr, update_multicast = 0; unsigned long flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); spin_lock_irqsave(&lp->lock, flags); SMC_GET_MAC_CR(lp, mcr); @@ -1314,7 +1312,7 @@ static void smc911x_set_multicast_list(struct net_device *dev) if (dev->flags & IFF_PROMISC) { - DBG(SMC_DEBUG_MISC, "%s: RCR_PRMS\n", dev->name); + DBG(SMC_DEBUG_MISC, dev, "RCR_PRMS\n"); mcr |= MAC_CR_PRMS_; } /* @@ -1323,7 +1321,7 @@ static void smc911x_set_multicast_list(struct net_device *dev) * checked before the table is */ else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > 16) { - DBG(SMC_DEBUG_MISC, "%s: RCR_ALMUL\n", dev->name); + DBG(SMC_DEBUG_MISC, dev, "RCR_ALMUL\n"); mcr |= MAC_CR_MCPAS_; } @@ -1363,8 +1361,7 @@ static void smc911x_set_multicast_list(struct net_device *dev) /* now, the table can be loaded into the chipset */ update_multicast = 1; } else { - DBG(SMC_DEBUG_MISC, "%s: ~(MAC_CR_PRMS_|MAC_CR_MCPAS_)\n", - dev->name); + DBG(SMC_DEBUG_MISC, dev, "~(MAC_CR_PRMS_|MAC_CR_MCPAS_)\n"); mcr &= ~(MAC_CR_PRMS_ | MAC_CR_MCPAS_); /* @@ -1378,9 +1375,9 @@ static void smc911x_set_multicast_list(struct net_device *dev) spin_lock_irqsave(&lp->lock, flags); SMC_SET_MAC_CR(lp, mcr); if (update_multicast) { - DBG(SMC_DEBUG_MISC, - "%s: update mcast hash table 0x%08x 0x%08x\n", - dev->name, multicast_table[0], multicast_table[1]); + DBG(SMC_DEBUG_MISC, dev, + "update mcast hash table 0x%08x 0x%08x\n", + multicast_table[0], multicast_table[1]); SMC_SET_HASHL(lp, multicast_table[0]); SMC_SET_HASHH(lp, multicast_table[1]); } @@ -1398,7 +1395,7 @@ smc911x_open(struct net_device *dev) { struct smc911x_local *lp = netdev_priv(dev); - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); /* reset the hardware */ smc911x_reset(dev); @@ -1425,7 +1422,7 @@ static int smc911x_close(struct net_device *dev) { struct smc911x_local *lp = netdev_priv(dev); - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); netif_stop_queue(dev); netif_carrier_off(dev); @@ -1459,7 +1456,7 @@ smc911x_ethtool_getsettings(struct net_device *dev, struct ethtool_cmd *cmd) int ret, status; unsigned long flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); cmd->maxtxpkt = 1; cmd->maxrxpkt = 1; @@ -1597,16 +1594,16 @@ static int smc911x_ethtool_wait_eeprom_ready(struct net_device *dev) e2p_cmd = SMC_GET_E2P_CMD(lp); for(timeout=10;(e2p_cmd & E2P_CMD_EPC_BUSY_) && timeout; timeout--) { if (e2p_cmd & E2P_CMD_EPC_TIMEOUT_) { - PRINTK("%s: %s timeout waiting for EEPROM to respond\n", - dev->name, __func__); + PRINTK(dev, "%s timeout waiting for EEPROM to respond\n", + __func__); return -EFAULT; } mdelay(1); e2p_cmd = SMC_GET_E2P_CMD(lp); } if (timeout == 0) { - PRINTK("%s: %s timeout waiting for EEPROM CMD not busy\n", - dev->name, __func__); + PRINTK(dev, "%s timeout waiting for EEPROM CMD not busy\n", + __func__); return -ETIMEDOUT; } return 0; @@ -1719,7 +1716,7 @@ static int smc911x_findirq(struct net_device *dev) int timeout = 20; unsigned long cookie; - DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); cookie = probe_irq_on(); @@ -1799,13 +1796,14 @@ static int smc911x_probe(struct net_device *dev) const char *version_string; unsigned long irq_flags; - DBG(SMC_DEBUG_FUNC, "%s: --> %s\n", dev->name, __func__); + DBG(SMC_DEBUG_FUNC, dev, "--> %s\n", __func__); /* First, see if the endian word is recognized */ val = SMC_GET_BYTE_TEST(lp); - DBG(SMC_DEBUG_MISC, "%s: endian probe returned 0x%04x\n", CARDNAME, val); + DBG(SMC_DEBUG_MISC, dev, "%s: endian probe returned 0x%04x\n", + CARDNAME, val); if (val != 0x87654321) { - printk(KERN_ERR "Invalid chip endian 0x%08x\n",val); + netdev_err(dev, "Invalid chip endian 0x%08x\n", val); retval = -ENODEV; goto err_out; } @@ -1816,26 +1814,29 @@ static int smc911x_probe(struct net_device *dev) * as future revisions could be added. */ chip_id = SMC_GET_PN(lp); - DBG(SMC_DEBUG_MISC, "%s: id probe returned 0x%04x\n", CARDNAME, chip_id); + DBG(SMC_DEBUG_MISC, dev, "%s: id probe returned 0x%04x\n", + CARDNAME, chip_id); for(i=0;chip_ids[i].id != 0; i++) { if (chip_ids[i].id == chip_id) break; } if (!chip_ids[i].id) { - printk(KERN_ERR "Unknown chip ID %04x\n", chip_id); + netdev_err(dev, "Unknown chip ID %04x\n", chip_id); retval = -ENODEV; goto err_out; } version_string = chip_ids[i].name; revision = SMC_GET_REV(lp); - DBG(SMC_DEBUG_MISC, "%s: revision = 0x%04x\n", CARDNAME, revision); + DBG(SMC_DEBUG_MISC, dev, "%s: revision = 0x%04x\n", CARDNAME, revision); /* At this point I'll assume that the chip is an SMC911x. */ - DBG(SMC_DEBUG_MISC, "%s: Found a %s\n", CARDNAME, chip_ids[i].name); + DBG(SMC_DEBUG_MISC, dev, "%s: Found a %s\n", + CARDNAME, chip_ids[i].name); /* Validate the TX FIFO size requested */ if ((tx_fifo_kb < 2) || (tx_fifo_kb > 14)) { - printk(KERN_ERR "Invalid TX FIFO size requested %d\n", tx_fifo_kb); + netdev_err(dev, "Invalid TX FIFO size requested %d\n", + tx_fifo_kb); retval = -EINVAL; goto err_out; } @@ -1887,14 +1888,13 @@ static int smc911x_probe(struct net_device *dev) case 14:/* 1920 Rx Data Fifo Size */ lp->afc_cfg=0x0006032F;break; default: - PRINTK("%s: ERROR -- no AFC_CFG setting found", - dev->name); + PRINTK(dev, "ERROR -- no AFC_CFG setting found"); break; } - DBG(SMC_DEBUG_MISC | SMC_DEBUG_TX | SMC_DEBUG_RX, - "%s: tx_fifo %d rx_fifo %d afc_cfg 0x%08x\n", CARDNAME, - lp->tx_fifo_size, lp->rx_fifo_size, lp->afc_cfg); + DBG(SMC_DEBUG_MISC | SMC_DEBUG_TX | SMC_DEBUG_RX, dev, + "%s: tx_fifo %d rx_fifo %d afc_cfg 0x%08x\n", CARDNAME, + lp->tx_fifo_size, lp->rx_fifo_size, lp->afc_cfg); spin_lock_init(&lp->lock); @@ -1924,8 +1924,7 @@ static int smc911x_probe(struct net_device *dev) } } if (dev->irq == 0) { - printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n", - dev->name); + netdev_warn(dev, "Couldn't autodetect your IRQ. Use irq=xx.\n"); retval = -ENODEV; goto err_out; } @@ -1980,33 +1979,32 @@ static int smc911x_probe(struct net_device *dev) retval = register_netdev(dev); if (retval == 0) { /* now, print out the card info, in a short format.. */ - printk("%s: %s (rev %d) at %#lx IRQ %d", - dev->name, version_string, lp->revision, - dev->base_addr, dev->irq); + netdev_info(dev, "%s (rev %d) at %#lx IRQ %d", + version_string, lp->revision, + dev->base_addr, dev->irq); #ifdef SMC_USE_DMA if (lp->rxdma != -1) - printk(" RXDMA %d ", lp->rxdma); + pr_cont(" RXDMA %d", lp->rxdma); if (lp->txdma != -1) - printk("TXDMA %d", lp->txdma); + pr_cont(" TXDMA %d", lp->txdma); #endif - printk("\n"); + pr_cont("\n"); if (!is_valid_ether_addr(dev->dev_addr)) { - printk("%s: Invalid ethernet MAC address. Please " - "set using ifconfig\n", dev->name); + netdev_warn(dev, "Invalid ethernet MAC address. Please set using ifconfig\n"); } else { /* Print the Ethernet address */ - printk("%s: Ethernet addr: %pM\n", - dev->name, dev->dev_addr); + netdev_info(dev, "Ethernet addr: %pM\n", + dev->dev_addr); } if (lp->phy_type == 0) { - PRINTK("%s: No PHY found\n", dev->name); + PRINTK(dev, "No PHY found\n"); } else if ((lp->phy_type & ~0xff) == LAN911X_INTERNAL_PHY_ID) { - PRINTK("%s: LAN911x Internal PHY\n", dev->name); + PRINTK(dev, "LAN911x Internal PHY\n"); } else { - PRINTK("%s: External PHY 0x%08x\n", dev->name, lp->phy_type); + PRINTK(dev, "External PHY 0x%08x\n", lp->phy_type); } } @@ -2025,7 +2023,7 @@ err_out: } /* - * smc911x_init(void) + * smc911x_drv_probe(void) * * Output: * 0 --> there is a device @@ -2039,6 +2037,7 @@ static int smc911x_drv_probe(struct platform_device *pdev) void __iomem *addr; int ret; + /* ndev is not valid yet, so avoid passing it in. */ DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -2093,7 +2092,7 @@ release_both: release_1: release_mem_region(res->start, SMC911X_IO_EXTENT); out: - printk("%s: not found (%d).\n", CARDNAME, ret); + pr_info("%s: not found (%d).\n", CARDNAME, ret); } #ifdef SMC_USE_DMA else { @@ -2111,7 +2110,7 @@ static int smc911x_drv_remove(struct platform_device *pdev) struct smc911x_local *lp = netdev_priv(ndev); struct resource *res; - DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__); + DBG(SMC_DEBUG_FUNC, ndev, "--> %s\n", __func__); unregister_netdev(ndev); @@ -2140,7 +2139,7 @@ static int smc911x_drv_suspend(struct platform_device *dev, pm_message_t state) struct net_device *ndev = platform_get_drvdata(dev); struct smc911x_local *lp = netdev_priv(ndev); - DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__); + DBG(SMC_DEBUG_FUNC, ndev, "--> %s\n", __func__); if (ndev) { if (netif_running(ndev)) { netif_device_detach(ndev); @@ -2158,7 +2157,7 @@ static int smc911x_drv_resume(struct platform_device *dev) { struct net_device *ndev = platform_get_drvdata(dev); - DBG(SMC_DEBUG_FUNC, "--> %s\n", __func__); + DBG(SMC_DEBUG_FUNC, ndev, "--> %s\n", __func__); if (ndev) { struct smc911x_local *lp = netdev_priv(ndev); diff --git a/drivers/net/ethernet/smsc/smc911x.h b/drivers/net/ethernet/smsc/smc911x.h index d51261ba464243c936b0baaa3eaa6f8a68c6091a..9965da39281b5ec720f9f2ce8851cf09a653a8b9 100644 --- a/drivers/net/ethernet/smsc/smc911x.h +++ b/drivers/net/ethernet/smsc/smc911x.h @@ -227,7 +227,7 @@ static inline void SMC_outsl(struct smc911x_local *lp, int reg, #define SMC_DMA_ACK_IRQ(dev, dma) \ { \ if (DCSR(dma) & DCSR_BUSERR) { \ - printk("%s: DMA %d bus error!\n", dev->name, dma); \ + netdev_err(dev, "DMA %d bus error!\n", dma); \ } \ DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; \ } diff --git a/drivers/net/ethernet/smsc/smc9194.c b/drivers/net/ethernet/smsc/smc9194.c index afd9873e9bdb4a527666681e2b55da0504e43ea5..67d9fdeedd86d24db373b927e6fa0a8a67e072b5 100644 --- a/drivers/net/ethernet/smsc/smc9194.c +++ b/drivers/net/ethernet/smsc/smc9194.c @@ -55,7 +55,7 @@ ----------------------------------------------------------------------------*/ static const char version[] = - "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)\n"; + "smc9194.c:v0.14 12/15/00 by Erik Stahlman (erik@vt.edu)"; #include #include @@ -597,7 +597,7 @@ static void smc_hardware_send_packet( struct net_device * dev ) packet_no = inb( ioaddr + PNR_ARR + 1 ); if ( packet_no & 0x80 ) { /* or isn't there? BAD CHIP! */ - printk(KERN_DEBUG CARDNAME": Memory allocation failed.\n"); + netdev_dbg(dev, CARDNAME": Memory allocation failed.\n"); dev_kfree_skb_any(skb); lp->saved_skb = NULL; netif_wake_queue(dev); @@ -610,7 +610,7 @@ static void smc_hardware_send_packet( struct net_device * dev ) /* point to the beginning of the packet */ outw( PTR_AUTOINC , ioaddr + POINTER ); - PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length )); + PRINTK3((CARDNAME": Trying to xmit packet of length %x\n", length)); #if SMC_DEBUG > 2 print_packet( buf, length ); #endif @@ -846,7 +846,6 @@ static const struct net_device_ops smc_netdev_ops = { static int __init smc_probe(struct net_device *dev, int ioaddr) { int i, memory, retval; - static unsigned version_printed; unsigned int bank; const char *version_string; @@ -913,8 +912,7 @@ static int __init smc_probe(struct net_device *dev, int ioaddr) It might be prudent to check a listing of MAC addresses against the hardware address, or do some other tests. */ - if (version_printed++ == 0) - printk("%s", version); + pr_info_once("%s\n", version); /* fill in some of the fields */ dev->base_addr = ioaddr; @@ -1003,21 +1001,21 @@ static int __init smc_probe(struct net_device *dev, int ioaddr) /* now, print out the card info, in a short format.. */ - printk("%s: %s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", dev->name, - version_string, revision_register & 0xF, ioaddr, dev->irq, - if_string, memory ); + netdev_info(dev, "%s(r:%d) at %#3x IRQ:%d INTF:%s MEM:%db ", + version_string, revision_register & 0xF, ioaddr, dev->irq, + if_string, memory); /* . Print the Ethernet address */ - printk("ADDR: %pM\n", dev->dev_addr); + netdev_info(dev, "ADDR: %pM\n", dev->dev_addr); /* Grab the IRQ */ - retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev); - if (retval) { - printk("%s: unable to get IRQ %d (irqval=%d).\n", DRV_NAME, - dev->irq, retval); - goto err_out; - } + retval = request_irq(dev->irq, smc_interrupt, 0, DRV_NAME, dev); + if (retval) { + netdev_warn(dev, "%s: unable to get IRQ %d (irqval=%d).\n", + DRV_NAME, dev->irq, retval); + goto err_out; + } dev->netdev_ops = &smc_netdev_ops; dev->watchdog_timeo = HZ/20; @@ -1037,30 +1035,32 @@ static void print_packet( byte * buf, int length ) int remainder; int lines; - printk("Packet of length %d\n", length); + pr_dbg("Packet of length %d\n", length); lines = length / 16; remainder = length % 16; for ( i = 0; i < lines ; i ++ ) { int cur; + printk(KERN_DEBUG); for ( cur = 0; cur < 8; cur ++ ) { byte a, b; a = *(buf ++ ); b = *(buf ++ ); - printk("%02x%02x ", a, b ); + pr_cont("%02x%02x ", a, b); } - printk("\n"); + pr_cont("\n"); } + printk(KERN_DEBUG); for ( i = 0; i < remainder/2 ; i++ ) { byte a, b; a = *(buf ++ ); b = *(buf ++ ); - printk("%02x%02x ", a, b ); + pr_cont("%02x%02x ", a, b); } - printk("\n"); + pr_cont("\n"); #endif } #endif @@ -1127,9 +1127,8 @@ static void smc_timeout(struct net_device *dev) { /* If we get here, some higher level has decided we are broken. There should really be a "kick me" function call instead. */ - printk(KERN_WARNING CARDNAME": transmit timed out, %s?\n", - tx_done(dev) ? "IRQ conflict" : - "network cable problem"); + netdev_warn(dev, CARDNAME": transmit timed out, %s?\n", + tx_done(dev) ? "IRQ conflict" : "network cable problem"); /* "kick" the adaptor */ smc_reset( dev->base_addr ); smc_enable( dev->base_addr ); @@ -1299,8 +1298,7 @@ static void smc_tx( struct net_device * dev ) dev->stats.tx_errors++; if ( tx_status & TS_LOSTCAR ) dev->stats.tx_carrier_errors++; if ( tx_status & TS_LATCOL ) { - printk(KERN_DEBUG CARDNAME - ": Late collision occurred on last xmit.\n"); + netdev_dbg(dev, CARDNAME": Late collision occurred on last xmit.\n"); dev->stats.tx_window_errors++; } #if 0 @@ -1308,7 +1306,7 @@ static void smc_tx( struct net_device * dev ) #endif if ( tx_status & TS_SUCCESS ) { - printk(CARDNAME": Successful packet caused interrupt\n"); + netdev_info(dev, CARDNAME": Successful packet caused interrupt\n"); } /* re-enable transmit */ SMC_SELECT_BANK( 0 ); @@ -1547,9 +1545,7 @@ int __init init_module(void) /* copy the parameters from insmod into the device structure */ devSMC9194 = smc_init(-1); - if (IS_ERR(devSMC9194)) - return PTR_ERR(devSMC9194); - return 0; + return PTR_ERR_OR_ZERO(devSMC9194); } void __exit cleanup_module(void) diff --git a/drivers/net/ethernet/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c index 656d2e2ebfc9325c18e397f71074897dc43fc521..8ef70d9c20c1d8e7f4aad6e48152c7bc9420c861 100644 --- a/drivers/net/ethernet/smsc/smc91c92_cs.c +++ b/drivers/net/ethernet/smsc/smc91c92_cs.c @@ -740,7 +740,7 @@ static int smc91c92_resume(struct pcmcia_device *link) (smc->cardid == PRODID_PSION_NET100))) { i = osi_load_firmware(link); if (i) { - pr_err("smc91c92_cs: Failed to load firmware\n"); + netdev_err(dev, "Failed to load firmware\n"); return i; } } @@ -793,7 +793,7 @@ static int check_sig(struct pcmcia_device *link) } if (width) { - pr_info("using 8-bit IO window\n"); + netdev_info(dev, "using 8-bit IO window\n"); smc91c92_suspend(link); pcmcia_fixup_iowidth(link); @@ -1036,7 +1036,7 @@ static void smc_dump(struct net_device *dev) save = inw(ioaddr + BANK_SELECT); for (w = 0; w < 4; w++) { SMC_SELECT_BANK(w); - netdev_printk(KERN_DEBUG, dev, "bank %d: ", w); + netdev_dbg(dev, "bank %d: ", w); for (i = 0; i < 14; i += 2) pr_cont(" %04x", inw(ioaddr + i)); pr_cont("\n"); @@ -1213,8 +1213,7 @@ static netdev_tx_t smc_start_xmit(struct sk_buff *skb, if (smc->saved_skb) { /* THIS SHOULD NEVER HAPPEN. */ dev->stats.tx_aborted_errors++; - netdev_printk(KERN_DEBUG, dev, - "Internal error -- sent packet while busy\n"); + netdev_dbg(dev, "Internal error -- sent packet while busy\n"); return NETDEV_TX_BUSY; } smc->saved_skb = skb; @@ -1254,7 +1253,7 @@ static netdev_tx_t smc_start_xmit(struct sk_buff *skb, } /* Otherwise defer until the Tx-space-allocated interrupt. */ - pr_debug("%s: memory allocation deferred.\n", dev->name); + netdev_dbg(dev, "memory allocation deferred.\n"); outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT); spin_unlock_irqrestore(&smc->lock, flags); @@ -1317,8 +1316,8 @@ static void smc_eph_irq(struct net_device *dev) SMC_SELECT_BANK(0); ephs = inw(ioaddr + EPH); - pr_debug("%s: Ethernet protocol handler interrupt, status" - " %4.4x.\n", dev->name, ephs); + netdev_dbg(dev, "Ethernet protocol handler interrupt, status %4.4x.\n", + ephs); /* Could be a counter roll-over warning: update stats. */ card_stats = inw(ioaddr + COUNTER); /* single collisions */ @@ -1357,8 +1356,8 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) ioaddr = dev->base_addr; - pr_debug("%s: SMC91c92 interrupt %d at %#x.\n", dev->name, - irq, ioaddr); + netdev_dbg(dev, "SMC91c92 interrupt %d at %#x.\n", + irq, ioaddr); spin_lock(&smc->lock); smc->watchdog = 0; @@ -1366,8 +1365,8 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) if ((saved_bank & 0xff00) != 0x3300) { /* The device does not exist -- the card could be off-line, or maybe it has been ejected. */ - pr_debug("%s: SMC91c92 interrupt %d for non-existent" - "/ejected device.\n", dev->name, irq); + netdev_dbg(dev, "SMC91c92 interrupt %d for non-existent/ejected device.\n", + irq); handled = 0; goto irq_done; } @@ -1380,8 +1379,8 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) do { /* read the status flag, and mask it */ status = inw(ioaddr + INTERRUPT) & 0xff; - pr_debug("%s: Status is %#2.2x (mask %#2.2x).\n", dev->name, - status, mask); + netdev_dbg(dev, "Status is %#2.2x (mask %#2.2x).\n", + status, mask); if ((status & mask) == 0) { if (bogus_cnt == INTR_WORK) handled = 0; @@ -1425,15 +1424,15 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) smc_eph_irq(dev); } while (--bogus_cnt); - pr_debug(" Restoring saved registers mask %2.2x bank %4.4x" - " pointer %4.4x.\n", mask, saved_bank, saved_pointer); + netdev_dbg(dev, " Restoring saved registers mask %2.2x bank %4.4x pointer %4.4x.\n", + mask, saved_bank, saved_pointer); /* restore state register */ outw((mask<<8), ioaddr + INTERRUPT); outw(saved_pointer, ioaddr + POINTER); SMC_SELECT_BANK(saved_bank); - pr_debug("%s: Exiting interrupt IRQ%d.\n", dev->name, irq); + netdev_dbg(dev, "Exiting interrupt IRQ%d.\n", irq); irq_done: @@ -1491,10 +1490,10 @@ static void smc_rx(struct net_device *dev) rx_status = inw(ioaddr + DATA_1); packet_length = inw(ioaddr + DATA_1) & 0x07ff; - pr_debug("%s: Receive status %4.4x length %d.\n", - dev->name, rx_status, packet_length); + netdev_dbg(dev, "Receive status %4.4x length %d.\n", + rx_status, packet_length); - if (!(rx_status & RS_ERRORS)) { + if (!(rx_status & RS_ERRORS)) { /* do stuff to make a new packet */ struct sk_buff *skb; @@ -1502,7 +1501,7 @@ static void smc_rx(struct net_device *dev) skb = netdev_alloc_skb(dev, packet_length+2); if (skb == NULL) { - pr_debug("%s: Low memory, packet dropped.\n", dev->name); + netdev_dbg(dev, "Low memory, packet dropped.\n"); dev->stats.rx_dropped++; outw(MC_RELEASE, ioaddr + MMU_CMD); return; @@ -1643,7 +1642,7 @@ static void smc_reset(struct net_device *dev) struct smc_private *smc = netdev_priv(dev); int i; - pr_debug("%s: smc91c92 reset called.\n", dev->name); + netdev_dbg(dev, "smc91c92 reset called.\n"); /* The first interaction must be a write to bring the chip out of sleep mode. */ diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 73be7f3982e6c7fbaea82016e8e1e7a9c746533b..0c9b5d94154f8d215ddaf40d3fb252764983d73b 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -58,7 +58,7 @@ * 22/09/04 Nicolas Pitre big update (see commit log for details) */ static const char version[] = - "smc91x.c: v1.1, sep 22 2004 by Nicolas Pitre \n"; + "smc91x.c: v1.1, sep 22 2004 by Nicolas Pitre "; /* Debugging level */ #ifndef SMC_DEBUG @@ -149,16 +149,16 @@ MODULE_ALIAS("platform:smc91x"); #define MII_DELAY 1 #if SMC_DEBUG > 0 -#define DBG(n, args...) \ +#define DBG(n, dev, args...) \ do { \ if (SMC_DEBUG >= (n)) \ - printk(args); \ + netdev_dbg(dev, args); \ } while (0) -#define PRINTK(args...) printk(args) +#define PRINTK(dev, args...) netdev_info(dev, args) #else -#define DBG(n, args...) do { } while(0) -#define PRINTK(args...) printk(KERN_DEBUG args) +#define DBG(n, dev, args...) do { } while (0) +#define PRINTK(dev, args...) netdev_dbg(dev, args) #endif #if SMC_DEBUG > 3 @@ -173,24 +173,26 @@ static void PRINT_PKT(u_char *buf, int length) for (i = 0; i < lines ; i ++) { int cur; + printk(KERN_DEBUG); for (cur = 0; cur < 8; cur++) { u_char a, b; a = *buf++; b = *buf++; - printk("%02x%02x ", a, b); + pr_cont("%02x%02x ", a, b); } - printk("\n"); + pr_cont("\n"); } + printk(KERN_DEBUG); for (i = 0; i < remainder/2 ; i++) { u_char a, b; a = *buf++; b = *buf++; - printk("%02x%02x ", a, b); + pr_cont("%02x%02x ", a, b); } - printk("\n"); + pr_cont("\n"); } #else -#define PRINT_PKT(x...) do { } while(0) +#define PRINT_PKT(x...) do { } while (0) #endif @@ -226,8 +228,8 @@ static void PRINT_PKT(u_char *buf, int length) unsigned long timeout = jiffies + 2; \ while (SMC_GET_MMU_CMD(lp) & MC_BUSY) { \ if (time_after(jiffies, timeout)) { \ - printk("%s: timeout %s line %d\n", \ - dev->name, __FILE__, __LINE__); \ + netdev_dbg(dev, "timeout %s line %d\n", \ + __FILE__, __LINE__); \ break; \ } \ cpu_relax(); \ @@ -246,7 +248,7 @@ static void smc_reset(struct net_device *dev) unsigned int ctl, cfg; struct sk_buff *pending_skb; - DBG(2, "%s: %s\n", dev->name, __func__); + DBG(2, dev, "%s\n", __func__); /* Disable all interrupts, block TX tasklet */ spin_lock_irq(&lp->lock); @@ -339,7 +341,7 @@ static void smc_enable(struct net_device *dev) void __iomem *ioaddr = lp->base; int mask; - DBG(2, "%s: %s\n", dev->name, __func__); + DBG(2, dev, "%s\n", __func__); /* see the header file for options in TCR/RCR DEFAULT */ SMC_SELECT_BANK(lp, 0); @@ -373,7 +375,7 @@ static void smc_shutdown(struct net_device *dev) void __iomem *ioaddr = lp->base; struct sk_buff *pending_skb; - DBG(2, "%s: %s\n", CARDNAME, __func__); + DBG(2, dev, "%s: %s\n", CARDNAME, __func__); /* no more interrupts for me */ spin_lock_irq(&lp->lock); @@ -406,11 +408,11 @@ static inline void smc_rcv(struct net_device *dev) void __iomem *ioaddr = lp->base; unsigned int packet_number, status, packet_len; - DBG(3, "%s: %s\n", dev->name, __func__); + DBG(3, dev, "%s\n", __func__); packet_number = SMC_GET_RXFIFO(lp); if (unlikely(packet_number & RXFIFO_REMPTY)) { - PRINTK("%s: smc_rcv with nothing on FIFO.\n", dev->name); + PRINTK(dev, "smc_rcv with nothing on FIFO.\n"); return; } @@ -420,9 +422,8 @@ static inline void smc_rcv(struct net_device *dev) /* First two words are status and packet length */ SMC_GET_PKT_HDR(lp, status, packet_len); packet_len &= 0x07ff; /* mask off top bits */ - DBG(2, "%s: RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n", - dev->name, packet_number, status, - packet_len, packet_len); + DBG(2, dev, "RX PNR 0x%x STATUS 0x%04x LENGTH 0x%04x (%d)\n", + packet_number, status, packet_len, packet_len); back: if (unlikely(packet_len < 6 || status & RS_ERRORS)) { @@ -433,8 +434,8 @@ static inline void smc_rcv(struct net_device *dev) } if (packet_len < 6) { /* bloody hardware */ - printk(KERN_ERR "%s: fubar (rxlen %u status %x\n", - dev->name, packet_len, status); + netdev_err(dev, "fubar (rxlen %u status %x\n", + packet_len, status); status |= RS_TOOSHORT; } SMC_WAIT_MMU_BUSY(lp); @@ -551,7 +552,7 @@ static void smc_hardware_send_pkt(unsigned long data) unsigned char *buf; unsigned long flags; - DBG(3, "%s: %s\n", dev->name, __func__); + DBG(3, dev, "%s\n", __func__); if (!smc_special_trylock(&lp->lock, flags)) { netif_stop_queue(dev); @@ -568,7 +569,7 @@ static void smc_hardware_send_pkt(unsigned long data) packet_no = SMC_GET_AR(lp); if (unlikely(packet_no & AR_FAILED)) { - printk("%s: Memory allocation failed.\n", dev->name); + netdev_err(dev, "Memory allocation failed.\n"); dev->stats.tx_errors++; dev->stats.tx_fifo_errors++; smc_special_unlock(&lp->lock, flags); @@ -581,8 +582,8 @@ static void smc_hardware_send_pkt(unsigned long data) buf = skb->data; len = skb->len; - DBG(2, "%s: TX PNR 0x%x LENGTH 0x%04x (%d) BUF 0x%p\n", - dev->name, packet_no, len, len, buf); + DBG(2, dev, "TX PNR 0x%x LENGTH 0x%04x (%d) BUF 0x%p\n", + packet_no, len, len, buf); PRINT_PKT(buf, len); /* @@ -637,7 +638,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) unsigned int numPages, poll_count, status; unsigned long flags; - DBG(3, "%s: %s\n", dev->name, __func__); + DBG(3, dev, "%s\n", __func__); BUG_ON(lp->pending_tx_skb != NULL); @@ -654,7 +655,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) */ numPages = ((skb->len & ~1) + (6 - 1)) >> 8; if (unlikely(numPages > 7)) { - printk("%s: Far too big packet error.\n", dev->name); + netdev_warn(dev, "Far too big packet error.\n"); dev->stats.tx_errors++; dev->stats.tx_dropped++; dev_kfree_skb(skb); @@ -685,7 +686,7 @@ static int smc_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) if (!poll_count) { /* oh well, wait until the chip finds memory later */ netif_stop_queue(dev); - DBG(2, "%s: TX memory allocation deferred.\n", dev->name); + DBG(2, dev, "TX memory allocation deferred.\n"); SMC_ENABLE_INT(lp, IM_ALLOC_INT); } else { /* @@ -709,12 +710,12 @@ static void smc_tx(struct net_device *dev) void __iomem *ioaddr = lp->base; unsigned int saved_packet, packet_no, tx_status, pkt_len; - DBG(3, "%s: %s\n", dev->name, __func__); + DBG(3, dev, "%s\n", __func__); /* If the TX FIFO is empty then nothing to do */ packet_no = SMC_GET_TXFIFO(lp); if (unlikely(packet_no & TXFIFO_TEMPTY)) { - PRINTK("%s: smc_tx with nothing on FIFO.\n", dev->name); + PRINTK(dev, "smc_tx with nothing on FIFO.\n"); return; } @@ -725,8 +726,8 @@ static void smc_tx(struct net_device *dev) /* read the first word (status word) from this packet */ SMC_SET_PTR(lp, PTR_AUTOINC | PTR_READ); SMC_GET_PKT_HDR(lp, tx_status, pkt_len); - DBG(2, "%s: TX STATUS 0x%04x PNR 0x%02x\n", - dev->name, tx_status, packet_no); + DBG(2, dev, "TX STATUS 0x%04x PNR 0x%02x\n", + tx_status, packet_no); if (!(tx_status & ES_TX_SUC)) dev->stats.tx_errors++; @@ -735,14 +736,12 @@ static void smc_tx(struct net_device *dev) dev->stats.tx_carrier_errors++; if (tx_status & (ES_LATCOL | ES_16COL)) { - PRINTK("%s: %s occurred on last xmit\n", dev->name, + PRINTK(dev, "%s occurred on last xmit\n", (tx_status & ES_LATCOL) ? "late collision" : "too many collisions"); dev->stats.tx_window_errors++; if (!(dev->stats.tx_window_errors & 63) && net_ratelimit()) { - printk(KERN_INFO "%s: unexpectedly large number of " - "bad collisions. Please check duplex " - "setting.\n", dev->name); + netdev_info(dev, "unexpectedly large number of bad collisions. Please check duplex setting.\n"); } } @@ -830,8 +829,8 @@ static int smc_phy_read(struct net_device *dev, int phyaddr, int phyreg) /* Return to idle state */ SMC_SET_MII(lp, SMC_GET_MII(lp) & ~(MII_MCLK|MII_MDOE|MII_MDO)); - DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", - __func__, phyaddr, phyreg, phydata); + DBG(3, dev, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", + __func__, phyaddr, phyreg, phydata); SMC_SELECT_BANK(lp, 2); return phydata; @@ -857,8 +856,8 @@ static void smc_phy_write(struct net_device *dev, int phyaddr, int phyreg, /* Return to idle state */ SMC_SET_MII(lp, SMC_GET_MII(lp) & ~(MII_MCLK|MII_MDOE|MII_MDO)); - DBG(3, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", - __func__, phyaddr, phyreg, phydata); + DBG(3, dev, "%s: phyaddr=0x%x, phyreg=0x%x, phydata=0x%x\n", + __func__, phyaddr, phyreg, phydata); SMC_SELECT_BANK(lp, 2); } @@ -871,7 +870,7 @@ static void smc_phy_detect(struct net_device *dev) struct smc_local *lp = netdev_priv(dev); int phyaddr; - DBG(2, "%s: %s\n", dev->name, __func__); + DBG(2, dev, "%s\n", __func__); lp->phy_type = 0; @@ -886,8 +885,8 @@ static void smc_phy_detect(struct net_device *dev) id1 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID1); id2 = smc_phy_read(dev, phyaddr & 31, MII_PHYSID2); - DBG(3, "%s: phy_id1=0x%x, phy_id2=0x%x\n", - dev->name, id1, id2); + DBG(3, dev, "phy_id1=0x%x, phy_id2=0x%x\n", + id1, id2); /* Make sure it is a valid identifier */ if (id1 != 0x0000 && id1 != 0xffff && id1 != 0x8000 && @@ -910,7 +909,7 @@ static int smc_phy_fixed(struct net_device *dev) int phyaddr = lp->mii.phy_id; int bmcr, cfg1; - DBG(3, "%s: %s\n", dev->name, __func__); + DBG(3, dev, "%s\n", __func__); /* Enter Link Disable state */ cfg1 = smc_phy_read(dev, phyaddr, PHY_CFG1_REG); @@ -1044,7 +1043,7 @@ static void smc_phy_configure(struct work_struct *work) int my_ad_caps; /* My Advertised capabilities */ int status; - DBG(3, "%s:smc_program_phy()\n", dev->name); + DBG(3, dev, "smc_program_phy()\n"); spin_lock_irq(&lp->lock); @@ -1055,7 +1054,7 @@ static void smc_phy_configure(struct work_struct *work) goto smc_phy_configure_exit; if (smc_phy_reset(dev, phyaddr)) { - printk("%s: PHY reset timed out\n", dev->name); + netdev_info(dev, "PHY reset timed out\n"); goto smc_phy_configure_exit; } @@ -1082,7 +1081,7 @@ static void smc_phy_configure(struct work_struct *work) my_phy_caps = smc_phy_read(dev, phyaddr, MII_BMSR); if (!(my_phy_caps & BMSR_ANEGCAPABLE)) { - printk(KERN_INFO "Auto negotiation NOT supported\n"); + netdev_info(dev, "Auto negotiation NOT supported\n"); smc_phy_fixed(dev); goto smc_phy_configure_exit; } @@ -1118,8 +1117,8 @@ static void smc_phy_configure(struct work_struct *work) */ status = smc_phy_read(dev, phyaddr, MII_ADVERTISE); - DBG(2, "%s: phy caps=%x\n", dev->name, my_phy_caps); - DBG(2, "%s: phy advertised caps=%x\n", dev->name, my_ad_caps); + DBG(2, dev, "phy caps=%x\n", my_phy_caps); + DBG(2, dev, "phy advertised caps=%x\n", my_ad_caps); /* Restart auto-negotiation process in order to advertise my caps */ smc_phy_write(dev, phyaddr, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART); @@ -1143,7 +1142,7 @@ static void smc_phy_interrupt(struct net_device *dev) int phyaddr = lp->mii.phy_id; int phy18; - DBG(2, "%s: %s\n", dev->name, __func__); + DBG(2, dev, "%s\n", __func__); if (lp->phy_type == 0) return; @@ -1179,8 +1178,8 @@ static void smc_10bt_check_media(struct net_device *dev, int init) netif_carrier_on(dev); } if (netif_msg_link(lp)) - printk(KERN_INFO "%s: link %s\n", dev->name, - new_carrier ? "up" : "down"); + netdev_info(dev, "link %s\n", + new_carrier ? "up" : "down"); } } @@ -1211,7 +1210,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) int status, mask, timeout, card_stats; int saved_pointer; - DBG(3, "%s: %s\n", dev->name, __func__); + DBG(3, dev, "%s\n", __func__); spin_lock(&lp->lock); @@ -1230,12 +1229,12 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) do { status = SMC_GET_INT(lp); - DBG(2, "%s: INT 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n", - dev->name, status, mask, - ({ int meminfo; SMC_SELECT_BANK(lp, 0); - meminfo = SMC_GET_MIR(lp); - SMC_SELECT_BANK(lp, 2); meminfo; }), - SMC_GET_FIFO(lp)); + DBG(2, dev, "INT 0x%02x MASK 0x%02x MEM 0x%04x FIFO 0x%04x\n", + status, mask, + ({ int meminfo; SMC_SELECT_BANK(lp, 0); + meminfo = SMC_GET_MIR(lp); + SMC_SELECT_BANK(lp, 2); meminfo; }), + SMC_GET_FIFO(lp)); status &= mask; if (!status) @@ -1243,20 +1242,20 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) if (status & IM_TX_INT) { /* do this before RX as it will free memory quickly */ - DBG(3, "%s: TX int\n", dev->name); + DBG(3, dev, "TX int\n"); smc_tx(dev); SMC_ACK_INT(lp, IM_TX_INT); if (THROTTLE_TX_PKTS) netif_wake_queue(dev); } else if (status & IM_RCV_INT) { - DBG(3, "%s: RX irq\n", dev->name); + DBG(3, dev, "RX irq\n"); smc_rcv(dev); } else if (status & IM_ALLOC_INT) { - DBG(3, "%s: Allocation irq\n", dev->name); + DBG(3, dev, "Allocation irq\n"); tasklet_hi_schedule(&lp->tx_task); mask &= ~IM_ALLOC_INT; } else if (status & IM_TX_EMPTY_INT) { - DBG(3, "%s: TX empty\n", dev->name); + DBG(3, dev, "TX empty\n"); mask &= ~IM_TX_EMPTY_INT; /* update stats */ @@ -1271,10 +1270,10 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) /* multiple collisions */ dev->stats.collisions += card_stats & 0xF; } else if (status & IM_RX_OVRN_INT) { - DBG(1, "%s: RX overrun (EPH_ST 0x%04x)\n", dev->name, - ({ int eph_st; SMC_SELECT_BANK(lp, 0); - eph_st = SMC_GET_EPH_STATUS(lp); - SMC_SELECT_BANK(lp, 2); eph_st; })); + DBG(1, dev, "RX overrun (EPH_ST 0x%04x)\n", + ({ int eph_st; SMC_SELECT_BANK(lp, 0); + eph_st = SMC_GET_EPH_STATUS(lp); + SMC_SELECT_BANK(lp, 2); eph_st; })); SMC_ACK_INT(lp, IM_RX_OVRN_INT); dev->stats.rx_errors++; dev->stats.rx_fifo_errors++; @@ -1285,7 +1284,7 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) smc_phy_interrupt(dev); } else if (status & IM_ERCV_INT) { SMC_ACK_INT(lp, IM_ERCV_INT); - PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT\n", dev->name); + PRINTK(dev, "UNSUPPORTED: ERCV INTERRUPT\n"); } } while (--timeout); @@ -1296,11 +1295,11 @@ static irqreturn_t smc_interrupt(int irq, void *dev_id) #ifndef CONFIG_NET_POLL_CONTROLLER if (timeout == MAX_IRQ_LOOPS) - PRINTK("%s: spurious interrupt (mask = 0x%02x)\n", - dev->name, mask); + PRINTK(dev, "spurious interrupt (mask = 0x%02x)\n", + mask); #endif - DBG(3, "%s: Interrupt done (%d loops)\n", - dev->name, MAX_IRQ_LOOPS - timeout); + DBG(3, dev, "Interrupt done (%d loops)\n", + MAX_IRQ_LOOPS - timeout); /* * We return IRQ_HANDLED unconditionally here even if there was @@ -1333,7 +1332,7 @@ static void smc_timeout(struct net_device *dev) void __iomem *ioaddr = lp->base; int status, mask, eph_st, meminfo, fifo; - DBG(2, "%s: %s\n", dev->name, __func__); + DBG(2, dev, "%s\n", __func__); spin_lock_irq(&lp->lock); status = SMC_GET_INT(lp); @@ -1344,9 +1343,8 @@ static void smc_timeout(struct net_device *dev) meminfo = SMC_GET_MIR(lp); SMC_SELECT_BANK(lp, 2); spin_unlock_irq(&lp->lock); - PRINTK( "%s: TX timeout (INT 0x%02x INTMASK 0x%02x " - "MEM 0x%04x FIFO 0x%04x EPH_ST 0x%04x)\n", - dev->name, status, mask, meminfo, fifo, eph_st ); + PRINTK(dev, "TX timeout (INT 0x%02x INTMASK 0x%02x MEM 0x%04x FIFO 0x%04x EPH_ST 0x%04x)\n", + status, mask, meminfo, fifo, eph_st); smc_reset(dev); smc_enable(dev); @@ -1377,10 +1375,10 @@ static void smc_set_multicast_list(struct net_device *dev) unsigned char multicast_table[8]; int update_multicast = 0; - DBG(2, "%s: %s\n", dev->name, __func__); + DBG(2, dev, "%s\n", __func__); if (dev->flags & IFF_PROMISC) { - DBG(2, "%s: RCR_PRMS\n", dev->name); + DBG(2, dev, "RCR_PRMS\n"); lp->rcr_cur_mode |= RCR_PRMS; } @@ -1395,7 +1393,7 @@ static void smc_set_multicast_list(struct net_device *dev) * checked before the table is */ else if (dev->flags & IFF_ALLMULTI || netdev_mc_count(dev) > 16) { - DBG(2, "%s: RCR_ALMUL\n", dev->name); + DBG(2, dev, "RCR_ALMUL\n"); lp->rcr_cur_mode |= RCR_ALMUL; } @@ -1437,7 +1435,7 @@ static void smc_set_multicast_list(struct net_device *dev) /* now, the table can be loaded into the chipset */ update_multicast = 1; } else { - DBG(2, "%s: ~(RCR_PRMS|RCR_ALMUL)\n", dev->name); + DBG(2, dev, "~(RCR_PRMS|RCR_ALMUL)\n"); lp->rcr_cur_mode &= ~(RCR_PRMS | RCR_ALMUL); /* @@ -1470,7 +1468,7 @@ smc_open(struct net_device *dev) { struct smc_local *lp = netdev_priv(dev); - DBG(2, "%s: %s\n", dev->name, __func__); + DBG(2, dev, "%s\n", __func__); /* Setup the default Register Modes */ lp->tcr_cur_mode = TCR_DEFAULT; @@ -1514,7 +1512,7 @@ static int smc_close(struct net_device *dev) { struct smc_local *lp = netdev_priv(dev); - DBG(2, "%s: %s\n", dev->name, __func__); + DBG(2, dev, "%s\n", __func__); netif_stop_queue(dev); netif_carrier_off(dev); @@ -1694,7 +1692,7 @@ static int smc_ethtool_geteeprom(struct net_device *dev, int i; int imax; - DBG(1, "Reading %d bytes at %d(0x%x)\n", + DBG(1, dev, "Reading %d bytes at %d(0x%x)\n", eeprom->len, eeprom->offset, eeprom->offset); imax = smc_ethtool_geteeprom_len(dev); for (i = 0; i < eeprom->len; i += 2) { @@ -1706,7 +1704,7 @@ static int smc_ethtool_geteeprom(struct net_device *dev, ret = smc_read_eeprom_word(dev, offset >> 1, &wbuf); if (ret != 0) return ret; - DBG(2, "Read 0x%x from 0x%x\n", wbuf, offset >> 1); + DBG(2, dev, "Read 0x%x from 0x%x\n", wbuf, offset >> 1); data[i] = (wbuf >> 8) & 0xff; data[i+1] = wbuf & 0xff; } @@ -1719,8 +1717,8 @@ static int smc_ethtool_seteeprom(struct net_device *dev, int i; int imax; - DBG(1, "Writing %d bytes to %d(0x%x)\n", - eeprom->len, eeprom->offset, eeprom->offset); + DBG(1, dev, "Writing %d bytes to %d(0x%x)\n", + eeprom->len, eeprom->offset, eeprom->offset); imax = smc_ethtool_geteeprom_len(dev); for (i = 0; i < eeprom->len; i += 2) { int ret; @@ -1729,7 +1727,7 @@ static int smc_ethtool_seteeprom(struct net_device *dev, if (offset > imax) break; wbuf = (data[i] << 8) | data[i + 1]; - DBG(2, "Writing 0x%x to 0x%x\n", wbuf, offset >> 1); + DBG(2, dev, "Writing 0x%x to 0x%x\n", wbuf, offset >> 1); ret = smc_write_eeprom_word(dev, offset >> 1, wbuf); if (ret != 0) return ret; @@ -1784,7 +1782,7 @@ static int smc_findirq(struct smc_local *lp) int timeout = 20; unsigned long cookie; - DBG(2, "%s: %s\n", CARDNAME, __func__); + DBG(2, dev, "%s: %s\n", CARDNAME, __func__); cookie = probe_irq_on(); @@ -1856,21 +1854,21 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr, unsigned long irq_flags) { struct smc_local *lp = netdev_priv(dev); - static int version_printed = 0; int retval; unsigned int val, revision_register; const char *version_string; - DBG(2, "%s: %s\n", CARDNAME, __func__); + DBG(2, dev, "%s: %s\n", CARDNAME, __func__); /* First, see if the high byte is 0x33 */ val = SMC_CURRENT_BANK(lp); - DBG(2, "%s: bank signature probe returned 0x%04x\n", CARDNAME, val); + DBG(2, dev, "%s: bank signature probe returned 0x%04x\n", + CARDNAME, val); if ((val & 0xFF00) != 0x3300) { if ((val & 0xFF) == 0x33) { - printk(KERN_WARNING - "%s: Detected possible byte-swapped interface" - " at IOADDR %p\n", CARDNAME, ioaddr); + netdev_warn(dev, + "%s: Detected possible byte-swapped interface at IOADDR %p\n", + CARDNAME, ioaddr); } retval = -ENODEV; goto err_out; @@ -1897,8 +1895,8 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr, val = SMC_GET_BASE(lp); val = ((val & 0x1F00) >> 3) << SMC_IO_SHIFT; if (((unsigned int)ioaddr & (0x3e0 << SMC_IO_SHIFT)) != val) { - printk("%s: IOADDR %p doesn't match configuration (%x).\n", - CARDNAME, ioaddr, val); + netdev_warn(dev, "%s: IOADDR %p doesn't match configuration (%x).\n", + CARDNAME, ioaddr, val); } /* @@ -1908,21 +1906,19 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr, */ SMC_SELECT_BANK(lp, 3); revision_register = SMC_GET_REV(lp); - DBG(2, "%s: revision = 0x%04x\n", CARDNAME, revision_register); + DBG(2, dev, "%s: revision = 0x%04x\n", CARDNAME, revision_register); version_string = chip_ids[ (revision_register >> 4) & 0xF]; if (!version_string || (revision_register & 0xff00) != 0x3300) { /* I don't recognize this chip, so... */ - printk("%s: IO %p: Unrecognized revision register 0x%04x" - ", Contact author.\n", CARDNAME, - ioaddr, revision_register); + netdev_warn(dev, "%s: IO %p: Unrecognized revision register 0x%04x, Contact author.\n", + CARDNAME, ioaddr, revision_register); retval = -ENODEV; goto err_out; } /* At this point I'll assume that the chip is an SMC91x. */ - if (version_printed++ == 0) - printk("%s", version); + pr_info_once("%s\n", version); /* fill in some of the fields */ dev->base_addr = (unsigned long)ioaddr; @@ -1940,7 +1936,7 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr, /* * If dev->irq is 0, then the device has to be banged on to see * what the IRQ is. - * + * * This banging doesn't always detect the IRQ, for unknown reasons. * a workaround is to reset the chip and try again. * @@ -1965,8 +1961,7 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr, } } if (dev->irq == 0) { - printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n", - dev->name); + netdev_warn(dev, "Couldn't autodetect your IRQ. Use irq=xx.\n"); retval = -ENODEV; goto err_out; } @@ -2030,32 +2025,31 @@ static int smc_probe(struct net_device *dev, void __iomem *ioaddr, retval = register_netdev(dev); if (retval == 0) { /* now, print out the card info, in a short format.. */ - printk("%s: %s (rev %d) at %p IRQ %d", - dev->name, version_string, revision_register & 0x0f, - lp->base, dev->irq); + netdev_info(dev, "%s (rev %d) at %p IRQ %d", + version_string, revision_register & 0x0f, + lp->base, dev->irq); if (dev->dma != (unsigned char)-1) - printk(" DMA %d", dev->dma); + pr_cont(" DMA %d", dev->dma); - printk("%s%s\n", + pr_cont("%s%s\n", lp->cfg.flags & SMC91X_NOWAIT ? " [nowait]" : "", THROTTLE_TX_PKTS ? " [throttle_tx]" : ""); if (!is_valid_ether_addr(dev->dev_addr)) { - printk("%s: Invalid ethernet MAC address. Please " - "set using ifconfig\n", dev->name); + netdev_warn(dev, "Invalid ethernet MAC address. Please set using ifconfig\n"); } else { /* Print the Ethernet address */ - printk("%s: Ethernet addr: %pM\n", - dev->name, dev->dev_addr); + netdev_info(dev, "Ethernet addr: %pM\n", + dev->dev_addr); } if (lp->phy_type == 0) { - PRINTK("%s: No PHY found\n", dev->name); + PRINTK(dev, "No PHY found\n"); } else if ((lp->phy_type & 0xfffffff0) == 0x0016f840) { - PRINTK("%s: PHY LAN83C183 (LAN91C111 Internal)\n", dev->name); + PRINTK(dev, "PHY LAN83C183 (LAN91C111 Internal)\n"); } else if ((lp->phy_type & 0xfffffff0) == 0x02821c50) { - PRINTK("%s: PHY LAN83C180\n", dev->name); + PRINTK(dev, "PHY LAN83C180\n"); } } @@ -2165,7 +2159,8 @@ static inline void smc_request_datacs(struct platform_device *pdev, struct net_d return; if(!request_mem_region(res->start, SMC_DATA_EXTENT, CARDNAME)) { - printk(KERN_INFO "%s: failed to request datacs memory region.\n", CARDNAME); + netdev_info(ndev, "%s: failed to request datacs memory region.\n", + CARDNAME); return; } @@ -2307,7 +2302,7 @@ static int smc_drv_probe(struct platform_device *pdev) out_free_netdev: free_netdev(ndev); out: - printk("%s: not found (%d).\n", CARDNAME, ret); + pr_info("%s: not found (%d).\n", CARDNAME, ret); return ret; } diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h index 98eedb90cdc3c0220c6f8f4d26d2b7f945cf9624..c9d4c872e81da1888cf8678ceb2fd7df5381ac03 100644 --- a/drivers/net/ethernet/smsc/smc91x.h +++ b/drivers/net/ethernet/smsc/smc91x.h @@ -907,8 +907,8 @@ static const char * chip_ids[ 16 ] = { ({ \ int __b = SMC_CURRENT_BANK(lp); \ if (unlikely((__b & ~0xf0) != (0x3300 | bank))) { \ - printk( "%s: bank reg screwed (0x%04x)\n", \ - CARDNAME, __b ); \ + pr_err("%s: bank reg screwed (0x%04x)\n", \ + CARDNAME, __b); \ BUG(); \ } \ reg<config.mac)) { - memcpy(dev->dev_addr, pdata->config.mac, 6); + memcpy(dev->dev_addr, pdata->config.mac, ETH_ALEN); SMSC_TRACE(pdata, probe, "MAC Address specified by platform data"); } else { diff --git a/drivers/net/ethernet/smsc/smsc9420.c b/drivers/net/ethernet/smsc/smsc9420.c index 5f9e79f7f2df52f8b6a3c1955bd7e99ba4bbb122..f433d97aa09703eb295284f97825851f483b7807 100644 --- a/drivers/net/ethernet/smsc/smsc9420.c +++ b/drivers/net/ethernet/smsc/smsc9420.c @@ -19,6 +19,8 @@ *************************************************************************** */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -33,7 +35,6 @@ #include "smsc9420.h" #define DRV_NAME "smsc9420" -#define PFX DRV_NAME ": " #define DRV_MDIONAME "smsc9420-mdio" #define DRV_DESCRIPTION "SMSC LAN9420 driver" #define DRV_VERSION "1.01" @@ -97,21 +98,6 @@ static uint debug = -1; module_param(debug, uint, 0); MODULE_PARM_DESC(debug, "debug level"); -#define smsc_dbg(TYPE, f, a...) \ -do { if ((pd)->msg_enable & NETIF_MSG_##TYPE) \ - printk(KERN_DEBUG PFX f "\n", ## a); \ -} while (0) - -#define smsc_info(TYPE, f, a...) \ -do { if ((pd)->msg_enable & NETIF_MSG_##TYPE) \ - printk(KERN_INFO PFX f "\n", ## a); \ -} while (0) - -#define smsc_warn(TYPE, f, a...) \ -do { if ((pd)->msg_enable & NETIF_MSG_##TYPE) \ - printk(KERN_WARNING PFX f "\n", ## a); \ -} while (0) - static inline u32 smsc9420_reg_read(struct smsc9420_pdata *pd, u32 offset) { return ioread32(pd->ioaddr + offset); @@ -140,7 +126,7 @@ static int smsc9420_mii_read(struct mii_bus *bus, int phyaddr, int regidx) /* confirm MII not busy */ if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) { - smsc_warn(DRV, "MII is busy???"); + netif_warn(pd, drv, pd->dev, "MII is busy???\n"); goto out; } @@ -159,7 +145,7 @@ static int smsc9420_mii_read(struct mii_bus *bus, int phyaddr, int regidx) udelay(10); } - smsc_warn(DRV, "MII busy timeout!"); + netif_warn(pd, drv, pd->dev, "MII busy timeout!\n"); out: spin_unlock_irqrestore(&pd->phy_lock, flags); @@ -178,7 +164,7 @@ static int smsc9420_mii_write(struct mii_bus *bus, int phyaddr, int regidx, /* confirm MII not busy */ if ((smsc9420_reg_read(pd, MII_ACCESS) & MII_ACCESS_MII_BUSY_)) { - smsc_warn(DRV, "MII is busy???"); + netif_warn(pd, drv, pd->dev, "MII is busy???\n"); goto out; } @@ -200,7 +186,7 @@ static int smsc9420_mii_write(struct mii_bus *bus, int phyaddr, int regidx, udelay(10); } - smsc_warn(DRV, "MII busy timeout!"); + netif_warn(pd, drv, pd->dev, "MII busy timeout!\n"); out: spin_unlock_irqrestore(&pd->phy_lock, flags); @@ -222,7 +208,7 @@ static int smsc9420_eeprom_reload(struct smsc9420_pdata *pd) BUG_ON(!pd); if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) { - smsc_dbg(DRV, "smsc9420_eeprom_reload: Eeprom busy"); + netif_dbg(pd, drv, pd->dev, "%s: Eeprom busy\n", __func__); return -EIO; } @@ -235,7 +221,7 @@ static int smsc9420_eeprom_reload(struct smsc9420_pdata *pd) return 0; } while (timeout--); - smsc_warn(DRV, "smsc9420_eeprom_reload: Eeprom timed out"); + netif_warn(pd, drv, pd->dev, "%s: Eeprom timed out\n", __func__); return -EIO; } @@ -347,9 +333,9 @@ static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op) int timeout = 100; u32 e2cmd; - smsc_dbg(HW, "op 0x%08x", op); + netif_dbg(pd, hw, pd->dev, "op 0x%08x\n", op); if (smsc9420_reg_read(pd, E2P_CMD) & E2P_CMD_EPC_BUSY_) { - smsc_warn(HW, "Busy at start"); + netif_warn(pd, hw, pd->dev, "Busy at start\n"); return -EBUSY; } @@ -362,12 +348,13 @@ static int smsc9420_eeprom_send_cmd(struct smsc9420_pdata *pd, u32 op) } while ((e2cmd & E2P_CMD_EPC_BUSY_) && (--timeout)); if (!timeout) { - smsc_info(HW, "TIMED OUT"); + netif_info(pd, hw, pd->dev, "TIMED OUT\n"); return -EAGAIN; } if (e2cmd & E2P_CMD_EPC_TIMEOUT_) { - smsc_info(HW, "Error occurred during eeprom operation"); + netif_info(pd, hw, pd->dev, + "Error occurred during eeprom operation\n"); return -EINVAL; } @@ -380,7 +367,7 @@ static int smsc9420_eeprom_read_location(struct smsc9420_pdata *pd, u32 op = E2P_CMD_EPC_CMD_READ_ | address; int ret; - smsc_dbg(HW, "address 0x%x", address); + netif_dbg(pd, hw, pd->dev, "address 0x%x\n", address); ret = smsc9420_eeprom_send_cmd(pd, op); if (!ret) @@ -395,7 +382,7 @@ static int smsc9420_eeprom_write_location(struct smsc9420_pdata *pd, u32 op = E2P_CMD_EPC_CMD_ERASE_ | address; int ret; - smsc_dbg(HW, "address 0x%x, data 0x%x", address, data); + netif_dbg(pd, hw, pd->dev, "address 0x%x, data 0x%x\n", address, data); ret = smsc9420_eeprom_send_cmd(pd, op); if (!ret) { @@ -492,7 +479,8 @@ static void smsc9420_check_mac_address(struct net_device *dev) /* Check if mac address has been specified when bringing interface up */ if (is_valid_ether_addr(dev->dev_addr)) { smsc9420_set_mac_address(dev); - smsc_dbg(PROBE, "MAC Address is specified by configuration"); + netif_dbg(pd, probe, pd->dev, + "MAC Address is specified by configuration\n"); } else { /* Try reading mac address from device. if EEPROM is present * it will already have been set */ @@ -507,12 +495,14 @@ static void smsc9420_check_mac_address(struct net_device *dev) if (is_valid_ether_addr(dev->dev_addr)) { /* eeprom values are valid so use them */ - smsc_dbg(PROBE, "Mac Address is read from EEPROM"); + netif_dbg(pd, probe, pd->dev, + "Mac Address is read from EEPROM\n"); } else { /* eeprom values are invalid, generate random MAC */ eth_hw_addr_random(dev); smsc9420_set_mac_address(dev); - smsc_dbg(PROBE, "MAC Address is set to random"); + netif_dbg(pd, probe, pd->dev, + "MAC Address is set to random\n"); } } } @@ -535,7 +525,7 @@ static void smsc9420_stop_tx(struct smsc9420_pdata *pd) } if (!timeout) - smsc_warn(IFDOWN, "TX DMAC failed to stop"); + netif_warn(pd, ifdown, pd->dev, "TX DMAC failed to stop\n"); /* ACK Tx DMAC stop bit */ smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_TXPS_); @@ -646,7 +636,8 @@ static void smsc9420_stop_rx(struct smsc9420_pdata *pd) } if (!timeout) - smsc_warn(IFDOWN, "RX DMAC did not stop! timeout."); + netif_warn(pd, ifdown, pd->dev, + "RX DMAC did not stop! timeout\n"); /* ACK the Rx DMAC stop bit */ smsc9420_reg_write(pd, DMAC_STATUS, DMAC_STS_RXPS_); @@ -736,7 +727,7 @@ static void smsc9420_dmac_soft_reset(struct smsc9420_pdata *pd) smsc9420_reg_read(pd, BUS_MODE); udelay(2); if (smsc9420_reg_read(pd, BUS_MODE) & BUS_MODE_SWR_) - smsc_warn(DRV, "Software reset not cleared"); + netif_warn(pd, drv, pd->dev, "Software reset not cleared\n"); } static int smsc9420_stop(struct net_device *dev) @@ -855,7 +846,7 @@ static int smsc9420_alloc_rx_buffer(struct smsc9420_pdata *pd, int index) PKT_BUF_SZ, PCI_DMA_FROMDEVICE); if (pci_dma_mapping_error(pd->pdev, mapping)) { dev_kfree_skb_any(skb); - smsc_warn(RX_ERR, "pci_map_single failed!"); + netif_warn(pd, rx_err, pd->dev, "pci_map_single failed!\n"); return -ENOMEM; } @@ -1004,7 +995,8 @@ static netdev_tx_t smsc9420_hard_start_xmit(struct sk_buff *skb, mapping = pci_map_single(pd->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); if (pci_dma_mapping_error(pd->pdev, mapping)) { - smsc_warn(TX_ERR, "pci_map_single failed, dropping packet"); + netif_warn(pd, tx_err, pd->dev, + "pci_map_single failed, dropping packet\n"); return NETDEV_TX_BUSY; } @@ -1056,12 +1048,12 @@ static void smsc9420_set_multicast_list(struct net_device *dev) u32 mac_cr = smsc9420_reg_read(pd, MAC_CR); if (dev->flags & IFF_PROMISC) { - smsc_dbg(HW, "Promiscuous Mode Enabled"); + netif_dbg(pd, hw, pd->dev, "Promiscuous Mode Enabled\n"); mac_cr |= MAC_CR_PRMS_; mac_cr &= (~MAC_CR_MCPAS_); mac_cr &= (~MAC_CR_HPFILT_); } else if (dev->flags & IFF_ALLMULTI) { - smsc_dbg(HW, "Receive all Multicast Enabled"); + netif_dbg(pd, hw, pd->dev, "Receive all Multicast Enabled\n"); mac_cr &= (~MAC_CR_PRMS_); mac_cr |= MAC_CR_MCPAS_; mac_cr &= (~MAC_CR_HPFILT_); @@ -1069,7 +1061,7 @@ static void smsc9420_set_multicast_list(struct net_device *dev) struct netdev_hw_addr *ha; u32 hash_lo = 0, hash_hi = 0; - smsc_dbg(HW, "Multicast filter enabled"); + netif_dbg(pd, hw, pd->dev, "Multicast filter enabled\n"); netdev_for_each_mc_addr(ha, dev) { u32 bit_num = smsc9420_hash(ha->addr); u32 mask = 1 << (bit_num & 0x1F); @@ -1087,7 +1079,7 @@ static void smsc9420_set_multicast_list(struct net_device *dev) mac_cr &= (~MAC_CR_MCPAS_); mac_cr |= MAC_CR_HPFILT_; } else { - smsc_dbg(HW, "Receive own packets only."); + netif_dbg(pd, hw, pd->dev, "Receive own packets only\n"); smsc9420_reg_write(pd, HASHH, 0); smsc9420_reg_write(pd, HASHL, 0); @@ -1115,11 +1107,11 @@ static void smsc9420_phy_update_flowcontrol(struct smsc9420_pdata *pd) else flow = 0; - smsc_info(LINK, "rx pause %s, tx pause %s", - (cap & FLOW_CTRL_RX ? "enabled" : "disabled"), - (cap & FLOW_CTRL_TX ? "enabled" : "disabled")); + netif_info(pd, link, pd->dev, "rx pause %s, tx pause %s\n", + cap & FLOW_CTRL_RX ? "enabled" : "disabled", + cap & FLOW_CTRL_TX ? "enabled" : "disabled"); } else { - smsc_info(LINK, "half duplex"); + netif_info(pd, link, pd->dev, "half duplex\n"); flow = 0; } @@ -1137,10 +1129,10 @@ static void smsc9420_phy_adjust_link(struct net_device *dev) if (phy_dev->duplex != pd->last_duplex) { u32 mac_cr = smsc9420_reg_read(pd, MAC_CR); if (phy_dev->duplex) { - smsc_dbg(LINK, "full duplex mode"); + netif_dbg(pd, link, pd->dev, "full duplex mode\n"); mac_cr |= MAC_CR_FDPX_; } else { - smsc_dbg(LINK, "half duplex mode"); + netif_dbg(pd, link, pd->dev, "half duplex mode\n"); mac_cr &= ~MAC_CR_FDPX_; } smsc9420_reg_write(pd, MAC_CR, mac_cr); @@ -1152,9 +1144,9 @@ static void smsc9420_phy_adjust_link(struct net_device *dev) carrier = netif_carrier_ok(dev); if (carrier != pd->last_carrier) { if (carrier) - smsc_dbg(LINK, "carrier OK"); + netif_dbg(pd, link, pd->dev, "carrier OK\n"); else - smsc_dbg(LINK, "no carrier"); + netif_dbg(pd, link, pd->dev, "no carrier\n"); pd->last_carrier = carrier; } } @@ -1168,24 +1160,24 @@ static int smsc9420_mii_probe(struct net_device *dev) /* Device only supports internal PHY at address 1 */ if (!pd->mii_bus->phy_map[1]) { - pr_err("%s: no PHY found at address 1\n", dev->name); + netdev_err(dev, "no PHY found at address 1\n"); return -ENODEV; } phydev = pd->mii_bus->phy_map[1]; - smsc_info(PROBE, "PHY addr %d, phy_id 0x%08X", phydev->addr, - phydev->phy_id); + netif_info(pd, probe, pd->dev, "PHY addr %d, phy_id 0x%08X\n", + phydev->addr, phydev->phy_id); phydev = phy_connect(dev, dev_name(&phydev->dev), smsc9420_phy_adjust_link, PHY_INTERFACE_MODE_MII); if (IS_ERR(phydev)) { - pr_err("%s: Could not attach to PHY\n", dev->name); + netdev_err(dev, "Could not attach to PHY\n"); return PTR_ERR(phydev); } - pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", - dev->name, phydev->drv->name, dev_name(&phydev->dev), phydev->irq); + netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n", + phydev->drv->name, dev_name(&phydev->dev), phydev->irq); /* mask with MAC supported features */ phydev->supported &= (PHY_BASIC_FEATURES | SUPPORTED_Pause | @@ -1223,12 +1215,12 @@ static int smsc9420_mii_init(struct net_device *dev) pd->mii_bus->phy_mask = ~(1 << 1); if (mdiobus_register(pd->mii_bus)) { - smsc_warn(PROBE, "Error registering mii bus"); + netif_warn(pd, probe, pd->dev, "Error registering mii bus\n"); goto err_out_free_bus_2; } if (smsc9420_mii_probe(dev) < 0) { - smsc_warn(PROBE, "Error probing mii bus"); + netif_warn(pd, probe, pd->dev, "Error probing mii bus\n"); goto err_out_unregister_bus_3; } @@ -1281,12 +1273,11 @@ static int smsc9420_alloc_rx_ring(struct smsc9420_pdata *pd) BUG_ON(!pd->rx_ring); - pd->rx_buffers = kmalloc((sizeof(struct smsc9420_ring_info) * - RX_RING_SIZE), GFP_KERNEL); - if (pd->rx_buffers == NULL) { - smsc_warn(IFUP, "Failed to allocated rx_buffers"); + pd->rx_buffers = kmalloc_array(RX_RING_SIZE, + sizeof(struct smsc9420_ring_info), + GFP_KERNEL); + if (pd->rx_buffers == NULL) goto out; - } /* initialize the rx ring */ for (i = 0; i < RX_RING_SIZE; i++) { @@ -1301,7 +1292,8 @@ static int smsc9420_alloc_rx_ring(struct smsc9420_pdata *pd) /* now allocate the entire ring of skbs */ for (i = 0; i < RX_RING_SIZE; i++) { if (smsc9420_alloc_rx_buffer(pd, i)) { - smsc_warn(IFUP, "failed to allocate rx skb %d", i); + netif_warn(pd, ifup, pd->dev, + "failed to allocate rx skb %d\n", i); goto out_free_rx_skbs; } } @@ -1310,13 +1302,14 @@ static int smsc9420_alloc_rx_ring(struct smsc9420_pdata *pd) pd->rx_ring_tail = 0; smsc9420_reg_write(pd, VLAN1, ETH_P_8021Q); - smsc_dbg(IFUP, "VLAN1 = 0x%08x", smsc9420_reg_read(pd, VLAN1)); + netif_dbg(pd, ifup, pd->dev, "VLAN1 = 0x%08x\n", + smsc9420_reg_read(pd, VLAN1)); if (pd->rx_csum) { /* Enable RX COE */ u32 coe = smsc9420_reg_read(pd, COE_CR) | RX_COE_EN; smsc9420_reg_write(pd, COE_CR, coe); - smsc_dbg(IFUP, "COE_CR = 0x%08x", coe); + netif_dbg(pd, ifup, pd->dev, "COE_CR = 0x%08x\n", coe); } smsc9420_reg_write(pd, RX_BASE_ADDR, pd->rx_dma_addr); @@ -1339,7 +1332,8 @@ static int smsc9420_open(struct net_device *dev) int result = 0, timeout; if (!is_valid_ether_addr(dev->dev_addr)) { - smsc_warn(IFUP, "dev_addr is not a valid MAC address"); + netif_warn(pd, ifup, pd->dev, + "dev_addr is not a valid MAC address\n"); result = -EADDRNOTAVAIL; goto out_0; } @@ -1358,7 +1352,7 @@ static int smsc9420_open(struct net_device *dev) result = request_irq(irq, smsc9420_isr, IRQF_SHARED, DRV_NAME, pd); if (result) { - smsc_warn(IFUP, "Unable to use IRQ = %d", irq); + netif_warn(pd, ifup, pd->dev, "Unable to use IRQ = %d\n", irq); result = -ENODEV; goto out_0; } @@ -1393,7 +1387,7 @@ static int smsc9420_open(struct net_device *dev) smsc9420_pci_flush_write(pd); /* test the IRQ connection to the ISR */ - smsc_dbg(IFUP, "Testing ISR using IRQ %d", irq); + netif_dbg(pd, ifup, pd->dev, "Testing ISR using IRQ %d\n", irq); pd->software_irq_signal = false; spin_lock_irqsave(&pd->int_lock, flags); @@ -1423,30 +1417,32 @@ static int smsc9420_open(struct net_device *dev) spin_unlock_irqrestore(&pd->int_lock, flags); if (!pd->software_irq_signal) { - smsc_warn(IFUP, "ISR failed signaling test"); + netif_warn(pd, ifup, pd->dev, "ISR failed signaling test\n"); result = -ENODEV; goto out_free_irq_1; } - smsc_dbg(IFUP, "ISR passed test using IRQ %d", irq); + netif_dbg(pd, ifup, pd->dev, "ISR passed test using IRQ %d\n", irq); result = smsc9420_alloc_tx_ring(pd); if (result) { - smsc_warn(IFUP, "Failed to Initialize tx dma ring"); + netif_warn(pd, ifup, pd->dev, + "Failed to Initialize tx dma ring\n"); result = -ENOMEM; goto out_free_irq_1; } result = smsc9420_alloc_rx_ring(pd); if (result) { - smsc_warn(IFUP, "Failed to Initialize rx dma ring"); + netif_warn(pd, ifup, pd->dev, + "Failed to Initialize rx dma ring\n"); result = -ENOMEM; goto out_free_tx_ring_2; } result = smsc9420_mii_init(dev); if (result) { - smsc_warn(IFUP, "Failed to initialize Phy"); + netif_warn(pd, ifup, pd->dev, "Failed to initialize Phy\n"); result = -ENODEV; goto out_free_rx_ring_3; } @@ -1547,7 +1543,8 @@ static int smsc9420_resume(struct pci_dev *pdev) err = pci_enable_wake(pdev, 0, 0); if (err) - smsc_warn(IFUP, "pci_enable_wake failed: %d", err); + netif_warn(pd, ifup, pd->dev, "pci_enable_wake failed: %d\n", + err); if (netif_running(dev)) { /* FIXME: gross. It looks like ancient PM relic.*/ @@ -1582,12 +1579,12 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id) int result = 0; u32 id_rev; - printk(KERN_INFO DRV_DESCRIPTION " version " DRV_VERSION "\n"); + pr_info("%s version %s\n", DRV_DESCRIPTION, DRV_VERSION); /* First do the PCI initialisation */ result = pci_enable_device(pdev); if (unlikely(result)) { - printk(KERN_ERR "Cannot enable smsc9420\n"); + pr_err("Cannot enable smsc9420\n"); goto out_0; } @@ -1600,24 +1597,24 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id) SET_NETDEV_DEV(dev, &pdev->dev); if (!(pci_resource_flags(pdev, SMSC_BAR) & IORESOURCE_MEM)) { - printk(KERN_ERR "Cannot find PCI device base address\n"); + netdev_err(dev, "Cannot find PCI device base address\n"); goto out_free_netdev_2; } if ((pci_request_regions(pdev, DRV_NAME))) { - printk(KERN_ERR "Cannot obtain PCI resources, aborting.\n"); + netdev_err(dev, "Cannot obtain PCI resources, aborting\n"); goto out_free_netdev_2; } if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { - printk(KERN_ERR "No usable DMA configuration, aborting.\n"); + netdev_err(dev, "No usable DMA configuration, aborting\n"); goto out_free_regions_3; } virt_addr = ioremap(pci_resource_start(pdev, SMSC_BAR), pci_resource_len(pdev, SMSC_BAR)); if (!virt_addr) { - printk(KERN_ERR "Cannot map device registers, aborting.\n"); + netdev_err(dev, "Cannot map device registers, aborting\n"); goto out_free_regions_3; } @@ -1646,16 +1643,17 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id) pd->msg_enable = smsc_debug; pd->rx_csum = true; - smsc_dbg(PROBE, "lan_base=0x%08lx", (ulong)virt_addr); + netif_dbg(pd, probe, pd->dev, "lan_base=0x%08lx\n", (ulong)virt_addr); id_rev = smsc9420_reg_read(pd, ID_REV); switch (id_rev & 0xFFFF0000) { case 0x94200000: - smsc_info(PROBE, "LAN9420 identified, ID_REV=0x%08X", id_rev); + netif_info(pd, probe, pd->dev, + "LAN9420 identified, ID_REV=0x%08X\n", id_rev); break; default: - smsc_warn(PROBE, "LAN9420 NOT identified"); - smsc_warn(PROBE, "ID_REV=0x%08X", id_rev); + netif_warn(pd, probe, pd->dev, "LAN9420 NOT identified\n"); + netif_warn(pd, probe, pd->dev, "ID_REV=0x%08X\n", id_rev); goto out_free_dmadesc_5; } @@ -1670,7 +1668,8 @@ smsc9420_probe(struct pci_dev *pdev, const struct pci_device_id *id) result = register_netdev(dev); if (result) { - smsc_warn(PROBE, "error %i registering device", result); + netif_warn(pd, probe, pd->dev, "error %i registering device\n", + result); goto out_free_dmadesc_5; } @@ -1707,8 +1706,6 @@ static void smsc9420_remove(struct pci_dev *pdev) if (!dev) return; - pci_set_drvdata(pdev, NULL); - pd = netdev_priv(dev); unregister_netdev(dev); diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 7eb8babed2cbe38f822aab596f2451d75112c9a4..fc94f202a43e40f24247019fb6c5906da8ac20be 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -451,14 +451,14 @@ struct mac_device_info { struct mac_device_info *dwmac1000_setup(void __iomem *ioaddr); struct mac_device_info *dwmac100_setup(void __iomem *ioaddr); -extern void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6], - unsigned int high, unsigned int low); -extern void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr, - unsigned int high, unsigned int low); +void stmmac_set_mac_addr(void __iomem *ioaddr, u8 addr[6], + unsigned int high, unsigned int low); +void stmmac_get_mac_addr(void __iomem *ioaddr, unsigned char *addr, + unsigned int high, unsigned int low); -extern void stmmac_set_mac(void __iomem *ioaddr, bool enable); +void stmmac_set_mac(void __iomem *ioaddr, bool enable); -extern void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr); +void dwmac_dma_flush_tx_fifo(void __iomem *ioaddr); extern const struct stmmac_ring_mode_ops ring_mode_ops; extern const struct stmmac_chain_mode_ops chain_mode_ops; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index 8e5662ce488bd80d2a0a7457c25fbf49ca523bb1..def266da55dbe617e8be83f7a3d630f8230b9c5c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -104,14 +104,13 @@ #define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ #define DMA_CONTROL_FTF 0x00100000 /* Flush transmit FIFO */ -extern void dwmac_enable_dma_transmission(void __iomem *ioaddr); -extern void dwmac_enable_dma_irq(void __iomem *ioaddr); -extern void dwmac_disable_dma_irq(void __iomem *ioaddr); -extern void dwmac_dma_start_tx(void __iomem *ioaddr); -extern void dwmac_dma_stop_tx(void __iomem *ioaddr); -extern void dwmac_dma_start_rx(void __iomem *ioaddr); -extern void dwmac_dma_stop_rx(void __iomem *ioaddr); -extern int dwmac_dma_interrupt(void __iomem *ioaddr, - struct stmmac_extra_stats *x); +void dwmac_enable_dma_transmission(void __iomem *ioaddr); +void dwmac_enable_dma_irq(void __iomem *ioaddr); +void dwmac_disable_dma_irq(void __iomem *ioaddr); +void dwmac_dma_start_tx(void __iomem *ioaddr); +void dwmac_dma_stop_tx(void __iomem *ioaddr); +void dwmac_dma_start_rx(void __iomem *ioaddr); +void dwmac_dma_stop_rx(void __iomem *ioaddr); +int dwmac_dma_interrupt(void __iomem *ioaddr, struct stmmac_extra_stats *x); #endif /* __DWMAC_DMA_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/mmc.h b/drivers/net/ethernet/stmicro/stmmac/mmc.h index 48ec001566b5540421c5e5a25fe0b18db3c25e58..8607488cbcfcfaea7bc70510f0a3f601c5dade7d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/mmc.h +++ b/drivers/net/ethernet/stmicro/stmmac/mmc.h @@ -128,8 +128,8 @@ struct stmmac_counters { unsigned int mmc_rx_icmp_err_octets; }; -extern void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode); -extern void dwmac_mmc_intr_all_mask(void __iomem *ioaddr); -extern void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc); +void dwmac_mmc_ctrl(void __iomem *ioaddr, unsigned int mode); +void dwmac_mmc_intr_all_mask(void __iomem *ioaddr); +void dwmac_mmc_read(void __iomem *ioaddr, struct stmmac_counters *mmc); #endif /* __MMC_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index f16a9bdf45bb6738c7fca4063cf96307d6303462..22f89ffdfd95fc3e8ca530808e4d6d0dfdcfeaf2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -110,14 +110,14 @@ struct stmmac_priv { extern int phyaddr; -extern int stmmac_mdio_unregister(struct net_device *ndev); -extern int stmmac_mdio_register(struct net_device *ndev); -extern void stmmac_set_ethtool_ops(struct net_device *netdev); +int stmmac_mdio_unregister(struct net_device *ndev); +int stmmac_mdio_register(struct net_device *ndev); +void stmmac_set_ethtool_ops(struct net_device *netdev); extern const struct stmmac_desc_ops enh_desc_ops; extern const struct stmmac_desc_ops ndesc_ops; extern const struct stmmac_hwtimestamp stmmac_ptp; -extern int stmmac_ptp_register(struct stmmac_priv *priv); -extern void stmmac_ptp_unregister(struct stmmac_priv *priv); +int stmmac_ptp_register(struct stmmac_priv *priv); +void stmmac_ptp_unregister(struct stmmac_priv *priv); int stmmac_freeze(struct net_device *ndev); int stmmac_restore(struct net_device *ndev); int stmmac_resume(struct net_device *ndev); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index 023b7c29cb2fc3408ff3e3a00c03b9656ddd2ab4..644d80ece067717f3dd296d6ed5f54d9c90a7134 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -138,7 +138,6 @@ static void stmmac_pci_remove(struct pci_dev *pdev) stmmac_dvr_remove(ndev); - pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, priv->ioaddr); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/ethernet/sun/cassini.c b/drivers/net/ethernet/sun/cassini.c index 759441b29e535b4c7b1c0f94e6f7520fc628fbdd..b4d50d74ba183b29156bce538af3c5c03369d9ea 100644 --- a/drivers/net/ethernet/sun/cassini.c +++ b/drivers/net/ethernet/sun/cassini.c @@ -3354,7 +3354,7 @@ use_random_mac_addr: #if defined(CONFIG_SPARC) addr = of_get_property(cp->of_node, "local-mac-address", NULL); if (addr != NULL) { - memcpy(dev_addr, addr, 6); + memcpy(dev_addr, addr, ETH_ALEN); goto done; } #endif @@ -5168,7 +5168,6 @@ err_out_free_netdev: err_out_disable_pdev: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); return -ENODEV; } @@ -5206,7 +5205,6 @@ static void cas_remove_one(struct pci_dev *pdev) free_netdev(dev); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index f28460ce24a7134a371dd148887cc5466e934f8b..388540fcb9773dab8973094bb4ec76f74fe4f73d 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -9875,7 +9875,6 @@ err_out_free_res: err_out_disable_pdev: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); return err; } @@ -9900,7 +9899,6 @@ static void niu_pci_remove_one(struct pci_dev *pdev) free_netdev(dev); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } } diff --git a/drivers/net/ethernet/sun/sungem.c b/drivers/net/ethernet/sun/sungem.c index e62df2b81302bd32881ca22daef127e04985679e..b5655b79bd3b25bcd2e90376fd18e67fb78e1f93 100644 --- a/drivers/net/ethernet/sun/sungem.c +++ b/drivers/net/ethernet/sun/sungem.c @@ -2779,7 +2779,7 @@ static int gem_get_device_address(struct gem *gp) return -1; #endif } - memcpy(dev->dev_addr, addr, 6); + memcpy(dev->dev_addr, addr, ETH_ALEN); #else get_gem_mac_nonobp(gp->pdev, gp->dev->dev_addr); #endif @@ -2806,8 +2806,6 @@ static void gem_remove_one(struct pci_dev *pdev) iounmap(gp->regs); pci_release_regions(pdev); free_netdev(dev); - - pci_set_drvdata(pdev, NULL); } } diff --git a/drivers/net/ethernet/sun/sunhme.c b/drivers/net/ethernet/sun/sunhme.c index e37b587b386048dd299ac6ad0af55dd47d3e199f..0dbf46f08ed56a479a647a18f4c73692f2ec6160 100644 --- a/drivers/net/ethernet/sun/sunhme.c +++ b/drivers/net/ethernet/sun/sunhme.c @@ -2675,10 +2675,10 @@ static int happy_meal_sbus_probe_one(struct platform_device *op, int is_qfe) addr = of_get_property(dp, "local-mac-address", &len); - if (qfe_slot != -1 && addr && len == 6) - memcpy(dev->dev_addr, addr, 6); + if (qfe_slot != -1 && addr && len == ETH_ALEN) + memcpy(dev->dev_addr, addr, ETH_ALEN); else - memcpy(dev->dev_addr, idprom->id_ethaddr, 6); + memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN); } hp = netdev_priv(dev); @@ -3024,9 +3024,9 @@ static int happy_meal_pci_probe(struct pci_dev *pdev, (addr = of_get_property(dp, "local-mac-address", &len)) != NULL && len == 6) { - memcpy(dev->dev_addr, addr, 6); + memcpy(dev->dev_addr, addr, ETH_ALEN); } else { - memcpy(dev->dev_addr, idprom->id_ethaddr, 6); + memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN); } #else get_hme_mac_nonsparc(pdev, &dev->dev_addr[0]); @@ -3170,8 +3170,6 @@ static void happy_meal_pci_remove(struct pci_dev *pdev) pci_release_regions(hp->happy_dev); free_netdev(net_dev); - - pci_set_drvdata(pdev, NULL); } static DEFINE_PCI_DEVICE_TABLE(happymeal_pci_ids) = { diff --git a/drivers/net/ethernet/sun/sunqe.c b/drivers/net/ethernet/sun/sunqe.c index b072f4dba033c1661bf4a341747b1d82d4874fc7..5695ae2411dea0f74718d3a87254aab4cf97ac42 100644 --- a/drivers/net/ethernet/sun/sunqe.c +++ b/drivers/net/ethernet/sun/sunqe.c @@ -843,7 +843,7 @@ static int qec_ether_init(struct platform_device *op) if (!dev) return -ENOMEM; - memcpy(dev->dev_addr, idprom->id_ethaddr, 6); + memcpy(dev->dev_addr, idprom->id_ethaddr, ETH_ALEN); qe = netdev_priv(dev); diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index 571452e786d5103460180e54481cd11ae88e4927..dd0dd6279b4eec8168c006457643568032ee358f 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -2447,7 +2447,6 @@ static void bdx_remove(struct pci_dev *pdev) iounmap(nic->regs); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); vfree(nic); RET(); diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index de71b1ec4625e8f21a9ad4d56db5fa8592c89e6e..53150c25a96bd455f58dc30ca3c592b6a598461e 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -49,11 +49,19 @@ config TI_DAVINCI_CPDMA To compile this driver as a module, choose M here: the module will be called davinci_cpdma. This is recommended. +config TI_CPSW_PHY_SEL + boolean "TI CPSW Switch Phy sel Support" + depends on TI_CPSW + ---help--- + This driver supports configuring of the phy mode connected to + the CPSW. + config TI_CPSW tristate "TI CPSW Switch Support" depends on ARM && (ARCH_DAVINCI || SOC_AM33XX) select TI_DAVINCI_CPDMA select TI_DAVINCI_MDIO + select TI_CPSW_PHY_SEL ---help--- This driver supports TI's CPSW Ethernet Switch. diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile index c65148e8aa1d4810e00cbe6d7448fa72572847d7..9cfaab8152be08aa16bc37c91f622ba8e287989b 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -7,5 +7,6 @@ obj-$(CONFIG_CPMAC) += cpmac.o obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o +obj-$(CONFIG_TI_CPSW_PHY_SEL) += cpsw-phy-sel.o obj-$(CONFIG_TI_CPSW) += ti_cpsw.o ti_cpsw-y := cpsw_ale.o cpsw.o cpts.o diff --git a/drivers/net/ethernet/ti/cpsw-phy-sel.c b/drivers/net/ethernet/ti/cpsw-phy-sel.c new file mode 100644 index 0000000000000000000000000000000000000000..148da9ae83666ce7cd2284c1cc75f18e9957f15f --- /dev/null +++ b/drivers/net/ethernet/ti/cpsw-phy-sel.c @@ -0,0 +1,161 @@ +/* Texas Instruments Ethernet Switch Driver + * + * Copyright (C) 2013 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "cpsw.h" + +/* AM33xx SoC specific definitions for the CONTROL port */ +#define AM33XX_GMII_SEL_MODE_MII 0 +#define AM33XX_GMII_SEL_MODE_RMII 1 +#define AM33XX_GMII_SEL_MODE_RGMII 2 + +#define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7) +#define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6) + +struct cpsw_phy_sel_priv { + struct device *dev; + u32 __iomem *gmii_sel; + bool rmii_clock_external; + void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv, + phy_interface_t phy_mode, int slave); +}; + + +static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv, + phy_interface_t phy_mode, int slave) +{ + u32 reg; + u32 mask; + u32 mode = 0; + + reg = readl(priv->gmii_sel); + + switch (phy_mode) { + case PHY_INTERFACE_MODE_RMII: + mode = AM33XX_GMII_SEL_MODE_RMII; + break; + + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + mode = AM33XX_GMII_SEL_MODE_RGMII; + break; + + case PHY_INTERFACE_MODE_MII: + default: + mode = AM33XX_GMII_SEL_MODE_MII; + break; + }; + + mask = 0x3 << (slave * 2) | BIT(slave + 6); + mode <<= slave * 2; + + if (priv->rmii_clock_external) { + if (slave == 0) + mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN; + else + mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN; + } + + reg &= ~mask; + reg |= mode; + + writel(reg, priv->gmii_sel); +} + +static struct platform_driver cpsw_phy_sel_driver; +static int match(struct device *dev, void *data) +{ + struct device_node *node = (struct device_node *)data; + return dev->of_node == node && + dev->driver == &cpsw_phy_sel_driver.driver; +} + +void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave) +{ + struct device_node *node; + struct cpsw_phy_sel_priv *priv; + + node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel"); + if (!node) { + dev_err(dev, "Phy mode driver DT not found\n"); + return; + } + + dev = bus_find_device(&platform_bus_type, NULL, node, match); + priv = dev_get_drvdata(dev); + + priv->cpsw_phy_sel(priv, phy_mode, slave); +} +EXPORT_SYMBOL_GPL(cpsw_phy_sel); + +static const struct of_device_id cpsw_phy_sel_id_table[] = { + { + .compatible = "ti,am3352-cpsw-phy-sel", + .data = &cpsw_gmii_sel_am3352, + }, + {} +}; +MODULE_DEVICE_TABLE(of, cpsw_phy_sel_id_table); + +static int cpsw_phy_sel_probe(struct platform_device *pdev) +{ + struct resource *res; + const struct of_device_id *of_id; + struct cpsw_phy_sel_priv *priv; + + of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node); + if (!of_id) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n"); + return -ENOMEM; + } + + priv->cpsw_phy_sel = of_id->data; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel"); + priv->gmii_sel = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->gmii_sel)) + return PTR_ERR(priv->gmii_sel); + + if (of_find_property(pdev->dev.of_node, "rmii-clock-ext", NULL)) + priv->rmii_clock_external = true; + + dev_set_drvdata(&pdev->dev, priv); + + return 0; +} + +static struct platform_driver cpsw_phy_sel_driver = { + .probe = cpsw_phy_sel_probe, + .driver = { + .name = "cpsw-phy-sel", + .owner = THIS_MODULE, + .of_match_table = cpsw_phy_sel_id_table, + }, +}; + +module_platform_driver(cpsw_phy_sel_driver); +MODULE_AUTHOR("Mugunthan V N "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index cc3ce557e4aa62074873ee34e348ec24a0719ccf..90d41d26ec6d8c37f04682aa05b8731a4d4c3002 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -367,8 +367,6 @@ struct cpsw_priv { spinlock_t lock; struct platform_device *pdev; struct net_device *ndev; - struct resource *cpsw_res; - struct resource *cpsw_wr_res; struct napi_struct napi; struct device *dev; struct cpsw_platform_data data; @@ -1016,6 +1014,10 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) dev_info(priv->dev, "phy found : id is : 0x%x\n", slave->phy->phy_id); phy_start(slave->phy); + + /* Configure GMII_SEL register */ + cpsw_phy_sel(&priv->pdev->dev, slave->phy->interface, + slave->slave_num); } } @@ -1705,62 +1707,55 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, if (of_property_read_u32(node, "active_slave", &prop)) { pr_err("Missing active_slave property in the DT.\n"); - ret = -EINVAL; - goto error_ret; + return -EINVAL; } data->active_slave = prop; if (of_property_read_u32(node, "cpts_clock_mult", &prop)) { pr_err("Missing cpts_clock_mult property in the DT.\n"); - ret = -EINVAL; - goto error_ret; + return -EINVAL; } data->cpts_clock_mult = prop; if (of_property_read_u32(node, "cpts_clock_shift", &prop)) { pr_err("Missing cpts_clock_shift property in the DT.\n"); - ret = -EINVAL; - goto error_ret; + return -EINVAL; } data->cpts_clock_shift = prop; - data->slave_data = kcalloc(data->slaves, sizeof(struct cpsw_slave_data), - GFP_KERNEL); + data->slave_data = devm_kzalloc(&pdev->dev, data->slaves + * sizeof(struct cpsw_slave_data), + GFP_KERNEL); if (!data->slave_data) - return -EINVAL; + return -ENOMEM; if (of_property_read_u32(node, "cpdma_channels", &prop)) { pr_err("Missing cpdma_channels property in the DT.\n"); - ret = -EINVAL; - goto error_ret; + return -EINVAL; } data->channels = prop; if (of_property_read_u32(node, "ale_entries", &prop)) { pr_err("Missing ale_entries property in the DT.\n"); - ret = -EINVAL; - goto error_ret; + return -EINVAL; } data->ale_entries = prop; if (of_property_read_u32(node, "bd_ram_size", &prop)) { pr_err("Missing bd_ram_size property in the DT.\n"); - ret = -EINVAL; - goto error_ret; + return -EINVAL; } data->bd_ram_size = prop; if (of_property_read_u32(node, "rx_descs", &prop)) { pr_err("Missing rx_descs property in the DT.\n"); - ret = -EINVAL; - goto error_ret; + return -EINVAL; } data->rx_descs = prop; if (of_property_read_u32(node, "mac_control", &prop)) { pr_err("Missing mac_control property in the DT.\n"); - ret = -EINVAL; - goto error_ret; + return -EINVAL; } data->mac_control = prop; @@ -1791,8 +1786,7 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, parp = of_get_property(slave_node, "phy_id", &lenp); if ((parp == NULL) || (lenp != (sizeof(void *) * 2))) { pr_err("Missing slave[%d] phy_id property\n", i); - ret = -EINVAL; - goto error_ret; + return -EINVAL; } mdio_node = of_find_node_by_phandle(be32_to_cpup(parp)); phyid = be32_to_cpup(parp+1); @@ -1822,10 +1816,6 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, } return 0; - -error_ret: - kfree(data->slave_data); - return ret; } static int cpsw_probe_dual_emac(struct platform_device *pdev, @@ -1867,7 +1857,6 @@ static int cpsw_probe_dual_emac(struct platform_device *pdev, priv_sl2->coal_intvl = 0; priv_sl2->bus_freq_mhz = priv->bus_freq_mhz; - priv_sl2->cpsw_res = priv->cpsw_res; priv_sl2->regs = priv->regs; priv_sl2->host_port = priv->host_port; priv_sl2->host_port_regs = priv->host_port_regs; @@ -1911,8 +1900,8 @@ static int cpsw_probe(struct platform_device *pdev) struct cpsw_priv *priv; struct cpdma_params dma_params; struct cpsw_ale_params ale_params; - void __iomem *ss_regs, *wr_regs; - struct resource *res; + void __iomem *ss_regs; + struct resource *res, *ss_res; u32 slave_offset, sliver_offset, slave_size; int ret = 0, i, k = 0; @@ -1948,7 +1937,7 @@ static int cpsw_probe(struct platform_device *pdev) if (cpsw_probe_dt(&priv->data, pdev)) { pr_err("cpsw: platform data missing\n"); ret = -ENODEV; - goto clean_ndev_ret; + goto clean_runtime_disable_ret; } data = &priv->data; @@ -1962,11 +1951,12 @@ static int cpsw_probe(struct platform_device *pdev) memcpy(ndev->dev_addr, priv->mac_addr, ETH_ALEN); - priv->slaves = kzalloc(sizeof(struct cpsw_slave) * data->slaves, - GFP_KERNEL); + priv->slaves = devm_kzalloc(&pdev->dev, + sizeof(struct cpsw_slave) * data->slaves, + GFP_KERNEL); if (!priv->slaves) { - ret = -EBUSY; - goto clean_ndev_ret; + ret = -ENOMEM; + goto clean_runtime_disable_ret; } for (i = 0; i < data->slaves; i++) priv->slaves[i].slave_num = i; @@ -1974,55 +1964,31 @@ static int cpsw_probe(struct platform_device *pdev) priv->slaves[0].ndev = ndev; priv->emac_port = 0; - priv->clk = clk_get(&pdev->dev, "fck"); + priv->clk = devm_clk_get(&pdev->dev, "fck"); if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "fck is not found\n"); + dev_err(priv->dev, "fck is not found\n"); ret = -ENODEV; - goto clean_slave_ret; + goto clean_runtime_disable_ret; } priv->coal_intvl = 0; priv->bus_freq_mhz = clk_get_rate(priv->clk) / 1000000; - priv->cpsw_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!priv->cpsw_res) { - dev_err(priv->dev, "error getting i/o resource\n"); - ret = -ENOENT; - goto clean_clk_ret; - } - if (!request_mem_region(priv->cpsw_res->start, - resource_size(priv->cpsw_res), ndev->name)) { - dev_err(priv->dev, "failed request i/o region\n"); - ret = -ENXIO; - goto clean_clk_ret; - } - ss_regs = ioremap(priv->cpsw_res->start, resource_size(priv->cpsw_res)); - if (!ss_regs) { - dev_err(priv->dev, "unable to map i/o region\n"); - goto clean_cpsw_iores_ret; + ss_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ss_regs = devm_ioremap_resource(&pdev->dev, ss_res); + if (IS_ERR(ss_regs)) { + ret = PTR_ERR(ss_regs); + goto clean_runtime_disable_ret; } priv->regs = ss_regs; priv->version = __raw_readl(&priv->regs->id_ver); priv->host_port = HOST_PORT_NUM; - priv->cpsw_wr_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!priv->cpsw_wr_res) { - dev_err(priv->dev, "error getting i/o resource\n"); - ret = -ENOENT; - goto clean_iomap_ret; - } - if (!request_mem_region(priv->cpsw_wr_res->start, - resource_size(priv->cpsw_wr_res), ndev->name)) { - dev_err(priv->dev, "failed request i/o region\n"); - ret = -ENXIO; - goto clean_iomap_ret; + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->wr_regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->wr_regs)) { + ret = PTR_ERR(priv->wr_regs); + goto clean_runtime_disable_ret; } - wr_regs = ioremap(priv->cpsw_wr_res->start, - resource_size(priv->cpsw_wr_res)); - if (!wr_regs) { - dev_err(priv->dev, "unable to map i/o region\n"); - goto clean_cpsw_wr_iores_ret; - } - priv->wr_regs = wr_regs; memset(&dma_params, 0, sizeof(dma_params)); memset(&ale_params, 0, sizeof(ale_params)); @@ -2053,12 +2019,12 @@ static int cpsw_probe(struct platform_device *pdev) slave_size = CPSW2_SLAVE_SIZE; sliver_offset = CPSW2_SLIVER_OFFSET; dma_params.desc_mem_phys = - (u32 __force) priv->cpsw_res->start + CPSW2_BD_OFFSET; + (u32 __force) ss_res->start + CPSW2_BD_OFFSET; break; default: dev_err(priv->dev, "unknown version 0x%08x\n", priv->version); ret = -ENODEV; - goto clean_cpsw_wr_iores_ret; + goto clean_runtime_disable_ret; } for (i = 0; i < priv->data.slaves; i++) { struct cpsw_slave *slave = &priv->slaves[i]; @@ -2086,7 +2052,7 @@ static int cpsw_probe(struct platform_device *pdev) if (!priv->dma) { dev_err(priv->dev, "error initializing dma\n"); ret = -ENOMEM; - goto clean_wr_iomap_ret; + goto clean_runtime_disable_ret; } priv->txch = cpdma_chan_create(priv->dma, tx_chan_num(0), @@ -2121,8 +2087,8 @@ static int cpsw_probe(struct platform_device *pdev) while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) { for (i = res->start; i <= res->end; i++) { - if (request_irq(i, cpsw_interrupt, 0, - dev_name(&pdev->dev), priv)) { + if (devm_request_irq(&pdev->dev, i, cpsw_interrupt, 0, + dev_name(priv->dev), priv)) { dev_err(priv->dev, "error attaching irq\n"); goto clean_ale_ret; } @@ -2144,7 +2110,7 @@ static int cpsw_probe(struct platform_device *pdev) if (ret) { dev_err(priv->dev, "error registering net device\n"); ret = -ENODEV; - goto clean_irq_ret; + goto clean_ale_ret; } if (cpts_register(&pdev->dev, priv->cpts, @@ -2152,44 +2118,27 @@ static int cpsw_probe(struct platform_device *pdev) dev_err(priv->dev, "error registering cpts device\n"); cpsw_notice(priv, probe, "initialized device (regs %x, irq %d)\n", - priv->cpsw_res->start, ndev->irq); + ss_res->start, ndev->irq); if (priv->data.dual_emac) { ret = cpsw_probe_dual_emac(pdev, priv); if (ret) { cpsw_err(priv, probe, "error probe slave 2 emac interface\n"); - goto clean_irq_ret; + goto clean_ale_ret; } } return 0; -clean_irq_ret: - for (i = 0; i < priv->num_irqs; i++) - free_irq(priv->irqs_table[i], priv); clean_ale_ret: cpsw_ale_destroy(priv->ale); clean_dma_ret: cpdma_chan_destroy(priv->txch); cpdma_chan_destroy(priv->rxch); cpdma_ctlr_destroy(priv->dma); -clean_wr_iomap_ret: - iounmap(priv->wr_regs); -clean_cpsw_wr_iores_ret: - release_mem_region(priv->cpsw_wr_res->start, - resource_size(priv->cpsw_wr_res)); -clean_iomap_ret: - iounmap(priv->regs); -clean_cpsw_iores_ret: - release_mem_region(priv->cpsw_res->start, - resource_size(priv->cpsw_res)); -clean_clk_ret: - clk_put(priv->clk); -clean_slave_ret: +clean_runtime_disable_ret: pm_runtime_disable(&pdev->dev); - kfree(priv->slaves); clean_ndev_ret: - kfree(priv->data.slave_data); free_netdev(priv->ndev); return ret; } @@ -2198,30 +2147,18 @@ static int cpsw_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); struct cpsw_priv *priv = netdev_priv(ndev); - int i; if (priv->data.dual_emac) unregister_netdev(cpsw_get_slave_ndev(priv, 1)); unregister_netdev(ndev); cpts_unregister(priv->cpts); - for (i = 0; i < priv->num_irqs; i++) - free_irq(priv->irqs_table[i], priv); cpsw_ale_destroy(priv->ale); cpdma_chan_destroy(priv->txch); cpdma_chan_destroy(priv->rxch); cpdma_ctlr_destroy(priv->dma); - iounmap(priv->regs); - release_mem_region(priv->cpsw_res->start, - resource_size(priv->cpsw_res)); - iounmap(priv->wr_regs); - release_mem_region(priv->cpsw_wr_res->start, - resource_size(priv->cpsw_wr_res)); pm_runtime_disable(&pdev->dev); - clk_put(priv->clk); - kfree(priv->slaves); - kfree(priv->data.slave_data); if (priv->data.dual_emac) free_netdev(cpsw_get_slave_ndev(priv, 1)); free_netdev(ndev); @@ -2277,7 +2214,7 @@ static struct platform_driver cpsw_driver = { .name = "cpsw", .owner = THIS_MODULE, .pm = &cpsw_pm_ops, - .of_match_table = of_match_ptr(cpsw_of_mtable), + .of_match_table = cpsw_of_mtable, }, .probe = cpsw_probe, .remove = cpsw_remove, diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index eb3e101ec04878c87f8a3a6dee6243f9d6970b52..574f49da693f194fe9a32ac731c90487498df9ba 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -39,4 +39,6 @@ struct cpsw_platform_data { bool dual_emac; /* Enable Dual EMAC mode */ }; +void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave); + #endif /* __CPSW_H__ */ diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index fe993cdd7e23b76ad13f09585c215d87c83a0e46..1a581ef7eee8fcbd2ed121d64a747e06e84c6113 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -127,8 +127,8 @@ struct cpts { }; #ifdef CONFIG_TI_CPTS -extern void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); -extern void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); +void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb); +void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb); #else static inline void cpts_rx_timestamp(struct cpts *cpts, struct sk_buff *skb) { @@ -138,8 +138,7 @@ static inline void cpts_tx_timestamp(struct cpts *cpts, struct sk_buff *skb) } #endif -extern int cpts_register(struct device *dev, struct cpts *cpts, - u32 mult, u32 shift); -extern void cpts_unregister(struct cpts *cpts); +int cpts_register(struct device *dev, struct cpts *cpts, u32 mult, u32 shift); +void cpts_unregister(struct cpts *cpts); #endif diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 6a32ef9d63ae2500d5a397a3d6837be525ff7d6f..41ba974bf37cb9175c74ab40bba1817e890749e7 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1852,7 +1852,7 @@ static int davinci_emac_probe(struct platform_device *pdev) } /* MAC addr and PHY mask , RMII enable info from platform_data */ - memcpy(priv->mac_addr, pdata->mac_addr, 6); + memcpy(priv->mac_addr, pdata->mac_addr, ETH_ALEN); priv->phy_id = pdata->phy_id; priv->rmii_en = pdata->rmii_en; priv->version = pdata->version; diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index 591437e59b9019c9fa0597a8342f63ae5357fcaa..62b19be5183d3349433ee5855ad082b1560b5c69 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -319,7 +319,6 @@ static void tlan_remove_one(struct pci_dev *pdev) free_netdev(dev); - pci_set_drvdata(pdev, NULL); cancel_work_sync(&priv->tlan_tqueue); } diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c index 13e6fff8ca23af28e4b1e2229846522082dd60c4..628b736e5ae776fcf00333bed8c355e4b518314e 100644 --- a/drivers/net/ethernet/tile/tilegx.c +++ b/drivers/net/ethernet/tile/tilegx.c @@ -2230,7 +2230,7 @@ static void tile_net_dev_init(const char *name, const uint8_t *mac) nz_addr |= mac[i]; if (nz_addr) { - memcpy(dev->dev_addr, mac, 6); + memcpy(dev->dev_addr, mac, ETH_ALEN); dev->addr_len = 6; } else { eth_hw_addr_random(dev); diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_net.h b/drivers/net/ethernet/toshiba/ps3_gelic_net.h index 309abb472aa2040e978fea46faff0cfd81c0bb43..8505196be9f52bd7a982ec9dcb91f850379cfd07 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_net.h +++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.h @@ -359,27 +359,26 @@ static inline void *port_priv(struct gelic_port *port) } #ifdef CONFIG_PPC_EARLY_DEBUG_PS3GELIC -extern void udbg_shutdown_ps3gelic(void); +void udbg_shutdown_ps3gelic(void); #else static inline void udbg_shutdown_ps3gelic(void) {} #endif -extern int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask); +int gelic_card_set_irq_mask(struct gelic_card *card, u64 mask); /* shared netdev ops */ -extern void gelic_card_up(struct gelic_card *card); -extern void gelic_card_down(struct gelic_card *card); -extern int gelic_net_open(struct net_device *netdev); -extern int gelic_net_stop(struct net_device *netdev); -extern int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev); -extern void gelic_net_set_multi(struct net_device *netdev); -extern void gelic_net_tx_timeout(struct net_device *netdev); -extern int gelic_net_change_mtu(struct net_device *netdev, int new_mtu); -extern int gelic_net_setup_netdev(struct net_device *netdev, - struct gelic_card *card); +void gelic_card_up(struct gelic_card *card); +void gelic_card_down(struct gelic_card *card); +int gelic_net_open(struct net_device *netdev); +int gelic_net_stop(struct net_device *netdev); +int gelic_net_xmit(struct sk_buff *skb, struct net_device *netdev); +void gelic_net_set_multi(struct net_device *netdev); +void gelic_net_tx_timeout(struct net_device *netdev); +int gelic_net_change_mtu(struct net_device *netdev, int new_mtu); +int gelic_net_setup_netdev(struct net_device *netdev, struct gelic_card *card); /* shared ethtool ops */ -extern void gelic_net_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *info); -extern void gelic_net_poll_controller(struct net_device *netdev); +void gelic_net_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info); +void gelic_net_poll_controller(struct net_device *netdev); #endif /* _GELIC_NET_H */ diff --git a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.h b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.h index f7e51b7d704960a2eb45ae63d4ed19f134d6d105..11f443d8e4ea9042b6eb6831b0c8d62612b8ad52 100644 --- a/drivers/net/ethernet/toshiba/ps3_gelic_wireless.h +++ b/drivers/net/ethernet/toshiba/ps3_gelic_wireless.h @@ -320,7 +320,7 @@ struct gelic_eurus_cmd { #define GELIC_WL_PRIV_SET_PSK (SIOCIWFIRSTPRIV + 0) #define GELIC_WL_PRIV_GET_PSK (SIOCIWFIRSTPRIV + 1) -extern int gelic_wl_driver_probe(struct gelic_card *card); -extern int gelic_wl_driver_remove(struct gelic_card *card); -extern void gelic_wl_interrupt(struct net_device *netdev, u64 status); +int gelic_wl_driver_probe(struct gelic_card *card); +int gelic_wl_driver_remove(struct gelic_card *card); +void gelic_wl_interrupt(struct net_device *netdev, u64 status); #endif /* _GELIC_WIRELESS_H */ diff --git a/drivers/net/ethernet/toshiba/spider_net.c b/drivers/net/ethernet/toshiba/spider_net.c index 5734480c1ecfa5f91a134720fe478bb68826fc50..3f4a32e39d276f6fcf9c9d9d16c4aec440b9daf6 100644 --- a/drivers/net/ethernet/toshiba/spider_net.c +++ b/drivers/net/ethernet/toshiba/spider_net.c @@ -2478,7 +2478,6 @@ out_release_regions: pci_release_regions(pdev); out_disable_dev: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); return NULL; } diff --git a/drivers/net/ethernet/toshiba/spider_net.h b/drivers/net/ethernet/toshiba/spider_net.h index 4ba2135474d1a118a7a86b579aae38b11eb2c56a..9b6af0845a1101deca4b2083412dd1764fe2cc5b 100644 --- a/drivers/net/ethernet/toshiba/spider_net.h +++ b/drivers/net/ethernet/toshiba/spider_net.h @@ -29,8 +29,8 @@ #include -extern int spider_net_stop(struct net_device *netdev); -extern int spider_net_open(struct net_device *netdev); +int spider_net_stop(struct net_device *netdev); +int spider_net_open(struct net_device *netdev); extern const struct ethtool_ops spider_net_ethtool_ops; diff --git a/drivers/net/ethernet/toshiba/tc35815.c b/drivers/net/ethernet/toshiba/tc35815.c index a971b9cca564c910f8e928806ea9ac443ed22cf2..1322546d92ac97287a358cbea644568aedb98168 100644 --- a/drivers/net/ethernet/toshiba/tc35815.c +++ b/drivers/net/ethernet/toshiba/tc35815.c @@ -887,7 +887,6 @@ static void tc35815_remove_one(struct pci_dev *pdev) mdiobus_free(lp->mii_bus); unregister_netdev(dev); free_netdev(dev); - pci_set_drvdata(pdev, NULL); } static int diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index bdf697b184ae14246a4173d09833367ba312d7df..4a7293ed95e9c124b84ec87cabf1a9c1ef6ab1f9 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -2292,7 +2292,6 @@ static void rhine_remove_one(struct pci_dev *pdev) free_netdev(dev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static void rhine_shutdown (struct pci_dev *pdev) diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 4c619ea5189fc22308b8fe36851824e72eaad0ab..74234a51c851186c0c9bcfbc140b261724144e20 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -31,7 +31,7 @@ #define DRIVER_NAME "xilinx_emaclite" /* Register offsets for the EmacLite Core */ -#define XEL_TXBUFF_OFFSET 0x0 /* Transmit Buffer */ +#define XEL_TXBUFF_OFFSET 0x0 /* Transmit Buffer */ #define XEL_MDIOADDR_OFFSET 0x07E4 /* MDIO Address Register */ #define XEL_MDIOWR_OFFSET 0x07E8 /* MDIO Write Data Register */ #define XEL_MDIORD_OFFSET 0x07EC /* MDIO Read Data Register */ @@ -63,13 +63,13 @@ #define XEL_MDIOCTRL_MDIOEN_MASK 0x00000008 /* MDIO Enable */ /* Global Interrupt Enable Register (GIER) Bit Masks */ -#define XEL_GIER_GIE_MASK 0x80000000 /* Global Enable */ +#define XEL_GIER_GIE_MASK 0x80000000 /* Global Enable */ /* Transmit Status Register (TSR) Bit Masks */ -#define XEL_TSR_XMIT_BUSY_MASK 0x00000001 /* Tx complete */ -#define XEL_TSR_PROGRAM_MASK 0x00000002 /* Program the MAC address */ -#define XEL_TSR_XMIT_IE_MASK 0x00000008 /* Tx interrupt enable bit */ -#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000 /* Buffer is active, SW bit +#define XEL_TSR_XMIT_BUSY_MASK 0x00000001 /* Tx complete */ +#define XEL_TSR_PROGRAM_MASK 0x00000002 /* Program the MAC address */ +#define XEL_TSR_XMIT_IE_MASK 0x00000008 /* Tx interrupt enable bit */ +#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000 /* Buffer is active, SW bit * only. This is not documented * in the HW spec */ @@ -77,21 +77,21 @@ #define XEL_TSR_PROG_MAC_ADDR (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK) /* Receive Status Register (RSR) */ -#define XEL_RSR_RECV_DONE_MASK 0x00000001 /* Rx complete */ -#define XEL_RSR_RECV_IE_MASK 0x00000008 /* Rx interrupt enable bit */ +#define XEL_RSR_RECV_DONE_MASK 0x00000001 /* Rx complete */ +#define XEL_RSR_RECV_IE_MASK 0x00000008 /* Rx interrupt enable bit */ /* Transmit Packet Length Register (TPLR) */ -#define XEL_TPLR_LENGTH_MASK 0x0000FFFF /* Tx packet length */ +#define XEL_TPLR_LENGTH_MASK 0x0000FFFF /* Tx packet length */ /* Receive Packet Length Register (RPLR) */ -#define XEL_RPLR_LENGTH_MASK 0x0000FFFF /* Rx packet length */ +#define XEL_RPLR_LENGTH_MASK 0x0000FFFF /* Rx packet length */ -#define XEL_HEADER_OFFSET 12 /* Offset to length field */ -#define XEL_HEADER_SHIFT 16 /* Shift value for length */ +#define XEL_HEADER_OFFSET 12 /* Offset to length field */ +#define XEL_HEADER_SHIFT 16 /* Shift value for length */ /* General Ethernet Definitions */ -#define XEL_ARP_PACKET_SIZE 28 /* Max ARP packet size */ -#define XEL_HEADER_IP_LENGTH_OFFSET 16 /* IP Length Offset */ +#define XEL_ARP_PACKET_SIZE 28 /* Max ARP packet size */ +#define XEL_HEADER_IP_LENGTH_OFFSET 16 /* IP Length Offset */ @@ -1075,14 +1075,9 @@ static int xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev) * This function un maps the IO region of the Emaclite device and frees the net * device. */ -static void xemaclite_remove_ndev(struct net_device *ndev, - struct platform_device *pdev) +static void xemaclite_remove_ndev(struct net_device *ndev) { if (ndev) { - struct net_local *lp = netdev_priv(ndev); - - if (lp->base_addr) - devm_iounmap(&pdev->dev, lp->base_addr); free_netdev(ndev); } } @@ -1177,7 +1172,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev) if (mac_address) /* Set the MAC address. */ - memcpy(ndev->dev_addr, mac_address, 6); + memcpy(ndev->dev_addr, mac_address, ETH_ALEN); else dev_warn(dev, "No MAC address found\n"); @@ -1214,7 +1209,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev) return 0; error: - xemaclite_remove_ndev(ndev, ofdev); + xemaclite_remove_ndev(ndev); return rc; } @@ -1248,7 +1243,7 @@ static int xemaclite_of_remove(struct platform_device *of_dev) of_node_put(lp->phy_node); lp->phy_node = NULL; - xemaclite_remove_ndev(ndev, of_dev); + xemaclite_remove_ndev(ndev); return 0; } diff --git a/drivers/net/fddi/skfp/fplustm.c b/drivers/net/fddi/skfp/fplustm.c index a20ed1a98099f3d959317f68964bbd88639e3d8b..f8399359017405756f06641fa6c0405c5d5ea0a9 100644 --- a/drivers/net/fddi/skfp/fplustm.c +++ b/drivers/net/fddi/skfp/fplustm.c @@ -453,7 +453,7 @@ static void directed_beacon(struct s_smc *smc) */ * (char *) a = (char) ((long)DBEACON_INFO<<24L) ; a[1] = 0 ; - memcpy((char *)a+1,(char *) &smc->mib.m[MAC0].fddiMACUpstreamNbr,6) ; + memcpy((char *)a+1, (char *) &smc->mib.m[MAC0].fddiMACUpstreamNbr, ETH_ALEN); CHECK_NPP() ; /* set memory address reg for writes */ diff --git a/drivers/net/fddi/skfp/h/smc.h b/drivers/net/fddi/skfp/h/smc.h index 3ca308b282148832b534ae91f17f33991f6f6d16..bd1166bf8f61a55c0e3440d2762f1b1e0765cc7b 100644 --- a/drivers/net/fddi/skfp/h/smc.h +++ b/drivers/net/fddi/skfp/h/smc.h @@ -469,20 +469,20 @@ struct s_smc { extern const struct fddi_addr fddi_broadcast; -extern void all_selection_criteria(struct s_smc *smc); -extern void card_stop(struct s_smc *smc); -extern void init_board(struct s_smc *smc, u_char *mac_addr); -extern int init_fplus(struct s_smc *smc); -extern void init_plc(struct s_smc *smc); -extern int init_smt(struct s_smc *smc, u_char * mac_addr); -extern void mac1_irq(struct s_smc *smc, u_short stu, u_short stl); -extern void mac2_irq(struct s_smc *smc, u_short code_s2u, u_short code_s2l); -extern void mac3_irq(struct s_smc *smc, u_short code_s3u, u_short code_s3l); -extern int pcm_status_twisted(struct s_smc *smc); -extern void plc1_irq(struct s_smc *smc); -extern void plc2_irq(struct s_smc *smc); -extern void read_address(struct s_smc *smc, u_char * mac_addr); -extern void timer_irq(struct s_smc *smc); +void all_selection_criteria(struct s_smc *smc); +void card_stop(struct s_smc *smc); +void init_board(struct s_smc *smc, u_char *mac_addr); +int init_fplus(struct s_smc *smc); +void init_plc(struct s_smc *smc); +int init_smt(struct s_smc *smc, u_char *mac_addr); +void mac1_irq(struct s_smc *smc, u_short stu, u_short stl); +void mac2_irq(struct s_smc *smc, u_short code_s2u, u_short code_s2l); +void mac3_irq(struct s_smc *smc, u_short code_s3u, u_short code_s3l); +int pcm_status_twisted(struct s_smc *smc); +void plc1_irq(struct s_smc *smc); +void plc2_irq(struct s_smc *smc); +void read_address(struct s_smc *smc, u_char *mac_addr); +void timer_irq(struct s_smc *smc); #endif /* _SCMECM_ */ diff --git a/drivers/net/fddi/skfp/skfddi.c b/drivers/net/fddi/skfp/skfddi.c index f5d7305a5784174f9868f045d186e9c90d2bbc20..713d303a06a9e1d2ea06e198a9682beb4bc54fc2 100644 --- a/drivers/net/fddi/skfp/skfddi.c +++ b/drivers/net/fddi/skfp/skfddi.c @@ -436,7 +436,7 @@ static int skfp_driver_init(struct net_device *dev) } read_address(smc, NULL); pr_debug("HW-Addr: %pMF\n", smc->hw.fddi_canon_addr.a); - memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6); + memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, ETH_ALEN); smt_reset_defaults(smc, 0); @@ -503,7 +503,7 @@ static int skfp_open(struct net_device *dev) * address. */ read_address(smc, NULL); - memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6); + memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, ETH_ALEN); init_smt(smc, NULL); smt_online(smc, 1); @@ -1213,7 +1213,7 @@ static void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr) if ((unsigned short) frame[1 + 10] != 0) return; SRBit = frame[1 + 6] & 0x01; - memcpy(&frame[1 + 6], hw_addr, 6); + memcpy(&frame[1 + 6], hw_addr, ETH_ALEN); frame[8] |= SRBit; } // CheckSourceAddress diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index a974727dd9a2bb604746a20b3f4e65d22b9801db..636b65c66d49e6568b1e8535e66fac5b9a8dbd49 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -445,7 +445,7 @@ static int ser12_open(struct net_device *dev) outb(0, FCR(dev->base_addr)); /* disable FIFOs */ outb(0x0d, MCR(dev->base_addr)); outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, IRQF_DISABLED | IRQF_SHARED, + if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED, "baycom_ser_fdx", dev)) { release_region(dev->base_addr, SER12_EXTENT); return -EBUSY; diff --git a/drivers/net/hamradio/baycom_ser_hdx.c b/drivers/net/hamradio/baycom_ser_hdx.c index e349d867449b2d744ff9550535317c93c069f07b..f9a8976195ba05f0fc07723969e5bb2136140a9b 100644 --- a/drivers/net/hamradio/baycom_ser_hdx.c +++ b/drivers/net/hamradio/baycom_ser_hdx.c @@ -490,7 +490,7 @@ static int ser12_open(struct net_device *dev) outb(0, FCR(dev->base_addr)); /* disable FIFOs */ outb(0x0d, MCR(dev->base_addr)); outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, ser12_interrupt, IRQF_DISABLED | IRQF_SHARED, + if (request_irq(dev->irq, ser12_interrupt, IRQF_SHARED, "baycom_ser12", dev)) { release_region(dev->base_addr, SER12_EXTENT); return -EBUSY; diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index bc1d5217038985565f05e15fe98b2df4078c4114..4bc6ee8e7987796b04a6f1ef5a1a5ffe044ec8af 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -1734,7 +1734,7 @@ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!Ivec[hwcfg.irq].used && hwcfg.irq) { if (request_irq(hwcfg.irq, scc_isr, - IRQF_DISABLED, "AX.25 SCC", + 0, "AX.25 SCC", (void *)(long) hwcfg.irq)) printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); else diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index 5af1c3e5032addd1441722899d3ab1fb0b010ae9..1971411574db1c7ae2dd6549490ab6975e816cd1 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -888,7 +888,7 @@ static int yam_open(struct net_device *dev) goto out_release_base; } outb(0, IER(dev->base_addr)); - if (request_irq(dev->irq, yam_interrupt, IRQF_DISABLED | IRQF_SHARED, dev->name, dev)) { + if (request_irq(dev->irq, yam_interrupt, IRQF_SHARED, dev->name, dev)) { printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq); ret = -EBUSY; goto out_release_base; diff --git a/drivers/net/irda/bfin_sir.c b/drivers/net/irda/bfin_sir.c index c74f384c87d50a0314b84e2808ab735c00ee8684..303c4bd26e17953de1693475b042266eac8dca90 100644 --- a/drivers/net/irda/bfin_sir.c +++ b/drivers/net/irda/bfin_sir.c @@ -411,12 +411,12 @@ static int bfin_sir_startup(struct bfin_sir_port *port, struct net_device *dev) #else - if (request_irq(port->irq, bfin_sir_rx_int, IRQF_DISABLED, "BFIN_SIR_RX", dev)) { + if (request_irq(port->irq, bfin_sir_rx_int, 0, "BFIN_SIR_RX", dev)) { dev_warn(&dev->dev, "Unable to attach SIR RX interrupt\n"); return -EBUSY; } - if (request_irq(port->irq+1, bfin_sir_tx_int, IRQF_DISABLED, "BFIN_SIR_TX", dev)) { + if (request_irq(port->irq+1, bfin_sir_tx_int, 0, "BFIN_SIR_TX", dev)) { dev_warn(&dev->dev, "Unable to attach SIR TX interrupt\n"); free_irq(port->irq, dev); return -EBUSY; diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c index 31bcb98ef35609b9f0be24cfc63bcd4fd43ae852..768dfe9a93159e4964d4b9c0bc6986da2e6c09b6 100644 --- a/drivers/net/irda/donauboe.c +++ b/drivers/net/irda/donauboe.c @@ -1352,7 +1352,7 @@ toshoboe_net_open (struct net_device *dev) return 0; rc = request_irq (self->io.irq, toshoboe_interrupt, - IRQF_SHARED | IRQF_DISABLED, dev->name, self); + IRQF_SHARED, dev->name, self); if (rc) return rc; @@ -1559,7 +1559,7 @@ toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid) self->io.fir_base = self->base; self->io.fir_ext = OBOE_IO_EXTENT; self->io.irq = pci_dev->irq; - self->io.irqflags = IRQF_SHARED | IRQF_DISABLED; + self->io.irqflags = IRQF_SHARED; self->speed = self->io.speed = 9600; self->async = 0; diff --git a/drivers/net/irda/sh_irda.c b/drivers/net/irda/sh_irda.c index 4455425f1c777865676150fa5da63b94f0ffc736..ff45cd0d60e84f88b5e34ec40f61e2e5e0d85eb3 100644 --- a/drivers/net/irda/sh_irda.c +++ b/drivers/net/irda/sh_irda.c @@ -804,7 +804,7 @@ static int sh_irda_probe(struct platform_device *pdev) goto err_mem_4; platform_set_drvdata(pdev, ndev); - err = request_irq(irq, sh_irda_irq, IRQF_DISABLED, "sh_irda", self); + err = request_irq(irq, sh_irda_irq, 0, "sh_irda", self); if (err) { dev_warn(&pdev->dev, "Unable to attach sh_irda interrupt\n"); goto err_mem_4; diff --git a/drivers/net/irda/sh_sir.c b/drivers/net/irda/sh_sir.c index 89682b49900ff2cac834c85cc58429ec4fb65ec3..8d9ae5a086d54d8d78ba8ae8ee4fee1c9fd76475 100644 --- a/drivers/net/irda/sh_sir.c +++ b/drivers/net/irda/sh_sir.c @@ -761,7 +761,7 @@ static int sh_sir_probe(struct platform_device *pdev) goto err_mem_4; platform_set_drvdata(pdev, ndev); - err = request_irq(irq, sh_sir_irq, IRQF_DISABLED, "sh_sir", self); + err = request_irq(irq, sh_sir_irq, 0, "sh_sir", self); if (err) { dev_warn(&pdev->dev, "Unable to attach sh_sir interrupt\n"); goto err_mem_4; diff --git a/drivers/net/irda/sir-dev.h b/drivers/net/irda/sir-dev.h index 6d5b1e2b12893271adb7fd7d4c373cde4823d151..f50b9c1c0639008b1e314099f4918e3eb0ed57db 100644 --- a/drivers/net/irda/sir-dev.h +++ b/drivers/net/irda/sir-dev.h @@ -102,28 +102,29 @@ struct sir_driver { /* exported */ -extern int irda_register_dongle(struct dongle_driver *new); -extern int irda_unregister_dongle(struct dongle_driver *drv); +int irda_register_dongle(struct dongle_driver *new); +int irda_unregister_dongle(struct dongle_driver *drv); -extern struct sir_dev * sirdev_get_instance(const struct sir_driver *drv, const char *name); -extern int sirdev_put_instance(struct sir_dev *self); +struct sir_dev *sirdev_get_instance(const struct sir_driver *drv, + const char *name); +int sirdev_put_instance(struct sir_dev *self); -extern int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type); -extern void sirdev_write_complete(struct sir_dev *dev); -extern int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count); +int sirdev_set_dongle(struct sir_dev *dev, IRDA_DONGLE type); +void sirdev_write_complete(struct sir_dev *dev); +int sirdev_receive(struct sir_dev *dev, const unsigned char *cp, size_t count); /* low level helpers for SIR device/dongle setup */ -extern int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len); -extern int sirdev_raw_read(struct sir_dev *dev, char *buf, int len); -extern int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts); +int sirdev_raw_write(struct sir_dev *dev, const char *buf, int len); +int sirdev_raw_read(struct sir_dev *dev, char *buf, int len); +int sirdev_set_dtr_rts(struct sir_dev *dev, int dtr, int rts); /* not exported */ -extern int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type); -extern int sirdev_put_dongle(struct sir_dev *self); +int sirdev_get_dongle(struct sir_dev *self, IRDA_DONGLE type); +int sirdev_put_dongle(struct sir_dev *self); -extern void sirdev_enable_rx(struct sir_dev *dev); -extern int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param); +void sirdev_enable_rx(struct sir_dev *dev); +int sirdev_schedule_request(struct sir_dev *dev, int state, unsigned param); /* inline helpers */ diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 9bf46bd19b87bf947fd79e547bdde626c8a12097..af4aaa5893ff5a23a8d0014ab21403eb3177783f 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -297,7 +297,13 @@ netdev_tx_t macvlan_start_xmit(struct sk_buff *skb, int ret; const struct macvlan_dev *vlan = netdev_priv(dev); - ret = macvlan_queue_xmit(skb, dev); + if (vlan->fwd_priv) { + skb->dev = vlan->lowerdev; + ret = dev_hard_start_xmit(skb, skb->dev, NULL, vlan->fwd_priv); + } else { + ret = macvlan_queue_xmit(skb, dev); + } + if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) { struct macvlan_pcpu_stats *pcpu_stats; @@ -347,6 +353,21 @@ static int macvlan_open(struct net_device *dev) goto hash_add; } + if (lowerdev->features & NETIF_F_HW_L2FW_DOFFLOAD) { + vlan->fwd_priv = + lowerdev->netdev_ops->ndo_dfwd_add_station(lowerdev, dev); + + /* If we get a NULL pointer back, or if we get an error + * then we should just fall through to the non accelerated path + */ + if (IS_ERR_OR_NULL(vlan->fwd_priv)) { + vlan->fwd_priv = NULL; + } else { + dev->features &= ~NETIF_F_LLTX; + return 0; + } + } + err = -EBUSY; if (macvlan_addr_busy(vlan->port, dev->dev_addr)) goto out; @@ -367,6 +388,11 @@ hash_add: del_unicast: dev_uc_del(lowerdev, dev->dev_addr); out: + if (vlan->fwd_priv) { + lowerdev->netdev_ops->ndo_dfwd_del_station(lowerdev, + vlan->fwd_priv); + vlan->fwd_priv = NULL; + } return err; } @@ -375,6 +401,13 @@ static int macvlan_stop(struct net_device *dev) struct macvlan_dev *vlan = netdev_priv(dev); struct net_device *lowerdev = vlan->lowerdev; + if (vlan->fwd_priv) { + lowerdev->netdev_ops->ndo_dfwd_del_station(lowerdev, + vlan->fwd_priv); + vlan->fwd_priv = NULL; + return 0; + } + dev_uc_unsync(lowerdev, dev); dev_mc_unsync(lowerdev, dev); @@ -828,22 +861,22 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, eth_hw_addr_inherit(dev, lowerdev); } + port->count += 1; + err = register_netdevice(dev); + if (err < 0) + goto destroy_port; + + dev->priv_flags |= IFF_MACVLAN; err = netdev_upper_dev_link(lowerdev, dev); if (err) goto destroy_port; - port->count += 1; - err = register_netdevice(dev); - if (err < 0) - goto upper_dev_unlink; list_add_tail_rcu(&vlan->list, &port->vlans); netif_stacked_transfer_operstate(lowerdev, dev); return 0; -upper_dev_unlink: - netdev_upper_dev_unlink(lowerdev, dev); destroy_port: port->count -= 1; if (!port->count) diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c index c9a15925a1f757bf1da6ceef594389df15315f24..ba2f5e710af12cc7587f0503ea8dc177d2aba4ed 100644 --- a/drivers/net/netconsole.c +++ b/drivers/net/netconsole.c @@ -34,6 +34,8 @@ * ****************************************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -320,8 +322,8 @@ static ssize_t store_enabled(struct netconsole_target *nt, if (enabled < 0 || enabled > 1) return -EINVAL; if (enabled == nt->enabled) { - printk(KERN_INFO "netconsole: network logging has already %s\n", - nt->enabled ? "started" : "stopped"); + pr_info("network logging has already %s\n", + nt->enabled ? "started" : "stopped"); return -EINVAL; } @@ -336,7 +338,7 @@ static ssize_t store_enabled(struct netconsole_target *nt, if (err) return err; - printk(KERN_INFO "netconsole: network logging started\n"); + pr_info("netconsole: network logging started\n"); } else { /* 0 */ /* We need to disable the netconsole before cleaning it up * otherwise we might end up in write_msg() with @@ -360,9 +362,8 @@ static ssize_t store_dev_name(struct netconsole_target *nt, size_t len; if (nt->enabled) { - printk(KERN_ERR "netconsole: target (%s) is enabled, " - "disable to update parameters\n", - config_item_name(&nt->item)); + pr_err("target (%s) is enabled, disable to update parameters\n", + config_item_name(&nt->item)); return -EINVAL; } @@ -383,9 +384,8 @@ static ssize_t store_local_port(struct netconsole_target *nt, int rv; if (nt->enabled) { - printk(KERN_ERR "netconsole: target (%s) is enabled, " - "disable to update parameters\n", - config_item_name(&nt->item)); + pr_err("target (%s) is enabled, disable to update parameters\n", + config_item_name(&nt->item)); return -EINVAL; } @@ -402,9 +402,8 @@ static ssize_t store_remote_port(struct netconsole_target *nt, int rv; if (nt->enabled) { - printk(KERN_ERR "netconsole: target (%s) is enabled, " - "disable to update parameters\n", - config_item_name(&nt->item)); + pr_err("target (%s) is enabled, disable to update parameters\n", + config_item_name(&nt->item)); return -EINVAL; } @@ -419,9 +418,8 @@ static ssize_t store_local_ip(struct netconsole_target *nt, size_t count) { if (nt->enabled) { - printk(KERN_ERR "netconsole: target (%s) is enabled, " - "disable to update parameters\n", - config_item_name(&nt->item)); + pr_err("target (%s) is enabled, disable to update parameters\n", + config_item_name(&nt->item)); return -EINVAL; } @@ -429,7 +427,7 @@ static ssize_t store_local_ip(struct netconsole_target *nt, const char *end; if (in6_pton(buf, count, nt->np.local_ip.in6.s6_addr, -1, &end) > 0) { if (*end && *end != '\n') { - printk(KERN_ERR "netconsole: invalid IPv6 address at: <%c>\n", *end); + pr_err("invalid IPv6 address at: <%c>\n", *end); return -EINVAL; } nt->np.ipv6 = true; @@ -450,9 +448,8 @@ static ssize_t store_remote_ip(struct netconsole_target *nt, size_t count) { if (nt->enabled) { - printk(KERN_ERR "netconsole: target (%s) is enabled, " - "disable to update parameters\n", - config_item_name(&nt->item)); + pr_err("target (%s) is enabled, disable to update parameters\n", + config_item_name(&nt->item)); return -EINVAL; } @@ -460,7 +457,7 @@ static ssize_t store_remote_ip(struct netconsole_target *nt, const char *end; if (in6_pton(buf, count, nt->np.remote_ip.in6.s6_addr, -1, &end) > 0) { if (*end && *end != '\n') { - printk(KERN_ERR "netconsole: invalid IPv6 address at: <%c>\n", *end); + pr_err("invalid IPv6 address at: <%c>\n", *end); return -EINVAL; } nt->np.ipv6 = true; @@ -483,9 +480,8 @@ static ssize_t store_remote_mac(struct netconsole_target *nt, u8 remote_mac[ETH_ALEN]; if (nt->enabled) { - printk(KERN_ERR "netconsole: target (%s) is enabled, " - "disable to update parameters\n", - config_item_name(&nt->item)); + pr_err("target (%s) is enabled, disable to update parameters\n", + config_item_name(&nt->item)); return -EINVAL; } @@ -708,19 +704,20 @@ restart: } spin_unlock_irqrestore(&target_list_lock, flags); if (stopped) { - printk(KERN_INFO "netconsole: network logging stopped on " - "interface %s as it ", dev->name); + const char *msg = "had an event"; switch (event) { case NETDEV_UNREGISTER: - printk(KERN_CONT "unregistered\n"); + msg = "unregistered"; break; case NETDEV_RELEASE: - printk(KERN_CONT "released slaves\n"); + msg = "released slaves"; break; case NETDEV_JOIN: - printk(KERN_CONT "is joining a master device\n"); + msg = "is joining a master device"; break; } + pr_info("network logging stopped on interface %s as it %s\n", + dev->name, msg); } done: @@ -806,7 +803,7 @@ static int __init init_netconsole(void) goto undonotifier; register_console(&netconsole); - printk(KERN_INFO "netconsole: network logging started\n"); + pr_info("network logging started\n"); return err; @@ -814,7 +811,7 @@ undonotifier: unregister_netdevice_notifier(&netconsole_netdev_notifier); fail: - printk(KERN_ERR "netconsole: cleaning up\n"); + pr_err("cleaning up\n"); /* * Remove all targets and destroy them (only targets created diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 342561ad3158d2660e2d08df349359530950ca0d..9b5d46c03eed34d244bf279ea13bf38ffebea009 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -154,6 +154,13 @@ config MDIO_SUN4I interface units of the Allwinner SoC that have an EMAC (A10, A12, A10s, etc.) +config MDIO_MOXART + tristate "MOXA ART MDIO interface support" + depends on ARCH_MOXART + help + This driver supports the MDIO interface found in the network + interface units of the MOXA ART SoC + config MDIO_BUS_MUX tristate depends on OF_MDIO diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 23a2ab2e847e54d0d5b7ace4e4002f6c836c49e4..9013dfa12aa39ac7fe504734e6b3e62d7865b0d7 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -31,3 +31,4 @@ obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o +obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c index ac22283aaf23213ab9bf9d931f76df602ccea09d..bc71947b1ec329f2eacd52b915b37ce56a4f16bb 100644 --- a/drivers/net/phy/at803x.c +++ b/drivers/net/phy/at803x.c @@ -100,6 +100,45 @@ static void at803x_get_wol(struct phy_device *phydev, wol->wolopts |= WAKE_MAGIC; } +static int at803x_suspend(struct phy_device *phydev) +{ + int value; + int wol_enabled; + + mutex_lock(&phydev->lock); + + value = phy_read(phydev, AT803X_INTR_ENABLE); + wol_enabled = value & AT803X_WOL_ENABLE; + + value = phy_read(phydev, MII_BMCR); + + if (wol_enabled) + value |= BMCR_ISOLATE; + else + value |= BMCR_PDOWN; + + phy_write(phydev, MII_BMCR, value); + + mutex_unlock(&phydev->lock); + + return 0; +} + +static int at803x_resume(struct phy_device *phydev) +{ + int value; + + mutex_lock(&phydev->lock); + + value = phy_read(phydev, MII_BMCR); + value &= ~(BMCR_PDOWN | BMCR_ISOLATE); + phy_write(phydev, MII_BMCR, value); + + mutex_unlock(&phydev->lock); + + return 0; +} + static int at803x_config_init(struct phy_device *phydev) { int val; @@ -161,10 +200,12 @@ static struct phy_driver at803x_driver[] = { .config_init = at803x_config_init, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, + .suspend = at803x_suspend, + .resume = at803x_resume, .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, - .config_aneg = &genphy_config_aneg, - .read_status = &genphy_read_status, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, .driver = { .owner = THIS_MODULE, }, @@ -176,10 +217,12 @@ static struct phy_driver at803x_driver[] = { .config_init = at803x_config_init, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, + .suspend = at803x_suspend, + .resume = at803x_resume, .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, - .config_aneg = &genphy_config_aneg, - .read_status = &genphy_read_status, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, .driver = { .owner = THIS_MODULE, }, @@ -191,10 +234,12 @@ static struct phy_driver at803x_driver[] = { .config_init = at803x_config_init, .set_wol = at803x_set_wol, .get_wol = at803x_get_wol, + .suspend = at803x_suspend, + .resume = at803x_resume, .features = PHY_GBIT_FEATURES, .flags = PHY_HAS_INTERRUPT, - .config_aneg = &genphy_config_aneg, - .read_status = &genphy_read_status, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, .driver = { .owner = THIS_MODULE, }, diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 2e91477362d4d70b15df3db59ad41dec990a3c57..2e3c778ea9bf6f0437f95759f12c902a76cf9e7c 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -34,9 +34,9 @@ #include #include -#include +#include #include -#include +#include #define MII_MARVELL_PHY_PAGE 22 diff --git a/drivers/net/phy/mdio-moxart.c b/drivers/net/phy/mdio-moxart.c new file mode 100644 index 0000000000000000000000000000000000000000..a5741cb0304e413867b4b7a8177a99822de0f77e --- /dev/null +++ b/drivers/net/phy/mdio-moxart.c @@ -0,0 +1,201 @@ +/* MOXA ART Ethernet (RTL8201CP) MDIO interface driver + * + * Copyright (C) 2013 Jonas Jensen + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_PHY_CTRL 0 +#define REG_PHY_WRITE_DATA 4 + +/* REG_PHY_CTRL */ +#define MIIWR BIT(27) /* init write sequence (auto cleared)*/ +#define MIIRD BIT(26) +#define REGAD_MASK 0x3e00000 +#define PHYAD_MASK 0x1f0000 +#define MIIRDATA_MASK 0xffff + +/* REG_PHY_WRITE_DATA */ +#define MIIWDATA_MASK 0xffff + +struct moxart_mdio_data { + void __iomem *base; +}; + +static int moxart_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct moxart_mdio_data *data = bus->priv; + u32 ctrl = 0; + unsigned int count = 5; + + dev_dbg(&bus->dev, "%s\n", __func__); + + ctrl |= MIIRD | ((mii_id << 16) & PHYAD_MASK) | + ((regnum << 21) & REGAD_MASK); + + writel(ctrl, data->base + REG_PHY_CTRL); + + do { + ctrl = readl(data->base + REG_PHY_CTRL); + + if (!(ctrl & MIIRD)) + return ctrl & MIIRDATA_MASK; + + mdelay(10); + count--; + } while (count > 0); + + dev_dbg(&bus->dev, "%s timed out\n", __func__); + + return -ETIMEDOUT; +} + +static int moxart_mdio_write(struct mii_bus *bus, int mii_id, + int regnum, u16 value) +{ + struct moxart_mdio_data *data = bus->priv; + u32 ctrl = 0; + unsigned int count = 5; + + dev_dbg(&bus->dev, "%s\n", __func__); + + ctrl |= MIIWR | ((mii_id << 16) & PHYAD_MASK) | + ((regnum << 21) & REGAD_MASK); + + value &= MIIWDATA_MASK; + + writel(value, data->base + REG_PHY_WRITE_DATA); + writel(ctrl, data->base + REG_PHY_CTRL); + + do { + ctrl = readl(data->base + REG_PHY_CTRL); + + if (!(ctrl & MIIWR)) + return 0; + + mdelay(10); + count--; + } while (count > 0); + + dev_dbg(&bus->dev, "%s timed out\n", __func__); + + return -ETIMEDOUT; +} + +static int moxart_mdio_reset(struct mii_bus *bus) +{ + int data, i; + + for (i = 0; i < PHY_MAX_ADDR; i++) { + data = moxart_mdio_read(bus, i, MII_BMCR); + if (data < 0) + continue; + + data |= BMCR_RESET; + if (moxart_mdio_write(bus, i, MII_BMCR, data) < 0) + continue; + } + + return 0; +} + +static int moxart_mdio_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct mii_bus *bus; + struct moxart_mdio_data *data; + struct resource *res; + int ret, i; + + bus = mdiobus_alloc_size(sizeof(*data)); + if (!bus) + return -ENOMEM; + + bus->name = "MOXA ART Ethernet MII"; + bus->read = &moxart_mdio_read; + bus->write = &moxart_mdio_write; + bus->reset = &moxart_mdio_reset; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d-mii", pdev->name, pdev->id); + bus->parent = &pdev->dev; + + bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR, + GFP_KERNEL); + if (!bus->irq) { + ret = -ENOMEM; + goto err_out_free_mdiobus; + } + + /* Setting PHY_IGNORE_INTERRUPT here even if it has no effect, + * of_mdiobus_register() sets these PHY_POLL. + * Ideally, the interrupt from MAC controller could be used to + * detect link state changes, not polling, i.e. if there was + * a way phy_driver could set PHY_HAS_INTERRUPT but have that + * interrupt handled in ethernet drivercode. + */ + for (i = 0; i < PHY_MAX_ADDR; i++) + bus->irq[i] = PHY_IGNORE_INTERRUPT; + + data = bus->priv; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->base)) { + ret = PTR_ERR(data->base); + goto err_out_free_mdiobus; + } + + ret = of_mdiobus_register(bus, np); + if (ret < 0) + goto err_out_free_mdiobus; + + platform_set_drvdata(pdev, bus); + + return 0; + +err_out_free_mdiobus: + mdiobus_free(bus); + return ret; +} + +static int moxart_mdio_remove(struct platform_device *pdev) +{ + struct mii_bus *bus = platform_get_drvdata(pdev); + + mdiobus_unregister(bus); + mdiobus_free(bus); + + return 0; +} + +static const struct of_device_id moxart_mdio_dt_ids[] = { + { .compatible = "moxa,moxart-mdio" }, + { } +}; +MODULE_DEVICE_TABLE(of, moxart_mdio_dt_ids); + +static struct platform_driver moxart_mdio_driver = { + .probe = moxart_mdio_probe, + .remove = moxart_mdio_remove, + .driver = { + .name = "moxart-mdio", + .of_match_table = moxart_mdio_dt_ids, + }, +}; + +module_platform_driver(moxart_mdio_driver); + +MODULE_DESCRIPTION("MOXA ART MDIO interface driver"); +MODULE_AUTHOR("Jonas Jensen "); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index c31aad0004cb5ed93453089114e9f7dc31894ab2..3ae28f420868fc35af3b3ed4e652f96e43a8f35b 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -287,6 +287,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = ks8737_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8021, @@ -300,6 +302,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8031, @@ -313,6 +317,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8041, @@ -326,6 +332,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8051, @@ -339,6 +347,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8001, @@ -351,6 +361,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8081, @@ -363,6 +375,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ8061, @@ -375,6 +389,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }, { .phy_id = PHY_ID_KSZ9021, @@ -387,6 +403,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = ksz9021_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE, }, }, { .phy_id = PHY_ID_KSZ9031, @@ -400,6 +418,8 @@ static struct phy_driver ksphy_driver[] = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = ksz9021_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE, }, }, { .phy_id = PHY_ID_KSZ8873MLL, @@ -410,6 +430,8 @@ static struct phy_driver ksphy_driver[] = { .config_init = kszphy_config_init, .config_aneg = ksz8873mll_config_aneg, .read_status = ksz8873mll_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE, }, }, { .phy_id = PHY_ID_KSZ886X, @@ -420,6 +442,8 @@ static struct phy_driver ksphy_driver[] = { .config_init = kszphy_config_init, .config_aneg = genphy_config_aneg, .read_status = genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE, }, } }; diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 138de837977f1e5762ecb8ae6fecac59ade8c181..fa1d69a38ccf9cd374f0292d653e4c587b1ecb88 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -64,6 +64,18 @@ static int rtl8211e_config_intr(struct phy_device *phydev) return err; } +/* RTL8201CP */ +static struct phy_driver rtl8201cp_driver = { + .phy_id = 0x00008201, + .name = "RTL8201CP Ethernet", + .phy_id_mask = 0x0000ffff, + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &genphy_config_aneg, + .read_status = &genphy_read_status, + .driver = { .owner = THIS_MODULE,}, +}; + /* RTL8211B */ static struct phy_driver rtl8211b_driver = { .phy_id = 0x001cc912, @@ -98,6 +110,9 @@ static int __init realtek_init(void) { int ret; + ret = phy_driver_register(&rtl8201cp_driver); + if (ret < 0) + return -ENODEV; ret = phy_driver_register(&rtl8211b_driver); if (ret < 0) return -ENODEV; diff --git a/drivers/net/plip/plip.c b/drivers/net/plip/plip.c index 1f7bef90b46757a6ce2485c9aefd5d197464b7b1..7b4ff35c8bf7dcb28b455fb3a578eacddb298900 100644 --- a/drivers/net/plip/plip.c +++ b/drivers/net/plip/plip.c @@ -1002,7 +1002,7 @@ plip_rewrite_address(const struct net_device *dev, struct ethhdr *eth) /* Any address will do - we take the first */ const struct in_ifaddr *ifa = in_dev->ifa_list; if (ifa) { - memcpy(eth->h_source, dev->dev_addr, 6); + memcpy(eth->h_source, dev->dev_addr, ETH_ALEN); memset(eth->h_dest, 0xfc, 2); memcpy(eth->h_dest+2, &ifa->ifa_address, 4); } diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 40db31233313d461a0f7f59bd928d40b8b689634..85e4a01670f06e207609299ecacd035df3e098f3 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -242,6 +242,21 @@ config USB_NET_CDC_NCM * ST-Ericsson M343 HSPA Mobile Broadband Modem (reference design) * Ericsson F5521gw Mobile Broadband Module +config USB_NET_HUAWEI_CDC_NCM + tristate "Huawei NCM embedded AT channel support" + depends on USB_USBNET + select USB_WDM + select USB_NET_CDC_NCM + help + This driver supports huawei-style NCM devices, that use NCM as a + transport for other protocols, usually an embedded AT channel. + Good examples are: + * Huawei E3131 + * Huawei E3251 + + To compile this driver as a module, choose M here: the module will be + called huawei_cdc_ncm.ko. + config USB_NET_CDC_MBIM tristate "CDC MBIM support" depends on USB_USBNET diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index 8b342cf992fdd82221b2a8fc228e554f3358d28b..b17b5e88bbaf71acc06640a0f3fe6967b53ff2ab 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_USB_IPHETH) += ipheth.o obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o +obj-$(CONFIG_USB_NET_HUAWEI_CDC_NCM) += huawei_cdc_ncm.o obj-$(CONFIG_USB_VL600) += lg-vl600.o obj-$(CONFIG_USB_NET_QMI_WWAN) += qmi_wwan.o obj-$(CONFIG_USB_NET_CDC_MBIM) += cdc_mbim.o diff --git a/drivers/net/usb/catc.c b/drivers/net/usb/catc.c index 8d5cac2d8e33bee6b2545d45e83d24f52f8ac4c3..df507e6dbb9c99d9a15f718128e008f06ba11cdb 100644 --- a/drivers/net/usb/catc.c +++ b/drivers/net/usb/catc.c @@ -640,10 +640,10 @@ static void catc_set_multicast_list(struct net_device *netdev) { struct catc *catc = netdev_priv(netdev); struct netdev_hw_addr *ha; - u8 broadcast[6]; + u8 broadcast[ETH_ALEN]; u8 rx = RxEnable | RxPolarity | RxMultiCast; - memset(broadcast, 0xff, 6); + memset(broadcast, 0xff, ETH_ALEN); memset(catc->multicast, 0, 64); catc_multicast(broadcast, catc->multicast); @@ -778,7 +778,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id struct usb_device *usbdev = interface_to_usbdev(intf); struct net_device *netdev; struct catc *catc; - u8 broadcast[6]; + u8 broadcast[ETH_ALEN]; int i, pktsz; if (usb_set_interface(usbdev, @@ -882,7 +882,7 @@ static int catc_probe(struct usb_interface *intf, const struct usb_device_id *id dev_dbg(dev, "Filling the multicast list.\n"); - memset(broadcast, 0xff, 6); + memset(broadcast, 0xff, ETH_ALEN); catc_multicast(broadcast, catc->multicast); catc_multicast(netdev->dev_addr, catc->multicast); catc_write_mem(catc, 0xfa80, catc->multicast, 64); diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c index 7d78669000d704d8448aea5e24a8a73c312badfc..6358d420e185b4d4cb7b64a0c2a459fc5b70ed7f 100644 --- a/drivers/net/usb/cdc-phonet.c +++ b/drivers/net/usb/cdc-phonet.c @@ -328,7 +328,7 @@ MODULE_DEVICE_TABLE(usb, usbpn_ids); static struct usb_driver usbpn_driver; -int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) +static int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id) { static const char ifname[] = "usbpn%d"; const struct usb_cdc_union_desc *union_header = NULL; diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index 25ba7eca9a13bdaa7950627111b2eab88a292447..c9f3281506af568e534a47789418b466a0a77adc 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include /* driver specific data - must match cdc_ncm usage */ struct cdc_mbim_state { @@ -42,13 +44,11 @@ static int cdc_mbim_manage_power(struct usbnet *dev, int on) if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) { /* need autopm_get/put here to ensure the usbcore sees the new value */ rv = usb_autopm_get_interface(dev->intf); - if (rv < 0) - goto err; dev->intf->needs_remote_wakeup = on; - usb_autopm_put_interface(dev->intf); + if (!rv) + usb_autopm_put_interface(dev->intf); } -err: - return rv; + return 0; } static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status) @@ -173,7 +173,7 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb } spin_lock_bh(&ctx->mtx); - skb_out = cdc_ncm_fill_tx_frame(ctx, skb, sign); + skb_out = cdc_ncm_fill_tx_frame(dev, skb, sign); spin_unlock_bh(&ctx->mtx); return skb_out; @@ -184,6 +184,60 @@ error: return NULL; } +/* Some devices are known to send Neigbor Solicitation messages and + * require Neigbor Advertisement replies. The IPv6 core will not + * respond since IFF_NOARP is set, so we must handle them ourselves. + */ +static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) +{ + struct ipv6hdr *iph = (void *)buf; + struct nd_msg *msg = (void *)(iph + 1); + struct net_device *netdev; + struct inet6_dev *in6_dev; + bool is_router; + + /* we'll only respond to requests from unicast addresses to + * our solicited node addresses. + */ + if (!ipv6_addr_is_solict_mult(&iph->daddr) || + !(ipv6_addr_type(&iph->saddr) & IPV6_ADDR_UNICAST)) + return; + + /* need to send the NA on the VLAN dev, if any */ + if (tci) + netdev = __vlan_find_dev_deep(dev->net, htons(ETH_P_8021Q), + tci); + else + netdev = dev->net; + if (!netdev) + return; + + in6_dev = in6_dev_get(netdev); + if (!in6_dev) + return; + is_router = !!in6_dev->cnf.forwarding; + in6_dev_put(in6_dev); + + /* ipv6_stub != NULL if in6_dev_get returned an inet6_dev */ + ipv6_stub->ndisc_send_na(netdev, NULL, &iph->saddr, &msg->target, + is_router /* router */, + true /* solicited */, + false /* override */, + true /* inc_opt */); +} + +static bool is_neigh_solicit(u8 *buf, size_t len) +{ + struct ipv6hdr *iph = (void *)buf; + struct nd_msg *msg = (void *)(iph + 1); + + return (len >= sizeof(struct ipv6hdr) + sizeof(struct nd_msg) && + iph->nexthdr == IPPROTO_ICMPV6 && + msg->icmph.icmp6_code == 0 && + msg->icmph.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION); +} + + static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len, u16 tci) { __be16 proto = htons(ETH_P_802_3); @@ -198,6 +252,8 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_ proto = htons(ETH_P_IP); break; case 0x60: + if (is_neigh_solicit(buf, len)) + do_neigh_solicit(dev, buf, tci); proto = htons(ETH_P_IPV6); break; default: @@ -313,15 +369,13 @@ error: static int cdc_mbim_suspend(struct usb_interface *intf, pm_message_t message) { - int ret = 0; + int ret = -ENODEV; struct usbnet *dev = usb_get_intfdata(intf); struct cdc_mbim_state *info = (void *)&dev->data; struct cdc_ncm_ctx *ctx = info->ctx; - if (ctx == NULL) { - ret = -1; + if (!ctx) goto error; - } /* * Both usbnet_suspend() and subdriver->suspend() MUST return 0 @@ -354,7 +408,7 @@ static int cdc_mbim_resume(struct usb_interface *intf) if (ret < 0) goto err; ret = usbnet_resume(intf); - if (ret < 0 && callsub && info->subdriver->suspend) + if (ret < 0 && callsub) info->subdriver->suspend(intf, PMSG_SUSPEND); err: return ret; @@ -371,9 +425,18 @@ static const struct driver_info cdc_mbim_info = { }; /* MBIM and NCM devices should not need a ZLP after NTBs with - * dwNtbOutMaxSize length. This driver_info is for the exceptional - * devices requiring it anyway, allowing them to be supported without - * forcing the performance penalty on all the sane devices. + * dwNtbOutMaxSize length. Nevertheless, a number of devices from + * different vendor IDs will fail unless we send ZLPs, forcing us + * to make this the default. + * + * This default may cause a performance penalty for spec conforming + * devices wanting to take advantage of optimizations possible without + * ZLPs. A whitelist is added in an attempt to avoid this for devices + * known to conform to the MBIM specification. + * + * All known devices supporting NCM compatibility mode are also + * conforming to the NCM and MBIM specifications. For this reason, the + * NCM subclass entry is also in the ZLP whitelist. */ static const struct driver_info cdc_mbim_info_zlp = { .description = "CDC MBIM", @@ -396,16 +459,13 @@ static const struct usb_device_id mbim_devs[] = { { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long)&cdc_mbim_info, }, - /* Sierra Wireless MC7710 need ZLPs */ - { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long)&cdc_mbim_info_zlp, - }, - /* HP hs2434 Mobile Broadband Module needs ZLPs */ - { USB_DEVICE_AND_INTERFACE_INFO(0x3f0, 0x4b1d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long)&cdc_mbim_info_zlp, + /* ZLP conformance whitelist: All Ericsson MBIM devices */ + { USB_VENDOR_AND_INTERFACE_INFO(0x0bdb, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long)&cdc_mbim_info, }, + /* default entry */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), - .driver_info = (unsigned long)&cdc_mbim_info, + .driver_info = (unsigned long)&cdc_mbim_info_zlp, }, { }, diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 43afde8f48d23e2cc3ee15e4cc55bd298b45dfe4..f74786aa37be3cca7a4020cc82b08f0777aec4f5 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -53,8 +53,6 @@ #include #include -#define DRIVER_VERSION "14-Mar-2012" - #if IS_ENABLED(CONFIG_USB_NET_CDC_MBIM) static bool prefer_mbim = true; #else @@ -68,71 +66,67 @@ static void cdc_ncm_tx_timeout_start(struct cdc_ncm_ctx *ctx); static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *hr_timer); static struct usb_driver cdc_ncm_driver; -static void -cdc_ncm_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info) -{ - struct usbnet *dev = netdev_priv(net); - - strlcpy(info->driver, dev->driver_name, sizeof(info->driver)); - strlcpy(info->version, DRIVER_VERSION, sizeof(info->version)); - strlcpy(info->fw_version, dev->driver_info->description, - sizeof(info->fw_version)); - usb_make_path(dev->udev, info->bus_info, sizeof(info->bus_info)); -} - -static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) +static u8 cdc_ncm_setup(struct usbnet *dev) { + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; + struct usb_cdc_ncm_ntb_parameters ncm_parm; u32 val; u8 flags; u8 iface_no; int err; int eth_hlen; u16 ntb_fmt_supported; - u32 min_dgram_size; - u32 min_hdr_size; - struct usbnet *dev = netdev_priv(ctx->netdev); + __le16 max_datagram_size; iface_no = ctx->control->cur_altsetting->desc.bInterfaceNumber; err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_PARAMETERS, USB_TYPE_CLASS | USB_DIR_IN |USB_RECIP_INTERFACE, - 0, iface_no, &ctx->ncm_parm, - sizeof(ctx->ncm_parm)); + 0, iface_no, &ncm_parm, + sizeof(ncm_parm)); if (err < 0) { - pr_debug("failed GET_NTB_PARAMETERS\n"); - return 1; + dev_err(&dev->intf->dev, "failed GET_NTB_PARAMETERS\n"); + return err; /* GET_NTB_PARAMETERS is required */ } /* read correct set of parameters according to device mode */ - ctx->rx_max = le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize); - ctx->tx_max = le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize); - ctx->tx_remainder = le16_to_cpu(ctx->ncm_parm.wNdpOutPayloadRemainder); - ctx->tx_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutDivisor); - ctx->tx_ndp_modulus = le16_to_cpu(ctx->ncm_parm.wNdpOutAlignment); + ctx->rx_max = le32_to_cpu(ncm_parm.dwNtbInMaxSize); + ctx->tx_max = le32_to_cpu(ncm_parm.dwNtbOutMaxSize); + ctx->tx_remainder = le16_to_cpu(ncm_parm.wNdpOutPayloadRemainder); + ctx->tx_modulus = le16_to_cpu(ncm_parm.wNdpOutDivisor); + ctx->tx_ndp_modulus = le16_to_cpu(ncm_parm.wNdpOutAlignment); /* devices prior to NCM Errata shall set this field to zero */ - ctx->tx_max_datagrams = le16_to_cpu(ctx->ncm_parm.wNtbOutMaxDatagrams); - ntb_fmt_supported = le16_to_cpu(ctx->ncm_parm.bmNtbFormatsSupported); + ctx->tx_max_datagrams = le16_to_cpu(ncm_parm.wNtbOutMaxDatagrams); + ntb_fmt_supported = le16_to_cpu(ncm_parm.bmNtbFormatsSupported); - eth_hlen = ETH_HLEN; - min_dgram_size = CDC_NCM_MIN_DATAGRAM_SIZE; - min_hdr_size = CDC_NCM_MIN_HDR_SIZE; - if (ctx->mbim_desc != NULL) { - flags = ctx->mbim_desc->bmNetworkCapabilities; + /* there are some minor differences in NCM and MBIM defaults */ + if (cdc_ncm_comm_intf_is_mbim(ctx->control->cur_altsetting)) { + if (!ctx->mbim_desc) + return -EINVAL; eth_hlen = 0; - min_dgram_size = CDC_MBIM_MIN_DATAGRAM_SIZE; - min_hdr_size = 0; - } else if (ctx->func_desc != NULL) { - flags = ctx->func_desc->bmNetworkCapabilities; + flags = ctx->mbim_desc->bmNetworkCapabilities; + ctx->max_datagram_size = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize); + if (ctx->max_datagram_size < CDC_MBIM_MIN_DATAGRAM_SIZE) + ctx->max_datagram_size = CDC_MBIM_MIN_DATAGRAM_SIZE; } else { - flags = 0; + if (!ctx->func_desc) + return -EINVAL; + eth_hlen = ETH_HLEN; + flags = ctx->func_desc->bmNetworkCapabilities; + ctx->max_datagram_size = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); + if (ctx->max_datagram_size < CDC_NCM_MIN_DATAGRAM_SIZE) + ctx->max_datagram_size = CDC_NCM_MIN_DATAGRAM_SIZE; } - pr_debug("dwNtbInMaxSize=%u dwNtbOutMaxSize=%u " - "wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u " - "wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n", - ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus, - ctx->tx_ndp_modulus, ctx->tx_max_datagrams, flags); + /* common absolute max for NCM and MBIM */ + if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE) + ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE; + + dev_dbg(&dev->intf->dev, + "dwNtbInMaxSize=%u dwNtbOutMaxSize=%u wNdpOutPayloadRemainder=%u wNdpOutDivisor=%u wNdpOutAlignment=%u wNtbOutMaxDatagrams=%u flags=0x%x\n", + ctx->rx_max, ctx->tx_max, ctx->tx_remainder, ctx->tx_modulus, + ctx->tx_ndp_modulus, ctx->tx_max_datagrams, flags); /* max count of tx datagrams */ if ((ctx->tx_max_datagrams == 0) || @@ -141,19 +135,19 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) /* verify maximum size of received NTB in bytes */ if (ctx->rx_max < USB_CDC_NCM_NTB_MIN_IN_SIZE) { - pr_debug("Using min receive length=%d\n", - USB_CDC_NCM_NTB_MIN_IN_SIZE); + dev_dbg(&dev->intf->dev, "Using min receive length=%d\n", + USB_CDC_NCM_NTB_MIN_IN_SIZE); ctx->rx_max = USB_CDC_NCM_NTB_MIN_IN_SIZE; } if (ctx->rx_max > CDC_NCM_NTB_MAX_SIZE_RX) { - pr_debug("Using default maximum receive length=%d\n", - CDC_NCM_NTB_MAX_SIZE_RX); + dev_dbg(&dev->intf->dev, "Using default maximum receive length=%d\n", + CDC_NCM_NTB_MAX_SIZE_RX); ctx->rx_max = CDC_NCM_NTB_MAX_SIZE_RX; } /* inform device about NTB input size changes */ - if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) { + if (ctx->rx_max != le32_to_cpu(ncm_parm.dwNtbInMaxSize)) { __le32 dwNtbInMaxSize = cpu_to_le32(ctx->rx_max); err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_INPUT_SIZE, @@ -161,16 +155,22 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) | USB_RECIP_INTERFACE, 0, iface_no, &dwNtbInMaxSize, 4); if (err < 0) - pr_debug("Setting NTB Input Size failed\n"); + dev_dbg(&dev->intf->dev, "Setting NTB Input Size failed\n"); } /* verify maximum size of transmitted NTB in bytes */ - if ((ctx->tx_max < - (min_hdr_size + min_dgram_size)) || - (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX)) { - pr_debug("Using default maximum transmit length=%d\n", - CDC_NCM_NTB_MAX_SIZE_TX); + if (ctx->tx_max > CDC_NCM_NTB_MAX_SIZE_TX) { + dev_dbg(&dev->intf->dev, "Using default maximum transmit length=%d\n", + CDC_NCM_NTB_MAX_SIZE_TX); ctx->tx_max = CDC_NCM_NTB_MAX_SIZE_TX; + + /* Adding a pad byte here simplifies the handling in + * cdc_ncm_fill_tx_frame, by making tx_max always + * represent the real skb max size. + */ + if (ctx->tx_max % usb_maxpacket(dev->udev, dev->out, 1) == 0) + ctx->tx_max++; + } /* @@ -183,7 +183,7 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) || (val != ((-val) & val)) || (val >= ctx->tx_max)) { - pr_debug("Using default alignment: 4 bytes\n"); + dev_dbg(&dev->intf->dev, "Using default alignment: 4 bytes\n"); ctx->tx_ndp_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE; } @@ -197,13 +197,13 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) if ((val < USB_CDC_NCM_NDP_ALIGN_MIN_SIZE) || (val != ((-val) & val)) || (val >= ctx->tx_max)) { - pr_debug("Using default transmit modulus: 4 bytes\n"); + dev_dbg(&dev->intf->dev, "Using default transmit modulus: 4 bytes\n"); ctx->tx_modulus = USB_CDC_NCM_NDP_ALIGN_MIN_SIZE; } /* verify the payload remainder */ if (ctx->tx_remainder >= ctx->tx_modulus) { - pr_debug("Using default transmit remainder: 0 bytes\n"); + dev_dbg(&dev->intf->dev, "Using default transmit remainder: 0 bytes\n"); ctx->tx_remainder = 0; } @@ -221,7 +221,7 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) USB_CDC_NCM_CRC_NOT_APPENDED, iface_no, NULL, 0); if (err < 0) - pr_debug("Setting CRC mode off failed\n"); + dev_dbg(&dev->intf->dev, "Setting CRC mode off failed\n"); } /* set NTB format, if both formats are supported */ @@ -232,69 +232,43 @@ static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx) USB_CDC_NCM_NTB16_FORMAT, iface_no, NULL, 0); if (err < 0) - pr_debug("Setting NTB format to 16-bit failed\n"); + dev_dbg(&dev->intf->dev, "Setting NTB format to 16-bit failed\n"); } - ctx->max_datagram_size = min_dgram_size; + /* inform the device about the selected Max Datagram Size */ + if (!(flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE)) + goto out; - /* set Max Datagram Size (MTU) */ - if (flags & USB_CDC_NCM_NCAP_MAX_DATAGRAM_SIZE) { - __le16 max_datagram_size; - u16 eth_max_sz; - if (ctx->ether_desc != NULL) - eth_max_sz = le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); - else if (ctx->mbim_desc != NULL) - eth_max_sz = le16_to_cpu(ctx->mbim_desc->wMaxSegmentSize); - else - goto max_dgram_err; - - err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE, - USB_TYPE_CLASS | USB_DIR_IN - | USB_RECIP_INTERFACE, - 0, iface_no, &max_datagram_size, 2); - if (err < 0) { - pr_debug("GET_MAX_DATAGRAM_SIZE failed, use size=%u\n", - min_dgram_size); - } else { - ctx->max_datagram_size = - le16_to_cpu(max_datagram_size); - /* Check Eth descriptor value */ - if (ctx->max_datagram_size > eth_max_sz) - ctx->max_datagram_size = eth_max_sz; - - if (ctx->max_datagram_size > CDC_NCM_MAX_DATAGRAM_SIZE) - ctx->max_datagram_size = CDC_NCM_MAX_DATAGRAM_SIZE; - - if (ctx->max_datagram_size < min_dgram_size) - ctx->max_datagram_size = min_dgram_size; - - /* if value changed, update device */ - if (ctx->max_datagram_size != - le16_to_cpu(max_datagram_size)) { - err = usbnet_write_cmd(dev, - USB_CDC_SET_MAX_DATAGRAM_SIZE, - USB_TYPE_CLASS | USB_DIR_OUT - | USB_RECIP_INTERFACE, - 0, - iface_no, &max_datagram_size, - 2); - if (err < 0) - pr_debug("SET_MAX_DGRAM_SIZE failed\n"); - } - } + /* read current mtu value from device */ + err = usbnet_read_cmd(dev, USB_CDC_GET_MAX_DATAGRAM_SIZE, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, iface_no, &max_datagram_size, 2); + if (err < 0) { + dev_dbg(&dev->intf->dev, "GET_MAX_DATAGRAM_SIZE failed\n"); + goto out; } -max_dgram_err: - if (ctx->netdev->mtu != (ctx->max_datagram_size - eth_hlen)) - ctx->netdev->mtu = ctx->max_datagram_size - eth_hlen; + if (le16_to_cpu(max_datagram_size) == ctx->max_datagram_size) + goto out; + + max_datagram_size = cpu_to_le16(ctx->max_datagram_size); + err = usbnet_write_cmd(dev, USB_CDC_SET_MAX_DATAGRAM_SIZE, + USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, + 0, iface_no, &max_datagram_size, 2); + if (err < 0) + dev_dbg(&dev->intf->dev, "SET_MAX_DATAGRAM_SIZE failed\n"); +out: + /* set MTU to max supported by the device if necessary */ + if (dev->net->mtu > ctx->max_datagram_size - eth_hlen) + dev->net->mtu = ctx->max_datagram_size - eth_hlen; return 0; } static void -cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf) +cdc_ncm_find_endpoints(struct usbnet *dev, struct usb_interface *intf) { - struct usb_host_endpoint *e; + struct usb_host_endpoint *e, *in = NULL, *out = NULL; u8 ep; for (ep = 0; ep < intf->cur_altsetting->desc.bNumEndpoints; ep++) { @@ -303,18 +277,18 @@ cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf) switch (e->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_INT: if (usb_endpoint_dir_in(&e->desc)) { - if (ctx->status_ep == NULL) - ctx->status_ep = e; + if (!dev->status) + dev->status = e; } break; case USB_ENDPOINT_XFER_BULK: if (usb_endpoint_dir_in(&e->desc)) { - if (ctx->in_ep == NULL) - ctx->in_ep = e; + if (!in) + in = e; } else { - if (ctx->out_ep == NULL) - ctx->out_ep = e; + if (!out) + out = e; } break; @@ -322,6 +296,14 @@ cdc_ncm_find_endpoints(struct cdc_ncm_ctx *ctx, struct usb_interface *intf) break; } } + if (in && !dev->in) + dev->in = usb_rcvbulkpipe(dev->udev, + in->desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + if (out && !dev->out) + dev->out = usb_sndbulkpipe(dev->udev, + out->desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); } static void cdc_ncm_free(struct cdc_ncm_ctx *ctx) @@ -342,18 +324,9 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx) kfree(ctx); } -static const struct ethtool_ops cdc_ncm_ethtool_ops = { - .get_drvinfo = cdc_ncm_get_drvinfo, - .get_link = usbnet_get_link, - .get_msglevel = usbnet_get_msglevel, - .set_msglevel = usbnet_set_msglevel, - .get_settings = usbnet_get_settings, - .set_settings = usbnet_set_settings, - .nway_reset = usbnet_nway_reset, -}; - int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting) { + const struct usb_cdc_union_desc *union_desc = NULL; struct cdc_ncm_ctx *ctx; struct usb_driver *driver; u8 *buf; @@ -367,23 +340,22 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ hrtimer_init(&ctx->tx_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ctx->tx_timer.function = &cdc_ncm_tx_timer_cb; - ctx->bh.data = (unsigned long)ctx; + ctx->bh.data = (unsigned long)dev; ctx->bh.func = cdc_ncm_txpath_bh; atomic_set(&ctx->stop, 0); spin_lock_init(&ctx->mtx); - ctx->netdev = dev->net; /* store ctx pointer in device data field */ dev->data[0] = (unsigned long)ctx; + /* only the control interface can be successfully probed */ + ctx->control = intf; + /* get some pointers */ driver = driver_of(intf); buf = intf->cur_altsetting->extra; len = intf->cur_altsetting->extralen; - ctx->udev = dev->udev; - ctx->intf = intf; - /* parse through descriptors associated with control interface */ while ((len > 0) && (buf[0] > 2) && (buf[0] <= len)) { @@ -392,16 +364,18 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ switch (buf[2]) { case USB_CDC_UNION_TYPE: - if (buf[0] < sizeof(*(ctx->union_desc))) + if (buf[0] < sizeof(*union_desc)) break; - ctx->union_desc = - (const struct usb_cdc_union_desc *)buf; - - ctx->control = usb_ifnum_to_if(dev->udev, - ctx->union_desc->bMasterInterface0); + union_desc = (const struct usb_cdc_union_desc *)buf; + /* the master must be the interface we are probing */ + if (intf->cur_altsetting->desc.bInterfaceNumber != + union_desc->bMasterInterface0) { + dev_dbg(&intf->dev, "bogus CDC Union\n"); + goto error; + } ctx->data = usb_ifnum_to_if(dev->udev, - ctx->union_desc->bSlaveInterface0); + union_desc->bSlaveInterface0); break; case USB_CDC_ETHERNET_TYPE: @@ -410,13 +384,6 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ ctx->ether_desc = (const struct usb_cdc_ether_desc *)buf; - dev->hard_mtu = - le16_to_cpu(ctx->ether_desc->wMaxSegmentSize); - - if (dev->hard_mtu < CDC_NCM_MIN_DATAGRAM_SIZE) - dev->hard_mtu = CDC_NCM_MIN_DATAGRAM_SIZE; - else if (dev->hard_mtu > CDC_NCM_MAX_DATAGRAM_SIZE) - dev->hard_mtu = CDC_NCM_MAX_DATAGRAM_SIZE; break; case USB_CDC_NCM_TYPE: @@ -444,69 +411,71 @@ advance: } /* some buggy devices have an IAD but no CDC Union */ - if (!ctx->union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { - ctx->control = intf; + if (!union_desc && intf->intf_assoc && intf->intf_assoc->bInterfaceCount == 2) { ctx->data = usb_ifnum_to_if(dev->udev, intf->cur_altsetting->desc.bInterfaceNumber + 1); dev_dbg(&intf->dev, "CDC Union missing - got slave from IAD\n"); } /* check if we got everything */ - if ((ctx->control == NULL) || (ctx->data == NULL) || - ((!ctx->mbim_desc) && ((ctx->ether_desc == NULL) || (ctx->control != intf)))) + if (!ctx->data || (!ctx->mbim_desc && !ctx->ether_desc)) { + dev_dbg(&intf->dev, "CDC descriptors missing\n"); goto error; + } /* claim data interface, if different from control */ if (ctx->data != ctx->control) { temp = usb_driver_claim_interface(driver, ctx->data, dev); - if (temp) + if (temp) { + dev_dbg(&intf->dev, "failed to claim data intf\n"); goto error; + } } iface_no = ctx->data->cur_altsetting->desc.bInterfaceNumber; /* reset data interface */ temp = usb_set_interface(dev->udev, iface_no, 0); - if (temp) - goto error2; - - /* initialize data interface */ - if (cdc_ncm_setup(ctx)) + if (temp) { + dev_dbg(&intf->dev, "set interface failed\n"); goto error2; + } /* configure data interface */ temp = usb_set_interface(dev->udev, iface_no, data_altsetting); - if (temp) + if (temp) { + dev_dbg(&intf->dev, "set interface failed\n"); goto error2; + } - cdc_ncm_find_endpoints(ctx, ctx->data); - cdc_ncm_find_endpoints(ctx, ctx->control); - - if ((ctx->in_ep == NULL) || (ctx->out_ep == NULL) || - (ctx->status_ep == NULL)) + cdc_ncm_find_endpoints(dev, ctx->data); + cdc_ncm_find_endpoints(dev, ctx->control); + if (!dev->in || !dev->out || !dev->status) { + dev_dbg(&intf->dev, "failed to collect endpoints\n"); goto error2; + } - dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; + /* initialize data interface */ + if (cdc_ncm_setup(dev)) { + dev_dbg(&intf->dev, "cdc_ncm_setup() failed\n"); + goto error2; + } usb_set_intfdata(ctx->data, dev); usb_set_intfdata(ctx->control, dev); - usb_set_intfdata(ctx->intf, dev); if (ctx->ether_desc) { temp = usbnet_get_ethernet_addr(dev, ctx->ether_desc->iMACAddress); - if (temp) + if (temp) { + dev_dbg(&intf->dev, "failed to get mac address\n"); goto error2; - dev_info(&dev->udev->dev, "MAC-Address: %pM\n", dev->net->dev_addr); + } + dev_info(&intf->dev, "MAC-Address: %pM\n", dev->net->dev_addr); } - - dev->in = usb_rcvbulkpipe(dev->udev, - ctx->in_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - dev->out = usb_sndbulkpipe(dev->udev, - ctx->out_ep->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); - dev->status = ctx->status_ep; + /* usbnet use these values for sizing tx/rx queues */ + dev->hard_mtu = ctx->tx_max; dev->rx_urb_size = ctx->rx_max; - ctx->tx_speed = ctx->rx_speed = 0; return 0; error2: @@ -517,7 +486,7 @@ error2: error: cdc_ncm_free((struct cdc_ncm_ctx *)dev->data[0]); dev->data[0] = 0; - dev_info(&dev->udev->dev, "bind() failure\n"); + dev_info(&intf->dev, "bind() failure\n"); return -ENODEV; } EXPORT_SYMBOL_GPL(cdc_ncm_bind_common); @@ -553,7 +522,7 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf) ctx->control = NULL; } - usb_set_intfdata(ctx->intf, NULL); + usb_set_intfdata(intf, NULL); cdc_ncm_free(ctx); } EXPORT_SYMBOL_GPL(cdc_ncm_unbind); @@ -662,8 +631,9 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_ } struct sk_buff * -cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) +cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) { + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; struct usb_cdc_ncm_nth16 *nth16; struct usb_cdc_ncm_ndp16 *ndp16; struct sk_buff *skb_out; @@ -683,11 +653,11 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) /* allocate a new OUT skb */ if (!skb_out) { - skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC); + skb_out = alloc_skb(ctx->tx_max, GFP_ATOMIC); if (skb_out == NULL) { if (skb != NULL) { dev_kfree_skb_any(skb); - ctx->netdev->stats.tx_dropped++; + dev->net->stats.tx_dropped++; } goto exit_no_skb; } @@ -725,12 +695,12 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) /* won't fit, MTU problem? */ dev_kfree_skb_any(skb); skb = NULL; - ctx->netdev->stats.tx_dropped++; + dev->net->stats.tx_dropped++; } else { /* no room for skb - store for later */ if (ctx->tx_rem_skb != NULL) { dev_kfree_skb_any(ctx->tx_rem_skb); - ctx->netdev->stats.tx_dropped++; + dev->net->stats.tx_dropped++; } ctx->tx_rem_skb = skb; ctx->tx_rem_sign = sign; @@ -763,7 +733,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) if (skb != NULL) { dev_kfree_skb_any(skb); skb = NULL; - ctx->netdev->stats.tx_dropped++; + dev->net->stats.tx_dropped++; } ctx->tx_curr_frame_num = n; @@ -788,19 +758,20 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) /* variables will be reset at next call */ } - /* - * If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes, - * we send buffers as it is. If we get more data, it would be more - * efficient for USB HS mobile device with DMA engine to receive a full - * size NTB, than canceling DMA transfer and receiving a short packet. + /* If collected data size is less or equal CDC_NCM_MIN_TX_PKT + * bytes, we send buffers as it is. If we get more data, it + * would be more efficient for USB HS mobile device with DMA + * engine to receive a full size NTB, than canceling DMA + * transfer and receiving a short packet. + * + * This optimization support is pointless if we end up sending + * a ZLP after full sized NTBs. */ - if (skb_out->len > CDC_NCM_MIN_TX_PKT) - /* final zero padding */ - memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len); - - /* do we need to prevent a ZLP? */ - if (((skb_out->len % le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0) && - (skb_out->len < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)) && skb_tailroom(skb_out)) + if (!(dev->driver_info->flags & FLAG_SEND_ZLP) && + skb_out->len > CDC_NCM_MIN_TX_PKT) + memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, + ctx->tx_max - skb_out->len); + else if ((skb_out->len % dev->maxpacket) == 0) *skb_put(skb_out, 1) = 0; /* force short packet */ /* set final frame length */ @@ -809,7 +780,7 @@ cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign) /* return skb */ ctx->tx_curr_skb = NULL; - ctx->netdev->stats.tx_packets += ctx->tx_curr_frame_num; + dev->net->stats.tx_packets += ctx->tx_curr_frame_num; return skb_out; exit_no_skb: @@ -841,24 +812,25 @@ static enum hrtimer_restart cdc_ncm_tx_timer_cb(struct hrtimer *timer) static void cdc_ncm_txpath_bh(unsigned long param) { - struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)param; + struct usbnet *dev = (struct usbnet *)param; + struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; spin_lock_bh(&ctx->mtx); if (ctx->tx_timer_pending != 0) { ctx->tx_timer_pending--; cdc_ncm_tx_timeout_start(ctx); spin_unlock_bh(&ctx->mtx); - } else if (ctx->netdev != NULL) { + } else if (dev->net != NULL) { spin_unlock_bh(&ctx->mtx); - netif_tx_lock_bh(ctx->netdev); - usbnet_start_xmit(NULL, ctx->netdev); - netif_tx_unlock_bh(ctx->netdev); + netif_tx_lock_bh(dev->net); + usbnet_start_xmit(NULL, dev->net); + netif_tx_unlock_bh(dev->net); } else { spin_unlock_bh(&ctx->mtx); } } -static struct sk_buff * +struct sk_buff * cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) { struct sk_buff *skb_out; @@ -875,7 +847,7 @@ cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) goto error; spin_lock_bh(&ctx->mtx); - skb_out = cdc_ncm_fill_tx_frame(ctx, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); + skb_out = cdc_ncm_fill_tx_frame(dev, skb, cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)); spin_unlock_bh(&ctx->mtx); return skb_out; @@ -885,10 +857,12 @@ error: return NULL; } +EXPORT_SYMBOL_GPL(cdc_ncm_tx_fixup); /* verify NTB header and return offset of first NDP, or negative error */ int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in) { + struct usbnet *dev = netdev_priv(skb_in->dev); struct usb_cdc_ncm_nth16 *nth16; int len; int ret = -EINVAL; @@ -898,30 +872,33 @@ int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in) if (skb_in->len < (sizeof(struct usb_cdc_ncm_nth16) + sizeof(struct usb_cdc_ncm_ndp16))) { - pr_debug("frame too short\n"); + netif_dbg(dev, rx_err, dev->net, "frame too short\n"); goto error; } nth16 = (struct usb_cdc_ncm_nth16 *)skb_in->data; - if (le32_to_cpu(nth16->dwSignature) != USB_CDC_NCM_NTH16_SIGN) { - pr_debug("invalid NTH16 signature <%u>\n", - le32_to_cpu(nth16->dwSignature)); + if (nth16->dwSignature != cpu_to_le32(USB_CDC_NCM_NTH16_SIGN)) { + netif_dbg(dev, rx_err, dev->net, + "invalid NTH16 signature <%#010x>\n", + le32_to_cpu(nth16->dwSignature)); goto error; } len = le16_to_cpu(nth16->wBlockLength); if (len > ctx->rx_max) { - pr_debug("unsupported NTB block length %u/%u\n", len, - ctx->rx_max); + netif_dbg(dev, rx_err, dev->net, + "unsupported NTB block length %u/%u\n", len, + ctx->rx_max); goto error; } if ((ctx->rx_seq + 1) != le16_to_cpu(nth16->wSequence) && - (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) && - !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) { - pr_debug("sequence number glitch prev=%d curr=%d\n", - ctx->rx_seq, le16_to_cpu(nth16->wSequence)); + (ctx->rx_seq || le16_to_cpu(nth16->wSequence)) && + !((ctx->rx_seq == 0xffff) && !le16_to_cpu(nth16->wSequence))) { + netif_dbg(dev, rx_err, dev->net, + "sequence number glitch prev=%d curr=%d\n", + ctx->rx_seq, le16_to_cpu(nth16->wSequence)); } ctx->rx_seq = le16_to_cpu(nth16->wSequence); @@ -934,18 +911,20 @@ EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_nth16); /* verify NDP header and return number of datagrams, or negative error */ int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset) { + struct usbnet *dev = netdev_priv(skb_in->dev); struct usb_cdc_ncm_ndp16 *ndp16; int ret = -EINVAL; if ((ndpoffset + sizeof(struct usb_cdc_ncm_ndp16)) > skb_in->len) { - pr_debug("invalid NDP offset <%u>\n", ndpoffset); + netif_dbg(dev, rx_err, dev->net, "invalid NDP offset <%u>\n", + ndpoffset); goto error; } ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); if (le16_to_cpu(ndp16->wLength) < USB_CDC_NCM_NDP16_LENGTH_MIN) { - pr_debug("invalid DPT16 length <%u>\n", - le32_to_cpu(ndp16->dwSignature)); + netif_dbg(dev, rx_err, dev->net, "invalid DPT16 length <%u>\n", + le16_to_cpu(ndp16->wLength)); goto error; } @@ -954,9 +933,9 @@ int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset) sizeof(struct usb_cdc_ncm_dpe16)); ret--; /* we process NDP entries except for the last one */ - if ((sizeof(struct usb_cdc_ncm_ndp16) + ret * (sizeof(struct usb_cdc_ncm_dpe16))) > - skb_in->len) { - pr_debug("Invalid nframes = %d\n", ret); + if ((sizeof(struct usb_cdc_ncm_ndp16) + + ret * (sizeof(struct usb_cdc_ncm_dpe16))) > skb_in->len) { + netif_dbg(dev, rx_err, dev->net, "Invalid nframes = %d\n", ret); ret = -EINVAL; } @@ -965,7 +944,7 @@ error: } EXPORT_SYMBOL_GPL(cdc_ncm_rx_verify_ndp16); -static int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) +int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) { struct sk_buff *skb; struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0]; @@ -989,9 +968,10 @@ next_ndp: ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); - if (le32_to_cpu(ndp16->dwSignature) != USB_CDC_NCM_NDP16_NOCRC_SIGN) { - pr_debug("invalid DPT16 signature <%u>\n", - le32_to_cpu(ndp16->dwSignature)); + if (ndp16->dwSignature != cpu_to_le32(USB_CDC_NCM_NDP16_NOCRC_SIGN)) { + netif_dbg(dev, rx_err, dev->net, + "invalid DPT16 signature <%#010x>\n", + le32_to_cpu(ndp16->dwSignature)); goto err_ndp; } dpe16 = ndp16->dpe16; @@ -1013,9 +993,9 @@ next_ndp: /* sanity checking */ if (((offset + len) > skb_in->len) || (len > ctx->rx_max) || (len < ETH_HLEN)) { - pr_debug("invalid frame detected (ignored)" - "offset[%u]=%u, length=%u, skb=%p\n", - x, offset, len, skb_in); + netif_dbg(dev, rx_err, dev->net, + "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n", + x, offset, len, skb_in); if (!x) goto err_ndp; break; @@ -1040,9 +1020,10 @@ err_ndp: error: return 0; } +EXPORT_SYMBOL_GPL(cdc_ncm_rx_fixup); static void -cdc_ncm_speed_change(struct cdc_ncm_ctx *ctx, +cdc_ncm_speed_change(struct usbnet *dev, struct usb_cdc_speed_change *data) { uint32_t rx_speed = le32_to_cpu(data->DLBitRRate); @@ -1052,25 +1033,16 @@ cdc_ncm_speed_change(struct cdc_ncm_ctx *ctx, * Currently the USB-NET API does not support reporting the actual * device speed. Do print it instead. */ - if ((tx_speed != ctx->tx_speed) || (rx_speed != ctx->rx_speed)) { - ctx->tx_speed = tx_speed; - ctx->rx_speed = rx_speed; - - if ((tx_speed > 1000000) && (rx_speed > 1000000)) { - printk(KERN_INFO KBUILD_MODNAME - ": %s: %u mbit/s downlink " - "%u mbit/s uplink\n", - ctx->netdev->name, - (unsigned int)(rx_speed / 1000000U), - (unsigned int)(tx_speed / 1000000U)); - } else { - printk(KERN_INFO KBUILD_MODNAME - ": %s: %u kbit/s downlink " - "%u kbit/s uplink\n", - ctx->netdev->name, - (unsigned int)(rx_speed / 1000U), - (unsigned int)(tx_speed / 1000U)); - } + if ((tx_speed > 1000000) && (rx_speed > 1000000)) { + netif_info(dev, link, dev->net, + "%u mbit/s downlink %u mbit/s uplink\n", + (unsigned int)(rx_speed / 1000000U), + (unsigned int)(tx_speed / 1000000U)); + } else { + netif_info(dev, link, dev->net, + "%u kbit/s downlink %u kbit/s uplink\n", + (unsigned int)(rx_speed / 1000U), + (unsigned int)(tx_speed / 1000U)); } } @@ -1086,7 +1058,7 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb) /* test for split data in 8-byte chunks */ if (test_and_clear_bit(EVENT_STS_SPLIT, &dev->flags)) { - cdc_ncm_speed_change(ctx, + cdc_ncm_speed_change(dev, (struct usb_cdc_speed_change *)urb->transfer_buffer); return; } @@ -1101,14 +1073,10 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb) * sent by device after USB_CDC_NOTIFY_SPEED_CHANGE. */ ctx->connected = le16_to_cpu(event->wValue); - - printk(KERN_INFO KBUILD_MODNAME ": %s: network connection:" - " %sconnected\n", - ctx->netdev->name, ctx->connected ? "" : "dis"); - + netif_info(dev, link, dev->net, + "network connection: %sconnected\n", + ctx->connected ? "" : "dis"); usbnet_link_change(dev, ctx->connected, 0); - if (!ctx->connected) - ctx->tx_speed = ctx->rx_speed = 0; break; case USB_CDC_NOTIFY_SPEED_CHANGE: @@ -1116,8 +1084,8 @@ static void cdc_ncm_status(struct usbnet *dev, struct urb *urb) sizeof(struct usb_cdc_speed_change))) set_bit(EVENT_STS_SPLIT, &dev->flags); else - cdc_ncm_speed_change(ctx, - (struct usb_cdc_speed_change *) &event[1]); + cdc_ncm_speed_change(dev, + (struct usb_cdc_speed_change *)&event[1]); break; default: @@ -1139,22 +1107,6 @@ static int cdc_ncm_check_connect(struct usbnet *dev) return !ctx->connected; } -static int -cdc_ncm_probe(struct usb_interface *udev, const struct usb_device_id *prod) -{ - return usbnet_probe(udev, prod); -} - -static void cdc_ncm_disconnect(struct usb_interface *intf) -{ - struct usbnet *dev = usb_get_intfdata(intf); - - if (dev == NULL) - return; /* already disconnected */ - - usbnet_disconnect(intf); -} - static const struct driver_info cdc_ncm_info = { .description = "CDC NCM", .flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET, @@ -1234,17 +1186,6 @@ static const struct usb_device_id cdc_devs[] = { .driver_info = (unsigned long)&wwan_info, }, - /* Huawei NCM devices disguised as vendor specific */ - { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16), - .driver_info = (unsigned long)&wwan_info, - }, - { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46), - .driver_info = (unsigned long)&wwan_info, - }, - { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76), - .driver_info = (unsigned long)&wwan_info, - }, - /* Infineon(now Intel) HSPA Modem platform */ { USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443, USB_CLASS_COMM, @@ -1265,8 +1206,8 @@ MODULE_DEVICE_TABLE(usb, cdc_devs); static struct usb_driver cdc_ncm_driver = { .name = "cdc_ncm", .id_table = cdc_devs, - .probe = cdc_ncm_probe, - .disconnect = cdc_ncm_disconnect, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, .suspend = usbnet_suspend, .resume = usbnet_resume, .reset_resume = usbnet_resume, diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c new file mode 100644 index 0000000000000000000000000000000000000000..312178d7b698e06a6bc97ede9c7c1eb49510c322 --- /dev/null +++ b/drivers/net/usb/huawei_cdc_ncm.c @@ -0,0 +1,230 @@ +/* huawei_cdc_ncm.c - handles Huawei devices using the CDC NCM protocol as + * transport layer. + * Copyright (C) 2013 Enrico Mioso + * + * + * ABSTRACT: + * This driver handles devices resembling the CDC NCM standard, but + * encapsulating another protocol inside it. An example are some Huawei 3G + * devices, exposing an embedded AT channel where you can set up the NCM + * connection. + * This code has been heavily inspired by the cdc_mbim.c driver, which is + * Copyright (c) 2012 Smith Micro Software, Inc. + * Copyright (c) 2012 Bjørn Mork + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Driver data */ +struct huawei_cdc_ncm_state { + struct cdc_ncm_ctx *ctx; + atomic_t pmcount; + struct usb_driver *subdriver; + struct usb_interface *control; + struct usb_interface *data; +}; + +static int huawei_cdc_ncm_manage_power(struct usbnet *usbnet_dev, int on) +{ + struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; + int rv; + + if ((on && atomic_add_return(1, &drvstate->pmcount) == 1) || + (!on && atomic_dec_and_test(&drvstate->pmcount))) { + rv = usb_autopm_get_interface(usbnet_dev->intf); + usbnet_dev->intf->needs_remote_wakeup = on; + if (!rv) + usb_autopm_put_interface(usbnet_dev->intf); + } + return 0; +} + +static int huawei_cdc_ncm_wdm_manage_power(struct usb_interface *intf, + int status) +{ + struct usbnet *usbnet_dev = usb_get_intfdata(intf); + + /* can be called while disconnecting */ + if (!usbnet_dev) + return 0; + + return huawei_cdc_ncm_manage_power(usbnet_dev, status); +} + + +static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev, + struct usb_interface *intf) +{ + struct cdc_ncm_ctx *ctx; + struct usb_driver *subdriver = ERR_PTR(-ENODEV); + int ret = -ENODEV; + struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; + + /* altsetting should always be 1 for NCM devices - so we hard-coded + * it here + */ + ret = cdc_ncm_bind_common(usbnet_dev, intf, 1); + if (ret) + goto err; + + ctx = drvstate->ctx; + + if (usbnet_dev->status) + /* CDC-WMC r1.1 requires wMaxCommand to be "at least 256 + * decimal (0x100)" + */ + subdriver = usb_cdc_wdm_register(ctx->control, + &usbnet_dev->status->desc, + 256, /* wMaxCommand */ + huawei_cdc_ncm_wdm_manage_power); + if (IS_ERR(subdriver)) { + ret = PTR_ERR(subdriver); + cdc_ncm_unbind(usbnet_dev, intf); + goto err; + } + + /* Prevent usbnet from using the status descriptor */ + usbnet_dev->status = NULL; + + drvstate->subdriver = subdriver; + +err: + return ret; +} + +static void huawei_cdc_ncm_unbind(struct usbnet *usbnet_dev, + struct usb_interface *intf) +{ + struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; + struct cdc_ncm_ctx *ctx = drvstate->ctx; + + if (drvstate->subdriver && drvstate->subdriver->disconnect) + drvstate->subdriver->disconnect(ctx->control); + drvstate->subdriver = NULL; + + cdc_ncm_unbind(usbnet_dev, intf); +} + +static int huawei_cdc_ncm_suspend(struct usb_interface *intf, + pm_message_t message) +{ + int ret = 0; + struct usbnet *usbnet_dev = usb_get_intfdata(intf); + struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; + struct cdc_ncm_ctx *ctx = drvstate->ctx; + + if (ctx == NULL) { + ret = -ENODEV; + goto error; + } + + ret = usbnet_suspend(intf, message); + if (ret < 0) + goto error; + + if (intf == ctx->control && + drvstate->subdriver && + drvstate->subdriver->suspend) + ret = drvstate->subdriver->suspend(intf, message); + if (ret < 0) + usbnet_resume(intf); + +error: + return ret; +} + +static int huawei_cdc_ncm_resume(struct usb_interface *intf) +{ + int ret = 0; + struct usbnet *usbnet_dev = usb_get_intfdata(intf); + struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; + bool callsub; + struct cdc_ncm_ctx *ctx = drvstate->ctx; + + /* should we call subdriver's resume function? */ + callsub = + (intf == ctx->control && + drvstate->subdriver && + drvstate->subdriver->resume); + + if (callsub) + ret = drvstate->subdriver->resume(intf); + if (ret < 0) + goto err; + ret = usbnet_resume(intf); + if (ret < 0 && callsub) + drvstate->subdriver->suspend(intf, PMSG_SUSPEND); +err: + return ret; +} + +static int huawei_cdc_ncm_check_connect(struct usbnet *usbnet_dev) +{ + struct cdc_ncm_ctx *ctx; + + ctx = (struct cdc_ncm_ctx *)usbnet_dev->data[0]; + + if (ctx == NULL) + return 1; /* disconnected */ + + return !ctx->connected; +} + +static const struct driver_info huawei_cdc_ncm_info = { + .description = "Huawei CDC NCM device", + .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, + .bind = huawei_cdc_ncm_bind, + .unbind = huawei_cdc_ncm_unbind, + .check_connect = huawei_cdc_ncm_check_connect, + .manage_power = huawei_cdc_ncm_manage_power, + .rx_fixup = cdc_ncm_rx_fixup, + .tx_fixup = cdc_ncm_tx_fixup, +}; + +static const struct usb_device_id huawei_cdc_ncm_devs[] = { + /* Huawei NCM devices disguised as vendor specific */ + { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x16), + .driver_info = (unsigned long)&huawei_cdc_ncm_info, + }, + { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46), + .driver_info = (unsigned long)&huawei_cdc_ncm_info, + }, + { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76), + .driver_info = (unsigned long)&huawei_cdc_ncm_info, + }, + + /* Terminating entry */ + { + }, +}; +MODULE_DEVICE_TABLE(usb, huawei_cdc_ncm_devs); + +static struct usb_driver huawei_cdc_ncm_driver = { + .name = "huawei_cdc_ncm", + .id_table = huawei_cdc_ncm_devs, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, + .suspend = huawei_cdc_ncm_suspend, + .resume = huawei_cdc_ncm_resume, + .reset_resume = huawei_cdc_ncm_resume, + .supports_autosuspend = 1, + .disable_hub_initiated_lpm = 1, +}; +module_usb_driver(huawei_cdc_ncm_driver); +MODULE_AUTHOR("Enrico Mioso "); +MODULE_DESCRIPTION("USB CDC NCM host driver with encapsulated protocol support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 818ce90185b5dee9e736923cee09d2b7a0a8d840..23bdd5b9274ddb6becb5eabad2d4709b7b2a732d 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -143,24 +143,28 @@ static const struct net_device_ops qmi_wwan_netdev_ops = { .ndo_validate_addr = eth_validate_addr, }; -/* using a counter to merge subdriver requests with our own into a combined state */ +/* using a counter to merge subdriver requests with our own into a + * combined state + */ static int qmi_wwan_manage_power(struct usbnet *dev, int on) { struct qmi_wwan_state *info = (void *)&dev->data; - int rv = 0; + int rv; - dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on); + dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, + atomic_read(&info->pmcount), on); - if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) { - /* need autopm_get/put here to ensure the usbcore sees the new value */ + if ((on && atomic_add_return(1, &info->pmcount) == 1) || + (!on && atomic_dec_and_test(&info->pmcount))) { + /* need autopm_get/put here to ensure the usbcore sees + * the new value + */ rv = usb_autopm_get_interface(dev->intf); - if (rv < 0) - goto err; dev->intf->needs_remote_wakeup = on; - usb_autopm_put_interface(dev->intf); + if (!rv) + usb_autopm_put_interface(dev->intf); } -err: - return rv; + return 0; } static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) @@ -199,7 +203,8 @@ static int qmi_wwan_register_subdriver(struct usbnet *dev) atomic_set(&info->pmcount, 0); /* register subdriver */ - subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, 4096, &qmi_wwan_cdc_wdm_manage_power); + subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, + 4096, &qmi_wwan_cdc_wdm_manage_power); if (IS_ERR(subdriver)) { dev_err(&info->control->dev, "subdriver registration failed\n"); rv = PTR_ERR(subdriver); @@ -228,7 +233,8 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) struct usb_driver *driver = driver_of(intf); struct qmi_wwan_state *info = (void *)&dev->data; - BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); + BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < + sizeof(struct qmi_wwan_state))); /* set up initial state */ info->control = intf; @@ -250,7 +256,8 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) goto err; } if (h->bLength != sizeof(struct usb_cdc_header_desc)) { - dev_dbg(&intf->dev, "CDC header len %u\n", h->bLength); + dev_dbg(&intf->dev, "CDC header len %u\n", + h->bLength); goto err; } break; @@ -260,7 +267,8 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) goto err; } if (h->bLength != sizeof(struct usb_cdc_union_desc)) { - dev_dbg(&intf->dev, "CDC union len %u\n", h->bLength); + dev_dbg(&intf->dev, "CDC union len %u\n", + h->bLength); goto err; } cdc_union = (struct usb_cdc_union_desc *)buf; @@ -271,15 +279,15 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) goto err; } if (h->bLength != sizeof(struct usb_cdc_ether_desc)) { - dev_dbg(&intf->dev, "CDC ether len %u\n", h->bLength); + dev_dbg(&intf->dev, "CDC ether len %u\n", + h->bLength); goto err; } cdc_ether = (struct usb_cdc_ether_desc *)buf; break; } - /* - * Remember which CDC functional descriptors we've seen. Works + /* Remember which CDC functional descriptors we've seen. Works * for all types we care about, of which USB_CDC_ETHERNET_TYPE * (0x0f) is the highest numbered */ @@ -293,10 +301,14 @@ next_desc: /* Use separate control and data interfaces if we found a CDC Union */ if (cdc_union) { - info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); - if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || !info->data) { - dev_err(&intf->dev, "bogus CDC Union: master=%u, slave=%u\n", - cdc_union->bMasterInterface0, cdc_union->bSlaveInterface0); + info->data = usb_ifnum_to_if(dev->udev, + cdc_union->bSlaveInterface0); + if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || + !info->data) { + dev_err(&intf->dev, + "bogus CDC Union: master=%u, slave=%u\n", + cdc_union->bMasterInterface0, + cdc_union->bSlaveInterface0); goto err; } } @@ -374,8 +386,7 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message) struct qmi_wwan_state *info = (void *)&dev->data; int ret; - /* - * Both usbnet_suspend() and subdriver->suspend() MUST return 0 + /* Both usbnet_suspend() and subdriver->suspend() MUST return 0 * in system sleep context, otherwise, the resume callback has * to recover device from previous suspend failure. */ @@ -383,7 +394,8 @@ static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message) if (ret < 0) goto err; - if (intf == info->control && info->subdriver && info->subdriver->suspend) + if (intf == info->control && info->subdriver && + info->subdriver->suspend) ret = info->subdriver->suspend(intf, message); if (ret < 0) usbnet_resume(intf); @@ -396,14 +408,15 @@ static int qmi_wwan_resume(struct usb_interface *intf) struct usbnet *dev = usb_get_intfdata(intf); struct qmi_wwan_state *info = (void *)&dev->data; int ret = 0; - bool callsub = (intf == info->control && info->subdriver && info->subdriver->resume); + bool callsub = (intf == info->control && info->subdriver && + info->subdriver->resume); if (callsub) ret = info->subdriver->resume(intf); if (ret < 0) goto err; ret = usbnet_resume(intf); - if (ret < 0 && callsub && info->subdriver->suspend) + if (ret < 0 && callsub) info->subdriver->suspend(intf, PMSG_SUSPEND); err: return ret; @@ -714,6 +727,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ {QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */ {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */ + {QMI_FIXED_INTF(0x1bc7, 0x1201, 2)}, /* Telit LE920 */ {QMI_FIXED_INTF(0x0b3c, 0xc005, 6)}, /* Olivetti Olicard 200 */ {QMI_FIXED_INTF(0x1e2d, 0x0060, 4)}, /* Cinterion PLxx */ @@ -777,7 +791,8 @@ static const struct usb_device_id products[] = { }; MODULE_DEVICE_TABLE(usb, products); -static int qmi_wwan_probe(struct usb_interface *intf, const struct usb_device_id *prod) +static int qmi_wwan_probe(struct usb_interface *intf, + const struct usb_device_id *prod) { struct usb_device_id *id = (struct usb_device_id *)prod; diff --git a/drivers/net/veth.c b/drivers/net/veth.c index eee1f19ef1e9397469e343133490ec6972b4bbdc..b24db7acbf1207e6e1d905111cf5a59108744743 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -188,6 +188,11 @@ static struct rtnl_link_stats64 *veth_get_stats64(struct net_device *dev, return tot; } +/* fake multicast ability */ +static void veth_set_multicast_list(struct net_device *dev) +{ +} + static int veth_open(struct net_device *dev) { struct veth_priv *priv = netdev_priv(dev); @@ -250,11 +255,14 @@ static const struct net_device_ops veth_netdev_ops = { .ndo_start_xmit = veth_xmit, .ndo_change_mtu = veth_change_mtu, .ndo_get_stats64 = veth_get_stats64, + .ndo_set_rx_mode = veth_set_multicast_list, .ndo_set_mac_address = eth_mac_addr, }; #define VETH_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \ NETIF_F_HW_CSUM | NETIF_F_RXCSUM | NETIF_F_HIGHDMA | \ + NETIF_F_GSO_GRE | NETIF_F_GSO_UDP_TUNNEL | \ + NETIF_F_GSO_IPIP | NETIF_F_GSO_SIT | NETIF_F_UFO | \ NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | \ NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_VLAN_STAG_RX ) @@ -273,6 +281,7 @@ static void veth_setup(struct net_device *dev) dev->destructor = veth_dev_free; dev->hw_features = VETH_FEATURES; + dev->hw_enc_features = VETH_FEATURES; } /* diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index bbc9cb84ec1fe4f857bc5df63a0ecb3f80659b1b..01f4eb5c8b786aca792cd42b8e87568ced1413d1 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -124,12 +124,14 @@ struct virtnet_info { /* Lock for config space updates */ struct mutex config_lock; + /* Page_frag for GFP_KERNEL packet buffer allocation when we run + * low on memory. + */ + struct page_frag alloc_frag; + /* Does the affinity hint is set for virtqueues? */ bool affinity_hint_set; - /* Per-cpu variable to show the mapping from CPU to virtqueue */ - int __percpu *vq_index; - /* CPU hot plug notifier */ struct notifier_block nb; }; @@ -217,33 +219,18 @@ static void skb_xmit_done(struct virtqueue *vq) netif_wake_subqueue(vi->dev, vq2txq(vq)); } -static void set_skb_frag(struct sk_buff *skb, struct page *page, - unsigned int offset, unsigned int *len) -{ - int size = min((unsigned)PAGE_SIZE - offset, *len); - int i = skb_shinfo(skb)->nr_frags; - - __skb_fill_page_desc(skb, i, page, offset, size); - - skb->data_len += size; - skb->len += size; - skb->truesize += PAGE_SIZE; - skb_shinfo(skb)->nr_frags++; - skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; - *len -= size; -} - /* Called from bottom half context */ static struct sk_buff *page_to_skb(struct receive_queue *rq, - struct page *page, unsigned int len) + struct page *page, unsigned int offset, + unsigned int len, unsigned int truesize) { struct virtnet_info *vi = rq->vq->vdev->priv; struct sk_buff *skb; struct skb_vnet_hdr *hdr; - unsigned int copy, hdr_len, offset; + unsigned int copy, hdr_len, hdr_padded_len; char *p; - p = page_address(page); + p = page_address(page) + offset; /* copy small packet so we can reuse these pages for small data */ skb = netdev_alloc_skb_ip_align(vi->dev, GOOD_COPY_LEN); @@ -254,16 +241,17 @@ static struct sk_buff *page_to_skb(struct receive_queue *rq, if (vi->mergeable_rx_bufs) { hdr_len = sizeof hdr->mhdr; - offset = hdr_len; + hdr_padded_len = sizeof hdr->mhdr; } else { hdr_len = sizeof hdr->hdr; - offset = sizeof(struct padded_vnet_hdr); + hdr_padded_len = sizeof(struct padded_vnet_hdr); } memcpy(hdr, p, hdr_len); len -= hdr_len; - p += offset; + offset += hdr_padded_len; + p += hdr_padded_len; copy = len; if (copy > skb_tailroom(skb)) @@ -273,6 +261,14 @@ static struct sk_buff *page_to_skb(struct receive_queue *rq, len -= copy; offset += copy; + if (vi->mergeable_rx_bufs) { + if (len) + skb_add_rx_frag(skb, 0, page, offset, len, truesize); + else + put_page(page); + return skb; + } + /* * Verify that we can indeed put this data into a skb. * This is here to handle cases when the device erroneously @@ -284,9 +280,12 @@ static struct sk_buff *page_to_skb(struct receive_queue *rq, dev_kfree_skb(skb); return NULL; } - + BUG_ON(offset >= PAGE_SIZE); while (len) { - set_skb_frag(skb, page, offset, &len); + unsigned int frag_size = min((unsigned)PAGE_SIZE - offset, len); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, offset, + frag_size, truesize); + len -= frag_size; page = (struct page *)page->private; offset = 0; } @@ -297,33 +296,59 @@ static struct sk_buff *page_to_skb(struct receive_queue *rq, return skb; } -static int receive_mergeable(struct receive_queue *rq, struct sk_buff *skb) +static int receive_mergeable(struct receive_queue *rq, struct sk_buff *head_skb) { - struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb); + struct skb_vnet_hdr *hdr = skb_vnet_hdr(head_skb); + struct sk_buff *curr_skb = head_skb; + char *buf; struct page *page; - int num_buf, i, len; + int num_buf, len, offset; num_buf = hdr->mhdr.num_buffers; while (--num_buf) { - i = skb_shinfo(skb)->nr_frags; - if (i >= MAX_SKB_FRAGS) { - pr_debug("%s: packet too long\n", skb->dev->name); - skb->dev->stats.rx_length_errors++; - return -EINVAL; - } - page = virtqueue_get_buf(rq->vq, &len); - if (!page) { + int num_skb_frags = skb_shinfo(curr_skb)->nr_frags; + buf = virtqueue_get_buf(rq->vq, &len); + if (unlikely(!buf)) { pr_debug("%s: rx error: %d buffers missing\n", - skb->dev->name, hdr->mhdr.num_buffers); - skb->dev->stats.rx_length_errors++; + head_skb->dev->name, hdr->mhdr.num_buffers); + head_skb->dev->stats.rx_length_errors++; return -EINVAL; } - - if (len > PAGE_SIZE) - len = PAGE_SIZE; - - set_skb_frag(skb, page, 0, &len); - + if (unlikely(len > MAX_PACKET_LEN)) { + pr_debug("%s: rx error: merge buffer too long\n", + head_skb->dev->name); + len = MAX_PACKET_LEN; + } + if (unlikely(num_skb_frags == MAX_SKB_FRAGS)) { + struct sk_buff *nskb = alloc_skb(0, GFP_ATOMIC); + if (unlikely(!nskb)) { + head_skb->dev->stats.rx_dropped++; + return -ENOMEM; + } + if (curr_skb == head_skb) + skb_shinfo(curr_skb)->frag_list = nskb; + else + curr_skb->next = nskb; + curr_skb = nskb; + head_skb->truesize += nskb->truesize; + num_skb_frags = 0; + } + if (curr_skb != head_skb) { + head_skb->data_len += len; + head_skb->len += len; + head_skb->truesize += MAX_PACKET_LEN; + } + page = virt_to_head_page(buf); + offset = buf - (char *)page_address(page); + if (skb_can_coalesce(curr_skb, num_skb_frags, page, offset)) { + put_page(page); + skb_coalesce_rx_frag(curr_skb, num_skb_frags - 1, + len, MAX_PACKET_LEN); + } else { + skb_add_rx_frag(curr_skb, num_skb_frags, page, + offset, len, + MAX_PACKET_LEN); + } --rq->num; } return 0; @@ -341,8 +366,10 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) if (unlikely(len < sizeof(struct virtio_net_hdr) + ETH_HLEN)) { pr_debug("%s: short packet %i\n", dev->name, len); dev->stats.rx_length_errors++; - if (vi->mergeable_rx_bufs || vi->big_packets) + if (vi->big_packets) give_pages(rq, buf); + else if (vi->mergeable_rx_bufs) + put_page(virt_to_head_page(buf)); else dev_kfree_skb(buf); return; @@ -352,19 +379,28 @@ static void receive_buf(struct receive_queue *rq, void *buf, unsigned int len) skb = buf; len -= sizeof(struct virtio_net_hdr); skb_trim(skb, len); + } else if (vi->mergeable_rx_bufs) { + struct page *page = virt_to_head_page(buf); + skb = page_to_skb(rq, page, + (char *)buf - (char *)page_address(page), + len, MAX_PACKET_LEN); + if (unlikely(!skb)) { + dev->stats.rx_dropped++; + put_page(page); + return; + } + if (receive_mergeable(rq, skb)) { + dev_kfree_skb(skb); + return; + } } else { page = buf; - skb = page_to_skb(rq, page, len); + skb = page_to_skb(rq, page, 0, len, PAGE_SIZE); if (unlikely(!skb)) { dev->stats.rx_dropped++; give_pages(rq, page); return; } - if (vi->mergeable_rx_bufs) - if (receive_mergeable(rq, skb)) { - dev_kfree_skb(skb); - return; - } } hdr = skb_vnet_hdr(skb); @@ -501,18 +537,28 @@ static int add_recvbuf_big(struct receive_queue *rq, gfp_t gfp) static int add_recvbuf_mergeable(struct receive_queue *rq, gfp_t gfp) { - struct page *page; + struct virtnet_info *vi = rq->vq->vdev->priv; + char *buf = NULL; int err; - page = get_a_page(rq, gfp); - if (!page) + if (gfp & __GFP_WAIT) { + if (skb_page_frag_refill(MAX_PACKET_LEN, &vi->alloc_frag, + gfp)) { + buf = (char *)page_address(vi->alloc_frag.page) + + vi->alloc_frag.offset; + get_page(vi->alloc_frag.page); + vi->alloc_frag.offset += MAX_PACKET_LEN; + } + } else { + buf = netdev_alloc_frag(MAX_PACKET_LEN); + } + if (!buf) return -ENOMEM; - sg_init_one(rq->sg, page_address(page), PAGE_SIZE); - - err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, page, gfp); + sg_init_one(rq->sg, buf, MAX_PACKET_LEN); + err = virtqueue_add_inbuf(rq->vq, rq->sg, 1, buf, gfp); if (err < 0) - give_pages(rq, page); + put_page(virt_to_head_page(buf)); return err; } @@ -1065,7 +1111,6 @@ static int virtnet_vlan_rx_kill_vid(struct net_device *dev, static void virtnet_clean_affinity(struct virtnet_info *vi, long hcpu) { int i; - int cpu; if (vi->affinity_hint_set) { for (i = 0; i < vi->max_queue_pairs; i++) { @@ -1075,16 +1120,6 @@ static void virtnet_clean_affinity(struct virtnet_info *vi, long hcpu) vi->affinity_hint_set = false; } - - i = 0; - for_each_online_cpu(cpu) { - if (cpu == hcpu) { - *per_cpu_ptr(vi->vq_index, cpu) = -1; - } else { - *per_cpu_ptr(vi->vq_index, cpu) = - ++i % vi->curr_queue_pairs; - } - } } static void virtnet_set_affinity(struct virtnet_info *vi) @@ -1106,7 +1141,7 @@ static void virtnet_set_affinity(struct virtnet_info *vi) for_each_online_cpu(cpu) { virtqueue_set_affinity(vi->rq[i].vq, cpu); virtqueue_set_affinity(vi->sq[i].vq, cpu); - *per_cpu_ptr(vi->vq_index, cpu) = i; + netif_set_xps_queue(vi->dev, cpumask_of(cpu), i); i++; } @@ -1220,28 +1255,6 @@ static int virtnet_change_mtu(struct net_device *dev, int new_mtu) return 0; } -/* To avoid contending a lock hold by a vcpu who would exit to host, select the - * txq based on the processor id. - */ -static u16 virtnet_select_queue(struct net_device *dev, struct sk_buff *skb) -{ - int txq; - struct virtnet_info *vi = netdev_priv(dev); - - if (skb_rx_queue_recorded(skb)) { - txq = skb_get_rx_queue(skb); - } else { - txq = *__this_cpu_ptr(vi->vq_index); - if (txq == -1) - txq = 0; - } - - while (unlikely(txq >= dev->real_num_tx_queues)) - txq -= dev->real_num_tx_queues; - - return txq; -} - static const struct net_device_ops virtnet_netdev = { .ndo_open = virtnet_open, .ndo_stop = virtnet_close, @@ -1253,7 +1266,6 @@ static const struct net_device_ops virtnet_netdev = { .ndo_get_stats64 = virtnet_stats, .ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid, - .ndo_select_queue = virtnet_select_queue, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = virtnet_netpoll, #endif @@ -1336,8 +1348,10 @@ static void free_unused_bufs(struct virtnet_info *vi) struct virtqueue *vq = vi->rq[i].vq; while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) { - if (vi->mergeable_rx_bufs || vi->big_packets) + if (vi->big_packets) give_pages(&vi->rq[i], buf); + else if (vi->mergeable_rx_bufs) + put_page(virt_to_head_page(buf)); else dev_kfree_skb(buf); --vi->rq[i].num; @@ -1562,10 +1576,6 @@ static int virtnet_probe(struct virtio_device *vdev) if (vi->stats == NULL) goto free; - vi->vq_index = alloc_percpu(int); - if (vi->vq_index == NULL) - goto free_stats; - mutex_init(&vi->config_lock); vi->config_enable = true; INIT_WORK(&vi->config_work, virtnet_config_changed_work); @@ -1592,7 +1602,7 @@ static int virtnet_probe(struct virtio_device *vdev) /* Allocate/initialize the rx/tx queues, and invoke find_vqs */ err = init_vqs(vi); if (err) - goto free_index; + goto free_stats; netif_set_real_num_tx_queues(dev, 1); netif_set_real_num_rx_queues(dev, 1); @@ -1643,8 +1653,8 @@ free_recv_bufs: free_vqs: cancel_delayed_work_sync(&vi->refill); virtnet_del_vqs(vi); -free_index: - free_percpu(vi->vq_index); + if (vi->alloc_frag.page) + put_page(vi->alloc_frag.page); free_stats: free_percpu(vi->stats); free: @@ -1678,10 +1688,11 @@ static void virtnet_remove(struct virtio_device *vdev) unregister_netdev(vi->dev); remove_vq_common(vi); + if (vi->alloc_frag.page) + put_page(vi->alloc_frag.page); flush_work(&vi->config_work); - free_percpu(vi->vq_index); free_percpu(vi->stats); free_netdev(vi->dev); } diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h index a03f358fd58b9a44fc480ea33c67adf28e3cf0ef..12040a35d95d17223e5200b0a2716e288f31163d 100644 --- a/drivers/net/vmxnet3/vmxnet3_int.h +++ b/drivers/net/vmxnet3/vmxnet3_int.h @@ -410,9 +410,9 @@ int vmxnet3_create_queues(struct vmxnet3_adapter *adapter, u32 tx_ring_size, u32 rx_ring_size, u32 rx_ring2_size); -extern void vmxnet3_set_ethtool_ops(struct net_device *netdev); +void vmxnet3_set_ethtool_ops(struct net_device *netdev); -extern struct rtnl_link_stats64 * +struct rtnl_link_stats64 * vmxnet3_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats); extern char vmxnet3_driver_name[]; diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 2ef5b6219f3f46db28cb1256ce6eaa8fffc0bb3c..78df8f39e57cec7e9280811005b4a417b45ff35d 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -60,10 +60,6 @@ #define VXLAN_N_VID (1u << 24) #define VXLAN_VID_MASK (VXLAN_N_VID - 1) -/* IP header + UDP + VXLAN + Ethernet header */ -#define VXLAN_HEADROOM (20 + 8 + 8 + 14) -/* IPv6 header + UDP + VXLAN + Ethernet header */ -#define VXLAN6_HEADROOM (40 + 8 + 8 + 14) #define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr)) #define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */ @@ -2087,7 +2083,7 @@ static void vxlan_setup(struct net_device *dev) vxlan->age_timer.function = vxlan_cleanup; vxlan->age_timer.data = (unsigned long) vxlan; - inet_get_local_port_range(&low, &high); + inet_get_local_port_range(dev_net(dev), &low, &high); vxlan->port_min = low; vxlan->port_max = high; vxlan->dst_port = htons(vxlan_port); @@ -2180,7 +2176,7 @@ static void vxlan_del_work(struct work_struct *work) * could be used for both IPv4 and IPv6 communications, but * users may set bindv6only=1. */ -static int create_v6_sock(struct net *net, __be16 port, struct socket **psock) +static struct socket *create_v6_sock(struct net *net, __be16 port) { struct sock *sk; struct socket *sock; @@ -2193,7 +2189,7 @@ static int create_v6_sock(struct net *net, __be16 port, struct socket **psock) rc = sock_create_kern(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, &sock); if (rc < 0) { pr_debug("UDPv6 socket create failed\n"); - return rc; + return ERR_PTR(rc); } /* Put in proper namespace */ @@ -2208,28 +2204,27 @@ static int create_v6_sock(struct net *net, __be16 port, struct socket **psock) pr_debug("bind for UDPv6 socket %pI6:%u (%d)\n", &vxlan_addr.sin6_addr, ntohs(vxlan_addr.sin6_port), rc); sk_release_kernel(sk); - return rc; + return ERR_PTR(rc); } /* At this point, IPv6 module should have been loaded in * sock_create_kern(). */ BUG_ON(!ipv6_stub); - *psock = sock; /* Disable multicast loopback */ inet_sk(sk)->mc_loop = 0; - return 0; + return sock; } #else -static int create_v6_sock(struct net *net, __be16 port, struct socket **psock) +static struct socket *create_v6_sock(struct net *net, __be16 port) { - return -EPFNOSUPPORT; + return ERR_PTR(-EPFNOSUPPORT); } #endif -static int create_v4_sock(struct net *net, __be16 port, struct socket **psock) +static struct socket *create_v4_sock(struct net *net, __be16 port) { struct sock *sk; struct socket *sock; @@ -2244,7 +2239,7 @@ static int create_v4_sock(struct net *net, __be16 port, struct socket **psock) rc = sock_create_kern(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock); if (rc < 0) { pr_debug("UDP socket create failed\n"); - return rc; + return ERR_PTR(rc); } /* Put in proper namespace */ @@ -2257,13 +2252,12 @@ static int create_v4_sock(struct net *net, __be16 port, struct socket **psock) pr_debug("bind for UDP socket %pI4:%u (%d)\n", &vxlan_addr.sin_addr, ntohs(vxlan_addr.sin_port), rc); sk_release_kernel(sk); - return rc; + return ERR_PTR(rc); } - *psock = sock; /* Disable multicast loopback */ inet_sk(sk)->mc_loop = 0; - return 0; + return sock; } /* Create new listen socket if needed */ @@ -2274,7 +2268,6 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, struct vxlan_sock *vs; struct socket *sock; struct sock *sk; - int rc = 0; unsigned int h; vs = kmalloc(sizeof(*vs), GFP_KERNEL); @@ -2287,12 +2280,12 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, INIT_WORK(&vs->del_work, vxlan_del_work); if (ipv6) - rc = create_v6_sock(net, port, &sock); + sock = create_v6_sock(net, port); else - rc = create_v4_sock(net, port, &sock); - if (rc < 0) { + sock = create_v4_sock(net, port); + if (IS_ERR(sock)) { kfree(vs); - return ERR_PTR(rc); + return ERR_CAST(sock); } vs->sock = sock; diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c index 3d80e4267de83f3fae3e3aedf611ad1b3dbd3e2a..3d741663fd677a42eb2a3823e54412cf8afe7ee0 100644 --- a/drivers/net/wan/hostess_sv11.c +++ b/drivers/net/wan/hostess_sv11.c @@ -220,7 +220,7 @@ static struct z8530_dev *sv11_init(int iobase, int irq) /* We want a fast IRQ for this device. Actually we'd like an even faster IRQ ;) - This is one driver RtLinux is made for */ - if (request_irq(irq, z8530_interrupt, IRQF_DISABLED, + if (request_irq(irq, z8530_interrupt, 0, "Hostess SV11", sv) < 0) { pr_warn("IRQ %d already in use\n", irq); goto err_irq; diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c index 4f7748478984750fa4a9872341f3db5f36d802a8..27860b4f59081a46f71ad4f5baa2f7605ef8e17c 100644 --- a/drivers/net/wan/sealevel.c +++ b/drivers/net/wan/sealevel.c @@ -266,7 +266,7 @@ static __init struct slvl_board *slvl_init(int iobase, int irq, /* We want a fast IRQ for this device. Actually we'd like an even faster IRQ ;) - This is one driver RtLinux is made for */ - if (request_irq(irq, z8530_interrupt, IRQF_DISABLED, + if (request_irq(irq, z8530_interrupt, 0, "SeaLevel", dev) < 0) { pr_warn("IRQ %d already in use\n", irq); goto err_request_irq; diff --git a/drivers/net/wan/x25_asy.h b/drivers/net/wan/x25_asy.h index 8f0fc2e57e2be06fce081c6f835bb0ea72243859..f57ee67836aea02fc58ff782e38a1b8e3302c94d 100644 --- a/drivers/net/wan/x25_asy.h +++ b/drivers/net/wan/x25_asy.h @@ -41,6 +41,6 @@ struct x25_asy { #define X25_ASY_MAGIC 0x5303 -extern int x25_asy_init(struct net_device *dev); +int x25_asy_init(struct net_device *dev); #endif /* _LINUX_X25_ASY.H */ diff --git a/drivers/net/wan/z85230.h b/drivers/net/wan/z85230.h index f29d554fc07d4f893c0857593f0b108fa9b4a614..2416a9d60bd69e4781efcede38e93af611264e00 100644 --- a/drivers/net/wan/z85230.h +++ b/drivers/net/wan/z85230.h @@ -395,20 +395,19 @@ struct z8530_dev extern u8 z8530_dead_port[]; extern u8 z8530_hdlc_kilostream_85230[]; extern u8 z8530_hdlc_kilostream[]; -extern irqreturn_t z8530_interrupt(int, void *); -extern void z8530_describe(struct z8530_dev *, char *mapping, unsigned long io); -extern int z8530_init(struct z8530_dev *); -extern int z8530_shutdown(struct z8530_dev *); -extern int z8530_sync_open(struct net_device *, struct z8530_channel *); -extern int z8530_sync_close(struct net_device *, struct z8530_channel *); -extern int z8530_sync_dma_open(struct net_device *, struct z8530_channel *); -extern int z8530_sync_dma_close(struct net_device *, struct z8530_channel *); -extern int z8530_sync_txdma_open(struct net_device *, struct z8530_channel *); -extern int z8530_sync_txdma_close(struct net_device *, struct z8530_channel *); -extern int z8530_channel_load(struct z8530_channel *, u8 *); -extern netdev_tx_t z8530_queue_xmit(struct z8530_channel *c, - struct sk_buff *skb); -extern void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb); +irqreturn_t z8530_interrupt(int, void *); +void z8530_describe(struct z8530_dev *, char *mapping, unsigned long io); +int z8530_init(struct z8530_dev *); +int z8530_shutdown(struct z8530_dev *); +int z8530_sync_open(struct net_device *, struct z8530_channel *); +int z8530_sync_close(struct net_device *, struct z8530_channel *); +int z8530_sync_dma_open(struct net_device *, struct z8530_channel *); +int z8530_sync_dma_close(struct net_device *, struct z8530_channel *); +int z8530_sync_txdma_open(struct net_device *, struct z8530_channel *); +int z8530_sync_txdma_close(struct net_device *, struct z8530_channel *); +int z8530_channel_load(struct z8530_channel *, u8 *); +netdev_tx_t z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb); +void z8530_null_rx(struct z8530_channel *c, struct sk_buff *skb); /* diff --git a/drivers/net/wimax/i2400m/i2400m-usb.h b/drivers/net/wimax/i2400m/i2400m-usb.h index 9f1e947f3557823bc228549a2fd9b0636fed55b2..649ecad6844c73baa58a9e8b0e1850b9fdb88b36 100644 --- a/drivers/net/wimax/i2400m/i2400m-usb.h +++ b/drivers/net/wimax/i2400m/i2400m-usb.h @@ -256,21 +256,20 @@ void i2400mu_init(struct i2400mu *i2400mu) i2400mu->rx_size_auto_shrink = 1; } -extern int i2400mu_notification_setup(struct i2400mu *); -extern void i2400mu_notification_release(struct i2400mu *); +int i2400mu_notification_setup(struct i2400mu *); +void i2400mu_notification_release(struct i2400mu *); -extern int i2400mu_rx_setup(struct i2400mu *); -extern void i2400mu_rx_release(struct i2400mu *); -extern void i2400mu_rx_kick(struct i2400mu *); +int i2400mu_rx_setup(struct i2400mu *); +void i2400mu_rx_release(struct i2400mu *); +void i2400mu_rx_kick(struct i2400mu *); -extern int i2400mu_tx_setup(struct i2400mu *); -extern void i2400mu_tx_release(struct i2400mu *); -extern void i2400mu_bus_tx_kick(struct i2400m *); +int i2400mu_tx_setup(struct i2400mu *); +void i2400mu_tx_release(struct i2400mu *); +void i2400mu_bus_tx_kick(struct i2400m *); -extern ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *, - const struct i2400m_bootrom_header *, - size_t, int); -extern ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *, - struct i2400m_bootrom_header *, - size_t); +ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *, + const struct i2400m_bootrom_header *, size_t, + int); +ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *, + struct i2400m_bootrom_header *, size_t); #endif /* #ifndef __I2400M_USB_H__ */ diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 79c6505b5c2030c0f3b0813b97aef5fdc8780278..5a34e72bab9afb42d670f7ea72286c9e191a31e2 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h @@ -710,18 +710,18 @@ enum i2400m_bri { I2400M_BRI_MAC_REINIT = 1 << 3, }; -extern void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *); -extern int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri); -extern int i2400m_read_mac_addr(struct i2400m *); -extern int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri); -extern int i2400m_is_boot_barker(struct i2400m *, const void *, size_t); +void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *); +int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri); +int i2400m_read_mac_addr(struct i2400m *); +int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri); +int i2400m_is_boot_barker(struct i2400m *, const void *, size_t); static inline int i2400m_is_d2h_barker(const void *buf) { const __le32 *barker = buf; return le32_to_cpu(*barker) == I2400M_D2H_MSG_BARKER; } -extern void i2400m_unknown_barker(struct i2400m *, const void *, size_t); +void i2400m_unknown_barker(struct i2400m *, const void *, size_t); /* Make/grok boot-rom header commands */ @@ -789,32 +789,31 @@ unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr) /* * Driver / device setup and internal functions */ -extern void i2400m_init(struct i2400m *); -extern int i2400m_reset(struct i2400m *, enum i2400m_reset_type); -extern void i2400m_netdev_setup(struct net_device *net_dev); -extern int i2400m_sysfs_setup(struct device_driver *); -extern void i2400m_sysfs_release(struct device_driver *); -extern int i2400m_tx_setup(struct i2400m *); -extern void i2400m_wake_tx_work(struct work_struct *); -extern void i2400m_tx_release(struct i2400m *); - -extern int i2400m_rx_setup(struct i2400m *); -extern void i2400m_rx_release(struct i2400m *); - -extern void i2400m_fw_cache(struct i2400m *); -extern void i2400m_fw_uncache(struct i2400m *); - -extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, - const void *, int); -extern void i2400m_net_erx(struct i2400m *, struct sk_buff *, - enum i2400m_cs); -extern void i2400m_net_wake_stop(struct i2400m *); +void i2400m_init(struct i2400m *); +int i2400m_reset(struct i2400m *, enum i2400m_reset_type); +void i2400m_netdev_setup(struct net_device *net_dev); +int i2400m_sysfs_setup(struct device_driver *); +void i2400m_sysfs_release(struct device_driver *); +int i2400m_tx_setup(struct i2400m *); +void i2400m_wake_tx_work(struct work_struct *); +void i2400m_tx_release(struct i2400m *); + +int i2400m_rx_setup(struct i2400m *); +void i2400m_rx_release(struct i2400m *); + +void i2400m_fw_cache(struct i2400m *); +void i2400m_fw_uncache(struct i2400m *); + +void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned, const void *, + int); +void i2400m_net_erx(struct i2400m *, struct sk_buff *, enum i2400m_cs); +void i2400m_net_wake_stop(struct i2400m *); enum i2400m_pt; -extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt); +int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt); #ifdef CONFIG_DEBUG_FS -extern int i2400m_debugfs_add(struct i2400m *); -extern void i2400m_debugfs_rm(struct i2400m *); +int i2400m_debugfs_add(struct i2400m *); +void i2400m_debugfs_rm(struct i2400m *); #else static inline int i2400m_debugfs_add(struct i2400m *i2400m) { @@ -824,8 +823,8 @@ static inline void i2400m_debugfs_rm(struct i2400m *i2400m) {} #endif /* Initialize/shutdown the device */ -extern int i2400m_dev_initialize(struct i2400m *); -extern void i2400m_dev_shutdown(struct i2400m *); +int i2400m_dev_initialize(struct i2400m *); +void i2400m_dev_shutdown(struct i2400m *); extern struct attribute_group i2400m_dev_attr_group; @@ -873,21 +872,21 @@ void i2400m_put(struct i2400m *i2400m) dev_put(i2400m->wimax_dev.net_dev); } -extern int i2400m_dev_reset_handle(struct i2400m *, const char *); -extern int i2400m_pre_reset(struct i2400m *); -extern int i2400m_post_reset(struct i2400m *); -extern void i2400m_error_recovery(struct i2400m *); +int i2400m_dev_reset_handle(struct i2400m *, const char *); +int i2400m_pre_reset(struct i2400m *); +int i2400m_post_reset(struct i2400m *); +void i2400m_error_recovery(struct i2400m *); /* * _setup()/_release() are called by the probe/disconnect functions of * the bus-specific drivers. */ -extern int i2400m_setup(struct i2400m *, enum i2400m_bri bm_flags); -extern void i2400m_release(struct i2400m *); +int i2400m_setup(struct i2400m *, enum i2400m_bri bm_flags); +void i2400m_release(struct i2400m *); -extern int i2400m_rx(struct i2400m *, struct sk_buff *); -extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *); -extern void i2400m_tx_msg_sent(struct i2400m *); +int i2400m_rx(struct i2400m *, struct sk_buff *); +struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *); +void i2400m_tx_msg_sent(struct i2400m *); /* @@ -900,20 +899,19 @@ struct device *i2400m_dev(struct i2400m *i2400m) return i2400m->wimax_dev.net_dev->dev.parent; } -extern int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *, - char *, size_t); -extern int i2400m_msg_size_check(struct i2400m *, - const struct i2400m_l3l4_hdr *, size_t); -extern struct sk_buff *i2400m_msg_to_dev(struct i2400m *, const void *, size_t); -extern void i2400m_msg_to_dev_cancel_wait(struct i2400m *, int); -extern void i2400m_report_hook(struct i2400m *, - const struct i2400m_l3l4_hdr *, size_t); -extern void i2400m_report_hook_work(struct work_struct *); -extern int i2400m_cmd_enter_powersave(struct i2400m *); -extern int i2400m_cmd_exit_idle(struct i2400m *); -extern struct sk_buff *i2400m_get_device_info(struct i2400m *); -extern int i2400m_firmware_check(struct i2400m *); -extern int i2400m_set_idle_timeout(struct i2400m *, unsigned); +int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *, char *, size_t); +int i2400m_msg_size_check(struct i2400m *, const struct i2400m_l3l4_hdr *, + size_t); +struct sk_buff *i2400m_msg_to_dev(struct i2400m *, const void *, size_t); +void i2400m_msg_to_dev_cancel_wait(struct i2400m *, int); +void i2400m_report_hook(struct i2400m *, const struct i2400m_l3l4_hdr *, + size_t); +void i2400m_report_hook_work(struct work_struct *); +int i2400m_cmd_enter_powersave(struct i2400m *); +int i2400m_cmd_exit_idle(struct i2400m *); +struct sk_buff *i2400m_get_device_info(struct i2400m *); +int i2400m_firmware_check(struct i2400m *); +int i2400m_set_idle_timeout(struct i2400m *, unsigned); static inline struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep) @@ -921,10 +919,9 @@ struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep) return &iface->cur_altsetting->endpoint[ep].desc; } -extern int i2400m_op_rfkill_sw_toggle(struct wimax_dev *, - enum wimax_rf_state); -extern void i2400m_report_tlv_rf_switches_status( - struct i2400m *, const struct i2400m_tlv_rf_switches_status *); +int i2400m_op_rfkill_sw_toggle(struct wimax_dev *, enum wimax_rf_state); +void i2400m_report_tlv_rf_switches_status(struct i2400m *, + const struct i2400m_tlv_rf_switches_status *); /* * Helpers for firmware backwards compatibility @@ -968,8 +965,8 @@ void __i2400m_msleep(unsigned ms) /* module initialization helpers */ -extern int i2400m_barker_db_init(const char *); -extern void i2400m_barker_db_exit(void); +int i2400m_barker_db_init(const char *); +void i2400m_barker_db_exit(void); diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index f9a24e599dee4c7acf9b8a5bf0a0caa2306d01e5..cfce83e1f273f0259ee07d072fd8c8f59df8e99a 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1924,7 +1924,6 @@ static int adm8211_probe(struct pci_dev *pdev, pci_iounmap(pdev, priv->map); err_free_dev: - pci_set_drvdata(pdev, NULL); ieee80211_free_hw(dev); err_free_reg: diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index 7fe19648f10e8f1e966b857c12077f34025dc8b5..edf4b57c4aaa306ebbf12beea5100d6f6a7f5e2e 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -5570,7 +5570,6 @@ static void airo_pci_remove(struct pci_dev *pdev) airo_print_info(dev->name, "Unregistering..."); stop_airo_card(dev, 1); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state) diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 1abf1d421173085e72a4bfa549fd147f83a87ca5..c63d1159db5ca939718f4c34385347865b53ef67 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -25,6 +25,23 @@ config ATH_DEBUG Say Y, if you want to debug atheros wireless drivers. Right now only ath9k makes use of this. +config ATH_REG_DYNAMIC_USER_REG_HINTS + bool "Atheros dynamic user regulatory hints" + depends on CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled in countries where + this feature is explicitly allowed and only on cards that + specifically have been tested for this. + +config ATH_REG_DYNAMIC_USER_CERT_TESTING + bool "Atheros dynamic user regulatory testing" + depends on ATH_REG_DYNAMIC_USER_REG_HINTS && CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled on systems + undergoing certification testing. + source "drivers/net/wireless/ath/ath5k/Kconfig" source "drivers/net/wireless/ath/ath9k/Kconfig" source "drivers/net/wireless/ath/carl9170/Kconfig" @@ -32,5 +49,6 @@ source "drivers/net/wireless/ath/ath6kl/Kconfig" source "drivers/net/wireless/ath/ar5523/Kconfig" source "drivers/net/wireless/ath/wil6210/Kconfig" source "drivers/net/wireless/ath/ath10k/Kconfig" +source "drivers/net/wireless/ath/wcn36xx/Kconfig" endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index fb05cfd193616ea65cecbfc489653409b173e617..7d023b0f13b47d064cd2db064c58690e2da8e1ea 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -5,13 +5,16 @@ obj-$(CONFIG_ATH6KL) += ath6kl/ obj-$(CONFIG_AR5523) += ar5523/ obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_ATH10K) += ath10k/ +obj-$(CONFIG_WCN36XX) += wcn36xx/ obj-$(CONFIG_ATH_COMMON) += ath.o ath-objs := main.o \ regd.o \ hw.o \ - key.o + key.o \ + dfs_pattern_detector.o \ + dfs_pri_detector.o ath-$(CONFIG_ATH_DEBUG) += debug.o ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 17d7fece35d2c203eedef5fed0725074a0067cf7..280fc3d53a36466ca8a3f4ff7fd73074efc86b75 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -1762,6 +1762,7 @@ static struct usb_device_id ar5523_id_table[] = { AR5523_DEVICE_UX(0x2001, 0x3a00), /* Dlink / DWLAG132 */ AR5523_DEVICE_UG(0x2001, 0x3a02), /* Dlink / DWLG132 */ AR5523_DEVICE_UX(0x2001, 0x3a04), /* Dlink / DWLAG122 */ + AR5523_DEVICE_UG(0x07d1, 0x3a07), /* D-Link / WUA-2340 rev A1 */ AR5523_DEVICE_UG(0x1690, 0x0712), /* Gigaset / AR5523 */ AR5523_DEVICE_UG(0x1690, 0x0710), /* Gigaset / SMCWUSBTG */ AR5523_DEVICE_UG(0x129b, 0x160c), /* Gigaset / USB stick 108 diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 744da6d1c405d91a645428d0f686c3c9cb4f00f0..a1f0996288508e3cad8ecd8e03f33153980e593e 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -22,7 +22,8 @@ void ath10k_bmi_start(struct ath10k *ar) { - ath10k_dbg(ATH10K_DBG_CORE, "BMI started\n"); + ath10k_dbg(ATH10K_DBG_BMI, "bmi start\n"); + ar->bmi.done_sent = false; } @@ -32,8 +33,10 @@ int ath10k_bmi_done(struct ath10k *ar) u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.done); int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi done\n"); + if (ar->bmi.done_sent) { - ath10k_dbg(ATH10K_DBG_CORE, "%s skipped\n", __func__); + ath10k_dbg(ATH10K_DBG_BMI, "bmi skipped\n"); return 0; } @@ -46,7 +49,6 @@ int ath10k_bmi_done(struct ath10k *ar) return ret; } - ath10k_dbg(ATH10K_DBG_CORE, "BMI done\n"); return 0; } @@ -59,6 +61,8 @@ int ath10k_bmi_get_target_info(struct ath10k *ar, u32 resplen = sizeof(resp.get_target_info); int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi get target info\n"); + if (ar->bmi.done_sent) { ath10k_warn("BMI Get Target Info Command disallowed\n"); return -EBUSY; @@ -80,6 +84,7 @@ int ath10k_bmi_get_target_info(struct ath10k *ar, target_info->version = __le32_to_cpu(resp.get_target_info.version); target_info->type = __le32_to_cpu(resp.get_target_info.type); + return 0; } @@ -92,15 +97,14 @@ int ath10k_bmi_read_memory(struct ath10k *ar, u32 rxlen; int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi read address 0x%x length %d\n", + address, length); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; } - ath10k_dbg(ATH10K_DBG_CORE, - "%s: (device: 0x%p, address: 0x%x, length: %d)\n", - __func__, ar, address, length); - while (length) { rxlen = min_t(u32, length, BMI_MAX_DATA_SIZE); @@ -133,15 +137,14 @@ int ath10k_bmi_write_memory(struct ath10k *ar, u32 txlen; int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi write address 0x%x length %d\n", + address, length); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; } - ath10k_dbg(ATH10K_DBG_CORE, - "%s: (device: 0x%p, address: 0x%x, length: %d)\n", - __func__, ar, address, length); - while (length) { txlen = min(length, BMI_MAX_DATA_SIZE - hdrlen); @@ -180,15 +183,14 @@ int ath10k_bmi_execute(struct ath10k *ar, u32 address, u32 *param) u32 resplen = sizeof(resp.execute); int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi execute address 0x%x param 0x%x\n", + address, *param); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; } - ath10k_dbg(ATH10K_DBG_CORE, - "%s: (device: 0x%p, address: 0x%x, param: %d)\n", - __func__, ar, address, *param); - cmd.id = __cpu_to_le32(BMI_EXECUTE); cmd.execute.addr = __cpu_to_le32(address); cmd.execute.param = __cpu_to_le32(*param); @@ -216,6 +218,9 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) u32 txlen; int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n", + buffer, length); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; @@ -250,6 +255,9 @@ int ath10k_bmi_lz_stream_start(struct ath10k *ar, u32 address) u32 cmdlen = sizeof(cmd.id) + sizeof(cmd.lz_start); int ret; + ath10k_dbg(ATH10K_DBG_BMI, "bmi lz stream start address 0x%x\n", + address); + if (ar->bmi.done_sent) { ath10k_warn("command disallowed\n"); return -EBUSY; @@ -275,6 +283,10 @@ int ath10k_bmi_fast_download(struct ath10k *ar, u32 trailer_len = length - head_len; int ret; + ath10k_dbg(ATH10K_DBG_BMI, + "bmi fast download address 0x%x buffer 0x%p length %d\n", + address, buffer, length); + ret = ath10k_bmi_lz_stream_start(ar, address); if (ret) return ret; diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index f8b969f518f84ecee55c4e38f5d5f8247d501cf0..e46951b8fb925df8a4cd4bb4ed14a738871175b2 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -76,36 +76,7 @@ static inline void ath10k_ce_src_ring_write_index_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - void __iomem *indicator_addr; - - if (!test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) { - ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); - return; - } - - /* workaround for QCA988x_1.0 HW CE */ - indicator_addr = ar_pci->mem + ce_ctrl_addr + DST_WATERMARK_ADDRESS; - - if (ce_ctrl_addr == ath10k_ce_base_address(CDC_WAR_DATA_CE)) { - iowrite32((CDC_WAR_MAGIC_STR | n), indicator_addr); - } else { - unsigned long irq_flags; - local_irq_save(irq_flags); - iowrite32(1, indicator_addr); - - /* - * PCIE write waits for ACK in IPQ8K, there is no - * need to read back value. - */ - (void)ioread32(indicator_addr); - (void)ioread32(indicator_addr); /* conservative */ - - ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); - - iowrite32(0, indicator_addr); - local_irq_restore(irq_flags); - } + ath10k_pci_write32(ar, ce_ctrl_addr + SR_WR_INDEX_ADDRESS, n); } static inline u32 ath10k_ce_src_ring_write_index_get(struct ath10k *ar, @@ -285,7 +256,7 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, * ath10k_ce_sendlist_send. * The caller takes responsibility for any needed locking. */ -static int ath10k_ce_send_nolock(struct ce_state *ce_state, +static int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state, void *per_transfer_context, u32 buffer, unsigned int nbytes, @@ -293,7 +264,7 @@ static int ath10k_ce_send_nolock(struct ce_state *ce_state, unsigned int flags) { struct ath10k *ar = ce_state->ar; - struct ce_ring_state *src_ring = ce_state->src_ring; + struct ath10k_ce_ring *src_ring = ce_state->src_ring; struct ce_desc *desc, *sdesc; unsigned int nentries_mask = src_ring->nentries_mask; unsigned int sw_index = src_ring->sw_index; @@ -306,11 +277,13 @@ static int ath10k_ce_send_nolock(struct ce_state *ce_state, ath10k_warn("%s: send more we can (nbytes: %d, max: %d)\n", __func__, nbytes, ce_state->src_sz_max); - ath10k_pci_wake(ar); + ret = ath10k_pci_wake(ar); + if (ret) + return ret; if (unlikely(CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) <= 0)) { - ret = -EIO; + ret = -ENOSR; goto exit; } @@ -346,7 +319,7 @@ exit: return ret; } -int ath10k_ce_send(struct ce_state *ce_state, +int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, void *per_transfer_context, u32 buffer, unsigned int nbytes, @@ -365,77 +338,26 @@ int ath10k_ce_send(struct ce_state *ce_state, return ret; } -void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, u32 buffer, - unsigned int nbytes, u32 flags) +int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe) { - unsigned int num_items = sendlist->num_items; - struct ce_sendlist_item *item; - - item = &sendlist->item[num_items]; - item->data = buffer; - item->u.nbytes = nbytes; - item->flags = flags; - sendlist->num_items++; -} - -int ath10k_ce_sendlist_send(struct ce_state *ce_state, - void *per_transfer_context, - struct ce_sendlist *sendlist, - unsigned int transfer_id) -{ - struct ce_ring_state *src_ring = ce_state->src_ring; - struct ce_sendlist_item *item; - struct ath10k *ar = ce_state->ar; + struct ath10k *ar = pipe->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - unsigned int nentries_mask = src_ring->nentries_mask; - unsigned int num_items = sendlist->num_items; - unsigned int sw_index; - unsigned int write_index; - int i, delta, ret = -ENOMEM; + int delta; spin_lock_bh(&ar_pci->ce_lock); - - sw_index = src_ring->sw_index; - write_index = src_ring->write_index; - - delta = CE_RING_DELTA(nentries_mask, write_index, sw_index - 1); - - if (delta >= num_items) { - /* - * Handle all but the last item uniformly. - */ - for (i = 0; i < num_items - 1; i++) { - item = &sendlist->item[i]; - ret = ath10k_ce_send_nolock(ce_state, - CE_SENDLIST_ITEM_CTXT, - (u32) item->data, - item->u.nbytes, transfer_id, - item->flags | - CE_SEND_FLAG_GATHER); - if (ret) - ath10k_warn("CE send failed for item: %d\n", i); - } - /* - * Provide valid context pointer for final item. - */ - item = &sendlist->item[i]; - ret = ath10k_ce_send_nolock(ce_state, per_transfer_context, - (u32) item->data, item->u.nbytes, - transfer_id, item->flags); - if (ret) - ath10k_warn("CE send failed for last item: %d\n", i); - } - + delta = CE_RING_DELTA(pipe->src_ring->nentries_mask, + pipe->src_ring->write_index, + pipe->src_ring->sw_index - 1); spin_unlock_bh(&ar_pci->ce_lock); - return ret; + return delta; } -int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, +int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state, void *per_recv_context, u32 buffer) { - struct ce_ring_state *dest_ring = ce_state->dest_ring; + struct ath10k_ce_ring *dest_ring = ce_state->dest_ring; u32 ctrl_addr = ce_state->ctrl_addr; struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -448,7 +370,9 @@ int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, write_index = dest_ring->write_index; sw_index = dest_ring->sw_index; - ath10k_pci_wake(ar); + ret = ath10k_pci_wake(ar); + if (ret) + goto out; if (CE_RING_DELTA(nentries_mask, write_index, sw_index - 1) > 0) { struct ce_desc *base = dest_ring->base_addr_owner_space; @@ -470,6 +394,8 @@ int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, ret = -EIO; } ath10k_pci_sleep(ar); + +out: spin_unlock_bh(&ar_pci->ce_lock); return ret; @@ -479,14 +405,14 @@ int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, * Guts of ath10k_ce_completed_recv_next. * The caller takes responsibility for any necessary locking. */ -static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state, +static int ath10k_ce_completed_recv_next_nolock(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, unsigned int *transfer_idp, unsigned int *flagsp) { - struct ce_ring_state *dest_ring = ce_state->dest_ring; + struct ath10k_ce_ring *dest_ring = ce_state->dest_ring; unsigned int nentries_mask = dest_ring->nentries_mask; unsigned int sw_index = dest_ring->sw_index; @@ -535,7 +461,7 @@ static int ath10k_ce_completed_recv_next_nolock(struct ce_state *ce_state, return 0; } -int ath10k_ce_completed_recv_next(struct ce_state *ce_state, +int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, @@ -556,11 +482,11 @@ int ath10k_ce_completed_recv_next(struct ce_state *ce_state, return ret; } -int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, +int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp) { - struct ce_ring_state *dest_ring; + struct ath10k_ce_ring *dest_ring; unsigned int nentries_mask; unsigned int sw_index; unsigned int write_index; @@ -612,19 +538,20 @@ int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, * Guts of ath10k_ce_completed_send_next. * The caller takes responsibility for any necessary locking. */ -static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state, +static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, unsigned int *transfer_idp) { - struct ce_ring_state *src_ring = ce_state->src_ring; + struct ath10k_ce_ring *src_ring = ce_state->src_ring; u32 ctrl_addr = ce_state->ctrl_addr; struct ath10k *ar = ce_state->ar; unsigned int nentries_mask = src_ring->nentries_mask; unsigned int sw_index = src_ring->sw_index; + struct ce_desc *sdesc, *sbase; unsigned int read_index; - int ret = -EIO; + int ret; if (src_ring->hw_index == sw_index) { /* @@ -634,48 +561,54 @@ static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state, * the SW has really caught up to the HW, or if the cached * value of the HW index has become stale. */ - ath10k_pci_wake(ar); + + ret = ath10k_pci_wake(ar); + if (ret) + return ret; + src_ring->hw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); src_ring->hw_index &= nentries_mask; + ath10k_pci_sleep(ar); } + read_index = src_ring->hw_index; - if ((read_index != sw_index) && (read_index != 0xffffffff)) { - struct ce_desc *sbase = src_ring->shadow_base; - struct ce_desc *sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index); + if ((read_index == sw_index) || (read_index == 0xffffffff)) + return -EIO; - /* Return data from completed source descriptor */ - *bufferp = __le32_to_cpu(sdesc->addr); - *nbytesp = __le16_to_cpu(sdesc->nbytes); - *transfer_idp = MS(__le16_to_cpu(sdesc->flags), - CE_DESC_FLAGS_META_DATA); + sbase = src_ring->shadow_base; + sdesc = CE_SRC_RING_TO_DESC(sbase, sw_index); - if (per_transfer_contextp) - *per_transfer_contextp = - src_ring->per_transfer_context[sw_index]; + /* Return data from completed source descriptor */ + *bufferp = __le32_to_cpu(sdesc->addr); + *nbytesp = __le16_to_cpu(sdesc->nbytes); + *transfer_idp = MS(__le16_to_cpu(sdesc->flags), + CE_DESC_FLAGS_META_DATA); - /* sanity */ - src_ring->per_transfer_context[sw_index] = NULL; + if (per_transfer_contextp) + *per_transfer_contextp = + src_ring->per_transfer_context[sw_index]; - /* Update sw_index */ - sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); - src_ring->sw_index = sw_index; - ret = 0; - } + /* sanity */ + src_ring->per_transfer_context[sw_index] = NULL; - return ret; + /* Update sw_index */ + sw_index = CE_RING_IDX_INCR(nentries_mask, sw_index); + src_ring->sw_index = sw_index; + + return 0; } /* NB: Modeled after ath10k_ce_completed_send_next */ -int ath10k_ce_cancel_send_next(struct ce_state *ce_state, +int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, unsigned int *transfer_idp) { - struct ce_ring_state *src_ring; + struct ath10k_ce_ring *src_ring; unsigned int nentries_mask; unsigned int sw_index; unsigned int write_index; @@ -727,7 +660,7 @@ int ath10k_ce_cancel_send_next(struct ce_state *ce_state, return ret; } -int ath10k_ce_completed_send_next(struct ce_state *ce_state, +int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, @@ -756,53 +689,29 @@ int ath10k_ce_completed_send_next(struct ce_state *ce_state, void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id]; + struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; u32 ctrl_addr = ce_state->ctrl_addr; - void *transfer_context; - u32 buf; - unsigned int nbytes; - unsigned int id; - unsigned int flags; + int ret; + + ret = ath10k_pci_wake(ar); + if (ret) + return; - ath10k_pci_wake(ar); spin_lock_bh(&ar_pci->ce_lock); /* Clear the copy-complete interrupts that will be handled here. */ ath10k_ce_engine_int_status_clear(ar, ctrl_addr, HOST_IS_COPY_COMPLETE_MASK); - if (ce_state->recv_cb) { - /* - * Pop completed recv buffers and call the registered - * recv callback for each - */ - while (ath10k_ce_completed_recv_next_nolock(ce_state, - &transfer_context, - &buf, &nbytes, - &id, &flags) == 0) { - spin_unlock_bh(&ar_pci->ce_lock); - ce_state->recv_cb(ce_state, transfer_context, buf, - nbytes, id, flags); - spin_lock_bh(&ar_pci->ce_lock); - } - } + spin_unlock_bh(&ar_pci->ce_lock); - if (ce_state->send_cb) { - /* - * Pop completed send buffers and call the registered - * send callback for each - */ - while (ath10k_ce_completed_send_next_nolock(ce_state, - &transfer_context, - &buf, - &nbytes, - &id) == 0) { - spin_unlock_bh(&ar_pci->ce_lock); - ce_state->send_cb(ce_state, transfer_context, - buf, nbytes, id); - spin_lock_bh(&ar_pci->ce_lock); - } - } + if (ce_state->recv_cb) + ce_state->recv_cb(ce_state); + + if (ce_state->send_cb) + ce_state->send_cb(ce_state); + + spin_lock_bh(&ar_pci->ce_lock); /* * Misc CE interrupts are not being handled, but still need @@ -823,10 +732,13 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) void ath10k_ce_per_engine_service_any(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ce_id; + int ce_id, ret; u32 intr_summary; - ath10k_pci_wake(ar); + ret = ath10k_pci_wake(ar); + if (ret) + return; + intr_summary = CE_INTERRUPT_SUMMARY(ar); for (ce_id = 0; intr_summary && (ce_id < ar_pci->ce_count); ce_id++) { @@ -849,13 +761,16 @@ void ath10k_ce_per_engine_service_any(struct ath10k *ar) * * Called with ce_lock held. */ -static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state, +static void ath10k_ce_per_engine_handler_adjust(struct ath10k_ce_pipe *ce_state, int disable_copy_compl_intr) { u32 ctrl_addr = ce_state->ctrl_addr; struct ath10k *ar = ce_state->ar; + int ret; - ath10k_pci_wake(ar); + ret = ath10k_pci_wake(ar); + if (ret) + return; if ((!disable_copy_compl_intr) && (ce_state->send_cb || ce_state->recv_cb)) @@ -871,11 +786,14 @@ static void ath10k_ce_per_engine_handler_adjust(struct ce_state *ce_state, void ath10k_ce_disable_interrupts(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - int ce_id; + int ce_id, ret; + + ret = ath10k_pci_wake(ar); + if (ret) + return; - ath10k_pci_wake(ar); for (ce_id = 0; ce_id < ar_pci->ce_count; ce_id++) { - struct ce_state *ce_state = ar_pci->ce_id_to_state[ce_id]; + struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; u32 ctrl_addr = ce_state->ctrl_addr; ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); @@ -883,12 +801,8 @@ void ath10k_ce_disable_interrupts(struct ath10k *ar) ath10k_pci_sleep(ar); } -void ath10k_ce_send_cb_register(struct ce_state *ce_state, - void (*send_cb) (struct ce_state *ce_state, - void *transfer_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id), +void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, + void (*send_cb)(struct ath10k_ce_pipe *), int disable_interrupts) { struct ath10k *ar = ce_state->ar; @@ -900,13 +814,8 @@ void ath10k_ce_send_cb_register(struct ce_state *ce_state, spin_unlock_bh(&ar_pci->ce_lock); } -void ath10k_ce_recv_cb_register(struct ce_state *ce_state, - void (*recv_cb) (struct ce_state *ce_state, - void *transfer_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags)) +void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state, + void (*recv_cb)(struct ath10k_ce_pipe *)) { struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); @@ -919,11 +828,11 @@ void ath10k_ce_recv_cb_register(struct ce_state *ce_state, static int ath10k_ce_init_src_ring(struct ath10k *ar, unsigned int ce_id, - struct ce_state *ce_state, + struct ath10k_ce_pipe *ce_state, const struct ce_attr *attr) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_ring_state *src_ring; + struct ath10k_ce_ring *src_ring; unsigned int nentries = attr->src_nentries; unsigned int ce_nbytes; u32 ctrl_addr = ath10k_ce_base_address(ce_id); @@ -937,19 +846,18 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, return 0; } - ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *)); + ce_nbytes = sizeof(struct ath10k_ce_ring) + (nentries * sizeof(void *)); ptr = kzalloc(ce_nbytes, GFP_KERNEL); if (ptr == NULL) return -ENOMEM; - ce_state->src_ring = (struct ce_ring_state *)ptr; + ce_state->src_ring = (struct ath10k_ce_ring *)ptr; src_ring = ce_state->src_ring; - ptr += sizeof(struct ce_ring_state); + ptr += sizeof(struct ath10k_ce_ring); src_ring->nentries = nentries; src_ring->nentries_mask = nentries - 1; - ath10k_pci_wake(ar); src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); src_ring->sw_index &= src_ring->nentries_mask; src_ring->hw_index = src_ring->sw_index; @@ -957,7 +865,6 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, src_ring->write_index = ath10k_ce_src_ring_write_index_get(ar, ctrl_addr); src_ring->write_index &= src_ring->nentries_mask; - ath10k_pci_sleep(ar); src_ring->per_transfer_context = (void **)ptr; @@ -970,6 +877,12 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, (nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN), &base_addr); + if (!src_ring->base_addr_owner_space_unaligned) { + kfree(ce_state->src_ring); + ce_state->src_ring = NULL; + return -ENOMEM; + } + src_ring->base_addr_ce_space_unaligned = base_addr; src_ring->base_addr_owner_space = PTR_ALIGN( @@ -986,12 +899,21 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, src_ring->shadow_base_unaligned = kmalloc((nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN), GFP_KERNEL); + if (!src_ring->shadow_base_unaligned) { + pci_free_consistent(ar_pci->pdev, + (nentries * sizeof(struct ce_desc) + + CE_DESC_RING_ALIGN), + src_ring->base_addr_owner_space, + src_ring->base_addr_ce_space); + kfree(ce_state->src_ring); + ce_state->src_ring = NULL; + return -ENOMEM; + } src_ring->shadow_base = PTR_ALIGN( src_ring->shadow_base_unaligned, CE_DESC_RING_ALIGN); - ath10k_pci_wake(ar); ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, src_ring->base_addr_ce_space); ath10k_ce_src_ring_size_set(ar, ctrl_addr, nentries); @@ -999,18 +921,21 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, ath10k_ce_src_ring_byte_swap_set(ar, ctrl_addr, 0); ath10k_ce_src_ring_lowmark_set(ar, ctrl_addr, 0); ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries); - ath10k_pci_sleep(ar); + + ath10k_dbg(ATH10K_DBG_BOOT, + "boot ce src ring id %d entries %d base_addr %p\n", + ce_id, nentries, src_ring->base_addr_owner_space); return 0; } static int ath10k_ce_init_dest_ring(struct ath10k *ar, unsigned int ce_id, - struct ce_state *ce_state, + struct ath10k_ce_pipe *ce_state, const struct ce_attr *attr) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_ring_state *dest_ring; + struct ath10k_ce_ring *dest_ring; unsigned int nentries = attr->dest_nentries; unsigned int ce_nbytes; u32 ctrl_addr = ath10k_ce_base_address(ce_id); @@ -1024,25 +949,23 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, return 0; } - ce_nbytes = sizeof(struct ce_ring_state) + (nentries * sizeof(void *)); + ce_nbytes = sizeof(struct ath10k_ce_ring) + (nentries * sizeof(void *)); ptr = kzalloc(ce_nbytes, GFP_KERNEL); if (ptr == NULL) return -ENOMEM; - ce_state->dest_ring = (struct ce_ring_state *)ptr; + ce_state->dest_ring = (struct ath10k_ce_ring *)ptr; dest_ring = ce_state->dest_ring; - ptr += sizeof(struct ce_ring_state); + ptr += sizeof(struct ath10k_ce_ring); dest_ring->nentries = nentries; dest_ring->nentries_mask = nentries - 1; - ath10k_pci_wake(ar); dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr); dest_ring->sw_index &= dest_ring->nentries_mask; dest_ring->write_index = ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr); dest_ring->write_index &= dest_ring->nentries_mask; - ath10k_pci_sleep(ar); dest_ring->per_transfer_context = (void **)ptr; @@ -1055,6 +978,12 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, (nentries * sizeof(struct ce_desc) + CE_DESC_RING_ALIGN), &base_addr); + if (!dest_ring->base_addr_owner_space_unaligned) { + kfree(ce_state->dest_ring); + ce_state->dest_ring = NULL; + return -ENOMEM; + } + dest_ring->base_addr_ce_space_unaligned = base_addr; /* @@ -1071,44 +1000,35 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, dest_ring->base_addr_ce_space_unaligned, CE_DESC_RING_ALIGN); - ath10k_pci_wake(ar); ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, dest_ring->base_addr_ce_space); ath10k_ce_dest_ring_size_set(ar, ctrl_addr, nentries); ath10k_ce_dest_ring_byte_swap_set(ar, ctrl_addr, 0); ath10k_ce_dest_ring_lowmark_set(ar, ctrl_addr, 0); ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries); - ath10k_pci_sleep(ar); + + ath10k_dbg(ATH10K_DBG_BOOT, + "boot ce dest ring id %d entries %d base_addr %p\n", + ce_id, nentries, dest_ring->base_addr_owner_space); return 0; } -static struct ce_state *ath10k_ce_init_state(struct ath10k *ar, +static struct ath10k_ce_pipe *ath10k_ce_init_state(struct ath10k *ar, unsigned int ce_id, const struct ce_attr *attr) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_state = NULL; + struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; u32 ctrl_addr = ath10k_ce_base_address(ce_id); spin_lock_bh(&ar_pci->ce_lock); - if (!ar_pci->ce_id_to_state[ce_id]) { - ce_state = kzalloc(sizeof(*ce_state), GFP_ATOMIC); - if (ce_state == NULL) { - spin_unlock_bh(&ar_pci->ce_lock); - return NULL; - } - - ar_pci->ce_id_to_state[ce_id] = ce_state; - ce_state->ar = ar; - ce_state->id = ce_id; - ce_state->ctrl_addr = ctrl_addr; - ce_state->state = CE_RUNNING; - /* Save attribute flags */ - ce_state->attr_flags = attr->flags; - ce_state->src_sz_max = attr->src_sz_max; - } + ce_state->ar = ar; + ce_state->id = ce_id; + ce_state->ctrl_addr = ctrl_addr; + ce_state->attr_flags = attr->flags; + ce_state->src_sz_max = attr->src_sz_max; spin_unlock_bh(&ar_pci->ce_lock); @@ -1122,12 +1042,17 @@ static struct ce_state *ath10k_ce_init_state(struct ath10k *ar, * initialization. It may be that only one side or the other is * initialized by software/firmware. */ -struct ce_state *ath10k_ce_init(struct ath10k *ar, +struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar, unsigned int ce_id, const struct ce_attr *attr) { - struct ce_state *ce_state; + struct ath10k_ce_pipe *ce_state; u32 ctrl_addr = ath10k_ce_base_address(ce_id); + int ret; + + ret = ath10k_pci_wake(ar); + if (ret) + return NULL; ce_state = ath10k_ce_init_state(ar, ce_id, attr); if (!ce_state) { @@ -1136,40 +1061,38 @@ struct ce_state *ath10k_ce_init(struct ath10k *ar, } if (attr->src_nentries) { - if (ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr)) { - ath10k_err("Failed to initialize CE src ring for ID: %d\n", - ce_id); + ret = ath10k_ce_init_src_ring(ar, ce_id, ce_state, attr); + if (ret) { + ath10k_err("Failed to initialize CE src ring for ID: %d (%d)\n", + ce_id, ret); ath10k_ce_deinit(ce_state); return NULL; } } if (attr->dest_nentries) { - if (ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr)) { - ath10k_err("Failed to initialize CE dest ring for ID: %d\n", - ce_id); + ret = ath10k_ce_init_dest_ring(ar, ce_id, ce_state, attr); + if (ret) { + ath10k_err("Failed to initialize CE dest ring for ID: %d (%d)\n", + ce_id, ret); ath10k_ce_deinit(ce_state); return NULL; } } /* Enable CE error interrupts */ - ath10k_pci_wake(ar); ath10k_ce_error_intr_enable(ar, ctrl_addr); + ath10k_pci_sleep(ar); return ce_state; } -void ath10k_ce_deinit(struct ce_state *ce_state) +void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state) { - unsigned int ce_id = ce_state->id; struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - ce_state->state = CE_UNUSED; - ar_pci->ce_id_to_state[ce_id] = NULL; - if (ce_state->src_ring) { kfree(ce_state->src_ring->shadow_base_unaligned); pci_free_consistent(ar_pci->pdev, @@ -1190,5 +1113,7 @@ void ath10k_ce_deinit(struct ce_state *ce_state) ce_state->dest_ring->base_addr_ce_space); kfree(ce_state->dest_ring); } - kfree(ce_state); + + ce_state->src_ring = NULL; + ce_state->dest_ring = NULL; } diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index c17f07c026f48f585fc8699de1d850e2f1440a9d..15d45b5b76153b60133502be39b1803eb86c8517 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -27,7 +27,6 @@ /* Descriptor rings must be aligned to this boundary */ #define CE_DESC_RING_ALIGN 8 -#define CE_SENDLIST_ITEMS_MAX 12 #define CE_SEND_FLAG_GATHER 0x00010000 /* @@ -36,16 +35,9 @@ * how to use copy engines. */ -struct ce_state; +struct ath10k_ce_pipe; -/* Copy Engine operational state */ -enum ce_op_state { - CE_UNUSED, - CE_PAUSED, - CE_RUNNING, -}; - #define CE_DESC_FLAGS_GATHER (1 << 0) #define CE_DESC_FLAGS_BYTE_SWAP (1 << 1) #define CE_DESC_FLAGS_META_DATA_MASK 0xFFFC @@ -57,8 +49,7 @@ struct ce_desc { __le16 flags; /* %CE_DESC_FLAGS_ */ }; -/* Copy Engine Ring internal state */ -struct ce_ring_state { +struct ath10k_ce_ring { /* Number of entries in this ring; must be power of 2 */ unsigned int nentries; unsigned int nentries_mask; @@ -116,49 +107,20 @@ struct ce_ring_state { void **per_transfer_context; }; -/* Copy Engine internal state */ -struct ce_state { +struct ath10k_ce_pipe { struct ath10k *ar; unsigned int id; unsigned int attr_flags; u32 ctrl_addr; - enum ce_op_state state; - - void (*send_cb) (struct ce_state *ce_state, - void *per_transfer_send_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id); - void (*recv_cb) (struct ce_state *ce_state, - void *per_transfer_recv_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags); - - unsigned int src_sz_max; - struct ce_ring_state *src_ring; - struct ce_ring_state *dest_ring; -}; -struct ce_sendlist_item { - /* e.g. buffer or desc list */ - dma_addr_t data; - union { - /* simple buffer */ - unsigned int nbytes; - /* Rx descriptor list */ - unsigned int ndesc; - } u; - /* externally-specified flags; OR-ed with internal flags */ - u32 flags; -}; + void (*send_cb)(struct ath10k_ce_pipe *); + void (*recv_cb)(struct ath10k_ce_pipe *); -struct ce_sendlist { - unsigned int num_items; - struct ce_sendlist_item item[CE_SENDLIST_ITEMS_MAX]; + unsigned int src_sz_max; + struct ath10k_ce_ring *src_ring; + struct ath10k_ce_ring *dest_ring; }; /* Copy Engine settable attributes */ @@ -182,7 +144,7 @@ struct ce_attr; * * Implementation note: pushes 1 buffer to Source ring */ -int ath10k_ce_send(struct ce_state *ce_state, +int ath10k_ce_send(struct ath10k_ce_pipe *ce_state, void *per_transfer_send_context, u32 buffer, unsigned int nbytes, @@ -190,36 +152,11 @@ int ath10k_ce_send(struct ce_state *ce_state, unsigned int transfer_id, unsigned int flags); -void ath10k_ce_send_cb_register(struct ce_state *ce_state, - void (*send_cb) (struct ce_state *ce_state, - void *transfer_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id), +void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state, + void (*send_cb)(struct ath10k_ce_pipe *), int disable_interrupts); -/* Append a simple buffer (address/length) to a sendlist. */ -void ath10k_ce_sendlist_buf_add(struct ce_sendlist *sendlist, - u32 buffer, - unsigned int nbytes, - /* OR-ed with internal flags */ - u32 flags); - -/* - * Queue a "sendlist" of buffers to be sent using gather to a single - * anonymous destination buffer - * ce - which copy engine to use - * sendlist - list of simple buffers to send using gather - * transfer_id - arbitrary ID; reflected to destination - * Returns 0 on success; otherwise an error status. - * - * Implemenation note: Pushes multiple buffers with Gather to Source ring. - */ -int ath10k_ce_sendlist_send(struct ce_state *ce_state, - void *per_transfer_send_context, - struct ce_sendlist *sendlist, - /* 14 bits */ - unsigned int transfer_id); +int ath10k_ce_num_free_src_entries(struct ath10k_ce_pipe *pipe); /*==================Recv=======================*/ @@ -233,17 +170,12 @@ int ath10k_ce_sendlist_send(struct ce_state *ce_state, * * Implemenation note: Pushes a buffer to Dest ring. */ -int ath10k_ce_recv_buf_enqueue(struct ce_state *ce_state, +int ath10k_ce_recv_buf_enqueue(struct ath10k_ce_pipe *ce_state, void *per_transfer_recv_context, u32 buffer); -void ath10k_ce_recv_cb_register(struct ce_state *ce_state, - void (*recv_cb) (struct ce_state *ce_state, - void *transfer_context, - u32 buffer, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags)); +void ath10k_ce_recv_cb_register(struct ath10k_ce_pipe *ce_state, + void (*recv_cb)(struct ath10k_ce_pipe *)); /* recv flags */ /* Data is byte-swapped */ @@ -253,7 +185,7 @@ void ath10k_ce_recv_cb_register(struct ce_state *ce_state, * Supply data for the next completed unprocessed receive descriptor. * Pops buffer from Dest ring. */ -int ath10k_ce_completed_recv_next(struct ce_state *ce_state, +int ath10k_ce_completed_recv_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, @@ -263,7 +195,7 @@ int ath10k_ce_completed_recv_next(struct ce_state *ce_state, * Supply data for the next completed unprocessed send descriptor. * Pops 1 completed send buffer from Source ring. */ -int ath10k_ce_completed_send_next(struct ce_state *ce_state, +int ath10k_ce_completed_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, @@ -272,7 +204,7 @@ int ath10k_ce_completed_send_next(struct ce_state *ce_state, /*==================CE Engine Initialization=======================*/ /* Initialize an instance of a CE */ -struct ce_state *ath10k_ce_init(struct ath10k *ar, +struct ath10k_ce_pipe *ath10k_ce_init(struct ath10k *ar, unsigned int ce_id, const struct ce_attr *attr); @@ -282,7 +214,7 @@ struct ce_state *ath10k_ce_init(struct ath10k *ar, * receive buffers. Target DMA must be stopped before using * this API. */ -int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, +int ath10k_ce_revoke_recv_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp); @@ -291,13 +223,13 @@ int ath10k_ce_revoke_recv_next(struct ce_state *ce_state, * pending sends. Target DMA must be stopped before using * this API. */ -int ath10k_ce_cancel_send_next(struct ce_state *ce_state, +int ath10k_ce_cancel_send_next(struct ath10k_ce_pipe *ce_state, void **per_transfer_contextp, u32 *bufferp, unsigned int *nbytesp, unsigned int *transfer_idp); -void ath10k_ce_deinit(struct ce_state *ce_state); +void ath10k_ce_deinit(struct ath10k_ce_pipe *ce_state); /*==================CE Interrupt Handlers====================*/ void ath10k_ce_per_engine_service_any(struct ath10k *ar); @@ -322,9 +254,6 @@ struct ce_attr { /* CE_ATTR_* values */ unsigned int flags; - /* currently not in use */ - unsigned int priority; - /* #entries in source ring - Must be a power of 2 */ unsigned int src_nentries; @@ -336,21 +265,8 @@ struct ce_attr { /* #entries in destination ring - Must be a power of 2 */ unsigned int dest_nentries; - - /* Future use */ - void *reserved; }; -/* - * When using sendlist_send to transfer multiple buffer fragments, the - * transfer context of each fragment, except last one, will be filled - * with CE_SENDLIST_ITEM_CTXT. ce_completed_send will return success for - * each fragment done with send and the transfer context would be - * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the - * status of a send completion. - */ -#define CE_SENDLIST_ITEM_CTXT ((void *)0xcecebeef) - #define SR_BA_ADDRESS 0x0000 #define SR_SIZE_ADDRESS 0x0004 #define DR_BA_ADDRESS 0x0008 diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7226c23b956991165f5f1f7c5e7253d951400387..1129994fb10505864c57e1025d95cec8aa009093 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -38,17 +38,6 @@ MODULE_PARM_DESC(uart_print, "Uart target debugging"); MODULE_PARM_DESC(p2p, "Enable ath10k P2P support"); static const struct ath10k_hw_params ath10k_hw_params_list[] = { - { - .id = QCA988X_HW_1_0_VERSION, - .name = "qca988x hw1.0", - .patch_load_addr = QCA988X_HW_1_0_PATCH_LOAD_ADDR, - .fw = { - .dir = QCA988X_HW_1_0_FW_DIR, - .fw = QCA988X_HW_1_0_FW_FILE, - .otp = QCA988X_HW_1_0_OTP_FILE, - .board = QCA988X_HW_1_0_BOARD_DATA_FILE, - }, - }, { .id = QCA988X_HW_2_0_VERSION, .name = "qca988x hw2.0", @@ -64,33 +53,12 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { static void ath10k_send_suspend_complete(struct ath10k *ar) { - ath10k_dbg(ATH10K_DBG_CORE, "%s\n", __func__); + ath10k_dbg(ATH10K_DBG_BOOT, "boot suspend complete\n"); ar->is_target_paused = true; wake_up(&ar->event_queue); } -static int ath10k_check_fw_version(struct ath10k *ar) -{ - char version[32]; - - if (ar->fw_version_major >= SUPPORTED_FW_MAJOR && - ar->fw_version_minor >= SUPPORTED_FW_MINOR && - ar->fw_version_release >= SUPPORTED_FW_RELEASE && - ar->fw_version_build >= SUPPORTED_FW_BUILD) - return 0; - - snprintf(version, sizeof(version), "%u.%u.%u.%u", - SUPPORTED_FW_MAJOR, SUPPORTED_FW_MINOR, - SUPPORTED_FW_RELEASE, SUPPORTED_FW_BUILD); - - ath10k_warn("WARNING: Firmware version %s is not officially supported.\n", - ar->hw->wiphy->fw_version); - ath10k_warn("Please upgrade to version %s (or newer)\n", version); - - return 0; -} - static int ath10k_init_connect_htc(struct ath10k *ar) { int status; @@ -112,7 +80,7 @@ static int ath10k_init_connect_htc(struct ath10k *ar) goto timeout; } - ath10k_dbg(ATH10K_DBG_CORE, "core wmi ready\n"); + ath10k_dbg(ATH10K_DBG_BOOT, "boot wmi ready\n"); return 0; timeout: @@ -200,8 +168,7 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, return fw; } -static int ath10k_push_board_ext_data(struct ath10k *ar, - const struct firmware *fw) +static int ath10k_push_board_ext_data(struct ath10k *ar) { u32 board_data_size = QCA988X_BOARD_DATA_SZ; u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ; @@ -214,21 +181,21 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, return ret; } - ath10k_dbg(ATH10K_DBG_CORE, - "ath10k: Board extended Data download addr: 0x%x\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot push board extended data addr 0x%x\n", board_ext_data_addr); if (board_ext_data_addr == 0) return 0; - if (fw->size != (board_data_size + board_ext_data_size)) { + if (ar->board_len != (board_data_size + board_ext_data_size)) { ath10k_err("invalid board (ext) data sizes %zu != %d+%d\n", - fw->size, board_data_size, board_ext_data_size); + ar->board_len, board_data_size, board_ext_data_size); return -EINVAL; } ret = ath10k_bmi_write_memory(ar, board_ext_data_addr, - fw->data + board_data_size, + ar->board_data + board_data_size, board_ext_data_size); if (ret) { ath10k_err("could not write board ext data (%d)\n", ret); @@ -247,12 +214,11 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, static int ath10k_download_board_data(struct ath10k *ar) { - const struct firmware *fw = ar->board_data; u32 board_data_size = QCA988X_BOARD_DATA_SZ; u32 address; int ret; - ret = ath10k_push_board_ext_data(ar, fw); + ret = ath10k_push_board_ext_data(ar); if (ret) { ath10k_err("could not push board ext data (%d)\n", ret); goto exit; @@ -264,8 +230,9 @@ static int ath10k_download_board_data(struct ath10k *ar) goto exit; } - ret = ath10k_bmi_write_memory(ar, address, fw->data, - min_t(u32, board_data_size, fw->size)); + ret = ath10k_bmi_write_memory(ar, address, ar->board_data, + min_t(u32, board_data_size, + ar->board_len)); if (ret) { ath10k_err("could not write board data (%d)\n", ret); goto exit; @@ -283,17 +250,16 @@ exit: static int ath10k_download_and_run_otp(struct ath10k *ar) { - const struct firmware *fw = ar->otp; u32 address = ar->hw_params.patch_load_addr; u32 exec_param; int ret; /* OTP is optional */ - if (!ar->otp) + if (!ar->otp_data || !ar->otp_len) return 0; - ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + ret = ath10k_bmi_fast_download(ar, address, ar->otp_data, ar->otp_len); if (ret) { ath10k_err("could not write otp (%d)\n", ret); goto exit; @@ -312,13 +278,13 @@ exit: static int ath10k_download_fw(struct ath10k *ar) { - const struct firmware *fw = ar->firmware; u32 address; int ret; address = ar->hw_params.patch_load_addr; - ret = ath10k_bmi_fast_download(ar, address, fw->data, fw->size); + ret = ath10k_bmi_fast_download(ar, address, ar->firmware_data, + ar->firmware_len); if (ret) { ath10k_err("could not write fw (%d)\n", ret); goto exit; @@ -330,8 +296,8 @@ exit: static void ath10k_core_free_firmware_files(struct ath10k *ar) { - if (ar->board_data && !IS_ERR(ar->board_data)) - release_firmware(ar->board_data); + if (ar->board && !IS_ERR(ar->board)) + release_firmware(ar->board); if (ar->otp && !IS_ERR(ar->otp)) release_firmware(ar->otp); @@ -339,12 +305,20 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar) if (ar->firmware && !IS_ERR(ar->firmware)) release_firmware(ar->firmware); + ar->board = NULL; ar->board_data = NULL; + ar->board_len = 0; + ar->otp = NULL; + ar->otp_data = NULL; + ar->otp_len = 0; + ar->firmware = NULL; + ar->firmware_data = NULL; + ar->firmware_len = 0; } -static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +static int ath10k_core_fetch_firmware_api_1(struct ath10k *ar) { int ret = 0; @@ -358,15 +332,18 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) return -EINVAL; } - ar->board_data = ath10k_fetch_fw_file(ar, - ar->hw_params.fw.dir, - ar->hw_params.fw.board); - if (IS_ERR(ar->board_data)) { - ret = PTR_ERR(ar->board_data); + ar->board = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(ar->board)) { + ret = PTR_ERR(ar->board); ath10k_err("could not fetch board data (%d)\n", ret); goto err; } + ar->board_data = ar->board->data; + ar->board_len = ar->board->size; + ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, ar->hw_params.fw.fw); @@ -376,6 +353,9 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) goto err; } + ar->firmware_data = ar->firmware->data; + ar->firmware_len = ar->firmware->size; + /* OTP may be undefined. If so, don't fetch it at all */ if (ar->hw_params.fw.otp == NULL) return 0; @@ -389,6 +369,172 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) goto err; } + ar->otp_data = ar->otp->data; + ar->otp_len = ar->otp->size; + + return 0; + +err: + ath10k_core_free_firmware_files(ar); + return ret; +} + +static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) +{ + size_t magic_len, len, ie_len; + int ie_id, i, index, bit, ret; + struct ath10k_fw_ie *hdr; + const u8 *data; + __le32 *timestamp; + + /* first fetch the firmware file (firmware-*.bin) */ + ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name); + if (IS_ERR(ar->firmware)) { + ath10k_err("Could not fetch firmware file '%s': %ld\n", + name, PTR_ERR(ar->firmware)); + return PTR_ERR(ar->firmware); + } + + data = ar->firmware->data; + len = ar->firmware->size; + + /* magic also includes the null byte, check that as well */ + magic_len = strlen(ATH10K_FIRMWARE_MAGIC) + 1; + + if (len < magic_len) { + ath10k_err("firmware image too small to contain magic: %zu\n", + len); + ret = -EINVAL; + goto err; + } + + if (memcmp(data, ATH10K_FIRMWARE_MAGIC, magic_len) != 0) { + ath10k_err("Invalid firmware magic\n"); + ret = -EINVAL; + goto err; + } + + /* jump over the padding */ + magic_len = ALIGN(magic_len, 4); + + len -= magic_len; + data += magic_len; + + /* loop elements */ + while (len > sizeof(struct ath10k_fw_ie)) { + hdr = (struct ath10k_fw_ie *)data; + + ie_id = le32_to_cpu(hdr->id); + ie_len = le32_to_cpu(hdr->len); + + len -= sizeof(*hdr); + data += sizeof(*hdr); + + if (len < ie_len) { + ath10k_err("Invalid length for FW IE %d (%zu < %zu)\n", + ie_id, len, ie_len); + ret = -EINVAL; + goto err; + } + + switch (ie_id) { + case ATH10K_FW_IE_FW_VERSION: + if (ie_len > sizeof(ar->hw->wiphy->fw_version) - 1) + break; + + memcpy(ar->hw->wiphy->fw_version, data, ie_len); + ar->hw->wiphy->fw_version[ie_len] = '\0'; + + ath10k_dbg(ATH10K_DBG_BOOT, + "found fw version %s\n", + ar->hw->wiphy->fw_version); + break; + case ATH10K_FW_IE_TIMESTAMP: + if (ie_len != sizeof(u32)) + break; + + timestamp = (__le32 *)data; + + ath10k_dbg(ATH10K_DBG_BOOT, "found fw timestamp %d\n", + le32_to_cpup(timestamp)); + break; + case ATH10K_FW_IE_FEATURES: + ath10k_dbg(ATH10K_DBG_BOOT, + "found firmware features ie (%zd B)\n", + ie_len); + + for (i = 0; i < ATH10K_FW_FEATURE_COUNT; i++) { + index = i / 8; + bit = i % 8; + + if (index == ie_len) + break; + + if (data[index] & (1 << bit)) + __set_bit(i, ar->fw_features); + } + + ath10k_dbg_dump(ATH10K_DBG_BOOT, "features", "", + ar->fw_features, + sizeof(ar->fw_features)); + break; + case ATH10K_FW_IE_FW_IMAGE: + ath10k_dbg(ATH10K_DBG_BOOT, + "found fw image ie (%zd B)\n", + ie_len); + + ar->firmware_data = data; + ar->firmware_len = ie_len; + + break; + case ATH10K_FW_IE_OTP_IMAGE: + ath10k_dbg(ATH10K_DBG_BOOT, + "found otp image ie (%zd B)\n", + ie_len); + + ar->otp_data = data; + ar->otp_len = ie_len; + + break; + default: + ath10k_warn("Unknown FW IE: %u\n", + le32_to_cpu(hdr->id)); + break; + } + + /* jump over the padding */ + ie_len = ALIGN(ie_len, 4); + + len -= ie_len; + data += ie_len; + } + + if (!ar->firmware_data || !ar->firmware_len) { + ath10k_warn("No ATH10K_FW_IE_FW_IMAGE found from %s, skipping\n", + name); + ret = -ENOMEDIUM; + goto err; + } + + /* now fetch the board file */ + if (ar->hw_params.fw.board == NULL) { + ath10k_err("board data file not defined"); + ret = -EINVAL; + goto err; + } + + ar->board = ath10k_fetch_fw_file(ar, + ar->hw_params.fw.dir, + ar->hw_params.fw.board); + if (IS_ERR(ar->board)) { + ret = PTR_ERR(ar->board); + ath10k_err("could not fetch board data (%d)\n", ret); + goto err; + } + + ar->board_data = ar->board->data; + ar->board_len = ar->board->size; + return 0; err: @@ -396,6 +542,28 @@ err: return ret; } +static int ath10k_core_fetch_firmware_files(struct ath10k *ar) +{ + int ret; + + ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API2_FILE); + if (ret == 0) { + ar->fw_api = 2; + goto out; + } + + ret = ath10k_core_fetch_firmware_api_1(ar); + if (ret) + return ret; + + ar->fw_api = 1; + +out: + ath10k_dbg(ATH10K_DBG_BOOT, "using fw api %d\n", ar->fw_api); + + return 0; +} + static int ath10k_init_download_firmware(struct ath10k *ar) { int ret; @@ -446,6 +614,13 @@ static int ath10k_init_uart(struct ath10k *ar) return ret; } + /* Set the UART baud rate to 19200. */ + ret = ath10k_bmi_write32(ar, hi_desired_baud_rate, 19200); + if (ret) { + ath10k_warn("could not set the baud rate (%d)\n", ret); + return ret; + } + ath10k_info("UART prints enabled\n"); return 0; } @@ -545,6 +720,9 @@ struct ath10k *ath10k_core_create(void *hif_priv, struct device *dev, INIT_WORK(&ar->offchan_tx_work, ath10k_offchan_tx_work); skb_queue_head_init(&ar->offchan_tx_queue); + INIT_WORK(&ar->wmi_mgmt_tx_work, ath10k_mgmt_over_wmi_tx_work); + skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + init_waitqueue_head(&ar->event_queue); INIT_WORK(&ar->restart_work, ath10k_core_restart); @@ -559,6 +737,8 @@ EXPORT_SYMBOL(ath10k_core_create); void ath10k_core_destroy(struct ath10k *ar) { + ath10k_debug_destroy(ar); + flush_workqueue(ar->workqueue); destroy_workqueue(ar->workqueue); @@ -570,6 +750,8 @@ int ath10k_core_start(struct ath10k *ar) { int status; + lockdep_assert_held(&ar->conf_mutex); + ath10k_bmi_start(ar); if (ath10k_init_configure_target(ar)) { @@ -620,10 +802,6 @@ int ath10k_core_start(struct ath10k *ar) ath10k_info("firmware %s booted\n", ar->hw->wiphy->fw_version); - status = ath10k_check_fw_version(ar); - if (status) - goto err_disconnect_htc; - status = ath10k_wmi_cmd_init(ar); if (status) { ath10k_err("could not send WMI init command (%d)\n", status); @@ -641,7 +819,12 @@ int ath10k_core_start(struct ath10k *ar) if (status) goto err_disconnect_htc; + status = ath10k_debug_start(ar); + if (status) + goto err_disconnect_htc; + ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; + INIT_LIST_HEAD(&ar->arvifs); return 0; @@ -658,6 +841,9 @@ EXPORT_SYMBOL(ath10k_core_start); void ath10k_core_stop(struct ath10k *ar) { + lockdep_assert_held(&ar->conf_mutex); + + ath10k_debug_stop(ar); ath10k_htc_stop(&ar->htc); ath10k_htt_detach(&ar->htt); ath10k_wmi_detach(ar); @@ -704,23 +890,65 @@ static int ath10k_core_probe_fw(struct ath10k *ar) return ret; } + mutex_lock(&ar->conf_mutex); + ret = ath10k_core_start(ar); if (ret) { ath10k_err("could not init core (%d)\n", ret); ath10k_core_free_firmware_files(ar); ath10k_hif_power_down(ar); + mutex_unlock(&ar->conf_mutex); return ret; } ath10k_core_stop(ar); + + mutex_unlock(&ar->conf_mutex); + ath10k_hif_power_down(ar); return 0; } -int ath10k_core_register(struct ath10k *ar) +static int ath10k_core_check_chip_id(struct ath10k *ar) +{ + u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV); + + ath10k_dbg(ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n", + ar->chip_id, hw_revision); + + /* Check that we are not using hw1.0 (some of them have same pci id + * as hw2.0) before doing anything else as ath10k crashes horribly + * due to missing hw1.0 workarounds. */ + switch (hw_revision) { + case QCA988X_HW_1_0_CHIP_ID_REV: + ath10k_err("ERROR: qca988x hw1.0 is not supported\n"); + return -EOPNOTSUPP; + + case QCA988X_HW_2_0_CHIP_ID_REV: + /* known hardware revision, continue normally */ + return 0; + + default: + ath10k_warn("Warning: hardware revision unknown (0x%x), expect problems\n", + ar->chip_id); + return 0; + } + + return 0; +} + +int ath10k_core_register(struct ath10k *ar, u32 chip_id) { int status; + ar->chip_id = chip_id; + + status = ath10k_core_check_chip_id(ar); + if (status) { + ath10k_err("Unsupported chip id 0x%08x\n", ar->chip_id); + return status; + } + status = ath10k_core_probe_fw(ar); if (status) { ath10k_err("could not probe fw (%d)\n", status); @@ -755,6 +983,7 @@ void ath10k_core_unregister(struct ath10k *ar) * Otherwise we will fail to submit commands to FW and mac80211 will be * unhappy about callback failures. */ ath10k_mac_unregister(ar); + ath10k_core_free_firmware_files(ar); } EXPORT_SYMBOL(ath10k_core_unregister); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index e4bba563ed4273613e58332e45c4146c04818516..0934f7633de399df82df8ab74a9e0f6ef64f5b82 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -43,27 +43,23 @@ /* Antenna noise floor */ #define ATH10K_DEFAULT_NOISE_FLOOR -95 +#define ATH10K_MAX_NUM_MGMT_PENDING 16 + struct ath10k; struct ath10k_skb_cb { dma_addr_t paddr; bool is_mapped; bool is_aborted; + u8 vdev_id; struct { - u8 vdev_id; - u16 msdu_id; u8 tid; bool is_offchan; - bool is_conf; - bool discard; - bool no_ack; - u8 refcount; - struct sk_buff *txfrag; - struct sk_buff *msdu; - } __packed htt; - /* 4 bytes left on 64bit arch */ + u8 frag_len; + u8 pad_len; + } __packed htt; } __packed; static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) @@ -108,15 +104,26 @@ struct ath10k_bmi { bool done_sent; }; +#define ATH10K_MAX_MEM_REQS 16 + +struct ath10k_mem_chunk { + void *vaddr; + dma_addr_t paddr; + u32 len; + u32 req_id; +}; + struct ath10k_wmi { enum ath10k_htc_ep_id eid; struct completion service_ready; struct completion unified_ready; - atomic_t pending_tx_count; - wait_queue_head_t wq; + wait_queue_head_t tx_credits_wq; + struct wmi_cmd_map *cmd; + struct wmi_vdev_param_map *vdev_param; + struct wmi_pdev_param_map *pdev_param; - struct sk_buff_head wmi_event_list; - struct work_struct wmi_event_work; + u32 num_mem_chunks; + struct ath10k_mem_chunk mem_chunks[ATH10K_MAX_MEM_REQS]; }; struct ath10k_peer_stat { @@ -198,17 +205,22 @@ struct ath10k_peer { #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ) struct ath10k_vif { + struct list_head list; + u32 vdev_id; enum wmi_vdev_type vdev_type; enum wmi_vdev_subtype vdev_subtype; u32 beacon_interval; u32 dtim_period; + struct sk_buff *beacon; struct ath10k *ar; struct ieee80211_vif *vif; + struct work_struct wep_key_work; struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1]; - u8 def_wep_key_index; + u8 def_wep_key_idx; + u8 def_wep_key_newidx; u16 tx_seq_no; @@ -246,6 +258,9 @@ struct ath10k_debug { u32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; struct completion event_stats_compl; + + unsigned long htt_stats_mask; + struct delayed_work htt_stats_dwork; }; enum ath10k_state { @@ -270,12 +285,27 @@ enum ath10k_state { ATH10K_STATE_WEDGED, }; +enum ath10k_fw_features { + /* wmi_mgmt_rx_hdr contains extra RSSI information */ + ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0, + + /* firmware from 10X branch */ + ATH10K_FW_FEATURE_WMI_10X = 1, + + /* firmware support tx frame management over WMI, otherwise it's HTT */ + ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX = 2, + + /* keep last */ + ATH10K_FW_FEATURE_COUNT, +}; + struct ath10k { struct ath_common ath_common; struct ieee80211_hw *hw; struct device *dev; u8 mac_addr[ETH_ALEN]; + u32 chip_id; u32 target_version; u8 fw_version_major; u32 fw_version_minor; @@ -288,6 +318,8 @@ struct ath10k { u32 vht_cap_info; u32 num_rf_chains; + DECLARE_BITMAP(fw_features, ATH10K_FW_FEATURE_COUNT); + struct targetdef *targetdef; struct hostdef *hostdef; @@ -319,9 +351,19 @@ struct ath10k { } fw; } hw_params; - const struct firmware *board_data; + const struct firmware *board; + const void *board_data; + size_t board_len; + const struct firmware *otp; + const void *otp_data; + size_t otp_len; + const struct firmware *firmware; + const void *firmware_data; + size_t firmware_len; + + int fw_api; struct { struct completion started; @@ -364,6 +406,7 @@ struct ath10k { /* protects shared structure data */ spinlock_t data_lock; + struct list_head arvifs; struct list_head peers; wait_queue_head_t peer_mapping_wq; @@ -372,6 +415,9 @@ struct ath10k { struct completion offchan_tx_completed; struct sk_buff *offchan_tx_skb; + struct work_struct wmi_mgmt_tx_work; + struct sk_buff_head wmi_mgmt_tx_queue; + enum ath10k_state state; struct work_struct restart_work; @@ -393,7 +439,7 @@ void ath10k_core_destroy(struct ath10k *ar); int ath10k_core_start(struct ath10k *ar); void ath10k_core_stop(struct ath10k *ar); -int ath10k_core_register(struct ath10k *ar); +int ath10k_core_register(struct ath10k *ar, u32 chip_id); void ath10k_core_unregister(struct ath10k *ar); #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 3d65594fa098a40f638f73499d423e80d2a64a7e..760ff2289e3cf2903773544d3349cab7bde1a852 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -21,6 +21,9 @@ #include "core.h" #include "debug.h" +/* ms */ +#define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000 + static int ath10k_printk(const char *level, const char *fmt, ...) { struct va_format vaf; @@ -260,7 +263,6 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, } spin_unlock_bh(&ar->data_lock); - mutex_unlock(&ar->conf_mutex); complete(&ar->debug.event_stats_compl); } @@ -499,6 +501,144 @@ static const struct file_operations fops_simulate_fw_crash = { .llseek = default_llseek, }; +static ssize_t ath10k_read_chip_id(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned int len; + char buf[50]; + + len = scnprintf(buf, sizeof(buf), "0x%08x\n", ar->chip_id); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_chip_id = { + .read = ath10k_read_chip_id, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath10k_debug_htt_stats_req(struct ath10k *ar) +{ + u64 cookie; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + if (ar->debug.htt_stats_mask == 0) + /* htt stats are disabled */ + return 0; + + if (ar->state != ATH10K_STATE_ON) + return 0; + + cookie = get_jiffies_64(); + + ret = ath10k_htt_h2t_stats_req(&ar->htt, ar->debug.htt_stats_mask, + cookie); + if (ret) { + ath10k_warn("failed to send htt stats request: %d\n", ret); + return ret; + } + + queue_delayed_work(ar->workqueue, &ar->debug.htt_stats_dwork, + msecs_to_jiffies(ATH10K_DEBUG_HTT_STATS_INTERVAL)); + + return 0; +} + +static void ath10k_debug_htt_stats_dwork(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, + debug.htt_stats_dwork.work); + + mutex_lock(&ar->conf_mutex); + + ath10k_debug_htt_stats_req(ar); + + mutex_unlock(&ar->conf_mutex); +} + +static ssize_t ath10k_read_htt_stats_mask(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[32]; + unsigned int len; + + len = scnprintf(buf, sizeof(buf), "%lu\n", ar->debug.htt_stats_mask); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_write_htt_stats_mask(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long mask; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &mask); + if (ret) + return ret; + + /* max 8 bit masks (for now) */ + if (mask > 0xff) + return -E2BIG; + + mutex_lock(&ar->conf_mutex); + + ar->debug.htt_stats_mask = mask; + + ret = ath10k_debug_htt_stats_req(ar); + if (ret) + goto out; + + ret = count; + +out: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static const struct file_operations fops_htt_stats_mask = { + .read = ath10k_read_htt_stats_mask, + .write = ath10k_write_htt_stats_mask, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +int ath10k_debug_start(struct ath10k *ar) +{ + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + ret = ath10k_debug_htt_stats_req(ar); + if (ret) + /* continue normally anyway, this isn't serious */ + ath10k_warn("failed to start htt stats workqueue: %d\n", ret); + + return 0; +} + +void ath10k_debug_stop(struct ath10k *ar) +{ + lockdep_assert_held(&ar->conf_mutex); + + /* Must not use _sync to avoid deadlock, we do that in + * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid + * warning from del_timer(). */ + if (ar->debug.htt_stats_mask != 0) + cancel_delayed_work(&ar->debug.htt_stats_dwork); +} + int ath10k_debug_create(struct ath10k *ar) { ar->debug.debugfs_phy = debugfs_create_dir("ath10k", @@ -507,6 +647,9 @@ int ath10k_debug_create(struct ath10k *ar) if (!ar->debug.debugfs_phy) return -ENOMEM; + INIT_DELAYED_WORK(&ar->debug.htt_stats_dwork, + ath10k_debug_htt_stats_dwork); + init_completion(&ar->debug.event_stats_compl); debugfs_create_file("fw_stats", S_IRUSR, ar->debug.debugfs_phy, ar, @@ -518,8 +661,20 @@ int ath10k_debug_create(struct ath10k *ar) debugfs_create_file("simulate_fw_crash", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_simulate_fw_crash); + debugfs_create_file("chip_id", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_chip_id); + + debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy, + ar, &fops_htt_stats_mask); + return 0; } + +void ath10k_debug_destroy(struct ath10k *ar) +{ + cancel_delayed_work_sync(&ar->debug.htt_stats_dwork); +} + #endif /* CONFIG_ATH10K_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 168140c54028eb16d7482ecbb7a6a835d4807736..3cfe3ee90dbe1d802d7c3b8b4fe0c7cd8c815ee9 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -27,22 +27,26 @@ enum ath10k_debug_mask { ATH10K_DBG_HTC = 0x00000004, ATH10K_DBG_HTT = 0x00000008, ATH10K_DBG_MAC = 0x00000010, - ATH10K_DBG_CORE = 0x00000020, + ATH10K_DBG_BOOT = 0x00000020, ATH10K_DBG_PCI_DUMP = 0x00000040, ATH10K_DBG_HTT_DUMP = 0x00000080, ATH10K_DBG_MGMT = 0x00000100, ATH10K_DBG_DATA = 0x00000200, + ATH10K_DBG_BMI = 0x00000400, ATH10K_DBG_ANY = 0xffffffff, }; extern unsigned int ath10k_debug_mask; -extern __printf(1, 2) int ath10k_info(const char *fmt, ...); -extern __printf(1, 2) int ath10k_err(const char *fmt, ...); -extern __printf(1, 2) int ath10k_warn(const char *fmt, ...); +__printf(1, 2) int ath10k_info(const char *fmt, ...); +__printf(1, 2) int ath10k_err(const char *fmt, ...); +__printf(1, 2) int ath10k_warn(const char *fmt, ...); #ifdef CONFIG_ATH10K_DEBUGFS +int ath10k_debug_start(struct ath10k *ar); +void ath10k_debug_stop(struct ath10k *ar); int ath10k_debug_create(struct ath10k *ar); +void ath10k_debug_destroy(struct ath10k *ar); void ath10k_debug_read_service_map(struct ath10k *ar, void *service_map, size_t map_size); @@ -50,11 +54,24 @@ void ath10k_debug_read_target_stats(struct ath10k *ar, struct wmi_stats_event *ev); #else +static inline int ath10k_debug_start(struct ath10k *ar) +{ + return 0; +} + +static inline void ath10k_debug_stop(struct ath10k *ar) +{ +} + static inline int ath10k_debug_create(struct ath10k *ar) { return 0; } +static inline void ath10k_debug_destroy(struct ath10k *ar) +{ +} + static inline void ath10k_debug_read_service_map(struct ath10k *ar, void *service_map, size_t map_size) @@ -68,7 +85,7 @@ static inline void ath10k_debug_read_target_stats(struct ath10k *ar, #endif /* CONFIG_ATH10K_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG -extern __printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask, +__printf(2, 3) void ath10k_dbg(enum ath10k_debug_mask mask, const char *fmt, ...); void ath10k_dbg_dump(enum ath10k_debug_mask mask, const char *msg, const char *prefix, diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index ef3329ef52f369f0b65a7fd2e830ab07d99c573b..3118d7506734267c8fcca7e80ad9eeba9269fce1 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -103,10 +103,10 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep, struct ath10k_htc_hdr *hdr; hdr = (struct ath10k_htc_hdr *)skb->data; - memset(hdr, 0, sizeof(*hdr)); hdr->eid = ep->eid; hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr)); + hdr->flags = 0; spin_lock_bh(&ep->htc->tx_lock); hdr->seq_no = ep->seq_no++; @@ -117,134 +117,13 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep, spin_unlock_bh(&ep->htc->tx_lock); } -static int ath10k_htc_issue_skb(struct ath10k_htc *htc, - struct ath10k_htc_ep *ep, - struct sk_buff *skb, - u8 credits) -{ - struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); - int ret; - - ath10k_dbg(ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, - ep->eid, skb); - - ath10k_htc_prepare_tx_skb(ep, skb); - - ret = ath10k_skb_map(htc->ar->dev, skb); - if (ret) - goto err; - - ret = ath10k_hif_send_head(htc->ar, - ep->ul_pipe_id, - ep->eid, - skb->len, - skb); - if (unlikely(ret)) - goto err; - - return 0; -err: - ath10k_warn("HTC issue failed: %d\n", ret); - - spin_lock_bh(&htc->tx_lock); - ep->tx_credits += credits; - spin_unlock_bh(&htc->tx_lock); - - /* this is the simplest way to handle out-of-resources for non-credit - * based endpoints. credit based endpoints can still get -ENOSR, but - * this is highly unlikely as credit reservation should prevent that */ - if (ret == -ENOSR) { - spin_lock_bh(&htc->tx_lock); - __skb_queue_head(&ep->tx_queue, skb); - spin_unlock_bh(&htc->tx_lock); - - return ret; - } - - skb_cb->is_aborted = true; - ath10k_htc_notify_tx_completion(ep, skb); - - return ret; -} - -static struct sk_buff *ath10k_htc_get_skb_credit_based(struct ath10k_htc *htc, - struct ath10k_htc_ep *ep, - u8 *credits) -{ - struct sk_buff *skb; - struct ath10k_skb_cb *skb_cb; - int credits_required; - int remainder; - unsigned int transfer_len; - - lockdep_assert_held(&htc->tx_lock); - - skb = __skb_dequeue(&ep->tx_queue); - if (!skb) - return NULL; - - skb_cb = ATH10K_SKB_CB(skb); - transfer_len = skb->len; - - if (likely(transfer_len <= htc->target_credit_size)) { - credits_required = 1; - } else { - /* figure out how many credits this message requires */ - credits_required = transfer_len / htc->target_credit_size; - remainder = transfer_len % htc->target_credit_size; - - if (remainder) - credits_required++; - } - - ath10k_dbg(ATH10K_DBG_HTC, "Credits required %d got %d\n", - credits_required, ep->tx_credits); - - if (ep->tx_credits < credits_required) { - __skb_queue_head(&ep->tx_queue, skb); - return NULL; - } - - ep->tx_credits -= credits_required; - *credits = credits_required; - return skb; -} - -static void ath10k_htc_send_work(struct work_struct *work) -{ - struct ath10k_htc_ep *ep = container_of(work, - struct ath10k_htc_ep, send_work); - struct ath10k_htc *htc = ep->htc; - struct sk_buff *skb; - u8 credits = 0; - int ret; - - while (true) { - if (ep->ul_is_polled) - ath10k_htc_send_complete_check(ep, 0); - - spin_lock_bh(&htc->tx_lock); - if (ep->tx_credit_flow_enabled) - skb = ath10k_htc_get_skb_credit_based(htc, ep, - &credits); - else - skb = __skb_dequeue(&ep->tx_queue); - spin_unlock_bh(&htc->tx_lock); - - if (!skb) - break; - - ret = ath10k_htc_issue_skb(htc, ep, skb, credits); - if (ret == -ENOSR) - break; - } -} - int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid, struct sk_buff *skb) { struct ath10k_htc_ep *ep = &htc->endpoint[eid]; + int credits = 0; + int ret; if (htc->ar->state == ATH10K_STATE_WEDGED) return -ECOMM; @@ -254,18 +133,55 @@ int ath10k_htc_send(struct ath10k_htc *htc, return -ENOENT; } + /* FIXME: This looks ugly, can we fix it? */ spin_lock_bh(&htc->tx_lock); if (htc->stopped) { spin_unlock_bh(&htc->tx_lock); return -ESHUTDOWN; } + spin_unlock_bh(&htc->tx_lock); - __skb_queue_tail(&ep->tx_queue, skb); skb_push(skb, sizeof(struct ath10k_htc_hdr)); - spin_unlock_bh(&htc->tx_lock); - queue_work(htc->ar->workqueue, &ep->send_work); + if (ep->tx_credit_flow_enabled) { + credits = DIV_ROUND_UP(skb->len, htc->target_credit_size); + spin_lock_bh(&htc->tx_lock); + if (ep->tx_credits < credits) { + spin_unlock_bh(&htc->tx_lock); + ret = -EAGAIN; + goto err_pull; + } + ep->tx_credits -= credits; + spin_unlock_bh(&htc->tx_lock); + } + + ath10k_htc_prepare_tx_skb(ep, skb); + + ret = ath10k_skb_map(htc->ar->dev, skb); + if (ret) + goto err_credits; + + ret = ath10k_hif_send_head(htc->ar, ep->ul_pipe_id, ep->eid, + skb->len, skb); + if (ret) + goto err_unmap; + return 0; + +err_unmap: + ath10k_skb_unmap(htc->ar->dev, skb); +err_credits: + if (ep->tx_credit_flow_enabled) { + spin_lock_bh(&htc->tx_lock); + ep->tx_credits += credits; + spin_unlock_bh(&htc->tx_lock); + + if (ep->ep_ops.ep_tx_credits) + ep->ep_ops.ep_tx_credits(htc->ar); + } +err_pull: + skb_pull(skb, sizeof(struct ath10k_htc_hdr)); + return ret; } static int ath10k_htc_tx_completion_handler(struct ath10k *ar, @@ -278,39 +194,9 @@ static int ath10k_htc_tx_completion_handler(struct ath10k *ar, ath10k_htc_notify_tx_completion(ep, skb); /* the skb now belongs to the completion handler */ - /* note: when using TX credit flow, the re-checking of queues happens - * when credits flow back from the target. in the non-TX credit case, - * we recheck after the packet completes */ - spin_lock_bh(&htc->tx_lock); - if (!ep->tx_credit_flow_enabled && !htc->stopped) - queue_work(ar->workqueue, &ep->send_work); - spin_unlock_bh(&htc->tx_lock); - return 0; } -/* flush endpoint TX queue */ -static void ath10k_htc_flush_endpoint_tx(struct ath10k_htc *htc, - struct ath10k_htc_ep *ep) -{ - struct sk_buff *skb; - struct ath10k_skb_cb *skb_cb; - - spin_lock_bh(&htc->tx_lock); - for (;;) { - skb = __skb_dequeue(&ep->tx_queue); - if (!skb) - break; - - skb_cb = ATH10K_SKB_CB(skb); - skb_cb->is_aborted = true; - ath10k_htc_notify_tx_completion(ep, skb); - } - spin_unlock_bh(&htc->tx_lock); - - cancel_work_sync(&ep->send_work); -} - /***********/ /* Receive */ /***********/ @@ -340,8 +226,11 @@ ath10k_htc_process_credit_report(struct ath10k_htc *htc, ep = &htc->endpoint[report->eid]; ep->tx_credits += report->credits; - if (ep->tx_credits && !skb_queue_empty(&ep->tx_queue)) - queue_work(htc->ar->workqueue, &ep->send_work); + if (ep->ep_ops.ep_tx_credits) { + spin_unlock_bh(&htc->tx_lock); + ep->ep_ops.ep_tx_credits(htc->ar); + spin_lock_bh(&htc->tx_lock); + } } spin_unlock_bh(&htc->tx_lock); } @@ -599,10 +488,8 @@ static void ath10k_htc_reset_endpoint_states(struct ath10k_htc *htc) ep->max_ep_message_len = 0; ep->max_tx_queue_depth = 0; ep->eid = i; - skb_queue_head_init(&ep->tx_queue); ep->htc = htc; ep->tx_credit_flow_enabled = true; - INIT_WORK(&ep->send_work, ath10k_htc_send_work); } } @@ -752,8 +639,8 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, tx_alloc = ath10k_htc_get_credit_allocation(htc, conn_req->service_id); if (!tx_alloc) - ath10k_dbg(ATH10K_DBG_HTC, - "HTC Service %s does not allocate target credits\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot htc service %s does not allocate target credits\n", htc_service_name(conn_req->service_id)); skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); @@ -772,16 +659,16 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC); - req_msg = &msg->connect_service; - req_msg->flags = __cpu_to_le16(flags); - req_msg->service_id = __cpu_to_le16(conn_req->service_id); - /* Only enable credit flow control for WMI ctrl service */ if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) { flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; disable_credit_flow_ctrl = true; } + req_msg = &msg->connect_service; + req_msg->flags = __cpu_to_le16(flags); + req_msg->service_id = __cpu_to_le16(conn_req->service_id); + INIT_COMPLETION(htc->ctl_resp); status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb); @@ -873,19 +760,19 @@ setup: if (status) return status; - ath10k_dbg(ATH10K_DBG_HTC, - "HTC service: %s UL pipe: %d DL pipe: %d eid: %d ready\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", htc_service_name(ep->service_id), ep->ul_pipe_id, ep->dl_pipe_id, ep->eid); - ath10k_dbg(ATH10K_DBG_HTC, - "EP %d UL polled: %d, DL polled: %d\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot htc ep %d ul polled %d dl polled %d\n", ep->eid, ep->ul_is_polled, ep->dl_is_polled); if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { ep->tx_credit_flow_enabled = false; - ath10k_dbg(ATH10K_DBG_HTC, - "HTC service: %s eid: %d TX flow control disabled\n", + ath10k_dbg(ATH10K_DBG_BOOT, + "boot htc service '%s' eid %d TX flow control disabled\n", htc_service_name(ep->service_id), assigned_eid); } @@ -945,18 +832,10 @@ int ath10k_htc_start(struct ath10k_htc *htc) */ void ath10k_htc_stop(struct ath10k_htc *htc) { - int i; - struct ath10k_htc_ep *ep; - spin_lock_bh(&htc->tx_lock); htc->stopped = true; spin_unlock_bh(&htc->tx_lock); - for (i = ATH10K_HTC_EP_0; i < ATH10K_HTC_EP_COUNT; i++) { - ep = &htc->endpoint[i]; - ath10k_htc_flush_endpoint_tx(htc, ep); - } - ath10k_hif_stop(htc->ar); } diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h index e1dd8c761853d7d3197c173aeacb9499d4bd274a..4716d331e6b6504d712c858345545a410cbcbab1 100644 --- a/drivers/net/wireless/ath/ath10k/htc.h +++ b/drivers/net/wireless/ath/ath10k/htc.h @@ -276,6 +276,7 @@ struct ath10k_htc_ops { struct ath10k_htc_ep_ops { void (*ep_tx_complete)(struct ath10k *, struct sk_buff *); void (*ep_rx_complete)(struct ath10k *, struct sk_buff *); + void (*ep_tx_credits)(struct ath10k *); }; /* service connection information */ @@ -315,15 +316,11 @@ struct ath10k_htc_ep { int ul_is_polled; /* call HIF to get tx completions */ int dl_is_polled; /* call HIF to fetch rx (not implemented) */ - struct sk_buff_head tx_queue; - u8 seq_no; /* for debugging */ int tx_credits; int tx_credit_size; int tx_credits_per_max_message; bool tx_credit_flow_enabled; - - struct work_struct send_work; }; struct ath10k_htc_svc_tx_credits { diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 39342c5cfcb270d8ded3d53fb8b0aaba011cc789..5f7eeebc54327736cbfc2b6fb77bc030029bf7c3 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -104,21 +104,16 @@ err_htc_attach: static int ath10k_htt_verify_version(struct ath10k_htt *htt) { - ath10k_dbg(ATH10K_DBG_HTT, - "htt target version %d.%d; host version %d.%d\n", - htt->target_version_major, - htt->target_version_minor, - HTT_CURRENT_VERSION_MAJOR, - HTT_CURRENT_VERSION_MINOR); - - if (htt->target_version_major != HTT_CURRENT_VERSION_MAJOR) { - ath10k_err("htt major versions are incompatible!\n"); + ath10k_info("htt target version %d.%d\n", + htt->target_version_major, htt->target_version_minor); + + if (htt->target_version_major != 2 && + htt->target_version_major != 3) { + ath10k_err("unsupported htt major version %d. supported versions are 2 and 3\n", + htt->target_version_major); return -ENOTSUPP; } - if (htt->target_version_minor != HTT_CURRENT_VERSION_MINOR) - ath10k_warn("htt minor version differ but still compatible\n"); - return 0; } diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 318be4629cded3b19248fe13c17d6def30253b92..1a337e93b7e95e9ef70ab9053696dce7cf4e4c95 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -19,13 +19,11 @@ #define _HTT_H_ #include +#include #include "htc.h" #include "rx_desc.h" -#define HTT_CURRENT_VERSION_MAJOR 2 -#define HTT_CURRENT_VERSION_MINOR 1 - enum htt_dbg_stats_type { HTT_DBG_STATS_WAL_PDEV_TXRX = 1 << 0, HTT_DBG_STATS_RX_REORDER = 1 << 1, @@ -45,6 +43,9 @@ enum htt_h2t_msg_type { /* host-to-target */ HTT_H2T_MSG_TYPE_SYNC = 4, HTT_H2T_MSG_TYPE_AGGR_CFG = 5, HTT_H2T_MSG_TYPE_FRAG_DESC_BANK_CFG = 6, + + /* This command is used for sending management frames in HTT < 3.0. + * HTT >= 3.0 uses TX_FRM for everything. */ HTT_H2T_MSG_TYPE_MGMT_TX = 7, HTT_H2T_NUM_MSGS /* keep this last */ @@ -1268,6 +1269,7 @@ struct ath10k_htt { /* set if host-fw communication goes haywire * used to avoid further failures */ bool rx_confused; + struct tasklet_struct rx_replenish_task; }; #define RX_HTT_HDR_STATUS_LEN 64 @@ -1308,6 +1310,10 @@ struct htt_rx_desc { #define HTT_RX_BUF_SIZE 1920 #define HTT_RX_MSDU_SIZE (HTT_RX_BUF_SIZE - (int)sizeof(struct htt_rx_desc)) +/* Refill a bunch of RX buffers for each refill round so that FW/HW can handle + * aggregated traffic more nicely. */ +#define ATH10K_HTT_MAX_NUM_REFILL 16 + /* * DMA_MAP expects the buffer to be an integral number of cache lines. * Rather than checking the actual cache line size, this code makes a @@ -1327,6 +1333,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt); void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb); void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); +int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie); int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt); void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt); diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index e784c40b904b55165a112df250c61351c606979b..90d4f74c28d7deaa4c087bfdbc47ca83ac1a9716 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -20,6 +20,7 @@ #include "htt.h" #include "txrx.h" #include "debug.h" +#include "trace.h" #include @@ -40,6 +41,10 @@ /* when under memory pressure rx ring refill may fail and needs a retry */ #define HTT_RX_RING_REFILL_RETRY_MS 50 + +static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb); + + static int ath10k_htt_rx_ring_size(struct ath10k_htt *htt) { int size; @@ -177,10 +182,27 @@ static int ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) { - int ret, num_to_fill; + int ret, num_deficit, num_to_fill; + /* Refilling the whole RX ring buffer proves to be a bad idea. The + * reason is RX may take up significant amount of CPU cycles and starve + * other tasks, e.g. TX on an ethernet device while acting as a bridge + * with ath10k wlan interface. This ended up with very poor performance + * once CPU the host system was overwhelmed with RX on ath10k. + * + * By limiting the number of refills the replenishing occurs + * progressively. This in turns makes use of the fact tasklets are + * processed in FIFO order. This means actual RX processing can starve + * out refilling. If there's not enough buffers on RX ring FW will not + * report RX until it is refilled with enough buffers. This + * automatically balances load wrt to CPU power. + * + * This probably comes at a cost of lower maximum throughput but + * improves the avarage and stability. */ spin_lock_bh(&htt->rx_ring.lock); - num_to_fill = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt; + num_deficit = htt->rx_ring.fill_level - htt->rx_ring.fill_cnt; + num_to_fill = min(ATH10K_HTT_MAX_NUM_REFILL, num_deficit); + num_deficit -= num_to_fill; ret = ath10k_htt_rx_ring_fill_n(htt, num_to_fill); if (ret == -ENOMEM) { /* @@ -191,6 +213,8 @@ static void ath10k_htt_rx_msdu_buff_replenish(struct ath10k_htt *htt) */ mod_timer(&htt->rx_ring.refill_retry_timer, jiffies + msecs_to_jiffies(HTT_RX_RING_REFILL_RETRY_MS)); + } else if (num_deficit > 0) { + tasklet_schedule(&htt->rx_replenish_task); } spin_unlock_bh(&htt->rx_ring.lock); } @@ -212,6 +236,7 @@ void ath10k_htt_rx_detach(struct ath10k_htt *htt) int sw_rd_idx = htt->rx_ring.sw_rd_idx.msdu_payld; del_timer_sync(&htt->rx_ring.refill_retry_timer); + tasklet_kill(&htt->rx_replenish_task); while (sw_rd_idx != __le32_to_cpu(*(htt->rx_ring.alloc_idx.vaddr))) { struct sk_buff *skb = @@ -441,6 +466,12 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, return msdu_chaining; } +static void ath10k_htt_rx_replenish_task(unsigned long ptr) +{ + struct ath10k_htt *htt = (struct ath10k_htt *)ptr; + ath10k_htt_rx_msdu_buff_replenish(htt); +} + int ath10k_htt_rx_attach(struct ath10k_htt *htt) { dma_addr_t paddr; @@ -501,7 +532,10 @@ int ath10k_htt_rx_attach(struct ath10k_htt *htt) if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level)) goto err_fill_ring; - ath10k_dbg(ATH10K_DBG_HTT, "HTT RX ring size: %d, fill_level: %d\n", + tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task, + (unsigned long)htt); + + ath10k_dbg(ATH10K_DBG_BOOT, "htt rx ring size %d fill_level %d\n", htt->rx_ring.size, htt->rx_ring.fill_level); return 0; @@ -590,134 +624,144 @@ static bool ath10k_htt_rx_hdr_is_amsdu(struct ieee80211_hdr *hdr) return false; } -static int ath10k_htt_rx_amsdu(struct ath10k_htt *htt, - struct htt_rx_info *info) +struct rfc1042_hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + __be16 snap_type; +} __packed; + +struct amsdu_subframe_hdr { + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + __be16 len; +} __packed; + +static void ath10k_htt_rx_amsdu(struct ath10k_htt *htt, + struct htt_rx_info *info) { struct htt_rx_desc *rxd; - struct sk_buff *amsdu; struct sk_buff *first; - struct ieee80211_hdr *hdr; struct sk_buff *skb = info->skb; enum rx_msdu_decap_format fmt; enum htt_rx_mpdu_encrypt_type enctype; + struct ieee80211_hdr *hdr; + u8 hdr_buf[64], addr[ETH_ALEN], *qos; unsigned int hdr_len; - int crypto_len; rxd = (void *)skb->data - sizeof(*rxd); - fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), - RX_MSDU_START_INFO1_DECAP_FORMAT); enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); - /* FIXME: No idea what assumptions are safe here. Need logs */ - if ((fmt == RX_MSDU_DECAP_RAW && skb->next) || - (fmt == RX_MSDU_DECAP_8023_SNAP_LLC)) { - ath10k_htt_rx_free_msdu_chain(skb->next); - skb->next = NULL; - return -ENOTSUPP; - } + hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + memcpy(hdr_buf, hdr, hdr_len); + hdr = (struct ieee80211_hdr *)hdr_buf; - /* A-MSDU max is a little less than 8K */ - amsdu = dev_alloc_skb(8*1024); - if (!amsdu) { - ath10k_warn("A-MSDU allocation failed\n"); - ath10k_htt_rx_free_msdu_chain(skb->next); - skb->next = NULL; - return -ENOMEM; - } - - if (fmt >= RX_MSDU_DECAP_NATIVE_WIFI) { - int hdrlen; - - hdr = (void *)rxd->rx_hdr_status; - hdrlen = ieee80211_hdrlen(hdr->frame_control); - memcpy(skb_put(amsdu, hdrlen), hdr, hdrlen); - } + /* FIXME: Hopefully this is a temporary measure. + * + * Reporting individual A-MSDU subframes means each reported frame + * shares the same sequence number. + * + * mac80211 drops frames it recognizes as duplicates, i.e. + * retransmission flag is set and sequence number matches sequence + * number from a previous frame (as per IEEE 802.11-2012: 9.3.2.10 + * "Duplicate detection and recovery") + * + * To avoid frames being dropped clear retransmission flag for all + * received A-MSDUs. + * + * Worst case: actual duplicate frames will be reported but this should + * still be handled gracefully by other OSI/ISO layers. */ + hdr->frame_control &= cpu_to_le16(~IEEE80211_FCTL_RETRY); first = skb; while (skb) { void *decap_hdr; - int decap_len = 0; + int len; rxd = (void *)skb->data - sizeof(*rxd); fmt = MS(__le32_to_cpu(rxd->msdu_start.info1), - RX_MSDU_START_INFO1_DECAP_FORMAT); + RX_MSDU_START_INFO1_DECAP_FORMAT); decap_hdr = (void *)rxd->rx_hdr_status; - if (skb == first) { - /* We receive linked A-MSDU subframe skbuffs. The - * first one contains the original 802.11 header (and - * possible crypto param) in the RX descriptor. The - * A-MSDU subframe header follows that. Each part is - * aligned to 4 byte boundary. */ - - hdr = (void *)amsdu->data; - hdr_len = ieee80211_hdrlen(hdr->frame_control); - crypto_len = ath10k_htt_rx_crypto_param_len(enctype); - - decap_hdr += roundup(hdr_len, 4); - decap_hdr += roundup(crypto_len, 4); - } + skb->ip_summed = ath10k_htt_rx_get_csum_state(skb); - if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) { - /* Ethernet2 decap inserts ethernet header in place of - * A-MSDU subframe header. */ - skb_pull(skb, 6 + 6 + 2); - - /* A-MSDU subframe header length */ - decap_len += 6 + 6 + 2; - - /* Ethernet2 decap also strips the LLC/SNAP so we need - * to re-insert it. The LLC/SNAP follows A-MSDU - * subframe header. */ - /* FIXME: Not all LLCs are 8 bytes long */ - decap_len += 8; - - memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len); + /* First frame in an A-MSDU chain has more decapped data. */ + if (skb == first) { + len = round_up(ieee80211_hdrlen(hdr->frame_control), 4); + len += round_up(ath10k_htt_rx_crypto_param_len(enctype), + 4); + decap_hdr += len; } - if (fmt == RX_MSDU_DECAP_NATIVE_WIFI) { - /* Native Wifi decap inserts regular 802.11 header - * in place of A-MSDU subframe header. */ + switch (fmt) { + case RX_MSDU_DECAP_RAW: + /* remove trailing FCS */ + skb_trim(skb, skb->len - FCS_LEN); + break; + case RX_MSDU_DECAP_NATIVE_WIFI: + /* pull decapped header and copy DA */ hdr = (struct ieee80211_hdr *)skb->data; - skb_pull(skb, ieee80211_hdrlen(hdr->frame_control)); + hdr_len = ieee80211_hdrlen(hdr->frame_control); + memcpy(addr, ieee80211_get_DA(hdr), ETH_ALEN); + skb_pull(skb, hdr_len); - /* A-MSDU subframe header length */ - decap_len += 6 + 6 + 2; + /* push original 802.11 header */ + hdr = (struct ieee80211_hdr *)hdr_buf; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); - memcpy(skb_put(amsdu, decap_len), decap_hdr, decap_len); - } + /* original A-MSDU header has the bit set but we're + * not including A-MSDU subframe header */ + hdr = (struct ieee80211_hdr *)skb->data; + qos = ieee80211_get_qos_ctl(hdr); + qos[0] &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; - if (fmt == RX_MSDU_DECAP_RAW) - skb_trim(skb, skb->len - 4); /* remove FCS */ + /* original 802.11 header has a different DA */ + memcpy(ieee80211_get_DA(hdr), addr, ETH_ALEN); + break; + case RX_MSDU_DECAP_ETHERNET2_DIX: + /* strip ethernet header and insert decapped 802.11 + * header, amsdu subframe header and rfc1042 header */ - memcpy(skb_put(amsdu, skb->len), skb->data, skb->len); + len = 0; + len += sizeof(struct rfc1042_hdr); + len += sizeof(struct amsdu_subframe_hdr); - /* A-MSDU subframes are padded to 4bytes - * but relative to first subframe, not the whole MPDU */ - if (skb->next && ((decap_len + skb->len) & 3)) { - int padlen = 4 - ((decap_len + skb->len) & 3); - memset(skb_put(amsdu, padlen), 0, padlen); + skb_pull(skb, sizeof(struct ethhdr)); + memcpy(skb_push(skb, len), decap_hdr, len); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); + break; + case RX_MSDU_DECAP_8023_SNAP_LLC: + /* insert decapped 802.11 header making a singly + * A-MSDU */ + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); + break; } + info->skb = skb; + info->encrypt_type = enctype; skb = skb->next; - } + info->skb->next = NULL; - info->skb = amsdu; - info->encrypt_type = enctype; - - ath10k_htt_rx_free_msdu_chain(first); + ath10k_process_rx(htt->ar, info); + } - return 0; + /* FIXME: It might be nice to re-assemble the A-MSDU when there's a + * monitor interface active for sniffing purposes. */ } -static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) +static void ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) { struct sk_buff *skb = info->skb; struct htt_rx_desc *rxd; struct ieee80211_hdr *hdr; enum rx_msdu_decap_format fmt; enum htt_rx_mpdu_encrypt_type enctype; + int hdr_len; + void *rfc1042; /* This shouldn't happen. If it does than it may be a FW bug. */ if (skb->next) { @@ -731,49 +775,53 @@ static int ath10k_htt_rx_msdu(struct ath10k_htt *htt, struct htt_rx_info *info) RX_MSDU_START_INFO1_DECAP_FORMAT); enctype = MS(__le32_to_cpu(rxd->mpdu_start.info0), RX_MPDU_START_INFO0_ENCRYPT_TYPE); - hdr = (void *)skb->data - RX_HTT_HDR_STATUS_LEN; + hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + skb->ip_summed = ath10k_htt_rx_get_csum_state(skb); switch (fmt) { case RX_MSDU_DECAP_RAW: /* remove trailing FCS */ - skb_trim(skb, skb->len - 4); + skb_trim(skb, skb->len - FCS_LEN); break; case RX_MSDU_DECAP_NATIVE_WIFI: - /* nothing to do here */ + /* Pull decapped header */ + hdr = (struct ieee80211_hdr *)skb->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + skb_pull(skb, hdr_len); + + /* Push original header */ + hdr = (struct ieee80211_hdr *)rxd->rx_hdr_status; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); break; case RX_MSDU_DECAP_ETHERNET2_DIX: - /* macaddr[6] + macaddr[6] + ethertype[2] */ - skb_pull(skb, 6 + 6 + 2); - break; - case RX_MSDU_DECAP_8023_SNAP_LLC: - /* macaddr[6] + macaddr[6] + len[2] */ - /* we don't need this for non-A-MSDU */ - skb_pull(skb, 6 + 6 + 2); - break; - } + /* strip ethernet header and insert decapped 802.11 header and + * rfc1042 header */ - if (fmt == RX_MSDU_DECAP_ETHERNET2_DIX) { - void *llc; - int llclen; + rfc1042 = hdr; + rfc1042 += roundup(hdr_len, 4); + rfc1042 += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4); - llclen = 8; - llc = hdr; - llc += roundup(ieee80211_hdrlen(hdr->frame_control), 4); - llc += roundup(ath10k_htt_rx_crypto_param_len(enctype), 4); - - skb_push(skb, llclen); - memcpy(skb->data, llc, llclen); - } + skb_pull(skb, sizeof(struct ethhdr)); + memcpy(skb_push(skb, sizeof(struct rfc1042_hdr)), + rfc1042, sizeof(struct rfc1042_hdr)); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); + break; + case RX_MSDU_DECAP_8023_SNAP_LLC: + /* remove A-MSDU subframe header and insert + * decapped 802.11 header. rfc1042 header is already there */ - if (fmt >= RX_MSDU_DECAP_ETHERNET2_DIX) { - int len = ieee80211_hdrlen(hdr->frame_control); - skb_push(skb, len); - memcpy(skb->data, hdr, len); + skb_pull(skb, sizeof(struct amsdu_subframe_hdr)); + memcpy(skb_push(skb, hdr_len), hdr, hdr_len); + break; } info->skb = skb; info->encrypt_type = enctype; - return 0; + + ath10k_process_rx(htt->ar, info); } static bool ath10k_htt_rx_has_decrypt_err(struct sk_buff *skb) @@ -845,8 +893,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, int fw_desc_len; u8 *fw_desc; int i, j; - int ret; - int ip_summed; memset(&info, 0, sizeof(info)); @@ -921,11 +967,6 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, continue; } - /* The skb is not yet processed and it may be - * reallocated. Since the offload is in the original - * skb extract the checksum now and assign it later */ - ip_summed = ath10k_htt_rx_get_csum_state(msdu_head); - info.skb = msdu_head; info.fcs_err = ath10k_htt_rx_has_fcs_err(msdu_head); info.signal = ATH10K_DEFAULT_NOISE_FLOOR; @@ -938,28 +979,13 @@ static void ath10k_htt_rx_handler(struct ath10k_htt *htt, hdr = ath10k_htt_rx_skb_get_hdr(msdu_head); if (ath10k_htt_rx_hdr_is_amsdu(hdr)) - ret = ath10k_htt_rx_amsdu(htt, &info); + ath10k_htt_rx_amsdu(htt, &info); else - ret = ath10k_htt_rx_msdu(htt, &info); - - if (ret && !info.fcs_err) { - ath10k_warn("error processing msdus %d\n", ret); - dev_kfree_skb_any(info.skb); - continue; - } - - if (ath10k_htt_rx_hdr_is_amsdu((void *)info.skb->data)) - ath10k_dbg(ATH10K_DBG_HTT, "htt mpdu is amsdu\n"); - - info.skb->ip_summed = ip_summed; - - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "htt mpdu: ", - info.skb->data, info.skb->len); - ath10k_process_rx(htt->ar, &info); + ath10k_htt_rx_msdu(htt, &info); } } - ath10k_htt_rx_msdu_buff_replenish(htt); + tasklet_schedule(&htt->rx_replenish_task); } static void ath10k_htt_rx_frag_handler(struct ath10k_htt *htt, @@ -1131,7 +1157,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) break; } - ath10k_txrx_tx_completed(htt, &tx_done); + ath10k_txrx_tx_unref(htt, &tx_done); break; } case HTT_T2H_MSG_TYPE_TX_COMPL_IND: { @@ -1165,7 +1191,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) for (i = 0; i < resp->data_tx_completion.num_msdus; i++) { msdu_id = resp->data_tx_completion.msdus[i]; tx_done.msdu_id = __le16_to_cpu(msdu_id); - ath10k_txrx_tx_completed(htt, &tx_done); + ath10k_txrx_tx_unref(htt, &tx_done); } break; } @@ -1190,8 +1216,10 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) case HTT_T2H_MSG_TYPE_TEST: /* FIX THIS */ break; - case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: case HTT_T2H_MSG_TYPE_STATS_CONF: + trace_ath10k_htt_stats(skb->data, skb->len); + break; + case HTT_T2H_MSG_TYPE_TX_INSPECT_IND: case HTT_T2H_MSG_TYPE_RX_ADDBA: case HTT_T2H_MSG_TYPE_RX_DELBA: case HTT_T2H_MSG_TYPE_RX_FLUSH: diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 656c2546b2949825a38b1b9b05d266e5af8bc5f0..d9335e9d0d04d247e868e465323037e4f9f78d9e 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -96,7 +96,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt) htt->max_num_pending_tx = ath10k_hif_get_free_queue_number(htt->ar, pipe); - ath10k_dbg(ATH10K_DBG_HTT, "htt tx max num pending tx %d\n", + ath10k_dbg(ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n", htt->max_num_pending_tx); htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) * @@ -117,7 +117,7 @@ int ath10k_htt_tx_attach(struct ath10k_htt *htt) static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) { - struct sk_buff *txdesc; + struct htt_tx_done tx_done = {0}; int msdu_id; /* No locks needed. Called after communication with the device has @@ -127,18 +127,13 @@ static void ath10k_htt_tx_cleanup_pending(struct ath10k_htt *htt) if (!test_bit(msdu_id, htt->used_msdu_ids)) continue; - txdesc = htt->pending_tx[msdu_id]; - if (!txdesc) - continue; - ath10k_dbg(ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id); - if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0) - ATH10K_SKB_CB(txdesc)->htt.refcount = 1; + tx_done.discard = 1; + tx_done.msdu_id = msdu_id; - ATH10K_SKB_CB(txdesc)->htt.discard = true; - ath10k_txrx_tx_unref(htt, txdesc); + ath10k_txrx_tx_unref(htt, &tx_done); } } @@ -152,26 +147,7 @@ void ath10k_htt_tx_detach(struct ath10k_htt *htt) void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) { - struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); - struct ath10k_htt *htt = &ar->htt; - - if (skb_cb->htt.is_conf) { - dev_kfree_skb_any(skb); - return; - } - - if (skb_cb->is_aborted) { - skb_cb->htt.discard = true; - - /* if the skbuff is aborted we need to make sure we'll free up - * the tx resources, we can't simply run tx_unref() 2 times - * because if htt tx completion came in earlier we'd access - * unallocated memory */ - if (skb_cb->htt.refcount > 1) - skb_cb->htt.refcount = 1; - } - - ath10k_txrx_tx_unref(htt, skb); + dev_kfree_skb_any(skb); } int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) @@ -192,10 +168,48 @@ int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt) cmd = (struct htt_cmd *)skb->data; cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_VERSION_REQ; - ATH10K_SKB_CB(skb)->htt.is_conf = true; + ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + +int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie) +{ + struct htt_stats_req *req; + struct sk_buff *skb; + struct htt_cmd *cmd; + int len = 0, ret; + + len += sizeof(cmd->hdr); + len += sizeof(cmd->stats_req); + + skb = ath10k_htc_alloc_skb(len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + cmd = (struct htt_cmd *)skb->data; + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_STATS_REQ; + + req = &cmd->stats_req; + + memset(req, 0, sizeof(*req)); + + /* currently we support only max 8 bit masks so no need to worry + * about endian support */ + req->upload_types[0] = mask; + req->reset_types[0] = mask; + req->stat_type = HTT_STATS_REQ_CFG_STAT_TYPE_INVALID; + req->cookie_lsb = cpu_to_le32(cookie & 0xffffffff); + req->cookie_msb = cpu_to_le32((cookie & 0xffffffff00000000ULL) >> 32); ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { + ath10k_warn("failed to send htt type stats request: %d", ret); dev_kfree_skb_any(skb); return ret; } @@ -279,8 +293,6 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) #undef desc_offset - ATH10K_SKB_CB(skb)->htt.is_conf = true; - ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); if (ret) { dev_kfree_skb_any(skb); @@ -293,10 +305,10 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) { struct device *dev = htt->ar->dev; - struct ath10k_skb_cb *skb_cb; struct sk_buff *txdesc = NULL; struct htt_cmd *cmd; - u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); + u8 vdev_id = skb_cb->vdev_id; int len = 0; int msdu_id = -1; int res; @@ -304,30 +316,30 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) res = ath10k_htt_tx_inc_pending(htt); if (res) - return res; + goto err; len += sizeof(cmd->hdr); len += sizeof(cmd->mgmt_tx); - txdesc = ath10k_htc_alloc_skb(len); - if (!txdesc) { - res = -ENOMEM; - goto err; - } - spin_lock_bh(&htt->tx_lock); - msdu_id = ath10k_htt_tx_alloc_msdu_id(htt); - if (msdu_id < 0) { + res = ath10k_htt_tx_alloc_msdu_id(htt); + if (res < 0) { spin_unlock_bh(&htt->tx_lock); - res = msdu_id; - goto err; + goto err_tx_dec; } - htt->pending_tx[msdu_id] = txdesc; + msdu_id = res; + htt->pending_tx[msdu_id] = msdu; spin_unlock_bh(&htt->tx_lock); + txdesc = ath10k_htc_alloc_skb(len); + if (!txdesc) { + res = -ENOMEM; + goto err_free_msdu_id; + } + res = ath10k_skb_map(dev, msdu); if (res) - goto err; + goto err_free_txdesc; skb_put(txdesc, len); cmd = (struct htt_cmd *)txdesc->data; @@ -339,31 +351,27 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) memcpy(cmd->mgmt_tx.hdr, msdu->data, min_t(int, msdu->len, HTT_MGMT_FRM_HDR_DOWNLOAD_LEN)); - /* refcount is decremented by HTC and HTT completions until it reaches - * zero and is freed */ - skb_cb = ATH10K_SKB_CB(txdesc); - skb_cb->htt.msdu_id = msdu_id; - skb_cb->htt.refcount = 2; - skb_cb->htt.msdu = msdu; + skb_cb->htt.frag_len = 0; + skb_cb->htt.pad_len = 0; res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); if (res) - goto err; + goto err_unmap_msdu; return 0; -err: +err_unmap_msdu: ath10k_skb_unmap(dev, msdu); - - if (txdesc) - dev_kfree_skb_any(txdesc); - if (msdu_id >= 0) { - spin_lock_bh(&htt->tx_lock); - htt->pending_tx[msdu_id] = NULL; - ath10k_htt_tx_free_msdu_id(htt, msdu_id); - spin_unlock_bh(&htt->tx_lock); - } +err_free_txdesc: + dev_kfree_skb_any(txdesc); +err_free_msdu_id: + spin_lock_bh(&htt->tx_lock); + htt->pending_tx[msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, msdu_id); + spin_unlock_bh(&htt->tx_lock); +err_tx_dec: ath10k_htt_tx_dec_pending(htt); +err: return res; } @@ -373,13 +381,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) struct htt_cmd *cmd; struct htt_data_tx_desc_frag *tx_frags; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data; - struct ath10k_skb_cb *skb_cb; + struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(msdu); struct sk_buff *txdesc = NULL; - struct sk_buff *txfrag = NULL; - u8 vdev_id = ATH10K_SKB_CB(msdu)->htt.vdev_id; + bool use_frags; + u8 vdev_id = ATH10K_SKB_CB(msdu)->vdev_id; u8 tid; - int prefetch_len, desc_len, frag_len; - dma_addr_t frags_paddr; + int prefetch_len, desc_len; int msdu_id = -1; int res; u8 flags0; @@ -387,69 +394,82 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) res = ath10k_htt_tx_inc_pending(htt); if (res) - return res; + goto err; + + spin_lock_bh(&htt->tx_lock); + res = ath10k_htt_tx_alloc_msdu_id(htt); + if (res < 0) { + spin_unlock_bh(&htt->tx_lock); + goto err_tx_dec; + } + msdu_id = res; + htt->pending_tx[msdu_id] = msdu; + spin_unlock_bh(&htt->tx_lock); prefetch_len = min(htt->prefetch_len, msdu->len); prefetch_len = roundup(prefetch_len, 4); desc_len = sizeof(cmd->hdr) + sizeof(cmd->data_tx) + prefetch_len; - frag_len = sizeof(*tx_frags) * 2; txdesc = ath10k_htc_alloc_skb(desc_len); if (!txdesc) { res = -ENOMEM; - goto err; + goto err_free_msdu_id; } - txfrag = dev_alloc_skb(frag_len); - if (!txfrag) { - res = -ENOMEM; - goto err; - } + /* Since HTT 3.0 there is no separate mgmt tx command. However in case + * of mgmt tx using TX_FRM there is not tx fragment list. Instead of tx + * fragment list host driver specifies directly frame pointer. */ + use_frags = htt->target_version_major < 3 || + !ieee80211_is_mgmt(hdr->frame_control); if (!IS_ALIGNED((unsigned long)txdesc->data, 4)) { ath10k_warn("htt alignment check failed. dropping packet.\n"); res = -EIO; - goto err; + goto err_free_txdesc; } - spin_lock_bh(&htt->tx_lock); - msdu_id = ath10k_htt_tx_alloc_msdu_id(htt); - if (msdu_id < 0) { - spin_unlock_bh(&htt->tx_lock); - res = msdu_id; - goto err; + if (use_frags) { + skb_cb->htt.frag_len = sizeof(*tx_frags) * 2; + skb_cb->htt.pad_len = (unsigned long)msdu->data - + round_down((unsigned long)msdu->data, 4); + + skb_push(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len); + } else { + skb_cb->htt.frag_len = 0; + skb_cb->htt.pad_len = 0; } - htt->pending_tx[msdu_id] = txdesc; - spin_unlock_bh(&htt->tx_lock); res = ath10k_skb_map(dev, msdu); if (res) - goto err; - - /* tx fragment list must be terminated with zero-entry */ - skb_put(txfrag, frag_len); - tx_frags = (struct htt_data_tx_desc_frag *)txfrag->data; - tx_frags[0].paddr = __cpu_to_le32(ATH10K_SKB_CB(msdu)->paddr); - tx_frags[0].len = __cpu_to_le32(msdu->len); - tx_frags[1].paddr = __cpu_to_le32(0); - tx_frags[1].len = __cpu_to_le32(0); - - res = ath10k_skb_map(dev, txfrag); - if (res) - goto err; + goto err_pull_txfrag; + + if (use_frags) { + dma_sync_single_for_cpu(dev, skb_cb->paddr, msdu->len, + DMA_TO_DEVICE); + + /* tx fragment list must be terminated with zero-entry */ + tx_frags = (struct htt_data_tx_desc_frag *)msdu->data; + tx_frags[0].paddr = __cpu_to_le32(skb_cb->paddr + + skb_cb->htt.frag_len + + skb_cb->htt.pad_len); + tx_frags[0].len = __cpu_to_le32(msdu->len - + skb_cb->htt.frag_len - + skb_cb->htt.pad_len); + tx_frags[1].paddr = __cpu_to_le32(0); + tx_frags[1].len = __cpu_to_le32(0); + + dma_sync_single_for_device(dev, skb_cb->paddr, msdu->len, + DMA_TO_DEVICE); + } - ath10k_dbg(ATH10K_DBG_HTT, "txfrag 0x%llx msdu 0x%llx\n", - (unsigned long long) ATH10K_SKB_CB(txfrag)->paddr, + ath10k_dbg(ATH10K_DBG_HTT, "msdu 0x%llx\n", (unsigned long long) ATH10K_SKB_CB(msdu)->paddr); - ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "txfrag: ", - txfrag->data, frag_len); ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "msdu: ", msdu->data, msdu->len); skb_put(txdesc, desc_len); cmd = (struct htt_cmd *)txdesc->data; - memset(cmd, 0, desc_len); tid = ATH10K_SKB_CB(msdu)->htt.tid; @@ -459,8 +479,13 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) if (!ieee80211_has_protected(hdr->frame_control)) flags0 |= HTT_DATA_TX_DESC_FLAGS0_NO_ENCRYPT; flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT; - flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI, - HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); + + if (use_frags) + flags0 |= SM(ATH10K_HW_TXRX_NATIVE_WIFI, + HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); + else + flags0 |= SM(ATH10K_HW_TXRX_MGMT, + HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE); flags1 = 0; flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID); @@ -468,45 +493,37 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD; flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD; - frags_paddr = ATH10K_SKB_CB(txfrag)->paddr; - cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FRM; cmd->data_tx.flags0 = flags0; cmd->data_tx.flags1 = __cpu_to_le16(flags1); - cmd->data_tx.len = __cpu_to_le16(msdu->len); + cmd->data_tx.len = __cpu_to_le16(msdu->len - + skb_cb->htt.frag_len - + skb_cb->htt.pad_len); cmd->data_tx.id = __cpu_to_le16(msdu_id); - cmd->data_tx.frags_paddr = __cpu_to_le32(frags_paddr); + cmd->data_tx.frags_paddr = __cpu_to_le32(skb_cb->paddr); cmd->data_tx.peerid = __cpu_to_le32(HTT_INVALID_PEERID); - memcpy(cmd->data_tx.prefetch, msdu->data, prefetch_len); - - /* refcount is decremented by HTC and HTT completions until it reaches - * zero and is freed */ - skb_cb = ATH10K_SKB_CB(txdesc); - skb_cb->htt.msdu_id = msdu_id; - skb_cb->htt.refcount = 2; - skb_cb->htt.txfrag = txfrag; - skb_cb->htt.msdu = msdu; + memcpy(cmd->data_tx.prefetch, hdr, prefetch_len); res = ath10k_htc_send(&htt->ar->htc, htt->eid, txdesc); if (res) - goto err; + goto err_unmap_msdu; return 0; -err: - if (txfrag) - ath10k_skb_unmap(dev, txfrag); - if (txdesc) - dev_kfree_skb_any(txdesc); - if (txfrag) - dev_kfree_skb_any(txfrag); - if (msdu_id >= 0) { - spin_lock_bh(&htt->tx_lock); - htt->pending_tx[msdu_id] = NULL; - ath10k_htt_tx_free_msdu_id(htt, msdu_id); - spin_unlock_bh(&htt->tx_lock); - } - ath10k_htt_tx_dec_pending(htt); + +err_unmap_msdu: ath10k_skb_unmap(dev, msdu); +err_pull_txfrag: + skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len); +err_free_txdesc: + dev_kfree_skb_any(txdesc); +err_free_msdu_id: + spin_lock_bh(&htt->tx_lock); + htt->pending_tx[msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, msdu_id); + spin_unlock_bh(&htt->tx_lock); +err_tx_dec: + ath10k_htt_tx_dec_pending(htt); +err: return res; } diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 44ed5af0a2043f19122685a2f0ff548bbc89feea..8aeb46d9b53409da2ced923d49780b763105a5d6 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -20,28 +20,37 @@ #include "targaddrs.h" -/* Supported FW version */ -#define SUPPORTED_FW_MAJOR 1 -#define SUPPORTED_FW_MINOR 0 -#define SUPPORTED_FW_RELEASE 0 -#define SUPPORTED_FW_BUILD 629 - -/* QCA988X 1.0 definitions */ -#define QCA988X_HW_1_0_VERSION 0x4000002c -#define QCA988X_HW_1_0_FW_DIR "ath10k/QCA988X/hw1.0" -#define QCA988X_HW_1_0_FW_FILE "firmware.bin" -#define QCA988X_HW_1_0_OTP_FILE "otp.bin" -#define QCA988X_HW_1_0_BOARD_DATA_FILE "board.bin" -#define QCA988X_HW_1_0_PATCH_LOAD_ADDR 0x1234 +/* QCA988X 1.0 definitions (unsupported) */ +#define QCA988X_HW_1_0_CHIP_ID_REV 0x0 /* QCA988X 2.0 definitions */ #define QCA988X_HW_2_0_VERSION 0x4100016c +#define QCA988X_HW_2_0_CHIP_ID_REV 0x2 #define QCA988X_HW_2_0_FW_DIR "ath10k/QCA988X/hw2.0" #define QCA988X_HW_2_0_FW_FILE "firmware.bin" #define QCA988X_HW_2_0_OTP_FILE "otp.bin" #define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234 +#define ATH10K_FW_API2_FILE "firmware-2.bin" + +/* includes also the null byte */ +#define ATH10K_FIRMWARE_MAGIC "QCA-ATH10K" + +struct ath10k_fw_ie { + __le32 id; + __le32 len; + u8 data[0]; +}; + +enum ath10k_fw_ie_type { + ATH10K_FW_IE_FW_VERSION = 0, + ATH10K_FW_IE_TIMESTAMP = 1, + ATH10K_FW_IE_FEATURES = 2, + ATH10K_FW_IE_FW_IMAGE = 3, + ATH10K_FW_IE_OTP_IMAGE = 4, +}; + /* Known pecularities: * - current FW doesn't support raw rx mode (last tested v599) * - current FW dumps upon raw tx mode (last tested v599) @@ -53,6 +62,9 @@ enum ath10k_hw_txrx_mode { ATH10K_HW_TXRX_RAW = 0, ATH10K_HW_TXRX_NATIVE_WIFI = 1, ATH10K_HW_TXRX_ETHERNET = 2, + + /* Valid for HTT >= 3.0. Used for management frames in TX_FRM. */ + ATH10K_HW_TXRX_MGMT = 3, }; enum ath10k_mcast2ucast_mode { @@ -60,6 +72,7 @@ enum ath10k_mcast2ucast_mode { ATH10K_MCAST2UCAST_ENABLED = 1, }; +/* Target specific defines for MAIN firmware */ #define TARGET_NUM_VDEVS 8 #define TARGET_NUM_PEER_AST 2 #define TARGET_NUM_WDS_ENTRIES 32 @@ -75,7 +88,11 @@ enum ath10k_mcast2ucast_mode { #define TARGET_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) #define TARGET_RX_TIMEOUT_LO_PRI 100 #define TARGET_RX_TIMEOUT_HI_PRI 40 -#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_ETHERNET + +/* Native Wifi decap mode is used to align IP frames to 4-byte boundaries and + * avoid a very expensive re-alignment in mac80211. */ +#define TARGET_RX_DECAP_MODE ATH10K_HW_TXRX_NATIVE_WIFI + #define TARGET_SCAN_MAX_PENDING_REQS 4 #define TARGET_BMISS_OFFLOAD_MAX_VDEV 3 #define TARGET_ROAM_OFFLOAD_MAX_VDEV 3 @@ -90,6 +107,36 @@ enum ath10k_mcast2ucast_mode { #define TARGET_NUM_MSDU_DESC (1024 + 400) #define TARGET_MAX_FRAG_ENTRIES 0 +/* Target specific defines for 10.X firmware */ +#define TARGET_10X_NUM_VDEVS 16 +#define TARGET_10X_NUM_PEER_AST 2 +#define TARGET_10X_NUM_WDS_ENTRIES 32 +#define TARGET_10X_DMA_BURST_SIZE 0 +#define TARGET_10X_MAC_AGGR_DELIM 0 +#define TARGET_10X_AST_SKID_LIMIT 16 +#define TARGET_10X_NUM_PEERS (128 + (TARGET_10X_NUM_VDEVS)) +#define TARGET_10X_NUM_OFFLOAD_PEERS 0 +#define TARGET_10X_NUM_OFFLOAD_REORDER_BUFS 0 +#define TARGET_10X_NUM_PEER_KEYS 2 +#define TARGET_10X_NUM_TIDS 256 +#define TARGET_10X_TX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_10X_RX_CHAIN_MASK (BIT(0) | BIT(1) | BIT(2)) +#define TARGET_10X_RX_TIMEOUT_LO_PRI 100 +#define TARGET_10X_RX_TIMEOUT_HI_PRI 40 +#define TARGET_10X_RX_DECAP_MODE ATH10K_HW_TXRX_NATIVE_WIFI +#define TARGET_10X_SCAN_MAX_PENDING_REQS 4 +#define TARGET_10X_BMISS_OFFLOAD_MAX_VDEV 2 +#define TARGET_10X_ROAM_OFFLOAD_MAX_VDEV 2 +#define TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES 8 +#define TARGET_10X_GTK_OFFLOAD_MAX_VDEV 3 +#define TARGET_10X_NUM_MCAST_GROUPS 0 +#define TARGET_10X_NUM_MCAST_TABLE_ELEMS 0 +#define TARGET_10X_MCAST2UCAST_MODE ATH10K_MCAST2UCAST_DISABLED +#define TARGET_10X_TX_DBG_LOG_SIZE 1024 +#define TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK 1 +#define TARGET_10X_VOW_CONFIG 0 +#define TARGET_10X_NUM_MSDU_DESC (1024 + 400) +#define TARGET_10X_MAX_FRAG_ENTRIES 0 /* Number of Copy Engines supported */ #define CE_COUNT 8 @@ -169,6 +216,10 @@ enum ath10k_mcast2ucast_mode { #define SOC_LPO_CAL_ENABLE_LSB 20 #define SOC_LPO_CAL_ENABLE_MASK 0x00100000 +#define SOC_CHIP_ID_ADDRESS 0x000000ec +#define SOC_CHIP_ID_REV_LSB 8 +#define SOC_CHIP_ID_REV_MASK 0x00000f00 + #define WLAN_RESET_CONTROL_COLD_RST_MASK 0x00000008 #define WLAN_RESET_CONTROL_WARM_RST_MASK 0x00000004 #define WLAN_SYSTEM_SLEEP_DISABLE_LSB 0 diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index cf2ba4d850c9bf0cedb8d123c0e05cccb821e6a2..0b1cc516e778c912e77d341a90f4921e5227a258 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -334,25 +334,29 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr) static int ath10k_mac_set_rts(struct ath10k_vif *arvif, u32 value) { + struct ath10k *ar = arvif->ar; + u32 vdev_param; + if (value != 0xFFFFFFFF) value = min_t(u32, arvif->ar->hw->wiphy->rts_threshold, ATH10K_RTS_MAX); - return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, - WMI_VDEV_PARAM_RTS_THRESHOLD, - value); + vdev_param = ar->wmi.vdev_param->rts_threshold; + return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value); } static int ath10k_mac_set_frag(struct ath10k_vif *arvif, u32 value) { + struct ath10k *ar = arvif->ar; + u32 vdev_param; + if (value != 0xFFFFFFFF) value = clamp_t(u32, arvif->ar->hw->wiphy->frag_threshold, ATH10K_FRAGMT_THRESHOLD_MIN, ATH10K_FRAGMT_THRESHOLD_MAX); - return ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, - WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, - value); + vdev_param = ar->wmi.vdev_param->fragmentation_threshold; + return ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, value); } static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr) @@ -460,6 +464,11 @@ static int ath10k_vdev_start(struct ath10k_vif *arvif) arg.ssid_len = arvif->vif->bss_conf.ssid_len; } + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d start center_freq %d phymode %s\n", + arg.vdev_id, arg.channel.freq, + ath10k_wmi_phymode_str(arg.channel.mode)); + ret = ath10k_wmi_vdev_start(ar, &arg); if (ret) { ath10k_warn("WMI vdev start failed: ret %d\n", ret); @@ -503,13 +512,10 @@ static int ath10k_monitor_start(struct ath10k *ar, int vdev_id) { struct ieee80211_channel *channel = ar->hw->conf.chandef.chan; struct wmi_vdev_start_request_arg arg = {}; - enum nl80211_channel_type type; int ret = 0; lockdep_assert_held(&ar->conf_mutex); - type = cfg80211_get_chandef_type(&ar->hw->conf.chandef); - arg.vdev_id = vdev_id; arg.channel.freq = channel->center_freq; arg.channel.band_center_freq1 = ar->hw->conf.chandef.center_freq1; @@ -560,12 +566,9 @@ static int ath10k_monitor_stop(struct ath10k *ar) lockdep_assert_held(&ar->conf_mutex); - /* For some reasons, ath10k_wmi_vdev_down() here couse - * often ath10k_wmi_vdev_stop() to fail. Next we could - * not run monitor vdev and driver reload - * required. Don't see such problems we skip - * ath10k_wmi_vdev_down() here. - */ + ret = ath10k_wmi_vdev_down(ar, ar->monitor_vdev_id); + if (ret) + ath10k_warn("Monitor vdev down failed: %d\n", ret); ret = ath10k_wmi_vdev_stop(ar, ar->monitor_vdev_id); if (ret) @@ -607,7 +610,7 @@ static int ath10k_monitor_create(struct ath10k *ar) goto vdev_fail; } - ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface created, vdev id: %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d created\n", ar->monitor_vdev_id); ar->monitor_present = true; @@ -639,7 +642,7 @@ static int ath10k_monitor_destroy(struct ath10k *ar) ar->free_vdev_map |= 1 << (ar->monitor_vdev_id); ar->monitor_present = false; - ath10k_dbg(ATH10K_DBG_MAC, "Monitor interface destroyed, vdev id: %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor vdev %d deleted\n", ar->monitor_vdev_id); return ret; } @@ -668,13 +671,14 @@ static void ath10k_control_beaconing(struct ath10k_vif *arvif, arvif->vdev_id); return; } - ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d up\n", arvif->vdev_id); + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d up\n", arvif->vdev_id); } static void ath10k_control_ibss(struct ath10k_vif *arvif, struct ieee80211_bss_conf *info, const u8 self_peer[ETH_ALEN]) { + u32 vdev_param; int ret = 0; lockdep_assert_held(&arvif->ar->conf_mutex); @@ -708,8 +712,8 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, return; } - ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, - WMI_VDEV_PARAM_ATIM_WINDOW, + vdev_param = arvif->ar->wmi.vdev_param->atim_window; + ret = ath10k_wmi_vdev_set_param(arvif->ar, arvif->vdev_id, vdev_param, ATH10K_DEFAULT_ATIM); if (ret) ath10k_warn("Failed to set IBSS ATIM for VDEV:%d ret:%d\n", @@ -719,47 +723,45 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, /* * Review this when mac80211 gains per-interface powersave support. */ -static void ath10k_ps_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) { - struct ath10k_generic_iter *ar_iter = data; - struct ieee80211_conf *conf = &ar_iter->ar->hw->conf; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct ath10k *ar = arvif->ar; + struct ieee80211_conf *conf = &ar->hw->conf; enum wmi_sta_powersave_param param; enum wmi_sta_ps_mode psmode; int ret; lockdep_assert_held(&arvif->ar->conf_mutex); - if (vif->type != NL80211_IFTYPE_STATION) - return; + if (arvif->vif->type != NL80211_IFTYPE_STATION) + return 0; if (conf->flags & IEEE80211_CONF_PS) { psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; - ret = ath10k_wmi_set_sta_ps_param(ar_iter->ar, - arvif->vdev_id, - param, + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, conf->dynamic_ps_timeout); if (ret) { ath10k_warn("Failed to set inactivity time for VDEV: %d\n", arvif->vdev_id); - return; + return ret; } - - ar_iter->ret = ret; } else { psmode = WMI_STA_PS_MODE_DISABLED; } - ar_iter->ret = ath10k_wmi_set_psmode(ar_iter->ar, arvif->vdev_id, - psmode); - if (ar_iter->ret) + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d psmode %s\n", + arvif->vdev_id, psmode ? "enable" : "disable"); + + ret = ath10k_wmi_set_psmode(ar, arvif->vdev_id, psmode); + if (ret) { ath10k_warn("Failed to set PS Mode: %d for VDEV: %d\n", psmode, arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, "Set PS Mode: %d for VDEV: %d\n", - psmode, arvif->vdev_id); + return ret; + } + + return 0; } /**********************/ @@ -949,7 +951,8 @@ static void ath10k_peer_assoc_h_ht(struct ath10k *ar, arg->peer_ht_rates.num_rates = n; arg->peer_num_spatial_streams = max((n+7) / 8, 1); - ath10k_dbg(ATH10K_DBG_MAC, "mcs cnt %d nss %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac ht peer %pM mcs cnt %d nss %d\n", + arg->addr, arg->peer_ht_rates.num_rates, arg->peer_num_spatial_streams); } @@ -969,11 +972,11 @@ static void ath10k_peer_assoc_h_qos_ap(struct ath10k *ar, arg->peer_flags |= WMI_PEER_QOS; if (sta->wme && sta->uapsd_queues) { - ath10k_dbg(ATH10K_DBG_MAC, "uapsd_queues: 0x%X, max_sp: %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac uapsd_queues 0x%x max_sp %d\n", sta->uapsd_queues, sta->max_sp); arg->peer_flags |= WMI_PEER_APSD; - arg->peer_flags |= WMI_RC_UAPSD_FLAG; + arg->peer_rate_caps |= WMI_RC_UAPSD_FLAG; if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) uapsd |= WMI_AP_PS_UAPSD_AC3_DELIVERY_EN | @@ -1028,14 +1031,27 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, struct wmi_peer_assoc_complete_arg *arg) { const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + u8 ampdu_factor; if (!vht_cap->vht_supported) return; arg->peer_flags |= WMI_PEER_VHT; - arg->peer_vht_caps = vht_cap->cap; + + ampdu_factor = (vht_cap->cap & + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >> + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + + /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to + * zero in VHT IE. Using it would result in degraded throughput. + * arg->peer_max_mpdu at this point contains HT max_mpdu so keep + * it if VHT max_mpdu is smaller. */ + arg->peer_max_mpdu = max(arg->peer_max_mpdu, + (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR + + ampdu_factor)) - 1); + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) arg->peer_flags |= WMI_PEER_80MHZ; @@ -1048,7 +1064,8 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, arg->peer_vht_rates.tx_mcs_set = __le16_to_cpu(vht_cap->vht_mcs.tx_mcs_map); - ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer\n"); + ath10k_dbg(ATH10K_DBG_MAC, "mac vht peer %pM max_mpdu %d flags 0x%x\n", + sta->addr, arg->peer_max_mpdu, arg->peer_flags); } static void ath10k_peer_assoc_h_qos(struct ath10k *ar, @@ -1076,8 +1093,6 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, { enum wmi_phy_mode phymode = MODE_UNKNOWN; - /* FIXME: add VHT */ - switch (ar->hw->conf.chandef.chan->band) { case IEEE80211_BAND_2GHZ: if (sta->ht_cap.ht_supported) { @@ -1091,7 +1106,17 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, break; case IEEE80211_BAND_5GHZ: - if (sta->ht_cap.ht_supported) { + /* + * Check VHT first. + */ + if (sta->vht_cap.vht_supported) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_80) + phymode = MODE_11AC_VHT80; + else if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11AC_VHT40; + else if (sta->bandwidth == IEEE80211_STA_RX_BW_20) + phymode = MODE_11AC_VHT20; + } else if (sta->ht_cap.ht_supported) { if (sta->bandwidth == IEEE80211_STA_RX_BW_40) phymode = MODE_11NA_HT40; else @@ -1105,30 +1130,32 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, break; } + ath10k_dbg(ATH10K_DBG_MAC, "mac peer %pM phymode %s\n", + sta->addr, ath10k_wmi_phymode_str(phymode)); + arg->peer_phymode = phymode; WARN_ON(phymode == MODE_UNKNOWN); } -static int ath10k_peer_assoc(struct ath10k *ar, - struct ath10k_vif *arvif, - struct ieee80211_sta *sta, - struct ieee80211_bss_conf *bss_conf) +static int ath10k_peer_assoc_prepare(struct ath10k *ar, + struct ath10k_vif *arvif, + struct ieee80211_sta *sta, + struct ieee80211_bss_conf *bss_conf, + struct wmi_peer_assoc_complete_arg *arg) { - struct wmi_peer_assoc_complete_arg arg; - lockdep_assert_held(&ar->conf_mutex); - memset(&arg, 0, sizeof(struct wmi_peer_assoc_complete_arg)); + memset(arg, 0, sizeof(*arg)); - ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, &arg); - ath10k_peer_assoc_h_crypto(ar, arvif, &arg); - ath10k_peer_assoc_h_rates(ar, sta, &arg); - ath10k_peer_assoc_h_ht(ar, sta, &arg); - ath10k_peer_assoc_h_vht(ar, sta, &arg); - ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, &arg); - ath10k_peer_assoc_h_phymode(ar, arvif, sta, &arg); + ath10k_peer_assoc_h_basic(ar, arvif, sta, bss_conf, arg); + ath10k_peer_assoc_h_crypto(ar, arvif, arg); + ath10k_peer_assoc_h_rates(ar, sta, arg); + ath10k_peer_assoc_h_ht(ar, sta, arg); + ath10k_peer_assoc_h_vht(ar, sta, arg); + ath10k_peer_assoc_h_qos(ar, arvif, sta, bss_conf, arg); + ath10k_peer_assoc_h_phymode(ar, arvif, sta, arg); - return ath10k_wmi_peer_assoc(ar, &arg); + return 0; } /* can be called only in mac80211 callbacks due to `key_count` usage */ @@ -1138,6 +1165,7 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, { struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct wmi_peer_assoc_complete_arg peer_arg; struct ieee80211_sta *ap_sta; int ret; @@ -1153,24 +1181,33 @@ static void ath10k_bss_assoc(struct ieee80211_hw *hw, return; } - ret = ath10k_peer_assoc(ar, arvif, ap_sta, bss_conf); + ret = ath10k_peer_assoc_prepare(ar, arvif, ap_sta, + bss_conf, &peer_arg); if (ret) { - ath10k_warn("Peer assoc failed for %pM\n", bss_conf->bssid); + ath10k_warn("Peer assoc prepare failed for %pM\n: %d", + bss_conf->bssid, ret); rcu_read_unlock(); return; } rcu_read_unlock(); + ret = ath10k_wmi_peer_assoc(ar, &peer_arg); + if (ret) { + ath10k_warn("Peer assoc failed for %pM\n: %d", + bss_conf->bssid, ret); + return; + } + + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d up (associated) bssid %pM aid %d\n", + arvif->vdev_id, bss_conf->bssid, bss_conf->aid); + ret = ath10k_wmi_vdev_up(ar, arvif->vdev_id, bss_conf->aid, bss_conf->bssid); if (ret) ath10k_warn("VDEV: %d up failed: ret %d\n", arvif->vdev_id, ret); - else - ath10k_dbg(ATH10K_DBG_MAC, - "VDEV: %d associated, BSSID: %pM, AID: %d\n", - arvif->vdev_id, bss_conf->bssid, bss_conf->aid); } /* @@ -1191,10 +1228,11 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, * No idea why this happens, even though VDEV-DOWN is supposed * to be analogous to link down, so just stop the VDEV. */ + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d stop (disassociated\n", + arvif->vdev_id); + + /* FIXME: check return value */ ret = ath10k_vdev_stop(arvif); - if (!ret) - ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d stopped\n", - arvif->vdev_id); /* * If we don't call VDEV-DOWN after VDEV-STOP FW will remain active and @@ -1203,26 +1241,33 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, * interfaces as it expects there is no rx when no interface is * running. */ - ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); - if (ret) - ath10k_dbg(ATH10K_DBG_MAC, "VDEV: %d ath10k_wmi_vdev_down failed (%d)\n", - arvif->vdev_id, ret); + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d down\n", arvif->vdev_id); - ath10k_wmi_flush_tx(ar); + /* FIXME: why don't we print error if wmi call fails? */ + ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id); - arvif->def_wep_key_index = 0; + arvif->def_wep_key_idx = 0; } static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif, struct ieee80211_sta *sta) { + struct wmi_peer_assoc_complete_arg peer_arg; int ret = 0; lockdep_assert_held(&ar->conf_mutex); - ret = ath10k_peer_assoc(ar, arvif, sta, NULL); + ret = ath10k_peer_assoc_prepare(ar, arvif, sta, NULL, &peer_arg); + if (ret) { + ath10k_warn("WMI peer assoc prepare failed for %pM\n", + sta->addr); + return ret; + } + + ret = ath10k_wmi_peer_assoc(ar, &peer_arg); if (ret) { - ath10k_warn("WMI peer assoc failed for %pM\n", sta->addr); + ath10k_warn("Peer assoc failed for STA %pM\n: %d", + sta->addr, ret); return ret; } @@ -1333,8 +1378,8 @@ static int ath10k_update_channel_list(struct ath10k *ar) continue; ath10k_dbg(ATH10K_DBG_WMI, - "%s: [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", - __func__, ch - arg.channels, arg.n_channels, + "mac channel [%zd/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", + ch - arg.channels, arg.n_channels, ch->freq, ch->max_power, ch->max_reg_power, ch->max_antenna_gain, ch->mode); @@ -1391,6 +1436,33 @@ static void ath10k_reg_notifier(struct wiphy *wiphy, /* TX handlers */ /***************/ +static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr) +{ + if (ieee80211_is_mgmt(hdr->frame_control)) + return HTT_DATA_TX_EXT_TID_MGMT; + + if (!ieee80211_is_data_qos(hdr->frame_control)) + return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + + if (!is_unicast_ether_addr(ieee80211_get_DA(hdr))) + return HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; + + return ieee80211_get_qos_ctl(hdr)[0] & IEEE80211_QOS_CTL_TID_MASK; +} + +static u8 ath10k_tx_h_get_vdev_id(struct ath10k *ar, + struct ieee80211_tx_info *info) +{ + if (info->control.vif) + return ath10k_vif_to_arvif(info->control.vif)->vdev_id; + + if (ar->monitor_enabled) + return ar->monitor_vdev_id; + + ath10k_warn("could not resolve vdev id\n"); + return 0; +} + /* * Frames sent to the FW have to be in "Native Wifi" format. * Strip the QoS field from the 802.11 header. @@ -1411,6 +1483,30 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw, skb_pull(skb, IEEE80211_QOS_CTL_LEN); } +static void ath10k_tx_wep_key_work(struct work_struct *work) +{ + struct ath10k_vif *arvif = container_of(work, struct ath10k_vif, + wep_key_work); + int ret, keyidx = arvif->def_wep_key_newidx; + + if (arvif->def_wep_key_idx == keyidx) + return; + + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n", + arvif->vdev_id, keyidx); + + ret = ath10k_wmi_vdev_set_param(arvif->ar, + arvif->vdev_id, + arvif->ar->wmi.vdev_param->def_keyid, + keyidx); + if (ret) { + ath10k_warn("could not update wep keyidx (%d)\n", ret); + return; + } + + arvif->def_wep_key_idx = keyidx; +} + static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1419,11 +1515,6 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) struct ath10k *ar = arvif->ar; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; - int ret; - - /* TODO AP mode should be implemented */ - if (vif->type != NL80211_IFTYPE_STATION) - return; if (!ieee80211_has_protected(hdr->frame_control)) return; @@ -1435,20 +1526,14 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb) key->cipher != WLAN_CIPHER_SUITE_WEP104) return; - if (key->keyidx == arvif->def_wep_key_index) + if (key->keyidx == arvif->def_wep_key_idx) return; - ath10k_dbg(ATH10K_DBG_MAC, "new wep keyidx will be %d\n", key->keyidx); - - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_DEF_KEYID, - key->keyidx); - if (ret) { - ath10k_warn("could not update wep keyidx (%d)\n", ret); - return; - } - - arvif->def_wep_key_index = key->keyidx; + /* FIXME: Most likely a few frames will be TXed with an old key. Simply + * queueing frames until key index is updated is not an option because + * sk_buff may need more processing to be done, e.g. offchannel */ + arvif->def_wep_key_newidx = key->keyidx; + ieee80211_queue_work(ar->hw, &arvif->wep_key_work); } static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb) @@ -1478,19 +1563,42 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb) static void ath10k_tx_htt(struct ath10k *ar, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - int ret; + int ret = 0; - if (ieee80211_is_mgmt(hdr->frame_control)) - ret = ath10k_htt_mgmt_tx(&ar->htt, skb); - else if (ieee80211_is_nullfunc(hdr->frame_control)) + if (ar->htt.target_version_major >= 3) { + /* Since HTT 3.0 there is no separate mgmt tx command */ + ret = ath10k_htt_tx(&ar->htt, skb); + goto exit; + } + + if (ieee80211_is_mgmt(hdr->frame_control)) { + if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, + ar->fw_features)) { + if (skb_queue_len(&ar->wmi_mgmt_tx_queue) >= + ATH10K_MAX_NUM_MGMT_PENDING) { + ath10k_warn("wmi mgmt_tx queue limit reached\n"); + ret = -EBUSY; + goto exit; + } + + skb_queue_tail(&ar->wmi_mgmt_tx_queue, skb); + ieee80211_queue_work(ar->hw, &ar->wmi_mgmt_tx_work); + } else { + ret = ath10k_htt_mgmt_tx(&ar->htt, skb); + } + } else if (!test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX, + ar->fw_features) && + ieee80211_is_nullfunc(hdr->frame_control)) { /* FW does not report tx status properly for NullFunc frames * unless they are sent through mgmt tx path. mac80211 sends - * those frames when it detects link/beacon loss and depends on - * the tx status to be correct. */ + * those frames when it detects link/beacon loss and depends + * on the tx status to be correct. */ ret = ath10k_htt_mgmt_tx(&ar->htt, skb); - else + } else { ret = ath10k_htt_tx(&ar->htt, skb); + } +exit: if (ret) { ath10k_warn("tx failed (%d). dropping packet.\n", ret); ieee80211_free_txskb(ar->hw, skb); @@ -1534,18 +1642,19 @@ void ath10k_offchan_tx_work(struct work_struct *work) mutex_lock(&ar->conf_mutex); - ath10k_dbg(ATH10K_DBG_MAC, "processing offchannel skb %p\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac offchannel skb %p\n", skb); hdr = (struct ieee80211_hdr *)skb->data; peer_addr = ieee80211_get_DA(hdr); - vdev_id = ATH10K_SKB_CB(skb)->htt.vdev_id; + vdev_id = ATH10K_SKB_CB(skb)->vdev_id; spin_lock_bh(&ar->data_lock); peer = ath10k_peer_find(ar, vdev_id, peer_addr); spin_unlock_bh(&ar->data_lock); if (peer) + /* FIXME: should this use ath10k_warn()? */ ath10k_dbg(ATH10K_DBG_MAC, "peer %pM on vdev %d already present\n", peer_addr, vdev_id); @@ -1580,6 +1689,36 @@ void ath10k_offchan_tx_work(struct work_struct *work) } } +void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar) +{ + struct sk_buff *skb; + + for (;;) { + skb = skb_dequeue(&ar->wmi_mgmt_tx_queue); + if (!skb) + break; + + ieee80211_free_txskb(ar->hw, skb); + } +} + +void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work) +{ + struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work); + struct sk_buff *skb; + int ret; + + for (;;) { + skb = skb_dequeue(&ar->wmi_mgmt_tx_queue); + if (!skb) + break; + + ret = ath10k_wmi_mgmt_tx(ar, skb); + if (ret) + ath10k_warn("wmi mgmt_tx failed (%d)\n", ret); + } +} + /************/ /* Scanning */ /************/ @@ -1643,8 +1782,6 @@ static int ath10k_abort_scan(struct ath10k *ar) return -EIO; } - ath10k_wmi_flush_tx(ar); - ret = wait_for_completion_timeout(&ar->scan.completed, 3*HZ); if (ret == 0) ath10k_warn("timed out while waiting for scan to stop\n"); @@ -1678,10 +1815,6 @@ static int ath10k_start_scan(struct ath10k *ar, if (ret) return ret; - /* make sure we submit the command so the completion - * timeout makes sense */ - ath10k_wmi_flush_tx(ar); - ret = wait_for_completion_timeout(&ar->scan.started, 1*HZ); if (ret == 0) { ath10k_abort_scan(ar); @@ -1709,16 +1842,7 @@ static void ath10k_tx(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif = NULL; - u32 vdev_id = 0; - u8 tid; - - if (info->control.vif) { - arvif = ath10k_vif_to_arvif(info->control.vif); - vdev_id = arvif->vdev_id; - } else if (ar->monitor_enabled) { - vdev_id = ar->monitor_vdev_id; - } + u8 tid, vdev_id; /* We should disable CCK RATE due to P2P */ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE) @@ -1726,12 +1850,8 @@ static void ath10k_tx(struct ieee80211_hw *hw, /* we must calculate tid before we apply qos workaround * as we'd lose the qos control field */ - tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; - if (ieee80211_is_data_qos(hdr->frame_control) && - is_unicast_ether_addr(ieee80211_get_DA(hdr))) { - u8 *qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - } + tid = ath10k_tx_h_get_tid(hdr); + vdev_id = ath10k_tx_h_get_vdev_id(ar, info); /* it makes no sense to process injected frames like that */ if (info->control.vif && @@ -1742,14 +1862,14 @@ static void ath10k_tx(struct ieee80211_hw *hw, ath10k_tx_h_seq_no(skb); } - memset(ATH10K_SKB_CB(skb), 0, sizeof(*ATH10K_SKB_CB(skb))); - ATH10K_SKB_CB(skb)->htt.vdev_id = vdev_id; + ATH10K_SKB_CB(skb)->vdev_id = vdev_id; + ATH10K_SKB_CB(skb)->htt.is_offchan = false; ATH10K_SKB_CB(skb)->htt.tid = tid; if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { spin_lock_bh(&ar->data_lock); ATH10K_SKB_CB(skb)->htt.is_offchan = true; - ATH10K_SKB_CB(skb)->htt.vdev_id = ar->scan.vdev_id; + ATH10K_SKB_CB(skb)->vdev_id = ar->scan.vdev_id; spin_unlock_bh(&ar->data_lock); ath10k_dbg(ATH10K_DBG_MAC, "queued offchannel skb %p\n", skb); @@ -1771,6 +1891,7 @@ void ath10k_halt(struct ath10k *ar) del_timer_sync(&ar->scan.timeout); ath10k_offchan_tx_purge(ar); + ath10k_mgmt_over_wmi_tx_purge(ar); ath10k_peer_cleanup_all(ar); ath10k_core_stop(ar); ath10k_hif_power_down(ar); @@ -1817,12 +1938,12 @@ static int ath10k_start(struct ieee80211_hw *hw) else if (ar->state == ATH10K_STATE_RESTARTING) ar->state = ATH10K_STATE_RESTARTED; - ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_PMF_QOS, 1); + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->pmf_qos, 1); if (ret) ath10k_warn("could not enable WMI_PDEV_PARAM_PMF_QOS (%d)\n", ret); - ret = ath10k_wmi_pdev_set_param(ar, WMI_PDEV_PARAM_DYNAMIC_BW, 0); + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->dynamic_bw, 0); if (ret) ath10k_warn("could not init WMI_PDEV_PARAM_DYNAMIC_BW (%d)\n", ret); @@ -1847,32 +1968,29 @@ static void ath10k_stop(struct ieee80211_hw *hw) ar->state = ATH10K_STATE_OFF; mutex_unlock(&ar->conf_mutex); + ath10k_mgmt_over_wmi_tx_purge(ar); + cancel_work_sync(&ar->offchan_tx_work); + cancel_work_sync(&ar->wmi_mgmt_tx_work); cancel_work_sync(&ar->restart_work); } -static void ath10k_config_ps(struct ath10k *ar) +static int ath10k_config_ps(struct ath10k *ar) { - struct ath10k_generic_iter ar_iter; + struct ath10k_vif *arvif; + int ret = 0; lockdep_assert_held(&ar->conf_mutex); - /* During HW reconfiguration mac80211 reports all interfaces that were - * running until reconfiguration was started. Since FW doesn't have any - * vdevs at this point we must not iterate over this interface list. - * This setting will be updated upon add_interface(). */ - if (ar->state == ATH10K_STATE_RESTARTED) - return; - - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; - - ieee80211_iterate_active_interfaces_atomic( - ar->hw, IEEE80211_IFACE_ITER_NORMAL, - ath10k_ps_iter, &ar_iter); + list_for_each_entry(arvif, &ar->arvifs, list) { + ret = ath10k_mac_vif_setup_ps(arvif); + if (ret) { + ath10k_warn("could not setup powersave (%d)\n", ret); + break; + } + } - if (ar_iter.ret) - ath10k_warn("failed to set ps config (%d)\n", ar_iter.ret); + return ret; } static int ath10k_config(struct ieee80211_hw *hw, u32 changed) @@ -1884,7 +2002,7 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&ar->conf_mutex); if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { - ath10k_dbg(ATH10K_DBG_MAC, "Config channel %d mhz\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac config channel %d mhz\n", conf->chandef.chan->center_freq); spin_lock_bh(&ar->data_lock); ar->rx_channel = conf->chandef.chan; @@ -1901,7 +2019,6 @@ static int ath10k_config(struct ieee80211_hw *hw, u32 changed) ret = ath10k_monitor_destroy(ar); } - ath10k_wmi_flush_tx(ar); mutex_unlock(&ar->conf_mutex); return ret; } @@ -1922,6 +2039,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, int ret = 0; u32 value; int bit; + u32 vdev_param; mutex_lock(&ar->conf_mutex); @@ -1930,21 +2048,22 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, arvif->ar = ar; arvif->vif = vif; + INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work); + if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) { ath10k_warn("Only one monitor interface allowed\n"); ret = -EBUSY; - goto exit; + goto err; } bit = ffs(ar->free_vdev_map); if (bit == 0) { ret = -EBUSY; - goto exit; + goto err; } arvif->vdev_id = bit - 1; arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; - ar->free_vdev_map &= ~(1 << arvif->vdev_id); if (ar->p2p) arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; @@ -1973,32 +2092,41 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, break; } - ath10k_dbg(ATH10K_DBG_MAC, "Add interface: id %d type %d subtype %d\n", + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev create %d (add interface) type %d subtype %d\n", arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype); ret = ath10k_wmi_vdev_create(ar, arvif->vdev_id, arvif->vdev_type, arvif->vdev_subtype, vif->addr); if (ret) { ath10k_warn("WMI vdev create failed: ret %d\n", ret); - goto exit; + goto err; } - ret = ath10k_wmi_vdev_set_param(ar, 0, WMI_VDEV_PARAM_DEF_KEYID, - arvif->def_wep_key_index); - if (ret) + ar->free_vdev_map &= ~BIT(arvif->vdev_id); + list_add(&arvif->list, &ar->arvifs); + + vdev_param = ar->wmi.vdev_param->def_keyid; + ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param, + arvif->def_wep_key_idx); + if (ret) { ath10k_warn("Failed to set default keyid: %d\n", ret); + goto err_vdev_delete; + } - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_TX_ENCAP_TYPE, + vdev_param = ar->wmi.vdev_param->tx_encap_type; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ATH10K_HW_TXRX_NATIVE_WIFI); - if (ret) + /* 10.X firmware does not support this VDEV parameter. Do not warn */ + if (ret && ret != -EOPNOTSUPP) { ath10k_warn("Failed to set TX encap: %d\n", ret); + goto err_vdev_delete; + } if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr); if (ret) { ath10k_warn("Failed to create peer for AP: %d\n", ret); - goto exit; + goto err_vdev_delete; } } @@ -2007,39 +2135,62 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, value = WMI_STA_PS_RX_WAKE_POLICY_WAKE; ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); - if (ret) + if (ret) { ath10k_warn("Failed to set RX wake policy: %d\n", ret); + goto err_peer_delete; + } param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); - if (ret) + if (ret) { ath10k_warn("Failed to set TX wake thresh: %d\n", ret); + goto err_peer_delete; + } param = WMI_STA_PS_PARAM_PSPOLL_COUNT; value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); - if (ret) + if (ret) { ath10k_warn("Failed to set PSPOLL count: %d\n", ret); + goto err_peer_delete; + } } ret = ath10k_mac_set_rts(arvif, ar->hw->wiphy->rts_threshold); - if (ret) + if (ret) { ath10k_warn("failed to set rts threshold for vdev %d (%d)\n", arvif->vdev_id, ret); + goto err_peer_delete; + } ret = ath10k_mac_set_frag(arvif, ar->hw->wiphy->frag_threshold); - if (ret) + if (ret) { ath10k_warn("failed to set frag threshold for vdev %d (%d)\n", arvif->vdev_id, ret); + goto err_peer_delete; + } if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) ar->monitor_present = true; -exit: mutex_unlock(&ar->conf_mutex); + return 0; + +err_peer_delete: + if (arvif->vdev_type == WMI_VDEV_TYPE_AP) + ath10k_wmi_peer_delete(ar, arvif->vdev_id, vif->addr); + +err_vdev_delete: + ath10k_wmi_vdev_delete(ar, arvif->vdev_id); + ar->free_vdev_map &= ~BIT(arvif->vdev_id); + list_del(&arvif->list); + +err: + mutex_unlock(&ar->conf_mutex); + return ret; } @@ -2052,9 +2203,17 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); - ath10k_dbg(ATH10K_DBG_MAC, "Remove interface: id %d\n", arvif->vdev_id); + cancel_work_sync(&arvif->wep_key_work); + + spin_lock_bh(&ar->data_lock); + if (arvif->beacon) { + dev_kfree_skb_any(arvif->beacon); + arvif->beacon = NULL; + } + spin_unlock_bh(&ar->data_lock); ar->free_vdev_map |= 1 << (arvif->vdev_id); + list_del(&arvif->list); if (arvif->vdev_type == WMI_VDEV_TYPE_AP) { ret = ath10k_peer_delete(arvif->ar, arvif->vdev_id, vif->addr); @@ -2064,6 +2223,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, kfree(arvif->u.ap.noa_data); } + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev delete %d (remove interface)\n", + arvif->vdev_id); + ret = ath10k_wmi_vdev_delete(ar, arvif->vdev_id); if (ret) ath10k_warn("WMI vdev delete failed: %d\n", ret); @@ -2105,18 +2267,20 @@ static void ath10k_configure_filter(struct ieee80211_hw *hw, if ((ar->filter_flags & FIF_PROMISC_IN_BSS) && !ar->monitor_enabled) { + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d start\n", + ar->monitor_vdev_id); + ret = ath10k_monitor_start(ar, ar->monitor_vdev_id); if (ret) ath10k_warn("Unable to start monitor mode\n"); - else - ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode started\n"); } else if (!(ar->filter_flags & FIF_PROMISC_IN_BSS) && ar->monitor_enabled) { + ath10k_dbg(ATH10K_DBG_MAC, "mac monitor %d stop\n", + ar->monitor_vdev_id); + ret = ath10k_monitor_stop(ar); if (ret) ath10k_warn("Unable to stop monitor mode\n"); - else - ath10k_dbg(ATH10K_DBG_MAC, "Monitor mode stopped\n"); } mutex_unlock(&ar->conf_mutex); @@ -2130,6 +2294,7 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); int ret = 0; + u32 vdev_param, pdev_param; mutex_lock(&ar->conf_mutex); @@ -2138,44 +2303,44 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BEACON_INT) { arvif->beacon_interval = info->beacon_int; - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_BEACON_INTERVAL, + vdev_param = ar->wmi.vdev_param->beacon_interval; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, arvif->beacon_interval); + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d beacon_interval %d\n", + arvif->vdev_id, arvif->beacon_interval); + if (ret) ath10k_warn("Failed to set beacon interval for VDEV: %d\n", arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Beacon interval: %d set for VDEV: %d\n", - arvif->beacon_interval, arvif->vdev_id); } if (changed & BSS_CHANGED_BEACON) { - ret = ath10k_wmi_pdev_set_param(ar, - WMI_PDEV_PARAM_BEACON_TX_MODE, + ath10k_dbg(ATH10K_DBG_MAC, + "vdev %d set beacon tx mode to staggered\n", + arvif->vdev_id); + + pdev_param = ar->wmi.pdev_param->beacon_tx_mode; + ret = ath10k_wmi_pdev_set_param(ar, pdev_param, WMI_BEACON_STAGGERED_MODE); if (ret) ath10k_warn("Failed to set beacon mode for VDEV: %d\n", arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set staggered beacon mode for VDEV: %d\n", - arvif->vdev_id); } if (changed & BSS_CHANGED_BEACON_INFO) { arvif->dtim_period = info->dtim_period; - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_DTIM_PERIOD, + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d dtim_period %d\n", + arvif->vdev_id, arvif->dtim_period); + + vdev_param = ar->wmi.vdev_param->dtim_period; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, arvif->dtim_period); if (ret) ath10k_warn("Failed to set dtim period for VDEV: %d\n", arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set dtim period: %d for VDEV: %d\n", - arvif->dtim_period, arvif->vdev_id); } if (changed & BSS_CHANGED_SSID && @@ -2188,16 +2353,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BSSID) { if (!is_zero_ether_addr(info->bssid)) { + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d create peer %pM\n", + arvif->vdev_id, info->bssid); + ret = ath10k_peer_create(ar, arvif->vdev_id, info->bssid); if (ret) ath10k_warn("Failed to add peer: %pM for VDEV: %d\n", info->bssid, arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Added peer: %pM for VDEV: %d\n", - info->bssid, arvif->vdev_id); - if (vif->type == NL80211_IFTYPE_STATION) { /* @@ -2207,11 +2371,12 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, memcpy(arvif->u.sta.bssid, info->bssid, ETH_ALEN); + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d start %pM\n", + arvif->vdev_id, info->bssid); + + /* FIXME: check return value */ ret = ath10k_vdev_start(arvif); - if (!ret) - ath10k_dbg(ATH10K_DBG_MAC, - "VDEV: %d started with BSSID: %pM\n", - arvif->vdev_id, info->bssid); } /* @@ -2235,16 +2400,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, else cts_prot = 0; - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_ENABLE_RTSCTS, + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d cts_prot %d\n", + arvif->vdev_id, cts_prot); + + vdev_param = ar->wmi.vdev_param->enable_rtscts; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, cts_prot); if (ret) ath10k_warn("Failed to set CTS prot for VDEV: %d\n", arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set CTS prot: %d for VDEV: %d\n", - cts_prot, arvif->vdev_id); } if (changed & BSS_CHANGED_ERP_SLOT) { @@ -2255,16 +2419,15 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, else slottime = WMI_VDEV_SLOT_TIME_LONG; /* 20us */ - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_SLOT_TIME, + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d slot_time %d\n", + arvif->vdev_id, slottime); + + vdev_param = ar->wmi.vdev_param->slot_time; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, slottime); if (ret) ath10k_warn("Failed to set erp slot for VDEV: %d\n", arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set slottime: %d for VDEV: %d\n", - slottime, arvif->vdev_id); } if (changed & BSS_CHANGED_ERP_PREAMBLE) { @@ -2274,16 +2437,16 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, else preamble = WMI_VDEV_PREAMBLE_LONG; - ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, - WMI_VDEV_PARAM_PREAMBLE, + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d preamble %dn", + arvif->vdev_id, preamble); + + vdev_param = ar->wmi.vdev_param->preamble; + ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, preamble); if (ret) ath10k_warn("Failed to set preamble for VDEV: %d\n", arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set preamble: %d for VDEV: %d\n", - preamble, arvif->vdev_id); } if (changed & BSS_CHANGED_ASSOC) { @@ -2474,27 +2637,26 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * New station addition. */ + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d peer create %pM (new sta)\n", + arvif->vdev_id, sta->addr); + ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr); if (ret) ath10k_warn("Failed to add peer: %pM for VDEV: %d\n", sta->addr, arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Added peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); } else if ((old_state == IEEE80211_STA_NONE && new_state == IEEE80211_STA_NOTEXIST)) { /* * Existing station deletion. */ + ath10k_dbg(ATH10K_DBG_MAC, + "mac vdev %d peer delete %pM (sta gone)\n", + arvif->vdev_id, sta->addr); ret = ath10k_peer_delete(ar, arvif->vdev_id, sta->addr); if (ret) ath10k_warn("Failed to delete peer: %pM for VDEV: %d\n", sta->addr, arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Removed peer: %pM for VDEV: %d\n", - sta->addr, arvif->vdev_id); if (vif->type == NL80211_IFTYPE_STATION) ath10k_bss_disassoc(hw, vif); @@ -2505,14 +2667,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * New association. */ + ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM associated\n", + sta->addr); + ret = ath10k_station_assoc(ar, arvif, sta); if (ret) ath10k_warn("Failed to associate station: %pM\n", sta->addr); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Station %pM moved to assoc state\n", - sta->addr); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTH && (vif->type == NL80211_IFTYPE_AP || @@ -2520,14 +2681,13 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, /* * Disassociation. */ + ath10k_dbg(ATH10K_DBG_MAC, "mac sta %pM disassociated\n", + sta->addr); + ret = ath10k_station_disassoc(ar, arvif, sta); if (ret) ath10k_warn("Failed to disassociate station: %pM\n", sta->addr); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Station %pM moved to disassociated state\n", - sta->addr); } mutex_unlock(&ar->conf_mutex); @@ -2732,88 +2892,51 @@ static int ath10k_cancel_remain_on_channel(struct ieee80211_hw *hw) * Both RTS and Fragmentation threshold are interface-specific * in ath10k, but device-specific in mac80211. */ -static void ath10k_set_rts_iter(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct ath10k_generic_iter *ar_iter = data; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - u32 rts = ar_iter->ar->hw->wiphy->rts_threshold; - - lockdep_assert_held(&arvif->ar->conf_mutex); - - /* During HW reconfiguration mac80211 reports all interfaces that were - * running until reconfiguration was started. Since FW doesn't have any - * vdevs at this point we must not iterate over this interface list. - * This setting will be updated upon add_interface(). */ - if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) - return; - - ar_iter->ret = ath10k_mac_set_rts(arvif, rts); - if (ar_iter->ret) - ath10k_warn("Failed to set RTS threshold for VDEV: %d\n", - arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set RTS threshold: %d for VDEV: %d\n", - rts, arvif->vdev_id); -} static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { - struct ath10k_generic_iter ar_iter; struct ath10k *ar = hw->priv; - - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; + struct ath10k_vif *arvif; + int ret = 0; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces_atomic( - hw, IEEE80211_IFACE_ITER_NORMAL, - ath10k_set_rts_iter, &ar_iter); - mutex_unlock(&ar->conf_mutex); - - return ar_iter.ret; -} + list_for_each_entry(arvif, &ar->arvifs, list) { + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d rts threshold %d\n", + arvif->vdev_id, value); -static void ath10k_set_frag_iter(void *data, u8 *mac, struct ieee80211_vif *vif) -{ - struct ath10k_generic_iter *ar_iter = data; - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - u32 frag = ar_iter->ar->hw->wiphy->frag_threshold; - - lockdep_assert_held(&arvif->ar->conf_mutex); - - /* During HW reconfiguration mac80211 reports all interfaces that were - * running until reconfiguration was started. Since FW doesn't have any - * vdevs at this point we must not iterate over this interface list. - * This setting will be updated upon add_interface(). */ - if (ar_iter->ar->state == ATH10K_STATE_RESTARTED) - return; + ret = ath10k_mac_set_rts(arvif, value); + if (ret) { + ath10k_warn("could not set rts threshold for vdev %d (%d)\n", + arvif->vdev_id, ret); + break; + } + } + mutex_unlock(&ar->conf_mutex); - ar_iter->ret = ath10k_mac_set_frag(arvif, frag); - if (ar_iter->ret) - ath10k_warn("Failed to set frag threshold for VDEV: %d\n", - arvif->vdev_id); - else - ath10k_dbg(ATH10K_DBG_MAC, - "Set frag threshold: %d for VDEV: %d\n", - frag, arvif->vdev_id); + return ret; } static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) { - struct ath10k_generic_iter ar_iter; struct ath10k *ar = hw->priv; - - memset(&ar_iter, 0, sizeof(struct ath10k_generic_iter)); - ar_iter.ar = ar; + struct ath10k_vif *arvif; + int ret = 0; mutex_lock(&ar->conf_mutex); - ieee80211_iterate_active_interfaces_atomic( - hw, IEEE80211_IFACE_ITER_NORMAL, - ath10k_set_frag_iter, &ar_iter); + list_for_each_entry(arvif, &ar->arvifs, list) { + ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n", + arvif->vdev_id, value); + + ret = ath10k_mac_set_rts(arvif, value); + if (ret) { + ath10k_warn("could not set fragmentation threshold for vdev %d (%d)\n", + arvif->vdev_id, ret); + break; + } + } mutex_unlock(&ar->conf_mutex); - return ar_iter.ret; + return ret; } static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) @@ -2836,8 +2959,7 @@ static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop) bool empty; spin_lock_bh(&ar->htt.tx_lock); - empty = bitmap_empty(ar->htt.used_msdu_ids, - ar->htt.max_num_pending_tx); + empty = (ar->htt.num_pending_tx == 0); spin_unlock_bh(&ar->htt.tx_lock); skip = (ar->state == ATH10K_STATE_WEDGED); @@ -3326,6 +3448,10 @@ int ath10k_mac_register(struct ath10k *ar) IEEE80211_HW_WANT_MONITOR_VIF | IEEE80211_HW_AP_LINK_PS; + /* MSDU can have HTT TX fragment pushed in front. The additional 4 + * bytes is used for padding/alignment if necessary. */ + ar->hw->extra_tx_headroom += sizeof(struct htt_data_tx_desc_frag)*2 + 4; + if (ar->ht_cap_info & WMI_HT_CAP_DYNAMIC_SMPS) ar->hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS; diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h index 6fce9bfb19a5f2340d2e41fe2f668b7e8c9f4dce..ba1021997b8fc8c9604884604c2facd57ab0a8d1 100644 --- a/drivers/net/wireless/ath/ath10k/mac.h +++ b/drivers/net/wireless/ath/ath10k/mac.h @@ -34,6 +34,8 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id); void ath10k_reset_scan(unsigned long ptr); void ath10k_offchan_tx_purge(struct ath10k *ar); void ath10k_offchan_tx_work(struct work_struct *work); +void ath10k_mgmt_over_wmi_tx_purge(struct ath10k *ar); +void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work); void ath10k_halt(struct ath10k *ar); static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index e2f9ef50b1bd3999f6b7cc2989c24e407e9bd72a..f8d59c7b90821a69b3401a7ec143bd6d8e0fc367 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -36,11 +36,9 @@ static unsigned int ath10k_target_ps; module_param(ath10k_target_ps, uint, 0644); MODULE_PARM_DESC(ath10k_target_ps, "Enable ath10k Target (SoC) PS option"); -#define QCA988X_1_0_DEVICE_ID (0xabcd) #define QCA988X_2_0_DEVICE_ID (0x003c) static DEFINE_PCI_DEVICE_TABLE(ath10k_pci_id_table) = { - { PCI_VDEVICE(ATHEROS, QCA988X_1_0_DEVICE_ID) }, /* PCI-E QCA988X V1 */ { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */ {0} }; @@ -50,9 +48,9 @@ static int ath10k_pci_diag_read_access(struct ath10k *ar, u32 address, static void ath10k_pci_process_ce(struct ath10k *ar); static int ath10k_pci_post_rx(struct ath10k *ar); -static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, +static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info, int num); -static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info); +static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info); static void ath10k_pci_stop_ce(struct ath10k *ar); static void ath10k_pci_device_reset(struct ath10k *ar); static int ath10k_pci_reset_target(struct ath10k *ar); @@ -60,43 +58,145 @@ static int ath10k_pci_start_intr(struct ath10k *ar); static void ath10k_pci_stop_intr(struct ath10k *ar); static const struct ce_attr host_ce_config_wlan[] = { - /* host->target HTC control and raw streams */ - { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,}, - /* could be moved to share CE3 */ - /* target->host HTT + HTC control */ - { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 512, 512, NULL,}, - /* target->host WMI */ - { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,}, - /* host->target WMI */ - { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,}, - /* host->target HTT */ - { /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, 0, - CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,}, - /* unused */ - { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,}, - /* Target autonomous hif_memcpy */ - { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,}, - /* ce_diag, the Diagnostic Window */ - { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,}, + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 512, + .dest_nentries = 512, + }, + + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 32, + }, + + /* CE3: host->target WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = CE_HTT_H2T_MSG_SRC_NENTRIES, + .src_sz_max = 256, + .dest_nentries = 0, + }, + + /* CE5: unused */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE6: target autonomous hif_memcpy */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + + /* CE7: ce_diag, the Diagnostic Window */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 2, + .src_sz_max = DIAG_TRANSFER_LIMIT, + .dest_nentries = 2, + }, }; /* Target firmware's Copy Engine configuration. */ static const struct ce_pipe_config target_ce_config_wlan[] = { - /* host->target HTC control and raw streams */ - { /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,}, - /* target->host HTT + HTC control */ - { /* CE1 */ 1, PIPEDIR_IN, 32, 512, CE_ATTR_FLAGS, 0,}, - /* target->host WMI */ - { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}, - /* host->target WMI */ - { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,}, - /* host->target HTT */ - { /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,}, + /* CE0: host->target HTC control and raw streams */ + { + .pipenum = 0, + .pipedir = PIPEDIR_OUT, + .nentries = 32, + .nbytes_max = 256, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE1: target->host HTT + HTC control */ + { + .pipenum = 1, + .pipedir = PIPEDIR_IN, + .nentries = 32, + .nbytes_max = 512, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE2: target->host WMI */ + { + .pipenum = 2, + .pipedir = PIPEDIR_IN, + .nentries = 32, + .nbytes_max = 2048, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE3: host->target WMI */ + { + .pipenum = 3, + .pipedir = PIPEDIR_OUT, + .nentries = 32, + .nbytes_max = 2048, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE4: host->target HTT */ + { + .pipenum = 4, + .pipedir = PIPEDIR_OUT, + .nentries = 256, + .nbytes_max = 256, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + /* NB: 50% of src nentries, since tx has 2 frags */ - /* unused */ - { /* CE5 */ 5, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,}, - /* Reserved for target autonomous hif_memcpy */ - { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,}, + + /* CE5: unused */ + { + .pipenum = 5, + .pipedir = PIPEDIR_OUT, + .nentries = 32, + .nbytes_max = 2048, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + + /* CE6: Reserved for target autonomous hif_memcpy */ + { + .pipenum = 6, + .pipedir = PIPEDIR_INOUT, + .nentries = 32, + .nbytes_max = 4096, + .flags = CE_ATTR_FLAGS, + .reserved = 0, + }, + /* CE7 used only by Host */ }; @@ -114,7 +214,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data, unsigned int completed_nbytes, orig_nbytes, remaining_bytes; unsigned int id; unsigned int flags; - struct ce_state *ce_diag; + struct ath10k_ce_pipe *ce_diag; /* Host buffer address in CE space */ u32 ce_data; dma_addr_t ce_data_base = 0; @@ -278,7 +378,7 @@ static int ath10k_pci_diag_write_mem(struct ath10k *ar, u32 address, unsigned int completed_nbytes, orig_nbytes, remaining_bytes; unsigned int id; unsigned int flags; - struct ce_state *ce_diag; + struct ath10k_ce_pipe *ce_diag; void *data_buf = NULL; u32 ce_data; /* Host buffer address in CE space */ dma_addr_t ce_data_base = 0; @@ -437,7 +537,7 @@ static void ath10k_pci_wait(struct ath10k *ar) ath10k_warn("Unable to wakeup target\n"); } -void ath10k_do_pci_wake(struct ath10k *ar) +int ath10k_do_pci_wake(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); void __iomem *pci_addr = ar_pci->mem; @@ -453,18 +553,19 @@ void ath10k_do_pci_wake(struct ath10k *ar) atomic_inc(&ar_pci->keep_awake_count); if (ar_pci->verified_awake) - return; + return 0; for (;;) { if (ath10k_pci_target_is_awake(ar)) { ar_pci->verified_awake = true; - break; + return 0; } if (tot_delay > PCIE_WAKE_TIMEOUT) { - ath10k_warn("target takes too long to wake up (awake count %d)\n", + ath10k_warn("target took longer %d us to wake up (awake count %d)\n", + PCIE_WAKE_TIMEOUT, atomic_read(&ar_pci->keep_awake_count)); - break; + return -ETIMEDOUT; } udelay(curr_delay); @@ -493,7 +594,7 @@ void ath10k_do_pci_sleep(struct ath10k *ar) * FIXME: Handle OOM properly. */ static inline -struct ath10k_pci_compl *get_free_compl(struct hif_ce_pipe_info *pipe_info) +struct ath10k_pci_compl *get_free_compl(struct ath10k_pci_pipe *pipe_info) { struct ath10k_pci_compl *compl = NULL; @@ -511,39 +612,28 @@ exit: } /* Called by lower (CE) layer when a send to Target completes. */ -static void ath10k_pci_ce_send_done(struct ce_state *ce_state, - void *transfer_context, - u32 ce_data, - unsigned int nbytes, - unsigned int transfer_id) +static void ath10k_pci_ce_send_done(struct ath10k_ce_pipe *ce_state) { struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info = &ar_pci->pipe_info[ce_state->id]; + struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id]; struct ath10k_pci_compl *compl; - bool process = false; - - do { - /* - * For the send completion of an item in sendlist, just - * increment num_sends_allowed. The upper layer callback will - * be triggered when last fragment is done with send. - */ - if (transfer_context == CE_SENDLIST_ITEM_CTXT) { - spin_lock_bh(&pipe_info->pipe_lock); - pipe_info->num_sends_allowed++; - spin_unlock_bh(&pipe_info->pipe_lock); - continue; - } + void *transfer_context; + u32 ce_data; + unsigned int nbytes; + unsigned int transfer_id; + while (ath10k_ce_completed_send_next(ce_state, &transfer_context, + &ce_data, &nbytes, + &transfer_id) == 0) { compl = get_free_compl(pipe_info); if (!compl) break; - compl->send_or_recv = HIF_CE_COMPLETE_SEND; + compl->state = ATH10K_PCI_COMPL_SEND; compl->ce_state = ce_state; compl->pipe_info = pipe_info; - compl->transfer_context = transfer_context; + compl->skb = transfer_context; compl->nbytes = nbytes; compl->transfer_id = transfer_id; compl->flags = 0; @@ -554,46 +644,36 @@ static void ath10k_pci_ce_send_done(struct ce_state *ce_state, spin_lock_bh(&ar_pci->compl_lock); list_add_tail(&compl->list, &ar_pci->compl_process); spin_unlock_bh(&ar_pci->compl_lock); - - process = true; - } while (ath10k_ce_completed_send_next(ce_state, - &transfer_context, - &ce_data, &nbytes, - &transfer_id) == 0); - - /* - * If only some of the items within a sendlist have completed, - * don't invoke completion processing until the entire sendlist - * has been sent. - */ - if (!process) - return; + } ath10k_pci_process_ce(ar); } /* Called by lower (CE) layer when data is received from the Target. */ -static void ath10k_pci_ce_recv_data(struct ce_state *ce_state, - void *transfer_context, u32 ce_data, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags) +static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state) { struct ath10k *ar = ce_state->ar; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info = &ar_pci->pipe_info[ce_state->id]; + struct ath10k_pci_pipe *pipe_info = &ar_pci->pipe_info[ce_state->id]; struct ath10k_pci_compl *compl; struct sk_buff *skb; + void *transfer_context; + u32 ce_data; + unsigned int nbytes; + unsigned int transfer_id; + unsigned int flags; - do { + while (ath10k_ce_completed_recv_next(ce_state, &transfer_context, + &ce_data, &nbytes, &transfer_id, + &flags) == 0) { compl = get_free_compl(pipe_info); if (!compl) break; - compl->send_or_recv = HIF_CE_COMPLETE_RECV; + compl->state = ATH10K_PCI_COMPL_RECV; compl->ce_state = ce_state; compl->pipe_info = pipe_info; - compl->transfer_context = transfer_context; + compl->skb = transfer_context; compl->nbytes = nbytes; compl->transfer_id = transfer_id; compl->flags = flags; @@ -608,12 +688,7 @@ static void ath10k_pci_ce_recv_data(struct ce_state *ce_state, spin_lock_bh(&ar_pci->compl_lock); list_add_tail(&compl->list, &ar_pci->compl_process); spin_unlock_bh(&ar_pci->compl_lock); - - } while (ath10k_ce_completed_recv_next(ce_state, - &transfer_context, - &ce_data, &nbytes, - &transfer_id, - &flags) == 0); + } ath10k_pci_process_ce(ar); } @@ -625,15 +700,12 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id, { struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(nbuf); struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe_id]); - struct ce_state *ce_hdl = pipe_info->ce_hdl; - struct ce_sendlist sendlist; + struct ath10k_pci_pipe *pipe_info = &(ar_pci->pipe_info[pipe_id]); + struct ath10k_ce_pipe *ce_hdl = pipe_info->ce_hdl; unsigned int len; u32 flags = 0; int ret; - memset(&sendlist, 0, sizeof(struct ce_sendlist)); - len = min(bytes, nbuf->len); bytes -= len; @@ -648,19 +720,8 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id, "ath10k tx: data: ", nbuf->data, nbuf->len); - ath10k_ce_sendlist_buf_add(&sendlist, skb_cb->paddr, len, flags); - - /* Make sure we have resources to handle this request */ - spin_lock_bh(&pipe_info->pipe_lock); - if (!pipe_info->num_sends_allowed) { - ath10k_warn("Pipe: %d is full\n", pipe_id); - spin_unlock_bh(&pipe_info->pipe_lock); - return -ENOSR; - } - pipe_info->num_sends_allowed--; - spin_unlock_bh(&pipe_info->pipe_lock); - - ret = ath10k_ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id); + ret = ath10k_ce_send(ce_hdl, nbuf, skb_cb->paddr, len, transfer_id, + flags); if (ret) ath10k_warn("CE send failed: %p\n", nbuf); @@ -670,14 +731,7 @@ static int ath10k_pci_hif_send_head(struct ath10k *ar, u8 pipe_id, static u16 ath10k_pci_hif_get_free_queue_number(struct ath10k *ar, u8 pipe) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info = &(ar_pci->pipe_info[pipe]); - int ret; - - spin_lock_bh(&pipe_info->pipe_lock); - ret = pipe_info->num_sends_allowed; - spin_unlock_bh(&pipe_info->pipe_lock); - - return ret; + return ath10k_ce_num_free_src_entries(ar_pci->pipe_info[pipe].ce_hdl); } static void ath10k_pci_hif_dump_area(struct ath10k *ar) @@ -764,9 +818,9 @@ static void ath10k_pci_hif_set_callbacks(struct ath10k *ar, static int ath10k_pci_start_ce(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_diag = ar_pci->ce_diag; + struct ath10k_ce_pipe *ce_diag = ar_pci->ce_diag; const struct ce_attr *attr; - struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_pipe *pipe_info; struct ath10k_pci_compl *compl; int i, pipe_num, completions, disable_interrupts; @@ -792,7 +846,6 @@ static int ath10k_pci_start_ce(struct ath10k *ar) ath10k_pci_ce_send_done, disable_interrupts); completions += attr->src_nentries; - pipe_info->num_sends_allowed = attr->src_nentries - 1; } if (attr->dest_nentries) { @@ -805,15 +858,14 @@ static int ath10k_pci_start_ce(struct ath10k *ar) continue; for (i = 0; i < completions; i++) { - compl = kmalloc(sizeof(struct ath10k_pci_compl), - GFP_KERNEL); + compl = kmalloc(sizeof(*compl), GFP_KERNEL); if (!compl) { ath10k_warn("No memory for completion state\n"); ath10k_pci_stop_ce(ar); return -ENOMEM; } - compl->send_or_recv = HIF_CE_COMPLETE_FREE; + compl->state = ATH10K_PCI_COMPL_FREE; list_add_tail(&compl->list, &pipe_info->compl_free); } } @@ -840,7 +892,7 @@ static void ath10k_pci_stop_ce(struct ath10k *ar) * their associated resources */ spin_lock_bh(&ar_pci->compl_lock); list_for_each_entry(compl, &ar_pci->compl_process, list) { - skb = (struct sk_buff *)compl->transfer_context; + skb = compl->skb; ATH10K_SKB_CB(skb)->is_aborted = true; } spin_unlock_bh(&ar_pci->compl_lock); @@ -850,7 +902,7 @@ static void ath10k_pci_cleanup_ce(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_pci_compl *compl, *tmp; - struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_pipe *pipe_info; struct sk_buff *netbuf; int pipe_num; @@ -861,7 +913,7 @@ static void ath10k_pci_cleanup_ce(struct ath10k *ar) list_for_each_entry_safe(compl, tmp, &ar_pci->compl_process, list) { list_del(&compl->list); - netbuf = (struct sk_buff *)compl->transfer_context; + netbuf = compl->skb; dev_kfree_skb_any(netbuf); kfree(compl); } @@ -912,12 +964,14 @@ static void ath10k_pci_process_ce(struct ath10k *ar) list_del(&compl->list); spin_unlock_bh(&ar_pci->compl_lock); - if (compl->send_or_recv == HIF_CE_COMPLETE_SEND) { + switch (compl->state) { + case ATH10K_PCI_COMPL_SEND: cb->tx_completion(ar, - compl->transfer_context, + compl->skb, compl->transfer_id); send_done = 1; - } else { + break; + case ATH10K_PCI_COMPL_RECV: ret = ath10k_pci_post_rx_pipe(compl->pipe_info, 1); if (ret) { ath10k_warn("Unable to post recv buffer for pipe: %d\n", @@ -925,7 +979,7 @@ static void ath10k_pci_process_ce(struct ath10k *ar) break; } - skb = (struct sk_buff *)compl->transfer_context; + skb = compl->skb; nbytes = compl->nbytes; ath10k_dbg(ATH10K_DBG_PCI, @@ -944,16 +998,23 @@ static void ath10k_pci_process_ce(struct ath10k *ar) nbytes, skb->len + skb_tailroom(skb)); } + break; + case ATH10K_PCI_COMPL_FREE: + ath10k_warn("free completion cannot be processed\n"); + break; + default: + ath10k_warn("invalid completion state (%d)\n", + compl->state); + break; } - compl->send_or_recv = HIF_CE_COMPLETE_FREE; + compl->state = ATH10K_PCI_COMPL_FREE; /* * Add completion back to the pipe's free list. */ spin_lock_bh(&compl->pipe_info->pipe_lock); list_add_tail(&compl->list, &compl->pipe_info->compl_free); - compl->pipe_info->num_sends_allowed += send_done; spin_unlock_bh(&compl->pipe_info->pipe_lock); } @@ -1037,12 +1098,12 @@ static void ath10k_pci_hif_get_default_pipe(struct ath10k *ar, &dl_is_polled); } -static int ath10k_pci_post_rx_pipe(struct hif_ce_pipe_info *pipe_info, +static int ath10k_pci_post_rx_pipe(struct ath10k_pci_pipe *pipe_info, int num) { struct ath10k *ar = pipe_info->hif_ce_state; struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_state = pipe_info->ce_hdl; + struct ath10k_ce_pipe *ce_state = pipe_info->ce_hdl; struct sk_buff *skb; dma_addr_t ce_data; int i, ret = 0; @@ -1097,7 +1158,7 @@ err: static int ath10k_pci_post_rx(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_pipe *pipe_info; const struct ce_attr *attr; int pipe_num, ret = 0; @@ -1147,11 +1208,11 @@ static int ath10k_pci_hif_start(struct ath10k *ar) return 0; } -static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) +static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) { struct ath10k *ar; struct ath10k_pci *ar_pci; - struct ce_state *ce_hdl; + struct ath10k_ce_pipe *ce_hdl; u32 buf_sz; struct sk_buff *netbuf; u32 ce_data; @@ -1179,11 +1240,11 @@ static void ath10k_pci_rx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) } } -static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) +static void ath10k_pci_tx_pipe_cleanup(struct ath10k_pci_pipe *pipe_info) { struct ath10k *ar; struct ath10k_pci *ar_pci; - struct ce_state *ce_hdl; + struct ath10k_ce_pipe *ce_hdl; struct sk_buff *netbuf; u32 ce_data; unsigned int nbytes; @@ -1206,15 +1267,14 @@ static void ath10k_pci_tx_pipe_cleanup(struct hif_ce_pipe_info *pipe_info) while (ath10k_ce_cancel_send_next(ce_hdl, (void **)&netbuf, &ce_data, &nbytes, &id) == 0) { - if (netbuf != CE_SENDLIST_ITEM_CTXT) - /* - * Indicate the completion to higer layer to free - * the buffer - */ - ATH10K_SKB_CB(netbuf)->is_aborted = true; - ar_pci->msg_callbacks_current.tx_completion(ar, - netbuf, - id); + /* + * Indicate the completion to higer layer to free + * the buffer + */ + ATH10K_SKB_CB(netbuf)->is_aborted = true; + ar_pci->msg_callbacks_current.tx_completion(ar, + netbuf, + id); } } @@ -1232,7 +1292,7 @@ static void ath10k_pci_buffer_cleanup(struct ath10k *ar) int pipe_num; for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { - struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_pipe *pipe_info; pipe_info = &ar_pci->pipe_info[pipe_num]; ath10k_pci_rx_pipe_cleanup(pipe_info); @@ -1243,7 +1303,7 @@ static void ath10k_pci_buffer_cleanup(struct ath10k *ar) static void ath10k_pci_ce_deinit(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_pipe *pipe_info; int pipe_num; for (pipe_num = 0; pipe_num < ar_pci->ce_count; pipe_num++) { @@ -1293,8 +1353,10 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, void *resp, u32 *resp_len) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct ce_state *ce_tx = ar_pci->pipe_info[BMI_CE_NUM_TO_TARG].ce_hdl; - struct ce_state *ce_rx = ar_pci->pipe_info[BMI_CE_NUM_TO_HOST].ce_hdl; + struct ath10k_pci_pipe *pci_tx = &ar_pci->pipe_info[BMI_CE_NUM_TO_TARG]; + struct ath10k_pci_pipe *pci_rx = &ar_pci->pipe_info[BMI_CE_NUM_TO_HOST]; + struct ath10k_ce_pipe *ce_tx = pci_tx->ce_hdl; + struct ath10k_ce_pipe *ce_rx = pci_rx->ce_hdl; dma_addr_t req_paddr = 0; dma_addr_t resp_paddr = 0; struct bmi_xfer xfer = {}; @@ -1378,13 +1440,16 @@ err_dma: return ret; } -static void ath10k_pci_bmi_send_done(struct ce_state *ce_state, - void *transfer_context, - u32 data, - unsigned int nbytes, - unsigned int transfer_id) +static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state) { - struct bmi_xfer *xfer = transfer_context; + struct bmi_xfer *xfer; + u32 ce_data; + unsigned int nbytes; + unsigned int transfer_id; + + if (ath10k_ce_completed_send_next(ce_state, (void **)&xfer, &ce_data, + &nbytes, &transfer_id)) + return; if (xfer->wait_for_resp) return; @@ -1392,14 +1457,17 @@ static void ath10k_pci_bmi_send_done(struct ce_state *ce_state, complete(&xfer->done); } -static void ath10k_pci_bmi_recv_data(struct ce_state *ce_state, - void *transfer_context, - u32 data, - unsigned int nbytes, - unsigned int transfer_id, - unsigned int flags) +static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state) { - struct bmi_xfer *xfer = transfer_context; + struct bmi_xfer *xfer; + u32 ce_data; + unsigned int nbytes; + unsigned int transfer_id; + unsigned int flags; + + if (ath10k_ce_completed_recv_next(ce_state, (void **)&xfer, &ce_data, + &nbytes, &transfer_id, &flags)) + return; if (!xfer->wait_for_resp) { ath10k_warn("unexpected: BMI data received; ignoring\n"); @@ -1679,7 +1747,7 @@ static int ath10k_pci_init_config(struct ath10k *ar) static int ath10k_pci_ce_init(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - struct hif_ce_pipe_info *pipe_info; + struct ath10k_pci_pipe *pipe_info; const struct ce_attr *attr; int pipe_num; @@ -1895,7 +1963,7 @@ static const struct ath10k_hif_ops ath10k_pci_hif_ops = { static void ath10k_pci_ce_tasklet(unsigned long ptr) { - struct hif_ce_pipe_info *pipe = (struct hif_ce_pipe_info *)ptr; + struct ath10k_pci_pipe *pipe = (struct ath10k_pci_pipe *)ptr; struct ath10k_pci *ar_pci = pipe->ar_pci; ath10k_ce_per_engine_service(ar_pci->ar, pipe->pipe_num); @@ -2212,18 +2280,13 @@ static int ath10k_pci_reset_target(struct ath10k *ar) static void ath10k_pci_device_reset(struct ath10k *ar) { - struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - void __iomem *mem = ar_pci->mem; int i; u32 val; if (!SOC_GLOBAL_RESET_ADDRESS) return; - if (!mem) - return; - - ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, + ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_V_MASK); for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { if (ath10k_pci_target_is_awake(ar)) @@ -2232,12 +2295,12 @@ static void ath10k_pci_device_reset(struct ath10k *ar) } /* Put Target, including PCIe, into RESET. */ - val = ath10k_pci_reg_read32(mem, SOC_GLOBAL_RESET_ADDRESS); + val = ath10k_pci_reg_read32(ar, SOC_GLOBAL_RESET_ADDRESS); val |= 1; - ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val); + ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val); for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { - if (ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) & + if (ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) & RTC_STATE_COLD_RESET_MASK) break; msleep(1); @@ -2245,16 +2308,16 @@ static void ath10k_pci_device_reset(struct ath10k *ar) /* Pull Target, including PCIe, out of RESET. */ val &= ~1; - ath10k_pci_reg_write32(mem, SOC_GLOBAL_RESET_ADDRESS, val); + ath10k_pci_reg_write32(ar, SOC_GLOBAL_RESET_ADDRESS, val); for (i = 0; i < ATH_PCI_RESET_WAIT_MAX; i++) { - if (!(ath10k_pci_reg_read32(mem, RTC_STATE_ADDRESS) & + if (!(ath10k_pci_reg_read32(ar, RTC_STATE_ADDRESS) & RTC_STATE_COLD_RESET_MASK)) break; msleep(1); } - ath10k_pci_reg_write32(mem, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET); + ath10k_pci_reg_write32(ar, PCIE_SOC_WAKE_ADDRESS, PCIE_SOC_WAKE_RESET); } static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) @@ -2267,13 +2330,10 @@ static void ath10k_pci_dump_features(struct ath10k_pci *ar_pci) switch (i) { case ATH10K_PCI_FEATURE_MSI_X: - ath10k_dbg(ATH10K_DBG_PCI, "device supports MSI-X\n"); - break; - case ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND: - ath10k_dbg(ATH10K_DBG_PCI, "QCA988X_1.0 workaround enabled\n"); + ath10k_dbg(ATH10K_DBG_BOOT, "device supports MSI-X\n"); break; case ATH10K_PCI_FEATURE_SOC_POWER_SAVE: - ath10k_dbg(ATH10K_DBG_PCI, "QCA98XX SoC power save enabled\n"); + ath10k_dbg(ATH10K_DBG_BOOT, "QCA98XX SoC power save enabled\n"); break; } } @@ -2286,7 +2346,7 @@ static int ath10k_pci_probe(struct pci_dev *pdev, int ret = 0; struct ath10k *ar; struct ath10k_pci *ar_pci; - u32 lcr_val; + u32 lcr_val, chip_id; ath10k_dbg(ATH10K_DBG_PCI, "%s\n", __func__); @@ -2298,9 +2358,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev, ar_pci->dev = &pdev->dev; switch (pci_dev->device) { - case QCA988X_1_0_DEVICE_ID: - set_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features); - break; case QCA988X_2_0_DEVICE_ID: set_bit(ATH10K_PCI_FEATURE_MSI_X, ar_pci->features); break; @@ -2322,10 +2379,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_ar_pci; } - /* Enable QCA988X_1.0 HW workarounds */ - if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) - spin_lock_init(&ar_pci->hw_v1_workaround_lock); - ar_pci->ar = ar; ar_pci->fw_indicator_address = FW_INDICATOR_ADDRESS; atomic_set(&ar_pci->keep_awake_count, 0); @@ -2395,9 +2448,20 @@ static int ath10k_pci_probe(struct pci_dev *pdev, spin_lock_init(&ar_pci->ce_lock); - ar_pci->cacheline_sz = dma_get_cache_alignment(); + ret = ath10k_do_pci_wake(ar); + if (ret) { + ath10k_err("Failed to get chip id: %d\n", ret); + return ret; + } + + chip_id = ath10k_pci_read32(ar, + RTC_SOC_BASE_ADDRESS + SOC_CHIP_ID_ADDRESS); + + ath10k_do_pci_sleep(ar); + + ath10k_dbg(ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem); - ret = ath10k_core_register(ar); + ret = ath10k_core_register(ar, chip_id); if (ret) { ath10k_err("could not register driver core (%d)\n", ret); goto err_iomap; @@ -2414,7 +2478,6 @@ err_region: err_device: pci_disable_device(pdev); err_ar: - pci_set_drvdata(pdev, NULL); ath10k_core_destroy(ar); err_ar_pci: /* call HIF PCI free here */ @@ -2442,7 +2505,6 @@ static void ath10k_pci_remove(struct pci_dev *pdev) ath10k_core_unregister(ar); - pci_set_drvdata(pdev, NULL); pci_iounmap(pdev, ar_pci->mem); pci_release_region(pdev, BAR_NUM); pci_clear_master(pdev); @@ -2483,9 +2545,6 @@ module_exit(ath10k_pci_exit); MODULE_AUTHOR("Qualcomm Atheros"); MODULE_DESCRIPTION("Driver support for Atheros QCA988X PCIe devices"); MODULE_LICENSE("Dual BSD/GPL"); -MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_FW_FILE); -MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_OTP_FILE); -MODULE_FIRMWARE(QCA988X_HW_1_0_FW_DIR "/" QCA988X_HW_1_0_BOARD_DATA_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_FW_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_OTP_FILE); MODULE_FIRMWARE(QCA988X_HW_2_0_FW_DIR "/" QCA988X_HW_2_0_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index 871bb339d56dc3fbb19d475ed50ea938c59fefa2..52fb7b9735714ea3f2dbf3ed03936ac3ae11f5c7 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -43,22 +43,23 @@ struct bmi_xfer { u32 resp_len; }; +enum ath10k_pci_compl_state { + ATH10K_PCI_COMPL_FREE = 0, + ATH10K_PCI_COMPL_SEND, + ATH10K_PCI_COMPL_RECV, +}; + struct ath10k_pci_compl { struct list_head list; - int send_or_recv; - struct ce_state *ce_state; - struct hif_ce_pipe_info *pipe_info; - void *transfer_context; + enum ath10k_pci_compl_state state; + struct ath10k_ce_pipe *ce_state; + struct ath10k_pci_pipe *pipe_info; + struct sk_buff *skb; unsigned int nbytes; unsigned int transfer_id; unsigned int flags; }; -/* compl_state.send_or_recv */ -#define HIF_CE_COMPLETE_FREE 0 -#define HIF_CE_COMPLETE_SEND 1 -#define HIF_CE_COMPLETE_RECV 2 - /* * PCI-specific Target state * @@ -152,17 +153,16 @@ struct service_to_pipe { enum ath10k_pci_features { ATH10K_PCI_FEATURE_MSI_X = 0, - ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND = 1, - ATH10K_PCI_FEATURE_SOC_POWER_SAVE = 2, + ATH10K_PCI_FEATURE_SOC_POWER_SAVE = 1, /* keep last */ ATH10K_PCI_FEATURE_COUNT }; /* Per-pipe state. */ -struct hif_ce_pipe_info { +struct ath10k_pci_pipe { /* Handle of underlying Copy Engine */ - struct ce_state *ce_hdl; + struct ath10k_ce_pipe *ce_hdl; /* Our pipe number; facilitiates use of pipe_info ptrs. */ u8 pipe_num; @@ -178,9 +178,6 @@ struct hif_ce_pipe_info { /* List of free CE completion slots */ struct list_head compl_free; - /* Limit the number of outstanding send requests. */ - int num_sends_allowed; - struct ath10k_pci *ar_pci; struct tasklet_struct intr; }; @@ -190,7 +187,6 @@ struct ath10k_pci { struct device *dev; struct ath10k *ar; void __iomem *mem; - int cacheline_sz; DECLARE_BITMAP(features, ATH10K_PCI_FEATURE_COUNT); @@ -219,7 +215,7 @@ struct ath10k_pci { bool compl_processing; - struct hif_ce_pipe_info pipe_info[CE_COUNT_MAX]; + struct ath10k_pci_pipe pipe_info[CE_COUNT_MAX]; struct ath10k_hif_cb msg_callbacks_current; @@ -227,16 +223,13 @@ struct ath10k_pci { u32 fw_indicator_address; /* Copy Engine used for Diagnostic Accesses */ - struct ce_state *ce_diag; + struct ath10k_ce_pipe *ce_diag; /* FIXME: document what this really protects */ spinlock_t ce_lock; /* Map CE id to ce_state */ - struct ce_state *ce_id_to_state[CE_COUNT_MAX]; - - /* makes sure that dummy reads are atomic */ - spinlock_t hw_v1_workaround_lock; + struct ath10k_ce_pipe ce_states[CE_COUNT_MAX]; }; static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) @@ -244,14 +237,18 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) return ar->hif.priv; } -static inline u32 ath10k_pci_reg_read32(void __iomem *mem, u32 addr) +static inline u32 ath10k_pci_reg_read32(struct ath10k *ar, u32 addr) { - return ioread32(mem + PCIE_LOCAL_BASE_ADDRESS + addr); + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + return ioread32(ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); } -static inline void ath10k_pci_reg_write32(void __iomem *mem, u32 addr, u32 val) +static inline void ath10k_pci_reg_write32(struct ath10k *ar, u32 addr, u32 val) { - iowrite32(val, mem + PCIE_LOCAL_BASE_ADDRESS + addr); + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + iowrite32(val, ar_pci->mem + PCIE_LOCAL_BASE_ADDRESS + addr); } #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */ @@ -310,23 +307,8 @@ static inline void ath10k_pci_write32(struct ath10k *ar, u32 offset, u32 value) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - void __iomem *addr = ar_pci->mem; - - if (test_bit(ATH10K_PCI_FEATURE_HW_1_0_WORKAROUND, ar_pci->features)) { - unsigned long irq_flags; - spin_lock_irqsave(&ar_pci->hw_v1_workaround_lock, irq_flags); - - ioread32(addr+offset+4); /* 3rd read prior to write */ - ioread32(addr+offset+4); /* 2nd read prior to write */ - ioread32(addr+offset+4); /* 1st read prior to write */ - iowrite32(value, addr+offset); - - spin_unlock_irqrestore(&ar_pci->hw_v1_workaround_lock, - irq_flags); - } else { - iowrite32(value, addr+offset); - } + iowrite32(value, ar_pci->mem + offset); } static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) @@ -336,15 +318,17 @@ static inline u32 ath10k_pci_read32(struct ath10k *ar, u32 offset) return ioread32(ar_pci->mem + offset); } -void ath10k_do_pci_wake(struct ath10k *ar); +int ath10k_do_pci_wake(struct ath10k *ar); void ath10k_do_pci_sleep(struct ath10k *ar); -static inline void ath10k_pci_wake(struct ath10k *ar) +static inline int ath10k_pci_wake(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); if (test_bit(ATH10K_PCI_FEATURE_SOC_POWER_SAVE, ar_pci->features)) - ath10k_do_pci_wake(ar); + return ath10k_do_pci_wake(ar); + + return 0; } static inline void ath10k_pci_sleep(struct ath10k *ar) diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h index bfec6c8f2ecb5aed3946ae23c6a93f81ef28f117..1c584c4b019cb7362bd611688fe57e01b3d38b02 100644 --- a/drivers/net/wireless/ath/ath10k/rx_desc.h +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -422,10 +422,30 @@ struct rx_mpdu_end { #define RX_MSDU_START_INFO1_IP_FRAG (1 << 14) #define RX_MSDU_START_INFO1_TCP_ONLY_ACK (1 << 15) +/* The decapped header (rx_hdr_status) contains the following: + * a) 802.11 header + * [padding to 4 bytes] + * b) HW crypto parameter + * - 0 bytes for no security + * - 4 bytes for WEP + * - 8 bytes for TKIP, AES + * [padding to 4 bytes] + * c) A-MSDU subframe header (14 bytes) if appliable + * d) LLC/SNAP (RFC1042, 8 bytes) + * + * In case of A-MSDU only first frame in sequence contains (a) and (b). */ enum rx_msdu_decap_format { - RX_MSDU_DECAP_RAW = 0, - RX_MSDU_DECAP_NATIVE_WIFI = 1, + RX_MSDU_DECAP_RAW = 0, + + /* Note: QoS frames are reported as non-QoS. The rx_hdr_status in + * htt_rx_desc contains the original decapped 802.11 header. */ + RX_MSDU_DECAP_NATIVE_WIFI = 1, + + /* Payload contains an ethernet header (struct ethhdr). */ RX_MSDU_DECAP_ETHERNET2_DIX = 2, + + /* Payload contains two 48-bit addresses and 2-byte length (14 bytes + * total), followed by an RFC1042 header (8 bytes). */ RX_MSDU_DECAP_8023_SNAP_LLC = 3 }; diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index 85e806bf7257ca231e21ace94a1abf8f9d83290a..90817ddc92ba70857ed9d3075c1e663ec75c59ce 100644 --- a/drivers/net/wireless/ath/ath10k/trace.h +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -111,26 +111,29 @@ TRACE_EVENT(ath10k_log_dbg_dump, ); TRACE_EVENT(ath10k_wmi_cmd, - TP_PROTO(int id, void *buf, size_t buf_len), + TP_PROTO(int id, void *buf, size_t buf_len, int ret), - TP_ARGS(id, buf, buf_len), + TP_ARGS(id, buf, buf_len, ret), TP_STRUCT__entry( __field(unsigned int, id) __field(size_t, buf_len) __dynamic_array(u8, buf, buf_len) + __field(int, ret) ), TP_fast_assign( __entry->id = id; __entry->buf_len = buf_len; + __entry->ret = ret; memcpy(__get_dynamic_array(buf), buf, buf_len); ), TP_printk( - "id %d len %zu", + "id %d len %zu ret %d", __entry->id, - __entry->buf_len + __entry->buf_len, + __entry->ret ) ); @@ -158,6 +161,27 @@ TRACE_EVENT(ath10k_wmi_event, ) ); +TRACE_EVENT(ath10k_htt_stats, + TP_PROTO(void *buf, size_t buf_len), + + TP_ARGS(buf, buf_len), + + TP_STRUCT__entry( + __field(size_t, buf_len) + __dynamic_array(u8, buf, buf_len) + ), + + TP_fast_assign( + __entry->buf_len = buf_len; + memcpy(__get_dynamic_array(buf), buf, buf_len); + ), + + TP_printk( + "len %zu", + __entry->buf_len + ) +); + #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ /* we don't want to use include/trace/events */ diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 68b6faefd1d883f89642775c03390b67a9f929ac..5ae373a1e2942fbcb3d185533d41eaf9dd8e24bd 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -44,40 +44,39 @@ out: spin_unlock_bh(&ar->data_lock); } -void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc) +void ath10k_txrx_tx_unref(struct ath10k_htt *htt, + const struct htt_tx_done *tx_done) { struct device *dev = htt->ar->dev; struct ieee80211_tx_info *info; - struct sk_buff *txfrag = ATH10K_SKB_CB(txdesc)->htt.txfrag; - struct sk_buff *msdu = ATH10K_SKB_CB(txdesc)->htt.msdu; + struct ath10k_skb_cb *skb_cb; + struct sk_buff *msdu; int ret; - if (ATH10K_SKB_CB(txdesc)->htt.refcount == 0) - return; - - ATH10K_SKB_CB(txdesc)->htt.refcount--; + ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n", + tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack); - if (ATH10K_SKB_CB(txdesc)->htt.refcount > 0) + if (tx_done->msdu_id >= htt->max_num_pending_tx) { + ath10k_warn("warning: msdu_id %d too big, ignoring\n", + tx_done->msdu_id); return; - - if (txfrag) { - ret = ath10k_skb_unmap(dev, txfrag); - if (ret) - ath10k_warn("txfrag unmap failed (%d)\n", ret); - - dev_kfree_skb_any(txfrag); } + msdu = htt->pending_tx[tx_done->msdu_id]; + skb_cb = ATH10K_SKB_CB(msdu); + ret = ath10k_skb_unmap(dev, msdu); if (ret) ath10k_warn("data skb unmap failed (%d)\n", ret); + if (skb_cb->htt.frag_len) + skb_pull(msdu, skb_cb->htt.frag_len + skb_cb->htt.pad_len); + ath10k_report_offchan_tx(htt->ar, msdu); info = IEEE80211_SKB_CB(msdu); - memset(&info->status, 0, sizeof(info->status)); - if (ATH10K_SKB_CB(txdesc)->htt.discard) { + if (tx_done->discard) { ieee80211_free_txskb(htt->ar->hw, msdu); goto exit; } @@ -85,7 +84,7 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc) if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) info->flags |= IEEE80211_TX_STAT_ACK; - if (ATH10K_SKB_CB(txdesc)->htt.no_ack) + if (tx_done->no_ack) info->flags &= ~IEEE80211_TX_STAT_ACK; ieee80211_tx_status(htt->ar->hw, msdu); @@ -93,36 +92,12 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc) exit: spin_lock_bh(&htt->tx_lock); - htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL; - ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id); + htt->pending_tx[tx_done->msdu_id] = NULL; + ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id); __ath10k_htt_tx_dec_pending(htt); - if (bitmap_empty(htt->used_msdu_ids, htt->max_num_pending_tx)) + if (htt->num_pending_tx == 0) wake_up(&htt->empty_tx_wq); spin_unlock_bh(&htt->tx_lock); - - dev_kfree_skb_any(txdesc); -} - -void ath10k_txrx_tx_completed(struct ath10k_htt *htt, - const struct htt_tx_done *tx_done) -{ - struct sk_buff *txdesc; - - ath10k_dbg(ATH10K_DBG_HTT, "htt tx completion msdu_id %u discard %d no_ack %d\n", - tx_done->msdu_id, !!tx_done->discard, !!tx_done->no_ack); - - if (tx_done->msdu_id >= htt->max_num_pending_tx) { - ath10k_warn("warning: msdu_id %d too big, ignoring\n", - tx_done->msdu_id); - return; - } - - txdesc = htt->pending_tx[tx_done->msdu_id]; - - ATH10K_SKB_CB(txdesc)->htt.discard = tx_done->discard; - ATH10K_SKB_CB(txdesc)->htt.no_ack = tx_done->no_ack; - - ath10k_txrx_tx_unref(htt, txdesc); } static const u8 rx_legacy_rate_idx[] = { @@ -293,6 +268,8 @@ void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info) status->vht_nss, status->freq, status->band); + ath10k_dbg_dump(ATH10K_DBG_HTT_DUMP, NULL, "rx skb: ", + info->skb->data, info->skb->len); ieee80211_rx(ar->hw, info->skb); } diff --git a/drivers/net/wireless/ath/ath10k/txrx.h b/drivers/net/wireless/ath/ath10k/txrx.h index e78632a76df7bd4451cadaada87740bd4bc79991..356dc9c04c9e3981feaeb04d8df4e8f45fc36f7f 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.h +++ b/drivers/net/wireless/ath/ath10k/txrx.h @@ -19,9 +19,8 @@ #include "htt.h" -void ath10k_txrx_tx_unref(struct ath10k_htt *htt, struct sk_buff *txdesc); -void ath10k_txrx_tx_completed(struct ath10k_htt *htt, - const struct htt_tx_done *tx_done); +void ath10k_txrx_tx_unref(struct ath10k_htt *htt, + const struct htt_tx_done *tx_done); void ath10k_process_rx(struct ath10k *ar, struct htt_rx_info *info); struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id, diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 55f90c761868ddd9fa228510efb27465d360c00b..ccf3597fd9e2511c11cce44e136c3d1c50c59d06 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -23,29 +23,470 @@ #include "wmi.h" #include "mac.h" -void ath10k_wmi_flush_tx(struct ath10k *ar) -{ - int ret; - - lockdep_assert_held(&ar->conf_mutex); - - if (ar->state == ATH10K_STATE_WEDGED) { - ath10k_warn("wmi flush skipped - device is wedged anyway\n"); - return; - } - - ret = wait_event_timeout(ar->wmi.wq, - atomic_read(&ar->wmi.pending_tx_count) == 0, - 5*HZ); - if (atomic_read(&ar->wmi.pending_tx_count) == 0) - return; - - if (ret == 0) - ret = -ETIMEDOUT; - - if (ret < 0) - ath10k_warn("wmi flush failed (%d)\n", ret); -} +/* MAIN WMI cmd track */ +static struct wmi_cmd_map wmi_cmd_map = { + .init_cmdid = WMI_INIT_CMDID, + .start_scan_cmdid = WMI_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_SCAN_SCH_PRIO_TBL_CMDID, + .pdev_set_regdomain_cmdid = WMI_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_dscp_tid_map_cmdid = WMI_PDEV_SET_DSCP_TID_MAP_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_BCN_TMPL_CMDID, + .bcn_filter_rx_cmdid = WMI_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_PRB_TMPL_CMDID, + .addba_clear_resp_cmdid = WMI_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = WMI_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_ROAM_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_P2P_SET_VENDOR_IE_DATA_CMDID, + .ap_ps_peer_param_cmdid = WMI_AP_PS_PEER_PARAM_CMDID, + .ap_ps_peer_uapsd_coex_cmdid = WMI_AP_PS_PEER_UAPSD_COEX_CMDID, + .peer_rate_retry_sched_cmdid = WMI_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = WMI_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = + WMI_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + .vdev_spectral_scan_enable_cmdid = WMI_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_SET_ARP_NS_OFFLOAD_CMDID, + .network_list_offload_config_cmdid = + WMI_NETWORK_LIST_OFFLOAD_CONFIG_CMDID, + .gtk_offload_cmdid = WMI_GTK_OFFLOAD_CMDID, + .csa_offload_enable_cmdid = WMI_CSA_OFFLOAD_ENABLE_CMDID, + .csa_offload_chanswitch_cmdid = WMI_CSA_OFFLOAD_CHANSWITCH_CMDID, + .chatter_set_mode_cmdid = WMI_CHATTER_SET_MODE_CMDID, + .peer_tid_addba_cmdid = WMI_PEER_TID_ADDBA_CMDID, + .peer_tid_delba_cmdid = WMI_PEER_TID_DELBA_CMDID, + .sta_dtim_ps_method_cmdid = WMI_STA_DTIM_PS_METHOD_CMDID, + .sta_uapsd_auto_trig_cmdid = WMI_STA_UAPSD_AUTO_TRIG_CMDID, + .sta_keepalive_cmd = WMI_STA_KEEPALIVE_CMD, + .echo_cmdid = WMI_ECHO_CMDID, + .pdev_utf_cmdid = WMI_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_PDEV_FTM_INTG_CMDID, + .vdev_set_keepalive_cmdid = WMI_VDEV_SET_KEEPALIVE_CMDID, + .vdev_get_keepalive_cmdid = WMI_VDEV_GET_KEEPALIVE_CMDID, + .force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID, + .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID, +}; + +/* 10.X WMI cmd track */ +static struct wmi_cmd_map wmi_10x_cmd_map = { + .init_cmdid = WMI_10X_INIT_CMDID, + .start_scan_cmdid = WMI_10X_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_10X_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_10X_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_set_regdomain_cmdid = WMI_10X_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_10X_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_10X_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_10X_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_10X_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_10X_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_10X_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_dscp_tid_map_cmdid = WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_10X_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_10X_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_10X_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_10X_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_10X_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_10X_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_10X_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_10X_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_10X_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_10X_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_10X_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_10X_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_10X_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_10X_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_10X_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_10X_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_10X_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_10X_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_10X_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_10X_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_10X_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .bcn_filter_rx_cmdid = WMI_10X_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_10X_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_10X_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .addba_clear_resp_cmdid = WMI_10X_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_10X_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_10X_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_10X_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_10X_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_10X_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_10X_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_10X_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_10X_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_10X_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_10X_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_10X_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_10X_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_10X_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = + WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_10X_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_10X_OFL_SCAN_ADD_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_10X_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_10X_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_10X_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_10X_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_10X_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED, + .ap_ps_peer_param_cmdid = WMI_CMD_UNSUPPORTED, + .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED, + .peer_rate_retry_sched_cmdid = WMI_10X_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_10X_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_10X_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_10X_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_10X_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_10X_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_10X_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = + WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_10X_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_10X_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = + WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + .vdev_spectral_scan_enable_cmdid = + WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_10X_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED, + .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED, + .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED, + .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED, + .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED, + .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED, + .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED, + .echo_cmdid = WMI_10X_ECHO_CMDID, + .pdev_utf_cmdid = WMI_10X_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_10X_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_10X_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, + .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID, +}; + +/* MAIN WMI VDEV param map */ +static struct wmi_vdev_param_map wmi_vdev_param_map = { + .rts_threshold = WMI_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_VDEV_PARAM_WDS, + .atim_window = WMI_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_VDEV_PARAM_BMISS_FIRST_BCNT, + .bmiss_final_bcnt = WMI_VDEV_PARAM_BMISS_FINAL_BCNT, + .feature_wmm = WMI_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_VDEV_PARAM_SGI, + .ldpc = WMI_VDEV_PARAM_LDPC, + .tx_stbc = WMI_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_VDEV_PARAM_DEF_KEYID, + .nss = WMI_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_VDEV_PARAM_UNSUPPORTED, + .enable_rtscts = WMI_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_VDEV_PARAM_TXBF, + .packet_powersave = WMI_VDEV_PARAM_PACKET_POWERSAVE, + .drop_unencry = WMI_VDEV_PARAM_DROP_UNENCRY, + .tx_encap_type = WMI_VDEV_PARAM_TX_ENCAP_TYPE, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_VDEV_PARAM_UNSUPPORTED, +}; + +/* 10.X WMI VDEV param map */ +static struct wmi_vdev_param_map wmi_10x_vdev_param_map = { + .rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_10X_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_10X_VDEV_PARAM_WDS, + .atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_10X_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_10X_VDEV_PARAM_SGI, + .ldpc = WMI_10X_VDEV_PARAM_LDPC, + .tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID, + .nss = WMI_10X_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET, + .enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_VDEV_PARAM_UNSUPPORTED, + .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED, + .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED, + .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, +}; + +static struct wmi_pdev_param_map wmi_pdev_param_map = { + .tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + .pcielp_txbuf_watermark = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + .pdev_stats_update_period = WMI_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = WMI_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_PDEV_PARAM_ARP_AC_OVERRIDE, + .arpdhcp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED, + .dcs = WMI_PDEV_PARAM_DCS, + .ani_enable = WMI_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_PDEV_PARAM_PROXY_STA, + .idle_ps_config = WMI_PDEV_PARAM_IDLE_PS_CONFIG, + .power_gating_sleep = WMI_PDEV_PARAM_POWER_GATING_SLEEP, + .fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED, + .burst_dur = WMI_PDEV_PARAM_UNSUPPORTED, + .burst_enable = WMI_PDEV_PARAM_UNSUPPORTED, +}; + +static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { + .tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED, + .pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = + WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_PDEV_PARAM_UNSUPPORTED, + .arpdhcp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, + .dcs = WMI_10X_PDEV_PARAM_DCS, + .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED, + .idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED, + .power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED, + .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, + .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR, + .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE, +}; int ath10k_wmi_wait_for_service_ready(struct ath10k *ar) { @@ -85,18 +526,14 @@ static struct sk_buff *ath10k_wmi_alloc_skb(u32 len) static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) { dev_kfree_skb(skb); - - if (atomic_sub_return(1, &ar->wmi.pending_tx_count) == 0) - wake_up(&ar->wmi.wq); } -/* WMI command API */ -static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, - enum wmi_cmd_id cmd_id) +static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, + u32 cmd_id) { struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); struct wmi_cmd_hdr *cmd_hdr; - int status; + int ret; u32 cmd = 0; if (skb_push(skb, sizeof(struct wmi_cmd_hdr)) == NULL) @@ -107,25 +544,146 @@ static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, cmd_hdr = (struct wmi_cmd_hdr *)skb->data; cmd_hdr->cmd_id = __cpu_to_le32(cmd); - if (atomic_add_return(1, &ar->wmi.pending_tx_count) > - WMI_MAX_PENDING_TX_COUNT) { - /* avoid using up memory when FW hangs */ - atomic_dec(&ar->wmi.pending_tx_count); - return -EBUSY; + memset(skb_cb, 0, sizeof(*skb_cb)); + ret = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb); + trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len, ret); + + if (ret) + goto err_pull; + + return 0; + +err_pull: + skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + return ret; +} + +static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif) +{ + struct wmi_bcn_tx_arg arg = {0}; + int ret; + + lockdep_assert_held(&arvif->ar->data_lock); + + if (arvif->beacon == NULL) + return; + + arg.vdev_id = arvif->vdev_id; + arg.tx_rate = 0; + arg.tx_power = 0; + arg.bcn = arvif->beacon->data; + arg.bcn_len = arvif->beacon->len; + + ret = ath10k_wmi_beacon_send_nowait(arvif->ar, &arg); + if (ret) + return; + + dev_kfree_skb_any(arvif->beacon); + arvif->beacon = NULL; +} + +static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + ath10k_wmi_tx_beacon_nowait(arvif); +} + +static void ath10k_wmi_tx_beacons_nowait(struct ath10k *ar) +{ + spin_lock_bh(&ar->data_lock); + ieee80211_iterate_active_interfaces_atomic(ar->hw, + IEEE80211_IFACE_ITER_NORMAL, + ath10k_wmi_tx_beacons_iter, + NULL); + spin_unlock_bh(&ar->data_lock); +} + +static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar) +{ + /* try to send pending beacons first. they take priority */ + ath10k_wmi_tx_beacons_nowait(ar); + + wake_up(&ar->wmi.tx_credits_wq); +} + +static int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, + u32 cmd_id) +{ + int ret = -EOPNOTSUPP; + + might_sleep(); + + if (cmd_id == WMI_CMD_UNSUPPORTED) { + ath10k_warn("wmi command %d is not supported by firmware\n", + cmd_id); + return ret; } - memset(skb_cb, 0, sizeof(*skb_cb)); + wait_event_timeout(ar->wmi.tx_credits_wq, ({ + /* try to send pending beacons first. they take priority */ + ath10k_wmi_tx_beacons_nowait(ar); - trace_ath10k_wmi_cmd(cmd_id, skb->data, skb->len); + ret = ath10k_wmi_cmd_send_nowait(ar, skb, cmd_id); + (ret != -EAGAIN); + }), 3*HZ); - status = ath10k_htc_send(&ar->htc, ar->wmi.eid, skb); - if (status) { + if (ret) dev_kfree_skb_any(skb); - atomic_dec(&ar->wmi.pending_tx_count); - return status; + + return ret; +} + +int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) +{ + int ret = 0; + struct wmi_mgmt_tx_cmd *cmd; + struct ieee80211_hdr *hdr; + struct sk_buff *wmi_skb; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + int len; + u16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control))) + return -EINVAL; + + len = sizeof(cmd->hdr) + skb->len; + len = round_up(len, 4); + + wmi_skb = ath10k_wmi_alloc_skb(len); + if (!wmi_skb) + return -ENOMEM; + + cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data; + + cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id); + cmd->hdr.tx_rate = 0; + cmd->hdr.tx_power = 0; + cmd->hdr.buf_len = __cpu_to_le32((u32)(skb->len)); + + memcpy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr), ETH_ALEN); + memcpy(cmd->buf, skb->data, skb->len); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n", + wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE, + fc & IEEE80211_FCTL_STYPE); + + /* Send the management frame buffer to the target */ + ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid); + if (ret) { + dev_kfree_skb_any(skb); + return ret; } - return 0; + /* TODO: report tx status to mac80211 - temporary just ACK */ + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status_irqsafe(ar->hw, skb); + + return ret; } static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) @@ -315,7 +873,9 @@ static inline u8 get_rate_idx(u32 rate, enum ieee80211_band band) static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) { - struct wmi_mgmt_rx_event *event = (struct wmi_mgmt_rx_event *)skb->data; + struct wmi_mgmt_rx_event_v1 *ev_v1; + struct wmi_mgmt_rx_event_v2 *ev_v2; + struct wmi_mgmt_rx_hdr_v1 *ev_hdr; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr; u32 rx_status; @@ -325,13 +885,24 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) u32 rate; u32 buf_len; u16 fc; + int pull_len; + + if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) { + ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data; + ev_hdr = &ev_v2->hdr.v1; + pull_len = sizeof(*ev_v2); + } else { + ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data; + ev_hdr = &ev_v1->hdr; + pull_len = sizeof(*ev_v1); + } - channel = __le32_to_cpu(event->hdr.channel); - buf_len = __le32_to_cpu(event->hdr.buf_len); - rx_status = __le32_to_cpu(event->hdr.status); - snr = __le32_to_cpu(event->hdr.snr); - phy_mode = __le32_to_cpu(event->hdr.phy_mode); - rate = __le32_to_cpu(event->hdr.rate); + channel = __le32_to_cpu(ev_hdr->channel); + buf_len = __le32_to_cpu(ev_hdr->buf_len); + rx_status = __le32_to_cpu(ev_hdr->status); + snr = __le32_to_cpu(ev_hdr->snr); + phy_mode = __le32_to_cpu(ev_hdr->phy_mode); + rate = __le32_to_cpu(ev_hdr->rate); memset(status, 0, sizeof(*status)); @@ -358,7 +929,7 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; status->rate_idx = get_rate_idx(rate, status->band); - skb_pull(skb, sizeof(event->hdr)); + skb_pull(skb, pull_len); hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); @@ -734,10 +1305,8 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) int i = -1; struct wmi_bcn_info *bcn_info; struct ath10k_vif *arvif; - struct wmi_bcn_tx_arg arg; struct sk_buff *bcn; int vdev_id = 0; - int ret; ath10k_dbg(ATH10K_DBG_MGMT, "WMI_HOST_SWBA_EVENTID\n"); @@ -794,17 +1363,17 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info); ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info); - arg.vdev_id = arvif->vdev_id; - arg.tx_rate = 0; - arg.tx_power = 0; - arg.bcn = bcn->data; - arg.bcn_len = bcn->len; + spin_lock_bh(&ar->data_lock); + if (arvif->beacon) { + ath10k_warn("SWBA overrun on vdev %d\n", + arvif->vdev_id); + dev_kfree_skb_any(arvif->beacon); + } - ret = ath10k_wmi_beacon_send(ar, &arg); - if (ret) - ath10k_warn("could not send beacon (%d)\n", ret); + arvif->beacon = bcn; - dev_kfree_skb_any(bcn); + ath10k_wmi_tx_beacon_nowait(arvif); + spin_unlock_bh(&ar->data_lock); } } @@ -919,6 +1488,55 @@ static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n"); } +static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n"); +} + +static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n"); +} + +static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, + struct sk_buff *skb) +{ + ath10k_dbg(ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); +} + +static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, + u32 num_units, u32 unit_len) +{ + dma_addr_t paddr; + u32 pool_size; + int idx = ar->wmi.num_mem_chunks; + + pool_size = num_units * round_up(unit_len, 4); + + if (!pool_size) + return -EINVAL; + + ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev, + pool_size, + &paddr, + GFP_ATOMIC); + if (!ar->wmi.mem_chunks[idx].vaddr) { + ath10k_warn("failed to allocate memory chunk\n"); + return -ENOMEM; + } + + memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size); + + ar->wmi.mem_chunks[idx].paddr = paddr; + ar->wmi.mem_chunks[idx].len = pool_size; + ar->wmi.mem_chunks[idx].req_id = req_id; + ar->wmi.num_mem_chunks++; + + return 0; +} + static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) { @@ -943,6 +1561,10 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, ar->phy_capability = __le32_to_cpu(ev->phy_capability); ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains); + /* only manually set fw features when not using FW IE format */ + if (ar->fw_api == 1 && ar->fw_version_build > 636) + set_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features); + if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) { ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n", ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM); @@ -987,6 +1609,108 @@ static void ath10k_wmi_service_ready_event_rx(struct ath10k *ar, complete(&ar->wmi.service_ready); } +static void ath10k_wmi_10x_service_ready_event_rx(struct ath10k *ar, + struct sk_buff *skb) +{ + u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; + int ret; + struct wmi_service_ready_event_10x *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) { + ath10k_warn("Service ready event was %d B but expected %zu B. Wrong firmware version?\n", + skb->len, sizeof(*ev)); + return; + } + + ar->hw_min_tx_power = __le32_to_cpu(ev->hw_min_tx_power); + ar->hw_max_tx_power = __le32_to_cpu(ev->hw_max_tx_power); + ar->ht_cap_info = __le32_to_cpu(ev->ht_cap_info); + ar->vht_cap_info = __le32_to_cpu(ev->vht_cap_info); + ar->fw_version_major = + (__le32_to_cpu(ev->sw_version) & 0xff000000) >> 24; + ar->fw_version_minor = (__le32_to_cpu(ev->sw_version) & 0x00ffffff); + ar->phy_capability = __le32_to_cpu(ev->phy_capability); + ar->num_rf_chains = __le32_to_cpu(ev->num_rf_chains); + + if (ar->num_rf_chains > WMI_MAX_SPATIAL_STREAM) { + ath10k_warn("hardware advertises support for more spatial streams than it should (%d > %d)\n", + ar->num_rf_chains, WMI_MAX_SPATIAL_STREAM); + ar->num_rf_chains = WMI_MAX_SPATIAL_STREAM; + } + + ar->ath_common.regulatory.current_rd = + __le32_to_cpu(ev->hal_reg_capabilities.eeprom_rd); + + ath10k_debug_read_service_map(ar, ev->wmi_service_bitmap, + sizeof(ev->wmi_service_bitmap)); + + if (strlen(ar->hw->wiphy->fw_version) == 0) { + snprintf(ar->hw->wiphy->fw_version, + sizeof(ar->hw->wiphy->fw_version), + "%u.%u", + ar->fw_version_major, + ar->fw_version_minor); + } + + num_mem_reqs = __le32_to_cpu(ev->num_mem_reqs); + + if (num_mem_reqs > ATH10K_MAX_MEM_REQS) { + ath10k_warn("requested memory chunks number (%d) exceeds the limit\n", + num_mem_reqs); + return; + } + + if (!num_mem_reqs) + goto exit; + + ath10k_dbg(ATH10K_DBG_WMI, "firmware has requested %d memory chunks\n", + num_mem_reqs); + + for (i = 0; i < num_mem_reqs; ++i) { + req_id = __le32_to_cpu(ev->mem_reqs[i].req_id); + num_units = __le32_to_cpu(ev->mem_reqs[i].num_units); + unit_size = __le32_to_cpu(ev->mem_reqs[i].unit_size); + num_unit_info = __le32_to_cpu(ev->mem_reqs[i].num_unit_info); + + if (num_unit_info & NUM_UNITS_IS_NUM_PEERS) + /* number of units to allocate is number of + * peers, 1 extra for self peer on target */ + /* this needs to be tied, host and target + * can get out of sync */ + num_units = TARGET_10X_NUM_PEERS + 1; + else if (num_unit_info & NUM_UNITS_IS_NUM_VDEVS) + num_units = TARGET_10X_NUM_VDEVS + 1; + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi mem_req_id %d num_units %d num_unit_info %d unit size %d actual units %d\n", + req_id, + __le32_to_cpu(ev->mem_reqs[i].num_units), + num_unit_info, + unit_size, + num_units); + + ret = ath10k_wmi_alloc_host_mem(ar, req_id, num_units, + unit_size); + if (ret) + return; + } + +exit: + ath10k_dbg(ATH10K_DBG_WMI, + "wmi event service ready sw_ver 0x%08x abi_ver %u phy_cap 0x%08x ht_cap 0x%08x vht_cap 0x%08x vht_supp_msc 0x%08x sys_cap_info 0x%08x mem_reqs %u num_rf_chains %u\n", + __le32_to_cpu(ev->sw_version), + __le32_to_cpu(ev->abi_version), + __le32_to_cpu(ev->phy_capability), + __le32_to_cpu(ev->ht_cap_info), + __le32_to_cpu(ev->vht_cap_info), + __le32_to_cpu(ev->vht_supp_mcs), + __le32_to_cpu(ev->sys_cap_info), + __le32_to_cpu(ev->num_mem_reqs), + __le32_to_cpu(ev->num_rf_chains)); + + complete(&ar->wmi.service_ready); +} + static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data; @@ -1007,7 +1731,7 @@ static int ath10k_wmi_ready_event_rx(struct ath10k *ar, struct sk_buff *skb) return 0; } -static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_event_id id; @@ -1126,64 +1850,158 @@ static void ath10k_wmi_event_process(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb(skb); } -static void ath10k_wmi_event_work(struct work_struct *work) +static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb) { - struct ath10k *ar = container_of(work, struct ath10k, - wmi.wmi_event_work); - struct sk_buff *skb; + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_10x_event_id id; + u16 len; - for (;;) { - skb = skb_dequeue(&ar->wmi.wmi_event_list); - if (!skb) - break; + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); - ath10k_wmi_event_process(ar, skb); - } -} + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return; -static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) -{ - struct wmi_cmd_hdr *cmd_hdr = (struct wmi_cmd_hdr *)skb->data; - enum wmi_event_id event_id; + len = skb->len; - event_id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + trace_ath10k_wmi_event(id, skb->data, skb->len); - /* some events require to be handled ASAP - * thus can't be defered to a worker thread */ - switch (event_id) { - case WMI_HOST_SWBA_EVENTID: - case WMI_MGMT_RX_EVENTID: - ath10k_wmi_event_process(ar, skb); + switch (id) { + case WMI_10X_MGMT_RX_EVENTID: + ath10k_wmi_event_mgmt_rx(ar, skb); + /* mgmt_rx() owns the skb now! */ return; + case WMI_10X_SCAN_EVENTID: + ath10k_wmi_event_scan(ar, skb); + break; + case WMI_10X_CHAN_INFO_EVENTID: + ath10k_wmi_event_chan_info(ar, skb); + break; + case WMI_10X_ECHO_EVENTID: + ath10k_wmi_event_echo(ar, skb); + break; + case WMI_10X_DEBUG_MESG_EVENTID: + ath10k_wmi_event_debug_mesg(ar, skb); + break; + case WMI_10X_UPDATE_STATS_EVENTID: + ath10k_wmi_event_update_stats(ar, skb); + break; + case WMI_10X_VDEV_START_RESP_EVENTID: + ath10k_wmi_event_vdev_start_resp(ar, skb); + break; + case WMI_10X_VDEV_STOPPED_EVENTID: + ath10k_wmi_event_vdev_stopped(ar, skb); + break; + case WMI_10X_PEER_STA_KICKOUT_EVENTID: + ath10k_wmi_event_peer_sta_kickout(ar, skb); + break; + case WMI_10X_HOST_SWBA_EVENTID: + ath10k_wmi_event_host_swba(ar, skb); + break; + case WMI_10X_TBTTOFFSET_UPDATE_EVENTID: + ath10k_wmi_event_tbttoffset_update(ar, skb); + break; + case WMI_10X_PHYERR_EVENTID: + ath10k_wmi_event_phyerr(ar, skb); + break; + case WMI_10X_ROAM_EVENTID: + ath10k_wmi_event_roam(ar, skb); + break; + case WMI_10X_PROFILE_MATCH: + ath10k_wmi_event_profile_match(ar, skb); + break; + case WMI_10X_DEBUG_PRINT_EVENTID: + ath10k_wmi_event_debug_print(ar, skb); + break; + case WMI_10X_PDEV_QVIT_EVENTID: + ath10k_wmi_event_pdev_qvit(ar, skb); + break; + case WMI_10X_WLAN_PROFILE_DATA_EVENTID: + ath10k_wmi_event_wlan_profile_data(ar, skb); + break; + case WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_rtt_measurement_report(ar, skb); + break; + case WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_tsf_measurement_report(ar, skb); + break; + case WMI_10X_RTT_ERROR_REPORT_EVENTID: + ath10k_wmi_event_rtt_error_report(ar, skb); + break; + case WMI_10X_WOW_WAKEUP_HOST_EVENTID: + ath10k_wmi_event_wow_wakeup_host(ar, skb); + break; + case WMI_10X_DCS_INTERFERENCE_EVENTID: + ath10k_wmi_event_dcs_interference(ar, skb); + break; + case WMI_10X_PDEV_TPC_CONFIG_EVENTID: + ath10k_wmi_event_pdev_tpc_config(ar, skb); + break; + case WMI_10X_INST_RSSI_STATS_EVENTID: + ath10k_wmi_event_inst_rssi_stats(ar, skb); + break; + case WMI_10X_VDEV_STANDBY_REQ_EVENTID: + ath10k_wmi_event_vdev_standby_req(ar, skb); + break; + case WMI_10X_VDEV_RESUME_REQ_EVENTID: + ath10k_wmi_event_vdev_resume_req(ar, skb); + break; + case WMI_10X_SERVICE_READY_EVENTID: + ath10k_wmi_10x_service_ready_event_rx(ar, skb); + break; + case WMI_10X_READY_EVENTID: + ath10k_wmi_ready_event_rx(ar, skb); + break; default: + ath10k_warn("Unknown eventid: %d\n", id); break; } - skb_queue_tail(&ar->wmi.wmi_event_list, skb); - queue_work(ar->workqueue, &ar->wmi.wmi_event_work); + dev_kfree_skb(skb); +} + + +static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) +{ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + ath10k_wmi_10x_process_rx(ar, skb); + else + ath10k_wmi_main_process_rx(ar, skb); } /* WMI Initialization functions */ int ath10k_wmi_attach(struct ath10k *ar) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + ar->wmi.cmd = &wmi_10x_cmd_map; + ar->wmi.vdev_param = &wmi_10x_vdev_param_map; + ar->wmi.pdev_param = &wmi_10x_pdev_param_map; + } else { + ar->wmi.cmd = &wmi_cmd_map; + ar->wmi.vdev_param = &wmi_vdev_param_map; + ar->wmi.pdev_param = &wmi_pdev_param_map; + } + init_completion(&ar->wmi.service_ready); init_completion(&ar->wmi.unified_ready); - init_waitqueue_head(&ar->wmi.wq); - - skb_queue_head_init(&ar->wmi.wmi_event_list); - INIT_WORK(&ar->wmi.wmi_event_work, ath10k_wmi_event_work); + init_waitqueue_head(&ar->wmi.tx_credits_wq); return 0; } void ath10k_wmi_detach(struct ath10k *ar) { - /* HTC should've drained the packets already */ - if (WARN_ON(atomic_read(&ar->wmi.pending_tx_count) > 0)) - ath10k_warn("there are still pending packets\n"); + int i; + + /* free the host memory chunks requested by firmware */ + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + dma_free_coherent(ar->dev, + ar->wmi.mem_chunks[i].len, + ar->wmi.mem_chunks[i].vaddr, + ar->wmi.mem_chunks[i].paddr); + } - cancel_work_sync(&ar->wmi.wmi_event_work); - skb_queue_purge(&ar->wmi.wmi_event_list); + ar->wmi.num_mem_chunks = 0; } int ath10k_wmi_connect_htc_service(struct ath10k *ar) @@ -1198,6 +2016,7 @@ int ath10k_wmi_connect_htc_service(struct ath10k *ar) /* these fields are the same for all service endpoints */ conn_req.ep_ops.ep_tx_complete = ath10k_wmi_htc_tx_complete; conn_req.ep_ops.ep_rx_complete = ath10k_wmi_process_rx; + conn_req.ep_ops.ep_tx_credits = ath10k_wmi_op_ep_tx_credits; /* connect to control service */ conn_req.service_id = ATH10K_HTC_SVC_ID_WMI_CONTROL; @@ -1234,7 +2053,8 @@ int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n", rd, rd2g, rd5g, ctl2g, ctl5g); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_REGDOMAIN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_regdomain_cmdid); } int ath10k_wmi_pdev_set_channel(struct ath10k *ar, @@ -1264,7 +2084,8 @@ int ath10k_wmi_pdev_set_channel(struct ath10k *ar, "wmi set channel mode %d freq %d\n", arg->mode, arg->freq); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_CHANNEL_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_channel_cmdid); } int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) @@ -1279,7 +2100,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar) cmd = (struct wmi_pdev_suspend_cmd *)skb->data; cmd->suspend_opt = WMI_PDEV_SUSPEND; - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SUSPEND_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid); } int ath10k_wmi_pdev_resume_target(struct ath10k *ar) @@ -1290,15 +2111,19 @@ int ath10k_wmi_pdev_resume_target(struct ath10k *ar) if (skb == NULL) return -ENOMEM; - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_RESUME_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid); } -int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, - u32 value) +int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) { struct wmi_pdev_set_param_cmd *cmd; struct sk_buff *skb; + if (id == WMI_PDEV_PARAM_UNSUPPORTED) { + ath10k_warn("pdev param %d not supported by firmware\n", id); + return -EOPNOTSUPP; + } + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1309,15 +2134,16 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n", id, value); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid); } -int ath10k_wmi_cmd_init(struct ath10k *ar) +static int ath10k_wmi_main_cmd_init(struct ath10k *ar) { struct wmi_init_cmd *cmd; struct sk_buff *buf; struct wmi_resource_config config = {}; - u32 val; + u32 len, val; + int i; config.num_vdevs = __cpu_to_le32(TARGET_NUM_VDEVS); config.num_peers = __cpu_to_le32(TARGET_NUM_PEERS + TARGET_NUM_VDEVS); @@ -1370,23 +2196,158 @@ int ath10k_wmi_cmd_init(struct ath10k *ar) config.num_msdu_desc = __cpu_to_le32(TARGET_NUM_MSDU_DESC); config.max_frag_entries = __cpu_to_le32(TARGET_MAX_FRAG_ENTRIES); - buf = ath10k_wmi_alloc_skb(sizeof(*cmd)); + len = sizeof(*cmd) + + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); + + buf = ath10k_wmi_alloc_skb(len); if (!buf) return -ENOMEM; cmd = (struct wmi_init_cmd *)buf->data; - cmd->num_host_mem_chunks = 0; + + if (ar->wmi.num_mem_chunks == 0) { + cmd->num_host_mem_chunks = 0; + goto out; + } + + ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", + __cpu_to_le32(ar->wmi.num_mem_chunks)); + + cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); + + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + cmd->host_mem_chunks[i].ptr = + __cpu_to_le32(ar->wmi.mem_chunks[i].paddr); + cmd->host_mem_chunks[i].size = + __cpu_to_le32(ar->wmi.mem_chunks[i].len); + cmd->host_mem_chunks[i].req_id = + __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi chunk %d len %d requested, addr 0x%x\n", + i, + cmd->host_mem_chunks[i].size, + cmd->host_mem_chunks[i].ptr); + } +out: memcpy(&cmd->resource_config, &config, sizeof(config)); ath10k_dbg(ATH10K_DBG_WMI, "wmi init\n"); - return ath10k_wmi_cmd_send(ar, buf, WMI_INIT_CMDID); + return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); } -static int ath10k_wmi_start_scan_calc_len(const struct wmi_start_scan_arg *arg) +static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) +{ + struct wmi_init_cmd_10x *cmd; + struct sk_buff *buf; + struct wmi_resource_config_10x config = {}; + u32 len, val; + int i; + + config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS); + config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS); + config.num_peer_keys = __cpu_to_le32(TARGET_10X_NUM_PEER_KEYS); + config.num_tids = __cpu_to_le32(TARGET_10X_NUM_TIDS); + config.ast_skid_limit = __cpu_to_le32(TARGET_10X_AST_SKID_LIMIT); + config.tx_chain_mask = __cpu_to_le32(TARGET_10X_TX_CHAIN_MASK); + config.rx_chain_mask = __cpu_to_le32(TARGET_10X_RX_CHAIN_MASK); + config.rx_timeout_pri_vo = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_vi = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_be = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_LO_PRI); + config.rx_timeout_pri_bk = __cpu_to_le32(TARGET_10X_RX_TIMEOUT_HI_PRI); + config.rx_decap_mode = __cpu_to_le32(TARGET_10X_RX_DECAP_MODE); + + config.scan_max_pending_reqs = + __cpu_to_le32(TARGET_10X_SCAN_MAX_PENDING_REQS); + + config.bmiss_offload_max_vdev = + __cpu_to_le32(TARGET_10X_BMISS_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_vdev = + __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_VDEV); + + config.roam_offload_max_ap_profiles = + __cpu_to_le32(TARGET_10X_ROAM_OFFLOAD_MAX_AP_PROFILES); + + config.num_mcast_groups = __cpu_to_le32(TARGET_10X_NUM_MCAST_GROUPS); + config.num_mcast_table_elems = + __cpu_to_le32(TARGET_10X_NUM_MCAST_TABLE_ELEMS); + + config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE); + config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE); + config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES); + config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE); + config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM); + + val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; + config.rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(val); + + config.vow_config = __cpu_to_le32(TARGET_10X_VOW_CONFIG); + + config.num_msdu_desc = __cpu_to_le32(TARGET_10X_NUM_MSDU_DESC); + config.max_frag_entries = __cpu_to_le32(TARGET_10X_MAX_FRAG_ENTRIES); + + len = sizeof(*cmd) + + (sizeof(struct host_memory_chunk) * ar->wmi.num_mem_chunks); + + buf = ath10k_wmi_alloc_skb(len); + if (!buf) + return -ENOMEM; + + cmd = (struct wmi_init_cmd_10x *)buf->data; + + if (ar->wmi.num_mem_chunks == 0) { + cmd->num_host_mem_chunks = 0; + goto out; + } + + ath10k_dbg(ATH10K_DBG_WMI, "wmi sending %d memory chunks info.\n", + __cpu_to_le32(ar->wmi.num_mem_chunks)); + + cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); + + for (i = 0; i < ar->wmi.num_mem_chunks; i++) { + cmd->host_mem_chunks[i].ptr = + __cpu_to_le32(ar->wmi.mem_chunks[i].paddr); + cmd->host_mem_chunks[i].size = + __cpu_to_le32(ar->wmi.mem_chunks[i].len); + cmd->host_mem_chunks[i].req_id = + __cpu_to_le32(ar->wmi.mem_chunks[i].req_id); + + ath10k_dbg(ATH10K_DBG_WMI, + "wmi chunk %d len %d requested, addr 0x%x\n", + i, + cmd->host_mem_chunks[i].size, + cmd->host_mem_chunks[i].ptr); + } +out: + memcpy(&cmd->resource_config, &config, sizeof(config)); + + ath10k_dbg(ATH10K_DBG_WMI, "wmi init 10x\n"); + return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); +} + +int ath10k_wmi_cmd_init(struct ath10k *ar) +{ + int ret; + + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + ret = ath10k_wmi_10x_cmd_init(ar); + else + ret = ath10k_wmi_main_cmd_init(ar); + + return ret; +} + +static int ath10k_wmi_start_scan_calc_len(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) { int len; - len = sizeof(struct wmi_start_scan_cmd); + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + len = sizeof(struct wmi_start_scan_cmd_10x); + else + len = sizeof(struct wmi_start_scan_cmd); if (arg->ie_len) { if (!arg->ie) @@ -1446,7 +2407,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar, int len = 0; int i; - len = ath10k_wmi_start_scan_calc_len(arg); + len = ath10k_wmi_start_scan_calc_len(ar, arg); if (len < 0) return len; /* len contains error code here */ @@ -1478,7 +2439,14 @@ int ath10k_wmi_start_scan(struct ath10k *ar, cmd->scan_ctrl_flags = __cpu_to_le32(arg->scan_ctrl_flags); /* TLV list starts after fields included in the struct */ - off = sizeof(*cmd); + /* There's just one filed that differes the two start_scan + * structures - burst_duration, which we are not using btw, + no point to make the split here, just shift the buffer to fit with + given FW */ + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + off = sizeof(struct wmi_start_scan_cmd_10x); + else + off = sizeof(struct wmi_start_scan_cmd); if (arg->n_channels) { channels = (void *)skb->data + off; @@ -1540,7 +2508,7 @@ int ath10k_wmi_start_scan(struct ath10k *ar, } ath10k_dbg(ATH10K_DBG_WMI, "wmi start scan\n"); - return ath10k_wmi_cmd_send(ar, skb, WMI_START_SCAN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid); } void ath10k_wmi_start_scan_init(struct ath10k *ar, @@ -1556,7 +2524,7 @@ void ath10k_wmi_start_scan_init(struct ath10k *ar, arg->repeat_probe_time = 0; arg->probe_spacing_time = 0; arg->idle_time = 0; - arg->max_scan_time = 5000; + arg->max_scan_time = 20000; arg->probe_delay = 5; arg->notify_scan_events = WMI_SCAN_EVENT_STARTED | WMI_SCAN_EVENT_COMPLETED @@ -1600,7 +2568,7 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) ath10k_dbg(ATH10K_DBG_WMI, "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n", arg->req_id, arg->req_type, arg->u.scan_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_STOP_SCAN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid); } int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, @@ -1625,7 +2593,7 @@ int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, "WMI vdev create: id %d type %d subtype %d macaddr %pM\n", vdev_id, type, subtype, macaddr); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_CREATE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid); } int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) @@ -1643,20 +2611,20 @@ int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) ath10k_dbg(ATH10K_DBG_WMI, "WMI vdev delete id %d\n", vdev_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DELETE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid); } static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, const struct wmi_vdev_start_request_arg *arg, - enum wmi_cmd_id cmd_id) + u32 cmd_id) { struct wmi_vdev_start_request_cmd *cmd; struct sk_buff *skb; const char *cmdname; u32 flags = 0; - if (cmd_id != WMI_VDEV_START_REQUEST_CMDID && - cmd_id != WMI_VDEV_RESTART_REQUEST_CMDID) + if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid && + cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid) return -EINVAL; if (WARN_ON(arg->ssid && arg->ssid_len == 0)) return -EINVAL; @@ -1665,9 +2633,9 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) return -EINVAL; - if (cmd_id == WMI_VDEV_START_REQUEST_CMDID) + if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid) cmdname = "start"; - else if (cmd_id == WMI_VDEV_RESTART_REQUEST_CMDID) + else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid) cmdname = "restart"; else return -EINVAL; /* should not happen, we already check cmd_id */ @@ -1718,15 +2686,17 @@ static int ath10k_wmi_vdev_start_restart(struct ath10k *ar, int ath10k_wmi_vdev_start(struct ath10k *ar, const struct wmi_vdev_start_request_arg *arg) { - return ath10k_wmi_vdev_start_restart(ar, arg, - WMI_VDEV_START_REQUEST_CMDID); + u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid; + + return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); } int ath10k_wmi_vdev_restart(struct ath10k *ar, const struct wmi_vdev_start_request_arg *arg) { - return ath10k_wmi_vdev_start_restart(ar, arg, - WMI_VDEV_RESTART_REQUEST_CMDID); + u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid; + + return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); } int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) @@ -1743,7 +2713,7 @@ int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_STOP_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid); } int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) @@ -1758,13 +2728,13 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) cmd = (struct wmi_vdev_up_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); cmd->vdev_assoc_id = __cpu_to_le32(aid); - memcpy(&cmd->vdev_bssid.addr, bssid, 6); + memcpy(&cmd->vdev_bssid.addr, bssid, ETH_ALEN); ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n", vdev_id, aid, bssid); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_UP_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid); } int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) @@ -1782,15 +2752,22 @@ int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) ath10k_dbg(ATH10K_DBG_WMI, "wmi mgmt vdev down id 0x%x\n", vdev_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_DOWN_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid); } int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_param param_id, u32 param_value) + u32 param_id, u32 param_value) { struct wmi_vdev_set_param_cmd *cmd; struct sk_buff *skb; + if (param_id == WMI_VDEV_PARAM_UNSUPPORTED) { + ath10k_dbg(ATH10K_DBG_WMI, + "vdev param %d not supported by firmware\n", + param_id); + return -EOPNOTSUPP; + } + skb = ath10k_wmi_alloc_skb(sizeof(*cmd)); if (!skb) return -ENOMEM; @@ -1804,7 +2781,7 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, "wmi vdev id 0x%x set param %d value %d\n", vdev_id, param_id, param_value); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_SET_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid); } int ath10k_wmi_vdev_install_key(struct ath10k *ar, @@ -1839,7 +2816,8 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev install key idx %d cipher %d len %d\n", arg->key_idx, arg->key_cipher, arg->key_len); - return ath10k_wmi_cmd_send(ar, skb, WMI_VDEV_INSTALL_KEY_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->vdev_install_key_cmdid); } int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, @@ -1859,7 +2837,7 @@ int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer create vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_CREATE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid); } int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, @@ -1879,7 +2857,7 @@ int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer delete vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_DELETE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid); } int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, @@ -1900,7 +2878,7 @@ int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n", vdev_id, peer_addr, tid_bitmap); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_FLUSH_TIDS_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid); } int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, @@ -1918,13 +2896,13 @@ int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, cmd->vdev_id = __cpu_to_le32(vdev_id); cmd->param_id = __cpu_to_le32(param_id); cmd->param_value = __cpu_to_le32(param_value); - memcpy(&cmd->peer_macaddr.addr, peer_addr, 6); + memcpy(&cmd->peer_macaddr.addr, peer_addr, ETH_ALEN); ath10k_dbg(ATH10K_DBG_WMI, "wmi vdev %d peer 0x%pM set param %d value %d\n", vdev_id, peer_addr, param_id, param_value); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_SET_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid); } int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, @@ -1945,7 +2923,8 @@ int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, "wmi set powersave id 0x%x mode %d\n", vdev_id, psmode); - return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_MODE_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_mode_cmdid); } int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, @@ -1967,7 +2946,8 @@ int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ATH10K_DBG_WMI, "wmi sta ps param vdev_id 0x%x param %d value %d\n", vdev_id, param_id, value); - return ath10k_wmi_cmd_send(ar, skb, WMI_STA_POWERSAVE_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_param_cmdid); } int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, @@ -1993,7 +2973,8 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n", vdev_id, param_id, value, mac); - return ath10k_wmi_cmd_send(ar, skb, WMI_AP_PS_PEER_PARAM_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->ap_ps_peer_param_cmdid); } int ath10k_wmi_scan_chan_list(struct ath10k *ar, @@ -2046,7 +3027,7 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, ci->flags |= __cpu_to_le32(flags); } - return ath10k_wmi_cmd_send(ar, skb, WMI_SCAN_CHAN_LIST_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid); } int ath10k_wmi_peer_assoc(struct ath10k *ar, @@ -2105,10 +3086,11 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "wmi peer assoc vdev %d addr %pM\n", arg->vdev_id, arg->addr); - return ath10k_wmi_cmd_send(ar, skb, WMI_PEER_ASSOC_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid); } -int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg) +int ath10k_wmi_beacon_send_nowait(struct ath10k *ar, + const struct wmi_bcn_tx_arg *arg) { struct wmi_bcn_tx_cmd *cmd; struct sk_buff *skb; @@ -2124,7 +3106,7 @@ int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg) cmd->hdr.bcn_len = __cpu_to_le32(arg->bcn_len); memcpy(cmd->bcn, arg->bcn, arg->bcn_len); - return ath10k_wmi_cmd_send(ar, skb, WMI_BCN_TX_CMDID); + return ath10k_wmi_cmd_send_nowait(ar, skb, ar->wmi.cmd->bcn_tx_cmdid); } static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, @@ -2155,7 +3137,8 @@ int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo); ath10k_dbg(ATH10K_DBG_WMI, "wmi pdev set wmm params\n"); - return ath10k_wmi_cmd_send(ar, skb, WMI_PDEV_SET_WMM_PARAMS_CMDID); + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_wmm_params_cmdid); } int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) @@ -2171,7 +3154,7 @@ int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) cmd->stats_id = __cpu_to_le32(stats_id); ath10k_dbg(ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); - return ath10k_wmi_cmd_send(ar, skb, WMI_REQUEST_STATS_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid); } int ath10k_wmi_force_fw_hang(struct ath10k *ar, @@ -2190,5 +3173,5 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar, ath10k_dbg(ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n", type, delay_ms); - return ath10k_wmi_cmd_send(ar, skb, WMI_FORCE_FW_HANG_CMDID); + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); } diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 2c5a4f8daf2ee4b80e9a9e84391586e638389271..78c991aec7f93d6419cc62d1b09ea2f63033b627 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -208,6 +208,118 @@ struct wmi_mac_addr { (c_macaddr)[5] = (((pwmi_mac_addr)->word1) >> 8) & 0xff; \ } while (0) +struct wmi_cmd_map { + u32 init_cmdid; + u32 start_scan_cmdid; + u32 stop_scan_cmdid; + u32 scan_chan_list_cmdid; + u32 scan_sch_prio_tbl_cmdid; + u32 pdev_set_regdomain_cmdid; + u32 pdev_set_channel_cmdid; + u32 pdev_set_param_cmdid; + u32 pdev_pktlog_enable_cmdid; + u32 pdev_pktlog_disable_cmdid; + u32 pdev_set_wmm_params_cmdid; + u32 pdev_set_ht_cap_ie_cmdid; + u32 pdev_set_vht_cap_ie_cmdid; + u32 pdev_set_dscp_tid_map_cmdid; + u32 pdev_set_quiet_mode_cmdid; + u32 pdev_green_ap_ps_enable_cmdid; + u32 pdev_get_tpc_config_cmdid; + u32 pdev_set_base_macaddr_cmdid; + u32 vdev_create_cmdid; + u32 vdev_delete_cmdid; + u32 vdev_start_request_cmdid; + u32 vdev_restart_request_cmdid; + u32 vdev_up_cmdid; + u32 vdev_stop_cmdid; + u32 vdev_down_cmdid; + u32 vdev_set_param_cmdid; + u32 vdev_install_key_cmdid; + u32 peer_create_cmdid; + u32 peer_delete_cmdid; + u32 peer_flush_tids_cmdid; + u32 peer_set_param_cmdid; + u32 peer_assoc_cmdid; + u32 peer_add_wds_entry_cmdid; + u32 peer_remove_wds_entry_cmdid; + u32 peer_mcast_group_cmdid; + u32 bcn_tx_cmdid; + u32 pdev_send_bcn_cmdid; + u32 bcn_tmpl_cmdid; + u32 bcn_filter_rx_cmdid; + u32 prb_req_filter_rx_cmdid; + u32 mgmt_tx_cmdid; + u32 prb_tmpl_cmdid; + u32 addba_clear_resp_cmdid; + u32 addba_send_cmdid; + u32 addba_status_cmdid; + u32 delba_send_cmdid; + u32 addba_set_resp_cmdid; + u32 send_singleamsdu_cmdid; + u32 sta_powersave_mode_cmdid; + u32 sta_powersave_param_cmdid; + u32 sta_mimo_ps_mode_cmdid; + u32 pdev_dfs_enable_cmdid; + u32 pdev_dfs_disable_cmdid; + u32 roam_scan_mode; + u32 roam_scan_rssi_threshold; + u32 roam_scan_period; + u32 roam_scan_rssi_change_threshold; + u32 roam_ap_profile; + u32 ofl_scan_add_ap_profile; + u32 ofl_scan_remove_ap_profile; + u32 ofl_scan_period; + u32 p2p_dev_set_device_info; + u32 p2p_dev_set_discoverability; + u32 p2p_go_set_beacon_ie; + u32 p2p_go_set_probe_resp_ie; + u32 p2p_set_vendor_ie_data_cmdid; + u32 ap_ps_peer_param_cmdid; + u32 ap_ps_peer_uapsd_coex_cmdid; + u32 peer_rate_retry_sched_cmdid; + u32 wlan_profile_trigger_cmdid; + u32 wlan_profile_set_hist_intvl_cmdid; + u32 wlan_profile_get_profile_data_cmdid; + u32 wlan_profile_enable_profile_id_cmdid; + u32 wlan_profile_list_profile_id_cmdid; + u32 pdev_suspend_cmdid; + u32 pdev_resume_cmdid; + u32 add_bcn_filter_cmdid; + u32 rmv_bcn_filter_cmdid; + u32 wow_add_wake_pattern_cmdid; + u32 wow_del_wake_pattern_cmdid; + u32 wow_enable_disable_wake_event_cmdid; + u32 wow_enable_cmdid; + u32 wow_hostwakeup_from_sleep_cmdid; + u32 rtt_measreq_cmdid; + u32 rtt_tsf_cmdid; + u32 vdev_spectral_scan_configure_cmdid; + u32 vdev_spectral_scan_enable_cmdid; + u32 request_stats_cmdid; + u32 set_arp_ns_offload_cmdid; + u32 network_list_offload_config_cmdid; + u32 gtk_offload_cmdid; + u32 csa_offload_enable_cmdid; + u32 csa_offload_chanswitch_cmdid; + u32 chatter_set_mode_cmdid; + u32 peer_tid_addba_cmdid; + u32 peer_tid_delba_cmdid; + u32 sta_dtim_ps_method_cmdid; + u32 sta_uapsd_auto_trig_cmdid; + u32 sta_keepalive_cmd; + u32 echo_cmdid; + u32 pdev_utf_cmdid; + u32 dbglog_cfg_cmdid; + u32 pdev_qvit_cmdid; + u32 pdev_ftm_intg_cmdid; + u32 vdev_set_keepalive_cmdid; + u32 vdev_get_keepalive_cmdid; + u32 force_fw_hang_cmdid; + u32 gpio_config_cmdid; + u32 gpio_output_cmdid; +}; + /* * wmi command groups. */ @@ -247,7 +359,9 @@ enum wmi_cmd_group { #define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1) #define WMI_EVT_GRP_START_ID(grp_id) (((grp_id) << 12) | 0x1) -/* Command IDs and commande events. */ +#define WMI_CMD_UNSUPPORTED 0 + +/* Command IDs and command events for MAIN FW. */ enum wmi_cmd_id { WMI_INIT_CMDID = 0x1, @@ -488,6 +602,217 @@ enum wmi_event_id { WMI_GPIO_INPUT_EVENTID = WMI_EVT_GRP_START_ID(WMI_GRP_GPIO), }; +/* Command IDs and command events for 10.X firmware */ +enum wmi_10x_cmd_id { + WMI_10X_START_CMDID = 0x9000, + WMI_10X_END_CMDID = 0x9FFF, + + /* initialize the wlan sub system */ + WMI_10X_INIT_CMDID, + + /* Scan specific commands */ + + WMI_10X_START_SCAN_CMDID = WMI_10X_START_CMDID, + WMI_10X_STOP_SCAN_CMDID, + WMI_10X_SCAN_CHAN_LIST_CMDID, + WMI_10X_ECHO_CMDID, + + /* PDEV(physical device) specific commands */ + WMI_10X_PDEV_SET_REGDOMAIN_CMDID, + WMI_10X_PDEV_SET_CHANNEL_CMDID, + WMI_10X_PDEV_SET_PARAM_CMDID, + WMI_10X_PDEV_PKTLOG_ENABLE_CMDID, + WMI_10X_PDEV_PKTLOG_DISABLE_CMDID, + WMI_10X_PDEV_SET_WMM_PARAMS_CMDID, + WMI_10X_PDEV_SET_HT_CAP_IE_CMDID, + WMI_10X_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_10X_PDEV_SET_BASE_MACADDR_CMDID, + WMI_10X_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_10X_PDEV_SET_QUIET_MODE_CMDID, + WMI_10X_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_10X_PDEV_GET_TPC_CONFIG_CMDID, + + /* VDEV(virtual device) specific commands */ + WMI_10X_VDEV_CREATE_CMDID, + WMI_10X_VDEV_DELETE_CMDID, + WMI_10X_VDEV_START_REQUEST_CMDID, + WMI_10X_VDEV_RESTART_REQUEST_CMDID, + WMI_10X_VDEV_UP_CMDID, + WMI_10X_VDEV_STOP_CMDID, + WMI_10X_VDEV_DOWN_CMDID, + WMI_10X_VDEV_STANDBY_RESPONSE_CMDID, + WMI_10X_VDEV_RESUME_RESPONSE_CMDID, + WMI_10X_VDEV_SET_PARAM_CMDID, + WMI_10X_VDEV_INSTALL_KEY_CMDID, + + /* peer specific commands */ + WMI_10X_PEER_CREATE_CMDID, + WMI_10X_PEER_DELETE_CMDID, + WMI_10X_PEER_FLUSH_TIDS_CMDID, + WMI_10X_PEER_SET_PARAM_CMDID, + WMI_10X_PEER_ASSOC_CMDID, + WMI_10X_PEER_ADD_WDS_ENTRY_CMDID, + WMI_10X_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_10X_PEER_MCAST_GROUP_CMDID, + + /* beacon/management specific commands */ + + WMI_10X_BCN_TX_CMDID, + WMI_10X_BCN_PRB_TMPL_CMDID, + WMI_10X_BCN_FILTER_RX_CMDID, + WMI_10X_PRB_REQ_FILTER_RX_CMDID, + WMI_10X_MGMT_TX_CMDID, + + /* commands to directly control ba negotiation directly from host. */ + WMI_10X_ADDBA_CLEAR_RESP_CMDID, + WMI_10X_ADDBA_SEND_CMDID, + WMI_10X_ADDBA_STATUS_CMDID, + WMI_10X_DELBA_SEND_CMDID, + WMI_10X_ADDBA_SET_RESP_CMDID, + WMI_10X_SEND_SINGLEAMSDU_CMDID, + + /* Station power save specific config */ + WMI_10X_STA_POWERSAVE_MODE_CMDID, + WMI_10X_STA_POWERSAVE_PARAM_CMDID, + WMI_10X_STA_MIMO_PS_MODE_CMDID, + + /* set debug log config */ + WMI_10X_DBGLOG_CFG_CMDID, + + /* DFS-specific commands */ + WMI_10X_PDEV_DFS_ENABLE_CMDID, + WMI_10X_PDEV_DFS_DISABLE_CMDID, + + /* QVIT specific command id */ + WMI_10X_PDEV_QVIT_CMDID, + + /* Offload Scan and Roaming related commands */ + WMI_10X_ROAM_SCAN_MODE, + WMI_10X_ROAM_SCAN_RSSI_THRESHOLD, + WMI_10X_ROAM_SCAN_PERIOD, + WMI_10X_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_10X_ROAM_AP_PROFILE, + WMI_10X_OFL_SCAN_ADD_AP_PROFILE, + WMI_10X_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_10X_OFL_SCAN_PERIOD, + + /* P2P specific commands */ + WMI_10X_P2P_DEV_SET_DEVICE_INFO, + WMI_10X_P2P_DEV_SET_DISCOVERABILITY, + WMI_10X_P2P_GO_SET_BEACON_IE, + WMI_10X_P2P_GO_SET_PROBE_RESP_IE, + + /* AP power save specific config */ + WMI_10X_AP_PS_PEER_PARAM_CMDID, + WMI_10X_AP_PS_PEER_UAPSD_COEX_CMDID, + + /* Rate-control specific commands */ + WMI_10X_PEER_RATE_RETRY_SCHED_CMDID, + + /* WLAN Profiling commands. */ + WMI_10X_WLAN_PROFILE_TRIGGER_CMDID, + WMI_10X_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_10X_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_10X_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_10X_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + + /* Suspend resume command Ids */ + WMI_10X_PDEV_SUSPEND_CMDID, + WMI_10X_PDEV_RESUME_CMDID, + + /* Beacon filter commands */ + WMI_10X_ADD_BCN_FILTER_CMDID, + WMI_10X_RMV_BCN_FILTER_CMDID, + + /* WOW Specific WMI commands*/ + WMI_10X_WOW_ADD_WAKE_PATTERN_CMDID, + WMI_10X_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_10X_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_10X_WOW_ENABLE_CMDID, + WMI_10X_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + + /* RTT measurement related cmd */ + WMI_10X_RTT_MEASREQ_CMDID, + WMI_10X_RTT_TSF_CMDID, + + /* transmit beacon by value */ + WMI_10X_PDEV_SEND_BCN_CMDID, + + /* F/W stats */ + WMI_10X_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + WMI_10X_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + WMI_10X_REQUEST_STATS_CMDID, + + /* GPIO Configuration */ + WMI_10X_GPIO_CONFIG_CMDID, + WMI_10X_GPIO_OUTPUT_CMDID, + + WMI_10X_PDEV_UTF_CMDID = WMI_10X_END_CMDID - 1, +}; + +enum wmi_10x_event_id { + WMI_10X_SERVICE_READY_EVENTID = 0x8000, + WMI_10X_READY_EVENTID, + WMI_10X_START_EVENTID = 0x9000, + WMI_10X_END_EVENTID = 0x9FFF, + + /* Scan specific events */ + WMI_10X_SCAN_EVENTID = WMI_10X_START_EVENTID, + WMI_10X_ECHO_EVENTID, + WMI_10X_DEBUG_MESG_EVENTID, + WMI_10X_UPDATE_STATS_EVENTID, + + /* Instantaneous RSSI event */ + WMI_10X_INST_RSSI_STATS_EVENTID, + + /* VDEV specific events */ + WMI_10X_VDEV_START_RESP_EVENTID, + WMI_10X_VDEV_STANDBY_REQ_EVENTID, + WMI_10X_VDEV_RESUME_REQ_EVENTID, + WMI_10X_VDEV_STOPPED_EVENTID, + + /* peer specific events */ + WMI_10X_PEER_STA_KICKOUT_EVENTID, + + /* beacon/mgmt specific events */ + WMI_10X_HOST_SWBA_EVENTID, + WMI_10X_TBTTOFFSET_UPDATE_EVENTID, + WMI_10X_MGMT_RX_EVENTID, + + /* Channel stats event */ + WMI_10X_CHAN_INFO_EVENTID, + + /* PHY Error specific WMI event */ + WMI_10X_PHYERR_EVENTID, + + /* Roam event to trigger roaming on host */ + WMI_10X_ROAM_EVENTID, + + /* matching AP found from list of profiles */ + WMI_10X_PROFILE_MATCH, + + /* debug print message used for tracing FW code while debugging */ + WMI_10X_DEBUG_PRINT_EVENTID, + /* VI spoecific event */ + WMI_10X_PDEV_QVIT_EVENTID, + /* FW code profile data in response to profile request */ + WMI_10X_WLAN_PROFILE_DATA_EVENTID, + + /*RTT related event ID*/ + WMI_10X_RTT_MEASUREMENT_REPORT_EVENTID, + WMI_10X_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_10X_RTT_ERROR_REPORT_EVENTID, + + WMI_10X_WOW_WAKEUP_HOST_EVENTID, + WMI_10X_DCS_INTERFERENCE_EVENTID, + + /* TPC config for the current operating channel */ + WMI_10X_PDEV_TPC_CONFIG_EVENTID, + + WMI_10X_GPIO_INPUT_EVENTID, + WMI_10X_PDEV_UTF_EVENTID = WMI_10X_END_EVENTID-1, +}; + enum wmi_phy_mode { MODE_11A = 0, /* 11a Mode */ MODE_11G = 1, /* 11b/g Mode */ @@ -508,6 +833,48 @@ enum wmi_phy_mode { MODE_MAX = 14 }; +static inline const char *ath10k_wmi_phymode_str(enum wmi_phy_mode mode) +{ + switch (mode) { + case MODE_11A: + return "11a"; + case MODE_11G: + return "11g"; + case MODE_11B: + return "11b"; + case MODE_11GONLY: + return "11gonly"; + case MODE_11NA_HT20: + return "11na-ht20"; + case MODE_11NG_HT20: + return "11ng-ht20"; + case MODE_11NA_HT40: + return "11na-ht40"; + case MODE_11NG_HT40: + return "11ng-ht40"; + case MODE_11AC_VHT20: + return "11ac-vht20"; + case MODE_11AC_VHT40: + return "11ac-vht40"; + case MODE_11AC_VHT80: + return "11ac-vht80"; + case MODE_11AC_VHT20_2G: + return "11ac-vht20-2g"; + case MODE_11AC_VHT40_2G: + return "11ac-vht40-2g"; + case MODE_11AC_VHT80_2G: + return "11ac-vht80-2g"; + case MODE_UNKNOWN: + /* skip */ + break; + + /* no default handler to allow compiler to check that the + * enum is fully handled */ + }; + + return ""; +} + #define WMI_CHAN_LIST_TAG 0x1 #define WMI_SSID_LIST_TAG 0x2 #define WMI_BSSID_LIST_TAG 0x3 @@ -763,13 +1130,45 @@ struct wmi_service_ready_event { struct wlan_host_mem_req mem_reqs[1]; } __packed; -/* - * status consists of upper 16 bits fo int status and lower 16 bits of - * module ID that retuned status - */ -#define WLAN_INIT_STATUS_SUCCESS 0x0 -#define WLAN_GET_INIT_STATUS_REASON(status) ((status) & 0xffff) -#define WLAN_GET_INIT_STATUS_MODULE_ID(status) (((status) >> 16) & 0xffff) +/* This is the definition from 10.X firmware branch */ +struct wmi_service_ready_event_10x { + __le32 sw_version; + __le32 abi_version; + + /* WMI_PHY_CAPABILITY */ + __le32 phy_capability; + + /* Maximum number of frag table entries that SW will populate less 1 */ + __le32 max_frag_entry; + __le32 wmi_service_bitmap[WMI_SERVICE_BM_SIZE]; + __le32 num_rf_chains; + + /* + * The following field is only valid for service type + * WMI_SERVICE_11AC + */ + __le32 ht_cap_info; /* WMI HT Capability */ + __le32 vht_cap_info; /* VHT capability info field of 802.11ac */ + __le32 vht_supp_mcs; /* VHT Supported MCS Set field Rx/Tx same */ + __le32 hw_min_tx_power; + __le32 hw_max_tx_power; + + struct hal_reg_capabilities hal_reg_capabilities; + + __le32 sys_cap_info; + __le32 min_pkt_size_enable; /* Enterprise mode short pkt enable */ + + /* + * request to host to allocate a chuck of memory and pss it down to FW + * via WM_INIT. FW uses this as FW extesnsion memory for saving its + * data structures. Only valid for low latency interfaces like PCIE + * where FW can access this memory directly (or) by DMA. + */ + __le32 num_mem_reqs; + + struct wlan_host_mem_req mem_reqs[1]; +} __packed; + #define WMI_SERVICE_READY_TIMEOUT_HZ (5*HZ) #define WMI_UNIFIED_READY_TIMEOUT_HZ (5*HZ) @@ -978,6 +1377,192 @@ struct wmi_resource_config { __le32 max_frag_entries; } __packed; +struct wmi_resource_config_10x { + /* number of virtual devices (VAPs) to support */ + __le32 num_vdevs; + + /* number of peer nodes to support */ + __le32 num_peers; + + /* number of keys per peer */ + __le32 num_peer_keys; + + /* total number of TX/RX data TIDs */ + __le32 num_tids; + + /* + * max skid for resolving hash collisions + * + * The address search table is sparse, so that if two MAC addresses + * result in the same hash value, the second of these conflicting + * entries can slide to the next index in the address search table, + * and use it, if it is unoccupied. This ast_skid_limit parameter + * specifies the upper bound on how many subsequent indices to search + * over to find an unoccupied space. + */ + __le32 ast_skid_limit; + + /* + * the nominal chain mask for transmit + * + * The chain mask may be modified dynamically, e.g. to operate AP + * tx with a reduced number of chains if no clients are associated. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of tx chains. + */ + __le32 tx_chain_mask; + + /* + * the nominal chain mask for receive + * + * The chain mask may be modified dynamically, e.g. for a client + * to use a reduced number of chains for receive if the traffic to + * the client is low enough that it doesn't require downlink MIMO + * or antenna diversity. + * This configuration parameter specifies the nominal chain-mask that + * should be used when not operating with a reduced set of rx chains. + */ + __le32 rx_chain_mask; + + /* + * what rx reorder timeout (ms) to use for the AC + * + * Each WMM access class (voice, video, best-effort, background) will + * have its own timeout value to dictate how long to wait for missing + * rx MPDUs to arrive before flushing subsequent MPDUs that have + * already been received. + * This parameter specifies the timeout in milliseconds for each + * class. + */ + __le32 rx_timeout_pri_vi; + __le32 rx_timeout_pri_vo; + __le32 rx_timeout_pri_be; + __le32 rx_timeout_pri_bk; + + /* + * what mode the rx should decap packets to + * + * MAC can decap to RAW (no decap), native wifi or Ethernet types + * THis setting also determines the default TX behavior, however TX + * behavior can be modified on a per VAP basis during VAP init + */ + __le32 rx_decap_mode; + + /* what is the maximum scan requests than can be queued */ + __le32 scan_max_pending_reqs; + + /* maximum VDEV that could use BMISS offload */ + __le32 bmiss_offload_max_vdev; + + /* maximum VDEV that could use offload roaming */ + __le32 roam_offload_max_vdev; + + /* maximum AP profiles that would push to offload roaming */ + __le32 roam_offload_max_ap_profiles; + + /* + * how many groups to use for mcast->ucast conversion + * + * The target's WAL maintains a table to hold information regarding + * which peers belong to a given multicast group, so that if + * multicast->unicast conversion is enabled, the target can convert + * multicast tx frames to a series of unicast tx frames, to each + * peer within the multicast group. + This num_mcast_groups configuration parameter tells the target how + * many multicast groups to provide storage for within its multicast + * group membership table. + */ + __le32 num_mcast_groups; + + /* + * size to alloc for the mcast membership table + * + * This num_mcast_table_elems configuration parameter tells the + * target how many peer elements it needs to provide storage for in + * its multicast group membership table. + * These multicast group membership table elements are shared by the + * multicast groups stored within the table. + */ + __le32 num_mcast_table_elems; + + /* + * whether/how to do multicast->unicast conversion + * + * This configuration parameter specifies whether the target should + * perform multicast --> unicast conversion on transmit, and if so, + * what to do if it finds no entries in its multicast group + * membership table for the multicast IP address in the tx frame. + * Configuration value: + * 0 -> Do not perform multicast to unicast conversion. + * 1 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * drop the frame. + * 2 -> Convert multicast frames to unicast, if the IP multicast + * address from the tx frame is found in the multicast group + * membership table. If the IP multicast address is not found, + * transmit the frame as multicast. + */ + __le32 mcast2ucast_mode; + + /* + * how much memory to allocate for a tx PPDU dbg log + * + * This parameter controls how much memory the target will allocate + * to store a log of tx PPDU meta-information (how large the PPDU + * was, when it was sent, whether it was successful, etc.) + */ + __le32 tx_dbg_log_size; + + /* how many AST entries to be allocated for WDS */ + __le32 num_wds_entries; + + /* + * MAC DMA burst size, e.g., For target PCI limit can be + * 0 -default, 1 256B + */ + __le32 dma_burst_size; + + /* + * Fixed delimiters to be inserted after every MPDU to + * account for interface latency to avoid underrun. + */ + __le32 mac_aggr_delim; + + /* + * determine whether target is responsible for detecting duplicate + * non-aggregate MPDU and timing out stale fragments. + * + * A-MPDU reordering is always performed on the target. + * + * 0: target responsible for frag timeout and dup checking + * 1: host responsible for frag timeout and dup checking + */ + __le32 rx_skip_defrag_timeout_dup_detection_check; + + /* + * Configuration for VoW : + * No of Video Nodes to be supported + * and Max no of descriptors for each Video link (node). + */ + __le32 vow_config; + + /* Number of msdu descriptors target should use */ + __le32 num_msdu_desc; + + /* + * Max. number of Tx fragments per MSDU + * This parameter controls the max number of Tx fragments per MSDU. + * This is sent by the target as part of the WMI_SERVICE_READY event + * and is overriden by the OS shim as required. + */ + __le32 max_frag_entries; +} __packed; + + +#define NUM_UNITS_IS_NUM_VDEVS 0x1 +#define NUM_UNITS_IS_NUM_PEERS 0x2 + /* strucutre describing host memory chunk. */ struct host_memory_chunk { /* id of the request that is passed up in service ready */ @@ -999,6 +1584,18 @@ struct wmi_init_cmd { struct host_memory_chunk host_mem_chunks[1]; } __packed; +/* _10x stucture is from 10.X FW API */ +struct wmi_init_cmd_10x { + struct wmi_resource_config_10x resource_config; + __le32 num_host_mem_chunks; + + /* + * variable number of host memory chunks. + * This should be the last element in the structure + */ + struct host_memory_chunk host_mem_chunks[1]; +} __packed; + /* TLV for channel list */ struct wmi_chan_list { __le32 tag; /* WMI_CHAN_LIST_TAG */ @@ -1118,6 +1715,88 @@ struct wmi_start_scan_cmd { */ } __packed; +/* This is the definition from 10.X firmware branch */ +struct wmi_start_scan_cmd_10x { + /* Scan ID */ + __le32 scan_id; + + /* Scan requestor ID */ + __le32 scan_req_id; + + /* VDEV id(interface) that is requesting scan */ + __le32 vdev_id; + + /* Scan Priority, input to scan scheduler */ + __le32 scan_priority; + + /* Scan events subscription */ + __le32 notify_scan_events; + + /* dwell time in msec on active channels */ + __le32 dwell_time_active; + + /* dwell time in msec on passive channels */ + __le32 dwell_time_passive; + + /* + * min time in msec on the BSS channel,only valid if atleast one + * VDEV is active + */ + __le32 min_rest_time; + + /* + * max rest time in msec on the BSS channel,only valid if at least + * one VDEV is active + */ + /* + * the scanner will rest on the bss channel at least min_rest_time + * after min_rest_time the scanner will start checking for tx/rx + * activity on all VDEVs. if there is no activity the scanner will + * switch to off channel. if there is activity the scanner will let + * the radio on the bss channel until max_rest_time expires.at + * max_rest_time scanner will switch to off channel irrespective of + * activity. activity is determined by the idle_time parameter. + */ + __le32 max_rest_time; + + /* + * time before sending next set of probe requests. + * The scanner keeps repeating probe requests transmission with + * period specified by repeat_probe_time. + * The number of probe requests specified depends on the ssid_list + * and bssid_list + */ + __le32 repeat_probe_time; + + /* time in msec between 2 consequetive probe requests with in a set. */ + __le32 probe_spacing_time; + + /* + * data inactivity time in msec on bss channel that will be used by + * scanner for measuring the inactivity. + */ + __le32 idle_time; + + /* maximum time in msec allowed for scan */ + __le32 max_scan_time; + + /* + * delay in msec before sending first probe request after switching + * to a channel + */ + __le32 probe_delay; + + /* Scan control flags */ + __le32 scan_ctrl_flags; + + /* + * TLV (tag length value ) paramerters follow the scan_cmd structure. + * TLV can contain channel list, bssid list, ssid list and + * ie. the TLV tags are defined above; + */ +} __packed; + + struct wmi_ssid_arg { int len; const u8 *ssid; @@ -1268,7 +1947,7 @@ struct wmi_scan_event { * good idea to pass all the fields in the RX status * descriptor up to the host. */ -struct wmi_mgmt_rx_hdr { +struct wmi_mgmt_rx_hdr_v1 { __le32 channel; __le32 snr; __le32 rate; @@ -1277,8 +1956,18 @@ struct wmi_mgmt_rx_hdr { __le32 status; /* %WMI_RX_STATUS_ */ } __packed; -struct wmi_mgmt_rx_event { - struct wmi_mgmt_rx_hdr hdr; +struct wmi_mgmt_rx_hdr_v2 { + struct wmi_mgmt_rx_hdr_v1 v1; + __le32 rssi_ctl[4]; +} __packed; + +struct wmi_mgmt_rx_event_v1 { + struct wmi_mgmt_rx_hdr_v1 hdr; + u8 buf[0]; +} __packed; + +struct wmi_mgmt_rx_event_v2 { + struct wmi_mgmt_rx_hdr_v2 hdr; u8 buf[0]; } __packed; @@ -1465,6 +2154,60 @@ struct wmi_csa_event { #define VDEV_DEFAULT_STATS_UPDATE_PERIOD 500 #define PEER_DEFAULT_STATS_UPDATE_PERIOD 500 +struct wmi_pdev_param_map { + u32 tx_chain_mask; + u32 rx_chain_mask; + u32 txpower_limit2g; + u32 txpower_limit5g; + u32 txpower_scale; + u32 beacon_gen_mode; + u32 beacon_tx_mode; + u32 resmgr_offchan_mode; + u32 protection_mode; + u32 dynamic_bw; + u32 non_agg_sw_retry_th; + u32 agg_sw_retry_th; + u32 sta_kickout_th; + u32 ac_aggrsize_scaling; + u32 ltr_enable; + u32 ltr_ac_latency_be; + u32 ltr_ac_latency_bk; + u32 ltr_ac_latency_vi; + u32 ltr_ac_latency_vo; + u32 ltr_ac_latency_timeout; + u32 ltr_sleep_override; + u32 ltr_rx_override; + u32 ltr_tx_activity_timeout; + u32 l1ss_enable; + u32 dsleep_enable; + u32 pcielp_txbuf_flush; + u32 pcielp_txbuf_watermark; + u32 pcielp_txbuf_tmo_en; + u32 pcielp_txbuf_tmo_value; + u32 pdev_stats_update_period; + u32 vdev_stats_update_period; + u32 peer_stats_update_period; + u32 bcnflt_stats_update_period; + u32 pmf_qos; + u32 arp_ac_override; + u32 arpdhcp_ac_override; + u32 dcs; + u32 ani_enable; + u32 ani_poll_period; + u32 ani_listen_period; + u32 ani_ofdm_level; + u32 ani_cck_level; + u32 dyntxchain; + u32 proxy_sta; + u32 idle_ps_config; + u32 power_gating_sleep; + u32 fast_channel_reset; + u32 burst_dur; + u32 burst_enable; +}; + +#define WMI_PDEV_PARAM_UNSUPPORTED 0 + enum wmi_pdev_param { /* TX chian mask */ WMI_PDEV_PARAM_TX_CHAIN_MASK = 0x1, @@ -1564,6 +2307,97 @@ enum wmi_pdev_param { WMI_PDEV_PARAM_POWER_GATING_SLEEP, }; +enum wmi_10x_pdev_param { + /* TX chian mask */ + WMI_10X_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + /* RX chian mask */ + WMI_10X_PDEV_PARAM_RX_CHAIN_MASK, + /* TX power limit for 2G Radio */ + WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G, + /* TX power limit for 5G Radio */ + WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G, + /* TX power scale */ + WMI_10X_PDEV_PARAM_TXPOWER_SCALE, + /* Beacon generation mode . 0: host, 1: target */ + WMI_10X_PDEV_PARAM_BEACON_GEN_MODE, + /* Beacon generation mode . 0: staggered 1: bursted */ + WMI_10X_PDEV_PARAM_BEACON_TX_MODE, + /* + * Resource manager off chan mode . + * 0: turn off off chan mode. 1: turn on offchan mode + */ + WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + /* + * Protection mode: + * 0: no protection 1:use CTS-to-self 2: use RTS/CTS + */ + WMI_10X_PDEV_PARAM_PROTECTION_MODE, + /* Dynamic bandwidth 0: disable 1: enable */ + WMI_10X_PDEV_PARAM_DYNAMIC_BW, + /* Non aggregrate/ 11g sw retry threshold.0-disable */ + WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + /* aggregrate sw retry threshold. 0-disable*/ + WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH, + /* Station kickout threshold (non of consecutive failures).0-disable */ + WMI_10X_PDEV_PARAM_STA_KICKOUT_TH, + /* Aggerate size scaling configuration per AC */ + WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING, + /* LTR enable */ + WMI_10X_PDEV_PARAM_LTR_ENABLE, + /* LTR latency for BE, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE, + /* LTR latency for BK, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK, + /* LTR latency for VI, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI, + /* LTR latency for VO, in us */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO, + /* LTR AC latency timeout, in ms */ + WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + /* LTR platform latency override, in us */ + WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + /* LTR-RX override, in us */ + WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE, + /* Tx activity timeout for LTR, in us */ + WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + /* L1SS state machine enable */ + WMI_10X_PDEV_PARAM_L1SS_ENABLE, + /* Deep sleep state machine enable */ + WMI_10X_PDEV_PARAM_DSLEEP_ENABLE, + /* pdev level stats update period in ms */ + WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + /* vdev level stats update period in ms */ + WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + /* peer level stats update period in ms */ + WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + /* beacon filter status update period */ + WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + /* QOS Mgmt frame protection MFP/PMF 0: disable, 1: enable */ + WMI_10X_PDEV_PARAM_PMF_QOS, + /* Access category on which ARP and DHCP frames are sent */ + WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, + /* DCS configuration */ + WMI_10X_PDEV_PARAM_DCS, + /* Enable/Disable ANI on target */ + WMI_10X_PDEV_PARAM_ANI_ENABLE, + /* configure the ANI polling period */ + WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD, + /* configure the ANI listening period */ + WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD, + /* configure OFDM immunity level */ + WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL, + /* configure CCK immunity level */ + WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL, + /* Enable/Disable CDD for 1x1 STAs in rate control module */ + WMI_10X_PDEV_PARAM_DYNTXCHAIN, + /* Enable/Disable Fast channel reset*/ + WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, + /* Set Bursting DUR */ + WMI_10X_PDEV_PARAM_BURST_DUR, + /* Set Bursting Enable*/ + WMI_10X_PDEV_PARAM_BURST_ENABLE, +}; + struct wmi_pdev_set_param_cmd { __le32 param_id; __le32 param_value; @@ -2088,6 +2922,61 @@ enum wmi_rate_preamble { /* Value to disable fixed rate setting */ #define WMI_FIXED_RATE_NONE (0xff) +struct wmi_vdev_param_map { + u32 rts_threshold; + u32 fragmentation_threshold; + u32 beacon_interval; + u32 listen_interval; + u32 multicast_rate; + u32 mgmt_tx_rate; + u32 slot_time; + u32 preamble; + u32 swba_time; + u32 wmi_vdev_stats_update_period; + u32 wmi_vdev_pwrsave_ageout_time; + u32 wmi_vdev_host_swba_interval; + u32 dtim_period; + u32 wmi_vdev_oc_scheduler_air_time_limit; + u32 wds; + u32 atim_window; + u32 bmiss_count_max; + u32 bmiss_first_bcnt; + u32 bmiss_final_bcnt; + u32 feature_wmm; + u32 chwidth; + u32 chextoffset; + u32 disable_htprotection; + u32 sta_quickkickout; + u32 mgmt_rate; + u32 protection_mode; + u32 fixed_rate; + u32 sgi; + u32 ldpc; + u32 tx_stbc; + u32 rx_stbc; + u32 intra_bss_fwd; + u32 def_keyid; + u32 nss; + u32 bcast_data_rate; + u32 mcast_data_rate; + u32 mcast_indicate; + u32 dhcp_indicate; + u32 unknown_dest_indicate; + u32 ap_keepalive_min_idle_inactive_time_secs; + u32 ap_keepalive_max_idle_inactive_time_secs; + u32 ap_keepalive_max_unresponsive_time_secs; + u32 ap_enable_nawds; + u32 mcast2ucast_set; + u32 enable_rtscts; + u32 txbf; + u32 packet_powersave; + u32 drop_unencry; + u32 tx_encap_type; + u32 ap_detect_out_of_sync_sleeping_sta_time_secs; +}; + +#define WMI_VDEV_PARAM_UNSUPPORTED 0 + /* the definition of different VDEV parameters */ enum wmi_vdev_param { /* RTS Threshold */ @@ -2219,6 +3108,121 @@ enum wmi_vdev_param { WMI_VDEV_PARAM_TX_ENCAP_TYPE, }; +/* the definition of different VDEV parameters */ +enum wmi_10x_vdev_param { + /* RTS Threshold */ + WMI_10X_VDEV_PARAM_RTS_THRESHOLD = 0x1, + /* Fragmentation threshold */ + WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + /* beacon interval in TUs */ + WMI_10X_VDEV_PARAM_BEACON_INTERVAL, + /* Listen interval in TUs */ + WMI_10X_VDEV_PARAM_LISTEN_INTERVAL, + /* muticast rate in Mbps */ + WMI_10X_VDEV_PARAM_MULTICAST_RATE, + /* management frame rate in Mbps */ + WMI_10X_VDEV_PARAM_MGMT_TX_RATE, + /* slot time (long vs short) */ + WMI_10X_VDEV_PARAM_SLOT_TIME, + /* preamble (long vs short) */ + WMI_10X_VDEV_PARAM_PREAMBLE, + /* SWBA time (time before tbtt in msec) */ + WMI_10X_VDEV_PARAM_SWBA_TIME, + /* time period for updating VDEV stats */ + WMI_10X_VDEV_STATS_UPDATE_PERIOD, + /* age out time in msec for frames queued for station in power save */ + WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME, + /* + * Host SWBA interval (time in msec before tbtt for SWBA event + * generation). + */ + WMI_10X_VDEV_HOST_SWBA_INTERVAL, + /* DTIM period (specified in units of num beacon intervals) */ + WMI_10X_VDEV_PARAM_DTIM_PERIOD, + /* + * scheduler air time limit for this VDEV. used by off chan + * scheduler. + */ + WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + /* enable/dsiable WDS for this VDEV */ + WMI_10X_VDEV_PARAM_WDS, + /* ATIM Window */ + WMI_10X_VDEV_PARAM_ATIM_WINDOW, + /* BMISS max */ + WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX, + /* WMM enables/disabled */ + WMI_10X_VDEV_PARAM_FEATURE_WMM, + /* Channel width */ + WMI_10X_VDEV_PARAM_CHWIDTH, + /* Channel Offset */ + WMI_10X_VDEV_PARAM_CHEXTOFFSET, + /* Disable HT Protection */ + WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION, + /* Quick STA Kickout */ + WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT, + /* Rate to be used with Management frames */ + WMI_10X_VDEV_PARAM_MGMT_RATE, + /* Protection Mode */ + WMI_10X_VDEV_PARAM_PROTECTION_MODE, + /* Fixed rate setting */ + WMI_10X_VDEV_PARAM_FIXED_RATE, + /* Short GI Enable/Disable */ + WMI_10X_VDEV_PARAM_SGI, + /* Enable LDPC */ + WMI_10X_VDEV_PARAM_LDPC, + /* Enable Tx STBC */ + WMI_10X_VDEV_PARAM_TX_STBC, + /* Enable Rx STBC */ + WMI_10X_VDEV_PARAM_RX_STBC, + /* Intra BSS forwarding */ + WMI_10X_VDEV_PARAM_INTRA_BSS_FWD, + /* Setting Default xmit key for Vdev */ + WMI_10X_VDEV_PARAM_DEF_KEYID, + /* NSS width */ + WMI_10X_VDEV_PARAM_NSS, + /* Set the custom rate for the broadcast data frames */ + WMI_10X_VDEV_PARAM_BCAST_DATA_RATE, + /* Set the custom rate (rate-code) for multicast data frames */ + WMI_10X_VDEV_PARAM_MCAST_DATA_RATE, + /* Tx multicast packet indicate Enable/Disable */ + WMI_10X_VDEV_PARAM_MCAST_INDICATE, + /* Tx DHCP packet indicate Enable/Disable */ + WMI_10X_VDEV_PARAM_DHCP_INDICATE, + /* Enable host inspection of Tx unicast packet to unknown destination */ + WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + + /* The minimum amount of time AP begins to consider STA inactive */ + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered inactive when there is no recent + * TX/RX activity and no downlink frames are buffered for it. Once a + * STA exceeds the maximum idle inactive time, the AP will send an + * 802.11 data-null as a keep alive to verify the STA is still + * associated. If the STA does ACK the data-null, or if the data-null + * is buffered and the STA does not retrieve it, the STA will be + * considered unresponsive + * (see WMI_10X_VDEV_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS). + */ + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + + /* + * An associated STA is considered unresponsive if there is no recent + * TX/RX activity and downlink frames are buffered for it. Once a STA + * exceeds the maximum unresponsive time, the AP will send a + * WMI_10X_STA_KICKOUT event to the host so the STA can be deleted. */ + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + + /* Enable NAWDS : MCAST INSPECT Enable, NAWDS Flag set */ + WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS, + + WMI_10X_VDEV_PARAM_MCAST2UCAST_SET, + /* Enable/Disable RTS-CTS */ + WMI_10X_VDEV_PARAM_ENABLE_RTSCTS, + + WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, +}; + /* slot time long */ #define WMI_VDEV_SLOT_TIME_LONG 0x1 /* slot time short */ @@ -3000,7 +4004,6 @@ struct wmi_force_fw_hang_cmd { #define WMI_MAX_EVENT 0x1000 /* Maximum number of pending TXed WMI packets */ -#define WMI_MAX_PENDING_TX_COUNT 128 #define WMI_SKB_HEADROOM sizeof(struct wmi_cmd_hdr) /* By default disable power save for IBSS */ @@ -3013,7 +4016,6 @@ int ath10k_wmi_attach(struct ath10k *ar); void ath10k_wmi_detach(struct ath10k *ar); int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); -void ath10k_wmi_flush_tx(struct ath10k *ar); int ath10k_wmi_connect_htc_service(struct ath10k *ar); int ath10k_wmi_pdev_set_channel(struct ath10k *ar, @@ -3022,8 +4024,7 @@ int ath10k_wmi_pdev_suspend_target(struct ath10k *ar); int ath10k_wmi_pdev_resume_target(struct ath10k *ar); int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g, u16 ctl2g, u16 ctl5g); -int ath10k_wmi_pdev_set_param(struct ath10k *ar, enum wmi_pdev_param id, - u32 value); +int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value); int ath10k_wmi_cmd_init(struct ath10k *ar); int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *); void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *); @@ -3043,7 +4044,7 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid); int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id); int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_param param_id, u32 param_value); + u32 param_id, u32 param_value); int ath10k_wmi_vdev_install_key(struct ath10k *ar, const struct wmi_vdev_install_key_arg *arg); int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, @@ -3066,11 +4067,13 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, enum wmi_ap_ps_peer_param param_id, u32 value); int ath10k_wmi_scan_chan_list(struct ath10k *ar, const struct wmi_scan_chan_list_arg *arg); -int ath10k_wmi_beacon_send(struct ath10k *ar, const struct wmi_bcn_tx_arg *arg); +int ath10k_wmi_beacon_send_nowait(struct ath10k *ar, + const struct wmi_bcn_tx_arg *arg); int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, const struct wmi_pdev_set_wmm_params_arg *arg); int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); int ath10k_wmi_force_fw_hang(struct ath10k *ar, enum wmi_force_fw_hang_type type, u32 delay_ms); +int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb); #endif /* _WMI_H_ */ diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index e9bc9e616b69c94e4c6758ae2c2327768778b942..79bffe165caba6e1f963b9fa81deed4844cb42d1 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -37,12 +37,9 @@ ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) { struct ath5k_hw *ah = common->priv; struct platform_device *pdev = to_platform_device(ah->dev); - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); u16 *eeprom, *eeprom_end; - - - bcfg = pdev->dev.platform_data; eeprom = (u16 *) bcfg->radio; eeprom_end = ((void *) bcfg->config) + BOARD_CONFIG_BUFSZ; @@ -57,7 +54,7 @@ ath5k_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) int ath5k_hw_read_srev(struct ath5k_hw *ah) { struct platform_device *pdev = to_platform_device(ah->dev); - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); ah->ah_mac_srev = bcfg->devid; return 0; } @@ -65,7 +62,7 @@ int ath5k_hw_read_srev(struct ath5k_hw *ah) static int ath5k_ahb_eeprom_read_mac(struct ath5k_hw *ah, u8 *mac) { struct platform_device *pdev = to_platform_device(ah->dev); - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); u8 *cfg_mac; if (to_platform_device(ah->dev)->id == 0) @@ -87,7 +84,7 @@ static const struct ath_bus_ops ath_ahb_bus_ops = { /*Initialization*/ static int ath_ahb_probe(struct platform_device *pdev) { - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); struct ath5k_hw *ah; struct ieee80211_hw *hw; struct resource *res; @@ -96,7 +93,7 @@ static int ath_ahb_probe(struct platform_device *pdev) int ret = 0; u32 reg; - if (!pdev->dev.platform_data) { + if (!dev_get_platdata(&pdev->dev)) { dev_err(&pdev->dev, "no platform data specified\n"); ret = -EINVAL; goto err_out; @@ -193,7 +190,7 @@ static int ath_ahb_probe(struct platform_device *pdev) static int ath_ahb_remove(struct platform_device *pdev) { - struct ar231x_board_config *bcfg = pdev->dev.platform_data; + struct ar231x_board_config *bcfg = dev_get_platdata(&pdev->dev); struct ieee80211_hw *hw = platform_get_drvdata(pdev); struct ath5k_hw *ah; u32 reg; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 48161edec8de84769fd9f3db92fa1c4aa165d70b..69f58b073e85ff1a183ec1f06e803ff9da00806c 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1663,15 +1663,15 @@ ath5k_tx_frame_completed(struct ath5k_hw *ah, struct sk_buff *skb, ah->stats.tx_bytes_count += skb->len; info = IEEE80211_SKB_CB(skb); + size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates)); + memcpy(info->status.rates, bf->rates, size); + tries[0] = info->status.rates[0].count; tries[1] = info->status.rates[1].count; tries[2] = info->status.rates[2].count; ieee80211_tx_info_clear_status(info); - size = min_t(int, sizeof(info->status.rates), sizeof(bf->rates)); - memcpy(info->status.rates, bf->rates, size); - for (i = 0; i < ts->ts_final_idx; i++) { struct ieee80211_tx_rate *r = &info->status.rates[i]; diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h index 98a886154d9cc59775eaa0c17c74305452e52e8e..05debf700a846db00f55e0071df53e207d2e6c63 100644 --- a/drivers/net/wireless/ath/ath6kl/common.h +++ b/drivers/net/wireless/ath/ath6kl/common.h @@ -22,8 +22,7 @@ #define ATH6KL_MAX_IE 256 -extern __printf(2, 3) -int ath6kl_printk(const char *level, const char *fmt, ...); +__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...); /* * Reflects the version of binary interface exposed by ATH6KL target diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 74369de00fb57a40dcebd72ced2002d0cb571b24..ca9ba005f2871f3e42bbc914bb5ca90f6e8b90e9 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -50,11 +50,10 @@ enum ATH6K_DEBUG_MASK { }; extern unsigned int debug_mask; -extern __printf(2, 3) -int ath6kl_printk(const char *level, const char *fmt, ...); -extern __printf(1, 2) int ath6kl_info(const char *fmt, ...); -extern __printf(1, 2) int ath6kl_err(const char *fmt, ...); -extern __printf(1, 2) int ath6kl_warn(const char *fmt, ...); +__printf(2, 3) int ath6kl_printk(const char *level, const char *fmt, ...); +__printf(1, 2) int ath6kl_info(const char *fmt, ...); +__printf(1, 2) int ath6kl_err(const char *fmt, ...); +__printf(1, 2) int ath6kl_warn(const char *fmt, ...); enum ath6kl_war { ATH6KL_WAR_INVALID_RATE, diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h index a2c8ff8097939240f4fecc4e238203b6a5c7de73..14cab1403dd6071d48179922346b676d89e9610b 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.h +++ b/drivers/net/wireless/ath/ath6kl/htc.h @@ -60,7 +60,7 @@ /* disable credit flow control on a specific service */ #define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL (1 << 3) #define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT 8 -#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00 +#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00U /* connect response status codes */ #define HTC_SERVICE_SUCCESS 0 diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 7944c25c9a43d54940ffc3e5ee9bc95591fe69d6..32f139e2e897e255b4c378c33c6d99df8abd51e0 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -84,6 +84,26 @@ config ATH9K_DFS_CERTIFIED developed. At this point enabling this option won't do anything except increase code size. +config ATH9K_TX99 + bool "Atheros ath9k TX99 testing support" + depends on CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled on systems undergoing + certification testing and evaluation in a controlled environment. + Enabling this will only enable TX99 support, all other modes of + operation will be disabled. + + TX99 support enables Specific Absorption Rate (SAR) testing. + SAR is the unit of measurement for the amount of radio frequency(RF) + absorbed by the body when using a wireless device. The RF exposure + limits used are expressed in the terms of SAR, which is a measure + of the electric and magnetic field strength and power density for + transmitters operating at frequencies from 300 kHz to 100 GHz. + Regulatory bodies around the world require that wireless device + be evaluated to meet the RF exposure limits set forth in the + governmental SAR regulations. + config ATH9K_LEGACY_RATE_CONTROL bool "Atheros ath9k rate control" depends on ATH9K diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 75ee9e7704ce627eb52939c25d5ea0b6d1eb4f3d..6205ef5a9321e8072c283d24e8e7814b2a8392df 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -14,9 +14,7 @@ ath9k-$(CONFIG_ATH9K_AHB) += ahb.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \ - dfs.o \ - dfs_pattern_detector.o \ - dfs_pri_detector.o + dfs.o ath9k-$(CONFIG_PM_SLEEP) += wow.o obj-$(CONFIG_ATH9K) += ath9k.o diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 072e4b53106765ce8e0d6c833652501a7abde62d..2dff2765769bb339eea79b05877414b6b9bdc0f9 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -54,7 +54,7 @@ static bool ath_ahb_eeprom_read(struct ath_common *common, u32 off, u16 *data) struct platform_device *pdev = to_platform_device(sc->dev); struct ath9k_platform_data *pdata; - pdata = (struct ath9k_platform_data *) pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (off >= (ARRAY_SIZE(pdata->eeprom_data))) { ath_err(common, "%s: flash read failed, offset %08x is out of range\n", @@ -84,7 +84,7 @@ static int ath_ahb_probe(struct platform_device *pdev) struct ath_hw *ah; char hw_name[64]; - if (!pdev->dev.platform_data) { + if (!dev_get_platdata(&pdev->dev)) { dev_err(&pdev->dev, "no platform data specified\n"); return -EINVAL; } diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index be466b0ef7a7725c736114ee439596ca45a4a380..d28923b7435b257f13a91e3f8896115c30adb1c9 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -338,10 +338,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) aniState->cckNoiseImmunityLevel != ATH9K_ANI_CCK_DEF_LEVEL) { ath_dbg(common, ANI, - "Restore defaults: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n", + "Restore defaults: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n", ah->opmode, chan->channel, - chan->channelFlags, is_scanning, aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); @@ -354,10 +353,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) * restore historical levels for this channel */ ath_dbg(common, ANI, - "Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n", + "Restore history: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n", ah->opmode, chan->channel, - chan->channelFlags, is_scanning, aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c index dd1cc73d7946104d2353a777586c6da4bf3c6f67..bd048cc69a334d6234b6967ecff5e1aec8b75fbe 100644 --- a/drivers/net/wireless/ath/ath9k/antenna.c +++ b/drivers/net/wireless/ath/ath9k/antenna.c @@ -332,7 +332,7 @@ static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, } if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + - ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA) + div_ant_conf->lna1_lna2_switch_delta) div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; else div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; @@ -554,42 +554,22 @@ static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, ant_conf->fast_div_bias = 0x1; break; case 0x10: /* LNA2 A-B */ - if ((antcomb->scan == 0) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { - ant_conf->fast_div_bias = 0x3f; - } else { - ant_conf->fast_div_bias = 0x1; - } + ant_conf->fast_div_bias = 0x2; break; case 0x12: /* LNA2 LNA1 */ - ant_conf->fast_div_bias = 0x39; + ant_conf->fast_div_bias = 0x3f; break; case 0x13: /* LNA2 A+B */ - if ((antcomb->scan == 0) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { - ant_conf->fast_div_bias = 0x3f; - } else { - ant_conf->fast_div_bias = 0x1; - } + ant_conf->fast_div_bias = 0x2; break; case 0x20: /* LNA1 A-B */ - if ((antcomb->scan == 0) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { - ant_conf->fast_div_bias = 0x3f; - } else { - ant_conf->fast_div_bias = 0x4; - } + ant_conf->fast_div_bias = 0x3; break; case 0x21: /* LNA1 LNA2 */ - ant_conf->fast_div_bias = 0x6; + ant_conf->fast_div_bias = 0x3; break; case 0x23: /* LNA1 A+B */ - if ((antcomb->scan == 0) && - (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { - ant_conf->fast_div_bias = 0x3f; - } else { - ant_conf->fast_div_bias = 0x6; - } + ant_conf->fast_div_bias = 0x3; break; case 0x30: /* A+B A-B */ ant_conf->fast_div_bias = 0x1; @@ -638,7 +618,7 @@ static void ath_ant_try_scan(struct ath_ant_comb *antcomb, antcomb->rssi_sub = alt_rssi_avg; antcomb->scan = false; if (antcomb->rssi_lna2 > - (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { + (antcomb->rssi_lna1 + conf->lna1_lna2_switch_delta)) { /* use LNA2 as main LNA */ if ((antcomb->rssi_add > antcomb->rssi_lna1) && (antcomb->rssi_add > antcomb->rssi_sub)) { diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 08656473c63e5a796b37bcd25f087504d85433fb..ff415e863ee9cc5140879ca008df366b8f1ad967 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -626,12 +626,11 @@ static void ar5008_hw_override_ini(struct ath_hw *ah, if (AR_SREV_9287_11_OR_LATER(ah)) val = val & (~AR_PCU_MISC_MODE2_HWWAR2); + val |= AR_PCU_MISC_MODE2_CFP_IGNORE; + REG_WRITE(ah, AR_PCU_MISC_MODE2, val); } - REG_SET_BIT(ah, AR_PHY_CCK_DETECT, - AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); - if (AR_SREV_9280_20_OR_LATER(ah)) return; /* @@ -667,14 +666,13 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah, if (IS_CHAN_HT40(chan)) { phymode |= AR_PHY_FC_DYN2040_EN; - if ((chan->chanmode == CHANNEL_A_HT40PLUS) || - (chan->chanmode == CHANNEL_G_HT40PLUS)) + if (IS_CHAN_HT40PLUS(chan)) phymode |= AR_PHY_FC_DYN2040_PRI_CH; } REG_WRITE(ah, AR_PHY_TURBO, phymode); - ath9k_hw_set11nmac2040(ah); + ath9k_hw_set11nmac2040(ah, chan); ENABLE_REGWRITE_BUFFER(ah); @@ -692,31 +690,12 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, int i, regWrites = 0; u32 modesIndex, freqIndex; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - modesIndex = 1; - freqIndex = 1; - break; - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - modesIndex = 2; + if (IS_CHAN_5GHZ(chan)) { freqIndex = 1; - break; - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - modesIndex = 4; - freqIndex = 2; - break; - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - modesIndex = 3; + modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; + } else { freqIndex = 2; - break; - - default: - return -EINVAL; + modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; } /* @@ -815,8 +794,10 @@ static void ar5008_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan) if (chan == NULL) return; - rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) - ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; + if (IS_CHAN_2GHZ(chan)) + rfMode |= AR_PHY_MODE_DYNAMIC; + else + rfMode |= AR_PHY_MODE_OFDM; if (!AR_SREV_9280_20_OR_LATER(ah)) rfMode |= (IS_CHAN_5GHZ(chan)) ? @@ -1219,12 +1200,11 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) iniDef = &aniState->iniDef; - ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", + ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n", ah->hw_version.macVersion, ah->hw_version.macRev, ah->opmode, - chan->channel, - chan->channelFlags); + chan->channel); val = REG_READ(ah, AR_PHY_SFCORR); iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index 9f589744a9f945b285ca1c2988d795488cbf7a47..cdc74005650ce9848147ee0e051c64f9df989ffd 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -33,15 +33,12 @@ static bool ar9002_hw_is_cal_supported(struct ath_hw *ah, bool supported = false; switch (ah->supp_cals & cal_type) { case IQ_MISMATCH_CAL: - /* Run IQ Mismatch for non-CCK only */ - if (!IS_CHAN_B(chan)) - supported = true; + supported = true; break; case ADC_GAIN_CAL: case ADC_DC_CAL: /* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */ - if (!IS_CHAN_B(chan) && - !((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) && + if (!((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) && IS_CHAN_HT20(chan))) supported = true; break; @@ -671,7 +668,7 @@ static bool ar9002_hw_calibrate(struct ath_hw *ah, nfcal = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF); if (ah->caldata) - nfcal_pending = ah->caldata->nfcal_pending; + nfcal_pending = test_bit(NFCAL_PENDING, &ah->caldata->cal_flags); if (currCal && !nfcal && (currCal->calState == CAL_RUNNING || @@ -861,7 +858,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) ar9002_hw_pa_cal(ah, true); if (ah->caldata) - ah->caldata->nfcal_pending = true; + set_bit(NFCAL_PENDING, &ah->caldata->cal_flags); ah->cal_list = ah->cal_list_last = ah->cal_list_curr = NULL; diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index fb61b081d1721124518f7c4cad85792cedaefac0..5c95fd9e9c9e9c861edb283a2a8cfbf4a9b5e3b9 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -419,28 +419,10 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan) u32 modesIndex; int i; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - modesIndex = 1; - break; - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - modesIndex = 2; - break; - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - modesIndex = 4; - break; - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - modesIndex = 3; - break; - - default: - return; - } + if (IS_CHAN_5GHZ(chan)) + modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; + else + modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; ENABLE_REGWRITE_BUFFER(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 1fc1fa955d44fff8ddd60e7f231701f873df5473..f087117b2e6b6b2592ae8eb7a2270314cffb7325 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -485,7 +485,7 @@ static void ar9002_hw_do_getnf(struct ath_hw *ah, if (IS_CHAN_HT40(ah->curchan)) nfarray[3] = sign_extend32(nf, 8); - if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) + if (!(ah->rxchainmask & BIT(1))) return; nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR9280_PHY_CH1_MINCCA_PWR); @@ -532,6 +532,7 @@ static void ar9002_hw_antdiv_comb_conf_get(struct ath_hw *ah, AR_PHY_9285_ANT_DIV_ALT_LNACONF_S; antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >> AR_PHY_9285_FAST_DIV_BIAS_S; + antconf->lna1_lna2_switch_delta = -1; antconf->lna1_lna2_delta = -3; antconf->div_group = 0; } @@ -679,6 +680,26 @@ static void ar9002_hw_spectral_scan_wait(struct ath_hw *ah) } } +static void ar9002_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + REG_SET_BIT(ah, 0x9864, 0x7f000); + REG_SET_BIT(ah, 0x9924, 0x7f00fe); + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + REG_WRITE(ah, AR_CR, AR_CR_RXD); + REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); + REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); + REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20); + REG_WRITE(ah, AR_D_FPCTL, 0x10|qnum); + REG_WRITE(ah, AR_TIME_OUT, 0x00000400); + REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff); + REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ); +} + +static void ar9002_hw_tx99_stop(struct ath_hw *ah) +{ + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); +} + void ar9002_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -700,6 +721,8 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah) #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity; #endif + ops->tx99_start = ar9002_hw_tx99_start; + ops->tx99_stop = ar9002_hw_tx99_stop; ar9002_hw_set_nf_limits(ah); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 6988e1d081f225c0c0a8efd0dc49fbacb4fc6a1a..22934d3ca54413fa9558a928fdccbc4ab108131a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -727,8 +727,12 @@ static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_RX_IQCAL_CORR_B0, AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); - if (caldata) - caldata->done_txiqcal_once = is_reusable; + if (caldata) { + if (is_reusable) + set_bit(TXIQCAL_DONE, &caldata->cal_flags); + else + clear_bit(TXIQCAL_DONE, &caldata->cal_flags); + } return; } @@ -961,18 +965,44 @@ static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) } static void ar9003_hw_do_manual_peak_cal(struct ath_hw *ah, - struct ath9k_channel *chan) + struct ath9k_channel *chan, + bool run_rtt_cal) { + struct ath9k_hw_cal_data *caldata = ah->caldata; int i; if (!AR_SREV_9462(ah) && !AR_SREV_9565(ah) && !AR_SREV_9485(ah)) return; + if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && !run_rtt_cal) + return; + for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->rxchainmask & (1 << i))) continue; ar9003_hw_manual_peak_cal(ah, i, IS_CHAN_2GHZ(chan)); } + + if (caldata) + set_bit(SW_PKDET_DONE, &caldata->cal_flags); + + if ((ah->caps.hw_caps & ATH9K_HW_CAP_RTT) && caldata) { + if (IS_CHAN_2GHZ(chan)){ + caldata->caldac[0] = REG_READ_FIELD(ah, + AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR); + caldata->caldac[1] = REG_READ_FIELD(ah, + AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR); + } else { + caldata->caldac[0] = REG_READ_FIELD(ah, + AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR); + caldata->caldac[1] = REG_READ_FIELD(ah, + AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR); + } + } } static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable) @@ -990,7 +1020,7 @@ static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable) txclcal_done = !!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_CLC_SUCCESS); - if (caldata->done_txclcal_once) { + if (test_bit(TXCLCAL_DONE, &caldata->cal_flags)) { for (i = 0; i < AR9300_MAX_CHAINS; i++) { if (!(ah->txchainmask & (1 << i))) continue; @@ -1006,7 +1036,7 @@ static void ar9003_hw_cl_cal_post_proc(struct ath_hw *ah, bool is_reusable) caldata->tx_clcal[i][j] = REG_READ(ah, CL_TAB_ENTRY(cl_idx[i])); } - caldata->done_txclcal_once = true; + set_bit(TXCLCAL_DONE, &caldata->cal_flags); } } @@ -1019,6 +1049,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, bool is_reusable = true, status = true; bool run_rtt_cal = false, run_agc_cal, sep_iq_cal = false; bool rtt = !!(ah->caps.hw_caps & ATH9K_HW_CAP_RTT); + u32 rx_delay = 0; u32 agc_ctrl = 0, agc_supp_cals = AR_PHY_AGC_CONTROL_OFFSET_CAL | AR_PHY_AGC_CONTROL_FLTR_CAL | AR_PHY_AGC_CONTROL_PKDET_CAL; @@ -1042,17 +1073,22 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, ar9003_hw_rtt_clear_hist(ah); } - if (rtt && !run_rtt_cal) { - agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL); - agc_supp_cals &= agc_ctrl; - agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL | - AR_PHY_AGC_CONTROL_FLTR_CAL | - AR_PHY_AGC_CONTROL_PKDET_CAL); - REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); + if (rtt) { + if (!run_rtt_cal) { + agc_ctrl = REG_READ(ah, AR_PHY_AGC_CONTROL); + agc_supp_cals &= agc_ctrl; + agc_ctrl &= ~(AR_PHY_AGC_CONTROL_OFFSET_CAL | + AR_PHY_AGC_CONTROL_FLTR_CAL | + AR_PHY_AGC_CONTROL_PKDET_CAL); + REG_WRITE(ah, AR_PHY_AGC_CONTROL, agc_ctrl); + } else { + if (ah->ah_flags & AH_FASTCC) + run_agc_cal = true; + } } if (ah->enabled_cals & TX_CL_CAL) { - if (caldata && caldata->done_txclcal_once) + if (caldata && test_bit(TXCLCAL_DONE, &caldata->cal_flags)) REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); else { @@ -1076,14 +1112,14 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, * AGC calibration */ if (ah->enabled_cals & TX_IQ_ON_AGC_CAL) { - if (caldata && !caldata->done_txiqcal_once) + if (caldata && !test_bit(TXIQCAL_DONE, &caldata->cal_flags)) REG_SET_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); else REG_CLR_BIT(ah, AR_PHY_TX_IQCAL_CONTROL_0, AR_PHY_TX_IQCAL_CONTROL_0_ENABLE_TXIQ_CAL); txiqcal_done = run_agc_cal = true; - } else if (caldata && !caldata->done_txiqcal_once) { + } else if (caldata && !test_bit(TXIQCAL_DONE, &caldata->cal_flags)) { run_agc_cal = true; sep_iq_cal = true; } @@ -1099,6 +1135,15 @@ skip_tx_iqcal: REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); } + if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) { + rx_delay = REG_READ(ah, AR_PHY_RX_DELAY); + /* Disable BB_active */ + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); + udelay(5); + REG_WRITE(ah, AR_PHY_RX_DELAY, AR_PHY_RX_DELAY_DELAY); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + } + if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { /* Calibrate the AGC */ REG_WRITE(ah, AR_PHY_AGC_CONTROL, @@ -1110,7 +1155,12 @@ skip_tx_iqcal: AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT); - ar9003_hw_do_manual_peak_cal(ah, chan); + ar9003_hw_do_manual_peak_cal(ah, chan, run_rtt_cal); + } + + if (REG_READ(ah, AR_PHY_CL_CAL_CTL) & AR_PHY_CL_CAL_ENABLE) { + REG_WRITE(ah, AR_PHY_RX_DELAY, rx_delay); + udelay(5); } if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) @@ -1133,19 +1183,23 @@ skip_tx_iqcal: if (txiqcal_done) ar9003_hw_tx_iq_cal_post_proc(ah, is_reusable); - else if (caldata && caldata->done_txiqcal_once) + else if (caldata && test_bit(TXIQCAL_DONE, &caldata->cal_flags)) ar9003_hw_tx_iq_cal_reload(ah); ar9003_hw_cl_cal_post_proc(ah, is_reusable); if (run_rtt_cal && caldata) { if (is_reusable) { - if (!ath9k_hw_rfbus_req(ah)) + if (!ath9k_hw_rfbus_req(ah)) { ath_err(ath9k_hw_common(ah), "Could not stop baseband\n"); - else + } else { ar9003_hw_rtt_fill_hist(ah); + if (test_bit(SW_PKDET_DONE, &caldata->cal_flags)) + ar9003_hw_rtt_load_hist(ah); + } + ath9k_hw_rfbus_done(ah); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index f4864807e15bc9fd127c439cb4770d7ff75b1c8a..1ec52356b5a16956dda70a2f7365103497b43c68 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -2991,7 +2991,10 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah, case EEP_CHAIN_MASK_REDUCE: return (pBase->miscConfiguration >> 0x3) & 0x1; case EEP_ANT_DIV_CTL1: - return eep->base_ext1.ant_div_control; + if (AR_SREV_9565(ah)) + return AR9300_EEP_ANTDIV_CONTROL_DEFAULT_VALUE; + else + return eep->base_ext1.ant_div_control; case EEP_ANTENNA_GAIN_5G: return eep->modalHeader5G.antennaGain; case EEP_ANTENNA_GAIN_2G: @@ -3424,12 +3427,12 @@ static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, struct ar9300_base_eep_hdr *pBase; if (!dump_base_hdr) { - len += snprintf(buf + len, size - len, - "%20s :\n", "2GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "2GHz modal Header"); len = ar9003_dump_modal_eeprom(buf, len, size, &eep->modalHeader2G); - len += snprintf(buf + len, size - len, - "%20s :\n", "5GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "5GHz modal Header"); len = ar9003_dump_modal_eeprom(buf, len, size, &eep->modalHeader5G); goto out; @@ -3479,8 +3482,8 @@ static u32 ath9k_hw_ar9003_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, PR_EEP("Rx Gain", pBase->txrxgain & 0xf); PR_EEP("SW Reg", le32_to_cpu(pBase->swreg)); - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - ah->eeprom.ar9300_eep.macAddr); + len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", + ah->eeprom.ar9300_eep.macAddr); out: if (len > size) len = size; @@ -3656,9 +3659,23 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) if (AR_SREV_9565(ah)) { if (common->bt_ant_diversity) { regval |= (1 << AR_PHY_ANT_SW_RX_PROT_S); + + REG_SET_BIT(ah, AR_PHY_RESTART, + AR_PHY_RESTART_ENABLE_DIV_M2FLAG); + + /* Force WLAN LNA diversity ON */ + REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, + AR_BTCOEX_WL_LNADIV_FORCE_ON); } else { regval &= ~(1 << AR_PHY_ANT_DIV_LNADIV_S); regval &= ~(1 << AR_PHY_ANT_SW_RX_PROT_S); + + REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, + (1 << AR_PHY_ANT_SW_RX_PROT_S)); + + /* Force WLAN LNA diversity OFF */ + REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV, + AR_BTCOEX_WL_LNADIV_FORCE_ON); } } @@ -3669,7 +3686,8 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) regval &= (~AR_FAST_DIV_ENABLE); regval |= ((value >> 7) & 0x1) << AR_FAST_DIV_ENABLE_S; - if (AR_SREV_9485(ah) && common->bt_ant_diversity) + if ((AR_SREV_9485(ah) || AR_SREV_9565(ah)) + && common->bt_ant_diversity) regval |= AR_FAST_DIV_ENABLE; REG_WRITE(ah, AR_PHY_CCK_DETECT, regval); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h index 75d4fb41962f312567cd161c900b9d3578007f0d..0e5daa58a4fc14371d360bdc5aa22937902bc824 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h @@ -52,6 +52,8 @@ #define AR9300_PAPRD_SCALE_2 0x70000000 #define AR9300_PAPRD_SCALE_2_S 28 +#define AR9300_EEP_ANTDIV_CONTROL_DEFAULT_VALUE 0xc9 + /* Delta from which to start power to pdadc table */ /* This offset is used in both open loop and closed loop power control * schemes. In open loop power control, it is not really needed, but for diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 608bb4824e2a2f800c057ff1e717f9219e8414db..b07f164d65cf582a63c2ddfbfccaced509a6ceca 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -364,6 +364,8 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesFastClock, ar9565_1p0_modes_fast_clock); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9565_1p0_baseband_core_txfir_coeff_japan_2484); } else { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], @@ -628,6 +630,9 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) else if (AR_SREV_9462_20(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9462_common_rx_gain_table_2p0); + else if (AR_SREV_9565(ah)) + INIT_INI_ARRAY(&ah->iniModesRxGain, + ar9565_1p0_Common_rx_gain_table); else INIT_INI_ARRAY(&ah->iniModesRxGain, ar9300Common_rx_gain_table_2p2); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 8dd069259e7b7ea7d9dd40212571cab73424f131..7b94a6c7db3d50dd4feb53dd74e1973bd68f2e6d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -753,9 +753,9 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, 1 << AR_PHY_TIMING_CONTROL4_DO_GAIN_DC_IQ_CAL_SHIFT); if (caldata) { - caldata->done_txiqcal_once = false; - caldata->done_txclcal_once = false; - caldata->rtt_done = false; + clear_bit(TXIQCAL_DONE, &caldata->cal_flags); + clear_bit(TXCLCAL_DONE, &caldata->cal_flags); + clear_bit(RTT_DONE, &caldata->cal_flags); } if (!ath9k_hw_init_cal(ah, chan)) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index e897648d32335dd1800df489916afa80fca81a2b..11f53589a3f34879b6ab3e8d9062e294fb6f7260 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -551,8 +551,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah, if (IS_CHAN_HT40(chan)) { phymode |= AR_PHY_GC_DYN2040_EN; /* Configure control (primary) channel at +-10MHz */ - if ((chan->chanmode == CHANNEL_A_HT40PLUS) || - (chan->chanmode == CHANNEL_G_HT40PLUS)) + if (IS_CHAN_HT40PLUS(chan)) phymode |= AR_PHY_GC_DYN2040_PRI_CH; } @@ -565,7 +564,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah, REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode); /* Configure MAC for 20/40 operation */ - ath9k_hw_set11nmac2040(ah); + ath9k_hw_set11nmac2040(ah, chan); /* global transmit timeout (25 TUs default)*/ REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); @@ -627,11 +626,10 @@ static void ar9003_hw_override_ini(struct ath_hw *ah) * MAC addr only will fail. */ val = REG_READ(ah, AR_PCU_MISC_MODE2) & (~AR_ADHOC_MCAST_KEYID_ENABLE); - REG_WRITE(ah, AR_PCU_MISC_MODE2, - val | AR_AGG_WEP_ENABLE_FIX | AR_AGG_WEP_ENABLE); - - REG_SET_BIT(ah, AR_PHY_CCK_DETECT, - AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + val |= AR_AGG_WEP_ENABLE_FIX | + AR_AGG_WEP_ENABLE | + AR_PCU_MISC_MODE2_CFP_IGNORE; + REG_WRITE(ah, AR_PCU_MISC_MODE2, val); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE, @@ -683,41 +681,22 @@ static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah, { int ret; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - if (chan->channel <= 5350) - ret = 1; - else if ((chan->channel > 5350) && (chan->channel <= 5600)) - ret = 3; - else - ret = 5; - break; - - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - if (chan->channel <= 5350) - ret = 2; - else if ((chan->channel > 5350) && (chan->channel <= 5600)) - ret = 4; + if (IS_CHAN_2GHZ(chan)) { + if (IS_CHAN_HT40(chan)) + return 7; else - ret = 6; - break; - - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - ret = 8; - break; + return 8; + } - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - ret = 7; - break; + if (chan->channel <= 5350) + ret = 1; + else if ((chan->channel > 5350) && (chan->channel <= 5600)) + ret = 3; + else + ret = 5; - default: - ret = -EINVAL; - } + if (IS_CHAN_HT40(chan)) + ret++; return ret; } @@ -728,28 +707,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, unsigned int regWrites = 0, i; u32 modesIndex; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - modesIndex = 1; - break; - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - modesIndex = 2; - break; - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - modesIndex = 4; - break; - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - modesIndex = 3; - break; - - default: - return -EINVAL; - } + if (IS_CHAN_5GHZ(chan)) + modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; + else + modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; /* * SOC, MAC, BB, RADIO initvals. @@ -847,8 +808,10 @@ static void ar9003_hw_set_rfmode(struct ath_hw *ah, if (chan == NULL) return; - rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) - ? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; + if (IS_CHAN_2GHZ(chan)) + rfMode |= AR_PHY_MODE_DYNAMIC; + else + rfMode |= AR_PHY_MODE_OFDM; if (IS_CHAN_A_FAST_CLOCK(ah, chan)) rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); @@ -1274,12 +1237,11 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) aniState = &ah->ani; iniDef = &aniState->iniDef; - ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n", + ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n", ah->hw_version.macVersion, ah->hw_version.macRev, ah->opmode, - chan->channel, - chan->channelFlags); + chan->channel); val = REG_READ(ah, AR_PHY_SFCORR); iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH); @@ -1375,15 +1337,19 @@ static void ar9003_hw_antdiv_comb_conf_get(struct ath_hw *ah, AR_PHY_ANT_FAST_DIV_BIAS_S; if (AR_SREV_9330_11(ah)) { + antconf->lna1_lna2_switch_delta = -1; antconf->lna1_lna2_delta = -9; antconf->div_group = 1; } else if (AR_SREV_9485(ah)) { + antconf->lna1_lna2_switch_delta = -1; antconf->lna1_lna2_delta = -9; antconf->div_group = 2; } else if (AR_SREV_9565(ah)) { - antconf->lna1_lna2_delta = -3; + antconf->lna1_lna2_switch_delta = 3; + antconf->lna1_lna2_delta = -9; antconf->div_group = 3; } else { + antconf->lna1_lna2_switch_delta = -1; antconf->lna1_lna2_delta = -3; antconf->div_group = 0; } @@ -1488,18 +1454,25 @@ static void ar9003_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) } } else if (AR_SREV_9565(ah)) { if (enable) { + REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL, + AR_ANT_DIV_ENABLE); REG_SET_BIT(ah, AR_PHY_MC_GAIN_CTRL, (1 << AR_PHY_ANT_SW_RX_PROT_S)); - if (ah->curchan && IS_CHAN_2GHZ(ah->curchan)) - REG_SET_BIT(ah, AR_PHY_RESTART, - AR_PHY_RESTART_ENABLE_DIV_M2FLAG); + REG_SET_BIT(ah, AR_PHY_CCK_DETECT, + AR_FAST_DIV_ENABLE); + REG_SET_BIT(ah, AR_PHY_RESTART, + AR_PHY_RESTART_ENABLE_DIV_M2FLAG); REG_SET_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); } else { - REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, AR_ANT_DIV_ENABLE); + REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, + AR_ANT_DIV_ENABLE); REG_CLR_BIT(ah, AR_PHY_MC_GAIN_CTRL, (1 << AR_PHY_ANT_SW_RX_PROT_S)); - REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, AR_FAST_DIV_ENABLE); + REG_CLR_BIT(ah, AR_PHY_CCK_DETECT, + AR_FAST_DIV_ENABLE); + REG_CLR_BIT(ah, AR_PHY_RESTART, + AR_PHY_RESTART_ENABLE_DIV_M2FLAG); REG_CLR_BIT(ah, AR_BTCOEX_WL_LNADIV, AR_BTCOEX_WL_LNADIV_FORCE_ON); @@ -1526,28 +1499,10 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah, unsigned int regWrites = 0; u32 modesIndex; - switch (chan->chanmode) { - case CHANNEL_A: - case CHANNEL_A_HT20: - modesIndex = 1; - break; - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - modesIndex = 2; - break; - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_B: - modesIndex = 4; - break; - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - modesIndex = 3; - break; - - default: - return -EINVAL; - } + if (IS_CHAN_5GHZ(chan)) + modesIndex = IS_CHAN_HT40(chan) ? 2 : 1; + else + modesIndex = IS_CHAN_HT40(chan) ? 3 : 4; if (modesIndex == ah->modes_index) { *ini_reloaded = false; @@ -1662,6 +1617,98 @@ static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah) } } +static void ar9003_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + REG_SET_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR); + REG_SET_BIT(ah, 0x9864, 0x7f000); + REG_SET_BIT(ah, 0x9924, 0x7f00fe); + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + REG_WRITE(ah, AR_CR, AR_CR_RXD); + REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); + REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); /* 50 OK */ + REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20); + REG_WRITE(ah, AR_TIME_OUT, 0x00000400); + REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff); + REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ); +} + +static void ar9003_hw_tx99_stop(struct ath_hw *ah) +{ + REG_CLR_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR); + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); +} + +static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower) +{ + static s16 p_pwr_array[ar9300RateSize] = { 0 }; + unsigned int i; + + if (txpower <= MAX_RATE_POWER) { + for (i = 0; i < ar9300RateSize; i++) + p_pwr_array[i] = txpower; + } else { + for (i = 0; i < ar9300RateSize; i++) + p_pwr_array[i] = MAX_RATE_POWER; + } + + REG_WRITE(ah, 0xa458, 0); + + REG_WRITE(ah, 0xa3c0, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0)); + REG_WRITE(ah, 0xa3c4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_54], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_48], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_36], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0)); + REG_WRITE(ah, 0xa3c8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0)); + REG_WRITE(ah, 0xa3cc, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11S], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11L], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_5S], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0)); + REG_WRITE(ah, 0xa3d0, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_5], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_4], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_1_3_9_11_17_19], 8)| + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_0_8_16], 0)); + REG_WRITE(ah, 0xa3d4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_13], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_12], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_7], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_6], 0)); + REG_WRITE(ah, 0xa3e4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_21], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_20], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_15], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_14], 0)); + REG_WRITE(ah, 0xa3e8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_23], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_22], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_23], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_22], 0)); + REG_WRITE(ah, 0xa3d8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_5], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_4], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_1_3_9_11_17_19], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_0_8_16], 0)); + REG_WRITE(ah, 0xa3dc, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_13], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_12], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_7], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_6], 0)); + REG_WRITE(ah, 0xa3ec, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_21], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_20], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_15], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14], 0)); +} + void ar9003_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -1701,6 +1748,9 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity; #endif + ops->tx99_start = ar9003_hw_tx99_start; + ops->tx99_stop = ar9003_hw_tx99_stop; + ops->tx99_set_txpower = ar9003_hw_tx99_set_txpower; ar9003_hw_set_nf_limits(ah); ar9003_hw_set_radar_conf(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 6fd752321e3616c171c81be16a754b31486de667..fca624322dc8886f991632d7a2d5e78b0bdfa114 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -343,8 +343,12 @@ #define AR_PHY_CCA_NOM_VAL_9462_2GHZ -127 #define AR_PHY_CCA_MIN_GOOD_VAL_9462_2GHZ -127 +#define AR_PHY_CCA_MAX_GOOD_VAL_9462_2GHZ -60 +#define AR_PHY_CCA_MAX_GOOD_VAL_9462_FCC_2GHZ -95 #define AR_PHY_CCA_NOM_VAL_9462_5GHZ -127 #define AR_PHY_CCA_MIN_GOOD_VAL_9462_5GHZ -127 +#define AR_PHY_CCA_MAX_GOOD_VAL_9462_5GHZ -60 +#define AR_PHY_CCA_MAX_GOOD_VAL_9462_FCC_5GHZ -100 #define AR_PHY_CCA_NOM_VAL_9330_2GHZ -118 diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c index 74de3539c2c8337680bb273933ea7158b0e946ec..934418872e8e156a641145a512280f3bee6f86bd 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c @@ -118,6 +118,27 @@ void ar9003_hw_rtt_load_hist(struct ath_hw *ah) } } +static void ar9003_hw_patch_rtt(struct ath_hw *ah, int index, int chain) +{ + int agc, caldac; + + if (!test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags)) + return; + + if ((index != 5) || (chain >= 2)) + return; + + agc = REG_READ_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE); + if (!agc) + return; + + caldac = ah->caldata->caldac[chain]; + ah->caldata->rtt_table[chain][index] &= 0xFFFF05FF; + caldac = (caldac & 0x20) | ((caldac & 0x1F) << 7); + ah->caldata->rtt_table[chain][index] |= (caldac << 4); +} + static int ar9003_hw_rtt_fill_hist_entry(struct ath_hw *ah, u8 chain, u32 index) { u32 val; @@ -155,13 +176,16 @@ void ar9003_hw_rtt_fill_hist(struct ath_hw *ah) for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) { ah->caldata->rtt_table[chain][i] = ar9003_hw_rtt_fill_hist_entry(ah, chain, i); + + ar9003_hw_patch_rtt(ah, i, chain); + ath_dbg(ath9k_hw_common(ah), CALIBRATE, "RTT value at idx %d, chain %d is: 0x%x\n", i, chain, ah->caldata->rtt_table[chain][i]); } } - ah->caldata->rtt_done = true; + set_bit(RTT_DONE, &ah->caldata->cal_flags); } void ar9003_hw_rtt_clear_hist(struct ath_hw *ah) @@ -176,7 +200,7 @@ void ar9003_hw_rtt_clear_hist(struct ath_hw *ah) } if (ah->caldata) - ah->caldata->rtt_done = false; + clear_bit(RTT_DONE, &ah->caldata->cal_flags); } bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan) @@ -186,11 +210,37 @@ bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan) if (!ah->caldata) return false; - if (!ah->caldata->rtt_done) + if (test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags)) { + if (IS_CHAN_2GHZ(chan)){ + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, + ah->caldata->caldac[0]); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, + ah->caldata->caldac[1]); + } else { + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, + ah->caldata->caldac[0]); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, + ah->caldata->caldac[1]); + } + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(1), + AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(0), + AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1); + } + + if (!test_bit(RTT_DONE, &ah->caldata->cal_flags)) return false; ar9003_hw_rtt_enable(ah); - ar9003_hw_rtt_set_mask(ah, 0x10); + + if (test_bit(SW_PKDET_DONE, &ah->caldata->cal_flags)) + ar9003_hw_rtt_set_mask(ah, 0x30); + else + ar9003_hw_rtt_set_mask(ah, 0x10); if (!ath9k_hw_rfbus_req(ah)) { ath_err(ath9k_hw_common(ah), "Could not stop baseband\n"); diff --git a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h index 88ff1d7b53ab41c642333d553646e90065c3045b..6f899c6926474ba2f60cd642399e10d61814ec3d 100644 --- a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h @@ -20,7 +20,17 @@ /* AR9485 1.1 */ -#define ar9485_1_1_mac_postamble ar9300_2p2_mac_postamble +static const u32 ar9485_1_1_mac_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, + {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, + {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, + {0x00008014, 0x03e803e8, 0x07d007d0, 0x10801600, 0x08400b00}, + {0x0000801c, 0x128d8027, 0x128d804f, 0x12e00057, 0x12e0002b}, + {0x00008120, 0x08f04800, 0x08f04800, 0x08f04810, 0x08f04810}, + {0x000081d0, 0x00003210, 0x00003210, 0x0000320a, 0x0000320a}, + {0x00008318, 0x00003e80, 0x00007d00, 0x00006880, 0x00003440}, +}; static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { /* Addr allmodes */ @@ -34,6 +44,7 @@ static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { {0x00009e00, 0x037216a0}, {0x00009e04, 0x00182020}, {0x00009e18, 0x00000000}, + {0x00009e20, 0x000003a8}, {0x00009e2c, 0x00004121}, {0x00009e44, 0x02282324}, {0x0000a000, 0x00060005}, @@ -174,7 +185,7 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050da, 0x000050da}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, @@ -200,14 +211,14 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62001eee, 0x62001eee}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001ff6, 0x66001ff6}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -263,6 +274,11 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { static const u32 ar9485Modes_green_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006}, @@ -297,6 +313,22 @@ static const u32 ar9485Modes_green_ob_db_tx_gain_1_1[][5] = { {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501}, + {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, @@ -341,6 +373,100 @@ static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { {0x0000a2e0, 0x00000000, 0x00000000, 0xffc63a84, 0xffc63a84}, {0x0000a2e4, 0x00000000, 0x00000000, 0xfe0fc000, 0xfe0fc000}, {0x0000a2e8, 0x00000000, 0x00000000, 0xfff00000, 0xfff00000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050da, 0x000050da}, + {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, + {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, + {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, + {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, + {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, + {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, + {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, + {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, + {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603}, + {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605}, + {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03}, + {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04}, + {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20}, + {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21}, + {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, + {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, + {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, + {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, + {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, + {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, + {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, + {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, + {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, + {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62001eee, 0x62001eee}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x66001ff6, 0x66001ff6}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x66001ff6, 0x66001ff6}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501}, + {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, + {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, +}; + +static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, @@ -427,7 +553,7 @@ static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { +static const u32 ar9485_modes_lowest_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, @@ -521,12 +647,15 @@ static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -#define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1 - static const u32 ar9485Modes_green_spur_ob_db_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000003, 0x00000003, 0x00000003, 0x00000003}, - {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xffad452a, 0xffad452a}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xffc98634, 0xffc98634}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfff60780, 0xfff60780}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfffff800, 0xfffff800}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d9, 0x000050d9}, {0x0000a458, 0x80000000, 0x80000000, 0x80000000, 0x80000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000006, 0x00000006}, {0x0000a504, 0x05062002, 0x05062002, 0x03000201, 0x03000201}, @@ -543,23 +672,39 @@ static const u32 ar9485Modes_green_spur_ob_db_tx_gain_1_1[][5] = { {0x0000a530, 0x48023ec6, 0x48023ec6, 0x310006e0, 0x310006e0}, {0x0000a534, 0x4d023f01, 0x4d023f01, 0x330006e0, 0x330006e0}, {0x0000a538, 0x53023f4b, 0x53023f4b, 0x3e0008e3, 0x3e0008e3}, - {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x410008e5, 0x410008e5}, - {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x430008e6, 0x430008e6}, - {0x0000a544, 0x6502feca, 0x6502feca, 0x4a0008ec, 0x4a0008ec}, - {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4e0008f1, 0x4e0008f1}, - {0x0000a54c, 0x7203feca, 0x7203feca, 0x520008f3, 0x520008f3}, - {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x54000eed, 0x54000eed}, - {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x58000ef1, 0x58000ef1}, - {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5c000ef3, 0x5c000ef3}, - {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x60000ef5, 0x60000ef5}, - {0x0000a560, 0x900fff0b, 0x900fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a564, 0x960fffcb, 0x960fffcb, 0x62000ef6, 0x62000ef6}, - {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, - {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x430008e6, 0x430008e6}, + {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x4a0008ec, 0x4a0008ec}, + {0x0000a544, 0x6502feca, 0x6502feca, 0x4e0008f1, 0x4e0008f1}, + {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x520008f3, 0x520008f3}, + {0x0000a54c, 0x7203feca, 0x7203feca, 0x54000eed, 0x54000eed}, + {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x58000ef1, 0x58000ef1}, + {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x5c000ef3, 0x5c000ef3}, + {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x62000ef6, 0x62000ef6}, + {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x66001ff0, 0x66001ff0}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x68001ff6, 0x68001ff6}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x68001ff6, 0x68001ff6}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x01804000, 0x01804000}, + {0x0000a590, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a594, 0x00000000, 0x00000000, 0x0340ca02, 0x0340ca02}, + {0x0000a598, 0x00000000, 0x00000000, 0x0340cd03, 0x0340cd03}, + {0x0000a59c, 0x00000000, 0x00000000, 0x0340cd03, 0x0340cd03}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x06415304, 0x06415304}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x04c11905, 0x04c11905}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x06415905, 0x06415905}, {0x0000b500, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, {0x0000b504, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, {0x0000b508, 0x0000001a, 0x0000001a, 0x0000001a, 0x0000001a}, @@ -823,6 +968,7 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x00009e00, 0x03721b20}, {0x00009e04, 0x00082020}, {0x00009e18, 0x0300501e}, + {0x00009e20, 0x000003ba}, {0x00009e2c, 0x00002e21}, {0x00009e44, 0x02182324}, {0x0000a000, 0x00060005}, @@ -1001,7 +1147,6 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = { {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec80d2e, 0x7ec80d2e}, {0x00009e14, 0x31395d53, 0x31396053, 0x312e6053, 0x312e5d53}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, @@ -1020,7 +1165,7 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = { {0x0000a284, 0x00000000, 0x00000000, 0x000002a0, 0x000002a0}, {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, {0x0000a2d0, 0x00071981, 0x00071981, 0x00071982, 0x00071982}, {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -1206,6 +1351,11 @@ static const u32 ar9485_1_1_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; -#define ar9485_1_1_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 +static const u32 ar9485_1_1_baseband_core_txfir_coeff_japan_2484[][2] = { + /* Addr allmodes */ + {0x0000a398, 0x00000000}, + {0x0000a39c, 0x6f7f0301}, + {0x0000a3a0, 0xca9228ee}, +}; #endif /* INITVALS_9485_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h index e85a8b076c223e5bd3fbc4549789519c296b7761..a8c757b6124fdac483e21149733c18944f160fd7 100644 --- a/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9565_1p0_initvals.h @@ -272,9 +272,9 @@ static const u32 ar9565_1p0_baseband_core[][2] = { {0x0000a398, 0x001f0e0f}, {0x0000a39c, 0x0075393f}, {0x0000a3a0, 0xb79f6427}, - {0x0000a3a4, 0x00000000}, - {0x0000a3a8, 0xaaaaaaaa}, - {0x0000a3ac, 0x3c466478}, + {0x0000a3a4, 0x00000011}, + {0x0000a3a8, 0xaaaaaa6e}, + {0x0000a3ac, 0x3c466455}, {0x0000a3c0, 0x20202020}, {0x0000a3c4, 0x22222220}, {0x0000a3c8, 0x20200020}, @@ -295,11 +295,11 @@ static const u32 ar9565_1p0_baseband_core[][2] = { {0x0000a404, 0x00000000}, {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, - {0x0000a414, 0x1ce739ce}, + {0x0000a414, 0x1ce739c5}, {0x0000a418, 0x2d001dce}, - {0x0000a41c, 0x1ce739ce}, + {0x0000a41c, 0x1ce739c5}, {0x0000a420, 0x000001ce}, - {0x0000a424, 0x1ce739ce}, + {0x0000a424, 0x1ce739c5}, {0x0000a428, 0x000001ce}, {0x0000a42c, 0x1ce739ce}, {0x0000a430, 0x1ce739ce}, @@ -351,9 +351,9 @@ static const u32 ar9565_1p0_baseband_postamble[][5] = { {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, - {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003a4, 0x000003a4}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, - {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946222, 0xcf946222}, + {0x00009e3c, 0xcf946222, 0xcf946222, 0xcf946220, 0xcf946220}, {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, @@ -452,6 +452,7 @@ static const u32 ar9565_1p0_Common_rx_gain_table[][2] = { /* Addr allmodes */ {0x00004050, 0x00300300}, {0x0000406c, 0x00100000}, + {0x00009e20, 0x000003b6}, {0x0000a000, 0x00010000}, {0x0000a004, 0x00030002}, {0x0000a008, 0x00050004}, @@ -1230,4 +1231,11 @@ static const u32 ar9565_1p0_modes_high_power_tx_gain_table[][5] = { {0x00016054, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, }; +static const u32 ar9565_1p0_baseband_core_txfir_coeff_japan_2484[][2] = { + /* Addr allmodes */ + {0x0000a398, 0x00000000}, + {0x0000a39c, 0x6f7f0301}, + {0x0000a3a0, 0xca9228ee}, +}; + #endif /* INITVALS_9565_1P0_H */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 2ee35f677c0e8843cdc66534fd40cde15755275b..e7a38d844a6a4e7a2e9ba738e56606d946fea47a 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -64,7 +64,6 @@ struct ath_node; struct ath_config { u16 txpowlimit; - u8 cabqReadytime; }; /*************************/ @@ -207,6 +206,14 @@ struct ath_frame_info { u8 baw_tracked : 1; }; +struct ath_rxbuf { + struct list_head list; + struct sk_buff *bf_mpdu; + void *bf_desc; + dma_addr_t bf_daddr; + dma_addr_t bf_buf_addr; +}; + struct ath_buf_state { u8 bf_type; u8 bfs_paprd; @@ -307,7 +314,7 @@ struct ath_rx { struct ath_descdma rxdma; struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; - struct ath_buf *buf_hold; + struct ath_rxbuf *buf_hold; struct sk_buff *frag; u32 ampdu_ref; @@ -459,8 +466,8 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); #define ATH_DUMP_BTCOEX(_s, _val) \ do { \ - len += snprintf(buf + len, size - len, \ - "%20s : %10d\n", _s, (_val)); \ + len += scnprintf(buf + len, size - len, \ + "%20s : %10d\n", _s, (_val)); \ } while (0) enum bt_op_flags { @@ -581,7 +588,6 @@ static inline void ath_fill_led_pin(struct ath_softc *sc) #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI 50 #define ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI 50 -#define ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA -1 #define ATH_ANT_DIV_COMB_LNA1_DELTA_HI -4 #define ATH_ANT_DIV_COMB_LNA1_DELTA_MID -2 #define ATH_ANT_DIV_COMB_LNA1_DELTA_LOW 2 @@ -626,12 +632,15 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); /* Main driver core */ /********************/ -#define ATH9K_PCI_CUS198 0x0001 -#define ATH9K_PCI_CUS230 0x0002 -#define ATH9K_PCI_CUS217 0x0004 -#define ATH9K_PCI_WOW 0x0008 -#define ATH9K_PCI_BT_ANT_DIV 0x0010 -#define ATH9K_PCI_D3_L1_WAR 0x0020 +#define ATH9K_PCI_CUS198 0x0001 +#define ATH9K_PCI_CUS230 0x0002 +#define ATH9K_PCI_CUS217 0x0004 +#define ATH9K_PCI_CUS252 0x0008 +#define ATH9K_PCI_WOW 0x0010 +#define ATH9K_PCI_BT_ANT_DIV 0x0020 +#define ATH9K_PCI_D3_L1_WAR 0x0040 +#define ATH9K_PCI_AR9565_1ANT 0x0080 +#define ATH9K_PCI_AR9565_2ANT 0x0100 /* * Default cache line size, in bytes. @@ -769,6 +778,11 @@ struct ath_softc { enum spectral_mode spectral_mode; struct ath_spec_scan spec_config; + struct ieee80211_vif *tx99_vif; + struct sk_buff *tx99_skb; + bool tx99_state; + s16 tx99_power; + #ifdef CONFIG_PM_SLEEP atomic_t wow_got_bmiss_intr; atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */ @@ -877,6 +891,7 @@ static inline u8 spectral_bitmap_weight(u8 *bins) */ enum ath_fft_sample_type { ATH_FFT_SAMPLE_HT20 = 1, + ATH_FFT_SAMPLE_HT20_40, }; struct fft_sample_tlv { @@ -903,6 +918,39 @@ struct fft_sample_ht20 { u8 data[SPECTRAL_HT20_NUM_BINS]; } __packed; +struct fft_sample_ht20_40 { + struct fft_sample_tlv tlv; + + u8 channel_type; + __be16 freq; + + s8 lower_rssi; + s8 upper_rssi; + + __be64 tsf; + + s8 lower_noise; + s8 upper_noise; + + __be16 lower_max_magnitude; + __be16 upper_max_magnitude; + + u8 lower_max_index; + u8 upper_max_index; + + u8 lower_bitmap_weight; + u8 upper_bitmap_weight; + + u8 max_exp; + + u8 data[SPECTRAL_HT20_40_NUM_BINS]; +} __packed; + +int ath9k_tx99_init(struct ath_softc *sc); +void ath9k_tx99_deinit(struct ath_softc *sc); +int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, + struct ath_tx_control *txctl); + void ath9k_tasklet(unsigned long data); int ath_cabq_update(struct ath_softc *); @@ -924,7 +972,6 @@ void ath9k_deinit_device(struct ath_softc *sc); void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); void ath9k_reload_chainmask_settings(struct ath_softc *sc); -bool ath9k_uses_beacons(int type); void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw); int ath9k_spectral_scan_config(struct ieee80211_hw *hw, enum spectral_mode spectral_mode); @@ -952,7 +999,7 @@ void ath9k_ps_restore(struct ath_softc *sc); u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate); void ath_start_rfkill_poll(struct ath_softc *sc); -extern void ath9k_rfkill_poll_state(struct ieee80211_hw *hw); +void ath9k_rfkill_poll_state(struct ieee80211_hw *hw); void ath9k_calculate_iter_data(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ath9k_vif_iter_data *iter_data); diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index b5c16b3a37b953133038d0450616d3c5b2be16e3..17be35392bb4f5134996a6e14b6bdc2547d40de9 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -334,6 +334,8 @@ void ath9k_beacon_tasklet(unsigned long data) if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { sc->beacon.bmisscnt++; + ath9k_hw_check_nav(ah); + if (!ath9k_hw_check_alive(ah)) ieee80211_queue_work(sc->hw, &sc->hw_check_work); diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 5e8219a91e252b3d4a9ac2f5fda599caf4ae9ae8..278365b8a8955dda362292b99949e63155be354c 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -63,13 +63,13 @@ static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, return ath9k_hw_get_nf_limits(ah, chan)->nominal; } -s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, + s16 nf) { s8 noise = ATH_DEFAULT_NOISE_FLOOR; - if (chan && chan->noisefloor) { - s8 delta = chan->noisefloor - - ATH9K_NF_CAL_NOISE_THRESH - + if (nf) { + s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH - ath9k_hw_get_default_nf(ah, chan); if (delta > 0) noise += delta; @@ -119,7 +119,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, ath_dbg(common, CALIBRATE, "NFmid[%d] (%d) > MAX (%d), %s\n", i, h[i].privNF, limit->max, - (cal->nfcal_interference ? + (test_bit(NFCAL_INTF, &cal->cal_flags) ? "not corrected (due to interference)" : "correcting to MAX")); @@ -130,7 +130,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, * we bypass this limit here in order to better deal * with our environment. */ - if (!cal->nfcal_interference) + if (!test_bit(NFCAL_INTF, &cal->cal_flags)) h[i].privNF = limit->max; } } @@ -141,7 +141,7 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath_hw *ah, * Re-enable the enforcement of the NF maximum again. */ if (!high_nf_mid) - cal->nfcal_interference = false; + clear_bit(NFCAL_INTF, &cal->cal_flags); } static bool ath9k_hw_get_nf_thresh(struct ath_hw *ah, @@ -186,7 +186,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah, bool ath9k_hw_reset_calvalid(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; struct ath9k_cal_list *currCal = ah->cal_list_curr; if (!ah->caldata) @@ -208,7 +207,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah) return true; ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n", - currCal->calData->calType, conf->chandef.chan->center_freq); + currCal->calData->calType, ah->curchan->chan->center_freq); ah->caldata->CalValid &= ~currCal->calData->calType; currCal->calState = CAL_WAITING; @@ -220,7 +219,7 @@ EXPORT_SYMBOL(ath9k_hw_reset_calvalid); void ath9k_hw_start_nfcal(struct ath_hw *ah, bool update) { if (ah->caldata) - ah->caldata->nfcal_pending = true; + set_bit(NFCAL_PENDING, &ah->caldata->cal_flags); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF); @@ -242,7 +241,6 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) int32_t val; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; s16 default_nf = ath9k_hw_get_default_nf(ah, chan); if (ah->caldata) @@ -252,7 +250,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) if (chainmask & (1 << i)) { s16 nfval; - if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) + if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) continue; if (h) @@ -314,7 +312,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) ENABLE_REGWRITE_BUFFER(ah); for (i = 0; i < NUM_NF_READINGS; i++) { if (chainmask & (1 << i)) { - if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) + if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan)) continue; val = REG_READ(ah, ah->nf_regs[i]); @@ -391,10 +389,10 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) } h = caldata->nfCalHist; - caldata->nfcal_pending = false; + clear_bit(NFCAL_PENDING, &caldata->cal_flags); ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); chan->noisefloor = h[0].privNF; - ah->noise = ath9k_hw_getchan_noise(ah, chan); + ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); return true; } EXPORT_SYMBOL(ath9k_hw_getnf); @@ -408,7 +406,6 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, ah->caldata->channel = chan->channel; ah->caldata->channelFlags = chan->channelFlags; - ah->caldata->chanmode = chan->chanmode; h = ah->caldata->nfCalHist; default_nf = ath9k_hw_get_default_nf(ah, chan); for (i = 0; i < NUM_NF_READINGS; i++) { @@ -437,12 +434,12 @@ void ath9k_hw_bstuck_nfcal(struct ath_hw *ah) * the baseband update the internal NF value itself, similar to * what is being done after a full reset. */ - if (!caldata->nfcal_pending) + if (!test_bit(NFCAL_PENDING, &caldata->cal_flags)) ath9k_hw_start_nfcal(ah, true); else if (!(REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF)) ath9k_hw_getnf(ah, ah->curchan); - caldata->nfcal_interference = true; + set_bit(NFCAL_INTF, &caldata->cal_flags); } EXPORT_SYMBOL(ath9k_hw_bstuck_nfcal); diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 3d70b8c2bcdd0a3ddad872f0ceff9c241f59f0a4..b8ed95e9a335d90d86e8d0dd4031e5abc2e8dffc 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -116,7 +116,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, void ath9k_hw_bstuck_nfcal(struct ath_hw *ah); void ath9k_hw_reset_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal); -s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, + s16 nf); #endif /* CALIB_H */ diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index d3063c21e16c7efbd67d073c756de2198e5794e5..a7e5a05b2eff88e8d367cea6403058cc2813ebbc 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -49,103 +49,64 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb) } EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype); -static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef) -{ - u32 chanmode = 0; - - switch (chandef->chan->band) { - case IEEE80211_BAND_2GHZ: - switch (chandef->width) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - chanmode = CHANNEL_G_HT20; - break; - case NL80211_CHAN_WIDTH_40: - if (chandef->center_freq1 > chandef->chan->center_freq) - chanmode = CHANNEL_G_HT40PLUS; - else - chanmode = CHANNEL_G_HT40MINUS; - break; - default: - break; - } - break; - case IEEE80211_BAND_5GHZ: - switch (chandef->width) { - case NL80211_CHAN_WIDTH_20_NOHT: - case NL80211_CHAN_WIDTH_20: - chanmode = CHANNEL_A_HT20; - break; - case NL80211_CHAN_WIDTH_40: - if (chandef->center_freq1 > chandef->chan->center_freq) - chanmode = CHANNEL_A_HT40PLUS; - else - chanmode = CHANNEL_A_HT40MINUS; - break; - default: - break; - } - break; - default: - break; - } - - return chanmode; -} - /* * Update internal channel flags. */ -void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, - struct cfg80211_chan_def *chandef) +static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, + struct cfg80211_chan_def *chandef) { - ichan->channel = chandef->chan->center_freq; - ichan->chan = chandef->chan; - - if (chandef->chan->band == IEEE80211_BAND_2GHZ) { - ichan->chanmode = CHANNEL_G; - ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM; - } else { - ichan->chanmode = CHANNEL_A; - ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM; - } + struct ieee80211_channel *chan = chandef->chan; + u16 flags = 0; + + ichan->channel = chan->center_freq; + ichan->chan = chan; + + if (chan->band == IEEE80211_BAND_5GHZ) + flags |= CHANNEL_5GHZ; switch (chandef->width) { case NL80211_CHAN_WIDTH_5: - ichan->channelFlags |= CHANNEL_QUARTER; + flags |= CHANNEL_QUARTER; break; case NL80211_CHAN_WIDTH_10: - ichan->channelFlags |= CHANNEL_HALF; + flags |= CHANNEL_HALF; break; case NL80211_CHAN_WIDTH_20_NOHT: break; case NL80211_CHAN_WIDTH_20: + flags |= CHANNEL_HT; + break; case NL80211_CHAN_WIDTH_40: - ichan->chanmode = ath9k_get_extchanmode(chandef); + if (chandef->center_freq1 > chandef->chan->center_freq) + flags |= CHANNEL_HT40PLUS | CHANNEL_HT; + else + flags |= CHANNEL_HT40MINUS | CHANNEL_HT; break; default: WARN_ON(1); } + + ichan->channelFlags = flags; } -EXPORT_SYMBOL(ath9k_cmn_update_ichannel); /* * Get the internal channel reference. */ -struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, - struct ath_hw *ah) +struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw, + struct ath_hw *ah, + struct cfg80211_chan_def *chandef) { - struct ieee80211_channel *curchan = hw->conf.chandef.chan; + struct ieee80211_channel *curchan = chandef->chan; struct ath9k_channel *channel; u8 chan_idx; chan_idx = curchan->hw_value; channel = &ah->channels[chan_idx]; - ath9k_cmn_update_ichannel(channel, &hw->conf.chandef); + ath9k_cmn_update_ichannel(channel, chandef); return channel; } -EXPORT_SYMBOL(ath9k_cmn_get_curchannel); +EXPORT_SYMBOL(ath9k_cmn_get_channel); int ath9k_cmn_count_streams(unsigned int chainmask, int max) { diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index e039bcbfbd7923b4f8013f1c4535915a83d4b1fc..eb85e1bdca889a4e43b46261be31e691ff403363 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -43,10 +43,9 @@ (((x) + ((mul)/2)) / (mul)) int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb); -void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, - struct cfg80211_chan_def *chandef); -struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, - struct ath_hw *ah); +struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw, + struct ath_hw *ah, + struct cfg80211_chan_def *chandef); int ath9k_cmn_count_streams(unsigned int chainmask, int max); void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common, enum ath_stomp_type stomp_type); diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index c088744a6bfb6ea924fb97b07fe47671b53f5e74..83a2c59f680b0445173d25cc5ebccc2f7eb5777e 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -104,37 +104,37 @@ static ssize_t read_file_ani(struct file *file, char __user *user_buf, return -ENOMEM; if (common->disable_ani) { - len += snprintf(buf + len, size - len, "%s: %s\n", - "ANI", "DISABLED"); + len += scnprintf(buf + len, size - len, "%s: %s\n", + "ANI", "DISABLED"); goto exit; } - len += snprintf(buf + len, size - len, "%15s: %s\n", - "ANI", "ENABLED"); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "ANI RESET", ah->stats.ast_ani_reset); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "SPUR UP", ah->stats.ast_ani_spurup); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "SPUR DOWN", ah->stats.ast_ani_spurup); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "MRC-CCK ON", ah->stats.ast_ani_ccklow); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "MRC-CCK OFF", ah->stats.ast_ani_cckhigh); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "FIR-STEP UP", ah->stats.ast_ani_stepup); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "FIR-STEP DOWN", ah->stats.ast_ani_stepdown); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs); - len += snprintf(buf + len, size - len, "%15s: %u\n", - "CCK ERRORS", ah->stats.ast_ani_cckerrs); + len += scnprintf(buf + len, size - len, "%15s: %s\n", + "ANI", "ENABLED"); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "ANI RESET", ah->stats.ast_ani_reset); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "SPUR UP", ah->stats.ast_ani_spurup); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "SPUR DOWN", ah->stats.ast_ani_spurup); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "OFDM WS-DET ON", ah->stats.ast_ani_ofdmon); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "OFDM WS-DET OFF", ah->stats.ast_ani_ofdmoff); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "MRC-CCK ON", ah->stats.ast_ani_ccklow); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "MRC-CCK OFF", ah->stats.ast_ani_cckhigh); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "FIR-STEP UP", ah->stats.ast_ani_stepup); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "FIR-STEP DOWN", ah->stats.ast_ani_stepdown); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "INV LISTENTIME", ah->stats.ast_ani_lneg_or_lzero); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "OFDM ERRORS", ah->stats.ast_ani_ofdmerrs); + len += scnprintf(buf + len, size - len, "%15s: %u\n", + "CCK ERRORS", ah->stats.ast_ani_cckerrs); exit: if (len > size) len = size; @@ -280,70 +280,70 @@ static ssize_t read_file_antenna_diversity(struct file *file, return -ENOMEM; if (!(pCap->hw_caps & ATH9K_HW_CAP_ANT_DIV_COMB)) { - len += snprintf(buf + len, size - len, "%s\n", - "Antenna Diversity Combining is disabled"); + len += scnprintf(buf + len, size - len, "%s\n", + "Antenna Diversity Combining is disabled"); goto exit; } ath9k_ps_wakeup(sc); ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf); - len += snprintf(buf + len, size - len, "Current MAIN config : %s\n", - lna_conf_str[div_ant_conf.main_lna_conf]); - len += snprintf(buf + len, size - len, "Current ALT config : %s\n", - lna_conf_str[div_ant_conf.alt_lna_conf]); - len += snprintf(buf + len, size - len, "Average MAIN RSSI : %d\n", - as_main->rssi_avg); - len += snprintf(buf + len, size - len, "Average ALT RSSI : %d\n\n", - as_alt->rssi_avg); + len += scnprintf(buf + len, size - len, "Current MAIN config : %s\n", + lna_conf_str[div_ant_conf.main_lna_conf]); + len += scnprintf(buf + len, size - len, "Current ALT config : %s\n", + lna_conf_str[div_ant_conf.alt_lna_conf]); + len += scnprintf(buf + len, size - len, "Average MAIN RSSI : %d\n", + as_main->rssi_avg); + len += scnprintf(buf + len, size - len, "Average ALT RSSI : %d\n\n", + as_alt->rssi_avg); ath9k_ps_restore(sc); - len += snprintf(buf + len, size - len, "Packet Receive Cnt:\n"); - len += snprintf(buf + len, size - len, "-------------------\n"); - - len += snprintf(buf + len, size - len, "%30s%15s\n", - "MAIN", "ALT"); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "TOTAL COUNT", - as_main->recv_cnt, - as_alt->recv_cnt); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1", - as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1], - as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA2", - as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2], - as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1 + LNA2", - as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], - as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1 - LNA2", - as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], - as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); - - len += snprintf(buf + len, size - len, "\nLNA Config Attempts:\n"); - len += snprintf(buf + len, size - len, "--------------------\n"); - - len += snprintf(buf + len, size - len, "%30s%15s\n", - "MAIN", "ALT"); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1", - as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1], - as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA2", - as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2], - as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1 + LNA2", - as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], - as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); - len += snprintf(buf + len, size - len, "%-14s:%15d%15d\n", - "LNA1 - LNA2", - as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], - as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); + len += scnprintf(buf + len, size - len, "Packet Receive Cnt:\n"); + len += scnprintf(buf + len, size - len, "-------------------\n"); + + len += scnprintf(buf + len, size - len, "%30s%15s\n", + "MAIN", "ALT"); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "TOTAL COUNT", + as_main->recv_cnt, + as_alt->recv_cnt); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA2", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA2]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 + LNA2", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 - LNA2", + as_main->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], + as_alt->lna_recv_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); + + len += scnprintf(buf + len, size - len, "\nLNA Config Attempts:\n"); + len += scnprintf(buf + len, size - len, "--------------------\n"); + + len += scnprintf(buf + len, size - len, "%30s%15s\n", + "MAIN", "ALT"); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA2", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA2]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 + LNA2", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2]); + len += scnprintf(buf + len, size - len, "%-14s:%15d%15d\n", + "LNA1 - LNA2", + as_main->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2], + as_alt->lna_attempt_cnt[ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2]); exit: if (len > size) @@ -385,21 +385,21 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, (AR_MACMISC_MISC_OBS_BUS_1 << AR_MACMISC_MISC_OBS_BUS_MSB_S))); - len += snprintf(buf + len, DMA_BUF_LEN - len, - "Raw DMA Debug values:\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, + "Raw DMA Debug values:\n"); for (i = 0; i < ATH9K_NUM_DMA_DEBUG_REGS; i++) { if (i % 4 == 0) - len += snprintf(buf + len, DMA_BUF_LEN - len, "\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n"); val[i] = REG_READ_D(ah, AR_DMADBG_0 + (i * sizeof(u32))); - len += snprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ", - i, val[i]); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "%d: %08x ", + i, val[i]); } - len += snprintf(buf + len, DMA_BUF_LEN - len, "\n\n"); - len += snprintf(buf + len, DMA_BUF_LEN - len, - "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, + "Num QCU: chain_st fsp_ok fsp_st DCU: chain_st\n"); for (i = 0; i < ATH9K_NUM_QUEUES; i++, qcuOffset += 4, dcuOffset += 5) { if (i == 8) { @@ -412,39 +412,39 @@ static ssize_t read_file_dma(struct file *file, char __user *user_buf, dcuBase++; } - len += snprintf(buf + len, DMA_BUF_LEN - len, - "%2d %2x %1x %2x %2x\n", - i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, - (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3), - val[2] & (0x7 << (i * 3)) >> (i * 3), - (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); + len += scnprintf(buf + len, DMA_BUF_LEN - len, + "%2d %2x %1x %2x %2x\n", + i, (*qcuBase & (0x7 << qcuOffset)) >> qcuOffset, + (*qcuBase & (0x8 << qcuOffset)) >> (qcuOffset + 3), + val[2] & (0x7 << (i * 3)) >> (i * 3), + (*dcuBase & (0x1f << dcuOffset)) >> dcuOffset); } - len += snprintf(buf + len, DMA_BUF_LEN - len, "\n"); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "\n"); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "qcu_stitch state: %2x qcu_fetch state: %2x\n", (val[3] & 0x003c0000) >> 18, (val[3] & 0x03c00000) >> 22); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "qcu_complete state: %2x dcu_complete state: %2x\n", (val[3] & 0x1c000000) >> 26, (val[6] & 0x3)); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "dcu_arb state: %2x dcu_fp state: %2x\n", (val[5] & 0x06000000) >> 25, (val[5] & 0x38000000) >> 27); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "chan_idle_dur: %3d chan_idle_dur_valid: %1d\n", (val[6] & 0x000003fc) >> 2, (val[6] & 0x00000400) >> 10); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "txfifo_valid_0: %1d txfifo_valid_1: %1d\n", (val[6] & 0x00000800) >> 11, (val[6] & 0x00001000) >> 12); - len += snprintf(buf + len, DMA_BUF_LEN - len, + len += scnprintf(buf + len, DMA_BUF_LEN - len, "txfifo_dcu_num_0: %2d txfifo_dcu_num_1: %2d\n", (val[6] & 0x0001e000) >> 13, (val[6] & 0x001e0000) >> 17); - len += snprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x\n", - REG_READ_D(ah, AR_OBS_BUS_1)); - len += snprintf(buf + len, DMA_BUF_LEN - len, - "AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR)); + len += scnprintf(buf + len, DMA_BUF_LEN - len, "pcu observe: 0x%x\n", + REG_READ_D(ah, AR_OBS_BUS_1)); + len += scnprintf(buf + len, DMA_BUF_LEN - len, + "AR_CR: 0x%x\n", REG_READ_D(ah, AR_CR)); ath9k_ps_restore(sc); @@ -530,9 +530,9 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, #define PR_IS(a, s) \ do { \ - len += snprintf(buf + len, mxlen - len, \ - "%21s: %10u\n", a, \ - sc->debug.stats.istats.s); \ + len += scnprintf(buf + len, mxlen - len, \ + "%21s: %10u\n", a, \ + sc->debug.stats.istats.s); \ } while (0) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { @@ -563,8 +563,8 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, PR_IS("GENTIMER", gen_timer); PR_IS("TOTAL", total); - len += snprintf(buf + len, mxlen - len, - "SYNC_CAUSE stats:\n"); + len += scnprintf(buf + len, mxlen - len, + "SYNC_CAUSE stats:\n"); PR_IS("Sync-All", sync_cause_all); PR_IS("RTC-IRQ", sync_rtc_irq); @@ -655,16 +655,16 @@ static ssize_t print_queue(struct ath_softc *sc, struct ath_txq *txq, ath_txq_lock(sc, txq); - len += snprintf(buf + len, size - len, "%s: %d ", - "qnum", txq->axq_qnum); - len += snprintf(buf + len, size - len, "%s: %2d ", - "qdepth", txq->axq_depth); - len += snprintf(buf + len, size - len, "%s: %2d ", - "ampdu-depth", txq->axq_ampdu_depth); - len += snprintf(buf + len, size - len, "%s: %3d ", - "pending", txq->pending_frames); - len += snprintf(buf + len, size - len, "%s: %d\n", - "stopped", txq->stopped); + len += scnprintf(buf + len, size - len, "%s: %d ", + "qnum", txq->axq_qnum); + len += scnprintf(buf + len, size - len, "%s: %2d ", + "qdepth", txq->axq_depth); + len += scnprintf(buf + len, size - len, "%s: %2d ", + "ampdu-depth", txq->axq_ampdu_depth); + len += scnprintf(buf + len, size - len, "%s: %3d ", + "pending", txq->pending_frames); + len += scnprintf(buf + len, size - len, "%s: %d\n", + "stopped", txq->stopped); ath_txq_unlock(sc, txq); return len; @@ -687,11 +687,11 @@ static ssize_t read_file_queues(struct file *file, char __user *user_buf, for (i = 0; i < IEEE80211_NUM_ACS; i++) { txq = sc->tx.txq_map[i]; - len += snprintf(buf + len, size - len, "(%s): ", qname[i]); + len += scnprintf(buf + len, size - len, "(%s): ", qname[i]); len += print_queue(sc, txq, buf + len, size - len); } - len += snprintf(buf + len, size - len, "(CAB): "); + len += scnprintf(buf + len, size - len, "(CAB): "); len += print_queue(sc, sc->beacon.cabq, buf + len, size - len); if (len > size) @@ -716,80 +716,82 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf, unsigned int reg; u32 rxfilter; - len += snprintf(buf + len, sizeof(buf) - len, - "BSSID: %pM\n", common->curbssid); - len += snprintf(buf + len, sizeof(buf) - len, - "BSSID-MASK: %pM\n", common->bssidmask); - len += snprintf(buf + len, sizeof(buf) - len, - "OPMODE: %s\n", ath_opmode_to_string(sc->sc_ah->opmode)); + len += scnprintf(buf + len, sizeof(buf) - len, + "BSSID: %pM\n", common->curbssid); + len += scnprintf(buf + len, sizeof(buf) - len, + "BSSID-MASK: %pM\n", common->bssidmask); + len += scnprintf(buf + len, sizeof(buf) - len, + "OPMODE: %s\n", + ath_opmode_to_string(sc->sc_ah->opmode)); ath9k_ps_wakeup(sc); rxfilter = ath9k_hw_getrxfilter(sc->sc_ah); ath9k_ps_restore(sc); - len += snprintf(buf + len, sizeof(buf) - len, - "RXFILTER: 0x%x", rxfilter); + len += scnprintf(buf + len, sizeof(buf) - len, + "RXFILTER: 0x%x", rxfilter); if (rxfilter & ATH9K_RX_FILTER_UCAST) - len += snprintf(buf + len, sizeof(buf) - len, " UCAST"); + len += scnprintf(buf + len, sizeof(buf) - len, " UCAST"); if (rxfilter & ATH9K_RX_FILTER_MCAST) - len += snprintf(buf + len, sizeof(buf) - len, " MCAST"); + len += scnprintf(buf + len, sizeof(buf) - len, " MCAST"); if (rxfilter & ATH9K_RX_FILTER_BCAST) - len += snprintf(buf + len, sizeof(buf) - len, " BCAST"); + len += scnprintf(buf + len, sizeof(buf) - len, " BCAST"); if (rxfilter & ATH9K_RX_FILTER_CONTROL) - len += snprintf(buf + len, sizeof(buf) - len, " CONTROL"); + len += scnprintf(buf + len, sizeof(buf) - len, " CONTROL"); if (rxfilter & ATH9K_RX_FILTER_BEACON) - len += snprintf(buf + len, sizeof(buf) - len, " BEACON"); + len += scnprintf(buf + len, sizeof(buf) - len, " BEACON"); if (rxfilter & ATH9K_RX_FILTER_PROM) - len += snprintf(buf + len, sizeof(buf) - len, " PROM"); + len += scnprintf(buf + len, sizeof(buf) - len, " PROM"); if (rxfilter & ATH9K_RX_FILTER_PROBEREQ) - len += snprintf(buf + len, sizeof(buf) - len, " PROBEREQ"); + len += scnprintf(buf + len, sizeof(buf) - len, " PROBEREQ"); if (rxfilter & ATH9K_RX_FILTER_PHYERR) - len += snprintf(buf + len, sizeof(buf) - len, " PHYERR"); + len += scnprintf(buf + len, sizeof(buf) - len, " PHYERR"); if (rxfilter & ATH9K_RX_FILTER_MYBEACON) - len += snprintf(buf + len, sizeof(buf) - len, " MYBEACON"); + len += scnprintf(buf + len, sizeof(buf) - len, " MYBEACON"); if (rxfilter & ATH9K_RX_FILTER_COMP_BAR) - len += snprintf(buf + len, sizeof(buf) - len, " COMP_BAR"); + len += scnprintf(buf + len, sizeof(buf) - len, " COMP_BAR"); if (rxfilter & ATH9K_RX_FILTER_PSPOLL) - len += snprintf(buf + len, sizeof(buf) - len, " PSPOLL"); + len += scnprintf(buf + len, sizeof(buf) - len, " PSPOLL"); if (rxfilter & ATH9K_RX_FILTER_PHYRADAR) - len += snprintf(buf + len, sizeof(buf) - len, " PHYRADAR"); + len += scnprintf(buf + len, sizeof(buf) - len, " PHYRADAR"); if (rxfilter & ATH9K_RX_FILTER_MCAST_BCAST_ALL) - len += snprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL"); + len += scnprintf(buf + len, sizeof(buf) - len, " MCAST_BCAST_ALL"); if (rxfilter & ATH9K_RX_FILTER_CONTROL_WRAPPER) - len += snprintf(buf + len, sizeof(buf) - len, " CONTROL_WRAPPER"); + len += scnprintf(buf + len, sizeof(buf) - len, " CONTROL_WRAPPER"); - len += snprintf(buf + len, sizeof(buf) - len, "\n"); + len += scnprintf(buf + len, sizeof(buf) - len, "\n"); reg = sc->sc_ah->imask; - len += snprintf(buf + len, sizeof(buf) - len, "INTERRUPT-MASK: 0x%x", reg); + len += scnprintf(buf + len, sizeof(buf) - len, + "INTERRUPT-MASK: 0x%x", reg); if (reg & ATH9K_INT_SWBA) - len += snprintf(buf + len, sizeof(buf) - len, " SWBA"); + len += scnprintf(buf + len, sizeof(buf) - len, " SWBA"); if (reg & ATH9K_INT_BMISS) - len += snprintf(buf + len, sizeof(buf) - len, " BMISS"); + len += scnprintf(buf + len, sizeof(buf) - len, " BMISS"); if (reg & ATH9K_INT_CST) - len += snprintf(buf + len, sizeof(buf) - len, " CST"); + len += scnprintf(buf + len, sizeof(buf) - len, " CST"); if (reg & ATH9K_INT_RX) - len += snprintf(buf + len, sizeof(buf) - len, " RX"); + len += scnprintf(buf + len, sizeof(buf) - len, " RX"); if (reg & ATH9K_INT_RXHP) - len += snprintf(buf + len, sizeof(buf) - len, " RXHP"); + len += scnprintf(buf + len, sizeof(buf) - len, " RXHP"); if (reg & ATH9K_INT_RXLP) - len += snprintf(buf + len, sizeof(buf) - len, " RXLP"); + len += scnprintf(buf + len, sizeof(buf) - len, " RXLP"); if (reg & ATH9K_INT_BB_WATCHDOG) - len += snprintf(buf + len, sizeof(buf) - len, " BB_WATCHDOG"); + len += scnprintf(buf + len, sizeof(buf) - len, " BB_WATCHDOG"); - len += snprintf(buf + len, sizeof(buf) - len, "\n"); + len += scnprintf(buf + len, sizeof(buf) - len, "\n"); ath9k_calculate_iter_data(hw, NULL, &iter_data); - len += snprintf(buf + len, sizeof(buf) - len, - "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i" - " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", - iter_data.naps, iter_data.nstations, iter_data.nmeshes, - iter_data.nwds, iter_data.nadhocs, - sc->nvifs, sc->nbcnvifs); + len += scnprintf(buf + len, sizeof(buf) - len, + "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i" + " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n", + iter_data.naps, iter_data.nstations, iter_data.nmeshes, + iter_data.nwds, iter_data.nadhocs, + sc->nvifs, sc->nbcnvifs); if (len > sizeof(buf)) len = sizeof(buf); @@ -805,27 +807,27 @@ static ssize_t read_file_reset(struct file *file, char __user *user_buf, char buf[512]; unsigned int len = 0; - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "Baseband Hang", - sc->debug.stats.reset[RESET_TYPE_BB_HANG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "Baseband Watchdog", - sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "Fatal HW Error", - sc->debug.stats.reset[RESET_TYPE_FATAL_INT]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "TX HW error", - sc->debug.stats.reset[RESET_TYPE_TX_ERROR]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "TX Path Hang", - sc->debug.stats.reset[RESET_TYPE_TX_HANG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "PLL RX Hang", - sc->debug.stats.reset[RESET_TYPE_PLL_HANG]); - len += snprintf(buf + len, sizeof(buf) - len, - "%17s: %2d\n", "MCI Reset", - sc->debug.stats.reset[RESET_TYPE_MCI]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "Baseband Hang", + sc->debug.stats.reset[RESET_TYPE_BB_HANG]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "Baseband Watchdog", + sc->debug.stats.reset[RESET_TYPE_BB_WATCHDOG]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "Fatal HW Error", + sc->debug.stats.reset[RESET_TYPE_FATAL_INT]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "TX HW error", + sc->debug.stats.reset[RESET_TYPE_TX_ERROR]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "TX Path Hang", + sc->debug.stats.reset[RESET_TYPE_TX_HANG]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "PLL RX Hang", + sc->debug.stats.reset[RESET_TYPE_PLL_HANG]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "MCI Reset", + sc->debug.stats.reset[RESET_TYPE_MCI]); if (len > sizeof(buf)) len = sizeof(buf); @@ -902,14 +904,14 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { #define PHY_ERR(s, p) \ - len += snprintf(buf + len, size - len, "%22s : %10u\n", s, \ - sc->debug.stats.rxstats.phy_err_stats[p]); + len += scnprintf(buf + len, size - len, "%22s : %10u\n", s, \ + sc->debug.stats.rxstats.phy_err_stats[p]); #define RXS_ERR(s, e) \ do { \ - len += snprintf(buf + len, size - len, \ - "%22s : %10u\n", s, \ - sc->debug.stats.rxstats.e); \ + len += scnprintf(buf + len, size - len, \ + "%22s : %10u\n", s, \ + sc->debug.stats.rxstats.e);\ } while (0) struct ath_softc *sc = file->private_data; @@ -1048,6 +1050,9 @@ static ssize_t write_file_spec_scan_ctl(struct file *file, char buf[32]; ssize_t len; + if (config_enabled(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; + len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; @@ -1439,22 +1444,22 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; - len += snprintf(buf + len, size - len, - "Channel Noise Floor : %d\n", ah->noise); - len += snprintf(buf + len, size - len, - "Chain | privNF | # Readings | NF Readings\n"); + len += scnprintf(buf + len, size - len, + "Channel Noise Floor : %d\n", ah->noise); + len += scnprintf(buf + len, size - len, + "Chain | privNF | # Readings | NF Readings\n"); for (i = 0; i < NUM_NF_READINGS; i++) { if (!(chainmask & (1 << i)) || ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))) continue; nread = AR_PHY_CCA_FILTERWINDOW_LENGTH - h[i].invalidNFcount; - len += snprintf(buf + len, size - len, " %d\t %d\t %d\t\t", - i, h[i].privNF, nread); + len += scnprintf(buf + len, size - len, " %d\t %d\t %d\t\t", + i, h[i].privNF, nread); for (j = 0; j < nread; j++) - len += snprintf(buf + len, size - len, - " %d", h[i].nfCalBuffer[j]); - len += snprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, + " %d", h[i].nfCalBuffer[j]); + len += scnprintf(buf + len, size - len, "\n"); } if (len > size) @@ -1543,8 +1548,8 @@ static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, return -ENOMEM; if (!sc->sc_ah->common.btcoex_enabled) { - len = snprintf(buf, size, "%s\n", - "BTCOEX is disabled"); + len = scnprintf(buf, size, "%s\n", + "BTCOEX is disabled"); goto exit; } @@ -1582,43 +1587,43 @@ static ssize_t read_file_node_stat(struct file *file, char __user *user_buf, return -ENOMEM; if (!an->sta->ht_cap.ht_supported) { - len = snprintf(buf, size, "%s\n", - "HT not supported"); + len = scnprintf(buf, size, "%s\n", + "HT not supported"); goto exit; } - len = snprintf(buf, size, "Max-AMPDU: %d\n", - an->maxampdu); - len += snprintf(buf + len, size - len, "MPDU Density: %d\n\n", - an->mpdudensity); + len = scnprintf(buf, size, "Max-AMPDU: %d\n", + an->maxampdu); + len += scnprintf(buf + len, size - len, "MPDU Density: %d\n\n", + an->mpdudensity); - len += snprintf(buf + len, size - len, - "%2s%7s\n", "AC", "SCHED"); + len += scnprintf(buf + len, size - len, + "%2s%7s\n", "AC", "SCHED"); for (acno = 0, ac = &an->ac[acno]; acno < IEEE80211_NUM_ACS; acno++, ac++) { txq = ac->txq; ath_txq_lock(sc, txq); - len += snprintf(buf + len, size - len, - "%2d%7d\n", - acno, ac->sched); + len += scnprintf(buf + len, size - len, + "%2d%7d\n", + acno, ac->sched); ath_txq_unlock(sc, txq); } - len += snprintf(buf + len, size - len, - "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n", - "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE", - "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED"); + len += scnprintf(buf + len, size - len, + "\n%3s%11s%10s%10s%10s%10s%9s%6s%8s\n", + "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE", + "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED"); for (tidno = 0, tid = &an->tid[tidno]; tidno < IEEE80211_NUM_TIDS; tidno++, tid++) { txq = tid->ac->txq; ath_txq_lock(sc, txq); - len += snprintf(buf + len, size - len, - "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n", - tid->tidno, tid->seq_start, tid->seq_next, - tid->baw_size, tid->baw_head, tid->baw_tail, - tid->bar_index, tid->sched, tid->paused); + len += scnprintf(buf + len, size - len, + "%3d%11d%10d%10d%10d%10d%9d%6d%8d\n", + tid->tidno, tid->seq_start, tid->seq_next, + tid->baw_size, tid->baw_head, tid->baw_tail, + tid->bar_index, tid->sched, tid->paused); ath_txq_unlock(sc, txq); } exit: @@ -1773,6 +1778,111 @@ void ath9k_deinit_debug(struct ath_softc *sc) } } +static ssize_t read_file_tx99(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[3]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->tx99_state); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_tx99(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + char buf[32]; + bool start; + ssize_t len; + int r; + + if (sc->nvifs > 1) + return -EOPNOTSUPP; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + if (strtobool(buf, &start)) + return -EINVAL; + + if (start == sc->tx99_state) { + if (!start) + return count; + ath_dbg(common, XMIT, "Resetting TX99\n"); + ath9k_tx99_deinit(sc); + } + + if (!start) { + ath9k_tx99_deinit(sc); + return count; + } + + r = ath9k_tx99_init(sc); + if (r) + return r; + + return count; +} + +static const struct file_operations fops_tx99 = { + .read = read_file_tx99, + .write = write_file_tx99, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_tx99_power(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d (%d dBm)\n", + sc->tx99_power, + sc->tx99_power / 2); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_tx99_power(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + int r; + u8 tx_power; + + r = kstrtou8_from_user(user_buf, count, 0, &tx_power); + if (r) + return r; + + if (tx_power > MAX_RATE_POWER) + return -EINVAL; + + sc->tx99_power = tx_power; + + ath9k_ps_wakeup(sc); + ath9k_hw_tx99_set_txpower(sc->sc_ah, sc->tx99_power); + ath9k_ps_restore(sc); + + return count; +} + +static const struct file_operations fops_tx99_power = { + .read = read_file_tx99_power, + .write = write_file_tx99_power, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath9k_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -1864,5 +1974,15 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_btcoex); #endif + if (config_enabled(CONFIG_ATH9K_TX99) && + AR_SREV_9300_20_OR_LATER(ah)) { + debugfs_create_file("tx99", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_tx99); + debugfs_create_file("tx99_power", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_tx99_power); + } + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 6e1556fa2f3e88713fa834cb56d3a4d9f6d87c48..d6e3fa4299a4fa5e4f85e49502a900920d48f718 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -193,12 +193,12 @@ struct ath_tx_stats { #define TXSTATS sc->debug.stats.txstats #define PR(str, elem) \ do { \ - len += snprintf(buf + len, size - len, \ - "%s%13u%11u%10u%10u\n", str, \ - TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem, \ - TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem, \ - TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem, \ - TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \ + len += scnprintf(buf + len, size - len, \ + "%s%13u%11u%10u%10u\n", str, \ + TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem,\ + TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem,\ + TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem,\ + TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \ } while(0) #define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++) diff --git a/drivers/net/wireless/ath/ath9k/dfs.h b/drivers/net/wireless/ath/ath9k/dfs.h index 3c839f06a06afb7903382fcf91554fa8046d252f..c6fa3d5b5d74e3137fc34f08a430ccb1c4a670e5 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.h +++ b/drivers/net/wireless/ath/ath9k/dfs.h @@ -17,7 +17,7 @@ #ifndef ATH9K_DFS_H #define ATH9K_DFS_H -#include "dfs_pattern_detector.h" +#include "../dfs_pattern_detector.h" #if defined(CONFIG_ATH9K_DFS_CERTIFIED) /** diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c index 3c6e4138a95d13405ed8b2cff14ffbd5a2f52bb5..90b8342d1ed4bd2e95389541f299d49d59261e6a 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c @@ -20,16 +20,16 @@ #include "ath9k.h" #include "dfs_debug.h" +#include "../dfs_pattern_detector.h" - -struct ath_dfs_pool_stats global_dfs_pool_stats = { 0 }; +static struct ath_dfs_pool_stats dfs_pool_stats = { 0 }; #define ATH9K_DFS_STAT(s, p) \ - len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \ - sc->debug.stats.dfs_stats.p); + len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \ + sc->debug.stats.dfs_stats.p); #define ATH9K_DFS_POOL_STAT(s, p) \ - len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \ - global_dfs_pool_stats.p); + len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \ + dfs_pool_stats.p); static ssize_t read_file_dfs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -44,12 +44,15 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; - len += snprintf(buf + len, size - len, "DFS support for " - "macVersion = 0x%x, macRev = 0x%x: %s\n", - hw_ver->macVersion, hw_ver->macRev, - (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_DFS) ? + if (sc->dfs_detector) + dfs_pool_stats = sc->dfs_detector->get_stats(sc->dfs_detector); + + len += scnprintf(buf + len, size - len, "DFS support for " + "macVersion = 0x%x, macRev = 0x%x: %s\n", + hw_ver->macVersion, hw_ver->macRev, + (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_DFS) ? "enabled" : "disabled"); - len += snprintf(buf + len, size - len, "Pulse detector statistics:\n"); + len += scnprintf(buf + len, size - len, "Pulse detector statistics:\n"); ATH9K_DFS_STAT("pulse events reported ", pulses_total); ATH9K_DFS_STAT("invalid pulse events ", pulses_no_dfs); ATH9K_DFS_STAT("DFS pulses detected ", pulses_detected); @@ -59,11 +62,12 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, ATH9K_DFS_STAT("Primary channel pulses ", pri_phy_errors); ATH9K_DFS_STAT("Secondary channel pulses", ext_phy_errors); ATH9K_DFS_STAT("Dual channel pulses ", dc_phy_errors); - len += snprintf(buf + len, size - len, "Radar detector statistics " - "(current DFS region: %d)\n", sc->dfs_detector->region); + len += scnprintf(buf + len, size - len, "Radar detector statistics " + "(current DFS region: %d)\n", + sc->dfs_detector->region); ATH9K_DFS_STAT("Pulse events processed ", pulses_processed); ATH9K_DFS_STAT("Radars detected ", radar_detected); - len += snprintf(buf + len, size - len, "Global Pool statistics:\n"); + len += scnprintf(buf + len, size - len, "Global Pool statistics:\n"); ATH9K_DFS_POOL_STAT("Pool references ", pool_reference); ATH9K_DFS_POOL_STAT("Pulses allocated ", pulse_allocated); ATH9K_DFS_POOL_STAT("Pulses alloc error ", pulse_alloc_error); diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.h b/drivers/net/wireless/ath/ath9k/dfs_debug.h index e36810a4b585c51515e6fc3336410e1b58e3a9b0..0a7ddf4c88c93eb55589dedff95394e2157c3510 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.h +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.h @@ -51,25 +51,11 @@ struct ath_dfs_stats { u32 radar_detected; }; -/** - * struct ath_dfs_pool_stats - DFS Statistics for global pools - */ -struct ath_dfs_pool_stats { - u32 pool_reference; - u32 pulse_allocated; - u32 pulse_alloc_error; - u32 pulse_used; - u32 pseq_allocated; - u32 pseq_alloc_error; - u32 pseq_used; -}; #if defined(CONFIG_ATH9K_DFS_DEBUGFS) #define DFS_STAT_INC(sc, c) (sc->debug.stats.dfs_stats.c++) void ath9k_dfs_init_debug(struct ath_softc *sc); -#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) -#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) extern struct ath_dfs_pool_stats global_dfs_pool_stats; #else @@ -77,8 +63,6 @@ extern struct ath_dfs_pool_stats global_dfs_pool_stats; #define DFS_STAT_INC(sc, c) do { } while (0) static inline void ath9k_dfs_init_debug(struct ath_softc *sc) { } -#define DFS_POOL_STAT_INC(c) do { } while (0) -#define DFS_POOL_STAT_DEC(c) do { } while (0) #endif /* CONFIG_ATH9K_DFS_DEBUGFS */ #endif /* ATH9K_DFS_DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 9ea8e4b779c97c99b329619616e1ed232a1f5044..b4091716e9b3322881a6ed65fc3996a57e59a8b8 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -129,10 +129,10 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, struct base_eep_header_4k *pBase = &eep->baseEepHeader; if (!dump_base_hdr) { - len += snprintf(buf + len, size - len, - "%20s :\n", "2GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "2GHz modal Header"); len = ath9k_dump_4k_modal_eeprom(buf, len, size, - &eep->modalHeader); + &eep->modalHeader); goto out; } @@ -160,8 +160,8 @@ static u32 ath9k_hw_4k_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF); PR_EEP("TX Gain type", pBase->txGainType); - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - pBase->macAddr); + len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", + pBase->macAddr); out: if (len > size) diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index 3ae1f3df063758f000519a5f7d08984e8fb474b2..e1d0c217c104f9ba6c2b0a1e55a941e2ccc63d20 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -125,8 +125,8 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, struct base_eep_ar9287_header *pBase = &eep->baseEepHeader; if (!dump_base_hdr) { - len += snprintf(buf + len, size - len, - "%20s :\n", "2GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "2GHz modal Header"); len = ar9287_dump_modal_eeprom(buf, len, size, &eep->modalHeader); goto out; @@ -157,8 +157,8 @@ static u32 ath9k_hw_ar9287_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, PR_EEP("Power Table Offset", pBase->pwrTableOffset); PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl); - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - pBase->macAddr); + len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", + pBase->macAddr); out: if (len > size) diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 1c25368b3836f756f39c7f17f884ba43a2fb233c..39107e31e79aaa98fa93453f74db8b4e416ae457 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -205,12 +205,12 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, struct base_eep_header *pBase = &eep->baseEepHeader; if (!dump_base_hdr) { - len += snprintf(buf + len, size - len, - "%20s :\n", "2GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "2GHz modal Header"); len = ath9k_def_dump_modal_eeprom(buf, len, size, &eep->modalHeader[0]); - len += snprintf(buf + len, size - len, - "%20s :\n", "5GHz modal Header"); + len += scnprintf(buf + len, size - len, + "%20s :\n", "5GHz modal Header"); len = ath9k_def_dump_modal_eeprom(buf, len, size, &eep->modalHeader[1]); goto out; @@ -240,8 +240,8 @@ static u32 ath9k_hw_def_dump_eeprom(struct ath_hw *ah, bool dump_base_hdr, PR_EEP("Cal Bin Build", (pBase->binBuildNumber >> 8) & 0xFF); PR_EEP("OpenLoop Power Ctrl", pBase->openLoopPwrCntl); - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - pBase->macAddr); + len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", + pBase->macAddr); out: if (len > size) diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 4b412aaf4f3699e65396598be9f7485f2af0754f..c34f21241da947e5297bc7ad18e6de9cc3430825 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -522,22 +522,22 @@ static int ath9k_dump_mci_btcoex(struct ath_softc *sc, u8 *buf, u32 size) ATH_DUMP_BTCOEX("Concurrent Tx", btcoex_hw->mci.concur_tx); ATH_DUMP_BTCOEX("Concurrent RSSI cnt", btcoex->rssi_count); - len += snprintf(buf + len, size - len, "BT Weights: "); + len += scnprintf(buf + len, size - len, "BT Weights: "); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) - len += snprintf(buf + len, size - len, "%08x ", - btcoex_hw->bt_weight[i]); - len += snprintf(buf + len, size - len, "\n"); - len += snprintf(buf + len, size - len, "WLAN Weights: "); + len += scnprintf(buf + len, size - len, "%08x ", + btcoex_hw->bt_weight[i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, "WLAN Weights: "); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) - len += snprintf(buf + len, size - len, "%08x ", - btcoex_hw->wlan_weight[i]); - len += snprintf(buf + len, size - len, "\n"); - len += snprintf(buf + len, size - len, "Tx Priorities: "); + len += scnprintf(buf + len, size - len, "%08x ", + btcoex_hw->wlan_weight[i]); + len += scnprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, "Tx Priorities: "); for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++) - len += snprintf(buf + len, size - len, "%08x ", + len += scnprintf(buf + len, size - len, "%08x ", btcoex_hw->tx_prio[i]); - len += snprintf(buf + len, size - len, "\n"); + len += scnprintf(buf + len, size - len, "\n"); return len; } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c index c1b45e2f848124bdeb0d28a156ed570f4928e635..fb071ee4fcfb3dd5969005a40537908ec92791c6 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c @@ -37,29 +37,29 @@ static ssize_t read_file_tgt_int_stats(struct file *file, char __user *user_buf, ath9k_htc_ps_restore(priv); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "RX", - be32_to_cpu(cmd_rsp.rx)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "RX", + be32_to_cpu(cmd_rsp.rx)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "RXORN", - be32_to_cpu(cmd_rsp.rxorn)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "RXORN", + be32_to_cpu(cmd_rsp.rxorn)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "RXEOL", - be32_to_cpu(cmd_rsp.rxeol)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "RXEOL", + be32_to_cpu(cmd_rsp.rxeol)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "TXURN", - be32_to_cpu(cmd_rsp.txurn)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "TXURN", + be32_to_cpu(cmd_rsp.txurn)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "TXTO", - be32_to_cpu(cmd_rsp.txto)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "TXTO", + be32_to_cpu(cmd_rsp.txto)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "CST", - be32_to_cpu(cmd_rsp.cst)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "CST", + be32_to_cpu(cmd_rsp.cst)); if (len > sizeof(buf)) len = sizeof(buf); @@ -95,41 +95,41 @@ static ssize_t read_file_tgt_tx_stats(struct file *file, char __user *user_buf, ath9k_htc_ps_restore(priv); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "Xretries", - be32_to_cpu(cmd_rsp.xretries)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Xretries", + be32_to_cpu(cmd_rsp.xretries)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "FifoErr", - be32_to_cpu(cmd_rsp.fifoerr)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "FifoErr", + be32_to_cpu(cmd_rsp.fifoerr)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "Filtered", - be32_to_cpu(cmd_rsp.filtered)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Filtered", + be32_to_cpu(cmd_rsp.filtered)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "TimerExp", - be32_to_cpu(cmd_rsp.timer_exp)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "TimerExp", + be32_to_cpu(cmd_rsp.timer_exp)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "ShortRetries", - be32_to_cpu(cmd_rsp.shortretries)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "ShortRetries", + be32_to_cpu(cmd_rsp.shortretries)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "LongRetries", - be32_to_cpu(cmd_rsp.longretries)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "LongRetries", + be32_to_cpu(cmd_rsp.longretries)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "QueueNull", - be32_to_cpu(cmd_rsp.qnull)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "QueueNull", + be32_to_cpu(cmd_rsp.qnull)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "EncapFail", - be32_to_cpu(cmd_rsp.encap_fail)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "EncapFail", + be32_to_cpu(cmd_rsp.encap_fail)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "NoBuf", - be32_to_cpu(cmd_rsp.nobuf)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "NoBuf", + be32_to_cpu(cmd_rsp.nobuf)); if (len > sizeof(buf)) len = sizeof(buf); @@ -165,17 +165,17 @@ static ssize_t read_file_tgt_rx_stats(struct file *file, char __user *user_buf, ath9k_htc_ps_restore(priv); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "NoBuf", - be32_to_cpu(cmd_rsp.nobuf)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "NoBuf", + be32_to_cpu(cmd_rsp.nobuf)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "HostSend", - be32_to_cpu(cmd_rsp.host_send)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "HostSend", + be32_to_cpu(cmd_rsp.host_send)); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "HostDone", - be32_to_cpu(cmd_rsp.host_done)); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "HostDone", + be32_to_cpu(cmd_rsp.host_done)); if (len > sizeof(buf)) len = sizeof(buf); @@ -197,37 +197,37 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, char buf[512]; unsigned int len = 0; - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "Buffers queued", - priv->debug.tx_stats.buf_queued); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "Buffers completed", - priv->debug.tx_stats.buf_completed); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "SKBs queued", - priv->debug.tx_stats.skb_queued); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "SKBs success", - priv->debug.tx_stats.skb_success); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "SKBs failed", - priv->debug.tx_stats.skb_failed); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "CAB queued", - priv->debug.tx_stats.cab_queued); - - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "BE queued", - priv->debug.tx_stats.queue_stats[IEEE80211_AC_BE]); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "BK queued", - priv->debug.tx_stats.queue_stats[IEEE80211_AC_BK]); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "VI queued", - priv->debug.tx_stats.queue_stats[IEEE80211_AC_VI]); - len += snprintf(buf + len, sizeof(buf) - len, - "%20s : %10u\n", "VO queued", - priv->debug.tx_stats.queue_stats[IEEE80211_AC_VO]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Buffers queued", + priv->debug.tx_stats.buf_queued); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Buffers completed", + priv->debug.tx_stats.buf_completed); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs queued", + priv->debug.tx_stats.skb_queued); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs success", + priv->debug.tx_stats.skb_success); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs failed", + priv->debug.tx_stats.skb_failed); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "CAB queued", + priv->debug.tx_stats.cab_queued); + + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "BE queued", + priv->debug.tx_stats.queue_stats[IEEE80211_AC_BE]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "BK queued", + priv->debug.tx_stats.queue_stats[IEEE80211_AC_BK]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "VI queued", + priv->debug.tx_stats.queue_stats[IEEE80211_AC_VI]); + len += scnprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "VO queued", + priv->debug.tx_stats.queue_stats[IEEE80211_AC_VO]); if (len > sizeof(buf)) len = sizeof(buf); @@ -273,8 +273,8 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { #define PHY_ERR(s, p) \ - len += snprintf(buf + len, size - len, "%20s : %10u\n", s, \ - priv->debug.rx_stats.err_phy_stats[p]); + len += scnprintf(buf + len, size - len, "%20s : %10u\n", s, \ + priv->debug.rx_stats.err_phy_stats[p]); struct ath9k_htc_priv *priv = file->private_data; char *buf; @@ -285,37 +285,37 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "SKBs allocated", - priv->debug.rx_stats.skb_allocated); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "SKBs completed", - priv->debug.rx_stats.skb_completed); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "SKBs Dropped", - priv->debug.rx_stats.skb_dropped); - - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "CRC ERR", - priv->debug.rx_stats.err_crc); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "DECRYPT CRC ERR", - priv->debug.rx_stats.err_decrypt_crc); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "MIC ERR", - priv->debug.rx_stats.err_mic); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "PRE-DELIM CRC ERR", - priv->debug.rx_stats.err_pre_delim); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "POST-DELIM CRC ERR", - priv->debug.rx_stats.err_post_delim); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "DECRYPT BUSY ERR", - priv->debug.rx_stats.err_decrypt_busy); - len += snprintf(buf + len, size - len, - "%20s : %10u\n", "TOTAL PHY ERR", - priv->debug.rx_stats.err_phy); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "SKBs allocated", + priv->debug.rx_stats.skb_allocated); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "SKBs completed", + priv->debug.rx_stats.skb_completed); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "SKBs Dropped", + priv->debug.rx_stats.skb_dropped); + + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "CRC ERR", + priv->debug.rx_stats.err_crc); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "DECRYPT CRC ERR", + priv->debug.rx_stats.err_decrypt_crc); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "MIC ERR", + priv->debug.rx_stats.err_mic); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "PRE-DELIM CRC ERR", + priv->debug.rx_stats.err_pre_delim); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "POST-DELIM CRC ERR", + priv->debug.rx_stats.err_post_delim); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "DECRYPT BUSY ERR", + priv->debug.rx_stats.err_decrypt_busy); + len += scnprintf(buf + len, size - len, + "%20s : %10u\n", "TOTAL PHY ERR", + priv->debug.rx_stats.err_phy); PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN); @@ -372,16 +372,16 @@ static ssize_t read_file_slot(struct file *file, char __user *user_buf, spin_lock_bh(&priv->tx.tx_lock); - len += snprintf(buf + len, sizeof(buf) - len, "TX slot bitmap : "); + len += scnprintf(buf + len, sizeof(buf) - len, "TX slot bitmap : "); len += bitmap_scnprintf(buf + len, sizeof(buf) - len, priv->tx.tx_slot, MAX_TX_BUF_NUM); - len += snprintf(buf + len, sizeof(buf) - len, "\n"); + len += scnprintf(buf + len, sizeof(buf) - len, "\n"); - len += snprintf(buf + len, sizeof(buf) - len, - "Used slots : %d\n", - bitmap_weight(priv->tx.tx_slot, MAX_TX_BUF_NUM)); + len += scnprintf(buf + len, sizeof(buf) - len, + "Used slots : %d\n", + bitmap_weight(priv->tx.tx_slot, MAX_TX_BUF_NUM)); spin_unlock_bh(&priv->tx.tx_lock); @@ -405,30 +405,30 @@ static ssize_t read_file_queue(struct file *file, char __user *user_buf, char buf[512]; unsigned int len = 0; - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Mgmt endpoint", skb_queue_len(&priv->tx.mgmt_ep_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Mgmt endpoint", skb_queue_len(&priv->tx.mgmt_ep_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Cab endpoint", skb_queue_len(&priv->tx.cab_ep_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Cab endpoint", skb_queue_len(&priv->tx.cab_ep_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Data BE endpoint", skb_queue_len(&priv->tx.data_be_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Data BE endpoint", skb_queue_len(&priv->tx.data_be_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Data BK endpoint", skb_queue_len(&priv->tx.data_bk_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Data BK endpoint", skb_queue_len(&priv->tx.data_bk_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Data VI endpoint", skb_queue_len(&priv->tx.data_vi_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Data VI endpoint", skb_queue_len(&priv->tx.data_vi_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Data VO endpoint", skb_queue_len(&priv->tx.data_vo_queue)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Data VO endpoint", skb_queue_len(&priv->tx.data_vo_queue)); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Failed queue", skb_queue_len(&priv->tx.tx_failed)); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Failed queue", skb_queue_len(&priv->tx.tx_failed)); spin_lock_bh(&priv->tx.tx_lock); - len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", - "Queued count", priv->tx.queued_cnt); + len += scnprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", + "Queued count", priv->tx.queued_cnt); spin_unlock_bh(&priv->tx.tx_lock); if (len > sizeof(buf)) @@ -507,70 +507,70 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "Major Version", - pBase->version >> 12); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "Minor Version", - pBase->version & 0xFFF); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "Checksum", - pBase->checksum); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "Length", - pBase->length); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "RegDomain1", - pBase->regDmn[0]); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", "RegDomain2", - pBase->regDmn[1]); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "TX Mask", pBase->txMask); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "RX Mask", pBase->rxMask); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Allow 5GHz", - !!(pBase->opCapFlags & AR5416_OPFLAGS_11A)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Allow 2GHz", - !!(pBase->opCapFlags & AR5416_OPFLAGS_11G)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 2GHz HT20", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 2GHz HT40", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 5Ghz HT20", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Disable 5Ghz HT40", - !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Big Endian", - !!(pBase->eepMisc & 0x01)); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Cal Bin Major Ver", - (pBase->binBuildNumber >> 24) & 0xFF); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Cal Bin Minor Ver", - (pBase->binBuildNumber >> 16) & 0xFF); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "Cal Bin Build", - (pBase->binBuildNumber >> 8) & 0xFF); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", "Major Version", + pBase->version >> 12); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", "Minor Version", + pBase->version & 0xFFF); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", "Checksum", + pBase->checksum); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", "Length", + pBase->length); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", "RegDomain1", + pBase->regDmn[0]); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", "RegDomain2", + pBase->regDmn[1]); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "TX Mask", pBase->txMask); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "RX Mask", pBase->rxMask); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Allow 5GHz", + !!(pBase->opCapFlags & AR5416_OPFLAGS_11A)); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Allow 2GHz", + !!(pBase->opCapFlags & AR5416_OPFLAGS_11G)); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Disable 2GHz HT20", + !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT20)); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Disable 2GHz HT40", + !!(pBase->opCapFlags & AR5416_OPFLAGS_N_2G_HT40)); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Disable 5Ghz HT20", + !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT20)); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Disable 5Ghz HT40", + !!(pBase->opCapFlags & AR5416_OPFLAGS_N_5G_HT40)); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Big Endian", + !!(pBase->eepMisc & 0x01)); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Cal Bin Major Ver", + (pBase->binBuildNumber >> 24) & 0xFF); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Cal Bin Minor Ver", + (pBase->binBuildNumber >> 16) & 0xFF); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "Cal Bin Build", + (pBase->binBuildNumber >> 8) & 0xFF); /* * UB91 specific data. @@ -579,10 +579,10 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, struct base_eep_header_4k *pBase4k = &priv->ah->eeprom.map4k.baseEepHeader; - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "TX Gain type", - pBase4k->txGainType); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "TX Gain type", + pBase4k->txGainType); } /* @@ -592,19 +592,19 @@ static ssize_t read_file_base_eeprom(struct file *file, char __user *user_buf, struct base_eep_ar9287_header *pBase9287 = &priv->ah->eeprom.map9287.baseEepHeader; - len += snprintf(buf + len, size - len, - "%20s : %10ddB\n", - "Power Table Offset", - pBase9287->pwrTableOffset); + len += scnprintf(buf + len, size - len, + "%20s : %10ddB\n", + "Power Table Offset", + pBase9287->pwrTableOffset); - len += snprintf(buf + len, size - len, - "%20s : %10d\n", - "OpenLoop Power Ctrl", - pBase9287->openLoopPwrCntl); + len += scnprintf(buf + len, size - len, + "%20s : %10d\n", + "OpenLoop Power Ctrl", + pBase9287->openLoopPwrCntl); } - len += snprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", - pBase->macAddr); + len += scnprintf(buf + len, size - len, "%20s : %pM\n", "MacAddress", + pBase->macAddr); if (len > size) len = size; @@ -627,8 +627,8 @@ static ssize_t read_4k_modal_eeprom(struct file *file, { #define PR_EEP(_s, _val) \ do { \ - len += snprintf(buf + len, size - len, "%20s : %10d\n", \ - _s, (_val)); \ + len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ + _s, (_val)); \ } while (0) struct ath9k_htc_priv *priv = file->private_data; @@ -708,12 +708,12 @@ static ssize_t read_def_modal_eeprom(struct file *file, do { \ if (pBase->opCapFlags & AR5416_OPFLAGS_11G) { \ pModal = &priv->ah->eeprom.def.modalHeader[1]; \ - len += snprintf(buf + len, size - len, "%20s : %8d%7s", \ - _s, (_val), "|"); \ + len += scnprintf(buf + len, size - len, "%20s : %8d%7s", \ + _s, (_val), "|"); \ } \ if (pBase->opCapFlags & AR5416_OPFLAGS_11A) { \ pModal = &priv->ah->eeprom.def.modalHeader[0]; \ - len += snprintf(buf + len, size - len, "%9d\n", \ + len += scnprintf(buf + len, size - len, "%9d\n",\ (_val)); \ } \ } while (0) @@ -729,10 +729,10 @@ static ssize_t read_def_modal_eeprom(struct file *file, if (buf == NULL) return -ENOMEM; - len += snprintf(buf + len, size - len, - "%31s %15s\n", "2G", "5G"); - len += snprintf(buf + len, size - len, - "%32s %16s\n", "====", "====\n"); + len += scnprintf(buf + len, size - len, + "%31s %15s\n", "2G", "5G"); + len += scnprintf(buf + len, size - len, + "%32s %16s\n", "====", "====\n"); PR_EEP("Chain0 Ant. Control", pModal->antCtrlChain[0]); PR_EEP("Chain1 Ant. Control", pModal->antCtrlChain[1]); @@ -814,8 +814,8 @@ static ssize_t read_9287_modal_eeprom(struct file *file, { #define PR_EEP(_s, _val) \ do { \ - len += snprintf(buf + len, size - len, "%20s : %10d\n", \ - _s, (_val)); \ + len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ + _s, (_val)); \ } while (0) struct ath9k_htc_priv *priv = file->private_data; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index d44258172c0f640236719a7e8eb4e8480c9c682b..9a2657fdd9ccd4ec62f96f8a639182e2ded29fec 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -24,30 +24,10 @@ static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, struct ath9k_channel *ichan) { - enum htc_phymode mode; - - mode = -EINVAL; - - switch (ichan->chanmode) { - case CHANNEL_G: - case CHANNEL_G_HT20: - case CHANNEL_G_HT40PLUS: - case CHANNEL_G_HT40MINUS: - mode = HTC_MODE_11NG; - break; - case CHANNEL_A: - case CHANNEL_A_HT20: - case CHANNEL_A_HT40PLUS: - case CHANNEL_A_HT40MINUS: - mode = HTC_MODE_11NA; - break; - default: - break; - } + if (IS_CHAN_5GHZ(ichan)) + return HTC_MODE_11NA; - WARN_ON(mode < 0); - - return mode; + return HTC_MODE_11NG; } bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, @@ -926,7 +906,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) WMI_CMD(WMI_FLUSH_RECV_CMDID); /* setup initial channel */ - init_channel = ath9k_cmn_get_curchannel(hw, ah); + init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false); if (ret) { @@ -1208,9 +1188,7 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) ath_dbg(common, CONFIG, "Set channel: %d MHz\n", curchan->center_freq); - ath9k_cmn_update_ichannel(&priv->ah->channels[pos], - &hw->conf.chandef); - + ath9k_cmn_get_channel(hw, priv->ah, &hw->conf.chandef); if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { ath_err(common, "Unable to set channel\n"); ret = -EINVAL; diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 83f4927aeacae1d07a2b18057ea313ad716b0cec..4f9378ddf07f21455a33a908180b9a28729bf6cd 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -78,6 +78,22 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); } +static inline void ath9k_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + ath9k_hw_ops(ah)->tx99_start(ah, qnum); +} + +static inline void ath9k_hw_tx99_stop(struct ath_hw *ah) +{ + ath9k_hw_ops(ah)->tx99_stop(ah); +} + +static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power) +{ + if (ath9k_hw_ops(ah)->tx99_set_txpower) + ath9k_hw_ops(ah)->tx99_set_txpower(ah, power); +} + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index ecc6ec4a1edb39b7f3e4d26da756652fc324971b..54b04155e43b1058575aa44df3e6ece1ab18e55e 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -130,29 +130,29 @@ void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause) static void ath9k_hw_set_clockrate(struct ath_hw *ah) { - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_channel *chan = ah->curchan; unsigned int clockrate; /* AR9287 v1.3+ uses async FIFO and runs the MAC at 117 MHz */ if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) clockrate = 117; - else if (!ah->curchan) /* should really check for CCK instead */ + else if (!chan) /* should really check for CCK instead */ clockrate = ATH9K_CLOCK_RATE_CCK; - else if (conf->chandef.chan->band == IEEE80211_BAND_2GHZ) + else if (IS_CHAN_2GHZ(chan)) clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM; else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK) clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; else clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM; - if (conf_is_ht40(conf)) + if (IS_CHAN_HT40(chan)) clockrate *= 2; if (ah->curchan) { - if (IS_CHAN_HALF_RATE(ah->curchan)) + if (IS_CHAN_HALF_RATE(chan)) clockrate /= 2; - if (IS_CHAN_QUARTER_RATE(ah->curchan)) + if (IS_CHAN_QUARTER_RATE(chan)) clockrate /= 4; } @@ -190,10 +190,7 @@ EXPORT_SYMBOL(ath9k_hw_wait); void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, int hw_delay) { - if (IS_CHAN_B(chan)) - hw_delay = (4 * hw_delay) / 22; - else - hw_delay /= 10; + hw_delay /= 10; if (IS_CHAN_HALF_RATE(chan)) hw_delay *= 2; @@ -294,8 +291,7 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah, return; } - if ((chan->chanmode == CHANNEL_A_HT40PLUS) || - (chan->chanmode == CHANNEL_G_HT40PLUS)) { + if (IS_CHAN_HT40PLUS(chan)) { centers->synth_center = chan->channel + HT40_CHANNEL_CENTER_SHIFT; extoff = 1; @@ -549,6 +545,18 @@ static int ath9k_hw_post_init(struct ath_hw *ah) ath9k_hw_ani_init(ah); + /* + * EEPROM needs to be initialized before we do this. + * This is required for regulatory compliance. + */ + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { + u16 regdmn = ah->eep_ops->get_eeprom(ah, EEP_REG_0); + if ((regdmn & 0xF0) == CTL_FCC) { + ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9462_FCC_2GHZ; + ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9462_FCC_5GHZ; + } + } + return 0; } @@ -1030,7 +1038,6 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) void ath9k_hw_init_global_settings(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - struct ieee80211_conf *conf = &common->hw->conf; const struct ath9k_channel *chan = ah->curchan; int acktimeout, ctstimeout, ack_offset = 0; int slottime; @@ -1105,8 +1112,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) * BA frames in some implementations, but it has been found to fix ACK * timeout issues in other cases as well. */ - if (conf->chandef.chan && - conf->chandef.chan->band == IEEE80211_BAND_2GHZ && + if (IS_CHAN_2GHZ(chan) && !IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) { acktimeout += 64 - sifstime - ah->slottime; ctstimeout += 48 - sifstime - ah->slottime; @@ -1148,9 +1154,7 @@ u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan) { u32 ctl = ath_regd_get_band_ctl(reg, chan->chan->band); - if (IS_CHAN_B(chan)) - ctl |= CTL_11B; - else if (IS_CHAN_G(chan)) + if (IS_CHAN_2GHZ(chan)) ctl |= CTL_11G; else ctl |= CTL_11A; @@ -1498,10 +1502,8 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, int r; if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) { - u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ); - u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ); - band_switch = (cur != new); - mode_diff = (chan->chanmode != ah->curchan->chanmode); + band_switch = IS_CHAN_5GHZ(ah->curchan) != IS_CHAN_5GHZ(chan); + mode_diff = (chan->channelFlags != ah->curchan->channelFlags); } for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { @@ -1540,9 +1542,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah, ath9k_hw_set_clockrate(ah); ath9k_hw_apply_txpower(ah, chan, false); - if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) - ath9k_hw_set_delta_slope(ah, chan); - + ath9k_hw_set_delta_slope(ah, chan); ath9k_hw_spur_mitigate_freq(ah, chan); if (band_switch || ini_reloaded) @@ -1644,6 +1644,19 @@ hang_check_iter: return true; } +void ath9k_hw_check_nav(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + u32 val; + + val = REG_READ(ah, AR_NAV); + if (val != 0xdeadbeef && val > 0x7fff) { + ath_dbg(common, BSTUCK, "Abnormal NAV: 0x%x\n", val); + REG_WRITE(ah, AR_NAV, 0); + } +} +EXPORT_SYMBOL(ath9k_hw_check_nav); + bool ath9k_hw_check_alive(struct ath_hw *ah) { int count = 50; @@ -1799,20 +1812,11 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) goto fail; /* - * If cross-band fcc is not supoprted, bail out if - * either channelFlags or chanmode differ. - * - * chanmode will be different if the HT operating mode - * changes because of CSA. + * If cross-band fcc is not supoprted, bail out if channelFlags differ. */ - if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) { - if ((chan->channelFlags & CHANNEL_ALL) != - (ah->curchan->channelFlags & CHANNEL_ALL)) - goto fail; - - if (chan->chanmode != ah->curchan->chanmode) - goto fail; - } + if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) && + chan->channelFlags != ah->curchan->channelFlags) + goto fail; if (!ath9k_hw_check_alive(ah)) goto fail; @@ -1822,9 +1826,9 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) * re-using are present. */ if (AR_SREV_9462(ah) && (ah->caldata && - (!ah->caldata->done_txiqcal_once || - !ah->caldata->done_txclcal_once || - !ah->caldata->rtt_done))) + (!test_bit(TXIQCAL_DONE, &ah->caldata->cal_flags) || + !test_bit(TXCLCAL_DONE, &ah->caldata->cal_flags) || + !test_bit(RTT_DONE, &ah->caldata->cal_flags)))) goto fail; ath_dbg(common, RESET, "FastChannelChange for %d -> %d\n", @@ -1874,15 +1878,14 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ah->caldata = caldata; if (caldata && (chan->channel != caldata->channel || - chan->channelFlags != caldata->channelFlags || - chan->chanmode != caldata->chanmode)) { + chan->channelFlags != caldata->channelFlags)) { /* Operating channel changed, reset channel calibration data */ memset(caldata, 0, sizeof(*caldata)); ath9k_init_nfcal_hist_buffer(ah, chan); } else if (caldata) { - caldata->paprd_packet_sent = false; + clear_bit(PAPRD_PACKET_SENT, &caldata->cal_flags); } - ah->noise = ath9k_hw_getchan_noise(ah, chan); + ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); if (fastcc) { r = ath9k_hw_do_fastcc(ah, chan); @@ -1964,9 +1967,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_init_mfp(ah); - if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan)) - ath9k_hw_set_delta_slope(ah, chan); - + ath9k_hw_set_delta_slope(ah, chan); ath9k_hw_spur_mitigate_freq(ah, chan); ah->eep_ops->set_board_values(ah, chan); @@ -2017,8 +2018,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_init_bb(ah, chan); if (caldata) { - caldata->done_txiqcal_once = false; - caldata->done_txclcal_once = false; + clear_bit(TXIQCAL_DONE, &caldata->cal_flags); + clear_bit(TXCLCAL_DONE, &caldata->cal_flags); } if (!ath9k_hw_init_cal(ah, chan)) return -EIO; @@ -2943,12 +2944,11 @@ void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set) } EXPORT_SYMBOL(ath9k_hw_set_tsfadjust); -void ath9k_hw_set11nmac2040(struct ath_hw *ah) +void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan) { - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; u32 macmode; - if (conf_is_ht40(conf) && !ah->config.cwm_ignore_extcca) + if (IS_CHAN_HT40(chan) && !ah->config.cwm_ignore_extcca) macmode = AR_2040_JOINED_RX_CLEAR; else macmode = 0; @@ -3240,19 +3240,19 @@ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len) /* chipsets >= AR9280 are single-chip */ if (AR_SREV_9280_20_OR_LATER(ah)) { - used = snprintf(hw_name, len, - "Atheros AR%s Rev:%x", - ath9k_hw_mac_bb_name(ah->hw_version.macVersion), - ah->hw_version.macRev); + used = scnprintf(hw_name, len, + "Atheros AR%s Rev:%x", + ath9k_hw_mac_bb_name(ah->hw_version.macVersion), + ah->hw_version.macRev); } else { - used = snprintf(hw_name, len, - "Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x", - ath9k_hw_mac_bb_name(ah->hw_version.macVersion), - ah->hw_version.macRev, - ath9k_hw_rf_name((ah->hw_version.analog5GhzRev & - AR_RADIO_SREV_MAJOR)), - ah->hw_version.phyRev); + used = scnprintf(hw_name, len, + "Atheros AR%s MAC/BB Rev:%x AR%s RF Rev:%x", + ath9k_hw_mac_bb_name(ah->hw_version.macVersion), + ah->hw_version.macRev, + ath9k_hw_rf_name((ah->hw_version.analog5GhzRev + & AR_RADIO_SREV_MAJOR)), + ah->hw_version.phyRev); } hw_name[used] = '\0'; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 69a907b55a73b129fae1171f2d9859a3303ec3e7..9ea24f1cba73f812de0b3352806e89434c0d59f9 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -98,8 +98,8 @@ #define PR_EEP(_s, _val) \ do { \ - len += snprintf(buf + len, size - len, "%20s : %10d\n", \ - _s, (_val)); \ + len += scnprintf(buf + len, size - len, "%20s : %10d\n",\ + _s, (_val)); \ } while (0) #define SM(_v, _f) (((_v) << _f##_S) & _f) @@ -369,55 +369,30 @@ enum ath9k_int { ATH9K_INT_NOCARD = 0xffffffff }; -#define CHANNEL_CCK 0x00020 -#define CHANNEL_OFDM 0x00040 -#define CHANNEL_2GHZ 0x00080 -#define CHANNEL_5GHZ 0x00100 -#define CHANNEL_PASSIVE 0x00200 -#define CHANNEL_DYN 0x00400 -#define CHANNEL_HALF 0x04000 -#define CHANNEL_QUARTER 0x08000 -#define CHANNEL_HT20 0x10000 -#define CHANNEL_HT40PLUS 0x20000 -#define CHANNEL_HT40MINUS 0x40000 - -#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM) -#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK) -#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM) -#define CHANNEL_G_HT20 (CHANNEL_2GHZ|CHANNEL_HT20) -#define CHANNEL_A_HT20 (CHANNEL_5GHZ|CHANNEL_HT20) -#define CHANNEL_G_HT40PLUS (CHANNEL_2GHZ|CHANNEL_HT40PLUS) -#define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS) -#define CHANNEL_A_HT40PLUS (CHANNEL_5GHZ|CHANNEL_HT40PLUS) -#define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS) -#define CHANNEL_ALL \ - (CHANNEL_OFDM| \ - CHANNEL_CCK| \ - CHANNEL_2GHZ | \ - CHANNEL_5GHZ | \ - CHANNEL_HT20 | \ - CHANNEL_HT40PLUS | \ - CHANNEL_HT40MINUS) - #define MAX_RTT_TABLE_ENTRY 6 #define MAX_IQCAL_MEASUREMENT 8 #define MAX_CL_TAB_ENTRY 16 #define CL_TAB_ENTRY(reg_base) (reg_base + (4 * j)) +enum ath9k_cal_flags { + RTT_DONE, + PAPRD_PACKET_SENT, + PAPRD_DONE, + NFCAL_PENDING, + NFCAL_INTF, + TXIQCAL_DONE, + TXCLCAL_DONE, + SW_PKDET_DONE, +}; + struct ath9k_hw_cal_data { u16 channel; - u32 channelFlags; - u32 chanmode; + u16 channelFlags; + unsigned long cal_flags; int32_t CalValid; int8_t iCoff; int8_t qCoff; - bool rtt_done; - bool paprd_packet_sent; - bool paprd_done; - bool nfcal_pending; - bool nfcal_interference; - bool done_txiqcal_once; - bool done_txclcal_once; + u8 caldac[2]; u16 small_signal_gain[AR9300_MAX_CHAINS]; u32 pa_table[AR9300_MAX_CHAINS][PAPRD_TABLE_SZ]; u32 num_measures[AR9300_MAX_CHAINS]; @@ -430,33 +405,34 @@ struct ath9k_hw_cal_data { struct ath9k_channel { struct ieee80211_channel *chan; u16 channel; - u32 channelFlags; - u32 chanmode; + u16 channelFlags; s16 noisefloor; }; -#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \ - (((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \ - (((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \ - (((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS)) -#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0) -#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0) -#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0) -#define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0) -#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0) +#define CHANNEL_5GHZ BIT(0) +#define CHANNEL_HALF BIT(1) +#define CHANNEL_QUARTER BIT(2) +#define CHANNEL_HT BIT(3) +#define CHANNEL_HT40PLUS BIT(4) +#define CHANNEL_HT40MINUS BIT(5) + +#define IS_CHAN_5GHZ(_c) (!!((_c)->channelFlags & CHANNEL_5GHZ)) +#define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c)) + +#define IS_CHAN_HALF_RATE(_c) (!!((_c)->channelFlags & CHANNEL_HALF)) +#define IS_CHAN_QUARTER_RATE(_c) (!!((_c)->channelFlags & CHANNEL_QUARTER)) #define IS_CHAN_A_FAST_CLOCK(_ah, _c) \ - ((((_c)->channelFlags & CHANNEL_5GHZ) != 0) && \ - ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)) - -/* These macros check chanmode and not channelFlags */ -#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B) -#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) || \ - ((_c)->chanmode == CHANNEL_G_HT20)) -#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) || \ - ((_c)->chanmode == CHANNEL_A_HT40MINUS) || \ - ((_c)->chanmode == CHANNEL_G_HT40PLUS) || \ - ((_c)->chanmode == CHANNEL_G_HT40MINUS)) -#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c))) + (IS_CHAN_5GHZ(_c) && ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)) + +#define IS_CHAN_HT(_c) ((_c)->channelFlags & CHANNEL_HT) + +#define IS_CHAN_HT20(_c) (IS_CHAN_HT(_c) && !IS_CHAN_HT40(_c)) + +#define IS_CHAN_HT40(_c) \ + (!!((_c)->channelFlags & (CHANNEL_HT40PLUS | CHANNEL_HT40MINUS))) + +#define IS_CHAN_HT40PLUS(_c) ((_c)->channelFlags & CHANNEL_HT40PLUS) +#define IS_CHAN_HT40MINUS(_c) ((_c)->channelFlags & CHANNEL_HT40MINUS) enum ath9k_power_mode { ATH9K_PM_AWAKE = 0, @@ -558,6 +534,7 @@ struct ath_hw_antcomb_conf { u8 main_gaintb; u8 alt_gaintb; int lna1_lna2_delta; + int lna1_lna2_switch_delta; u8 div_group; }; @@ -726,6 +703,10 @@ struct ath_hw_ops { void (*spectral_scan_trigger)(struct ath_hw *ah); void (*spectral_scan_wait)(struct ath_hw *ah); + void (*tx99_start)(struct ath_hw *ah, u32 qnum); + void (*tx99_stop)(struct ath_hw *ah); + void (*tx99_set_txpower)(struct ath_hw *ah, u8 power); + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable); #endif @@ -1026,10 +1007,11 @@ void ath9k_hw_reset_tsf(struct ath_hw *ah); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set); void ath9k_hw_init_global_settings(struct ath_hw *ah); u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah); -void ath9k_hw_set11nmac2040(struct ath_hw *ah); +void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan); void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period); void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, const struct ath9k_beacon_state *bs); +void ath9k_hw_check_nav(struct ath_hw *ah); bool ath9k_hw_check_alive(struct ath_hw *ah); bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 9a1f349f926001662dee74a796db2c580d21dbb7..d8643ebabd3001b6a00544157a65a9bc9f3265c5 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -347,7 +347,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, { struct ath_common *common = ath9k_hw_common(sc->sc_ah); u8 *ds; - struct ath_buf *bf; int i, bsize, desc_len; ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n", @@ -399,33 +398,68 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); /* allocate buffers */ - bsize = sizeof(struct ath_buf) * nbuf; - bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); - if (!bf) - return -ENOMEM; + if (is_tx) { + struct ath_buf *bf; + + bsize = sizeof(struct ath_buf) * nbuf; + bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); + if (!bf) + return -ENOMEM; + + for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->caps.hw_caps & + ATH9K_HW_CAP_4KB_SPLITTRANS)) { + /* + * Skip descriptor addresses which can cause 4KB + * boundary crossing (addr + length) with a 32 dword + * descriptor fetch. + */ + while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { + BUG_ON((caddr_t) bf->bf_desc >= + ((caddr_t) dd->dd_desc + + dd->dd_desc_len)); + + ds += (desc_len * ndesc); + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } + } + list_add_tail(&bf->list, head); + } + } else { + struct ath_rxbuf *bf; - for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) { - bf->bf_desc = ds; - bf->bf_daddr = DS2PHYS(dd, ds); - - if (!(sc->sc_ah->caps.hw_caps & - ATH9K_HW_CAP_4KB_SPLITTRANS)) { - /* - * Skip descriptor addresses which can cause 4KB - * boundary crossing (addr + length) with a 32 dword - * descriptor fetch. - */ - while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { - BUG_ON((caddr_t) bf->bf_desc >= - ((caddr_t) dd->dd_desc + - dd->dd_desc_len)); - - ds += (desc_len * ndesc); - bf->bf_desc = ds; - bf->bf_daddr = DS2PHYS(dd, ds); + bsize = sizeof(struct ath_rxbuf) * nbuf; + bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); + if (!bf) + return -ENOMEM; + + for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->caps.hw_caps & + ATH9K_HW_CAP_4KB_SPLITTRANS)) { + /* + * Skip descriptor addresses which can cause 4KB + * boundary crossing (addr + length) with a 32 dword + * descriptor fetch. + */ + while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { + BUG_ON((caddr_t) bf->bf_desc >= + ((caddr_t) dd->dd_desc + + dd->dd_desc_len)); + + ds += (desc_len * ndesc); + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } } + list_add_tail(&bf->list, head); } - list_add_tail(&bf->list, head); } return 0; } @@ -437,7 +471,6 @@ static int ath9k_init_queues(struct ath_softc *sc) sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah); sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); - sc->config.cabqReadytime = ATH_CABQ_READY_TIME; ath_cabq_update(sc); sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0); @@ -547,6 +580,26 @@ static void ath9k_init_platform(struct ath_softc *sc) if (sc->driver_data & ATH9K_PCI_CUS217) ath_info(common, "CUS217 card detected\n"); + if (sc->driver_data & ATH9K_PCI_CUS252) + ath_info(common, "CUS252 card detected\n"); + + if (sc->driver_data & ATH9K_PCI_AR9565_1ANT) + ath_info(common, "WB335 1-ANT card detected\n"); + + if (sc->driver_data & ATH9K_PCI_AR9565_2ANT) + ath_info(common, "WB335 2-ANT card detected\n"); + + /* + * Some WB335 cards do not support antenna diversity. Since + * we use a hardcoded value for AR9565 instead of using the + * EEPROM/OTP data, remove the combining feature from + * the HW capabilities bitmap. + */ + if (sc->driver_data & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) { + if (!(sc->driver_data & ATH9K_PCI_BT_ANT_DIV)) + pCap->hw_caps &= ~ATH9K_HW_CAP_ANT_DIV_COMB; + } + if (sc->driver_data & ATH9K_PCI_BT_ANT_DIV) { pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV; ath_info(common, "Set BT/WLAN RX diversity capability\n"); @@ -627,7 +680,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->sc_ah = ah; pCap = &ah->caps; - sc->dfs_detector = dfs_pattern_detector_init(ah, NL80211_DFS_UNSET); + common = ath9k_hw_common(ah); + sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET); + sc->tx99_power = MAX_RATE_POWER + 1; if (!pdata) { ah->ah_flags |= AH_USE_EEPROM; @@ -641,7 +696,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ah->external_reset = pdata->external_reset; } - common = ath9k_hw_common(ah); common->ops = &ah->reg_ops; common->bus_ops = bus_ops; common->ah = ah; @@ -732,6 +786,7 @@ err_queues: ath9k_hw_deinit(ah); err_hw: ath9k_eeprom_release(sc); + dev_kfree_skb_any(sc->tx99_skb); return ret; } @@ -748,7 +803,7 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band) chan = &sband->channels[i]; ah->curchan = &ah->channels[chan->hw_value]; cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); - ath9k_cmn_update_ichannel(ah->curchan, &chandef); + ath9k_cmn_get_channel(sc->hw, ah, &chandef); ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true); } } @@ -789,9 +844,9 @@ static const struct ieee80211_iface_limit if_limits[] = { BIT(NL80211_IFTYPE_P2P_GO) }, }; - static const struct ieee80211_iface_limit if_dfs_limits[] = { - { .max = 1, .types = BIT(NL80211_IFTYPE_AP) }, + { .max = 1, .types = BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_ADHOC) }, }; static const struct ieee80211_iface_combination if_comb[] = { @@ -850,17 +905,18 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_WDS) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT); - - hw->wiphy->iface_combinations = if_comb; - hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + if (!config_enabled(CONFIG_ATH9K_TX99)) { + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + hw->wiphy->iface_combinations = if_comb; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 2f831db396ac2fd84c0ca1e3a6e46bc325b1013e..aed7e29dc50f152b954278020b8b2999f3e795e7 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -28,6 +28,13 @@ void ath_tx_complete_poll_work(struct work_struct *work) int i; bool needreset = false; + + if (sc->tx99_state) { + ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, + "skip tx hung detection on tx99\n"); + return; + } + for (i = 0; i < IEEE80211_NUM_ACS; i++) { txq = sc->tx.txq_map[i]; @@ -70,7 +77,7 @@ void ath_hw_check(struct work_struct *work) ath9k_ps_wakeup(sc); is_alive = ath9k_hw_check_alive(sc->sc_ah); - if (is_alive && !AR_SREV_9300(sc->sc_ah)) + if ((is_alive && !AR_SREV_9300(sc->sc_ah)) || sc->tx99_state) goto out; else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { ath_dbg(common, RESET, @@ -141,6 +148,9 @@ void ath_hw_pll_work(struct work_struct *work) if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) return; + if (sc->tx99_state) + return; + ath9k_ps_wakeup(sc); pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); ath9k_ps_restore(sc); @@ -184,7 +194,7 @@ static void ath_paprd_activate(struct ath_softc *sc) struct ath9k_hw_cal_data *caldata = ah->caldata; int chain; - if (!caldata || !caldata->paprd_done) { + if (!caldata || !test_bit(PAPRD_DONE, &caldata->cal_flags)) { ath_dbg(common, CALIBRATE, "Failed to activate PAPRD\n"); return; } @@ -256,7 +266,9 @@ void ath_paprd_calibrate(struct work_struct *work) int len = 1800; int ret; - if (!caldata || !caldata->paprd_packet_sent || caldata->paprd_done) { + if (!caldata || + !test_bit(PAPRD_PACKET_SENT, &caldata->cal_flags) || + test_bit(PAPRD_DONE, &caldata->cal_flags)) { ath_dbg(common, CALIBRATE, "Skipping PAPRD calibration\n"); return; } @@ -316,7 +328,7 @@ void ath_paprd_calibrate(struct work_struct *work) kfree_skb(skb); if (chain_ok) { - caldata->paprd_done = true; + set_bit(PAPRD_DONE, &caldata->cal_flags); ath_paprd_activate(sc); } @@ -343,7 +355,7 @@ void ath_ani_calibrate(unsigned long data) u32 cal_interval, short_cal_interval, long_cal_interval; unsigned long flags; - if (ah->caldata && ah->caldata->nfcal_interference) + if (ah->caldata && test_bit(NFCAL_INTF, &ah->caldata->cal_flags)) long_cal_interval = ATH_LONG_CALINTERVAL_INT; else long_cal_interval = ATH_LONG_CALINTERVAL; @@ -432,7 +444,7 @@ set_timer: mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval)); if (ar9003_is_paprd_enabled(ah) && ah->caldata) { - if (!ah->caldata->paprd_done) { + if (!test_bit(PAPRD_DONE, &ah->caldata->cal_flags)) { ieee80211_queue_work(sc->hw, &sc->paprd_work); } else if (!ah->paprd_table_write_done) { ath9k_ps_wakeup(sc); @@ -516,7 +528,8 @@ void ath_update_survey_nf(struct ath_softc *sc, int channel) if (chan->noisefloor) { survey->filled |= SURVEY_INFO_NOISE_DBM; - survey->noise = ath9k_hw_getchan_noise(ah, chan); + survey->noise = ath9k_hw_getchan_noise(ah, chan, + chan->noisefloor); } } diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index a3eff0986a3f95c1ea6b523fc71b02a1ac2b4eba..6a18f9d3e9cc952ef116f9fda1bf56f37526d8bc 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -374,7 +374,6 @@ EXPORT_SYMBOL(ath9k_hw_releasetxqueue); bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) { struct ath_common *common = ath9k_hw_common(ah); - struct ath9k_channel *chan = ah->curchan; struct ath9k_tx_queue_info *qi; u32 cwMin, chanCwMin, value; @@ -387,10 +386,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) ath_dbg(common, QUEUE, "Reset TX queue: %u\n", q); if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) { - if (chan && IS_CHAN_B(chan)) - chanCwMin = INIT_CWMIN_11B; - else - chanCwMin = INIT_CWMIN; + chanCwMin = INIT_CWMIN; for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1); } else diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index bfccaceed44ef82f34c7e9df6562975f4322bf5e..e3eed81f24391c61b1389a749d471cf4ad662056 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -603,8 +603,6 @@ enum ath9k_tx_queue_flags { #define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001 #define ATH9K_DECOMP_MASK_SIZE 128 -#define ATH9K_READY_TIME_LO_BOUND 50 -#define ATH9K_READY_TIME_HI_BOUND 96 enum ath9k_pkt_type { ATH9K_PKT_TYPE_NORMAL = 0, diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 709301f88dcd26210a18db4d6eb1f8e230905608..74f452c7b1667c47a65506a077042f2b0668c3a8 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -312,17 +312,91 @@ out: * by reseting the chip. To accomplish this we must first cleanup any pending * DMA, then restart stuff. */ -static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, - struct ath9k_channel *hchan) +static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef) { + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_hw *hw = sc->hw; + struct ath9k_channel *hchan; + struct ieee80211_channel *chan = chandef->chan; + unsigned long flags; + bool offchannel; + int pos = chan->hw_value; + int old_pos = -1; int r; if (test_bit(SC_OP_INVALID, &sc->sc_flags)) return -EIO; + offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); + + if (ah->curchan) + old_pos = ah->curchan - &ah->channels[0]; + + ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", + chan->center_freq, chandef->width); + + /* update survey stats for the old channel before switching */ + spin_lock_irqsave(&common->cc_lock, flags); + ath_update_survey_stats(sc); + spin_unlock_irqrestore(&common->cc_lock, flags); + + ath9k_cmn_get_channel(hw, ah, chandef); + + /* + * If the operating channel changes, change the survey in-use flags + * along with it. + * Reset the survey data for the new channel, unless we're switching + * back to the operating channel from an off-channel operation. + */ + if (!offchannel && sc->cur_survey != &sc->survey[pos]) { + if (sc->cur_survey) + sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; + + sc->cur_survey = &sc->survey[pos]; + + memset(sc->cur_survey, 0, sizeof(struct survey_info)); + sc->cur_survey->filled |= SURVEY_INFO_IN_USE; + } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { + memset(&sc->survey[pos], 0, sizeof(struct survey_info)); + } + + hchan = &sc->sc_ah->channels[pos]; r = ath_reset_internal(sc, hchan); + if (r) + return r; - return r; + /* + * The most recent snapshot of channel->noisefloor for the old + * channel is only available after the hardware reset. Copy it to + * the survey stats now. + */ + if (old_pos >= 0) + ath_update_survey_nf(sc, old_pos); + + /* + * Enable radar pulse detection if on a DFS channel. Spectral + * scanning and radar detection can not be used concurrently. + */ + if (hw->conf.radar_enabled) { + u32 rxfilter; + + /* set HW specific DFS configuration */ + ath9k_hw_set_radar_params(ah); + rxfilter = ath9k_hw_getrxfilter(ah); + rxfilter |= ATH9K_RX_FILTER_PHYRADAR | + ATH9K_RX_FILTER_PHYERR; + ath9k_hw_setrxfilter(ah, rxfilter); + ath_dbg(common, DFS, "DFS enabled at freq %d\n", + chan->center_freq); + } else { + /* perform spectral scan if requested. */ + if (test_bit(SC_OP_SCANNING, &sc->sc_flags) && + sc->spectral_mode == SPECTRAL_CHANSCAN) + ath9k_spectral_scan_trigger(hw); + } + + return 0; } static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, @@ -372,6 +446,13 @@ void ath9k_tasklet(unsigned long data) type = RESET_TYPE_BB_WATCHDOG; ath9k_queue_reset(sc, type); + + /* + * Increment the ref. counter here so that + * interrupts are enabled in the reset routine. + */ + atomic_inc(&ah->intr_ref_cnt); + ath_dbg(common, ANY, "FATAL: Skipping interrupts\n"); goto out; } @@ -410,10 +491,9 @@ void ath9k_tasklet(unsigned long data) ath9k_btcoex_handle_interrupt(sc, status); -out: /* re-enable hardware interrupt */ ath9k_hw_enable_interrupts(ah); - +out: spin_unlock(&sc->sc_pcu_lock); ath9k_ps_restore(sc); } @@ -594,7 +674,7 @@ static int ath9k_start(struct ieee80211_hw *hw) ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); - init_channel = ath9k_cmn_get_curchannel(hw, ah); + init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); /* Reset SERDES registers */ ath9k_hw_configpcipowersave(ah, false); @@ -797,7 +877,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) } if (!ah->curchan) - ah->curchan = ath9k_cmn_get_curchannel(hw, ah); + ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef); ath9k_hw_reset(ah, ah->curchan, ah->caldata, false); ath9k_hw_phy_disable(ah); @@ -816,7 +896,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) ath_dbg(common, CONFIG, "Driver halt\n"); } -bool ath9k_uses_beacons(int type) +static bool ath9k_uses_beacons(int type) { switch (type) { case NL80211_IFTYPE_AP: @@ -966,6 +1046,14 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); + if (config_enabled(CONFIG_ATH9K_TX99)) { + if (sc->nvifs >= 1) { + mutex_unlock(&sc->mutex); + return -EOPNOTSUPP; + } + sc->tx99_vif = vif; + } + ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->nvifs++; @@ -994,9 +1082,15 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - ath_dbg(common, CONFIG, "Change Interface\n"); mutex_lock(&sc->mutex); + if (config_enabled(CONFIG_ATH9K_TX99)) { + mutex_unlock(&sc->mutex); + return -EOPNOTSUPP; + } + + ath_dbg(common, CONFIG, "Change Interface\n"); + if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); @@ -1026,6 +1120,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); sc->nvifs--; + sc->tx99_vif = NULL; if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); @@ -1047,6 +1142,9 @@ static void ath9k_enable_ps(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + sc->ps_enabled = true; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) { @@ -1063,6 +1161,9 @@ static void ath9k_disable_ps(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + sc->ps_enabled = false; ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { @@ -1086,6 +1187,9 @@ void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw) struct ath_common *common = ath9k_hw_common(ah); u32 rxfilter; + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { ath_err(common, "spectrum analyzer not implemented on this hardware\n"); return; @@ -1201,81 +1305,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) { - struct ieee80211_channel *curchan = hw->conf.chandef.chan; - int pos = curchan->hw_value; - int old_pos = -1; - unsigned long flags; - - if (ah->curchan) - old_pos = ah->curchan - &ah->channels[0]; - - ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n", - curchan->center_freq, hw->conf.chandef.width); - - /* update survey stats for the old channel before switching */ - spin_lock_irqsave(&common->cc_lock, flags); - ath_update_survey_stats(sc); - spin_unlock_irqrestore(&common->cc_lock, flags); - - ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos], - &conf->chandef); - - /* - * If the operating channel changes, change the survey in-use flags - * along with it. - * Reset the survey data for the new channel, unless we're switching - * back to the operating channel from an off-channel operation. - */ - if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && - sc->cur_survey != &sc->survey[pos]) { - - if (sc->cur_survey) - sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE; - - sc->cur_survey = &sc->survey[pos]; - - memset(sc->cur_survey, 0, sizeof(struct survey_info)); - sc->cur_survey->filled |= SURVEY_INFO_IN_USE; - } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) { - memset(&sc->survey[pos], 0, sizeof(struct survey_info)); - } - - if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) { + if (ath_set_channel(sc, &hw->conf.chandef) < 0) { ath_err(common, "Unable to set channel\n"); mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); return -EINVAL; } - - /* - * The most recent snapshot of channel->noisefloor for the old - * channel is only available after the hardware reset. Copy it to - * the survey stats now. - */ - if (old_pos >= 0) - ath_update_survey_nf(sc, old_pos); - - /* - * Enable radar pulse detection if on a DFS channel. Spectral - * scanning and radar detection can not be used concurrently. - */ - if (hw->conf.radar_enabled) { - u32 rxfilter; - - /* set HW specific DFS configuration */ - ath9k_hw_set_radar_params(ah); - rxfilter = ath9k_hw_getrxfilter(ah); - rxfilter |= ATH9K_RX_FILTER_PHYRADAR | - ATH9K_RX_FILTER_PHYERR; - ath9k_hw_setrxfilter(ah, rxfilter); - ath_dbg(common, DFS, "DFS enabled at freq %d\n", - curchan->center_freq); - } else { - /* perform spectral scan if requested. */ - if (test_bit(SC_OP_SCANNING, &sc->sc_flags) && - sc->spectral_mode == SPECTRAL_CHANSCAN) - ath9k_spectral_scan_trigger(hw); - } } if (changed & IEEE80211_CONF_CHANGE_POWER) { @@ -1734,6 +1769,9 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, unsigned long flags; int pos; + if (config_enabled(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; + spin_lock_irqsave(&common->cc_lock, flags); if (idx == 0) ath_update_survey_stats(sc); @@ -1766,6 +1804,9 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + mutex_lock(&sc->mutex); ah->coverage_class = coverage_class; @@ -2332,6 +2373,134 @@ static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw, sc->csa_vif = vif; } +static void ath9k_tx99_stop(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + + ath_drain_all_txq(sc); + ath_startrecv(sc); + + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + + ieee80211_wake_queues(sc->hw); + + kfree_skb(sc->tx99_skb); + sc->tx99_skb = NULL; + sc->tx99_state = false; + + ath9k_hw_tx99_stop(sc->sc_ah); + ath_dbg(common, XMIT, "TX99 stopped\n"); +} + +static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) +{ + static u8 PN9Data[] = {0xff, 0x87, 0xb8, 0x59, 0xb7, 0xa1, 0xcc, 0x24, + 0x57, 0x5e, 0x4b, 0x9c, 0x0e, 0xe9, 0xea, 0x50, + 0x2a, 0xbe, 0xb4, 0x1b, 0xb6, 0xb0, 0x5d, 0xf1, + 0xe6, 0x9a, 0xe3, 0x45, 0xfd, 0x2c, 0x53, 0x18, + 0x0c, 0xca, 0xc9, 0xfb, 0x49, 0x37, 0xe5, 0xa8, + 0x51, 0x3b, 0x2f, 0x61, 0xaa, 0x72, 0x18, 0x84, + 0x02, 0x23, 0x23, 0xab, 0x63, 0x89, 0x51, 0xb3, + 0xe7, 0x8b, 0x72, 0x90, 0x4c, 0xe8, 0xfb, 0xc0}; + u32 len = 1200; + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info; + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return NULL; + + skb_put(skb, len); + + memset(skb->data, 0, len); + + hdr = (struct ieee80211_hdr *)skb->data; + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); + hdr->duration_id = 0; + + memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + + hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); + + tx_info = IEEE80211_SKB_CB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->band = hw->conf.chandef.chan->band; + tx_info->flags = IEEE80211_TX_CTL_NO_ACK; + tx_info->control.vif = sc->tx99_vif; + + memcpy(skb->data + sizeof(*hdr), PN9Data, sizeof(PN9Data)); + + return skb; +} + +void ath9k_tx99_deinit(struct ath_softc *sc) +{ + ath_reset(sc); + + ath9k_ps_wakeup(sc); + ath9k_tx99_stop(sc); + ath9k_ps_restore(sc); +} + +int ath9k_tx99_init(struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_tx_control txctl; + int r; + + if (sc->sc_flags & SC_OP_INVALID) { + ath_err(common, + "driver is in invalid state unable to use TX99"); + return -EINVAL; + } + + sc->tx99_skb = ath9k_build_tx99_skb(sc); + if (!sc->tx99_skb) + return -ENOMEM; + + memset(&txctl, 0, sizeof(txctl)); + txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; + + ath_reset(sc); + + ath9k_ps_wakeup(sc); + + ath9k_hw_disable_interrupts(ah); + atomic_set(&ah->intr_ref_cnt, -1); + ath_drain_all_txq(sc); + ath_stoprecv(sc); + + sc->tx99_state = true; + + ieee80211_stop_queues(hw); + + if (sc->tx99_power == MAX_RATE_POWER + 1) + sc->tx99_power = MAX_RATE_POWER; + + ath9k_hw_tx99_set_txpower(ah, sc->tx99_power); + r = ath9k_tx99_send(sc, sc->tx99_skb, &txctl); + if (r) { + ath_dbg(common, XMIT, "Failed to xmit TX99 skb\n"); + return r; + } + + ath_dbg(common, XMIT, "TX99 xmit started using %d ( %ddBm)\n", + sc->tx99_power, + sc->tx99_power / 2); + + /* We leave the harware awake as it will be chugging on */ + + return 0; +} + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 815bee21c19a0fe1aeb2194fbdba2372be2b279b..0ac1b5f04256517050be277696e9a9eae4f2391c 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -661,9 +661,9 @@ void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all) chan_start = wlan_chan - 10; chan_end = wlan_chan + 10; - if (chan->chanmode == CHANNEL_G_HT40PLUS) + if (IS_CHAN_HT40PLUS(chan)) chan_end += 20; - else if (chan->chanmode == CHANNEL_G_HT40MINUS) + else if (IS_CHAN_HT40MINUS(chan)) chan_start -= 20; /* adjust side band */ @@ -707,11 +707,11 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, if (setchannel) { struct ath9k_hw_cal_data *caldata = &sc->caldata; - if ((caldata->chanmode == CHANNEL_G_HT40PLUS) && + if (IS_CHAN_HT40PLUS(ah->curchan) && (ah->curchan->channel > caldata->channel) && (ah->curchan->channel <= caldata->channel + 20)) return; - if ((caldata->chanmode == CHANNEL_G_HT40MINUS) && + if (IS_CHAN_HT40MINUS(ah->curchan) && (ah->curchan->channel < caldata->channel) && (ah->curchan->channel >= caldata->channel - 20)) return; diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index d089a7cf01c43810e88353d6504a90c238abf721..7e4c2524b63052006650ff76b92b3b839d6c9cd6 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -269,7 +269,200 @@ static DEFINE_PCI_DEVICE_TABLE(ath_pci_id_table) = { { PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E AR9462 */ { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E AR1111/AR9485 */ - { PCI_VDEVICE(ATHEROS, 0x0036) }, /* PCI-E AR9565 */ + + /* CUS252 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3028), + .driver_data = ATH9K_PCI_CUS252 | + ATH9K_PCI_AR9565_2ANT | + ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2176), + .driver_data = ATH9K_PCI_CUS252 | + ATH9K_PCI_AR9565_2ANT | + ATH9K_PCI_BT_ANT_DIV }, + + /* WB335 1-ANT */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE068), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0xA119), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0632), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x6671), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2811), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2812), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + + /* WB335 1-ANT / Antenna Diversity */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3025), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3026), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x302B), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE069), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0x3028), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0622), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0672), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0662), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x213A), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_LENOVO, + 0x3026), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x18E3), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x217F), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_DELL, + 0x020E), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + + /* WB335 2-ANT */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411A), + .driver_data = ATH9K_PCI_AR9565_2ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411B), + .driver_data = ATH9K_PCI_AR9565_2ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411C), + .driver_data = ATH9K_PCI_AR9565_2ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411D), + .driver_data = ATH9K_PCI_AR9565_2ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411E), + .driver_data = ATH9K_PCI_AR9565_2ANT }, + + /* WB335 2-ANT / Antenna-Diversity */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3027), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x302C), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0642), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0652), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0612), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2130), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x144F, /* ASKEY */ + 0x7202), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2810), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0x3027), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + + /* PCI-E AR9565 (WB335) */ + { PCI_VDEVICE(ATHEROS, 0x0036), + .driver_data = ATH9K_PCI_BT_ANT_DIV }, + { 0 } }; diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index d3d7c51fa6c8bd65d7df5f803c99450aff980d95..d829bb62a3fc6d9e94ae867b2a675530ea4d1d7a 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1387,31 +1387,31 @@ static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, int used_mcs = 0, used_htmode = 0; if (WLAN_RC_PHY_HT(rc->rate_table->info[i].phy)) { - used_mcs = snprintf(mcs, 5, "%d", - rc->rate_table->info[i].ratecode); + used_mcs = scnprintf(mcs, 5, "%d", + rc->rate_table->info[i].ratecode); if (WLAN_RC_PHY_40(rc->rate_table->info[i].phy)) - used_htmode = snprintf(htmode, 5, "HT40"); + used_htmode = scnprintf(htmode, 5, "HT40"); else if (WLAN_RC_PHY_20(rc->rate_table->info[i].phy)) - used_htmode = snprintf(htmode, 5, "HT20"); + used_htmode = scnprintf(htmode, 5, "HT20"); else - used_htmode = snprintf(htmode, 5, "????"); + used_htmode = scnprintf(htmode, 5, "????"); } mcs[used_mcs] = '\0'; htmode[used_htmode] = '\0'; - len += snprintf(buf + len, max - len, - "%6s %6s %3u.%d: " - "%10u %10u %10u %10u\n", - htmode, - mcs, - ratekbps / 1000, - (ratekbps % 1000) / 100, - stats->success, - stats->retries, - stats->xretries, - stats->per); + len += scnprintf(buf + len, max - len, + "%6s %6s %3u.%d: " + "%10u %10u %10u %10u\n", + htmode, + mcs, + ratekbps / 1000, + (ratekbps % 1000) / 100, + stats->success, + stats->retries, + stats->xretries, + stats->per); } if (len > max) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index ab9e3a8410bc2065fff9cb58996645fbf67be4fc..95ddca5495d492cb5a1bf4bb99d8ee3283a9e1ec 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -19,7 +19,7 @@ #include "ath9k.h" #include "ar9003_mac.h" -#define SKB_CB_ATHBUF(__skb) (*((struct ath_buf **)__skb->cb)) +#define SKB_CB_ATHBUF(__skb) (*((struct ath_rxbuf **)__skb->cb)) static inline bool ath9k_check_auto_sleep(struct ath_softc *sc) { @@ -35,7 +35,7 @@ static inline bool ath9k_check_auto_sleep(struct ath_softc *sc) * buffer (or rx fifo). This can incorrectly acknowledge packets * to a sender if last desc is self-linked. */ -static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) +static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); @@ -68,7 +68,7 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf) sc->rx.rxlink = &ds->ds_link; } -static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf) +static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf) { if (sc->rx.buf_hold) ath_rx_buf_link(sc, sc->rx.buf_hold); @@ -112,13 +112,13 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc, struct ath_hw *ah = sc->sc_ah; struct ath_rx_edma *rx_edma; struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; rx_edma = &sc->rx.rx_edma[qtype]; if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize) return false; - bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); + bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list); list_del_init(&bf->list); skb = bf->bf_mpdu; @@ -138,7 +138,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc, enum ath9k_rx_qtype qtype) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_buf *bf, *tbf; + struct ath_rxbuf *bf, *tbf; if (list_empty(&sc->rx.rxbuf)) { ath_dbg(common, QUEUE, "No free rx buf available\n"); @@ -154,7 +154,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc, static void ath_rx_remove_buffer(struct ath_softc *sc, enum ath9k_rx_qtype qtype) { - struct ath_buf *bf; + struct ath_rxbuf *bf; struct ath_rx_edma *rx_edma; struct sk_buff *skb; @@ -171,7 +171,7 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - struct ath_buf *bf; + struct ath_rxbuf *bf; ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP); ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP); @@ -199,7 +199,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_hw *ah = sc->sc_ah; struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; int error = 0, i; u32 size; @@ -211,7 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs) ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP], ah->caps.rx_hp_qdepth); - size = sizeof(struct ath_buf) * nbufs; + size = sizeof(struct ath_rxbuf) * nbufs; bf = devm_kzalloc(sc->dev, size, GFP_KERNEL); if (!bf) return -ENOMEM; @@ -271,7 +271,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; int error = 0; spin_lock_init(&sc->sc_pcu_lock); @@ -332,7 +332,7 @@ void ath_rx_cleanup(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { ath_rx_edma_cleanup(sc); @@ -375,6 +375,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc) { u32 rfilt; + if (config_enabled(CONFIG_ATH9K_TX99)) + return 0; + rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST | ATH9K_RX_FILTER_MCAST; @@ -427,7 +430,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc) int ath_startrecv(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; - struct ath_buf *bf, *tbf; + struct ath_rxbuf *bf, *tbf; if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { ath_edma_start_recv(sc); @@ -447,7 +450,7 @@ int ath_startrecv(struct ath_softc *sc) if (list_empty(&sc->rx.rxbuf)) goto start_recv; - bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); + bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list); ath9k_hw_putrxbuf(ah, bf->bf_daddr); ath9k_hw_rxena(ah); @@ -603,13 +606,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon) static bool ath_edma_get_buffers(struct ath_softc *sc, enum ath9k_rx_qtype qtype, struct ath_rx_status *rs, - struct ath_buf **dest) + struct ath_rxbuf **dest) { struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype]; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct sk_buff *skb; - struct ath_buf *bf; + struct ath_rxbuf *bf; int ret; skb = skb_peek(&rx_edma->rx_fifo); @@ -653,11 +656,11 @@ static bool ath_edma_get_buffers(struct ath_softc *sc, return true; } -static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc, +static struct ath_rxbuf *ath_edma_get_next_rx_buf(struct ath_softc *sc, struct ath_rx_status *rs, enum ath9k_rx_qtype qtype) { - struct ath_buf *bf = NULL; + struct ath_rxbuf *bf = NULL; while (ath_edma_get_buffers(sc, qtype, rs, &bf)) { if (!bf) @@ -668,13 +671,13 @@ static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc, return NULL; } -static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, +static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc, struct ath_rx_status *rs) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_desc *ds; - struct ath_buf *bf; + struct ath_rxbuf *bf; int ret; if (list_empty(&sc->rx.rxbuf)) { @@ -682,7 +685,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, return NULL; } - bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list); + bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list); if (bf == sc->rx.buf_hold) return NULL; @@ -702,7 +705,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, ret = ath9k_hw_rxprocdesc(ah, ds, rs); if (ret == -EINPROGRESS) { struct ath_rx_status trs; - struct ath_buf *tbf; + struct ath_rxbuf *tbf; struct ath_desc *tds; memset(&trs, 0, sizeof(trs)); @@ -711,7 +714,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc, return NULL; } - tbf = list_entry(bf->list.next, struct ath_buf, list); + tbf = list_entry(bf->list.next, struct ath_rxbuf, list); /* * On some hardware the descriptor status words could @@ -972,14 +975,15 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, { #ifdef CONFIG_ATH9K_DEBUGFS struct ath_hw *ah = sc->sc_ah; - u8 bins[SPECTRAL_HT20_NUM_BINS]; - u8 *vdata = (u8 *)hdr; - struct fft_sample_ht20 fft_sample; + u8 num_bins, *bins, *vdata = (u8 *)hdr; + struct fft_sample_ht20 fft_sample_20; + struct fft_sample_ht20_40 fft_sample_40; + struct fft_sample_tlv *tlv; struct ath_radar_info *radar_info; - struct ath_ht20_mag_info *mag_info; int len = rs->rs_datalen; int dc_pos; - u16 length, max_magnitude; + u16 fft_len, length, freq = ah->curchan->chan->center_freq; + enum nl80211_channel_type chan_type; /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT @@ -997,45 +1001,44 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) return 0; - /* Variation in the data length is possible and will be fixed later. - * Note that we only support HT20 for now. - * - * TODO: add HT20_40 support as well. - */ - if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) || - (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1)) - return 1; - - fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20; - length = sizeof(fft_sample) - sizeof(fft_sample.tlv); - fft_sample.tlv.length = __cpu_to_be16(length); + chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef); + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_40_NUM_BINS; + bins = (u8 *)fft_sample_40.data; + } else { + fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_NUM_BINS; + bins = (u8 *)fft_sample_20.data; + } - fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq); - fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - fft_sample.noise = ah->noise; + /* Variation in the data length is possible and will be fixed later */ + if ((len > fft_len + 2) || (len < fft_len - 1)) + return 1; - switch (len - SPECTRAL_HT20_TOTAL_DATA_LEN) { + switch (len - fft_len) { case 0: /* length correct, nothing to do. */ - memcpy(bins, vdata, SPECTRAL_HT20_NUM_BINS); + memcpy(bins, vdata, num_bins); break; case -1: /* first byte missing, duplicate it. */ - memcpy(&bins[1], vdata, SPECTRAL_HT20_NUM_BINS - 1); + memcpy(&bins[1], vdata, num_bins - 1); bins[0] = vdata[0]; break; case 2: /* MAC added 2 extra bytes at bin 30 and 32, remove them. */ memcpy(bins, vdata, 30); bins[30] = vdata[31]; - memcpy(&bins[31], &vdata[33], SPECTRAL_HT20_NUM_BINS - 31); + memcpy(&bins[31], &vdata[33], num_bins - 31); break; case 1: /* MAC added 2 extra bytes AND first byte is missing. */ bins[0] = vdata[0]; - memcpy(&bins[0], vdata, 30); + memcpy(&bins[1], vdata, 30); bins[31] = vdata[31]; - memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32); + memcpy(&bins[32], &vdata[33], num_bins - 32); break; default: return 1; @@ -1044,23 +1047,93 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, /* DC value (value in the middle) is the blind spot of the spectral * sample and invalid, interpolate it. */ - dc_pos = SPECTRAL_HT20_NUM_BINS / 2; + dc_pos = num_bins / 2; bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2; - /* mag data is at the end of the frame, in front of radar_info */ - mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + s8 lower_rssi, upper_rssi; + s16 ext_nf; + u8 lower_max_index, upper_max_index; + u8 lower_bitmap_w, upper_bitmap_w; + u16 lower_mag, upper_mag; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_ht20_40_mag_info *mag_info; + + if (caldata) + ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan, + caldata->nfCalHist[3].privNF); + else + ext_nf = ATH_DEFAULT_NOISE_FLOOR; + + length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); + fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; + fft_sample_40.tlv.length = __cpu_to_be16(length); + fft_sample_40.freq = __cpu_to_be16(freq); + fft_sample_40.channel_type = chan_type; + + if (chan_type == NL80211_CHAN_HT40PLUS) { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0); - /* copy raw bins without scaling them */ - memcpy(fft_sample.data, bins, SPECTRAL_HT20_NUM_BINS); - fft_sample.max_exp = mag_info->max_exp & 0xf; + fft_sample_40.lower_noise = ah->noise; + fft_sample_40.upper_noise = ext_nf; + } else { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - max_magnitude = spectral_max_magnitude(mag_info->all_bins); - fft_sample.max_magnitude = __cpu_to_be16(max_magnitude); - fft_sample.max_index = spectral_max_index(mag_info->all_bins); - fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins); - fft_sample.tsf = __cpu_to_be64(tsf); + fft_sample_40.lower_noise = ext_nf; + fft_sample_40.upper_noise = ah->noise; + } + fft_sample_40.lower_rssi = lower_rssi; + fft_sample_40.upper_rssi = upper_rssi; + + mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1; + lower_mag = spectral_max_magnitude(mag_info->lower_bins); + upper_mag = spectral_max_magnitude(mag_info->upper_bins); + fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); + fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); + lower_max_index = spectral_max_index(mag_info->lower_bins); + upper_max_index = spectral_max_index(mag_info->upper_bins); + fft_sample_40.lower_max_index = lower_max_index; + fft_sample_40.upper_max_index = upper_max_index; + lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); + upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins); + fft_sample_40.lower_bitmap_weight = lower_bitmap_w; + fft_sample_40.upper_bitmap_weight = upper_bitmap_w; + fft_sample_40.max_exp = mag_info->max_exp & 0xf; + + fft_sample_40.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_40; + } else { + u8 max_index, bitmap_w; + u16 magnitude; + struct ath_ht20_mag_info *mag_info; + + length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); + fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; + fft_sample_20.tlv.length = __cpu_to_be16(length); + fft_sample_20.freq = __cpu_to_be16(freq); + + fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); + fft_sample_20.noise = ah->noise; + + mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; + magnitude = spectral_max_magnitude(mag_info->all_bins); + fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); + max_index = spectral_max_index(mag_info->all_bins); + fft_sample_20.max_index = max_index; + bitmap_w = spectral_bitmap_weight(mag_info->all_bins); + fft_sample_20.bitmap_weight = bitmap_w; + fft_sample_20.max_exp = mag_info->max_exp & 0xf; + + fft_sample_20.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_20; + } - ath_debug_send_fft_sample(sc, &fft_sample.tlv); + ath_debug_send_fft_sample(sc, tlv); return 1; #else return 0; @@ -1308,7 +1381,7 @@ static void ath9k_apply_ampdu_details(struct ath_softc *sc, int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) { - struct ath_buf *bf; + struct ath_rxbuf *bf; struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb; struct ieee80211_rx_status *rxs; struct ath_hw *ah = sc->sc_ah; diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h index fde6da619f30f96e146c00b017e5f37a9df0e80d..0db37f230018ee2692ef4fcbff7cf3f0b3135e8e 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.h +++ b/drivers/net/wireless/ath/ath9k/wmi.h @@ -39,7 +39,7 @@ struct wmi_fw_version { struct wmi_event_swba { __be64 tsf; u8 beacon_pending; -}; +} __packed; /* * 64 - HTC header - WMI header - 1 / txstatus diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index dd30452df9663574d54024f20eeac031a9910ba3..09cdbcd097394a3a2c324230c2743f5d181b0900 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1241,12 +1241,13 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, if (bf->bf_next) info.link = bf->bf_next->bf_daddr; else - info.link = 0; + info.link = (sc->tx99_state) ? bf->bf_daddr : 0; if (!bf_first) { bf_first = bf; - info.flags = ATH9K_TXDESC_INTREQ; + if (!sc->tx99_state) + info.flags = ATH9K_TXDESC_INTREQ; if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) || txq == sc->tx.uapsdq) info.flags |= ATH9K_TXDESC_CLRDMASK; @@ -1704,16 +1705,9 @@ int ath_cabq_update(struct ath_softc *sc) int qnum = sc->beacon.cabq->axq_qnum; ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi); - /* - * Ensure the readytime % is within the bounds. - */ - if (sc->config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND) - sc->config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND; - else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND) - sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND; qi.tqi_readyTime = (cur_conf->beacon_interval * - sc->config.cabqReadytime) / 100; + ATH_CABQ_READY_TIME) / 100; ath_txq_update(sc, qnum, &qi); return 0; @@ -1948,7 +1942,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc); } - if (!edma) { + if (!edma || sc->tx99_state) { TX_STAT_INC(txq->axq_qnum, txstart); ath9k_hw_txstart(ah, txq->axq_qnum); } @@ -2027,6 +2021,9 @@ static void setup_frame_info(struct ieee80211_hw *hw, fi->keyix = ATH9K_TXKEYIX_INVALID; fi->keytype = keytype; fi->framelen = framelen; + + if (!rate) + return; fi->rtscts_rate = rate->hw_value; if (short_preamble) fi->rtscts_rate |= rate->hw_value_short; @@ -2037,8 +2034,7 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate) struct ath_hw *ah = sc->sc_ah; struct ath9k_channel *curchan = ah->curchan; - if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && - (curchan->channelFlags & CHANNEL_5GHZ) && + if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && IS_CHAN_5GHZ(curchan) && (chainmask == 0x7) && (rate < 0x90)) return 0x3; else if (AR_SREV_9462(ah) && ath9k_hw_btcoex_is_enabled(ah) && @@ -2329,7 +2325,7 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, ath_dbg(common, XMIT, "TX complete: skb: %p\n", skb); if (sc->sc_ah->caldata) - sc->sc_ah->caldata->paprd_packet_sent = true; + set_bit(PAPRD_PACKET_SENT, &sc->sc_ah->caldata->cal_flags); if (!(tx_flags & ATH_TX_ERROR)) /* Frame was ACKed */ @@ -2379,6 +2375,8 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); bf->bf_buf_addr = 0; + if (sc->tx99_state) + goto skip_tx_complete; if (bf->bf_state.bfs_paprd) { if (time_after(jiffies, @@ -2391,6 +2389,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, ath_debug_stat_tx(sc, bf, ts, txq, tx_flags); ath_tx_complete(sc, skb, tx_flags, txq); } +skip_tx_complete: /* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't * accidentally reference it later. */ @@ -2749,3 +2748,46 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) ath_txq_unlock(sc, txq); } } + +int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, + struct ath_tx_control *txctl) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ath_frame_info *fi = get_frame_info(skb); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_buf *bf; + int padpos, padsize; + + padpos = ieee80211_hdrlen(hdr->frame_control); + padsize = padpos & 3; + + if (padsize && skb->len > padpos) { + if (skb_headroom(skb) < padsize) { + ath_dbg(common, XMIT, + "tx99 padding failed\n"); + return -EINVAL; + } + + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, padpos); + } + + fi->keyix = ATH9K_TXKEYIX_INVALID; + fi->framelen = skb->len + FCS_LEN; + fi->keytype = ATH9K_KEY_TYPE_CLEAR; + + bf = ath_tx_setup_buffer(sc, txctl->txq, NULL, skb); + if (!bf) { + ath_dbg(common, XMIT, "tx99 buffer setup failed\n"); + return -EINVAL; + } + + ath_set_rates(sc->tx99_vif, NULL, bf); + + ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr); + ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum); + + ath_tx_send_normal(sc, txctl->txq, NULL, skb); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c similarity index 95% rename from drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c rename to drivers/net/wireless/ath/dfs_pattern_detector.c index 491305c81fcecd36f5bc9bae0d7b66afd1a7e45e..a1a69c5db409262e3f63a1f1246d6590e448cbfa 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -19,7 +19,7 @@ #include "dfs_pattern_detector.h" #include "dfs_pri_detector.h" -#include "ath9k.h" +#include "ath.h" /* * tolerated deviation of radar time stamp in usecs on both sides @@ -143,7 +143,6 @@ channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) { u32 sz, i; struct channel_detector *cd; - struct ath_common *common = ath9k_hw_common(dpd->ah); cd = kmalloc(sizeof(*cd), GFP_ATOMIC); if (cd == NULL) @@ -167,7 +166,7 @@ channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) return cd; fail: - ath_dbg(common, DFS, + ath_dbg(dpd->common, DFS, "failed to allocate channel_detector for freq=%d\n", freq); channel_detector_exit(dpd, cd); return NULL; @@ -242,7 +241,7 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) struct pri_detector *pd = cd->detectors[i]; struct pri_sequence *ps = pd->add_pulse(pd, event); if (ps != NULL) { - ath_dbg(ath9k_hw_common(dpd->ah), DFS, + ath_dbg(dpd->common, DFS, "DFS: radar found on freq=%d: id=%d, pri=%d, " "count=%d, count_false=%d\n", event->freq, pd->rs->type_id, @@ -254,6 +253,12 @@ dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) return false; } +static struct ath_dfs_pool_stats +dpd_get_stats(struct dfs_pattern_detector *dpd) +{ + return global_dfs_pool_stats; +} + static bool dpd_set_domain(struct dfs_pattern_detector *dpd, enum nl80211_dfs_regions region) { @@ -284,14 +289,18 @@ static struct dfs_pattern_detector default_dpd = { .exit = dpd_exit, .set_dfs_domain = dpd_set_domain, .add_pulse = dpd_add_pulse, + .get_stats = dpd_get_stats, .region = NL80211_DFS_UNSET, }; struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) +dfs_pattern_detector_init(struct ath_common *common, + enum nl80211_dfs_regions region) { struct dfs_pattern_detector *dpd; - struct ath_common *common = ath9k_hw_common(ah); + + if (!config_enabled(CONFIG_CFG80211_CERTIFICATION_ONUS)) + return NULL; dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); if (dpd == NULL) @@ -300,7 +309,7 @@ dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) *dpd = default_dpd; INIT_LIST_HEAD(&dpd->channel_detectors); - dpd->ah = ah; + dpd->common = common; if (dpd->set_dfs_domain(dpd, region)) return dpd; diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/dfs_pattern_detector.h similarity index 87% rename from drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h rename to drivers/net/wireless/ath/dfs_pattern_detector.h index 90a5abcc4265aeeddcaa012058de9e3c6838d415..dde2652b787cd533d7d0c2c411aefd99ada56c4b 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/dfs_pattern_detector.h @@ -21,6 +21,19 @@ #include #include +/** + * struct ath_dfs_pool_stats - DFS Statistics for global pools + */ +struct ath_dfs_pool_stats { + u32 pool_reference; + u32 pulse_allocated; + u32 pulse_alloc_error; + u32 pulse_used; + u32 pseq_allocated; + u32 pseq_alloc_error; + u32 pseq_used; +}; + /** * struct pulse_event - describing pulses reported by PHY * @ts: pulse time stamp in us @@ -77,11 +90,12 @@ struct dfs_pattern_detector { bool (*add_pulse)(struct dfs_pattern_detector *dpd, struct pulse_event *pe); + struct ath_dfs_pool_stats (*get_stats)(struct dfs_pattern_detector *dpd); enum nl80211_dfs_regions region; u8 num_radar_types; u64 last_pulse_ts; /* needed for ath_dbg() */ - struct ath_hw *ah; + struct ath_common *common; const struct radar_detector_specs *radar_spec; struct list_head channel_detectors; @@ -92,15 +106,7 @@ struct dfs_pattern_detector { * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation * @return instance pointer on success, NULL otherwise */ -#if defined(CONFIG_ATH9K_DFS_CERTIFIED) extern struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region); -#else -static inline struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) -{ - return NULL; -} -#endif /* CONFIG_ATH9K_DFS_CERTIFIED */ - +dfs_pattern_detector_init(struct ath_common *common, + enum nl80211_dfs_regions region); #endif /* DFS_PATTERN_DETECTOR_H */ diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/dfs_pri_detector.c similarity index 98% rename from drivers/net/wireless/ath/ath9k/dfs_pri_detector.c rename to drivers/net/wireless/ath/dfs_pri_detector.c index 5ba4b6fe37c0aa630692c5291858fea0fcae6b35..43b60817888450555cd579b7803848054811d387 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c +++ b/drivers/net/wireless/ath/dfs_pri_detector.c @@ -17,10 +17,14 @@ #include #include -#include "ath9k.h" +#include "ath.h" #include "dfs_pattern_detector.h" #include "dfs_pri_detector.h" -#include "dfs_debug.h" + +struct ath_dfs_pool_stats global_dfs_pool_stats = {}; + +#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) +#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) /** * struct pulse_elem - elements in pulse queue @@ -392,7 +396,7 @@ static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de, if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) { pri_detector_reset(de, ts); - return false; + return NULL; } ps = pseq_handler_check_detection(de); diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/dfs_pri_detector.h similarity index 97% rename from drivers/net/wireless/ath/ath9k/dfs_pri_detector.h rename to drivers/net/wireless/ath/dfs_pri_detector.h index 723962d1abc68421a73b4c200cc6cb1bcf272be6..79f0fff4d1e662d065a4afb43ebde80d83eaa96c 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h +++ b/drivers/net/wireless/ath/dfs_pri_detector.h @@ -19,6 +19,8 @@ #include +extern struct ath_dfs_pool_stats global_dfs_pool_stats; + /** * struct pri_sequence - sequence of pulses matching one PRI * @head: list_head diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 7d077c752dd56ca9e3c3d9cb0aae0c951795cadb..c00687e05688e6498b70131ba11c1105d0b933b1 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -356,14 +356,131 @@ static u16 ath_regd_find_country_by_name(char *alpha2) return -1; } +static int __ath_reg_dyn_country(struct wiphy *wiphy, + struct ath_regulatory *reg, + struct regulatory_request *request) +{ + u16 country_code; + + if (!ath_is_world_regd(reg)) + return -EINVAL; + + country_code = ath_regd_find_country_by_name(request->alpha2); + if (country_code == (u16) -1) + return -EINVAL; + + reg->current_rd = COUNTRY_ERD_FLAG; + reg->current_rd |= country_code; + + __ath_regd_init(reg); + + ath_reg_apply_world_flags(wiphy, request->initiator, reg); + + return 0; +} + +static void ath_reg_dyn_country(struct wiphy *wiphy, + struct ath_regulatory *reg, + struct regulatory_request *request) +{ + if (__ath_reg_dyn_country(wiphy, reg, request)) + return; + + printk(KERN_DEBUG "ath: regdomain 0x%0x " + "dynamically updated by %s\n", + reg->current_rd, + reg_initiator_name(request->initiator)); +} + +static bool dynamic_country_user_possible(struct ath_regulatory *reg) +{ + if (config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_CERT_TESTING)) + return true; + + switch (reg->country_code) { + case CTRY_UNITED_STATES: + case CTRY_JAPAN1: + case CTRY_JAPAN2: + case CTRY_JAPAN3: + case CTRY_JAPAN4: + case CTRY_JAPAN5: + case CTRY_JAPAN6: + case CTRY_JAPAN7: + case CTRY_JAPAN8: + case CTRY_JAPAN9: + case CTRY_JAPAN10: + case CTRY_JAPAN11: + case CTRY_JAPAN12: + case CTRY_JAPAN13: + case CTRY_JAPAN14: + case CTRY_JAPAN15: + case CTRY_JAPAN16: + case CTRY_JAPAN17: + case CTRY_JAPAN18: + case CTRY_JAPAN19: + case CTRY_JAPAN20: + case CTRY_JAPAN21: + case CTRY_JAPAN22: + case CTRY_JAPAN23: + case CTRY_JAPAN24: + case CTRY_JAPAN25: + case CTRY_JAPAN26: + case CTRY_JAPAN27: + case CTRY_JAPAN28: + case CTRY_JAPAN29: + case CTRY_JAPAN30: + case CTRY_JAPAN31: + case CTRY_JAPAN32: + case CTRY_JAPAN33: + case CTRY_JAPAN34: + case CTRY_JAPAN35: + case CTRY_JAPAN36: + case CTRY_JAPAN37: + case CTRY_JAPAN38: + case CTRY_JAPAN39: + case CTRY_JAPAN40: + case CTRY_JAPAN41: + case CTRY_JAPAN42: + case CTRY_JAPAN43: + case CTRY_JAPAN44: + case CTRY_JAPAN45: + case CTRY_JAPAN46: + case CTRY_JAPAN47: + case CTRY_JAPAN48: + case CTRY_JAPAN49: + case CTRY_JAPAN50: + case CTRY_JAPAN51: + case CTRY_JAPAN52: + case CTRY_JAPAN53: + case CTRY_JAPAN54: + case CTRY_JAPAN55: + case CTRY_JAPAN56: + case CTRY_JAPAN57: + case CTRY_JAPAN58: + case CTRY_JAPAN59: + return false; + } + + return true; +} + +static void ath_reg_dyn_country_user(struct wiphy *wiphy, + struct ath_regulatory *reg, + struct regulatory_request *request) +{ + if (!config_enabled(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS)) + return; + if (!dynamic_country_user_possible(reg)) + return; + ath_reg_dyn_country(wiphy, reg, request); +} + void ath_reg_notifier_apply(struct wiphy *wiphy, struct regulatory_request *request, struct ath_regulatory *reg) { struct ath_common *common = container_of(reg, struct ath_common, regulatory); - u16 country_code; - /* We always apply this */ ath_reg_apply_radar_flags(wiphy); @@ -388,25 +505,12 @@ void ath_reg_notifier_apply(struct wiphy *wiphy, sizeof(struct ath_regulatory)); break; case NL80211_REGDOM_SET_BY_DRIVER: + break; case NL80211_REGDOM_SET_BY_USER: + ath_reg_dyn_country_user(wiphy, reg, request); break; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - if (!ath_is_world_regd(reg)) - break; - - country_code = ath_regd_find_country_by_name(request->alpha2); - if (country_code == (u16) -1) - break; - - reg->current_rd = COUNTRY_ERD_FLAG; - reg->current_rd |= country_code; - - printk(KERN_DEBUG "ath: regdomain 0x%0x updated by CountryIE\n", - reg->current_rd); - __ath_regd_init(reg); - - ath_reg_apply_world_flags(wiphy, request->initiator, reg); - + ath_reg_dyn_country(wiphy, reg, request); break; } } diff --git a/drivers/net/wireless/ath/wcn36xx/Kconfig b/drivers/net/wireless/ath/wcn36xx/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..591ebaea826564bf339f82b8152394dca022b5e8 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/Kconfig @@ -0,0 +1,16 @@ +config WCN36XX + tristate "Qualcomm Atheros WCN3660/3680 support" + depends on MAC80211 && HAS_DMA + ---help--- + This module adds support for wireless adapters based on + Qualcomm Atheros WCN3660 and WCN3680 mobile chipsets. + + If you choose to build a module, it'll be called wcn36xx. + +config WCN36XX_DEBUGFS + bool "WCN36XX debugfs support" + depends on WCN36XX + ---help--- + Enabled debugfs support + + If unsure, say Y to make it easier to debug problems. diff --git a/drivers/net/wireless/ath/wcn36xx/Makefile b/drivers/net/wireless/ath/wcn36xx/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..50c43b4382bacd91c9ff8380a1af0eaaafea5ba1 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_WCN36XX) := wcn36xx.o +wcn36xx-y += main.o \ + dxe.o \ + txrx.o \ + smd.o \ + pmc.o \ + debug.o diff --git a/drivers/net/wireless/ath/wcn36xx/debug.c b/drivers/net/wireless/ath/wcn36xx/debug.c new file mode 100644 index 0000000000000000000000000000000000000000..5b84f7ae0b1e3820cdee9737f9e4bacd8ed7b8d3 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/debug.c @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include "wcn36xx.h" +#include "debug.h" +#include "pmc.h" + +#ifdef CONFIG_WCN36XX_DEBUGFS + +static ssize_t read_file_bool_bmps(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wcn36xx *wcn = file->private_data; + struct wcn36xx_vif *vif_priv = NULL; + struct ieee80211_vif *vif = NULL; + char buf[3]; + + list_for_each_entry(vif_priv, &wcn->vif_list, list) { + vif = container_of((void *)vif_priv, + struct ieee80211_vif, + drv_priv); + if (NL80211_IFTYPE_STATION == vif->type) { + if (vif_priv->pw_state == WCN36XX_BMPS) + buf[0] = '1'; + else + buf[0] = '0'; + break; + } + } + buf[1] = '\n'; + buf[2] = 0x00; + + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t write_file_bool_bmps(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wcn36xx *wcn = file->private_data; + struct wcn36xx_vif *vif_priv = NULL; + struct ieee80211_vif *vif = NULL; + + char buf[32]; + int buf_size; + + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + switch (buf[0]) { + case 'y': + case 'Y': + case '1': + list_for_each_entry(vif_priv, &wcn->vif_list, list) { + vif = container_of((void *)vif_priv, + struct ieee80211_vif, + drv_priv); + if (NL80211_IFTYPE_STATION == vif->type) { + wcn36xx_enable_keep_alive_null_packet(wcn, vif); + wcn36xx_pmc_enter_bmps_state(wcn, vif); + } + } + break; + case 'n': + case 'N': + case '0': + list_for_each_entry(vif_priv, &wcn->vif_list, list) { + vif = container_of((void *)vif_priv, + struct ieee80211_vif, + drv_priv); + if (NL80211_IFTYPE_STATION == vif->type) + wcn36xx_pmc_exit_bmps_state(wcn, vif); + } + break; + } + + return count; +} + +static const struct file_operations fops_wcn36xx_bmps = { + .open = simple_open, + .read = read_file_bool_bmps, + .write = write_file_bool_bmps, +}; + +static ssize_t write_file_dump(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wcn36xx *wcn = file->private_data; + char buf[255], *tmp; + int buf_size; + u32 arg[WCN36xx_MAX_DUMP_ARGS]; + int i; + + memset(buf, 0, sizeof(buf)); + memset(arg, 0, sizeof(arg)); + + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + tmp = buf; + + for (i = 0; i < WCN36xx_MAX_DUMP_ARGS; i++) { + char *begin; + begin = strsep(&tmp, " "); + if (begin == NULL) + break; + + if (kstrtoul(begin, 0, (unsigned long *)(arg + i)) != 0) + break; + } + + wcn36xx_info("DUMP args is %d %d %d %d %d\n", arg[0], arg[1], arg[2], + arg[3], arg[4]); + wcn36xx_smd_dump_cmd_req(wcn, arg[0], arg[1], arg[2], arg[3], arg[4]); + + return count; +} + +static const struct file_operations fops_wcn36xx_dump = { + .open = simple_open, + .write = write_file_dump, +}; + +#define ADD_FILE(name, mode, fop, priv_data) \ + do { \ + struct dentry *d; \ + d = debugfs_create_file(__stringify(name), \ + mode, dfs->rootdir, \ + priv_data, fop); \ + dfs->file_##name.dentry = d; \ + if (IS_ERR(d)) { \ + wcn36xx_warn("Create the debugfs entry failed");\ + dfs->file_##name.dentry = NULL; \ + } \ + } while (0) + + +void wcn36xx_debugfs_init(struct wcn36xx *wcn) +{ + struct wcn36xx_dfs_entry *dfs = &wcn->dfs; + + dfs->rootdir = debugfs_create_dir(KBUILD_MODNAME, + wcn->hw->wiphy->debugfsdir); + if (IS_ERR(dfs->rootdir)) { + wcn36xx_warn("Create the debugfs failed\n"); + dfs->rootdir = NULL; + } + + ADD_FILE(bmps_switcher, S_IRUSR | S_IWUSR, + &fops_wcn36xx_bmps, wcn); + ADD_FILE(dump, S_IWUSR, &fops_wcn36xx_dump, wcn); +} + +void wcn36xx_debugfs_exit(struct wcn36xx *wcn) +{ + struct wcn36xx_dfs_entry *dfs = &wcn->dfs; + debugfs_remove_recursive(dfs->rootdir); +} + +#endif /* CONFIG_WCN36XX_DEBUGFS */ diff --git a/drivers/net/wireless/ath/wcn36xx/debug.h b/drivers/net/wireless/ath/wcn36xx/debug.h new file mode 100644 index 0000000000000000000000000000000000000000..46307aa562d37226e21eab4f976d128109f59380 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/debug.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WCN36XX_DEBUG_H_ +#define _WCN36XX_DEBUG_H_ + +#include + +#define WCN36xx_MAX_DUMP_ARGS 5 + +#ifdef CONFIG_WCN36XX_DEBUGFS +struct wcn36xx_dfs_file { + struct dentry *dentry; + u32 value; +}; + +struct wcn36xx_dfs_entry { + struct dentry *rootdir; + struct wcn36xx_dfs_file file_bmps_switcher; + struct wcn36xx_dfs_file file_dump; +}; + +void wcn36xx_debugfs_init(struct wcn36xx *wcn); +void wcn36xx_debugfs_exit(struct wcn36xx *wcn); + +#else +static inline void wcn36xx_debugfs_init(struct wcn36xx *wcn) +{ +} +static inline void wcn36xx_debugfs_exit(struct wcn36xx *wcn) +{ +} + +#endif /* CONFIG_WCN36XX_DEBUGFS */ + +#endif /* _WCN36XX_DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c new file mode 100644 index 0000000000000000000000000000000000000000..ee25786b44478fd094df1c3b165c9caf0d0bc73b --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -0,0 +1,805 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* DXE - DMA transfer engine + * we have 2 channels(High prio and Low prio) for TX and 2 channels for RX. + * through low channels data packets are transfered + * through high channels managment packets are transfered + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "wcn36xx.h" +#include "txrx.h" + +void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low) +{ + struct wcn36xx_dxe_ch *ch = is_low ? + &wcn->dxe_tx_l_ch : + &wcn->dxe_tx_h_ch; + + return ch->head_blk_ctl->bd_cpu_addr; +} + +static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data) +{ + wcn36xx_dbg(WCN36XX_DBG_DXE, + "wcn36xx_dxe_write_register: addr=%x, data=%x\n", + addr, data); + + writel(data, wcn->mmio + addr); +} + +static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data) +{ + *data = readl(wcn->mmio + addr); + + wcn36xx_dbg(WCN36XX_DBG_DXE, + "wcn36xx_dxe_read_register: addr=%x, data=%x\n", + addr, *data); +} + +static void wcn36xx_dxe_free_ctl_block(struct wcn36xx_dxe_ch *ch) +{ + struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl, *next; + int i; + + for (i = 0; i < ch->desc_num && ctl; i++) { + next = ctl->next; + kfree(ctl); + ctl = next; + } +} + +static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch) +{ + struct wcn36xx_dxe_ctl *prev_ctl = NULL; + struct wcn36xx_dxe_ctl *cur_ctl = NULL; + int i; + + for (i = 0; i < ch->desc_num; i++) { + cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL); + if (!cur_ctl) + goto out_fail; + + cur_ctl->ctl_blk_order = i; + if (i == 0) { + ch->head_blk_ctl = cur_ctl; + ch->tail_blk_ctl = cur_ctl; + } else if (ch->desc_num - 1 == i) { + prev_ctl->next = cur_ctl; + cur_ctl->next = ch->head_blk_ctl; + } else { + prev_ctl->next = cur_ctl; + } + prev_ctl = cur_ctl; + } + + return 0; + +out_fail: + wcn36xx_dxe_free_ctl_block(ch); + return -ENOMEM; +} + +int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn) +{ + int ret; + + wcn->dxe_tx_l_ch.ch_type = WCN36XX_DXE_CH_TX_L; + wcn->dxe_tx_h_ch.ch_type = WCN36XX_DXE_CH_TX_H; + wcn->dxe_rx_l_ch.ch_type = WCN36XX_DXE_CH_RX_L; + wcn->dxe_rx_h_ch.ch_type = WCN36XX_DXE_CH_RX_H; + + wcn->dxe_tx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_L; + wcn->dxe_tx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_H; + wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L; + wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H; + + wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L; + wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H; + + wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD; + wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD; + + wcn->dxe_tx_l_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_L_SKB; + wcn->dxe_tx_h_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_H_SKB; + + wcn->dxe_tx_l_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_L; + wcn->dxe_tx_h_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_H; + + wcn->dxe_tx_l_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_L; + wcn->dxe_tx_h_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_H; + + /* DXE control block allocation */ + ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_l_ch); + if (ret) + goto out_err; + ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_h_ch); + if (ret) + goto out_err; + ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_l_ch); + if (ret) + goto out_err; + ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_h_ch); + if (ret) + goto out_err; + + /* Initialize SMSM state Clear TX Enable RING EMPTY STATE */ + ret = wcn->ctrl_ops->smsm_change_state( + WCN36XX_SMSM_WLAN_TX_ENABLE, + WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY); + + return 0; + +out_err: + wcn36xx_err("Failed to allocate DXE control blocks\n"); + wcn36xx_dxe_free_ctl_blks(wcn); + return -ENOMEM; +} + +void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn) +{ + wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_l_ch); + wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_h_ch); + wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_l_ch); + wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch); +} + +static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch) +{ + struct wcn36xx_dxe_desc *cur_dxe = NULL; + struct wcn36xx_dxe_desc *prev_dxe = NULL; + struct wcn36xx_dxe_ctl *cur_ctl = NULL; + size_t size; + int i; + + size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc); + wcn_ch->cpu_addr = dma_alloc_coherent(NULL, size, &wcn_ch->dma_addr, + GFP_KERNEL); + if (!wcn_ch->cpu_addr) + return -ENOMEM; + + memset(wcn_ch->cpu_addr, 0, size); + + cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr; + cur_ctl = wcn_ch->head_blk_ctl; + + for (i = 0; i < wcn_ch->desc_num; i++) { + cur_ctl->desc = cur_dxe; + cur_ctl->desc_phy_addr = wcn_ch->dma_addr + + i * sizeof(struct wcn36xx_dxe_desc); + + switch (wcn_ch->ch_type) { + case WCN36XX_DXE_CH_TX_L: + cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L; + cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L; + break; + case WCN36XX_DXE_CH_TX_H: + cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H; + cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H; + break; + case WCN36XX_DXE_CH_RX_L: + cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; + cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_L; + break; + case WCN36XX_DXE_CH_RX_H: + cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; + cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_H; + break; + } + if (0 == i) { + cur_dxe->phy_next_l = 0; + } else if ((0 < i) && (i < wcn_ch->desc_num - 1)) { + prev_dxe->phy_next_l = + cur_ctl->desc_phy_addr; + } else if (i == (wcn_ch->desc_num - 1)) { + prev_dxe->phy_next_l = + cur_ctl->desc_phy_addr; + cur_dxe->phy_next_l = + wcn_ch->head_blk_ctl->desc_phy_addr; + } + cur_ctl = cur_ctl->next; + prev_dxe = cur_dxe; + cur_dxe++; + } + + return 0; +} + +static void wcn36xx_dxe_init_tx_bd(struct wcn36xx_dxe_ch *ch, + struct wcn36xx_dxe_mem_pool *pool) +{ + int i, chunk_size = pool->chunk_size; + dma_addr_t bd_phy_addr = pool->phy_addr; + void *bd_cpu_addr = pool->virt_addr; + struct wcn36xx_dxe_ctl *cur = ch->head_blk_ctl; + + for (i = 0; i < ch->desc_num; i++) { + /* Only every second dxe needs a bd pointer, + the other will point to the skb data */ + if (!(i & 1)) { + cur->bd_phy_addr = bd_phy_addr; + cur->bd_cpu_addr = bd_cpu_addr; + bd_phy_addr += chunk_size; + bd_cpu_addr += chunk_size; + } else { + cur->bd_phy_addr = 0; + cur->bd_cpu_addr = NULL; + } + cur = cur->next; + } +} + +static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch) +{ + int reg_data = 0; + + wcn36xx_dxe_read_register(wcn, + WCN36XX_DXE_INT_MASK_REG, + ®_data); + + reg_data |= wcn_ch; + + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_INT_MASK_REG, + (int)reg_data); + return 0; +} + +static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl) +{ + struct wcn36xx_dxe_desc *dxe = ctl->desc; + struct sk_buff *skb; + + skb = alloc_skb(WCN36XX_PKT_SIZE, GFP_ATOMIC); + if (skb == NULL) + return -ENOMEM; + + dxe->dst_addr_l = dma_map_single(NULL, + skb_tail_pointer(skb), + WCN36XX_PKT_SIZE, + DMA_FROM_DEVICE); + ctl->skb = skb; + + return 0; +} + +static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn, + struct wcn36xx_dxe_ch *wcn_ch) +{ + int i; + struct wcn36xx_dxe_ctl *cur_ctl = NULL; + + cur_ctl = wcn_ch->head_blk_ctl; + + for (i = 0; i < wcn_ch->desc_num; i++) { + wcn36xx_dxe_fill_skb(cur_ctl); + cur_ctl = cur_ctl->next; + } + + return 0; +} + +static void wcn36xx_dxe_ch_free_skbs(struct wcn36xx *wcn, + struct wcn36xx_dxe_ch *wcn_ch) +{ + struct wcn36xx_dxe_ctl *cur = wcn_ch->head_blk_ctl; + int i; + + for (i = 0; i < wcn_ch->desc_num; i++) { + kfree_skb(cur->skb); + cur = cur->next; + } +} + +void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) +{ + struct ieee80211_tx_info *info; + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&wcn->dxe_lock, flags); + skb = wcn->tx_ack_skb; + wcn->tx_ack_skb = NULL; + spin_unlock_irqrestore(&wcn->dxe_lock, flags); + + if (!skb) { + wcn36xx_warn("Spurious TX complete indication\n"); + return; + } + + info = IEEE80211_SKB_CB(skb); + + if (status == 1) + info->flags |= IEEE80211_TX_STAT_ACK; + + wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status); + + ieee80211_tx_status_irqsafe(wcn->hw, skb); + ieee80211_wake_queues(wcn->hw); +} + +static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) +{ + struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl; + struct ieee80211_tx_info *info; + unsigned long flags; + + /* + * Make at least one loop of do-while because in case ring is + * completely full head and tail are pointing to the same element + * and while-do will not make any cycles. + */ + do { + if (ctl->skb) { + dma_unmap_single(NULL, ctl->desc->src_addr_l, + ctl->skb->len, DMA_TO_DEVICE); + info = IEEE80211_SKB_CB(ctl->skb); + if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) { + /* Keep frame until TX status comes */ + ieee80211_free_txskb(wcn->hw, ctl->skb); + } + spin_lock_irqsave(&ctl->skb_lock, flags); + if (wcn->queues_stopped) { + wcn->queues_stopped = false; + ieee80211_wake_queues(wcn->hw); + } + spin_unlock_irqrestore(&ctl->skb_lock, flags); + + ctl->skb = NULL; + } + ctl = ctl->next; + } while (ctl != ch->head_blk_ctl && + !(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)); + + ch->tail_blk_ctl = ctl; +} + +static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev) +{ + struct wcn36xx *wcn = (struct wcn36xx *)dev; + int int_src, int_reason; + + wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); + + if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) { + wcn36xx_dxe_read_register(wcn, + WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H, + &int_reason); + + /* TODO: Check int_reason */ + + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_CLR, + WCN36XX_INT_MASK_CHAN_TX_H); + + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, + WCN36XX_INT_MASK_CHAN_TX_H); + wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n"); + reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch); + } + + if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) { + wcn36xx_dxe_read_register(wcn, + WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L, + &int_reason); + /* TODO: Check int_reason */ + + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_0_INT_CLR, + WCN36XX_INT_MASK_CHAN_TX_L); + + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR, + WCN36XX_INT_MASK_CHAN_TX_L); + wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n"); + reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch); + } + + return IRQ_HANDLED; +} + +static irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev) +{ + struct wcn36xx *wcn = (struct wcn36xx *)dev; + + disable_irq_nosync(wcn->rx_irq); + wcn36xx_dxe_rx_frame(wcn); + enable_irq(wcn->rx_irq); + return IRQ_HANDLED; +} + +static int wcn36xx_dxe_request_irqs(struct wcn36xx *wcn) +{ + int ret; + + ret = request_irq(wcn->tx_irq, wcn36xx_irq_tx_complete, + IRQF_TRIGGER_HIGH, "wcn36xx_tx", wcn); + if (ret) { + wcn36xx_err("failed to alloc tx irq\n"); + goto out_err; + } + + ret = request_irq(wcn->rx_irq, wcn36xx_irq_rx_ready, IRQF_TRIGGER_HIGH, + "wcn36xx_rx", wcn); + if (ret) { + wcn36xx_err("failed to alloc rx irq\n"); + goto out_txirq; + } + + enable_irq_wake(wcn->rx_irq); + + return 0; + +out_txirq: + free_irq(wcn->tx_irq, wcn); +out_err: + return ret; + +} + +static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn, + struct wcn36xx_dxe_ch *ch) +{ + struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl; + struct wcn36xx_dxe_desc *dxe = ctl->desc; + dma_addr_t dma_addr; + struct sk_buff *skb; + + while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) { + skb = ctl->skb; + dma_addr = dxe->dst_addr_l; + wcn36xx_dxe_fill_skb(ctl); + + switch (ch->ch_type) { + case WCN36XX_DXE_CH_RX_L: + dxe->ctrl = WCN36XX_DXE_CTRL_RX_L; + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, + WCN36XX_DXE_INT_CH1_MASK); + break; + case WCN36XX_DXE_CH_RX_H: + dxe->ctrl = WCN36XX_DXE_CTRL_RX_H; + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR, + WCN36XX_DXE_INT_CH3_MASK); + break; + default: + wcn36xx_warn("Unknown channel\n"); + } + + dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE, + DMA_FROM_DEVICE); + wcn36xx_rx_skb(wcn, skb); + ctl = ctl->next; + dxe = ctl->desc; + } + + ch->head_blk_ctl = ctl; + + return 0; +} + +void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn) +{ + int int_src; + + wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src); + + /* RX_LOW_PRI */ + if (int_src & WCN36XX_DXE_INT_CH1_MASK) { + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, + WCN36XX_DXE_INT_CH1_MASK); + wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch)); + } + + /* RX_HIGH_PRI */ + if (int_src & WCN36XX_DXE_INT_CH3_MASK) { + /* Clean up all the INT within this channel */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR, + WCN36XX_DXE_INT_CH3_MASK); + wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch)); + } + + if (!int_src) + wcn36xx_warn("No DXE interrupt pending\n"); +} + +int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn) +{ + size_t s; + void *cpu_addr; + + /* Allocate BD headers for MGMT frames */ + + /* Where this come from ask QC */ + wcn->mgmt_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE + + 16 - (WCN36XX_BD_CHUNK_SIZE % 8); + + s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H; + cpu_addr = dma_alloc_coherent(NULL, s, &wcn->mgmt_mem_pool.phy_addr, + GFP_KERNEL); + if (!cpu_addr) + goto out_err; + + wcn->mgmt_mem_pool.virt_addr = cpu_addr; + memset(cpu_addr, 0, s); + + /* Allocate BD headers for DATA frames */ + + /* Where this come from ask QC */ + wcn->data_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE + + 16 - (WCN36XX_BD_CHUNK_SIZE % 8); + + s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L; + cpu_addr = dma_alloc_coherent(NULL, s, &wcn->data_mem_pool.phy_addr, + GFP_KERNEL); + if (!cpu_addr) + goto out_err; + + wcn->data_mem_pool.virt_addr = cpu_addr; + memset(cpu_addr, 0, s); + + return 0; + +out_err: + wcn36xx_dxe_free_mem_pools(wcn); + wcn36xx_err("Failed to allocate BD mempool\n"); + return -ENOMEM; +} + +void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn) +{ + if (wcn->mgmt_mem_pool.virt_addr) + dma_free_coherent(NULL, wcn->mgmt_mem_pool.chunk_size * + WCN36XX_DXE_CH_DESC_NUMB_TX_H, + wcn->mgmt_mem_pool.virt_addr, + wcn->mgmt_mem_pool.phy_addr); + + if (wcn->data_mem_pool.virt_addr) { + dma_free_coherent(NULL, wcn->data_mem_pool.chunk_size * + WCN36XX_DXE_CH_DESC_NUMB_TX_L, + wcn->data_mem_pool.virt_addr, + wcn->data_mem_pool.phy_addr); + } +} + +int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, + struct wcn36xx_vif *vif_priv, + struct sk_buff *skb, + bool is_low) +{ + struct wcn36xx_dxe_ctl *ctl = NULL; + struct wcn36xx_dxe_desc *desc = NULL; + struct wcn36xx_dxe_ch *ch = NULL; + unsigned long flags; + + ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch; + + ctl = ch->head_blk_ctl; + + spin_lock_irqsave(&ctl->next->skb_lock, flags); + + /* + * If skb is not null that means that we reached the tail of the ring + * hence ring is full. Stop queues to let mac80211 back off until ring + * has an empty slot again. + */ + if (NULL != ctl->next->skb) { + ieee80211_stop_queues(wcn->hw); + wcn->queues_stopped = true; + spin_unlock_irqrestore(&ctl->next->skb_lock, flags); + return -EBUSY; + } + spin_unlock_irqrestore(&ctl->next->skb_lock, flags); + + ctl->skb = NULL; + desc = ctl->desc; + + /* Set source address of the BD we send */ + desc->src_addr_l = ctl->bd_phy_addr; + + desc->dst_addr_l = ch->dxe_wq; + desc->fr_len = sizeof(struct wcn36xx_tx_bd); + desc->ctrl = ch->ctrl_bd; + + wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n"); + + wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ", + (char *)desc, sizeof(*desc)); + wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, + "BD >>> ", (char *)ctl->bd_cpu_addr, + sizeof(struct wcn36xx_tx_bd)); + + /* Set source address of the SKB we send */ + ctl = ctl->next; + ctl->skb = skb; + desc = ctl->desc; + if (ctl->bd_cpu_addr) { + wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n"); + return -EINVAL; + } + + desc->src_addr_l = dma_map_single(NULL, + ctl->skb->data, + ctl->skb->len, + DMA_TO_DEVICE); + + desc->dst_addr_l = ch->dxe_wq; + desc->fr_len = ctl->skb->len; + + /* set dxe descriptor to VALID */ + desc->ctrl = ch->ctrl_skb; + + wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ", + (char *)desc, sizeof(*desc)); + wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB >>> ", + (char *)ctl->skb->data, ctl->skb->len); + + /* Move the head of the ring to the next empty descriptor */ + ch->head_blk_ctl = ctl->next; + + /* + * When connected and trying to send data frame chip can be in sleep + * mode and writing to the register will not wake up the chip. Instead + * notify chip about new frame through SMSM bus. + */ + if (is_low && vif_priv->pw_state == WCN36XX_BMPS) { + wcn->ctrl_ops->smsm_change_state( + 0, + WCN36XX_SMSM_WLAN_TX_ENABLE); + } else { + /* indicate End Of Packet and generate interrupt on descriptor + * done. + */ + wcn36xx_dxe_write_register(wcn, + ch->reg_ctrl, ch->def_ctrl); + } + + return 0; +} + +int wcn36xx_dxe_init(struct wcn36xx *wcn) +{ + int reg_data = 0, ret; + + reg_data = WCN36XX_DXE_REG_RESET; + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data); + + /* Setting interrupt path */ + reg_data = WCN36XX_DXE_CCU_INT; + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data); + + /***************************************/ + /* Init descriptors for TX LOW channel */ + /***************************************/ + wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch); + wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool); + + /* Write channel head to a NEXT register */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L, + wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr); + + /* Program DMA destination addr for TX LOW */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_DEST_ADDR_TX_L, + WCN36XX_DXE_WQ_TX_L); + + wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L); + + /***************************************/ + /* Init descriptors for TX HIGH channel */ + /***************************************/ + wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch); + wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool); + + /* Write channel head to a NEXT register */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H, + wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr); + + /* Program DMA destination addr for TX HIGH */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_DEST_ADDR_TX_H, + WCN36XX_DXE_WQ_TX_H); + + wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data); + + /* Enable channel interrupts */ + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H); + + /***************************************/ + /* Init descriptors for RX LOW channel */ + /***************************************/ + wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch); + + /* For RX we need to preallocated buffers */ + wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch); + + /* Write channel head to a NEXT register */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L, + wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr); + + /* Write DMA source address */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_SRC_ADDR_RX_L, + WCN36XX_DXE_WQ_RX_L); + + /* Program preallocated destination address */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_DEST_ADDR_RX_L, + wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l); + + /* Enable default control registers */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_REG_CTL_RX_L, + WCN36XX_DXE_CH_DEFAULT_CTL_RX_L); + + /* Enable channel interrupts */ + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L); + + /***************************************/ + /* Init descriptors for RX HIGH channel */ + /***************************************/ + wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch); + + /* For RX we need to prealocat buffers */ + wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch); + + /* Write chanel head to a NEXT register */ + wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H, + wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr); + + /* Write DMA source address */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_SRC_ADDR_RX_H, + WCN36XX_DXE_WQ_RX_H); + + /* Program preallocated destination address */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_CH_DEST_ADDR_RX_H, + wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l); + + /* Enable default control registers */ + wcn36xx_dxe_write_register(wcn, + WCN36XX_DXE_REG_CTL_RX_H, + WCN36XX_DXE_CH_DEFAULT_CTL_RX_H); + + /* Enable channel interrupts */ + wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H); + + ret = wcn36xx_dxe_request_irqs(wcn); + if (ret < 0) + goto out_err; + + return 0; + +out_err: + return ret; +} + +void wcn36xx_dxe_deinit(struct wcn36xx *wcn) +{ + free_irq(wcn->tx_irq, wcn); + free_irq(wcn->rx_irq, wcn); + + if (wcn->tx_ack_skb) { + ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); + wcn->tx_ack_skb = NULL; + } + + wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch); + wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch); +} diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.h b/drivers/net/wireless/ath/wcn36xx/dxe.h new file mode 100644 index 0000000000000000000000000000000000000000..c88562f85de1c21a30c80a884cc36f85c7c6cfc9 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/dxe.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _DXE_H_ +#define _DXE_H_ + +#include "wcn36xx.h" + +/* +TX_LOW = DMA0 +TX_HIGH = DMA4 +RX_LOW = DMA1 +RX_HIGH = DMA3 +H2H_TEST_RX_TX = DMA2 +*/ + +/* DXE registers */ +#define WCN36XX_DXE_MEM_BASE 0x03000000 +#define WCN36XX_DXE_MEM_REG 0x202000 + +#define WCN36XX_DXE_CCU_INT 0xA0011 +#define WCN36XX_DXE_REG_CCU_INT 0x200b10 + +/* TODO This must calculated properly but not hardcoded */ +#define WCN36XX_DXE_CTRL_TX_L 0x328a44 +#define WCN36XX_DXE_CTRL_TX_H 0x32ce44 +#define WCN36XX_DXE_CTRL_RX_L 0x12ad2f +#define WCN36XX_DXE_CTRL_RX_H 0x12d12f +#define WCN36XX_DXE_CTRL_TX_H_BD 0x30ce45 +#define WCN36XX_DXE_CTRL_TX_H_SKB 0x32ce4d +#define WCN36XX_DXE_CTRL_TX_L_BD 0x308a45 +#define WCN36XX_DXE_CTRL_TX_L_SKB 0x328a4d + +/* TODO This must calculated properly but not hardcoded */ +#define WCN36XX_DXE_WQ_TX_L 0x17 +#define WCN36XX_DXE_WQ_TX_H 0x17 +#define WCN36XX_DXE_WQ_RX_L 0xB +#define WCN36XX_DXE_WQ_RX_H 0x4 + +/* DXE descriptor control filed */ +#define WCN36XX_DXE_CTRL_VALID_MASK (0x00000001) + +/* TODO This must calculated properly but not hardcoded */ +/* DXE default control register values */ +#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L 0x847EAD2F +#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H 0x84FED12F +#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H 0x853ECF4D +#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L 0x843e8b4d + +/* Common DXE registers */ +#define WCN36XX_DXE_MEM_CSR (WCN36XX_DXE_MEM_REG + 0x00) +#define WCN36XX_DXE_REG_CSR_RESET (WCN36XX_DXE_MEM_REG + 0x00) +#define WCN36XX_DXE_ENCH_ADDR (WCN36XX_DXE_MEM_REG + 0x04) +#define WCN36XX_DXE_REG_CH_EN (WCN36XX_DXE_MEM_REG + 0x08) +#define WCN36XX_DXE_REG_CH_DONE (WCN36XX_DXE_MEM_REG + 0x0C) +#define WCN36XX_DXE_REG_CH_ERR (WCN36XX_DXE_MEM_REG + 0x10) +#define WCN36XX_DXE_INT_MASK_REG (WCN36XX_DXE_MEM_REG + 0x18) +#define WCN36XX_DXE_INT_SRC_RAW_REG (WCN36XX_DXE_MEM_REG + 0x20) + /* #define WCN36XX_DXE_INT_CH6_MASK 0x00000040 */ + /* #define WCN36XX_DXE_INT_CH5_MASK 0x00000020 */ + #define WCN36XX_DXE_INT_CH4_MASK 0x00000010 + #define WCN36XX_DXE_INT_CH3_MASK 0x00000008 + /* #define WCN36XX_DXE_INT_CH2_MASK 0x00000004 */ + #define WCN36XX_DXE_INT_CH1_MASK 0x00000002 + #define WCN36XX_DXE_INT_CH0_MASK 0x00000001 +#define WCN36XX_DXE_0_INT_CLR (WCN36XX_DXE_MEM_REG + 0x30) +#define WCN36XX_DXE_0_INT_ED_CLR (WCN36XX_DXE_MEM_REG + 0x34) +#define WCN36XX_DXE_0_INT_DONE_CLR (WCN36XX_DXE_MEM_REG + 0x38) +#define WCN36XX_DXE_0_INT_ERR_CLR (WCN36XX_DXE_MEM_REG + 0x3C) + +#define WCN36XX_DXE_0_CH0_STATUS (WCN36XX_DXE_MEM_REG + 0x404) +#define WCN36XX_DXE_0_CH1_STATUS (WCN36XX_DXE_MEM_REG + 0x444) +#define WCN36XX_DXE_0_CH2_STATUS (WCN36XX_DXE_MEM_REG + 0x484) +#define WCN36XX_DXE_0_CH3_STATUS (WCN36XX_DXE_MEM_REG + 0x4C4) +#define WCN36XX_DXE_0_CH4_STATUS (WCN36XX_DXE_MEM_REG + 0x504) + +#define WCN36XX_DXE_REG_RESET 0x5c89 + +/* Temporary BMU Workqueue 4 */ +#define WCN36XX_DXE_BMU_WQ_RX_LOW 0xB +#define WCN36XX_DXE_BMU_WQ_RX_HIGH 0x4 +/* DMA channel offset */ +#define WCN36XX_DXE_TX_LOW_OFFSET 0x400 +#define WCN36XX_DXE_TX_HIGH_OFFSET 0x500 +#define WCN36XX_DXE_RX_LOW_OFFSET 0x440 +#define WCN36XX_DXE_RX_HIGH_OFFSET 0x4C0 + +/* Address of the next DXE descriptor */ +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR 0x001C +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_LOW_OFFSET + \ + WCN36XX_DXE_CH_NEXT_DESC_ADDR) +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_NEXT_DESC_ADDR) +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET + \ + WCN36XX_DXE_CH_NEXT_DESC_ADDR) +#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_NEXT_DESC_ADDR) + +/* DXE Descriptor source address */ +#define WCN36XX_DXE_CH_SRC_ADDR 0x000C +#define WCN36XX_DXE_CH_SRC_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET + \ + WCN36XX_DXE_CH_SRC_ADDR) +#define WCN36XX_DXE_CH_SRC_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_SRC_ADDR) + +/* DXE Descriptor address destination address */ +#define WCN36XX_DXE_CH_DEST_ADDR 0x0014 +#define WCN36XX_DXE_CH_DEST_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_LOW_OFFSET + \ + WCN36XX_DXE_CH_DEST_ADDR) +#define WCN36XX_DXE_CH_DEST_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_DEST_ADDR) +#define WCN36XX_DXE_CH_DEST_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET + \ + WCN36XX_DXE_CH_DEST_ADDR) +#define WCN36XX_DXE_CH_DEST_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_DEST_ADDR) + +/* Interrupt status */ +#define WCN36XX_DXE_CH_STATUS_REG_ADDR 0x0004 +#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_LOW_OFFSET + \ + WCN36XX_DXE_CH_STATUS_REG_ADDR) +#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_STATUS_REG_ADDR) +#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET + \ + WCN36XX_DXE_CH_STATUS_REG_ADDR) +#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET + \ + WCN36XX_DXE_CH_STATUS_REG_ADDR) + + +/* DXE default control register */ +#define WCN36XX_DXE_REG_CTL_RX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_LOW_OFFSET) +#define WCN36XX_DXE_REG_CTL_RX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_RX_HIGH_OFFSET) +#define WCN36XX_DXE_REG_CTL_TX_H (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_HIGH_OFFSET) +#define WCN36XX_DXE_REG_CTL_TX_L (WCN36XX_DXE_MEM_REG + \ + WCN36XX_DXE_TX_LOW_OFFSET) + +#define WCN36XX_SMSM_WLAN_TX_ENABLE 0x00000400 +#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY 0x00000200 + + +/* Interrupt control channel mask */ +#define WCN36XX_INT_MASK_CHAN_TX_L 0x00000001 +#define WCN36XX_INT_MASK_CHAN_RX_L 0x00000002 +#define WCN36XX_INT_MASK_CHAN_RX_H 0x00000008 +#define WCN36XX_INT_MASK_CHAN_TX_H 0x00000010 + +#define WCN36XX_BD_CHUNK_SIZE 128 + +#define WCN36XX_PKT_SIZE 0xF20 +enum wcn36xx_dxe_ch_type { + WCN36XX_DXE_CH_TX_L, + WCN36XX_DXE_CH_TX_H, + WCN36XX_DXE_CH_RX_L, + WCN36XX_DXE_CH_RX_H +}; + +/* amount of descriptors per channel */ +enum wcn36xx_dxe_ch_desc_num { + WCN36XX_DXE_CH_DESC_NUMB_TX_L = 128, + WCN36XX_DXE_CH_DESC_NUMB_TX_H = 10, + WCN36XX_DXE_CH_DESC_NUMB_RX_L = 512, + WCN36XX_DXE_CH_DESC_NUMB_RX_H = 40 +}; + +/** + * struct wcn36xx_dxe_desc - describes descriptor of one DXE buffer + * + * @ctrl: is a union that consists of following bits: + * union { + * u32 valid :1; //0 = DMA stop, 1 = DMA continue with this + * //descriptor + * u32 transfer_type :2; //0 = Host to Host space + * u32 eop :1; //End of Packet + * u32 bd_handling :1; //if transferType = Host to BMU, then 0 + * // means first 128 bytes contain BD, and 1 + * // means create new empty BD + * u32 siq :1; // SIQ + * u32 diq :1; // DIQ + * u32 pdu_rel :1; //0 = don't release BD and PDUs when done, + * // 1 = release them + * u32 bthld_sel :4; //BMU Threshold Select + * u32 prio :3; //Specifies the priority level to use for + * // the transfer + * u32 stop_channel :1; //1 = DMA stops processing further, channel + * //requires re-enabling after this + * u32 intr :1; //Interrupt on Descriptor Done + * u32 rsvd :1; //reserved + * u32 size :14;//14 bits used - ignored for BMU transfers, + * //only used for host to host transfers? + * } ctrl; + */ +struct wcn36xx_dxe_desc { + u32 ctrl; + u32 fr_len; + + u32 src_addr_l; + u32 dst_addr_l; + u32 phy_next_l; + u32 src_addr_h; + u32 dst_addr_h; + u32 phy_next_h; +} __packed; + +/* DXE Control block */ +struct wcn36xx_dxe_ctl { + struct wcn36xx_dxe_ctl *next; + struct wcn36xx_dxe_desc *desc; + unsigned int desc_phy_addr; + int ctl_blk_order; + struct sk_buff *skb; + spinlock_t skb_lock; + void *bd_cpu_addr; + dma_addr_t bd_phy_addr; +}; + +struct wcn36xx_dxe_ch { + enum wcn36xx_dxe_ch_type ch_type; + void *cpu_addr; + dma_addr_t dma_addr; + enum wcn36xx_dxe_ch_desc_num desc_num; + /* DXE control block ring */ + struct wcn36xx_dxe_ctl *head_blk_ctl; + struct wcn36xx_dxe_ctl *tail_blk_ctl; + + /* DXE channel specific configs */ + u32 dxe_wq; + u32 ctrl_bd; + u32 ctrl_skb; + u32 reg_ctrl; + u32 def_ctrl; +}; + +/* Memory Pool for BD headers */ +struct wcn36xx_dxe_mem_pool { + int chunk_size; + void *virt_addr; + dma_addr_t phy_addr; +}; + +struct wcn36xx_vif; +int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn); +void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn); +void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn); +int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn); +void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn); +int wcn36xx_dxe_init(struct wcn36xx *wcn); +void wcn36xx_dxe_deinit(struct wcn36xx *wcn); +int wcn36xx_dxe_init_channels(struct wcn36xx *wcn); +int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn, + struct wcn36xx_vif *vif_priv, + struct sk_buff *skb, + bool is_low); +void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status); +void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low); +#endif /* _DXE_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/hal.h b/drivers/net/wireless/ath/wcn36xx/hal.h new file mode 100644 index 0000000000000000000000000000000000000000..c02dbc618724720cee3c97a3fa86a249188fff5b --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/hal.h @@ -0,0 +1,4657 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _HAL_H_ +#define _HAL_H_ + +/*--------------------------------------------------------------------------- + API VERSIONING INFORMATION + + The RIVA API is versioned as MAJOR.MINOR.VERSION.REVISION + The MAJOR is incremented for major product/architecture changes + (and then MINOR/VERSION/REVISION are zeroed) + The MINOR is incremented for minor product/architecture changes + (and then VERSION/REVISION are zeroed) + The VERSION is incremented if a significant API change occurs + (and then REVISION is zeroed) + The REVISION is incremented if an insignificant API change occurs + or if a new API is added + All values are in the range 0..255 (ie they are 8-bit values) + ---------------------------------------------------------------------------*/ +#define WCN36XX_HAL_VER_MAJOR 1 +#define WCN36XX_HAL_VER_MINOR 4 +#define WCN36XX_HAL_VER_VERSION 1 +#define WCN36XX_HAL_VER_REVISION 2 + +/* This is to force compiler to use the maximum of an int ( 4 bytes ) */ +#define WCN36XX_HAL_MAX_ENUM_SIZE 0x7FFFFFFF +#define WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE 0x7FFF + +/* Max no. of transmit categories */ +#define STACFG_MAX_TC 8 + +/* The maximum value of access category */ +#define WCN36XX_HAL_MAX_AC 4 + +#define WCN36XX_HAL_IPV4_ADDR_LEN 4 + +#define WALN_HAL_STA_INVALID_IDX 0xFF +#define WCN36XX_HAL_BSS_INVALID_IDX 0xFF + +/* Default Beacon template size */ +#define BEACON_TEMPLATE_SIZE 0x180 + +/* Param Change Bitmap sent to HAL */ +#define PARAM_BCN_INTERVAL_CHANGED (1 << 0) +#define PARAM_SHORT_PREAMBLE_CHANGED (1 << 1) +#define PARAM_SHORT_SLOT_TIME_CHANGED (1 << 2) +#define PARAM_llACOEXIST_CHANGED (1 << 3) +#define PARAM_llBCOEXIST_CHANGED (1 << 4) +#define PARAM_llGCOEXIST_CHANGED (1 << 5) +#define PARAM_HT20MHZCOEXIST_CHANGED (1<<6) +#define PARAM_NON_GF_DEVICES_PRESENT_CHANGED (1<<7) +#define PARAM_RIFS_MODE_CHANGED (1<<8) +#define PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED (1<<9) +#define PARAM_OBSS_MODE_CHANGED (1<<10) +#define PARAM_BEACON_UPDATE_MASK \ + (PARAM_BCN_INTERVAL_CHANGED | \ + PARAM_SHORT_PREAMBLE_CHANGED | \ + PARAM_SHORT_SLOT_TIME_CHANGED | \ + PARAM_llACOEXIST_CHANGED | \ + PARAM_llBCOEXIST_CHANGED | \ + PARAM_llGCOEXIST_CHANGED | \ + PARAM_HT20MHZCOEXIST_CHANGED | \ + PARAM_NON_GF_DEVICES_PRESENT_CHANGED | \ + PARAM_RIFS_MODE_CHANGED | \ + PARAM_LSIG_TXOP_FULL_SUPPORT_CHANGED | \ + PARAM_OBSS_MODE_CHANGED) + +/* dump command response Buffer size */ +#define DUMPCMD_RSP_BUFFER 100 + +/* version string max length (including NULL) */ +#define WCN36XX_HAL_VERSION_LENGTH 64 + +/* message types for messages exchanged between WDI and HAL */ +enum wcn36xx_hal_host_msg_type { + /* Init/De-Init */ + WCN36XX_HAL_START_REQ = 0, + WCN36XX_HAL_START_RSP = 1, + WCN36XX_HAL_STOP_REQ = 2, + WCN36XX_HAL_STOP_RSP = 3, + + /* Scan */ + WCN36XX_HAL_INIT_SCAN_REQ = 4, + WCN36XX_HAL_INIT_SCAN_RSP = 5, + WCN36XX_HAL_START_SCAN_REQ = 6, + WCN36XX_HAL_START_SCAN_RSP = 7, + WCN36XX_HAL_END_SCAN_REQ = 8, + WCN36XX_HAL_END_SCAN_RSP = 9, + WCN36XX_HAL_FINISH_SCAN_REQ = 10, + WCN36XX_HAL_FINISH_SCAN_RSP = 11, + + /* HW STA configuration/deconfiguration */ + WCN36XX_HAL_CONFIG_STA_REQ = 12, + WCN36XX_HAL_CONFIG_STA_RSP = 13, + WCN36XX_HAL_DELETE_STA_REQ = 14, + WCN36XX_HAL_DELETE_STA_RSP = 15, + WCN36XX_HAL_CONFIG_BSS_REQ = 16, + WCN36XX_HAL_CONFIG_BSS_RSP = 17, + WCN36XX_HAL_DELETE_BSS_REQ = 18, + WCN36XX_HAL_DELETE_BSS_RSP = 19, + + /* Infra STA asscoiation */ + WCN36XX_HAL_JOIN_REQ = 20, + WCN36XX_HAL_JOIN_RSP = 21, + WCN36XX_HAL_POST_ASSOC_REQ = 22, + WCN36XX_HAL_POST_ASSOC_RSP = 23, + + /* Security */ + WCN36XX_HAL_SET_BSSKEY_REQ = 24, + WCN36XX_HAL_SET_BSSKEY_RSP = 25, + WCN36XX_HAL_SET_STAKEY_REQ = 26, + WCN36XX_HAL_SET_STAKEY_RSP = 27, + WCN36XX_HAL_RMV_BSSKEY_REQ = 28, + WCN36XX_HAL_RMV_BSSKEY_RSP = 29, + WCN36XX_HAL_RMV_STAKEY_REQ = 30, + WCN36XX_HAL_RMV_STAKEY_RSP = 31, + + /* Qos Related */ + WCN36XX_HAL_ADD_TS_REQ = 32, + WCN36XX_HAL_ADD_TS_RSP = 33, + WCN36XX_HAL_DEL_TS_REQ = 34, + WCN36XX_HAL_DEL_TS_RSP = 35, + WCN36XX_HAL_UPD_EDCA_PARAMS_REQ = 36, + WCN36XX_HAL_UPD_EDCA_PARAMS_RSP = 37, + WCN36XX_HAL_ADD_BA_REQ = 38, + WCN36XX_HAL_ADD_BA_RSP = 39, + WCN36XX_HAL_DEL_BA_REQ = 40, + WCN36XX_HAL_DEL_BA_RSP = 41, + + WCN36XX_HAL_CH_SWITCH_REQ = 42, + WCN36XX_HAL_CH_SWITCH_RSP = 43, + WCN36XX_HAL_SET_LINK_ST_REQ = 44, + WCN36XX_HAL_SET_LINK_ST_RSP = 45, + WCN36XX_HAL_GET_STATS_REQ = 46, + WCN36XX_HAL_GET_STATS_RSP = 47, + WCN36XX_HAL_UPDATE_CFG_REQ = 48, + WCN36XX_HAL_UPDATE_CFG_RSP = 49, + + WCN36XX_HAL_MISSED_BEACON_IND = 50, + WCN36XX_HAL_UNKNOWN_ADDR2_FRAME_RX_IND = 51, + WCN36XX_HAL_MIC_FAILURE_IND = 52, + WCN36XX_HAL_FATAL_ERROR_IND = 53, + WCN36XX_HAL_SET_KEYDONE_MSG = 54, + + /* NV Interface */ + WCN36XX_HAL_DOWNLOAD_NV_REQ = 55, + WCN36XX_HAL_DOWNLOAD_NV_RSP = 56, + + WCN36XX_HAL_ADD_BA_SESSION_REQ = 57, + WCN36XX_HAL_ADD_BA_SESSION_RSP = 58, + WCN36XX_HAL_TRIGGER_BA_REQ = 59, + WCN36XX_HAL_TRIGGER_BA_RSP = 60, + WCN36XX_HAL_UPDATE_BEACON_REQ = 61, + WCN36XX_HAL_UPDATE_BEACON_RSP = 62, + WCN36XX_HAL_SEND_BEACON_REQ = 63, + WCN36XX_HAL_SEND_BEACON_RSP = 64, + + WCN36XX_HAL_SET_BCASTKEY_REQ = 65, + WCN36XX_HAL_SET_BCASTKEY_RSP = 66, + WCN36XX_HAL_DELETE_STA_CONTEXT_IND = 67, + WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_REQ = 68, + WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_RSP = 69, + + /* PTT interface support */ + WCN36XX_HAL_PROCESS_PTT_REQ = 70, + WCN36XX_HAL_PROCESS_PTT_RSP = 71, + + /* BTAMP related events */ + WCN36XX_HAL_SIGNAL_BTAMP_EVENT_REQ = 72, + WCN36XX_HAL_SIGNAL_BTAMP_EVENT_RSP = 73, + WCN36XX_HAL_TL_HAL_FLUSH_AC_REQ = 74, + WCN36XX_HAL_TL_HAL_FLUSH_AC_RSP = 75, + + WCN36XX_HAL_ENTER_IMPS_REQ = 76, + WCN36XX_HAL_EXIT_IMPS_REQ = 77, + WCN36XX_HAL_ENTER_BMPS_REQ = 78, + WCN36XX_HAL_EXIT_BMPS_REQ = 79, + WCN36XX_HAL_ENTER_UAPSD_REQ = 80, + WCN36XX_HAL_EXIT_UAPSD_REQ = 81, + WCN36XX_HAL_UPDATE_UAPSD_PARAM_REQ = 82, + WCN36XX_HAL_CONFIGURE_RXP_FILTER_REQ = 83, + WCN36XX_HAL_ADD_BCN_FILTER_REQ = 84, + WCN36XX_HAL_REM_BCN_FILTER_REQ = 85, + WCN36XX_HAL_ADD_WOWL_BCAST_PTRN = 86, + WCN36XX_HAL_DEL_WOWL_BCAST_PTRN = 87, + WCN36XX_HAL_ENTER_WOWL_REQ = 88, + WCN36XX_HAL_EXIT_WOWL_REQ = 89, + WCN36XX_HAL_HOST_OFFLOAD_REQ = 90, + WCN36XX_HAL_SET_RSSI_THRESH_REQ = 91, + WCN36XX_HAL_GET_RSSI_REQ = 92, + WCN36XX_HAL_SET_UAPSD_AC_PARAMS_REQ = 93, + WCN36XX_HAL_CONFIGURE_APPS_CPU_WAKEUP_STATE_REQ = 94, + + WCN36XX_HAL_ENTER_IMPS_RSP = 95, + WCN36XX_HAL_EXIT_IMPS_RSP = 96, + WCN36XX_HAL_ENTER_BMPS_RSP = 97, + WCN36XX_HAL_EXIT_BMPS_RSP = 98, + WCN36XX_HAL_ENTER_UAPSD_RSP = 99, + WCN36XX_HAL_EXIT_UAPSD_RSP = 100, + WCN36XX_HAL_SET_UAPSD_AC_PARAMS_RSP = 101, + WCN36XX_HAL_UPDATE_UAPSD_PARAM_RSP = 102, + WCN36XX_HAL_CONFIGURE_RXP_FILTER_RSP = 103, + WCN36XX_HAL_ADD_BCN_FILTER_RSP = 104, + WCN36XX_HAL_REM_BCN_FILTER_RSP = 105, + WCN36XX_HAL_SET_RSSI_THRESH_RSP = 106, + WCN36XX_HAL_HOST_OFFLOAD_RSP = 107, + WCN36XX_HAL_ADD_WOWL_BCAST_PTRN_RSP = 108, + WCN36XX_HAL_DEL_WOWL_BCAST_PTRN_RSP = 109, + WCN36XX_HAL_ENTER_WOWL_RSP = 110, + WCN36XX_HAL_EXIT_WOWL_RSP = 111, + WCN36XX_HAL_RSSI_NOTIFICATION_IND = 112, + WCN36XX_HAL_GET_RSSI_RSP = 113, + WCN36XX_HAL_CONFIGURE_APPS_CPU_WAKEUP_STATE_RSP = 114, + + /* 11k related events */ + WCN36XX_HAL_SET_MAX_TX_POWER_REQ = 115, + WCN36XX_HAL_SET_MAX_TX_POWER_RSP = 116, + + /* 11R related msgs */ + WCN36XX_HAL_AGGR_ADD_TS_REQ = 117, + WCN36XX_HAL_AGGR_ADD_TS_RSP = 118, + + /* P2P WLAN_FEATURE_P2P */ + WCN36XX_HAL_SET_P2P_GONOA_REQ = 119, + WCN36XX_HAL_SET_P2P_GONOA_RSP = 120, + + /* WLAN Dump commands */ + WCN36XX_HAL_DUMP_COMMAND_REQ = 121, + WCN36XX_HAL_DUMP_COMMAND_RSP = 122, + + /* OEM_DATA FEATURE SUPPORT */ + WCN36XX_HAL_START_OEM_DATA_REQ = 123, + WCN36XX_HAL_START_OEM_DATA_RSP = 124, + + /* ADD SELF STA REQ and RSP */ + WCN36XX_HAL_ADD_STA_SELF_REQ = 125, + WCN36XX_HAL_ADD_STA_SELF_RSP = 126, + + /* DEL SELF STA SUPPORT */ + WCN36XX_HAL_DEL_STA_SELF_REQ = 127, + WCN36XX_HAL_DEL_STA_SELF_RSP = 128, + + /* Coex Indication */ + WCN36XX_HAL_COEX_IND = 129, + + /* Tx Complete Indication */ + WCN36XX_HAL_OTA_TX_COMPL_IND = 130, + + /* Host Suspend/resume messages */ + WCN36XX_HAL_HOST_SUSPEND_IND = 131, + WCN36XX_HAL_HOST_RESUME_REQ = 132, + WCN36XX_HAL_HOST_RESUME_RSP = 133, + + WCN36XX_HAL_SET_TX_POWER_REQ = 134, + WCN36XX_HAL_SET_TX_POWER_RSP = 135, + WCN36XX_HAL_GET_TX_POWER_REQ = 136, + WCN36XX_HAL_GET_TX_POWER_RSP = 137, + + WCN36XX_HAL_P2P_NOA_ATTR_IND = 138, + + WCN36XX_HAL_ENABLE_RADAR_DETECT_REQ = 139, + WCN36XX_HAL_ENABLE_RADAR_DETECT_RSP = 140, + WCN36XX_HAL_GET_TPC_REPORT_REQ = 141, + WCN36XX_HAL_GET_TPC_REPORT_RSP = 142, + WCN36XX_HAL_RADAR_DETECT_IND = 143, + WCN36XX_HAL_RADAR_DETECT_INTR_IND = 144, + WCN36XX_HAL_KEEP_ALIVE_REQ = 145, + WCN36XX_HAL_KEEP_ALIVE_RSP = 146, + + /* PNO messages */ + WCN36XX_HAL_SET_PREF_NETWORK_REQ = 147, + WCN36XX_HAL_SET_PREF_NETWORK_RSP = 148, + WCN36XX_HAL_SET_RSSI_FILTER_REQ = 149, + WCN36XX_HAL_SET_RSSI_FILTER_RSP = 150, + WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ = 151, + WCN36XX_HAL_UPDATE_SCAN_PARAM_RSP = 152, + WCN36XX_HAL_PREF_NETW_FOUND_IND = 153, + + WCN36XX_HAL_SET_TX_PER_TRACKING_REQ = 154, + WCN36XX_HAL_SET_TX_PER_TRACKING_RSP = 155, + WCN36XX_HAL_TX_PER_HIT_IND = 156, + + WCN36XX_HAL_8023_MULTICAST_LIST_REQ = 157, + WCN36XX_HAL_8023_MULTICAST_LIST_RSP = 158, + + WCN36XX_HAL_SET_PACKET_FILTER_REQ = 159, + WCN36XX_HAL_SET_PACKET_FILTER_RSP = 160, + WCN36XX_HAL_PACKET_FILTER_MATCH_COUNT_REQ = 161, + WCN36XX_HAL_PACKET_FILTER_MATCH_COUNT_RSP = 162, + WCN36XX_HAL_CLEAR_PACKET_FILTER_REQ = 163, + WCN36XX_HAL_CLEAR_PACKET_FILTER_RSP = 164, + + /* + * This is temp fix. Should be removed once Host and Riva code is + * in sync. + */ + WCN36XX_HAL_INIT_SCAN_CON_REQ = 165, + + WCN36XX_HAL_SET_POWER_PARAMS_REQ = 166, + WCN36XX_HAL_SET_POWER_PARAMS_RSP = 167, + + WCN36XX_HAL_TSM_STATS_REQ = 168, + WCN36XX_HAL_TSM_STATS_RSP = 169, + + /* wake reason indication (WOW) */ + WCN36XX_HAL_WAKE_REASON_IND = 170, + + /* GTK offload support */ + WCN36XX_HAL_GTK_OFFLOAD_REQ = 171, + WCN36XX_HAL_GTK_OFFLOAD_RSP = 172, + WCN36XX_HAL_GTK_OFFLOAD_GETINFO_REQ = 173, + WCN36XX_HAL_GTK_OFFLOAD_GETINFO_RSP = 174, + + WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_REQ = 175, + WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP = 176, + WCN36XX_HAL_EXCLUDE_UNENCRYPTED_IND = 177, + + WCN36XX_HAL_SET_THERMAL_MITIGATION_REQ = 178, + WCN36XX_HAL_SET_THERMAL_MITIGATION_RSP = 179, + + WCN36XX_HAL_UPDATE_VHT_OP_MODE_REQ = 182, + WCN36XX_HAL_UPDATE_VHT_OP_MODE_RSP = 183, + + WCN36XX_HAL_P2P_NOA_START_IND = 184, + + WCN36XX_HAL_GET_ROAM_RSSI_REQ = 185, + WCN36XX_HAL_GET_ROAM_RSSI_RSP = 186, + + WCN36XX_HAL_CLASS_B_STATS_IND = 187, + WCN36XX_HAL_DEL_BA_IND = 188, + WCN36XX_HAL_DHCP_START_IND = 189, + WCN36XX_HAL_DHCP_STOP_IND = 190, + + WCN36XX_HAL_MSG_MAX = WCN36XX_HAL_MSG_TYPE_MAX_ENUM_SIZE +}; + +/* Enumeration for Version */ +enum wcn36xx_hal_host_msg_version { + WCN36XX_HAL_MSG_VERSION0 = 0, + WCN36XX_HAL_MSG_VERSION1 = 1, + /* define as 2 bytes data */ + WCN36XX_HAL_MSG_WCNSS_CTRL_VERSION = 0x7FFF, + WCN36XX_HAL_MSG_VERSION_MAX_FIELD = WCN36XX_HAL_MSG_WCNSS_CTRL_VERSION +}; + +enum driver_type { + DRIVER_TYPE_PRODUCTION = 0, + DRIVER_TYPE_MFG = 1, + DRIVER_TYPE_DVT = 2, + DRIVER_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_stop_type { + HAL_STOP_TYPE_SYS_RESET, + HAL_STOP_TYPE_SYS_DEEP_SLEEP, + HAL_STOP_TYPE_RF_KILL, + HAL_STOP_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_sys_mode { + HAL_SYS_MODE_NORMAL, + HAL_SYS_MODE_LEARN, + HAL_SYS_MODE_SCAN, + HAL_SYS_MODE_PROMISC, + HAL_SYS_MODE_SUSPEND_LINK, + HAL_SYS_MODE_ROAM_SCAN, + HAL_SYS_MODE_ROAM_SUSPEND_LINK, + HAL_SYS_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum phy_chan_bond_state { + /* 20MHz IF bandwidth centered on IF carrier */ + PHY_SINGLE_CHANNEL_CENTERED = 0, + + /* 40MHz IF bandwidth with lower 20MHz supporting the primary channel */ + PHY_DOUBLE_CHANNEL_LOW_PRIMARY = 1, + + /* 40MHz IF bandwidth centered on IF carrier */ + PHY_DOUBLE_CHANNEL_CENTERED = 2, + + /* 40MHz IF bandwidth with higher 20MHz supporting the primary ch */ + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY = 3, + + /* 20/40MHZ offset LOW 40/80MHZ offset CENTERED */ + PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED = 4, + + /* 20/40MHZ offset CENTERED 40/80MHZ offset CENTERED */ + PHY_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED = 5, + + /* 20/40MHZ offset HIGH 40/80MHZ offset CENTERED */ + PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED = 6, + + /* 20/40MHZ offset LOW 40/80MHZ offset LOW */ + PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW = 7, + + /* 20/40MHZ offset HIGH 40/80MHZ offset LOW */ + PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW = 8, + + /* 20/40MHZ offset LOW 40/80MHZ offset HIGH */ + PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH = 9, + + /* 20/40MHZ offset-HIGH 40/80MHZ offset HIGH */ + PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH = 10, + + PHY_CHANNEL_BONDING_STATE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* Spatial Multiplexing(SM) Power Save mode */ +enum wcn36xx_hal_ht_mimo_state { + /* Static SM Power Save mode */ + WCN36XX_HAL_HT_MIMO_PS_STATIC = 0, + + /* Dynamic SM Power Save mode */ + WCN36XX_HAL_HT_MIMO_PS_DYNAMIC = 1, + + /* reserved */ + WCN36XX_HAL_HT_MIMO_PS_NA = 2, + + /* SM Power Save disabled */ + WCN36XX_HAL_HT_MIMO_PS_NO_LIMIT = 3, + + WCN36XX_HAL_HT_MIMO_PS_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* each station added has a rate mode which specifies the sta attributes */ +enum sta_rate_mode { + STA_TAURUS = 0, + STA_TITAN, + STA_POLARIS, + STA_11b, + STA_11bg, + STA_11a, + STA_11n, + STA_11ac, + STA_INVALID_RATE_MODE = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* 1,2,5.5,11 */ +#define WCN36XX_HAL_NUM_DSSS_RATES 4 + +/* 6,9,12,18,24,36,48,54 */ +#define WCN36XX_HAL_NUM_OFDM_RATES 8 + +/* 72,96,108 */ +#define WCN36XX_HAL_NUM_POLARIS_RATES 3 + +#define WCN36XX_HAL_MAC_MAX_SUPPORTED_MCS_SET 16 + +enum wcn36xx_hal_bss_type { + WCN36XX_HAL_INFRASTRUCTURE_MODE, + + /* Added for softAP support */ + WCN36XX_HAL_INFRA_AP_MODE, + + WCN36XX_HAL_IBSS_MODE, + + /* Added for BT-AMP support */ + WCN36XX_HAL_BTAMP_STA_MODE, + + /* Added for BT-AMP support */ + WCN36XX_HAL_BTAMP_AP_MODE, + + WCN36XX_HAL_AUTO_MODE, + + WCN36XX_HAL_DONOT_USE_BSS_TYPE = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_nw_type { + WCN36XX_HAL_11A_NW_TYPE, + WCN36XX_HAL_11B_NW_TYPE, + WCN36XX_HAL_11G_NW_TYPE, + WCN36XX_HAL_11N_NW_TYPE, + WCN36XX_HAL_DONOT_USE_NW_TYPE = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +#define WCN36XX_HAL_MAC_RATESET_EID_MAX 12 + +enum wcn36xx_hal_ht_operating_mode { + /* No Protection */ + WCN36XX_HAL_HT_OP_MODE_PURE, + + /* Overlap Legacy device present, protection is optional */ + WCN36XX_HAL_HT_OP_MODE_OVERLAP_LEGACY, + + /* No legacy device, but 20 MHz HT present */ + WCN36XX_HAL_HT_OP_MODE_NO_LEGACY_20MHZ_HT, + + /* Protection is required */ + WCN36XX_HAL_HT_OP_MODE_MIXED, + + WCN36XX_HAL_HT_OP_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* Encryption type enum used with peer */ +enum ani_ed_type { + WCN36XX_HAL_ED_NONE, + WCN36XX_HAL_ED_WEP40, + WCN36XX_HAL_ED_WEP104, + WCN36XX_HAL_ED_TKIP, + WCN36XX_HAL_ED_CCMP, + WCN36XX_HAL_ED_WPI, + WCN36XX_HAL_ED_AES_128_CMAC, + WCN36XX_HAL_ED_NOT_IMPLEMENTED = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +#define WLAN_MAX_KEY_RSC_LEN 16 +#define WLAN_WAPI_KEY_RSC_LEN 16 + +/* MAX key length when ULA is used */ +#define WCN36XX_HAL_MAC_MAX_KEY_LENGTH 32 +#define WCN36XX_HAL_MAC_MAX_NUM_OF_DEFAULT_KEYS 4 + +/* + * Enum to specify whether key is used for TX only, RX only or both. + */ +enum ani_key_direction { + WCN36XX_HAL_TX_ONLY, + WCN36XX_HAL_RX_ONLY, + WCN36XX_HAL_TX_RX, + WCN36XX_HAL_TX_DEFAULT, + WCN36XX_HAL_DONOT_USE_KEY_DIRECTION = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum ani_wep_type { + WCN36XX_HAL_WEP_STATIC, + WCN36XX_HAL_WEP_DYNAMIC, + WCN36XX_HAL_WEP_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_link_state { + + WCN36XX_HAL_LINK_IDLE_STATE = 0, + WCN36XX_HAL_LINK_PREASSOC_STATE = 1, + WCN36XX_HAL_LINK_POSTASSOC_STATE = 2, + WCN36XX_HAL_LINK_AP_STATE = 3, + WCN36XX_HAL_LINK_IBSS_STATE = 4, + + /* BT-AMP Case */ + WCN36XX_HAL_LINK_BTAMP_PREASSOC_STATE = 5, + WCN36XX_HAL_LINK_BTAMP_POSTASSOC_STATE = 6, + WCN36XX_HAL_LINK_BTAMP_AP_STATE = 7, + WCN36XX_HAL_LINK_BTAMP_STA_STATE = 8, + + /* Reserved for HAL Internal Use */ + WCN36XX_HAL_LINK_LEARN_STATE = 9, + WCN36XX_HAL_LINK_SCAN_STATE = 10, + WCN36XX_HAL_LINK_FINISH_SCAN_STATE = 11, + WCN36XX_HAL_LINK_INIT_CAL_STATE = 12, + WCN36XX_HAL_LINK_FINISH_CAL_STATE = 13, + WCN36XX_HAL_LINK_LISTEN_STATE = 14, + + WCN36XX_HAL_LINK_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_stats_mask { + HAL_SUMMARY_STATS_INFO = 0x00000001, + HAL_GLOBAL_CLASS_A_STATS_INFO = 0x00000002, + HAL_GLOBAL_CLASS_B_STATS_INFO = 0x00000004, + HAL_GLOBAL_CLASS_C_STATS_INFO = 0x00000008, + HAL_GLOBAL_CLASS_D_STATS_INFO = 0x00000010, + HAL_PER_STA_STATS_INFO = 0x00000020 +}; + +/* BT-AMP events type */ +enum bt_amp_event_type { + BTAMP_EVENT_CONNECTION_START, + BTAMP_EVENT_CONNECTION_STOP, + BTAMP_EVENT_CONNECTION_TERMINATED, + + /* This and beyond are invalid values */ + BTAMP_EVENT_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE, +}; + +/* PE Statistics */ +enum pe_stats_mask { + PE_SUMMARY_STATS_INFO = 0x00000001, + PE_GLOBAL_CLASS_A_STATS_INFO = 0x00000002, + PE_GLOBAL_CLASS_B_STATS_INFO = 0x00000004, + PE_GLOBAL_CLASS_C_STATS_INFO = 0x00000008, + PE_GLOBAL_CLASS_D_STATS_INFO = 0x00000010, + PE_PER_STA_STATS_INFO = 0x00000020, + + /* This and beyond are invalid values */ + PE_STATS_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* + * Configuration Parameter IDs + */ +#define WCN36XX_HAL_CFG_STA_ID 0 +#define WCN36XX_HAL_CFG_CURRENT_TX_ANTENNA 1 +#define WCN36XX_HAL_CFG_CURRENT_RX_ANTENNA 2 +#define WCN36XX_HAL_CFG_LOW_GAIN_OVERRIDE 3 +#define WCN36XX_HAL_CFG_POWER_STATE_PER_CHAIN 4 +#define WCN36XX_HAL_CFG_CAL_PERIOD 5 +#define WCN36XX_HAL_CFG_CAL_CONTROL 6 +#define WCN36XX_HAL_CFG_PROXIMITY 7 +#define WCN36XX_HAL_CFG_NETWORK_DENSITY 8 +#define WCN36XX_HAL_CFG_MAX_MEDIUM_TIME 9 +#define WCN36XX_HAL_CFG_MAX_MPDUS_IN_AMPDU 10 +#define WCN36XX_HAL_CFG_RTS_THRESHOLD 11 +#define WCN36XX_HAL_CFG_SHORT_RETRY_LIMIT 12 +#define WCN36XX_HAL_CFG_LONG_RETRY_LIMIT 13 +#define WCN36XX_HAL_CFG_FRAGMENTATION_THRESHOLD 14 +#define WCN36XX_HAL_CFG_DYNAMIC_THRESHOLD_ZERO 15 +#define WCN36XX_HAL_CFG_DYNAMIC_THRESHOLD_ONE 16 +#define WCN36XX_HAL_CFG_DYNAMIC_THRESHOLD_TWO 17 +#define WCN36XX_HAL_CFG_FIXED_RATE 18 +#define WCN36XX_HAL_CFG_RETRYRATE_POLICY 19 +#define WCN36XX_HAL_CFG_RETRYRATE_SECONDARY 20 +#define WCN36XX_HAL_CFG_RETRYRATE_TERTIARY 21 +#define WCN36XX_HAL_CFG_FORCE_POLICY_PROTECTION 22 +#define WCN36XX_HAL_CFG_FIXED_RATE_MULTICAST_24GHZ 23 +#define WCN36XX_HAL_CFG_FIXED_RATE_MULTICAST_5GHZ 24 +#define WCN36XX_HAL_CFG_DEFAULT_RATE_INDEX_24GHZ 25 +#define WCN36XX_HAL_CFG_DEFAULT_RATE_INDEX_5GHZ 26 +#define WCN36XX_HAL_CFG_MAX_BA_SESSIONS 27 +#define WCN36XX_HAL_CFG_PS_DATA_INACTIVITY_TIMEOUT 28 +#define WCN36XX_HAL_CFG_PS_ENABLE_BCN_FILTER 29 +#define WCN36XX_HAL_CFG_PS_ENABLE_RSSI_MONITOR 30 +#define WCN36XX_HAL_CFG_NUM_BEACON_PER_RSSI_AVERAGE 31 +#define WCN36XX_HAL_CFG_STATS_PERIOD 32 +#define WCN36XX_HAL_CFG_CFP_MAX_DURATION 33 +#define WCN36XX_HAL_CFG_FRAME_TRANS_ENABLED 34 +#define WCN36XX_HAL_CFG_DTIM_PERIOD 35 +#define WCN36XX_HAL_CFG_EDCA_WMM_ACBK 36 +#define WCN36XX_HAL_CFG_EDCA_WMM_ACBE 37 +#define WCN36XX_HAL_CFG_EDCA_WMM_ACVO 38 +#define WCN36XX_HAL_CFG_EDCA_WMM_ACVI 39 +#define WCN36XX_HAL_CFG_BA_THRESHOLD_HIGH 40 +#define WCN36XX_HAL_CFG_MAX_BA_BUFFERS 41 +#define WCN36XX_HAL_CFG_RPE_POLLING_THRESHOLD 42 +#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC0_REG 43 +#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC1_REG 44 +#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC2_REG 45 +#define WCN36XX_HAL_CFG_RPE_AGING_THRESHOLD_FOR_AC3_REG 46 +#define WCN36XX_HAL_CFG_NO_OF_ONCHIP_REORDER_SESSIONS 47 +#define WCN36XX_HAL_CFG_PS_LISTEN_INTERVAL 48 +#define WCN36XX_HAL_CFG_PS_HEART_BEAT_THRESHOLD 49 +#define WCN36XX_HAL_CFG_PS_NTH_BEACON_FILTER 50 +#define WCN36XX_HAL_CFG_PS_MAX_PS_POLL 51 +#define WCN36XX_HAL_CFG_PS_MIN_RSSI_THRESHOLD 52 +#define WCN36XX_HAL_CFG_PS_RSSI_FILTER_PERIOD 53 +#define WCN36XX_HAL_CFG_PS_BROADCAST_FRAME_FILTER_ENABLE 54 +#define WCN36XX_HAL_CFG_PS_IGNORE_DTIM 55 +#define WCN36XX_HAL_CFG_PS_ENABLE_BCN_EARLY_TERM 56 +#define WCN36XX_HAL_CFG_DYNAMIC_PS_POLL_VALUE 57 +#define WCN36XX_HAL_CFG_PS_NULLDATA_AP_RESP_TIMEOUT 58 +#define WCN36XX_HAL_CFG_TELE_BCN_WAKEUP_EN 59 +#define WCN36XX_HAL_CFG_TELE_BCN_TRANS_LI 60 +#define WCN36XX_HAL_CFG_TELE_BCN_TRANS_LI_IDLE_BCNS 61 +#define WCN36XX_HAL_CFG_TELE_BCN_MAX_LI 62 +#define WCN36XX_HAL_CFG_TELE_BCN_MAX_LI_IDLE_BCNS 63 +#define WCN36XX_HAL_CFG_TX_PWR_CTRL_ENABLE 64 +#define WCN36XX_HAL_CFG_VALID_RADAR_CHANNEL_LIST 65 +#define WCN36XX_HAL_CFG_TX_POWER_24_20 66 +#define WCN36XX_HAL_CFG_TX_POWER_24_40 67 +#define WCN36XX_HAL_CFG_TX_POWER_50_20 68 +#define WCN36XX_HAL_CFG_TX_POWER_50_40 69 +#define WCN36XX_HAL_CFG_MCAST_BCAST_FILTER_SETTING 70 +#define WCN36XX_HAL_CFG_BCN_EARLY_TERM_WAKEUP_INTERVAL 71 +#define WCN36XX_HAL_CFG_MAX_TX_POWER_2_4 72 +#define WCN36XX_HAL_CFG_MAX_TX_POWER_5 73 +#define WCN36XX_HAL_CFG_INFRA_STA_KEEP_ALIVE_PERIOD 74 +#define WCN36XX_HAL_CFG_ENABLE_CLOSE_LOOP 75 +#define WCN36XX_HAL_CFG_BTC_EXECUTION_MODE 76 +#define WCN36XX_HAL_CFG_BTC_DHCP_BT_SLOTS_TO_BLOCK 77 +#define WCN36XX_HAL_CFG_BTC_A2DP_DHCP_BT_SUB_INTERVALS 78 +#define WCN36XX_HAL_CFG_PS_TX_INACTIVITY_TIMEOUT 79 +#define WCN36XX_HAL_CFG_WCNSS_API_VERSION 80 +#define WCN36XX_HAL_CFG_AP_KEEPALIVE_TIMEOUT 81 +#define WCN36XX_HAL_CFG_GO_KEEPALIVE_TIMEOUT 82 +#define WCN36XX_HAL_CFG_ENABLE_MC_ADDR_LIST 83 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_INQ_BT 84 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_PAGE_BT 85 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_CONN_BT 86 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_LE_BT 87 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_INQ_WLAN 88 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_PAGE_WLAN 89 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_CONN_WLAN 90 +#define WCN36XX_HAL_CFG_BTC_STATIC_LEN_LE_WLAN 91 +#define WCN36XX_HAL_CFG_BTC_DYN_MAX_LEN_BT 92 +#define WCN36XX_HAL_CFG_BTC_DYN_MAX_LEN_WLAN 93 +#define WCN36XX_HAL_CFG_BTC_MAX_SCO_BLOCK_PERC 94 +#define WCN36XX_HAL_CFG_BTC_DHCP_PROT_ON_A2DP 95 +#define WCN36XX_HAL_CFG_BTC_DHCP_PROT_ON_SCO 96 +#define WCN36XX_HAL_CFG_ENABLE_UNICAST_FILTER 97 +#define WCN36XX_HAL_CFG_MAX_ASSOC_LIMIT 98 +#define WCN36XX_HAL_CFG_ENABLE_LPWR_IMG_TRANSITION 99 +#define WCN36XX_HAL_CFG_ENABLE_MCC_ADAPTIVE_SCHEDULER 100 +#define WCN36XX_HAL_CFG_ENABLE_DETECT_PS_SUPPORT 101 +#define WCN36XX_HAL_CFG_AP_LINK_MONITOR_TIMEOUT 102 +#define WCN36XX_HAL_CFG_BTC_DWELL_TIME_MULTIPLIER 103 +#define WCN36XX_HAL_CFG_ENABLE_TDLS_OXYGEN_MODE 104 +#define WCN36XX_HAL_CFG_MAX_PARAMS 105 + +/* Message definitons - All the messages below need to be packed */ + +/* Definition for HAL API Version. */ +struct wcnss_wlan_version { + u8 revision; + u8 version; + u8 minor; + u8 major; +} __packed; + +/* Definition for Encryption Keys */ +struct wcn36xx_hal_keys { + u8 id; + + /* 0 for multicast */ + u8 unicast; + + enum ani_key_direction direction; + + /* Usage is unknown */ + u8 rsc[WLAN_MAX_KEY_RSC_LEN]; + + /* =1 for authenticator,=0 for supplicant */ + u8 pae_role; + + u16 length; + u8 key[WCN36XX_HAL_MAC_MAX_KEY_LENGTH]; +} __packed; + +/* + * set_sta_key_params Moving here since it is shared by + * configbss/setstakey msgs + */ +struct wcn36xx_hal_set_sta_key_params { + /* STA Index */ + u16 sta_index; + + /* Encryption Type used with peer */ + enum ani_ed_type enc_type; + + /* STATIC/DYNAMIC - valid only for WEP */ + enum ani_wep_type wep_type; + + /* Default WEP key, valid only for static WEP, must between 0 and 3. */ + u8 def_wep_idx; + + /* valid only for non-static WEP encyrptions */ + struct wcn36xx_hal_keys key[WCN36XX_HAL_MAC_MAX_NUM_OF_DEFAULT_KEYS]; + + /* + * Control for Replay Count, 1= Single TID based replay count on Tx + * 0 = Per TID based replay count on TX + */ + u8 single_tid_rc; + +} __packed; + +/* 4-byte control message header used by HAL*/ +struct wcn36xx_hal_msg_header { + enum wcn36xx_hal_host_msg_type msg_type:16; + enum wcn36xx_hal_host_msg_version msg_version:16; + u32 len; +} __packed; + +/* Config format required by HAL for each CFG item*/ +struct wcn36xx_hal_cfg { + /* Cfg Id. The Id required by HAL is exported by HAL + * in shared header file between UMAC and HAL.*/ + u16 id; + + /* Length of the Cfg. This parameter is used to go to next cfg + * in the TLV format.*/ + u16 len; + + /* Padding bytes for unaligned address's */ + u16 pad_bytes; + + /* Reserve bytes for making cfgVal to align address */ + u16 reserve; + + /* Following the uCfgLen field there should be a 'uCfgLen' bytes + * containing the uCfgValue ; u8 uCfgValue[uCfgLen] */ +} __packed; + +struct wcn36xx_hal_mac_start_parameters { + /* Drive Type - Production or FTM etc */ + enum driver_type type; + + /* Length of the config buffer */ + u32 len; + + /* Following this there is a TLV formatted buffer of length + * "len" bytes containing all config values. + * The TLV is expected to be formatted like this: + * 0 15 31 31+CFG_LEN-1 length-1 + * | CFG_ID | CFG_LEN | CFG_BODY | CFG_ID |......| + */ +} __packed; + +struct wcn36xx_hal_mac_start_req_msg { + /* config buffer must start in TLV format just here */ + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_mac_start_parameters params; +} __packed; + +struct wcn36xx_hal_mac_start_rsp_params { + /* success or failure */ + u16 status; + + /* Max number of STA supported by the device */ + u8 stations; + + /* Max number of BSS supported by the device */ + u8 bssids; + + /* API Version */ + struct wcnss_wlan_version version; + + /* CRM build information */ + u8 crm_version[WCN36XX_HAL_VERSION_LENGTH]; + + /* hardware/chipset/misc version information */ + u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH]; + +} __packed; + +struct wcn36xx_hal_mac_start_rsp_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_mac_start_rsp_params start_rsp_params; +} __packed; + +struct wcn36xx_hal_mac_stop_req_params { + /* The reason for which the device is being stopped */ + enum wcn36xx_hal_stop_type reason; + +} __packed; + +struct wcn36xx_hal_mac_stop_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_mac_stop_req_params stop_req_params; +} __packed; + +struct wcn36xx_hal_mac_stop_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +struct wcn36xx_hal_update_cfg_req_msg { + /* + * Note: The length specified in tHalUpdateCfgReqMsg messages should be + * header.msgLen = sizeof(tHalUpdateCfgReqMsg) + uConfigBufferLen + */ + struct wcn36xx_hal_msg_header header; + + /* Length of the config buffer. Allows UMAC to update multiple CFGs */ + u32 len; + + /* + * Following this there is a TLV formatted buffer of length + * "uConfigBufferLen" bytes containing all config values. + * The TLV is expected to be formatted like this: + * 0 15 31 31+CFG_LEN-1 length-1 + * | CFG_ID | CFG_LEN | CFG_BODY | CFG_ID |......| + */ + +} __packed; + +struct wcn36xx_hal_update_cfg_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + +} __packed; + +/* Frame control field format (2 bytes) */ +struct wcn36xx_hal_mac_frame_ctl { + +#ifndef ANI_LITTLE_BIT_ENDIAN + + u8 subType:4; + u8 type:2; + u8 protVer:2; + + u8 order:1; + u8 wep:1; + u8 moreData:1; + u8 powerMgmt:1; + u8 retry:1; + u8 moreFrag:1; + u8 fromDS:1; + u8 toDS:1; + +#else + + u8 protVer:2; + u8 type:2; + u8 subType:4; + + u8 toDS:1; + u8 fromDS:1; + u8 moreFrag:1; + u8 retry:1; + u8 powerMgmt:1; + u8 moreData:1; + u8 wep:1; + u8 order:1; + +#endif + +}; + +/* Sequence control field */ +struct wcn36xx_hal_mac_seq_ctl { + u8 fragNum:4; + u8 seqNumLo:4; + u8 seqNumHi:8; +}; + +/* Management header format */ +struct wcn36xx_hal_mac_mgmt_hdr { + struct wcn36xx_hal_mac_frame_ctl fc; + u8 durationLo; + u8 durationHi; + u8 da[6]; + u8 sa[6]; + u8 bssId[6]; + struct wcn36xx_hal_mac_seq_ctl seqControl; +}; + +/* FIXME: pronto v1 apparently has 4 */ +#define WCN36XX_HAL_NUM_BSSID 2 + +/* Scan Entry to hold active BSS idx's */ +struct wcn36xx_hal_scan_entry { + u8 bss_index[WCN36XX_HAL_NUM_BSSID]; + u8 active_bss_count; +}; + +struct wcn36xx_hal_init_scan_req_msg { + struct wcn36xx_hal_msg_header header; + + /* LEARN - AP Role + SCAN - STA Role */ + enum wcn36xx_hal_sys_mode mode; + + /* BSSID of the BSS */ + u8 bssid[ETH_ALEN]; + + /* Whether BSS needs to be notified */ + u8 notify; + + /* Kind of frame to be used for notifying the BSS (Data Null, QoS + * Null, or CTS to Self). Must always be a valid frame type. */ + u8 frame_type; + + /* UMAC has the option of passing the MAC frame to be used for + * notifying the BSS. If non-zero, HAL will use the MAC frame + * buffer pointed to by macMgmtHdr. If zero, HAL will generate the + * appropriate MAC frame based on frameType. */ + u8 frame_len; + + /* Following the framelength there is a MAC frame buffer if + * frameLength is non-zero. */ + struct wcn36xx_hal_mac_mgmt_hdr mac_mgmt_hdr; + + /* Entry to hold number of active BSS idx's */ + struct wcn36xx_hal_scan_entry scan_entry; +}; + +struct wcn36xx_hal_init_scan_con_req_msg { + struct wcn36xx_hal_msg_header header; + + /* LEARN - AP Role + SCAN - STA Role */ + enum wcn36xx_hal_sys_mode mode; + + /* BSSID of the BSS */ + u8 bssid[ETH_ALEN]; + + /* Whether BSS needs to be notified */ + u8 notify; + + /* Kind of frame to be used for notifying the BSS (Data Null, QoS + * Null, or CTS to Self). Must always be a valid frame type. */ + u8 frame_type; + + /* UMAC has the option of passing the MAC frame to be used for + * notifying the BSS. If non-zero, HAL will use the MAC frame + * buffer pointed to by macMgmtHdr. If zero, HAL will generate the + * appropriate MAC frame based on frameType. */ + u8 frame_length; + + /* Following the framelength there is a MAC frame buffer if + * frameLength is non-zero. */ + struct wcn36xx_hal_mac_mgmt_hdr mac_mgmt_hdr; + + /* Entry to hold number of active BSS idx's */ + struct wcn36xx_hal_scan_entry scan_entry; + + /* Single NoA usage in Scanning */ + u8 use_noa; + + /* Indicates the scan duration (in ms) */ + u16 scan_duration; + +}; + +struct wcn36xx_hal_init_scan_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + +} __packed; + +struct wcn36xx_hal_start_scan_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Indicates the channel to scan */ + u8 scan_channel; +} __packed; + +struct wcn36xx_hal_start_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u32 start_tsf[2]; + u8 tx_mgmt_power; + +} __packed; + +struct wcn36xx_hal_end_scan_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Indicates the channel to stop scanning. Not used really. But + * retained for symmetry with "start Scan" message. It can also + * help in error check if needed. */ + u8 scan_channel; +} __packed; + +struct wcn36xx_hal_end_scan_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +struct wcn36xx_hal_finish_scan_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Identifies the operational state of the AP/STA + * LEARN - AP Role SCAN - STA Role */ + enum wcn36xx_hal_sys_mode mode; + + /* Operating channel to tune to. */ + u8 oper_channel; + + /* Channel Bonding state If 20/40 MHz is operational, this will + * indicate the 40 MHz extension channel in combination with the + * control channel */ + enum phy_chan_bond_state cb_state; + + /* BSSID of the BSS */ + u8 bssid[ETH_ALEN]; + + /* Whether BSS needs to be notified */ + u8 notify; + + /* Kind of frame to be used for notifying the BSS (Data Null, QoS + * Null, or CTS to Self). Must always be a valid frame type. */ + u8 frame_type; + + /* UMAC has the option of passing the MAC frame to be used for + * notifying the BSS. If non-zero, HAL will use the MAC frame + * buffer pointed to by macMgmtHdr. If zero, HAL will generate the + * appropriate MAC frame based on frameType. */ + u8 frame_length; + + /* Following the framelength there is a MAC frame buffer if + * frameLength is non-zero. */ + struct wcn36xx_hal_mac_mgmt_hdr mac_mgmt_hdr; + + /* Entry to hold number of active BSS idx's */ + struct wcn36xx_hal_scan_entry scan_entry; + +} __packed; + +struct wcn36xx_hal_finish_scan_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + +} __packed; + +enum wcn36xx_hal_rate_index { + HW_RATE_INDEX_1MBPS = 0x82, + HW_RATE_INDEX_2MBPS = 0x84, + HW_RATE_INDEX_5_5MBPS = 0x8B, + HW_RATE_INDEX_6MBPS = 0x0C, + HW_RATE_INDEX_9MBPS = 0x12, + HW_RATE_INDEX_11MBPS = 0x96, + HW_RATE_INDEX_12MBPS = 0x18, + HW_RATE_INDEX_18MBPS = 0x24, + HW_RATE_INDEX_24MBPS = 0x30, + HW_RATE_INDEX_36MBPS = 0x48, + HW_RATE_INDEX_48MBPS = 0x60, + HW_RATE_INDEX_54MBPS = 0x6C +}; + +struct wcn36xx_hal_supported_rates { + /* + * For Self STA Entry: this represents Self Mode. + * For Peer Stations, this represents the mode of the peer. + * On Station: + * + * --this mode is updated when PE adds the Self Entry. + * + * -- OR when PE sends 'ADD_BSS' message and station context in BSS + * is used to indicate the mode of the AP. + * + * ON AP: + * + * -- this mode is updated when PE sends 'ADD_BSS' and Sta entry + * for that BSS is used to indicate the self mode of the AP. + * + * -- OR when a station is associated, PE sends 'ADD_STA' message + * with this mode updated. + */ + + enum sta_rate_mode op_rate_mode; + + /* 11b, 11a and aniLegacyRates are IE rates which gives rate in + * unit of 500Kbps */ + u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES]; + u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES]; + u16 legacy_rates[WCN36XX_HAL_NUM_POLARIS_RATES]; + u16 reserved; + + /* Taurus only supports 26 Titan Rates(no ESF/concat Rates will be + * supported) First 26 bits are reserved for those Titan rates and + * the last 4 bits(bit28-31) for Taurus, 2(bit26-27) bits are + * reserved. */ + /* Titan and Taurus Rates */ + u32 enhanced_rate_bitmap; + + /* + * 0-76 bits used, remaining reserved + * bits 0-15 and 32 should be set. + */ + u8 supported_mcs_set[WCN36XX_HAL_MAC_MAX_SUPPORTED_MCS_SET]; + + /* + * RX Highest Supported Data Rate defines the highest data + * rate that the STA is able to receive, in unites of 1Mbps. + * This value is derived from "Supported MCS Set field" inside + * the HT capability element. + */ + u16 rx_highest_data_rate; + +} __packed; + +struct wcn36xx_hal_config_sta_params { + /* BSSID of STA */ + u8 bssid[ETH_ALEN]; + + /* ASSOC ID, as assigned by UMAC */ + u16 aid; + + /* STA entry Type: 0 - Self, 1 - Other/Peer, 2 - BSSID, 3 - BCAST */ + u8 type; + + /* Short Preamble Supported. */ + u8 short_preamble_supported; + + /* MAC Address of STA */ + u8 mac[ETH_ALEN]; + + /* Listen interval of the STA */ + u16 listen_interval; + + /* Support for 11e/WMM */ + u8 wmm_enabled; + + /* 11n HT capable STA */ + u8 ht_capable; + + /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */ + u8 tx_channel_width_set; + + /* RIFS mode 0 - NA, 1 - Allowed */ + u8 rifs_mode; + + /* L-SIG TXOP Protection mechanism + 0 - No Support, 1 - Supported + SG - there is global field */ + u8 lsig_txop_protection; + + /* Max Ampdu Size supported by STA. TPE programming. + 0 : 8k , 1 : 16k, 2 : 32k, 3 : 64k */ + u8 max_ampdu_size; + + /* Max Ampdu density. Used by RA. 3 : 0~7 : 2^(11nAMPDUdensity -4) */ + u8 max_ampdu_density; + + /* Max AMSDU size 1 : 3839 bytes, 0 : 7935 bytes */ + u8 max_amsdu_size; + + /* Short GI support for 40Mhz packets */ + u8 sgi_40mhz; + + /* Short GI support for 20Mhz packets */ + u8 sgi_20Mhz; + + /* TODO move this parameter to the end for 3680 */ + /* These rates are the intersection of peer and self capabilities. */ + struct wcn36xx_hal_supported_rates supported_rates; + + /* Robust Management Frame (RMF) enabled/disabled */ + u8 rmf; + + /* The unicast encryption type in the association */ + u32 encrypt_type; + + /* HAL should update the existing STA entry, if this flag is set. UMAC + will set this flag in case of RE-ASSOC, where we want to reuse the + old STA ID. 0 = Add, 1 = Update */ + u8 action; + + /* U-APSD Flags: 1b per AC. Encoded as follows: + b7 b6 b5 b4 b3 b2 b1 b0 = + X X X X BE BK VI VO */ + u8 uapsd; + + /* Max SP Length */ + u8 max_sp_len; + + /* 11n Green Field preamble support + 0 - Not supported, 1 - Supported */ + u8 green_field_capable; + + /* MIMO Power Save mode */ + enum wcn36xx_hal_ht_mimo_state mimo_ps; + + /* Delayed BA Support */ + u8 delayed_ba_support; + + /* Max AMPDU duration in 32us */ + u8 max_ampdu_duration; + + /* HT STA should set it to 1 if it is enabled in BSS. HT STA should + * set it to 0 if AP does not support it. This indication is sent + * to HAL and HAL uses this flag to pickup up appropriate 40Mhz + * rates. */ + u8 dsss_cck_mode_40mhz; + + /* Valid STA Idx when action=Update. Set to 0xFF when invalid! + * Retained for backward compalibity with existing HAL code */ + u8 sta_index; + + /* BSSID of BSS to which station is associated. Set to 0xFF when + * invalid. Retained for backward compalibity with existing HAL + * code */ + u8 bssid_index; + + u8 p2p; + + /* TODO add this parameter for 3680. */ + /* Reserved to align next field on a dword boundary */ + /* u8 reserved; */ +} __packed; + +struct wcn36xx_hal_config_sta_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_sta_params sta_params; +} __packed; + +struct wcn36xx_hal_config_sta_params_v1 { + /* BSSID of STA */ + u8 bssid[ETH_ALEN]; + + /* ASSOC ID, as assigned by UMAC */ + u16 aid; + + /* STA entry Type: 0 - Self, 1 - Other/Peer, 2 - BSSID, 3 - BCAST */ + u8 type; + + /* Short Preamble Supported. */ + u8 short_preamble_supported; + + /* MAC Address of STA */ + u8 mac[ETH_ALEN]; + + /* Listen interval of the STA */ + u16 listen_interval; + + /* Support for 11e/WMM */ + u8 wmm_enabled; + + /* 11n HT capable STA */ + u8 ht_capable; + + /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */ + u8 tx_channel_width_set; + + /* RIFS mode 0 - NA, 1 - Allowed */ + u8 rifs_mode; + + /* L-SIG TXOP Protection mechanism + 0 - No Support, 1 - Supported + SG - there is global field */ + u8 lsig_txop_protection; + + /* Max Ampdu Size supported by STA. TPE programming. + 0 : 8k , 1 : 16k, 2 : 32k, 3 : 64k */ + u8 max_ampdu_size; + + /* Max Ampdu density. Used by RA. 3 : 0~7 : 2^(11nAMPDUdensity -4) */ + u8 max_ampdu_density; + + /* Max AMSDU size 1 : 3839 bytes, 0 : 7935 bytes */ + u8 max_amsdu_size; + + /* Short GI support for 40Mhz packets */ + u8 sgi_40mhz; + + /* Short GI support for 20Mhz packets */ + u8 sgi_20Mhz; + + /* Robust Management Frame (RMF) enabled/disabled */ + u8 rmf; + + /* The unicast encryption type in the association */ + u32 encrypt_type; + + /* HAL should update the existing STA entry, if this flag is set. UMAC + will set this flag in case of RE-ASSOC, where we want to reuse the + old STA ID. 0 = Add, 1 = Update */ + u8 action; + + /* U-APSD Flags: 1b per AC. Encoded as follows: + b7 b6 b5 b4 b3 b2 b1 b0 = + X X X X BE BK VI VO */ + u8 uapsd; + + /* Max SP Length */ + u8 max_sp_len; + + /* 11n Green Field preamble support + 0 - Not supported, 1 - Supported */ + u8 green_field_capable; + + /* MIMO Power Save mode */ + enum wcn36xx_hal_ht_mimo_state mimo_ps; + + /* Delayed BA Support */ + u8 delayed_ba_support; + + /* Max AMPDU duration in 32us */ + u8 max_ampdu_duration; + + /* HT STA should set it to 1 if it is enabled in BSS. HT STA should + * set it to 0 if AP does not support it. This indication is sent + * to HAL and HAL uses this flag to pickup up appropriate 40Mhz + * rates. */ + u8 dsss_cck_mode_40mhz; + + /* Valid STA Idx when action=Update. Set to 0xFF when invalid! + * Retained for backward compalibity with existing HAL code */ + u8 sta_index; + + /* BSSID of BSS to which station is associated. Set to 0xFF when + * invalid. Retained for backward compalibity with existing HAL + * code */ + u8 bssid_index; + + u8 p2p; + + /* Reserved to align next field on a dword boundary */ + u8 reserved; + + /* These rates are the intersection of peer and self capabilities. */ + struct wcn36xx_hal_supported_rates supported_rates; +} __packed; + +struct wcn36xx_hal_config_sta_req_msg_v1 { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_sta_params_v1 sta_params; +} __packed; + +struct config_sta_rsp_params { + /* success or failure */ + u32 status; + + /* Station index; valid only when 'status' field value SUCCESS */ + u8 sta_index; + + /* BSSID Index of BSS to which the station is associated */ + u8 bssid_index; + + /* DPU Index for PTK */ + u8 dpu_index; + + /* DPU Index for GTK */ + u8 bcast_dpu_index; + + /* DPU Index for IGTK */ + u8 bcast_mgmt_dpu_idx; + + /* PTK DPU signature */ + u8 uc_ucast_sig; + + /* GTK DPU isignature */ + u8 uc_bcast_sig; + + /* IGTK DPU signature */ + u8 uc_mgmt_sig; + + u8 p2p; + +} __packed; + +struct wcn36xx_hal_config_sta_rsp_msg { + struct wcn36xx_hal_msg_header header; + + struct config_sta_rsp_params params; +} __packed; + +/* Delete STA Request message */ +struct wcn36xx_hal_delete_sta_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Index of STA to delete */ + u8 sta_index; + +} __packed; + +/* Delete STA Response message */ +struct wcn36xx_hal_delete_sta_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Index of STA deleted */ + u8 sta_id; +} __packed; + +/* 12 Bytes long because this structure can be used to represent rate and + * extended rate set IEs. The parser assume this to be at least 12 */ +struct wcn36xx_hal_rate_set { + u8 num_rates; + u8 rate[WCN36XX_HAL_MAC_RATESET_EID_MAX]; +} __packed; + +/* access category record */ +struct wcn36xx_hal_aci_aifsn { +#ifndef ANI_LITTLE_BIT_ENDIAN + u8 rsvd:1; + u8 aci:2; + u8 acm:1; + u8 aifsn:4; +#else + u8 aifsn:4; + u8 acm:1; + u8 aci:2; + u8 rsvd:1; +#endif +} __packed; + +/* contention window size */ +struct wcn36xx_hal_mac_cw { +#ifndef ANI_LITTLE_BIT_ENDIAN + u8 max:4; + u8 min:4; +#else + u8 min:4; + u8 max:4; +#endif +} __packed; + +struct wcn36xx_hal_edca_param_record { + struct wcn36xx_hal_aci_aifsn aci; + struct wcn36xx_hal_mac_cw cw; + u16 txop_limit; +} __packed; + +struct wcn36xx_hal_mac_ssid { + u8 length; + u8 ssid[32]; +} __packed; + +/* Concurrency role. These are generic IDs that identify the various roles + * in the software system. */ +enum wcn36xx_hal_con_mode { + WCN36XX_HAL_STA_MODE = 0, + + /* to support softAp mode . This is misleading. + It means AP MODE only. */ + WCN36XX_HAL_STA_SAP_MODE = 1, + + WCN36XX_HAL_P2P_CLIENT_MODE, + WCN36XX_HAL_P2P_GO_MODE, + WCN36XX_HAL_MONITOR_MODE, +}; + +/* This is a bit pattern to be set for each mode + * bit 0 - sta mode + * bit 1 - ap mode + * bit 2 - p2p client mode + * bit 3 - p2p go mode */ +enum wcn36xx_hal_concurrency_mode { + HAL_STA = 1, + HAL_SAP = 2, + + /* to support sta, softAp mode . This means STA+AP mode */ + HAL_STA_SAP = 3, + + HAL_P2P_CLIENT = 4, + HAL_P2P_GO = 8, + HAL_MAX_CONCURRENCY_PERSONA = 4 +}; + +struct wcn36xx_hal_config_bss_params { + /* BSSID */ + u8 bssid[ETH_ALEN]; + + /* Self Mac Address */ + u8 self_mac_addr[ETH_ALEN]; + + /* BSS type */ + enum wcn36xx_hal_bss_type bss_type; + + /* Operational Mode: AP =0, STA = 1 */ + u8 oper_mode; + + /* Network Type */ + enum wcn36xx_hal_nw_type nw_type; + + /* Used to classify PURE_11G/11G_MIXED to program MTU */ + u8 short_slot_time_supported; + + /* Co-exist with 11a STA */ + u8 lla_coexist; + + /* Co-exist with 11b STA */ + u8 llb_coexist; + + /* Co-exist with 11g STA */ + u8 llg_coexist; + + /* Coexistence with 11n STA */ + u8 ht20_coexist; + + /* Non GF coexist flag */ + u8 lln_non_gf_coexist; + + /* TXOP protection support */ + u8 lsig_tx_op_protection_full_support; + + /* RIFS mode */ + u8 rifs_mode; + + /* Beacon Interval in TU */ + u16 beacon_interval; + + /* DTIM period */ + u8 dtim_period; + + /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */ + u8 tx_channel_width_set; + + /* Operating channel */ + u8 oper_channel; + + /* Extension channel for channel bonding */ + u8 ext_channel; + + /* Reserved to align next field on a dword boundary */ + u8 reserved; + + /* TODO move sta to the end for 3680 */ + /* Context of the station being added in HW + * Add a STA entry for "itself" - + * + * On AP - Add the AP itself in an "STA context" + * + * On STA - Add the AP to which this STA is joining in an + * "STA context" + */ + struct wcn36xx_hal_config_sta_params sta; + /* SSID of the BSS */ + struct wcn36xx_hal_mac_ssid ssid; + + /* HAL should update the existing BSS entry, if this flag is set. + * UMAC will set this flag in case of reassoc, where we want to + * resue the the old BSSID and still return success 0 = Add, 1 = + * Update */ + u8 action; + + /* MAC Rate Set */ + struct wcn36xx_hal_rate_set rateset; + + /* Enable/Disable HT capabilities of the BSS */ + u8 ht; + + /* Enable/Disable OBSS protection */ + u8 obss_prot_enabled; + + /* RMF enabled/disabled */ + u8 rmf; + + /* HT Operating Mode operating mode of the 802.11n STA */ + enum wcn36xx_hal_ht_operating_mode ht_oper_mode; + + /* Dual CTS Protection: 0 - Unused, 1 - Used */ + u8 dual_cts_protection; + + /* Probe Response Max retries */ + u8 max_probe_resp_retry_limit; + + /* To Enable Hidden ssid */ + u8 hidden_ssid; + + /* To Enable Disable FW Proxy Probe Resp */ + u8 proxy_probe_resp; + + /* Boolean to indicate if EDCA params are valid. UMAC might not + * have valid EDCA params or might not desire to apply EDCA params + * during config BSS. 0 implies Not Valid ; Non-Zero implies + * valid */ + u8 edca_params_valid; + + /* EDCA Parameters for Best Effort Access Category */ + struct wcn36xx_hal_edca_param_record acbe; + + /* EDCA Parameters forBackground Access Category */ + struct wcn36xx_hal_edca_param_record acbk; + + /* EDCA Parameters for Video Access Category */ + struct wcn36xx_hal_edca_param_record acvi; + + /* EDCA Parameters for Voice Access Category */ + struct wcn36xx_hal_edca_param_record acvo; + + /* Ext Bss Config Msg if set */ + u8 ext_set_sta_key_param_valid; + + /* SetStaKeyParams for ext bss msg */ + struct wcn36xx_hal_set_sta_key_params ext_set_sta_key_param; + + /* Persona for the BSS can be STA,AP,GO,CLIENT value same as enum + * wcn36xx_hal_con_mode */ + u8 wcn36xx_hal_persona; + + u8 spectrum_mgt_enable; + + /* HAL fills in the tx power used for mgmt frames in txMgmtPower */ + s8 tx_mgmt_power; + + /* maxTxPower has max power to be used after applying the power + * constraint if any */ + s8 max_tx_power; +} __packed; + +struct wcn36xx_hal_config_bss_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_bss_params bss_params; +} __packed; + +struct wcn36xx_hal_config_bss_params_v1 { + /* BSSID */ + u8 bssid[ETH_ALEN]; + + /* Self Mac Address */ + u8 self_mac_addr[ETH_ALEN]; + + /* BSS type */ + enum wcn36xx_hal_bss_type bss_type; + + /* Operational Mode: AP =0, STA = 1 */ + u8 oper_mode; + + /* Network Type */ + enum wcn36xx_hal_nw_type nw_type; + + /* Used to classify PURE_11G/11G_MIXED to program MTU */ + u8 short_slot_time_supported; + + /* Co-exist with 11a STA */ + u8 lla_coexist; + + /* Co-exist with 11b STA */ + u8 llb_coexist; + + /* Co-exist with 11g STA */ + u8 llg_coexist; + + /* Coexistence with 11n STA */ + u8 ht20_coexist; + + /* Non GF coexist flag */ + u8 lln_non_gf_coexist; + + /* TXOP protection support */ + u8 lsig_tx_op_protection_full_support; + + /* RIFS mode */ + u8 rifs_mode; + + /* Beacon Interval in TU */ + u16 beacon_interval; + + /* DTIM period */ + u8 dtim_period; + + /* TX Width Set: 0 - 20 MHz only, 1 - 20/40 MHz */ + u8 tx_channel_width_set; + + /* Operating channel */ + u8 oper_channel; + + /* Extension channel for channel bonding */ + u8 ext_channel; + + /* Reserved to align next field on a dword boundary */ + u8 reserved; + + /* SSID of the BSS */ + struct wcn36xx_hal_mac_ssid ssid; + + /* HAL should update the existing BSS entry, if this flag is set. + * UMAC will set this flag in case of reassoc, where we want to + * resue the the old BSSID and still return success 0 = Add, 1 = + * Update */ + u8 action; + + /* MAC Rate Set */ + struct wcn36xx_hal_rate_set rateset; + + /* Enable/Disable HT capabilities of the BSS */ + u8 ht; + + /* Enable/Disable OBSS protection */ + u8 obss_prot_enabled; + + /* RMF enabled/disabled */ + u8 rmf; + + /* HT Operating Mode operating mode of the 802.11n STA */ + enum wcn36xx_hal_ht_operating_mode ht_oper_mode; + + /* Dual CTS Protection: 0 - Unused, 1 - Used */ + u8 dual_cts_protection; + + /* Probe Response Max retries */ + u8 max_probe_resp_retry_limit; + + /* To Enable Hidden ssid */ + u8 hidden_ssid; + + /* To Enable Disable FW Proxy Probe Resp */ + u8 proxy_probe_resp; + + /* Boolean to indicate if EDCA params are valid. UMAC might not + * have valid EDCA params or might not desire to apply EDCA params + * during config BSS. 0 implies Not Valid ; Non-Zero implies + * valid */ + u8 edca_params_valid; + + /* EDCA Parameters for Best Effort Access Category */ + struct wcn36xx_hal_edca_param_record acbe; + + /* EDCA Parameters forBackground Access Category */ + struct wcn36xx_hal_edca_param_record acbk; + + /* EDCA Parameters for Video Access Category */ + struct wcn36xx_hal_edca_param_record acvi; + + /* EDCA Parameters for Voice Access Category */ + struct wcn36xx_hal_edca_param_record acvo; + + /* Ext Bss Config Msg if set */ + u8 ext_set_sta_key_param_valid; + + /* SetStaKeyParams for ext bss msg */ + struct wcn36xx_hal_set_sta_key_params ext_set_sta_key_param; + + /* Persona for the BSS can be STA,AP,GO,CLIENT value same as enum + * wcn36xx_hal_con_mode */ + u8 wcn36xx_hal_persona; + + u8 spectrum_mgt_enable; + + /* HAL fills in the tx power used for mgmt frames in txMgmtPower */ + s8 tx_mgmt_power; + + /* maxTxPower has max power to be used after applying the power + * constraint if any */ + s8 max_tx_power; + + /* Context of the station being added in HW + * Add a STA entry for "itself" - + * + * On AP - Add the AP itself in an "STA context" + * + * On STA - Add the AP to which this STA is joining in an + * "STA context" + */ + struct wcn36xx_hal_config_sta_params_v1 sta; +} __packed; + +struct wcn36xx_hal_config_bss_req_msg_v1 { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_bss_params_v1 bss_params; +} __packed; + +struct wcn36xx_hal_config_bss_rsp_params { + /* Success or Failure */ + u32 status; + + /* BSS index allocated by HAL */ + u8 bss_index; + + /* DPU descriptor index for PTK */ + u8 dpu_desc_index; + + /* PTK DPU signature */ + u8 ucast_dpu_signature; + + /* DPU descriptor index for GTK */ + u8 bcast_dpu_desc_indx; + + /* GTK DPU signature */ + u8 bcast_dpu_signature; + + /* DPU descriptor for IGTK */ + u8 mgmt_dpu_desc_index; + + /* IGTK DPU signature */ + u8 mgmt_dpu_signature; + + /* Station Index for BSS entry */ + u8 bss_sta_index; + + /* Self station index for this BSS */ + u8 bss_self_sta_index; + + /* Bcast station for buffering bcast frames in AP role */ + u8 bss_bcast_sta_idx; + + /* MAC Address of STA(PEER/SELF) in staContext of configBSSReq */ + u8 mac[ETH_ALEN]; + + /* HAL fills in the tx power used for mgmt frames in this field. */ + s8 tx_mgmt_power; + +} __packed; + +struct wcn36xx_hal_config_bss_rsp_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_config_bss_rsp_params bss_rsp_params; +} __packed; + +struct wcn36xx_hal_delete_bss_req_msg { + struct wcn36xx_hal_msg_header header; + + /* BSS index to be deleted */ + u8 bss_index; + +} __packed; + +struct wcn36xx_hal_delete_bss_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Success or Failure */ + u32 status; + + /* BSS index that has been deleted */ + u8 bss_index; + +} __packed; + +struct wcn36xx_hal_join_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Indicates the BSSID to which STA is going to associate */ + u8 bssid[ETH_ALEN]; + + /* Indicates the channel to switch to. */ + u8 channel; + + /* Self STA MAC */ + u8 self_sta_mac_addr[ETH_ALEN]; + + /* Local power constraint */ + u8 local_power_constraint; + + /* Secondary channel offset */ + enum phy_chan_bond_state secondary_channel_offset; + + /* link State */ + enum wcn36xx_hal_link_state link_state; + + /* Max TX power */ + s8 max_tx_power; +} __packed; + +struct wcn36xx_hal_join_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* HAL fills in the tx power used for mgmt frames in this field */ + u8 tx_mgmt_power; +} __packed; + +struct post_assoc_req_msg { + struct wcn36xx_hal_msg_header header; + + struct wcn36xx_hal_config_sta_params sta_params; + struct wcn36xx_hal_config_bss_params bss_params; +}; + +struct post_assoc_rsp_msg { + struct wcn36xx_hal_msg_header header; + struct config_sta_rsp_params sta_rsp_params; + struct wcn36xx_hal_config_bss_rsp_params bss_rsp_params; +}; + +/* This is used to create a set of WEP keys for a given BSS. */ +struct wcn36xx_hal_set_bss_key_req_msg { + struct wcn36xx_hal_msg_header header; + + /* BSS Index of the BSS */ + u8 bss_idx; + + /* Encryption Type used with peer */ + enum ani_ed_type enc_type; + + /* Number of keys */ + u8 num_keys; + + /* Array of keys. */ + struct wcn36xx_hal_keys keys[WCN36XX_HAL_MAC_MAX_NUM_OF_DEFAULT_KEYS]; + + /* Control for Replay Count, 1= Single TID based replay count on Tx + * 0 = Per TID based replay count on TX */ + u8 single_tid_rc; +} __packed; + +/* tagged version of set bss key */ +struct wcn36xx_hal_set_bss_key_req_msg_tagged { + struct wcn36xx_hal_set_bss_key_req_msg Msg; + u32 tag; +} __packed; + +struct wcn36xx_hal_set_bss_key_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +/* + * This is used configure the key information on a given station. + * When the sec_type is WEP40 or WEP104, the def_wep_idx is used to locate + * a preconfigured key from a BSS the station assoicated with; otherwise + * a new key descriptor is created based on the key field. + */ +struct wcn36xx_hal_set_sta_key_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_set_sta_key_params set_sta_key_params; +} __packed; + +struct wcn36xx_hal_set_sta_key_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +struct wcn36xx_hal_remove_bss_key_req_msg { + struct wcn36xx_hal_msg_header header; + + /* BSS Index of the BSS */ + u8 bss_idx; + + /* Encryption Type used with peer */ + enum ani_ed_type enc_type; + + /* Key Id */ + u8 key_id; + + /* STATIC/DYNAMIC. Used in Nullifying in Key Descriptors for + * Static/Dynamic keys */ + enum ani_wep_type wep_type; +} __packed; + +struct wcn36xx_hal_remove_bss_key_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +/* + * This is used by PE to Remove the key information on a given station. + */ +struct wcn36xx_hal_remove_sta_key_req_msg { + struct wcn36xx_hal_msg_header header; + + /* STA Index */ + u16 sta_idx; + + /* Encryption Type used with peer */ + enum ani_ed_type enc_type; + + /* Key Id */ + u8 key_id; + + /* Whether to invalidate the Broadcast key or Unicast key. In case + * of WEP, the same key is used for both broadcast and unicast. */ + u8 unicast; + +} __packed; + +struct wcn36xx_hal_remove_sta_key_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /*success or failure */ + u32 status; + +} __packed; + +#ifdef FEATURE_OEM_DATA_SUPPORT + +#ifndef OEM_DATA_REQ_SIZE +#define OEM_DATA_REQ_SIZE 134 +#endif + +#ifndef OEM_DATA_RSP_SIZE +#define OEM_DATA_RSP_SIZE 1968 +#endif + +struct start_oem_data_req_msg { + struct wcn36xx_hal_msg_header header; + + u32 status; + tSirMacAddr self_mac_addr; + u8 oem_data_req[OEM_DATA_REQ_SIZE]; + +}; + +struct start_oem_data_rsp_msg { + struct wcn36xx_hal_msg_header header; + + u8 oem_data_rsp[OEM_DATA_RSP_SIZE]; +}; + +#endif + +struct wcn36xx_hal_switch_channel_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Channel number */ + u8 channel_number; + + /* Local power constraint */ + u8 local_power_constraint; + + /* Secondary channel offset */ + enum phy_chan_bond_state secondary_channel_offset; + + /* HAL fills in the tx power used for mgmt frames in this field. */ + u8 tx_mgmt_power; + + /* Max TX power */ + u8 max_tx_power; + + /* Self STA MAC */ + u8 self_sta_mac_addr[ETH_ALEN]; + + /* VO WIFI comment: BSSID needed to identify session. As the + * request has power constraints, this should be applied only to + * that session Since MTU timing and EDCA are sessionized, this + * struct needs to be sessionized and bssid needs to be out of the + * VOWifi feature flag V IMP: Keep bssId field at the end of this + * msg. It is used to mantain backward compatbility by way of + * ignoring if using new host/old FW or old host/new FW since it is + * at the end of this struct + */ + u8 bssid[ETH_ALEN]; +} __packed; + +struct wcn36xx_hal_switch_channel_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Status */ + u32 status; + + /* Channel number - same as in request */ + u8 channel_number; + + /* HAL fills in the tx power used for mgmt frames in this field */ + u8 tx_mgmt_power; + + /* BSSID needed to identify session - same as in request */ + u8 bssid[ETH_ALEN]; + +} __packed; + +struct update_edca_params_req_msg { + struct wcn36xx_hal_msg_header header; + + /*BSS Index */ + u16 bss_index; + + /* Best Effort */ + struct wcn36xx_hal_edca_param_record acbe; + + /* Background */ + struct wcn36xx_hal_edca_param_record acbk; + + /* Video */ + struct wcn36xx_hal_edca_param_record acvi; + + /* Voice */ + struct wcn36xx_hal_edca_param_record acvo; +}; + +struct update_edca_params_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct dpu_stats_params { + /* Index of STA to which the statistics */ + u16 sta_index; + + /* Encryption mode */ + u8 enc_mode; + + /* status */ + u32 status; + + /* Statistics */ + u32 send_blocks; + u32 recv_blocks; + u32 replays; + u8 mic_error_cnt; + u32 prot_excl_cnt; + u16 format_err_cnt; + u16 un_decryptable_cnt; + u32 decrypt_err_cnt; + u32 decrypt_ok_cnt; +}; + +struct wcn36xx_hal_stats_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Valid STA Idx for per STA stats request */ + u32 sta_id; + + /* Categories of stats requested as specified in eHalStatsMask */ + u32 stats_mask; +}; + +struct ani_summary_stats_info { + /* Total number of packets(per AC) that were successfully + * transmitted with retries */ + u32 retry_cnt[4]; + + /* The number of MSDU packets and MMPDU frames per AC that the + * 802.11 station successfully transmitted after more than one + * retransmission attempt */ + u32 multiple_retry_cnt[4]; + + /* Total number of packets(per AC) that were successfully + * transmitted (with and without retries, including multi-cast, + * broadcast) */ + u32 tx_frm_cnt[4]; + + /* Total number of packets that were successfully received (after + * appropriate filter rules including multi-cast, broadcast) */ + u32 rx_frm_cnt; + + /* Total number of duplicate frames received successfully */ + u32 frm_dup_cnt; + + /* Total number packets(per AC) failed to transmit */ + u32 fail_cnt[4]; + + /* Total number of RTS/CTS sequence failures for transmission of a + * packet */ + u32 rts_fail_cnt; + + /* Total number packets failed transmit because of no ACK from the + * remote entity */ + u32 ack_fail_cnt; + + /* Total number of RTS/CTS sequence success for transmission of a + * packet */ + u32 rts_succ_cnt; + + /* The sum of the receive error count and dropped-receive-buffer + * error count. HAL will provide this as a sum of (FCS error) + + * (Fail get BD/PDU in HW) */ + u32 rx_discard_cnt; + + /* + * The receive error count. HAL will provide the RxP FCS error + * global counter. */ + u32 rx_error_cnt; + + /* The sum of the transmit-directed byte count, transmit-multicast + * byte count and transmit-broadcast byte count. HAL will sum TPE + * UC/MC/BCAST global counters to provide this. */ + u32 tx_byte_cnt; +}; + +/* defines tx_rate_flags */ +enum tx_rate_info { + /* Legacy rates */ + HAL_TX_RATE_LEGACY = 0x1, + + /* HT20 rates */ + HAL_TX_RATE_HT20 = 0x2, + + /* HT40 rates */ + HAL_TX_RATE_HT40 = 0x4, + + /* Rate with Short guard interval */ + HAL_TX_RATE_SGI = 0x8, + + /* Rate with Long guard interval */ + HAL_TX_RATE_LGI = 0x10 +}; + +struct ani_global_class_a_stats_info { + /* The number of MPDU frames received by the 802.11 station for + * MSDU packets or MMPDU frames */ + u32 rx_frag_cnt; + + /* The number of MPDU frames received by the 802.11 station for + * MSDU packets or MMPDU frames when a promiscuous packet filter + * was enabled */ + u32 promiscuous_rx_frag_cnt; + + /* The receiver input sensitivity referenced to a FER of 8% at an + * MPDU length of 1024 bytes at the antenna connector. Each element + * of the array shall correspond to a supported rate and the order + * shall be the same as the supporteRates parameter. */ + u32 rx_input_sensitivity; + + /* The maximum transmit power in dBm upto one decimal. for eg: if + * it is 10.5dBm, the value would be 105 */ + u32 max_pwr; + + /* Number of times the receiver failed to synchronize with the + * incoming signal after detecting the sync in the preamble of the + * transmitted PLCP protocol data unit. */ + u32 sync_fail_cnt; + + /* Legacy transmit rate, in units of 500 kbit/sec, for the most + * recently transmitted frame */ + u32 tx_rate; + + /* mcs index for HT20 and HT40 rates */ + u32 mcs_index; + + /* to differentiate between HT20 and HT40 rates; short and long + * guard interval */ + u32 tx_rate_flags; +}; + +struct ani_global_security_stats { + /* The number of unencrypted received MPDU frames that the MAC + * layer discarded when the IEEE 802.11 dot11ExcludeUnencrypted + * management information base (MIB) object is enabled */ + u32 rx_wep_unencrypted_frm_cnt; + + /* The number of received MSDU packets that that the 802.11 station + * discarded because of MIC failures */ + u32 rx_mic_fail_cnt; + + /* The number of encrypted MPDU frames that the 802.11 station + * failed to decrypt because of a TKIP ICV error */ + u32 tkip_icv_err; + + /* The number of received MPDU frames that the 802.11 discarded + * because of an invalid AES-CCMP format */ + u32 aes_ccmp_format_err; + + /* The number of received MPDU frames that the 802.11 station + * discarded because of the AES-CCMP replay protection procedure */ + u32 aes_ccmp_replay_cnt; + + /* The number of received MPDU frames that the 802.11 station + * discarded because of errors detected by the AES-CCMP decryption + * algorithm */ + u32 aes_ccmp_decrpt_err; + + /* The number of encrypted MPDU frames received for which a WEP + * decryption key was not available on the 802.11 station */ + u32 wep_undecryptable_cnt; + + /* The number of encrypted MPDU frames that the 802.11 station + * failed to decrypt because of a WEP ICV error */ + u32 wep_icv_err; + + /* The number of received encrypted packets that the 802.11 station + * successfully decrypted */ + u32 rx_decrypt_succ_cnt; + + /* The number of encrypted packets that the 802.11 station failed + * to decrypt */ + u32 rx_decrypt_fail_cnt; +}; + +struct ani_global_class_b_stats_info { + struct ani_global_security_stats uc_stats; + struct ani_global_security_stats mc_bc_stats; +}; + +struct ani_global_class_c_stats_info { + /* This counter shall be incremented for a received A-MSDU frame + * with the stations MAC address in the address 1 field or an + * A-MSDU frame with a group address in the address 1 field */ + u32 rx_amsdu_cnt; + + /* This counter shall be incremented when the MAC receives an AMPDU + * from the PHY */ + u32 rx_ampdu_cnt; + + /* This counter shall be incremented when a Frame is transmitted + * only on the primary channel */ + u32 tx_20_frm_cnt; + + /* This counter shall be incremented when a Frame is received only + * on the primary channel */ + u32 rx_20_frm_cnt; + + /* This counter shall be incremented by the number of MPDUs + * received in the A-MPDU when an A-MPDU is received */ + u32 rx_mpdu_in_ampdu_cnt; + + /* This counter shall be incremented when an MPDU delimiter has a + * CRC error when this is the first CRC error in the received AMPDU + * or when the previous delimiter has been decoded correctly */ + u32 ampdu_delimiter_crc_err; +}; + +struct ani_per_sta_stats_info { + /* The number of MPDU frames that the 802.11 station transmitted + * and acknowledged through a received 802.11 ACK frame */ + u32 tx_frag_cnt[4]; + + /* This counter shall be incremented when an A-MPDU is transmitted */ + u32 tx_ampdu_cnt; + + /* This counter shall increment by the number of MPDUs in the AMPDU + * when an A-MPDU is transmitted */ + u32 tx_mpdu_in_ampdu_cnt; +}; + +struct wcn36xx_hal_stats_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Success or Failure */ + u32 status; + + /* STA Idx */ + u32 sta_index; + + /* Categories of STATS being returned as per eHalStatsMask */ + u32 stats_mask; + + /* message type is same as the request type */ + u16 msg_type; + + /* length of the entire request, includes the pStatsBuf length too */ + u16 msg_len; +}; + +struct wcn36xx_hal_set_link_state_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bssid[ETH_ALEN]; + enum wcn36xx_hal_link_state state; + u8 self_mac_addr[ETH_ALEN]; + +} __packed; + +struct set_link_state_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +/* TSPEC Params */ +struct wcn36xx_hal_ts_info_tfc { +#ifndef ANI_LITTLE_BIT_ENDIAN + u16 ackPolicy:2; + u16 userPrio:3; + u16 psb:1; + u16 aggregation:1; + u16 accessPolicy:2; + u16 direction:2; + u16 tsid:4; + u16 trafficType:1; +#else + u16 trafficType:1; + u16 tsid:4; + u16 direction:2; + u16 accessPolicy:2; + u16 aggregation:1; + u16 psb:1; + u16 userPrio:3; + u16 ackPolicy:2; +#endif +}; + +/* Flag to schedule the traffic type */ +struct wcn36xx_hal_ts_info_sch { +#ifndef ANI_LITTLE_BIT_ENDIAN + u8 rsvd:7; + u8 schedule:1; +#else + u8 schedule:1; + u8 rsvd:7; +#endif +}; + +/* Traffic and scheduling info */ +struct wcn36xx_hal_ts_info { + struct wcn36xx_hal_ts_info_tfc traffic; + struct wcn36xx_hal_ts_info_sch schedule; +}; + +/* Information elements */ +struct wcn36xx_hal_tspec_ie { + u8 type; + u8 length; + struct wcn36xx_hal_ts_info ts_info; + u16 nom_msdu_size; + u16 max_msdu_size; + u32 min_svc_interval; + u32 max_svc_interval; + u32 inact_interval; + u32 suspend_interval; + u32 svc_start_time; + u32 min_data_rate; + u32 mean_data_rate; + u32 peak_data_rate; + u32 max_burst_sz; + u32 delay_bound; + u32 min_phy_rate; + u16 surplus_bw; + u16 medium_time; +}; + +struct add_ts_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_index; + + /* TSPEC handler uniquely identifying a TSPEC for a STA in a BSS */ + u16 tspec_index; + + /* To program TPE with required parameters */ + struct wcn36xx_hal_tspec_ie tspec; + + /* U-APSD Flags: 1b per AC. Encoded as follows: + b7 b6 b5 b4 b3 b2 b1 b0 = + X X X X BE BK VI VO */ + u8 uapsd; + + /* These parameters are for all the access categories */ + + /* Service Interval */ + u32 service_interval[WCN36XX_HAL_MAX_AC]; + + /* Suspend Interval */ + u32 suspend_interval[WCN36XX_HAL_MAX_AC]; + + /* Delay Interval */ + u32 delay_interval[WCN36XX_HAL_MAX_AC]; +}; + +struct add_rs_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct del_ts_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_index; + + /* TSPEC identifier uniquely identifying a TSPEC for a STA in a BSS */ + u16 tspec_index; + + /* To lookup station id using the mac address */ + u8 bssid[ETH_ALEN]; +}; + +struct del_ts_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +/* End of TSpec Parameters */ + +/* Start of BLOCK ACK related Parameters */ + +struct wcn36xx_hal_add_ba_session_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_index; + + /* Peer MAC Address */ + u8 mac_addr[ETH_ALEN]; + + /* ADDBA Action Frame dialog token + HAL will not interpret this object */ + u8 dialog_token; + + /* TID for which the BA is being setup + This identifies the TC or TS of interest */ + u8 tid; + + /* 0 - Delayed BA (Not supported) + 1 - Immediate BA */ + u8 policy; + + /* Indicates the number of buffers for this TID (baTID) + NOTE - This is the requested buffer size. When this + is processed by HAL and subsequently by HDD, it is + possible that HDD may change this buffer size. Any + change in the buffer size should be noted by PE and + advertized appropriately in the ADDBA response */ + u16 buffer_size; + + /* BA timeout in TU's 0 means no timeout will occur */ + u16 timeout; + + /* b0..b3 - Fragment Number - Always set to 0 + b4..b15 - Starting Sequence Number of first MSDU + for which this BA is setup */ + u16 ssn; + + /* ADDBA direction + 1 - Originator + 0 - Recipient */ + u8 direction; +} __packed; + +struct wcn36xx_hal_add_ba_session_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Dialog token */ + u8 dialog_token; + + /* TID for which the BA session has been setup */ + u8 ba_tid; + + /* BA Buffer Size allocated for the current BA session */ + u8 ba_buffer_size; + + u8 ba_session_id; + + /* Reordering Window buffer */ + u8 win_size; + + /* Station Index to id the sta */ + u8 sta_index; + + /* Starting Sequence Number */ + u16 ssn; +} __packed; + +struct wcn36xx_hal_add_ba_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Session Id */ + u8 session_id; + + /* Reorder Window Size */ + u8 win_size; +/* Old FW 1.2.2.4 does not support this*/ +#ifdef FEATURE_ON_CHIP_REORDERING + u8 reordering_done_on_chip; +#endif +} __packed; + +struct wcn36xx_hal_add_ba_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Dialog token */ + u8 dialog_token; +} __packed; + +struct add_ba_info { + u16 ba_enable:1; + u16 starting_seq_num:12; + u16 reserved:3; +}; + +struct wcn36xx_hal_trigger_ba_rsp_candidate { + u8 sta_addr[ETH_ALEN]; + struct add_ba_info ba_info[STACFG_MAX_TC]; +} __packed; + +struct wcn36xx_hal_trigget_ba_req_candidate { + u8 sta_index; + u8 tid_bitmap; +} __packed; + +struct wcn36xx_hal_trigger_ba_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Session Id */ + u8 session_id; + + /* baCandidateCnt is followed by trigger BA + * Candidate List(tTriggerBaCandidate) + */ + u16 candidate_cnt; + +} __packed; + +struct wcn36xx_hal_trigger_ba_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* TO SUPPORT BT-AMP */ + u8 bssid[ETH_ALEN]; + + /* success or failure */ + u32 status; + + /* baCandidateCnt is followed by trigger BA + * Rsp Candidate List(tTriggerRspBaCandidate) + */ + u16 candidate_cnt; +} __packed; + +struct wcn36xx_hal_del_ba_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_index; + + /* TID for which the BA session is being deleted */ + u8 tid; + + /* DELBA direction + 1 - Originator + 0 - Recipient */ + u8 direction; +} __packed; + +struct wcn36xx_hal_del_ba_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +} __packed; + +struct tsm_stats_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Traffic Id */ + u8 tid; + + u8 bssid[ETH_ALEN]; +}; + +struct tsm_stats_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /*success or failure */ + u32 status; + + /* Uplink Packet Queue delay */ + u16 uplink_pkt_queue_delay; + + /* Uplink Packet Queue delay histogram */ + u16 uplink_pkt_queue_delay_hist[4]; + + /* Uplink Packet Transmit delay */ + u32 uplink_pkt_tx_delay; + + /* Uplink Packet loss */ + u16 uplink_pkt_loss; + + /* Uplink Packet count */ + u16 uplink_pkt_count; + + /* Roaming count */ + u8 roaming_count; + + /* Roaming Delay */ + u16 roaming_delay; +}; + +struct set_key_done_msg { + struct wcn36xx_hal_msg_header header; + + /*bssid of the keys */ + u8 bssidx; + u8 enc_type; +}; + +struct wcn36xx_hal_nv_img_download_req_msg { + /* Note: The length specified in wcn36xx_hal_nv_img_download_req_msg + * messages should be + * header.len = sizeof(wcn36xx_hal_nv_img_download_req_msg) + + * nv_img_buffer_size */ + struct wcn36xx_hal_msg_header header; + + /* Fragment sequence number of the NV Image. Note that NV Image + * might not fit into one message due to size limitation of the SMD + * channel FIFO. UMAC can hence choose to chop the NV blob into + * multiple fragments starting with seqeunce number 0, 1, 2 etc. + * The last fragment MUST be indicated by marking the + * isLastFragment field to 1. Note that all the NV blobs would be + * concatenated together by HAL without any padding bytes in + * between.*/ + u16 frag_number; + + /* Is this the last fragment? When set to 1 it indicates that no + * more fragments will be sent by UMAC and HAL can concatenate all + * the NV blobs rcvd & proceed with the parsing. HAL would generate + * a WCN36XX_HAL_DOWNLOAD_NV_RSP to the WCN36XX_HAL_DOWNLOAD_NV_REQ + * after it receives each fragment */ + u16 last_fragment; + + /* NV Image size (number of bytes) */ + u32 nv_img_buffer_size; + + /* Following the 'nv_img_buffer_size', there should be + * nv_img_buffer_size bytes of NV Image i.e. + * u8[nv_img_buffer_size] */ +} __packed; + +struct wcn36xx_hal_nv_img_download_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Success or Failure. HAL would generate a + * WCN36XX_HAL_DOWNLOAD_NV_RSP after each fragment */ + u32 status; +} __packed; + +struct wcn36xx_hal_nv_store_ind { + /* Note: The length specified in tHalNvStoreInd messages should be + * header.msgLen = sizeof(tHalNvStoreInd) + nvBlobSize */ + struct wcn36xx_hal_msg_header header; + + /* NV Item */ + u32 table_id; + + /* Size of NV Blob */ + u32 nv_blob_size; + + /* Following the 'nvBlobSize', there should be nvBlobSize bytes of + * NV blob i.e. u8[nvBlobSize] */ +}; + +/* End of Block Ack Related Parameters */ + +#define WCN36XX_HAL_CIPHER_SEQ_CTR_SIZE 6 + +/* Definition for MIC failure indication MAC reports this each time a MIC + * failure occures on Rx TKIP packet + */ +struct mic_failure_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 bssid[ETH_ALEN]; + + /* address used to compute MIC */ + u8 src_addr[ETH_ALEN]; + + /* transmitter address */ + u8 ta_addr[ETH_ALEN]; + + u8 dst_addr[ETH_ALEN]; + + u8 multicast; + + /* first byte of IV */ + u8 iv1; + + /* second byte of IV */ + u8 key_id; + + /* sequence number */ + u8 tsc[WCN36XX_HAL_CIPHER_SEQ_CTR_SIZE]; + + /* receive address */ + u8 rx_addr[ETH_ALEN]; +}; + +struct update_vht_op_mode_req_msg { + struct wcn36xx_hal_msg_header header; + + u16 op_mode; + u16 sta_id; +}; + +struct update_vht_op_mode_params_rsp_msg { + struct wcn36xx_hal_msg_header header; + + u32 status; +}; + +struct update_beacon_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; + + /* shortPreamble mode. HAL should update all the STA rates when it + * receives this message */ + u8 short_preamble; + + /* short Slot time. */ + u8 short_slot_time; + + /* Beacon Interval */ + u16 beacon_interval; + + /* Protection related */ + u8 lla_coexist; + u8 llb_coexist; + u8 llg_coexist; + u8 ht20_coexist; + u8 lln_non_gf_coexist; + u8 lsig_tx_op_protection_full_support; + u8 rifs_mode; + + u16 param_change_bitmap; +}; + +struct update_beacon_rsp_msg { + struct wcn36xx_hal_msg_header header; + u32 status; +}; + +struct wcn36xx_hal_send_beacon_req_msg { + struct wcn36xx_hal_msg_header header; + + /* length of the template. */ + u32 beacon_length; + + /* Beacon data. */ + u8 beacon[BEACON_TEMPLATE_SIZE]; + + u8 bssid[ETH_ALEN]; + + /* TIM IE offset from the beginning of the template. */ + u32 tim_ie_offset; + + /* P2P IE offset from the begining of the template */ + u16 p2p_ie_offset; +} __packed; + +struct send_beacon_rsp_msg { + struct wcn36xx_hal_msg_header header; + u32 status; +} __packed; + +struct enable_radar_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bssid[ETH_ALEN]; + u8 channel; +}; + +struct enable_radar_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Link Parameters */ + u8 bssid[ETH_ALEN]; + + /* success or failure */ + u32 status; +}; + +struct radar_detect_intr_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 radar_det_channel; +}; + +struct radar_detect_ind_msg { + struct wcn36xx_hal_msg_header header; + + /* channel number in which the RADAR detected */ + u8 channel_number; + + /* RADAR pulse width in usecond */ + u16 radar_pulse_width; + + /* Number of RADAR pulses */ + u16 num_radar_pulse; +}; + +struct wcn36xx_hal_get_tpc_report_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 sta[ETH_ALEN]; + u8 dialog_token; + u8 txpower; +}; + +struct wcn36xx_hal_get_tpc_report_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_send_probe_resp_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 probe_resp_template[BEACON_TEMPLATE_SIZE]; + u32 probe_resp_template_len; + u32 proxy_probe_req_valid_ie_bmap[8]; + u8 bssid[ETH_ALEN]; +}; + +struct send_probe_resp_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct send_unknown_frame_rx_ind_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_delete_sta_context_ind_msg { + struct wcn36xx_hal_msg_header header; + + u16 aid; + u16 sta_id; + + /* TO SUPPORT BT-AMP */ + u8 bssid[ETH_ALEN]; + + /* HAL copies bssid from the sta table. */ + u8 addr2[ETH_ALEN]; + + /* To unify the keepalive / unknown A2 / tim-based disa */ + u16 reason_code; +} __packed; + +struct indicate_del_sta { + struct wcn36xx_hal_msg_header header; + u8 aid; + u8 sta_index; + u8 bss_index; + u8 reason_code; + u32 status; +}; + +struct bt_amp_event_msg { + struct wcn36xx_hal_msg_header header; + + enum bt_amp_event_type btAmpEventType; +}; + +struct bt_amp_event_rsp { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct tl_hal_flush_ac_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index. originates from HAL */ + u8 sta_id; + + /* TID for which the transmit queue is being flushed */ + u8 tid; +}; + +struct tl_hal_flush_ac_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Station Index. originates from HAL */ + u8 sta_id; + + /* TID for which the transmit queue is being flushed */ + u8 tid; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_enter_imps_req_msg { + struct wcn36xx_hal_msg_header header; +}; + +struct wcn36xx_hal_exit_imps_req { + struct wcn36xx_hal_msg_header header; +}; + +struct wcn36xx_hal_enter_bmps_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; + + /* TBTT value derived from the last beacon */ +#ifndef BUILD_QWPTTSTATIC + u64 tbtt; +#endif + u8 dtim_count; + + /* DTIM period given to HAL during association may not be valid, if + * association is based on ProbeRsp instead of beacon. */ + u8 dtim_period; + + /* For CCX and 11R Roaming */ + u32 rssi_filter_period; + + u32 num_beacon_per_rssi_average; + u8 rssi_filter_enable; +} __packed; + +struct wcn36xx_hal_exit_bmps_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 send_data_null; + u8 bss_index; +} __packed; + +struct wcn36xx_hal_missed_beacon_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; +} __packed; + +/* Beacon Filtering data structures */ + +/* The above structure would be followed by multiple of below mentioned + * structure + */ +struct beacon_filter_ie { + u8 element_id; + u8 check_ie_presence; + u8 offset; + u8 value; + u8 bitmask; + u8 ref; +}; + +struct wcn36xx_hal_add_bcn_filter_req_msg { + struct wcn36xx_hal_msg_header header; + + u16 capability_info; + u16 capability_mask; + u16 beacon_interval; + u16 ie_num; + u8 bss_index; + u8 reserved; +}; + +struct wcn36xx_hal_rem_bcn_filter_req { + struct wcn36xx_hal_msg_header header; + + u8 ie_Count; + u8 rem_ie_id[1]; +}; + +#define WCN36XX_HAL_IPV4_ARP_REPLY_OFFLOAD 0 +#define WCN36XX_HAL_IPV6_NEIGHBOR_DISCOVERY_OFFLOAD 1 +#define WCN36XX_HAL_IPV6_NS_OFFLOAD 2 +#define WCN36XX_HAL_IPV6_ADDR_LEN 16 +#define WCN36XX_HAL_OFFLOAD_DISABLE 0 +#define WCN36XX_HAL_OFFLOAD_ENABLE 1 +#define WCN36XX_HAL_OFFLOAD_BCAST_FILTER_ENABLE 0x2 +#define WCN36XX_HAL_OFFLOAD_ARP_AND_BCAST_FILTER_ENABLE \ + (HAL_OFFLOAD_ENABLE|HAL_OFFLOAD_BCAST_FILTER_ENABLE) + +struct wcn36xx_hal_ns_offload_params { + u8 src_ipv6_addr[WCN36XX_HAL_IPV6_ADDR_LEN]; + u8 self_ipv6_addr[WCN36XX_HAL_IPV6_ADDR_LEN]; + + /* Only support 2 possible Network Advertisement IPv6 address */ + u8 target_ipv6_addr1[WCN36XX_HAL_IPV6_ADDR_LEN]; + u8 target_ipv6_addr2[WCN36XX_HAL_IPV6_ADDR_LEN]; + + u8 self_addr[ETH_ALEN]; + u8 src_ipv6_addr_valid:1; + u8 target_ipv6_addr1_valid:1; + u8 target_ipv6_addr2_valid:1; + u8 reserved1:5; + + /* make it DWORD aligned */ + u8 reserved2; + + /* slot index for this offload */ + u32 slot_index; + u8 bss_index; +}; + +struct wcn36xx_hal_host_offload_req { + u8 offload_Type; + + /* enable or disable */ + u8 enable; + + union { + u8 host_ipv4_addr[4]; + u8 host_ipv6_addr[WCN36XX_HAL_IPV6_ADDR_LEN]; + } u; +}; + +struct wcn36xx_hal_host_offload_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_host_offload_req host_offload_params; + struct wcn36xx_hal_ns_offload_params ns_offload_params; +}; + +/* Packet Types. */ +#define WCN36XX_HAL_KEEP_ALIVE_NULL_PKT 1 +#define WCN36XX_HAL_KEEP_ALIVE_UNSOLICIT_ARP_RSP 2 + +/* Enable or disable keep alive */ +#define WCN36XX_HAL_KEEP_ALIVE_DISABLE 0 +#define WCN36XX_HAL_KEEP_ALIVE_ENABLE 1 +#define WCN36XX_KEEP_ALIVE_TIME_PERIOD 30 /* unit: s */ + +/* Keep Alive request. */ +struct wcn36xx_hal_keep_alive_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 packet_type; + u32 time_period; + u8 host_ipv4_addr[WCN36XX_HAL_IPV4_ADDR_LEN]; + u8 dest_ipv4_addr[WCN36XX_HAL_IPV4_ADDR_LEN]; + u8 dest_addr[ETH_ALEN]; + u8 bss_index; +} __packed; + +struct wcn36xx_hal_rssi_threshold_req_msg { + struct wcn36xx_hal_msg_header header; + + s8 threshold1:8; + s8 threshold2:8; + s8 threshold3:8; + u8 thres1_pos_notify:1; + u8 thres1_neg_notify:1; + u8 thres2_pos_notify:1; + u8 thres2_neg_notify:1; + u8 thres3_pos_notify:1; + u8 thres3_neg_notify:1; + u8 reserved10:2; +}; + +struct wcn36xx_hal_enter_uapsd_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bk_delivery:1; + u8 be_delivery:1; + u8 vi_delivery:1; + u8 vo_delivery:1; + u8 bk_trigger:1; + u8 be_trigger:1; + u8 vi_trigger:1; + u8 vo_trigger:1; + u8 bss_index; +}; + +struct wcn36xx_hal_exit_uapsd_req_msg { + struct wcn36xx_hal_msg_header header; + u8 bss_index; +}; + +#define WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE 128 +#define WCN36XX_HAL_WOWL_BCAST_MAX_NUM_PATTERNS 16 + +struct wcn36xx_hal_wowl_add_bcast_ptrn_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Pattern ID */ + u8 id; + + /* Pattern byte offset from beginning of the 802.11 packet to start + * of the wake-up pattern */ + u8 byte_Offset; + + /* Non-Zero Pattern size */ + u8 size; + + /* Pattern */ + u8 pattern[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE]; + + /* Non-zero pattern mask size */ + u8 mask_size; + + /* Pattern mask */ + u8 mask[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE]; + + /* Extra pattern */ + u8 extra[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE]; + + /* Extra pattern mask */ + u8 mask_extra[WCN36XX_HAL_WOWL_BCAST_PATTERN_MAX_SIZE]; + + u8 bss_index; +}; + +struct wcn36xx_hal_wow_del_bcast_ptrn_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Pattern ID of the wakeup pattern to be deleted */ + u8 id; + u8 bss_index; +}; + +struct wcn36xx_hal_wowl_enter_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Enables/disables magic packet filtering */ + u8 magic_packet_enable; + + /* Magic pattern */ + u8 magic_pattern[ETH_ALEN]; + + /* Enables/disables packet pattern filtering in firmware. Enabling + * this flag enables broadcast pattern matching in Firmware. If + * unicast pattern matching is also desired, + * ucUcastPatternFilteringEnable flag must be set tot true as well + */ + u8 pattern_filtering_enable; + + /* Enables/disables unicast packet pattern filtering. This flag + * specifies whether we want to do pattern match on unicast packets + * as well and not just broadcast packets. This flag has no effect + * if the ucPatternFilteringEnable (main controlling flag) is set + * to false + */ + u8 ucast_pattern_filtering_enable; + + /* This configuration is valid only when magicPktEnable=1. It + * requests hardware to wake up when it receives the Channel Switch + * Action Frame. + */ + u8 wow_channel_switch_receive; + + /* This configuration is valid only when magicPktEnable=1. It + * requests hardware to wake up when it receives the + * Deauthentication Frame. + */ + u8 wow_deauth_receive; + + /* This configuration is valid only when magicPktEnable=1. It + * requests hardware to wake up when it receives the Disassociation + * Frame. + */ + u8 wow_disassoc_receive; + + /* This configuration is valid only when magicPktEnable=1. It + * requests hardware to wake up when it has missed consecutive + * beacons. This is a hardware register configuration (NOT a + * firmware configuration). + */ + u8 wow_max_missed_beacons; + + /* This configuration is valid only when magicPktEnable=1. This is + * a timeout value in units of microsec. It requests hardware to + * unconditionally wake up after it has stayed in WoWLAN mode for + * some time. Set 0 to disable this feature. + */ + u8 wow_max_sleep; + + /* This configuration directs the WoW packet filtering to look for + * EAP-ID requests embedded in EAPOL frames and use this as a wake + * source. + */ + u8 wow_eap_id_request_enable; + + /* This configuration directs the WoW packet filtering to look for + * EAPOL-4WAY requests and use this as a wake source. + */ + u8 wow_eapol_4way_enable; + + /* This configuration allows a host wakeup on an network scan + * offload match. + */ + u8 wow_net_scan_offload_match; + + /* This configuration allows a host wakeup on any GTK rekeying + * error. + */ + u8 wow_gtk_rekey_error; + + /* This configuration allows a host wakeup on BSS connection loss. + */ + u8 wow_bss_connection_loss; + + u8 bss_index; +}; + +struct wcn36xx_hal_wowl_exit_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; +}; + +struct wcn36xx_hal_get_rssi_req_msg { + struct wcn36xx_hal_msg_header header; +}; + +struct wcn36xx_hal_get_roam_rssi_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Valid STA Idx for per STA stats request */ + u32 sta_id; +}; + +struct wcn36xx_hal_set_uapsd_ac_params_req_msg { + struct wcn36xx_hal_msg_header header; + + /* STA index */ + u8 sta_idx; + + /* Access Category */ + u8 ac; + + /* User Priority */ + u8 up; + + /* Service Interval */ + u32 service_interval; + + /* Suspend Interval */ + u32 suspend_interval; + + /* Delay Interval */ + u32 delay_interval; +}; + +struct wcn36xx_hal_configure_rxp_filter_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 set_mcst_bcst_filter_setting; + u8 set_mcst_bcst_filter; +}; + +struct wcn36xx_hal_enter_imps_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_exit_imps_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_enter_bmps_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +} __packed; + +struct wcn36xx_hal_exit_bmps_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +} __packed; + +struct wcn36xx_hal_enter_uapsd_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +}; + +struct wcn36xx_hal_exit_uapsd_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +}; + +struct wcn36xx_hal_rssi_notification_ind_msg { + struct wcn36xx_hal_msg_header header; + + u32 rssi_thres1_pos_cross:1; + u32 rssi_thres1_neg_cross:1; + u32 rssi_thres2_pos_cross:1; + u32 rssi_thres2_neg_cross:1; + u32 rssi_thres3_pos_cross:1; + u32 rssi_thres3_neg_cross:1; + u32 avg_rssi:8; + u32 reserved:18; + +}; + +struct wcn36xx_hal_get_rssio_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + s8 rssi; + +}; + +struct wcn36xx_hal_get_roam_rssi_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 sta_id; + s8 rssi; +}; + +struct wcn36xx_hal_wowl_enter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + u8 bss_index; +}; + +struct wcn36xx_hal_wowl_exit_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + u8 bss_index; +}; + +struct wcn36xx_hal_add_bcn_filter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_rem_bcn_filter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_add_wowl_bcast_ptrn_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + u8 bss_index; +}; + +struct wcn36xx_hal_del_wowl_bcast_ptrn_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + u8 bss_index; +}; + +struct wcn36xx_hal_host_offload_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_keep_alive_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_set_rssi_thresh_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_set_uapsd_ac_params_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_configure_rxp_filter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct set_max_tx_pwr_req { + struct wcn36xx_hal_msg_header header; + + /* BSSID is needed to identify which session issued this request. + * As the request has power constraints, this should be applied + * only to that session */ + u8 bssid[ETH_ALEN]; + + u8 self_addr[ETH_ALEN]; + + /* In request, power == MaxTx power to be used. */ + u8 power; +}; + +struct set_max_tx_pwr_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* power == tx power used for management frames */ + u8 power; + + /* success or failure */ + u32 status; +}; + +struct set_tx_pwr_req_msg { + struct wcn36xx_hal_msg_header header; + + /* TX Power in milli watts */ + u32 tx_power; + + u8 bss_index; +}; + +struct set_tx_pwr_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct get_tx_pwr_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 sta_id; +}; + +struct get_tx_pwr_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* TX Power in milli watts */ + u32 tx_power; +}; + +struct set_p2p_gonoa_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 opp_ps; + u32 ct_window; + u8 count; + u32 duration; + u32 interval; + u32 single_noa_duration; + u8 ps_selection; +}; + +struct set_p2p_gonoa_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_add_sta_self_req { + struct wcn36xx_hal_msg_header header; + + u8 self_addr[ETH_ALEN]; + u32 status; +} __packed; + +struct wcn36xx_hal_add_sta_self_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Self STA Index */ + u8 self_sta_index; + + /* DPU Index (IGTK, PTK, GTK all same) */ + u8 dpu_index; + + /* DPU Signature */ + u8 dpu_signature; +} __packed; + +struct wcn36xx_hal_del_sta_self_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 self_addr[ETH_ALEN]; +} __packed; + +struct wcn36xx_hal_del_sta_self_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /*success or failure */ + u32 status; + + u8 self_addr[ETH_ALEN]; +} __packed; + +struct aggr_add_ts_req { + struct wcn36xx_hal_msg_header header; + + /* Station Index */ + u16 sta_idx; + + /* TSPEC handler uniquely identifying a TSPEC for a STA in a BSS. + * This will carry the bitmap with the bit positions representing + * different AC.s */ + u16 tspec_index; + + /* Tspec info per AC To program TPE with required parameters */ + struct wcn36xx_hal_tspec_ie tspec[WCN36XX_HAL_MAX_AC]; + + /* U-APSD Flags: 1b per AC. Encoded as follows: + b7 b6 b5 b4 b3 b2 b1 b0 = + X X X X BE BK VI VO */ + u8 uapsd; + + /* These parameters are for all the access categories */ + + /* Service Interval */ + u32 service_interval[WCN36XX_HAL_MAX_AC]; + + /* Suspend Interval */ + u32 suspend_interval[WCN36XX_HAL_MAX_AC]; + + /* Delay Interval */ + u32 delay_interval[WCN36XX_HAL_MAX_AC]; +}; + +struct aggr_add_ts_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status0; + + /* FIXME PRIMA for future use for 11R */ + u32 status1; +}; + +struct wcn36xx_hal_configure_apps_cpu_wakeup_state_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 is_apps_cpu_awake; +}; + +struct wcn36xx_hal_configure_apps_cpu_wakeup_state_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_dump_cmd_req_msg { + struct wcn36xx_hal_msg_header header; + + u32 arg1; + u32 arg2; + u32 arg3; + u32 arg4; + u32 arg5; +} __packed; + +struct wcn36xx_hal_dump_cmd_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* Length of the responce message */ + u32 rsp_length; + + /* FIXME: Currently considering the the responce will be less than + * 100bytes */ + u8 rsp_buffer[DUMPCMD_RSP_BUFFER]; +} __packed; + +#define WLAN_COEX_IND_DATA_SIZE (4) +#define WLAN_COEX_IND_TYPE_DISABLE_HB_MONITOR (0) +#define WLAN_COEX_IND_TYPE_ENABLE_HB_MONITOR (1) + +struct coex_ind_msg { + struct wcn36xx_hal_msg_header header; + + /* Coex Indication Type */ + u32 type; + + /* Coex Indication Data */ + u32 data[WLAN_COEX_IND_DATA_SIZE]; +}; + +struct wcn36xx_hal_tx_compl_ind_msg { + struct wcn36xx_hal_msg_header header; + + /* Tx Complete Indication Success or Failure */ + u32 status; +}; + +struct wcn36xx_hal_wlan_host_suspend_ind_msg { + struct wcn36xx_hal_msg_header header; + + u32 configured_mcst_bcst_filter_setting; + u32 active_session_count; +}; + +struct wcn36xx_hal_wlan_exclude_unencrpted_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 dot11_exclude_unencrypted; + u8 bssid[ETH_ALEN]; +}; + +struct noa_attr_ind_msg { + struct wcn36xx_hal_msg_header header; + + u8 index; + u8 opp_ps_flag; + u16 ctwin; + + u16 noa1_interval_count; + u16 bss_index; + u32 noa1_duration; + u32 noa1_interval; + u32 noa1_starttime; + + u16 noa2_interval_count; + u16 reserved2; + u32 noa2_duration; + u32 noa2_interval; + u32 noa2_start_time; + + u32 status; +}; + +struct noa_start_ind_msg { + struct wcn36xx_hal_msg_header header; + + u32 status; + u32 bss_index; +}; + +struct wcn36xx_hal_wlan_host_resume_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 configured_mcst_bcst_filter_setting; +}; + +struct wcn36xx_hal_host_resume_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +struct wcn36xx_hal_del_ba_ind_msg { + struct wcn36xx_hal_msg_header header; + + u16 sta_idx; + + /* Peer MAC Address, whose BA session has timed out */ + u8 peer_addr[ETH_ALEN]; + + /* TID for which a BA session timeout is being triggered */ + u8 ba_tid; + + /* DELBA direction + * 1 - Originator + * 0 - Recipient + */ + u8 direction; + + u32 reason_code; + + /* TO SUPPORT BT-AMP */ + u8 bssid[ETH_ALEN]; +}; + +/* PNO Messages */ + +/* Max number of channels that a network can be found on */ +#define WCN36XX_HAL_PNO_MAX_NETW_CHANNELS 26 + +/* Max number of channels that a network can be found on */ +#define WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX 60 + +/* Maximum numbers of networks supported by PNO */ +#define WCN36XX_HAL_PNO_MAX_SUPP_NETWORKS 16 + +/* The number of scan time intervals that can be programmed into PNO */ +#define WCN36XX_HAL_PNO_MAX_SCAN_TIMERS 10 + +/* Maximum size of the probe template */ +#define WCN36XX_HAL_PNO_MAX_PROBE_SIZE 450 + +/* Type of PNO enabling: + * + * Immediate - scanning will start immediately and PNO procedure will be + * repeated based on timer + * + * Suspend - scanning will start at suspend + * + * Resume - scanning will start on system resume + */ +enum pno_mode { + PNO_MODE_IMMEDIATE, + PNO_MODE_ON_SUSPEND, + PNO_MODE_ON_RESUME, + PNO_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* Authentication type */ +enum auth_type { + AUTH_TYPE_ANY = 0, + AUTH_TYPE_OPEN_SYSTEM = 1, + + /* Upper layer authentication types */ + AUTH_TYPE_WPA = 2, + AUTH_TYPE_WPA_PSK = 3, + + AUTH_TYPE_RSN = 4, + AUTH_TYPE_RSN_PSK = 5, + AUTH_TYPE_FT_RSN = 6, + AUTH_TYPE_FT_RSN_PSK = 7, + AUTH_TYPE_WAPI_WAI_CERTIFICATE = 8, + AUTH_TYPE_WAPI_WAI_PSK = 9, + + AUTH_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* Encryption type */ +enum ed_type { + ED_ANY = 0, + ED_NONE = 1, + ED_WEP = 2, + ED_TKIP = 3, + ED_CCMP = 4, + ED_WPI = 5, + + ED_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* SSID broadcast type */ +enum ssid_bcast_type { + BCAST_UNKNOWN = 0, + BCAST_NORMAL = 1, + BCAST_HIDDEN = 2, + + BCAST_TYPE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE +}; + +/* The network description for which PNO will have to look for */ +struct network_type { + /* SSID of the BSS */ + struct wcn36xx_hal_mac_ssid ssid; + + /* Authentication type for the network */ + enum auth_type authentication; + + /* Encryption type for the network */ + enum ed_type encryption; + + /* Indicate the channel on which the Network can be found 0 - if + * all channels */ + u8 channel_count; + u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS]; + + /* Indicates the RSSI threshold for the network to be considered */ + u8 rssi_threshold; +}; + +struct scan_timer { + /* How much it should wait */ + u32 value; + + /* How many times it should repeat that wait value 0 - keep using + * this timer until PNO is disabled */ + u32 repeat; + + /* e.g: 2 3 4 0 - it will wait 2s between consecutive scans for 3 + * times - after that it will wait 4s between consecutive scans + * until disabled */ +}; + +/* The network parameters to be sent to the PNO algorithm */ +struct scan_timers_type { + /* set to 0 if you wish for PNO to use its default telescopic timer */ + u8 count; + + /* A set value represents the amount of time that PNO will wait + * between two consecutive scan procedures If the desired is for a + * uniform timer that fires always at the exact same interval - one + * single value is to be set If there is a desire for a more + * complex - telescopic like timer multiple values can be set - + * once PNO reaches the end of the array it will continue scanning + * at intervals presented by the last value */ + struct scan_timer values[WCN36XX_HAL_PNO_MAX_SCAN_TIMERS]; +}; + +/* Preferred network list request */ +struct set_pref_netw_list_req { + struct wcn36xx_hal_msg_header header; + + /* Enable PNO */ + u32 enable; + + /* Immediate, On Suspend, On Resume */ + enum pno_mode mode; + + /* Number of networks sent for PNO */ + u32 networks_count; + + /* The networks that PNO needs to look for */ + struct network_type networks[WCN36XX_HAL_PNO_MAX_SUPP_NETWORKS]; + + /* The scan timers required for PNO */ + struct scan_timers_type scan_timers; + + /* Probe template for 2.4GHz band */ + u16 band_24g_probe_size; + u8 band_24g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE]; + + /* Probe template for 5GHz band */ + u16 band_5g_probe_size; + u8 band_5g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE]; +}; + +/* The network description for which PNO will have to look for */ +struct network_type_new { + /* SSID of the BSS */ + struct wcn36xx_hal_mac_ssid ssid; + + /* Authentication type for the network */ + enum auth_type authentication; + + /* Encryption type for the network */ + enum ed_type encryption; + + /* SSID broadcast type, normal, hidden or unknown */ + enum ssid_bcast_type bcast_network_type; + + /* Indicate the channel on which the Network can be found 0 - if + * all channels */ + u8 channel_count; + u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS]; + + /* Indicates the RSSI threshold for the network to be considered */ + u8 rssi_threshold; +}; + +/* Preferred network list request new */ +struct set_pref_netw_list_req_new { + struct wcn36xx_hal_msg_header header; + + /* Enable PNO */ + u32 enable; + + /* Immediate, On Suspend, On Resume */ + enum pno_mode mode; + + /* Number of networks sent for PNO */ + u32 networks_count; + + /* The networks that PNO needs to look for */ + struct network_type_new networks[WCN36XX_HAL_PNO_MAX_SUPP_NETWORKS]; + + /* The scan timers required for PNO */ + struct scan_timers_type scan_timers; + + /* Probe template for 2.4GHz band */ + u16 band_24g_probe_size; + u8 band_24g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE]; + + /* Probe template for 5GHz band */ + u16 band_5g_probe_size; + u8 band_5g_probe_template[WCN36XX_HAL_PNO_MAX_PROBE_SIZE]; +}; + +/* Preferred network list response */ +struct set_pref_netw_list_resp { + struct wcn36xx_hal_msg_header header; + + /* status of the request - just to indicate that PNO has + * acknowledged the request and will start scanning */ + u32 status; +}; + +/* Preferred network found indication */ +struct pref_netw_found_ind { + + struct wcn36xx_hal_msg_header header; + + /* Network that was found with the highest RSSI */ + struct wcn36xx_hal_mac_ssid ssid; + + /* Indicates the RSSI */ + u8 rssi; +}; + +/* RSSI Filter request */ +struct set_rssi_filter_req { + struct wcn36xx_hal_msg_header header; + + /* RSSI Threshold */ + u8 rssi_threshold; +}; + +/* Set RSSI filter resp */ +struct set_rssi_filter_resp { + struct wcn36xx_hal_msg_header header; + + /* status of the request */ + u32 status; +}; + +/* Update scan params - sent from host to PNO to be used during PNO + * scanningx */ +struct wcn36xx_hal_update_scan_params_req { + + struct wcn36xx_hal_msg_header header; + + /* Host setting for 11d */ + u8 dot11d_enabled; + + /* Lets PNO know that host has determined the regulatory domain */ + u8 dot11d_resolved; + + /* Channels on which PNO is allowed to scan */ + u8 channel_count; + u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS]; + + /* Minimum channel time */ + u16 active_min_ch_time; + + /* Maximum channel time */ + u16 active_max_ch_time; + + /* Minimum channel time */ + u16 passive_min_ch_time; + + /* Maximum channel time */ + u16 passive_max_ch_time; + + /* Cb State */ + enum phy_chan_bond_state state; +} __packed; + +/* Update scan params - sent from host to PNO to be used during PNO + * scanningx */ +struct update_scan_params_req_ex { + + struct wcn36xx_hal_msg_header header; + + /* Host setting for 11d */ + u8 dot11d_enabled; + + /* Lets PNO know that host has determined the regulatory domain */ + u8 dot11d_resolved; + + /* Channels on which PNO is allowed to scan */ + u8 channel_count; + u8 channels[WCN36XX_HAL_PNO_MAX_NETW_CHANNELS_EX]; + + /* Minimum channel time */ + u16 active_min_ch_time; + + /* Maximum channel time */ + u16 active_max_ch_time; + + /* Minimum channel time */ + u16 passive_min_ch_time; + + /* Maximum channel time */ + u16 passive_max_ch_time; + + /* Cb State */ + enum phy_chan_bond_state state; +}; + +/* Update scan params - sent from host to PNO to be used during PNO + * scanningx */ +struct wcn36xx_hal_update_scan_params_resp { + + struct wcn36xx_hal_msg_header header; + + /* status of the request */ + u32 status; +} __packed; + +struct wcn36xx_hal_set_tx_per_tracking_req_msg { + struct wcn36xx_hal_msg_header header; + + /* 0: disable, 1:enable */ + u8 tx_per_tracking_enable; + + /* Check period, unit is sec. */ + u8 tx_per_tracking_period; + + /* (Fail TX packet)/(Total TX packet) ratio, the unit is 10%. */ + u8 tx_per_tracking_ratio; + + /* A watermark of check number, once the tx packet exceed this + * number, we do the check, default is 5 */ + u32 tx_per_tracking_watermark; +}; + +struct wcn36xx_hal_set_tx_per_tracking_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + +}; + +struct tx_per_hit_ind_msg { + struct wcn36xx_hal_msg_header header; +}; + +/* Packet Filtering Definitions Begin */ +#define WCN36XX_HAL_PROTOCOL_DATA_LEN 8 +#define WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS 240 +#define WCN36XX_HAL_MAX_NUM_FILTERS 20 +#define WCN36XX_HAL_MAX_CMP_PER_FILTER 10 + +enum wcn36xx_hal_receive_packet_filter_type { + HAL_RCV_FILTER_TYPE_INVALID, + HAL_RCV_FILTER_TYPE_FILTER_PKT, + HAL_RCV_FILTER_TYPE_BUFFER_PKT, + HAL_RCV_FILTER_TYPE_MAX_ENUM_SIZE +}; + +enum wcn36xx_hal_rcv_pkt_flt_protocol_type { + HAL_FILTER_PROTO_TYPE_INVALID, + HAL_FILTER_PROTO_TYPE_MAC, + HAL_FILTER_PROTO_TYPE_ARP, + HAL_FILTER_PROTO_TYPE_IPV4, + HAL_FILTER_PROTO_TYPE_IPV6, + HAL_FILTER_PROTO_TYPE_UDP, + HAL_FILTER_PROTO_TYPE_MAX +}; + +enum wcn36xx_hal_rcv_pkt_flt_cmp_flag_type { + HAL_FILTER_CMP_TYPE_INVALID, + HAL_FILTER_CMP_TYPE_EQUAL, + HAL_FILTER_CMP_TYPE_MASK_EQUAL, + HAL_FILTER_CMP_TYPE_NOT_EQUAL, + HAL_FILTER_CMP_TYPE_MAX +}; + +struct wcn36xx_hal_rcv_pkt_filter_params { + u8 protocol_layer; + u8 cmp_flag; + + /* Length of the data to compare */ + u16 data_length; + + /* from start of the respective frame header */ + u8 data_offset; + + /* Reserved field */ + u8 reserved; + + /* Data to compare */ + u8 compare_data[WCN36XX_HAL_PROTOCOL_DATA_LEN]; + + /* Mask to be applied on the received packet data before compare */ + u8 data_mask[WCN36XX_HAL_PROTOCOL_DATA_LEN]; +}; + +struct wcn36xx_hal_sessionized_rcv_pkt_filter_cfg_type { + u8 id; + u8 type; + u8 params_count; + u32 coleasce_time; + u8 bss_index; + struct wcn36xx_hal_rcv_pkt_filter_params params[1]; +}; + +struct wcn36xx_hal_set_rcv_pkt_filter_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 id; + u8 type; + u8 params_count; + u32 coalesce_time; + struct wcn36xx_hal_rcv_pkt_filter_params params[1]; +}; + +struct wcn36xx_hal_rcv_flt_mc_addr_list_type { + /* from start of the respective frame header */ + u8 data_offset; + + u32 mc_addr_count; + u8 mc_addr[ETH_ALEN][WCN36XX_HAL_MAX_NUM_MULTICAST_ADDRESS]; + u8 bss_index; +}; + +struct wcn36xx_hal_set_pkt_filter_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +}; + +struct wcn36xx_hal_rcv_flt_pkt_match_cnt_req_msg { + struct wcn36xx_hal_msg_header header; + + u8 bss_index; +}; + +struct wcn36xx_hal_rcv_flt_pkt_match_cnt { + u8 id; + u32 match_cnt; +}; + +struct wcn36xx_hal_rcv_flt_pkt_match_cnt_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* Success or Failure */ + u32 status; + + u32 match_count; + struct wcn36xx_hal_rcv_flt_pkt_match_cnt + matches[WCN36XX_HAL_MAX_NUM_FILTERS]; + u8 bss_index; +}; + +struct wcn36xx_hal_rcv_flt_pkt_clear_param { + /* only valid for response message */ + u32 status; + u8 id; + u8 bss_index; +}; + +struct wcn36xx_hal_rcv_flt_pkt_clear_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_rcv_flt_pkt_clear_param param; +}; + +struct wcn36xx_hal_rcv_flt_pkt_clear_rsp_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_rcv_flt_pkt_clear_param param; +}; + +struct wcn36xx_hal_rcv_flt_pkt_set_mc_list_req_msg { + struct wcn36xx_hal_msg_header header; + struct wcn36xx_hal_rcv_flt_mc_addr_list_type mc_addr_list; +}; + +struct wcn36xx_hal_rcv_flt_pkt_set_mc_list_rsp_msg { + struct wcn36xx_hal_msg_header header; + u32 status; + u8 bss_index; +}; + +/* Packet Filtering Definitions End */ + +struct wcn36xx_hal_set_power_params_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Ignore DTIM */ + u32 ignore_dtim; + + /* DTIM Period */ + u32 dtim_period; + + /* Listen Interval */ + u32 listen_interval; + + /* Broadcast Multicast Filter */ + u32 bcast_mcast_filter; + + /* Beacon Early Termination */ + u32 enable_bet; + + /* Beacon Early Termination Interval */ + u32 bet_interval; +} __packed; + +struct wcn36xx_hal_set_power_params_resp { + + struct wcn36xx_hal_msg_header header; + + /* status of the request */ + u32 status; +} __packed; + +/* Capability bitmap exchange definitions and macros starts */ + +enum place_holder_in_cap_bitmap { + MCC = 0, + P2P = 1, + DOT11AC = 2, + SLM_SESSIONIZATION = 3, + DOT11AC_OPMODE = 4, + SAP32STA = 5, + TDLS = 6, + P2P_GO_NOA_DECOUPLE_INIT_SCAN = 7, + WLANACTIVE_OFFLOAD = 8, + BEACON_OFFLOAD = 9, + SCAN_OFFLOAD = 10, + ROAM_OFFLOAD = 11, + BCN_MISS_OFFLOAD = 12, + STA_POWERSAVE = 13, + STA_ADVANCED_PWRSAVE = 14, + AP_UAPSD = 15, + AP_DFS = 16, + BLOCKACK = 17, + PHY_ERR = 18, + BCN_FILTER = 19, + RTT = 20, + RATECTRL = 21, + WOW = 22, + MAX_FEATURE_SUPPORTED = 128, +}; + +struct wcn36xx_hal_feat_caps_msg { + + struct wcn36xx_hal_msg_header header; + + u32 feat_caps[4]; +} __packed; + +/* status codes to help debug rekey failures */ +enum gtk_rekey_status { + WCN36XX_HAL_GTK_REKEY_STATUS_SUCCESS = 0, + + /* rekey detected, but not handled */ + WCN36XX_HAL_GTK_REKEY_STATUS_NOT_HANDLED = 1, + + /* MIC check error on M1 */ + WCN36XX_HAL_GTK_REKEY_STATUS_MIC_ERROR = 2, + + /* decryption error on M1 */ + WCN36XX_HAL_GTK_REKEY_STATUS_DECRYPT_ERROR = 3, + + /* M1 replay detected */ + WCN36XX_HAL_GTK_REKEY_STATUS_REPLAY_ERROR = 4, + + /* missing GTK key descriptor in M1 */ + WCN36XX_HAL_GTK_REKEY_STATUS_MISSING_KDE = 5, + + /* missing iGTK key descriptor in M1 */ + WCN36XX_HAL_GTK_REKEY_STATUS_MISSING_IGTK_KDE = 6, + + /* key installation error */ + WCN36XX_HAL_GTK_REKEY_STATUS_INSTALL_ERROR = 7, + + /* iGTK key installation error */ + WCN36XX_HAL_GTK_REKEY_STATUS_IGTK_INSTALL_ERROR = 8, + + /* GTK rekey M2 response TX error */ + WCN36XX_HAL_GTK_REKEY_STATUS_RESP_TX_ERROR = 9, + + /* non-specific general error */ + WCN36XX_HAL_GTK_REKEY_STATUS_GEN_ERROR = 255 +}; + +/* wake reason types */ +enum wake_reason_type { + WCN36XX_HAL_WAKE_REASON_NONE = 0, + + /* magic packet match */ + WCN36XX_HAL_WAKE_REASON_MAGIC_PACKET = 1, + + /* host defined pattern match */ + WCN36XX_HAL_WAKE_REASON_PATTERN_MATCH = 2, + + /* EAP-ID frame detected */ + WCN36XX_HAL_WAKE_REASON_EAPID_PACKET = 3, + + /* start of EAPOL 4-way handshake detected */ + WCN36XX_HAL_WAKE_REASON_EAPOL4WAY_PACKET = 4, + + /* network scan offload match */ + WCN36XX_HAL_WAKE_REASON_NETSCAN_OFFL_MATCH = 5, + + /* GTK rekey status wakeup (see status) */ + WCN36XX_HAL_WAKE_REASON_GTK_REKEY_STATUS = 6, + + /* BSS connection lost */ + WCN36XX_HAL_WAKE_REASON_BSS_CONN_LOST = 7, +}; + +/* + Wake Packet which is saved at tWakeReasonParams.DataStart + This data is sent for any wake reasons that involve a packet-based wakeup : + + WCN36XX_HAL_WAKE_REASON_TYPE_MAGIC_PACKET + WCN36XX_HAL_WAKE_REASON_TYPE_PATTERN_MATCH + WCN36XX_HAL_WAKE_REASON_TYPE_EAPID_PACKET + WCN36XX_HAL_WAKE_REASON_TYPE_EAPOL4WAY_PACKET + WCN36XX_HAL_WAKE_REASON_TYPE_GTK_REKEY_STATUS + + The information is provided to the host for auditing and debug purposes + +*/ + +/* Wake reason indication */ +struct wcn36xx_hal_wake_reason_ind { + struct wcn36xx_hal_msg_header header; + + /* see tWakeReasonType */ + u32 reason; + + /* argument specific to the reason type */ + u32 reason_arg; + + /* length of optional data stored in this message, in case HAL + * truncates the data (i.e. data packets) this length will be less + * than the actual length */ + u32 stored_data_len; + + /* actual length of data */ + u32 actual_data_len; + + /* variable length start of data (length == storedDataLen) see + * specific wake type */ + u8 data_start[1]; + + u32 bss_index:8; + u32 reserved:24; +}; + +#define WCN36XX_HAL_GTK_KEK_BYTES 16 +#define WCN36XX_HAL_GTK_KCK_BYTES 16 + +#define WCN36XX_HAL_GTK_OFFLOAD_FLAGS_DISABLE (1 << 0) + +#define GTK_SET_BSS_KEY_TAG 0x1234AA55 + +struct wcn36xx_hal_gtk_offload_req_msg { + struct wcn36xx_hal_msg_header header; + + /* optional flags */ + u32 flags; + + /* Key confirmation key */ + u8 kck[WCN36XX_HAL_GTK_KCK_BYTES]; + + /* key encryption key */ + u8 kek[WCN36XX_HAL_GTK_KEK_BYTES]; + + /* replay counter */ + u64 key_replay_counter; + + u8 bss_index; +}; + +struct wcn36xx_hal_gtk_offload_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + u8 bss_index; +}; + +struct wcn36xx_hal_gtk_offload_get_info_req_msg { + struct wcn36xx_hal_msg_header header; + u8 bss_index; +}; + +struct wcn36xx_hal_gtk_offload_get_info_rsp_msg { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; + + /* last rekey status when the rekey was offloaded */ + u32 last_rekey_status; + + /* current replay counter value */ + u64 key_replay_counter; + + /* total rekey attempts */ + u32 total_rekey_count; + + /* successful GTK rekeys */ + u32 gtk_rekey_count; + + /* successful iGTK rekeys */ + u32 igtk_rekey_count; + + u8 bss_index; +}; + +struct dhcp_info { + /* Indicates the device mode which indicates about the DHCP activity */ + u8 device_mode; + + u8 addr[ETH_ALEN]; +}; + +struct dhcp_ind_status { + struct wcn36xx_hal_msg_header header; + + /* success or failure */ + u32 status; +}; + +/* + * Thermal Mitigation mode of operation. + * + * WCN36XX_HAL_THERMAL_MITIGATION_MODE_0 - Based on AMPDU disabling aggregation + * + * WCN36XX_HAL_THERMAL_MITIGATION_MODE_1 - Based on AMPDU disabling aggregation + * and reducing transmit power + * + * WCN36XX_HAL_THERMAL_MITIGATION_MODE_2 - Not supported */ +enum wcn36xx_hal_thermal_mitigation_mode_type { + HAL_THERMAL_MITIGATION_MODE_INVALID = -1, + HAL_THERMAL_MITIGATION_MODE_0, + HAL_THERMAL_MITIGATION_MODE_1, + HAL_THERMAL_MITIGATION_MODE_2, + HAL_THERMAL_MITIGATION_MODE_MAX = WCN36XX_HAL_MAX_ENUM_SIZE, +}; + + +/* + * Thermal Mitigation level. + * Note the levels are incremental i.e WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_2 = + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_0 + + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_1 + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_0 - lowest level of thermal mitigation. + * This level indicates normal mode of operation + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_1 - 1st level of thermal mitigation + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_2 - 2nd level of thermal mitigation + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_3 - 3rd level of thermal mitigation + * + * WCN36XX_HAL_THERMAL_MITIGATION_LEVEL_4 - 4th level of thermal mitigation + */ +enum wcn36xx_hal_thermal_mitigation_level_type { + HAL_THERMAL_MITIGATION_LEVEL_INVALID = -1, + HAL_THERMAL_MITIGATION_LEVEL_0, + HAL_THERMAL_MITIGATION_LEVEL_1, + HAL_THERMAL_MITIGATION_LEVEL_2, + HAL_THERMAL_MITIGATION_LEVEL_3, + HAL_THERMAL_MITIGATION_LEVEL_4, + HAL_THERMAL_MITIGATION_LEVEL_MAX = WCN36XX_HAL_MAX_ENUM_SIZE, +}; + + +/* WCN36XX_HAL_SET_THERMAL_MITIGATION_REQ */ +struct set_thermal_mitigation_req_msg { + struct wcn36xx_hal_msg_header header; + + /* Thermal Mitigation Operation Mode */ + enum wcn36xx_hal_thermal_mitigation_mode_type mode; + + /* Thermal Mitigation Level */ + enum wcn36xx_hal_thermal_mitigation_level_type level; +}; + +struct set_thermal_mitigation_resp { + + struct wcn36xx_hal_msg_header header; + + /* status of the request */ + u32 status; +}; + +/* Per STA Class B Statistics. Class B statistics are STA TX/RX stats + * provided to FW from Host via periodic messages */ +struct stats_class_b_ind { + struct wcn36xx_hal_msg_header header; + + /* Duration over which this stats was collected */ + u32 duration; + + /* Per STA Stats */ + + /* TX stats */ + u32 tx_bytes_pushed; + u32 tx_packets_pushed; + + /* RX stats */ + u32 rx_bytes_rcvd; + u32 rx_packets_rcvd; + u32 rx_time_total; +}; + +#endif /* _HAL_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c new file mode 100644 index 0000000000000000000000000000000000000000..7839b31e4826e5013a391b94f405483f6c4e977f --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -0,0 +1,1036 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include "wcn36xx.h" + +unsigned int wcn36xx_dbg_mask; +module_param_named(debug_mask, wcn36xx_dbg_mask, uint, 0644); +MODULE_PARM_DESC(debug_mask, "Debugging mask"); + +#define CHAN2G(_freq, _idx) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 25, \ +} + +#define CHAN5G(_freq, _idx) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 25, \ +} + +/* The wcn firmware expects channel values to matching + * their mnemonic values. So use these for .hw_value. */ +static struct ieee80211_channel wcn_2ghz_channels[] = { + CHAN2G(2412, 1), /* Channel 1 */ + CHAN2G(2417, 2), /* Channel 2 */ + CHAN2G(2422, 3), /* Channel 3 */ + CHAN2G(2427, 4), /* Channel 4 */ + CHAN2G(2432, 5), /* Channel 5 */ + CHAN2G(2437, 6), /* Channel 6 */ + CHAN2G(2442, 7), /* Channel 7 */ + CHAN2G(2447, 8), /* Channel 8 */ + CHAN2G(2452, 9), /* Channel 9 */ + CHAN2G(2457, 10), /* Channel 10 */ + CHAN2G(2462, 11), /* Channel 11 */ + CHAN2G(2467, 12), /* Channel 12 */ + CHAN2G(2472, 13), /* Channel 13 */ + CHAN2G(2484, 14) /* Channel 14 */ + +}; + +static struct ieee80211_channel wcn_5ghz_channels[] = { + CHAN5G(5180, 36), + CHAN5G(5200, 40), + CHAN5G(5220, 44), + CHAN5G(5240, 48), + CHAN5G(5260, 52), + CHAN5G(5280, 56), + CHAN5G(5300, 60), + CHAN5G(5320, 64), + CHAN5G(5500, 100), + CHAN5G(5520, 104), + CHAN5G(5540, 108), + CHAN5G(5560, 112), + CHAN5G(5580, 116), + CHAN5G(5600, 120), + CHAN5G(5620, 124), + CHAN5G(5640, 128), + CHAN5G(5660, 132), + CHAN5G(5700, 140), + CHAN5G(5745, 149), + CHAN5G(5765, 153), + CHAN5G(5785, 157), + CHAN5G(5805, 161), + CHAN5G(5825, 165) +}; + +#define RATE(_bitrate, _hw_rate, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate), \ + .hw_value_short = (_hw_rate) \ +} + +static struct ieee80211_rate wcn_2ghz_rates[] = { + RATE(10, HW_RATE_INDEX_1MBPS, 0), + RATE(20, HW_RATE_INDEX_2MBPS, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, HW_RATE_INDEX_5_5MBPS, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, HW_RATE_INDEX_11MBPS, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, HW_RATE_INDEX_6MBPS, 0), + RATE(90, HW_RATE_INDEX_9MBPS, 0), + RATE(120, HW_RATE_INDEX_12MBPS, 0), + RATE(180, HW_RATE_INDEX_18MBPS, 0), + RATE(240, HW_RATE_INDEX_24MBPS, 0), + RATE(360, HW_RATE_INDEX_36MBPS, 0), + RATE(480, HW_RATE_INDEX_48MBPS, 0), + RATE(540, HW_RATE_INDEX_54MBPS, 0) +}; + +static struct ieee80211_rate wcn_5ghz_rates[] = { + RATE(60, HW_RATE_INDEX_6MBPS, 0), + RATE(90, HW_RATE_INDEX_9MBPS, 0), + RATE(120, HW_RATE_INDEX_12MBPS, 0), + RATE(180, HW_RATE_INDEX_18MBPS, 0), + RATE(240, HW_RATE_INDEX_24MBPS, 0), + RATE(360, HW_RATE_INDEX_36MBPS, 0), + RATE(480, HW_RATE_INDEX_48MBPS, 0), + RATE(540, HW_RATE_INDEX_54MBPS, 0) +}; + +static struct ieee80211_supported_band wcn_band_2ghz = { + .channels = wcn_2ghz_channels, + .n_channels = ARRAY_SIZE(wcn_2ghz_channels), + .bitrates = wcn_2ghz_rates, + .n_bitrates = ARRAY_SIZE(wcn_2ghz_rates), + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_DSSSCCK40 | + IEEE80211_HT_CAP_LSIG_TXOP_PROT, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .mcs = { + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + .rx_highest = cpu_to_le16(72), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + } + } +}; + +static struct ieee80211_supported_band wcn_band_5ghz = { + .channels = wcn_5ghz_channels, + .n_channels = ARRAY_SIZE(wcn_5ghz_channels), + .bitrates = wcn_5ghz_rates, + .n_bitrates = ARRAY_SIZE(wcn_5ghz_rates), + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + IEEE80211_HT_CAP_SGI_20 | + IEEE80211_HT_CAP_DSSSCCK40 | + IEEE80211_HT_CAP_LSIG_TXOP_PROT | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_SUP_WIDTH_20_40, + .ht_supported = true, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_16, + .mcs = { + .rx_mask = { 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, }, + .rx_highest = cpu_to_le16(72), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + } + } +}; + +#ifdef CONFIG_PM + +static const struct wiphy_wowlan_support wowlan_support = { + .flags = WIPHY_WOWLAN_ANY +}; + +#endif + +static inline u8 get_sta_index(struct ieee80211_vif *vif, + struct wcn36xx_sta *sta_priv) +{ + return NL80211_IFTYPE_STATION == vif->type ? + sta_priv->bss_sta_index : + sta_priv->sta_index; +} + +static int wcn36xx_start(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + int ret; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac start\n"); + + /* SMD initialization */ + ret = wcn36xx_smd_open(wcn); + if (ret) { + wcn36xx_err("Failed to open smd channel: %d\n", ret); + goto out_err; + } + + /* Allocate memory pools for Mgmt BD headers and Data BD headers */ + ret = wcn36xx_dxe_allocate_mem_pools(wcn); + if (ret) { + wcn36xx_err("Failed to alloc DXE mempool: %d\n", ret); + goto out_smd_close; + } + + ret = wcn36xx_dxe_alloc_ctl_blks(wcn); + if (ret) { + wcn36xx_err("Failed to alloc DXE ctl blocks: %d\n", ret); + goto out_free_dxe_pool; + } + + wcn->hal_buf = kmalloc(WCN36XX_HAL_BUF_SIZE, GFP_KERNEL); + if (!wcn->hal_buf) { + wcn36xx_err("Failed to allocate smd buf\n"); + ret = -ENOMEM; + goto out_free_dxe_ctl; + } + + ret = wcn36xx_smd_load_nv(wcn); + if (ret) { + wcn36xx_err("Failed to push NV to chip\n"); + goto out_free_smd_buf; + } + + ret = wcn36xx_smd_start(wcn); + if (ret) { + wcn36xx_err("Failed to start chip\n"); + goto out_free_smd_buf; + } + + /* DMA channel initialization */ + ret = wcn36xx_dxe_init(wcn); + if (ret) { + wcn36xx_err("DXE init failed\n"); + goto out_smd_stop; + } + + wcn36xx_debugfs_init(wcn); + + if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { + ret = wcn36xx_smd_feature_caps_exchange(wcn); + if (ret) + wcn36xx_warn("Exchange feature caps failed\n"); + } + INIT_LIST_HEAD(&wcn->vif_list); + return 0; + +out_smd_stop: + wcn36xx_smd_stop(wcn); +out_free_smd_buf: + kfree(wcn->hal_buf); +out_free_dxe_pool: + wcn36xx_dxe_free_mem_pools(wcn); +out_free_dxe_ctl: + wcn36xx_dxe_free_ctl_blks(wcn); +out_smd_close: + wcn36xx_smd_close(wcn); +out_err: + return ret; +} + +static void wcn36xx_stop(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac stop\n"); + + wcn36xx_debugfs_exit(wcn); + wcn36xx_smd_stop(wcn); + wcn36xx_dxe_deinit(wcn); + wcn36xx_smd_close(wcn); + + wcn36xx_dxe_free_mem_pools(wcn); + wcn36xx_dxe_free_ctl_blks(wcn); + + kfree(wcn->hal_buf); +} + +static int wcn36xx_config(struct ieee80211_hw *hw, u32 changed) +{ + struct wcn36xx *wcn = hw->priv; + struct ieee80211_vif *vif = NULL; + struct wcn36xx_vif *tmp; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac config changed 0x%08x\n", changed); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + int ch = WCN36XX_HW_CHANNEL(wcn); + wcn36xx_dbg(WCN36XX_DBG_MAC, "wcn36xx_config channel switch=%d\n", + ch); + list_for_each_entry(tmp, &wcn->vif_list, list) { + vif = container_of((void *)tmp, + struct ieee80211_vif, + drv_priv); + wcn36xx_smd_switch_channel(wcn, vif, ch); + } + } + + return 0; +} + +#define WCN36XX_SUPPORTED_FILTERS (0) + +static void wcn36xx_configure_filter(struct ieee80211_hw *hw, + unsigned int changed, + unsigned int *total, u64 multicast) +{ + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac configure filter\n"); + + *total &= WCN36XX_SUPPORTED_FILTERS; +} + +static void wcn36xx_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, + struct sk_buff *skb) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_sta *sta_priv = NULL; + + if (control->sta) + sta_priv = (struct wcn36xx_sta *)control->sta->drv_priv; + + if (wcn36xx_start_tx(wcn, sta_priv, skb)) + ieee80211_free_txskb(wcn->hw, skb); +} + +static int wcn36xx_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key_conf) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + struct wcn36xx_sta *sta_priv = vif_priv->sta; + int ret = 0; + u8 key[WLAN_MAX_KEY_LEN]; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac80211 set key\n"); + wcn36xx_dbg(WCN36XX_DBG_MAC, "Key: cmd=0x%x algo:0x%x, id:%d, len:%d flags 0x%x\n", + cmd, key_conf->cipher, key_conf->keyidx, + key_conf->keylen, key_conf->flags); + wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "KEY: ", + key_conf->key, + key_conf->keylen); + + switch (key_conf->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; + break; + case WLAN_CIPHER_SUITE_WEP104: + vif_priv->encrypt_type = WCN36XX_HAL_ED_WEP40; + break; + case WLAN_CIPHER_SUITE_CCMP: + vif_priv->encrypt_type = WCN36XX_HAL_ED_CCMP; + break; + case WLAN_CIPHER_SUITE_TKIP: + vif_priv->encrypt_type = WCN36XX_HAL_ED_TKIP; + break; + default: + wcn36xx_err("Unsupported key type 0x%x\n", + key_conf->cipher); + ret = -EOPNOTSUPP; + goto out; + } + + switch (cmd) { + case SET_KEY: + if (WCN36XX_HAL_ED_TKIP == vif_priv->encrypt_type) { + /* + * Supplicant is sending key in the wrong order: + * Temporal Key (16 b) - TX MIC (8 b) - RX MIC (8 b) + * but HW expects it to be in the order as described in + * IEEE 802.11 spec (see chapter 11.7) like this: + * Temporal Key (16 b) - RX MIC (8 b) - TX MIC (8 b) + */ + memcpy(key, key_conf->key, 16); + memcpy(key + 16, key_conf->key + 24, 8); + memcpy(key + 24, key_conf->key + 16, 8); + } else { + memcpy(key, key_conf->key, key_conf->keylen); + } + + if (IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags) { + sta_priv->is_data_encrypted = true; + /* Reconfigure bss with encrypt_type */ + if (NL80211_IFTYPE_STATION == vif->type) + wcn36xx_smd_config_bss(wcn, + vif, + sta, + sta->addr, + true); + + wcn36xx_smd_set_stakey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx, + key_conf->keylen, + key, + get_sta_index(vif, sta_priv)); + } else { + wcn36xx_smd_set_bsskey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx, + key_conf->keylen, + key); + if ((WLAN_CIPHER_SUITE_WEP40 == key_conf->cipher) || + (WLAN_CIPHER_SUITE_WEP104 == key_conf->cipher)) { + sta_priv->is_data_encrypted = true; + wcn36xx_smd_set_stakey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx, + key_conf->keylen, + key, + get_sta_index(vif, sta_priv)); + } + } + break; + case DISABLE_KEY: + if (!(IEEE80211_KEY_FLAG_PAIRWISE & key_conf->flags)) { + wcn36xx_smd_remove_bsskey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx); + } else { + sta_priv->is_data_encrypted = false; + /* do not remove key if disassociated */ + if (sta_priv->aid) + wcn36xx_smd_remove_stakey(wcn, + vif_priv->encrypt_type, + key_conf->keyidx, + get_sta_index(vif, sta_priv)); + } + break; + default: + wcn36xx_err("Unsupported key cmd 0x%x\n", cmd); + ret = -EOPNOTSUPP; + goto out; + break; + } + +out: + return ret; +} + +static void wcn36xx_sw_scan_start(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_smd_init_scan(wcn, HAL_SYS_MODE_SCAN); + wcn36xx_smd_start_scan(wcn); +} + +static void wcn36xx_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_smd_end_scan(wcn); + wcn36xx_smd_finish_scan(wcn, HAL_SYS_MODE_SCAN); +} + +static void wcn36xx_update_allowed_rates(struct ieee80211_sta *sta, + enum ieee80211_band band) +{ + int i, size; + u16 *rates_table; + struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + u32 rates = sta->supp_rates[band]; + + memset(&sta_priv->supported_rates, 0, + sizeof(sta_priv->supported_rates)); + sta_priv->supported_rates.op_rate_mode = STA_11n; + + size = ARRAY_SIZE(sta_priv->supported_rates.dsss_rates); + rates_table = sta_priv->supported_rates.dsss_rates; + if (band == IEEE80211_BAND_2GHZ) { + for (i = 0; i < size; i++) { + if (rates & 0x01) { + rates_table[i] = wcn_2ghz_rates[i].hw_value; + rates = rates >> 1; + } + } + } + + size = ARRAY_SIZE(sta_priv->supported_rates.ofdm_rates); + rates_table = sta_priv->supported_rates.ofdm_rates; + for (i = 0; i < size; i++) { + if (rates & 0x01) { + rates_table[i] = wcn_5ghz_rates[i].hw_value; + rates = rates >> 1; + } + } + + if (sta->ht_cap.ht_supported) { + BUILD_BUG_ON(sizeof(sta->ht_cap.mcs.rx_mask) > + sizeof(sta_priv->supported_rates.supported_mcs_set)); + memcpy(sta_priv->supported_rates.supported_mcs_set, + sta->ht_cap.mcs.rx_mask, + sizeof(sta->ht_cap.mcs.rx_mask)); + } +} +void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates) +{ + u16 ofdm_rates[WCN36XX_HAL_NUM_OFDM_RATES] = { + HW_RATE_INDEX_6MBPS, + HW_RATE_INDEX_9MBPS, + HW_RATE_INDEX_12MBPS, + HW_RATE_INDEX_18MBPS, + HW_RATE_INDEX_24MBPS, + HW_RATE_INDEX_36MBPS, + HW_RATE_INDEX_48MBPS, + HW_RATE_INDEX_54MBPS + }; + u16 dsss_rates[WCN36XX_HAL_NUM_DSSS_RATES] = { + HW_RATE_INDEX_1MBPS, + HW_RATE_INDEX_2MBPS, + HW_RATE_INDEX_5_5MBPS, + HW_RATE_INDEX_11MBPS + }; + + rates->op_rate_mode = STA_11n; + memcpy(rates->dsss_rates, dsss_rates, + sizeof(*dsss_rates) * WCN36XX_HAL_NUM_DSSS_RATES); + memcpy(rates->ofdm_rates, ofdm_rates, + sizeof(*ofdm_rates) * WCN36XX_HAL_NUM_OFDM_RATES); + rates->supported_mcs_set[0] = 0xFF; +} +static void wcn36xx_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct wcn36xx *wcn = hw->priv; + struct sk_buff *skb = NULL; + u16 tim_off, tim_len; + enum wcn36xx_hal_link_state link_state; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss info changed vif %p changed 0x%08x\n", + vif, changed); + + if (changed & BSS_CHANGED_BEACON_INFO) { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac bss changed dtim period %d\n", + bss_conf->dtim_period); + + vif_priv->dtim_period = bss_conf->dtim_period; + } + + if (changed & BSS_CHANGED_PS) { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac bss PS set %d\n", + bss_conf->ps); + if (bss_conf->ps) { + wcn36xx_pmc_enter_bmps_state(wcn, vif); + } else { + wcn36xx_pmc_exit_bmps_state(wcn, vif); + } + } + + if (changed & BSS_CHANGED_BSSID) { + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed_bssid %pM\n", + bss_conf->bssid); + + if (!is_zero_ether_addr(bss_conf->bssid)) { + vif_priv->is_joining = true; + vif_priv->bss_index = 0xff; + wcn36xx_smd_join(wcn, bss_conf->bssid, + vif->addr, WCN36XX_HW_CHANNEL(wcn)); + wcn36xx_smd_config_bss(wcn, vif, NULL, + bss_conf->bssid, false); + } else { + vif_priv->is_joining = false; + wcn36xx_smd_delete_bss(wcn, vif); + } + } + + if (changed & BSS_CHANGED_SSID) { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac bss changed ssid\n"); + wcn36xx_dbg_dump(WCN36XX_DBG_MAC, "ssid ", + bss_conf->ssid, bss_conf->ssid_len); + + vif_priv->ssid.length = bss_conf->ssid_len; + memcpy(&vif_priv->ssid.ssid, + bss_conf->ssid, + bss_conf->ssid_len); + } + + if (changed & BSS_CHANGED_ASSOC) { + vif_priv->is_joining = false; + if (bss_conf->assoc) { + struct ieee80211_sta *sta; + struct wcn36xx_sta *sta_priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac assoc bss %pM vif %pM AID=%d\n", + bss_conf->bssid, + vif->addr, + bss_conf->aid); + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (!sta) { + wcn36xx_err("sta %pM is not found\n", + bss_conf->bssid); + rcu_read_unlock(); + goto out; + } + sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + + wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); + + wcn36xx_smd_set_link_st(wcn, bss_conf->bssid, + vif->addr, + WCN36XX_HAL_LINK_POSTASSOC_STATE); + wcn36xx_smd_config_bss(wcn, vif, sta, + bss_conf->bssid, + true); + sta_priv->aid = bss_conf->aid; + /* + * config_sta must be called from because this is the + * place where AID is available. + */ + wcn36xx_smd_config_sta(wcn, vif, sta); + rcu_read_unlock(); + } else { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "disassociated bss %pM vif %pM AID=%d\n", + bss_conf->bssid, + vif->addr, + bss_conf->aid); + wcn36xx_smd_set_link_st(wcn, + bss_conf->bssid, + vif->addr, + WCN36XX_HAL_LINK_IDLE_STATE); + } + } + + if (changed & BSS_CHANGED_AP_PROBE_RESP) { + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac bss changed ap probe resp\n"); + skb = ieee80211_proberesp_get(hw, vif); + if (!skb) { + wcn36xx_err("failed to alloc probereq skb\n"); + goto out; + } + + wcn36xx_smd_update_proberesp_tmpl(wcn, vif, skb); + dev_kfree_skb(skb); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + wcn36xx_dbg(WCN36XX_DBG_MAC, + "mac bss changed beacon enabled %d\n", + bss_conf->enable_beacon); + + if (bss_conf->enable_beacon) { + vif_priv->bss_index = 0xff; + wcn36xx_smd_config_bss(wcn, vif, NULL, + vif->addr, false); + skb = ieee80211_beacon_get_tim(hw, vif, &tim_off, + &tim_len); + if (!skb) { + wcn36xx_err("failed to alloc beacon skb\n"); + goto out; + } + wcn36xx_smd_send_beacon(wcn, vif, skb, tim_off, 0); + dev_kfree_skb(skb); + + if (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) + link_state = WCN36XX_HAL_LINK_IBSS_STATE; + else + link_state = WCN36XX_HAL_LINK_AP_STATE; + + wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, + link_state); + } else { + wcn36xx_smd_set_link_st(wcn, vif->addr, vif->addr, + WCN36XX_HAL_LINK_IDLE_STATE); + wcn36xx_smd_delete_bss(wcn, vif); + } + } +out: + return; +} + +/* this is required when using IEEE80211_HW_HAS_RATE_CONTROL */ +static int wcn36xx_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct wcn36xx *wcn = hw->priv; + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac set RTS threshold %d\n", value); + + wcn36xx_smd_update_cfg(wcn, WCN36XX_HAL_CFG_RTS_THRESHOLD, value); + return 0; +} + +static void wcn36xx_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac remove interface vif %p\n", vif); + + list_del(&vif_priv->list); + wcn36xx_smd_delete_sta_self(wcn, vif->addr); +} + +static int wcn36xx_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac add interface vif %p type %d\n", + vif, vif->type); + + if (!(NL80211_IFTYPE_STATION == vif->type || + NL80211_IFTYPE_AP == vif->type || + NL80211_IFTYPE_ADHOC == vif->type || + NL80211_IFTYPE_MESH_POINT == vif->type)) { + wcn36xx_warn("Unsupported interface type requested: %d\n", + vif->type); + return -EOPNOTSUPP; + } + + list_add(&vif_priv->list, &wcn->vif_list); + wcn36xx_smd_add_sta_self(wcn, vif); + + return 0; +} + +static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n", + vif, sta->addr); + + vif_priv->sta = sta_priv; + sta_priv->vif = vif_priv; + /* + * For STA mode HW will be configured on BSS_CHANGED_ASSOC because + * at this stage AID is not available yet. + */ + if (NL80211_IFTYPE_STATION != vif->type) { + wcn36xx_update_allowed_rates(sta, WCN36XX_BAND(wcn)); + sta_priv->aid = sta->aid; + wcn36xx_smd_config_sta(wcn, vif, sta); + } + return 0; +} + +static int wcn36xx_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta remove vif %p sta %pM index %d\n", + vif, sta->addr, sta_priv->sta_index); + + wcn36xx_smd_delete_sta(wcn, sta_priv->sta_index); + vif_priv->sta = NULL; + sta_priv->vif = NULL; + return 0; +} + +#ifdef CONFIG_PM + +static int wcn36xx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wow) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac suspend\n"); + + flush_workqueue(wcn->hal_ind_wq); + wcn36xx_smd_set_power_params(wcn, true); + return 0; +} + +static int wcn36xx_resume(struct ieee80211_hw *hw) +{ + struct wcn36xx *wcn = hw->priv; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac resume\n"); + + flush_workqueue(wcn->hal_ind_wq); + wcn36xx_smd_set_power_params(wcn, false); + return 0; +} + +#endif + +static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + struct wcn36xx *wcn = hw->priv; + struct wcn36xx_sta *sta_priv = NULL; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "mac ampdu action action %d tid %d\n", + action, tid); + + sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + sta_priv->tid = tid; + wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 0, + get_sta_index(vif, sta_priv)); + wcn36xx_smd_add_ba(wcn); + wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv)); + ieee80211_start_tx_ba_session(sta, tid, 0); + break; + case IEEE80211_AMPDU_RX_STOP: + wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv)); + break; + case IEEE80211_AMPDU_TX_START: + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1, + get_sta_index(vif, sta_priv)); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + case IEEE80211_AMPDU_TX_STOP_CONT: + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + break; + default: + wcn36xx_err("Unknown AMPDU action\n"); + } + + return 0; +} + +static const struct ieee80211_ops wcn36xx_ops = { + .start = wcn36xx_start, + .stop = wcn36xx_stop, + .add_interface = wcn36xx_add_interface, + .remove_interface = wcn36xx_remove_interface, +#ifdef CONFIG_PM + .suspend = wcn36xx_suspend, + .resume = wcn36xx_resume, +#endif + .config = wcn36xx_config, + .configure_filter = wcn36xx_configure_filter, + .tx = wcn36xx_tx, + .set_key = wcn36xx_set_key, + .sw_scan_start = wcn36xx_sw_scan_start, + .sw_scan_complete = wcn36xx_sw_scan_complete, + .bss_info_changed = wcn36xx_bss_info_changed, + .set_rts_threshold = wcn36xx_set_rts_threshold, + .sta_add = wcn36xx_sta_add, + .sta_remove = wcn36xx_sta_remove, + .ampdu_action = wcn36xx_ampdu_action, +}; + +static int wcn36xx_init_ieee80211(struct wcn36xx *wcn) +{ + int ret = 0; + + static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + }; + + wcn->hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_HAS_RATE_CONTROL | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_CONNECTION_MONITOR | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_TIMING_BEACON_ONLY; + + wcn->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + + wcn->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wcn_band_2ghz; + wcn->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wcn_band_5ghz; + + wcn->hw->wiphy->cipher_suites = cipher_suites; + wcn->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + + wcn->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + +#ifdef CONFIG_PM + wcn->hw->wiphy->wowlan = &wowlan_support; +#endif + + wcn->hw->max_listen_interval = 200; + + wcn->hw->queues = 4; + + SET_IEEE80211_DEV(wcn->hw, wcn->dev); + + wcn->hw->sta_data_size = sizeof(struct wcn36xx_sta); + wcn->hw->vif_data_size = sizeof(struct wcn36xx_vif); + + return ret; +} + +static int wcn36xx_platform_get_resources(struct wcn36xx *wcn, + struct platform_device *pdev) +{ + struct resource *res; + /* Set TX IRQ */ + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "wcnss_wlantx_irq"); + if (!res) { + wcn36xx_err("failed to get tx_irq\n"); + return -ENOENT; + } + wcn->tx_irq = res->start; + + /* Set RX IRQ */ + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + "wcnss_wlanrx_irq"); + if (!res) { + wcn36xx_err("failed to get rx_irq\n"); + return -ENOENT; + } + wcn->rx_irq = res->start; + + /* Map the memory */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "wcnss_mmio"); + if (!res) { + wcn36xx_err("failed to get mmio\n"); + return -ENOENT; + } + wcn->mmio = ioremap(res->start, resource_size(res)); + if (!wcn->mmio) { + wcn36xx_err("failed to map io memory\n"); + return -ENOMEM; + } + return 0; +} + +static int wcn36xx_probe(struct platform_device *pdev) +{ + struct ieee80211_hw *hw; + struct wcn36xx *wcn; + int ret; + u8 addr[ETH_ALEN]; + + wcn36xx_dbg(WCN36XX_DBG_MAC, "platform probe\n"); + + hw = ieee80211_alloc_hw(sizeof(struct wcn36xx), &wcn36xx_ops); + if (!hw) { + wcn36xx_err("failed to alloc hw\n"); + ret = -ENOMEM; + goto out_err; + } + platform_set_drvdata(pdev, hw); + wcn = hw->priv; + wcn->hw = hw; + wcn->dev = &pdev->dev; + wcn->ctrl_ops = pdev->dev.platform_data; + + mutex_init(&wcn->hal_mutex); + + if (!wcn->ctrl_ops->get_hw_mac(addr)) { + wcn36xx_info("mac address: %pM\n", addr); + SET_IEEE80211_PERM_ADDR(wcn->hw, addr); + } + + ret = wcn36xx_platform_get_resources(wcn, pdev); + if (ret) + goto out_wq; + + wcn36xx_init_ieee80211(wcn); + ret = ieee80211_register_hw(wcn->hw); + if (ret) + goto out_unmap; + + return 0; + +out_unmap: + iounmap(wcn->mmio); +out_wq: + ieee80211_free_hw(hw); +out_err: + return ret; +} +static int wcn36xx_remove(struct platform_device *pdev) +{ + struct ieee80211_hw *hw = platform_get_drvdata(pdev); + struct wcn36xx *wcn = hw->priv; + wcn36xx_dbg(WCN36XX_DBG_MAC, "platform remove\n"); + + mutex_destroy(&wcn->hal_mutex); + + ieee80211_unregister_hw(hw); + iounmap(wcn->mmio); + ieee80211_free_hw(hw); + + return 0; +} +static const struct platform_device_id wcn36xx_platform_id_table[] = { + { + .name = "wcn36xx", + .driver_data = 0 + }, + {} +}; +MODULE_DEVICE_TABLE(platform, wcn36xx_platform_id_table); + +static struct platform_driver wcn36xx_driver = { + .probe = wcn36xx_probe, + .remove = wcn36xx_remove, + .driver = { + .name = "wcn36xx", + .owner = THIS_MODULE, + }, + .id_table = wcn36xx_platform_id_table, +}; + +static int __init wcn36xx_init(void) +{ + platform_driver_register(&wcn36xx_driver); + return 0; +} +module_init(wcn36xx_init); + +static void __exit wcn36xx_exit(void) +{ + platform_driver_unregister(&wcn36xx_driver); +} +module_exit(wcn36xx_exit); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Eugene Krasnikov k.eugene.e@gmail.com"); +MODULE_FIRMWARE(WLAN_NV_FILE); diff --git a/drivers/net/wireless/ath/wcn36xx/pmc.c b/drivers/net/wireless/ath/wcn36xx/pmc.c new file mode 100644 index 0000000000000000000000000000000000000000..28b515c81b0e25ee4fcf019961d812ca9b5486d5 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/pmc.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "wcn36xx.h" + +int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn, + struct ieee80211_vif *vif) +{ + int ret = 0; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + /* TODO: Make sure the TX chain clean */ + ret = wcn36xx_smd_enter_bmps(wcn, vif); + if (!ret) { + wcn36xx_dbg(WCN36XX_DBG_PMC, "Entered BMPS\n"); + vif_priv->pw_state = WCN36XX_BMPS; + } else { + /* + * One of the reasons why HW will not enter BMPS is because + * driver is trying to enter bmps before first beacon was + * received just after auth complete + */ + wcn36xx_err("Can not enter BMPS!\n"); + } + return ret; +} + +int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn, + struct ieee80211_vif *vif) +{ + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + + if (WCN36XX_BMPS != vif_priv->pw_state) { + wcn36xx_err("Not in BMPS mode, no need to exit from BMPS mode!\n"); + return -EINVAL; + } + wcn36xx_smd_exit_bmps(wcn, vif); + vif_priv->pw_state = WCN36XX_FULL_POWER; + return 0; +} + +int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn, + struct ieee80211_vif *vif) +{ + wcn36xx_dbg(WCN36XX_DBG_PMC, "%s\n", __func__); + return wcn36xx_smd_keep_alive_req(wcn, vif, + WCN36XX_HAL_KEEP_ALIVE_NULL_PKT); +} diff --git a/drivers/net/wireless/ath/wcn36xx/pmc.h b/drivers/net/wireless/ath/wcn36xx/pmc.h new file mode 100644 index 0000000000000000000000000000000000000000..f72ed68b5a07ea646a3aa9f30d28149662d91bcc --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/pmc.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WCN36XX_PMC_H_ +#define _WCN36XX_PMC_H_ + +struct wcn36xx; + +enum wcn36xx_power_state { + WCN36XX_FULL_POWER, + WCN36XX_BMPS +}; + +int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn, + struct ieee80211_vif *vif); +int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn, + struct ieee80211_vif *vif); +int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn, + struct ieee80211_vif *vif); +#endif /* _WCN36XX_PMC_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c new file mode 100644 index 0000000000000000000000000000000000000000..f8c3a10510c22b01eb06d9b51310562cb0c8b7dd --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -0,0 +1,2126 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include "smd.h" + +static int put_cfg_tlv_u32(struct wcn36xx *wcn, size_t *len, u32 id, u32 value) +{ + struct wcn36xx_hal_cfg *entry; + u32 *val; + + if (*len + sizeof(*entry) + sizeof(u32) >= WCN36XX_HAL_BUF_SIZE) { + wcn36xx_err("Not enough room for TLV entry\n"); + return -ENOMEM; + } + + entry = (struct wcn36xx_hal_cfg *) (wcn->hal_buf + *len); + entry->id = id; + entry->len = sizeof(u32); + entry->pad_bytes = 0; + entry->reserve = 0; + + val = (u32 *) (entry + 1); + *val = value; + + *len += sizeof(*entry) + sizeof(u32); + + return 0; +} + +static void wcn36xx_smd_set_bss_nw_type(struct wcn36xx *wcn, + struct ieee80211_sta *sta, + struct wcn36xx_hal_config_bss_params *bss_params) +{ + if (IEEE80211_BAND_5GHZ == WCN36XX_BAND(wcn)) + bss_params->nw_type = WCN36XX_HAL_11A_NW_TYPE; + else if (sta && sta->ht_cap.ht_supported) + bss_params->nw_type = WCN36XX_HAL_11N_NW_TYPE; + else if (sta && (sta->supp_rates[IEEE80211_BAND_2GHZ] & 0x7f)) + bss_params->nw_type = WCN36XX_HAL_11G_NW_TYPE; + else + bss_params->nw_type = WCN36XX_HAL_11B_NW_TYPE; +} + +static inline u8 is_cap_supported(unsigned long caps, unsigned long flag) +{ + return caps & flag ? 1 : 0; +} +static void wcn36xx_smd_set_bss_ht_params(struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct wcn36xx_hal_config_bss_params *bss_params) +{ + if (sta && sta->ht_cap.ht_supported) { + unsigned long caps = sta->ht_cap.cap; + bss_params->ht = sta->ht_cap.ht_supported; + bss_params->tx_channel_width_set = is_cap_supported(caps, + IEEE80211_HT_CAP_SUP_WIDTH_20_40); + bss_params->lsig_tx_op_protection_full_support = + is_cap_supported(caps, + IEEE80211_HT_CAP_LSIG_TXOP_PROT); + + bss_params->ht_oper_mode = vif->bss_conf.ht_operation_mode; + bss_params->lln_non_gf_coexist = + !!(vif->bss_conf.ht_operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + /* IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT */ + bss_params->dual_cts_protection = 0; + /* IEEE80211_HT_OP_MODE_PROTECTION_20MHZ */ + bss_params->ht20_coexist = 0; + } +} + +static void wcn36xx_smd_set_sta_ht_params(struct ieee80211_sta *sta, + struct wcn36xx_hal_config_sta_params *sta_params) +{ + if (sta->ht_cap.ht_supported) { + unsigned long caps = sta->ht_cap.cap; + sta_params->ht_capable = sta->ht_cap.ht_supported; + sta_params->tx_channel_width_set = is_cap_supported(caps, + IEEE80211_HT_CAP_SUP_WIDTH_20_40); + sta_params->lsig_txop_protection = is_cap_supported(caps, + IEEE80211_HT_CAP_LSIG_TXOP_PROT); + + sta_params->max_ampdu_size = sta->ht_cap.ampdu_factor; + sta_params->max_ampdu_density = sta->ht_cap.ampdu_density; + sta_params->max_amsdu_size = is_cap_supported(caps, + IEEE80211_HT_CAP_MAX_AMSDU); + sta_params->sgi_20Mhz = is_cap_supported(caps, + IEEE80211_HT_CAP_SGI_20); + sta_params->sgi_40mhz = is_cap_supported(caps, + IEEE80211_HT_CAP_SGI_40); + sta_params->green_field_capable = is_cap_supported(caps, + IEEE80211_HT_CAP_GRN_FLD); + sta_params->delayed_ba_support = is_cap_supported(caps, + IEEE80211_HT_CAP_DELAY_BA); + sta_params->dsss_cck_mode_40mhz = is_cap_supported(caps, + IEEE80211_HT_CAP_DSSSCCK40); + } +} + +static void wcn36xx_smd_set_sta_params(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct wcn36xx_hal_config_sta_params *sta_params) +{ + struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv; + struct wcn36xx_sta *priv_sta = NULL; + if (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_MESH_POINT) { + sta_params->type = 1; + sta_params->sta_index = 0xFF; + } else { + sta_params->type = 0; + sta_params->sta_index = 1; + } + + sta_params->listen_interval = WCN36XX_LISTEN_INTERVAL(wcn); + + /* + * In STA mode ieee80211_sta contains bssid and ieee80211_vif + * contains our mac address. In AP mode we are bssid so vif + * contains bssid and ieee80211_sta contains mac. + */ + if (NL80211_IFTYPE_STATION == vif->type) + memcpy(&sta_params->mac, vif->addr, ETH_ALEN); + else + memcpy(&sta_params->bssid, vif->addr, ETH_ALEN); + + sta_params->encrypt_type = priv_vif->encrypt_type; + sta_params->short_preamble_supported = + !(WCN36XX_FLAGS(wcn) & + IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE); + + sta_params->rifs_mode = 0; + sta_params->rmf = 0; + sta_params->action = 0; + sta_params->uapsd = 0; + sta_params->mimo_ps = WCN36XX_HAL_HT_MIMO_PS_STATIC; + sta_params->max_ampdu_duration = 0; + sta_params->bssid_index = priv_vif->bss_index; + sta_params->p2p = 0; + + if (sta) { + priv_sta = (struct wcn36xx_sta *)sta->drv_priv; + if (NL80211_IFTYPE_STATION == vif->type) + memcpy(&sta_params->bssid, sta->addr, ETH_ALEN); + else + memcpy(&sta_params->mac, sta->addr, ETH_ALEN); + sta_params->wmm_enabled = sta->wme; + sta_params->max_sp_len = sta->max_sp; + sta_params->aid = priv_sta->aid; + wcn36xx_smd_set_sta_ht_params(sta, sta_params); + memcpy(&sta_params->supported_rates, &priv_sta->supported_rates, + sizeof(priv_sta->supported_rates)); + } else { + wcn36xx_set_default_rates(&sta_params->supported_rates); + } +} + +static int wcn36xx_smd_send_and_wait(struct wcn36xx *wcn, size_t len) +{ + int ret = 0; + wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "HAL >>> ", wcn->hal_buf, len); + + init_completion(&wcn->hal_rsp_compl); + ret = wcn->ctrl_ops->tx(wcn->hal_buf, len); + if (ret) { + wcn36xx_err("HAL TX failed\n"); + goto out; + } + if (wait_for_completion_timeout(&wcn->hal_rsp_compl, + msecs_to_jiffies(HAL_MSG_TIMEOUT)) <= 0) { + wcn36xx_err("Timeout while waiting SMD response\n"); + ret = -ETIME; + goto out; + } +out: + return ret; +} + +#define INIT_HAL_MSG(msg_body, type) \ + do { \ + memset(&msg_body, 0, sizeof(msg_body)); \ + msg_body.header.msg_type = type; \ + msg_body.header.msg_version = WCN36XX_HAL_MSG_VERSION0; \ + msg_body.header.len = sizeof(msg_body); \ + } while (0) \ + +#define PREPARE_HAL_BUF(send_buf, msg_body) \ + do { \ + memset(send_buf, 0, msg_body.header.len); \ + memcpy(send_buf, &msg_body, sizeof(msg_body)); \ + } while (0) \ + +static int wcn36xx_smd_rsp_status_check(void *buf, size_t len) +{ + struct wcn36xx_fw_msg_status_rsp *rsp; + + if (len < sizeof(struct wcn36xx_hal_msg_header) + + sizeof(struct wcn36xx_fw_msg_status_rsp)) + return -EIO; + + rsp = (struct wcn36xx_fw_msg_status_rsp *) + (buf + sizeof(struct wcn36xx_hal_msg_header)); + + if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status) + return rsp->status; + + return 0; +} + +int wcn36xx_smd_load_nv(struct wcn36xx *wcn) +{ + const struct firmware *nv; + struct nv_data *nv_d; + struct wcn36xx_hal_nv_img_download_req_msg msg_body; + int fw_bytes_left; + int ret; + u16 fm_offset = 0; + + ret = request_firmware(&nv, WLAN_NV_FILE, wcn->dev); + if (ret) { + wcn36xx_err("Failed to load nv file %s: %d\n", + WLAN_NV_FILE, ret); + goto out_free_nv; + } + + nv_d = (struct nv_data *)nv->data; + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DOWNLOAD_NV_REQ); + + msg_body.header.len += WCN36XX_NV_FRAGMENT_SIZE; + + msg_body.frag_number = 0; + /* hal_buf must be protected with mutex */ + mutex_lock(&wcn->hal_mutex); + + do { + fw_bytes_left = nv->size - fm_offset - 4; + if (fw_bytes_left > WCN36XX_NV_FRAGMENT_SIZE) { + msg_body.last_fragment = 0; + msg_body.nv_img_buffer_size = WCN36XX_NV_FRAGMENT_SIZE; + } else { + msg_body.last_fragment = 1; + msg_body.nv_img_buffer_size = fw_bytes_left; + + /* Do not forget update general message len */ + msg_body.header.len = sizeof(msg_body) + fw_bytes_left; + + } + + /* Add load NV request message header */ + memcpy(wcn->hal_buf, &msg_body, sizeof(msg_body)); + + /* Add NV body itself */ + memcpy(wcn->hal_buf + sizeof(msg_body), + &nv_d->table + fm_offset, + msg_body.nv_img_buffer_size); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) + goto out_unlock; + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_load_nv response failed err=%d\n", + ret); + goto out_unlock; + } + msg_body.frag_number++; + fm_offset += WCN36XX_NV_FRAGMENT_SIZE; + + } while (msg_body.last_fragment != 1); + +out_unlock: + mutex_unlock(&wcn->hal_mutex); +out_free_nv: + release_firmware(nv); + + return ret; +} + +static int wcn36xx_smd_start_rsp(struct wcn36xx *wcn, void *buf, size_t len) +{ + struct wcn36xx_hal_mac_start_rsp_msg *rsp; + + if (len < sizeof(*rsp)) + return -EIO; + + rsp = (struct wcn36xx_hal_mac_start_rsp_msg *)buf; + + if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->start_rsp_params.status) + return -EIO; + + memcpy(wcn->crm_version, rsp->start_rsp_params.crm_version, + WCN36XX_HAL_VERSION_LENGTH); + memcpy(wcn->wlan_version, rsp->start_rsp_params.wlan_version, + WCN36XX_HAL_VERSION_LENGTH); + + /* null terminate the strings, just in case */ + wcn->crm_version[WCN36XX_HAL_VERSION_LENGTH] = '\0'; + wcn->wlan_version[WCN36XX_HAL_VERSION_LENGTH] = '\0'; + + wcn->fw_revision = rsp->start_rsp_params.version.revision; + wcn->fw_version = rsp->start_rsp_params.version.version; + wcn->fw_minor = rsp->start_rsp_params.version.minor; + wcn->fw_major = rsp->start_rsp_params.version.major; + + wcn36xx_info("firmware WLAN version '%s' and CRM version '%s'\n", + wcn->wlan_version, wcn->crm_version); + + wcn36xx_info("firmware API %u.%u.%u.%u, %u stations, %u bssids\n", + wcn->fw_major, wcn->fw_minor, + wcn->fw_version, wcn->fw_revision, + rsp->start_rsp_params.stations, + rsp->start_rsp_params.bssids); + + return 0; +} + +int wcn36xx_smd_start(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_mac_start_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_REQ); + + msg_body.params.type = DRIVER_TYPE_PRODUCTION; + msg_body.params.len = 0; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal start type %d\n", + msg_body.params.type); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_start failed\n"); + goto out; + } + + ret = wcn36xx_smd_start_rsp(wcn, wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_start response failed err=%d\n", ret); + goto out; + } + +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_stop(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_mac_stop_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_STOP_REQ); + + msg_body.stop_req_params.reason = HAL_STOP_TYPE_RF_KILL; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_stop failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_stop response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode) +{ + struct wcn36xx_hal_init_scan_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_INIT_SCAN_REQ); + + msg_body.mode = mode; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal init scan mode %d\n", msg_body.mode); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_init_scan failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_init_scan response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_start_scan(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_start_scan_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_SCAN_REQ); + + msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal start scan channel %d\n", + msg_body.scan_channel); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_start_scan failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_start_scan response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_end_scan(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_end_scan_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_END_SCAN_REQ); + + msg_body.scan_channel = WCN36XX_HW_CHANNEL(wcn); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal end scan channel %d\n", + msg_body.scan_channel); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_end_scan failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_end_scan response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, + enum wcn36xx_hal_sys_mode mode) +{ + struct wcn36xx_hal_finish_scan_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_FINISH_SCAN_REQ); + + msg_body.mode = mode; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal finish scan mode %d\n", + msg_body.mode); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_finish_scan failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_finish_scan response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_switch_channel_rsp(void *buf, size_t len) +{ + struct wcn36xx_hal_switch_channel_rsp_msg *rsp; + int ret = 0; + + ret = wcn36xx_smd_rsp_status_check(buf, len); + if (ret) + return ret; + rsp = (struct wcn36xx_hal_switch_channel_rsp_msg *)buf; + wcn36xx_dbg(WCN36XX_DBG_HAL, "channel switched to: %d, status: %d\n", + rsp->channel_number, rsp->status); + return ret; +} + +int wcn36xx_smd_switch_channel(struct wcn36xx *wcn, + struct ieee80211_vif *vif, int ch) +{ + struct wcn36xx_hal_switch_channel_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_CH_SWITCH_REQ); + + msg_body.channel_number = (u8)ch; + msg_body.tx_mgmt_power = 0xbf; + msg_body.max_tx_power = 0xbf; + memcpy(msg_body.self_sta_mac_addr, vif->addr, ETH_ALEN); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_switch_channel failed\n"); + goto out; + } + ret = wcn36xx_smd_switch_channel_rsp(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_switch_channel response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_update_scan_params_rsp(void *buf, size_t len) +{ + struct wcn36xx_hal_update_scan_params_resp *rsp; + + rsp = (struct wcn36xx_hal_update_scan_params_resp *)buf; + + /* Remove the PNO version bit */ + rsp->status &= (~(WCN36XX_FW_MSG_PNO_VERSION_MASK)); + + if (WCN36XX_FW_MSG_RESULT_SUCCESS != rsp->status) { + wcn36xx_warn("error response from update scan\n"); + return rsp->status; + } + + return 0; +} + +int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_update_scan_params_req msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_SCAN_PARAM_REQ); + + msg_body.dot11d_enabled = 0; + msg_body.dot11d_resolved = 0; + msg_body.channel_count = 26; + msg_body.active_min_ch_time = 60; + msg_body.active_max_ch_time = 120; + msg_body.passive_min_ch_time = 60; + msg_body.passive_max_ch_time = 110; + msg_body.state = 0; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal update scan params channel_count %d\n", + msg_body.channel_count); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_update_scan_params failed\n"); + goto out; + } + ret = wcn36xx_smd_update_scan_params_rsp(wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_update_scan_params response failed err=%d\n", + ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_add_sta_self_rsp(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + void *buf, + size_t len) +{ + struct wcn36xx_hal_add_sta_self_rsp_msg *rsp; + struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv; + + if (len < sizeof(*rsp)) + return -EINVAL; + + rsp = (struct wcn36xx_hal_add_sta_self_rsp_msg *)buf; + + if (rsp->status != WCN36XX_FW_MSG_RESULT_SUCCESS) { + wcn36xx_warn("hal add sta self failure: %d\n", + rsp->status); + return rsp->status; + } + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal add sta self status %d self_sta_index %d dpu_index %d\n", + rsp->status, rsp->self_sta_index, rsp->dpu_index); + + priv_vif->self_sta_index = rsp->self_sta_index; + priv_vif->self_dpu_desc_index = rsp->dpu_index; + + return 0; +} + +int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif) +{ + struct wcn36xx_hal_add_sta_self_req msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_STA_SELF_REQ); + + memcpy(&msg_body.self_addr, vif->addr, ETH_ALEN); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal add sta self self_addr %pM status %d\n", + msg_body.self_addr, msg_body.status); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_add_sta_self failed\n"); + goto out; + } + ret = wcn36xx_smd_add_sta_self_rsp(wcn, + vif, + wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_add_sta_self response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr) +{ + struct wcn36xx_hal_del_sta_self_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DEL_STA_SELF_REQ); + + memcpy(&msg_body.self_addr, addr, ETH_ALEN); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_delete_sta_self failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_delete_sta_self response failed err=%d\n", + ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index) +{ + struct wcn36xx_hal_delete_sta_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DELETE_STA_REQ); + + msg_body.sta_index = sta_index; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal delete sta sta_index %d\n", + msg_body.sta_index); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_delete_sta failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_delete_sta response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_join_rsp(void *buf, size_t len) +{ + struct wcn36xx_hal_join_rsp_msg *rsp; + + if (wcn36xx_smd_rsp_status_check(buf, len)) + return -EIO; + + rsp = (struct wcn36xx_hal_join_rsp_msg *)buf; + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal rsp join status %d tx_mgmt_power %d\n", + rsp->status, rsp->tx_mgmt_power); + + return 0; +} + +int wcn36xx_smd_join(struct wcn36xx *wcn, const u8 *bssid, u8 *vif, u8 ch) +{ + struct wcn36xx_hal_join_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_JOIN_REQ); + + memcpy(&msg_body.bssid, bssid, ETH_ALEN); + memcpy(&msg_body.self_sta_mac_addr, vif, ETH_ALEN); + msg_body.channel = ch; + + if (conf_is_ht40_minus(&wcn->hw->conf)) + msg_body.secondary_channel_offset = + PHY_DOUBLE_CHANNEL_HIGH_PRIMARY; + else if (conf_is_ht40_plus(&wcn->hw->conf)) + msg_body.secondary_channel_offset = + PHY_DOUBLE_CHANNEL_LOW_PRIMARY; + else + msg_body.secondary_channel_offset = + PHY_SINGLE_CHANNEL_CENTERED; + + msg_body.link_state = WCN36XX_HAL_LINK_PREASSOC_STATE; + + msg_body.max_tx_power = 0xbf; + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal join req bssid %pM self_sta_mac_addr %pM channel %d link_state %d\n", + msg_body.bssid, msg_body.self_sta_mac_addr, + msg_body.channel, msg_body.link_state); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_join failed\n"); + goto out; + } + ret = wcn36xx_smd_join_rsp(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_join response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_set_link_st(struct wcn36xx *wcn, const u8 *bssid, + const u8 *sta_mac, + enum wcn36xx_hal_link_state state) +{ + struct wcn36xx_hal_set_link_state_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_LINK_ST_REQ); + + memcpy(&msg_body.bssid, bssid, ETH_ALEN); + memcpy(&msg_body.self_mac_addr, sta_mac, ETH_ALEN); + msg_body.state = state; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal set link state bssid %pM self_mac_addr %pM state %d\n", + msg_body.bssid, msg_body.self_mac_addr, msg_body.state); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_set_link_st failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_set_link_st response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static void wcn36xx_smd_convert_sta_to_v1(struct wcn36xx *wcn, + const struct wcn36xx_hal_config_sta_params *orig, + struct wcn36xx_hal_config_sta_params_v1 *v1) +{ + /* convert orig to v1 format */ + memcpy(&v1->bssid, orig->bssid, ETH_ALEN); + memcpy(&v1->mac, orig->mac, ETH_ALEN); + v1->aid = orig->aid; + v1->type = orig->type; + v1->listen_interval = orig->listen_interval; + v1->ht_capable = orig->ht_capable; + + v1->max_ampdu_size = orig->max_ampdu_size; + v1->max_ampdu_density = orig->max_ampdu_density; + v1->sgi_40mhz = orig->sgi_40mhz; + v1->sgi_20Mhz = orig->sgi_20Mhz; + + memcpy(&v1->supported_rates, &orig->supported_rates, + sizeof(orig->supported_rates)); + v1->sta_index = orig->sta_index; +} + +static int wcn36xx_smd_config_sta_rsp(struct wcn36xx *wcn, + struct ieee80211_sta *sta, + void *buf, + size_t len) +{ + struct wcn36xx_hal_config_sta_rsp_msg *rsp; + struct config_sta_rsp_params *params; + struct wcn36xx_sta *sta_priv = (struct wcn36xx_sta *)sta->drv_priv; + + if (len < sizeof(*rsp)) + return -EINVAL; + + rsp = (struct wcn36xx_hal_config_sta_rsp_msg *)buf; + params = &rsp->params; + + if (params->status != WCN36XX_FW_MSG_RESULT_SUCCESS) { + wcn36xx_warn("hal config sta response failure: %d\n", + params->status); + return -EIO; + } + + sta_priv->sta_index = params->sta_index; + sta_priv->dpu_desc_index = params->dpu_index; + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config sta rsp status %d sta_index %d bssid_index %d p2p %d\n", + params->status, params->sta_index, params->bssid_index, + params->p2p); + + return 0; +} + +static int wcn36xx_smd_config_sta_v1(struct wcn36xx *wcn, + const struct wcn36xx_hal_config_sta_req_msg *orig) +{ + struct wcn36xx_hal_config_sta_req_msg_v1 msg_body; + struct wcn36xx_hal_config_sta_params_v1 *sta = &msg_body.sta_params; + + INIT_HAL_MSG(msg_body, WCN36XX_HAL_CONFIG_STA_REQ); + + wcn36xx_smd_convert_sta_to_v1(wcn, &orig->sta_params, + &msg_body.sta_params); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config sta v1 action %d sta_index %d bssid_index %d bssid %pM type %d mac %pM aid %d\n", + sta->action, sta->sta_index, sta->bssid_index, + sta->bssid, sta->type, sta->mac, sta->aid); + + return wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); +} + +int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct wcn36xx_hal_config_sta_req_msg msg; + struct wcn36xx_hal_config_sta_params *sta_params; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg, WCN36XX_HAL_CONFIG_STA_REQ); + + sta_params = &msg.sta_params; + + wcn36xx_smd_set_sta_params(wcn, vif, sta, sta_params); + + if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { + ret = wcn36xx_smd_config_sta_v1(wcn, &msg); + } else { + PREPARE_HAL_BUF(wcn->hal_buf, msg); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config sta action %d sta_index %d bssid_index %d bssid %pM type %d mac %pM aid %d\n", + sta_params->action, sta_params->sta_index, + sta_params->bssid_index, sta_params->bssid, + sta_params->type, sta_params->mac, sta_params->aid); + + ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len); + } + if (ret) { + wcn36xx_err("Sending hal_config_sta failed\n"); + goto out; + } + ret = wcn36xx_smd_config_sta_rsp(wcn, + sta, + wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_config_sta response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_config_bss_v1(struct wcn36xx *wcn, + const struct wcn36xx_hal_config_bss_req_msg *orig) +{ + struct wcn36xx_hal_config_bss_req_msg_v1 msg_body; + struct wcn36xx_hal_config_bss_params_v1 *bss = &msg_body.bss_params; + struct wcn36xx_hal_config_sta_params_v1 *sta = &bss->sta; + + INIT_HAL_MSG(msg_body, WCN36XX_HAL_CONFIG_BSS_REQ); + + /* convert orig to v1 */ + memcpy(&msg_body.bss_params.bssid, + &orig->bss_params.bssid, ETH_ALEN); + memcpy(&msg_body.bss_params.self_mac_addr, + &orig->bss_params.self_mac_addr, ETH_ALEN); + + msg_body.bss_params.bss_type = orig->bss_params.bss_type; + msg_body.bss_params.oper_mode = orig->bss_params.oper_mode; + msg_body.bss_params.nw_type = orig->bss_params.nw_type; + + msg_body.bss_params.short_slot_time_supported = + orig->bss_params.short_slot_time_supported; + msg_body.bss_params.lla_coexist = orig->bss_params.lla_coexist; + msg_body.bss_params.llb_coexist = orig->bss_params.llb_coexist; + msg_body.bss_params.llg_coexist = orig->bss_params.llg_coexist; + msg_body.bss_params.ht20_coexist = orig->bss_params.ht20_coexist; + msg_body.bss_params.lln_non_gf_coexist = + orig->bss_params.lln_non_gf_coexist; + + msg_body.bss_params.lsig_tx_op_protection_full_support = + orig->bss_params.lsig_tx_op_protection_full_support; + msg_body.bss_params.rifs_mode = orig->bss_params.rifs_mode; + msg_body.bss_params.beacon_interval = orig->bss_params.beacon_interval; + msg_body.bss_params.dtim_period = orig->bss_params.dtim_period; + msg_body.bss_params.tx_channel_width_set = + orig->bss_params.tx_channel_width_set; + msg_body.bss_params.oper_channel = orig->bss_params.oper_channel; + msg_body.bss_params.ext_channel = orig->bss_params.ext_channel; + + msg_body.bss_params.reserved = orig->bss_params.reserved; + + memcpy(&msg_body.bss_params.ssid, + &orig->bss_params.ssid, + sizeof(orig->bss_params.ssid)); + + msg_body.bss_params.action = orig->bss_params.action; + msg_body.bss_params.rateset = orig->bss_params.rateset; + msg_body.bss_params.ht = orig->bss_params.ht; + msg_body.bss_params.obss_prot_enabled = + orig->bss_params.obss_prot_enabled; + msg_body.bss_params.rmf = orig->bss_params.rmf; + msg_body.bss_params.ht_oper_mode = orig->bss_params.ht_oper_mode; + msg_body.bss_params.dual_cts_protection = + orig->bss_params.dual_cts_protection; + + msg_body.bss_params.max_probe_resp_retry_limit = + orig->bss_params.max_probe_resp_retry_limit; + msg_body.bss_params.hidden_ssid = orig->bss_params.hidden_ssid; + msg_body.bss_params.proxy_probe_resp = + orig->bss_params.proxy_probe_resp; + msg_body.bss_params.edca_params_valid = + orig->bss_params.edca_params_valid; + + memcpy(&msg_body.bss_params.acbe, + &orig->bss_params.acbe, + sizeof(orig->bss_params.acbe)); + memcpy(&msg_body.bss_params.acbk, + &orig->bss_params.acbk, + sizeof(orig->bss_params.acbk)); + memcpy(&msg_body.bss_params.acvi, + &orig->bss_params.acvi, + sizeof(orig->bss_params.acvi)); + memcpy(&msg_body.bss_params.acvo, + &orig->bss_params.acvo, + sizeof(orig->bss_params.acvo)); + + msg_body.bss_params.ext_set_sta_key_param_valid = + orig->bss_params.ext_set_sta_key_param_valid; + + memcpy(&msg_body.bss_params.ext_set_sta_key_param, + &orig->bss_params.ext_set_sta_key_param, + sizeof(orig->bss_params.acvo)); + + msg_body.bss_params.wcn36xx_hal_persona = + orig->bss_params.wcn36xx_hal_persona; + msg_body.bss_params.spectrum_mgt_enable = + orig->bss_params.spectrum_mgt_enable; + msg_body.bss_params.tx_mgmt_power = orig->bss_params.tx_mgmt_power; + msg_body.bss_params.max_tx_power = orig->bss_params.max_tx_power; + + wcn36xx_smd_convert_sta_to_v1(wcn, &orig->bss_params.sta, + &msg_body.bss_params.sta); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config bss v1 bssid %pM self_mac_addr %pM bss_type %d oper_mode %d nw_type %d\n", + bss->bssid, bss->self_mac_addr, bss->bss_type, + bss->oper_mode, bss->nw_type); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "- sta bssid %pM action %d sta_index %d bssid_index %d aid %d type %d mac %pM\n", + sta->bssid, sta->action, sta->sta_index, + sta->bssid_index, sta->aid, sta->type, sta->mac); + + return wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); +} + + +static int wcn36xx_smd_config_bss_rsp(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + void *buf, + size_t len) +{ + struct wcn36xx_hal_config_bss_rsp_msg *rsp; + struct wcn36xx_hal_config_bss_rsp_params *params; + struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv; + + if (len < sizeof(*rsp)) + return -EINVAL; + + rsp = (struct wcn36xx_hal_config_bss_rsp_msg *)buf; + params = &rsp->bss_rsp_params; + + if (params->status != WCN36XX_FW_MSG_RESULT_SUCCESS) { + wcn36xx_warn("hal config bss response failure: %d\n", + params->status); + return -EIO; + } + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config bss rsp status %d bss_idx %d dpu_desc_index %d" + " sta_idx %d self_idx %d bcast_idx %d mac %pM" + " power %d ucast_dpu_signature %d\n", + params->status, params->bss_index, params->dpu_desc_index, + params->bss_sta_index, params->bss_self_sta_index, + params->bss_bcast_sta_idx, params->mac, + params->tx_mgmt_power, params->ucast_dpu_signature); + + priv_vif->bss_index = params->bss_index; + + if (priv_vif->sta) { + priv_vif->sta->bss_sta_index = params->bss_sta_index; + priv_vif->sta->bss_dpu_desc_index = params->dpu_desc_index; + } + + priv_vif->ucast_dpu_signature = params->ucast_dpu_signature; + + return 0; +} + +int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, const u8 *bssid, + bool update) +{ + struct wcn36xx_hal_config_bss_req_msg msg; + struct wcn36xx_hal_config_bss_params *bss; + struct wcn36xx_hal_config_sta_params *sta_params; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg, WCN36XX_HAL_CONFIG_BSS_REQ); + + bss = &msg.bss_params; + sta_params = &bss->sta; + + WARN_ON(is_zero_ether_addr(bssid)); + + memcpy(&bss->bssid, bssid, ETH_ALEN); + + memcpy(bss->self_mac_addr, vif->addr, ETH_ALEN); + + if (vif->type == NL80211_IFTYPE_STATION) { + bss->bss_type = WCN36XX_HAL_INFRASTRUCTURE_MODE; + + /* STA */ + bss->oper_mode = 1; + bss->wcn36xx_hal_persona = WCN36XX_HAL_STA_MODE; + } else if (vif->type == NL80211_IFTYPE_AP) { + bss->bss_type = WCN36XX_HAL_INFRA_AP_MODE; + + /* AP */ + bss->oper_mode = 0; + bss->wcn36xx_hal_persona = WCN36XX_HAL_STA_SAP_MODE; + } else if (vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) { + bss->bss_type = WCN36XX_HAL_IBSS_MODE; + + /* STA */ + bss->oper_mode = 1; + } else { + wcn36xx_warn("Unknown type for bss config: %d\n", vif->type); + } + + if (vif->type == NL80211_IFTYPE_STATION) + wcn36xx_smd_set_bss_nw_type(wcn, sta, bss); + else + bss->nw_type = WCN36XX_HAL_11N_NW_TYPE; + + bss->short_slot_time_supported = vif->bss_conf.use_short_slot; + bss->lla_coexist = 0; + bss->llb_coexist = 0; + bss->llg_coexist = 0; + bss->rifs_mode = 0; + bss->beacon_interval = vif->bss_conf.beacon_int; + bss->dtim_period = vif_priv->dtim_period; + + wcn36xx_smd_set_bss_ht_params(vif, sta, bss); + + bss->oper_channel = WCN36XX_HW_CHANNEL(wcn); + + if (conf_is_ht40_minus(&wcn->hw->conf)) + bss->ext_channel = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + else if (conf_is_ht40_plus(&wcn->hw->conf)) + bss->ext_channel = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + else + bss->ext_channel = IEEE80211_HT_PARAM_CHA_SEC_NONE; + + bss->reserved = 0; + wcn36xx_smd_set_sta_params(wcn, vif, sta, sta_params); + + /* wcn->ssid is only valid in AP and IBSS mode */ + bss->ssid.length = vif_priv->ssid.length; + memcpy(bss->ssid.ssid, vif_priv->ssid.ssid, vif_priv->ssid.length); + + bss->obss_prot_enabled = 0; + bss->rmf = 0; + bss->max_probe_resp_retry_limit = 0; + bss->hidden_ssid = vif->bss_conf.hidden_ssid; + bss->proxy_probe_resp = 0; + bss->edca_params_valid = 0; + + /* FIXME: set acbe, acbk, acvi and acvo */ + + bss->ext_set_sta_key_param_valid = 0; + + /* FIXME: set ext_set_sta_key_param */ + + bss->spectrum_mgt_enable = 0; + bss->tx_mgmt_power = 0; + bss->max_tx_power = WCN36XX_MAX_POWER(wcn); + + bss->action = update; + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal config bss bssid %pM self_mac_addr %pM bss_type %d oper_mode %d nw_type %d\n", + bss->bssid, bss->self_mac_addr, bss->bss_type, + bss->oper_mode, bss->nw_type); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "- sta bssid %pM action %d sta_index %d bssid_index %d aid %d type %d mac %pM\n", + sta_params->bssid, sta_params->action, + sta_params->sta_index, sta_params->bssid_index, + sta_params->aid, sta_params->type, + sta_params->mac); + + if (!wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { + ret = wcn36xx_smd_config_bss_v1(wcn, &msg); + } else { + PREPARE_HAL_BUF(wcn->hal_buf, msg); + + ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len); + } + if (ret) { + wcn36xx_err("Sending hal_config_bss failed\n"); + goto out; + } + ret = wcn36xx_smd_config_bss_rsp(wcn, + vif, + wcn->hal_buf, + wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_config_bss response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif) +{ + struct wcn36xx_hal_delete_bss_req_msg msg_body; + struct wcn36xx_vif *priv_vif = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DELETE_BSS_REQ); + + msg_body.bss_index = priv_vif->bss_index; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal delete bss %d\n", msg_body.bss_index); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_delete_bss failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_delete_bss response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct sk_buff *skb_beacon, u16 tim_off, + u16 p2p_off) +{ + struct wcn36xx_hal_send_beacon_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SEND_BEACON_REQ); + + /* TODO need to find out why this is needed? */ + msg_body.beacon_length = skb_beacon->len + 6; + + if (BEACON_TEMPLATE_SIZE > msg_body.beacon_length) { + memcpy(&msg_body.beacon, &skb_beacon->len, sizeof(u32)); + memcpy(&(msg_body.beacon[4]), skb_beacon->data, + skb_beacon->len); + } else { + wcn36xx_err("Beacon is to big: beacon size=%d\n", + msg_body.beacon_length); + return -ENOMEM; + } + memcpy(msg_body.bssid, vif->addr, ETH_ALEN); + + /* TODO need to find out why this is needed? */ + msg_body.tim_ie_offset = tim_off+4; + msg_body.p2p_ie_offset = p2p_off; + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal send beacon beacon_length %d\n", + msg_body.beacon_length); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_send_beacon failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_send_beacon response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + struct sk_buff *skb) +{ + struct wcn36xx_hal_send_probe_resp_req_msg msg; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg, WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_REQ); + + if (skb->len > BEACON_TEMPLATE_SIZE) { + wcn36xx_warn("probe response template is too big: %d\n", + skb->len); + return -E2BIG; + } + + msg.probe_resp_template_len = skb->len; + memcpy(&msg.probe_resp_template, skb->data, skb->len); + + memcpy(msg.bssid, vif->addr, ETH_ALEN); + + PREPARE_HAL_BUF(wcn->hal_buf, msg); + + wcn36xx_dbg(WCN36XX_DBG_HAL, + "hal update probe rsp len %d bssid %pM\n", + msg.probe_resp_template_len, msg.bssid); + + ret = wcn36xx_smd_send_and_wait(wcn, msg.header.len); + if (ret) { + wcn36xx_err("Sending hal_update_proberesp_tmpl failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_update_proberesp_tmpl response failed err=%d\n", + ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_set_stakey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 keylen, + u8 *key, + u8 sta_index) +{ + struct wcn36xx_hal_set_sta_key_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_STAKEY_REQ); + + msg_body.set_sta_key_params.sta_index = sta_index; + msg_body.set_sta_key_params.enc_type = enc_type; + + msg_body.set_sta_key_params.key[0].id = keyidx; + msg_body.set_sta_key_params.key[0].unicast = 1; + msg_body.set_sta_key_params.key[0].direction = WCN36XX_HAL_TX_RX; + msg_body.set_sta_key_params.key[0].pae_role = 0; + msg_body.set_sta_key_params.key[0].length = keylen; + memcpy(msg_body.set_sta_key_params.key[0].key, key, keylen); + msg_body.set_sta_key_params.single_tid_rc = 1; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_set_stakey failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_set_stakey response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 keylen, + u8 *key) +{ + struct wcn36xx_hal_set_bss_key_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_BSSKEY_REQ); + msg_body.bss_idx = 0; + msg_body.enc_type = enc_type; + msg_body.num_keys = 1; + msg_body.keys[0].id = keyidx; + msg_body.keys[0].unicast = 0; + msg_body.keys[0].direction = WCN36XX_HAL_RX_ONLY; + msg_body.keys[0].pae_role = 0; + msg_body.keys[0].length = keylen; + memcpy(msg_body.keys[0].key, key, keylen); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_set_bsskey failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_set_bsskey response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 sta_index) +{ + struct wcn36xx_hal_remove_sta_key_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_RMV_STAKEY_REQ); + + msg_body.sta_idx = sta_index; + msg_body.enc_type = enc_type; + msg_body.key_id = keyidx; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_remove_stakey failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_remove_stakey response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx) +{ + struct wcn36xx_hal_remove_bss_key_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_RMV_BSSKEY_REQ); + msg_body.bss_idx = 0; + msg_body.enc_type = enc_type; + msg_body.key_id = keyidx; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_remove_bsskey failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_remove_bsskey response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif) +{ + struct wcn36xx_hal_enter_bmps_req_msg msg_body; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_ENTER_BMPS_REQ); + + msg_body.bss_index = vif_priv->bss_index; + msg_body.tbtt = vif->bss_conf.sync_tsf; + msg_body.dtim_period = vif_priv->dtim_period; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_enter_bmps failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_enter_bmps response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif) +{ + struct wcn36xx_hal_enter_bmps_req_msg msg_body; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_EXIT_BMPS_REQ); + + msg_body.bss_index = vif_priv->bss_index; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_exit_bmps failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_exit_bmps response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} +int wcn36xx_smd_set_power_params(struct wcn36xx *wcn, bool ignore_dtim) +{ + struct wcn36xx_hal_set_power_params_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_SET_POWER_PARAMS_REQ); + + /* + * When host is down ignore every second dtim + */ + if (ignore_dtim) { + msg_body.ignore_dtim = 1; + msg_body.dtim_period = 2; + } + msg_body.listen_interval = WCN36XX_LISTEN_INTERVAL(wcn); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_set_power_params failed\n"); + goto out; + } + +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} +/* Notice: This function should be called after associated, or else it + * will be invalid + */ +int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + int packet_type) +{ + struct wcn36xx_hal_keep_alive_req_msg msg_body; + struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_KEEP_ALIVE_REQ); + + if (packet_type == WCN36XX_HAL_KEEP_ALIVE_NULL_PKT) { + msg_body.bss_index = vif_priv->bss_index; + msg_body.packet_type = WCN36XX_HAL_KEEP_ALIVE_NULL_PKT; + msg_body.time_period = WCN36XX_KEEP_ALIVE_TIME_PERIOD; + } else if (packet_type == WCN36XX_HAL_KEEP_ALIVE_UNSOLICIT_ARP_RSP) { + /* TODO: it also support ARP response type */ + } else { + wcn36xx_warn("unknow keep alive packet type %d\n", packet_type); + return -EINVAL; + } + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_exit_bmps failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_exit_bmps response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2, + u32 arg3, u32 arg4, u32 arg5) +{ + struct wcn36xx_hal_dump_cmd_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DUMP_COMMAND_REQ); + + msg_body.arg1 = arg1; + msg_body.arg2 = arg2; + msg_body.arg3 = arg3; + msg_body.arg4 = arg4; + msg_body.arg5 = arg5; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_dump_cmd failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_dump_cmd response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static inline void set_feat_caps(u32 *bitmap, + enum place_holder_in_cap_bitmap cap) +{ + int arr_idx, bit_idx; + + if (cap < 0 || cap > 127) { + wcn36xx_warn("error cap idx %d\n", cap); + return; + } + + arr_idx = cap / 32; + bit_idx = cap % 32; + bitmap[arr_idx] |= (1 << bit_idx); +} + +static inline int get_feat_caps(u32 *bitmap, + enum place_holder_in_cap_bitmap cap) +{ + int arr_idx, bit_idx; + int ret = 0; + + if (cap < 0 || cap > 127) { + wcn36xx_warn("error cap idx %d\n", cap); + return -EINVAL; + } + + arr_idx = cap / 32; + bit_idx = cap % 32; + ret = (bitmap[arr_idx] & (1 << bit_idx)) ? 1 : 0; + return ret; +} + +static inline void clear_feat_caps(u32 *bitmap, + enum place_holder_in_cap_bitmap cap) +{ + int arr_idx, bit_idx; + + if (cap < 0 || cap > 127) { + wcn36xx_warn("error cap idx %d\n", cap); + return; + } + + arr_idx = cap / 32; + bit_idx = cap % 32; + bitmap[arr_idx] &= ~(1 << bit_idx); +} + +int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_feat_caps_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_REQ); + + set_feat_caps(msg_body.feat_caps, STA_POWERSAVE); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_feature_caps_exchange failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_feature_caps_exchange response failed err=%d\n", + ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, + struct ieee80211_sta *sta, + u16 tid, + u16 *ssn, + u8 direction, + u8 sta_index) +{ + struct wcn36xx_hal_add_ba_session_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_BA_SESSION_REQ); + + msg_body.sta_index = sta_index; + memcpy(&msg_body.mac_addr, sta->addr, ETH_ALEN); + msg_body.dialog_token = 0x10; + msg_body.tid = tid; + + /* Immediate BA because Delayed BA is not supported */ + msg_body.policy = 1; + msg_body.buffer_size = WCN36XX_AGGR_BUFFER_SIZE; + msg_body.timeout = 0; + if (ssn) + msg_body.ssn = *ssn; + msg_body.direction = direction; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_add_ba_session failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_add_ba_session response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_add_ba(struct wcn36xx *wcn) +{ + struct wcn36xx_hal_add_ba_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_ADD_BA_REQ); + + msg_body.session_id = 0; + msg_body.win_size = WCN36XX_AGGR_BUFFER_SIZE; + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_add_ba failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_add_ba response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index) +{ + struct wcn36xx_hal_del_ba_req_msg msg_body; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_DEL_BA_REQ); + + msg_body.sta_index = sta_index; + msg_body.tid = tid; + msg_body.direction = 0; + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_del_ba failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_del_ba response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index) +{ + struct wcn36xx_hal_trigger_ba_req_msg msg_body; + struct wcn36xx_hal_trigget_ba_req_candidate *candidate; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_TRIGGER_BA_REQ); + + msg_body.session_id = 0; + msg_body.candidate_cnt = 1; + msg_body.header.len += sizeof(*candidate); + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + candidate = (struct wcn36xx_hal_trigget_ba_req_candidate *) + (wcn->hal_buf + sizeof(msg_body)); + candidate->sta_index = sta_index; + candidate->tid_bitmap = 1; + + ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + if (ret) { + wcn36xx_err("Sending hal_trigger_ba failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_trigger_ba response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} + +static int wcn36xx_smd_tx_compl_ind(struct wcn36xx *wcn, void *buf, size_t len) +{ + struct wcn36xx_hal_tx_compl_ind_msg *rsp = buf; + + if (len != sizeof(*rsp)) { + wcn36xx_warn("Bad TX complete indication\n"); + return -EIO; + } + + wcn36xx_dxe_tx_ack_ind(wcn, rsp->status); + + return 0; +} + +static int wcn36xx_smd_missed_beacon_ind(struct wcn36xx *wcn, + void *buf, + size_t len) +{ + struct wcn36xx_hal_missed_beacon_ind_msg *rsp = buf; + struct ieee80211_vif *vif = NULL; + struct wcn36xx_vif *tmp; + + /* Old FW does not have bss index */ + if (wcn36xx_is_fw_version(wcn, 1, 2, 2, 24)) { + list_for_each_entry(tmp, &wcn->vif_list, list) { + wcn36xx_dbg(WCN36XX_DBG_HAL, "beacon missed bss_index %d\n", + tmp->bss_index); + vif = container_of((void *)tmp, + struct ieee80211_vif, + drv_priv); + ieee80211_connection_loss(vif); + } + return 0; + } + + if (len != sizeof(*rsp)) { + wcn36xx_warn("Corrupted missed beacon indication\n"); + return -EIO; + } + + list_for_each_entry(tmp, &wcn->vif_list, list) { + if (tmp->bss_index == rsp->bss_index) { + wcn36xx_dbg(WCN36XX_DBG_HAL, "beacon missed bss_index %d\n", + rsp->bss_index); + vif = container_of((void *)tmp, + struct ieee80211_vif, + drv_priv); + ieee80211_connection_loss(vif); + return 0; + } + } + + wcn36xx_warn("BSS index %d not found\n", rsp->bss_index); + return -ENOENT; +} + +static int wcn36xx_smd_delete_sta_context_ind(struct wcn36xx *wcn, + void *buf, + size_t len) +{ + struct wcn36xx_hal_delete_sta_context_ind_msg *rsp = buf; + struct wcn36xx_vif *tmp; + struct ieee80211_sta *sta = NULL; + + if (len != sizeof(*rsp)) { + wcn36xx_warn("Corrupted delete sta indication\n"); + return -EIO; + } + + list_for_each_entry(tmp, &wcn->vif_list, list) { + if (sta && (tmp->sta->sta_index == rsp->sta_id)) { + sta = container_of((void *)tmp->sta, + struct ieee80211_sta, + drv_priv); + wcn36xx_dbg(WCN36XX_DBG_HAL, + "delete station indication %pM index %d\n", + rsp->addr2, + rsp->sta_id); + ieee80211_report_low_ack(sta, 0); + return 0; + } + } + + wcn36xx_warn("STA with addr %pM and index %d not found\n", + rsp->addr2, + rsp->sta_id); + return -ENOENT; +} + +int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value) +{ + struct wcn36xx_hal_update_cfg_req_msg msg_body, *body; + size_t len; + int ret = 0; + + mutex_lock(&wcn->hal_mutex); + INIT_HAL_MSG(msg_body, WCN36XX_HAL_UPDATE_CFG_REQ); + + PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + + body = (struct wcn36xx_hal_update_cfg_req_msg *) wcn->hal_buf; + len = msg_body.header.len; + + put_cfg_tlv_u32(wcn, &len, cfg_id, value); + body->header.len = len; + body->len = len - sizeof(*body); + + ret = wcn36xx_smd_send_and_wait(wcn, body->header.len); + if (ret) { + wcn36xx_err("Sending hal_update_cfg failed\n"); + goto out; + } + ret = wcn36xx_smd_rsp_status_check(wcn->hal_buf, wcn->hal_rsp_len); + if (ret) { + wcn36xx_err("hal_update_cfg response failed err=%d\n", ret); + goto out; + } +out: + mutex_unlock(&wcn->hal_mutex); + return ret; +} +static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len) +{ + struct wcn36xx_hal_msg_header *msg_header = buf; + struct wcn36xx_hal_ind_msg *msg_ind; + wcn36xx_dbg_dump(WCN36XX_DBG_SMD_DUMP, "SMD <<< ", buf, len); + + switch (msg_header->msg_type) { + case WCN36XX_HAL_START_RSP: + case WCN36XX_HAL_CONFIG_STA_RSP: + case WCN36XX_HAL_CONFIG_BSS_RSP: + case WCN36XX_HAL_ADD_STA_SELF_RSP: + case WCN36XX_HAL_STOP_RSP: + case WCN36XX_HAL_DEL_STA_SELF_RSP: + case WCN36XX_HAL_DELETE_STA_RSP: + case WCN36XX_HAL_INIT_SCAN_RSP: + case WCN36XX_HAL_START_SCAN_RSP: + case WCN36XX_HAL_END_SCAN_RSP: + case WCN36XX_HAL_FINISH_SCAN_RSP: + case WCN36XX_HAL_DOWNLOAD_NV_RSP: + case WCN36XX_HAL_DELETE_BSS_RSP: + case WCN36XX_HAL_SEND_BEACON_RSP: + case WCN36XX_HAL_SET_LINK_ST_RSP: + case WCN36XX_HAL_UPDATE_PROBE_RSP_TEMPLATE_RSP: + case WCN36XX_HAL_SET_BSSKEY_RSP: + case WCN36XX_HAL_SET_STAKEY_RSP: + case WCN36XX_HAL_RMV_STAKEY_RSP: + case WCN36XX_HAL_RMV_BSSKEY_RSP: + case WCN36XX_HAL_ENTER_BMPS_RSP: + case WCN36XX_HAL_SET_POWER_PARAMS_RSP: + case WCN36XX_HAL_EXIT_BMPS_RSP: + case WCN36XX_HAL_KEEP_ALIVE_RSP: + case WCN36XX_HAL_DUMP_COMMAND_RSP: + case WCN36XX_HAL_ADD_BA_SESSION_RSP: + case WCN36XX_HAL_ADD_BA_RSP: + case WCN36XX_HAL_DEL_BA_RSP: + case WCN36XX_HAL_TRIGGER_BA_RSP: + case WCN36XX_HAL_UPDATE_CFG_RSP: + case WCN36XX_HAL_JOIN_RSP: + case WCN36XX_HAL_UPDATE_SCAN_PARAM_RSP: + case WCN36XX_HAL_CH_SWITCH_RSP: + case WCN36XX_HAL_FEATURE_CAPS_EXCHANGE_RSP: + memcpy(wcn->hal_buf, buf, len); + wcn->hal_rsp_len = len; + complete(&wcn->hal_rsp_compl); + break; + + case WCN36XX_HAL_OTA_TX_COMPL_IND: + case WCN36XX_HAL_MISSED_BEACON_IND: + case WCN36XX_HAL_DELETE_STA_CONTEXT_IND: + mutex_lock(&wcn->hal_ind_mutex); + msg_ind = kmalloc(sizeof(*msg_ind), GFP_KERNEL); + msg_ind->msg_len = len; + msg_ind->msg = kmalloc(len, GFP_KERNEL); + memcpy(msg_ind->msg, buf, len); + list_add_tail(&msg_ind->list, &wcn->hal_ind_queue); + queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work); + wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n"); + mutex_unlock(&wcn->hal_ind_mutex); + break; + default: + wcn36xx_err("SMD_EVENT (%d) not supported\n", + msg_header->msg_type); + } +} +static void wcn36xx_ind_smd_work(struct work_struct *work) +{ + struct wcn36xx *wcn = + container_of(work, struct wcn36xx, hal_ind_work); + struct wcn36xx_hal_msg_header *msg_header; + struct wcn36xx_hal_ind_msg *hal_ind_msg; + + mutex_lock(&wcn->hal_ind_mutex); + + hal_ind_msg = list_first_entry(&wcn->hal_ind_queue, + struct wcn36xx_hal_ind_msg, + list); + + msg_header = (struct wcn36xx_hal_msg_header *)hal_ind_msg->msg; + + switch (msg_header->msg_type) { + case WCN36XX_HAL_OTA_TX_COMPL_IND: + wcn36xx_smd_tx_compl_ind(wcn, + hal_ind_msg->msg, + hal_ind_msg->msg_len); + break; + case WCN36XX_HAL_MISSED_BEACON_IND: + wcn36xx_smd_missed_beacon_ind(wcn, + hal_ind_msg->msg, + hal_ind_msg->msg_len); + break; + case WCN36XX_HAL_DELETE_STA_CONTEXT_IND: + wcn36xx_smd_delete_sta_context_ind(wcn, + hal_ind_msg->msg, + hal_ind_msg->msg_len); + break; + default: + wcn36xx_err("SMD_EVENT (%d) not supported\n", + msg_header->msg_type); + } + list_del(wcn->hal_ind_queue.next); + kfree(hal_ind_msg->msg); + kfree(hal_ind_msg); + mutex_unlock(&wcn->hal_ind_mutex); +} +int wcn36xx_smd_open(struct wcn36xx *wcn) +{ + int ret = 0; + wcn->hal_ind_wq = create_freezable_workqueue("wcn36xx_smd_ind"); + if (!wcn->hal_ind_wq) { + wcn36xx_err("failed to allocate wq\n"); + ret = -ENOMEM; + goto out; + } + INIT_WORK(&wcn->hal_ind_work, wcn36xx_ind_smd_work); + INIT_LIST_HEAD(&wcn->hal_ind_queue); + mutex_init(&wcn->hal_ind_mutex); + + ret = wcn->ctrl_ops->open(wcn, wcn36xx_smd_rsp_process); + if (ret) { + wcn36xx_err("failed to open control channel\n"); + goto free_wq; + } + + return ret; + +free_wq: + destroy_workqueue(wcn->hal_ind_wq); +out: + return ret; +} + +void wcn36xx_smd_close(struct wcn36xx *wcn) +{ + wcn->ctrl_ops->close(); + destroy_workqueue(wcn->hal_ind_wq); + mutex_destroy(&wcn->hal_ind_mutex); +} diff --git a/drivers/net/wireless/ath/wcn36xx/smd.h b/drivers/net/wireless/ath/wcn36xx/smd.h new file mode 100644 index 0000000000000000000000000000000000000000..e7c39019c6f1aece7d149b410a77bc42ae05093d --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/smd.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _SMD_H_ +#define _SMD_H_ + +#include "wcn36xx.h" + +/* Max shared size is 4k but we take less.*/ +#define WCN36XX_NV_FRAGMENT_SIZE 3072 + +#define WCN36XX_HAL_BUF_SIZE 4096 + +#define HAL_MSG_TIMEOUT 200 +#define WCN36XX_SMSM_WLAN_TX_ENABLE 0x00000400 +#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY 0x00000200 +/* The PNO version info be contained in the rsp msg */ +#define WCN36XX_FW_MSG_PNO_VERSION_MASK 0x8000 + +enum wcn36xx_fw_msg_result { + WCN36XX_FW_MSG_RESULT_SUCCESS = 0, + WCN36XX_FW_MSG_RESULT_SUCCESS_SYNC = 1, + + WCN36XX_FW_MSG_RESULT_MEM_FAIL = 5, +}; + +/******************************/ +/* SMD requests and responses */ +/******************************/ +struct wcn36xx_fw_msg_status_rsp { + u32 status; +} __packed; + +struct wcn36xx_hal_ind_msg { + struct list_head list; + u8 *msg; + size_t msg_len; +}; + +struct wcn36xx; + +int wcn36xx_smd_open(struct wcn36xx *wcn); +void wcn36xx_smd_close(struct wcn36xx *wcn); + +int wcn36xx_smd_load_nv(struct wcn36xx *wcn); +int wcn36xx_smd_start(struct wcn36xx *wcn); +int wcn36xx_smd_stop(struct wcn36xx *wcn); +int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode); +int wcn36xx_smd_start_scan(struct wcn36xx *wcn); +int wcn36xx_smd_end_scan(struct wcn36xx *wcn); +int wcn36xx_smd_finish_scan(struct wcn36xx *wcn, + enum wcn36xx_hal_sys_mode mode); +int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn); +int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif); +int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr); +int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index); +int wcn36xx_smd_join(struct wcn36xx *wcn, const u8 *bssid, u8 *vif, u8 ch); +int wcn36xx_smd_set_link_st(struct wcn36xx *wcn, const u8 *bssid, + const u8 *sta_mac, + enum wcn36xx_hal_link_state state); +int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, const u8 *bssid, + bool update); +int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif); +int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif, + struct sk_buff *skb_beacon, u16 tim_off, + u16 p2p_off); +int wcn36xx_smd_switch_channel(struct wcn36xx *wcn, + struct ieee80211_vif *vif, int ch); +int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + struct sk_buff *skb); +int wcn36xx_smd_set_stakey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 keylen, + u8 *key, + u8 sta_index); +int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 keylen, + u8 *key); +int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx, + u8 sta_index); +int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn, + enum ani_ed_type enc_type, + u8 keyidx); +int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif); +int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif); +int wcn36xx_smd_set_power_params(struct wcn36xx *wcn, bool ignore_dtim); +int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn, + struct ieee80211_vif *vif, + int packet_type); +int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2, + u32 arg3, u32 arg4, u32 arg5); +int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn); + +int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn, + struct ieee80211_sta *sta, + u16 tid, + u16 *ssn, + u8 direction, + u8 sta_index); +int wcn36xx_smd_add_ba(struct wcn36xx *wcn); +int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index); +int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index); + +int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value); +#endif /* _SMD_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c new file mode 100644 index 0000000000000000000000000000000000000000..b2b60e30caaf8492886467e6c8a14b32b6f95467 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "txrx.h" + +static inline int get_rssi0(struct wcn36xx_rx_bd *bd) +{ + return 100 - ((bd->phy_stat0 >> 24) & 0xff); +} + +int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb) +{ + struct ieee80211_rx_status status; + struct ieee80211_hdr *hdr; + struct wcn36xx_rx_bd *bd; + u16 fc, sn; + + /* + * All fields must be 0, otherwise it can lead to + * unexpected consequences. + */ + memset(&status, 0, sizeof(status)); + + bd = (struct wcn36xx_rx_bd *)skb->data; + buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32)); + wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, + "BD <<< ", (char *)bd, + sizeof(struct wcn36xx_rx_bd)); + + skb_put(skb, bd->pdu.mpdu_header_off + bd->pdu.mpdu_len); + skb_pull(skb, bd->pdu.mpdu_header_off); + + status.mactime = 10; + status.freq = WCN36XX_CENTER_FREQ(wcn); + status.band = WCN36XX_BAND(wcn); + status.signal = -get_rssi0(bd); + status.antenna = 1; + status.rate_idx = 1; + status.flag = 0; + status.rx_flags = 0; + status.flag |= RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED | + RX_FLAG_DECRYPTED; + + wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x status->vendor_radiotap_len=%x\n", + status.flag, status.vendor_radiotap_len); + + memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); + + hdr = (struct ieee80211_hdr *) skb->data; + fc = __le16_to_cpu(hdr->frame_control); + sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)); + + if (ieee80211_is_beacon(hdr->frame_control)) { + wcn36xx_dbg(WCN36XX_DBG_BEACON, "beacon skb %p len %d fc %04x sn %d\n", + skb, skb->len, fc, sn); + wcn36xx_dbg_dump(WCN36XX_DBG_BEACON_DUMP, "SKB <<< ", + (char *)skb->data, skb->len); + } else { + wcn36xx_dbg(WCN36XX_DBG_RX, "rx skb %p len %d fc %04x sn %d\n", + skb, skb->len, fc, sn); + wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, "SKB <<< ", + (char *)skb->data, skb->len); + } + + ieee80211_rx_irqsafe(wcn->hw, skb); + + return 0; +} + +static void wcn36xx_set_tx_pdu(struct wcn36xx_tx_bd *bd, + u32 mpdu_header_len, + u32 len, + u16 tid) +{ + bd->pdu.mpdu_header_len = mpdu_header_len; + bd->pdu.mpdu_header_off = sizeof(*bd); + bd->pdu.mpdu_data_off = bd->pdu.mpdu_header_len + + bd->pdu.mpdu_header_off; + bd->pdu.mpdu_len = len; + bd->pdu.tid = tid; +} + +static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn, + u8 *addr) +{ + struct wcn36xx_vif *vif_priv = NULL; + struct ieee80211_vif *vif = NULL; + list_for_each_entry(vif_priv, &wcn->vif_list, list) { + vif = container_of((void *)vif_priv, + struct ieee80211_vif, + drv_priv); + if (memcmp(vif->addr, addr, ETH_ALEN) == 0) + return vif_priv; + } + wcn36xx_warn("vif %pM not found\n", addr); + return NULL; +} +static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, + struct wcn36xx *wcn, + struct wcn36xx_vif **vif_priv, + struct wcn36xx_sta *sta_priv, + struct ieee80211_hdr *hdr, + bool bcast) +{ + struct ieee80211_vif *vif = NULL; + struct wcn36xx_vif *__vif_priv = NULL; + bd->bd_rate = WCN36XX_BD_RATE_DATA; + + /* + * For not unicast frames mac80211 will not set sta pointer so use + * self_sta_index instead. + */ + if (sta_priv) { + __vif_priv = sta_priv->vif; + vif = container_of((void *)__vif_priv, + struct ieee80211_vif, + drv_priv); + + if (vif->type == NL80211_IFTYPE_STATION) { + bd->sta_index = sta_priv->bss_sta_index; + bd->dpu_desc_idx = sta_priv->bss_dpu_desc_index; + } else if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_MESH_POINT) { + bd->sta_index = sta_priv->sta_index; + bd->dpu_desc_idx = sta_priv->dpu_desc_index; + } + } else { + __vif_priv = get_vif_by_addr(wcn, hdr->addr2); + bd->sta_index = __vif_priv->self_sta_index; + bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index; + } + + bd->dpu_sign = __vif_priv->ucast_dpu_signature; + + if (ieee80211_is_nullfunc(hdr->frame_control) || + (sta_priv && !sta_priv->is_data_encrypted)) + bd->dpu_ne = 1; + + if (bcast) { + bd->ub = 1; + bd->ack_policy = 1; + } + *vif_priv = __vif_priv; +} + +static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd, + struct wcn36xx *wcn, + struct wcn36xx_vif **vif_priv, + struct ieee80211_hdr *hdr, + bool bcast) +{ + struct wcn36xx_vif *__vif_priv = + get_vif_by_addr(wcn, hdr->addr2); + bd->sta_index = __vif_priv->self_sta_index; + bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index; + bd->dpu_ne = 1; + + /* default rate for unicast */ + if (ieee80211_is_mgmt(hdr->frame_control)) + bd->bd_rate = (WCN36XX_BAND(wcn) == IEEE80211_BAND_5GHZ) ? + WCN36XX_BD_RATE_CTRL : + WCN36XX_BD_RATE_MGMT; + else if (ieee80211_is_ctl(hdr->frame_control)) + bd->bd_rate = WCN36XX_BD_RATE_CTRL; + else + wcn36xx_warn("frame control type unknown\n"); + + /* + * In joining state trick hardware that probe is sent as + * unicast even if address is broadcast. + */ + if (__vif_priv->is_joining && + ieee80211_is_probe_req(hdr->frame_control)) + bcast = false; + + if (bcast) { + /* broadcast */ + bd->ub = 1; + /* No ack needed not unicast */ + bd->ack_policy = 1; + bd->queue_id = WCN36XX_TX_B_WQ_ID; + } else + bd->queue_id = WCN36XX_TX_U_WQ_ID; + *vif_priv = __vif_priv; +} + +int wcn36xx_start_tx(struct wcn36xx *wcn, + struct wcn36xx_sta *sta_priv, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct wcn36xx_vif *vif_priv = NULL; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + unsigned long flags; + bool is_low = ieee80211_is_data(hdr->frame_control); + bool bcast = is_broadcast_ether_addr(hdr->addr1) || + is_multicast_ether_addr(hdr->addr1); + struct wcn36xx_tx_bd *bd = wcn36xx_dxe_get_next_bd(wcn, is_low); + + if (!bd) { + /* + * TX DXE are used in pairs. One for the BD and one for the + * actual frame. The BD DXE's has a preallocated buffer while + * the skb ones does not. If this isn't true something is really + * wierd. TODO: Recover from this situation + */ + + wcn36xx_err("bd address may not be NULL for BD DXE\n"); + return -EINVAL; + } + + memset(bd, 0, sizeof(*bd)); + + wcn36xx_dbg(WCN36XX_DBG_TX, + "tx skb %p len %d fc %04x sn %d %s %s\n", + skb, skb->len, __le16_to_cpu(hdr->frame_control), + IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)), + is_low ? "low" : "high", bcast ? "bcast" : "ucast"); + + wcn36xx_dbg_dump(WCN36XX_DBG_TX_DUMP, "", skb->data, skb->len); + + bd->dpu_rf = WCN36XX_BMU_WQ_TX; + + bd->tx_comp = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS; + if (bd->tx_comp) { + wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n"); + spin_lock_irqsave(&wcn->dxe_lock, flags); + if (wcn->tx_ack_skb) { + spin_unlock_irqrestore(&wcn->dxe_lock, flags); + wcn36xx_warn("tx_ack_skb already set\n"); + return -EINVAL; + } + + wcn->tx_ack_skb = skb; + spin_unlock_irqrestore(&wcn->dxe_lock, flags); + + /* Only one at a time is supported by fw. Stop the TX queues + * until the ack status gets back. + * + * TODO: Add watchdog in case FW does not answer + */ + ieee80211_stop_queues(wcn->hw); + } + + /* Data frames served first*/ + if (is_low) { + wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, hdr, bcast); + wcn36xx_set_tx_pdu(bd, + ieee80211_is_data_qos(hdr->frame_control) ? + sizeof(struct ieee80211_qos_hdr) : + sizeof(struct ieee80211_hdr_3addr), + skb->len, sta_priv ? sta_priv->tid : 0); + } else { + /* MGMT and CTRL frames are handeld here*/ + wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, hdr, bcast); + wcn36xx_set_tx_pdu(bd, + ieee80211_is_data_qos(hdr->frame_control) ? + sizeof(struct ieee80211_qos_hdr) : + sizeof(struct ieee80211_hdr_3addr), + skb->len, WCN36XX_TID); + } + + buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32)); + bd->tx_bd_sign = 0xbdbdbdbd; + + return wcn36xx_dxe_tx_frame(wcn, vif_priv, skb, is_low); +} diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.h b/drivers/net/wireless/ath/wcn36xx/txrx.h new file mode 100644 index 0000000000000000000000000000000000000000..bbfbcf808c775dd7b50f8898735f611cb101682d --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/txrx.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _TXRX_H_ +#define _TXRX_H_ + +#include +#include "wcn36xx.h" + +/* TODO describe all properties */ +#define WCN36XX_802_11_HEADER_LEN 24 +#define WCN36XX_BMU_WQ_TX 25 +#define WCN36XX_TID 7 +/* broadcast wq ID */ +#define WCN36XX_TX_B_WQ_ID 0xA +#define WCN36XX_TX_U_WQ_ID 0x9 +/* bd_rate */ +#define WCN36XX_BD_RATE_DATA 0 +#define WCN36XX_BD_RATE_MGMT 2 +#define WCN36XX_BD_RATE_CTRL 3 + +struct wcn36xx_pdu { + u32 dpu_fb:8; + u32 adu_fb:8; + u32 pdu_id:16; + + /* 0x04*/ + u32 tail_pdu_idx:16; + u32 head_pdu_idx:16; + + /* 0x08*/ + u32 pdu_count:7; + u32 mpdu_data_off:9; + u32 mpdu_header_off:8; + u32 mpdu_header_len:8; + + /* 0x0c*/ + u32 reserved4:8; + u32 tid:4; + u32 reserved3:4; + u32 mpdu_len:16; +}; + +struct wcn36xx_rx_bd { + u32 bdt:2; + u32 ft:1; + u32 dpu_ne:1; + u32 rx_key_id:3; + u32 ub:1; + u32 rmf:1; + u32 uma_bypass:1; + u32 csr11:1; + u32 reserved0:1; + u32 scan_learn:1; + u32 rx_ch:4; + u32 rtsf:1; + u32 bsf:1; + u32 a2hf:1; + u32 st_auf:1; + u32 dpu_sign:3; + u32 dpu_rf:8; + + struct wcn36xx_pdu pdu; + + /* 0x14*/ + u32 addr3:8; + u32 addr2:8; + u32 addr1:8; + u32 dpu_desc_idx:8; + + /* 0x18*/ + u32 rxp_flags:23; + u32 rate_id:9; + + u32 phy_stat0; + u32 phy_stat1; + + /* 0x24 */ + u32 rx_times; + + u32 pmi_cmd[6]; + + /* 0x40 */ + u32 reserved7:4; + u32 reorder_slot_id:6; + u32 reorder_fwd_id:6; + u32 reserved6:12; + u32 reorder_code:4; + + /* 0x44 */ + u32 exp_seq_num:12; + u32 cur_seq_num:12; + u32 fr_type_subtype:8; + + /* 0x48 */ + u32 msdu_size:16; + u32 sub_fr_id:4; + u32 proc_order:4; + u32 reserved9:4; + u32 aef:1; + u32 lsf:1; + u32 esf:1; + u32 asf:1; +}; + +struct wcn36xx_tx_bd { + u32 bdt:2; + u32 ft:1; + u32 dpu_ne:1; + u32 fw_tx_comp:1; + u32 tx_comp:1; + u32 reserved1:1; + u32 ub:1; + u32 rmf:1; + u32 reserved0:12; + u32 dpu_sign:3; + u32 dpu_rf:8; + + struct wcn36xx_pdu pdu; + + /* 0x14*/ + u32 reserved5:7; + u32 queue_id:5; + u32 bd_rate:2; + u32 ack_policy:2; + u32 sta_index:8; + u32 dpu_desc_idx:8; + + u32 tx_bd_sign; + u32 reserved6; + u32 dxe_start_time; + u32 dxe_end_time; + + /*u32 tcp_udp_start_off:10; + u32 header_cks:16; + u32 reserved7:6;*/ +}; + +struct wcn36xx_sta; +struct wcn36xx; + +int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb); +int wcn36xx_start_tx(struct wcn36xx *wcn, + struct wcn36xx_sta *sta_priv, + struct sk_buff *skb); + +#endif /* _TXRX_H_ */ diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h new file mode 100644 index 0000000000000000000000000000000000000000..58b63833e8e72d1d8dee64b31c14374369ecb072 --- /dev/null +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2013 Eugene Krasnikov + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WCN36XX_H_ +#define _WCN36XX_H_ + +#include +#include +#include +#include + +#include "hal.h" +#include "smd.h" +#include "txrx.h" +#include "dxe.h" +#include "pmc.h" +#include "debug.h" + +#define WLAN_NV_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" +#define WCN36XX_AGGR_BUFFER_SIZE 64 + +extern unsigned int wcn36xx_dbg_mask; + +enum wcn36xx_debug_mask { + WCN36XX_DBG_DXE = 0x00000001, + WCN36XX_DBG_DXE_DUMP = 0x00000002, + WCN36XX_DBG_SMD = 0x00000004, + WCN36XX_DBG_SMD_DUMP = 0x00000008, + WCN36XX_DBG_RX = 0x00000010, + WCN36XX_DBG_RX_DUMP = 0x00000020, + WCN36XX_DBG_TX = 0x00000040, + WCN36XX_DBG_TX_DUMP = 0x00000080, + WCN36XX_DBG_HAL = 0x00000100, + WCN36XX_DBG_HAL_DUMP = 0x00000200, + WCN36XX_DBG_MAC = 0x00000400, + WCN36XX_DBG_BEACON = 0x00000800, + WCN36XX_DBG_BEACON_DUMP = 0x00001000, + WCN36XX_DBG_PMC = 0x00002000, + WCN36XX_DBG_PMC_DUMP = 0x00004000, + WCN36XX_DBG_ANY = 0xffffffff, +}; + +#define wcn36xx_err(fmt, arg...) \ + printk(KERN_ERR pr_fmt("ERROR " fmt), ##arg); + +#define wcn36xx_warn(fmt, arg...) \ + printk(KERN_WARNING pr_fmt("WARNING " fmt), ##arg) + +#define wcn36xx_info(fmt, arg...) \ + printk(KERN_INFO pr_fmt(fmt), ##arg) + +#define wcn36xx_dbg(mask, fmt, arg...) do { \ + if (wcn36xx_dbg_mask & mask) \ + printk(KERN_DEBUG pr_fmt(fmt), ##arg); \ +} while (0) + +#define wcn36xx_dbg_dump(mask, prefix_str, buf, len) do { \ + if (wcn36xx_dbg_mask & mask) \ + print_hex_dump(KERN_DEBUG, pr_fmt(prefix_str), \ + DUMP_PREFIX_OFFSET, 32, 1, \ + buf, len, false); \ +} while (0) + +#define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value) +#define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band) +#define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq) +#define WCN36XX_LISTEN_INTERVAL(__wcn) (__wcn->hw->conf.listen_interval) +#define WCN36XX_FLAGS(__wcn) (__wcn->hw->flags) +#define WCN36XX_MAX_POWER(__wcn) (__wcn->hw->conf.chandef.chan->max_power) + +static inline void buff_to_be(u32 *buf, size_t len) +{ + int i; + for (i = 0; i < len; i++) + buf[i] = cpu_to_be32(buf[i]); +} + +struct nv_data { + int is_valid; + u8 table; +}; + +/* Interface for platform control path + * + * @open: hook must be called when wcn36xx wants to open control channel. + * @tx: sends a buffer. + */ +struct wcn36xx_platform_ctrl_ops { + int (*open)(void *drv_priv, void *rsp_cb); + void (*close)(void); + int (*tx)(char *buf, size_t len); + int (*get_hw_mac)(u8 *addr); + int (*smsm_change_state)(u32 clear_mask, u32 set_mask); +}; + +/** + * struct wcn36xx_vif - holds VIF related fields + * + * @bss_index: bss_index is initially set to 0xFF. bss_index is received from + * HW after first config_bss call and must be used in delete_bss and + * enter/exit_bmps. + */ +struct wcn36xx_vif { + struct list_head list; + struct wcn36xx_sta *sta; + u8 dtim_period; + enum ani_ed_type encrypt_type; + bool is_joining; + struct wcn36xx_hal_mac_ssid ssid; + + /* Power management */ + enum wcn36xx_power_state pw_state; + + u8 bss_index; + u8 ucast_dpu_signature; + /* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */ + u8 self_sta_index; + u8 self_dpu_desc_index; +}; + +/** + * struct wcn36xx_sta - holds STA related fields + * + * @tid: traffic ID that is used during AMPDU and in TX BD. + * @sta_index: STA index is returned from HW after config_sta call and is + * used in both SMD channel and TX BD. + * @dpu_desc_index: DPU descriptor index is returned from HW after config_sta + * call and is used in TX BD. + * @bss_sta_index: STA index is returned from HW after config_bss call and is + * used in both SMD channel and TX BD. See table bellow when it is used. + * @bss_dpu_desc_index: DPU descriptor index is returned from HW after + * config_bss call and is used in TX BD. + * ______________________________________________ + * | | STA | AP | + * |______________|_____________|_______________| + * | TX BD |bss_sta_index| sta_index | + * |______________|_____________|_______________| + * |all SMD calls |bss_sta_index| sta_index | + * |______________|_____________|_______________| + * |smd_delete_sta| sta_index | sta_index | + * |______________|_____________|_______________| + */ +struct wcn36xx_sta { + struct wcn36xx_vif *vif; + u16 aid; + u16 tid; + u8 sta_index; + u8 dpu_desc_index; + u8 bss_sta_index; + u8 bss_dpu_desc_index; + bool is_data_encrypted; + /* Rates */ + struct wcn36xx_hal_supported_rates supported_rates; +}; +struct wcn36xx_dxe_ch; +struct wcn36xx { + struct ieee80211_hw *hw; + struct device *dev; + struct list_head vif_list; + + u8 fw_revision; + u8 fw_version; + u8 fw_minor; + u8 fw_major; + + /* extra byte for the NULL termination */ + u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1]; + u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH + 1]; + + /* IRQs */ + int tx_irq; + int rx_irq; + void __iomem *mmio; + + struct wcn36xx_platform_ctrl_ops *ctrl_ops; + /* + * smd_buf must be protected with smd_mutex to garantee + * that all messages are sent one after another + */ + u8 *hal_buf; + size_t hal_rsp_len; + struct mutex hal_mutex; + struct completion hal_rsp_compl; + struct workqueue_struct *hal_ind_wq; + struct work_struct hal_ind_work; + struct mutex hal_ind_mutex; + struct list_head hal_ind_queue; + + /* DXE channels */ + struct wcn36xx_dxe_ch dxe_tx_l_ch; /* TX low */ + struct wcn36xx_dxe_ch dxe_tx_h_ch; /* TX high */ + struct wcn36xx_dxe_ch dxe_rx_l_ch; /* RX low */ + struct wcn36xx_dxe_ch dxe_rx_h_ch; /* RX high */ + + /* For synchronization of DXE resources from BH, IRQ and WQ contexts */ + spinlock_t dxe_lock; + bool queues_stopped; + + /* Memory pools */ + struct wcn36xx_dxe_mem_pool mgmt_mem_pool; + struct wcn36xx_dxe_mem_pool data_mem_pool; + + struct sk_buff *tx_ack_skb; + +#ifdef CONFIG_WCN36XX_DEBUGFS + /* Debug file system entry */ + struct wcn36xx_dfs_entry dfs; +#endif /* CONFIG_WCN36XX_DEBUGFS */ + +}; + +static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn, + u8 major, + u8 minor, + u8 version, + u8 revision) +{ + return (wcn->fw_major == major && + wcn->fw_minor == minor && + wcn->fw_version == version && + wcn->fw_revision == revision); +} +void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates); + +#endif /* _WCN36XX_H_ */ diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 61c302a6bdeaa38ef1a1d20b3ac1e477db794745..5b340769d5bb2196bf3d0192e007ed13f23236fc 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -316,8 +316,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, } conn.channel = ch - 1; - memcpy(conn.bssid, bss->bssid, 6); - memcpy(conn.dst_mac, bss->bssid, 6); + memcpy(conn.bssid, bss->bssid, ETH_ALEN); + memcpy(conn.dst_mac, bss->bssid, ETH_ALEN); /* * FW don't support scan after connection attempt */ diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index eb1dc7ad80fb367bbb6c56959a8758aa85225ac4..eeceab39cda22aee81e2fcbb86fc0c9520766a5f 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -197,7 +197,6 @@ static void wil_pcie_remove(struct pci_dev *pdev) pci_iounmap(pdev, wil->csr); pci_release_region(pdev, 0); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index b827d51c30a37b93f1747d7df9b2049aeb97e25f..0d950f209dae01efd23ebe9956ac2dc68daa7063 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -844,18 +844,18 @@ static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev) if (priv->wep_is_on) frame_ctl |= IEEE80211_FCTL_PROTECTED; if (priv->operating_mode == IW_MODE_ADHOC) { - skb_copy_from_linear_data(skb, &header.addr1, 6); - memcpy(&header.addr2, dev->dev_addr, 6); - memcpy(&header.addr3, priv->BSSID, 6); + skb_copy_from_linear_data(skb, &header.addr1, ETH_ALEN); + memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&header.addr3, priv->BSSID, ETH_ALEN); } else { frame_ctl |= IEEE80211_FCTL_TODS; - memcpy(&header.addr1, priv->CurrentBSSID, 6); - memcpy(&header.addr2, dev->dev_addr, 6); - skb_copy_from_linear_data(skb, &header.addr3, 6); + memcpy(&header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(&header.addr2, dev->dev_addr, ETH_ALEN); + skb_copy_from_linear_data(skb, &header.addr3, ETH_ALEN); } if (priv->use_wpa) - memcpy(&header.addr4, SNAP_RFC1024, 6); + memcpy(&header.addr4, SNAP_RFC1024, ETH_ALEN); header.frame_control = cpu_to_le16(frame_ctl); /* Copy the wireless header into the card */ @@ -929,11 +929,11 @@ static void fast_rx_path(struct atmel_private *priv, } } - memcpy(skbp, header->addr1, 6); /* destination address */ + memcpy(skbp, header->addr1, ETH_ALEN); /* destination address */ if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - memcpy(&skbp[6], header->addr3, 6); + memcpy(&skbp[ETH_ALEN], header->addr3, ETH_ALEN); else - memcpy(&skbp[6], header->addr2, 6); /* source address */ + memcpy(&skbp[ETH_ALEN], header->addr2, ETH_ALEN); /* source address */ skb->protocol = eth_type_trans(skb, priv->dev); skb->ip_summed = CHECKSUM_NONE; @@ -969,14 +969,14 @@ static void frag_rx_path(struct atmel_private *priv, u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, u8 frag_no, int more_frags) { - u8 mac4[6]; - u8 source[6]; + u8 mac4[ETH_ALEN]; + u8 source[ETH_ALEN]; struct sk_buff *skb; if (le16_to_cpu(header->frame_control) & IEEE80211_FCTL_FROMDS) - memcpy(source, header->addr3, 6); + memcpy(source, header->addr3, ETH_ALEN); else - memcpy(source, header->addr2, 6); + memcpy(source, header->addr2, ETH_ALEN); rx_packet_loc += 24; /* skip header */ @@ -984,9 +984,9 @@ static void frag_rx_path(struct atmel_private *priv, msdu_size -= 4; if (frag_no == 0) { /* first fragment */ - atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, 6); - msdu_size -= 6; - rx_packet_loc += 6; + atmel_copy_to_host(priv->dev, mac4, rx_packet_loc, ETH_ALEN); + msdu_size -= ETH_ALEN; + rx_packet_loc += ETH_ALEN; if (priv->do_rx_crc) crc = crc32_le(crc, mac4, 6); @@ -994,9 +994,9 @@ static void frag_rx_path(struct atmel_private *priv, priv->frag_seq = seq_no; priv->frag_no = 1; priv->frag_len = msdu_size; - memcpy(priv->frag_source, source, 6); - memcpy(&priv->rx_buf[6], source, 6); - memcpy(priv->rx_buf, header->addr1, 6); + memcpy(priv->frag_source, source, ETH_ALEN); + memcpy(&priv->rx_buf[ETH_ALEN], source, ETH_ALEN); + memcpy(priv->rx_buf, header->addr1, ETH_ALEN); atmel_copy_to_host(priv->dev, &priv->rx_buf[12], rx_packet_loc, msdu_size); @@ -1006,13 +1006,13 @@ static void frag_rx_path(struct atmel_private *priv, atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); if ((crc ^ 0xffffffff) != netcrc) { priv->dev->stats.rx_crc_errors++; - memset(priv->frag_source, 0xff, 6); + memset(priv->frag_source, 0xff, ETH_ALEN); } } } else if (priv->frag_no == frag_no && priv->frag_seq == seq_no && - memcmp(priv->frag_source, source, 6) == 0) { + memcmp(priv->frag_source, source, ETH_ALEN) == 0) { atmel_copy_to_host(priv->dev, &priv->rx_buf[12 + priv->frag_len], rx_packet_loc, msdu_size); @@ -1024,7 +1024,7 @@ static void frag_rx_path(struct atmel_private *priv, atmel_copy_to_host(priv->dev, (void *)&netcrc, rx_packet_loc + msdu_size, 4); if ((crc ^ 0xffffffff) != netcrc) { priv->dev->stats.rx_crc_errors++; - memset(priv->frag_source, 0xff, 6); + memset(priv->frag_source, 0xff, ETH_ALEN); more_frags = 1; /* don't send broken assembly */ } } @@ -1033,7 +1033,7 @@ static void frag_rx_path(struct atmel_private *priv, priv->frag_no++; if (!more_frags) { /* last one */ - memset(priv->frag_source, 0xff, 6); + memset(priv->frag_source, 0xff, ETH_ALEN); if (!(skb = dev_alloc_skb(priv->frag_len + 14))) { priv->dev->stats.rx_dropped++; } else { @@ -1129,7 +1129,7 @@ static void rx_done_irq(struct atmel_private *priv) atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); /* we use the same buffer for frag reassembly and control packets */ - memset(priv->frag_source, 0xff, 6); + memset(priv->frag_source, 0xff, ETH_ALEN); if (priv->do_rx_crc) { /* last 4 octets is crc */ @@ -1557,7 +1557,7 @@ struct net_device *init_atmel_card(unsigned short irq, unsigned long port, priv->last_qual = jiffies; priv->last_beacon_timestamp = 0; memset(priv->frag_source, 0xff, sizeof(priv->frag_source)); - memset(priv->BSSID, 0, 6); + memset(priv->BSSID, 0, ETH_ALEN); priv->CurrentBSSID[0] = 0xFF; /* Initialize to something invalid.... */ priv->station_was_associated = 0; @@ -1718,7 +1718,7 @@ static int atmel_get_wap(struct net_device *dev, char *extra) { struct atmel_private *priv = netdev_priv(dev); - memcpy(awrq->sa_data, priv->CurrentBSSID, 6); + memcpy(awrq->sa_data, priv->CurrentBSSID, ETH_ALEN); awrq->sa_family = ARPHRD_ETHER; return 0; @@ -2356,7 +2356,7 @@ static int atmel_get_scan(struct net_device *dev, for (i = 0; i < priv->BSS_list_entries; i++) { iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; - memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, 6); + memcpy(iwe.u.ap_addr.sa_data, priv->BSSinfo[i].BSSID, ETH_ALEN); current_ev = iwe_stream_add_event(info, current_ev, extra + IW_SCAN_MAX_DATA, &iwe, IW_EV_ADDR_LEN); @@ -2760,7 +2760,7 @@ static void atmel_enter_state(struct atmel_private *priv, int new_state) static void atmel_scan(struct atmel_private *priv, int specific_ssid) { struct { - u8 BSSID[6]; + u8 BSSID[ETH_ALEN]; u8 SSID[MAX_SSID_LENGTH]; u8 scan_type; u8 channel; @@ -2771,7 +2771,7 @@ static void atmel_scan(struct atmel_private *priv, int specific_ssid) u8 SSID_size; } cmd; - memset(cmd.BSSID, 0xff, 6); + memset(cmd.BSSID, 0xff, ETH_ALEN); if (priv->fast_scan) { cmd.SSID_size = priv->SSID_size; @@ -2816,7 +2816,7 @@ static void join(struct atmel_private *priv, int type) cmd.SSID_size = priv->SSID_size; memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - memcpy(cmd.BSSID, priv->CurrentBSSID, 6); + memcpy(cmd.BSSID, priv->CurrentBSSID, ETH_ALEN); cmd.channel = (priv->channel & 0x7f); cmd.BSS_type = type; cmd.timeout = cpu_to_le16(2000); @@ -2837,7 +2837,7 @@ static void start(struct atmel_private *priv, int type) cmd.SSID_size = priv->SSID_size; memcpy(cmd.SSID, priv->SSID, priv->SSID_size); - memcpy(cmd.BSSID, priv->BSSID, 6); + memcpy(cmd.BSSID, priv->BSSID, ETH_ALEN); cmd.BSS_type = type; cmd.channel = (priv->channel & 0x7f); @@ -2883,9 +2883,9 @@ static void send_authentication_request(struct atmel_private *priv, u16 system, header.frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); header.duration_id = cpu_to_le16(0x8000); header.seq_ctrl = 0; - memcpy(header.addr1, priv->CurrentBSSID, 6); - memcpy(header.addr2, priv->dev->dev_addr, 6); - memcpy(header.addr3, priv->CurrentBSSID, 6); + memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); + memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); if (priv->wep_is_on && priv->CurrentAuthentTransactionSeqNum != 1) /* no WEP for authentication frames with TrSeqNo 1 */ @@ -2916,7 +2916,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc) struct ass_req_format { __le16 capability; __le16 listen_interval; - u8 ap[6]; /* nothing after here directly accessible */ + u8 ap[ETH_ALEN]; /* nothing after here directly accessible */ u8 ssid_el_id; u8 ssid_len; u8 ssid[MAX_SSID_LENGTH]; @@ -2930,9 +2930,9 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc) header.duration_id = cpu_to_le16(0x8000); header.seq_ctrl = 0; - memcpy(header.addr1, priv->CurrentBSSID, 6); - memcpy(header.addr2, priv->dev->dev_addr, 6); - memcpy(header.addr3, priv->CurrentBSSID, 6); + memcpy(header.addr1, priv->CurrentBSSID, ETH_ALEN); + memcpy(header.addr2, priv->dev->dev_addr, ETH_ALEN); + memcpy(header.addr3, priv->CurrentBSSID, ETH_ALEN); body.capability = cpu_to_le16(WLAN_CAPABILITY_ESS); if (priv->wep_is_on) @@ -2944,7 +2944,7 @@ static void send_association_request(struct atmel_private *priv, int is_reassoc) /* current AP address - only in reassoc frame */ if (is_reassoc) { - memcpy(body.ap, priv->CurrentBSSID, 6); + memcpy(body.ap, priv->CurrentBSSID, ETH_ALEN); ssid_el_p = &body.ssid_el_id; bodysize = 18 + priv->SSID_size; } else { @@ -3021,7 +3021,7 @@ static void store_bss_info(struct atmel_private *priv, int i, index; for (index = -1, i = 0; i < priv->BSS_list_entries; i++) - if (memcmp(bss, priv->BSSinfo[i].BSSID, 6) == 0) + if (memcmp(bss, priv->BSSinfo[i].BSSID, ETH_ALEN) == 0) index = i; /* If we process a probe and an entry from this BSS exists @@ -3032,7 +3032,7 @@ static void store_bss_info(struct atmel_private *priv, if (priv->BSS_list_entries == MAX_BSS_ENTRIES) return; index = priv->BSS_list_entries++; - memcpy(priv->BSSinfo[index].BSSID, bss, 6); + memcpy(priv->BSSinfo[index].BSSID, bss, ETH_ALEN); priv->BSSinfo[index].RSSI = rssi; } else { if (rssi > priv->BSSinfo[index].RSSI) @@ -3212,7 +3212,7 @@ static void associate(struct atmel_private *priv, u16 frame_len, u16 subtype) if (subtype == IEEE80211_STYPE_REASSOC_RESP && status != WLAN_STATUS_ASSOC_DENIED_RATES && status != WLAN_STATUS_CAPS_UNSUPPORTED && - priv->AssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { + priv->ReAssociationRequestRetryCnt < MAX_ASSOCIATION_RETRIES) { mod_timer(&priv->management_timer, jiffies + MGMT_JIFFIES); priv->ReAssociationRequestRetryCnt++; send_association_request(priv, 1); @@ -3235,7 +3235,7 @@ static void atmel_join_bss(struct atmel_private *priv, int bss_index) { struct bss_info *bss = &priv->BSSinfo[bss_index]; - memcpy(priv->CurrentBSSID, bss->BSSID, 6); + memcpy(priv->CurrentBSSID, bss->BSSID, ETH_ALEN); memcpy(priv->SSID, bss->SSID, priv->SSID_size = bss->SSIDsize); /* The WPA stuff cares about the current AP address */ @@ -3767,7 +3767,7 @@ static int probe_atmel_card(struct net_device *dev) 0x00, 0x04, 0x25, 0x00, 0x00, 0x00 }; printk(KERN_ALERT "%s: *** Invalid MAC address. UPGRADE Firmware ****\n", dev->name); - memcpy(dev->dev_addr, default_mac, 6); + memcpy(dev->dev_addr, default_mac, ETH_ALEN); } } @@ -3819,7 +3819,7 @@ static void build_wpa_mib(struct atmel_private *priv) struct { /* NB this is matched to the hardware, don't change. */ u8 cipher_default_key_value[MAX_ENCRYPTION_KEYS][MAX_ENCRYPTION_KEY_SIZE]; - u8 receiver_address[6]; + u8 receiver_address[ETH_ALEN]; u8 wep_is_on; u8 default_key; /* 0..3 */ u8 group_key; @@ -3837,7 +3837,7 @@ static void build_wpa_mib(struct atmel_private *priv) mib.wep_is_on = priv->wep_is_on; mib.exclude_unencrypted = priv->exclude_unencrypted; - memcpy(mib.receiver_address, priv->CurrentBSSID, 6); + memcpy(mib.receiver_address, priv->CurrentBSSID, ETH_ALEN); /* zero all the keys before adding in valid ones. */ memset(mib.cipher_default_key_value, 0, sizeof(mib.cipher_default_key_value)); diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 7c970d3ae358834a4230f35826d6c598661ee323..05ee7f10cc8f577532e9180ac9874adcf9343d0f 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -164,7 +164,8 @@ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field, } en_addr = en_addrs[override][i]; - val_addr = (i == 0) ? e->val_addr_core0 : e->val_addr_core1; + if (e) + val_addr = (i == 0) ? e->val_addr_core0 : e->val_addr_core1; if (off) { b43_phy_mask(dev, en_addr, ~en_mask); diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 8cb206a89083aaa314868ef2c07b906181fe6689..4ae63f4ddfb20394d1a38c4847b5c5732887f277 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -278,7 +278,7 @@ int b43_generate_txhdr(struct b43_wldev *dev, else txhdr->phy_rate = b43_plcp_get_ratecode_cck(rate); txhdr->mac_frame_ctl = wlhdr->frame_control; - memcpy(txhdr->tx_receiver, wlhdr->addr1, 6); + memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN); /* Calculate duration for fallback rate */ if ((rate_fb == rate) || diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index 849a28c803023e03570d69ca8d6de8f60462a6c6..86588c9ff0f2b6cddee0b3a7da5455fd05b29a9b 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -215,7 +215,7 @@ static int generate_txhdr_fw3(struct b43legacy_wldev *dev, rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value); txhdr->mac_frame_ctl = wlhdr->frame_control; - memcpy(txhdr->tx_receiver, wlhdr->addr1, 6); + memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN); /* Calculate duration for fallback rate */ if ((rate_fb->hw_value == rate) || diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index e13b1a65c65fe7469882b4eee83e6a08f567bce4..3e10b801eee84bc420867815ced96a3fca3428b4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -239,7 +238,9 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, func_num = SDIO_FUNC_1; reg_size = 4; - brcmf_sdio_addrprep(sdiodev, reg_size, &addr); + ret = brcmf_sdio_addrprep(sdiodev, reg_size, &addr); + if (ret) + goto done; } do { @@ -255,6 +256,7 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, func_num, addr, data, 4); } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); +done: if (ret != 0) brcmf_err("failed with %d\n", ret); @@ -315,8 +317,36 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, *ret = retval; } +static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, + bool write, u32 addr, struct sk_buff *pkt) +{ + unsigned int req_sz; + + brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); + if (brcmf_pm_resume_error(sdiodev)) + return -EIO; + + /* Single skb use the standard mmc interface */ + req_sz = pkt->len + 3; + req_sz &= (uint)~3; + + if (write) + return sdio_memcpy_toio(sdiodev->func[fn], addr, + ((u8 *)(pkt->data)), + req_sz); + else if (fn == 1) + return sdio_memcpy_fromio(sdiodev->func[fn], + ((u8 *)(pkt->data)), + addr, req_sz); + else + /* function 2 read is FIFO operation */ + return sdio_readsb(sdiodev->func[fn], + ((u8 *)(pkt->data)), addr, + req_sz); +} + /** - * brcmf_sdio_buffrw - SDIO interface function for block data access + * brcmf_sdio_sglist_rw - SDIO interface function for block data access * @sdiodev: brcmfmac sdio device * @fn: SDIO function number * @write: direction flag @@ -327,12 +357,13 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, * stack for block data access. It assumes that the skb passed down by the * caller has already been padded and aligned. */ -static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, - bool write, u32 addr, struct sk_buff_head *pktlist) +static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, + bool write, u32 addr, + struct sk_buff_head *pktlist) { unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset; - unsigned int max_blks, max_req_sz, orig_offset, dst_offset; - unsigned short max_seg_sz, seg_sz; + unsigned int max_req_sz, orig_offset, dst_offset; + unsigned short max_seg_cnt, seg_sz; unsigned char *pkt_data, *orig_data, *dst_data; struct sk_buff *pkt_next = NULL, *local_pkt_next; struct sk_buff_head local_list, *target_list; @@ -341,7 +372,6 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, struct mmc_data mmc_dat; struct sg_table st; struct scatterlist *sgl; - struct mmc_host *host; int ret = 0; if (!pktlist->qlen) @@ -351,27 +381,6 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, if (brcmf_pm_resume_error(sdiodev)) return -EIO; - /* Single skb use the standard mmc interface */ - if (pktlist->qlen == 1) { - pkt_next = pktlist->next; - req_sz = pkt_next->len + 3; - req_sz &= (uint)~3; - - if (write) - return sdio_memcpy_toio(sdiodev->func[fn], addr, - ((u8 *)(pkt_next->data)), - req_sz); - else if (fn == 1) - return sdio_memcpy_fromio(sdiodev->func[fn], - ((u8 *)(pkt_next->data)), - addr, req_sz); - else - /* function 2 read is FIFO operation */ - return sdio_readsb(sdiodev->func[fn], - ((u8 *)(pkt_next->data)), addr, - req_sz); - } - target_list = pktlist; /* for host with broken sg support, prepare a page aligned list */ __skb_queue_head_init(&local_list); @@ -398,38 +407,46 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, target_list = &local_list; } - host = sdiodev->func[fn]->card->host; func_blk_sz = sdiodev->func[fn]->cur_blksize; - /* Blocks per command is limited by host count, host transfer - * size and the maximum for IO_RW_EXTENDED of 511 blocks. - */ - max_blks = min_t(unsigned int, host->max_blk_count, 511u); - max_req_sz = min_t(unsigned int, host->max_req_size, - max_blks * func_blk_sz); - max_seg_sz = min_t(unsigned short, host->max_segs, SG_MAX_SINGLE_ALLOC); - max_seg_sz = min_t(unsigned short, max_seg_sz, target_list->qlen); + max_req_sz = sdiodev->max_request_size; + max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count, + target_list->qlen); seg_sz = target_list->qlen; pkt_offset = 0; pkt_next = target_list->next; - if (sg_alloc_table(&st, max_seg_sz, GFP_KERNEL)) { + if (sg_alloc_table(&st, max_seg_cnt, GFP_KERNEL)) { ret = -ENOMEM; goto exit; } + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + mmc_dat.sg = st.sgl; + mmc_dat.blksz = func_blk_sz; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + mmc_cmd.opcode = SD_IO_RW_EXTENDED; + mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ + mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */ + mmc_cmd.arg |= 1<<27; /* block mode */ + /* for function 1 the addr will be incremented */ + mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + while (seg_sz) { req_sz = 0; sg_cnt = 0; - memset(&mmc_req, 0, sizeof(struct mmc_request)); - memset(&mmc_cmd, 0, sizeof(struct mmc_command)); - memset(&mmc_dat, 0, sizeof(struct mmc_data)); sgl = st.sgl; /* prep sg table */ while (pkt_next != (struct sk_buff *)target_list) { pkt_data = pkt_next->data + pkt_offset; sg_data_sz = pkt_next->len - pkt_offset; - if (sg_data_sz > host->max_seg_size) - sg_data_sz = host->max_seg_size; + if (sg_data_sz > sdiodev->max_segment_size) + sg_data_sz = sdiodev->max_segment_size; if (sg_data_sz > max_req_sz - req_sz) sg_data_sz = max_req_sz - req_sz; @@ -444,7 +461,7 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, pkt_next = pkt_next->next; } - if (req_sz >= max_req_sz || sg_cnt >= max_seg_sz) + if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt) break; } seg_sz -= sg_cnt; @@ -455,27 +472,17 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, ret = -ENOTBLK; goto exit; } - mmc_dat.sg = st.sgl; + mmc_dat.sg_len = sg_cnt; - mmc_dat.blksz = func_blk_sz; mmc_dat.blocks = req_sz / func_blk_sz; - mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; - mmc_cmd.opcode = SD_IO_RW_EXTENDED; - mmc_cmd.arg = write ? 1<<31 : 0; /* write flag */ - mmc_cmd.arg |= (fn & 0x7) << 28; /* SDIO func num */ - mmc_cmd.arg |= 1<<27; /* block mode */ - /* incrementing addr for function 1 */ - mmc_cmd.arg |= (fn == 1) ? 1<<26 : 0; mmc_cmd.arg |= (addr & 0x1FFFF) << 9; /* address */ mmc_cmd.arg |= mmc_dat.blocks & 0x1FF; /* block count */ - mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; - mmc_req.cmd = &mmc_cmd; - mmc_req.data = &mmc_dat; + /* incrementing addr for function 1 */ if (fn == 1) addr += req_sz; mmc_set_data_timeout(&mmc_dat, sdiodev->func[fn]->card); - mmc_wait_for_req(host, &mmc_req); + mmc_wait_for_req(sdiodev->func[fn]->card->host, &mmc_req); ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; if (ret != 0) { @@ -546,7 +553,6 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, { uint width; int err = 0; - struct sk_buff_head pkt_list; brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); @@ -556,19 +562,17 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, if (err) goto done; - skb_queue_head_init(&pkt_list); - skb_queue_tail(&pkt_list, pkt); - err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, &pkt_list); - skb_dequeue_tail(&pkt_list); + err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pkt); done: return err; } int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq) + uint flags, struct sk_buff_head *pktq, uint totlen) { - uint incr_fix; + struct sk_buff *glom_skb; + struct sk_buff *skb; uint width; int err = 0; @@ -580,8 +584,22 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, if (err) goto done; - incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC; - err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq); + if (pktq->qlen == 1) + err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq->next); + else if (!sdiodev->sg_support) { + glom_skb = brcmu_pkt_buf_get_skb(totlen); + if (!glom_skb) + return -ENOMEM; + err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, glom_skb); + if (err) + goto done; + + skb_queue_walk(pktq, skb) { + memcpy(skb->data, glom_skb->data, skb->len); + skb_pull(glom_skb, skb->len); + } + } else + err = brcmf_sdio_sglist_rw(sdiodev, fn, false, addr, pktq); done: return err; @@ -592,7 +610,7 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, u8 *buf, uint nbytes) { struct sk_buff *mypkt; - struct sk_buff_head pktq; + uint width; int err; mypkt = brcmu_pkt_buf_get_skb(nbytes); @@ -603,10 +621,12 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, } memcpy(mypkt->data, buf, nbytes); - __skb_queue_head_init(&pktq); - __skb_queue_tail(&pktq, mypkt); - err = brcmf_sdcard_send_pkt(sdiodev, addr, fn, flags, &pktq); - __skb_dequeue_tail(&pktq); + + width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; + err = brcmf_sdio_addrprep(sdiodev, width, &addr); + + if (!err) + err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, mypkt); brcmu_pkt_buf_free_skb(mypkt); return err; @@ -617,16 +637,26 @@ int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, uint flags, struct sk_buff_head *pktq) { + struct sk_buff *skb; uint width; - int err = 0; + int err; brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pktq->qlen); width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - brcmf_sdio_addrprep(sdiodev, width, &addr); + err = brcmf_sdio_addrprep(sdiodev, width, &addr); + if (err) + return err; - err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, pktq); + if (pktq->qlen == 1 || !sdiodev->sg_support) + skb_queue_walk(pktq, skb) { + err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, skb); + if (err) + break; + } + else + err = brcmf_sdio_sglist_rw(sdiodev, fn, true, addr, pktq); return err; } @@ -639,7 +669,6 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, struct sk_buff *pkt; u32 sdaddr; uint dsize; - struct sk_buff_head pkt_list; dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size); pkt = dev_alloc_skb(dsize); @@ -648,7 +677,6 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, return -EIO; } pkt->priority = 0; - skb_queue_head_init(&pkt_list); /* Determine initial transfer parameters */ sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK; @@ -676,10 +704,8 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, skb_put(pkt, dsize); if (write) memcpy(pkt->data, data, dsize); - skb_queue_tail(&pkt_list, pkt); bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write, - sdaddr, &pkt_list); - skb_dequeue_tail(&pkt_list); + sdaddr, pkt); if (bcmerror) { brcmf_err("membytes transfer failed\n"); break; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index c3462b75bd080d4f7e9d72dd979f86bd5c894319..905704e335d7164b90a4ecb6fd51c2324911c154 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include /* request_irq() */ @@ -34,6 +35,7 @@ #include #include #include "sdio_host.h" +#include "sdio_chip.h" #include "dhd_dbg.h" #include "dhd_bus.h" @@ -41,13 +43,6 @@ #define DMA_ALIGN_MASK 0x03 -#define SDIO_DEVICE_ID_BROADCOM_43143 43143 -#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324 -#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 -#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 -#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 -#define SDIO_DEVICE_ID_BROADCOM_4335 0x4335 - #define SDIO_FUNC1_BLOCKSIZE 64 #define SDIO_FUNC2_BLOCKSIZE 512 @@ -58,7 +53,8 @@ static const struct sdio_device_id brcmf_sdmmc_ids[] = { {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4335)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, + SDIO_DEVICE_ID_BROADCOM_4335_4339)}, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); @@ -320,6 +316,8 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, int err; struct brcmf_sdio_dev *sdiodev; struct brcmf_bus *bus_if; + struct mmc_host *host; + uint max_blocks; brcmf_dbg(SDIO, "Enter\n"); brcmf_dbg(SDIO, "Class=%x\n", func->class); @@ -366,6 +364,20 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, brcmf_err("F2 error, probe failed %d...\n", err); goto fail; } + + /* + * determine host related variables after brcmf_sdio_probe() + * as func->cur_blksize is properly set and F2 init has been + * completed successfully. + */ + host = func->card->host; + sdiodev->sg_support = host->max_segs > 1; + max_blocks = min_t(uint, host->max_blk_count, 511u); + sdiodev->max_request_size = min_t(uint, host->max_req_size, + max_blocks * func->cur_blksize); + sdiodev->max_segment_count = min_t(uint, host->max_segs, + SG_MAX_SINGLE_ALLOC); + sdiodev->max_segment_size = host->max_seg_size; brcmf_dbg(SDIO, "F2 init completed...\n"); return 0; @@ -466,7 +478,7 @@ static int brcmf_sdio_pd_probe(struct platform_device *pdev) { brcmf_dbg(SDIO, "Enter\n"); - brcmfmac_sdio_pdata = pdev->dev.platform_data; + brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev); if (brcmfmac_sdio_pdata->power_on) brcmfmac_sdio_pdata->power_on(); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 2eb9e642c9bf80d604bea8d201d86a0bb5c1a524..899a2ada5b8212f28e30632551910b67474e6080 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -97,8 +97,6 @@ #define WLC_PHY_TYPE_LCN 8 #define WLC_PHY_TYPE_NULL 0xf -#define BRCMF_EVENTING_MASK_LEN 16 - #define TOE_TX_CSUM_OL 0x00000001 #define TOE_RX_CSUM_OL 0x00000002 @@ -632,29 +630,29 @@ struct brcmf_skb_reorder_data { u8 *reorder; }; -extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev); +int brcmf_netdev_wait_pend8021x(struct net_device *ndev); /* Return pointer to interface name */ -extern char *brcmf_ifname(struct brcmf_pub *drvr, int idx); +char *brcmf_ifname(struct brcmf_pub *drvr, int idx); /* Query dongle */ -extern int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, - uint cmd, void *buf, uint len); -extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, - void *buf, uint len); +int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, + void *buf, uint len); +int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, + void *buf, uint len); /* Remove any protocol-specific data header. */ -extern int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, - struct sk_buff *rxp); +int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, + struct sk_buff *rxp); -extern int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); -extern struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, - s32 ifidx, char *name, u8 *mac_addr); -extern void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); +int brcmf_net_attach(struct brcmf_if *ifp, bool rtnl_locked); +struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, + char *name, u8 *mac_addr); +void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); -extern u32 brcmf_get_chip_info(struct brcmf_if *ifp); -extern void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, - bool success); +u32 brcmf_get_chip_info(struct brcmf_if *ifp); +void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, + bool success); #endif /* _BRCMF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 74156f84180ca19b87df7ff244358f0ab830dab3..a6eb09e5d46f408acf9205937ebffd772b15846d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -132,35 +132,34 @@ struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus) * interface functions from common layer */ -extern bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, - struct sk_buff *pkt, int prec); +bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, + int prec); /* Receive frame for delivery to OS. Callee disposes of rxp. */ -extern void brcmf_rx_frames(struct device *dev, struct sk_buff_head *rxlist); +void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp); /* Indication from bus module regarding presence/insertion of dongle. */ -extern int brcmf_attach(uint bus_hdrlen, struct device *dev); +int brcmf_attach(uint bus_hdrlen, struct device *dev); /* Indication from bus module regarding removal/absence of dongle */ -extern void brcmf_detach(struct device *dev); +void brcmf_detach(struct device *dev); /* Indication from bus module that dongle should be reset */ -extern void brcmf_dev_reset(struct device *dev); +void brcmf_dev_reset(struct device *dev); /* Indication from bus module to change flow-control state */ -extern void brcmf_txflowblock(struct device *dev, bool state); +void brcmf_txflowblock(struct device *dev, bool state); /* Notify the bus has transferred the tx packet to firmware */ -extern void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, - bool success); +void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); -extern int brcmf_bus_start(struct device *dev); +int brcmf_bus_start(struct device *dev); #ifdef CONFIG_BRCMFMAC_SDIO -extern void brcmf_sdio_exit(void); -extern void brcmf_sdio_init(void); -extern void brcmf_sdio_register(void); +void brcmf_sdio_exit(void); +void brcmf_sdio_init(void); +void brcmf_sdio_register(void); #endif #ifdef CONFIG_BRCMFMAC_USB -extern void brcmf_usb_exit(void); -extern void brcmf_usb_register(void); +void brcmf_usb_exit(void); +void brcmf_usb_register(void); #endif #endif /* _BRCMF_BUS_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 40e7f854e10f9634b44e4475b3c78cab02ea4dc9..64e9cff241b9156745bab110010520bfba730394 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -509,9 +509,8 @@ netif_rx: } } -void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) +void brcmf_rx_frame(struct device *dev, struct sk_buff *skb) { - struct sk_buff *skb, *pnext; struct brcmf_if *ifp; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; @@ -519,29 +518,24 @@ void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list) u8 ifidx; int ret; - brcmf_dbg(DATA, "Enter: %s: count=%u\n", dev_name(dev), - skb_queue_len(skb_list)); + brcmf_dbg(DATA, "Enter: %s: rxp=%p\n", dev_name(dev), skb); - skb_queue_walk_safe(skb_list, skb, pnext) { - skb_unlink(skb, skb_list); - - /* process and remove protocol-specific header */ - ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb); - ifp = drvr->iflist[ifidx]; - - if (ret || !ifp || !ifp->ndev) { - if ((ret != -ENODATA) && ifp) - ifp->stats.rx_errors++; - brcmu_pkt_buf_free_skb(skb); - continue; - } + /* process and remove protocol-specific header */ + ret = brcmf_proto_hdrpull(drvr, true, &ifidx, skb); + ifp = drvr->iflist[ifidx]; - rd = (struct brcmf_skb_reorder_data *)skb->cb; - if (rd->reorder) - brcmf_rxreorder_process_info(ifp, rd->reorder, skb); - else - brcmf_netif_rx(ifp, skb); + if (ret || !ifp || !ifp->ndev) { + if ((ret != -ENODATA) && ifp) + ifp->stats.rx_errors++; + brcmu_pkt_buf_free_skb(skb); + return; } + + rd = (struct brcmf_skb_reorder_data *)skb->cb; + if (rd->reorder) + brcmf_rxreorder_process_info(ifp, rd->reorder, skb); + else + brcmf_netif_rx(ifp, skb); } void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h index ef9179883748f0aa9637900a6e7bab1b476cd937..53c6e710f2cb243c9997e9bb6f453a60b3f7634b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h @@ -22,21 +22,21 @@ */ /* Linkage, sets prot link and updates hdrlen in pub */ -extern int brcmf_proto_attach(struct brcmf_pub *drvr); +int brcmf_proto_attach(struct brcmf_pub *drvr); /* Unlink, frees allocated protocol memory (including brcmf_proto) */ -extern void brcmf_proto_detach(struct brcmf_pub *drvr); +void brcmf_proto_detach(struct brcmf_pub *drvr); /* Stop protocol: sync w/dongle state. */ -extern void brcmf_proto_stop(struct brcmf_pub *drvr); +void brcmf_proto_stop(struct brcmf_pub *drvr); /* Add any protocol-specific data header. * Caller must reserve prot_hdrlen prepend space. */ -extern void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx, u8 offset, - struct sk_buff *txp); +void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx, u8 offset, + struct sk_buff *txp); /* Sets dongle media info (drv_version, mac address). */ -extern int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); +int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); #endif /* _BRCMF_PROTO_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 1aa75d5951b82720f21f0ff486e701252837333b..b02953c4ade721235fbe0e4ed662aaa422ab21f7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -275,11 +275,6 @@ struct rte_console { /* Flags for SDH calls */ #define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED) -#define BRCMF_SDIO_FW_NAME "brcm/brcmfmac-sdio.bin" -#define BRCMF_SDIO_NV_NAME "brcm/brcmfmac-sdio.txt" -MODULE_FIRMWARE(BRCMF_SDIO_FW_NAME); -MODULE_FIRMWARE(BRCMF_SDIO_NV_NAME); - #define BRCMF_IDLE_IMMEDIATE (-1) /* Enter idle immediately */ #define BRCMF_IDLE_ACTIVE 0 /* Do not request any SD clock change * when idle @@ -454,9 +449,6 @@ struct brcmf_sdio { struct work_struct datawork; atomic_t dpc_tskcnt; - const struct firmware *firmware; - u32 fw_ptr; - bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; bool sr_enabled; /* SaveRestore enabled */ @@ -493,6 +485,100 @@ enum brcmf_sdio_frmtype { BRCMF_SDIO_FT_SUB, }; +#define BCM43143_FIRMWARE_NAME "brcm/brcmfmac43143-sdio.bin" +#define BCM43143_NVRAM_NAME "brcm/brcmfmac43143-sdio.txt" +#define BCM43241B0_FIRMWARE_NAME "brcm/brcmfmac43241b0-sdio.bin" +#define BCM43241B0_NVRAM_NAME "brcm/brcmfmac43241b0-sdio.txt" +#define BCM43241B4_FIRMWARE_NAME "brcm/brcmfmac43241b4-sdio.bin" +#define BCM43241B4_NVRAM_NAME "brcm/brcmfmac43241b4-sdio.txt" +#define BCM4329_FIRMWARE_NAME "brcm/brcmfmac4329-sdio.bin" +#define BCM4329_NVRAM_NAME "brcm/brcmfmac4329-sdio.txt" +#define BCM4330_FIRMWARE_NAME "brcm/brcmfmac4330-sdio.bin" +#define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" +#define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" +#define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" +#define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" +#define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" + +MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43143_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); +MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4329_NVRAM_NAME); +MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4330_NVRAM_NAME); +MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4334_NVRAM_NAME); +MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4335_NVRAM_NAME); + +struct brcmf_firmware_names { + u32 chipid; + u32 revmsk; + const char *bin; + const char *nv; +}; + +enum brcmf_firmware_type { + BRCMF_FIRMWARE_BIN, + BRCMF_FIRMWARE_NVRAM +}; + +#define BRCMF_FIRMWARE_NVRAM(name) \ + name ## _FIRMWARE_NAME, name ## _NVRAM_NAME + +static const struct brcmf_firmware_names brcmf_fwname_data[] = { + { BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, + { BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, + { BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, + { BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, + { BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, + { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, + { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) } +}; + + +static const struct firmware *brcmf_sdbrcm_get_fw(struct brcmf_sdio *bus, + enum brcmf_firmware_type type) +{ + const struct firmware *fw; + const char *name; + int err, i; + + for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { + if (brcmf_fwname_data[i].chipid == bus->ci->chip && + brcmf_fwname_data[i].revmsk & BIT(bus->ci->chiprev)) { + switch (type) { + case BRCMF_FIRMWARE_BIN: + name = brcmf_fwname_data[i].bin; + break; + case BRCMF_FIRMWARE_NVRAM: + name = brcmf_fwname_data[i].nv; + break; + default: + brcmf_err("invalid firmware type (%d)\n", type); + return NULL; + } + goto found; + } + } + brcmf_err("Unknown chipid %d [%d]\n", + bus->ci->chip, bus->ci->chiprev); + return NULL; + +found: + err = request_firmware(&fw, name, &bus->sdiodev->func[2]->dev); + if ((err) || (!fw)) { + brcmf_err("fail to request firmware %s (%d)\n", name, err); + return NULL; + } + + return fw; +} + static void pkt_align(struct sk_buff *p, int len, int align) { uint datalign; @@ -1061,6 +1147,8 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, u8 rx_seq, fc, tx_seq_max; u32 swheader; + trace_brcmf_sdpcm_hdr(false, header); + /* hw header */ len = get_unaligned_le16(header); checksum = get_unaligned_le16(header + sizeof(u16)); @@ -1183,6 +1271,7 @@ static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header, SDPCM_DOFFSET_MASK; *(((__le32 *)header) + 1) = cpu_to_le32(sw_header); *(((__le32 *)header) + 2) = 0; + trace_brcmf_sdpcm_hdr(true, header); } static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) @@ -1303,7 +1392,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) sdio_claim_host(bus->sdiodev->func[1]); errcode = brcmf_sdcard_recv_chain(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, &bus->glom); + SDIO_FUNC_2, F2SYNC, &bus->glom, dlen); sdio_release_host(bus->sdiodev->func[1]); bus->sdcnt.f2rxdata++; @@ -1406,13 +1495,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) bus->glom.qlen, pfirst, pfirst->data, pfirst->len, pfirst->next, pfirst->prev); + skb_unlink(pfirst, &bus->glom); + brcmf_rx_frame(bus->sdiodev->dev, pfirst); + bus->sdcnt.rxglompkts++; } - /* sent any remaining packets up */ - if (bus->glom.qlen) - brcmf_rx_frames(bus->sdiodev->dev, &bus->glom); bus->sdcnt.rxglomframes++; - bus->sdcnt.rxglompkts += bus->glom.qlen; } return num; } @@ -1557,7 +1645,6 @@ static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) { struct sk_buff *pkt; /* Packet for event or data frames */ - struct sk_buff_head pktlist; /* needed for bus interface */ u16 pad; /* Number of pad bytes to read */ uint rxleft = 0; /* Remaining number of frames allowed */ int ret; /* Return code from calls */ @@ -1759,9 +1846,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) continue; } - skb_queue_head_init(&pktlist); - skb_queue_tail(&pktlist, pkt); - brcmf_rx_frames(bus->sdiodev->dev, &pktlist); + brcmf_rx_frame(bus->sdiodev->dev, pkt); } rxcount = maxframes - rxleft; @@ -1786,10 +1871,65 @@ brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) return; } +/** + * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for + * bus layer usage. + */ /* flag marking a dummy skb added for DMA alignment requirement */ -#define DUMMY_SKB_FLAG 0x10000 +#define ALIGN_SKB_FLAG 0x8000 /* bit mask of data length chopped from the previous packet */ -#define DUMMY_SKB_CHOP_LEN_MASK 0xffff +#define ALIGN_SKB_CHOP_LEN_MASK 0x7fff + +static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq, + struct sk_buff *pkt, uint chan) +{ + struct sk_buff *pkt_pad; + u16 tail_pad, tail_chop, sg_align; + unsigned int blksize; + u8 *dat_buf; + int ntail; + + blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize; + sg_align = 4; + if (sdiodev->pdata && sdiodev->pdata->sd_sgentry_align > 4) + sg_align = sdiodev->pdata->sd_sgentry_align; + /* sg entry alignment should be a divisor of block size */ + WARN_ON(blksize % sg_align); + + /* Check tail padding */ + pkt_pad = NULL; + tail_chop = pkt->len % sg_align; + tail_pad = sg_align - tail_chop; + tail_pad += blksize - (pkt->len + tail_pad) % blksize; + if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) { + pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop); + if (pkt_pad == NULL) + return -ENOMEM; + memcpy(pkt_pad->data, + pkt->data + pkt->len - tail_chop, + tail_chop); + *(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; + skb_trim(pkt, pkt->len - tail_chop); + __skb_queue_after(pktq, pkt, pkt_pad); + } else { + ntail = pkt->data_len + tail_pad - + (pkt->end - pkt->tail); + if (skb_cloned(pkt) || ntail > 0) + if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC)) + return -ENOMEM; + if (skb_linearize(pkt)) + return -ENOMEM; + dat_buf = (u8 *)(pkt->data); + __skb_put(pkt, tail_pad); + } + + if (pkt_pad) + return pkt->len + tail_chop; + else + return pkt->len - tail_pad; +} + /** * brcmf_sdio_txpkt_prep - packet preparation for transmit * @bus: brcmf_sdio structure pointer @@ -1806,24 +1946,16 @@ static int brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, uint chan) { - u16 head_pad, tail_pad, tail_chop, head_align, sg_align; - int ntail; - struct sk_buff *pkt_next, *pkt_new; + u16 head_pad, head_align; + struct sk_buff *pkt_next; u8 *dat_buf; - unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize; + int err; struct brcmf_sdio_hdrinfo hd_info = {0}; /* SDIO ADMA requires at least 32 bit alignment */ head_align = 4; - sg_align = 4; - if (bus->sdiodev->pdata) { - head_align = bus->sdiodev->pdata->sd_head_align > 4 ? - bus->sdiodev->pdata->sd_head_align : 4; - sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ? - bus->sdiodev->pdata->sd_sgentry_align : 4; - } - /* sg entry alignment should be a divisor of block size */ - WARN_ON(blksize % sg_align); + if (bus->sdiodev->pdata && bus->sdiodev->pdata->sd_head_align > 4) + head_align = bus->sdiodev->pdata->sd_head_align; pkt_next = pktq->next; dat_buf = (u8 *)(pkt_next->data); @@ -1842,40 +1974,20 @@ brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, memset(dat_buf, 0, head_pad + bus->tx_hdrlen); } - /* Check tail padding */ - pkt_new = NULL; - tail_chop = pkt_next->len % sg_align; - tail_pad = sg_align - tail_chop; - tail_pad += blksize - (pkt_next->len + tail_pad) % blksize; - if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) { - pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop); - if (pkt_new == NULL) - return -ENOMEM; - memcpy(pkt_new->data, - pkt_next->data + pkt_next->len - tail_chop, - tail_chop); - *(u32 *)(pkt_new->cb) = DUMMY_SKB_FLAG + tail_chop; - skb_trim(pkt_next, pkt_next->len - tail_chop); - __skb_queue_after(pktq, pkt_next, pkt_new); + if (bus->sdiodev->sg_support && pktq->qlen > 1) { + err = brcmf_sdio_txpkt_prep_sg(bus->sdiodev, pktq, + pkt_next, chan); + if (err < 0) + return err; + hd_info.len = (u16)err; } else { - ntail = pkt_next->data_len + tail_pad - - (pkt_next->end - pkt_next->tail); - if (skb_cloned(pkt_next) || ntail > 0) - if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC)) - return -ENOMEM; - if (skb_linearize(pkt_next)) - return -ENOMEM; - dat_buf = (u8 *)(pkt_next->data); - __skb_put(pkt_next, tail_pad); + hd_info.len = pkt_next->len; } - /* Now prep the header */ - if (pkt_new) - hd_info.len = pkt_next->len + tail_chop; - else - hd_info.len = pkt_next->len - tail_pad; hd_info.channel = chan; hd_info.dat_offset = head_pad + bus->tx_hdrlen; + + /* Now fill the header */ brcmf_sdio_hdpack(bus, dat_buf, &hd_info); if (BRCMF_BYTES_ON() && @@ -1908,8 +2020,8 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) skb_queue_walk_safe(pktq, pkt_next, tmp) { dummy_flags = *(u32 *)(pkt_next->cb); - if (dummy_flags & DUMMY_SKB_FLAG) { - chop_len = dummy_flags & DUMMY_SKB_CHOP_LEN_MASK; + if (dummy_flags & ALIGN_SKB_FLAG) { + chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; if (chop_len) { pkt_prev = pkt_next->prev; memcpy(pkt_prev->data + pkt_prev->len, @@ -3037,69 +3149,43 @@ static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter) return true; } -static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_sdio *bus) -{ - if (bus->firmware->size < bus->fw_ptr + len) - len = bus->firmware->size - bus->fw_ptr; - - memcpy(buf, &bus->firmware->data[bus->fw_ptr], len); - bus->fw_ptr += len; - return len; -} - static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus) { + const struct firmware *fw; + int err; int offset; - uint len; - u8 *memblock = NULL, *memptr; - int ret; - u8 idx; - - brcmf_dbg(INFO, "Enter\n"); - - ret = request_firmware(&bus->firmware, BRCMF_SDIO_FW_NAME, - &bus->sdiodev->func[2]->dev); - if (ret) { - brcmf_err("Fail to request firmware %d\n", ret); - return ret; - } - bus->fw_ptr = 0; - - memptr = memblock = kmalloc(MEMBLOCK + BRCMF_SDALIGN, GFP_ATOMIC); - if (memblock == NULL) { - ret = -ENOMEM; - goto err; - } - if ((u32)(unsigned long)memblock % BRCMF_SDALIGN) - memptr += (BRCMF_SDALIGN - - ((u32)(unsigned long)memblock % BRCMF_SDALIGN)); - - offset = bus->ci->rambase; - - /* Download image */ - len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus); - idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4); - if (BRCMF_MAX_CORENUM != idx) - memcpy(&bus->ci->rst_vec, memptr, sizeof(bus->ci->rst_vec)); - while (len) { - ret = brcmf_sdio_ramrw(bus->sdiodev, true, offset, memptr, len); - if (ret) { + int address; + int len; + + fw = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_BIN); + if (fw == NULL) + return -ENOENT; + + if (brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4) != + BRCMF_MAX_CORENUM) + memcpy(&bus->ci->rst_vec, fw->data, sizeof(bus->ci->rst_vec)); + + err = 0; + offset = 0; + address = bus->ci->rambase; + while (offset < fw->size) { + len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK : + fw->size - offset; + err = brcmf_sdio_ramrw(bus->sdiodev, true, address, + (u8 *)&fw->data[offset], len); + if (err) { brcmf_err("error %d on writing %d membytes at 0x%08x\n", - ret, MEMBLOCK, offset); - goto err; + err, len, address); + goto failure; } - - offset += MEMBLOCK; - len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus); + offset += len; + address += len; } -err: - kfree(memblock); - - release_firmware(bus->firmware); - bus->fw_ptr = 0; +failure: + release_firmware(fw); - return ret; + return err; } /* @@ -3111,7 +3197,8 @@ err: * by two NULs. */ -static int brcmf_process_nvram_vars(struct brcmf_sdio *bus) +static int brcmf_process_nvram_vars(struct brcmf_sdio *bus, + const struct firmware *nv) { char *varbuf; char *dp; @@ -3120,12 +3207,12 @@ static int brcmf_process_nvram_vars(struct brcmf_sdio *bus) int ret = 0; uint buf_len, n, len; - len = bus->firmware->size; + len = nv->size; varbuf = vmalloc(len); if (!varbuf) return -ENOMEM; - memcpy(varbuf, bus->firmware->data, len); + memcpy(varbuf, nv->data, len); dp = varbuf; findNewline = false; @@ -3177,18 +3264,16 @@ err: static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus) { + const struct firmware *nv; int ret; - ret = request_firmware(&bus->firmware, BRCMF_SDIO_NV_NAME, - &bus->sdiodev->func[2]->dev); - if (ret) { - brcmf_err("Fail to request nvram %d\n", ret); - return ret; - } + nv = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_NVRAM); + if (nv == NULL) + return -ENOENT; - ret = brcmf_process_nvram_vars(bus); + ret = brcmf_process_nvram_vars(bus, nv); - release_firmware(bus->firmware); + release_firmware(nv); return ret; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h index e679214b3c98be52662e9f7f09d1e90eb0b62ec7..14bc24dc5baeb1f8272b4da0660d893c4bfc9b48 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -102,7 +102,8 @@ struct brcmf_event; BRCMF_ENUM_DEF(DCS_REQUEST, 73) \ BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ - BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) + BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \ + BRCMF_ENUM_DEF(PSTA_PRIMARY_INTF_IND, 128) #define BRCMF_ENUM_DEF(id, val) \ BRCMF_E_##id = (val), @@ -114,6 +115,8 @@ enum brcmf_fweh_event_code { }; #undef BRCMF_ENUM_DEF +#define BRCMF_EVENTING_MASK_LEN DIV_ROUND_UP(BRCMF_E_LAST, 8) + /* flags field values in struct brcmf_event_msg */ #define BRCMF_EVENT_MSG_LINK 0x01 #define BRCMF_EVENT_MSG_FLUSHTXQ 0x02 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 82f9140f3d35481766b317f4a31204fa7b4da6b7..d0cd0bf95c5af4393719a981d008049cde67756d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -168,6 +168,7 @@ enum brcmf_fws_skb_state { /** * struct brcmf_skbuff_cb - control buffer associated with skbuff. * + * @bus_flags: 2 bytes reserved for bus specific parameters * @if_flags: holds interface index and packet related flags. * @htod: host to device packet identifier (used in PKTTAG tlv). * @state: transmit state of the packet. @@ -177,6 +178,7 @@ enum brcmf_fws_skb_state { * provides 48 bytes of storage so this structure should not exceed that. */ struct brcmf_skbuff_cb { + u16 bus_flags; u16 if_flags; u32 htod; enum brcmf_fws_skb_state state; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index ca72177388b92726368a6764f86395db6f736b05..2096a14ef1fba5fcaf156140a64b2f52f4b67757 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -136,6 +137,8 @@ brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, u8 idx; idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return false; regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), @@ -154,6 +157,8 @@ brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, bool ret; idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return false; regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, NULL); @@ -261,6 +266,8 @@ brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, u32 regdata; idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return; /* if core is already in reset, just return */ regdata = brcmf_sdio_regrl(sdiodev, @@ -304,6 +311,8 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, u8 idx; idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return; /* * Must do the disable sequence first to work for @@ -368,6 +377,8 @@ brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, u32 regdata; idx = brcmf_sdio_chip_getinfidx(ci, coreid); + if (idx == BRCMF_MAX_CORENUM) + return; /* must disable first to work for arbitrary current core state */ brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits); @@ -444,6 +455,9 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, NULL); ci->chip = regdata & CID_ID_MASK; ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && + ci->chiprev >= 2) + ci->chip = BCM4339_CHIP_ID; ci->socitype = (regdata & CID_TYPE_MASK) >> CID_TYPE_SHIFT; brcmf_dbg(INFO, "chipid=0x%x chiprev=%d\n", ci->chip, ci->chiprev); @@ -541,6 +555,20 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, ci->ramsize = 0xc0000; ci->rambase = 0x180000; break; + case BCM4339_CHIP_ID: + ci->c_inf[0].wrapbase = 0x18100000; + ci->c_inf[0].cib = 0x2e084411; + ci->c_inf[1].id = BCMA_CORE_SDIO_DEV; + ci->c_inf[1].base = 0x18005000; + ci->c_inf[1].wrapbase = 0x18105000; + ci->c_inf[1].cib = 0x15004211; + ci->c_inf[2].id = BCMA_CORE_ARM_CR4; + ci->c_inf[2].base = 0x18002000; + ci->c_inf[2].wrapbase = 0x18102000; + ci->c_inf[2].cib = 0x04084411; + ci->ramsize = 0xc0000; + ci->rambase = 0x180000; + break; default: brcmf_err("chipid 0x%x is not supported\n", ci->chip); return -ENODEV; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h index 83c041f1bf4ad154afb0822d734818a7561f9ad6..507c61c991fa06c96b6adb1a0f99acefa5f74068 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h @@ -54,6 +54,14 @@ #define BRCMF_MAX_CORENUM 6 +/* SDIO device ID */ +#define SDIO_DEVICE_ID_BROADCOM_43143 43143 +#define SDIO_DEVICE_ID_BROADCOM_43241 0x4324 +#define SDIO_DEVICE_ID_BROADCOM_4329 0x4329 +#define SDIO_DEVICE_ID_BROADCOM_4330 0x4330 +#define SDIO_DEVICE_ID_BROADCOM_4334 0x4334 +#define SDIO_DEVICE_ID_BROADCOM_4335_4339 0x4335 + struct chip_core_info { u16 id; u16 rev; @@ -215,17 +223,16 @@ struct sdpcmd_regs { u16 PAD[0x80]; }; -extern int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct chip_info **ci_ptr, u32 regs); -extern void brcmf_sdio_chip_detach(struct chip_info **ci_ptr); -extern void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, - u32 drivestrength); -extern u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid); -extern void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci); -extern bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, char *nvram_dat, - uint nvram_sz); +int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, + struct chip_info **ci_ptr, u32 regs); +void brcmf_sdio_chip_detach(struct chip_info **ci_ptr); +void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, + struct chip_info *ci, u32 drivestrength); +u8 brcmf_sdio_chip_getinfidx(struct chip_info *ci, u16 coreid); +void brcmf_sdio_chip_enter_download(struct brcmf_sdio_dev *sdiodev, + struct chip_info *ci); +bool brcmf_sdio_chip_exit_download(struct brcmf_sdio_dev *sdiodev, + struct chip_info *ci, char *nvram_dat, + uint nvram_sz); #endif /* _BRCMFMAC_SDIO_CHIP_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 2b5407f002e53bf90b8e320d0b54384b2a200f9b..fc0d4f0129db269dd7d7139c286fa90063c3bab3 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -178,21 +178,25 @@ struct brcmf_sdio_dev { bool irq_en; /* irq enable flags */ spinlock_t irq_en_lock; bool irq_wake; /* irq wake enable flags */ + bool sg_support; + uint max_request_size; + ushort max_segment_count; + uint max_segment_size; }; /* Register/deregister interrupt handler. */ -extern int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev); -extern int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev); /* sdio device register access interface */ -extern u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -extern u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -extern void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, - u8 data, int *ret); -extern void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, - u32 data, int *ret); -extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, - void *data, bool write); +u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, + int *ret); +void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, + int *ret); +int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, + void *data, bool write); /* Buffer transfer to/from device (client) core via cmd53. * fn: function number @@ -206,22 +210,17 @@ extern int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, * Returns 0 or error code. * NOTE: Async operation is not currently supported. */ -extern int -brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq); -extern int -brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes); - -extern int -brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt); -extern int -brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes); -extern int -brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq); +int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, + uint flags, struct sk_buff_head *pktq); +int brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, + uint flags, u8 *buf, uint nbytes); + +int brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, + uint flags, struct sk_buff *pkt); +int brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, + uint flags, u8 *buf, uint nbytes); +int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, + uint flags, struct sk_buff_head *pktq, uint totlen); /* Flags bits */ @@ -237,46 +236,43 @@ brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, * nbytes: number of bytes to transfer to/from buf * Returns 0 or error code. */ -extern int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, - u32 addr, u8 *buf, uint nbytes); -extern int brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, - u32 address, u8 *data, uint size); +int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, + u8 *buf, uint nbytes); +int brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + u8 *data, uint size); /* Issue an abort to the specified function */ -extern int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn); +int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn); /* platform specific/high level functions */ -extern int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); -extern int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev); /* attach, return handler on success, NULL if failed. * The handler shall be provided by all subsequent calls. No local cache * cfghdl points to the starting address of pci device mapped memory */ -extern int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev); -extern void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev); /* read or write one byte using cmd52 */ -extern int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, - uint fnc, uint addr, u8 *byte); +int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc, + uint addr, u8 *byte); /* read or write 2/4 bytes using cmd53 */ -extern int -brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, - uint rw, uint fnc, uint addr, - u32 *word, uint nbyte); +int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc, + uint addr, u32 *word, uint nbyte); /* Watchdog timer interface for pm ops */ -extern void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, - bool enable); +void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable); -extern void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev); -extern void brcmf_sdbrcm_disconnect(void *ptr); -extern void brcmf_sdbrcm_isr(void *arg); +void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev); +void brcmf_sdbrcm_disconnect(void *ptr); +void brcmf_sdbrcm_isr(void *arg); -extern void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick); +void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick); -extern void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, - wait_queue_head_t *wq); -extern bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev); +void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, + wait_queue_head_t *wq); +bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev); #endif /* _BRCM_SDH_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h index bc29171128991a9836d8237c556be00482f66d00..3c67529b90743123691d08aa7ed4d3fdb4a45c6d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/tracepoint.h @@ -78,13 +78,15 @@ TRACE_EVENT(brcmf_hexdump, TP_ARGS(data, len), TP_STRUCT__entry( __field(unsigned long, len) + __field(unsigned long, addr) __dynamic_array(u8, hdata, len) ), TP_fast_assign( __entry->len = len; + __entry->addr = (unsigned long)data; memcpy(__get_dynamic_array(hdata), data, len); ), - TP_printk("hexdump [length=%lu]", __entry->len) + TP_printk("hexdump [addr=%lx, length=%lu]", __entry->addr, __entry->len) ); TRACE_EVENT(brcmf_bdchdr, @@ -108,6 +110,23 @@ TRACE_EVENT(brcmf_bdchdr, TP_printk("bdc: prio=%d siglen=%d", __entry->prio, __entry->siglen) ); +TRACE_EVENT(brcmf_sdpcm_hdr, + TP_PROTO(bool tx, void *data), + TP_ARGS(tx, data), + TP_STRUCT__entry( + __field(u8, tx) + __field(u16, len) + __array(u8, hdr, 12) + ), + TP_fast_assign( + memcpy(__entry->hdr, data, 12); + __entry->len = __entry->hdr[0] | (__entry->hdr[1] << 8); + __entry->tx = tx ? 1 : 0; + ), + TP_printk("sdpcm: %s len %u, seq %d", __entry->tx ? "TX" : "RX", + __entry->len, __entry->hdr[4]) +); + #ifdef CONFIG_BRCM_TRACING #undef TRACE_INCLUDE_PATH diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index f4aea47e0730996ec059f02dbc1f2edaf98c04d1..422f44c631756b2332dc10d4c1fb24c8172343ff 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -435,7 +435,6 @@ static void brcmf_usb_rx_complete(struct urb *urb) struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; struct brcmf_usbdev_info *devinfo = req->devinfo; struct sk_buff *skb; - struct sk_buff_head skbq; brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); brcmf_usb_del_fromq(devinfo, req); @@ -450,10 +449,8 @@ static void brcmf_usb_rx_complete(struct urb *urb) } if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { - skb_queue_head_init(&skbq); - skb_queue_tail(&skbq, skb); skb_put(skb, urb->actual_length); - brcmf_rx_frames(devinfo->dev, &skbq); + brcmf_rx_frame(devinfo->dev, skb); brcmf_usb_rx_refill(devinfo, req); } else { brcmu_pkt_buf_free_skb(skb); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h index a8a267b5b87aebfd6bd40006c628c7964afc9cca..2d08c155c23bcd93afba34a0f28d17e2e68e46c5 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h @@ -172,19 +172,19 @@ struct si_info { /* AMBA Interconnect exported externs */ -extern u32 ai_core_cflags(struct bcma_device *core, u32 mask, u32 val); +u32 ai_core_cflags(struct bcma_device *core, u32 mask, u32 val); /* === exported functions === */ -extern struct si_pub *ai_attach(struct bcma_bus *pbus); -extern void ai_detach(struct si_pub *sih); -extern uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val); -extern void ai_clkctl_init(struct si_pub *sih); -extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih); -extern bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode); -extern bool ai_deviceremoved(struct si_pub *sih); +struct si_pub *ai_attach(struct bcma_bus *pbus); +void ai_detach(struct si_pub *sih); +uint ai_cc_reg(struct si_pub *sih, uint regoff, u32 mask, u32 val); +void ai_clkctl_init(struct si_pub *sih); +u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih); +bool ai_clkctl_cc(struct si_pub *sih, enum bcma_clkmode mode); +bool ai_deviceremoved(struct si_pub *sih); /* Enable Ex-PA for 4313 */ -extern void ai_epa_4313war(struct si_pub *sih); +void ai_epa_4313war(struct si_pub *sih); static inline u32 ai_get_cccaps(struct si_pub *sih) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h index 73d01e5861090d99d875c886ae8783c9dff5561c..03bdcf29bd50ee228d269b4ae4359159ffa1df9d 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h @@ -37,17 +37,17 @@ struct brcms_ampdu_session { u16 dma_len; }; -extern void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, - struct brcms_c_info *wlc); -extern int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, - struct sk_buff *p); -extern void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session); +void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, + struct brcms_c_info *wlc); +int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, + struct sk_buff *p); +void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session); -extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); -extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu); -extern void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, - struct sk_buff *p, struct tx_status *txs); -extern void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc); -extern void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu); +struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); +void brcms_c_ampdu_detach(struct ampdu_info *ampdu); +void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, + struct sk_buff *p, struct tx_status *txs); +void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc); +void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu); #endif /* _BRCM_AMPDU_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h b/drivers/net/wireless/brcm80211/brcmsmac/antsel.h index 97ea3881a8ec7c969ee40839b65878650def99e6..a3d487ab19646bcc2dc2be25accc559052bc7afc 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/antsel.h @@ -17,13 +17,11 @@ #ifndef _BRCM_ANTSEL_H_ #define _BRCM_ANTSEL_H_ -extern struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc); -extern void brcms_c_antsel_detach(struct antsel_info *asi); -extern void brcms_c_antsel_init(struct antsel_info *asi); -extern void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, - bool sel, - u8 id, u8 fbid, u8 *antcfg, - u8 *fbantcfg); -extern u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel); +struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc); +void brcms_c_antsel_detach(struct antsel_info *asi); +void brcms_c_antsel_init(struct antsel_info *asi); +void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel, + u8 id, u8 fbid, u8 *antcfg, u8 *fbantcfg); +u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel); #endif /* _BRCM_ANTSEL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/brcm80211/brcmsmac/channel.h index 006483a0abe6452d022600666b5ddf9ad5d28248..39dd3a5b2979a572613c5335473cd121295b4649 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.h @@ -32,20 +32,16 @@ #define BRCMS_DFS_EU (BRCMS_DFS_TPC | BRCMS_RADAR_TYPE_EU) /* Flag for DFS EU */ -extern struct brcms_cm_info * -brcms_c_channel_mgr_attach(struct brcms_c_info *wlc); +struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc); -extern void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm); +void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm); -extern bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, - u16 chspec); +bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, u16 chspec); -extern void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, - u16 chanspec, - struct txpwr_limits *txpwr); -extern void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, - u16 chanspec, - u8 local_constraint_qdbm); -extern void brcms_c_regd_init(struct brcms_c_info *wlc); +void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, + struct txpwr_limits *txpwr); +void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, u16 chanspec, + u8 local_constraint_qdbm); +void brcms_c_regd_init(struct brcms_c_info *wlc); #endif /* _WLC_CHANNEL_H */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h index 4090032e81a29f5d4e9aaf0b9258ae8d8c80bfcc..198053dfc3102ccb17a12b051372d84ff720a32e 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h @@ -88,26 +88,26 @@ struct brcms_info { }; /* misc callbacks */ -extern void brcms_init(struct brcms_info *wl); -extern uint brcms_reset(struct brcms_info *wl); -extern void brcms_intrson(struct brcms_info *wl); -extern u32 brcms_intrsoff(struct brcms_info *wl); -extern void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask); -extern int brcms_up(struct brcms_info *wl); -extern void brcms_down(struct brcms_info *wl); -extern void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, - bool state, int prio); -extern bool brcms_rfkill_set_hw_state(struct brcms_info *wl); +void brcms_init(struct brcms_info *wl); +uint brcms_reset(struct brcms_info *wl); +void brcms_intrson(struct brcms_info *wl); +u32 brcms_intrsoff(struct brcms_info *wl); +void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask); +int brcms_up(struct brcms_info *wl); +void brcms_down(struct brcms_info *wl); +void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, + bool state, int prio); +bool brcms_rfkill_set_hw_state(struct brcms_info *wl); /* timer functions */ -extern struct brcms_timer *brcms_init_timer(struct brcms_info *wl, - void (*fn) (void *arg), void *arg, - const char *name); -extern void brcms_free_timer(struct brcms_timer *timer); -extern void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic); -extern bool brcms_del_timer(struct brcms_timer *timer); -extern void brcms_dpc(unsigned long data); -extern void brcms_timer(struct brcms_timer *t); -extern void brcms_fatal_error(struct brcms_info *wl); +struct brcms_timer *brcms_init_timer(struct brcms_info *wl, + void (*fn) (void *arg), void *arg, + const char *name); +void brcms_free_timer(struct brcms_timer *timer); +void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic); +bool brcms_del_timer(struct brcms_timer *timer); +void brcms_dpc(unsigned long data); +void brcms_timer(struct brcms_timer *t); +void brcms_fatal_error(struct brcms_info *wl); #endif /* _BRCM_MAC80211_IF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 4608e0eb14939d12a73a4af5d3c2408e27bc877c..8138f1cff4e56a3bd801dc48f1917963012653ca 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -1906,14 +1906,14 @@ static void brcms_c_get_macaddr(struct brcms_hardware *wlc_hw, u8 etheraddr[ETH_ /* If macaddr exists, use it (Sromrev4, CIS, ...). */ if (!is_zero_ether_addr(sprom->il0mac)) { - memcpy(etheraddr, sprom->il0mac, 6); + memcpy(etheraddr, sprom->il0mac, ETH_ALEN); return; } if (wlc_hw->_nbands > 1) - memcpy(etheraddr, sprom->et1mac, 6); + memcpy(etheraddr, sprom->et1mac, ETH_ALEN); else - memcpy(etheraddr, sprom->il0mac, 6); + memcpy(etheraddr, sprom->il0mac, ETH_ALEN); } /* power both the pll and external oscillator on/off */ @@ -5695,7 +5695,7 @@ static bool brcms_c_chipmatch_pci(struct bcma_device *core) return true; if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID)) return true; - if (device == BCM4313_D11N2G_ID) + if (device == BCM4313_D11N2G_ID || device == BCM4313_CHIP_ID) return true; if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID)) return true; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.h b/drivers/net/wireless/brcm80211/brcmsmac/main.h index b5d7a38b53fe3d8baf6f8cbc28ba8cef0aaa2b8a..c4d135cff04ad2f7883c783fb96244bbbabfa370 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.h @@ -616,66 +616,54 @@ struct brcms_bss_cfg { struct brcms_bss_info *current_bss; }; -extern int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, - struct sk_buff *p); -extern int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, - uint *blocks); - -extern int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config); -extern void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags); -extern u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, - uint mac_len); -extern u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, - u32 rspec, - bool use_rspec, u16 mimo_ctlchbw); -extern u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, - u32 rts_rate, - u32 frame_rate, - u8 rts_preamble_type, - u8 frame_preamble_type, uint frame_len, - bool ba); -extern void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, - struct ieee80211_sta *sta, - void (*dma_callback_fn)); -extern void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend); -extern int brcms_c_set_nmode(struct brcms_c_info *wlc); -extern void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, - u32 bcn_rate); -extern void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, - u8 antsel_type); -extern void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, - u16 chanspec, - bool mute, struct txpwr_limits *txpwr); -extern void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, - u16 v); -extern u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset); -extern void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, - u16 val, int bands); -extern void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags); -extern void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val); -extern void brcms_b_phy_reset(struct brcms_hardware *wlc_hw); -extern void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw); -extern void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw); -extern void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, - u32 override_bit); -extern void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, - u32 override_bit); -extern void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, - int offset, int len, void *buf); -extern u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate); -extern void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, - uint offset, const void *buf, int len, - u32 sel); -extern void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, - void *buf, int len, u32 sel); -extern void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode); -extern u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw); -extern void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk); -extern void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk); -extern void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on); -extern void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant); -extern void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, - u8 stf_mode); -extern void brcms_c_init_scb(struct scb *scb); +int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p); +int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, + uint *blocks); + +int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config); +void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags); +u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, uint mac_len); +u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, + bool use_rspec, u16 mimo_ctlchbw); +u16 brcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, + u32 rts_rate, u32 frame_rate, + u8 rts_preamble_type, u8 frame_preamble_type, + uint frame_len, bool ba); +void brcms_c_inval_dma_pkts(struct brcms_hardware *hw, + struct ieee80211_sta *sta, void (*dma_callback_fn)); +void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend); +int brcms_c_set_nmode(struct brcms_c_info *wlc); +void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, u32 bcn_rate); +void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type); +void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, + bool mute, struct txpwr_limits *txpwr); +void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v); +u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset); +void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val, + int bands); +void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags); +void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val); +void brcms_b_phy_reset(struct brcms_hardware *wlc_hw); +void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw); +void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw); +void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, + u32 override_bit); +void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, + u32 override_bit); +void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, + int len, void *buf); +u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate); +void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, + const void *buf, int len, u32 sel); +void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, + void *buf, int len, u32 sel); +void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode); +u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw); +void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk); +void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk); +void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on); +void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant); +void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode); +void brcms_c_init_scb(struct scb *scb); #endif /* _BRCM_MAIN_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h index e34a71e7d24204a0cc6dd0a3cb12fd043ec2636a..4d3734f48d9c7a444ef7aab2383655b471a0a5f2 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h @@ -179,121 +179,106 @@ struct shared_phy_params { }; -extern struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp); -extern struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh, - struct bcma_device *d11core, - int bandtype, struct wiphy *wiphy); -extern void wlc_phy_detach(struct brcms_phy_pub *ppi); - -extern bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, - u16 *phyrev, u16 *radioid, - u16 *radiover); -extern bool wlc_phy_get_encore(struct brcms_phy_pub *pih); -extern u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih); - -extern void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate); -extern void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate); -extern void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec); -extern void wlc_phy_watchdog(struct brcms_phy_pub *ppi); -extern int wlc_phy_down(struct brcms_phy_pub *ppi); -extern u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih); -extern void wlc_phy_cal_init(struct brcms_phy_pub *ppi); -extern void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init); - -extern void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, - u16 chanspec); -extern u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi); -extern void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, - u16 newch); -extern u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi); -extern void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw); - -extern int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, - struct d11rxhdr *rxh); -extern void wlc_phy_por_inform(struct brcms_phy_pub *ppi); -extern void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi); -extern bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi); - -extern void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag); - -extern void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on); -extern void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on); - - -extern void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi); - -extern void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, - bool wide_filter); -extern void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, - struct brcms_chanvec *channels); -extern u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, - uint band); - -extern void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan, - u8 *_min_, u8 *_max_, int rate); -extern void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, - uint chan, u8 *_max_, u8 *_min_); -extern void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, - uint band, s32 *, s32 *, u32 *); -extern void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, - struct txpwr_limits *, - u16 chanspec); -extern int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, - bool *override); -extern int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, - bool override); -extern void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, - struct txpwr_limits *); -extern bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi); -extern void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, - bool hwpwrctrl); -extern u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi); -extern u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi); -extern bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih); - -extern void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, - u8 rxchain); -extern void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, - u8 rxchain); -extern void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, - u8 *rxchain); -extern u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih); -extern s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, - u16 chanspec); -extern void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val); - -extern void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason); -extern void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi); -extern void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock); -extern void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi); - -extern void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val); -extern void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi); -extern void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val); -extern void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags); - -extern void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type); - -extern void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, - struct tx_power *power, uint channel); - -extern void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal); -extern bool wlc_phy_test_ison(struct brcms_phy_pub *ppi); -extern void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, - u8 txpwr_percent); -extern void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war); -extern void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, - bool bf_preempt); -extern void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap); - -extern void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end); - -extern void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi); -extern void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi); - -extern const u8 *wlc_phy_get_ofdm_rate_lookup(void); - -extern s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi, - u8 mcs_offset); -extern s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset); +struct shared_phy *wlc_phy_shared_attach(struct shared_phy_params *shp); +struct brcms_phy_pub *wlc_phy_attach(struct shared_phy *sh, + struct bcma_device *d11core, int bandtype, + struct wiphy *wiphy); +void wlc_phy_detach(struct brcms_phy_pub *ppi); + +bool wlc_phy_get_phyversion(struct brcms_phy_pub *pih, u16 *phytype, + u16 *phyrev, u16 *radioid, u16 *radiover); +bool wlc_phy_get_encore(struct brcms_phy_pub *pih); +u32 wlc_phy_get_coreflags(struct brcms_phy_pub *pih); + +void wlc_phy_hw_clk_state_upd(struct brcms_phy_pub *ppi, bool newstate); +void wlc_phy_hw_state_upd(struct brcms_phy_pub *ppi, bool newstate); +void wlc_phy_init(struct brcms_phy_pub *ppi, u16 chanspec); +void wlc_phy_watchdog(struct brcms_phy_pub *ppi); +int wlc_phy_down(struct brcms_phy_pub *ppi); +u32 wlc_phy_clk_bwbits(struct brcms_phy_pub *pih); +void wlc_phy_cal_init(struct brcms_phy_pub *ppi); +void wlc_phy_antsel_init(struct brcms_phy_pub *ppi, bool lut_init); + +void wlc_phy_chanspec_set(struct brcms_phy_pub *ppi, u16 chanspec); +u16 wlc_phy_chanspec_get(struct brcms_phy_pub *ppi); +void wlc_phy_chanspec_radio_set(struct brcms_phy_pub *ppi, u16 newch); +u16 wlc_phy_bw_state_get(struct brcms_phy_pub *ppi); +void wlc_phy_bw_state_set(struct brcms_phy_pub *ppi, u16 bw); + +int wlc_phy_rssi_compute(struct brcms_phy_pub *pih, struct d11rxhdr *rxh); +void wlc_phy_por_inform(struct brcms_phy_pub *ppi); +void wlc_phy_noise_sample_intr(struct brcms_phy_pub *ppi); +bool wlc_phy_bist_check_phy(struct brcms_phy_pub *ppi); + +void wlc_phy_set_deaf(struct brcms_phy_pub *ppi, bool user_flag); + +void wlc_phy_switch_radio(struct brcms_phy_pub *ppi, bool on); +void wlc_phy_anacore(struct brcms_phy_pub *ppi, bool on); + + +void wlc_phy_BSSinit(struct brcms_phy_pub *ppi, bool bonlyap, int rssi); + +void wlc_phy_chanspec_ch14_widefilter_set(struct brcms_phy_pub *ppi, + bool wide_filter); +void wlc_phy_chanspec_band_validch(struct brcms_phy_pub *ppi, uint band, + struct brcms_chanvec *channels); +u16 wlc_phy_chanspec_band_firstch(struct brcms_phy_pub *ppi, uint band); + +void wlc_phy_txpower_sromlimit(struct brcms_phy_pub *ppi, uint chan, u8 *_min_, + u8 *_max_, int rate); +void wlc_phy_txpower_sromlimit_max_get(struct brcms_phy_pub *ppi, uint chan, + u8 *_max_, u8 *_min_); +void wlc_phy_txpower_boardlimit_band(struct brcms_phy_pub *ppi, uint band, + s32 *, s32 *, u32 *); +void wlc_phy_txpower_limit_set(struct brcms_phy_pub *ppi, struct txpwr_limits *, + u16 chanspec); +int wlc_phy_txpower_get(struct brcms_phy_pub *ppi, uint *qdbm, bool *override); +int wlc_phy_txpower_set(struct brcms_phy_pub *ppi, uint qdbm, bool override); +void wlc_phy_txpower_target_set(struct brcms_phy_pub *ppi, + struct txpwr_limits *); +bool wlc_phy_txpower_hw_ctrl_get(struct brcms_phy_pub *ppi); +void wlc_phy_txpower_hw_ctrl_set(struct brcms_phy_pub *ppi, bool hwpwrctrl); +u8 wlc_phy_txpower_get_target_min(struct brcms_phy_pub *ppi); +u8 wlc_phy_txpower_get_target_max(struct brcms_phy_pub *ppi); +bool wlc_phy_txpower_ipa_ison(struct brcms_phy_pub *pih); + +void wlc_phy_stf_chain_init(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); +void wlc_phy_stf_chain_set(struct brcms_phy_pub *pih, u8 txchain, u8 rxchain); +void wlc_phy_stf_chain_get(struct brcms_phy_pub *pih, u8 *txchain, u8 *rxchain); +u8 wlc_phy_stf_chain_active_get(struct brcms_phy_pub *pih); +s8 wlc_phy_stf_ssmode_get(struct brcms_phy_pub *pih, u16 chanspec); +void wlc_phy_ldpc_override_set(struct brcms_phy_pub *ppi, bool val); + +void wlc_phy_cal_perical(struct brcms_phy_pub *ppi, u8 reason); +void wlc_phy_noise_sample_request_external(struct brcms_phy_pub *ppi); +void wlc_phy_edcrs_lock(struct brcms_phy_pub *pih, bool lock); +void wlc_phy_cal_papd_recal(struct brcms_phy_pub *ppi); + +void wlc_phy_ant_rxdiv_set(struct brcms_phy_pub *ppi, u8 val); +void wlc_phy_clear_tssi(struct brcms_phy_pub *ppi); +void wlc_phy_hold_upd(struct brcms_phy_pub *ppi, u32 id, bool val); +void wlc_phy_mute_upd(struct brcms_phy_pub *ppi, bool val, u32 flags); + +void wlc_phy_antsel_type_set(struct brcms_phy_pub *ppi, u8 antsel_type); + +void wlc_phy_txpower_get_current(struct brcms_phy_pub *ppi, + struct tx_power *power, uint channel); + +void wlc_phy_initcal_enable(struct brcms_phy_pub *pih, bool initcal); +bool wlc_phy_test_ison(struct brcms_phy_pub *ppi); +void wlc_phy_txpwr_percent_set(struct brcms_phy_pub *ppi, u8 txpwr_percent); +void wlc_phy_ofdm_rateset_war(struct brcms_phy_pub *pih, bool war); +void wlc_phy_bf_preempt_enable(struct brcms_phy_pub *pih, bool bf_preempt); +void wlc_phy_machwcap_set(struct brcms_phy_pub *ppi, u32 machwcap); + +void wlc_phy_runbist_config(struct brcms_phy_pub *ppi, bool start_end); + +void wlc_phy_freqtrack_start(struct brcms_phy_pub *ppi); +void wlc_phy_freqtrack_end(struct brcms_phy_pub *ppi); + +const u8 *wlc_phy_get_ofdm_rate_lookup(void); + +s8 wlc_phy_get_tx_power_offset_by_mcs(struct brcms_phy_pub *ppi, + u8 mcs_offset); +s8 wlc_phy_get_tx_power_offset(struct brcms_phy_pub *ppi, u8 tbl_offset); #endif /* _BRCM_PHY_HAL_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h index 1dc767c31653b29a9458cc08b9d113ab1c6979ad..4960f7d2680430313d42e3e7e64787a21c0f8dde 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h @@ -910,113 +910,103 @@ struct lcnphy_radio_regs { u8 do_init_g; }; -extern u16 read_phy_reg(struct brcms_phy *pi, u16 addr); -extern void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); - -extern u16 read_radio_reg(struct brcms_phy *pi, u16 addr); -extern void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); -extern void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, - u16 val); -extern void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask); - -extern void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); - -extern void wlc_phyreg_enter(struct brcms_phy_pub *pih); -extern void wlc_phyreg_exit(struct brcms_phy_pub *pih); -extern void wlc_radioreg_enter(struct brcms_phy_pub *pih); -extern void wlc_radioreg_exit(struct brcms_phy_pub *pih); - -extern void wlc_phy_read_table(struct brcms_phy *pi, - const struct phytbl_info *ptbl_info, - u16 tblAddr, u16 tblDataHi, - u16 tblDatalo); -extern void wlc_phy_write_table(struct brcms_phy *pi, - const struct phytbl_info *ptbl_info, - u16 tblAddr, u16 tblDataHi, u16 tblDatalo); -extern void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, - uint tbl_offset, u16 tblAddr, u16 tblDataHi, - u16 tblDataLo); -extern void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val); - -extern void write_phy_channel_reg(struct brcms_phy *pi, uint val); -extern void wlc_phy_txpower_update_shm(struct brcms_phy *pi); - -extern u8 wlc_phy_nbits(s32 value); -extern void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core); - -extern uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, - struct radio_20xx_regs *radioregs); -extern uint wlc_phy_init_radio_regs(struct brcms_phy *pi, - const struct radio_regs *radioregs, - u16 core_offset); - -extern void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi); - -extern void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on); -extern void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, - s32 *eps_imag); - -extern void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi); -extern void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi); - -extern bool wlc_phy_attach_nphy(struct brcms_phy *pi); -extern bool wlc_phy_attach_lcnphy(struct brcms_phy *pi); - -extern void wlc_phy_detach_lcnphy(struct brcms_phy *pi); - -extern void wlc_phy_init_nphy(struct brcms_phy *pi); -extern void wlc_phy_init_lcnphy(struct brcms_phy *pi); - -extern void wlc_phy_cal_init_nphy(struct brcms_phy *pi); -extern void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi); - -extern void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, - u16 chanspec); -extern void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, - u16 chanspec); -extern void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi, - u16 chanspec); -extern int wlc_phy_channel2freq(uint channel); -extern int wlc_phy_chanspec_freq2bandrange_lpssn(uint); -extern int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec); - -extern void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode); -extern s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi); - -extern void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi); -extern void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi); -extern void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi); - -extern void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index); -extern void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable); -extern void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi); -extern void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, - u16 max_val, bool iqcalmode); - -extern void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, - u8 *max_pwr, u8 rate_id); -extern void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, - u8 rate_mcs_end, - u8 rate_ofdm_start); -extern void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, - u8 rate_ofdm_start, - u8 rate_ofdm_end, - u8 rate_mcs_start); - -extern u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode); -extern s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode); -extern s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode); -extern s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode); -extern void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi); -extern void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel); -extern void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode); -extern void wlc_2064_vco_cal(struct brcms_phy *pi); - -extern void wlc_phy_txpower_recalc_target(struct brcms_phy *pi); +u16 read_phy_reg(struct brcms_phy *pi, u16 addr); +void write_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); +void and_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); +void or_phy_reg(struct brcms_phy *pi, u16 addr, u16 val); +void mod_phy_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); + +u16 read_radio_reg(struct brcms_phy *pi, u16 addr); +void or_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); +void and_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); +void mod_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask, u16 val); +void xor_radio_reg(struct brcms_phy *pi, u16 addr, u16 mask); + +void write_radio_reg(struct brcms_phy *pi, u16 addr, u16 val); + +void wlc_phyreg_enter(struct brcms_phy_pub *pih); +void wlc_phyreg_exit(struct brcms_phy_pub *pih); +void wlc_radioreg_enter(struct brcms_phy_pub *pih); +void wlc_radioreg_exit(struct brcms_phy_pub *pih); + +void wlc_phy_read_table(struct brcms_phy *pi, + const struct phytbl_info *ptbl_info, + u16 tblAddr, u16 tblDataHi, u16 tblDatalo); +void wlc_phy_write_table(struct brcms_phy *pi, + const struct phytbl_info *ptbl_info, + u16 tblAddr, u16 tblDataHi, u16 tblDatalo); +void wlc_phy_table_addr(struct brcms_phy *pi, uint tbl_id, uint tbl_offset, + u16 tblAddr, u16 tblDataHi, u16 tblDataLo); +void wlc_phy_table_data_write(struct brcms_phy *pi, uint width, u32 val); + +void write_phy_channel_reg(struct brcms_phy *pi, uint val); +void wlc_phy_txpower_update_shm(struct brcms_phy *pi); + +u8 wlc_phy_nbits(s32 value); +void wlc_phy_compute_dB(u32 *cmplx_pwr, s8 *p_dB, u8 core); + +uint wlc_phy_init_radio_regs_allbands(struct brcms_phy *pi, + struct radio_20xx_regs *radioregs); +uint wlc_phy_init_radio_regs(struct brcms_phy *pi, + const struct radio_regs *radioregs, + u16 core_offset); + +void wlc_phy_txpower_ipa_upd(struct brcms_phy *pi); + +void wlc_phy_do_dummy_tx(struct brcms_phy *pi, bool ofdm, bool pa_on); +void wlc_phy_papd_decode_epsilon(u32 epsilon, s32 *eps_real, s32 *eps_imag); + +void wlc_phy_cal_perical_mphase_reset(struct brcms_phy *pi); +void wlc_phy_cal_perical_mphase_restart(struct brcms_phy *pi); + +bool wlc_phy_attach_nphy(struct brcms_phy *pi); +bool wlc_phy_attach_lcnphy(struct brcms_phy *pi); + +void wlc_phy_detach_lcnphy(struct brcms_phy *pi); + +void wlc_phy_init_nphy(struct brcms_phy *pi); +void wlc_phy_init_lcnphy(struct brcms_phy *pi); + +void wlc_phy_cal_init_nphy(struct brcms_phy *pi); +void wlc_phy_cal_init_lcnphy(struct brcms_phy *pi); + +void wlc_phy_chanspec_set_nphy(struct brcms_phy *pi, u16 chanspec); +void wlc_phy_chanspec_set_lcnphy(struct brcms_phy *pi, u16 chanspec); +void wlc_phy_chanspec_set_fixup_lcnphy(struct brcms_phy *pi, u16 chanspec); +int wlc_phy_channel2freq(uint channel); +int wlc_phy_chanspec_freq2bandrange_lpssn(uint); +int wlc_phy_chanspec_bandrange_get(struct brcms_phy *, u16 chanspec); + +void wlc_lcnphy_set_tx_pwr_ctrl(struct brcms_phy *pi, u16 mode); +s8 wlc_lcnphy_get_current_tx_pwr_idx(struct brcms_phy *pi); + +void wlc_phy_txpower_recalc_target_nphy(struct brcms_phy *pi); +void wlc_lcnphy_txpower_recalc_target(struct brcms_phy *pi); +void wlc_phy_txpower_recalc_target_lcnphy(struct brcms_phy *pi); + +void wlc_lcnphy_set_tx_pwr_by_index(struct brcms_phy *pi, int index); +void wlc_lcnphy_tx_pu(struct brcms_phy *pi, bool bEnable); +void wlc_lcnphy_stop_tx_tone(struct brcms_phy *pi); +void wlc_lcnphy_start_tx_tone(struct brcms_phy *pi, s32 f_kHz, u16 max_val, + bool iqcalmode); + +void wlc_phy_txpower_sromlimit_get_nphy(struct brcms_phy *pi, uint chan, + u8 *max_pwr, u8 rate_id); +void wlc_phy_ofdm_to_mcs_powers_nphy(u8 *power, u8 rate_mcs_start, + u8 rate_mcs_end, u8 rate_ofdm_start); +void wlc_phy_mcs_to_ofdm_powers_nphy(u8 *power, u8 rate_ofdm_start, + u8 rate_ofdm_end, u8 rate_mcs_start); + +u16 wlc_lcnphy_tempsense(struct brcms_phy *pi, bool mode); +s16 wlc_lcnphy_tempsense_new(struct brcms_phy *pi, bool mode); +s8 wlc_lcnphy_tempsense_degree(struct brcms_phy *pi, bool mode); +s8 wlc_lcnphy_vbatsense(struct brcms_phy *pi, bool mode); +void wlc_phy_carrier_suppress_lcnphy(struct brcms_phy *pi); +void wlc_lcnphy_crsuprs(struct brcms_phy *pi, int channel); +void wlc_lcnphy_epa_switch(struct brcms_phy *pi, bool mode); +void wlc_2064_vco_cal(struct brcms_phy *pi); + +void wlc_phy_txpower_recalc_target(struct brcms_phy *pi); #define LCNPHY_TBL_ID_PAPDCOMPDELTATBL 0x18 #define LCNPHY_TX_POWER_TABLE_SIZE 128 @@ -1030,26 +1020,24 @@ extern void wlc_phy_txpower_recalc_target(struct brcms_phy *pi); #define LCNPHY_TX_PWR_CTRL_TEMPBASED 0xE001 -extern void wlc_lcnphy_write_table(struct brcms_phy *pi, - const struct phytbl_info *pti); -extern void wlc_lcnphy_read_table(struct brcms_phy *pi, - struct phytbl_info *pti); -extern void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b); -extern void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq); -extern void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b); -extern u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi); -extern void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0, - u8 *eq0, u8 *fi0, u8 *fq0); -extern void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode); -extern void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode); -extern bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi); -extern void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi); -extern s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1); -extern void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, - s8 *cck_pwr); -extern void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi); - -extern s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index); +void wlc_lcnphy_write_table(struct brcms_phy *pi, + const struct phytbl_info *pti); +void wlc_lcnphy_read_table(struct brcms_phy *pi, struct phytbl_info *pti); +void wlc_lcnphy_set_tx_iqcc(struct brcms_phy *pi, u16 a, u16 b); +void wlc_lcnphy_set_tx_locc(struct brcms_phy *pi, u16 didq); +void wlc_lcnphy_get_tx_iqcc(struct brcms_phy *pi, u16 *a, u16 *b); +u16 wlc_lcnphy_get_tx_locc(struct brcms_phy *pi); +void wlc_lcnphy_get_radio_loft(struct brcms_phy *pi, u8 *ei0, u8 *eq0, u8 *fi0, + u8 *fq0); +void wlc_lcnphy_calib_modes(struct brcms_phy *pi, uint mode); +void wlc_lcnphy_deaf_mode(struct brcms_phy *pi, bool mode); +bool wlc_phy_tpc_isenabled_lcnphy(struct brcms_phy *pi); +void wlc_lcnphy_tx_pwr_update_npt(struct brcms_phy *pi); +s32 wlc_lcnphy_tssi2dbm(s32 tssi, s32 a1, s32 b0, s32 b1); +void wlc_lcnphy_get_tssi(struct brcms_phy *pi, s8 *ofdm_pwr, s8 *cck_pwr); +void wlc_lcnphy_tx_power_adjustment(struct brcms_phy_pub *ppi); + +s32 wlc_lcnphy_rx_signal_power(struct brcms_phy *pi, s32 gain_index); #define NPHY_MAX_HPVGA1_INDEX 10 #define NPHY_DEF_HPVGA1_INDEXLIMIT 7 @@ -1060,9 +1048,8 @@ struct phy_iq_est { u32 q_pwr; }; -extern void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, - bool enable); -extern void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode); +void wlc_phy_stay_in_carriersearch_nphy(struct brcms_phy *pi, bool enable); +void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode); #define wlc_phy_write_table_nphy(pi, pti) \ wlc_phy_write_table(pi, pti, 0x72, 0x74, 0x73) @@ -1076,10 +1063,10 @@ extern void wlc_nphy_deaf_mode(struct brcms_phy *pi, bool mode); #define wlc_nphy_table_data_write(pi, w, v) \ wlc_phy_table_data_write((pi), (w), (v)) -extern void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o, - u32 w, void *d); -extern void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, - u32, const void *); +void wlc_phy_table_read_nphy(struct brcms_phy *pi, u32, u32 l, u32 o, u32 w, + void *d); +void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, u32, + const void *); #define PHY_IPA(pi) \ ((pi->ipa2g_on && CHSPEC_IS2G(pi->radio_chanspec)) || \ @@ -1089,73 +1076,67 @@ extern void wlc_phy_table_write_nphy(struct brcms_phy *pi, u32, u32, u32, if (NREV_LT((pi)->pubpi.phy_rev, 3)) \ (void)bcma_read32(pi->d11core, D11REGOFFS(maccontrol)) -extern void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype); -extern void wlc_phy_aci_reset_nphy(struct brcms_phy *pi); -extern void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en); - -extern u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan); -extern void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on); - -extern void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi); - -extern void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd); -extern s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi); - -extern u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val); - -extern void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, - u16 num_samps, u8 wait_time, - u8 wait_for_crs); - -extern void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, - struct nphy_iq_comp *comp); -extern void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi); - -extern void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, - u8 rxcore_bitmask); -extern u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih); - -extern void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type); -extern void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi); -extern void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi); -extern void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi); -extern u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi); - -extern struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi); -extern int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, - struct nphy_txgains target_gain, - bool full, bool m); -extern int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, - struct nphy_txgains target_gain, - u8 type, bool d); -extern void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, - s8 txpwrindex, bool res); -extern void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type); -extern int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, - s32 *rssi_buf, u8 nsamps); -extern void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi); -extern int wlc_phy_aci_scan_nphy(struct brcms_phy *pi); -extern void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, - s32 dBm_targetpower, bool debug); -extern int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, - u8 mode, u8, bool); -extern void wlc_phy_stopplayback_nphy(struct brcms_phy *pi); -extern void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, - u8 num_samps); -extern void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi); - -extern int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, - struct d11rxhdr *rxh); +void wlc_phy_cal_perical_nphy_run(struct brcms_phy *pi, u8 caltype); +void wlc_phy_aci_reset_nphy(struct brcms_phy *pi); +void wlc_phy_pa_override_nphy(struct brcms_phy *pi, bool en); + +u8 wlc_phy_get_chan_freq_range_nphy(struct brcms_phy *pi, uint chan); +void wlc_phy_switch_radio_nphy(struct brcms_phy *pi, bool on); + +void wlc_phy_stf_chain_upd_nphy(struct brcms_phy *pi); + +void wlc_phy_force_rfseq_nphy(struct brcms_phy *pi, u8 cmd); +s16 wlc_phy_tempsense_nphy(struct brcms_phy *pi); + +u16 wlc_phy_classifier_nphy(struct brcms_phy *pi, u16 mask, u16 val); + +void wlc_phy_rx_iq_est_nphy(struct brcms_phy *pi, struct phy_iq_est *est, + u16 num_samps, u8 wait_time, u8 wait_for_crs); + +void wlc_phy_rx_iq_coeffs_nphy(struct brcms_phy *pi, u8 write, + struct nphy_iq_comp *comp); +void wlc_phy_aci_and_noise_reduction_nphy(struct brcms_phy *pi); + +void wlc_phy_rxcore_setstate_nphy(struct brcms_phy_pub *pih, u8 rxcore_bitmask); +u8 wlc_phy_rxcore_getstate_nphy(struct brcms_phy_pub *pih); + +void wlc_phy_txpwrctrl_enable_nphy(struct brcms_phy *pi, u8 ctrl_type); +void wlc_phy_txpwr_fixpower_nphy(struct brcms_phy *pi); +void wlc_phy_txpwr_apply_nphy(struct brcms_phy *pi); +void wlc_phy_txpwr_papd_cal_nphy(struct brcms_phy *pi); +u16 wlc_phy_txpwr_idx_get_nphy(struct brcms_phy *pi); + +struct nphy_txgains wlc_phy_get_tx_gain_nphy(struct brcms_phy *pi); +int wlc_phy_cal_txiqlo_nphy(struct brcms_phy *pi, + struct nphy_txgains target_gain, bool full, bool m); +int wlc_phy_cal_rxiq_nphy(struct brcms_phy *pi, struct nphy_txgains target_gain, + u8 type, bool d); +void wlc_phy_txpwr_index_nphy(struct brcms_phy *pi, u8 core_mask, + s8 txpwrindex, bool res); +void wlc_phy_rssisel_nphy(struct brcms_phy *pi, u8 core, u8 rssi_type); +int wlc_phy_poll_rssi_nphy(struct brcms_phy *pi, u8 rssi_type, + s32 *rssi_buf, u8 nsamps); +void wlc_phy_rssi_cal_nphy(struct brcms_phy *pi); +int wlc_phy_aci_scan_nphy(struct brcms_phy *pi); +void wlc_phy_cal_txgainctrl_nphy(struct brcms_phy *pi, s32 dBm_targetpower, + bool debug); +int wlc_phy_tx_tone_nphy(struct brcms_phy *pi, u32 f_kHz, u16 max_val, u8 mode, + u8, bool); +void wlc_phy_stopplayback_nphy(struct brcms_phy *pi); +void wlc_phy_est_tonepwr_nphy(struct brcms_phy *pi, s32 *qdBm_pwrbuf, + u8 num_samps); +void wlc_phy_radio205x_vcocal_nphy(struct brcms_phy *pi); + +int wlc_phy_rssi_compute_nphy(struct brcms_phy *pi, struct d11rxhdr *rxh); #define NPHY_TESTPATTERN_BPHY_EVM 0 #define NPHY_TESTPATTERN_BPHY_RFCS 1 -extern void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs); +void wlc_phy_nphy_tkip_rifs_war(struct brcms_phy *pi, u8 rifs); void wlc_phy_get_pwrdet_offsets(struct brcms_phy *pi, s8 *cckoffset, s8 *ofdmoffset); -extern s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, - u16 chanspec); +s8 wlc_phy_upd_rssi_offset(struct brcms_phy *pi, s8 rssi, u16 chanspec); -extern bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih); +bool wlc_phy_n_txpower_ipa_ison(struct brcms_phy *pih); #endif /* _BRCM_PHY_INT_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h index 2c5b66b75970939ba6ee0ffe2b709faecbc3428e..dd8774717adee148134cb85229f422c72fa143e2 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h @@ -124,56 +124,49 @@ struct brcms_phy; -extern struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, - struct brcms_info *wl, - struct brcms_c_info *wlc); -extern void wlc_phy_shim_detach(struct phy_shim_info *physhim); +struct phy_shim_info *wlc_phy_shim_attach(struct brcms_hardware *wlc_hw, + struct brcms_info *wl, + struct brcms_c_info *wlc); +void wlc_phy_shim_detach(struct phy_shim_info *physhim); /* PHY to WL utility functions */ -extern struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, - void (*fn) (struct brcms_phy *pi), - void *arg, const char *name); -extern void wlapi_free_timer(struct wlapi_timer *t); -extern void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic); -extern bool wlapi_del_timer(struct wlapi_timer *t); -extern void wlapi_intrson(struct phy_shim_info *physhim); -extern u32 wlapi_intrsoff(struct phy_shim_info *physhim); -extern void wlapi_intrsrestore(struct phy_shim_info *physhim, - u32 macintmask); - -extern void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, - u16 v); -extern u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset); -extern void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, - u16 mask, u16 val, int bands); -extern void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags); -extern void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim); -extern void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode); -extern void wlapi_enable_mac(struct phy_shim_info *physhim); -extern void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, - u32 val); -extern void wlapi_bmac_phy_reset(struct phy_shim_info *physhim); -extern void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw); -extern void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk); -extern void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk); -extern void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on); -extern void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim); -extern void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info * - physhim); -extern void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info * - physhim); -extern void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o, - int len, void *buf); -extern u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, - u8 rate); -extern void wlapi_ucode_sample_init(struct phy_shim_info *physhim); -extern void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint, - void *buf, int, u32 sel); -extern void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint, - const void *buf, int, u32); - -extern void wlapi_high_update_phy_mode(struct phy_shim_info *physhim, - u32 phy_mode); -extern u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim); +struct wlapi_timer *wlapi_init_timer(struct phy_shim_info *physhim, + void (*fn)(struct brcms_phy *pi), + void *arg, const char *name); +void wlapi_free_timer(struct wlapi_timer *t); +void wlapi_add_timer(struct wlapi_timer *t, uint ms, int periodic); +bool wlapi_del_timer(struct wlapi_timer *t); +void wlapi_intrson(struct phy_shim_info *physhim); +u32 wlapi_intrsoff(struct phy_shim_info *physhim); +void wlapi_intrsrestore(struct phy_shim_info *physhim, u32 macintmask); + +void wlapi_bmac_write_shm(struct phy_shim_info *physhim, uint offset, u16 v); +u16 wlapi_bmac_read_shm(struct phy_shim_info *physhim, uint offset); +void wlapi_bmac_mhf(struct phy_shim_info *physhim, u8 idx, u16 mask, u16 val, + int bands); +void wlapi_bmac_corereset(struct phy_shim_info *physhim, u32 flags); +void wlapi_suspend_mac_and_wait(struct phy_shim_info *physhim); +void wlapi_switch_macfreq(struct phy_shim_info *physhim, u8 spurmode); +void wlapi_enable_mac(struct phy_shim_info *physhim); +void wlapi_bmac_mctrl(struct phy_shim_info *physhim, u32 mask, u32 val); +void wlapi_bmac_phy_reset(struct phy_shim_info *physhim); +void wlapi_bmac_bw_set(struct phy_shim_info *physhim, u16 bw); +void wlapi_bmac_phyclk_fgc(struct phy_shim_info *physhim, bool clk); +void wlapi_bmac_macphyclk_set(struct phy_shim_info *physhim, bool clk); +void wlapi_bmac_core_phypll_ctl(struct phy_shim_info *physhim, bool on); +void wlapi_bmac_core_phypll_reset(struct phy_shim_info *physhim); +void wlapi_bmac_ucode_wake_override_phyreg_set(struct phy_shim_info *physhim); +void wlapi_bmac_ucode_wake_override_phyreg_clear(struct phy_shim_info *physhim); +void wlapi_bmac_write_template_ram(struct phy_shim_info *physhim, int o, + int len, void *buf); +u16 wlapi_bmac_rate_shm_offset(struct phy_shim_info *physhim, u8 rate); +void wlapi_ucode_sample_init(struct phy_shim_info *physhim); +void wlapi_copyfrom_objmem(struct phy_shim_info *physhim, uint, void *buf, + int, u32 sel); +void wlapi_copyto_objmem(struct phy_shim_info *physhim, uint, const void *buf, + int, u32); + +void wlapi_high_update_phy_mode(struct phy_shim_info *physhim, u32 phy_mode); +u16 wlapi_bmac_get_txant(struct phy_shim_info *physhim); #endif /* _BRCM_PHY_SHIM_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h b/drivers/net/wireless/brcm80211/brcmsmac/pmu.h index 20e2012d5a3a2d2d83c8b7544f6828d14d3f6f33..a014bbc4f93555cc789bbc337adc36491da12902 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/pmu.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/pmu.h @@ -20,7 +20,7 @@ #include "types.h" -extern u16 si_pmu_fast_pwrup_delay(struct si_pub *sih); -extern u32 si_pmu_measure_alpclk(struct si_pub *sih); +u16 si_pmu_fast_pwrup_delay(struct si_pub *sih); +u32 si_pmu_measure_alpclk(struct si_pub *sih); #endif /* _BRCM_PMU_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/brcm80211/brcmsmac/pub.h index d36ea5e1cc494231e56dd18bb2f0dadc65f4304d..4da38cb4f31854a60878c5da31c22cf4e39980b6 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/pub.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/pub.h @@ -266,83 +266,76 @@ struct brcms_antselcfg { }; /* common functions for every port */ -extern struct brcms_c_info * -brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit, - bool piomode, uint *perr); -extern uint brcms_c_detach(struct brcms_c_info *wlc); -extern int brcms_c_up(struct brcms_c_info *wlc); -extern uint brcms_c_down(struct brcms_c_info *wlc); - -extern bool brcms_c_chipmatch(struct bcma_device *core); -extern void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx); -extern void brcms_c_reset(struct brcms_c_info *wlc); - -extern void brcms_c_intrson(struct brcms_c_info *wlc); -extern u32 brcms_c_intrsoff(struct brcms_c_info *wlc); -extern void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask); -extern bool brcms_c_intrsupd(struct brcms_c_info *wlc); -extern bool brcms_c_isr(struct brcms_c_info *wlc); -extern bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded); -extern bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, - struct sk_buff *sdu, - struct ieee80211_hw *hw); -extern bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid); -extern void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, - int val); -extern int brcms_c_get_header_len(void); -extern void brcms_c_set_addrmatch(struct brcms_c_info *wlc, - int match_reg_offset, - const u8 *addr); -extern void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, - const struct ieee80211_tx_queue_params *arg, - bool suspend); -extern struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc); -extern void brcms_c_ampdu_flush(struct brcms_c_info *wlc, - struct ieee80211_sta *sta, u16 tid); -extern void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, - u8 ba_wsize, uint max_rx_ampdu_bytes); -extern int brcms_c_module_register(struct brcms_pub *pub, - const char *name, struct brcms_info *hdl, - int (*down_fn)(void *handle)); -extern int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, - struct brcms_info *hdl); -extern void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc); -extern void brcms_c_enable_mac(struct brcms_c_info *wlc); -extern void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state); -extern void brcms_c_scan_start(struct brcms_c_info *wlc); -extern void brcms_c_scan_stop(struct brcms_c_info *wlc); -extern int brcms_c_get_curband(struct brcms_c_info *wlc); -extern int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel); -extern int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl); -extern void brcms_c_get_current_rateset(struct brcms_c_info *wlc, +struct brcms_c_info *brcms_c_attach(struct brcms_info *wl, + struct bcma_device *core, uint unit, + bool piomode, uint *perr); +uint brcms_c_detach(struct brcms_c_info *wlc); +int brcms_c_up(struct brcms_c_info *wlc); +uint brcms_c_down(struct brcms_c_info *wlc); + +bool brcms_c_chipmatch(struct bcma_device *core); +void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx); +void brcms_c_reset(struct brcms_c_info *wlc); + +void brcms_c_intrson(struct brcms_c_info *wlc); +u32 brcms_c_intrsoff(struct brcms_c_info *wlc); +void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask); +bool brcms_c_intrsupd(struct brcms_c_info *wlc); +bool brcms_c_isr(struct brcms_c_info *wlc); +bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded); +bool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, + struct ieee80211_hw *hw); +bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid); +void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val); +int brcms_c_get_header_len(void); +void brcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, + const u8 *addr); +void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, + const struct ieee80211_tx_queue_params *arg, + bool suspend); +struct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc); +void brcms_c_ampdu_flush(struct brcms_c_info *wlc, struct ieee80211_sta *sta, + u16 tid); +void brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, + u8 ba_wsize, uint max_rx_ampdu_bytes); +int brcms_c_module_register(struct brcms_pub *pub, const char *name, + struct brcms_info *hdl, + int (*down_fn)(void *handle)); +int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, + struct brcms_info *hdl); +void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc); +void brcms_c_enable_mac(struct brcms_c_info *wlc); +void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state); +void brcms_c_scan_start(struct brcms_c_info *wlc); +void brcms_c_scan_stop(struct brcms_c_info *wlc); +int brcms_c_get_curband(struct brcms_c_info *wlc); +int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel); +int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl); +void brcms_c_get_current_rateset(struct brcms_c_info *wlc, struct brcm_rateset *currs); -extern int brcms_c_set_rateset(struct brcms_c_info *wlc, - struct brcm_rateset *rs); -extern int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period); -extern u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx); -extern void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, +int brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs); +int brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period); +u16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx); +void brcms_c_set_shortslot_override(struct brcms_c_info *wlc, s8 sslot_override); -extern void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, - u8 interval); -extern u64 brcms_c_tsf_get(struct brcms_c_info *wlc); -extern void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf); -extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); -extern int brcms_c_get_tx_power(struct brcms_c_info *wlc); -extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); -extern void brcms_c_mute(struct brcms_c_info *wlc, bool on); -extern bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc); -extern void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr); -extern void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, - const u8 *bssid, u8 *ssid, size_t ssid_len); -extern void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr); -extern void brcms_c_update_beacon(struct brcms_c_info *wlc); -extern void brcms_c_set_new_beacon(struct brcms_c_info *wlc, - struct sk_buff *beacon, u16 tim_offset, - u16 dtim_period); -extern void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc, - struct sk_buff *probe_resp); -extern void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable); -extern void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, - size_t ssid_len); +void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval); +u64 brcms_c_tsf_get(struct brcms_c_info *wlc); +void brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf); +int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); +int brcms_c_get_tx_power(struct brcms_c_info *wlc); +bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); +void brcms_c_mute(struct brcms_c_info *wlc, bool on); +bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc); +void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr); +void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid, + u8 *ssid, size_t ssid_len); +void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr); +void brcms_c_update_beacon(struct brcms_c_info *wlc); +void brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon, + u16 tim_offset, u16 dtim_period); +void brcms_c_set_new_probe_resp(struct brcms_c_info *wlc, + struct sk_buff *probe_resp); +void brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable); +void brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len); #endif /* _BRCM_PUB_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/rate.h b/drivers/net/wireless/brcm80211/brcmsmac/rate.h index 980d578825cc0b3b131995bfcf6926bbab0bbbf1..5bb88b78ed648407dbbcb39db73d86ea503d76c9 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/rate.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/rate.h @@ -216,34 +216,30 @@ static inline u8 cck_phy2mac_rate(u8 signal) /* sanitize, and sort a rateset with the basic bit(s) preserved, validate * rateset */ -extern bool -brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, - const struct brcms_c_rateset *hw_rs, - bool check_brate, u8 txstreams); +bool brcms_c_rate_hwrs_filter_sort_validate(struct brcms_c_rateset *rs, + const struct brcms_c_rateset *hw_rs, + bool check_brate, u8 txstreams); /* copy rateset src to dst as-is (no masking or sorting) */ -extern void brcms_c_rateset_copy(const struct brcms_c_rateset *src, - struct brcms_c_rateset *dst); +void brcms_c_rateset_copy(const struct brcms_c_rateset *src, + struct brcms_c_rateset *dst); /* would be nice to have these documented ... */ -extern u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp); - -extern void brcms_c_rateset_filter(struct brcms_c_rateset *src, - struct brcms_c_rateset *dst, bool basic_only, u8 rates, uint xmask, - bool mcsallow); - -extern void -brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, - const struct brcms_c_rateset *rs_hw, uint phy_type, - int bandtype, bool cck_only, uint rate_mask, - bool mcsallow, u8 bw, u8 txstreams); - -extern s16 brcms_c_rate_legacy_phyctl(uint rate); - -extern void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams); -extern void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset); -extern void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, - u8 txstreams); -extern void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, - u8 bw); +u32 brcms_c_compute_rspec(struct d11rxhdr *rxh, u8 *plcp); + +void brcms_c_rateset_filter(struct brcms_c_rateset *src, + struct brcms_c_rateset *dst, bool basic_only, + u8 rates, uint xmask, bool mcsallow); + +void brcms_c_rateset_default(struct brcms_c_rateset *rs_tgt, + const struct brcms_c_rateset *rs_hw, uint phy_type, + int bandtype, bool cck_only, uint rate_mask, + bool mcsallow, u8 bw, u8 txstreams); + +s16 brcms_c_rate_legacy_phyctl(uint rate); + +void brcms_c_rateset_mcs_upd(struct brcms_c_rateset *rs, u8 txstreams); +void brcms_c_rateset_mcs_clear(struct brcms_c_rateset *rateset); +void brcms_c_rateset_mcs_build(struct brcms_c_rateset *rateset, u8 txstreams); +void brcms_c_rateset_bw_mcs_filter(struct brcms_c_rateset *rateset, u8 bw); #endif /* _BRCM_RATE_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.h b/drivers/net/wireless/brcm80211/brcmsmac/stf.h index 19f6580f69be27890eea13ddfe7c689cbe6f3089..ba9493009a3340db524bfef6dc5359c6a8be0ec4 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/stf.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/stf.h @@ -19,24 +19,19 @@ #include "types.h" -extern int brcms_c_stf_attach(struct brcms_c_info *wlc); -extern void brcms_c_stf_detach(struct brcms_c_info *wlc); +int brcms_c_stf_attach(struct brcms_c_info *wlc); +void brcms_c_stf_detach(struct brcms_c_info *wlc); -extern void brcms_c_tempsense_upd(struct brcms_c_info *wlc); -extern void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, - u16 *ss_algo_channel, - u16 chanspec); -extern int brcms_c_stf_ss_update(struct brcms_c_info *wlc, - struct brcms_band *band); -extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); -extern int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, - bool force); -extern bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val); -extern void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); -extern void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc); -extern u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, - u32 rspec); -extern u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, - u32 rspec); +void brcms_c_tempsense_upd(struct brcms_c_info *wlc); +void brcms_c_stf_ss_algo_channel_get(struct brcms_c_info *wlc, + u16 *ss_algo_channel, u16 chanspec); +int brcms_c_stf_ss_update(struct brcms_c_info *wlc, struct brcms_band *band); +void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); +int brcms_c_stf_txchain_set(struct brcms_c_info *wlc, s32 int_val, bool force); +bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val); +void brcms_c_stf_phy_txant_upd(struct brcms_c_info *wlc); +void brcms_c_stf_phy_chain_calc(struct brcms_c_info *wlc); +u16 brcms_c_stf_phytxchain_sel(struct brcms_c_info *wlc, u32 rspec); +u16 brcms_c_stf_d11hdrs_phyctl_txant(struct brcms_c_info *wlc, u32 rspec); #endif /* _BRCM_STF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h index 18750a814b4f219f918aaed081d8e99f01e7f6f4..c87dd89bcb78bddd2774bb7275741b1507aaf8d3 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h @@ -43,16 +43,14 @@ struct brcms_ucode { u32 *bcm43xx_bomminor; }; -extern int -brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode); +int brcms_ucode_data_init(struct brcms_info *wl, struct brcms_ucode *ucode); -extern void brcms_ucode_data_free(struct brcms_ucode *ucode); +void brcms_ucode_data_free(struct brcms_ucode *ucode); -extern int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, - unsigned int idx); -extern int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, - unsigned int idx); -extern void brcms_ucode_free_buf(void *); -extern int brcms_check_firmwares(struct brcms_info *wl); +int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, unsigned int idx); +int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, + unsigned int idx); +void brcms_ucode_free_buf(void *); +int brcms_check_firmwares(struct brcms_info *wl); #endif /* _BRCM_UCODE_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index c1fe245bb07ee18aba557892b274435a3ad40fcb..84113ea16f8434fcd0ee4383aeea18c0dfe9c480 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -41,5 +41,6 @@ #define BCM4331_CHIP_ID 0x4331 #define BCM4334_CHIP_ID 0x4334 #define BCM4335_CHIP_ID 0x4335 +#define BCM4339_CHIP_ID 0x4339 #endif /* _BRCM_HW_IDS_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/brcmu_d11.h b/drivers/net/wireless/brcm80211/include/brcmu_d11.h index 92623f02b1c045460c28d860520000c127cfc611..8660a2cba09810428f127967c996a02cfbc174a1 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_d11.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_d11.h @@ -140,6 +140,6 @@ struct brcmu_d11inf { void (*decchspec)(struct brcmu_chan *ch); }; -extern void brcmu_d11_attach(struct brcmu_d11inf *d11inf); +void brcmu_d11_attach(struct brcmu_d11inf *d11inf); #endif /* _BRCMU_CHANNELS_H_ */ diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h index 898cacb8d01df299f1e4dce5ebc72fc008e5b4f2..8ba445b3fd72a92ca78cbd1042dcc38528df8efb 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h @@ -114,31 +114,29 @@ static inline struct sk_buff *pktq_ppeek_tail(struct pktq *pq, int prec) return skb_peek_tail(&pq->q[prec].skblist); } -extern struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, - struct sk_buff *p); -extern struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, - struct sk_buff *p); -extern struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec); -extern struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec); -extern struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, - bool (*match_fn)(struct sk_buff *p, - void *arg), - void *arg); +struct sk_buff *brcmu_pktq_penq(struct pktq *pq, int prec, struct sk_buff *p); +struct sk_buff *brcmu_pktq_penq_head(struct pktq *pq, int prec, + struct sk_buff *p); +struct sk_buff *brcmu_pktq_pdeq(struct pktq *pq, int prec); +struct sk_buff *brcmu_pktq_pdeq_tail(struct pktq *pq, int prec); +struct sk_buff *brcmu_pktq_pdeq_match(struct pktq *pq, int prec, + bool (*match_fn)(struct sk_buff *p, + void *arg), + void *arg); /* packet primitives */ -extern struct sk_buff *brcmu_pkt_buf_get_skb(uint len); -extern void brcmu_pkt_buf_free_skb(struct sk_buff *skb); +struct sk_buff *brcmu_pkt_buf_get_skb(uint len); +void brcmu_pkt_buf_free_skb(struct sk_buff *skb); /* Empty the queue at particular precedence level */ /* callback function fn(pkt, arg) returns true if pkt belongs to if */ -extern void brcmu_pktq_pflush(struct pktq *pq, int prec, - bool dir, bool (*fn)(struct sk_buff *, void *), void *arg); +void brcmu_pktq_pflush(struct pktq *pq, int prec, bool dir, + bool (*fn)(struct sk_buff *, void *), void *arg); /* operations on a set of precedences in packet queue */ -extern int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp); -extern struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, - int *prec_out); +int brcmu_pktq_mlen(struct pktq *pq, uint prec_bmp); +struct sk_buff *brcmu_pktq_mdeq(struct pktq *pq, uint prec_bmp, int *prec_out); /* operations on packet queue as a whole */ @@ -167,11 +165,11 @@ static inline bool pktq_empty(struct pktq *pq) return pq->len == 0; } -extern void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len); +void brcmu_pktq_init(struct pktq *pq, int num_prec, int max_len); /* prec_out may be NULL if caller is not interested in return value */ -extern struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out); -extern void brcmu_pktq_flush(struct pktq *pq, bool dir, - bool (*fn)(struct sk_buff *, void *), void *arg); +struct sk_buff *brcmu_pktq_peek_tail(struct pktq *pq, int *prec_out); +void brcmu_pktq_flush(struct pktq *pq, bool dir, + bool (*fn)(struct sk_buff *, void *), void *arg); /* externs */ /* ip address */ @@ -204,13 +202,13 @@ static inline u16 brcmu_maskget16(u16 var, u16 mask, u8 shift) /* externs */ /* format/print */ #ifdef DEBUG -extern void brcmu_prpkt(const char *msg, struct sk_buff *p0); +void brcmu_prpkt(const char *msg, struct sk_buff *p0); #else #define brcmu_prpkt(a, b) #endif /* DEBUG */ #ifdef DEBUG -extern __printf(3, 4) +__printf(3, 4) void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...); #else __printf(3, 4) diff --git a/drivers/net/wireless/cw1200/cw1200_spi.c b/drivers/net/wireless/cw1200/cw1200_spi.c index 755a0c8edfe1235e73180b8b45d6143afa582547..40078f5f932ec6b1ea3a26af5453f2bc5d2206ec 100644 --- a/drivers/net/wireless/cw1200/cw1200_spi.c +++ b/drivers/net/wireless/cw1200/cw1200_spi.c @@ -365,7 +365,7 @@ static struct hwbus_ops cw1200_spi_hwbus_ops = { static int cw1200_spi_probe(struct spi_device *func) { const struct cw1200_platform_data_spi *plat_data = - func->dev.platform_data; + dev_get_platdata(&func->dev); struct hwbus_priv *self; int status; @@ -443,7 +443,7 @@ static int cw1200_spi_disconnect(struct spi_device *func) } kfree(self); } - cw1200_spi_off(func->dev.platform_data); + cw1200_spi_off(dev_get_platdata(&func->dev)); return 0; } diff --git a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c index 970a48baaf804a38ff1883702bbd9460d7d5b8c8..de7c4ffec3096b07ccaece961666b0bdde51ea27 100644 --- a/drivers/net/wireless/hostap/hostap_info.c +++ b/drivers/net/wireless/hostap/hostap_info.c @@ -217,7 +217,7 @@ static void prism2_host_roaming(local_info_t *local) } } - memcpy(req.bssid, selected->bssid, 6); + memcpy(req.bssid, selected->bssid, ETH_ALEN); req.channel = selected->chid; spin_unlock_irqrestore(&local->lock, flags); diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 6b823a1ab7892fd9c81be07ce56bc45e75a6b8f1..81903e33d5b1bd4779681da798dc0183c0638f48 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -2698,7 +2698,7 @@ static u16 eeprom_read_u16(struct ipw_priv *priv, u8 addr) /* data's copy of the eeprom data */ static void eeprom_parse_mac(struct ipw_priv *priv, u8 * mac) { - memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], 6); + memcpy(mac, &priv->eeprom[EEPROM_MAC_ADDRESS], ETH_ALEN); } static void ipw_read_eeprom(struct ipw_priv *priv) @@ -11885,7 +11885,6 @@ static int ipw_pci_probe(struct pci_dev *pdev, pci_release_regions(pdev); out_pci_disable_device: pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); out_free_libipw: free_libipw(priv->net_dev, 0); out: @@ -11966,7 +11965,6 @@ static void ipw_pci_remove(struct pci_dev *pdev) iounmap(priv->hw_base); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); /* wiphy_unregister needs to be here, before free_libipw */ wiphy_unregister(priv->ieee->wdev.wiphy); kfree(priv->ieee->a_band.channels); diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h index 6eede52ad8c0fbc0581a2b3a68d302cb589f4dde..5ce2f59d3378ed7ddcc4891769d72401eb66cc0d 100644 --- a/drivers/net/wireless/ipw2x00/libipw.h +++ b/drivers/net/wireless/ipw2x00/libipw.h @@ -950,66 +950,55 @@ static inline int libipw_is_cck_rate(u8 rate) } /* libipw.c */ -extern void free_libipw(struct net_device *dev, int monitor); -extern struct net_device *alloc_libipw(int sizeof_priv, int monitor); -extern int libipw_change_mtu(struct net_device *dev, int new_mtu); +void free_libipw(struct net_device *dev, int monitor); +struct net_device *alloc_libipw(int sizeof_priv, int monitor); +int libipw_change_mtu(struct net_device *dev, int new_mtu); -extern void libipw_networks_age(struct libipw_device *ieee, - unsigned long age_secs); +void libipw_networks_age(struct libipw_device *ieee, unsigned long age_secs); -extern int libipw_set_encryption(struct libipw_device *ieee); +int libipw_set_encryption(struct libipw_device *ieee); /* libipw_tx.c */ -extern netdev_tx_t libipw_xmit(struct sk_buff *skb, - struct net_device *dev); -extern void libipw_txb_free(struct libipw_txb *); +netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev); +void libipw_txb_free(struct libipw_txb *); /* libipw_rx.c */ -extern void libipw_rx_any(struct libipw_device *ieee, - struct sk_buff *skb, struct libipw_rx_stats *stats); -extern int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, - struct libipw_rx_stats *rx_stats); +void libipw_rx_any(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *stats); +int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, + struct libipw_rx_stats *rx_stats); /* make sure to set stats->len */ -extern void libipw_rx_mgt(struct libipw_device *ieee, - struct libipw_hdr_4addr *header, - struct libipw_rx_stats *stats); -extern void libipw_network_reset(struct libipw_network *network); +void libipw_rx_mgt(struct libipw_device *ieee, struct libipw_hdr_4addr *header, + struct libipw_rx_stats *stats); +void libipw_network_reset(struct libipw_network *network); /* libipw_geo.c */ -extern const struct libipw_geo *libipw_get_geo(struct libipw_device - *ieee); -extern void libipw_set_geo(struct libipw_device *ieee, - const struct libipw_geo *geo); - -extern int libipw_is_valid_channel(struct libipw_device *ieee, - u8 channel); -extern int libipw_channel_to_index(struct libipw_device *ieee, - u8 channel); -extern u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq); -extern u8 libipw_get_channel_flags(struct libipw_device *ieee, - u8 channel); -extern const struct libipw_channel *libipw_get_channel(struct - libipw_device - *ieee, u8 channel); -extern u32 libipw_channel_to_freq(struct libipw_device * ieee, - u8 channel); +const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee); +void libipw_set_geo(struct libipw_device *ieee, const struct libipw_geo *geo); + +int libipw_is_valid_channel(struct libipw_device *ieee, u8 channel); +int libipw_channel_to_index(struct libipw_device *ieee, u8 channel); +u8 libipw_freq_to_channel(struct libipw_device *ieee, u32 freq); +u8 libipw_get_channel_flags(struct libipw_device *ieee, u8 channel); +const struct libipw_channel *libipw_get_channel(struct libipw_device *ieee, + u8 channel); +u32 libipw_channel_to_freq(struct libipw_device *ieee, u8 channel); /* libipw_wx.c */ -extern int libipw_wx_get_scan(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -extern int libipw_wx_set_encode(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -extern int libipw_wx_get_encode(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *key); -extern int libipw_wx_set_encodeext(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra); -extern int libipw_wx_get_encodeext(struct libipw_device *ieee, - struct iw_request_info *info, - union iwreq_data *wrqu, char *extra); +int libipw_wx_get_scan(struct libipw_device *ieee, struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +int libipw_wx_set_encode(struct libipw_device *ieee, + struct iw_request_info *info, union iwreq_data *wrqu, + char *key); +int libipw_wx_get_encode(struct libipw_device *ieee, + struct iw_request_info *info, union iwreq_data *wrqu, + char *key); +int libipw_wx_set_encodeext(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); +int libipw_wx_get_encodeext(struct libipw_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); static inline void libipw_increment_scans(struct libipw_device *ieee) { diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index 9581d07a4242bbdabef6174e60560d42aa4e6b09..dea3b50d68b9c5bcfc1c95794262992bc59d917d 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3811,7 +3811,6 @@ out_iounmap: out_pci_release_regions: pci_release_regions(pdev); out_pci_disable_device: - pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); out_ieee80211_free_hw: ieee80211_free_hw(il->hw); @@ -3888,7 +3887,6 @@ il3945_pci_remove(struct pci_dev *pdev) iounmap(il->hw_base); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); il_free_channel_map(il); il_free_geos(il); diff --git a/drivers/net/wireless/iwlegacy/3945.h b/drivers/net/wireless/iwlegacy/3945.h index 9a8703def0ba1a1825c264b5e5806003412c5e91..00030d43a1947380cf818bae7ff11a31b26d92a8 100644 --- a/drivers/net/wireless/iwlegacy/3945.h +++ b/drivers/net/wireless/iwlegacy/3945.h @@ -189,15 +189,14 @@ struct il3945_ibss_seq { * for use by iwl-*.c * *****************************************************************************/ -extern int il3945_calc_db_from_ratio(int sig_ratio); -extern void il3945_rx_replenish(void *data); -extern void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); -extern unsigned int il3945_fill_beacon_frame(struct il_priv *il, - struct ieee80211_hdr *hdr, - int left); -extern int il3945_dump_nic_event_log(struct il_priv *il, bool full_log, - char **buf, bool display); -extern void il3945_dump_nic_error_log(struct il_priv *il); +int il3945_calc_db_from_ratio(int sig_ratio); +void il3945_rx_replenish(void *data); +void il3945_rx_queue_reset(struct il_priv *il, struct il_rx_queue *rxq); +unsigned int il3945_fill_beacon_frame(struct il_priv *il, + struct ieee80211_hdr *hdr, int left); +int il3945_dump_nic_event_log(struct il_priv *il, bool full_log, char **buf, + bool display); +void il3945_dump_nic_error_log(struct il_priv *il); /****************************************************************************** * @@ -215,39 +214,36 @@ extern void il3945_dump_nic_error_log(struct il_priv *il); * il3945_mac_ <-- mac80211 callback * ****************************************************************************/ -extern void il3945_hw_handler_setup(struct il_priv *il); -extern void il3945_hw_setup_deferred_work(struct il_priv *il); -extern void il3945_hw_cancel_deferred_work(struct il_priv *il); -extern int il3945_hw_rxq_stop(struct il_priv *il); -extern int il3945_hw_set_hw_params(struct il_priv *il); -extern int il3945_hw_nic_init(struct il_priv *il); -extern int il3945_hw_nic_stop_master(struct il_priv *il); -extern void il3945_hw_txq_ctx_free(struct il_priv *il); -extern void il3945_hw_txq_ctx_stop(struct il_priv *il); -extern int il3945_hw_nic_reset(struct il_priv *il); -extern int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, - struct il_tx_queue *txq, - dma_addr_t addr, u16 len, u8 reset, - u8 pad); -extern void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); -extern int il3945_hw_get_temperature(struct il_priv *il); -extern int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); -extern unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il, - struct il3945_frame *frame, - u8 rate); +void il3945_hw_handler_setup(struct il_priv *il); +void il3945_hw_setup_deferred_work(struct il_priv *il); +void il3945_hw_cancel_deferred_work(struct il_priv *il); +int il3945_hw_rxq_stop(struct il_priv *il); +int il3945_hw_set_hw_params(struct il_priv *il); +int il3945_hw_nic_init(struct il_priv *il); +int il3945_hw_nic_stop_master(struct il_priv *il); +void il3945_hw_txq_ctx_free(struct il_priv *il); +void il3945_hw_txq_ctx_stop(struct il_priv *il); +int il3945_hw_nic_reset(struct il_priv *il); +int il3945_hw_txq_attach_buf_to_tfd(struct il_priv *il, struct il_tx_queue *txq, + dma_addr_t addr, u16 len, u8 reset, u8 pad); +void il3945_hw_txq_free_tfd(struct il_priv *il, struct il_tx_queue *txq); +int il3945_hw_get_temperature(struct il_priv *il); +int il3945_hw_tx_queue_init(struct il_priv *il, struct il_tx_queue *txq); +unsigned int il3945_hw_get_beacon_cmd(struct il_priv *il, + struct il3945_frame *frame, u8 rate); void il3945_hw_build_tx_cmd_rate(struct il_priv *il, struct il_device_cmd *cmd, struct ieee80211_tx_info *info, struct ieee80211_hdr *hdr, int sta_id); -extern int il3945_hw_reg_send_txpower(struct il_priv *il); -extern int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power); -extern void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb); +int il3945_hw_reg_send_txpower(struct il_priv *il); +int il3945_hw_reg_set_txpower(struct il_priv *il, s8 power); +void il3945_hdl_stats(struct il_priv *il, struct il_rx_buf *rxb); void il3945_hdl_c_stats(struct il_priv *il, struct il_rx_buf *rxb); -extern void il3945_disable_events(struct il_priv *il); -extern int il4965_get_temperature(const struct il_priv *il); -extern void il3945_post_associate(struct il_priv *il); -extern void il3945_config_ap(struct il_priv *il); +void il3945_disable_events(struct il_priv *il); +int il4965_get_temperature(const struct il_priv *il); +void il3945_post_associate(struct il_priv *il); +void il3945_config_ap(struct il_priv *il); -extern int il3945_commit_rxon(struct il_priv *il); +int il3945_commit_rxon(struct il_priv *il); /** * il3945_hw_find_station - Find station id for a given BSSID @@ -257,14 +253,14 @@ extern int il3945_commit_rxon(struct il_priv *il); * not yet been merged into a single common layer for managing the * station tables. */ -extern u8 il3945_hw_find_station(struct il_priv *il, const u8 * bssid); +u8 il3945_hw_find_station(struct il_priv *il, const u8 *bssid); -extern __le32 il3945_get_antenna_flags(const struct il_priv *il); -extern int il3945_init_hw_rate_table(struct il_priv *il); -extern void il3945_reg_txpower_periodic(struct il_priv *il); -extern int il3945_txpower_set_from_eeprom(struct il_priv *il); +__le32 il3945_get_antenna_flags(const struct il_priv *il); +int il3945_init_hw_rate_table(struct il_priv *il); +void il3945_reg_txpower_periodic(struct il_priv *il); +int il3945_txpower_set_from_eeprom(struct il_priv *il); -extern int il3945_rs_next_rate(struct il_priv *il, int rate); +int il3945_rs_next_rate(struct il_priv *il, int rate); /* scanning */ int il3945_request_scan(struct il_priv *il, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 5ab50a5b48b1511708bd67783ccb7518a9a81eb1..3982ab76f3755fdbda4f0cab89aa6cca537a8f43 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -6706,7 +6706,6 @@ out_free_eeprom: out_iounmap: iounmap(il->hw_base); out_pci_release_regions: - pci_set_drvdata(pdev, NULL); pci_release_regions(pdev); out_pci_disable_device: pci_disable_device(pdev); @@ -6787,7 +6786,6 @@ il4965_pci_remove(struct pci_dev *pdev) iounmap(il->hw_base); pci_release_regions(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); il4965_uninit_drv(il); diff --git a/drivers/net/wireless/iwlegacy/4965.h b/drivers/net/wireless/iwlegacy/4965.h index 1b15b0b2292b4fe06c3fa1efcc95279c9901370e..337dfcf3bbde7c58fe4681268f63b9d29ea8f5fa 100644 --- a/drivers/net/wireless/iwlegacy/4965.h +++ b/drivers/net/wireless/iwlegacy/4965.h @@ -272,7 +272,7 @@ il4965_hw_valid_rtc_data_addr(u32 addr) ((t) < IL_TX_POWER_TEMPERATURE_MIN || \ (t) > IL_TX_POWER_TEMPERATURE_MAX) -extern void il4965_temperature_calib(struct il_priv *il); +void il4965_temperature_calib(struct il_priv *il); /********************* END TEMPERATURE ***************************************/ /********************* START TXPOWER *****************************************/ diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index 83f8ed8a5528cbd209c97560e9ab9c59d2294a90..ad123d66ab6c5c13e2bd120df7b3c2b412d1400a 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -858,9 +858,9 @@ struct il_hw_params { * il4965_mac_ <-- mac80211 callback * ****************************************************************************/ -extern void il4965_update_chain_flags(struct il_priv *il); +void il4965_update_chain_flags(struct il_priv *il); extern const u8 il_bcast_addr[ETH_ALEN]; -extern int il_queue_space(const struct il_queue *q); +int il_queue_space(const struct il_queue *q); static inline int il_queue_used(const struct il_queue *q, int i) { @@ -1727,7 +1727,7 @@ int il_alloc_txq_mem(struct il_priv *il); void il_free_txq_mem(struct il_priv *il); #ifdef CONFIG_IWLEGACY_DEBUGFS -extern void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len); +void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len); #else static inline void il_update_stats(struct il_priv *il, bool is_tx, __le16 fc, u16 len) @@ -1760,12 +1760,12 @@ void il_chswitch_done(struct il_priv *il, bool is_success); /***************************************************** * TX ******************************************************/ -extern void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq); -extern int il_tx_queue_init(struct il_priv *il, u32 txq_id); -extern void il_tx_queue_reset(struct il_priv *il, u32 txq_id); -extern void il_tx_queue_unmap(struct il_priv *il, int txq_id); -extern void il_tx_queue_free(struct il_priv *il, int txq_id); -extern void il_setup_watchdog(struct il_priv *il); +void il_txq_update_write_ptr(struct il_priv *il, struct il_tx_queue *txq); +int il_tx_queue_init(struct il_priv *il, u32 txq_id); +void il_tx_queue_reset(struct il_priv *il, u32 txq_id); +void il_tx_queue_unmap(struct il_priv *il, int txq_id); +void il_tx_queue_free(struct il_priv *il, int txq_id); +void il_setup_watchdog(struct il_priv *il); /***************************************************** * TX power ****************************************************/ @@ -1931,10 +1931,10 @@ il_is_ready_rf(struct il_priv *il) return il_is_ready(il); } -extern void il_send_bt_config(struct il_priv *il); -extern int il_send_stats_request(struct il_priv *il, u8 flags, bool clear); -extern void il_apm_stop(struct il_priv *il); -extern void _il_apm_stop(struct il_priv *il); +void il_send_bt_config(struct il_priv *il); +int il_send_stats_request(struct il_priv *il, u8 flags, bool clear); +void il_apm_stop(struct il_priv *il); +void _il_apm_stop(struct il_priv *il); int il_apm_init(struct il_priv *il); @@ -1968,15 +1968,15 @@ void il_tx_cmd_protection(struct il_priv *il, struct ieee80211_tx_info *info, irqreturn_t il_isr(int irq, void *data); -extern void il_set_bit(struct il_priv *p, u32 r, u32 m); -extern void il_clear_bit(struct il_priv *p, u32 r, u32 m); -extern bool _il_grab_nic_access(struct il_priv *il); -extern int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout); -extern int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout); -extern u32 il_rd_prph(struct il_priv *il, u32 reg); -extern void il_wr_prph(struct il_priv *il, u32 addr, u32 val); -extern u32 il_read_targ_mem(struct il_priv *il, u32 addr); -extern void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val); +void il_set_bit(struct il_priv *p, u32 r, u32 m); +void il_clear_bit(struct il_priv *p, u32 r, u32 m); +bool _il_grab_nic_access(struct il_priv *il); +int _il_poll_bit(struct il_priv *il, u32 addr, u32 bits, u32 mask, int timeout); +int il_poll_bit(struct il_priv *il, u32 addr, u32 mask, int timeout); +u32 il_rd_prph(struct il_priv *il, u32 reg); +void il_wr_prph(struct il_priv *il, u32 addr, u32 val); +u32 il_read_targ_mem(struct il_priv *il, u32 addr); +void il_write_targ_mem(struct il_priv *il, u32 addr, u32 val); static inline void _il_write8(struct il_priv *il, u32 ofs, u8 val) @@ -2868,13 +2868,13 @@ il4965_first_antenna(u8 mask) * The specific throughput table used is based on the type of network * the associated with, including A, B, G, and G w/ TGG protection */ -extern void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); +void il3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); /* Initialize station's rate scaling information after adding station */ -extern void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, - u8 sta_id); -extern void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, - u8 sta_id); +void il4965_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, + u8 sta_id); +void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, + u8 sta_id); /** * il_rate_control_register - Register the rate control algorithm callbacks @@ -2886,8 +2886,8 @@ extern void il3945_rs_rate_init(struct il_priv *il, struct ieee80211_sta *sta, * ieee80211_register_hw * */ -extern int il4965_rate_control_register(void); -extern int il3945_rate_control_register(void); +int il4965_rate_control_register(void); +int il3945_rate_control_register(void); /** * il_rate_control_unregister - Unregister the rate control callbacks @@ -2895,11 +2895,11 @@ extern int il3945_rate_control_register(void); * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ -extern void il4965_rate_control_unregister(void); -extern void il3945_rate_control_unregister(void); +void il4965_rate_control_unregister(void); +void il3945_rate_control_unregister(void); -extern int il_power_update_mode(struct il_priv *il, bool force); -extern void il_power_initialize(struct il_priv *il); +int il_power_update_mode(struct il_priv *il, bool force); +void il_power_initialize(struct il_priv *il); extern u32 il_debug_level; diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index f2a86ffc3b4cf09440874c88b6f0cc3d804ec3a6..23d5f0275ce98e1cde6c05e7b9a1654c870774ea 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -397,7 +397,7 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) return cpu_to_le32(flags|(u32)rate); } -extern int iwl_alive_start(struct iwl_priv *priv); +int iwl_alive_start(struct iwl_priv *priv); #ifdef CONFIG_IWLWIFI_DEBUG void iwl_print_rx_config_cmd(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index a79fdd137f956ce5cb4de2ef7e1c0f239e76895a..7434d9edf3b773566530b79eb542a8431bbfb24c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -270,7 +270,7 @@ struct iwl_sensitivity_ranges { * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) * ****************************************************************************/ -extern void iwl_update_chain_flags(struct iwl_priv *priv); +void iwl_update_chain_flags(struct iwl_priv *priv); extern const u8 iwl_bcast_addr[ETH_ALEN]; #define IWL_OPERATION_MODE_AUTO 0 diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.h b/drivers/net/wireless/iwlwifi/dvm/rs.h index 5d83cab22d625084389b8880b46a4abd21fb9f44..26fc550cd68c58ae24342232bda51be2dfd690a5 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rs.h +++ b/drivers/net/wireless/iwlwifi/dvm/rs.h @@ -407,8 +407,8 @@ static inline u8 first_antenna(u8 mask) /* Initialize station's rate scaling information after adding station */ -extern void iwl_rs_rate_init(struct iwl_priv *priv, - struct ieee80211_sta *sta, u8 sta_id); +void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, + u8 sta_id); /** * iwl_rate_control_register - Register the rate control algorithm callbacks @@ -420,7 +420,7 @@ extern void iwl_rs_rate_init(struct iwl_priv *priv, * ieee80211_register_hw * */ -extern int iwlagn_rate_control_register(void); +int iwlagn_rate_control_register(void); /** * iwl_rate_control_unregister - Unregister the rate control callbacks @@ -428,6 +428,6 @@ extern int iwlagn_rate_control_register(void); * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ -extern void iwlagn_rate_control_unregister(void); +void iwlagn_rate_control_unregister(void); #endif /* __iwl_agn__rs__ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index da442b81370a769054b4b77a946daa4bbd856ed8..1fef5240e6adc07317b7128bf7d7617385fa9aa8 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -433,27 +433,19 @@ int iwlagn_tx_skb(struct iwl_priv *priv, /* Copy MAC header from skb into command buffer */ memcpy(tx_cmd->hdr, hdr, hdr_len); + txq_id = info->hw_queue; + if (is_agg) txq_id = priv->tid_data[sta_id][tid].agg.txq_id; else if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - /* - * Send this frame after DTIM -- there's a special queue - * reserved for this for contexts that support AP mode. - */ - txq_id = ctx->mcast_queue; - /* * The microcode will clear the more data * bit in the last frame it transmits. */ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) - txq_id = IWL_AUX_QUEUE; - else - txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)]; + } - WARN_ON_ONCE(!is_agg && txq_id != info->hw_queue); WARN_ON_ONCE(is_agg && priv->queue_to_mac80211[txq_id] != info->hw_queue); diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 86270b69cd024c3e9fe88f6361b15acc1f77eb39..63637949a146d65246f9d2da0381ec8697e6e4a9 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -330,15 +330,14 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv, enum iwl_ucode_type old_type; static const u8 alive_cmd[] = { REPLY_ALIVE }; - old_type = priv->cur_ucode; - priv->cur_ucode = ucode_type; fw = iwl_get_ucode_image(priv, ucode_type); + if (WARN_ON(!fw)) + return -EINVAL; + old_type = priv->cur_ucode; + priv->cur_ucode = ucode_type; priv->ucode_loaded = false; - if (!fw) - return -EINVAL; - iwl_init_notification_wait(&priv->notif_wait, &alive_wait, alive_cmd, ARRAY_SIZE(alive_cmd), iwl_alive_fn, &alive_data); diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 76e14c046d9402cc0b7fbe39b0966c8195f0708a..85879dbaa402cdaa88d93247b678aaac9a3ff000 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -83,6 +83,8 @@ #define IWL7260_TX_POWER_VERSION 0xffff /* meaningless */ #define IWL3160_NVM_VERSION 0x709 #define IWL3160_TX_POWER_VERSION 0xffff /* meaningless */ +#define IWL7265_NVM_VERSION 0x0a1d +#define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */ #define IWL7260_FW_PRE "iwlwifi-7260-" #define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode" @@ -90,6 +92,9 @@ #define IWL3160_FW_PRE "iwlwifi-3160-" #define IWL3160_MODULE_FIRMWARE(api) IWL3160_FW_PRE __stringify(api) ".ucode" +#define IWL7265_FW_PRE "iwlwifi-7265-" +#define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode" + static const struct iwl_base_params iwl7000_base_params = { .eeprom_size = OTP_LOW_IMAGE_SIZE, .num_of_queues = IWLAGN_NUM_QUEUES, @@ -182,5 +187,14 @@ const struct iwl_cfg iwl3160_n_cfg = { .nvm_calib_ver = IWL3160_TX_POWER_VERSION, }; +const struct iwl_cfg iwl7265_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 7265", + .fw_name_pre = IWL7265_FW_PRE, + IWL_DEVICE_7000, + .ht_params = &iwl7000_ht_params, + .nvm_ver = IWL7265_NVM_VERSION, + .nvm_calib_ver = IWL7265_TX_POWER_VERSION, +}; + MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); MODULE_FIRMWARE(IWL3160_MODULE_FIRMWARE(IWL3160_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index b03c25e14903d05c150c176d608f4be1f654673e..18f232e8e81253b31d730755f242b7a51552157c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -293,6 +293,7 @@ extern const struct iwl_cfg iwl7260_n_cfg; extern const struct iwl_cfg iwl3160_2ac_cfg; extern const struct iwl_cfg iwl3160_2n_cfg; extern const struct iwl_cfg iwl3160_n_cfg; +extern const struct iwl_cfg iwl7265_2ac_cfg; #endif /* CONFIG_IWLMVM */ #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index a276af476e2d7ab64e859748c9b8fd48b84ebb80..54a4fdc631b73c987f12e459e8245d38b18f0c0b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -394,6 +394,38 @@ #define CSR_DRAM_INT_TBL_ENABLE (1 << 31) #define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) +/* SECURE boot registers */ +#define CSR_SECURE_BOOT_CONFIG_ADDR (0x100) +enum secure_boot_config_reg { + CSR_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001, + CSR_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ = 0x00000002, +}; + +#define CSR_SECURE_BOOT_CPU1_STATUS_ADDR (0x100) +#define CSR_SECURE_BOOT_CPU2_STATUS_ADDR (0x100) +enum secure_boot_status_reg { + CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS = 0x00000003, + CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED = 0x00000002, + CSR_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS = 0x00000004, + CSR_SECURE_BOOT_CPU_STATUS_VERF_FAIL = 0x00000008, + CSR_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL = 0x00000010, +}; + +#define CSR_UCODE_LOAD_STATUS_ADDR (0x100) +enum secure_load_status_reg { + CSR_CPU_STATUS_LOADING_STARTED = 0x00000001, + CSR_CPU_STATUS_LOADING_COMPLETED = 0x00000002, + CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8, + CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00, +}; + +#define CSR_SECURE_INSPECTOR_CODE_ADDR (0x100) +#define CSR_SECURE_INSPECTOR_DATA_ADDR (0x100) + +#define CSR_SECURE_TIME_OUT (100) + +#define FH_TCSR_0_REG0 (0x1D00) + /* * HBUS (Host-side Bus) * diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 99e1da3123c9a07649008e48d2a2a56dc685d820..ff570027e9dd9fa02f3e8360af6966b480c68069 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -483,6 +483,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, const u8 *tlv_data; char buildstr[25]; u32 build; + int num_of_cpus; if (len < sizeof(*ucode)) { IWL_ERR(drv, "uCode has invalid length: %zd\n", len); @@ -692,6 +693,42 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, goto invalid_tlv_len; drv->fw.phy_config = le32_to_cpup((__le32 *)tlv_data); break; + case IWL_UCODE_TLV_SECURE_SEC_RT: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_REGULAR, + tlv_len); + drv->fw.mvm_fw = true; + drv->fw.img[IWL_UCODE_REGULAR].is_secure = true; + break; + case IWL_UCODE_TLV_SECURE_SEC_INIT: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_INIT, + tlv_len); + drv->fw.mvm_fw = true; + drv->fw.img[IWL_UCODE_INIT].is_secure = true; + break; + case IWL_UCODE_TLV_SECURE_SEC_WOWLAN: + iwl_store_ucode_sec(pieces, tlv_data, IWL_UCODE_WOWLAN, + tlv_len); + drv->fw.mvm_fw = true; + drv->fw.img[IWL_UCODE_WOWLAN].is_secure = true; + break; + case IWL_UCODE_TLV_NUM_OF_CPU: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + num_of_cpus = + le32_to_cpup((__le32 *)tlv_data); + + if (num_of_cpus == 2) { + drv->fw.img[IWL_UCODE_REGULAR].is_dual_cpus = + true; + drv->fw.img[IWL_UCODE_INIT].is_dual_cpus = + true; + drv->fw.img[IWL_UCODE_WOWLAN].is_dual_cpus = + true; + } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) { + IWL_ERR(drv, "Driver support upto 2 CPUs\n"); + return -EINVAL; + } + break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 8b6c6fd95ed038ac1052827a2edb2a39f04f849a..6c6c35c5228cabd71d1520f533af7bd3adf8ad26 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -121,6 +121,10 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_SEC_WOWLAN = 21, IWL_UCODE_TLV_DEF_CALIB = 22, IWL_UCODE_TLV_PHY_SKU = 23, + IWL_UCODE_TLV_SECURE_SEC_RT = 24, + IWL_UCODE_TLV_SECURE_SEC_INIT = 25, + IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26, + IWL_UCODE_TLV_NUM_OF_CPU = 27, }; struct iwl_ucode_tlv { diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index a1223680bc70d2f765e32180176ac7997884a3d7..75db087120c30fc8f2201f1f4de1be4d3e8e270c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -75,11 +75,23 @@ * @IWL_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. * @IWL_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS * @IWL_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD + * @IWL_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan + * offload profile config command. * @IWL_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api * @IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API. * @IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six * (rather than two) IPv6 addresses * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API + * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element + * from the probe request template. + * @IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API: modified D3 API to allow keeping + * connection when going back to D0 + * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version) + * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version) + * @IWL_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan. + * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API + * @IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command + * containing CAM (Continuous Active Mode) indication. */ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_PAN = BIT(0), @@ -87,11 +99,21 @@ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_MFP = BIT(2), IWL_UCODE_TLV_FLAGS_P2P = BIT(3), IWL_UCODE_TLV_FLAGS_DW_BC_TABLE = BIT(4), - IWL_UCODE_TLV_FLAGS_UAPSD = BIT(6), + IWL_UCODE_TLV_FLAGS_NEWBT_COEX = BIT(5), + IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT = BIT(6), + IWL_UCODE_TLV_FLAGS_SHORT_BL = BIT(7), IWL_UCODE_TLV_FLAGS_RX_ENERGY_API = BIT(8), IWL_UCODE_TLV_FLAGS_TIME_EVENT_API_V2 = BIT(9), IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10), IWL_UCODE_TLV_FLAGS_BF_UPDATED = BIT(11), + IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12), + IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API = BIT(14), + IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15), + IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16), + IWL_UCODE_TLV_FLAGS_SCHED_SCAN = BIT(17), + IWL_UCODE_TLV_FLAGS_STA_KEY_CMD = BIT(19), + IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD = BIT(20), + IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24), }; /* The default calibrate table size if not specified by firmware file */ @@ -133,7 +155,8 @@ enum iwl_ucode_sec { * For 16.0 uCode and above, there is no differentiation between sections, * just an offset to the HW address. */ -#define IWL_UCODE_SECTION_MAX 4 +#define IWL_UCODE_SECTION_MAX 6 +#define IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU (IWL_UCODE_SECTION_MAX/2) struct iwl_ucode_capabilities { u32 max_probe_length; @@ -150,6 +173,8 @@ struct fw_desc { struct fw_img { struct fw_desc sec[IWL_UCODE_SECTION_MAX]; + bool is_secure; + bool is_dual_cpus; }; /* uCode version contains 4 values: Major/Minor/API/Serial */ diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index dfa4d2e3aaa28124c6074a497d7e003d72cd86a2..ad8e19a56eca034bf231c001ba16c0775fa047a3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -34,7 +34,6 @@ #include "iwl-csr.h" #include "iwl-debug.h" #include "iwl-fh.h" -#include "iwl-csr.h" #define IWL_POLL_INTERVAL 10 /* microseconds */ diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index ff8cc75c189d4d842abf8611fb8c5e7c7a63bf38..a70c7b9d9bad897345fb1e1e89d5c421e0d8a3da 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -97,6 +97,8 @@ #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) +#define APMG_RTC_INT_STT_RFKILL (0x10000000) + /* Device system time */ #define DEVICE_SYSTEM_TIME_REG 0xA0206C diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 80b47508647ca8d5861f4b4c8dc8e8ec879d34c3..143292b4dbbffeac763f86d5c9d9b1badae1aecc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -344,7 +344,7 @@ struct iwl_trans_config { u8 cmd_queue; u8 cmd_fifo; const u8 *no_reclaim_cmds; - int n_no_reclaim_cmds; + unsigned int n_no_reclaim_cmds; bool rx_buf_size_8k; bool bc_table_dword; @@ -601,7 +601,7 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans, { int ret; - if (trans->state != IWL_TRANS_FW_ALIVE) { + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); return -EIO; } @@ -640,8 +640,8 @@ static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans, static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int queue) { - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); return trans->ops->tx(trans, skb, dev_cmd, queue); } @@ -649,16 +649,16 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, static inline void iwl_trans_reclaim(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs) { - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); trans->ops->reclaim(trans, queue, ssn, skbs); } static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue) { - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); trans->ops->txq_disable(trans, queue); } @@ -669,8 +669,8 @@ static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, { might_sleep(); - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); + if (unlikely((trans->state != IWL_TRANS_FW_ALIVE))) + IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); trans->ops->txq_enable(trans, queue, fifo, sta_id, tid, frame_limit, ssn); @@ -685,8 +685,8 @@ static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans) { - WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, - "%s bad state = %d", __func__, trans->state); + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) + IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); return trans->ops->wait_tx_queue_empty(trans); } diff --git a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c index 0fad98b85f60dd8dedca668296b27382b15ca0f6..5d066cbc5ac7eda17914e85c2510c53fe4a5be2a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/bt-coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/bt-coex.c @@ -98,126 +98,258 @@ static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { #undef EVENT_PRIO_ANT -/* BT Antenna Coupling Threshold (dB) */ -#define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35) -#define IWL_BT_LOAD_FORCE_SISO_THRESHOLD (3) - #define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62) #define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65) -#define BT_REDUCED_TX_POWER_BIT BIT(7) - -static inline bool is_loose_coex(void) -{ - return iwlwifi_mod_params.ant_coupling > - IWL_BT_ANTENNA_COUPLING_THRESHOLD; -} +#define BT_ANTENNA_COUPLING_THRESHOLD (30) int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm) { + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX)) + return 0; + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, CMD_SYNC, sizeof(struct iwl_bt_coex_prio_tbl_cmd), &iwl_bt_prio_tbl); } -static int iwl_send_bt_env(struct iwl_mvm *mvm, u8 action, u8 type) -{ - struct iwl_bt_coex_prot_env_cmd env_cmd; - int ret; - - env_cmd.action = action; - env_cmd.type = type; - ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PROT_ENV, CMD_SYNC, - sizeof(env_cmd), &env_cmd); - if (ret) - IWL_ERR(mvm, "failed to send BT env command\n"); - return ret; -} - -enum iwl_bt_kill_msk { - BT_KILL_MSK_DEFAULT, - BT_KILL_MSK_SCO_HID_A2DP, - BT_KILL_MSK_REDUCED_TXPOW, - BT_KILL_MSK_MAX, -}; - -static const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = { +const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = { [BT_KILL_MSK_DEFAULT] = 0xffff0000, [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff, [BT_KILL_MSK_REDUCED_TXPOW] = 0, }; -static const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = { +const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX] = { [BT_KILL_MSK_DEFAULT] = 0xffff0000, [BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff, [BT_KILL_MSK_REDUCED_TXPOW] = 0, }; -#define IWL_BT_DEFAULT_BOOST (0xf0f0f0f0) - -/* Tight Coex */ -static const __le32 iwl_tight_lookup[BT_COEX_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaeaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xc0004000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), +static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = { + cpu_to_le32(0xf0f0f0f0), + cpu_to_le32(0xc0c0c0c0), + cpu_to_le32(0xfcfcfcfc), + cpu_to_le32(0xff00ff00), }; -/* Loose Coex */ -static const __le32 iwl_loose_lookup[BT_COEX_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xcc00ff28), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0xcc00aaaa), - cpu_to_le32(0x0000aaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xf0005000), - cpu_to_le32(0xf0005000), +static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, + { + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x40000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0x44000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xc0004000), + cpu_to_le32(0xf0005000), + }, }; -/* Full concurrency */ -static const __le32 iwl_concurrent_lookup[BT_COEX_LUT_SIZE] = { - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0xaaaaaaaa), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x00000000), +static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = { + { + /* Tight */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaeaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xc0004000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, + { + /* Loose */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), + cpu_to_le32(0xf0005000), + cpu_to_le32(0xf0005000), + }, + { + /* Tx Tx disabled */ + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xaaaaaaaa), + cpu_to_le32(0xcc00ff28), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xcc00aaaa), + cpu_to_le32(0x0000aaaa), + cpu_to_le32(0xC0004000), + cpu_to_le32(0xC0004000), + cpu_to_le32(0xF0005000), + cpu_to_le32(0xF0005000), + }, }; -/* single shared antenna */ -static const __le32 iwl_single_shared_ant_lookup[BT_COEX_LUT_SIZE] = { - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x40000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0x44000000), - cpu_to_le32(0x00000000), - cpu_to_le32(0xC0004000), - cpu_to_le32(0xF0005000), - cpu_to_le32(0xC0004000), - cpu_to_le32(0xF0005000), +/* 20MHz / 40MHz below / 40Mhz above*/ +static const __le64 iwl_ci_mask[][3] = { + /* dummy entry for channel 0 */ + {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)}, + { + cpu_to_le64(0x0000001FFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x00007FFFFFULL), + }, + { + cpu_to_le64(0x000000FFFFULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0003FFFFFFULL), + }, + { + cpu_to_le64(0x000003FFFCULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x000FFFFFFCULL), + }, + { + cpu_to_le64(0x00001FFFE0ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x007FFFFFE0ULL), + }, + { + cpu_to_le64(0x00007FFF80ULL), + cpu_to_le64(0x00007FFFFFULL), + cpu_to_le64(0x01FFFFFF80ULL), + }, + { + cpu_to_le64(0x0003FFFC00ULL), + cpu_to_le64(0x0003FFFFFFULL), + cpu_to_le64(0x0FFFFFFC00ULL), + }, + { + cpu_to_le64(0x000FFFF000ULL), + cpu_to_le64(0x000FFFFFFCULL), + cpu_to_le64(0x3FFFFFF000ULL), + }, + { + cpu_to_le64(0x007FFF8000ULL), + cpu_to_le64(0x007FFFFFE0ULL), + cpu_to_le64(0xFFFFFF8000ULL), + }, + { + cpu_to_le64(0x01FFFE0000ULL), + cpu_to_le64(0x01FFFFFF80ULL), + cpu_to_le64(0xFFFFFE0000ULL), + }, + { + cpu_to_le64(0x0FFFF00000ULL), + cpu_to_le64(0x0FFFFFFC00ULL), + cpu_to_le64(0x0ULL), + }, + { + cpu_to_le64(0x3FFFC00000ULL), + cpu_to_le64(0x3FFFFFF000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFFE000000ULL), + cpu_to_le64(0xFFFFFF8000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFFF8000000ULL), + cpu_to_le64(0xFFFFFE0000ULL), + cpu_to_le64(0x0) + }, + { + cpu_to_le64(0xFE00000000ULL), + cpu_to_le64(0x0ULL), + cpu_to_le64(0x0) + }, }; +static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = { + cpu_to_le32(0x22002200), + cpu_to_le32(0x33113311), +}; + +static enum iwl_bt_coex_lut_type +iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + enum iwl_bt_coex_lut_type ret; + u16 phy_ctx_id; + + /* + * Checking that we hold mvm->mutex is a good idea, but the rate + * control can't acquire the mutex since it runs in Tx path. + * So this is racy in that case, but in the worst case, the AMPDU + * size limit will be wrong for a short time which is not a big + * issue. + */ + + rcu_read_lock(); + + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + if (!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { + rcu_read_unlock(); + return BT_COEX_LOOSE_LUT; + } + + ret = BT_COEX_TX_DIS_LUT; + + if (mvm->cfg->bt_shared_single_ant) { + rcu_read_unlock(); + return ret; + } + + phy_ctx_id = *((u16 *)chanctx_conf->drv_priv); + + if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut); + else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id) + ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut); + /* else - default = TX TX disallowed */ + + rcu_read_unlock(); + + return ret; +} + int iwl_send_bt_init_conf(struct iwl_mvm *mvm) { struct iwl_bt_coex_cmd *bt_cmd; @@ -228,17 +360,10 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) .flags = CMD_SYNC, }; int ret; + u32 flags; - /* go to CALIB state in internal BT-Coex state machine */ - ret = iwl_send_bt_env(mvm, BT_COEX_ENV_OPEN, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; - - ret = iwl_send_bt_env(mvm, BT_COEX_ENV_CLOSE, - BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); - if (ret) - return ret; + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX)) + return 0; bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL); if (!bt_cmd) @@ -246,40 +371,52 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) cmd.data[0] = bt_cmd; bt_cmd->max_kill = 5; - bt_cmd->bt3_time_t7_value = 1; - bt_cmd->bt3_prio_sample_time = 2; - bt_cmd->bt3_timer_t2_value = 0xc; + bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD, + bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling, + bt_cmd->bt4_tx_tx_delta_freq_thr = 15, + bt_cmd->bt4_tx_rx_max_freq0 = 15, - bt_cmd->flags = iwlwifi_mod_params.bt_coex_active ? + flags = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE; - bt_cmd->flags |= BT_CH_PRIMARY_EN | BT_SYNC_2_BT_DISABLE; + flags |= BT_CH_PRIMARY_EN | BT_CH_SECONDARY_EN | BT_SYNC_2_BT_DISABLE; + bt_cmd->flags = cpu_to_le32(flags); - bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_ENABLE | + bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE | BT_VALID_BT_PRIO_BOOST | BT_VALID_MAX_KILL | BT_VALID_3W_TMRS | BT_VALID_KILL_ACK | BT_VALID_KILL_CTS | BT_VALID_REDUCED_TX_POWER | - BT_VALID_LUT); + BT_VALID_LUT | + BT_VALID_WIFI_RX_SW_PRIO_BOOST | + BT_VALID_WIFI_TX_SW_PRIO_BOOST | + BT_VALID_MULTI_PRIO_LUT | + BT_VALID_CORUN_LUT_20 | + BT_VALID_CORUN_LUT_40 | + BT_VALID_ANT_ISOLATION | + BT_VALID_ANT_ISOLATION_THRS | + BT_VALID_TXTX_DELTA_FREQ_THRS | + BT_VALID_TXRX_MAX_FREQ_0); if (mvm->cfg->bt_shared_single_ant) - memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant_lookup, - sizeof(iwl_single_shared_ant_lookup)); - else if (is_loose_coex()) - memcpy(&bt_cmd->decision_lut, iwl_loose_lookup, - sizeof(iwl_tight_lookup)); + memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant, + sizeof(iwl_single_shared_ant)); else - memcpy(&bt_cmd->decision_lut, iwl_tight_lookup, - sizeof(iwl_tight_lookup)); + memcpy(&bt_cmd->decision_lut, iwl_combined_lookup, + sizeof(iwl_combined_lookup)); - bt_cmd->bt_prio_boost = cpu_to_le32(IWL_BT_DEFAULT_BOOST); + memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost, + sizeof(iwl_bt_prio_boost)); + memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut, + sizeof(iwl_bt_mprio_lut)); bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]); bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]); memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif)); + memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd)); ret = iwl_mvm_send_cmd(mvm, &cmd); @@ -334,13 +471,17 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm, if (!bt_cmd) return -ENOMEM; cmd.data[0] = bt_cmd; + bt_cmd->flags = cpu_to_le32(BT_COEX_NW); bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]); bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]); - bt_cmd->valid_bit_msk = - cpu_to_le16(BT_VALID_KILL_ACK | BT_VALID_KILL_CTS); + bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE | + BT_VALID_KILL_ACK | + BT_VALID_KILL_CTS); - IWL_DEBUG_COEX(mvm, "bt_kill_msk = %d\n", bt_kill_msk); + IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n", + iwl_bt_ack_kill_msk[bt_kill_msk], + iwl_bt_cts_kill_msk[bt_kill_msk]); ret = iwl_mvm_send_cmd(mvm, &cmd); @@ -364,12 +505,16 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, struct iwl_mvm_sta *mvmsta; int ret; - /* This can happen if the station has been removed right now */ if (sta_id == IWL_MVM_STATION_COUNT) return 0; sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], lockdep_is_held(&mvm->mutex)); + + /* This can happen if the station has been removed right now */ + if (IS_ERR_OR_NULL(sta)) + return 0; + mvmsta = (void *)sta->drv_priv; /* nothing to do */ @@ -380,8 +525,10 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, if (!bt_cmd) return -ENOMEM; cmd.data[0] = bt_cmd; + bt_cmd->flags = cpu_to_le32(BT_COEX_NW); - bt_cmd->valid_bit_msk = cpu_to_le16(BT_VALID_REDUCED_TX_POWER), + bt_cmd->valid_bit_msk = + cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER); bt_cmd->bt_reduced_tx_power = sta_id; if (enable) @@ -403,8 +550,25 @@ struct iwl_bt_iterator_data { struct iwl_mvm *mvm; u32 num_bss_ifaces; bool reduced_tx_power; + struct ieee80211_chanctx_conf *primary; + struct ieee80211_chanctx_conf *secondary; }; +static inline +void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool enable, int rssi) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + mvmvif->bf_data.last_bt_coex_event = rssi; + mvmvif->bf_data.bt_coex_max_thold = + enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0; + mvmvif->bf_data.bt_coex_min_thold = + enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0; +} + +/* must be called under rcu_read_lock */ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { @@ -413,65 +577,94 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, struct iwl_mvm *mvm = data->mvm; struct ieee80211_chanctx_conf *chanctx_conf; enum ieee80211_smps_mode smps_mode; - enum ieee80211_band band; int ave_rssi; lockdep_assert_held(&mvm->mutex); - if (vif->type != NL80211_IFTYPE_STATION) - return; - rcu_read_lock(); - chanctx_conf = rcu_dereference(vif->chanctx_conf); - if (chanctx_conf && chanctx_conf->def.chan) - band = chanctx_conf->def.chan->band; - else - band = -1; - rcu_read_unlock(); + if (vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP) + return; smps_mode = IEEE80211_SMPS_AUTOMATIC; - /* non associated BSSes aren't to be considered */ - if (!vif->bss_conf.assoc) + chanctx_conf = rcu_dereference(vif->chanctx_conf); + + /* If channel context is invalid or not on 2.4GHz .. */ + if ((!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) { + /* ... and it is an associated STATION, relax constraints */ + if (vif->type == NL80211_IFTYPE_STATION && vif->bss_conf.assoc) + iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, + smps_mode); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); return; + } + + /* SoftAP / GO will always be primary */ + if (vif->type == NL80211_IFTYPE_AP) { + if (!mvmvif->ap_ibss_active) + return; - if (band != IEEE80211_BAND_2GHZ) { - iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, - smps_mode); + /* the Ack / Cts kill mask must be default if AP / GO */ + data->reduced_tx_power = false; + + if (chanctx_conf == data->primary) + return; + + /* downgrade the current primary no matter what its type is */ + data->secondary = data->primary; + data->primary = chanctx_conf; return; } - if (data->notif->bt_status) - smps_mode = IEEE80211_SMPS_DYNAMIC; + data->num_bss_ifaces++; + + /* we are now a STA / P2P Client, and take associated ones only */ + if (!vif->bss_conf.assoc) + return; + + /* STA / P2P Client, try to be primary if first vif */ + if (!data->primary || data->primary == chanctx_conf) + data->primary = chanctx_conf; + else if (!data->secondary) + /* if secondary is not NULL, it might be a GO */ + data->secondary = chanctx_conf; - if (data->notif->bt_traffic_load >= IWL_BT_LOAD_FORCE_SISO_THRESHOLD) + if (le32_to_cpu(data->notif->bt_activity_grading) >= BT_HIGH_TRAFFIC) smps_mode = IEEE80211_SMPS_STATIC; + else if (le32_to_cpu(data->notif->bt_activity_grading) >= + BT_LOW_TRAFFIC) + smps_mode = IEEE80211_SMPS_DYNAMIC; IWL_DEBUG_COEX(data->mvm, - "mac %d: bt_status %d traffic_load %d smps_req %d\n", + "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n", mvmvif->id, data->notif->bt_status, - data->notif->bt_traffic_load, smps_mode); + data->notif->bt_activity_grading, smps_mode); iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode); /* don't reduce the Tx power if in loose scheme */ - if (is_loose_coex()) + if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT || + mvm->cfg->bt_shared_single_ant) { + data->reduced_tx_power = false; + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); return; + } - data->num_bss_ifaces++; - - /* reduced Txpower only if there are open BT connections, so ...*/ - if (!BT_MBOX_MSG(data->notif, 3, OPEN_CON_2)) { + /* reduced Txpower only if BT is on, so ...*/ + if (!data->notif->bt_status) { /* ... cancel reduced Tx power ... */ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false)) IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n"); data->reduced_tx_power = false; /* ... and there is no need to get reports on RSSI any more. */ - ieee80211_disable_rssi_reports(vif); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0); return; } - ave_rssi = ieee80211_ave_rssi(vif); + /* try to get the avg rssi from fw */ + ave_rssi = mvmvif->bf_data.ave_beacon_signal; /* if the RSSI isn't valid, fake it is very low */ if (!ave_rssi) @@ -499,8 +692,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac, } /* Begin to monitor the RSSI: it may influence the reduced Tx power */ - ieee80211_enable_rssi_reports(vif, BT_DISABLE_REDUCED_TXPOWER_THRESHOLD, - BT_ENABLE_REDUCED_TXPOWER_THRESHOLD); + iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi); } static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) @@ -510,11 +702,72 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) .notif = &mvm->last_bt_notif, .reduced_tx_power = true, }; + struct iwl_bt_coex_ci_cmd cmd = {}; + u8 ci_bw_idx; + rcu_read_lock(); ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_bt_notif_iterator, &data); + if (data.primary) { + struct ieee80211_chanctx_conf *chan = data.primary; + if (WARN_ON(!chan->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + cmd.co_run_bw_primary = 0; + } else { + cmd.co_run_bw_primary = 1; + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_primary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv); + } + + if (data.secondary) { + struct ieee80211_chanctx_conf *chan = data.secondary; + if (WARN_ON(!data.secondary->def.chan)) { + rcu_read_unlock(); + return; + } + + if (chan->def.width < NL80211_CHAN_WIDTH_40) { + ci_bw_idx = 0; + cmd.co_run_bw_secondary = 0; + } else { + cmd.co_run_bw_secondary = 1; + if (chan->def.center_freq1 > + chan->def.chan->center_freq) + ci_bw_idx = 2; + else + ci_bw_idx = 1; + } + + cmd.bt_secondary_ci = + iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx]; + cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv); + } + + rcu_read_unlock(); + + /* Don't spam the fw with the same command over and over */ + if (memcmp(&cmd, &mvm->last_bt_ci_cmd, sizeof(cmd))) { + if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, CMD_SYNC, + sizeof(cmd), &cmd)) + IWL_ERR(mvm, "Failed to send BT_CI cmd"); + memcpy(&mvm->last_bt_ci_cmd, &cmd, sizeof(cmd)); + } + /* * If there are no BSS / P2P client interfaces, reduced Tx Power is * irrelevant since it is based on the RSSI coming from the beacon. @@ -536,12 +789,18 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n"); - IWL_DEBUG_COEX(mvm, "\tBT %salive\n", notif->bt_status ? "" : "not "); + IWL_DEBUG_COEX(mvm, "\tBT status: %s\n", + notif->bt_status ? "ON" : "OFF"); IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn); - IWL_DEBUG_COEX(mvm, "\tBT traffic load %d\n", notif->bt_traffic_load); + IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); + IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n", + le32_to_cpu(notif->primary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n", + le32_to_cpu(notif->bt_activity_grading)); IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n", notif->bt_agg_traffic_load); - IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance); /* remember this notification for future use: rssi fluctuations */ memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif)); @@ -565,6 +824,18 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, struct ieee80211_sta *sta; struct iwl_mvm_sta *mvmsta; + struct ieee80211_chanctx_conf *chanctx_conf; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(vif->chanctx_conf); + /* If channel context is invalid or not on 2.4GHz - don't count it */ + if (!chanctx_conf || + chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) { + rcu_read_unlock(); + return; + } + rcu_read_unlock(); + if (vif->type != NL80211_IFTYPE_STATION || mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT) return; @@ -594,15 +865,15 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, }; int ret; - mutex_lock(&mvm->mutex); + lockdep_assert_held(&mvm->mutex); /* Rssi update while not associated ?! */ if (WARN_ON_ONCE(mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)) - goto out_unlock; + return; - /* No open connection - reports should be disabled */ - if (!BT_MBOX_MSG(&mvm->last_bt_notif, 3, OPEN_CON_2)) - goto out_unlock; + /* No BT - reports should be disabled */ + if (!mvm->last_bt_notif.bt_status) + return; IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid, rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW"); @@ -611,7 +882,8 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * Check if rssi is good enough for reduced Tx power, but not in loose * scheme. */ - if (rssi_event == RSSI_EVENT_LOW || is_loose_coex()) + if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant || + iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT) ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false); else @@ -633,12 +905,52 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power)) IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n"); +} - out_unlock: - mutex_unlock(&mvm->mutex); +#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) +#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200) + +u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) +{ + struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + enum iwl_bt_coex_lut_type lut_type; + + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < + BT_LOW_TRAFFIC) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + lut_type = iwl_get_coex_type(mvm, mvmsta->vif); + + if (lut_type == BT_COEX_LOOSE_LUT) + return LINK_QUAL_AGG_TIME_LIMIT_DEF; + + /* tight coex, high bt traffic, reduce AGG time limit */ + return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT; } -void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, + struct ieee80211_sta *sta) { + struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; + + if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) < + BT_HIGH_TRAFFIC) + return true; + + /* + * In Tight, BT can't Rx while we Tx, so use both antennas since BT is + * already killed. + * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while we + * Tx. + */ + return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT; +} + +void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm) +{ + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWBT_COEX)) + return; + iwl_mvm_bt_coex_notif_handle(mvm); } diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index 2bf29f7992ee4d953536bf1ec6ab0b2fbf1debae..4b6d670c35092d18679e8437ce5dbeb14ecf7702 100644 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -70,7 +70,9 @@ #define IWL_MVM_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC) #define IWL_MVM_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC) #define IWL_MVM_PS_HEAVY_TX_THLD_PACKETS 20 -#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 20 +#define IWL_MVM_PS_HEAVY_RX_THLD_PACKETS 8 +#define IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30 +#define IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20 #define IWL_MVM_PS_HEAVY_TX_THLD_PERCENT 50 #define IWL_MVM_PS_HEAVY_RX_THLD_PERCENT 50 #define IWL_MVM_PS_SNOOZE_INTERVAL 25 diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 417639f77b01c8a9a166de79e4374cefa2313ba6..6f45966817bb4c1d34cd3a10c4db74536f4a13bf 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -67,6 +67,7 @@ #include #include #include +#include #include "iwl-modparams.h" #include "fw-api.h" #include "mvm.h" @@ -381,14 +382,74 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, union { struct iwl_proto_offload_cmd_v1 v1; struct iwl_proto_offload_cmd_v2 v2; + struct iwl_proto_offload_cmd_v3_small v3s; + struct iwl_proto_offload_cmd_v3_large v3l; } cmd = {}; + struct iwl_host_cmd hcmd = { + .id = PROT_OFFLOAD_CONFIG_CMD, + .flags = CMD_SYNC, + .data[0] = &cmd, + .dataflags[0] = IWL_HCMD_DFL_DUP, + }; struct iwl_proto_offload_cmd_common *common; u32 enabled = 0, size; + u32 capa_flags = mvm->fw->ucode_capa.flags; #if IS_ENABLED(CONFIG_IPV6) struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); int i; - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || + capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { + struct iwl_ns_config *nsc; + struct iwl_targ_addr *addrs; + int n_nsc, n_addrs; + int c; + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { + nsc = cmd.v3s.ns_config; + n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; + addrs = cmd.v3s.targ_addrs; + n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; + } else { + nsc = cmd.v3l.ns_config; + n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; + addrs = cmd.v3l.targ_addrs; + n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; + } + + if (mvmvif->num_target_ipv6_addrs) + enabled |= IWL_D3_PROTO_OFFLOAD_NS; + + /* + * For each address we have (and that will fit) fill a target + * address struct and combine for NS offload structs with the + * solicited node addresses. + */ + for (i = 0, c = 0; + i < mvmvif->num_target_ipv6_addrs && + i < n_addrs && c < n_nsc; i++) { + struct in6_addr solicited_addr; + int j; + + addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], + &solicited_addr); + for (j = 0; j < c; j++) + if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, + &solicited_addr) == 0) + break; + if (j == c) + c++; + addrs[i].addr = mvmvif->target_ipv6_addrs[i]; + addrs[i].config_num = cpu_to_le32(j); + nsc[j].dest_ipv6_addr = solicited_addr; + memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); + } + + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) + cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); + else + cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); + } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { if (mvmvif->num_target_ipv6_addrs) { enabled |= IWL_D3_PROTO_OFFLOAD_NS; memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); @@ -419,7 +480,13 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, } #endif - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { + if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { + common = &cmd.v3s.common; + size = sizeof(cmd.v3s); + } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { + common = &cmd.v3l.common; + size = sizeof(cmd.v3l); + } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { common = &cmd.v2.common; size = sizeof(cmd.v2); } else { @@ -438,8 +505,8 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, common->enabled = cpu_to_le32(enabled); - return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC, - size, &cmd); + hcmd.len[0] = size; + return iwl_mvm_send_cmd(mvm, &hcmd); } enum iwl_mvm_tcp_packet_type { @@ -793,6 +860,74 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return 0; } +static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_nonqos_seq_query_cmd query_cmd = { + .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET), + .mac_id_n_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)), + }; + struct iwl_host_cmd cmd = { + .id = NON_QOS_TX_COUNTER_CMD, + .flags = CMD_SYNC | CMD_WANT_SKB, + }; + int err; + u32 size; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) { + cmd.data[0] = &query_cmd; + cmd.len[0] = sizeof(query_cmd); + } + + err = iwl_mvm_send_cmd(mvm, &cmd); + if (err) + return err; + + size = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + size -= sizeof(cmd.resp_pkt->hdr); + if (size < sizeof(__le16)) { + err = -EINVAL; + } else { + err = le16_to_cpup((__le16 *)cmd.resp_pkt->data); + /* new API returns next, not last-used seqno */ + if (mvm->fw->ucode_capa.flags & + IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) + err -= 0x10; + } + + iwl_free_resp(&cmd); + return err; +} + +void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_nonqos_seq_query_cmd query_cmd = { + .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET), + .mac_id_n_color = + cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)), + .value = cpu_to_le16(mvmvif->seqno), + }; + + /* return if called during restart, not resume from D3 */ + if (!mvmvif->seqno_valid) + return; + + mvmvif->seqno_valid = false; + + if (!(mvm->fw->ucode_capa.flags & + IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)) + return; + + if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, CMD_SYNC, + sizeof(query_cmd), &query_cmd)) + IWL_ERR(mvm, "failed to set non-QoS seqno\n"); +} + static int __iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan, bool test) @@ -829,7 +964,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, }; int ret, i; int len __maybe_unused; - u16 seq; u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT; if (!wowlan) { @@ -872,26 +1006,15 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; - /* - * The D3 firmware still hardcodes the AP station ID for the - * BSS we're associated with as 0. Store the real STA ID here - * and assign 0. When we leave this function, we'll restore - * the original value for the resume code. - */ - old_ap_sta_id = mvm_ap_sta->sta_id; - mvm_ap_sta->sta_id = 0; - mvmvif->ap_sta_id = 0; - /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */ wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported; - /* - * We know the last used seqno, and the uCode expects to know that - * one, it will increment before TX. - */ - seq = mvm_ap_sta->last_seq_ctl & IEEE80211_SCTL_SEQ; - wowlan_config_cmd.non_qos_seq = cpu_to_le16(seq); + /* Query the last used seqno and set it */ + ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); + if (ret < 0) + goto out_noreset; + wowlan_config_cmd.non_qos_seq = cpu_to_le16(ret); /* * For QoS counters, we store the one to use next, so subtract 0x10 @@ -899,7 +1022,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, * increment after using the value (i.e. store the next value to use). */ for (i = 0; i < IWL_MAX_TID_COUNT; i++) { - seq = mvm_ap_sta->tid_data[i].seq_number; + u16 seq = mvm_ap_sta->tid_data[i].seq_number; seq -= 0x10; wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq); } @@ -944,6 +1067,16 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, iwl_trans_stop_device(mvm->trans); + /* + * The D3 firmware still hardcodes the AP station ID for the + * BSS we're associated with as 0. Store the real STA ID here + * and assign 0. When we leave this function, we'll restore + * the original value for the resume code. + */ + old_ap_sta_id = mvm_ap_sta->sta_id; + mvm_ap_sta->sta_id = 0; + mvmvif->ap_sta_id = 0; + /* * Set the HW restart bit -- this is mostly true as we're * going to load new firmware and reprogram that, though @@ -1059,6 +1192,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, if (ret) goto out; + ret = iwl_mvm_power_update_device_mode(mvm); + if (ret) + goto out; + ret = iwl_mvm_power_update_mode(mvm, vif); if (ret) goto out; @@ -1109,16 +1246,26 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return __iwl_mvm_suspend(hw, wowlan, false); } +/* converted data from the different status responses */ +struct iwl_wowlan_status_data { + u16 pattern_number; + u16 qos_seq_ctr[8]; + u32 wakeup_reasons; + u32 wake_packet_length; + u32 wake_packet_bufsize; + const u8 *wake_packet; +}; + static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_wowlan_status *status) + struct iwl_wowlan_status_data *status) { struct sk_buff *pkt = NULL; struct cfg80211_wowlan_wakeup wakeup = { .pattern_idx = -1, }; struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; - u32 reasons = le32_to_cpu(status->wakeup_reasons); + u32 reasons = status->wakeup_reasons; if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { wakeup_report = NULL; @@ -1130,7 +1277,7 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) wakeup.pattern_idx = - le16_to_cpu(status->pattern_number); + status->pattern_number; if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) @@ -1158,8 +1305,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, wakeup.tcp_match = true; if (status->wake_packet_bufsize) { - int pktsize = le32_to_cpu(status->wake_packet_bufsize); - int pktlen = le32_to_cpu(status->wake_packet_length); + int pktsize = status->wake_packet_bufsize; + int pktlen = status->wake_packet_length; const u8 *pktdata = status->wake_packet; struct ieee80211_hdr *hdr = (void *)pktdata; int truncated = pktlen - pktsize; @@ -1239,8 +1386,229 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, kfree_skb(pkt); } +static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc, + struct ieee80211_key_seq *seq) +{ + u64 pn; + + pn = le64_to_cpu(sc->pn); + seq->ccmp.pn[0] = pn >> 40; + seq->ccmp.pn[1] = pn >> 32; + seq->ccmp.pn[2] = pn >> 24; + seq->ccmp.pn[3] = pn >> 16; + seq->ccmp.pn[4] = pn >> 8; + seq->ccmp.pn[5] = pn; +} + +static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc, + struct ieee80211_key_seq *seq) +{ + seq->tkip.iv32 = le32_to_cpu(sc->iv32); + seq->tkip.iv16 = le16_to_cpu(sc->iv16); +} + +static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs, + struct ieee80211_key_conf *key) +{ + int tid; + + BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); + + for (tid = 0; tid < IWL_NUM_RSC; tid++) { + struct ieee80211_key_seq seq = {}; + + iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } +} + +static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs, + struct ieee80211_key_conf *key) +{ + int tid; + + BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); + + for (tid = 0; tid < IWL_NUM_RSC; tid++) { + struct ieee80211_key_seq seq = {}; + + iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq); + ieee80211_set_key_rx_seq(key, tid, &seq); + } +} + +static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, + struct iwl_wowlan_status_v6 *status) +{ + union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key); + break; + case WLAN_CIPHER_SUITE_TKIP: + iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key); + break; + default: + WARN_ON(1); + } +} + +struct iwl_mvm_d3_gtk_iter_data { + struct iwl_wowlan_status_v6 *status; + void *last_gtk; + u32 cipher; + bool find_phase, unhandled_cipher; + int num_keys; +}; + +static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mvm_d3_gtk_iter_data *data = _data; + + if (data->unhandled_cipher) + return; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* ignore WEP completely, nothing to do */ + return; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_TKIP: + /* we support these */ + break; + default: + /* everything else (even CMAC for MFP) - disconnect from AP */ + data->unhandled_cipher = true; + return; + } + + data->num_keys++; + + /* + * pairwise key - update sequence counters only; + * note that this assumes no TDLS sessions are active + */ + if (sta) { + struct ieee80211_key_seq seq = {}; + union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc; + + if (data->find_phase) + return; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + iwl_mvm_aes_sc_to_seq(&sc->aes.tsc, &seq); + iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); + break; + case WLAN_CIPHER_SUITE_TKIP: + iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); + iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); + break; + } + ieee80211_set_key_tx_seq(key, &seq); + + /* that's it for this key */ + return; + } + + if (data->find_phase) { + data->last_gtk = key; + data->cipher = key->cipher; + return; + } + + if (data->status->num_of_gtk_rekeys) + ieee80211_remove_key(key); + else if (data->last_gtk == key) + iwl_mvm_set_key_rx_seq(key, data->status); +} + +static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_wowlan_status_v6 *status) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm_d3_gtk_iter_data gtkdata = { + .status = status, + }; + + if (!status || !vif->bss_conf.bssid) + return false; + + /* find last GTK that we used initially, if any */ + gtkdata.find_phase = true; + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_d3_update_gtks, >kdata); + /* not trying to keep connections with MFP/unhandled ciphers */ + if (gtkdata.unhandled_cipher) + return false; + if (!gtkdata.num_keys) + return true; + if (!gtkdata.last_gtk) + return false; + + /* + * invalidate all other GTKs that might still exist and update + * the one that we used + */ + gtkdata.find_phase = false; + ieee80211_iter_keys(mvm->hw, vif, + iwl_mvm_d3_update_gtks, >kdata); + + if (status->num_of_gtk_rekeys) { + struct ieee80211_key_conf *key; + struct { + struct ieee80211_key_conf conf; + u8 key[32]; + } conf = { + .conf.cipher = gtkdata.cipher, + .conf.keyidx = status->gtk.key_index, + }; + + switch (gtkdata.cipher) { + case WLAN_CIPHER_SUITE_CCMP: + conf.conf.keylen = WLAN_KEY_LEN_CCMP; + memcpy(conf.conf.key, status->gtk.decrypt_key, + WLAN_KEY_LEN_CCMP); + break; + case WLAN_CIPHER_SUITE_TKIP: + conf.conf.keylen = WLAN_KEY_LEN_TKIP; + memcpy(conf.conf.key, status->gtk.decrypt_key, 16); + /* leave TX MIC key zeroed, we don't use it anyway */ + memcpy(conf.conf.key + + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, + status->gtk.tkip_mic_key, 8); + break; + } + + key = ieee80211_gtk_rekey_add(vif, &conf.conf); + if (IS_ERR(key)) + return false; + iwl_mvm_set_key_rx_seq(key, status); + } + + if (status->num_of_gtk_rekeys) { + __be64 replay_ctr = + cpu_to_be64(le64_to_cpu(status->replay_ctr)); + ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid, + (void *)&replay_ctr, GFP_KERNEL); + } + + mvmvif->seqno_valid = true; + /* +0x10 because the set API expects next-to-use, not last-used */ + mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10; + + return true; +} + /* releases the MVM mutex */ -static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, +static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { u32 base = mvm->error_event_table; @@ -1253,8 +1621,12 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, .id = WOWLAN_GET_STATUSES, .flags = CMD_SYNC | CMD_WANT_SKB, }; - struct iwl_wowlan_status *status; - int ret, len; + struct iwl_wowlan_status_data status; + struct iwl_wowlan_status_v6 *status_v6; + int ret, len, status_size, i; + bool keep; + struct ieee80211_sta *ap_sta; + struct iwl_mvm_sta *mvm_ap_sta; iwl_trans_read_mem_bytes(mvm->trans, base, &err_info, sizeof(err_info)); @@ -1287,32 +1659,83 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, if (!cmd.resp_pkt) goto out_unlock; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) + status_size = sizeof(struct iwl_wowlan_status_v6); + else + status_size = sizeof(struct iwl_wowlan_status_v4); + len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) { + if (len - sizeof(struct iwl_cmd_header) < status_size) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); goto out_free_resp; } - status = (void *)cmd.resp_pkt->data; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) { + status_v6 = (void *)cmd.resp_pkt->data; + + status.pattern_number = le16_to_cpu(status_v6->pattern_number); + for (i = 0; i < 8; i++) + status.qos_seq_ctr[i] = + le16_to_cpu(status_v6->qos_seq_ctr[i]); + status.wakeup_reasons = le32_to_cpu(status_v6->wakeup_reasons); + status.wake_packet_length = + le32_to_cpu(status_v6->wake_packet_length); + status.wake_packet_bufsize = + le32_to_cpu(status_v6->wake_packet_bufsize); + status.wake_packet = status_v6->wake_packet; + } else { + struct iwl_wowlan_status_v4 *status_v4; + status_v6 = NULL; + status_v4 = (void *)cmd.resp_pkt->data; + + status.pattern_number = le16_to_cpu(status_v4->pattern_number); + for (i = 0; i < 8; i++) + status.qos_seq_ctr[i] = + le16_to_cpu(status_v4->qos_seq_ctr[i]); + status.wakeup_reasons = le32_to_cpu(status_v4->wakeup_reasons); + status.wake_packet_length = + le32_to_cpu(status_v4->wake_packet_length); + status.wake_packet_bufsize = + le32_to_cpu(status_v4->wake_packet_bufsize); + status.wake_packet = status_v4->wake_packet; + } if (len - sizeof(struct iwl_cmd_header) != - sizeof(*status) + - ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) { + status_size + ALIGN(status.wake_packet_bufsize, 4)) { IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); goto out_free_resp; } + /* still at hard-coded place 0 for D3 image */ + ap_sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[0], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(ap_sta)) + goto out_free_resp; + + mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = status.qos_seq_ctr[i]; + /* firmware stores last-used value, we store next value */ + seq += 0x10; + mvm_ap_sta->tid_data[i].seq_number = seq; + } + /* now we have all the data we need, unlock to avoid mac80211 issues */ mutex_unlock(&mvm->mutex); - iwl_mvm_report_wakeup_reasons(mvm, vif, status); + iwl_mvm_report_wakeup_reasons(mvm, vif, &status); + + keep = iwl_mvm_setup_connection_keep(mvm, vif, status_v6); + iwl_free_resp(&cmd); - return; + return keep; out_free_resp: iwl_free_resp(&cmd); out_unlock: mutex_unlock(&mvm->mutex); + return false; } static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) @@ -1335,6 +1758,17 @@ static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) #endif } +static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + /* skip the one we keep connection on */ + if (data == vif) + return; + + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_resume_disconnect(vif); +} + static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) { struct iwl_d3_iter_data resume_iter_data = { @@ -1343,6 +1777,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) struct ieee80211_vif *vif = NULL; int ret; enum iwl_d3_status d3_status; + bool keep = false; mutex_lock(&mvm->mutex); @@ -1368,7 +1803,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) /* query SRAM first in case we want event logging */ iwl_mvm_read_d3_sram(mvm); - iwl_mvm_query_wakeup_reasons(mvm, vif); + keep = iwl_mvm_query_wakeup_reasons(mvm, vif); /* has unlocked the mutex, so skip that */ goto out; @@ -1376,8 +1811,10 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) mutex_unlock(&mvm->mutex); out: - if (!test && vif) - ieee80211_resume_disconnect(vif); + if (!test) + ieee80211_iterate_active_interfaces_rtnl(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); /* return 1 to reconfigure the device */ set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index aac81b8984b05ec87f50fa09b32f7ab9a3dc36ed..9864d713eb2cb54920053fb971a65866f5ec0bf4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -246,58 +246,56 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -static ssize_t iwl_dbgfs_power_down_allow_write(struct file *file, - const char __user *user_buf, +static ssize_t iwl_dbgfs_disable_power_off_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - char buf[8] = {}; - int allow; - - if (!mvm->ucode_loaded) - return -EIO; - - if (copy_from_user(buf, user_buf, sizeof(buf))) - return -EFAULT; - - if (sscanf(buf, "%d", &allow) != 1) - return -EINVAL; - - IWL_DEBUG_POWER(mvm, "%s device power down\n", - allow ? "allow" : "prevent"); + char buf[64]; + int bufsz = sizeof(buf); + int pos = 0; - /* - * TODO: Send REPLY_DEBUG_CMD (0xf0) when FW support it - */ + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d0=%d\n", + mvm->disable_power_off); + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off_d3=%d\n", + mvm->disable_power_off_d3); - return count; + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } -static ssize_t iwl_dbgfs_power_down_d3_allow_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t iwl_dbgfs_disable_power_off_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - char buf[8] = {}; - int allow; + char buf[64] = {}; + int ret; + int val; - if (copy_from_user(buf, user_buf, sizeof(buf))) + if (!mvm->ucode_loaded) + return -EIO; + + count = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, count)) return -EFAULT; - if (sscanf(buf, "%d", &allow) != 1) + if (!strncmp("disable_power_off_d0=", buf, 21)) { + if (sscanf(buf + 21, "%d", &val) != 1) + return -EINVAL; + mvm->disable_power_off = val; + } else if (!strncmp("disable_power_off_d3=", buf, 21)) { + if (sscanf(buf + 21, "%d", &val) != 1) + return -EINVAL; + mvm->disable_power_off_d3 = val; + } else { return -EINVAL; + } - IWL_DEBUG_POWER(mvm, "%s device power down in d3\n", - allow ? "allow" : "prevent"); - - /* - * TODO: When WoWLAN FW alive notification happens, driver will send - * REPLY_DEBUG_CMD setting power_down_allow flag according to - * mvm->prevent_power_down_d3 - */ - mvm->prevent_power_down_d3 = !allow; + mutex_lock(&mvm->mutex); + ret = iwl_mvm_power_update_device_mode(mvm); + mutex_unlock(&mvm->mutex); - return count; + return ret ?: count; } static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, @@ -344,6 +342,7 @@ static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, case MVM_DEBUGFS_PM_DISABLE_POWER_OFF: IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val); dbgfs_pm->disable_power_off = val; + break; case MVM_DEBUGFS_PM_LPRX_ENA: IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); dbgfs_pm->lprx_ena = val; @@ -371,7 +370,8 @@ static ssize_t iwl_dbgfs_pm_params_write(struct file *file, int val; int ret; - if (copy_from_user(buf, user_buf, sizeof(buf))) + count = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, count)) return -EFAULT; if (!strncmp("keep_alive=", buf, 11)) { @@ -394,7 +394,9 @@ static ssize_t iwl_dbgfs_pm_params_write(struct file *file, if (sscanf(buf + 16, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; - } else if (!strncmp("disable_power_off=", buf, 18)) { + } else if (!strncmp("disable_power_off=", buf, 18) && + !(mvm->fw->ucode_capa.flags & + IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) { if (sscanf(buf + 18, "%d", &val) != 1) return -EINVAL; param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF; @@ -581,15 +583,21 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, BT_MBOX_PRINT(3, UPDATE_REQUEST, true); pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n", - notif->bt_status); + notif->bt_status); pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n", - notif->bt_open_conn); + notif->bt_open_conn); pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n", - notif->bt_traffic_load); + notif->bt_traffic_load); pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n", - notif->bt_agg_traffic_load); + notif->bt_agg_traffic_load); pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n", - notif->bt_ci_compliance); + notif->bt_ci_compliance); + pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n", + le32_to_cpu(notif->primary_ch_lut)); + pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n", + le32_to_cpu(notif->secondary_ch_lut)); + pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n", + le32_to_cpu(notif->bt_activity_grading)); mutex_unlock(&mvm->mutex); @@ -600,6 +608,38 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf, } #undef BT_MBOX_PRINT +static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd; + char buf[256]; + int bufsz = sizeof(buf); + int pos = 0; + + mutex_lock(&mvm->mutex); + + pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, + "\tPrimary Channel Bitmap 0x%016llx Fat: %d\n", + le64_to_cpu(cmd->bt_primary_ci), + !!cmd->co_run_bw_primary); + pos += scnprintf(buf+pos, bufsz-pos, + "\tSecondary Channel Bitmap 0x%016llx Fat: %d\n", + le64_to_cpu(cmd->bt_secondary_ci), + !!cmd->co_run_bw_secondary); + + pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n"); + pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n", + iwl_bt_ack_kill_msk[mvm->bt_kill_msk]); + pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n", + iwl_bt_cts_kill_msk[mvm->bt_kill_msk]); + + mutex_unlock(&mvm->mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + #define PRINT_STATS_LE32(_str, _val) \ pos += scnprintf(buf + pos, bufsz - pos, \ fmt_table, _str, \ @@ -615,9 +655,11 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, int pos = 0; char *buf; int ret; - int bufsz = sizeof(struct mvm_statistics_rx_phy) * 20 + - sizeof(struct mvm_statistics_rx_non_phy) * 10 + - sizeof(struct mvm_statistics_rx_ht_phy) * 10 + 200; + /* 43 is the size of each data line, 33 is the size of each header */ + size_t bufsz = + ((sizeof(struct mvm_statistics_rx) / sizeof(__le32)) * 43) + + (4 * 33) + 1; + struct mvm_statistics_rx_phy *ofdm; struct mvm_statistics_rx_phy *cck; struct mvm_statistics_rx_non_phy *general; @@ -712,6 +754,7 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, PRINT_STATS_LE32("beacon_energy_b", general->beacon_energy_b); PRINT_STATS_LE32("beacon_energy_c", general->beacon_energy_c); PRINT_STATS_LE32("num_bt_kills", general->num_bt_kills); + PRINT_STATS_LE32("mac_id", general->mac_id); PRINT_STATS_LE32("directed_data_mpdu", general->directed_data_mpdu); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, @@ -757,6 +800,59 @@ static ssize_t iwl_dbgfs_fw_restart_write(struct file *file, return count; } +static ssize_t +iwl_dbgfs_scan_ant_rxchain_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + int pos = 0; + char buf[32]; + const size_t bufsz = sizeof(buf); + + /* print which antennas were set for the scan command by the user */ + pos += scnprintf(buf + pos, bufsz - pos, "Antennas for scan: "); + if (mvm->scan_rx_ant & ANT_A) + pos += scnprintf(buf + pos, bufsz - pos, "A"); + if (mvm->scan_rx_ant & ANT_B) + pos += scnprintf(buf + pos, bufsz - pos, "B"); + if (mvm->scan_rx_ant & ANT_C) + pos += scnprintf(buf + pos, bufsz - pos, "C"); + pos += scnprintf(buf + pos, bufsz - pos, " (%hhx)\n", mvm->scan_rx_ant); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t +iwl_dbgfs_scan_ant_rxchain_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + char buf[8]; + int buf_size; + u8 scan_rx_ant; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + + /* get the argument from the user and check if it is valid */ + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%hhx", &scan_rx_ant) != 1) + return -EINVAL; + if (scan_rx_ant > ANT_ABC) + return -EINVAL; + if (scan_rx_ant & ~iwl_fw_valid_rx_ant(mvm->fw)) + return -EINVAL; + + /* change the rx antennas for scan command */ + mvm->scan_rx_ant = scan_rx_ant; + + return count; +} + + static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, enum iwl_dbgfs_bf_mask param, int value) { @@ -968,7 +1064,8 @@ static ssize_t iwl_dbgfs_d3_sram_write(struct file *file, char buf[8] = {}; int store; - if (copy_from_user(buf, user_buf, sizeof(buf))) + count = min_t(size_t, count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, count)) return -EFAULT; if (sscanf(buf, "%d", &store) != 1) @@ -1063,10 +1160,12 @@ MVM_DEBUGFS_WRITE_FILE_OPS(sta_drain); MVM_DEBUGFS_READ_WRITE_FILE_OPS(sram); MVM_DEBUGFS_READ_FILE_OPS(stations); MVM_DEBUGFS_READ_FILE_OPS(bt_notif); -MVM_DEBUGFS_WRITE_FILE_OPS(power_down_allow); -MVM_DEBUGFS_WRITE_FILE_OPS(power_down_d3_allow); +MVM_DEBUGFS_READ_FILE_OPS(bt_cmd); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(disable_power_off); MVM_DEBUGFS_READ_FILE_OPS(fw_rx_stats); MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain); + #ifdef CONFIG_PM_SLEEP MVM_DEBUGFS_READ_WRITE_FILE_OPS(d3_sram); #endif @@ -1087,10 +1186,14 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) MVM_DEBUGFS_ADD_FILE(sram, mvm->debugfs_dir, S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(stations, dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(bt_notif, dbgfs_dir, S_IRUSR); - MVM_DEBUGFS_ADD_FILE(power_down_allow, mvm->debugfs_dir, S_IWUSR); - MVM_DEBUGFS_ADD_FILE(power_down_d3_allow, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(bt_cmd, dbgfs_dir, S_IRUSR); + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD) + MVM_DEBUGFS_ADD_FILE(disable_power_off, mvm->debugfs_dir, + S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(fw_rx_stats, mvm->debugfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir, + S_IWUSR | S_IRUSR); #ifdef CONFIG_PM_SLEEP MVM_DEBUGFS_ADD_FILE(d3_sram, mvm->debugfs_dir, S_IRUSR | S_IWUSR); MVM_DEBUGFS_ADD_FILE(d3_test, mvm->debugfs_dir, S_IRUSR); diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h index 05c61d6f384eeaa5f75ff2d461692cbec3b8a6fa..4ea5e24ca92d8da9a0a366a3ab0e0bf903f55ef1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-bt-coex.h @@ -82,6 +82,8 @@ * @BT_USE_DEFAULTS: * @BT_SYNC_2_BT_DISABLE: * @BT_COEX_CORUNNING_TBL_EN: + * + * The COEX_MODE must be set for each command. Even if it is not changed. */ enum iwl_bt_coex_flags { BT_CH_PRIMARY_EN = BIT(0), @@ -95,14 +97,16 @@ enum iwl_bt_coex_flags { BT_COEX_NW = 0x3 << BT_COEX_MODE_POS, BT_USE_DEFAULTS = BIT(6), BT_SYNC_2_BT_DISABLE = BIT(7), - /* - * For future use - when the flags will be enlarged - * BT_COEX_CORUNNING_TBL_EN = BIT(8), - */ + BT_COEX_CORUNNING_TBL_EN = BIT(8), + BT_COEX_MPLUT_TBL_EN = BIT(9), + /* Bit 10 is reserved */ + BT_COEX_WF_PRIO_BOOST_CHECK_EN = BIT(11), }; /* * indicates what has changed in the BT_COEX command. + * BT_VALID_ENABLE must be set for each command. Commands without this bit will + * discarded by the firmware */ enum iwl_bt_coex_valid_bit_msk { BT_VALID_ENABLE = BIT(0), @@ -121,11 +125,8 @@ enum iwl_bt_coex_valid_bit_msk { BT_VALID_CORUN_LUT_40 = BIT(13), BT_VALID_ANT_ISOLATION = BIT(14), BT_VALID_ANT_ISOLATION_THRS = BIT(15), - /* - * For future use - when the valid flags will be enlarged - * BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), - * BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), - */ + BT_VALID_TXTX_DELTA_FREQ_THRS = BIT(16), + BT_VALID_TXRX_MAX_FREQ_0 = BIT(17), }; /** @@ -142,48 +143,88 @@ enum iwl_bt_reduced_tx_power { BT_REDUCED_TX_POWER_DATA = BIT(1), }; +enum iwl_bt_coex_lut_type { + BT_COEX_TIGHT_LUT = 0, + BT_COEX_LOOSE_LUT, + BT_COEX_TX_DIS_LUT, + + BT_COEX_MAX_LUT, +}; + #define BT_COEX_LUT_SIZE (12) +#define BT_COEX_CORUN_LUT_SIZE (32) +#define BT_COEX_MULTI_PRIO_LUT_SIZE (2) +#define BT_COEX_BOOST_SIZE (4) +#define BT_REDUCED_TX_POWER_BIT BIT(7) /** * struct iwl_bt_coex_cmd - bt coex configuration command * @flags:&enum iwl_bt_coex_flags - * @lead_time: * @max_kill: - * @bt3_time_t7_value: - * @kill_ack_msk: - * @kill_cts_msk: - * @bt3_prio_sample_time: - * @bt3_timer_t2_value: - * @bt4_reaction_time: - * @decision_lut[12]: * @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power - * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk - * @bt_prio_boost: values for PTA boost register + * @bt4_antenna_isolation: + * @bt4_antenna_isolation_thr: + * @bt4_tx_tx_delta_freq_thr: + * @bt4_tx_rx_max_freq0: + * @bt_prio_boost: * @wifi_tx_prio_boost: SW boost of wifi tx priority * @wifi_rx_prio_boost: SW boost of wifi rx priority + * @kill_ack_msk: + * @kill_cts_msk: + * @decision_lut: + * @bt4_multiprio_lut: + * @bt4_corun_lut20: + * @bt4_corun_lut40: + * @valid_bit_msk: enum %iwl_bt_coex_valid_bit_msk * * The structure is used for the BT_COEX command. */ struct iwl_bt_coex_cmd { - u8 flags; - u8 lead_time; + __le32 flags; u8 max_kill; - u8 bt3_time_t7_value; + u8 bt_reduced_tx_power; + u8 reserved[2]; + + u8 bt4_antenna_isolation; + u8 bt4_antenna_isolation_thr; + u8 bt4_tx_tx_delta_freq_thr; + u8 bt4_tx_rx_max_freq0; + + __le32 bt_prio_boost[BT_COEX_BOOST_SIZE]; + __le32 wifi_tx_prio_boost; + __le32 wifi_rx_prio_boost; __le32 kill_ack_msk; __le32 kill_cts_msk; - u8 bt3_prio_sample_time; - u8 bt3_timer_t2_value; - __le16 bt4_reaction_time; - __le32 decision_lut[BT_COEX_LUT_SIZE]; - u8 bt_reduced_tx_power; - u8 reserved; - __le16 valid_bit_msk; - __le32 bt_prio_boost; - u8 reserved2; - u8 wifi_tx_prio_boost; - __le16 wifi_rx_prio_boost; + + __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE]; + __le32 bt4_multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE]; + __le32 bt4_corun_lut20[BT_COEX_CORUN_LUT_SIZE]; + __le32 bt4_corun_lut40[BT_COEX_CORUN_LUT_SIZE]; + + __le32 valid_bit_msk; } __packed; /* BT_COEX_CMD_API_S_VER_3 */ +/** + * struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command + * @bt_primary_ci: + * @bt_secondary_ci: + * @co_run_bw_primary: + * @co_run_bw_secondary: + * @primary_ch_phy_id: + * @secondary_ch_phy_id: + * + * Used for BT_COEX_CI command + */ +struct iwl_bt_coex_ci_cmd { + __le64 bt_primary_ci; + __le64 bt_secondary_ci; + + u8 co_run_bw_primary; + u8 co_run_bw_secondary; + u8 primary_ch_phy_id; + u8 secondary_ch_phy_id; +} __packed; /* BT_CI_MSG_API_S_VER_1 */ + #define BT_MBOX(n_dw, _msg, _pos, _nbits) \ BT_MBOX##n_dw##_##_msg##_POS = (_pos), \ BT_MBOX##n_dw##_##_msg = BITS(_nbits) << BT_MBOX##n_dw##_##_msg##_POS @@ -244,23 +285,39 @@ enum iwl_bt_mxbox_dw3 { ((le32_to_cpu((_notif)->mbox_msg[(_num)]) & BT_MBOX##_num##_##_field)\ >> BT_MBOX##_num##_##_field##_POS) +enum iwl_bt_activity_grading { + BT_OFF = 0, + BT_ON_NO_CONNECTION = 1, + BT_LOW_TRAFFIC = 2, + BT_HIGH_TRAFFIC = 3, +}; + /** * struct iwl_bt_coex_profile_notif - notification about BT coex * @mbox_msg: message from BT to WiFi - * @:bt_status: 0 - off, 1 - on - * @:bt_open_conn: number of BT connections open - * @:bt_traffic_load: load of BT traffic - * @:bt_agg_traffic_load: aggregated load of BT traffic - * @:bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant + * @msg_idx: the index of the message + * @bt_status: 0 - off, 1 - on + * @bt_open_conn: number of BT connections open + * @bt_traffic_load: load of BT traffic + * @bt_agg_traffic_load: aggregated load of BT traffic + * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant + * @primary_ch_lut: LUT used for primary channel + * @secondary_ch_lut: LUT used for secondary channel + * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading */ struct iwl_bt_coex_profile_notif { __le32 mbox_msg[4]; + __le32 msg_idx; u8 bt_status; u8 bt_open_conn; u8 bt_traffic_load; u8 bt_agg_traffic_load; u8 bt_ci_compliance; u8 reserved[3]; + + __le32 primary_ch_lut; + __le32 secondary_ch_lut; + __le32 bt_activity_grading; } __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_2 */ enum iwl_bt_coex_prio_table_event { @@ -300,20 +357,4 @@ struct iwl_bt_coex_prio_tbl_cmd { u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX]; } __packed; -enum iwl_bt_coex_env_action { - BT_COEX_ENV_CLOSE = 0, - BT_COEX_ENV_OPEN = 1, -}; /* BT_COEX_PROT_ENV_ACTION_API_E_VER_1 */ - -/** - * struct iwl_bt_coex_prot_env_cmd - BT Protection Envelope - * @action: enum %iwl_bt_coex_env_action - * @type: enum %iwl_bt_coex_prio_table_event - */ -struct iwl_bt_coex_prot_env_cmd { - u8 action; /* 0 = closed, 1 = open */ - u8 type; /* 0 .. 15 */ - u8 reserved[2]; -} __packed; - #endif /* __fw_api_bt_coex_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index df72fcdf81705b54e9a36f51ede8a652f8bcfa2d..4e7dd8cf87dce738cbe062d931dcabe49ce67a51 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h @@ -100,7 +100,12 @@ enum iwl_proto_offloads { #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 -#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 6 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4 +#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12 + +#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4 +#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2 /** * struct iwl_proto_offload_cmd_common - ARP/NS offload common part @@ -155,6 +160,43 @@ struct iwl_proto_offload_cmd_v2 { u8 reserved2[3]; } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ +struct iwl_ns_config { + struct in6_addr source_ipv6_addr; + struct in6_addr dest_ipv6_addr; + u8 target_mac_addr[ETH_ALEN]; + __le16 reserved; +} __packed; /* NS_OFFLOAD_CONFIG */ + +struct iwl_targ_addr { + struct in6_addr addr; + __le32 config_num; +} __packed; /* TARGET_IPV6_ADDRESS */ + +/** + * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @target_ipv6_addr: target IPv6 addresses + * @ns_config: NS offload configurations + */ +struct iwl_proto_offload_cmd_v3_small { + struct iwl_proto_offload_cmd_common common; + __le32 num_valid_ipv6_addrs; + struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S]; + struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ + +/** + * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration + * @common: common/IPv4 configuration + * @target_ipv6_addr: target IPv6 addresses + * @ns_config: NS offload configurations + */ +struct iwl_proto_offload_cmd_v3_large { + struct iwl_proto_offload_cmd_common common; + __le32 num_valid_ipv6_addrs; + struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L]; + struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L]; +} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ /* * WOWLAN_PATTERNS @@ -293,7 +335,7 @@ enum iwl_wowlan_wakeup_reason { IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET = BIT(12), }; /* WOWLAN_WAKE_UP_REASON_API_E_VER_2 */ -struct iwl_wowlan_status { +struct iwl_wowlan_status_v4 { __le64 replay_ctr; __le16 pattern_number; __le16 non_qos_seq_ctr; @@ -308,6 +350,29 @@ struct iwl_wowlan_status { u8 wake_packet[]; /* can be truncated from _length to _bufsize */ } __packed; /* WOWLAN_STATUSES_API_S_VER_4 */ +struct iwl_wowlan_gtk_status { + u8 key_index; + u8 reserved[3]; + u8 decrypt_key[16]; + u8 tkip_mic_key[8]; + struct iwl_wowlan_rsc_tsc_params_cmd rsc; +} __packed; + +struct iwl_wowlan_status_v6 { + struct iwl_wowlan_gtk_status gtk; + __le64 replay_ctr; + __le16 pattern_number; + __le16 non_qos_seq_ctr; + __le16 qos_seq_ctr[8]; + __le32 wakeup_reasons; + __le32 num_of_gtk_rekeys; + __le32 transmitted_ndps; + __le32 received_beacons; + __le32 wake_packet_length; + __le32 wake_packet_bufsize; + u8 wake_packet[]; /* can be truncated from _length to _bufsize */ +} __packed; /* WOWLAN_STATUSES_API_S_VER_6 */ + #define IWL_WOWLAN_TCP_MAX_PACKET_LEN 64 #define IWL_WOWLAN_REMOTE_WAKE_MAX_PACKET_LEN 128 #define IWL_WOWLAN_REMOTE_WAKE_MAX_TOKENS 2048 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h index 98b1feb43d388f70bc4aadf0f4434cc714744d05..39c3148bdfa8eff2a09a7641b317f8a8a1648da9 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-mac.h @@ -170,12 +170,14 @@ struct iwl_mac_data_ap { * @beacon_tsf: beacon transmit time in TSF * @bi: beacon interval in TU * @bi_reciprocal: 2^32 / bi + * @beacon_template: beacon template ID */ struct iwl_mac_data_ibss { __le32 beacon_time; __le64 beacon_tsf; __le32 bi; __le32 bi_reciprocal; + __le32 beacon_template; } __packed; /* IBSS_MAC_DATA_API_S_VER_1 */ /** @@ -372,4 +374,13 @@ static inline u32 iwl_mvm_reciprocal(u32 v) return 0xFFFFFFFF / v; } +#define IWL_NONQOS_SEQ_GET 0x1 +#define IWL_NONQOS_SEQ_SET 0x2 +struct iwl_nonqos_seq_query_cmd { + __le32 get_set_flag; + __le32 mac_id_n_color; + __le16 value; + __le16 reserved; +} __packed; /* NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */ + #endif /* __fw_api_mac_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 8e7ab41079ca6ca51eba975a2e39c92cee5f002b..5cb93ae5cd2f73bfff7a1daf5d381f6e9ccc987b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -131,6 +131,33 @@ struct iwl_powertable_cmd { __le32 lprx_rssi_threshold; } __packed; +/** + * enum iwl_device_power_flags - masks for device power command flags + * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off + * receiver and transmitter. '0' - does not allow. This flag should be + * always set to '1' unless one need to disable actual power down for debug + * purposes. + * @DEVICE_POWER_FLAGS_CAM_MSK: '1' CAM (Continuous Active Mode) is set, meaning + * that power management is disabled. '0' Power management is enabled, one + * of power schemes is applied. +*/ +enum iwl_device_power_flags { + DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), + DEVICE_POWER_FLAGS_CAM_MSK = BIT(13), +}; + +/** + * struct iwl_device_power_cmd - device wide power command. + * DEVICE_POWER_CMD = 0x77 (command, has simple generic response) + * + * @flags: Power table command flags from DEVICE_POWER_FLAGS_* + */ +struct iwl_device_power_cmd { + /* PM_POWER_TABLE_CMD_API_S_VER_6 */ + __le16 flags; + __le16 reserved; +} __packed; + /** * struct iwl_mac_power_cmd - New power command containing uAPSD support * MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) @@ -290,7 +317,7 @@ struct iwl_beacon_filter_cmd { #define IWL_BF_ESCAPE_TIMER_MIN 0 #define IWL_BA_ESCAPE_TIMER_DEFAULT 6 -#define IWL_BA_ESCAPE_TIMER_D3 6 +#define IWL_BA_ESCAPE_TIMER_D3 9 #define IWL_BA_ESCAPE_TIMER_MAX 1024 #define IWL_BA_ESCAPE_TIMER_MIN 0 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h index fdd33bc0a594a2d5456044b1a10df5d621ed8f8e..538f1c7a59664f9cb7c586eeb0974974c971b6f8 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h @@ -68,6 +68,7 @@ /* * These serve as indexes into * struct iwl_rate_info fw_rate_idx_to_plcp[IWL_RATE_COUNT]; + * TODO: avoid overlap between legacy and HT rates */ enum { IWL_RATE_1M_INDEX = 0, @@ -78,18 +79,31 @@ enum { IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, IWL_RATE_6M_INDEX, IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, + IWL_RATE_MCS_0_INDEX = IWL_RATE_6M_INDEX, + IWL_FIRST_HT_RATE = IWL_RATE_MCS_0_INDEX, + IWL_FIRST_VHT_RATE = IWL_RATE_MCS_0_INDEX, IWL_RATE_9M_INDEX, IWL_RATE_12M_INDEX, + IWL_RATE_MCS_1_INDEX = IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, + IWL_RATE_MCS_2_INDEX = IWL_RATE_18M_INDEX, IWL_RATE_24M_INDEX, + IWL_RATE_MCS_3_INDEX = IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, + IWL_RATE_MCS_4_INDEX = IWL_RATE_36M_INDEX, IWL_RATE_48M_INDEX, + IWL_RATE_MCS_5_INDEX = IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX, + IWL_RATE_MCS_6_INDEX = IWL_RATE_54M_INDEX, IWL_LAST_NON_HT_RATE = IWL_RATE_54M_INDEX, IWL_RATE_60M_INDEX, - IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, + IWL_RATE_MCS_7_INDEX = IWL_RATE_60M_INDEX, + IWL_LAST_HT_RATE = IWL_RATE_MCS_7_INDEX, + IWL_RATE_MCS_8_INDEX, + IWL_RATE_MCS_9_INDEX, + IWL_LAST_VHT_RATE = IWL_RATE_MCS_9_INDEX, IWL_RATE_COUNT_LEGACY = IWL_LAST_NON_HT_RATE + 1, - IWL_RATE_COUNT, + IWL_RATE_COUNT = IWL_LAST_VHT_RATE + 1, }; #define IWL_RATE_BIT_MSK(r) BIT(IWL_RATE_##r##M_INDEX) @@ -108,6 +122,7 @@ enum { IWL_RATE_2M_PLCP = 20, IWL_RATE_5M_PLCP = 55, IWL_RATE_11M_PLCP = 110, + IWL_RATE_INVM_PLCP = -1, }; /* @@ -164,6 +179,8 @@ enum { * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.) */ #define RATE_HT_MCS_RATE_CODE_MSK 0x7 +#define RATE_HT_MCS_NSS_POS 3 +#define RATE_HT_MCS_NSS_MSK (3 << RATE_HT_MCS_NSS_POS) /* Bit 10: (1) Use Green Field preamble */ #define RATE_HT_MCS_GF_POS 10 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 83cb9b992ea4622f39ff03a4c5bbcea36a7d128f..c3782b48ded14d8a52458f66677dc3d3cd766fd7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -356,6 +356,7 @@ struct iwl_scan_complete_notif { /* scan offload */ #define IWL_MAX_SCAN_CHANNELS 40 #define IWL_SCAN_MAX_BLACKLIST_LEN 64 +#define IWL_SCAN_SHORT_BLACKLIST_LEN 16 #define IWL_SCAN_MAX_PROFILES 11 #define SCAN_OFFLOAD_PROBE_REQ_SIZE 512 @@ -368,6 +369,12 @@ struct iwl_scan_complete_notif { #define IWL_FULL_SCAN_MULTIPLIER 5 #define IWL_FAST_SCHED_SCAN_ITERATIONS 3 +enum scan_framework_client { + SCAN_CLIENT_SCHED_SCAN = BIT(0), + SCAN_CLIENT_NETDETECT = BIT(1), + SCAN_CLIENT_ASSET_TRACKING = BIT(2), +}; + /** * struct iwl_scan_offload_cmd - SCAN_REQUEST_FIXED_PART_API_S_VER_6 * @scan_flags: see enum iwl_scan_flags @@ -449,11 +456,12 @@ struct iwl_scan_offload_cfg { * iwl_scan_offload_blacklist - SCAN_OFFLOAD_BLACKLIST_S * @ssid: MAC address to filter out * @reported_rssi: AP rssi reported to the host + * @client_bitmap: clients ignore this entry - enum scan_framework_client */ struct iwl_scan_offload_blacklist { u8 ssid[ETH_ALEN]; u8 reported_rssi; - u8 reserved; + u8 client_bitmap; } __packed; enum iwl_scan_offload_network_type { @@ -475,6 +483,7 @@ enum iwl_scan_offload_band_selection { * @aut_alg: authentication olgorithm to match - bitmap * @network_type: enum iwl_scan_offload_network_type * @band_selection: enum iwl_scan_offload_band_selection + * @client_bitmap: clients waiting for match - enum scan_framework_client */ struct iwl_scan_offload_profile { u8 ssid_index; @@ -482,7 +491,8 @@ struct iwl_scan_offload_profile { u8 auth_alg; u8 network_type; u8 band_selection; - u8 reserved[3]; + u8 client_bitmap; + u8 reserved[2]; } __packed; /** @@ -491,13 +501,18 @@ struct iwl_scan_offload_profile { * @profiles: profiles to search for match * @blacklist_len: length of blacklist * @num_profiles: num of profiles in the list + * @match_notify: clients waiting for match found notification + * @pass_match: clients waiting for the results + * @active_clients: active clients bitmap - enum scan_framework_client */ struct iwl_scan_offload_profile_cfg { - struct iwl_scan_offload_blacklist blacklist[IWL_SCAN_MAX_BLACKLIST_LEN]; struct iwl_scan_offload_profile profiles[IWL_SCAN_MAX_PROFILES]; u8 blacklist_len; u8 num_profiles; - u8 reserved[2]; + u8 match_notify; + u8 pass_match; + u8 active_clients; + u8 reserved[3]; } __packed; /** @@ -560,4 +575,15 @@ struct iwl_scan_offload_complete { u8 reserved; } __packed; +/** + * iwl_sched_scan_results - SCAN_OFFLOAD_MATCH_FOUND_NTF_API_S_VER_1 + * @ssid_bitmap: SSIDs indexes found in this iteration + * @client_bitmap: clients that are active and wait for this notification + */ +struct iwl_sched_scan_results { + __le16 ssid_bitmap; + u8 client_bitmap; + u8 reserved; +}; + #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index a30691a8a85b03a0b76815bf627480242a2458af..4aca5933a65d1619fc3e2e419ced4ddddc3d44c1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h @@ -247,7 +247,7 @@ struct iwl_mvm_keyinfo { } __packed; /** - * struct iwl_mvm_add_sta_cmd - Add / modify a station in the fw's station table + * struct iwl_mvm_add_sta_cmd_v5 - Add/modify a station in the fw's sta table. * ( REPLY_ADD_STA = 0x18 ) * @add_modify: 1: modify existing, 0: add new station * @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent @@ -286,7 +286,7 @@ struct iwl_mvm_keyinfo { * ADD_STA sets up the table entry for one station, either creating a new * entry, or modifying a pre-existing one. */ -struct iwl_mvm_add_sta_cmd { +struct iwl_mvm_add_sta_cmd_v5 { u8 add_modify; u8 unicast_tx_key_id; u8 multicast_tx_key_id; @@ -312,6 +312,57 @@ struct iwl_mvm_add_sta_cmd { __le32 tfd_queue_msk; } __packed; /* ADD_STA_CMD_API_S_VER_5 */ +/** + * struct iwl_mvm_add_sta_cmd_v6 - Add / modify a station + * VER_6 of this command is quite similar to VER_5 except + * exclusion of all fields related to the security key installation. + */ +struct iwl_mvm_add_sta_cmd_v6 { + u8 add_modify; + u8 reserved1; + __le16 tid_disable_tx; + __le32 mac_id_n_color; + u8 addr[ETH_ALEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ + __le16 reserved2; + u8 sta_id; + u8 modify_mask; + __le16 reserved3; + __le32 station_flags; + __le32 station_flags_msk; + u8 add_immediate_ba_tid; + u8 remove_immediate_ba_tid; + __le16 add_immediate_ba_ssn; + __le16 sleep_tx_count; + __le16 sleep_state_flags; + __le16 assoc_id; + __le16 beamform_flags; + __le32 tfd_queue_msk; +} __packed; /* ADD_STA_CMD_API_S_VER_6 */ + +/** + * struct iwl_mvm_add_sta_key_cmd - add/modify sta key + * ( REPLY_ADD_STA_KEY = 0x17 ) + * @sta_id: index of station in uCode's station table + * @key_offset: key offset in key storage + * @key_flags: type %iwl_sta_key_flag + * @key: key material data + * @key2: key material data + * @rx_secur_seq_cnt: RX security sequence counter for the key + * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection + * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx + */ +struct iwl_mvm_add_sta_key_cmd { + u8 sta_id; + u8 key_offset; + __le16 key_flags; + u8 key[16]; + u8 key2[16]; + u8 rx_secur_seq_cnt[16]; + u8 tkip_rx_tsc_byte2; + u8 reserved; + __le16 tkip_rx_ttak[5]; +} __packed; /* ADD_MODIFY_STA_KEY_API_S_VER_1 */ + /** * enum iwl_mvm_add_sta_rsp_status - status in the response to ADD_STA command * @ADD_STA_SUCCESS: operation was executed successfully diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 66264cc5a0168d296ddac86be6617cd56f1d84d5..bad5a552dd8d10412f5d9d874026215d6c6e9359 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -72,17 +72,17 @@ #include "fw-api-d3.h" #include "fw-api-bt-coex.h" -/* queue and FIFO numbers by usage */ +/* maximal number of Tx queues in any platform */ +#define IWL_MVM_MAX_QUEUES 20 + +/* Tx queue numbers */ enum { IWL_MVM_OFFCHANNEL_QUEUE = 8, IWL_MVM_CMD_QUEUE = 9, - IWL_MVM_AUX_QUEUE = 15, - IWL_MVM_FIRST_AGG_QUEUE = 16, - IWL_MVM_NUM_QUEUES = 20, - IWL_MVM_LAST_AGG_QUEUE = IWL_MVM_NUM_QUEUES - 1, - IWL_MVM_CMD_FIFO = 7 }; +#define IWL_MVM_CMD_FIFO 7 + #define IWL_MVM_STATION_COUNT 16 /* commands */ @@ -97,6 +97,7 @@ enum { DBG_CFG = 0x9, /* station table */ + ADD_STA_KEY = 0x17, ADD_STA = 0x18, REMOVE_STA = 0x19, @@ -114,6 +115,7 @@ enum { TIME_EVENT_NOTIFICATION = 0x2a, BINDING_CONTEXT_CMD = 0x2b, TIME_QUOTA_CMD = 0x2c, + NON_QOS_TX_COUNTER_CMD = 0x2d, LQ_CMD = 0x4e, @@ -130,6 +132,7 @@ enum { SCAN_OFFLOAD_COMPLETE = 0x6D, SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, SCAN_OFFLOAD_CONFIG_CMD = 0x6f, + MATCH_FOUND_NOTIFICATION = 0xd9, /* Phy */ PHY_CONFIGURATION_CMD = 0x6a, @@ -178,6 +181,7 @@ enum { BT_COEX_PRIO_TABLE = 0xcc, BT_COEX_PROT_ENV = 0xcd, BT_PROFILE_NOTIFICATION = 0xce, + BT_COEX_CI = 0x5d, REPLY_BEACON_FILTERING_CMD = 0xd2, diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index c76299a3a1e0821708a91885ffab780b624fdbc1..70e5297646b29b9d2410699b53b5daa7ed3fab3a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -151,13 +151,11 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, enum iwl_ucode_type old_type = mvm->cur_ucode; static const u8 alive_cmd[] = { MVM_ALIVE }; - mvm->cur_ucode = ucode_type; fw = iwl_get_ucode_image(mvm, ucode_type); - - mvm->ucode_loaded = false; - - if (!fw) + if (WARN_ON(!fw)) return -EINVAL; + mvm->cur_ucode = ucode_type; + mvm->ucode_loaded = false; iwl_init_notification_wait(&mvm->notif_wait, &alive_wait, alive_cmd, ARRAY_SIZE(alive_cmd), @@ -199,7 +197,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, */ for (i = 0; i < IWL_MAX_HW_QUEUES; i++) { - if (i < IWL_MVM_FIRST_AGG_QUEUE && i != IWL_MVM_CMD_QUEUE) + if (i < mvm->first_agg_queue && i != IWL_MVM_CMD_QUEUE) mvm->queue_to_mac80211[i] = i; else mvm->queue_to_mac80211[i] = IWL_INVALID_MAC80211_QUEUE; @@ -243,7 +241,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) lockdep_assert_held(&mvm->mutex); - if (mvm->init_ucode_run) + if (mvm->init_ucode_complete) return 0; iwl_init_notification_wait(&mvm->notif_wait, @@ -264,6 +262,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) if (ret) goto error; + /* Read the NVM only at driver load time, no need to do this twice */ if (read_nvm) { /* Read nvm */ ret = iwl_nvm_init(mvm); @@ -273,6 +272,10 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) } } + /* In case we read the NVM from external file, load it to the NIC */ + if (iwlwifi_mod_params.nvm_file) + iwl_mvm_load_nvm_to_nic(mvm); + ret = iwl_nvm_check_version(mvm->nvm_data, mvm->trans); WARN_ON(ret); @@ -310,7 +313,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_wait_notification(&mvm->notif_wait, &calib_wait, MVM_UCODE_CALIB_TIMEOUT); if (!ret) - mvm->init_ucode_run = true; + mvm->init_ucode_complete = true; goto out; error: @@ -353,8 +356,12 @@ int iwl_mvm_up(struct iwl_mvm *mvm) if (ret) return ret; - /* If we were in RFKILL during module loading, load init ucode now */ - if (!mvm->init_ucode_run) { + /* + * If we haven't completed the run of the init ucode during + * module loading, load init ucode now + * (for example, if we were in RFKILL) + */ + if (!mvm->init_ucode_complete) { ret = iwl_run_init_mvm_ucode(mvm, false); if (ret && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", ret); @@ -424,6 +431,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) goto error; } + ret = iwl_mvm_power_update_device_mode(mvm); + if (ret) + goto error; + IWL_DEBUG_INFO(mvm, "RT uCode started.\n"); return 0; error: diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index 5fe23a5ea9b66b662cd746c113b644b1318517e2..f41f9b079831cfe7722b4d0c23203640993e7249 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -80,7 +80,7 @@ struct iwl_mvm_mac_iface_iterator_data { struct ieee80211_vif *vif; unsigned long available_mac_ids[BITS_TO_LONGS(NUM_MAC_INDEX_DRIVER)]; unsigned long available_tsf_ids[BITS_TO_LONGS(NUM_TSF_IDS)]; - unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_FIRST_AGG_QUEUE)]; + unsigned long used_hw_queues[BITS_TO_LONGS(IWL_MVM_MAX_QUEUES)]; enum iwl_tsf_id preferred_tsf; bool found_vif; }; @@ -218,7 +218,7 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, .preferred_tsf = NUM_TSF_IDS, .used_hw_queues = { BIT(IWL_MVM_OFFCHANNEL_QUEUE) | - BIT(IWL_MVM_AUX_QUEUE) | + BIT(mvm->aux_queue) | BIT(IWL_MVM_CMD_QUEUE) }, .found_vif = false, @@ -242,9 +242,17 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, * that we should share it with another interface. */ - /* Currently, MAC ID 0 should be used only for the managed vif */ - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + /* Currently, MAC ID 0 should be used only for the managed/IBSS vif */ + switch (vif->type) { + case NL80211_IFTYPE_ADHOC: + break; + case NL80211_IFTYPE_STATION: + if (!vif->p2p) + break; + /* fall through */ + default: __clear_bit(0, data.available_mac_ids); + } ieee80211_iterate_active_interfaces_atomic( mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL, @@ -302,9 +310,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, /* Find available queues, and allocate them to the ACs */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { u8 queue = find_first_zero_bit(data.used_hw_queues, - IWL_MVM_FIRST_AGG_QUEUE); + mvm->first_agg_queue); - if (queue >= IWL_MVM_FIRST_AGG_QUEUE) { + if (queue >= mvm->first_agg_queue) { IWL_ERR(mvm, "Failed to allocate queue\n"); ret = -EIO; goto exit_fail; @@ -317,9 +325,9 @@ static int iwl_mvm_mac_ctxt_allocate_resources(struct iwl_mvm *mvm, /* Allocate the CAB queue for softAP and GO interfaces */ if (vif->type == NL80211_IFTYPE_AP) { u8 queue = find_first_zero_bit(data.used_hw_queues, - IWL_MVM_FIRST_AGG_QUEUE); + mvm->first_agg_queue); - if (queue >= IWL_MVM_FIRST_AGG_QUEUE) { + if (queue >= mvm->first_agg_queue) { IWL_ERR(mvm, "Failed to allocate cab queue\n"); ret = -EIO; goto exit_fail; @@ -559,8 +567,12 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm, cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); /* Don't use cts to self as the fw doesn't support it currently. */ - if (vif->bss_conf.use_cts_prot) + if (vif->bss_conf.use_cts_prot) { cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT); + if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8) + cmd->protection_flags |= + cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN); + } /* * I think that we should enable these 2 flags regardless the HT PROT @@ -707,8 +719,35 @@ static int iwl_mvm_mac_ctxt_cmd_listener(struct iwl_mvm *mvm, cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_PROMISC | MAC_FILTER_IN_CONTROL_AND_MGMT | MAC_FILTER_IN_BEACON | + MAC_FILTER_IN_PROBE_REQUEST | + MAC_FILTER_IN_CRC32); + mvm->hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS; + + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); +} + +static int iwl_mvm_mac_ctxt_cmd_ibss(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mac_ctx_cmd cmd = {}; + + WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); + + iwl_mvm_mac_ctxt_cmd_common(mvm, vif, &cmd, action); + + cmd.filter_flags = cpu_to_le32(MAC_FILTER_IN_BEACON | MAC_FILTER_IN_PROBE_REQUEST); + /* cmd.ibss.beacon_time/cmd.ibss.beacon_tsf are curently ignored */ + cmd.ibss.bi = cpu_to_le32(vif->bss_conf.beacon_int); + cmd.ibss.bi_reciprocal = + cpu_to_le32(iwl_mvm_reciprocal(vif->bss_conf.beacon_int)); + + /* TODO: Assumes that the beacon id == mac context id */ + cmd.ibss.beacon_template = cpu_to_le32(mvmvif->id); + return iwl_mvm_mac_ctxt_send_cmd(mvm, &cmd); } @@ -721,7 +760,8 @@ static void iwl_mvm_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) struct iwl_mvm_go_iterator_data *data = _data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - if (vif->type == NL80211_IFTYPE_AP && vif->p2p && mvmvif->ap_active) + if (vif->type == NL80211_IFTYPE_AP && vif->p2p && + mvmvif->ap_ibss_active) data->go_active = true; } @@ -833,9 +873,10 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); /* Set up TX beacon command fields */ - iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd, - beacon->data, - beacon_skb_len); + if (vif->type == NL80211_IFTYPE_AP) + iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd, + beacon->data, + beacon_skb_len); /* Submit command */ cmd.len[0] = sizeof(beacon_cmd); @@ -848,14 +889,15 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, return iwl_mvm_send_cmd(mvm, &cmd); } -/* The beacon template for the AP/GO context has changed and needs update */ +/* The beacon template for the AP/GO/IBSS has changed and needs update */ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct sk_buff *beacon; int ret; - WARN_ON(vif->type != NL80211_IFTYPE_AP); + WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC); beacon = ieee80211_beacon_get(mvm->hw, vif); if (!beacon) @@ -1018,6 +1060,8 @@ static int iwl_mvm_mac_ctx_send(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return iwl_mvm_mac_ctxt_cmd_listener(mvm, vif, action); case NL80211_IFTYPE_P2P_DEVICE: return iwl_mvm_mac_ctxt_cmd_p2p_device(mvm, vif, action); + case NL80211_IFTYPE_ADHOC: + return iwl_mvm_mac_ctxt_cmd_ibss(mvm, vif, action); default: break; } @@ -1038,6 +1082,9 @@ int iwl_mvm_mac_ctxt_add(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (ret) return ret; + /* will only do anything at resume from D3 time */ + iwl_mvm_set_last_nonqos_seq(mvm, vif); + mvmvif->uploaded = true; return 0; } @@ -1077,6 +1124,10 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif) } mvmvif->uploaded = false; + + if (vif->type == NL80211_IFTYPE_MONITOR) + mvm->hw->flags &= ~IEEE80211_HW_RX_INCLUDES_FCS; + return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index 9833cdf6177cd34d199de063cb0ff466042cfed0..74bc2c8af06d62fb360de4357621dca50201a8c2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -77,6 +77,7 @@ #include "iwl-eeprom-parse.h" #include "fw-api-scan.h" #include "iwl-phy-db.h" +#include "testmode.h" static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { @@ -138,6 +139,14 @@ static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm) } } +static int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm) +{ + /* we create the 802.11 header and SSID element */ + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID) + return mvm->fw->ucode_capa.max_probe_length - 24 - 2; + return mvm->fw->ucode_capa.max_probe_length - 24 - 34; +} + int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) { struct ieee80211_hw *hw = mvm->hw; @@ -155,10 +164,9 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) IEEE80211_HW_TIMING_BEACON_ONLY | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | - IEEE80211_HW_SUPPORTS_STATIC_SMPS | - IEEE80211_HW_SUPPORTS_UAPSD; + IEEE80211_HW_SUPPORTS_STATIC_SMPS; - hw->queues = IWL_MVM_FIRST_AGG_QUEUE; + hw->queues = mvm->first_agg_queue; hw->offchannel_tx_hw_queue = IWL_MVM_OFFCHANNEL_QUEUE; hw->rate_control_algorithm = "iwl-mvm-rs"; @@ -171,6 +179,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) !iwlwifi_mod_params.sw_crypto) hw->flags |= IEEE80211_HW_MFP_CAPABLE; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT) { + hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD; + hw->uapsd_queues = IWL_UAPSD_AC_INFO; + hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; + } + hw->sta_data_size = sizeof(struct iwl_mvm_sta); hw->vif_data_size = sizeof(struct iwl_mvm_vif); hw->chanctx_data_size = sizeof(u16); @@ -181,6 +195,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); + /* IBSS has bugs in older versions */ + if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 8) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | WIPHY_FLAG_DISABLE_BEACON_HINTS | WIPHY_FLAG_IBSS_RSN; @@ -191,8 +209,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_remain_on_channel_duration = 10000; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - hw->uapsd_queues = IWL_UAPSD_AC_INFO; - hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; /* Extract MAC address */ memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); @@ -212,9 +228,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) iwl_mvm_reset_phy_ctxts(mvm); - /* we create the 802.11 header and a max-length SSID element */ - hw->wiphy->max_scan_ie_len = - mvm->fw->ucode_capa.max_probe_length - 24 - 34; + hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm); + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels) @@ -231,6 +246,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) else hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SCHED_SCAN) { + hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; + hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES; + /* we create the 802.11 header and zero length SSID IE. */ + hw->wiphy->max_sched_scan_ie_len = + SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2; + } + hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN | NL80211_FEATURE_P2P_GO_OPPPS; @@ -548,7 +572,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, * In short: there's not much we can do at this point, other than * allocating resources :) */ - if (vif->type == NL80211_IFTYPE_AP) { + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { u32 qmask = iwl_mvm_mac_get_queues_mask(mvm, vif); ret = iwl_mvm_allocate_int_sta(mvm, &mvmvif->bcast_sta, qmask); @@ -698,7 +723,14 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, * For AP/GO interface, the tear down of the resources allocated to the * interface is be handled as part of the stop_ap flow. */ - if (vif->type == NL80211_IFTYPE_AP) { + if (vif->type == NL80211_IFTYPE_AP || + vif->type == NL80211_IFTYPE_ADHOC) { +#ifdef CONFIG_NL80211_TESTMODE + if (vif == mvm->noa_vif) { + mvm->noa_vif = NULL; + mvm->noa_duration = 0; + } +#endif iwl_mvm_dealloc_int_sta(mvm, &mvmvif->bcast_sta); goto out_release; } @@ -796,6 +828,27 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, return; } iwl_mvm_configure_mcast_filter(mvm, vif); + + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)) { + /* + * If we're restarting then the firmware will + * obviously have lost synchronisation with + * the AP. It will attempt to synchronise by + * itself, but we can make it more reliable by + * scheduling a session protection time event. + * + * The firmware needs to receive a beacon to + * catch up with synchronisation, use 110% of + * the beacon interval. + * + * Set a large maximum delay to allow for more + * than a single interface. + */ + u32 dur = (11 * vif->bss_conf.beacon_int) / 10; + iwl_mvm_protect_session(mvm, vif, dur, dur, + 5 * dur); + } } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { /* remove AP station now that the MAC is unassoc */ ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); @@ -811,7 +864,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, /* reset rssi values */ mvmvif->bf_data.ave_beacon_signal = 0; - if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD)) { + if (!(mvm->fw->ucode_capa.flags & + IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) { /* Workaround for FW bug, otherwise FW disables device * power save upon disassociation */ @@ -819,7 +873,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, if (ret) IWL_ERR(mvm, "failed to update power mode\n"); } - iwl_mvm_bt_coex_vif_assoc(mvm, vif); + iwl_mvm_bt_coex_vif_change(mvm); } else if (changes & BSS_CHANGED_BEACON_INFO) { /* * We received a beacon _after_ association so @@ -848,7 +902,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, } } -static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -871,7 +926,7 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) if (ret) goto out_remove; - mvmvif->ap_active = true; + mvmvif->ap_ibss_active = true; /* Send the bcast station. At this stage the TBTT and DTIM time events * are added and applied to the scheduler */ @@ -883,10 +938,12 @@ static int iwl_mvm_start_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) if (ret) goto out_rm_bcast; - /* Need to update the P2P Device MAC */ + /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); + iwl_mvm_bt_coex_vif_change(mvm); + mutex_unlock(&mvm->mutex); return 0; @@ -901,7 +958,8 @@ out_unlock: return ret; } -static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); @@ -910,9 +968,11 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mutex_lock(&mvm->mutex); - mvmvif->ap_active = false; + mvmvif->ap_ibss_active = false; + + iwl_mvm_bt_coex_vif_change(mvm); - /* Need to update the P2P Device MAC */ + /* Need to update the P2P Device MAC (only GO, IBSS is single vif) */ if (vif->p2p && mvm->p2p_device_vif) iwl_mvm_mac_ctxt_changed(mvm, mvm->p2p_device_vif); @@ -924,10 +984,11 @@ static void iwl_mvm_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mutex_unlock(&mvm->mutex); } -static void iwl_mvm_bss_info_changed_ap(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) +static void +iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) { /* Need to send a new beacon template to the FW */ if (changes & BSS_CHANGED_BEACON) { @@ -950,7 +1011,8 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw, iwl_mvm_bss_info_changed_station(mvm, vif, bss_conf, changes); break; case NL80211_IFTYPE_AP: - iwl_mvm_bss_info_changed_ap(mvm, vif, bss_conf, changes); + case NL80211_IFTYPE_ADHOC: + iwl_mvm_bss_info_changed_ap_ibss(mvm, vif, bss_conf, changes); break; default: /* shouldn't happen */ @@ -1163,7 +1225,54 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw, mutex_lock(&mvm->mutex); /* Try really hard to protect the session and hear a beacon */ - iwl_mvm_protect_session(mvm, vif, duration, min_duration); + iwl_mvm_protect_session(mvm, vif, duration, min_duration, 500); + mutex_unlock(&mvm->mutex); +} + +static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int ret; + + mutex_lock(&mvm->mutex); + + if (mvm->scan_status != IWL_MVM_SCAN_NONE) { + IWL_DEBUG_SCAN(mvm, + "SCHED SCAN request during internal scan - abort\n"); + ret = -EBUSY; + goto out; + } + + mvm->scan_status = IWL_MVM_SCAN_SCHED; + + ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies); + if (ret) + goto err; + + ret = iwl_mvm_config_sched_scan_profiles(mvm, req); + if (ret) + goto err; + + ret = iwl_mvm_sched_scan_start(mvm, req); + if (!ret) + goto out; +err: + mvm->scan_status = IWL_MVM_SCAN_NONE; +out: + mutex_unlock(&mvm->mutex); + return ret; +} + +static void iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + mutex_lock(&mvm->mutex); + iwl_mvm_sched_scan_stop(mvm); mutex_unlock(&mvm->mutex); } @@ -1207,8 +1316,13 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, switch (cmd) { case SET_KEY: - if (vif->type == NL80211_IFTYPE_AP && !sta) { - /* GTK on AP interface is a TX-only key, return 0 */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_AP) && !sta) { + /* + * GTK on AP interface is a TX-only key, return 0; + * on IBSS they're per-station and because we're lazy + * we don't support them for RX, so do the same. + */ ret = 0; key->hw_key_idx = STA_KEY_IDX_INVALID; break; @@ -1252,6 +1366,9 @@ static void iwl_mvm_mac_update_tkip_key(struct ieee80211_hw *hw, { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + if (keyconf->hw_key_idx == STA_KEY_IDX_INVALID) + return; + iwl_mvm_update_tkip_key(mvm, vif, keyconf, sta, iv32, phase1key); } @@ -1445,6 +1562,7 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, ctx->rx_chains_static, ctx->rx_chains_dynamic); + iwl_mvm_bt_coex_vif_change(mvm); mutex_unlock(&mvm->mutex); } @@ -1464,14 +1582,14 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, switch (vif->type) { case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: /* * The AP binding flow is handled as part of the start_ap flow - * (in bss_info_changed). + * (in bss_info_changed), similarly for IBSS. */ ret = 0; goto out_unlock; case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_MONITOR: break; default: @@ -1517,10 +1635,10 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); - if (vif->type == NL80211_IFTYPE_AP) - goto out_unlock; - switch (vif->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + goto out_unlock; case NL80211_IFTYPE_MONITOR: mvmvif->monitor_active = false; iwl_mvm_update_quotas(mvm, NULL); @@ -1550,14 +1668,72 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw, return iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm_sta->vif); } -static void iwl_mvm_mac_rssi_callback(struct ieee80211_hw *hw, +#ifdef CONFIG_NL80211_TESTMODE +static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = { + [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 }, + [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 }, + [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 }, +}; + +static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - enum ieee80211_rssi_event rssi_event) + void *data, int len) +{ + struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1]; + int err; + u32 noa_duration; + + err = nla_parse(tb, IWL_MVM_TM_ATTR_MAX, data, len, iwl_mvm_tm_policy); + if (err) + return err; + + if (!tb[IWL_MVM_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) { + case IWL_MVM_TM_CMD_SET_NOA: + if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p || + !vif->bss_conf.enable_beacon || + !tb[IWL_MVM_TM_ATTR_NOA_DURATION]) + return -EINVAL; + + noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]); + if (noa_duration >= vif->bss_conf.beacon_int) + return -EINVAL; + + mvm->noa_duration = noa_duration; + mvm->noa_vif = vif; + + return iwl_mvm_update_quotas(mvm, NULL); + case IWL_MVM_TM_CMD_SET_BEACON_FILTER: + /* must be associated client vif - ignore authorized */ + if (!vif || vif->type != NL80211_IFTYPE_STATION || + !vif->bss_conf.assoc || !vif->bss_conf.dtim_period || + !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]) + return -EINVAL; + + if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])) + return iwl_mvm_enable_beacon_filter(mvm, vif); + return iwl_mvm_disable_beacon_filter(mvm, vif); + } + + return -EOPNOTSUPP; +} + +static int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + int err; - iwl_mvm_bt_rssi_event(mvm, vif, rssi_event); + mutex_lock(&mvm->mutex); + err = __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len); + mutex_unlock(&mvm->mutex); + + return err; } +#endif struct ieee80211_ops iwl_mvm_hw_ops = { .tx = iwl_mvm_mac_tx, @@ -1578,23 +1754,27 @@ struct ieee80211_ops iwl_mvm_hw_ops = { .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, .conf_tx = iwl_mvm_mac_conf_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, + .sched_scan_start = iwl_mvm_mac_sched_scan_start, + .sched_scan_stop = iwl_mvm_mac_sched_scan_stop, .set_key = iwl_mvm_mac_set_key, .update_tkip_key = iwl_mvm_mac_update_tkip_key, .remain_on_channel = iwl_mvm_roc, .cancel_remain_on_channel = iwl_mvm_cancel_roc, - .rssi_callback = iwl_mvm_mac_rssi_callback, - .add_chanctx = iwl_mvm_add_chanctx, .remove_chanctx = iwl_mvm_remove_chanctx, .change_chanctx = iwl_mvm_change_chanctx, .assign_vif_chanctx = iwl_mvm_assign_vif_chanctx, .unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx, - .start_ap = iwl_mvm_start_ap, - .stop_ap = iwl_mvm_stop_ap, + .start_ap = iwl_mvm_start_ap_ibss, + .stop_ap = iwl_mvm_stop_ap_ibss, + .join_ibss = iwl_mvm_start_ap_ibss, + .leave_ibss = iwl_mvm_stop_ap_ibss, .set_tim = iwl_mvm_set_tim, + CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) + #ifdef CONFIG_PM_SLEEP /* look at d3.c */ .suspend = iwl_mvm_suspend, diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index b0389279cc1ed774f9f90f20137d11b6691d8eae..fed21ef4162d231102ab7ae76e3229ac24a86f1f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -73,7 +73,6 @@ #include "iwl-trans.h" #include "iwl-notif-wait.h" #include "iwl-eeprom-parse.h" -#include "iwl-trans.h" #include "sta.h" #include "fw-api.h" #include "constants.h" @@ -162,6 +161,7 @@ enum iwl_power_scheme { struct iwl_mvm_power_ops { int (*power_update_mode)(struct iwl_mvm *mvm, struct ieee80211_vif *vif); + int (*power_update_device_mode)(struct iwl_mvm *mvm); int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif); #ifdef CONFIG_IWLWIFI_DEBUGFS int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -241,12 +241,18 @@ enum iwl_mvm_smps_type_request { * @last_beacon_signal: last beacon rssi signal in dbm * @ave_beacon_signal: average beacon signal * @last_cqm_event: rssi of the last cqm event +* @bt_coex_min_thold: minimum threshold for BT coex +* @bt_coex_max_thold: maximum threshold for BT coex +* @last_bt_coex_event: rssi of the last BT coex event */ struct iwl_mvm_vif_bf_data { bool bf_enabled; bool ba_enabled; s8 ave_beacon_signal; s8 last_cqm_event; + s8 bt_coex_min_thold; + s8 bt_coex_max_thold; + s8 last_bt_coex_event; }; /** @@ -255,8 +261,8 @@ struct iwl_mvm_vif_bf_data { * @color: to solve races upon MAC addition and removal * @ap_sta_id: the sta_id of the AP - valid only if VIF type is STA * @uploaded: indicates the MAC context has been added to the device - * @ap_active: indicates that ap context is configured, and that the interface - * should get quota etc. + * @ap_ibss_active: indicates that AP/IBSS is configured and that the interface + * should get quota etc. * @monitor_active: indicates that monitor context is configured, and that the * interface should get quota etc. * @queue_params: QoS params for this MAC @@ -272,7 +278,7 @@ struct iwl_mvm_vif { u8 ap_sta_id; bool uploaded; - bool ap_active; + bool ap_ibss_active; bool monitor_active; struct iwl_mvm_vif_bf_data bf_data; @@ -306,6 +312,9 @@ struct iwl_mvm_vif { int tx_key_idx; + bool seqno_valid; + u16 seqno; + #if IS_ENABLED(CONFIG_IPV6) /* IPv6 addresses for WoWLAN */ struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; @@ -333,6 +342,7 @@ iwl_mvm_vif_from_mac80211(struct ieee80211_vif *vif) enum iwl_scan_status { IWL_MVM_SCAN_NONE, IWL_MVM_SCAN_OS, + IWL_MVM_SCAN_SCHED, }; /** @@ -434,7 +444,7 @@ struct iwl_mvm { enum iwl_ucode_type cur_ucode; bool ucode_loaded; - bool init_ucode_run; + bool init_ucode_complete; u32 error_event_table; u32 log_event_table; @@ -470,6 +480,9 @@ struct iwl_mvm { enum iwl_scan_status scan_status; struct iwl_scan_cmd *scan_cmd; + /* rx chain antennas set through debugfs for the scan command */ + u8 scan_rx_ant; + /* Internal station */ struct iwl_mvm_int_sta aux_sta; @@ -479,7 +492,8 @@ struct iwl_mvm { #ifdef CONFIG_IWLWIFI_DEBUGFS struct dentry *debugfs_dir; u32 dbgfs_sram_offset, dbgfs_sram_len; - bool prevent_power_down_d3; + bool disable_power_off; + bool disable_power_off_d3; #endif struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; @@ -523,12 +537,23 @@ struct iwl_mvm { /* BT-Coex */ u8 bt_kill_msk; struct iwl_bt_coex_profile_notif last_bt_notif; + struct iwl_bt_coex_ci_cmd last_bt_ci_cmd; /* Thermal Throttling and CTkill */ struct iwl_mvm_tt_mgmt thermal_throttle; s32 temperature; /* Celsius */ const struct iwl_mvm_power_ops *pm_ops; + +#ifdef CONFIG_NL80211_TESTMODE + u32 noa_duration; + struct ieee80211_vif *noa_vif; +#endif + + /* Tx queues */ + u8 aux_queue; + u8 first_agg_queue; + u8 last_agg_queue; }; /* Extract MVM priv from op_mode and _hw */ @@ -570,6 +595,9 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm); /* Utils */ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); +void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, + enum ieee80211_band band, + struct ieee80211_tx_rate *r); u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx); void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm); void iwl_mvm_dump_sram(struct iwl_mvm *mvm); @@ -608,6 +636,7 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, /* NVM */ int iwl_nvm_init(struct iwl_mvm *mvm); +int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); int iwl_mvm_up(struct iwl_mvm *mvm); int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm); @@ -682,6 +711,23 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); void iwl_mvm_cancel_scan(struct iwl_mvm *mvm); +/* Scheduled scan */ +int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); +int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); +int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req); +int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req); +void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm); +int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + /* MVM debugfs */ #ifdef CONFIG_IWLWIFI_DEBUGFS int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir); @@ -720,6 +766,13 @@ static inline int iwl_mvm_power_disable(struct iwl_mvm *mvm, return mvm->pm_ops->power_disable(mvm, vif); } +static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm) +{ + if (mvm->pm_ops->power_update_device_mode) + return mvm->pm_ops->power_update_device_mode(mvm); + return 0; +} + #ifdef CONFIG_IWLWIFI_DEBUGFS static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -745,6 +798,15 @@ void iwl_mvm_ipv6_addr_change(struct ieee80211_hw *hw, void iwl_mvm_set_default_unicast_key(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int idx); extern const struct file_operations iwl_dbgfs_d3_test_ops; +#ifdef CONFIG_PM_SLEEP +void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, + struct ieee80211_vif *vif); +#else +static inline void +iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ +} +#endif /* BT Coex */ int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm); @@ -754,7 +816,20 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, struct iwl_device_cmd *cmd); void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event); -void iwl_mvm_bt_coex_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm); +u16 iwl_mvm_bt_coex_agg_time_limit(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); +bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm, + struct ieee80211_sta *sta); + +enum iwl_bt_kill_msk { + BT_KILL_MSK_DEFAULT, + BT_KILL_MSK_SCO_HID_A2DP, + BT_KILL_MSK_REDUCED_TXPOW, + BT_KILL_MSK_MAX, +}; +extern const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX]; +extern const u32 iwl_bt_cts_kill_msk[BT_KILL_MSK_MAX]; /* beacon filtering */ #ifdef CONFIG_IWLWIFI_DEBUGFS diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index edb94ea316545f439a18e8c16e0952066336e2a2..2beffd028b67cef7469faa40899b6b6992501976 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -77,7 +77,7 @@ static const int nvm_to_read[] = { /* Default NVM size to read */ #define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWL_MAX_NVM_SECTION_SIZE 6000 +#define IWL_MAX_NVM_SECTION_SIZE 7000 #define NVM_WRITE_OPCODE 1 #define NVM_READ_OPCODE 0 @@ -259,6 +259,8 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) #define MAX_NVM_FILE_LEN 16384 /* + * Reads external NVM from a file into mvm->nvm_sections + * * HOW TO CREATE THE NVM FILE FORMAT: * ------------------------------ * 1. create hex file, format: @@ -277,20 +279,23 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) * * 4. save as "iNVM_xxx.bin" under /lib/firmware */ -static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) +static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) { - int ret, section_id, section_size; + int ret, section_size; + u16 section_id; const struct firmware *fw_entry; const struct { __le16 word1; __le16 word2; u8 data[]; } *file_sec; - const u8 *eof; + const u8 *eof, *temp; #define NVM_WORD1_LEN(x) (8 * (x & 0x03FF)) #define NVM_WORD2_ID(x) (x >> 12) + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n"); + /* * Obtain NVM image via request_firmware. Since we already used * request_firmware_nowait() for the firmware binary load and only @@ -362,12 +367,18 @@ static int iwl_mvm_load_external_nvm(struct iwl_mvm *mvm) break; } - ret = iwl_nvm_write_section(mvm, section_id, file_sec->data, - section_size); - if (ret < 0) { - IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); + temp = kmemdup(file_sec->data, section_size, GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + break; + } + if (WARN_ON(section_id >= NVM_NUM_OF_SECTIONS)) { + IWL_ERR(mvm, "Invalid NVM section ID\n"); + ret = -EINVAL; break; } + mvm->nvm_sections[section_id].data = temp; + mvm->nvm_sections[section_id].length = section_size; /* advance to the next section */ file_sec = (void *)(file_sec->data + section_size); @@ -377,6 +388,28 @@ out: return ret; } +/* Loads the NVM data stored in mvm->nvm_sections into the NIC */ +int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm) +{ + int i, ret; + u16 section_id; + struct iwl_nvm_section *sections = mvm->nvm_sections; + + IWL_DEBUG_EEPROM(mvm->trans->dev, "'Write to NVM\n"); + + for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) { + section_id = nvm_to_read[i]; + ret = iwl_nvm_write_section(mvm, section_id, + sections[section_id].data, + sections[section_id].length); + if (ret < 0) { + IWL_ERR(mvm, "iwl_mvm_send_cmd failed: %d\n", ret); + break; + } + } + return ret; +} + int iwl_nvm_init(struct iwl_mvm *mvm) { int ret, i, section; @@ -385,36 +418,36 @@ int iwl_nvm_init(struct iwl_mvm *mvm) /* load external NVM if configured */ if (iwlwifi_mod_params.nvm_file) { /* move to External NVM flow */ - ret = iwl_mvm_load_external_nvm(mvm); + ret = iwl_mvm_read_external_nvm(mvm); if (ret) return ret; - } - - /* Read From FW NVM */ - IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); - - /* TODO: find correct NVM max size for a section */ - nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, - GFP_KERNEL); - if (!nvm_buffer) - return -ENOMEM; - for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) { - section = nvm_to_read[i]; - /* we override the constness for initial read */ - ret = iwl_nvm_read_section(mvm, section, nvm_buffer); - if (ret < 0) - break; - temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); - if (!temp) { - ret = -ENOMEM; - break; + } else { + /* Read From FW NVM */ + IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); + + /* TODO: find correct NVM max size for a section */ + nvm_buffer = kmalloc(mvm->cfg->base_params->eeprom_size, + GFP_KERNEL); + if (!nvm_buffer) + return -ENOMEM; + for (i = 0; i < ARRAY_SIZE(nvm_to_read); i++) { + section = nvm_to_read[i]; + /* we override the constness for initial read */ + ret = iwl_nvm_read_section(mvm, section, nvm_buffer); + if (ret < 0) + break; + temp = kmemdup(nvm_buffer, ret, GFP_KERNEL); + if (!temp) { + ret = -ENOMEM; + break; + } + mvm->nvm_sections[section].data = temp; + mvm->nvm_sections[section].length = ret; } - mvm->nvm_sections[section].data = temp; - mvm->nvm_sections[section].length = ret; + kfree(nvm_buffer); + if (ret < 0) + return ret; } - kfree(nvm_buffer); - if (ret < 0) - return ret; mvm->nvm_data = iwl_parse_nvm_sections(mvm); if (!mvm->nvm_data) diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 2fcc8ef88a68d78fbc0f2225bbabc733d8d4e619..d86083c6f445ac40cbcf824df4892b597ee11f35 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -224,6 +224,10 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, false), + RX_HANDLER(SCAN_OFFLOAD_COMPLETE, + iwl_mvm_rx_scan_offload_complete_notif, false), + RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results, + false), RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false), RX_HANDLER(CARD_STATE_NOTIFICATION, iwl_mvm_rx_card_state_notif, false), @@ -249,6 +253,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(TIME_EVENT_NOTIFICATION), CMD(BINDING_CONTEXT_CMD), CMD(TIME_QUOTA_CMD), + CMD(NON_QOS_TX_COUNTER_CMD), CMD(RADIO_VERSION_NOTIFICATION), CMD(SCAN_REQUEST_CMD), CMD(SCAN_ABORT_CMD), @@ -260,10 +265,12 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(CALIB_RES_NOTIF_PHY_DB), CMD(SET_CALIB_DEFAULT_CMD), CMD(CALIBRATION_COMPLETE_NOTIFICATION), + CMD(ADD_STA_KEY), CMD(ADD_STA), CMD(REMOVE_STA), CMD(LQ_CMD), CMD(SCAN_OFFLOAD_CONFIG_CMD), + CMD(MATCH_FOUND_NOTIFICATION), CMD(SCAN_OFFLOAD_REQUEST_CMD), CMD(SCAN_OFFLOAD_ABORT_CMD), CMD(SCAN_OFFLOAD_COMPLETE), @@ -303,6 +310,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(REPLY_BEACON_FILTERING_CMD), CMD(REPLY_THERMAL_MNG_BACKOFF), CMD(MAC_PM_POWER_TABLE), + CMD(BT_COEX_CI), }; #undef CMD @@ -344,6 +352,14 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; + mvm->aux_queue = 15; + mvm->first_agg_queue = 16; + mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1; + if (mvm->cfg->base_params->num_of_queues == 16) { + mvm->aux_queue = 11; + mvm->first_agg_queue = 12; + } + mutex_init(&mvm->mutex); spin_lock_init(&mvm->async_handlers_lock); INIT_LIST_HEAD(&mvm->time_event_list); @@ -401,24 +417,32 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, IWL_INFO(mvm, "Detected %s, REV=0x%X\n", mvm->cfg->name, mvm->trans->hw_rev); - err = iwl_trans_start_hw(mvm->trans); - if (err) - goto out_free; - iwl_mvm_tt_initialize(mvm); - mutex_lock(&mvm->mutex); - err = iwl_run_init_mvm_ucode(mvm, true); - mutex_unlock(&mvm->mutex); - /* returns 0 if successful, 1 if success but in rfkill */ - if (err < 0 && !iwlmvm_mod_params.init_dbg) { - IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); - goto out_free; - } + /* + * If the NVM exists in an external file, + * there is no need to unnecessarily power up the NIC at driver load + */ + if (iwlwifi_mod_params.nvm_file) { + iwl_nvm_init(mvm); + } else { + err = iwl_trans_start_hw(mvm->trans); + if (err) + goto out_free; + + mutex_lock(&mvm->mutex); + err = iwl_run_init_mvm_ucode(mvm, true); + mutex_unlock(&mvm->mutex); + /* returns 0 if successful, 1 if success but in rfkill */ + if (err < 0 && !iwlmvm_mod_params.init_dbg) { + IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); + goto out_free; + } - /* Stop the hw after the ALIVE and NVM has been read */ - if (!iwlmvm_mod_params.init_dbg) - iwl_trans_stop_hw(mvm->trans, false); + /* Stop the hw after the ALIVE and NVM has been read */ + if (!iwlmvm_mod_params.init_dbg) + iwl_trans_stop_hw(mvm->trans, false); + } scan_size = sizeof(struct iwl_scan_cmd) + mvm->fw->ucode_capa.max_probe_length + @@ -435,7 +459,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (err) goto out_unregister; - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD) + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PM_CMD_SUPPORT) mvm->pm_ops = &pm_mac_ops; else mvm->pm_ops = &pm_legacy_ops; @@ -449,7 +473,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, out_free: iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); - iwl_trans_stop_hw(trans, true); + if (!iwlwifi_mod_params.nvm_file) + iwl_trans_stop_hw(trans, true); ieee80211_free_hw(mvm->hw); return NULL; } @@ -715,6 +740,9 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm) case IWL_MVM_SCAN_OS: ieee80211_scan_completed(mvm->hw, true); break; + case IWL_MVM_SCAN_SCHED: + ieee80211_sched_scan_stopped(mvm->hw); + break; } if (mvm->restart_fw > 0) diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index d58e393324ef3ff9591f1cf297cf656c259037ae..550824aa84ea4bde8794b267bda27401c6b6f058 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -300,11 +300,6 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, } if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { - cmd->rx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); - cmd->tx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); - if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | BIT(IEEE80211_AC_VI) | BIT(IEEE80211_AC_BE) | @@ -319,10 +314,31 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, } cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP; - cmd->heavy_tx_thld_packets = - IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; - cmd->heavy_rx_thld_packets = - IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; + + if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags & + cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); + } else { + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); + } + + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { + cmd->heavy_tx_thld_packets = + IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS; + cmd->heavy_rx_thld_packets = + IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS; + } else { + cmd->heavy_tx_thld_packets = + IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; + cmd->heavy_rx_thld_packets = + IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; + } cmd->heavy_tx_thld_percentage = IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; cmd->heavy_rx_thld_percentage = @@ -430,6 +446,32 @@ static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm, sizeof(cmd), &cmd); } +static int iwl_mvm_power_update_device(struct iwl_mvm *mvm) +{ + struct iwl_device_power_cmd cmd = { + .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK), + }; + + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) + return 0; + + if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) + cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if ((mvm->cur_ucode == IWL_UCODE_WOWLAN) ? mvm->disable_power_off_d3 : + mvm->disable_power_off) + cmd.flags &= + cpu_to_le16(~DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); +#endif + IWL_DEBUG_POWER(mvm, + "Sending device power command with flags = 0x%X\n", + cmd.flags); + + return iwl_mvm_send_cmd_pdu(mvm, POWER_TABLE_CMD, CMD_SYNC, sizeof(cmd), + &cmd); +} + #ifdef CONFIG_IWLWIFI_DEBUGFS static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, char *buf, @@ -440,10 +482,11 @@ static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, iwl_mvm_power_build_cmd(mvm, vif, &cmd); - pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? - 0 : 1); + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) + pos += scnprintf(buf+pos, bufsz-pos, "disable_power_off = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK)) ? + 0 : 1); pos += scnprintf(buf+pos, bufsz-pos, "power_scheme = %d\n", iwlmvm_mod_params.power_scheme); pos += scnprintf(buf+pos, bufsz-pos, "flags = 0x%x\n", @@ -609,6 +652,7 @@ int iwl_mvm_update_beacon_filter(struct iwl_mvm *mvm, const struct iwl_mvm_power_ops pm_mac_ops = { .power_update_mode = iwl_mvm_power_mac_update_mode, + .power_update_device_mode = iwl_mvm_power_update_device, .power_disable = iwl_mvm_power_mac_disable, #ifdef CONFIG_IWLWIFI_DEBUGFS .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read, diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c index 5c6ae16ec52b934c16835a4fecdffb300a1df38b..17e2bc827f9a3b20b7da9e0d7ee4dd43ac00606a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/iwlwifi/mvm/quota.c @@ -110,7 +110,8 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, data->n_interfaces[id]++; break; case NL80211_IFTYPE_AP: - if (mvmvif->ap_active) + case NL80211_IFTYPE_ADHOC: + if (mvmvif->ap_ibss_active) data->n_interfaces[id]++; break; case NL80211_IFTYPE_MONITOR: @@ -119,16 +120,45 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, break; case NL80211_IFTYPE_P2P_DEVICE: break; - case NL80211_IFTYPE_ADHOC: - if (vif->bss_conf.ibss_joined) - data->n_interfaces[id]++; - break; default: WARN_ON_ONCE(1); break; } } +static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, + struct iwl_time_quota_cmd *cmd) +{ +#ifdef CONFIG_NL80211_TESTMODE + struct iwl_mvm_vif *mvmvif; + int i, phy_id = -1, beacon_int = 0; + + if (!mvm->noa_duration || !mvm->noa_vif) + return; + + mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif); + if (!mvmvif->ap_ibss_active) + return; + + phy_id = mvmvif->phy_ctxt->id; + beacon_int = mvm->noa_vif->bss_conf.beacon_int; + + for (i = 0; i < MAX_BINDINGS; i++) { + u32 id_n_c = le32_to_cpu(cmd->quotas[i].id_and_color); + u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS; + u32 quota = le32_to_cpu(cmd->quotas[i].quota); + + if (id != phy_id) + continue; + + quota *= (beacon_int - mvm->noa_duration); + quota /= beacon_int; + + cmd->quotas[i].quota = cpu_to_le32(quota); + } +#endif +} + int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) { struct iwl_time_quota_cmd cmd = {}; @@ -196,6 +226,8 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif) /* Give the remainder of the session to the first binding */ le32_add_cpu(&cmd.quotas[0].quota, quota_rem); + iwl_mvm_adjust_quota_for_noa(mvm, &cmd); + ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, CMD_SYNC, sizeof(cmd), &cmd); if (ret) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 4ffaa3fa153f78e7d14f2ad90223b01146562c50..a0b4cc8d9c3b412c931d04606d02b72c4d769e61 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -82,13 +82,24 @@ static const u8 ant_toggle_lookup[] = { [ANT_ABC] = ANT_ABC, }; -#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \ - [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ - IWL_RATE_SISO_##s##M_PLCP, \ - IWL_RATE_MIMO2_##s##M_PLCP,\ - IWL_RATE_##rp##M_INDEX, \ +#define IWL_DECLARE_RATE_INFO(r, s, rp, rn) \ + [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ + IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ + IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP,\ + IWL_RATE_##rp##M_INDEX, \ IWL_RATE_##rn##M_INDEX } +#define IWL_DECLARE_MCS_RATE(s) \ + [IWL_RATE_MCS_##s##_INDEX] = { IWL_RATE_INVM_PLCP, \ + IWL_RATE_HT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_HT_MIMO2_MCS_##s##_PLCP, \ + IWL_RATE_VHT_SISO_MCS_##s##_PLCP, \ + IWL_RATE_VHT_MIMO2_MCS_##s##_PLCP, \ + IWL_RATE_INVM_INDEX, \ + IWL_RATE_INVM_INDEX } + /* * Parameter order: * rate, ht rate, prev rate, next rate @@ -102,16 +113,17 @@ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = { IWL_DECLARE_RATE_INFO(2, INV, 1, 5), /* 2mbps */ IWL_DECLARE_RATE_INFO(5, INV, 2, 11), /*5.5mbps */ IWL_DECLARE_RATE_INFO(11, INV, 9, 12), /* 11mbps */ - IWL_DECLARE_RATE_INFO(6, 6, 5, 11), /* 6mbps */ - IWL_DECLARE_RATE_INFO(9, 6, 6, 11), /* 9mbps */ - IWL_DECLARE_RATE_INFO(12, 12, 11, 18), /* 12mbps */ - IWL_DECLARE_RATE_INFO(18, 18, 12, 24), /* 18mbps */ - IWL_DECLARE_RATE_INFO(24, 24, 18, 36), /* 24mbps */ - IWL_DECLARE_RATE_INFO(36, 36, 24, 48), /* 36mbps */ - IWL_DECLARE_RATE_INFO(48, 48, 36, 54), /* 48mbps */ - IWL_DECLARE_RATE_INFO(54, 54, 48, INV), /* 54mbps */ - IWL_DECLARE_RATE_INFO(60, 60, 48, INV), /* 60mbps */ - /* FIXME:RS: ^^ should be INV (legacy) */ + IWL_DECLARE_RATE_INFO(6, 0, 5, 11), /* 6mbps ; MCS 0 */ + IWL_DECLARE_RATE_INFO(9, INV, 6, 11), /* 9mbps */ + IWL_DECLARE_RATE_INFO(12, 1, 11, 18), /* 12mbps ; MCS 1 */ + IWL_DECLARE_RATE_INFO(18, 2, 12, 24), /* 18mbps ; MCS 2 */ + IWL_DECLARE_RATE_INFO(24, 3, 18, 36), /* 24mbps ; MCS 3 */ + IWL_DECLARE_RATE_INFO(36, 4, 24, 48), /* 36mbps ; MCS 4 */ + IWL_DECLARE_RATE_INFO(48, 5, 36, 54), /* 48mbps ; MCS 5 */ + IWL_DECLARE_RATE_INFO(54, 6, 48, INV), /* 54mbps ; MCS 6 */ + IWL_DECLARE_MCS_RATE(7), /* MCS 7 */ + IWL_DECLARE_MCS_RATE(8), /* MCS 8 */ + IWL_DECLARE_MCS_RATE(9), /* MCS 9 */ }; static inline u8 rs_extract_rate(u32 rate_n_flags) @@ -124,26 +136,30 @@ static int iwl_hwrate_to_plcp_idx(u32 rate_n_flags) { int idx = 0; - /* HT rate format */ if (rate_n_flags & RATE_MCS_HT_MSK) { - idx = rs_extract_rate(rate_n_flags); - - WARN_ON_ONCE(idx >= IWL_RATE_MIMO3_6M_PLCP); - if (idx >= IWL_RATE_MIMO2_6M_PLCP) - idx = idx - IWL_RATE_MIMO2_6M_PLCP; + idx = rate_n_flags & RATE_HT_MCS_RATE_CODE_MSK; + idx += IWL_RATE_MCS_0_INDEX; - idx += IWL_FIRST_OFDM_RATE; - /* skip 9M not supported in ht*/ + /* skip 9M not supported in HT*/ if (idx >= IWL_RATE_9M_INDEX) idx += 1; - if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE)) + if ((idx >= IWL_FIRST_HT_RATE) && (idx <= IWL_LAST_HT_RATE)) return idx; + } else if (rate_n_flags & RATE_MCS_VHT_MSK) { + idx = rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK; + idx += IWL_RATE_MCS_0_INDEX; - /* legacy rate format, search for match in table */ + /* skip 9M not supported in VHT*/ + if (idx >= IWL_RATE_9M_INDEX) + idx++; + if ((idx >= IWL_FIRST_VHT_RATE) && (idx <= IWL_LAST_VHT_RATE)) + return idx; } else { + /* legacy rate format, search for match in table */ + + u8 legacy_rate = rs_extract_rate(rate_n_flags); for (idx = 0; idx < ARRAY_SIZE(iwl_rates); idx++) - if (iwl_rates[idx].plcp == - rs_extract_rate(rate_n_flags)) + if (iwl_rates[idx].plcp == legacy_rate) return idx; } @@ -155,6 +171,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta); static void rs_fill_link_cmd(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, u32 rate_n_flags); static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); @@ -180,35 +197,52 @@ static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, */ static s32 expected_tpt_legacy[IWL_RATE_COUNT] = { - 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 + 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0, 0, 0 }; -static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202}, /* Norm */ - {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210}, /* SGI */ - {0, 0, 0, 0, 47, 0, 91, 133, 171, 242, 305, 334, 362}, /* AGG */ - {0, 0, 0, 0, 52, 0, 101, 145, 187, 264, 330, 361, 390}, /* AGG+SGI */ +/* Expected TpT tables. 4 indexes: + * 0 - NGI, 1 - SGI, 2 - AGG+NGI, 3 - AGG+SGI + */ +static s32 expected_tpt_siso_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 42, 0, 76, 102, 124, 159, 183, 193, 202, 216, 0}, + {0, 0, 0, 0, 46, 0, 82, 110, 132, 168, 192, 202, 210, 225, 0}, + {0, 0, 0, 0, 49, 0, 97, 145, 192, 285, 375, 420, 464, 551, 0}, + {0, 0, 0, 0, 54, 0, 108, 160, 213, 315, 415, 465, 513, 608, 0}, }; -static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ - {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ - {0, 0, 0, 0, 94, 0, 177, 249, 313, 423, 512, 550, 586}, /* AGG */ - {0, 0, 0, 0, 104, 0, 193, 270, 338, 454, 545, 584, 620}, /* AGG+SGI */ +static s32 expected_tpt_siso_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257, 269, 275}, + {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264, 275, 280}, + {0, 0, 0, 0, 101, 0, 199, 295, 389, 570, 744, 828, 911, 1070, 1173}, + {0, 0, 0, 0, 112, 0, 220, 326, 429, 629, 819, 912, 1000, 1173, 1284}, +}; + +static s32 expected_tpt_siso_80MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 130, 0, 191, 223, 244, 273, 288, 294, 298, 305, 308}, + {0, 0, 0, 0, 138, 0, 200, 231, 251, 279, 293, 298, 302, 308, 312}, + {0, 0, 0, 0, 217, 0, 429, 634, 834, 1220, 1585, 1760, 1931, 2258, 2466}, + {0, 0, 0, 0, 241, 0, 475, 701, 921, 1343, 1741, 1931, 2117, 2468, 2691}, }; static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 74, 0, 123, 155, 179, 214, 236, 244, 251}, /* Norm */ - {0, 0, 0, 0, 81, 0, 131, 164, 188, 223, 243, 251, 257}, /* SGI */ - {0, 0, 0, 0, 89, 0, 167, 235, 296, 402, 488, 526, 560}, /* AGG */ - {0, 0, 0, 0, 97, 0, 182, 255, 320, 431, 520, 558, 593}, /* AGG+SGI*/ + {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250, 261, 0}, + {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256, 267, 0}, + {0, 0, 0, 0, 98, 0, 193, 286, 375, 550, 718, 799, 878, 1032, 0}, + {0, 0, 0, 0, 109, 0, 214, 316, 414, 607, 790, 879, 965, 1132, 0}, }; static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { - {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ - {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ - {0, 0, 0, 0, 171, 0, 305, 410, 496, 634, 731, 771, 805}, /* AGG */ - {0, 0, 0, 0, 186, 0, 329, 439, 527, 667, 764, 803, 838}, /* AGG+SGI */ + {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289, 296, 300}, + {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293, 300, 303}, + {0, 0, 0, 0, 200, 0, 390, 571, 741, 1067, 1365, 1505, 1640, 1894, 2053}, + {0, 0, 0, 0, 221, 0, 430, 630, 816, 1169, 1490, 1641, 1784, 2053, 2221}, +}; + +static s32 expected_tpt_mimo2_80MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 182, 0, 240, 264, 278, 299, 308, 311, 313, 317, 319}, + {0, 0, 0, 0, 190, 0, 247, 269, 282, 302, 310, 313, 315, 319, 320}, + {0, 0, 0, 0, 428, 0, 833, 1215, 1577, 2254, 2863, 3147, 3418, 3913, 4219}, + {0, 0, 0, 0, 474, 0, 920, 1338, 1732, 2464, 3116, 3418, 3705, 4225, 4545}, }; /* mbps, mcs */ @@ -263,7 +297,7 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm, lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); if (lq_sta->dbg_fixed_rate) { - rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); + rs_fill_link_cmd(NULL, NULL, lq_sta, lq_sta->dbg_fixed_rate); iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false); } } @@ -275,17 +309,6 @@ static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, { int ret = -EAGAIN; - /* - * Don't create TX aggregation sessions when in high - * BT traffic, as they would just be disrupted by BT. - */ - if (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) { - IWL_DEBUG_COEX(mvm, "BT traffic (%d), no aggregation allowed\n", - BT_MBOX_MSG(&mvm->last_bt_notif, - 3, TRAFFIC_LOAD)); - return ret; - } - IWL_DEBUG_HT(mvm, "Starting Tx agg: STA: %pM tid: %d\n", sta->addr, tid); ret = ieee80211_start_tx_ba_session(sta, tid, 5000); @@ -416,49 +439,54 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, */ /* FIXME:RS:remove this function and put the flags statically in the table */ static u32 rate_n_flags_from_tbl(struct iwl_mvm *mvm, - struct iwl_scale_tbl_info *tbl, - int index, u8 use_green) + struct iwl_scale_tbl_info *tbl, int index) { u32 rate_n_flags = 0; + rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & + RATE_MCS_ANT_ABC_MSK); + if (is_legacy(tbl->lq_type)) { - rate_n_flags = iwl_rates[index].plcp; + rate_n_flags |= iwl_rates[index].plcp; if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) rate_n_flags |= RATE_MCS_CCK_MSK; - } else if (is_Ht(tbl->lq_type)) { - if (index > IWL_LAST_OFDM_RATE) { + return rate_n_flags; + } + + if (is_ht(tbl->lq_type)) { + if (index < IWL_FIRST_HT_RATE || index > IWL_LAST_HT_RATE) { IWL_ERR(mvm, "Invalid HT rate index %d\n", index); - index = IWL_LAST_OFDM_RATE; + index = IWL_LAST_HT_RATE; } - rate_n_flags = RATE_MCS_HT_MSK; + rate_n_flags |= RATE_MCS_HT_MSK; - if (is_siso(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_siso; - else if (is_mimo2(tbl->lq_type)) - rate_n_flags |= iwl_rates[index].plcp_mimo2; + if (is_ht_siso(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_ht_siso; + else if (is_ht_mimo2(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_ht_mimo2; else WARN_ON_ONCE(1); + } else if (is_vht(tbl->lq_type)) { + if (index < IWL_FIRST_VHT_RATE || index > IWL_LAST_VHT_RATE) { + IWL_ERR(mvm, "Invalid VHT rate index %d\n", index); + index = IWL_LAST_VHT_RATE; + } + rate_n_flags |= RATE_MCS_VHT_MSK; + if (is_vht_siso(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_vht_siso; + else if (is_vht_mimo2(tbl->lq_type)) + rate_n_flags |= iwl_rates[index].plcp_vht_mimo2; + else + WARN_ON_ONCE(1); + } else { IWL_ERR(mvm, "Invalid tbl->lq_type %d\n", tbl->lq_type); } - rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & - RATE_MCS_ANT_ABC_MSK); - - if (is_Ht(tbl->lq_type)) { - if (tbl->is_ht40) - rate_n_flags |= RATE_MCS_CHAN_WIDTH_40; - if (tbl->is_SGI) - rate_n_flags |= RATE_MCS_SGI_MSK; - - if (use_green) { - rate_n_flags |= RATE_HT_MCS_GF_MSK; - if (is_siso(tbl->lq_type) && tbl->is_SGI) { - rate_n_flags &= ~RATE_MCS_SGI_MSK; - IWL_ERR(mvm, "GF was set with SGI:SISO\n"); - } - } - } + rate_n_flags |= tbl->bw; + if (tbl->is_SGI) + rate_n_flags |= RATE_MCS_SGI_MSK; + return rate_n_flags; } @@ -473,7 +501,7 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, { u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); u8 num_of_ant = get_num_of_ant_from_rate(rate_n_flags); - u8 mcs; + u8 nss; memset(tbl, 0, offsetof(struct iwl_scale_tbl_info, win)); *rate_idx = iwl_hwrate_to_plcp_idx(rate_n_flags); @@ -483,41 +511,62 @@ static int rs_get_tbl_info_from_mcs(const u32 rate_n_flags, return -EINVAL; } tbl->is_SGI = 0; /* default legacy setup */ - tbl->is_ht40 = 0; + tbl->bw = 0; tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); tbl->lq_type = LQ_NONE; tbl->max_search = IWL_MAX_SEARCH; - /* legacy rate format */ - if (!(rate_n_flags & RATE_MCS_HT_MSK)) { + /* Legacy */ + if (!(rate_n_flags & RATE_MCS_HT_MSK) && + !(rate_n_flags & RATE_MCS_VHT_MSK)) { if (num_of_ant == 1) { if (band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; + tbl->lq_type = LQ_LEGACY_A; else - tbl->lq_type = LQ_G; + tbl->lq_type = LQ_LEGACY_G; } - /* HT rate format */ - } else { - if (rate_n_flags & RATE_MCS_SGI_MSK) - tbl->is_SGI = 1; - - if (rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */ - tbl->is_ht40 = 1; - - mcs = rs_extract_rate(rate_n_flags); - - /* SISO */ - if (mcs <= IWL_RATE_SISO_60M_PLCP) { - if (num_of_ant == 1) - tbl->lq_type = LQ_SISO; /*else NONE*/ - /* MIMO2 */ - } else if (mcs <= IWL_RATE_MIMO2_60M_PLCP) { - if (num_of_ant == 2) - tbl->lq_type = LQ_MIMO2; + + return 0; + } + + /* HT or VHT */ + if (rate_n_flags & RATE_MCS_SGI_MSK) + tbl->is_SGI = 1; + + tbl->bw = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; + + if (rate_n_flags & RATE_MCS_HT_MSK) { + nss = ((rate_n_flags & RATE_HT_MCS_NSS_MSK) >> + RATE_HT_MCS_NSS_POS) + 1; + + if (nss == 1) { + tbl->lq_type = LQ_HT_SISO; + WARN_ON_ONCE(num_of_ant != 1); + } else if (nss == 2) { + tbl->lq_type = LQ_HT_MIMO2; + WARN_ON_ONCE(num_of_ant != 2); + } else { + WARN_ON_ONCE(1); + } + } else if (rate_n_flags & RATE_MCS_VHT_MSK) { + nss = ((rate_n_flags & RATE_VHT_MCS_NSS_MSK) >> + RATE_VHT_MCS_NSS_POS) + 1; + + if (nss == 1) { + tbl->lq_type = LQ_VHT_SISO; + WARN_ON_ONCE(num_of_ant != 1); + } else if (nss == 2) { + tbl->lq_type = LQ_VHT_MIMO2; + WARN_ON_ONCE(num_of_ant != 2); } else { - WARN_ON_ONCE(num_of_ant == 3); + WARN_ON_ONCE(1); } } + + WARN_ON_ONCE(tbl->bw == RATE_MCS_CHAN_WIDTH_160); + WARN_ON_ONCE(tbl->bw == RATE_MCS_CHAN_WIDTH_80 && + !is_vht(tbl->lq_type)); + return 0; } @@ -549,22 +598,6 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, return 1; } -/** - * Green-field mode is valid if the station supports it and - * there are no non-GF stations present in the BSS. - */ -static bool rs_use_green(struct ieee80211_sta *sta) -{ - /* - * There's a bug somewhere in this code that causes the - * scaling to get stuck because GF+SGI can't be combined - * in SISO rates. Until we find that bug, disable GF, it - * has only limited benefit and we still interoperate with - * GF APs since we can always receive GF transmissions. - */ - return false; -} - /** * rs_get_supported_rates - get the available rates * @@ -576,16 +609,15 @@ static u16 rs_get_supported_rates(struct iwl_lq_sta *lq_sta, struct ieee80211_hdr *hdr, enum iwl_table_type rate_type) { - if (is_legacy(rate_type)) { + if (is_legacy(rate_type)) return lq_sta->active_legacy_rate; - } else { - if (is_siso(rate_type)) - return lq_sta->active_siso_rate; - else { - WARN_ON_ONCE(!is_mimo2(rate_type)); - return lq_sta->active_mimo2_rate; - } - } + else if (is_siso(rate_type)) + return lq_sta->active_siso_rate; + else if (is_mimo2(rate_type)) + return lq_sta->active_mimo2_rate; + + WARN_ON_ONCE(1); + return 0; } static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, @@ -652,7 +684,6 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, u16 rate_mask; u16 high_low; u8 switch_to_legacy = 0; - u8 is_green = lq_sta->is_green; struct iwl_mvm *mvm = lq_sta->drv; /* check if we need to switch from HT to legacy rates. @@ -662,15 +693,15 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, switch_to_legacy = 1; scale_index = rs_ht_to_legacy[scale_index]; if (lq_sta->band == IEEE80211_BAND_5GHZ) - tbl->lq_type = LQ_A; + tbl->lq_type = LQ_LEGACY_A; else - tbl->lq_type = LQ_G; + tbl->lq_type = LQ_LEGACY_G; if (num_of_ant(tbl->ant_type) > 1) tbl->ant_type = first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - tbl->is_ht40 = 0; + tbl->bw = 0; tbl->is_SGI = 0; tbl->max_search = IWL_MAX_SEARCH; } @@ -701,7 +732,7 @@ static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, low = scale_index; out: - return rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); + return rate_n_flags_from_tbl(lq_sta->drv, tbl, low); } /* @@ -714,6 +745,18 @@ static bool table_type_matches(struct iwl_scale_tbl_info *a, (a->is_SGI == b->is_SGI); } +static u32 rs_ch_width_from_mac_flags(enum mac80211_rate_control_flags flags) +{ + if (flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + return RATE_MCS_CHAN_WIDTH_40; + else if (flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + return RATE_MCS_CHAN_WIDTH_80; + else if (flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + return RATE_MCS_CHAN_WIDTH_160; + + return RATE_MCS_CHAN_WIDTH_20; +} + /* * mac80211 sends us Tx status */ @@ -783,16 +826,23 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, */ if (info->band == IEEE80211_BAND_2GHZ) mac_index += IWL_FIRST_OFDM_RATE; + } else if (mac_flags & IEEE80211_TX_RC_VHT_MCS) { + mac_index &= RATE_VHT_MCS_RATE_CODE_MSK; + if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) + mac_index++; } + /* Here we actually compare this rate to the latest LQ command */ if ((mac_index < 0) || (tbl_type.is_SGI != !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || - (tbl_type.is_ht40 != !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || + (tbl_type.bw != rs_ch_width_from_mac_flags(mac_flags)) || (tbl_type.ant_type != info->status.antenna) || (!!(tx_rate & RATE_MCS_HT_MSK) != - !!(mac_flags & IEEE80211_TX_RC_MCS)) || + !!(mac_flags & IEEE80211_TX_RC_MCS)) || + (!!(tx_rate & RATE_MCS_VHT_MSK) != + !!(mac_flags & IEEE80211_TX_RC_VHT_MCS)) || (!!(tx_rate & RATE_HT_MCS_GF_MSK) != - !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || + !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || (rs_index != mac_index)) { IWL_DEBUG_RATE(mvm, "initial rate %d does not match %d (0x%x)\n", @@ -947,7 +997,8 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, s32 (*ht_tbl_pointer)[IWL_RATE_COUNT]; /* Check for invalid LQ type */ - if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { + if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_ht(tbl->lq_type) && + !(is_vht(tbl->lq_type)))) { tbl->expected_tpt = expected_tpt_legacy; return; } @@ -958,18 +1009,40 @@ static void rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, return; } + ht_tbl_pointer = expected_tpt_mimo2_20MHz; /* Choose among many HT tables depending on number of streams - * (SISO/MIMO2), channel width (20/40), SGI, and aggregation + * (SISO/MIMO2), channel width (20/40/80), SGI, and aggregation * status */ - if (is_siso(tbl->lq_type) && !tbl->is_ht40) - ht_tbl_pointer = expected_tpt_siso20MHz; - else if (is_siso(tbl->lq_type)) - ht_tbl_pointer = expected_tpt_siso40MHz; - else if (is_mimo2(tbl->lq_type) && !tbl->is_ht40) - ht_tbl_pointer = expected_tpt_mimo2_20MHz; - else { - WARN_ON_ONCE(!is_mimo2(tbl->lq_type)); - ht_tbl_pointer = expected_tpt_mimo2_40MHz; + if (is_siso(tbl->lq_type)) { + switch (tbl->bw) { + case RATE_MCS_CHAN_WIDTH_20: + ht_tbl_pointer = expected_tpt_siso_20MHz; + break; + case RATE_MCS_CHAN_WIDTH_40: + ht_tbl_pointer = expected_tpt_siso_40MHz; + break; + case RATE_MCS_CHAN_WIDTH_80: + ht_tbl_pointer = expected_tpt_siso_80MHz; + break; + default: + WARN_ON_ONCE(1); + } + } else if (is_mimo2(tbl->lq_type)) { + switch (tbl->bw) { + case RATE_MCS_CHAN_WIDTH_20: + ht_tbl_pointer = expected_tpt_mimo2_20MHz; + break; + case RATE_MCS_CHAN_WIDTH_40: + ht_tbl_pointer = expected_tpt_mimo2_40MHz; + break; + case RATE_MCS_CHAN_WIDTH_80: + ht_tbl_pointer = expected_tpt_mimo2_80MHz; + break; + default: + WARN_ON_ONCE(1); + } + } else { + WARN_ON_ONCE(1); } if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ @@ -1084,9 +1157,47 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, return new_rate; } -static bool iwl_is_ht40_tx_allowed(struct ieee80211_sta *sta) +/* Move to the next action and wrap around to the first action in case + * we're at the last action. Assumes actions start at 0. + */ +static inline void rs_move_next_action(struct iwl_scale_tbl_info *tbl, + u8 last_action) +{ + BUILD_BUG_ON(IWL_LEGACY_FIRST_ACTION != 0); + BUILD_BUG_ON(IWL_SISO_FIRST_ACTION != 0); + BUILD_BUG_ON(IWL_MIMO2_FIRST_ACTION != 0); + + tbl->action = (tbl->action + 1) % (last_action + 1); +} + +static void rs_set_bw_from_sta(struct iwl_scale_tbl_info *tbl, + struct ieee80211_sta *sta) +{ + if (sta->bandwidth >= IEEE80211_STA_RX_BW_80) + tbl->bw = RATE_MCS_CHAN_WIDTH_80; + else if (sta->bandwidth >= IEEE80211_STA_RX_BW_40) + tbl->bw = RATE_MCS_CHAN_WIDTH_40; + else + tbl->bw = RATE_MCS_CHAN_WIDTH_20; +} + +static bool rs_sgi_allowed(struct iwl_scale_tbl_info *tbl, + struct ieee80211_sta *sta) { - return sta->bandwidth >= IEEE80211_STA_RX_BW_40; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; + + if (is_ht20(tbl) && (ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + return true; + if (is_ht40(tbl) && (ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + return true; + if (is_ht80(tbl) && (vht_cap->cap & + IEEE80211_VHT_CAP_SHORT_GI_80)) + return true; + + return false; } /* @@ -1099,7 +1210,6 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm, { u16 rate_mask; s32 rate; - s8 is_green = lq_sta->is_green; if (!sta->ht_cap.ht_supported) return -1; @@ -1113,16 +1223,12 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm, IWL_DEBUG_RATE(mvm, "LQ: try to switch to MIMO2\n"); - tbl->lq_type = LQ_MIMO2; + tbl->lq_type = lq_sta->is_vht ? LQ_VHT_MIMO2 : LQ_HT_MIMO2; tbl->action = 0; tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_mimo2_rate; - if (iwl_is_ht40_tx_allowed(sta)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - + rs_set_bw_from_sta(tbl, sta); rs_set_expected_tpt_table(lq_sta, tbl); rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index); @@ -1134,10 +1240,10 @@ static int rs_switch_to_mimo2(struct iwl_mvm *mvm, rate, rate_mask); return -1; } - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green); + tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate); - IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); + IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index\n", + tbl->current_rate); return 0; } @@ -1150,7 +1256,6 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm, struct iwl_scale_tbl_info *tbl, int index) { u16 rate_mask; - u8 is_green = lq_sta->is_green; s32 rate; if (!sta->ht_cap.ht_supported) @@ -1158,19 +1263,12 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm, IWL_DEBUG_RATE(mvm, "LQ: try to switch to SISO\n"); - tbl->lq_type = LQ_SISO; + tbl->lq_type = lq_sta->is_vht ? LQ_VHT_SISO : LQ_HT_SISO; tbl->action = 0; tbl->max_search = IWL_MAX_SEARCH; rate_mask = lq_sta->active_siso_rate; - if (iwl_is_ht40_tx_allowed(sta)) - tbl->is_ht40 = 1; - else - tbl->is_ht40 = 0; - - if (is_green) - tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ - + rs_set_bw_from_sta(tbl, sta); rs_set_expected_tpt_table(lq_sta, tbl); rate = rs_get_best_rate(mvm, lq_sta, tbl, rate_mask, index); @@ -1181,9 +1279,9 @@ static int rs_switch_to_siso(struct iwl_mvm *mvm, rate, rate_mask); return -1; } - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate, is_green); - IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index is green %X\n", - tbl->current_rate, is_green); + tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, rate); + IWL_DEBUG_RATE(mvm, "LQ: Switch to new mcs %X index\n", + tbl->current_rate); return 0; } @@ -1211,14 +1309,10 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm, while (1) { lq_sta->action_counter++; switch (tbl->action) { - case IWL_LEGACY_SWITCH_ANTENNA1: - case IWL_LEGACY_SWITCH_ANTENNA2: + case IWL_LEGACY_SWITCH_ANTENNA: IWL_DEBUG_RATE(mvm, "LQ: Legacy toggle Antenna\n"); - if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) + if (tx_chains_num <= 1) break; /* Don't change antenna if success has been great */ @@ -1273,9 +1367,7 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm, default: WARN_ON_ONCE(1); } - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + rs_move_next_action(tbl, IWL_LEGACY_LAST_ACTION); if (tbl->action == start_action) break; @@ -1285,9 +1377,7 @@ static int rs_move_legacy_other(struct iwl_mvm *mvm, out: lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_LEGACY_SWITCH_MIMO2) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + rs_move_next_action(tbl, IWL_LEGACY_LAST_ACTION); if (update_search_tbl_counter) search_tbl->action = tbl->action; return 0; @@ -1300,12 +1390,10 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, struct ieee80211_sta *sta, int index) { - u8 is_green = lq_sta->is_green; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct iwl_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; @@ -1314,40 +1402,17 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, u8 update_search_tbl_counter = 0; int ret; - switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) - tbl->action = IWL_SISO_SWITCH_MIMO2; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - valid_tx_ant = - first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - if (tbl->action != IWL_SISO_SWITCH_ANTENNA1) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; - break; - default: - IWL_ERR(mvm, "Invalid BT load %d", - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)); - break; - } + if (tbl->action == IWL_SISO_SWITCH_MIMO2 && + !iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) + tbl->action = IWL_SISO_SWITCH_ANTENNA; start_action = tbl->action; while (1) { lq_sta->action_counter++; switch (tbl->action) { - case IWL_SISO_SWITCH_ANTENNA1: - case IWL_SISO_SWITCH_ANTENNA2: + case IWL_SISO_SWITCH_ANTENNA: IWL_DEBUG_RATE(mvm, "LQ: SISO toggle Antenna\n"); - if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 && - tx_chains_num <= 1) || - (tbl->action == IWL_SISO_SWITCH_ANTENNA2 && - tx_chains_num <= 2)) + if (tx_chains_num <= 1) break; if (window->success_ratio >= IWL_RS_GOOD_RATIO && @@ -1380,23 +1445,12 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, goto out; break; case IWL_SISO_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) + if (!rs_sgi_allowed(tbl, sta)) break; IWL_DEBUG_RATE(mvm, "LQ: SISO toggle SGI/NGI\n"); memcpy(search_tbl, tbl, sz); - if (is_green) { - if (!tbl->is_SGI) - break; - else - IWL_ERR(mvm, - "SGI was set in GF+SISO\n"); - } search_tbl->is_SGI = !tbl->is_SGI; rs_set_expected_tpt_table(lq_sta, search_tbl); if (tbl->is_SGI) { @@ -1405,16 +1459,13 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, break; } search_tbl->current_rate = - rate_n_flags_from_tbl(mvm, search_tbl, - index, is_green); + rate_n_flags_from_tbl(mvm, search_tbl, index); update_search_tbl_counter = 1; goto out; default: WARN_ON_ONCE(1); } - tbl->action++; - if (tbl->action > IWL_SISO_SWITCH_GI) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; + rs_move_next_action(tbl, IWL_SISO_LAST_ACTION); if (tbl->action == start_action) break; @@ -1424,9 +1475,7 @@ static int rs_move_siso_to_other(struct iwl_mvm *mvm, out: lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_SISO_SWITCH_GI) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; + rs_move_next_action(tbl, IWL_SISO_LAST_ACTION); if (update_search_tbl_counter) search_tbl->action = tbl->action; @@ -1440,63 +1489,20 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, struct ieee80211_sta *sta, int index) { - s8 is_green = lq_sta->is_green; struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); struct iwl_scale_tbl_info *search_tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); - struct iwl_rate_scale_data *window = &(tbl->win[index]); - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; u32 sz = (sizeof(struct iwl_scale_tbl_info) - (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); u8 start_action; u8 valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - u8 tx_chains_num = num_of_ant(valid_tx_ant); u8 update_search_tbl_counter = 0; int ret; - switch (BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { - case IWL_BT_COEX_TRAFFIC_LOAD_NONE: - /* nothing */ - break; - case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: - case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: - /* avoid antenna B and MIMO */ - if (tbl->action != IWL_MIMO2_SWITCH_SISO_A) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - break; - case IWL_BT_COEX_TRAFFIC_LOAD_LOW: - /* avoid antenna B unless MIMO */ - if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) - tbl->action = IWL_MIMO2_SWITCH_SISO_A; - break; - default: - IWL_ERR(mvm, "Invalid BT load %d", - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)); - break; - } - start_action = tbl->action; while (1) { lq_sta->action_counter++; switch (tbl->action) { - case IWL_MIMO2_SWITCH_ANTENNA1: - case IWL_MIMO2_SWITCH_ANTENNA2: - IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle Antennas\n"); - - if (tx_chains_num <= 2) - break; - - if (window->success_ratio >= IWL_RS_GOOD_RATIO) - break; - - memcpy(search_tbl, tbl, sz); - if (rs_toggle_antenna(valid_tx_ant, - &search_tbl->current_rate, - search_tbl)) { - update_search_tbl_counter = 1; - goto out; - } - break; case IWL_MIMO2_SWITCH_SISO_A: case IWL_MIMO2_SWITCH_SISO_B: IWL_DEBUG_RATE(mvm, "LQ: MIMO2 switch to SISO\n"); @@ -1521,11 +1527,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, break; case IWL_MIMO2_SWITCH_GI: - if (!tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_20)) - break; - if (tbl->is_ht40 && !(ht_cap->cap & - IEEE80211_HT_CAP_SGI_40)) + if (!rs_sgi_allowed(tbl, sta)) break; IWL_DEBUG_RATE(mvm, "LQ: MIMO2 toggle SGI/NGI\n"); @@ -1546,16 +1548,13 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, break; } search_tbl->current_rate = - rate_n_flags_from_tbl(mvm, search_tbl, - index, is_green); + rate_n_flags_from_tbl(mvm, search_tbl, index); update_search_tbl_counter = 1; goto out; default: WARN_ON_ONCE(1); } - tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_GI) - tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; + rs_move_next_action(tbl, IWL_MIMO2_LAST_ACTION); if (tbl->action == start_action) break; @@ -1564,9 +1563,7 @@ static int rs_move_mimo2_to_other(struct iwl_mvm *mvm, return 0; out: lq_sta->search_better_tbl = 1; - tbl->action++; - if (tbl->action > IWL_MIMO2_SWITCH_GI) - tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; + rs_move_next_action(tbl, IWL_MIMO2_LAST_ACTION); if (update_search_tbl_counter) search_tbl->action = tbl->action; @@ -1660,15 +1657,16 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) * setup rate table in uCode */ static void rs_update_rate_tbl(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl, - int index, u8 is_green) + int index) { u32 rate; /* Update uCode's rate table. */ - rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green); - rs_fill_link_cmd(mvm, lq_sta, rate); + rate = rate_n_flags_from_tbl(mvm, tbl, index); + rs_fill_link_cmd(mvm, sta, lq_sta, rate); iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false); } @@ -1712,7 +1710,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, u8 update_lq = 0; struct iwl_scale_tbl_info *tbl, *tbl1; u16 rate_scale_index_msk = 0; - u8 is_green = 0; u8 active_tbl = 0; u8 done_search = 0; u16 high_low; @@ -1754,11 +1751,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, active_tbl = 1 - lq_sta->active_tbl; tbl = &(lq_sta->lq_info[active_tbl]); - if (is_legacy(tbl->lq_type)) - lq_sta->is_green = 0; - else - lq_sta->is_green = rs_use_green(sta); - is_green = lq_sta->is_green; /* current tx rate */ index = lq_sta->last_txrate_idx; @@ -1797,7 +1789,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); /* get "active" rate info */ index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green); + rs_update_rate_tbl(mvm, sta, lq_sta, tbl, index); } return; } @@ -1978,24 +1970,24 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, (current_tpt > (100 * tbl->expected_tpt[low])))) scale_action = 0; - if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= + if ((le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && (is_mimo(tbl->lq_type))) { if (lq_sta->last_bt_traffic > - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { + le32_to_cpu(mvm->last_bt_notif.bt_activity_grading)) { /* * don't set scale_action, don't want to scale up if * the rate scale doesn't otherwise think that is a * good idea. */ } else if (lq_sta->last_bt_traffic <= - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD)) { + le32_to_cpu(mvm->last_bt_notif.bt_activity_grading)) { scale_action = -1; } } lq_sta->last_bt_traffic = - BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD); + le32_to_cpu(mvm->last_bt_notif.bt_activity_grading); - if ((BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= + if ((le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) && is_mimo(tbl->lq_type)) { /* search for a new modulation */ rs_stay_in_table(lq_sta, true); @@ -2032,7 +2024,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, lq_update: /* Replace uCode's rate table for the destination station. */ if (update_lq) - rs_update_rate_tbl(mvm, lq_sta, tbl, index, is_green); + rs_update_rate_tbl(mvm, sta, lq_sta, tbl, index); rs_stay_in_table(lq_sta, false); @@ -2071,7 +2063,7 @@ lq_update: IWL_DEBUG_RATE(mvm, "Switch current mcs: %X index: %d\n", tbl->current_rate, index); - rs_fill_link_cmd(mvm, lq_sta, tbl->current_rate); + rs_fill_link_cmd(mvm, sta, lq_sta, tbl->current_rate); iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_ASYNC, false); } else { done_search = 1; @@ -2113,7 +2105,7 @@ lq_update: } out: - tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index, is_green); + tbl->current_rate = rate_n_flags_from_tbl(mvm, tbl, index); lq_sta->last_txrate_idx = index; } @@ -2140,7 +2132,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, int rate_idx; int i; u32 rate; - u8 use_green = rs_use_green(sta); u8 active_tbl = 0; u8 valid_tx_ant; @@ -2172,10 +2163,10 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, if (!rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) rs_toggle_antenna(valid_tx_ant, &rate, tbl); - rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx, use_green); + rate = rate_n_flags_from_tbl(mvm, tbl, rate_idx); tbl->current_rate = rate; rs_set_expected_tpt_table(lq_sta, tbl); - rs_fill_link_cmd(NULL, lq_sta, rate); + rs_fill_link_cmd(NULL, NULL, lq_sta, rate); /* TODO restore station should remember the lq cmd */ iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, CMD_SYNC, true); } @@ -2190,7 +2181,6 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = mvm_sta; - int rate_idx; IWL_DEBUG_RATE_LIMIT(mvm, "rate scale calculate new rate for skb\n"); @@ -2215,36 +2205,9 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, if (rate_control_send_low(sta, mvm_sta, txrc)) return; - rate_idx = lq_sta->last_txrate_idx; - - if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { - rate_idx -= IWL_FIRST_OFDM_RATE; - /* 6M and 9M shared same MCS index */ - rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; - WARN_ON_ONCE(rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO3_6M_PLCP); - if (rs_extract_rate(lq_sta->last_rate_n_flags) >= - IWL_RATE_MIMO2_6M_PLCP) - rate_idx = rate_idx + MCS_INDEX_PER_STREAM; - info->control.rates[0].flags = IEEE80211_TX_RC_MCS; - if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_SHORT_GI; - if (lq_sta->last_rate_n_flags & RATE_MCS_CHAN_WIDTH_40) /* TODO */ - info->control.rates[0].flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; - if (lq_sta->last_rate_n_flags & RATE_HT_MCS_GF_MSK) - info->control.rates[0].flags |= IEEE80211_TX_RC_GREEN_FIELD; - } else { - /* Check for invalid rates */ - if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) || - ((sband->band == IEEE80211_BAND_5GHZ) && - (rate_idx < IWL_FIRST_OFDM_RATE))) - rate_idx = rate_lowest_index(sband, sta); - /* On valid 5 GHz rate, adjust index */ - else if (sband->band == IEEE80211_BAND_5GHZ) - rate_idx -= IWL_FIRST_OFDM_RATE; - info->control.rates[0].flags = 0; - } - info->control.rates[0].idx = rate_idx; + iwl_mvm_hwrate_to_tx_rate(lq_sta->last_rate_n_flags, + info->band, &info->control.rates[0]); + info->control.rates[0].count = 1; } @@ -2261,6 +2224,24 @@ static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, return &sta_priv->lq_sta; } +static int rs_vht_highest_rx_mcs_index(struct ieee80211_sta_vht_cap *vht_cap, + int nss) +{ + u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & + (0x3 << (2 * (nss - 1))); + rx_mcs >>= (2 * (nss - 1)); + + if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_7) + return IWL_RATE_MCS_7_INDEX; + else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_8) + return IWL_RATE_MCS_8_INDEX; + else if (rx_mcs == IEEE80211_VHT_MCS_SUPPORT_0_9) + return IWL_RATE_MCS_9_INDEX; + + WARN_ON_ONCE(rx_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED); + return -1; +} + /* * Called after adding a new station to initialize rate scaling */ @@ -2270,6 +2251,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int i, j; struct ieee80211_hw *hw = mvm->hw; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; struct iwl_mvm_sta *sta_priv; struct iwl_lq_sta *lq_sta; struct ieee80211_supported_band *sband; @@ -2298,7 +2280,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->max_rate_idx = -1; lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; - lq_sta->is_green = rs_use_green(sta); lq_sta->band = sband->band; /* * active legacy rates as per supported rates bitmap @@ -2308,25 +2289,54 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, for_each_set_bit(i, &supp, BITS_PER_LONG) lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); - /* - * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), - * supp_rates[] does not; shift to convert format, force 9 MBits off. - */ - lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; - lq_sta->active_siso_rate &= ~((u16)0x2); - lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + /* TODO: should probably account for rx_highest for both HT/VHT */ + if (!vht_cap || !vht_cap->vht_supported) { + /* active_siso_rate mask includes 9 MBits (bit 5), + * and CCK (bits 0-3), supp_rates[] does not; + * shift to convert format, force 9 MBits off. + */ + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate &= ~((u16)0x2); + lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + + /* Same here */ + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate &= ~((u16)0x2); + lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + + lq_sta->is_vht = false; + } else { + int highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 1); + if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { + for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { + if (i == IWL_RATE_9M_INDEX) + continue; + + lq_sta->active_siso_rate |= BIT(i); + } + } + + highest_mcs = rs_vht_highest_rx_mcs_index(vht_cap, 2); + if (highest_mcs >= IWL_RATE_MCS_0_INDEX) { + for (i = IWL_RATE_MCS_0_INDEX; i <= highest_mcs; i++) { + if (i == IWL_RATE_9M_INDEX) + continue; - /* Same here */ - lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; - lq_sta->active_mimo2_rate &= ~((u16)0x2); - lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + lq_sta->active_mimo2_rate |= BIT(i); + } + } + + /* TODO: avoid MCS9 in 20Mhz which isn't valid for 11ac */ + lq_sta->is_vht = true; + } IWL_DEBUG_RATE(mvm, - "SISO-RATE=%X MIMO2-RATE=%X\n", + "SISO-RATE=%X MIMO2-RATE=%X VHT=%d\n", lq_sta->active_siso_rate, - lq_sta->active_mimo2_rate); + lq_sta->active_mimo2_rate, + lq_sta->is_vht); /* These values will be overridden later */ lq_sta->lq.single_stream_ant_msk = @@ -2358,6 +2368,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } static void rs_fill_link_cmd(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, u32 new_rate) { struct iwl_scale_tbl_info tbl_type; @@ -2429,7 +2440,6 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, &tbl_type, &rate_idx); - /* Indicate to uCode which entries might be MIMO. * If initial rate was MIMO, this will finally end up * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ @@ -2455,7 +2465,9 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, } /* Don't allow HT rates after next pass. - * rs_get_lower_rate() will change type to LQ_A or LQ_G. */ + * rs_get_lower_rate() will change type to LQ_LEGACY_A + * or LQ_LEGACY_G. + */ use_ht_possible = 0; /* Override next rate if needed for debug purposes */ @@ -2474,12 +2486,9 @@ static void rs_fill_link_cmd(struct iwl_mvm *mvm, lq_cmd->agg_time_limit = cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - /* - * overwrite if needed, pass aggregation time limit - * to uCode in uSec - This is racy - but heh, at least it helps... - */ - if (mvm && BT_MBOX_MSG(&mvm->last_bt_notif, 3, TRAFFIC_LOAD) >= 2) - lq_cmd->agg_time_limit = cpu_to_le16(1200); + if (sta) + lq_cmd->agg_time_limit = + cpu_to_le16(iwl_mvm_bt_coex_agg_time_limit(mvm, sta)); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) @@ -2586,16 +2595,18 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, (iwl_fw_valid_tx_ant(mvm->fw) & ANT_B) ? "ANT_B," : "", (iwl_fw_valid_tx_ant(mvm->fw) & ANT_C) ? "ANT_C" : ""); desc += sprintf(buff+desc, "lq type %s\n", - (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); - if (is_Ht(tbl->lq_type)) { + (is_legacy(tbl->lq_type)) ? "legacy" : + is_vht(tbl->lq_type) ? "VHT" : "HT"); + if (is_ht(tbl->lq_type)) { desc += sprintf(buff+desc, " %s", (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2"); desc += sprintf(buff+desc, " %s", - (tbl->is_ht40) ? "40MHz" : "20MHz"); - desc += sprintf(buff+desc, " %s %s %s\n", + (is_ht20(tbl)) ? "20MHz" : + (is_ht40(tbl)) ? "40MHz" : + (is_ht80(tbl)) ? "80Mhz" : "BAD BW"); + desc += sprintf(buff+desc, " %s %s\n", (tbl->is_SGI) ? "SGI" : "", - (lq_sta->is_green) ? "GF enabled" : "", - (lq_sta->is_agg) ? "AGG on" : ""); + (lq_sta->is_agg) ? "AGG on" : ""); } desc += sprintf(buff+desc, "last tx rate=0x%X\n", lq_sta->last_rate_n_flags); @@ -2653,7 +2664,7 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, int desc = 0; int i, j; ssize_t ret; - + struct iwl_scale_tbl_info *tbl; struct iwl_lq_sta *lq_sta = file->private_data; buff = kmalloc(1024, GFP_KERNEL); @@ -2661,21 +2672,23 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, return -ENOMEM; for (i = 0; i < LQ_SIZE; i++) { + tbl = &(lq_sta->lq_info[i]); desc += sprintf(buff+desc, - "%s type=%d SGI=%d HT40=%d DUP=0 GF=%d\n" + "%s type=%d SGI=%d BW=%s DUP=0\n" "rate=0x%X\n", lq_sta->active_tbl == i ? "*" : "x", - lq_sta->lq_info[i].lq_type, - lq_sta->lq_info[i].is_SGI, - lq_sta->lq_info[i].is_ht40, - lq_sta->is_green, - lq_sta->lq_info[i].current_rate); + tbl->lq_type, + tbl->is_SGI, + is_ht20(tbl) ? "20Mhz" : + is_ht40(tbl) ? "40Mhz" : + is_ht80(tbl) ? "80Mhz" : "ERR", + tbl->current_rate); for (j = 0; j < IWL_RATE_COUNT; j++) { desc += sprintf(buff+desc, "counter=%d success=%d %%=%d\n", - lq_sta->lq_info[i].win[j].counter, - lq_sta->lq_info[i].win[j].success_counter, - lq_sta->lq_info[i].win[j].success_ratio); + tbl->win[j].counter, + tbl->win[j].success_counter, + tbl->win[j].success_ratio); } } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index 335cf16829023e6702ef1d5921c19cccb3196c84..5d5344f7070bf5de62a4cde464c573b43d01f8db 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -35,9 +35,11 @@ #include "iwl-trans.h" struct iwl_rs_rate_info { - u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ - u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ - u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 plcp_ht_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ + u8 plcp_ht_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 plcp_vht_siso; + u8 plcp_vht_mimo2; u8 prev_rs; /* previous rate used in rs algo */ u8 next_rs; /* next rate used in rs algo */ }; @@ -83,35 +85,52 @@ enum { #define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) -/* uCode API values for OFDM high-throughput (HT) bit rates */ +/* uCode API values for HT/VHT bit rates */ enum { - IWL_RATE_SISO_6M_PLCP = 0, - IWL_RATE_SISO_12M_PLCP = 1, - IWL_RATE_SISO_18M_PLCP = 2, - IWL_RATE_SISO_24M_PLCP = 3, - IWL_RATE_SISO_36M_PLCP = 4, - IWL_RATE_SISO_48M_PLCP = 5, - IWL_RATE_SISO_54M_PLCP = 6, - IWL_RATE_SISO_60M_PLCP = 7, - IWL_RATE_MIMO2_6M_PLCP = 0x8, - IWL_RATE_MIMO2_12M_PLCP = 0x9, - IWL_RATE_MIMO2_18M_PLCP = 0xa, - IWL_RATE_MIMO2_24M_PLCP = 0xb, - IWL_RATE_MIMO2_36M_PLCP = 0xc, - IWL_RATE_MIMO2_48M_PLCP = 0xd, - IWL_RATE_MIMO2_54M_PLCP = 0xe, - IWL_RATE_MIMO2_60M_PLCP = 0xf, - IWL_RATE_MIMO3_6M_PLCP = 0x10, - IWL_RATE_MIMO3_12M_PLCP = 0x11, - IWL_RATE_MIMO3_18M_PLCP = 0x12, - IWL_RATE_MIMO3_24M_PLCP = 0x13, - IWL_RATE_MIMO3_36M_PLCP = 0x14, - IWL_RATE_MIMO3_48M_PLCP = 0x15, - IWL_RATE_MIMO3_54M_PLCP = 0x16, - IWL_RATE_MIMO3_60M_PLCP = 0x17, - IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, - IWL_RATE_MIMO3_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, + IWL_RATE_HT_SISO_MCS_0_PLCP = 0, + IWL_RATE_HT_SISO_MCS_1_PLCP = 1, + IWL_RATE_HT_SISO_MCS_2_PLCP = 2, + IWL_RATE_HT_SISO_MCS_3_PLCP = 3, + IWL_RATE_HT_SISO_MCS_4_PLCP = 4, + IWL_RATE_HT_SISO_MCS_5_PLCP = 5, + IWL_RATE_HT_SISO_MCS_6_PLCP = 6, + IWL_RATE_HT_SISO_MCS_7_PLCP = 7, + IWL_RATE_HT_MIMO2_MCS_0_PLCP = 0x8, + IWL_RATE_HT_MIMO2_MCS_1_PLCP = 0x9, + IWL_RATE_HT_MIMO2_MCS_2_PLCP = 0xA, + IWL_RATE_HT_MIMO2_MCS_3_PLCP = 0xB, + IWL_RATE_HT_MIMO2_MCS_4_PLCP = 0xC, + IWL_RATE_HT_MIMO2_MCS_5_PLCP = 0xD, + IWL_RATE_HT_MIMO2_MCS_6_PLCP = 0xE, + IWL_RATE_HT_MIMO2_MCS_7_PLCP = 0xF, + IWL_RATE_VHT_SISO_MCS_0_PLCP = 0, + IWL_RATE_VHT_SISO_MCS_1_PLCP = 1, + IWL_RATE_VHT_SISO_MCS_2_PLCP = 2, + IWL_RATE_VHT_SISO_MCS_3_PLCP = 3, + IWL_RATE_VHT_SISO_MCS_4_PLCP = 4, + IWL_RATE_VHT_SISO_MCS_5_PLCP = 5, + IWL_RATE_VHT_SISO_MCS_6_PLCP = 6, + IWL_RATE_VHT_SISO_MCS_7_PLCP = 7, + IWL_RATE_VHT_SISO_MCS_8_PLCP = 8, + IWL_RATE_VHT_SISO_MCS_9_PLCP = 9, + IWL_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10, + IWL_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11, + IWL_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12, + IWL_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13, + IWL_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14, + IWL_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15, + IWL_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16, + IWL_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17, + IWL_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18, + IWL_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19, + IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_VHT_SISO_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_VHT_MIMO2_MCS_INV_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_SISO_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_SISO_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_MIMO2_MCS_8_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, + IWL_RATE_HT_MIMO2_MCS_9_PLCP = IWL_RATE_HT_SISO_MCS_INV_PLCP, }; #define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) @@ -139,25 +158,33 @@ enum { #define IWL_RATE_DECREASE_TH 1920 /* 15% */ /* possible actions when in legacy mode */ -#define IWL_LEGACY_SWITCH_ANTENNA1 0 -#define IWL_LEGACY_SWITCH_ANTENNA2 1 -#define IWL_LEGACY_SWITCH_SISO 2 -#define IWL_LEGACY_SWITCH_MIMO2 3 +enum { + IWL_LEGACY_SWITCH_ANTENNA, + IWL_LEGACY_SWITCH_SISO, + IWL_LEGACY_SWITCH_MIMO2, + IWL_LEGACY_FIRST_ACTION = IWL_LEGACY_SWITCH_ANTENNA, + IWL_LEGACY_LAST_ACTION = IWL_LEGACY_SWITCH_MIMO2, +}; /* possible actions when in siso mode */ -#define IWL_SISO_SWITCH_ANTENNA1 0 -#define IWL_SISO_SWITCH_ANTENNA2 1 -#define IWL_SISO_SWITCH_MIMO2 2 -#define IWL_SISO_SWITCH_GI 3 +enum { + IWL_SISO_SWITCH_ANTENNA, + IWL_SISO_SWITCH_MIMO2, + IWL_SISO_SWITCH_GI, + IWL_SISO_FIRST_ACTION = IWL_SISO_SWITCH_ANTENNA, + IWL_SISO_LAST_ACTION = IWL_SISO_SWITCH_GI, +}; /* possible actions when in mimo mode */ -#define IWL_MIMO2_SWITCH_ANTENNA1 0 -#define IWL_MIMO2_SWITCH_ANTENNA2 1 -#define IWL_MIMO2_SWITCH_SISO_A 2 -#define IWL_MIMO2_SWITCH_SISO_B 3 -#define IWL_MIMO2_SWITCH_GI 4 +enum { + IWL_MIMO2_SWITCH_SISO_A, + IWL_MIMO2_SWITCH_SISO_B, + IWL_MIMO2_SWITCH_GI, + IWL_MIMO2_FIRST_ACTION = IWL_MIMO2_SWITCH_SISO_A, + IWL_MIMO2_LAST_ACTION = IWL_MIMO2_SWITCH_GI, +}; -#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_GI +#define IWL_MAX_SEARCH IWL_MIMO2_LAST_ACTION #define IWL_ACTION_LIMIT 3 /* # possible actions */ @@ -188,20 +215,31 @@ enum { enum iwl_table_type { LQ_NONE, - LQ_G, /* legacy types */ - LQ_A, - LQ_SISO, /* high-throughput types */ - LQ_MIMO2, + LQ_LEGACY_G, /* legacy types */ + LQ_LEGACY_A, + LQ_HT_SISO, /* HT types */ + LQ_HT_MIMO2, + LQ_VHT_SISO, /* VHT types */ + LQ_VHT_MIMO2, LQ_MAX, }; -#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) -#define is_siso(tbl) ((tbl) == LQ_SISO) -#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) -#define is_mimo(tbl) is_mimo2(tbl) -#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) -#define is_a_band(tbl) ((tbl) == LQ_A) -#define is_g_and(tbl) ((tbl) == LQ_G) +#define is_legacy(tbl) (((tbl) == LQ_LEGACY_G) || ((tbl) == LQ_LEGACY_A)) +#define is_ht_siso(tbl) ((tbl) == LQ_HT_SISO) +#define is_ht_mimo2(tbl) ((tbl) == LQ_HT_MIMO2) +#define is_vht_siso(tbl) ((tbl) == LQ_VHT_SISO) +#define is_vht_mimo2(tbl) ((tbl) == LQ_VHT_MIMO2) +#define is_siso(tbl) (is_ht_siso(tbl) || is_vht_siso(tbl)) +#define is_mimo2(tbl) (is_ht_mimo2(tbl) || is_vht_mimo2(tbl)) +#define is_mimo(tbl) (is_mimo2(tbl)) +#define is_ht(tbl) (is_ht_siso(tbl) || is_ht_mimo2(tbl)) +#define is_vht(tbl) (is_vht_siso(tbl) || is_vht_mimo2(tbl)) +#define is_a_band(tbl) ((tbl) == LQ_LEGACY_A) +#define is_g_band(tbl) ((tbl) == LQ_LEGACY_G) + +#define is_ht20(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_20) +#define is_ht40(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_40) +#define is_ht80(tbl) (tbl->bw == RATE_MCS_CHAN_WIDTH_80) #define IWL_MAX_MCS_DISPLAY_SIZE 12 @@ -232,7 +270,7 @@ struct iwl_scale_tbl_info { enum iwl_table_type lq_type; u8 ant_type; u8 is_SGI; /* 1 = short guard interval */ - u8 is_ht40; /* 1 = 40 MHz channel width */ + u32 bw; /* channel bandwidth; RATE_MCS_CHAN_WIDTH_XX */ u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ u8 max_search; /* maximun number of tables we can search */ s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ @@ -262,7 +300,7 @@ struct iwl_lq_sta { u64 flush_timer; /* time staying in mode before new search */ u8 action_counter; /* # mode-switch actions tried */ - u8 is_green; + bool is_vht; enum ieee80211_band band; /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ @@ -314,9 +352,8 @@ static inline u8 num_of_ant(u8 mask) } /* Initialize station's rate scaling information after adding station */ -extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - enum ieee80211_band band); +void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, + enum ieee80211_band band); /** * iwl_rate_control_register - Register the rate control algorithm callbacks @@ -328,7 +365,7 @@ extern void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, * ieee80211_register_hw * */ -extern int iwl_mvm_rate_control_register(void); +int iwl_mvm_rate_control_register(void); /** * iwl_rate_control_unregister - Unregister the rate control callbacks @@ -336,7 +373,7 @@ extern int iwl_mvm_rate_control_register(void); * This should be called after calling ieee80211_unregister_hw, but before * the driver is unloaded. */ -extern void iwl_mvm_rate_control_unregister(void); +void iwl_mvm_rate_control_unregister(void); struct iwl_mvm_sta; diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 2a8cb5a60535d161f410116c32967cbd9a983987..3a1f3982109d1dbc02bccba42588545e000b8271 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -300,10 +300,14 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, return 0; } + /* + * Keep packets with CRC errors (and with overrun) for monitor mode + * (otherwise the firmware discards them) but mark them as bad. + */ if (!(rx_pkt_status & RX_MPDU_RES_STATUS_CRC_OK) || !(rx_pkt_status & RX_MPDU_RES_STATUS_OVERRUN_OK)) { IWL_DEBUG_RX(mvm, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); - return 0; + rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; } /* This will be used in several places later */ @@ -422,6 +426,27 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac, mvmvif->bf_data.ave_beacon_signal = sig; + /* BT Coex */ + if (mvmvif->bf_data.bt_coex_min_thold != + mvmvif->bf_data.bt_coex_max_thold) { + last_event = mvmvif->bf_data.last_bt_coex_event; + if (sig > mvmvif->bf_data.bt_coex_max_thold && + (last_event <= mvmvif->bf_data.bt_coex_min_thold || + last_event == 0)) { + mvmvif->bf_data.last_bt_coex_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator bt coex high %d\n", + sig); + iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_HIGH); + } else if (sig < mvmvif->bf_data.bt_coex_min_thold && + (last_event >= mvmvif->bf_data.bt_coex_max_thold || + last_event == 0)) { + mvmvif->bf_data.last_bt_coex_event = sig; + IWL_DEBUG_RX(mvm, "cqm_iterator bt coex low %d\n", + sig); + iwl_mvm_bt_rssi_event(mvm, vif, RSSI_EVENT_LOW); + } + } + if (!(vif->driver_flags & IEEE80211_VIF_SUPPORTS_CQM_RSSI)) return; diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 621fb71f282a88e9a285bb3d4b2b26a1b2fef275..dff7592e1ff84e5c3f6a05d673e2a39514cbb870 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -74,8 +74,12 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) { u16 rx_chain; - u8 rx_ant = iwl_fw_valid_rx_ant(mvm->fw); + u8 rx_ant; + if (mvm->scan_rx_ant != ANT_NONE) + rx_ant = mvm->scan_rx_ant; + else + rx_ant = iwl_fw_valid_rx_ant(mvm->fw); rx_chain = rx_ant << PHY_RX_CHAIN_VALID_POS; rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; rx_chain |= rx_ant << PHY_RX_CHAIN_FORCE_SEL_POS; @@ -93,10 +97,10 @@ static inline __le32 iwl_mvm_scan_max_out_time(struct ieee80211_vif *vif) static inline __le32 iwl_mvm_scan_suspend_time(struct ieee80211_vif *vif) { - if (vif->bss_conf.assoc) - return cpu_to_le32(vif->bss_conf.beacon_int); - else + if (!vif->bss_conf.assoc) return 0; + + return cpu_to_le32(ieee80211_tu_to_usec(vif->bss_conf.beacon_int)); } static inline __le32 @@ -133,11 +137,12 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, * request. */ static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd, - struct cfg80211_scan_request *req) + struct cfg80211_scan_request *req, + int first) { int fw_idx, req_idx; - for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx > 0; + for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx >= first; req_idx--, fw_idx++) { cmd->direct_scan[fw_idx].id = WLAN_EID_SSID; cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len; @@ -153,9 +158,9 @@ static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd, * just to notify that this scan is active and not passive. * In order to notify the FW of the number of SSIDs we wish to scan (including * the zero-length one), we need to set the corresponding bits in chan->type, - * one for each SSID, and set the active bit (first). The first SSID is already - * included in the probe template, so we need to set only req->n_ssids - 1 bits - * in addition to the first bit. + * one for each SSID, and set the active bit (first). If the first SSID is + * already included in the probe template, so we need to set only + * req->n_ssids - 1 bits in addition to the first bit. */ static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids) { @@ -170,7 +175,8 @@ static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band) } static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd, - struct cfg80211_scan_request *req) + struct cfg80211_scan_request *req, + bool basic_ssid) { u16 passive_dwell = iwl_mvm_get_passive_dwell(req->channels[0]->band); u16 active_dwell = iwl_mvm_get_active_dwell(req->channels[0]->band, @@ -178,10 +184,14 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd, struct iwl_scan_channel *chan = (struct iwl_scan_channel *) (cmd->data + le16_to_cpu(cmd->tx_cmd.len)); int i; + int type = BIT(req->n_ssids) - 1; + + if (!basic_ssid) + type |= BIT(req->n_ssids); for (i = 0; i < cmd->channel_count; i++) { chan->channel = cpu_to_le16(req->channels[i]->hw_value); - chan->type = cpu_to_le32(BIT(req->n_ssids) - 1); + chan->type = cpu_to_le32(type); if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) chan->type &= cpu_to_le32(~SCAN_CHANNEL_TYPE_ACTIVE); chan->active_dwell = cpu_to_le16(active_dwell); @@ -268,6 +278,8 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, u32 status; int ssid_len = 0; u8 *ssid = NULL; + bool basic_ssid = !(mvm->fw->ucode_capa.flags & + IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID); lockdep_assert_held(&mvm->mutex); BUG_ON(mvm->scan_cmd == NULL); @@ -302,14 +314,16 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, if (req->n_ssids > 0) { cmd->passive2active = cpu_to_le16(1); cmd->scan_flags |= SCAN_FLAGS_PASSIVE2ACTIVE; - ssid = req->ssids[0].ssid; - ssid_len = req->ssids[0].ssid_len; + if (basic_ssid) { + ssid = req->ssids[0].ssid; + ssid_len = req->ssids[0].ssid_len; + } } else { cmd->passive2active = 0; cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE; } - iwl_mvm_scan_fill_ssids(cmd, req); + iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0); cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL); cmd->tx_cmd.sta_id = mvm->aux_sta.sta_id; @@ -326,7 +340,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm, req->ie, req->ie_len, mvm->fw->ucode_capa.max_probe_length)); - iwl_mvm_scan_fill_channels(cmd, req); + iwl_mvm_scan_fill_channels(cmd, req, basic_ssid); cmd->len = cpu_to_le16(sizeof(struct iwl_scan_cmd) + le16_to_cpu(cmd->tx_cmd.len) + @@ -377,6 +391,21 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, return 0; } +int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_sched_scan_results *notif = (void *)pkt->data; + + if (notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN) { + IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n"); + ieee80211_sched_scan_results(mvm->hw); + } + + return 0; +} + static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait, struct iwl_rx_packet *pkt, void *data) { @@ -447,3 +476,406 @@ void iwl_mvm_cancel_scan(struct iwl_mvm *mvm) out_remove_notif: iwl_remove_notification(&mvm->notif_wait, &wait_scan_abort); } + +int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data; + + IWL_DEBUG_SCAN(mvm, "Scheduled scan completed, status %s\n", + scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? + "completed" : "aborted"); + + mvm->scan_status = IWL_MVM_SCAN_NONE; + ieee80211_sched_scan_stopped(mvm->hw); + + return 0; +} + +static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_sched_scan_ies *ies, + enum ieee80211_band band, + struct iwl_tx_cmd *cmd, + u8 *data) +{ + u16 cmd_len; + + cmd->tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL); + cmd->life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); + cmd->sta_id = mvm->aux_sta.sta_id; + + cmd->rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm, band, false); + + cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data, + vif->addr, + 1, NULL, 0, + ies->ie[band], ies->len[band], + SCAN_OFFLOAD_PROBE_REQ_SIZE); + cmd->len = cpu_to_le16(cmd_len); +} + +static void iwl_build_scan_cmd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct iwl_scan_offload_cmd *scan) +{ + scan->channel_count = + mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels + + mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; + scan->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME); + scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH); + scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT; + scan->rx_chain = iwl_mvm_scan_rx_chain(mvm); + scan->max_out_time = cpu_to_le32(200 * 1024); + scan->suspend_time = iwl_mvm_scan_suspend_time(vif); + scan->filter_flags |= cpu_to_le32(MAC_FILTER_ACCEPT_GRP | + MAC_FILTER_IN_BEACON); + scan->scan_type = cpu_to_le32(SCAN_TYPE_BACKGROUND); + scan->rep_count = cpu_to_le32(1); +} + +static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) +{ + int i; + + for (i = 0; i < PROBE_OPTION_MAX; i++) { + if (!ssid_list[i].len) + break; + if (ssid_list[i].len == ssid_len && + !memcmp(ssid_list->ssid, ssid, ssid_len)) + return i; + } + return -1; +} + +static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req, + struct iwl_scan_offload_cmd *scan, + u32 *ssid_bitmap) +{ + int i, j; + int index; + + /* + * copy SSIDs from match list. + * iwl_config_sched_scan_profiles() uses the order of these ssids to + * config match list. + */ + for (i = 0; i < req->n_match_sets && i < PROBE_OPTION_MAX; i++) { + scan->direct_scan[i].id = WLAN_EID_SSID; + scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len; + memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid, + scan->direct_scan[i].len); + } + + /* add SSIDs from scan SSID list */ + *ssid_bitmap = 0; + for (j = 0; j < req->n_ssids && i < PROBE_OPTION_MAX; j++) { + index = iwl_ssid_exist(req->ssids[j].ssid, + req->ssids[j].ssid_len, + scan->direct_scan); + if (index < 0) { + if (!req->ssids[j].ssid_len) + continue; + scan->direct_scan[i].id = WLAN_EID_SSID; + scan->direct_scan[i].len = req->ssids[j].ssid_len; + memcpy(scan->direct_scan[i].ssid, req->ssids[j].ssid, + scan->direct_scan[i].len); + *ssid_bitmap |= BIT(i + 1); + i++; + } else { + *ssid_bitmap |= BIT(index + 1); + } + } +} + +static void iwl_build_channel_cfg(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req, + struct iwl_scan_channel_cfg *channels, + enum ieee80211_band band, + int *head, int *tail, + u32 ssid_bitmap) +{ + struct ieee80211_supported_band *s_band; + int n_probes = req->n_ssids; + int n_channels = req->n_channels; + u8 active_dwell, passive_dwell; + int i, j, index = 0; + bool partial; + + /* + * We have to configure all supported channels, even if we don't want to + * scan on them, but we have to send channels in the order that we want + * to scan. So add requested channels to head of the list and others to + * the end. + */ + active_dwell = iwl_mvm_get_active_dwell(band, n_probes); + passive_dwell = iwl_mvm_get_passive_dwell(band); + s_band = &mvm->nvm_data->bands[band]; + + for (i = 0; i < s_band->n_channels && *head <= *tail; i++) { + partial = false; + for (j = 0; j < n_channels; j++) + if (s_band->channels[i].center_freq == + req->channels[j]->center_freq) { + index = *head; + (*head)++; + /* + * Channels that came with the request will be + * in partial scan . + */ + partial = true; + break; + } + if (!partial) { + index = *tail; + (*tail)--; + } + channels->channel_number[index] = + cpu_to_le16(ieee80211_frequency_to_channel( + s_band->channels[i].center_freq)); + channels->dwell_time[index][0] = active_dwell; + channels->dwell_time[index][1] = passive_dwell; + + channels->iter_count[index] = cpu_to_le16(1); + channels->iter_interval[index] = 0; + + if (!(s_band->channels[i].flags & IEEE80211_CHAN_PASSIVE_SCAN)) + channels->type[index] |= + cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE); + + channels->type[index] |= + cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL); + if (partial) + channels->type[index] |= + cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL); + + if (s_band->channels[i].flags & IEEE80211_CHAN_NO_HT40) + channels->type[index] |= + cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW); + + /* scan for all SSIDs from req->ssids */ + channels->type[index] |= cpu_to_le32(ssid_bitmap); + } +} + +int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + int supported_bands = 0; + int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels; + int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels; + int head = 0; + int tail = band_2ghz + band_5ghz; + u32 ssid_bitmap; + int cmd_len; + int ret; + + struct iwl_scan_offload_cfg *scan_cfg; + struct iwl_host_cmd cmd = { + .id = SCAN_OFFLOAD_CONFIG_CMD, + .flags = CMD_SYNC, + }; + + lockdep_assert_held(&mvm->mutex); + + if (band_2ghz) + supported_bands++; + if (band_5ghz) + supported_bands++; + + cmd_len = sizeof(struct iwl_scan_offload_cfg) + + supported_bands * SCAN_OFFLOAD_PROBE_REQ_SIZE; + + scan_cfg = kzalloc(cmd_len, GFP_KERNEL); + if (!scan_cfg) + return -ENOMEM; + + iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd); + scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len); + + iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap); + /* build tx frames for supported bands */ + if (band_2ghz) { + iwl_scan_offload_build_tx_cmd(mvm, vif, ies, + IEEE80211_BAND_2GHZ, + &scan_cfg->scan_cmd.tx_cmd[0], + scan_cfg->data); + iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, + IEEE80211_BAND_2GHZ, &head, &tail, + ssid_bitmap); + } + if (band_5ghz) { + iwl_scan_offload_build_tx_cmd(mvm, vif, ies, + IEEE80211_BAND_5GHZ, + &scan_cfg->scan_cmd.tx_cmd[1], + scan_cfg->data + + SCAN_OFFLOAD_PROBE_REQ_SIZE); + iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg, + IEEE80211_BAND_5GHZ, &head, &tail, + ssid_bitmap); + } + + cmd.data[0] = scan_cfg; + cmd.len[0] = cmd_len; + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + + IWL_DEBUG_SCAN(mvm, "Sending scheduled scan config\n"); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + kfree(scan_cfg); + return ret; +} + +int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req) +{ + struct iwl_scan_offload_profile *profile; + struct iwl_scan_offload_profile_cfg *profile_cfg; + struct iwl_scan_offload_blacklist *blacklist; + struct iwl_host_cmd cmd = { + .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, + .flags = CMD_SYNC, + .len[1] = sizeof(*profile_cfg), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .dataflags[1] = IWL_HCMD_DFL_NOCOPY, + }; + int blacklist_len; + int i; + int ret; + + if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES)) + return -EIO; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_SHORT_BL) + blacklist_len = IWL_SCAN_SHORT_BLACKLIST_LEN; + else + blacklist_len = IWL_SCAN_MAX_BLACKLIST_LEN; + + blacklist = kzalloc(sizeof(*blacklist) * blacklist_len, GFP_KERNEL); + if (!blacklist) + return -ENOMEM; + + profile_cfg = kzalloc(sizeof(*profile_cfg), GFP_KERNEL); + if (!profile_cfg) { + ret = -ENOMEM; + goto free_blacklist; + } + + cmd.data[0] = blacklist; + cmd.len[0] = sizeof(*blacklist) * blacklist_len; + cmd.data[1] = profile_cfg; + + /* No blacklist configuration */ + + profile_cfg->num_profiles = req->n_match_sets; + profile_cfg->active_clients = SCAN_CLIENT_SCHED_SCAN; + profile_cfg->pass_match = SCAN_CLIENT_SCHED_SCAN; + profile_cfg->match_notify = SCAN_CLIENT_SCHED_SCAN; + + for (i = 0; i < req->n_match_sets; i++) { + profile = &profile_cfg->profiles[i]; + profile->ssid_index = i; + /* Support any cipher and auth algorithm */ + profile->unicast_cipher = 0xff; + profile->auth_alg = 0xff; + profile->network_type = IWL_NETWORK_TYPE_ANY; + profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY; + profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN; + } + + IWL_DEBUG_SCAN(mvm, "Sending scheduled scan profile config\n"); + + ret = iwl_mvm_send_cmd(mvm, &cmd); + kfree(profile_cfg); +free_blacklist: + kfree(blacklist); + + return ret; +} + +int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm, + struct cfg80211_sched_scan_request *req) +{ + struct iwl_scan_offload_req scan_req = { + .watchdog = IWL_SCHED_SCAN_WATCHDOG, + + .schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS, + .schedule_line[0].delay = req->interval / 1000, + .schedule_line[0].full_scan_mul = 1, + + .schedule_line[1].iterations = 0xff, + .schedule_line[1].delay = req->interval / 1000, + .schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER, + }; + + if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { + IWL_DEBUG_SCAN(mvm, + "Sending scheduled scan with filtering, filter len %d\n", + req->n_match_sets); + scan_req.flags |= + cpu_to_le16(IWL_SCAN_OFFLOAD_FLAG_FILTER_SSID); + } else { + IWL_DEBUG_SCAN(mvm, + "Sending Scheduled scan without filtering\n"); + } + + return iwl_mvm_send_cmd_pdu(mvm, SCAN_OFFLOAD_REQUEST_CMD, CMD_SYNC, + sizeof(scan_req), &scan_req); +} + +static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = SCAN_OFFLOAD_ABORT_CMD, + .flags = CMD_SYNC, + }; + u32 status; + + /* Exit instantly with error when device is not ready + * to receive scan abort command or it does not perform + * scheduled scan currently */ + if (mvm->scan_status != IWL_MVM_SCAN_SCHED) + return -EIO; + + ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status); + if (ret) + return ret; + + if (status != CAN_ABORT_STATUS) { + /* + * The scan abort will return 1 for success or + * 2 for "failure". A failure condition can be + * due to simply not being in an active scan which + * can occur if we send the scan abort before the + * microcode has notified us that a scan is completed. + */ + IWL_DEBUG_SCAN(mvm, "SCAN OFFLOAD ABORT ret %d.\n", status); + ret = -EIO; + } + + return ret; +} + +void iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm) +{ + int ret; + + lockdep_assert_held(&mvm->mutex); + + if (mvm->scan_status != IWL_MVM_SCAN_SCHED) { + IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n"); + return; + } + + ret = iwl_mvm_send_sched_scan_abort(mvm); + if (ret) + IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret); + else + IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n"); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 44add291531bb505a6d388fde3fdf961b7cc14e2..329952363a54f3b2c0913d018c401e9c7004fcfc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -66,6 +66,115 @@ #include "sta.h" #include "rs.h" +static void iwl_mvm_add_sta_cmd_v6_to_v5(struct iwl_mvm_add_sta_cmd_v6 *cmd_v6, + struct iwl_mvm_add_sta_cmd_v5 *cmd_v5) +{ + memset(cmd_v5, 0, sizeof(*cmd_v5)); + + cmd_v5->add_modify = cmd_v6->add_modify; + cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx; + cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color; + memcpy(cmd_v5->addr, cmd_v6->addr, ETH_ALEN); + cmd_v5->sta_id = cmd_v6->sta_id; + cmd_v5->modify_mask = cmd_v6->modify_mask; + cmd_v5->station_flags = cmd_v6->station_flags; + cmd_v5->station_flags_msk = cmd_v6->station_flags_msk; + cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid; + cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid; + cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn; + cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count; + cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags; + cmd_v5->assoc_id = cmd_v6->assoc_id; + cmd_v5->beamform_flags = cmd_v6->beamform_flags; + cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk; +} + +static void +iwl_mvm_add_sta_key_to_add_sta_cmd_v5(struct iwl_mvm_add_sta_key_cmd *key_cmd, + struct iwl_mvm_add_sta_cmd_v5 *sta_cmd, + u32 mac_id_n_color) +{ + memset(sta_cmd, 0, sizeof(*sta_cmd)); + + sta_cmd->sta_id = key_cmd->sta_id; + sta_cmd->add_modify = STA_MODE_MODIFY; + sta_cmd->modify_mask = STA_MODIFY_KEY; + sta_cmd->mac_id_n_color = cpu_to_le32(mac_id_n_color); + + sta_cmd->key.key_offset = key_cmd->key_offset; + sta_cmd->key.key_flags = key_cmd->key_flags; + memcpy(sta_cmd->key.key, key_cmd->key, sizeof(sta_cmd->key.key)); + sta_cmd->key.tkip_rx_tsc_byte2 = key_cmd->tkip_rx_tsc_byte2; + memcpy(sta_cmd->key.tkip_rx_ttak, key_cmd->tkip_rx_ttak, + sizeof(sta_cmd->key.tkip_rx_ttak)); +} + +static int iwl_mvm_send_add_sta_cmd_status(struct iwl_mvm *mvm, + struct iwl_mvm_add_sta_cmd_v6 *cmd, + int *status) +{ + struct iwl_mvm_add_sta_cmd_v5 cmd_v5; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD) + return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(*cmd), + cmd, status); + + iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5); + + return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd_v5), + &cmd_v5, status); +} + +static int iwl_mvm_send_add_sta_cmd(struct iwl_mvm *mvm, u32 flags, + struct iwl_mvm_add_sta_cmd_v6 *cmd) +{ + struct iwl_mvm_add_sta_cmd_v5 cmd_v5; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD) + return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, + sizeof(*cmd), cmd); + + iwl_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5); + + return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(cmd_v5), + &cmd_v5); +} + +static int +iwl_mvm_send_add_sta_key_cmd_status(struct iwl_mvm *mvm, + struct iwl_mvm_add_sta_key_cmd *cmd, + u32 mac_id_n_color, + int *status) +{ + struct iwl_mvm_add_sta_cmd_v5 sta_cmd; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD) + return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, + sizeof(*cmd), cmd, status); + + iwl_mvm_add_sta_key_to_add_sta_cmd_v5(cmd, &sta_cmd, mac_id_n_color); + + return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(sta_cmd), + &sta_cmd, status); +} + +static int iwl_mvm_send_add_sta_key_cmd(struct iwl_mvm *mvm, + u32 flags, + struct iwl_mvm_add_sta_key_cmd *cmd, + u32 mac_id_n_color) +{ + struct iwl_mvm_add_sta_cmd_v5 sta_cmd; + + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_STA_KEY_CMD) + return iwl_mvm_send_cmd_pdu(mvm, ADD_STA_KEY, flags, + sizeof(*cmd), cmd); + + iwl_mvm_add_sta_key_to_add_sta_cmd_v5(cmd, &sta_cmd, mac_id_n_color); + + return iwl_mvm_send_cmd_pdu(mvm, ADD_STA, flags, sizeof(sta_cmd), + &sta_cmd); +} + static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm) { int sta_id; @@ -87,7 +196,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, bool update) { struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; - struct iwl_mvm_add_sta_cmd add_sta_cmd; + struct iwl_mvm_add_sta_cmd_v6 add_sta_cmd; int ret; u32 status; u32 agg_size = 0, mpdu_dens = 0; @@ -175,8 +284,7 @@ int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, cpu_to_le32(mpdu_dens << STA_FLG_AGG_MPDU_DENS_SHIFT); status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(add_sta_cmd), - &add_sta_cmd, &status); + ret = iwl_mvm_send_add_sta_cmd_status(mvm, &add_sta_cmd, &status); if (ret) return ret; @@ -229,8 +337,12 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, if (vif->hw_queue[i] != IEEE80211_INVAL_HW_QUEUE) mvm_sta->tfd_queue_msk |= BIT(vif->hw_queue[i]); - /* for HW restart - need to reset the seq_number etc... */ - memset(mvm_sta->tid_data, 0, sizeof(mvm_sta->tid_data)); + /* for HW restart - reset everything but the sequence number */ + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + u16 seq = mvm_sta->tid_data[i].seq_number; + memset(&mvm_sta->tid_data[i], 0, sizeof(mvm_sta->tid_data[i])); + mvm_sta->tid_data[i].seq_number = seq; + } ret = iwl_mvm_sta_send_to_fw(mvm, sta, false); if (ret) @@ -256,7 +368,7 @@ int iwl_mvm_update_sta(struct iwl_mvm *mvm, int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool drain) { - struct iwl_mvm_add_sta_cmd cmd = {}; + struct iwl_mvm_add_sta_cmd_v6 cmd = {}; int ret; u32 status; @@ -269,8 +381,7 @@ int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, cmd.station_flags_msk = cpu_to_le32(STA_FLG_DRAIN_FLOW); status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); + ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status); if (ret) return ret; @@ -469,13 +580,13 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, const u8 *addr, u16 mac_id, u16 color) { - struct iwl_mvm_add_sta_cmd cmd; + struct iwl_mvm_add_sta_cmd_v6 cmd; int ret; u32 status; lockdep_assert_held(&mvm->mutex); - memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd)); + memset(&cmd, 0, sizeof(struct iwl_mvm_add_sta_cmd_v6)); cmd.sta_id = sta->sta_id; cmd.mac_id_n_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mac_id, color)); @@ -485,8 +596,7 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, if (addr) memcpy(cmd.addr, addr, ETH_ALEN); - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); + ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status); if (ret) return ret; @@ -534,10 +644,14 @@ int iwl_mvm_send_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mvm_int_sta *bsta) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - static const u8 baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + static const u8 _baddr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + static const u8 *baddr = _baddr; lockdep_assert_held(&mvm->mutex); + if (vif->type == NL80211_IFTYPE_ADHOC) + baddr = vif->bss_conf.bssid; + if (WARN_ON_ONCE(bsta->sta_id == IWL_MVM_STATION_COUNT)) return -ENOSPC; @@ -614,7 +728,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int tid, u16 ssn, bool start) { struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; - struct iwl_mvm_add_sta_cmd cmd = {}; + struct iwl_mvm_add_sta_cmd_v6 cmd = {}; int ret; u32 status; @@ -638,8 +752,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, STA_MODIFY_REMOVE_BA_TID; status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); + ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status); if (ret) return ret; @@ -674,7 +787,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int tid, u8 queue, bool start) { struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; - struct iwl_mvm_add_sta_cmd cmd = {}; + struct iwl_mvm_add_sta_cmd_v6 cmd = {}; int ret; u32 status; @@ -696,8 +809,7 @@ static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, cmd.tid_disable_tx = cpu_to_le16(mvm_sta->tid_disable_agg); status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); + ret = iwl_mvm_send_add_sta_cmd_status(mvm, &cmd, &status); if (ret) return ret; @@ -743,13 +855,13 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); - for (txq_id = IWL_MVM_FIRST_AGG_QUEUE; - txq_id <= IWL_MVM_LAST_AGG_QUEUE; txq_id++) + for (txq_id = mvm->first_agg_queue; + txq_id <= mvm->last_agg_queue; txq_id++) if (mvm->queue_to_mac80211[txq_id] == IWL_INVALID_MAC80211_QUEUE) break; - if (txq_id > IWL_MVM_LAST_AGG_QUEUE) { + if (txq_id > mvm->last_agg_queue) { IWL_ERR(mvm, "Failed to allocate agg queue\n"); return -EIO; } @@ -987,10 +1099,11 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, u32 cmd_flags) { __le16 key_flags; - struct iwl_mvm_add_sta_cmd cmd = {}; + struct iwl_mvm_add_sta_key_cmd cmd = {}; int ret, status; u16 keyidx; int i; + u32 mac_id_n_color = mvm_sta->mac_id_n_color; keyidx = (keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & STA_KEY_FLG_KEYID_MSK; @@ -1000,14 +1113,14 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_TKIP: key_flags |= cpu_to_le16(STA_KEY_FLG_TKIP); - cmd.key.tkip_rx_tsc_byte2 = tkip_iv32; + cmd.tkip_rx_tsc_byte2 = tkip_iv32; for (i = 0; i < 5; i++) - cmd.key.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); - memcpy(cmd.key.key, keyconf->key, keyconf->keylen); + cmd.tkip_rx_ttak[i] = cpu_to_le16(tkip_p1k[i]); + memcpy(cmd.key, keyconf->key, keyconf->keylen); break; case WLAN_CIPHER_SUITE_CCMP: key_flags |= cpu_to_le16(STA_KEY_FLG_CCM); - memcpy(cmd.key.key, keyconf->key, keyconf->keylen); + memcpy(cmd.key, keyconf->key, keyconf->keylen); break; default: WARN_ON(1); @@ -1017,20 +1130,18 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); - cmd.key.key_offset = keyconf->hw_key_idx; - cmd.key.key_flags = key_flags; - cmd.add_modify = STA_MODE_MODIFY; - cmd.modify_mask = STA_MODIFY_KEY; + cmd.key_offset = keyconf->hw_key_idx; + cmd.key_flags = key_flags; cmd.sta_id = sta_id; status = ADD_STA_SUCCESS; if (cmd_flags == CMD_SYNC) - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); + ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd, + mac_id_n_color, + &status); else - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, - sizeof(cmd), &cmd); + ret = iwl_mvm_send_add_sta_key_cmd(mvm, CMD_ASYNC, &cmd, + mac_id_n_color); switch (status) { case ADD_STA_SUCCESS: @@ -1197,7 +1308,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, struct ieee80211_key_conf *keyconf) { struct iwl_mvm_sta *mvm_sta; - struct iwl_mvm_add_sta_cmd cmd = {}; + struct iwl_mvm_add_sta_key_cmd cmd = {}; __le16 key_flags; int ret, status; u8 sta_id; @@ -1252,17 +1363,14 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - cmd.mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color); - cmd.key.key_flags = key_flags; - cmd.key.key_offset = keyconf->hw_key_idx; + cmd.key_flags = key_flags; + cmd.key_offset = keyconf->hw_key_idx; cmd.sta_id = sta_id; - cmd.modify_mask = STA_MODIFY_KEY; - cmd.add_modify = STA_MODE_MODIFY; - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, sizeof(cmd), - &cmd, &status); + ret = iwl_mvm_send_add_sta_key_cmd_status(mvm, &cmd, + mvm_sta->mac_id_n_color, + &status); switch (status) { case ADD_STA_SUCCESS: @@ -1309,7 +1417,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; - struct iwl_mvm_add_sta_cmd cmd = { + struct iwl_mvm_add_sta_cmd_v6 cmd = { .add_modify = STA_MODE_MODIFY, .sta_id = mvmsta->sta_id, .station_flags_msk = cpu_to_le32(STA_FLG_PS), @@ -1317,7 +1425,7 @@ void iwl_mvm_sta_modify_ps_wake(struct iwl_mvm *mvm, }; int ret; - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); } @@ -1331,7 +1439,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, (reason == IEEE80211_FRAME_RELEASE_UAPSD) ? STA_SLEEP_STATE_UAPSD : STA_SLEEP_STATE_PS_POLL; struct iwl_mvm_sta *mvmsta = (void *)sta->drv_priv; - struct iwl_mvm_add_sta_cmd cmd = { + struct iwl_mvm_add_sta_cmd_v6 cmd = { .add_modify = STA_MODE_MODIFY, .sta_id = mvmsta->sta_id, .modify_mask = STA_MODIFY_SLEEPING_STA_TX_COUNT, @@ -1346,7 +1454,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, int ret; /* TODO: somehow the fw doesn't seem to take PS_POLL into account */ - ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd); + ret = iwl_mvm_send_add_sta_cmd(mvm, CMD_ASYNC, &cmd); if (ret) IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret); } diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h index 94b265eb32b82bfb7a5f467cef60963f5da8d252..4dfc359a4bdda187cbb1fdcbda8b4cfe112c78fd 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/sta.h @@ -293,10 +293,6 @@ struct iwl_mvm_sta { struct iwl_lq_sta lq_sta; struct ieee80211_vif *vif; -#ifdef CONFIG_PM_SLEEP - u16 last_seq_ctl; -#endif - /* Temporary, until the new TLC will control the Tx protection */ s8 tx_protection; bool tt_tx_protection; diff --git a/drivers/net/wireless/iwlwifi/mvm/testmode.h b/drivers/net/wireless/iwlwifi/mvm/testmode.h new file mode 100644 index 0000000000000000000000000000000000000000..eb74391d91cac9a7469277c3beab830427bbfa0f --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/testmode.h @@ -0,0 +1,95 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2013 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ + +#ifndef __IWL_MVM_TESTMODE_H__ +#define __IWL_MVM_TESTMODE_H__ + +/** + * enum iwl_mvm_testmode_attrs - testmode attributes inside NL80211_ATTR_TESTDATA + * @IWL_MVM_TM_ATTR_UNSPEC: (invalid attribute) + * @IWL_MVM_TM_ATTR_CMD: sub command, see &enum iwl_mvm_testmode_commands (u32) + * @IWL_MVM_TM_ATTR_NOA_DURATION: requested NoA duration (u32) + * @IWL_MVM_TM_ATTR_BEACON_FILTER_STATE: beacon filter state (0 or 1, u32) + */ +enum iwl_mvm_testmode_attrs { + IWL_MVM_TM_ATTR_UNSPEC, + IWL_MVM_TM_ATTR_CMD, + IWL_MVM_TM_ATTR_NOA_DURATION, + IWL_MVM_TM_ATTR_BEACON_FILTER_STATE, + + /* keep last */ + NUM_IWL_MVM_TM_ATTRS, + IWL_MVM_TM_ATTR_MAX = NUM_IWL_MVM_TM_ATTRS - 1, +}; + +/** + * enum iwl_mvm_testmode_commands - MVM testmode commands + * @IWL_MVM_TM_CMD_SET_NOA: set NoA on GO vif for testing + * @IWL_MVM_TM_CMD_SET_BEACON_FILTER: turn beacon filtering off/on + */ +enum iwl_mvm_testmode_commands { + IWL_MVM_TM_CMD_SET_NOA, + IWL_MVM_TM_CMD_SET_BEACON_FILTER, +}; + +#endif /* __IWL_MVM_TESTMODE_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c index 76a3c177e100ab28660ac9433fe5c26edc5a79e8..33cf56fdfc41f86b8bb517a8824740bdbb71a896 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c @@ -387,7 +387,8 @@ static int iwl_mvm_time_event_send_add(struct iwl_mvm *mvm, void iwl_mvm_protect_session(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 duration, u32 min_duration) + u32 duration, u32 min_duration, + u32 max_delay) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data; @@ -426,7 +427,7 @@ void iwl_mvm_protect_session(struct iwl_mvm *mvm, cpu_to_le32(iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG)); time_cmd.max_frags = TE_V2_FRAG_NONE; - time_cmd.max_delay = cpu_to_le32(500); + time_cmd.max_delay = cpu_to_le32(max_delay); /* TODO: why do we need to interval = bi if it is not periodic? */ time_cmd.interval = cpu_to_le32(1); time_cmd.duration = cpu_to_le32(duration); diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h index f86c51065ed3afe1e860f8fb15d50cc1f6eaef9b..d9c8d6cfa2db0c4a44a58b0ec2bc0eaf6fca94cc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/time-event.h +++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h @@ -123,6 +123,7 @@ * @duration: the duration of the session in TU. * @min_duration: will start a new session if the current session will end * in less than min_duration. + * @max_delay: maximum delay before starting the time event (in TU) * * This function can be used to start a session protection which means that the * fw will stay on the channel for %duration_ms milliseconds. This function @@ -133,7 +134,8 @@ */ void iwl_mvm_protect_session(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u32 duration, u32 min_duration); + u32 duration, u32 min_duration, + u32 max_delay); /** * iwl_mvm_stop_session_protection - cancel the session protection. diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index e05440d90319b339f203a2f1ff9658a0d97f615e..43d97c33a75abcbf3833958b0c5cbca5a91e0950 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -417,7 +417,7 @@ int iwl_mvm_tx_skb(struct iwl_mvm *mvm, struct sk_buff *skb, spin_unlock(&mvmsta->lock); - if (txq_id < IWL_MVM_FIRST_AGG_QUEUE) + if (txq_id < mvm->first_agg_queue) atomic_inc(&mvm->pending_frames[mvmsta->sta_id]); return 0; @@ -511,16 +511,10 @@ const char *iwl_mvm_get_tx_fail_reason(u32 status) } #endif /* CONFIG_IWLWIFI_DEBUG */ -/** - * translate ucode response to mac80211 tx status control values - */ -static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags, - struct ieee80211_tx_info *info) +void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, + enum ieee80211_band band, + struct ieee80211_tx_rate *r) { - struct ieee80211_tx_rate *r = &info->status.rates[0]; - - info->status.antenna = - ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); if (rate_n_flags & RATE_HT_MCS_GF_MSK) r->flags |= IEEE80211_TX_RC_GREEN_FIELD; switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { @@ -549,10 +543,23 @@ static void iwl_mvm_hwrate_to_tx_control(u32 rate_n_flags, r->flags |= IEEE80211_TX_RC_VHT_MCS; } else { r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - info->band); + band); } } +/** + * translate ucode response to mac80211 tx status control values + */ +static void iwl_mvm_hwrate_to_tx_status(u32 rate_n_flags, + struct ieee80211_tx_info *info) +{ + struct ieee80211_tx_rate *r = &info->status.rates[0]; + + info->status.antenna = + ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); + iwl_mvm_hwrate_to_tx_rate(rate_n_flags, info->band, r); +} + static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt) { @@ -602,11 +609,11 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, } info->status.rates[0].count = tx_resp->failure_frame + 1; - iwl_mvm_hwrate_to_tx_control(le32_to_cpu(tx_resp->initial_rate), - info); + iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate), + info); /* Single frame failure in an AMPDU queue => send BAR */ - if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE && + if (txq_id >= mvm->first_agg_queue && !(info->flags & IEEE80211_TX_STAT_ACK)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; @@ -619,7 +626,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, ieee80211_tx_status_ni(mvm->hw, skb); } - if (txq_id >= IWL_MVM_FIRST_AGG_QUEUE) { + if (txq_id >= mvm->first_agg_queue) { /* If this is an aggregation queue, we use the ssn since: * ssn = wifi seq_num % 256. * The seq_ctl is the sequence control of the packet to which @@ -668,10 +675,6 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, iwl_mvm_check_ratid_empty(mvm, sta, tid); spin_unlock_bh(&mvmsta->lock); } - -#ifdef CONFIG_PM_SLEEP - mvmsta->last_seq_ctl = seq_ctl; -#endif } else { sta = NULL; mvmsta = NULL; @@ -681,7 +684,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, * If the txq is not an AMPDU queue, there is no chance we freed * several skbs. Check that out... */ - if (txq_id < IWL_MVM_FIRST_AGG_QUEUE && !WARN_ON(skb_freed > 1) && + if (txq_id < mvm->first_agg_queue && !WARN_ON(skb_freed > 1) && atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id])) { if (mvmsta) { /* @@ -777,7 +780,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, u16 sequence = le16_to_cpu(pkt->hdr.sequence); struct ieee80211_sta *sta; - if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < IWL_MVM_FIRST_AGG_QUEUE)) + if (WARN_ON_ONCE(SEQ_TO_QUEUE(sequence) < mvm->first_agg_queue)) return; if (WARN_ON_ONCE(tid == IWL_TID_NON_QOS)) @@ -904,8 +907,8 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, info->flags |= IEEE80211_TX_STAT_AMPDU; info->status.ampdu_ack_len = ba_notif->txed_2_done; info->status.ampdu_len = ba_notif->txed; - iwl_mvm_hwrate_to_tx_control(tid_data->rate_n_flags, - info); + iwl_mvm_hwrate_to_tx_status(tid_data->rate_n_flags, + info); } } diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index a9c3574914348ad3583fd19387a6c48211530f8d..ed69e9b78e82ba325a533624107974ea81af526d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -466,7 +466,7 @@ void iwl_mvm_dump_sram(struct iwl_mvm *mvm) ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; len = img->sec[IWL_UCODE_SECTION_DATA].len; - buf = kzalloc(len, GFP_KERNEL); + buf = kzalloc(len, GFP_ATOMIC); if (!buf) return; diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 26108a1a29fa6d6871cea350aae20820c27d4220..941c0c88f982639b28436a60e52a0fdc6fba8c4b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -268,7 +268,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { #endif /* CONFIG_IWLDVM */ #if IS_ENABLED(CONFIG_IWLMVM) -/* 7000 Series */ +/* 7260 Series */ {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)}, @@ -350,6 +350,9 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)}, {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)}, + +/* 7265 Series */ + {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, #endif /* CONFIG_IWLMVM */ {0} @@ -391,7 +394,6 @@ out_free_drv: iwl_drv_stop(trans_pcie->drv); out_free_trans: iwl_trans_pcie_free(iwl_trans); - pci_set_drvdata(pdev, NULL); return ret; } @@ -402,8 +404,6 @@ static void iwl_pci_remove(struct pci_dev *pdev) iwl_drv_stop(trans_pcie->drv); iwl_trans_pcie_free(trans); - - pci_set_drvdata(pdev, NULL); } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index c3f904d422b08b1074345fcba99a31390f663945..5d9337bec67a87c59f3ccef73e5f5b26644930d5 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -220,6 +220,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + /* Clear the interrupt in APMG if the NIC is in RFKILL */ + iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL); + set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); out: @@ -443,22 +446,138 @@ static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, return ret; } +static int iwl_pcie_secure_set(struct iwl_trans *trans, int cpu) +{ + int shift_param; + u32 address; + int ret = 0; + + if (cpu == 1) { + shift_param = 0; + address = CSR_SECURE_BOOT_CPU1_STATUS_ADDR; + } else { + shift_param = 16; + address = CSR_SECURE_BOOT_CPU2_STATUS_ADDR; + } + + /* set CPU to started */ + iwl_trans_set_bits_mask(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + CSR_CPU_STATUS_LOADING_STARTED << shift_param, + 1); + + /* set last complete descriptor number */ + iwl_trans_set_bits_mask(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED + << shift_param, + 1); + + /* set last loaded block */ + iwl_trans_set_bits_mask(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK + << shift_param, + 1); + + /* image loading complete */ + iwl_trans_set_bits_mask(trans, + CSR_UCODE_LOAD_STATUS_ADDR, + CSR_CPU_STATUS_LOADING_COMPLETED + << shift_param, + 1); + + /* set FH_TCSR_0_REG */ + iwl_trans_set_bits_mask(trans, FH_TCSR_0_REG0, 0x00400000, 1); + + /* verify image verification started */ + ret = iwl_poll_bit(trans, address, + CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS, + CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS, + CSR_SECURE_TIME_OUT); + if (ret < 0) { + IWL_ERR(trans, "secure boot process didn't start\n"); + return ret; + } + + /* wait for image verification to complete */ + ret = iwl_poll_bit(trans, address, + CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED, + CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED, + CSR_SECURE_TIME_OUT); + + if (ret < 0) { + IWL_ERR(trans, "Time out on secure boot process\n"); + return ret; + } + + return 0; +} + static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, const struct fw_img *image) { int i, ret = 0; - for (i = 0; i < IWL_UCODE_SECTION_MAX; i++) { + IWL_DEBUG_FW(trans, + "working with %s image\n", + image->is_secure ? "Secured" : "Non Secured"); + IWL_DEBUG_FW(trans, + "working with %s CPU\n", + image->is_dual_cpus ? "Dual" : "Single"); + + /* configure the ucode to be ready to get the secured image */ + if (image->is_secure) { + /* set secure boot inspector addresses */ + iwl_write32(trans, CSR_SECURE_INSPECTOR_CODE_ADDR, 0); + iwl_write32(trans, CSR_SECURE_INSPECTOR_DATA_ADDR, 0); + + /* release CPU1 reset if secure inspector image burned in OTP */ + iwl_write32(trans, CSR_RESET, 0); + } + + /* load to FW the binary sections of CPU1 */ + IWL_DEBUG_INFO(trans, "Loading CPU1\n"); + for (i = 0; + i < IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU; + i++) { if (!image->sec[i].data) break; - ret = iwl_pcie_load_section(trans, i, &image->sec[i]); if (ret) return ret; } - /* Remove all resets to allow NIC to operate */ - iwl_write32(trans, CSR_RESET, 0); + /* configure the ucode to start secure process on CPU1 */ + if (image->is_secure) { + /* config CPU1 to start secure protocol */ + ret = iwl_pcie_secure_set(trans, 1); + if (ret) + return ret; + } else { + /* Remove all resets to allow NIC to operate */ + iwl_write32(trans, CSR_RESET, 0); + } + + if (image->is_dual_cpus) { + /* load to FW the binary sections of CPU2 */ + IWL_DEBUG_INFO(trans, "working w/ DUAL CPUs - Loading CPU2\n"); + for (i = IWL_UCODE_FIRST_SECTION_OF_SECOND_CPU; + i < IWL_UCODE_SECTION_MAX; i++) { + if (!image->sec[i].data) + break; + ret = iwl_pcie_load_section(trans, i, &image->sec[i]); + if (ret) + return ret; + } + + if (image->is_secure) { + /* set CPU2 for secure protocol */ + ret = iwl_pcie_secure_set(trans, 2); + if (ret) + return ret; + } + } return 0; } diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 1424335163b97e4d6625a8fca7ec7c19a1d07c9c..059c5acad3a0d2e7b9eb73bf6466ef088e54e5c2 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1465,7 +1465,8 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, spin_unlock_bh(&txq->lock); } -#define HOST_COMPLETE_TIMEOUT (2 * HZ) +#define HOST_COMPLETE_TIMEOUT (2 * HZ) +#define COMMAND_POKE_TIMEOUT (HZ / 10) static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) @@ -1493,16 +1494,16 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int cmd_idx; int ret; + int timeout = HOST_COMPLETE_TIMEOUT; IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", get_cmd_string(trans_pcie, cmd->id)); - if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status))) { - IWL_ERR(trans, "Command %s: a command is already active!\n", - get_cmd_string(trans_pcie, cmd->id)); + if (WARN(test_and_set_bit(STATUS_HCMD_ACTIVE, + &trans_pcie->status), + "Command %s: a command is already active!\n", + get_cmd_string(trans_pcie, cmd->id))) return -EIO; - } IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", get_cmd_string(trans_pcie, cmd->id)); @@ -1517,10 +1518,29 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, return ret; } - ret = wait_event_timeout(trans_pcie->wait_command_queue, - !test_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status), - HOST_COMPLETE_TIMEOUT); + while (timeout > 0) { + unsigned long flags; + + timeout -= COMMAND_POKE_TIMEOUT; + ret = wait_event_timeout(trans_pcie->wait_command_queue, + !test_bit(STATUS_HCMD_ACTIVE, + &trans_pcie->status), + COMMAND_POKE_TIMEOUT); + if (ret) + break; + /* poke the device - it may have lost the command */ + if (iwl_trans_grab_nic_access(trans, true, &flags)) { + iwl_trans_release_nic_access(trans, &flags); + IWL_DEBUG_INFO(trans, + "Tried to wake NIC for command %s\n", + get_cmd_string(trans_pcie, cmd->id)); + } else { + IWL_ERR(trans, "Failed to poke NIC for command %s\n", + get_cmd_string(trans_pcie, cmd->id)); + break; + } + } + if (!ret) { if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { struct iwl_txq *txq = @@ -1541,6 +1561,9 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, "Clearing HCMD_ACTIVE for command %s\n", get_cmd_string(trans_pcie, cmd->id)); ret = -ETIMEDOUT; + + iwl_op_mode_nic_error(trans->op_mode); + goto cancel; } } diff --git a/drivers/net/wireless/libertas/firmware.c b/drivers/net/wireless/libertas/firmware.c index c0f9e7e862f66e8bfcb549ae4a7f3ba7425306ae..51b92b5df11956adfedadb5137f8f6b61342b0c4 100644 --- a/drivers/net/wireless/libertas/firmware.c +++ b/drivers/net/wireless/libertas/firmware.c @@ -53,6 +53,11 @@ static void main_firmware_cb(const struct firmware *firmware, void *context) /* Firmware found! */ lbs_fw_loaded(priv, 0, priv->helper_fw, firmware); + if (priv->helper_fw) { + release_firmware (priv->helper_fw); + priv->helper_fw = NULL; + } + release_firmware (firmware); } static void helper_firmware_cb(const struct firmware *firmware, void *context) diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c index c94dd6802672a053d795b052710d5ada4ff3e2bb..ef8c98e21098479d231df5844fb2abab8ddab04f 100644 --- a/drivers/net/wireless/libertas/if_cs.c +++ b/drivers/net/wireless/libertas/if_cs.c @@ -754,14 +754,14 @@ static void if_cs_prog_firmware(struct lbs_private *priv, int ret, if (ret == 0 && (card->model != MODEL_8305)) ret = if_cs_prog_real(card, mainfw); if (ret) - goto out; + return; /* Now actually get the IRQ */ ret = request_irq(card->p_dev->irq, if_cs_interrupt, IRQF_SHARED, DRV_NAME, card); if (ret) { pr_err("error in request_irq\n"); - goto out; + return; } /* @@ -777,10 +777,6 @@ static void if_cs_prog_firmware(struct lbs_private *priv, int ret, pr_err("could not activate card\n"); free_irq(card->p_dev->irq, card); } - -out: - release_firmware(helper); - release_firmware(mainfw); } diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 45578335e4200f2f47a3fc8284f9bd09127209ee..991238afd1b60c04ae437fafeb7a108713ea3134 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -708,20 +708,16 @@ static void if_sdio_do_prog_firmware(struct lbs_private *priv, int ret, ret = if_sdio_prog_helper(card, helper); if (ret) - goto out; + return; lbs_deb_sdio("Helper firmware loaded\n"); ret = if_sdio_prog_real(card, mainfw); if (ret) - goto out; + return; lbs_deb_sdio("Firmware loaded\n"); if_sdio_finish_power_on(card); - -out: - release_firmware(helper); - release_firmware(mainfw); } static int if_sdio_prog_firmware(struct if_sdio_card *card) diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c index 4bb6574f40737f462f3b7f4ad3d9fea3d705b415..83669151bb8242e31931735700eb27fe4cd01821 100644 --- a/drivers/net/wireless/libertas/if_spi.c +++ b/drivers/net/wireless/libertas/if_spi.c @@ -1094,11 +1094,7 @@ static int if_spi_init_card(struct if_spi_card *card) goto out; out: - release_firmware(helper); - release_firmware(mainfw); - lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err); - return err; } @@ -1128,7 +1124,7 @@ static int if_spi_probe(struct spi_device *spi) { struct if_spi_card *card; struct lbs_private *priv = NULL; - struct libertas_spi_platform_data *pdata = spi->dev.platform_data; + struct libertas_spi_platform_data *pdata = dev_get_platdata(&spi->dev); int err = 0; lbs_deb_enter(LBS_DEB_SPI); diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c index 27980778d992db783e58f777446a1cc03d40c28c..dff08a2896a38f644894749c006787e26b1f3ea6 100644 --- a/drivers/net/wireless/libertas/if_usb.c +++ b/drivers/net/wireless/libertas/if_usb.c @@ -844,7 +844,7 @@ static void if_usb_prog_firmware(struct lbs_private *priv, int ret, cardp->fw = fw; if (check_fwfile_format(cardp->fw->data, cardp->fw->size)) { ret = -EINVAL; - goto release_fw; + goto done; } /* Cancel any pending usb business */ @@ -861,7 +861,7 @@ restart: if (if_usb_submit_rx_urb_fwload(cardp) < 0) { lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n"); ret = -EIO; - goto release_fw; + goto done; } cardp->bootcmdresp = 0; @@ -883,14 +883,14 @@ restart: usb_kill_urb(cardp->tx_urb); if (if_usb_submit_rx_urb(cardp) < 0) ret = -EIO; - goto release_fw; + goto done; } else if (cardp->bootcmdresp <= 0) { if (--reset_count >= 0) { if_usb_reset_device(cardp); goto restart; } ret = -EIO; - goto release_fw; + goto done; } i = 0; @@ -921,14 +921,14 @@ restart: pr_info("FW download failure, time = %d ms\n", i * 100); ret = -EIO; - goto release_fw; + goto done; } cardp->priv->fw_ready = 1; if_usb_submit_rx_urb(cardp); if (lbs_start_card(priv)) - goto release_fw; + goto done; if_usb_setup_firmware(priv); @@ -939,11 +939,8 @@ restart: if (lbs_host_sleep_cfg(priv, priv->wol_criteria, NULL)) priv->ehs_remove_supported = false; - release_fw: - release_firmware(cardp->fw); - cardp->fw = NULL; - done: + cardp->fw = NULL; lbs_deb_leave(LBS_DEB_USB); } diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 2cd3f54e1efa14652e83691e3353290d17e76939..de0df86704e714778891b7fb1af6b2cfc1340bef 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -167,6 +167,7 @@ struct hwsim_vif_priv { u32 magic; u8 bssid[ETH_ALEN]; bool assoc; + bool bcn_en; u16 aid; }; @@ -1170,6 +1171,16 @@ static void mac80211_hwsim_configure_filter(struct ieee80211_hw *hw, *total_flags = data->rx_filter; } +static void mac80211_hwsim_bcn_en_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + unsigned int *count = data; + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + + if (vp->bcn_en) + (*count)++; +} + static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -1180,7 +1191,8 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, hwsim_check_magic(vif); - wiphy_debug(hw->wiphy, "%s(changed=0x%x)\n", __func__, changed); + wiphy_debug(hw->wiphy, "%s(changed=0x%x vif->addr=%pM)\n", + __func__, changed, vif->addr); if (changed & BSS_CHANGED_BSSID) { wiphy_debug(hw->wiphy, "%s: BSSID changed: %pM\n", @@ -1202,6 +1214,7 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BEACON_ENABLED) { wiphy_debug(hw->wiphy, " BCN EN: %d\n", info->enable_beacon); + vp->bcn_en = info->enable_beacon; if (data->started && !hrtimer_is_queued(&data->beacon_timer.timer) && info->enable_beacon) { @@ -1215,8 +1228,16 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, tasklet_hrtimer_start(&data->beacon_timer, ns_to_ktime(until_tbtt * 1000), HRTIMER_MODE_REL); - } else if (!info->enable_beacon) - tasklet_hrtimer_cancel(&data->beacon_timer); + } else if (!info->enable_beacon) { + unsigned int count = 0; + ieee80211_iterate_active_interfaces( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_bcn_en_iter, &count); + wiphy_debug(hw->wiphy, " beaconing vifs remaining: %u", + count); + if (count == 0) + tasklet_hrtimer_cancel(&data->beacon_timer); + } } if (changed & BSS_CHANGED_ERP_CTS_PROT) { diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index a6c46f3b6e3a0d622b796f094b5b75016428438e..e47f4e3012b85344c7d400d78799634cb5bf0234 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -1048,7 +1048,7 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; unsigned long cmd_flags; unsigned long scan_pending_q_flags; - uint16_t cancel_scan_cmd = false; + bool cancel_scan_cmd = false; if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 37f873bb342f5043046b18c3fe3b573ebdedac64..4e4686e6ac092b3ce23282c575eadc2b40071eff 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -621,7 +621,7 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, int ret = 0; struct ieee_types_assoc_rsp *assoc_rsp; struct mwifiex_bssdescriptor *bss_desc; - u8 enable_data = true; + bool enable_data = true; u16 cap_info, status_code; assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index c2b91f566e05073d83dbbe60e265a78ddaf8fa9e..9d7c9d354d34aeb9e0b3e4bf854fc6a999671958 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -882,7 +882,9 @@ mwifiex_add_card(void *card, struct semaphore *sem, adapter->cmd_wait_q.status = 0; adapter->scan_wait_q_woken = false; - adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE"); + adapter->workqueue = + alloc_workqueue("MWIFIEX_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); if (!adapter->workqueue) goto err_kmalloc; diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 52da8ee7599a041d7922180122c1744d19fe743e..33fa9432b241b353c3ae381ab57c48d5d71f763b 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -93,7 +93,7 @@ static int mwifiex_pcie_suspend(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); if (pdev) { - card = (struct pcie_service_card *) pci_get_drvdata(pdev); + card = pci_get_drvdata(pdev); if (!card || !card->adapter) { pr_err("Card or adapter structure is not valid\n"); return 0; @@ -128,7 +128,7 @@ static int mwifiex_pcie_resume(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); if (pdev) { - card = (struct pcie_service_card *) pci_get_drvdata(pdev); + card = pci_get_drvdata(pdev); if (!card || !card->adapter) { pr_err("Card or adapter structure is not valid\n"); return 0; @@ -2037,7 +2037,7 @@ static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) goto exit; } - card = (struct pcie_service_card *) pci_get_drvdata(pdev); + card = pci_get_drvdata(pdev); if (!card || !card->adapter) { pr_debug("info: %s: card=%p adapter=%p\n", __func__, card, card ? card->adapter : NULL); diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index c0268b5977480b384f04f452e3278a9443c93e85..7d66018a2e33060d1bdc1a96b970fd61f6480e4a 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -327,7 +327,7 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, { struct mwifiex_adapter *adapter = priv->adapter; struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; - u16 hs_activate = false; + bool hs_activate = false; if (!hscfg_param) /* New Activate command */ diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 95fa3599b4070b02aecc8d4d46073137b05c5b98..5dd0ccc70b863ea15fad25d739adebd39ce09dbd 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -708,7 +708,7 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, { u8 *curr = (u8 *) &resp->params.get_wmm_status; uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; - int valid = true; + bool valid = true; struct mwifiex_ie_types_data *tlv_hdr; struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index 644d6e0c51ccf235e5b457da85f57c64029e5c2d..0f129d498fb1057d898db2c6e92868d8ff1f3baf 100644 --- a/drivers/net/wireless/mwifiex/wmm.h +++ b/drivers/net/wireless/mwifiex/wmm.h @@ -83,11 +83,10 @@ mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) } void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb); + struct sk_buff *skb); void mwifiex_ralist_add(struct mwifiex_private *priv, u8 *ra); void mwifiex_rotate_priolists(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra, - int tid); + struct mwifiex_ra_list_tbl *ra, int tid); int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter); void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter); @@ -95,21 +94,18 @@ int mwifiex_is_ralist_valid(struct mwifiex_private *priv, struct mwifiex_ra_list_tbl *ra_list, int tid); u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, - const struct sk_buff *skb); + const struct sk_buff *skb); void mwifiex_wmm_init(struct mwifiex_adapter *adapter); -extern u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, - u8 **assoc_buf, - struct ieee_types_wmm_parameter - *wmmie, - struct ieee80211_ht_cap - *htcap); +u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, + u8 **assoc_buf, + struct ieee_types_wmm_parameter *wmmie, + struct ieee80211_ht_cap *htcap); void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, - struct ieee_types_wmm_parameter - *wmm_ie); + struct ieee_types_wmm_parameter *wmm_ie); void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); -extern int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, - const struct host_cmd_ds_command *resp); +int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, + const struct host_cmd_ds_command *resp); #endif /* !_MWIFIEX_WMM_H_ */ diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a3707fd4ef623e5968afc85e5d265cb49024d764..b953ad621e0b90ef23e9fbe009bc0c4cd5f66876 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -6093,7 +6093,6 @@ err_iounmap: if (priv->sram != NULL) pci_iounmap(pdev, priv->sram); - pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); err_free_reg: @@ -6147,7 +6146,6 @@ static void mwl8k_remove(struct pci_dev *pdev) unmap: pci_iounmap(pdev, priv->regs); pci_iounmap(pdev, priv->sram); - pci_set_drvdata(pdev, NULL); ieee80211_free_hw(hw); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/net/wireless/orinoco/orinoco.h b/drivers/net/wireless/orinoco/orinoco.h index 3bb936b9558c7f96dedc215189f72b49a313b8d7..eebd2be21ee9067e7d90f45f32b227bb07601998 100644 --- a/drivers/net/wireless/orinoco/orinoco.h +++ b/drivers/net/wireless/orinoco/orinoco.h @@ -182,23 +182,20 @@ extern int orinoco_debug; /* Exported prototypes */ /********************************************************************/ -extern struct orinoco_private *alloc_orinocodev( - int sizeof_card, struct device *device, - int (*hard_reset)(struct orinoco_private *), - int (*stop_fw)(struct orinoco_private *, int)); -extern void free_orinocodev(struct orinoco_private *priv); -extern int orinoco_init(struct orinoco_private *priv); -extern int orinoco_if_add(struct orinoco_private *priv, - unsigned long base_addr, - unsigned int irq, - const struct net_device_ops *ops); -extern void orinoco_if_del(struct orinoco_private *priv); -extern int orinoco_up(struct orinoco_private *priv); -extern void orinoco_down(struct orinoco_private *priv); -extern irqreturn_t orinoco_interrupt(int irq, void *dev_id); - -extern void __orinoco_ev_info(struct net_device *dev, struct hermes *hw); -extern void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw); +struct orinoco_private *alloc_orinocodev(int sizeof_card, struct device *device, + int (*hard_reset)(struct orinoco_private *), + int (*stop_fw)(struct orinoco_private *, int)); +void free_orinocodev(struct orinoco_private *priv); +int orinoco_init(struct orinoco_private *priv); +int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr, + unsigned int irq, const struct net_device_ops *ops); +void orinoco_if_del(struct orinoco_private *priv); +int orinoco_up(struct orinoco_private *priv); +void orinoco_down(struct orinoco_private *priv); +irqreturn_t orinoco_interrupt(int irq, void *dev_id); + +void __orinoco_ev_info(struct net_device *dev, struct hermes *hw); +void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw); int orinoco_process_xmit_skb(struct sk_buff *skb, struct net_device *dev, diff --git a/drivers/net/wireless/orinoco/orinoco_nortel.c b/drivers/net/wireless/orinoco/orinoco_nortel.c index d73fdf6185a2c7495891544ffe693ed140432f54..ffb2469eb6794c16453022e2b4a0a17a239077d3 100644 --- a/drivers/net/wireless/orinoco/orinoco_nortel.c +++ b/drivers/net/wireless/orinoco/orinoco_nortel.c @@ -234,7 +234,6 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev, free_irq(pdev->irq, priv); fail_irq: - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: @@ -265,7 +264,6 @@ static void orinoco_nortel_remove_one(struct pci_dev *pdev) orinoco_if_del(priv); free_irq(pdev->irq, priv); - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->attr_io); diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c index 677bf14eca844a1dd7c8cc2dca0724e9c5a5b505..5ae1191d2532545af84df76970140902906bb373 100644 --- a/drivers/net/wireless/orinoco/orinoco_pci.c +++ b/drivers/net/wireless/orinoco/orinoco_pci.c @@ -184,7 +184,6 @@ static int orinoco_pci_init_one(struct pci_dev *pdev, free_irq(pdev->irq, priv); fail_irq: - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: @@ -205,7 +204,6 @@ static void orinoco_pci_remove_one(struct pci_dev *pdev) orinoco_if_del(priv); free_irq(pdev->irq, priv); - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_release_regions(pdev); diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c index 2559dbd6184b2421b95a2d73630e14a887a9525c..bbd36d1676ff16aff0ff04b70a4e4a761e2ba9ba 100644 --- a/drivers/net/wireless/orinoco/orinoco_plx.c +++ b/drivers/net/wireless/orinoco/orinoco_plx.c @@ -273,7 +273,6 @@ static int orinoco_plx_init_one(struct pci_dev *pdev, free_irq(pdev->irq, priv); fail_irq: - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: @@ -301,7 +300,6 @@ static void orinoco_plx_remove_one(struct pci_dev *pdev) orinoco_if_del(priv); free_irq(pdev->irq, priv); - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->attr_io); diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c index 42afeeea2c405c2403f0075f1aeabacd7af57767..04b08de5fd5db33cc73b89fefe8d2f9cea2adb69 100644 --- a/drivers/net/wireless/orinoco/orinoco_tmd.c +++ b/drivers/net/wireless/orinoco/orinoco_tmd.c @@ -170,7 +170,6 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev, free_irq(pdev->irq, priv); fail_irq: - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); fail_alloc: @@ -195,7 +194,6 @@ static void orinoco_tmd_remove_one(struct pci_dev *pdev) orinoco_if_del(priv); free_irq(pdev->irq, priv); - pci_set_drvdata(pdev, NULL); free_orinocodev(priv); pci_iounmap(pdev, priv->hw.iobase); pci_iounmap(pdev, card->bridge_io); diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index 57e3af8ebb4b39622450d1fbee0742b39ab5a0a3..f9a07b0d83acd2f40df4755880fa1551e6276091 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -631,7 +631,6 @@ static int p54p_probe(struct pci_dev *pdev, iounmap(priv->map); err_free_dev: - pci_set_drvdata(pdev, NULL); p54_free_common(dev); err_free_reg: diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c index 7fc46f26cf2be3085f08c56673931a44d538d098..de15171e2cd896f8dfd78c6d5e00966e2161ec12 100644 --- a/drivers/net/wireless/p54/p54spi.c +++ b/drivers/net/wireless/p54/p54spi.c @@ -636,7 +636,7 @@ static int p54spi_probe(struct spi_device *spi) gpio_direction_input(p54spi_gpio_irq); ret = request_irq(gpio_to_irq(p54spi_gpio_irq), - p54spi_interrupt, IRQF_DISABLED, "p54spi", + p54spi_interrupt, 0, "p54spi", priv->spi); if (ret < 0) { dev_err(&priv->spi->dev, "request_irq() failed"); diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 1c22b81e6ef35e30f86afc994445ff66ef4f7c36..8863a6cb2388d952926cf8fe209a8b67fa004ddb 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -183,7 +183,7 @@ prism54_update_stats(struct work_struct *work) data = r.ptr; /* copy this MAC to the bss */ - memcpy(bss.address, data, 6); + memcpy(bss.address, data, ETH_ALEN); kfree(data); /* now ask for the corresponding bss */ @@ -531,7 +531,7 @@ prism54_set_wap(struct net_device *ndev, struct iw_request_info *info, return -EINVAL; /* prepare the structure for the set object */ - memcpy(&bssid[0], awrq->sa_data, 6); + memcpy(&bssid[0], awrq->sa_data, ETH_ALEN); /* set the bssid -- does this make sense when in AP mode? */ rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid); @@ -550,7 +550,7 @@ prism54_get_wap(struct net_device *ndev, struct iw_request_info *info, int rvalue; rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r); - memcpy(awrq->sa_data, r.ptr, 6); + memcpy(awrq->sa_data, r.ptr, ETH_ALEN); awrq->sa_family = ARPHRD_ETHER; kfree(r.ptr); @@ -582,7 +582,7 @@ prism54_translate_bss(struct net_device *ndev, struct iw_request_info *info, size_t wpa_ie_len; /* The first entry must be the MAC address */ - memcpy(iwe.u.ap_addr.sa_data, bss->address, 6); + memcpy(iwe.u.ap_addr.sa_data, bss->address, ETH_ALEN); iwe.u.ap_addr.sa_family = ARPHRD_ETHER; iwe.cmd = SIOCGIWAP; current_ev = iwe_stream_add_event(info, current_ev, end_buf, @@ -2489,7 +2489,7 @@ prism54_set_mac_address(struct net_device *ndev, void *addr) &((struct sockaddr *) addr)->sa_data); if (!ret) memcpy(priv->ndev->dev_addr, - &((struct sockaddr *) addr)->sa_data, 6); + &((struct sockaddr *) addr)->sa_data, ETH_ALEN); return ret; } diff --git a/drivers/net/wireless/prism54/islpci_dev.c b/drivers/net/wireless/prism54/islpci_dev.c index 5970ff6f40cc315dd253054c84f031cceda01170..41a16d30c79c5be46f89229d3185a033d8131f0f 100644 --- a/drivers/net/wireless/prism54/islpci_dev.c +++ b/drivers/net/wireless/prism54/islpci_dev.c @@ -837,7 +837,7 @@ islpci_setup(struct pci_dev *pdev) /* ndev->set_multicast_list = &islpci_set_multicast_list; */ ndev->addr_len = ETH_ALEN; /* Get a non-zero dummy MAC address for nameif. Jean II */ - memcpy(ndev->dev_addr, dummy_mac, 6); + memcpy(ndev->dev_addr, dummy_mac, ETH_ALEN); ndev->watchdog_timeo = ISLPCI_TX_TIMEOUT; diff --git a/drivers/net/wireless/prism54/oid_mgt.c b/drivers/net/wireless/prism54/oid_mgt.c index a01606b36e03f836a5cc57ab292dd47f2ba2352f..056af38e72e399d88528343e978fec15d512f180 100644 --- a/drivers/net/wireless/prism54/oid_mgt.c +++ b/drivers/net/wireless/prism54/oid_mgt.c @@ -682,7 +682,7 @@ mgt_update_addr(islpci_private *priv) isl_oid[GEN_OID_MACADDRESS].size, &res); if ((ret == 0) && res && (res->header->operation != PIMFOR_OP_ERROR)) - memcpy(priv->ndev->dev_addr, res->data, 6); + memcpy(priv->ndev->dev_addr, res->data, ETH_ALEN); else ret = -EIO; if (res) diff --git a/drivers/net/wireless/rt2x00/Kconfig b/drivers/net/wireless/rt2x00/Kconfig index 68dbbb9c6d1259eb3d83d7c1458126f6fc69a2f2..006b8bcb2e31dfc5c21d7ed602546003e4a9e54c 100644 --- a/drivers/net/wireless/rt2x00/Kconfig +++ b/drivers/net/wireless/rt2x00/Kconfig @@ -58,11 +58,11 @@ config RT61PCI config RT2800PCI tristate "Ralink rt27xx/rt28xx/rt30xx (PCI/PCIe/PCMCIA) support" - depends on PCI || SOC_RT288X || SOC_RT305X + depends on PCI select RT2800_LIB + select RT2800_LIB_MMIO select RT2X00_LIB_MMIO - select RT2X00_LIB_PCI if PCI - select RT2X00_LIB_SOC if SOC_RT288X || SOC_RT305X + select RT2X00_LIB_PCI select RT2X00_LIB_FIRMWARE select RT2X00_LIB_CRYPTO select CRC_CCITT @@ -199,9 +199,30 @@ config RT2800USB_UNKNOWN endif +config RT2800SOC + tristate "Ralink WiSoC support" + depends on SOC_RT288X || SOC_RT305X + select RT2X00_LIB_SOC + select RT2X00_LIB_MMIO + select RT2X00_LIB_CRYPTO + select RT2X00_LIB_FIRMWARE + select RT2800_LIB + select RT2800_LIB_MMIO + ---help--- + This adds support for Ralink WiSoC devices. + Supported chips: RT2880, RT3050, RT3052, RT3350, RT3352. + + When compiled as a module, this driver will be called rt2800soc. + + config RT2800_LIB tristate +config RT2800_LIB_MMIO + tristate + select RT2X00_LIB_MMIO + select RT2800_LIB + config RT2X00_LIB_MMIO tristate @@ -219,6 +240,7 @@ config RT2X00_LIB_USB config RT2X00_LIB tristate + select AVERAGE config RT2X00_LIB_FIRMWARE boolean diff --git a/drivers/net/wireless/rt2x00/Makefile b/drivers/net/wireless/rt2x00/Makefile index f069d8bc5b678332151d7a6fe337f44e29205d82..24a66015a4959f6fd6d44a18e6bd8d0aed7d1c1c 100644 --- a/drivers/net/wireless/rt2x00/Makefile +++ b/drivers/net/wireless/rt2x00/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_RT2X00_LIB_PCI) += rt2x00pci.o obj-$(CONFIG_RT2X00_LIB_SOC) += rt2x00soc.o obj-$(CONFIG_RT2X00_LIB_USB) += rt2x00usb.o obj-$(CONFIG_RT2800_LIB) += rt2800lib.o +obj-$(CONFIG_RT2800_LIB_MMIO) += rt2800mmio.o obj-$(CONFIG_RT2400PCI) += rt2400pci.o obj-$(CONFIG_RT2500PCI) += rt2500pci.o obj-$(CONFIG_RT61PCI) += rt61pci.o @@ -21,3 +22,4 @@ obj-$(CONFIG_RT2800PCI) += rt2800pci.o obj-$(CONFIG_RT2500USB) += rt2500usb.o obj-$(CONFIG_RT73USB) += rt73usb.o obj-$(CONFIG_RT2800USB) += rt2800usb.o +obj-$(CONFIG_RT2800SOC) += rt2800soc.o diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index 3d53a09da5a12da22f2697c9a22b7dfb49daaff0..38ed9a3e44c8c00c06ea8f1cdd9ae1a68bbe2d60 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1261,7 +1261,7 @@ static void rt2400pci_fill_rxdone(struct queue_entry *entry, */ rxdesc->timestamp = ((u64)rx_high << 32) | rx_low; rxdesc->signal = rt2x00_get_field32(word2, RXD_W2_SIGNAL) & ~0x08; - rxdesc->rssi = rt2x00_get_field32(word2, RXD_W3_RSSI) - + rxdesc->rssi = rt2x00_get_field32(word3, RXD_W3_RSSI) - entry->queue->rt2x00dev->rssi_offset; rxdesc->size = rt2x00_get_field32(word0, RXD_W0_DATABYTE_COUNT); diff --git a/drivers/net/wireless/rt2x00/rt2800.h b/drivers/net/wireless/rt2x00/rt2800.h index fa33b5edf931ba688cb40297937e519a67883b76..aab6b5e4f5ddd0fb0a52b7385388f3a07c738add 100644 --- a/drivers/net/wireless/rt2x00/rt2800.h +++ b/drivers/net/wireless/rt2x00/rt2800.h @@ -52,6 +52,7 @@ * RF3322 2.4G 2T2R(RT3352/RT3371/RT3372/RT3391/RT3392) * RF3053 2.4G/5G 3T3R(RT3883/RT3563/RT3573/RT3593/RT3662) * RF5592 2.4G/5G 2T2R + * RF3070 2.4G 1T1R * RF5360 2.4G 1T1R * RF5370 2.4G 1T1R * RF5390 2.4G 1T1R @@ -70,6 +71,7 @@ #define RF3322 0x000c #define RF3053 0x000d #define RF5592 0x000f +#define RF3070 0x3070 #define RF3290 0x3290 #define RF5360 0x5360 #define RF5370 0x5370 @@ -122,7 +124,7 @@ /* * MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number. */ -#define MAC_CSR0_3290 0x0000 +#define MAC_CSR0_3290 0x0000 /* * E2PROM_CSR: PCI EEPROM control register. @@ -211,17 +213,17 @@ /* * COEX_CFG_0 */ -#define COEX_CFG0 0x0040 +#define COEX_CFG0 0x0040 #define COEX_CFG_ANT FIELD32(0xff000000) /* * COEX_CFG_1 */ -#define COEX_CFG1 0x0044 +#define COEX_CFG1 0x0044 /* * COEX_CFG_2 */ -#define COEX_CFG2 0x0048 +#define COEX_CFG2 0x0048 #define BT_COEX_CFG1 FIELD32(0xff000000) #define BT_COEX_CFG0 FIELD32(0x00ff0000) #define WL_COEX_CFG1 FIELD32(0x0000ff00) @@ -235,8 +237,8 @@ #define PLL_RESERVED_INPUT2 FIELD32(0x0000ff00) #define PLL_CONTROL FIELD32(0x00070000) #define PLL_LPF_R1 FIELD32(0x00080000) -#define PLL_LPF_C1_CTRL FIELD32(0x00300000) -#define PLL_LPF_C2_CTRL FIELD32(0x00c00000) +#define PLL_LPF_C1_CTRL FIELD32(0x00300000) +#define PLL_LPF_C2_CTRL FIELD32(0x00c00000) #define PLL_CP_CURRENT_CTRL FIELD32(0x03000000) #define PLL_PFD_DELAY_CTRL FIELD32(0x0c000000) #define PLL_LOCK_CTRL FIELD32(0x70000000) @@ -2164,7 +2166,7 @@ struct mac_iveiv_entry { */ #define RFCSR6_R1 FIELD8(0x03) #define RFCSR6_R2 FIELD8(0x40) -#define RFCSR6_TXDIV FIELD8(0x0c) +#define RFCSR6_TXDIV FIELD8(0x0c) /* bits for RF3053 */ #define RFCSR6_VCO_IC FIELD8(0xc0) @@ -2202,13 +2204,13 @@ struct mac_iveiv_entry { * RFCSR 12: */ #define RFCSR12_TX_POWER FIELD8(0x1f) -#define RFCSR12_DR0 FIELD8(0xe0) +#define RFCSR12_DR0 FIELD8(0xe0) /* * RFCSR 13: */ #define RFCSR13_TX_POWER FIELD8(0x1f) -#define RFCSR13_DR0 FIELD8(0xe0) +#define RFCSR13_DR0 FIELD8(0xe0) /* * RFCSR 15: @@ -2226,7 +2228,7 @@ struct mac_iveiv_entry { #define RFCSR17_TXMIXER_GAIN FIELD8(0x07) #define RFCSR17_TX_LO1_EN FIELD8(0x08) #define RFCSR17_R FIELD8(0x20) -#define RFCSR17_CODE FIELD8(0x7f) +#define RFCSR17_CODE FIELD8(0x7f) /* RFCSR 18 */ #define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40) @@ -2449,7 +2451,7 @@ enum rt2800_eeprom_word { */ #define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f) #define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0) -#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00) +#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00) /* * EEPROM NIC Configuration 1 @@ -2471,18 +2473,18 @@ enum rt2800_eeprom_word { * DAC_TEST: 0: disable, 1: enable */ #define EEPROM_NIC_CONF1_HW_RADIO FIELD16(0x0001) -#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002) -#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004) -#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008) +#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002) +#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004) +#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008) #define EEPROM_NIC_CONF1_CARDBUS_ACCEL FIELD16(0x0010) #define EEPROM_NIC_CONF1_BW40M_SB_2G FIELD16(0x0020) #define EEPROM_NIC_CONF1_BW40M_SB_5G FIELD16(0x0040) #define EEPROM_NIC_CONF1_WPS_PBC FIELD16(0x0080) #define EEPROM_NIC_CONF1_BW40M_2G FIELD16(0x0100) #define EEPROM_NIC_CONF1_BW40M_5G FIELD16(0x0200) -#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400) +#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400) #define EEPROM_NIC_CONF1_ANT_DIVERSITY FIELD16(0x1800) -#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000) +#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000) #define EEPROM_NIC_CONF1_BT_COEXIST FIELD16(0x4000) #define EEPROM_NIC_CONF1_DAC_TEST FIELD16(0x8000) @@ -2521,9 +2523,9 @@ enum rt2800_eeprom_word { * TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream * CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved */ -#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) -#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) -#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) +#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f) +#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0) +#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600) /* * EEPROM LNA @@ -2790,7 +2792,7 @@ enum rt2800_eeprom_word { #define MCU_CURRENT 0x36 #define MCU_LED 0x50 #define MCU_LED_STRENGTH 0x51 -#define MCU_LED_AG_CONF 0x52 +#define MCU_LED_AG_CONF 0x52 #define MCU_LED_ACT_CONF 0x53 #define MCU_LED_LED_POLARITY 0x54 #define MCU_RADAR 0x60 @@ -2799,7 +2801,7 @@ enum rt2800_eeprom_word { #define MCU_FREQ_OFFSET 0x74 #define MCU_BBP_SIGNAL 0x80 #define MCU_POWER_SAVE 0x83 -#define MCU_BAND_SELECT 0x91 +#define MCU_BAND_SELECT 0x91 /* * MCU mailbox tokens diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 88ce656f96cda3d7ea1f7ae5756827538edfc6d2..c5738f14c4ba21b7a60453ab282309089134bdc2 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -278,12 +278,9 @@ static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = { [EEPROM_LNA] = 0x0026, [EEPROM_EXT_LNA2] = 0x0027, [EEPROM_RSSI_BG] = 0x0028, - [EEPROM_TXPOWER_DELTA] = 0x0028, /* Overlaps with RSSI_BG */ [EEPROM_RSSI_BG2] = 0x0029, - [EEPROM_TXMIXER_GAIN_BG] = 0x0029, /* Overlaps with RSSI_BG2 */ [EEPROM_RSSI_A] = 0x002a, [EEPROM_RSSI_A2] = 0x002b, - [EEPROM_TXMIXER_GAIN_A] = 0x002b, /* Overlaps with RSSI_A2 */ [EEPROM_TXPOWER_BG1] = 0x0030, [EEPROM_TXPOWER_BG2] = 0x0037, [EEPROM_EXT_TXPOWER_BG3] = 0x003e, @@ -1783,7 +1780,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) rt2800_bbp_read(rt2x00dev, 3, &r3); if (rt2x00_rt(rt2x00dev, RT3572) && - test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) + rt2x00_has_cap_bt_coexist(rt2x00dev)) rt2800_config_3572bt_ant(rt2x00dev); /* @@ -1795,7 +1792,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) break; case 2: if (rt2x00_rt(rt2x00dev, RT3572) && - test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) + rt2x00_has_cap_bt_coexist(rt2x00dev)) rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1); else rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); @@ -1825,7 +1822,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant) break; case 2: if (rt2x00_rt(rt2x00dev, RT3572) && - test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + rt2x00_has_cap_bt_coexist(rt2x00dev)) { rt2x00_set_field8(&r3, BBP3_RX_ADC, 1); rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); @@ -2029,13 +2026,6 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev, rt2x00dev->default_ant.tx_chain_num <= 2); rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); - rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr); - rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - msleep(1); - rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0); - rt2800_rfcsr_write(rt2x00dev, 30, rfcsr); - rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr); rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); @@ -2141,7 +2131,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0); - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { if (rf->channel <= 14) { rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1); @@ -2674,7 +2664,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev, if (rf->channel <= 14) { int idx = rf->channel-1; - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) { /* r55/r59 value array of channel 1~14 */ static const char r55_bt_rev[] = {0x83, 0x83, @@ -3152,6 +3142,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, case RF3322: rt2800_config_channel_rf3322(rt2x00dev, conf, rf, info); break; + case RF3070: case RF5360: case RF5370: case RF5372: @@ -3166,7 +3157,8 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2800_config_channel_rf2xxx(rt2x00dev, conf, rf, info); } - if (rt2x00_rf(rt2x00dev, RF3290) || + if (rt2x00_rf(rt2x00dev, RF3070) || + rt2x00_rf(rt2x00dev, RF3290) || rt2x00_rf(rt2x00dev, RF3322) || rt2x00_rf(rt2x00dev, RF5360) || rt2x00_rf(rt2x00dev, RF5370) || @@ -3218,8 +3210,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, if (rf->channel <= 14) { if (!rt2x00_rt(rt2x00dev, RT5390) && !rt2x00_rt(rt2x00dev, RT5392)) { - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, - &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 75, 0x46); } else { @@ -3244,7 +3235,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, if (rt2x00_rt(rt2x00dev, RT3593)) rt2800_bbp_write(rt2x00dev, 83, 0x9a); - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) rt2800_bbp_write(rt2x00dev, 75, 0x46); else rt2800_bbp_write(rt2x00dev, 75, 0x50); @@ -3280,7 +3271,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, /* Turn on primary PAs */ rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rf->channel > 14); - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1); else rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, @@ -3311,33 +3302,50 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev, rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin); - if (rt2x00_rt(rt2x00dev, RT3572)) + if (rt2x00_rt(rt2x00dev, RT3572)) { rt2800_rfcsr_write(rt2x00dev, 8, 0x80); + /* AGC init */ + if (rf->channel <= 14) + reg = 0x1c + (2 * rt2x00dev->lna_gain); + else + reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3); + + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg); + } + if (rt2x00_rt(rt2x00dev, RT3593)) { - if (rt2x00_is_usb(rt2x00dev)) { - rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); + rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); - /* Band selection. GPIO #8 controls all paths */ + /* Band selection */ + if (rt2x00_is_usb(rt2x00dev) || + rt2x00_is_pcie(rt2x00dev)) { + /* GPIO #8 controls all paths */ rt2x00_set_field32(®, GPIO_CTRL_DIR8, 0); if (rf->channel <= 14) rt2x00_set_field32(®, GPIO_CTRL_VAL8, 1); else rt2x00_set_field32(®, GPIO_CTRL_VAL8, 0); + } + /* LNA PE control. */ + if (rt2x00_is_usb(rt2x00dev)) { + /* GPIO #4 controls PE0 and PE1, + * GPIO #7 controls PE2 + */ rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); rt2x00_set_field32(®, GPIO_CTRL_DIR7, 0); - /* LNA PE control. - * GPIO #4 controls PE0 and PE1, - * GPIO #7 controls PE2 - */ rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); rt2x00_set_field32(®, GPIO_CTRL_VAL7, 1); - - rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + } else if (rt2x00_is_pcie(rt2x00dev)) { + /* GPIO #4 controls PE0, PE1 and PE2 */ + rt2x00_set_field32(®, GPIO_CTRL_DIR4, 0); + rt2x00_set_field32(®, GPIO_CTRL_VAL4, 1); } + rt2800_register_write(rt2x00dev, GPIO_CTRL, reg); + /* AGC init */ if (rf->channel <= 14) reg = 0x1c + 2 * rt2x00dev->lna_gain; @@ -3565,7 +3573,7 @@ static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev, { int delta; - if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_power_limit(rt2x00dev)) return 0; /* @@ -3594,7 +3602,7 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b, if (rt2x00_rt(rt2x00dev, RT3593)) return min_t(u8, txpower, 0xc); - if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_power_limit(rt2x00dev)) { /* * Check if eirp txpower exceed txpower_limit. * We use OFDM 6M as criterion and its eirp txpower @@ -4264,6 +4272,7 @@ void rt2800_vco_calibration(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 7, rfcsr); break; case RF3053: + case RF3070: case RF3290: case RF5360: case RF5370: @@ -4405,6 +4414,7 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) rt2x00_rt(rt2x00dev, RT3290) || rt2x00_rt(rt2x00dev, RT3390) || rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5392) || rt2x00_rt(rt2x00dev, RT5592)) @@ -4412,8 +4422,8 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev) else vgc = 0x2e + rt2x00dev->lna_gain; } else { /* 5GHZ band */ - if (rt2x00_rt(rt2x00dev, RT3572)) - vgc = 0x22 + (rt2x00dev->lna_gain * 5) / 3; + if (rt2x00_rt(rt2x00dev, RT3593)) + vgc = 0x20 + (rt2x00dev->lna_gain * 5) / 3; else if (rt2x00_rt(rt2x00dev, RT5592)) vgc = 0x24 + (2 * rt2x00dev->lna_gain); else { @@ -4431,11 +4441,17 @@ static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, u8 vgc_level) { if (qual->vgc_level != vgc_level) { - if (rt2x00_rt(rt2x00dev, RT5592)) { + if (rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593)) { + rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, + vgc_level); + } else if (rt2x00_rt(rt2x00dev, RT5592)) { rt2800_bbp_write(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a); rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level); - } else + } else { rt2800_bbp_write(rt2x00dev, 66, vgc_level); + } + qual->vgc_level = vgc_level; qual->vgc_level_reg = vgc_level; } @@ -4454,17 +4470,35 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C)) return; - /* - * When RSSI is better then -80 increase VGC level with 0x10, except - * for rt5592 chip. + + /* When RSSI is better than a certain threshold, increase VGC + * with a chip specific value in order to improve the balance + * between sensibility and noise isolation. */ vgc = rt2800_get_default_vgc(rt2x00dev); - if (rt2x00_rt(rt2x00dev, RT5592) && qual->rssi > -65) - vgc += 0x20; - else if (qual->rssi > -80) - vgc += 0x10; + switch (rt2x00dev->chip.rt) { + case RT3572: + case RT3593: + if (qual->rssi > -65) { + if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ) + vgc += 0x20; + else + vgc += 0x10; + } + break; + + case RT5592: + if (qual->rssi > -65) + vgc += 0x20; + break; + + default: + if (qual->rssi > -80) + vgc += 0x10; + break; + } rt2800_set_vgc(rt2x00dev, qual, vgc); } @@ -5489,7 +5523,7 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev) ant = (div_mode == 3) ? 1 : 0; /* check if this is a Bluetooth combo card */ - if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_bt_coexist(rt2x00dev)) { u32 reg; rt2800_register_read(rt2x00dev, GPIO_CTRL, ®); @@ -5798,7 +5832,7 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev) rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { - if (!test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_external_lna_bg(rt2x00dev)) rt2x00_set_field8(&rfcsr, RFCSR17_R, 1); } @@ -5985,7 +6019,7 @@ static void rt2800_init_rfcsr_30xx(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 20, 0xba); rt2800_rfcsr_write(rt2x00dev, 21, 0xdb); rt2800_rfcsr_write(rt2x00dev, 24, 0x16); - rt2800_rfcsr_write(rt2x00dev, 25, 0x01); + rt2800_rfcsr_write(rt2x00dev, 25, 0x03); rt2800_rfcsr_write(rt2x00dev, 29, 0x1f); if (rt2x00_rt_rev_lt(rt2x00dev, RT3070, REV_RT3070F)) { @@ -6441,7 +6475,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 28, 0x00); rt2800_rfcsr_write(rt2x00dev, 29, 0x10); - rt2800_rfcsr_write(rt2x00dev, 30, 0x00); + rt2800_rfcsr_write(rt2x00dev, 30, 0x10); rt2800_rfcsr_write(rt2x00dev, 31, 0x80); rt2800_rfcsr_write(rt2x00dev, 32, 0x80); rt2800_rfcsr_write(rt2x00dev, 33, 0x00); @@ -6479,7 +6513,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev) rt2800_rfcsr_write(rt2x00dev, 56, 0x22); rt2800_rfcsr_write(rt2x00dev, 57, 0x80); rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); - rt2800_rfcsr_write(rt2x00dev, 59, 0x63); + rt2800_rfcsr_write(rt2x00dev, 59, 0x8f); rt2800_rfcsr_write(rt2x00dev, 60, 0x45); if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) @@ -6499,7 +6533,6 @@ static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev) rt2800_rf_init_calibration(rt2x00dev, 2); rt2800_rfcsr_write(rt2x00dev, 1, 0x17); - rt2800_rfcsr_write(rt2x00dev, 2, 0x80); rt2800_rfcsr_write(rt2x00dev, 3, 0x88); rt2800_rfcsr_write(rt2x00dev, 5, 0x10); rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); @@ -6653,17 +6686,20 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) u16 word; /* - * Initialize all registers. + * Initialize MAC registers. */ if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || rt2800_init_registers(rt2x00dev))) return -EIO; + /* + * Wait BBP/RF to wake up. + */ if (unlikely(rt2800_wait_bbp_rf_ready(rt2x00dev))) return -EIO; /* - * Send signal to firmware during boot time. + * Send signal during boot time to initialize firmware. */ rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); @@ -6672,9 +6708,15 @@ int rt2800_enable_radio(struct rt2x00_dev *rt2x00dev) rt2800_mcu_request(rt2x00dev, MCU_BOOT_SIGNAL, 0, 0, 0); msleep(1); + /* + * Make sure BBP is up and running. + */ if (unlikely(rt2800_wait_bbp_ready(rt2x00dev))) return -EIO; + /* + * Initialize BBP/RF registers. + */ rt2800_init_bbp(rt2x00dev); rt2800_init_rfcsr(rt2x00dev); @@ -7021,6 +7063,7 @@ static int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) case RF3022: case RF3052: case RF3053: + case RF3070: case RF3290: case RF3320: case RF3322: @@ -7203,7 +7246,7 @@ static const struct rf_channel rf_vals[] = { /* * RF value list for rt3xxx - * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052) + * Supports: 2.4 GHz (all) & 5.2 GHz (RF3052 & RF3053) */ static const struct rf_channel rf_vals_3x[] = { {1, 241, 2, 2 }, @@ -7399,72 +7442,6 @@ static const struct rf_channel rf_vals_5592_xtal40[] = { {196, 83, 0, 12, 1}, }; -static const struct rf_channel rf_vals_3053[] = { - /* Channel, N, R, K */ - {1, 241, 2, 2}, - {2, 241, 2, 7}, - {3, 242, 2, 2}, - {4, 242, 2, 7}, - {5, 243, 2, 2}, - {6, 243, 2, 7}, - {7, 244, 2, 2}, - {8, 244, 2, 7}, - {9, 245, 2, 2}, - {10, 245, 2, 7}, - {11, 246, 2, 2}, - {12, 246, 2, 7}, - {13, 247, 2, 2}, - {14, 248, 2, 4}, - - {36, 0x56, 0, 4}, - {38, 0x56, 0, 6}, - {40, 0x56, 0, 8}, - {44, 0x57, 0, 0}, - {46, 0x57, 0, 2}, - {48, 0x57, 0, 4}, - {52, 0x57, 0, 8}, - {54, 0x57, 0, 10}, - {56, 0x58, 0, 0}, - {60, 0x58, 0, 4}, - {62, 0x58, 0, 6}, - {64, 0x58, 0, 8}, - - {100, 0x5B, 0, 8}, - {102, 0x5B, 0, 10}, - {104, 0x5C, 0, 0}, - {108, 0x5C, 0, 4}, - {110, 0x5C, 0, 6}, - {112, 0x5C, 0, 8}, - - /* NOTE: Channel 114 has been removed intentionally. - * The EEPROM contains no TX power values for that, - * and it is disabled in the vendor driver as well. - */ - - {116, 0x5D, 0, 0}, - {118, 0x5D, 0, 2}, - {120, 0x5D, 0, 4}, - {124, 0x5D, 0, 8}, - {126, 0x5D, 0, 10}, - {128, 0x5E, 0, 0}, - {132, 0x5E, 0, 4}, - {134, 0x5E, 0, 6}, - {136, 0x5E, 0, 8}, - {140, 0x5F, 0, 0}, - - {149, 0x5F, 0, 9}, - {151, 0x5F, 0, 11}, - {153, 0x60, 0, 1}, - {157, 0x60, 0, 5}, - {159, 0x60, 0, 7}, - {161, 0x60, 0, 9}, - {165, 0x61, 0, 1}, - {167, 0x61, 0, 3}, - {169, 0x61, 0, 5}, - {171, 0x61, 0, 7}, - {173, 0x61, 0, 9}, -}; - static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) { struct hw_mode_spec *spec = &rt2x00dev->spec; @@ -7473,7 +7450,6 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) char *default_power2; char *default_power3; unsigned int i; - u16 eeprom; u32 reg; /* @@ -7522,48 +7498,48 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) rt2x00dev->hw->max_report_rates = 7; rt2x00dev->hw->max_rate_tries = 1; - rt2800_eeprom_read(rt2x00dev, EEPROM_NIC_CONF0, &eeprom); - /* * Initialize hw_mode information. */ - spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (rt2x00_rf(rt2x00dev, RF2820) || - rt2x00_rf(rt2x00dev, RF2720)) { + switch (rt2x00dev->chip.rf) { + case RF2720: + case RF2820: spec->num_channels = 14; spec->channels = rf_vals; - } else if (rt2x00_rf(rt2x00dev, RF2850) || - rt2x00_rf(rt2x00dev, RF2750)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; + break; + + case RF2750: + case RF2850: spec->num_channels = ARRAY_SIZE(rf_vals); spec->channels = rf_vals; - } else if (rt2x00_rf(rt2x00dev, RF3020) || - rt2x00_rf(rt2x00dev, RF2020) || - rt2x00_rf(rt2x00dev, RF3021) || - rt2x00_rf(rt2x00dev, RF3022) || - rt2x00_rf(rt2x00dev, RF3290) || - rt2x00_rf(rt2x00dev, RF3320) || - rt2x00_rf(rt2x00dev, RF3322) || - rt2x00_rf(rt2x00dev, RF5360) || - rt2x00_rf(rt2x00dev, RF5370) || - rt2x00_rf(rt2x00dev, RF5372) || - rt2x00_rf(rt2x00dev, RF5390) || - rt2x00_rf(rt2x00dev, RF5392)) { + break; + + case RF2020: + case RF3020: + case RF3021: + case RF3022: + case RF3070: + case RF3290: + case RF3320: + case RF3322: + case RF5360: + case RF5370: + case RF5372: + case RF5390: + case RF5392: spec->num_channels = 14; spec->channels = rf_vals_3x; - } else if (rt2x00_rf(rt2x00dev, RF3052)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; + break; + + case RF3052: + case RF3053: spec->num_channels = ARRAY_SIZE(rf_vals_3x); spec->channels = rf_vals_3x; - } else if (rt2x00_rf(rt2x00dev, RF3053)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; - spec->num_channels = ARRAY_SIZE(rf_vals_3053); - spec->channels = rf_vals_3053; - } else if (rt2x00_rf(rt2x00dev, RF5592)) { - spec->supported_bands |= SUPPORT_BAND_5GHZ; + break; + case RF5592: rt2800_register_read(rt2x00dev, MAC_DEBUG_INDEX, ®); if (rt2x00_get_field32(reg, MAC_DEBUG_INDEX_XTAL)) { spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal40); @@ -7572,11 +7548,16 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->num_channels = ARRAY_SIZE(rf_vals_5592_xtal20); spec->channels = rf_vals_5592_xtal20; } + break; } if (WARN_ON_ONCE(!spec->channels)) return -ENODEV; + spec->supported_bands = SUPPORT_BAND_2GHZ; + if (spec->num_channels > 14) + spec->supported_bands |= SUPPORT_BAND_5GHZ; + /* * Initialize HT information. */ @@ -7591,22 +7572,21 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40; - if (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) >= 2) + if (rt2x00dev->default_ant.tx_chain_num >= 2) spec->ht.cap |= IEEE80211_HT_CAP_TX_STBC; - spec->ht.cap |= - rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH) << - IEEE80211_HT_CAP_RX_STBC_SHIFT; + spec->ht.cap |= rt2x00dev->default_ant.rx_chain_num << + IEEE80211_HT_CAP_RX_STBC_SHIFT; spec->ht.ampdu_factor = 3; spec->ht.ampdu_density = 4; spec->ht.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED | IEEE80211_HT_MCS_TX_RX_DIFF | - ((rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_TXPATH) - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + ((rt2x00dev->default_ant.tx_chain_num - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - switch (rt2x00_get_field16(eeprom, EEPROM_NIC_CONF0_RXPATH)) { + switch (rt2x00dev->default_ant.rx_chain_num) { case 3: spec->ht.mcs.rx_mask[2] = 0xff; case 2: @@ -7671,6 +7651,7 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) case RF3320: case RF3052: case RF3053: + case RF3070: case RF3290: case RF5360: case RF5370: diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.c b/drivers/net/wireless/rt2x00/rt2800mmio.c new file mode 100644 index 0000000000000000000000000000000000000000..ae152280e071621df4b9ccb93a4d614ed4ec50b1 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800mmio.c @@ -0,0 +1,873 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn + * Copyright (C) 2009 Alban Browaeys + * Copyright (C) 2009 Felix Fietkau + * Copyright (C) 2009 Luis Correia + * Copyright (C) 2009 Mattias Nissler + * Copyright (C) 2009 Mark Asselstine + * Copyright (C) 2009 Xose Vazquez Perez + * Copyright (C) 2009 Bart Zolnierkiewicz + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Module: rt2800mmio + * Abstract: rt2800 MMIO device routines. + */ + +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2800.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" + +/* + * TX descriptor initialization + */ +__le32 *rt2800mmio_get_txwi(struct queue_entry *entry) +{ + return (__le32 *) entry->skb->data; +} +EXPORT_SYMBOL_GPL(rt2800mmio_get_txwi); + +void rt2800mmio_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc) +{ + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *txd = entry_priv->desc; + u32 word; + const unsigned int txwi_size = entry->queue->winfo_size; + + /* + * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 + * must contains a TXWI structure + 802.11 header + padding + 802.11 + * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and + * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 + * data. It means that LAST_SEC0 is always 0. + */ + + /* + * Initialize TX descriptor + */ + word = 0; + rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); + rt2x00_desc_write(txd, 0, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, + !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_BURST, + test_bit(ENTRY_TXD_BURST, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); + rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); + rt2x00_desc_write(txd, 1, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W2_SD_PTR1, + skbdesc->skb_dma + txwi_size); + rt2x00_desc_write(txd, 2, word); + + word = 0; + rt2x00_set_field32(&word, TXD_W3_WIV, + !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); + rt2x00_set_field32(&word, TXD_W3_QSEL, 2); + rt2x00_desc_write(txd, 3, word); + + /* + * Register descriptor details in skb frame descriptor. + */ + skbdesc->desc = txd; + skbdesc->desc_len = TXD_DESC_SIZE; +} +EXPORT_SYMBOL_GPL(rt2800mmio_write_tx_desc); + +/* + * RX control handlers + */ +void rt2800mmio_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + __le32 *rxd = entry_priv->desc; + u32 word; + + rt2x00_desc_read(rxd, 3, &word); + + if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) + rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; + + /* + * Unfortunately we don't know the cipher type used during + * decryption. This prevents us from correct providing + * correct statistics through debugfs. + */ + rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); + + if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { + /* + * Hardware has stripped IV/EIV data from 802.11 frame during + * decryption. Unfortunately the descriptor doesn't contain + * any fields with the EIV/IV data either, so they can't + * be restored by rt2x00lib. + */ + rxdesc->flags |= RX_FLAG_IV_STRIPPED; + + /* + * The hardware has already checked the Michael Mic and has + * stripped it from the frame. Signal this to mac80211. + */ + rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; + + if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) + rxdesc->flags |= RX_FLAG_DECRYPTED; + else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) + rxdesc->flags |= RX_FLAG_MMIC_ERROR; + } + + if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) + rxdesc->dev_flags |= RXDONE_MY_BSS; + + if (rt2x00_get_field32(word, RXD_W3_L2PAD)) + rxdesc->dev_flags |= RXDONE_L2PAD; + + /* + * Process the RXWI structure that is at the start of the buffer. + */ + rt2800_process_rxwi(entry, rxdesc); +} +EXPORT_SYMBOL_GPL(rt2800mmio_fill_rxdone); + +/* + * Interrupt functions. + */ +static void rt2800mmio_wakeup(struct rt2x00_dev *rt2x00dev) +{ + struct ieee80211_conf conf = { .flags = 0 }; + struct rt2x00lib_conf libconf = { .conf = &conf }; + + rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); +} + +static bool rt2800mmio_txdone_entry_check(struct queue_entry *entry, u32 status) +{ + __le32 *txwi; + u32 word; + int wcid, tx_wcid; + + wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); + + txwi = rt2800_drv_get_txwi(entry); + rt2x00_desc_read(txwi, 1, &word); + tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); + + return (tx_wcid == wcid); +} + +static bool rt2800mmio_txdone_find_entry(struct queue_entry *entry, void *data) +{ + u32 status = *(u32 *)data; + + /* + * rt2800pci hardware might reorder frames when exchanging traffic + * with multiple BA enabled STAs. + * + * For example, a tx queue + * [ STA1 | STA2 | STA1 | STA2 ] + * can result in tx status reports + * [ STA1 | STA1 | STA2 | STA2 ] + * when the hw decides to aggregate the frames for STA1 into one AMPDU. + * + * To mitigate this effect, associate the tx status to the first frame + * in the tx queue with a matching wcid. + */ + if (rt2800mmio_txdone_entry_check(entry, status) && + !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + /* + * Got a matching frame, associate the tx status with + * the frame + */ + entry->status = status; + set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); + return true; + } + + /* Check the next frame */ + return false; +} + +static bool rt2800mmio_txdone_match_first(struct queue_entry *entry, void *data) +{ + u32 status = *(u32 *)data; + + /* + * Find the first frame without tx status and assign this status to it + * regardless if it matches or not. + */ + if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + /* + * Got a matching frame, associate the tx status with + * the frame + */ + entry->status = status; + set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); + return true; + } + + /* Check the next frame */ + return false; +} +static bool rt2800mmio_txdone_release_entries(struct queue_entry *entry, + void *data) +{ + if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { + rt2800_txdone_entry(entry, entry->status, + rt2800mmio_get_txwi(entry)); + return false; + } + + /* No more frames to release */ + return true; +} + +static bool rt2800mmio_txdone(struct rt2x00_dev *rt2x00dev) +{ + struct data_queue *queue; + u32 status; + u8 qid; + int max_tx_done = 16; + + while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { + qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); + if (unlikely(qid >= QID_RX)) { + /* + * Unknown queue, this shouldn't happen. Just drop + * this tx status. + */ + rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", + qid); + break; + } + + queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); + if (unlikely(queue == NULL)) { + /* + * The queue is NULL, this shouldn't happen. Stop + * processing here and drop the tx status + */ + rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", + qid); + break; + } + + if (unlikely(rt2x00queue_empty(queue))) { + /* + * The queue is empty. Stop processing here + * and drop the tx status. + */ + rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", + qid); + break; + } + + /* + * Let's associate this tx status with the first + * matching frame. + */ + if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, &status, + rt2800mmio_txdone_find_entry)) { + /* + * We cannot match the tx status to any frame, so just + * use the first one. + */ + if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, &status, + rt2800mmio_txdone_match_first)) { + rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", + qid); + break; + } + } + + /* + * Release all frames with a valid tx status. + */ + rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, + Q_INDEX, NULL, + rt2800mmio_txdone_release_entries); + + if (--max_tx_done == 0) + break; + } + + return !max_tx_done; +} + +static inline void rt2800mmio_enable_interrupt(struct rt2x00_dev *rt2x00dev, + struct rt2x00_field32 irq_field) +{ + u32 reg; + + /* + * Enable a single interrupt. The interrupt mask register + * access needs locking. + */ + spin_lock_irq(&rt2x00dev->irqmask_lock); + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + rt2x00_set_field32(®, irq_field, 1); + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock_irq(&rt2x00dev->irqmask_lock); +} + +void rt2800mmio_txstatus_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2800mmio_txdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->txstatus_tasklet); + + /* + * No need to enable the tx status interrupt here as we always + * leave it enabled to minimize the possibility of a tx status + * register overflow. See comment in interrupt handler. + */ +} +EXPORT_SYMBOL_GPL(rt2800mmio_txstatus_tasklet); + +void rt2800mmio_pretbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2x00lib_pretbtt(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); +} +EXPORT_SYMBOL_GPL(rt2800mmio_pretbtt_tasklet); + +void rt2800mmio_tbtt_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; + u32 reg; + + rt2x00lib_beacondone(rt2x00dev); + + if (rt2x00dev->intf_ap_count) { + /* + * The rt2800pci hardware tbtt timer is off by 1us per tbtt + * causing beacon skew and as a result causing problems with + * some powersaving clients over time. Shorten the beacon + * interval every 64 beacons by 64us to mitigate this effect. + */ + if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + (rt2x00dev->beacon_int * 16) - 1); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, + (rt2x00dev->beacon_int * 16)); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + } + drv_data->tbtt_tick++; + drv_data->tbtt_tick %= BCN_TBTT_OFFSET; + } + + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); +} +EXPORT_SYMBOL_GPL(rt2800mmio_tbtt_tasklet); + +void rt2800mmio_rxdone_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + if (rt2x00mmio_rxdone(rt2x00dev)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); +} +EXPORT_SYMBOL_GPL(rt2800mmio_rxdone_tasklet); + +void rt2800mmio_autowake_tasklet(unsigned long data) +{ + struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; + rt2800mmio_wakeup(rt2x00dev); + if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + rt2800mmio_enable_interrupt(rt2x00dev, + INT_MASK_CSR_AUTO_WAKEUP); +} +EXPORT_SYMBOL_GPL(rt2800mmio_autowake_tasklet); + +static void rt2800mmio_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) +{ + u32 status; + int i; + + /* + * The TX_FIFO_STATUS interrupt needs special care. We should + * read TX_STA_FIFO but we should do it immediately as otherwise + * the register can overflow and we would lose status reports. + * + * Hence, read the TX_STA_FIFO register and copy all tx status + * reports into a kernel FIFO which is handled in the txstatus + * tasklet. We use a tasklet to process the tx status reports + * because we can schedule the tasklet multiple times (when the + * interrupt fires again during tx status processing). + * + * Furthermore we don't disable the TX_FIFO_STATUS + * interrupt here but leave it enabled so that the TX_STA_FIFO + * can also be read while the tx status tasklet gets executed. + * + * Since we have only one producer and one consumer we don't + * need to lock the kfifo. + */ + for (i = 0; i < rt2x00dev->tx->limit; i++) { + rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); + + if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) + break; + + if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) { + rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); + break; + } + } + + /* Schedule the tasklet for processing the tx status. */ + tasklet_schedule(&rt2x00dev->txstatus_tasklet); +} + +irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance) +{ + struct rt2x00_dev *rt2x00dev = dev_instance; + u32 reg, mask; + + /* Read status and ACK all interrupts */ + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + + if (!reg) + return IRQ_NONE; + + if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) + return IRQ_HANDLED; + + /* + * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits + * for interrupts and interrupt masks we can just use the value of + * INT_SOURCE_CSR to create the interrupt mask. + */ + mask = ~reg; + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { + rt2800mmio_txstatus_interrupt(rt2x00dev); + /* + * Never disable the TX_FIFO_STATUS interrupt. + */ + rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); + } + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) + tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) + tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) + tasklet_schedule(&rt2x00dev->rxdone_tasklet); + + if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) + tasklet_schedule(&rt2x00dev->autowake_tasklet); + + /* + * Disable all interrupts for which a tasklet was scheduled right now, + * the tasklet will reenable the appropriate interrupts. + */ + spin_lock(&rt2x00dev->irqmask_lock); + rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); + reg &= mask; + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock(&rt2x00dev->irqmask_lock); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL_GPL(rt2800mmio_interrupt); + +void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + u32 reg; + unsigned long flags; + + /* + * When interrupts are being enabled, the interrupt registers + * should clear the register to assure a clean state. + */ + if (state == STATE_RADIO_IRQ_ON) { + rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); + rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); + } + + spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); + reg = 0; + if (state == STATE_RADIO_IRQ_ON) { + rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); + rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); + rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); + } + rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); + spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); + + if (state == STATE_RADIO_IRQ_OFF) { + /* + * Wait for possibly running tasklets to finish. + */ + tasklet_kill(&rt2x00dev->txstatus_tasklet); + tasklet_kill(&rt2x00dev->rxdone_tasklet); + tasklet_kill(&rt2x00dev->autowake_tasklet); + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_toggle_irq); + +/* + * Queue handlers. + */ +void rt2800mmio_start_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); + rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); + rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_start_queue); + +void rt2800mmio_kick_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + struct queue_entry *entry; + + switch (queue->qid) { + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + entry = rt2x00queue_get_entry(queue, Q_INDEX); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), + entry->entry_idx); + break; + case QID_MGMT: + entry = rt2x00queue_get_entry(queue, Q_INDEX); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5), + entry->entry_idx); + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_kick_queue); + +void rt2800mmio_stop_queue(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + u32 reg; + + switch (queue->qid) { + case QID_RX: + rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); + rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + break; + case QID_BEACON: + rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); + rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); + rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); + rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); + + rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); + rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); + rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); + + /* + * Wait for current invocation to finish. The tasklet + * won't be scheduled anymore afterwards since we disabled + * the TBTT and PRE TBTT timer. + */ + tasklet_kill(&rt2x00dev->tbtt_tasklet); + tasklet_kill(&rt2x00dev->pretbtt_tasklet); + + break; + default: + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_stop_queue); + +void rt2800mmio_queue_init(struct data_queue *queue) +{ + struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; + unsigned short txwi_size, rxwi_size; + + rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); + + switch (queue->qid) { + case QID_RX: + queue->limit = 128; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = RXD_DESC_SIZE; + queue->winfo_size = rxwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_AC_VO: + case QID_AC_VI: + case QID_AC_BE: + case QID_AC_BK: + queue->limit = 64; + queue->data_size = AGGREGATION_SIZE; + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_BEACON: + queue->limit = 8; + queue->data_size = 0; /* No DMA required for beacons */ + queue->desc_size = TXD_DESC_SIZE; + queue->winfo_size = txwi_size; + queue->priv_size = sizeof(struct queue_entry_priv_mmio); + break; + + case QID_ATIM: + /* fallthrough */ + default: + BUG(); + break; + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_queue_init); + +/* + * Initialization functions. + */ +bool rt2800mmio_get_entry_state(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + + return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_get_entry_state); + +void rt2800mmio_clear_entry(struct queue_entry *entry) +{ + struct queue_entry_priv_mmio *entry_priv = entry->priv_data; + struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); + struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; + u32 word; + + if (entry->queue->qid == QID_RX) { + rt2x00_desc_read(entry_priv->desc, 0, &word); + rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); + rt2x00_desc_write(entry_priv->desc, 0, word); + + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); + rt2x00_desc_write(entry_priv->desc, 1, word); + + /* + * Set RX IDX in register to inform hardware that we have + * handled this entry and it is available for reuse again. + */ + rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, + entry->entry_idx); + } else { + rt2x00_desc_read(entry_priv->desc, 1, &word); + rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); + rt2x00_desc_write(entry_priv->desc, 1, word); + } +} +EXPORT_SYMBOL_GPL(rt2800mmio_clear_entry); + +int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev) +{ + struct queue_entry_priv_mmio *entry_priv; + + /* + * Initialize registers. + */ + entry_priv = rt2x00dev->tx[0].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0, + rt2x00dev->tx[0].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0); + + entry_priv = rt2x00dev->tx[1].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1, + rt2x00dev->tx[1].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0); + + entry_priv = rt2x00dev->tx[2].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2, + rt2x00dev->tx[2].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0); + + entry_priv = rt2x00dev->tx[3].entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3, + rt2x00dev->tx[3].limit); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0); + + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0); + + rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0); + rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0); + + entry_priv = rt2x00dev->rx->entries[0].priv_data; + rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR, + entry_priv->desc_dma); + rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT, + rt2x00dev->rx[0].limit); + rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, + rt2x00dev->rx[0].limit - 1); + rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0); + + rt2800_disable_wpdma(rt2x00dev); + + rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800mmio_init_queues); + +int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev) +{ + u32 reg; + + /* + * Reset DMA indexes + */ + rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, ®); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); + rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); + rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg); + + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); + rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); + + if (rt2x00_is_pcie(rt2x00dev) && + (rt2x00_rt(rt2x00dev, RT3090) || + rt2x00_rt(rt2x00dev, RT3390) || + rt2x00_rt(rt2x00dev, RT3572) || + rt2x00_rt(rt2x00dev, RT3593) || + rt2x00_rt(rt2x00dev, RT5390) || + rt2x00_rt(rt2x00dev, RT5392) || + rt2x00_rt(rt2x00dev, RT5592))) { + rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, ®); + rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); + rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); + rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg); + } + + rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); + + reg = 0; + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); + rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); + + rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); + + return 0; +} +EXPORT_SYMBOL_GPL(rt2800mmio_init_registers); + +/* + * Device state switch handlers. + */ +int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev) +{ + /* Wait for DMA, ignore error until we initialize queues. */ + rt2800_wait_wpdma_ready(rt2x00dev); + + if (unlikely(rt2800mmio_init_queues(rt2x00dev))) + return -EIO; + + return rt2800_enable_radio(rt2x00dev); +} +EXPORT_SYMBOL_GPL(rt2800mmio_enable_radio); + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("rt2800 MMIO library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800mmio.h b/drivers/net/wireless/rt2x00/rt2800mmio.h new file mode 100644 index 0000000000000000000000000000000000000000..6a10de3eee3e02826c2ae086cf81b963e513b8ee --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800mmio.h @@ -0,0 +1,165 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn + * Copyright (C) 2009 Alban Browaeys + * Copyright (C) 2009 Felix Fietkau + * Copyright (C) 2009 Luis Correia + * Copyright (C) 2009 Mattias Nissler + * Copyright (C) 2009 Mark Asselstine + * Copyright (C) 2009 Xose Vazquez Perez + * Copyright (C) 2009 Bart Zolnierkiewicz + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Module: rt2800mmio + * Abstract: forward declarations for the rt2800mmio module. + */ + +#ifndef RT2800MMIO_H +#define RT2800MMIO_H + +/* + * Queue register offset macros + */ +#define TX_QUEUE_REG_OFFSET 0x10 +#define TX_BASE_PTR(__x) (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_MAX_CNT(__x) (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_CTX_IDX(__x) (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) +#define TX_DTX_IDX(__x) (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) + +/* + * DMA descriptor defines. + */ +#define TXD_DESC_SIZE (4 * sizeof(__le32)) +#define RXD_DESC_SIZE (4 * sizeof(__le32)) + +/* + * TX descriptor format for TX, PRIO and Beacon Ring. + */ + +/* + * Word0 + */ +#define TXD_W0_SD_PTR0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define TXD_W1_SD_LEN1 FIELD32(0x00003fff) +#define TXD_W1_LAST_SEC1 FIELD32(0x00004000) +#define TXD_W1_BURST FIELD32(0x00008000) +#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) +#define TXD_W1_LAST_SEC0 FIELD32(0x40000000) +#define TXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define TXD_W2_SD_PTR1 FIELD32(0xffffffff) + +/* + * Word3 + * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI + * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. + * 0:MGMT, 1:HCCA 2:EDCA + */ +#define TXD_W3_WIV FIELD32(0x01000000) +#define TXD_W3_QSEL FIELD32(0x06000000) +#define TXD_W3_TCO FIELD32(0x20000000) +#define TXD_W3_UCO FIELD32(0x40000000) +#define TXD_W3_ICO FIELD32(0x80000000) + +/* + * RX descriptor format for RX Ring. + */ + +/* + * Word0 + */ +#define RXD_W0_SDP0 FIELD32(0xffffffff) + +/* + * Word1 + */ +#define RXD_W1_SDL1 FIELD32(0x00003fff) +#define RXD_W1_SDL0 FIELD32(0x3fff0000) +#define RXD_W1_LS0 FIELD32(0x40000000) +#define RXD_W1_DMA_DONE FIELD32(0x80000000) + +/* + * Word2 + */ +#define RXD_W2_SDP1 FIELD32(0xffffffff) + +/* + * Word3 + * AMSDU: RX with 802.3 header, not 802.11 header. + * DECRYPTED: This frame is being decrypted. + */ +#define RXD_W3_BA FIELD32(0x00000001) +#define RXD_W3_DATA FIELD32(0x00000002) +#define RXD_W3_NULLDATA FIELD32(0x00000004) +#define RXD_W3_FRAG FIELD32(0x00000008) +#define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010) +#define RXD_W3_MULTICAST FIELD32(0x00000020) +#define RXD_W3_BROADCAST FIELD32(0x00000040) +#define RXD_W3_MY_BSS FIELD32(0x00000080) +#define RXD_W3_CRC_ERROR FIELD32(0x00000100) +#define RXD_W3_CIPHER_ERROR FIELD32(0x00000600) +#define RXD_W3_AMSDU FIELD32(0x00000800) +#define RXD_W3_HTC FIELD32(0x00001000) +#define RXD_W3_RSSI FIELD32(0x00002000) +#define RXD_W3_L2PAD FIELD32(0x00004000) +#define RXD_W3_AMPDU FIELD32(0x00008000) +#define RXD_W3_DECRYPTED FIELD32(0x00010000) +#define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000) +#define RXD_W3_PLCP_RSSI FIELD32(0x00040000) + +/* TX descriptor initialization */ +__le32 *rt2800mmio_get_txwi(struct queue_entry *entry); +void rt2800mmio_write_tx_desc(struct queue_entry *entry, + struct txentry_desc *txdesc); + +/* RX control handlers */ +void rt2800mmio_fill_rxdone(struct queue_entry *entry, + struct rxdone_entry_desc *rxdesc); + +/* Interrupt functions */ +void rt2800mmio_txstatus_tasklet(unsigned long data); +void rt2800mmio_pretbtt_tasklet(unsigned long data); +void rt2800mmio_tbtt_tasklet(unsigned long data); +void rt2800mmio_rxdone_tasklet(unsigned long data); +void rt2800mmio_autowake_tasklet(unsigned long data); +irqreturn_t rt2800mmio_interrupt(int irq, void *dev_instance); +void rt2800mmio_toggle_irq(struct rt2x00_dev *rt2x00dev, + enum dev_state state); + +/* Queue handlers */ +void rt2800mmio_start_queue(struct data_queue *queue); +void rt2800mmio_kick_queue(struct data_queue *queue); +void rt2800mmio_stop_queue(struct data_queue *queue); +void rt2800mmio_queue_init(struct data_queue *queue); + +/* Initialization functions */ +bool rt2800mmio_get_entry_state(struct queue_entry *entry); +void rt2800mmio_clear_entry(struct queue_entry *entry); +int rt2800mmio_init_queues(struct rt2x00_dev *rt2x00dev); +int rt2800mmio_init_registers(struct rt2x00_dev *rt2x00dev); + +/* Device state switch handlers. */ +int rt2800mmio_enable_radio(struct rt2x00_dev *rt2x00dev); + +#endif /* RT2800MMIO_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index f8f2abbfbb6554f1f432c3e7a8b51facb5d9adec..b504455b4fec10c69c4cc09c1f44be4b92ac3be4 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -37,14 +37,13 @@ #include #include #include -#include #include #include "rt2x00.h" #include "rt2x00mmio.h" #include "rt2x00pci.h" -#include "rt2x00soc.h" #include "rt2800lib.h" +#include "rt2800mmio.h" #include "rt2800.h" #include "rt2800pci.h" @@ -90,27 +89,6 @@ static void rt2800pci_mcu_status(struct rt2x00_dev *rt2x00dev, const u8 token) rt2x00mmio_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); } -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) -static int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) -{ - void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); - - if (!base_addr) - return -ENOMEM; - - memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); - - iounmap(base_addr); - return 0; -} -#else -static inline int rt2800pci_read_eeprom_soc(struct rt2x00_dev *rt2x00dev) -{ - return -ENOMEM; -} -#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */ - -#ifdef CONFIG_PCI static void rt2800pci_eepromregister_read(struct eeprom_93cx6 *eeprom) { struct rt2x00_dev *rt2x00dev = eeprom->data; @@ -183,112 +161,6 @@ static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) { return rt2800_read_eeprom_efuse(rt2x00dev); } -#else -static inline int rt2800pci_read_eeprom_pci(struct rt2x00_dev *rt2x00dev) -{ - return -EOPNOTSUPP; -} - -static inline int rt2800pci_efuse_detect(struct rt2x00_dev *rt2x00dev) -{ - return 0; -} - -static inline int rt2800pci_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev) -{ - return -EOPNOTSUPP; -} -#endif /* CONFIG_PCI */ - -/* - * Queue handlers. - */ -static void rt2800pci_start_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 1); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 1); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 1); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); - rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 1); - rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); - break; - default: - break; - } -} - -static void rt2800pci_kick_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - struct queue_entry *entry; - - switch (queue->qid) { - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - entry = rt2x00queue_get_entry(queue, Q_INDEX); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(queue->qid), - entry->entry_idx); - break; - case QID_MGMT: - entry = rt2x00queue_get_entry(queue, Q_INDEX); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX(5), - entry->entry_idx); - break; - default: - break; - } -} - -static void rt2800pci_stop_queue(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - u32 reg; - - switch (queue->qid) { - case QID_RX: - rt2x00mmio_register_read(rt2x00dev, MAC_SYS_CTRL, ®); - rt2x00_set_field32(®, MAC_SYS_CTRL_ENABLE_RX, 0); - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - break; - case QID_BEACON: - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_TSF_TICKING, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_TBTT_ENABLE, 0); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_GEN, 0); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - - rt2x00mmio_register_read(rt2x00dev, INT_TIMER_EN, ®); - rt2x00_set_field32(®, INT_TIMER_EN_PRE_TBTT_TIMER, 0); - rt2x00mmio_register_write(rt2x00dev, INT_TIMER_EN, reg); - - /* - * Wait for current invocation to finish. The tasklet - * won't be scheduled anymore afterwards since we disabled - * the TBTT and PRE TBTT timer. - */ - tasklet_kill(&rt2x00dev->tbtt_tasklet); - tasklet_kill(&rt2x00dev->pretbtt_tasklet); - - break; - default: - break; - } -} /* * Firmware functions @@ -331,218 +203,14 @@ static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev, return 0; } -/* - * Initialization functions. - */ -static bool rt2800pci_get_entry_state(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 1, &word); - - return (!rt2x00_get_field32(word, RXD_W1_DMA_DONE)); - } else { - rt2x00_desc_read(entry_priv->desc, 1, &word); - - return (!rt2x00_get_field32(word, TXD_W1_DMA_DONE)); - } -} - -static void rt2800pci_clear_entry(struct queue_entry *entry) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev; - u32 word; - - if (entry->queue->qid == QID_RX) { - rt2x00_desc_read(entry_priv->desc, 0, &word); - rt2x00_set_field32(&word, RXD_W0_SDP0, skbdesc->skb_dma); - rt2x00_desc_write(entry_priv->desc, 0, word); - - rt2x00_desc_read(entry_priv->desc, 1, &word); - rt2x00_set_field32(&word, RXD_W1_DMA_DONE, 0); - rt2x00_desc_write(entry_priv->desc, 1, word); - - /* - * Set RX IDX in register to inform hardware that we have - * handled this entry and it is available for reuse again. - */ - rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, - entry->entry_idx); - } else { - rt2x00_desc_read(entry_priv->desc, 1, &word); - rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 1); - rt2x00_desc_write(entry_priv->desc, 1, word); - } -} - -static int rt2800pci_init_queues(struct rt2x00_dev *rt2x00dev) -{ - struct queue_entry_priv_mmio *entry_priv; - - /* - * Initialize registers. - */ - entry_priv = rt2x00dev->tx[0].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR0, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT0, - rt2x00dev->tx[0].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX0, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX0, 0); - - entry_priv = rt2x00dev->tx[1].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR1, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT1, - rt2x00dev->tx[1].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX1, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX1, 0); - - entry_priv = rt2x00dev->tx[2].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR2, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT2, - rt2x00dev->tx[2].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX2, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX2, 0); - - entry_priv = rt2x00dev->tx[3].entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR3, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT3, - rt2x00dev->tx[3].limit); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX3, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX3, 0); - - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR4, 0); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT4, 0); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX4, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX4, 0); - - rt2x00mmio_register_write(rt2x00dev, TX_BASE_PTR5, 0); - rt2x00mmio_register_write(rt2x00dev, TX_MAX_CNT5, 0); - rt2x00mmio_register_write(rt2x00dev, TX_CTX_IDX5, 0); - rt2x00mmio_register_write(rt2x00dev, TX_DTX_IDX5, 0); - - entry_priv = rt2x00dev->rx->entries[0].priv_data; - rt2x00mmio_register_write(rt2x00dev, RX_BASE_PTR, - entry_priv->desc_dma); - rt2x00mmio_register_write(rt2x00dev, RX_MAX_CNT, - rt2x00dev->rx[0].limit); - rt2x00mmio_register_write(rt2x00dev, RX_CRX_IDX, - rt2x00dev->rx[0].limit - 1); - rt2x00mmio_register_write(rt2x00dev, RX_DRX_IDX, 0); - - rt2800_disable_wpdma(rt2x00dev); - - rt2x00mmio_register_write(rt2x00dev, DELAY_INT_CFG, 0); - - return 0; -} - /* * Device state switch handlers. */ -static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, - enum dev_state state) -{ - u32 reg; - unsigned long flags; - - /* - * When interrupts are being enabled, the interrupt registers - * should clear the register to assure a clean state. - */ - if (state == STATE_RADIO_IRQ_ON) { - rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); - } - - spin_lock_irqsave(&rt2x00dev->irqmask_lock, flags); - reg = 0; - if (state == STATE_RADIO_IRQ_ON) { - rt2x00_set_field32(®, INT_MASK_CSR_RX_DONE, 1); - rt2x00_set_field32(®, INT_MASK_CSR_TBTT, 1); - rt2x00_set_field32(®, INT_MASK_CSR_PRE_TBTT, 1); - rt2x00_set_field32(®, INT_MASK_CSR_TX_FIFO_STATUS, 1); - rt2x00_set_field32(®, INT_MASK_CSR_AUTO_WAKEUP, 1); - } - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - spin_unlock_irqrestore(&rt2x00dev->irqmask_lock, flags); - - if (state == STATE_RADIO_IRQ_OFF) { - /* - * Wait for possibly running tasklets to finish. - */ - tasklet_kill(&rt2x00dev->txstatus_tasklet); - tasklet_kill(&rt2x00dev->rxdone_tasklet); - tasklet_kill(&rt2x00dev->autowake_tasklet); - tasklet_kill(&rt2x00dev->tbtt_tasklet); - tasklet_kill(&rt2x00dev->pretbtt_tasklet); - } -} - -static int rt2800pci_init_registers(struct rt2x00_dev *rt2x00dev) -{ - u32 reg; - - /* - * Reset DMA indexes - */ - rt2x00mmio_register_read(rt2x00dev, WPDMA_RST_IDX, ®); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX0, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX1, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX2, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX3, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX4, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DTX_IDX5, 1); - rt2x00_set_field32(®, WPDMA_RST_IDX_DRX_IDX0, 1); - rt2x00mmio_register_write(rt2x00dev, WPDMA_RST_IDX, reg); - - rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e1f); - rt2x00mmio_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); - - if (rt2x00_is_pcie(rt2x00dev) && - (rt2x00_rt(rt2x00dev, RT3090) || - rt2x00_rt(rt2x00dev, RT3390) || - rt2x00_rt(rt2x00dev, RT3572) || - rt2x00_rt(rt2x00dev, RT3593) || - rt2x00_rt(rt2x00dev, RT5390) || - rt2x00_rt(rt2x00dev, RT5392) || - rt2x00_rt(rt2x00dev, RT5592))) { - rt2x00mmio_register_read(rt2x00dev, AUX_CTRL, ®); - rt2x00_set_field32(®, AUX_CTRL_FORCE_PCIE_CLK, 1); - rt2x00_set_field32(®, AUX_CTRL_WAKE_PCIE_EN, 1); - rt2x00mmio_register_write(rt2x00dev, AUX_CTRL, reg); - } - - rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000003); - - reg = 0; - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_CSR, 1); - rt2x00_set_field32(®, MAC_SYS_CTRL_RESET_BBP, 1); - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, reg); - - rt2x00mmio_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); - - return 0; -} - static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) { int retval; - /* Wait for DMA, ignore error until we initialize queues. */ - rt2800_wait_wpdma_ready(rt2x00dev); - - if (unlikely(rt2800pci_init_queues(rt2x00dev))) - return -EIO; - - retval = rt2800_enable_radio(rt2x00dev); + retval = rt2800mmio_enable_radio(rt2x00dev); if (retval) return retval; @@ -559,15 +227,6 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) return retval; } -static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev) -{ - if (rt2x00_is_soc(rt2x00dev)) { - rt2800_disable_radio(rt2x00dev); - rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0); - rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0); - } -} - static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, enum dev_state state) { @@ -601,12 +260,11 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, * After the radio has been disabled, the device should * be put to sleep for powersaving. */ - rt2800pci_disable_radio(rt2x00dev); rt2800pci_set_state(rt2x00dev, STATE_SLEEP); break; case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_OFF: - rt2800pci_toggle_irq(rt2x00dev, state); + rt2800mmio_toggle_irq(rt2x00dev, state); break; case STATE_DEEP_SLEEP: case STATE_SLEEP: @@ -626,470 +284,6 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev, return retval; } -/* - * TX descriptor initialization - */ -static __le32 *rt2800pci_get_txwi(struct queue_entry *entry) -{ - return (__le32 *) entry->skb->data; -} - -static void rt2800pci_write_tx_desc(struct queue_entry *entry, - struct txentry_desc *txdesc) -{ - struct skb_frame_desc *skbdesc = get_skb_frame_desc(entry->skb); - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - __le32 *txd = entry_priv->desc; - u32 word; - const unsigned int txwi_size = entry->queue->winfo_size; - - /* - * The buffers pointed by SD_PTR0/SD_LEN0 and SD_PTR1/SD_LEN1 - * must contains a TXWI structure + 802.11 header + padding + 802.11 - * data. We choose to have SD_PTR0/SD_LEN0 only contains TXWI and - * SD_PTR1/SD_LEN1 contains 802.11 header + padding + 802.11 - * data. It means that LAST_SEC0 is always 0. - */ - - /* - * Initialize TX descriptor - */ - word = 0; - rt2x00_set_field32(&word, TXD_W0_SD_PTR0, skbdesc->skb_dma); - rt2x00_desc_write(txd, 0, word); - - word = 0; - rt2x00_set_field32(&word, TXD_W1_SD_LEN1, entry->skb->len); - rt2x00_set_field32(&word, TXD_W1_LAST_SEC1, - !test_bit(ENTRY_TXD_MORE_FRAG, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W1_BURST, - test_bit(ENTRY_TXD_BURST, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W1_SD_LEN0, txwi_size); - rt2x00_set_field32(&word, TXD_W1_LAST_SEC0, 0); - rt2x00_set_field32(&word, TXD_W1_DMA_DONE, 0); - rt2x00_desc_write(txd, 1, word); - - word = 0; - rt2x00_set_field32(&word, TXD_W2_SD_PTR1, - skbdesc->skb_dma + txwi_size); - rt2x00_desc_write(txd, 2, word); - - word = 0; - rt2x00_set_field32(&word, TXD_W3_WIV, - !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc->flags)); - rt2x00_set_field32(&word, TXD_W3_QSEL, 2); - rt2x00_desc_write(txd, 3, word); - - /* - * Register descriptor details in skb frame descriptor. - */ - skbdesc->desc = txd; - skbdesc->desc_len = TXD_DESC_SIZE; -} - -/* - * RX control handlers - */ -static void rt2800pci_fill_rxdone(struct queue_entry *entry, - struct rxdone_entry_desc *rxdesc) -{ - struct queue_entry_priv_mmio *entry_priv = entry->priv_data; - __le32 *rxd = entry_priv->desc; - u32 word; - - rt2x00_desc_read(rxd, 3, &word); - - if (rt2x00_get_field32(word, RXD_W3_CRC_ERROR)) - rxdesc->flags |= RX_FLAG_FAILED_FCS_CRC; - - /* - * Unfortunately we don't know the cipher type used during - * decryption. This prevents us from correct providing - * correct statistics through debugfs. - */ - rxdesc->cipher_status = rt2x00_get_field32(word, RXD_W3_CIPHER_ERROR); - - if (rt2x00_get_field32(word, RXD_W3_DECRYPTED)) { - /* - * Hardware has stripped IV/EIV data from 802.11 frame during - * decryption. Unfortunately the descriptor doesn't contain - * any fields with the EIV/IV data either, so they can't - * be restored by rt2x00lib. - */ - rxdesc->flags |= RX_FLAG_IV_STRIPPED; - - /* - * The hardware has already checked the Michael Mic and has - * stripped it from the frame. Signal this to mac80211. - */ - rxdesc->flags |= RX_FLAG_MMIC_STRIPPED; - - if (rxdesc->cipher_status == RX_CRYPTO_SUCCESS) - rxdesc->flags |= RX_FLAG_DECRYPTED; - else if (rxdesc->cipher_status == RX_CRYPTO_FAIL_MIC) - rxdesc->flags |= RX_FLAG_MMIC_ERROR; - } - - if (rt2x00_get_field32(word, RXD_W3_MY_BSS)) - rxdesc->dev_flags |= RXDONE_MY_BSS; - - if (rt2x00_get_field32(word, RXD_W3_L2PAD)) - rxdesc->dev_flags |= RXDONE_L2PAD; - - /* - * Process the RXWI structure that is at the start of the buffer. - */ - rt2800_process_rxwi(entry, rxdesc); -} - -/* - * Interrupt functions. - */ -static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev) -{ - struct ieee80211_conf conf = { .flags = 0 }; - struct rt2x00lib_conf libconf = { .conf = &conf }; - - rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); -} - -static bool rt2800pci_txdone_entry_check(struct queue_entry *entry, u32 status) -{ - __le32 *txwi; - u32 word; - int wcid, tx_wcid; - - wcid = rt2x00_get_field32(status, TX_STA_FIFO_WCID); - - txwi = rt2800_drv_get_txwi(entry); - rt2x00_desc_read(txwi, 1, &word); - tx_wcid = rt2x00_get_field32(word, TXWI_W1_WIRELESS_CLI_ID); - - return (tx_wcid == wcid); -} - -static bool rt2800pci_txdone_find_entry(struct queue_entry *entry, void *data) -{ - u32 status = *(u32 *)data; - - /* - * rt2800pci hardware might reorder frames when exchanging traffic - * with multiple BA enabled STAs. - * - * For example, a tx queue - * [ STA1 | STA2 | STA1 | STA2 ] - * can result in tx status reports - * [ STA1 | STA1 | STA2 | STA2 ] - * when the hw decides to aggregate the frames for STA1 into one AMPDU. - * - * To mitigate this effect, associate the tx status to the first frame - * in the tx queue with a matching wcid. - */ - if (rt2800pci_txdone_entry_check(entry, status) && - !test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - /* - * Got a matching frame, associate the tx status with - * the frame - */ - entry->status = status; - set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); - return true; - } - - /* Check the next frame */ - return false; -} - -static bool rt2800pci_txdone_match_first(struct queue_entry *entry, void *data) -{ - u32 status = *(u32 *)data; - - /* - * Find the first frame without tx status and assign this status to it - * regardless if it matches or not. - */ - if (!test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - /* - * Got a matching frame, associate the tx status with - * the frame - */ - entry->status = status; - set_bit(ENTRY_DATA_STATUS_SET, &entry->flags); - return true; - } - - /* Check the next frame */ - return false; -} -static bool rt2800pci_txdone_release_entries(struct queue_entry *entry, - void *data) -{ - if (test_bit(ENTRY_DATA_STATUS_SET, &entry->flags)) { - rt2800_txdone_entry(entry, entry->status, - rt2800pci_get_txwi(entry)); - return false; - } - - /* No more frames to release */ - return true; -} - -static bool rt2800pci_txdone(struct rt2x00_dev *rt2x00dev) -{ - struct data_queue *queue; - u32 status; - u8 qid; - int max_tx_done = 16; - - while (kfifo_get(&rt2x00dev->txstatus_fifo, &status)) { - qid = rt2x00_get_field32(status, TX_STA_FIFO_PID_QUEUE); - if (unlikely(qid >= QID_RX)) { - /* - * Unknown queue, this shouldn't happen. Just drop - * this tx status. - */ - rt2x00_warn(rt2x00dev, "Got TX status report with unexpected pid %u, dropping\n", - qid); - break; - } - - queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); - if (unlikely(queue == NULL)) { - /* - * The queue is NULL, this shouldn't happen. Stop - * processing here and drop the tx status - */ - rt2x00_warn(rt2x00dev, "Got TX status for an unavailable queue %u, dropping\n", - qid); - break; - } - - if (unlikely(rt2x00queue_empty(queue))) { - /* - * The queue is empty. Stop processing here - * and drop the tx status. - */ - rt2x00_warn(rt2x00dev, "Got TX status for an empty queue %u, dropping\n", - qid); - break; - } - - /* - * Let's associate this tx status with the first - * matching frame. - */ - if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, &status, - rt2800pci_txdone_find_entry)) { - /* - * We cannot match the tx status to any frame, so just - * use the first one. - */ - if (!rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, &status, - rt2800pci_txdone_match_first)) { - rt2x00_warn(rt2x00dev, "No frame found for TX status on queue %u, dropping\n", - qid); - break; - } - } - - /* - * Release all frames with a valid tx status. - */ - rt2x00queue_for_each_entry(queue, Q_INDEX_DONE, - Q_INDEX, NULL, - rt2800pci_txdone_release_entries); - - if (--max_tx_done == 0) - break; - } - - return !max_tx_done; -} - -static inline void rt2800pci_enable_interrupt(struct rt2x00_dev *rt2x00dev, - struct rt2x00_field32 irq_field) -{ - u32 reg; - - /* - * Enable a single interrupt. The interrupt mask register - * access needs locking. - */ - spin_lock_irq(&rt2x00dev->irqmask_lock); - rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); - rt2x00_set_field32(®, irq_field, 1); - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - spin_unlock_irq(&rt2x00dev->irqmask_lock); -} - -static void rt2800pci_txstatus_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2800pci_txdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->txstatus_tasklet); - - /* - * No need to enable the tx status interrupt here as we always - * leave it enabled to minimize the possibility of a tx status - * register overflow. See comment in interrupt handler. - */ -} - -static void rt2800pci_pretbtt_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt2x00lib_pretbtt(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_PRE_TBTT); -} - -static void rt2800pci_tbtt_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - struct rt2800_drv_data *drv_data = rt2x00dev->drv_data; - u32 reg; - - rt2x00lib_beacondone(rt2x00dev); - - if (rt2x00dev->intf_ap_count) { - /* - * The rt2800pci hardware tbtt timer is off by 1us per tbtt - * causing beacon skew and as a result causing problems with - * some powersaving clients over time. Shorten the beacon - * interval every 64 beacons by 64us to mitigate this effect. - */ - if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 2)) { - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, - (rt2x00dev->beacon_int * 16) - 1); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - } else if (drv_data->tbtt_tick == (BCN_TBTT_OFFSET - 1)) { - rt2x00mmio_register_read(rt2x00dev, BCN_TIME_CFG, ®); - rt2x00_set_field32(®, BCN_TIME_CFG_BEACON_INTERVAL, - (rt2x00dev->beacon_int * 16)); - rt2x00mmio_register_write(rt2x00dev, BCN_TIME_CFG, reg); - } - drv_data->tbtt_tick++; - drv_data->tbtt_tick %= BCN_TBTT_OFFSET; - } - - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_TBTT); -} - -static void rt2800pci_rxdone_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - if (rt2x00mmio_rxdone(rt2x00dev)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - else if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_RX_DONE); -} - -static void rt2800pci_autowake_tasklet(unsigned long data) -{ - struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data; - rt2800pci_wakeup(rt2x00dev); - if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - rt2800pci_enable_interrupt(rt2x00dev, INT_MASK_CSR_AUTO_WAKEUP); -} - -static void rt2800pci_txstatus_interrupt(struct rt2x00_dev *rt2x00dev) -{ - u32 status; - int i; - - /* - * The TX_FIFO_STATUS interrupt needs special care. We should - * read TX_STA_FIFO but we should do it immediately as otherwise - * the register can overflow and we would lose status reports. - * - * Hence, read the TX_STA_FIFO register and copy all tx status - * reports into a kernel FIFO which is handled in the txstatus - * tasklet. We use a tasklet to process the tx status reports - * because we can schedule the tasklet multiple times (when the - * interrupt fires again during tx status processing). - * - * Furthermore we don't disable the TX_FIFO_STATUS - * interrupt here but leave it enabled so that the TX_STA_FIFO - * can also be read while the tx status tasklet gets executed. - * - * Since we have only one producer and one consumer we don't - * need to lock the kfifo. - */ - for (i = 0; i < rt2x00dev->tx->limit; i++) { - rt2x00mmio_register_read(rt2x00dev, TX_STA_FIFO, &status); - - if (!rt2x00_get_field32(status, TX_STA_FIFO_VALID)) - break; - - if (!kfifo_put(&rt2x00dev->txstatus_fifo, &status)) { - rt2x00_warn(rt2x00dev, "TX status FIFO overrun, drop tx status report\n"); - break; - } - } - - /* Schedule the tasklet for processing the tx status. */ - tasklet_schedule(&rt2x00dev->txstatus_tasklet); -} - -static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) -{ - struct rt2x00_dev *rt2x00dev = dev_instance; - u32 reg, mask; - - /* Read status and ACK all interrupts */ - rt2x00mmio_register_read(rt2x00dev, INT_SOURCE_CSR, ®); - rt2x00mmio_register_write(rt2x00dev, INT_SOURCE_CSR, reg); - - if (!reg) - return IRQ_NONE; - - if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) - return IRQ_HANDLED; - - /* - * Since INT_MASK_CSR and INT_SOURCE_CSR use the same bits - * for interrupts and interrupt masks we can just use the value of - * INT_SOURCE_CSR to create the interrupt mask. - */ - mask = ~reg; - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) { - rt2800pci_txstatus_interrupt(rt2x00dev); - /* - * Never disable the TX_FIFO_STATUS interrupt. - */ - rt2x00_set_field32(&mask, INT_MASK_CSR_TX_FIFO_STATUS, 1); - } - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT)) - tasklet_hi_schedule(&rt2x00dev->pretbtt_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT)) - tasklet_hi_schedule(&rt2x00dev->tbtt_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE)) - tasklet_schedule(&rt2x00dev->rxdone_tasklet); - - if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) - tasklet_schedule(&rt2x00dev->autowake_tasklet); - - /* - * Disable all interrupts for which a tasklet was scheduled right now, - * the tasklet will reenable the appropriate interrupts. - */ - spin_lock(&rt2x00dev->irqmask_lock); - rt2x00mmio_register_read(rt2x00dev, INT_MASK_CSR, ®); - reg &= mask; - rt2x00mmio_register_write(rt2x00dev, INT_MASK_CSR, reg); - spin_unlock(&rt2x00dev->irqmask_lock); - - return IRQ_HANDLED; -} - /* * Device probe functions. */ @@ -1097,9 +291,7 @@ static int rt2800pci_read_eeprom(struct rt2x00_dev *rt2x00dev) { int retval; - if (rt2x00_is_soc(rt2x00dev)) - retval = rt2800pci_read_eeprom_soc(rt2x00dev); - else if (rt2800pci_efuse_detect(rt2x00dev)) + if (rt2800pci_efuse_detect(rt2x00dev)) retval = rt2800pci_read_eeprom_efuse(rt2x00dev); else retval = rt2800pci_read_eeprom_pci(rt2x00dev); @@ -1145,25 +337,25 @@ static const struct rt2800_ops rt2800pci_rt2800_ops = { .read_eeprom = rt2800pci_read_eeprom, .hwcrypt_disabled = rt2800pci_hwcrypt_disabled, .drv_write_firmware = rt2800pci_write_firmware, - .drv_init_registers = rt2800pci_init_registers, - .drv_get_txwi = rt2800pci_get_txwi, + .drv_init_registers = rt2800mmio_init_registers, + .drv_get_txwi = rt2800mmio_get_txwi, }; static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { - .irq_handler = rt2800pci_interrupt, - .txstatus_tasklet = rt2800pci_txstatus_tasklet, - .pretbtt_tasklet = rt2800pci_pretbtt_tasklet, - .tbtt_tasklet = rt2800pci_tbtt_tasklet, - .rxdone_tasklet = rt2800pci_rxdone_tasklet, - .autowake_tasklet = rt2800pci_autowake_tasklet, + .irq_handler = rt2800mmio_interrupt, + .txstatus_tasklet = rt2800mmio_txstatus_tasklet, + .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, + .tbtt_tasklet = rt2800mmio_tbtt_tasklet, + .rxdone_tasklet = rt2800mmio_rxdone_tasklet, + .autowake_tasklet = rt2800mmio_autowake_tasklet, .probe_hw = rt2800_probe_hw, .get_firmware_name = rt2800pci_get_firmware_name, .check_firmware = rt2800_check_firmware, .load_firmware = rt2800_load_firmware, .initialize = rt2x00mmio_initialize, .uninitialize = rt2x00mmio_uninitialize, - .get_entry_state = rt2800pci_get_entry_state, - .clear_entry = rt2800pci_clear_entry, + .get_entry_state = rt2800mmio_get_entry_state, + .clear_entry = rt2800mmio_clear_entry, .set_device_state = rt2800pci_set_device_state, .rfkill_poll = rt2800_rfkill_poll, .link_stats = rt2800_link_stats, @@ -1171,15 +363,15 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .link_tuner = rt2800_link_tuner, .gain_calibration = rt2800_gain_calibration, .vco_calibration = rt2800_vco_calibration, - .start_queue = rt2800pci_start_queue, - .kick_queue = rt2800pci_kick_queue, - .stop_queue = rt2800pci_stop_queue, + .start_queue = rt2800mmio_start_queue, + .kick_queue = rt2800mmio_kick_queue, + .stop_queue = rt2800mmio_stop_queue, .flush_queue = rt2x00mmio_flush_queue, - .write_tx_desc = rt2800pci_write_tx_desc, + .write_tx_desc = rt2800mmio_write_tx_desc, .write_tx_data = rt2800_write_tx_data, .write_beacon = rt2800_write_beacon, .clear_beacon = rt2800_clear_beacon, - .fill_rxdone = rt2800pci_fill_rxdone, + .fill_rxdone = rt2800mmio_fill_rxdone, .config_shared_key = rt2800_config_shared_key, .config_pairwise_key = rt2800_config_pairwise_key, .config_filter = rt2800_config_filter, @@ -1191,49 +383,6 @@ static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { .sta_remove = rt2800_sta_remove, }; -static void rt2800pci_queue_init(struct data_queue *queue) -{ - struct rt2x00_dev *rt2x00dev = queue->rt2x00dev; - unsigned short txwi_size, rxwi_size; - - rt2800_get_txwi_rxwi_size(rt2x00dev, &txwi_size, &rxwi_size); - - switch (queue->qid) { - case QID_RX: - queue->limit = 128; - queue->data_size = AGGREGATION_SIZE; - queue->desc_size = RXD_DESC_SIZE; - queue->winfo_size = rxwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_AC_VO: - case QID_AC_VI: - case QID_AC_BE: - case QID_AC_BK: - queue->limit = 64; - queue->data_size = AGGREGATION_SIZE; - queue->desc_size = TXD_DESC_SIZE; - queue->winfo_size = txwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_BEACON: - queue->limit = 8; - queue->data_size = 0; /* No DMA required for beacons */ - queue->desc_size = TXD_DESC_SIZE; - queue->winfo_size = txwi_size; - queue->priv_size = sizeof(struct queue_entry_priv_mmio); - break; - - case QID_ATIM: - /* fallthrough */ - default: - BUG(); - break; - } -} - static const struct rt2x00_ops rt2800pci_ops = { .name = KBUILD_MODNAME, .drv_data_size = sizeof(struct rt2800_drv_data), @@ -1241,7 +390,7 @@ static const struct rt2x00_ops rt2800pci_ops = { .eeprom_size = EEPROM_SIZE, .rf_size = RF_SIZE, .tx_queues = NUM_TX_QUEUES, - .queue_init = rt2800pci_queue_init, + .queue_init = rt2800mmio_queue_init, .lib = &rt2800pci_rt2x00_ops, .drv = &rt2800pci_rt2800_ops, .hw = &rt2800pci_mac80211_ops, @@ -1253,7 +402,6 @@ static const struct rt2x00_ops rt2800pci_ops = { /* * RT2800pci module information. */ -#ifdef CONFIG_PCI static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = { { PCI_DEVICE(0x1814, 0x0601) }, { PCI_DEVICE(0x1814, 0x0681) }, @@ -1298,38 +446,15 @@ static DEFINE_PCI_DEVICE_TABLE(rt2800pci_device_table) = { #endif { 0, } }; -#endif /* CONFIG_PCI */ MODULE_AUTHOR(DRV_PROJECT); MODULE_VERSION(DRV_VERSION); MODULE_DESCRIPTION("Ralink RT2800 PCI & PCMCIA Wireless LAN driver."); MODULE_SUPPORTED_DEVICE("Ralink RT2860 PCI & PCMCIA chipset based cards"); -#ifdef CONFIG_PCI MODULE_FIRMWARE(FIRMWARE_RT2860); MODULE_DEVICE_TABLE(pci, rt2800pci_device_table); -#endif /* CONFIG_PCI */ MODULE_LICENSE("GPL"); -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) -static int rt2800soc_probe(struct platform_device *pdev) -{ - return rt2x00soc_probe(pdev, &rt2800pci_ops); -} - -static struct platform_driver rt2800soc_driver = { - .driver = { - .name = "rt2800_wmac", - .owner = THIS_MODULE, - .mod_name = KBUILD_MODNAME, - }, - .probe = rt2800soc_probe, - .remove = rt2x00soc_remove, - .suspend = rt2x00soc_suspend, - .resume = rt2x00soc_resume, -}; -#endif /* CONFIG_SOC_RT288X || CONFIG_SOC_RT305X */ - -#ifdef CONFIG_PCI static int rt2800pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { @@ -1344,39 +469,5 @@ static struct pci_driver rt2800pci_driver = { .suspend = rt2x00pci_suspend, .resume = rt2x00pci_resume, }; -#endif /* CONFIG_PCI */ - -static int __init rt2800pci_init(void) -{ - int ret = 0; - -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) - ret = platform_driver_register(&rt2800soc_driver); - if (ret) - return ret; -#endif -#ifdef CONFIG_PCI - ret = pci_register_driver(&rt2800pci_driver); - if (ret) { -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) - platform_driver_unregister(&rt2800soc_driver); -#endif - return ret; - } -#endif - - return ret; -} - -static void __exit rt2800pci_exit(void) -{ -#ifdef CONFIG_PCI - pci_unregister_driver(&rt2800pci_driver); -#endif -#if defined(CONFIG_SOC_RT288X) || defined(CONFIG_SOC_RT305X) - platform_driver_unregister(&rt2800soc_driver); -#endif -} -module_init(rt2800pci_init); -module_exit(rt2800pci_exit); +module_pci_driver(rt2800pci_driver); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.h b/drivers/net/wireless/rt2x00/rt2800pci.h index ab22a087c50dba15cf67b3ff237f8915bc8e2996..a81c9ee281c075dbf267e6b4b9b9c78de912c315 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.h +++ b/drivers/net/wireless/rt2x00/rt2800pci.h @@ -34,15 +34,6 @@ #ifndef RT2800PCI_H #define RT2800PCI_H -/* - * Queue register offset macros - */ -#define TX_QUEUE_REG_OFFSET 0x10 -#define TX_BASE_PTR(__x) (TX_BASE_PTR0 + ((__x) * TX_QUEUE_REG_OFFSET)) -#define TX_MAX_CNT(__x) (TX_MAX_CNT0 + ((__x) * TX_QUEUE_REG_OFFSET)) -#define TX_CTX_IDX(__x) (TX_CTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) -#define TX_DTX_IDX(__x) (TX_DTX_IDX0 + ((__x) * TX_QUEUE_REG_OFFSET)) - /* * 8051 firmware image. */ @@ -50,92 +41,4 @@ #define FIRMWARE_RT3290 "rt3290.bin" #define FIRMWARE_IMAGE_BASE 0x2000 -/* - * DMA descriptor defines. - */ -#define TXD_DESC_SIZE (4 * sizeof(__le32)) -#define RXD_DESC_SIZE (4 * sizeof(__le32)) - -/* - * TX descriptor format for TX, PRIO and Beacon Ring. - */ - -/* - * Word0 - */ -#define TXD_W0_SD_PTR0 FIELD32(0xffffffff) - -/* - * Word1 - */ -#define TXD_W1_SD_LEN1 FIELD32(0x00003fff) -#define TXD_W1_LAST_SEC1 FIELD32(0x00004000) -#define TXD_W1_BURST FIELD32(0x00008000) -#define TXD_W1_SD_LEN0 FIELD32(0x3fff0000) -#define TXD_W1_LAST_SEC0 FIELD32(0x40000000) -#define TXD_W1_DMA_DONE FIELD32(0x80000000) - -/* - * Word2 - */ -#define TXD_W2_SD_PTR1 FIELD32(0xffffffff) - -/* - * Word3 - * WIV: Wireless Info Valid. 1: Driver filled WI, 0: DMA needs to copy WI - * QSEL: Select on-chip FIFO ID for 2nd-stage output scheduler. - * 0:MGMT, 1:HCCA 2:EDCA - */ -#define TXD_W3_WIV FIELD32(0x01000000) -#define TXD_W3_QSEL FIELD32(0x06000000) -#define TXD_W3_TCO FIELD32(0x20000000) -#define TXD_W3_UCO FIELD32(0x40000000) -#define TXD_W3_ICO FIELD32(0x80000000) - -/* - * RX descriptor format for RX Ring. - */ - -/* - * Word0 - */ -#define RXD_W0_SDP0 FIELD32(0xffffffff) - -/* - * Word1 - */ -#define RXD_W1_SDL1 FIELD32(0x00003fff) -#define RXD_W1_SDL0 FIELD32(0x3fff0000) -#define RXD_W1_LS0 FIELD32(0x40000000) -#define RXD_W1_DMA_DONE FIELD32(0x80000000) - -/* - * Word2 - */ -#define RXD_W2_SDP1 FIELD32(0xffffffff) - -/* - * Word3 - * AMSDU: RX with 802.3 header, not 802.11 header. - * DECRYPTED: This frame is being decrypted. - */ -#define RXD_W3_BA FIELD32(0x00000001) -#define RXD_W3_DATA FIELD32(0x00000002) -#define RXD_W3_NULLDATA FIELD32(0x00000004) -#define RXD_W3_FRAG FIELD32(0x00000008) -#define RXD_W3_UNICAST_TO_ME FIELD32(0x00000010) -#define RXD_W3_MULTICAST FIELD32(0x00000020) -#define RXD_W3_BROADCAST FIELD32(0x00000040) -#define RXD_W3_MY_BSS FIELD32(0x00000080) -#define RXD_W3_CRC_ERROR FIELD32(0x00000100) -#define RXD_W3_CIPHER_ERROR FIELD32(0x00000600) -#define RXD_W3_AMSDU FIELD32(0x00000800) -#define RXD_W3_HTC FIELD32(0x00001000) -#define RXD_W3_RSSI FIELD32(0x00002000) -#define RXD_W3_L2PAD FIELD32(0x00004000) -#define RXD_W3_AMPDU FIELD32(0x00008000) -#define RXD_W3_DECRYPTED FIELD32(0x00010000) -#define RXD_W3_PLCP_SIGNAL FIELD32(0x00020000) -#define RXD_W3_PLCP_RSSI FIELD32(0x00040000) - #endif /* RT2800PCI_H */ diff --git a/drivers/net/wireless/rt2x00/rt2800soc.c b/drivers/net/wireless/rt2x00/rt2800soc.c new file mode 100644 index 0000000000000000000000000000000000000000..1359227ca41122d55825a191b9b4a1aa605a9c46 --- /dev/null +++ b/drivers/net/wireless/rt2x00/rt2800soc.c @@ -0,0 +1,263 @@ +/* Copyright (C) 2009 - 2010 Ivo van Doorn + * Copyright (C) 2009 Alban Browaeys + * Copyright (C) 2009 Felix Fietkau + * Copyright (C) 2009 Luis Correia + * Copyright (C) 2009 Mattias Nissler + * Copyright (C) 2009 Mark Asselstine + * Copyright (C) 2009 Xose Vazquez Perez + * Copyright (C) 2009 Bart Zolnierkiewicz + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the + * Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Module: rt2800soc + * Abstract: rt2800 WiSoC specific routines. + */ + +#include +#include +#include +#include +#include + +#include "rt2x00.h" +#include "rt2x00mmio.h" +#include "rt2x00soc.h" +#include "rt2800.h" +#include "rt2800lib.h" +#include "rt2800mmio.h" + +/* Allow hardware encryption to be disabled. */ +static bool modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, bool, S_IRUGO); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption."); + +static bool rt2800soc_hwcrypt_disabled(struct rt2x00_dev *rt2x00dev) +{ + return modparam_nohwcrypt; +} + +static void rt2800soc_disable_radio(struct rt2x00_dev *rt2x00dev) +{ + rt2800_disable_radio(rt2x00dev); + rt2x00mmio_register_write(rt2x00dev, PWR_PIN_CFG, 0); + rt2x00mmio_register_write(rt2x00dev, TX_PIN_CFG, 0); +} + +static int rt2800soc_set_device_state(struct rt2x00_dev *rt2x00dev, + enum dev_state state) +{ + int retval = 0; + + switch (state) { + case STATE_RADIO_ON: + retval = rt2800mmio_enable_radio(rt2x00dev); + break; + + case STATE_RADIO_OFF: + rt2800soc_disable_radio(rt2x00dev); + break; + + case STATE_RADIO_IRQ_ON: + case STATE_RADIO_IRQ_OFF: + rt2800mmio_toggle_irq(rt2x00dev, state); + break; + + case STATE_DEEP_SLEEP: + case STATE_SLEEP: + case STATE_STANDBY: + case STATE_AWAKE: + /* These states are not supported, but don't report an error */ + retval = 0; + break; + + default: + retval = -ENOTSUPP; + break; + } + + if (unlikely(retval)) + rt2x00_err(rt2x00dev, "Device failed to enter state %d (%d)\n", + state, retval); + + return retval; +} + +static int rt2800soc_read_eeprom(struct rt2x00_dev *rt2x00dev) +{ + void __iomem *base_addr = ioremap(0x1F040000, EEPROM_SIZE); + + if (!base_addr) + return -ENOMEM; + + memcpy_fromio(rt2x00dev->eeprom, base_addr, EEPROM_SIZE); + + iounmap(base_addr); + return 0; +} + +/* Firmware functions */ +static char *rt2800soc_get_firmware_name(struct rt2x00_dev *rt2x00dev) +{ + WARN_ON_ONCE(1); + return NULL; +} + +static int rt2800soc_load_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static int rt2800soc_check_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static int rt2800soc_write_firmware(struct rt2x00_dev *rt2x00dev, + const u8 *data, const size_t len) +{ + WARN_ON_ONCE(1); + return 0; +} + +static const struct ieee80211_ops rt2800soc_mac80211_ops = { + .tx = rt2x00mac_tx, + .start = rt2x00mac_start, + .stop = rt2x00mac_stop, + .add_interface = rt2x00mac_add_interface, + .remove_interface = rt2x00mac_remove_interface, + .config = rt2x00mac_config, + .configure_filter = rt2x00mac_configure_filter, + .set_key = rt2x00mac_set_key, + .sw_scan_start = rt2x00mac_sw_scan_start, + .sw_scan_complete = rt2x00mac_sw_scan_complete, + .get_stats = rt2x00mac_get_stats, + .get_tkip_seq = rt2800_get_tkip_seq, + .set_rts_threshold = rt2800_set_rts_threshold, + .sta_add = rt2x00mac_sta_add, + .sta_remove = rt2x00mac_sta_remove, + .bss_info_changed = rt2x00mac_bss_info_changed, + .conf_tx = rt2800_conf_tx, + .get_tsf = rt2800_get_tsf, + .rfkill_poll = rt2x00mac_rfkill_poll, + .ampdu_action = rt2800_ampdu_action, + .flush = rt2x00mac_flush, + .get_survey = rt2800_get_survey, + .get_ringparam = rt2x00mac_get_ringparam, + .tx_frames_pending = rt2x00mac_tx_frames_pending, +}; + +static const struct rt2800_ops rt2800soc_rt2800_ops = { + .register_read = rt2x00mmio_register_read, + .register_read_lock = rt2x00mmio_register_read, /* same for SoCs */ + .register_write = rt2x00mmio_register_write, + .register_write_lock = rt2x00mmio_register_write, /* same for SoCs */ + .register_multiread = rt2x00mmio_register_multiread, + .register_multiwrite = rt2x00mmio_register_multiwrite, + .regbusy_read = rt2x00mmio_regbusy_read, + .read_eeprom = rt2800soc_read_eeprom, + .hwcrypt_disabled = rt2800soc_hwcrypt_disabled, + .drv_write_firmware = rt2800soc_write_firmware, + .drv_init_registers = rt2800mmio_init_registers, + .drv_get_txwi = rt2800mmio_get_txwi, +}; + +static const struct rt2x00lib_ops rt2800soc_rt2x00_ops = { + .irq_handler = rt2800mmio_interrupt, + .txstatus_tasklet = rt2800mmio_txstatus_tasklet, + .pretbtt_tasklet = rt2800mmio_pretbtt_tasklet, + .tbtt_tasklet = rt2800mmio_tbtt_tasklet, + .rxdone_tasklet = rt2800mmio_rxdone_tasklet, + .autowake_tasklet = rt2800mmio_autowake_tasklet, + .probe_hw = rt2800_probe_hw, + .get_firmware_name = rt2800soc_get_firmware_name, + .check_firmware = rt2800soc_check_firmware, + .load_firmware = rt2800soc_load_firmware, + .initialize = rt2x00mmio_initialize, + .uninitialize = rt2x00mmio_uninitialize, + .get_entry_state = rt2800mmio_get_entry_state, + .clear_entry = rt2800mmio_clear_entry, + .set_device_state = rt2800soc_set_device_state, + .rfkill_poll = rt2800_rfkill_poll, + .link_stats = rt2800_link_stats, + .reset_tuner = rt2800_reset_tuner, + .link_tuner = rt2800_link_tuner, + .gain_calibration = rt2800_gain_calibration, + .vco_calibration = rt2800_vco_calibration, + .start_queue = rt2800mmio_start_queue, + .kick_queue = rt2800mmio_kick_queue, + .stop_queue = rt2800mmio_stop_queue, + .flush_queue = rt2x00mmio_flush_queue, + .write_tx_desc = rt2800mmio_write_tx_desc, + .write_tx_data = rt2800_write_tx_data, + .write_beacon = rt2800_write_beacon, + .clear_beacon = rt2800_clear_beacon, + .fill_rxdone = rt2800mmio_fill_rxdone, + .config_shared_key = rt2800_config_shared_key, + .config_pairwise_key = rt2800_config_pairwise_key, + .config_filter = rt2800_config_filter, + .config_intf = rt2800_config_intf, + .config_erp = rt2800_config_erp, + .config_ant = rt2800_config_ant, + .config = rt2800_config, + .sta_add = rt2800_sta_add, + .sta_remove = rt2800_sta_remove, +}; + +static const struct rt2x00_ops rt2800soc_ops = { + .name = KBUILD_MODNAME, + .drv_data_size = sizeof(struct rt2800_drv_data), + .max_ap_intf = 8, + .eeprom_size = EEPROM_SIZE, + .rf_size = RF_SIZE, + .tx_queues = NUM_TX_QUEUES, + .queue_init = rt2800mmio_queue_init, + .lib = &rt2800soc_rt2x00_ops, + .drv = &rt2800soc_rt2800_ops, + .hw = &rt2800soc_mac80211_ops, +#ifdef CONFIG_RT2X00_LIB_DEBUGFS + .debugfs = &rt2800_rt2x00debug, +#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ +}; + +static int rt2800soc_probe(struct platform_device *pdev) +{ + return rt2x00soc_probe(pdev, &rt2800soc_ops); +} + +static struct platform_driver rt2800soc_driver = { + .driver = { + .name = "rt2800_wmac", + .owner = THIS_MODULE, + .mod_name = KBUILD_MODNAME, + }, + .probe = rt2800soc_probe, + .remove = rt2x00soc_remove, + .suspend = rt2x00soc_suspend, + .resume = rt2x00soc_resume, +}; + +module_platform_driver(rt2800soc_driver); + +MODULE_AUTHOR(DRV_PROJECT); +MODULE_VERSION(DRV_VERSION); +MODULE_DESCRIPTION("Ralink WiSoC Wireless LAN driver."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 96961b9a395cf0d2e01c32449c4e9bcc55e7828c..997df03a0c2e22abd46bd274bf13f4b1e4f81f01 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -148,6 +148,8 @@ static bool rt2800usb_txstatus_timeout(struct rt2x00_dev *rt2x00dev) return false; } +#define TXSTATUS_READ_INTERVAL 1000000 + static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, int urb_status, u32 tx_status) { @@ -176,8 +178,9 @@ static bool rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev, queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); if (rt2800usb_txstatus_pending(rt2x00dev)) { - /* Read register after 250 us */ - hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 250000), + /* Read register after 1 ms */ + hrtimer_start(&rt2x00dev->txstatus_timer, + ktime_set(0, TXSTATUS_READ_INTERVAL), HRTIMER_MODE_REL); return false; } @@ -202,8 +205,9 @@ static void rt2800usb_async_read_tx_status(struct rt2x00_dev *rt2x00dev) if (test_and_set_bit(TX_STATUS_READING, &rt2x00dev->flags)) return; - /* Read TX_STA_FIFO register after 500 us */ - hrtimer_start(&rt2x00dev->txstatus_timer, ktime_set(0, 500000), + /* Read TX_STA_FIFO register after 2 ms */ + hrtimer_start(&rt2x00dev->txstatus_timer, + ktime_set(0, 2*TXSTATUS_READ_INTERVAL), HRTIMER_MODE_REL); } @@ -1176,6 +1180,8 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Linksys */ { USB_DEVICE(0x13b1, 0x002f) }, { USB_DEVICE(0x1737, 0x0079) }, + /* Logitec */ + { USB_DEVICE(0x0789, 0x0170) }, /* Ralink */ { USB_DEVICE(0x148f, 0x3572) }, /* Sitecom */ @@ -1199,6 +1205,8 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x050d, 0x1103) }, /* Cameo */ { USB_DEVICE(0x148f, 0xf301) }, + /* D-Link */ + { USB_DEVICE(0x2001, 0x3c1f) }, /* Edimax */ { USB_DEVICE(0x7392, 0x7733) }, /* Hawking */ @@ -1212,6 +1220,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0789, 0x016b) }, /* NETGEAR */ { USB_DEVICE(0x0846, 0x9012) }, + { USB_DEVICE(0x0846, 0x9013) }, { USB_DEVICE(0x0846, 0x9019) }, /* Planex */ { USB_DEVICE(0x2019, 0xed19) }, @@ -1220,6 +1229,7 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Sitecom */ { USB_DEVICE(0x0df6, 0x0067) }, { USB_DEVICE(0x0df6, 0x006a) }, + { USB_DEVICE(0x0df6, 0x006e) }, /* ZyXEL */ { USB_DEVICE(0x0586, 0x3421) }, #endif @@ -1236,6 +1246,9 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x2001, 0x3c1c) }, { USB_DEVICE(0x2001, 0x3c1d) }, { USB_DEVICE(0x2001, 0x3c1e) }, + { USB_DEVICE(0x2001, 0x3c20) }, + { USB_DEVICE(0x2001, 0x3c22) }, + { USB_DEVICE(0x2001, 0x3c23) }, /* LG innotek */ { USB_DEVICE(0x043e, 0x7a22) }, { USB_DEVICE(0x043e, 0x7a42) }, @@ -1258,12 +1271,17 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x043e, 0x7a32) }, /* AVM GmbH */ { USB_DEVICE(0x057c, 0x8501) }, - /* D-Link DWA-160-B2 */ + /* Buffalo */ + { USB_DEVICE(0x0411, 0x0241) }, + /* D-Link */ { USB_DEVICE(0x2001, 0x3c1a) }, + { USB_DEVICE(0x2001, 0x3c21) }, /* Proware */ { USB_DEVICE(0x043e, 0x7a13) }, /* Ralink */ { USB_DEVICE(0x148f, 0x5572) }, + /* TRENDnet */ + { USB_DEVICE(0x20f4, 0x724a) }, #endif #ifdef CONFIG_RT2800USB_UNKNOWN /* @@ -1333,6 +1351,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x1d4d, 0x0010) }, /* Planex */ { USB_DEVICE(0x2019, 0xab24) }, + { USB_DEVICE(0x2019, 0xab29) }, /* Qcom */ { USB_DEVICE(0x18e8, 0x6259) }, /* RadioShack */ diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index fe4c572db52c2749317690b75ad4cc296aec5157..e4ba2ce0f212b955f15db89df45d3b32af9942de 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -138,17 +139,6 @@ #define SHORT_EIFS ( SIFS + SHORT_DIFS + \ GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) -/* - * Structure for average calculation - * The avg field contains the actual average value, - * but avg_weight is internally used during calculations - * to prevent rounding errors. - */ -struct avg_val { - int avg; - int avg_weight; -}; - enum rt2x00_chip_intf { RT2X00_CHIP_INTF_PCI, RT2X00_CHIP_INTF_PCIE, @@ -297,7 +287,7 @@ struct link_ant { * Similar to the avg_rssi in the link_qual structure * this value is updated by using the walking average. */ - struct avg_val rssi_ant; + struct ewma rssi_ant; }; /* @@ -326,7 +316,7 @@ struct link { /* * Currently active average RSSI value */ - struct avg_val avg_rssi; + struct ewma avg_rssi; /* * Work structure for scheduling periodic link tuning. @@ -1179,6 +1169,93 @@ static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev) return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC); } +/* Helpers for capability flags */ + +static inline bool +rt2x00_has_cap_flag(struct rt2x00_dev *rt2x00dev, + enum rt2x00_capability_flags cap_flag) +{ + return test_bit(cap_flag, &rt2x00dev->cap_flags); +} + +static inline bool +rt2x00_has_cap_hw_crypto(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_HW_CRYPTO); +} + +static inline bool +rt2x00_has_cap_power_limit(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_POWER_LIMIT); +} + +static inline bool +rt2x00_has_cap_control_filters(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTERS); +} + +static inline bool +rt2x00_has_cap_control_filter_pspoll(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTER_PSPOLL); +} + +static inline bool +rt2x00_has_cap_pre_tbtt_interrupt(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_PRE_TBTT_INTERRUPT); +} + +static inline bool +rt2x00_has_cap_link_tuning(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_LINK_TUNING); +} + +static inline bool +rt2x00_has_cap_frame_type(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_FRAME_TYPE); +} + +static inline bool +rt2x00_has_cap_rf_sequence(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_RF_SEQUENCE); +} + +static inline bool +rt2x00_has_cap_external_lna_a(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_A); +} + +static inline bool +rt2x00_has_cap_external_lna_bg(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_BG); +} + +static inline bool +rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_DOUBLE_ANTENNA); +} + +static inline bool +rt2x00_has_cap_bt_coexist(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_BT_COEXIST); +} + +static inline bool +rt2x00_has_cap_vco_recalibration(struct rt2x00_dev *rt2x00dev) +{ + return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_VCO_RECALIBRATION); +} + /** * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes. * @entry: Pointer to &struct queue_entry diff --git a/drivers/net/wireless/rt2x00/rt2x00crypto.c b/drivers/net/wireless/rt2x00/rt2x00crypto.c index 1ca4c7ffc1898c9ce97d87506b0b8e78464e78ad..3db0d99d9da7a9980f63939186f7c2b7971cd533 100644 --- a/drivers/net/wireless/rt2x00/rt2x00crypto.c +++ b/drivers/net/wireless/rt2x00/rt2x00crypto.c @@ -52,7 +52,7 @@ void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; - if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !hw_key) + if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key) return; __set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); @@ -80,7 +80,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev, struct ieee80211_key_conf *key = tx_info->control.hw_key; unsigned int overhead = 0; - if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !key) + if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key) return overhead; /* diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c index fe7a7f63a9edc2cf75c0a2caa2f6b8ffe02234aa..7f7baae5ae029e6c252676841682cf8fabf270f4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00debug.c +++ b/drivers/net/wireless/rt2x00/rt2x00debug.c @@ -750,7 +750,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev) intf, &rt2x00debug_fop_queue_stats); #ifdef CONFIG_RT2X00_LIB_CRYPTO - if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) intf->crypto_stats_entry = debugfs_create_file("crypto", S_IRUGO, intf->queue_folder, intf, &rt2x00debug_fop_crypto_stats); diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 712eea9d398ffa747a1c76ebfd0da0ad84ed8854..080b1fcae5fa8f3f2b376d5b77bffab2519ddf57 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -88,7 +88,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev) rt2x00queue_start_queues(rt2x00dev); rt2x00link_start_tuner(rt2x00dev); rt2x00link_start_agc(rt2x00dev); - if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) rt2x00link_start_vcocal(rt2x00dev); /* @@ -113,7 +113,7 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev) * Stop all queues */ rt2x00link_stop_agc(rt2x00dev); - if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) rt2x00link_stop_vcocal(rt2x00dev); rt2x00link_stop_tuner(rt2x00dev); rt2x00queue_stop_queues(rt2x00dev); @@ -234,7 +234,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) * here as they will fetch the next beacon directly prior to * transmission. */ - if (test_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev)) return; /* fetch next beacon */ @@ -358,7 +358,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, * mac80211 will expect the same data to be present it the * frame as it was passed to us. */ - if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) rt2x00crypto_tx_insert_iv(entry->skb, header_length); /* diff --git a/drivers/net/wireless/rt2x00/rt2x00link.c b/drivers/net/wireless/rt2x00/rt2x00link.c index 8368aab86f286ee6ff5be65b85a73940f3c24cd0..c2b3b66291884e6171b2cd92b78024406ceb7252 100644 --- a/drivers/net/wireless/rt2x00/rt2x00link.c +++ b/drivers/net/wireless/rt2x00/rt2x00link.c @@ -35,50 +35,28 @@ */ #define DEFAULT_RSSI -128 -/* - * Helper struct and macro to work with moving/walking averages. - * When adding a value to the average value the following calculation - * is needed: - * - * avg_rssi = ((avg_rssi * 7) + rssi) / 8; - * - * The advantage of this approach is that we only need 1 variable - * to store the average in (No need for a count and a total). - * But more importantly, normal average values will over time - * move less and less towards newly added values this results - * that with link tuning, the device can have a very good RSSI - * for a few minutes but when the device is moved away from the AP - * the average will not decrease fast enough to compensate. - * The walking average compensates this and will move towards - * the new values correctly allowing a effective link tuning, - * the speed of the average moving towards other values depends - * on the value for the number of samples. The higher the number - * of samples, the slower the average will move. - * We use two variables to keep track of the average value to - * compensate for the rounding errors. This can be a significant - * error (>5dBm) if the factor is too low. - */ -#define AVG_SAMPLES 8 -#define AVG_FACTOR 1000 -#define MOVING_AVERAGE(__avg, __val) \ -({ \ - struct avg_val __new; \ - __new.avg_weight = \ - (__avg).avg_weight ? \ - ((((__avg).avg_weight * ((AVG_SAMPLES) - 1)) + \ - ((__val) * (AVG_FACTOR))) / \ - (AVG_SAMPLES)) : \ - ((__val) * (AVG_FACTOR)); \ - __new.avg = __new.avg_weight / (AVG_FACTOR); \ - __new; \ -}) +/* Constants for EWMA calculations. */ +#define RT2X00_EWMA_FACTOR 1024 +#define RT2X00_EWMA_WEIGHT 8 + +static inline int rt2x00link_get_avg_rssi(struct ewma *ewma) +{ + unsigned long avg; + + avg = ewma_read(ewma); + if (avg) + return -avg; + + return DEFAULT_RSSI; +} static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) { struct link_ant *ant = &rt2x00dev->link.ant; - if (ant->rssi_ant.avg && rt2x00dev->link.qual.rx_success) - return ant->rssi_ant.avg; + if (rt2x00dev->link.qual.rx_success) + return rt2x00link_get_avg_rssi(&ant->rssi_ant); + return DEFAULT_RSSI; } @@ -100,8 +78,8 @@ static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev, static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev) { - rt2x00dev->link.ant.rssi_ant.avg = 0; - rt2x00dev->link.ant.rssi_ant.avg_weight = 0; + ewma_init(&rt2x00dev->link.ant.rssi_ant, RT2X00_EWMA_FACTOR, + RT2X00_EWMA_WEIGHT); } static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) @@ -249,12 +227,12 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev, /* * Update global RSSI */ - link->avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi); + ewma_add(&link->avg_rssi, -rxdesc->rssi); /* * Update antenna RSSI */ - ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi); + ewma_add(&ant->rssi_ant, -rxdesc->rssi); } void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev) @@ -309,6 +287,8 @@ void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna) */ rt2x00dev->link.count = 0; memset(qual, 0, sizeof(*qual)); + ewma_init(&rt2x00dev->link.avg_rssi, RT2X00_EWMA_FACTOR, + RT2X00_EWMA_WEIGHT); /* * Restore the VGC level as stored in the registers, @@ -363,17 +343,17 @@ static void rt2x00link_tuner(struct work_struct *work) * collect the RSSI data we could use this. Otherwise we * must fallback to the default RSSI value. */ - if (!link->avg_rssi.avg || !qual->rx_success) + if (!qual->rx_success) qual->rssi = DEFAULT_RSSI; else - qual->rssi = link->avg_rssi.avg; + qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi); /* * Check if link tuning is supported by the hardware, some hardware * do not support link tuning at all, while other devices can disable * the feature from the EEPROM. */ - if (test_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_link_tuning(rt2x00dev)) rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count); /* @@ -513,7 +493,7 @@ static void rt2x00link_vcocal(struct work_struct *work) void rt2x00link_register(struct rt2x00_dev *rt2x00dev) { INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc); - if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_vco_recalibration(rt2x00dev)) INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal); INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog); INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index f883802f350585322338ed7683e065bb85349ff3..7c157857f5cee925e796a49396a5843a758a01cd 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -382,11 +382,11 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw, * of different types, but has no a separate filter for PS Poll frames, * FIF_CONTROL flag implies FIF_PSPOLL. */ - if (!test_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags)) { + if (!rt2x00_has_cap_control_filters(rt2x00dev)) { if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL) *total_flags |= FIF_CONTROL | FIF_PSPOLL; } - if (!test_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags)) { + if (!rt2x00_has_cap_control_filter_pspoll(rt2x00dev)) { if (*total_flags & FIF_CONTROL) *total_flags |= FIF_PSPOLL; } @@ -469,7 +469,7 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) return 0; - if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_hw_crypto(rt2x00dev)) return -EOPNOTSUPP; /* @@ -754,6 +754,9 @@ void rt2x00mac_flush(struct ieee80211_hw *hw, u32 queues, bool drop) struct rt2x00_dev *rt2x00dev = hw->priv; struct data_queue *queue; + if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) + return; + tx_queue_for_each(rt2x00dev, queue) rt2x00queue_flush_queue(queue, drop); } diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.c b/drivers/net/wireless/rt2x00/rt2x00pci.c index dc49e525ae5e55e9a090308b80122998e08857d8..25da20e7e1f34ab2c0b1fd3e38a82d1faece0f9e 100644 --- a/drivers/net/wireless/rt2x00/rt2x00pci.c +++ b/drivers/net/wireless/rt2x00/rt2x00pci.c @@ -119,7 +119,7 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops) rt2x00dev->ops = ops; rt2x00dev->hw = hw; rt2x00dev->irq = pci_dev->irq; - rt2x00dev->name = pci_name(pci_dev); + rt2x00dev->name = ops->name; if (pci_is_pcie(pci_dev)) rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 6c8a33b6ee225082d137df7e392306fa0101534a..50590b1420a516863845249c96f689b28e766964 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -61,7 +61,7 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp) * at least 8 bytes bytes available in headroom for IV/EIV * and 8 bytes for ICV data as tailroon. */ - if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_hw_crypto(rt2x00dev)) { head_size += 8; tail_size += 8; } @@ -1033,38 +1033,21 @@ EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue); void rt2x00queue_flush_queue(struct data_queue *queue, bool drop) { - bool started; bool tx_queue = (queue->qid == QID_AC_VO) || (queue->qid == QID_AC_VI) || (queue->qid == QID_AC_BE) || (queue->qid == QID_AC_BK); - mutex_lock(&queue->status_lock); /* - * If the queue has been started, we must stop it temporarily - * to prevent any new frames to be queued on the device. If - * we are not dropping the pending frames, the queue must - * only be stopped in the software and not the hardware, - * otherwise the queue will never become empty on its own. + * If we are not supposed to drop any pending + * frames, this means we must force a start (=kick) + * to the queue to make sure the hardware will + * start transmitting. */ - started = test_bit(QUEUE_STARTED, &queue->flags); - if (started) { - /* - * Pause the queue - */ - rt2x00queue_pause_queue(queue); - - /* - * If we are not supposed to drop any pending - * frames, this means we must force a start (=kick) - * to the queue to make sure the hardware will - * start transmitting. - */ - if (!drop && tx_queue) - queue->rt2x00dev->ops->lib->kick_queue(queue); - } + if (!drop && tx_queue) + queue->rt2x00dev->ops->lib->kick_queue(queue); /* * Check if driver supports flushing, if that is the case we can @@ -1080,14 +1063,6 @@ void rt2x00queue_flush_queue(struct data_queue *queue, bool drop) if (unlikely(!rt2x00queue_empty(queue))) rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n", queue->qid); - - /* - * Restore the queue to the previous status - */ - if (started) - rt2x00queue_unpause_queue(queue); - - mutex_unlock(&queue->status_lock); } EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue); diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 88289873c0cfd754790192e670c6e13bb5736933..4e121627925d91db9de550b635b3fcece71c90e3 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -523,7 +523,9 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue) rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n", queue->qid); + rt2x00queue_stop_queue(queue); rt2x00queue_flush_queue(queue, true); + rt2x00queue_start_queue(queue); } static int rt2x00usb_dma_timeout(struct data_queue *queue) diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 54d3ddfc988845cfa844b5c1d76c6a78ec62e4de..a5b69cb49012162580ef78291b696ce15abbeb94 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -685,7 +685,7 @@ static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529)); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags)); + !rt2x00_has_cap_frame_type(rt2x00dev)); /* * Configure the RX antenna. @@ -813,10 +813,10 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev, if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { sel = antenna_sel_a; - lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); + lna = rt2x00_has_cap_external_lna_a(rt2x00dev); } else { sel = antenna_sel_bg; - lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); + lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); } for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) @@ -836,7 +836,7 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev, else if (rt2x00_rf(rt2x00dev, RF2527)) rt61pci_config_antenna_2x(rt2x00dev, ant); else if (rt2x00_rf(rt2x00dev, RF2529)) { - if (test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_double_antenna(rt2x00dev)) rt61pci_config_antenna_2x(rt2x00dev, ant); else rt61pci_config_antenna_2529(rt2x00dev, ant); @@ -850,13 +850,13 @@ static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev, short lna_gain = 0; if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) lna_gain += 14; rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); } else { - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) lna_gain += 14; rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); @@ -1054,14 +1054,14 @@ static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev, if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { low_bound = 0x28; up_bound = 0x48; - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { low_bound += 0x10; up_bound += 0x10; } } else { low_bound = 0x20; up_bound = 0x40; - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { low_bound += 0x10; up_bound += 0x10; } @@ -2578,7 +2578,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev) * eeprom word. */ if (rt2x00_rf(rt2x00dev, RF2529) && - !test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) { + !rt2x00_has_cap_double_antenna(rt2x00dev)) { rt2x00dev->default_ant.rx = ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED); rt2x00dev->default_ant.tx = @@ -2793,7 +2793,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev) spec->supported_bands = SUPPORT_BAND_2GHZ; spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; - if (!test_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags)) { + if (!rt2x00_has_cap_rf_sequence(rt2x00dev)) { spec->num_channels = 14; spec->channels = rf_vals_noseq; } else { diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 1d3880e09a13eabb73e93c77b7af948ca12421f8..1baf9c896dcd6477efba878a1c0e407411635206 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -595,8 +595,8 @@ static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev, switch (ant->rx) { case ANTENNA_HW_DIVERSITY: rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); - temp = !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags) - && (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ); + temp = !rt2x00_has_cap_frame_type(rt2x00dev) && + (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp); break; case ANTENNA_A: @@ -636,7 +636,7 @@ static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev, rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, - !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags)); + !rt2x00_has_cap_frame_type(rt2x00dev)); /* * Configure the RX antenna. @@ -709,10 +709,10 @@ static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev, if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { sel = antenna_sel_a; - lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); + lna = rt2x00_has_cap_external_lna_a(rt2x00dev); } else { sel = antenna_sel_bg; - lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags); + lna = rt2x00_has_cap_external_lna_bg(rt2x00dev); } for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++) @@ -740,7 +740,7 @@ static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev, short lna_gain = 0; if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) lna_gain += 14; rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); @@ -930,7 +930,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev, low_bound = 0x28; up_bound = 0x48; - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { low_bound += 0x10; up_bound += 0x10; } @@ -946,7 +946,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev, up_bound = 0x1c; } - if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) { low_bound += 0x14; up_bound += 0x10; } @@ -1661,7 +1661,7 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1) } if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { - if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_external_lna_a(rt2x00dev)) { if (lna == 3 || lna == 2) offset += 10; } else { diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index fc207b268e4fd3db1926c494c8b470a6833c8102..a91506b12a627f26a084aef5e6dcb9a4213ac5e4 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -1122,7 +1122,6 @@ static int rtl8180_probe(struct pci_dev *pdev, iounmap(priv->map); err_free_dev: - pci_set_drvdata(pdev, NULL); ieee80211_free_hw(dev); err_free_reg: diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 8bb4a9a01a1838e47d0d64b844503726999f3ff9..9a78e3daf74264fa13f446a4779d342ab6341488 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -1613,6 +1613,35 @@ err_free: } EXPORT_SYMBOL(rtl_send_smps_action); +void rtl_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + enum io_type iotype; + + if (!is_hal_stop(rtlhal)) { + switch (operation) { + case SCAN_OPT_BACKUP: + iotype = IO_CMD_PAUSE_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_IO_CMD, + (u8 *)&iotype); + break; + case SCAN_OPT_RESTORE: + iotype = IO_CMD_RESUME_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_IO_CMD, + (u8 *)&iotype); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Unknown Scan Backup operation.\n"); + break; + } + } +} +EXPORT_SYMBOL(rtl_phy_scan_operation_backup); + /* There seem to be issues in mac80211 regarding when del ba frames can be * received. As a work around, we make a fake del_ba if we receive a ba_req; * however, rx_agg was opened to let mac80211 release some ba related diff --git a/drivers/net/wireless/rtlwifi/base.h b/drivers/net/wireless/rtlwifi/base.h index 0e5fe0902daf6eb180a1da431ce54477e49486cb..0cd07420777a702afca02422cab06f16e06a9121 100644 --- a/drivers/net/wireless/rtlwifi/base.h +++ b/drivers/net/wireless/rtlwifi/base.h @@ -114,7 +114,6 @@ void rtl_init_rfkill(struct ieee80211_hw *hw); void rtl_deinit_rfkill(struct ieee80211_hw *hw); void rtl_beacon_statistic(struct ieee80211_hw *hw, struct sk_buff *skb); -void rtl_watch_dog_timer_callback(unsigned long data); void rtl_deinit_deferred_work(struct ieee80211_hw *hw); bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); @@ -153,5 +152,6 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb); struct sk_buff *rtl_make_del_ba(struct ieee80211_hw *hw, u8 *sa, u8 *bssid, u16 tid); +void rtl_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation); #endif diff --git a/drivers/net/wireless/rtlwifi/cam.h b/drivers/net/wireless/rtlwifi/cam.h index 35e00086a520bf06393237b3aebf0aa3bbb9e617..0105e6c1901ed96fc6be6cfd4900b2285ed0835a 100644 --- a/drivers/net/wireless/rtlwifi/cam.h +++ b/drivers/net/wireless/rtlwifi/cam.h @@ -41,12 +41,12 @@ #define CAM_CONFIG_USEDK 1 #define CAM_CONFIG_NO_USEDK 0 -extern void rtl_cam_reset_all_entry(struct ieee80211_hw *hw); -extern u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, - u32 ul_key_id, u32 ul_entry_idx, u32 ul_enc_alg, - u32 ul_default_key, u8 *key_content); +void rtl_cam_reset_all_entry(struct ieee80211_hw *hw); +u8 rtl_cam_add_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, + u32 ul_key_id, u32 ul_entry_idx, u32 ul_enc_alg, + u32 ul_default_key, u8 *key_content); int rtl_cam_delete_one_entry(struct ieee80211_hw *hw, u8 *mac_addr, - u32 ul_key_id); + u32 ul_key_id); void rtl_cam_mark_invalid(struct ieee80211_hw *hw, u8 uc_index); void rtl_cam_empty_entry(struct ieee80211_hw *hw, u8 uc_index); void rtl_cam_reset_sec_info(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 733b7ce7f0e2a981f442cd9cdadd8f40a6426558..210ce7cd94d8d14201a68ce285e9c880993d30db 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -115,7 +115,7 @@ static void rtl_op_stop(struct ieee80211_hw *hw) mutex_lock(&rtlpriv->locks.conf_mutex); mac->link_state = MAC80211_NOLINK; - memset(mac->bssid, 0, 6); + memset(mac->bssid, 0, ETH_ALEN); mac->vendor = PEER_UNKNOWN; /*reset sec info */ @@ -280,7 +280,7 @@ static void rtl_op_remove_interface(struct ieee80211_hw *hw, mac->p2p = 0; mac->vif = NULL; mac->link_state = MAC80211_NOLINK; - memset(mac->bssid, 0, 6); + memset(mac->bssid, 0, ETH_ALEN); mac->vendor = PEER_UNKNOWN; mac->opmode = NL80211_IFTYPE_UNSPECIFIED; rtlpriv->cfg->ops->set_network_type(hw, mac->opmode); @@ -721,7 +721,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, mac->link_state = MAC80211_LINKED; mac->cnt_after_linked = 0; mac->assoc_id = bss_conf->aid; - memcpy(mac->bssid, bss_conf->bssid, 6); + memcpy(mac->bssid, bss_conf->bssid, ETH_ALEN); if (rtlpriv->cfg->ops->linked_set_reg) rtlpriv->cfg->ops->linked_set_reg(hw); @@ -750,7 +750,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, if (ppsc->p2p_ps_info.p2p_ps_mode > P2P_PS_NONE) rtl_p2p_ps_cmd(hw, P2P_PS_DISABLE); mac->link_state = MAC80211_NOLINK; - memset(mac->bssid, 0, 6); + memset(mac->bssid, 0, ETH_ALEN); mac->vendor = PEER_UNKNOWN; if (rtlpriv->dm.supp_phymode_switch) { @@ -826,7 +826,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, bss_conf->bssid); mac->vendor = PEER_UNKNOWN; - memcpy(mac->bssid, bss_conf->bssid, 6); + memcpy(mac->bssid, bss_conf->bssid, ETH_ALEN); rtlpriv->cfg->ops->set_network_type(hw, vif->type); rcu_read_lock(); diff --git a/drivers/net/wireless/rtlwifi/efuse.c b/drivers/net/wireless/rtlwifi/efuse.c index 838a1ed3f1942b9f3d56a122c2840548b38103c9..ae13fb94b2e8d7b82a259cdc830643cb2e144901 100644 --- a/drivers/net/wireless/rtlwifi/efuse.c +++ b/drivers/net/wireless/rtlwifi/efuse.c @@ -1203,20 +1203,18 @@ static void efuse_power_switch(struct ieee80211_hw *hw, u8 write, u8 pwrstate) static u16 efuse_get_current_size(struct ieee80211_hw *hw) { - int continual = true; u16 efuse_addr = 0; u8 hworden; u8 efuse_data, word_cnts; - while (continual && efuse_one_byte_read(hw, efuse_addr, &efuse_data) - && (efuse_addr < EFUSE_MAX_SIZE)) { - if (efuse_data != 0xFF) { - hworden = efuse_data & 0x0F; - word_cnts = efuse_calculate_word_cnts(hworden); - efuse_addr = efuse_addr + (word_cnts * 2) + 1; - } else { - continual = false; - } + while (efuse_one_byte_read(hw, efuse_addr, &efuse_data) && + efuse_addr < EFUSE_MAX_SIZE) { + if (efuse_data == 0xFF) + break; + + hworden = efuse_data & 0x0F; + word_cnts = efuse_calculate_word_cnts(hworden); + efuse_addr = efuse_addr + (word_cnts * 2) + 1; } return efuse_addr; diff --git a/drivers/net/wireless/rtlwifi/efuse.h b/drivers/net/wireless/rtlwifi/efuse.h index 395a326acfb44a0874b331b64442d23b5e120d1a..1663b3afd41e90e67512823f0c5a0f7ee02ef80b 100644 --- a/drivers/net/wireless/rtlwifi/efuse.h +++ b/drivers/net/wireless/rtlwifi/efuse.h @@ -104,20 +104,19 @@ struct efuse_priv { u8 tx_power_g[14]; }; -extern void read_efuse_byte(struct ieee80211_hw *hw, u16 _offset, u8 *pbuf); -extern void efuse_initialize(struct ieee80211_hw *hw); -extern u8 efuse_read_1byte(struct ieee80211_hw *hw, u16 address); -extern void efuse_write_1byte(struct ieee80211_hw *hw, u16 address, u8 value); -extern void read_efuse(struct ieee80211_hw *hw, u16 _offset, - u16 _size_byte, u8 *pbuf); -extern void efuse_shadow_read(struct ieee80211_hw *hw, u8 type, - u16 offset, u32 *value); -extern void efuse_shadow_write(struct ieee80211_hw *hw, u8 type, - u16 offset, u32 value); -extern bool efuse_shadow_update(struct ieee80211_hw *hw); -extern bool efuse_shadow_update_chk(struct ieee80211_hw *hw); -extern void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw); -extern void efuse_force_write_vendor_Id(struct ieee80211_hw *hw); -extern void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx); +void read_efuse_byte(struct ieee80211_hw *hw, u16 _offset, u8 *pbuf); +void efuse_initialize(struct ieee80211_hw *hw); +u8 efuse_read_1byte(struct ieee80211_hw *hw, u16 address); +void efuse_write_1byte(struct ieee80211_hw *hw, u16 address, u8 value); +void read_efuse(struct ieee80211_hw *hw, u16 _offset, u16 _size_byte, u8 *pbuf); +void efuse_shadow_read(struct ieee80211_hw *hw, u8 type, u16 offset, + u32 *value); +void efuse_shadow_write(struct ieee80211_hw *hw, u8 type, u16 offset, + u32 value); +bool efuse_shadow_update(struct ieee80211_hw *hw); +bool efuse_shadow_update_chk(struct ieee80211_hw *hw); +void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw); +void efuse_force_write_vendor_Id(struct ieee80211_hw *hw); +void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx); #endif diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 703f839af6ca0b4abefcfc754dc21eb3d13b2dac..0f494444bcd1d90b457b927d704bf77abe0592ca 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -736,7 +736,6 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; int index = rtlpci->rx_ring[rx_queue_idx].idx; @@ -2009,7 +2008,6 @@ fail2: fail1: if (hw) ieee80211_free_hw(hw); - pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); return err; @@ -2064,8 +2062,6 @@ void rtl_pci_disconnect(struct pci_dev *pdev) rtl_pci_disable_aspm(hw); - pci_set_drvdata(pdev, NULL); - ieee80211_free_hw(hw); } EXPORT_SYMBOL(rtl_pci_disconnect); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c index b68cae3024fc851fec0b58359328a39e14e1e4da..e06971be7df77e047e8d869d046a126d0e987d14 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c @@ -143,6 +143,7 @@ static void _rtl88ee_set_fw_clock_on(struct ieee80211_hw *hw, } else { rtlhal->fw_clk_change_in_progress = false; spin_unlock_bh(&rtlpriv->locks.fw_ps_lock); + break; } } diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c index e655c04732251f18ee1121625df732ab8b8d8096..d67f9c731cc4600e57d774e7125d80c945e196b6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.c @@ -1136,34 +1136,6 @@ void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) &bw40_pwr[0], channel); } -void rtl88e_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - enum io_type iotype; - - if (!is_hal_stop(rtlhal)) { - switch (operation) { - case SCAN_OPT_BACKUP: - iotype = IO_CMD_PAUSE_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - case SCAN_OPT_RESTORE: - iotype = IO_CMD_RESUME_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - default: - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Unknown Scan Backup operation.\n"); - break; - } - } -} - void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h index f1acd6d27e444ca4fac956d14190de873ef15d06..89f0f1ef14657ae467bea1445658cfd202c31da6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/phy.h @@ -200,37 +200,35 @@ enum _ANT_DIV_TYPE { CGCS_RX_SW_ANTDIV = 0x05, }; -extern u32 rtl88e_phy_query_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask); -extern void rtl88e_phy_set_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, u32 data); -extern u32 rtl88e_phy_query_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask); -extern void rtl88e_phy_set_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask, u32 data); -extern bool rtl88e_phy_mac_config(struct ieee80211_hw *hw); -extern bool rtl88e_phy_bb_config(struct ieee80211_hw *hw); -extern bool rtl88e_phy_rf_config(struct ieee80211_hw *hw); -extern void rtl88e_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -extern void rtl88e_phy_get_txpower_level(struct ieee80211_hw *hw, - long *powerlevel); -extern void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); -extern void rtl88e_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); -extern void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw); -extern void rtl88e_phy_set_bw_mode(struct ieee80211_hw *hw, - enum nl80211_channel_type ch_type); -extern void rtl88e_phy_sw_chnl_callback(struct ieee80211_hw *hw); -extern u8 rtl88e_phy_sw_chnl(struct ieee80211_hw *hw); -extern void rtl88e_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); +u32 rtl88e_phy_query_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask); +void rtl88e_phy_set_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, u32 data); +u32 rtl88e_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask); +void rtl88e_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask, u32 data); +bool rtl88e_phy_mac_config(struct ieee80211_hw *hw); +bool rtl88e_phy_bb_config(struct ieee80211_hw *hw); +bool rtl88e_phy_rf_config(struct ieee80211_hw *hw); +void rtl88e_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); +void rtl88e_phy_get_txpower_level(struct ieee80211_hw *hw, + long *powerlevel); +void rtl88e_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); +void rtl88e_phy_set_bw_mode_callback(struct ieee80211_hw *hw); +void rtl88e_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type); +void rtl88e_phy_sw_chnl_callback(struct ieee80211_hw *hw); +u8 rtl88e_phy_sw_chnl(struct ieee80211_hw *hw); +void rtl88e_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); void rtl88e_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl88e_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl88e_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath); bool rtl88e_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); -extern bool rtl88e_phy_set_rf_power_state(struct ieee80211_hw *hw, - enum rf_pwrstate rfpwr_state); +bool rtl88e_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c index c254693a1e6ac3b9ce69e6e89947748e5331b48e..347af1e4f438e57cf2c37b8975169a7c92941681 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/sw.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../core.h" #include "../pci.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -244,7 +245,7 @@ static struct rtl_hal_ops rtl8188ee_hal_ops = { .set_bw_mode = rtl88e_phy_set_bw_mode, .switch_channel = rtl88e_phy_sw_chnl, .dm_watchdog = rtl88e_dm_watchdog, - .scan_operation_backup = rtl88e_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl88e_phy_set_rf_power_state, .led_control = rtl88ee_led_control, .set_desc = rtl88ee_set_desc, diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c index 68685a8982574e3e6b88c94379d00e4518a02601..aece6c9cccf1b50febc16049f4fd962c5c834aa7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c @@ -478,7 +478,6 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, /*rx_status->qual = status->signal; */ rx_status->signal = status->recvsignalpower + 10; - /*rx_status->noise = -status->noise; */ if (status->packet_report_type == TX_REPORT2) { status->macid_valid_entry[0] = GET_RX_RPT2_DESC_MACID_VALID_1(pdesc); diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c index d2d57a27a7c1636538003fc408b36c2bda24ce73..e9caa5d4cff0f910488cd4b22899ad9b5c84665a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c @@ -541,29 +541,6 @@ EXPORT_SYMBOL(rtl92c_dm_write_dig); static void rtl92c_dm_pwdb_monitor(struct ieee80211_hw *hw) { - struct rtl_priv *rtlpriv = rtl_priv(hw); - long tmpentry_max_pwdb = 0, tmpentry_min_pwdb = 0xff; - - u8 h2c_parameter[3] = { 0 }; - - return; - - if (tmpentry_max_pwdb != 0) { - rtlpriv->dm.entry_max_undec_sm_pwdb = tmpentry_max_pwdb; - } else { - rtlpriv->dm.entry_max_undec_sm_pwdb = 0; - } - - if (tmpentry_min_pwdb != 0xff) { - rtlpriv->dm.entry_min_undec_sm_pwdb = tmpentry_min_pwdb; - } else { - rtlpriv->dm.entry_min_undec_sm_pwdb = 0; - } - - h2c_parameter[2] = (u8) (rtlpriv->dm.undec_sm_pwdb & 0xFF); - h2c_parameter[0] = 0; - - rtl92c_fill_h2c_cmd(hw, H2C_RSSI_REPORT, 3, h2c_parameter); } void rtl92c_dm_init_edca_turbo(struct ieee80211_hw *hw) @@ -673,7 +650,7 @@ static void rtl92c_dm_txpower_tracking_callback_thermalmeter(struct ieee80211_hw s8 cck_index = 0; int i; bool is2t = IS_92C_SERIAL(rtlhal->version); - s8 txpwr_level[2] = {0, 0}; + s8 txpwr_level[3] = {0, 0, 0}; u8 ofdm_min_index = 6, rf; rtlpriv->dm.txpower_trackinginit = true; diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c index 246e5352f2e15a850dbc7971c08bdbbcffbc7172..0c0e78263a665190aacf3d33d21fa87b99d4dd86 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c @@ -592,36 +592,6 @@ long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, } EXPORT_SYMBOL(_rtl92c_phy_txpwr_idx_to_dbm); -void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - enum io_type iotype; - - if (!is_hal_stop(rtlhal)) { - switch (operation) { - case SCAN_OPT_BACKUP: - iotype = IO_CMD_PAUSE_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - - break; - case SCAN_OPT_RESTORE: - iotype = IO_CMD_RESUME_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - default: - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Unknown Scan Backup operation\n"); - break; - } - } -} -EXPORT_SYMBOL(rtl92c_phy_scan_operation_backup); - void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h index cec10d696492ab4055a2982ffadb1202494eea5e..e79dabe9ba1de77c757e91b46c6ba48ca6e9f12f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h @@ -39,9 +39,7 @@ #define RT_CANNOT_IO(hw) false #define HIGHPOWER_RADIOA_ARRAYLEN 22 -#define IQK_ADDA_REG_NUM 16 #define MAX_TOLERANCE 5 -#define IQK_DELAY_TIME 1 #define APK_BB_REG_NUM 5 #define APK_AFE_REG_NUM 16 @@ -205,8 +203,6 @@ void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm); -void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type); void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h index 3cfa1bb0f47676853500b237a37aaeb9e49b1bad..fa24de43ce795d4588f002a93390ea8ffb5ee109 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h @@ -152,8 +152,6 @@ enum version_8192c { #define IS_VENDOR_UMC_A_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) ? \ ((GET_CVID_CUT_VERSION(version)) ? false : true) : false) #define IS_CHIP_VER_B(version) ((version & CHIP_VER_B) ? true : false) -#define IS_VENDOR_UMC_A_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) ? \ - ((GET_CVID_CUT_VERSION(version)) ? false : true) : false) #define IS_92C_SERIAL(version) ((version & CHIP_92C_BITMASK) ? true : false) #define IS_CHIP_VENDOR_UMC(version) \ ((version & CHIP_VENDOR_UMC) ? true : false) diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h index d5e3b704f9304a596bd943cfcbebd00db6559776..94486cca400077515ced7c81e0c35922af97e425 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h @@ -39,9 +39,7 @@ #define RT_CANNOT_IO(hw) false #define HIGHPOWER_RADIOA_ARRAYLEN 22 -#define IQK_ADDA_REG_NUM 16 #define MAX_TOLERANCE 5 -#define IQK_DELAY_TIME 1 #define APK_BB_REG_NUM 5 #define APK_AFE_REG_NUM 16 @@ -188,36 +186,29 @@ struct tx_power_struct { }; bool rtl92c_phy_bb_config(struct ieee80211_hw *hw); -u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask); -void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, u32 data); -u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask); -extern void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask, u32 data); +u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask); +void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, + u32 data); +u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, + u32 regaddr, u32 bitmask); +void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, + u32 regaddr, u32 bitmask, u32 data); bool rtl92c_phy_mac_config(struct ieee80211_hw *hw); bool rtl92ce_phy_bb_config(struct ieee80211_hw *hw); bool rtl92c_phy_rf_config(struct ieee80211_hw *hw); bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, - enum radio_path rfpath); + enum radio_path rfpath); void rtl92c_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, - long *powerlevel); +void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel); void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm); -void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, - enum nl80211_channel_type ch_type); + enum nl80211_channel_type ch_type); void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw); u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw); void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); -void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw, - u16 beaconinterval); +void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw, u16 beaconinterval); void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta); void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw); void _rtl92ce_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); @@ -225,28 +216,25 @@ void rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath); bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, - u32 rfpath); -bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); + u32 rfpath); bool rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, - enum rf_pwrstate rfpwr_state); + enum rf_pwrstate rfpwr_state); void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw); bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); void rtl92c_phy_set_io(struct ieee80211_hw *hw); void rtl92c_bb_block_on(struct ieee80211_hw *hw); -u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset); +u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, + u32 offset); u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset); + enum radio_path rfpath, u32 offset); u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask); void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset, - u32 data); + enum radio_path rfpath, u32 offset, u32 data); void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset, - u32 data); + enum radio_path rfpath, u32 offset, + u32 data); void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, - u32 data); + u32 regaddr, u32 bitmask, u32 data); bool _rtl92ce_phy_config_mac_with_headerfile(struct ieee80211_hw *hw); void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw); bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h index bd4aef74c0567929849c24008ead104b5a11b15d..8922ecb47ad245cdb7d1b759f44c558f97e52173 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h @@ -560,7 +560,6 @@ #define EEPROM_DEFAULT_TXPOWERLEVEL 0x22 #define EEPROM_DEFAULT_HT40_2SDIFF 0x0 #define EEPROM_DEFAULT_HT20_DIFF 2 -#define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3 #define EEPROM_DEFAULT_HT40_PWRMAXOFFSET 0 #define EEPROM_DEFAULT_HT20_PWRMAXOFFSET 0 @@ -639,17 +638,8 @@ #define EEPROM_TXPWR_GROUP 0x6F -#define EEPROM_TSSI_A 0x76 -#define EEPROM_TSSI_B 0x77 -#define EEPROM_THERMAL_METER 0x78 - #define EEPROM_CHANNELPLAN 0x75 -#define RF_OPTION1 0x79 -#define RF_OPTION2 0x7A -#define RF_OPTION3 0x7B -#define RF_OPTION4 0x7C - #define STOPBECON BIT(6) #define STOPHIGHT BIT(5) #define STOPMGT BIT(4) @@ -689,13 +679,6 @@ #define RSV_CTRL 0x001C #define RD_CTRL 0x0524 -#define REG_USB_INFO 0xFE17 -#define REG_USB_SPECIAL_OPTION 0xFE55 - -#define REG_USB_DMA_AGG_TO 0xFE5B -#define REG_USB_AGG_TO 0xFE5C -#define REG_USB_AGG_TH 0xFE5D - #define REG_USB_VID 0xFE60 #define REG_USB_PID 0xFE62 #define REG_USB_OPTIONAL 0xFE64 @@ -1196,9 +1179,6 @@ #define POLLING_LLT_THRESHOLD 20 #define POLLING_READY_TIMEOUT_COUNT 1000 -#define MAX_MSS_DENSITY_2T 0x13 -#define MAX_MSS_DENSITY_1T 0x0A - #define EPROM_CMD_OPERATING_MODE_MASK ((1<<7)|(1<<6)) #define EPROM_CMD_CONFIG 0x3 #define EPROM_CMD_LOAD 1 diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h index 6c8d56efceae2fd8e76e8e128fea6e720241cf12..d8fe68b389d213e17b133b7044fec647b315f2b8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h @@ -34,11 +34,10 @@ #define RF6052_MAX_REG 0x3F #define RF6052_MAX_PATH 2 -extern void rtl92ce_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, - u8 bandwidth); -extern void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel); -extern void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel, u8 channel); -extern bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw); +void rtl92ce_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); +void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c index 14203561b6ee5463d04841848a435cc9c51d59cd..b790320d20305427c5ed4fa5fd9f37d944957e31 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../core.h" #include "../pci.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -219,7 +220,7 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = { .set_bw_mode = rtl92c_phy_set_bw_mode, .switch_channel = rtl92c_phy_sw_chnl, .dm_watchdog = rtl92c_dm_watchdog, - .scan_operation_backup = rtl92c_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl92c_phy_set_rf_power_state, .led_control = rtl92ce_led_control, .set_desc = rtl92ce_set_desc, diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 6ad23b413eb3e6e4251e2242fc4dc912e3b7b6e5..52abf0a862fa70f26ec26ff62d2017f0a11ce5c9 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -420,7 +420,6 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, /*rx_status->qual = stats->signal; */ rx_status->signal = stats->recvsignalpower + 10; - /*rx_status->noise = -stats->noise; */ return true; } diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c index da4f587199ee5537cd20b0f62a326fd8157afb9a..393685390f3ee41f78a903d198eb51484654a55f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c @@ -32,6 +32,7 @@ #include "../usb.h" #include "../ps.h" #include "../cam.h" +#include "../stats.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -738,16 +739,6 @@ static u8 _rtl92c_evm_db_to_percentage(char value) return ret_val; } -static long _rtl92c_translate_todbm(struct ieee80211_hw *hw, - u8 signal_strength_index) -{ - long signal_power; - - signal_power = (long)((signal_strength_index + 1) >> 1); - signal_power -= 95; - return signal_power; -} - static long _rtl92c_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) { @@ -913,180 +904,6 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, (hw, total_rssi /= rf_rx_num)); } -static void _rtl92c_process_ui_rssi(struct ieee80211_hw *hw, - struct rtl_stats *pstats) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_phy *rtlphy = &(rtlpriv->phy); - u8 rfpath; - u32 last_rssi, tmpval; - - if (pstats->packet_toself || pstats->packet_beacon) { - rtlpriv->stats.rssi_calculate_cnt++; - if (rtlpriv->stats.ui_rssi.total_num++ >= - PHY_RSSI_SLID_WIN_MAX) { - rtlpriv->stats.ui_rssi.total_num = - PHY_RSSI_SLID_WIN_MAX; - last_rssi = - rtlpriv->stats.ui_rssi.elements[rtlpriv-> - stats.ui_rssi.index]; - rtlpriv->stats.ui_rssi.total_val -= last_rssi; - } - rtlpriv->stats.ui_rssi.total_val += pstats->signalstrength; - rtlpriv->stats.ui_rssi.elements[rtlpriv->stats.ui_rssi. - index++] = pstats->signalstrength; - if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX) - rtlpriv->stats.ui_rssi.index = 0; - tmpval = rtlpriv->stats.ui_rssi.total_val / - rtlpriv->stats.ui_rssi.total_num; - rtlpriv->stats.signal_strength = - _rtl92c_translate_todbm(hw, (u8) tmpval); - pstats->rssi = rtlpriv->stats.signal_strength; - } - if (!pstats->is_cck && pstats->packet_toself) { - for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; - rfpath++) { - if (!rtl8192_phy_check_is_legal_rfpath(hw, rfpath)) - continue; - if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) { - rtlpriv->stats.rx_rssi_percentage[rfpath] = - pstats->rx_mimo_signalstrength[rfpath]; - } - if (pstats->rx_mimo_signalstrength[rfpath] > - rtlpriv->stats.rx_rssi_percentage[rfpath]) { - rtlpriv->stats.rx_rssi_percentage[rfpath] = - ((rtlpriv->stats. - rx_rssi_percentage[rfpath] * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->rx_mimo_signalstrength[rfpath])) / - (RX_SMOOTH_FACTOR); - - rtlpriv->stats.rx_rssi_percentage[rfpath] = - rtlpriv->stats.rx_rssi_percentage[rfpath] + - 1; - } else { - rtlpriv->stats.rx_rssi_percentage[rfpath] = - ((rtlpriv->stats. - rx_rssi_percentage[rfpath] * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->rx_mimo_signalstrength[rfpath])) / - (RX_SMOOTH_FACTOR); - } - } - } -} - -static void _rtl92c_update_rxsignalstatistics(struct ieee80211_hw *hw, - struct rtl_stats *pstats) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - int weighting = 0; - - if (rtlpriv->stats.recv_signal_power == 0) - rtlpriv->stats.recv_signal_power = pstats->recvsignalpower; - if (pstats->recvsignalpower > rtlpriv->stats.recv_signal_power) - weighting = 5; - else if (pstats->recvsignalpower < rtlpriv->stats.recv_signal_power) - weighting = (-5); - rtlpriv->stats.recv_signal_power = - (rtlpriv->stats.recv_signal_power * 5 + - pstats->recvsignalpower + weighting) / 6; -} - -static void _rtl92c_process_pwdb(struct ieee80211_hw *hw, - struct rtl_stats *pstats) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); - long undec_sm_pwdb = 0; - - if (mac->opmode == NL80211_IFTYPE_ADHOC) { - return; - } else { - undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb; - } - if (pstats->packet_toself || pstats->packet_beacon) { - if (undec_sm_pwdb < 0) - undec_sm_pwdb = pstats->rx_pwdb_all; - if (pstats->rx_pwdb_all > (u32) undec_sm_pwdb) { - undec_sm_pwdb = (((undec_sm_pwdb) * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); - undec_sm_pwdb += 1; - } else { - undec_sm_pwdb = (((undec_sm_pwdb) * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); - } - rtlpriv->dm.undec_sm_pwdb = undec_sm_pwdb; - _rtl92c_update_rxsignalstatistics(hw, pstats); - } -} - -static void _rtl92c_process_LINK_Q(struct ieee80211_hw *hw, - struct rtl_stats *pstats) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u32 last_evm = 0, n_stream, tmpval; - - if (pstats->signalquality != 0) { - if (pstats->packet_toself || pstats->packet_beacon) { - if (rtlpriv->stats.LINK_Q.total_num++ >= - PHY_LINKQUALITY_SLID_WIN_MAX) { - rtlpriv->stats.LINK_Q.total_num = - PHY_LINKQUALITY_SLID_WIN_MAX; - last_evm = - rtlpriv->stats.LINK_Q.elements - [rtlpriv->stats.LINK_Q.index]; - rtlpriv->stats.LINK_Q.total_val -= - last_evm; - } - rtlpriv->stats.LINK_Q.total_val += - pstats->signalquality; - rtlpriv->stats.LINK_Q.elements - [rtlpriv->stats.LINK_Q.index++] = - pstats->signalquality; - if (rtlpriv->stats.LINK_Q.index >= - PHY_LINKQUALITY_SLID_WIN_MAX) - rtlpriv->stats.LINK_Q.index = 0; - tmpval = rtlpriv->stats.LINK_Q.total_val / - rtlpriv->stats.LINK_Q.total_num; - rtlpriv->stats.signal_quality = tmpval; - rtlpriv->stats.last_sigstrength_inpercent = tmpval; - for (n_stream = 0; n_stream < 2; - n_stream++) { - if (pstats->RX_SIGQ[n_stream] != -1) { - if (!rtlpriv->stats.RX_EVM[n_stream]) { - rtlpriv->stats.RX_EVM[n_stream] - = pstats->RX_SIGQ[n_stream]; - } - rtlpriv->stats.RX_EVM[n_stream] = - ((rtlpriv->stats.RX_EVM - [n_stream] * - (RX_SMOOTH_FACTOR - 1)) + - (pstats->RX_SIGQ - [n_stream] * 1)) / - (RX_SMOOTH_FACTOR); - } - } - } - } else { - ; - } -} - -static void _rtl92c_process_phyinfo(struct ieee80211_hw *hw, - u8 *buffer, - struct rtl_stats *pcurrent_stats) -{ - if (!pcurrent_stats->packet_matchbssid && - !pcurrent_stats->packet_beacon) - return; - _rtl92c_process_ui_rssi(hw, pcurrent_stats); - _rtl92c_process_pwdb(hw, pcurrent_stats); - _rtl92c_process_LINK_Q(hw, pcurrent_stats); -} - void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw, struct sk_buff *skb, struct rtl_stats *pstats, @@ -1123,5 +940,5 @@ void rtl92c_translate_rx_signal_stuff(struct ieee80211_hw *hw, _rtl92c_query_rxphystatus(hw, pstats, pdesc, p_drvinfo, packet_matchbssid, packet_toself, packet_beacon); - _rtl92c_process_phyinfo(hw, tmp_buf, pstats); + rtl_process_phyinfo(hw, tmp_buf, pstats); } diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h index 090fd33a158db1635c14d3ea323a5363f7c82169..11b439d6b67167c11e1529919bc5f07dfe96e060 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h @@ -34,15 +34,14 @@ #define RF6052_MAX_REG 0x3F #define RF6052_MAX_PATH 2 -extern void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, - u8 bandwidth); -extern void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel); -extern void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel, u8 channel); +void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); +void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); bool rtl92cu_phy_rf6052_config(struct ieee80211_hw *hw); bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, - enum radio_path rfpath); + enum radio_path rfpath); void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel); void rtl92cu_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index 2bd5985262171bfdfa81de5494a4ef9640d72cc5..9936de716ad58929e50dc0e6f02fca77f5efee68 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -31,6 +31,7 @@ #include "../core.h" #include "../usb.h" #include "../efuse.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -117,7 +118,7 @@ static struct rtl_hal_ops rtl8192cu_hal_ops = { .set_bw_mode = rtl92c_phy_set_bw_mode, .switch_channel = rtl92c_phy_sw_chnl, .dm_watchdog = rtl92c_dm_watchdog, - .scan_operation_backup = rtl92c_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl92cu_phy_set_rf_power_state, .led_control = rtl92cu_led_control, .enable_hw_sec = rtl92cu_enable_hw_security_config, diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index 5a060e537fbe99171f234c29a6320f75ad22a1f3..25e50ffc44ec8b42fd35b4ab0b60a08ee09edd7d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -350,7 +350,6 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, } /*rx_status->qual = stats->signal; */ rx_status->signal = stats->rssi + 10; - /*rx_status->noise = -stats->noise; */ return true; } @@ -365,7 +364,6 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) u8 *rxdesc; struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; struct rx_fwinfo_92c *p_drvinfo; diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c index f700f7a614b264691ce8be3cfb79f219f5edd9ad..7908e1c85819409091abd71ba5b57595947ff909 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c @@ -840,9 +840,9 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( bool internal_pa = false; long ele_a = 0, ele_d, temp_cck, val_x, value32; long val_y, ele_c = 0; - u8 ofdm_index[2]; + u8 ofdm_index[3]; s8 cck_index = 0; - u8 ofdm_index_old[2] = {0, 0}; + u8 ofdm_index_old[3] = {0, 0, 0}; s8 cck_index_old = 0; u8 index; int i; @@ -1118,6 +1118,10 @@ static void rtl92d_dm_txpower_tracking_callback_thermalmeter( val_x, val_y, ele_a, ele_c, ele_d, val_x, val_y); + if (cck_index >= CCK_TABLE_SIZE) + cck_index = CCK_TABLE_SIZE - 1; + if (cck_index < 0) + cck_index = 0; if (rtlhal->current_bandtype == BAND_ON_2_4G) { /* Adjust CCK according to IQK result */ if (!rtlpriv->dm.cck_inch14) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c index 7dd8f6de0550f4f03424472f8926577a23b513c9..c4a7db9135d6e3850dcd8490e5e9165807178b64 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c @@ -1194,25 +1194,7 @@ void rtl92d_linked_set_reg(struct ieee80211_hw *hw) * mac80211 will send pkt when scan */ void rtl92de_set_qos(struct ieee80211_hw *hw, int aci) { - struct rtl_priv *rtlpriv = rtl_priv(hw); rtl92d_dm_init_edca_turbo(hw); - return; - switch (aci) { - case AC1_BK: - rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f); - break; - case AC0_BE: - break; - case AC2_VI: - rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322); - break; - case AC3_VO: - rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222); - break; - default: - RT_ASSERT(false, "invalid aci: %d !\n", aci); - break; - } } void rtl92de_enable_interrupt(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.h b/drivers/net/wireless/rtlwifi/rtl8192de/hw.h index 7c9f7a2f1e427d2bac52cfacdea38c949f390a2e..1bc7b1a96d4aebe5577067460e56fa698405b3a8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.h @@ -55,10 +55,9 @@ void rtl92de_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all); -extern void rtl92de_write_dword_dbi(struct ieee80211_hw *hw, u16 offset, - u32 value, u8 direct); -extern u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset, - u8 direct); +void rtl92de_write_dword_dbi(struct ieee80211_hw *hw, u16 offset, u32 value, + u8 direct); +u32 rtl92de_read_dword_dbi(struct ieee80211_hw *hw, u16 offset, u8 direct); void rtl92de_suspend(struct ieee80211_hw *hw); void rtl92de_resume(struct ieee80211_hw *hw); void rtl92d_linked_set_reg(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c index 840bac5fa2f80bfa224bd5470b6562585aec0a02..13196cc4b1d380279e7ce3096b5861ba14087b81 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c @@ -1022,34 +1022,6 @@ void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) rtl92d_phy_rf6052_set_ofdm_txpower(hw, &ofdmpowerlevel[0], channel); } -void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - enum io_type iotype; - - if (!is_hal_stop(rtlhal)) { - switch (operation) { - case SCAN_OPT_BACKUP: - rtlhal->current_bandtypebackup = - rtlhal->current_bandtype; - iotype = IO_CMD_PAUSE_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - case SCAN_OPT_RESTORE: - iotype = IO_CMD_RESUME_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - default: - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Unknown Scan Backup operation\n"); - break; - } - } -} - void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.h b/drivers/net/wireless/rtlwifi/rtl8192de/phy.h index f074952bf25c53c3fcd6dbf6ae22f0a1f8de9316..48d5c6835b6a98a950daf3f14a5330606a656ba5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.h @@ -39,9 +39,7 @@ #define RT_CANNOT_IO(hw) false #define HIGHPOWER_RADIOA_ARRAYLEN 22 -#define IQK_ADDA_REG_NUM 16 #define MAX_TOLERANCE 5 -#define IQK_DELAY_TIME 1 #define APK_BB_REG_NUM 5 #define APK_AFE_REG_NUM 16 @@ -127,34 +125,32 @@ static inline void rtl92d_release_cckandrw_pagea_ctl(struct ieee80211_hw *hw, *flag); } -extern u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask); -extern void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, u32 data); -extern u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask); -extern void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask, u32 data); -extern bool rtl92d_phy_mac_config(struct ieee80211_hw *hw); -extern bool rtl92d_phy_bb_config(struct ieee80211_hw *hw); -extern bool rtl92d_phy_rf_config(struct ieee80211_hw *hw); -extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, - enum radio_path rfpath); -extern void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -extern void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); -extern void rtl92d_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); -extern void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw, - enum nl80211_channel_type ch_type); -extern u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw); +u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask); +void rtl92d_phy_set_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, u32 data); +u32 rtl92d_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask); +void rtl92d_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask, u32 data); +bool rtl92d_phy_mac_config(struct ieee80211_hw *hw); +bool rtl92d_phy_bb_config(struct ieee80211_hw *hw); +bool rtl92d_phy_rf_config(struct ieee80211_hw *hw); +bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, + enum radio_path rfpath); +void rtl92d_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); +void rtl92d_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); +void rtl92d_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type); +u8 rtl92d_phy_sw_chnl(struct ieee80211_hw *hw); bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum rf_content content, enum radio_path rfpath); bool rtl92d_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); -extern bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw, - enum rf_pwrstate rfpwr_state); +bool rtl92d_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); void rtl92d_phy_config_macphymode(struct ieee80211_hw *hw); void rtl92d_phy_config_macphymode_info(struct ieee80211_hw *hw); @@ -173,6 +169,5 @@ void rtl92d_acquire_cckandrw_pagea_ctl(struct ieee80211_hw *hw, unsigned long *flag); u8 rtl92d_get_rightchnlplace_for_iqk(u8 chnl); void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel); -void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/rf.h b/drivers/net/wireless/rtlwifi/rtl8192de/rf.h index 0fe1a48593e8b90dd797335efed9fd9e38598ef6..7303d12c266fbe2ed44d55aa7e46f689e1acc2c2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/rf.h @@ -30,15 +30,13 @@ #ifndef __RTL92D_RF_H__ #define __RTL92D_RF_H__ -extern void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, - u8 bandwidth); -extern void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel); -extern void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel, u8 channel); -extern bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw); -extern bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0); -extern void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw, - bool bmac0); +void rtl92d_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); +void rtl92d_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl92d_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +bool rtl92d_phy_rf6052_config(struct ieee80211_hw *hw); +bool rtl92d_phy_enable_anotherphy(struct ieee80211_hw *hw, bool bmac0); +void rtl92d_phy_powerdown_anotherphy(struct ieee80211_hw *hw, bool bmac0); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c index c18c04bf0c13e4ac98c050f205bf35ddd7d7d14a..edab5a5351b52a814ec10833109666309279e2b1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../core.h" #include "../pci.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -236,7 +237,7 @@ static struct rtl_hal_ops rtl8192de_hal_ops = { .set_bw_mode = rtl92d_phy_set_bw_mode, .switch_channel = rtl92d_phy_sw_chnl, .dm_watchdog = rtl92d_dm_watchdog, - .scan_operation_backup = rtl92d_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl92d_phy_set_rf_power_state, .led_control = rtl92de_led_control, .set_desc = rtl92de_set_desc, diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index b8ec718a0fabbf348df84820fa530f8e28031699..945ddecf90c9a3b6c6bcd916c3c1d293ac51dc15 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -526,7 +526,6 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, } /*rx_status->qual = stats->signal; */ rx_status->signal = stats->rssi + 10; - /*rx_status->noise = -stats->noise; */ return true; } diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/reg.h b/drivers/net/wireless/rtlwifi/rtl8192se/reg.h index 84d1181795b8aeba6ff9d3d0d0245f75e596933f..c81c8359194007893412cd3d55f1da054b1bc645 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/reg.h @@ -425,14 +425,9 @@ #define EXT_IMEM_CODE_DONE BIT(2) #define IMEM_CHK_RPT BIT(1) #define IMEM_CODE_DONE BIT(0) -#define IMEM_CODE_DONE BIT(0) -#define IMEM_CHK_RPT BIT(1) #define EMEM_CODE_DONE BIT(2) #define EMEM_CHK_RPT BIT(3) -#define DMEM_CODE_DONE BIT(4) #define IMEM_RDY BIT(5) -#define BASECHG BIT(6) -#define FWRDY BIT(7) #define LOAD_FW_READY (IMEM_CODE_DONE | \ IMEM_CHK_RPT | \ EMEM_CODE_DONE | \ diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index c7095118de6e8c23bbfd0ee5727fc353969db9fd..222d2e792ca6d259885fa6da3a8ad792bbb338ad 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -330,7 +330,6 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, /*rx_status->qual = stats->signal; */ rx_status->signal = stats->rssi + 10; - /*rx_status->noise = -stats->noise; */ return true; } diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c index eafbb18dd48e69a44d67cd60f171b5915662e58c..5d318a85eda4047100eeec7c5297412c15d615fd 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c @@ -934,35 +934,6 @@ static long _phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, return pwrout_dbm; } -void rtl8723ae_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - enum io_type iotype; - - if (!is_hal_stop(rtlhal)) { - switch (operation) { - case SCAN_OPT_BACKUP: - iotype = IO_CMD_PAUSE_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - - break; - case SCAN_OPT_RESTORE: - iotype = IO_CMD_RESUME_DM_BY_SCAN; - rtlpriv->cfg->ops->set_hw_reg(hw, - HW_VAR_IO_CMD, - (u8 *)&iotype); - break; - default: - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Unknown Scan Backup operation.\n"); - break; - } - } -} - void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h index e7a59eba351adf85cd951f28377517b8347a6807..007ebdbbe108623f88c163e77d3e548eabc00eec 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h @@ -183,42 +183,40 @@ struct tx_power_struct { u32 mcs_original_offset[4][16]; }; -extern u32 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask); -extern void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw, - u32 regaddr, u32 bitmask, u32 data); -extern u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask); -extern void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 regaddr, - u32 bitmask, u32 data); -extern bool rtl8723ae_phy_mac_config(struct ieee80211_hw *hw); -extern bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw); -extern bool rtl8723ae_phy_rf_config(struct ieee80211_hw *hw); -extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, - enum radio_path rfpath); -extern void rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -extern void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, - long *powerlevel); -extern void rtl8723ae_phy_set_txpower_level(struct ieee80211_hw *hw, - u8 channel); -extern bool rtl8723ae_phy_update_txpower_dbm(struct ieee80211_hw *hw, - long power_indbm); -extern void rtl8723ae_phy_scan_operation_backup(struct ieee80211_hw *hw, - u8 operation); -extern void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw); -extern void rtl8723ae_phy_set_bw_mode(struct ieee80211_hw *hw, - enum nl80211_channel_type ch_type); -extern void rtl8723ae_phy_sw_chnl_callback(struct ieee80211_hw *hw); -extern u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw); -extern void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery); +u32 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask); +void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, u32 data); +u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask); +void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask, u32 data); +bool rtl8723ae_phy_mac_config(struct ieee80211_hw *hw); +bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw); +bool rtl8723ae_phy_rf_config(struct ieee80211_hw *hw); +bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, + enum radio_path rfpath); +void rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); +void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, + long *powerlevel); +void rtl8723ae_phy_set_txpower_level(struct ieee80211_hw *hw, + u8 channel); +bool rtl8723ae_phy_update_txpower_dbm(struct ieee80211_hw *hw, + long power_indbm); +void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw); +void rtl8723ae_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type); +void rtl8723ae_phy_sw_chnl_callback(struct ieee80211_hw *hw); +u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw); +void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery); void rtl8723ae_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl8723ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl8723ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath); bool rtl8723ae_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); -extern bool rtl8723ae_phy_set_rf_power_state(struct ieee80211_hw *hw, - enum rf_pwrstate rfpwr_state); +bool rtl8723ae_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h index d0f9dd79abea6f9f40ed12a1c8cc3eeea337bd68..57f1933ee663e48b2433ef887a0b361a107e9770 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h @@ -32,12 +32,11 @@ #define RF6052_MAX_TX_PWR 0x3F -extern void rtl8723ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, - u8 bandwidth); -extern void rtl8723ae_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel); -extern void rtl8723ae_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, - u8 *ppowerlevel, u8 channel); -extern bool rtl8723ae_phy_rf6052_config(struct ieee80211_hw *hw); +void rtl8723ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth); +void rtl8723ae_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl8723ae_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +bool rtl8723ae_phy_rf6052_config(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c index d9ee2efffe5ffb1284335352eed2e13ab8e4df96..62b204faf773f74b1137aa2ada315375357268e8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c @@ -33,6 +33,7 @@ #include "../core.h" #include "../pci.h" +#include "../base.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -220,7 +221,7 @@ static struct rtl_hal_ops rtl8723ae_hal_ops = { .set_bw_mode = rtl8723ae_phy_set_bw_mode, .switch_channel = rtl8723ae_phy_sw_chnl, .dm_watchdog = rtl8723ae_dm_watchdog, - .scan_operation_backup = rtl8723ae_phy_scan_operation_backup, + .scan_operation_backup = rtl_phy_scan_operation_backup, .set_rf_power_state = rtl8723ae_phy_set_rf_power_state, .led_control = rtl8723ae_led_control, .set_desc = rtl8723ae_set_desc, diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index bcd82a1020a5bebd7104467594ffefc47bf28eba..50b7be3f3a605673756e543e456324993c66980d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -359,7 +359,6 @@ bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw, /*rx_status->qual = status->signal; */ rx_status->signal = status->recvsignalpower + 10; - /*rx_status->noise = -status->noise; */ return true; } diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index e56778cac9bfce39e56d5aa239e34f2fb1135ffb..6e2b5c5c83c8d21234ee013c802d7af317f6db8c 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -455,7 +455,6 @@ static void _rtl_usb_rx_process_agg(struct ieee80211_hw *hw, struct ieee80211_rx_status rx_status = {0}; struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; @@ -498,7 +497,6 @@ static void _rtl_usb_rx_process_noagg(struct ieee80211_hw *hw, struct ieee80211_rx_status rx_status = {0}; struct rtl_stats stats = { .signal = 0, - .noise = -98, .rate = 0, }; @@ -582,12 +580,15 @@ static void _rtl_rx_work(unsigned long param) static unsigned int _rtl_rx_get_padding(struct ieee80211_hdr *hdr, unsigned int len) { +#if NET_IP_ALIGN != 0 unsigned int padding = 0; +#endif /* make function no-op when possible */ if (NET_IP_ALIGN == 0 || len < sizeof(*hdr)) return 0; +#if NET_IP_ALIGN != 0 /* alignment calculation as in lbtf_rx() / carl9170_rx_copy_data() */ /* TODO: deduplicate common code, define helper function instead? */ @@ -608,6 +609,7 @@ static unsigned int _rtl_rx_get_padding(struct ieee80211_hdr *hdr, padding ^= NET_IP_ALIGN; return padding; +#endif } #define __RADIO_TAP_SIZE_RSV 32 diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 703258742d28eefd339bde254d30661c4175b1a9..d224dc3bb092b0ef04545cc57891a7bdb4de4b2f 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -192,8 +192,6 @@ enum hardware_type { (IS_HARDWARE_TYPE_8192DE(rtlhal) || IS_HARDWARE_TYPE_8192DU(rtlhal)) #define IS_HARDWARE_TYPE_8723(rtlhal) \ (IS_HARDWARE_TYPE_8723E(rtlhal) || IS_HARDWARE_TYPE_8723U(rtlhal)) -#define IS_HARDWARE_TYPE_8723U(rtlhal) \ - (rtlhal->hw_type == HARDWARE_TYPE_RTL8723U) #define RX_HAL_IS_CCK_RATE(_pdesc)\ (_pdesc->rxmcs == DESC92_RATE1M || \ diff --git a/drivers/net/wireless/ti/wl1251/spi.c b/drivers/net/wireless/ti/wl1251/spi.c index c7dc6feab2ff38cd2bbbef713ca4b2c0113a4b51..1342f81e683d1498bb16d5904d032009244bed58 100644 --- a/drivers/net/wireless/ti/wl1251/spi.c +++ b/drivers/net/wireless/ti/wl1251/spi.c @@ -243,7 +243,7 @@ static int wl1251_spi_probe(struct spi_device *spi) struct wl1251 *wl; int ret; - pdata = spi->dev.platform_data; + pdata = dev_get_platdata(&spi->dev); if (!pdata) { wl1251_error("no platform data"); return -ENODEV; diff --git a/drivers/net/wireless/ti/wl1251/wl1251.h b/drivers/net/wireless/ti/wl1251/wl1251.h index fd02060038de6479051accfa7b63c1993193315b..2c3bd1bff3f68e08de48b42101c52606a472fa6a 100644 --- a/drivers/net/wireless/ti/wl1251/wl1251.h +++ b/drivers/net/wireless/ti/wl1251/wl1251.h @@ -424,8 +424,8 @@ void wl1251_disable_interrupts(struct wl1251 *wl); #define CHIP_ID_1271_PG10 (0x4030101) #define CHIP_ID_1271_PG20 (0x4030111) -#define WL1251_FW_NAME "wl1251-fw.bin" -#define WL1251_NVS_NAME "wl1251-nvs.bin" +#define WL1251_FW_NAME "ti-connectivity/wl1251-fw.bin" +#define WL1251_NVS_NAME "ti-connectivity/wl1251-nvs.bin" #define WL1251_POWER_ON_SLEEP 10 /* in milliseconds */ diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index 1c627da85083949c9dd9837e09b53b5ca8a9ac74..be7129ba16ad651524910c1897a33b7d48570007 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -333,11 +333,11 @@ static struct wlcore_conf wl12xx_conf = { .always = 0, }, .fwlog = { - .mode = WL12XX_FWLOG_ON_DEMAND, + .mode = WL12XX_FWLOG_CONTINUOUS, .mem_blocks = 2, .severity = 0, .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, - .output = WL12XX_FWLOG_OUTPUT_HOST, + .output = WL12XX_FWLOG_OUTPUT_DBG_PINS, .threshold = 0, }, .rate = { @@ -717,6 +717,9 @@ static int wl12xx_identify_chip(struct wl1271 *wl) goto out; } + wl->fw_mem_block_size = 256; + wl->fwlog_end = 0x2000000; + /* common settings */ wl->scan_templ_id_2_4 = CMD_TEMPL_APP_PROBE_REQ_2_4_LEGACY; wl->scan_templ_id_5 = CMD_TEMPL_APP_PROBE_REQ_5_LEGACY; @@ -1262,9 +1265,10 @@ static int wl12xx_boot(struct wl1271 *wl) BA_SESSION_RX_CONSTRAINT_EVENT_ID | REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | INACTIVE_STA_EVENT_ID | - MAX_TX_RETRY_EVENT_ID | CHANNEL_SWITCH_COMPLETE_EVENT_ID; + wl->ap_event_mask = MAX_TX_RETRY_EVENT_ID; + ret = wlcore_boot_run_firmware(wl); if (ret < 0) goto out; @@ -1648,6 +1652,11 @@ static bool wl12xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, return true; } +static u32 wl12xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr) +{ + return hwaddr << 5; +} + static int wl12xx_setup(struct wl1271 *wl); static struct wlcore_ops wl12xx_ops = { @@ -1684,6 +1693,7 @@ static struct wlcore_ops wl12xx_ops = { .channel_switch = wl12xx_cmd_channel_switch, .pre_pkt_send = NULL, .set_peer_cap = wl12xx_set_peer_cap, + .convert_hwaddr = wl12xx_convert_hwaddr, .lnk_high_prio = wl12xx_lnk_high_prio, .lnk_low_prio = wl12xx_lnk_low_prio, }; @@ -1704,7 +1714,7 @@ static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { static int wl12xx_setup(struct wl1271 *wl) { struct wl12xx_priv *priv = wl->priv; - struct wlcore_platdev_data *pdev_data = wl->pdev->dev.platform_data; + struct wlcore_platdev_data *pdev_data = dev_get_platdata(&wl->pdev->dev); struct wl12xx_platform_data *pdata = pdev_data->pdata; wl->rtable = wl12xx_rtable; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 7aa0eb848c5a2c83cc93f120f96567cd04d5d0fa..ec37b16585df939938fb1a75ef060ef0ea3d73fd 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -456,11 +456,11 @@ static struct wlcore_conf wl18xx_conf = { .always = 0, }, .fwlog = { - .mode = WL12XX_FWLOG_ON_DEMAND, + .mode = WL12XX_FWLOG_CONTINUOUS, .mem_blocks = 2, .severity = 0, .timestamp = WL12XX_FWLOG_TIMESTAMP_DISABLED, - .output = WL12XX_FWLOG_OUTPUT_HOST, + .output = WL12XX_FWLOG_OUTPUT_DBG_PINS, .threshold = 0, }, .rate = { @@ -505,7 +505,7 @@ static struct wlcore_conf wl18xx_conf = { static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .ht = { - .mode = HT_MODE_DEFAULT, + .mode = HT_MODE_WIDE, }, .phy = { .phy_standalone = 0x00, @@ -516,7 +516,7 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .auto_detect = 0x00, .dedicated_fem = FEM_NONE, .low_band_component = COMPONENT_3_WAY_SWITCH, - .low_band_component_type = 0x04, + .low_band_component_type = 0x05, .high_band_component = COMPONENT_2_WAY_SWITCH, .high_band_component_type = 0x09, .tcxo_ldo_voltage = 0x00, @@ -556,15 +556,15 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .per_chan_pwr_limit_arr_11p = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, .psat = 0, - .low_power_val = 0x08, - .med_power_val = 0x12, - .high_power_val = 0x18, - .low_power_val_2nd = 0x05, - .med_power_val_2nd = 0x0a, - .high_power_val_2nd = 0x14, .external_pa_dc2dc = 0, .number_of_assembled_ant2_4 = 2, .number_of_assembled_ant5 = 1, + .low_power_val = 0xff, + .med_power_val = 0xff, + .high_power_val = 0xff, + .low_power_val_2nd = 0xff, + .med_power_val_2nd = 0xff, + .high_power_val_2nd = 0xff, .tx_rf_margin = 1, }, }; @@ -623,6 +623,18 @@ static const int wl18xx_rtable[REG_TABLE_LEN] = { [REG_RAW_FW_STATUS_ADDR] = WL18XX_FW_STATUS_ADDR, }; +static const struct wl18xx_clk_cfg wl18xx_clk_table_coex[NUM_CLOCK_CONFIGS] = { + [CLOCK_CONFIG_16_2_M] = { 8, 121, 0, 0, false }, + [CLOCK_CONFIG_16_368_M] = { 8, 120, 0, 0, false }, + [CLOCK_CONFIG_16_8_M] = { 8, 117, 0, 0, false }, + [CLOCK_CONFIG_19_2_M] = { 10, 128, 0, 0, false }, + [CLOCK_CONFIG_26_M] = { 11, 104, 0, 0, false }, + [CLOCK_CONFIG_32_736_M] = { 8, 120, 0, 0, false }, + [CLOCK_CONFIG_33_6_M] = { 8, 117, 0, 0, false }, + [CLOCK_CONFIG_38_468_M] = { 10, 128, 0, 0, false }, + [CLOCK_CONFIG_52_M] = { 11, 104, 0, 0, false }, +}; + static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { [CLOCK_CONFIG_16_2_M] = { 7, 104, 801, 4, true }, [CLOCK_CONFIG_16_368_M] = { 9, 132, 3751, 4, true }, @@ -674,6 +686,9 @@ static int wl18xx_identify_chip(struct wl1271 *wl) goto out; } + wl->fw_mem_block_size = 272; + wl->fwlog_end = 0x40000000; + wl->scan_templ_id_2_4 = CMD_TEMPL_CFG_PROBE_REQ_2_4; wl->scan_templ_id_5 = CMD_TEMPL_CFG_PROBE_REQ_5; wl->sched_scan_templ_id_2_4 = CMD_TEMPL_PROBE_REQ_2_4_PERIODIC; @@ -704,6 +719,23 @@ static int wl18xx_set_clk(struct wl1271 *wl) wl18xx_clk_table[clk_freq].p, wl18xx_clk_table[clk_freq].q, wl18xx_clk_table[clk_freq].swallow ? "swallow" : "spit"); + /* coex PLL configuration */ + ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_N, + wl18xx_clk_table_coex[clk_freq].n); + if (ret < 0) + goto out; + + ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_M, + wl18xx_clk_table_coex[clk_freq].m); + if (ret < 0) + goto out; + + /* bypass the swallowing logic */ + ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_SWALLOW_EN, + PLLSH_COEX_PLL_SWALLOW_EN_VAL1); + if (ret < 0) + goto out; + ret = wl18xx_top_reg_write(wl, PLLSH_WCS_PLL_N, wl18xx_clk_table[clk_freq].n); if (ret < 0) @@ -745,6 +777,30 @@ static int wl18xx_set_clk(struct wl1271 *wl) PLLSH_WCS_PLL_SWALLOW_EN_VAL2); } + /* choose WCS PLL */ + ret = wl18xx_top_reg_write(wl, PLLSH_WL_PLL_SEL, + PLLSH_WL_PLL_SEL_WCS_PLL); + if (ret < 0) + goto out; + + /* enable both PLLs */ + ret = wl18xx_top_reg_write(wl, PLLSH_WL_PLL_EN, PLLSH_WL_PLL_EN_VAL1); + if (ret < 0) + goto out; + + udelay(1000); + + /* disable coex PLL */ + ret = wl18xx_top_reg_write(wl, PLLSH_WL_PLL_EN, PLLSH_WL_PLL_EN_VAL2); + if (ret < 0) + goto out; + + /* reset the swallowing logic */ + ret = wl18xx_top_reg_write(wl, PLLSH_COEX_PLL_SWALLOW_EN, + PLLSH_COEX_PLL_SWALLOW_EN_VAL2); + if (ret < 0) + goto out; + out: return ret; } @@ -935,10 +991,11 @@ static int wl18xx_boot(struct wl1271 *wl) BA_SESSION_RX_CONSTRAINT_EVENT_ID | REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | INACTIVE_STA_EVENT_ID | - MAX_TX_FAILURE_EVENT_ID | CHANNEL_SWITCH_COMPLETE_EVENT_ID | DFS_CHANNELS_CONFIG_COMPLETE_EVENT; + wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID; + ret = wlcore_boot_run_firmware(wl); if (ret < 0) goto out; @@ -1175,16 +1232,48 @@ static u32 wl18xx_ap_get_mimo_wide_rate_mask(struct wl1271 *wl, } } +static const char *wl18xx_rdl_name(enum wl18xx_rdl_num rdl_num) +{ + switch (rdl_num) { + case RDL_1_HP: + return "183xH"; + case RDL_2_SP: + return "183x or 180x"; + case RDL_3_HP: + return "187xH"; + case RDL_4_SP: + return "187x"; + case RDL_5_SP: + return "RDL11 - Not Supported"; + case RDL_6_SP: + return "180xD"; + case RDL_7_SP: + return "RDL13 - Not Supported (1893Q)"; + case RDL_8_SP: + return "18xxQ"; + case RDL_NONE: + return "UNTRIMMED"; + default: + return "UNKNOWN"; + } +} + static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) { u32 fuse; - s8 rom = 0, metal = 0, pg_ver = 0, rdl_ver = 0; + s8 rom = 0, metal = 0, pg_ver = 0, rdl_ver = 0, package_type = 0; int ret; ret = wlcore_set_partition(wl, &wl->ptable[PART_TOP_PRCM_ELP_SOC]); if (ret < 0) goto out; + ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_2_3, &fuse); + if (ret < 0) + goto out; + + package_type = (fuse >> WL18XX_PACKAGE_TYPE_OFFSET) & 1; + ret = wlcore_read32(wl, WL18XX_REG_FUSE_DATA_1_3, &fuse); if (ret < 0) goto out; @@ -1192,7 +1281,7 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) pg_ver = (fuse & WL18XX_PG_VER_MASK) >> WL18XX_PG_VER_OFFSET; rom = (fuse & WL18XX_ROM_VER_MASK) >> WL18XX_ROM_VER_OFFSET; - if (rom <= 0xE) + if ((rom <= 0xE) && (package_type == WL18XX_PACKAGE_TYPE_WSP)) metal = (fuse & WL18XX_METAL_VER_MASK) >> WL18XX_METAL_VER_OFFSET; else @@ -1204,11 +1293,9 @@ static int wl18xx_get_pg_ver(struct wl1271 *wl, s8 *ver) goto out; rdl_ver = (fuse & WL18XX_RDL_VER_MASK) >> WL18XX_RDL_VER_OFFSET; - if (rdl_ver > RDL_MAX) - rdl_ver = RDL_NONE; - wl1271_info("wl18xx HW: RDL %d, %s, PG %x.%x (ROM %x)", - rdl_ver, rdl_names[rdl_ver], pg_ver, metal, rom); + wl1271_info("wl18xx HW: %s, PG %d.%d (ROM 0x%x)", + wl18xx_rdl_name(rdl_ver), pg_ver, metal, rom); if (ver) *ver = pg_ver; @@ -1521,6 +1608,11 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, return lnk->allocated_pkts < thold; } +static u32 wl18xx_convert_hwaddr(struct wl1271 *wl, u32 hwaddr) +{ + return hwaddr & ~0x80000000; +} + static int wl18xx_setup(struct wl1271 *wl); static struct wlcore_ops wl18xx_ops = { @@ -1558,6 +1650,7 @@ static struct wlcore_ops wl18xx_ops = { .pre_pkt_send = wl18xx_pre_pkt_send, .sta_rc_update = wl18xx_sta_rc_update, .set_peer_cap = wl18xx_set_peer_cap, + .convert_hwaddr = wl18xx_convert_hwaddr, .lnk_high_prio = wl18xx_lnk_high_prio, .lnk_low_prio = wl18xx_lnk_low_prio, }; diff --git a/drivers/net/wireless/ti/wl18xx/reg.h b/drivers/net/wireless/ti/wl18xx/reg.h index 05dd8bad27469389e0e5ff3cf713fab083d348d5..a433a75f3cd7c85d51f67cfb8d03830e35c041be 100644 --- a/drivers/net/wireless/ti/wl18xx/reg.h +++ b/drivers/net/wireless/ti/wl18xx/reg.h @@ -114,6 +114,11 @@ #define PLATFORM_DETECTION 0xA0E3E0 #define OCS_EN 0xA02080 #define PRIMARY_CLK_DETECT 0xA020A6 +#define PLLSH_COEX_PLL_N 0xA02384 +#define PLLSH_COEX_PLL_M 0xA02382 +#define PLLSH_COEX_PLL_SWALLOW_EN 0xA0238E +#define PLLSH_WL_PLL_SEL 0xA02398 + #define PLLSH_WCS_PLL_N 0xA02362 #define PLLSH_WCS_PLL_M 0xA02360 #define PLLSH_WCS_PLL_Q_FACTOR_CFG_1 0xA02364 @@ -128,19 +133,30 @@ #define PLLSH_WCS_PLL_P_FACTOR_CFG_1_MASK 0xFFFF #define PLLSH_WCS_PLL_P_FACTOR_CFG_2_MASK 0x000F +#define PLLSH_WL_PLL_EN_VAL1 0x7 +#define PLLSH_WL_PLL_EN_VAL2 0x2 +#define PLLSH_COEX_PLL_SWALLOW_EN_VAL1 0x2 +#define PLLSH_COEX_PLL_SWALLOW_EN_VAL2 0x11 + #define PLLSH_WCS_PLL_SWALLOW_EN_VAL1 0x1 #define PLLSH_WCS_PLL_SWALLOW_EN_VAL2 0x12 +#define PLLSH_WL_PLL_SEL_WCS_PLL 0x0 +#define PLLSH_WL_PLL_SEL_COEX_PLL 0x1 + #define WL18XX_REG_FUSE_DATA_1_3 0xA0260C #define WL18XX_PG_VER_MASK 0x70 #define WL18XX_PG_VER_OFFSET 4 -#define WL18XX_ROM_VER_MASK 0x3 -#define WL18XX_ROM_VER_OFFSET 0 +#define WL18XX_ROM_VER_MASK 0x3e00 +#define WL18XX_ROM_VER_OFFSET 9 #define WL18XX_METAL_VER_MASK 0xC #define WL18XX_METAL_VER_OFFSET 2 #define WL18XX_NEW_METAL_VER_MASK 0x180 #define WL18XX_NEW_METAL_VER_OFFSET 7 +#define WL18XX_PACKAGE_TYPE_OFFSET 13 +#define WL18XX_PACKAGE_TYPE_WSP 0 + #define WL18XX_REG_FUSE_DATA_2_3 0xA02614 #define WL18XX_RDL_VER_MASK 0x1f00 #define WL18XX_RDL_VER_OFFSET 8 @@ -201,24 +217,21 @@ enum { NUM_BOARD_TYPES, }; -enum { +enum wl18xx_rdl_num { RDL_NONE = 0, RDL_1_HP = 1, RDL_2_SP = 2, RDL_3_HP = 3, RDL_4_SP = 4, + RDL_5_SP = 0x11, + RDL_6_SP = 0x12, + RDL_7_SP = 0x13, + RDL_8_SP = 0x14, _RDL_LAST, RDL_MAX = _RDL_LAST - 1, }; -static const char * const rdl_names[] = { - [RDL_NONE] = "", - [RDL_1_HP] = "1853 SISO", - [RDL_2_SP] = "1857 MIMO", - [RDL_3_HP] = "1893 SISO", - [RDL_4_SP] = "1897 MIMO", -}; /* FPGA_SPARE_1 register - used to change the PHY ATPG clock at boot time */ #define WL18XX_PHY_FPGA_SPARE_1 0x8093CA40 diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index 7a970cd9c5551cc538ec0177758f023589c9e281..ec83675a244697537afd791613cfec9e87d13caa 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -162,7 +162,8 @@ int wl1271_acx_mem_map(struct wl1271 *wl, struct acx_header *mem_map, wl1271_debug(DEBUG_ACX, "acx mem map"); - ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, len); + ret = wl1271_cmd_interrogate(wl, ACX_MEM_MAP, mem_map, + sizeof(struct acx_header), len); if (ret < 0) return ret; @@ -722,6 +723,7 @@ int wl1271_acx_statistics(struct wl1271 *wl, void *stats) wl1271_debug(DEBUG_ACX, "acx statistics"); ret = wl1271_cmd_interrogate(wl, ACX_STATISTICS, stats, + sizeof(struct acx_header), wl->stats.fw_stats_len); if (ret < 0) { wl1271_warning("acx statistics failed: %d", ret); @@ -1470,8 +1472,8 @@ int wl12xx_acx_tsf_info(struct wl1271 *wl, struct wl12xx_vif *wlvif, tsf_info->role_id = wlvif->role_id; - ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO, - tsf_info, sizeof(*tsf_info)); + ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO, tsf_info, + sizeof(struct acx_header), sizeof(*tsf_info)); if (ret < 0) { wl1271_warning("acx tsf info interrogate failed"); goto out; @@ -1752,7 +1754,7 @@ int wlcore_acx_average_rssi(struct wl1271 *wl, struct wl12xx_vif *wlvif, acx->role_id = wlvif->role_id; ret = wl1271_cmd_interrogate(wl, ACX_ROAMING_STATISTICS_TBL, - acx, sizeof(*acx)); + acx, sizeof(*acx), sizeof(*acx)); if (ret < 0) { wl1271_warning("acx roaming statistics failed: %d", ret); ret = -ENOMEM; diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index c9e060795d13760befc72a9190b008b65b6f1b41..34d9dfff2ad39ead03d3da275cd3e4443385a220 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -60,7 +60,8 @@ static int __wlcore_cmd_send(struct wl1271 *wl, u16 id, void *buf, u16 status; u16 poll_count = 0; - if (WARN_ON(unlikely(wl->state == WLCORE_STATE_RESTARTING))) + if (WARN_ON(wl->state == WLCORE_STATE_RESTARTING && + id != CMD_STOP_FWLOGGER)) return -EIO; cmd = buf; @@ -845,7 +846,8 @@ EXPORT_SYMBOL_GPL(wl1271_cmd_test); * @buf: buffer for the response, including all headers, must work with dma * @len: length of buf */ -int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) +int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, + size_t cmd_len, size_t res_len) { struct acx_header *acx = buf; int ret; @@ -854,10 +856,10 @@ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len) acx->id = cpu_to_le16(id); - /* payload length, does not include any headers */ - acx->len = cpu_to_le16(len - sizeof(*acx)); + /* response payload length, does not include any headers */ + acx->len = cpu_to_le16(res_len - sizeof(*acx)); - ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, sizeof(*acx), len); + ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, cmd_len, res_len); if (ret < 0) wl1271_error("INTERROGATE command failed"); @@ -1126,6 +1128,8 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, u16 template_id_2_4 = wl->scan_templ_id_2_4; u16 template_id_5 = wl->scan_templ_id_5; + wl1271_debug(DEBUG_SCAN, "build probe request band %d", band); + skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len, ie_len); if (!skb) { @@ -1135,8 +1139,6 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif, if (ie_len) memcpy(skb_put(skb, ie_len), ie, ie_len); - wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); - if (sched_scan && (wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) { template_id_2_4 = wl->sched_scan_templ_id_2_4; @@ -1172,7 +1174,7 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, if (!skb) goto out; - wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len); + wl1271_debug(DEBUG_SCAN, "set ap probe request template"); rate = wl1271_tx_min_rate_get(wl, wlvif->bitrate_masks[wlvif->band]); if (wlvif->band == IEEE80211_BAND_2GHZ) @@ -1607,33 +1609,43 @@ out: static int wlcore_get_reg_conf_ch_idx(enum ieee80211_band band, u16 ch) { - int idx = -1; - + /* + * map the given band/channel to the respective predefined + * bit expected by the fw + */ switch (band) { - case IEEE80211_BAND_5GHZ: - if (ch >= 8 && ch <= 16) - idx = ((ch-8)/4 + 18); - else if (ch >= 34 && ch <= 64) - idx = ((ch-34)/2 + 3 + 18); - else if (ch >= 100 && ch <= 140) - idx = ((ch-100)/4 + 15 + 18); - else if (ch >= 149 && ch <= 165) - idx = ((ch-149)/4 + 26 + 18); - else - idx = -1; - break; case IEEE80211_BAND_2GHZ: + /* channels 1..14 are mapped to 0..13 */ if (ch >= 1 && ch <= 14) - idx = ch - 1; - else - idx = -1; + return ch - 1; + break; + case IEEE80211_BAND_5GHZ: + switch (ch) { + case 8 ... 16: + /* channels 8,12,16 are mapped to 18,19,20 */ + return 18 + (ch-8)/4; + case 34 ... 48: + /* channels 34,36..48 are mapped to 21..28 */ + return 21 + (ch-34)/2; + case 52 ... 64: + /* channels 52,56..64 are mapped to 29..32 */ + return 29 + (ch-52)/4; + case 100 ... 140: + /* channels 100,104..140 are mapped to 33..43 */ + return 33 + (ch-100)/4; + case 149 ... 165: + /* channels 149,153..165 are mapped to 44..48 */ + return 44 + (ch-149)/4; + default: + break; + } break; default: - wl1271_error("get reg conf ch idx - unknown band: %d", - (int)band); + break; } - return idx; + wl1271_error("%s: unknown band/channel: %d/%d", __func__, band, ch); + return -1; } void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel, @@ -1646,7 +1658,7 @@ void wlcore_set_pending_regdomain_ch(struct wl1271 *wl, u16 channel, ch_bit_idx = wlcore_get_reg_conf_ch_idx(band, channel); - if (ch_bit_idx > 0 && ch_bit_idx <= WL1271_MAX_CHANNELS) + if (ch_bit_idx >= 0 && ch_bit_idx <= WL1271_MAX_CHANNELS) set_bit(ch_bit_idx, (long *)wl->reg_ch_conf_pending); } diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index fd34123047cdd0255f5a48b7826ddd117b14814d..323d4a856e4ba80d37f52fadf0832ad09066996e 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -45,7 +45,8 @@ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum ieee80211_band band, int channel); int wl12xx_stop_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif); int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer); -int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, size_t len); +int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf, + size_t cmd_len, size_t res_len); int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len); int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf, size_t len, unsigned long valid_rets); diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 2b96ff821341103ac935ade4765409b21e142409..40995c42bef8882458c331c9c6cc863210958d63 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -1274,6 +1274,9 @@ struct conf_rx_streaming_settings { u8 always; } __packed; +#define CONF_FWLOG_MIN_MEM_BLOCKS 2 +#define CONF_FWLOG_MAX_MEM_BLOCKS 16 + struct conf_fwlog { /* Continuous or on-demand */ u8 mode; @@ -1281,7 +1284,7 @@ struct conf_fwlog { /* * Number of memory blocks dedicated for the FW logger * - * Range: 1-3, or 0 to disable the FW logger + * Range: 2-16, or 0 to disable the FW logger */ u8 mem_blocks; diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index e17630c2a84948d40bb5d0f2c05f4ce1e8ccea2c..89893c7170253c8cc5400bf7941cc9ce4a3d3051 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -437,6 +437,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, int res = 0; ssize_t ret; char *buf; + struct wl12xx_vif *wlvif; #define DRIVER_STATE_BUF_LEN 1024 @@ -450,12 +451,28 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\ #x " = " fmt "\n", wl->x)) +#define DRIVER_STATE_PRINT_GENERIC(x, fmt, args...) \ + (res += scnprintf(buf + res, DRIVER_STATE_BUF_LEN - res,\ + #x " = " fmt "\n", args)) + #define DRIVER_STATE_PRINT_LONG(x) DRIVER_STATE_PRINT(x, "%ld") #define DRIVER_STATE_PRINT_INT(x) DRIVER_STATE_PRINT(x, "%d") #define DRIVER_STATE_PRINT_STR(x) DRIVER_STATE_PRINT(x, "%s") #define DRIVER_STATE_PRINT_LHEX(x) DRIVER_STATE_PRINT(x, "0x%lx") #define DRIVER_STATE_PRINT_HEX(x) DRIVER_STATE_PRINT(x, "0x%x") + wl12xx_for_each_wlvif_sta(wl, wlvif) { + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + continue; + + DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel, + wlvif->p2p ? "P2P-CL" : "STA"); + } + + wl12xx_for_each_wlvif_ap(wl, wlvif) + DRIVER_STATE_PRINT_GENERIC(channel, "%d (%s)", wlvif->channel, + wlvif->p2p ? "P2P-GO" : "AP"); + DRIVER_STATE_PRINT_INT(tx_blocks_available); DRIVER_STATE_PRINT_INT(tx_allocated_blocks); DRIVER_STATE_PRINT_INT(tx_allocated_pkts[0]); @@ -474,7 +491,6 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_INT(tx_blocks_freed); DRIVER_STATE_PRINT_INT(rx_counter); DRIVER_STATE_PRINT_INT(state); - DRIVER_STATE_PRINT_INT(channel); DRIVER_STATE_PRINT_INT(band); DRIVER_STATE_PRINT_INT(power_level); DRIVER_STATE_PRINT_INT(sg_enabled); diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 67f61689b49edcba1e54198db538eb277aa4bda0..8d3b34965db3475f64178eeb6463ec2589434c3f 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -266,6 +266,7 @@ int wl1271_event_unmask(struct wl1271 *wl) { int ret; + wl1271_debug(DEBUG_EVENT, "unmasking event_mask 0x%x", wl->event_mask); ret = wl1271_acx_event_mbox_mask(wl, ~(wl->event_mask)); if (ret < 0) return ret; diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 7fd260c02a0a15aa2a0b8c92867a09fc9af96739..51f8d634d32f43274d2eaccb679a6dff732468f3 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -222,6 +222,15 @@ wlcore_hw_set_peer_cap(struct wl1271 *wl, return 0; } +static inline u32 +wlcore_hw_convert_hwaddr(struct wl1271 *wl, u32 hwaddr) +{ + if (!wl->ops->convert_hwaddr) + BUG_ON(1); + + return wl->ops->convert_hwaddr(wl, hwaddr); +} + static inline bool wlcore_hw_lnk_high_prio(struct wl1271 *wl, u8 hlid, struct wl1271_link *lnk) diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 5c6f11e157d9b0016633dfe932f34984fedeb65c..7699f9d07e2636e3528fa7e3c28f260bfaa63e3f 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -571,6 +571,12 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); if (ret < 0) return ret; + + /* unmask ap events */ + wl->event_mask |= wl->ap_event_mask; + ret = wl1271_event_unmask(wl); + if (ret < 0) + return ret; /* first STA, no APs */ } else if (wl->sta_count == 0 && wl->ap_count == 0 && !is_ap) { u8 sta_auth = wl->conf.conn.sta_sleep_auth; diff --git a/drivers/net/wireless/ti/wlcore/io.h b/drivers/net/wireless/ti/wlcore/io.h index af7d9f9b3b4db2140006fb0639af3ff028af79e5..07e3d6a049adf33d40dc586c27ef66c4428b83d1 100644 --- a/drivers/net/wireless/ti/wlcore/io.h +++ b/drivers/net/wireless/ti/wlcore/io.h @@ -165,8 +165,8 @@ static inline int __must_check wlcore_read_hwaddr(struct wl1271 *wl, int hwaddr, int physical; int addr; - /* Addresses are stored internally as addresses to 32 bytes blocks */ - addr = hwaddr << 5; + /* Convert from FW internal address which is chip arch dependent */ + addr = wl->ops->convert_hwaddr(wl, hwaddr); physical = wlcore_translate_addr(wl, addr); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 38995f90040dea19b50d9c46ea2d4c8b7dc1b41b..0368b9cbfb896d6460da3635b199ef6266805644 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -44,6 +44,7 @@ #define WL1271_BOOT_RETRIES 3 static char *fwlog_param; +static int fwlog_mem_blocks = -1; static int bug_on_recovery = -1; static int no_recovery = -1; @@ -291,6 +292,18 @@ static void wlcore_adjust_conf(struct wl1271 *wl) { /* Adjust settings according to optional module parameters */ + /* Firmware Logger params */ + if (fwlog_mem_blocks != -1) { + if (fwlog_mem_blocks >= CONF_FWLOG_MIN_MEM_BLOCKS && + fwlog_mem_blocks <= CONF_FWLOG_MAX_MEM_BLOCKS) { + wl->conf.fwlog.mem_blocks = fwlog_mem_blocks; + } else { + wl1271_error( + "Illegal fwlog_mem_blocks=%d using default %d", + fwlog_mem_blocks, wl->conf.fwlog.mem_blocks); + } + } + if (fwlog_param) { if (!strcmp(fwlog_param, "continuous")) { wl->conf.fwlog.mode = WL12XX_FWLOG_CONTINUOUS; @@ -780,6 +793,7 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl) if (wl->state == WLCORE_STATE_ON) { wl->state = WLCORE_STATE_RESTARTING; set_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags); + wl1271_ps_elp_wakeup(wl); wlcore_disable_interrupts_nosync(wl); ieee80211_queue_work(wl->hw, &wl->recovery_work); } @@ -787,19 +801,10 @@ void wl12xx_queue_recovery_work(struct wl1271 *wl) size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) { - size_t len = 0; - - /* The FW log is a length-value list, find where the log end */ - while (len < maxlen) { - if (memblock[len] == 0) - break; - if (len + memblock[len] + 1 > maxlen) - break; - len += memblock[len] + 1; - } + size_t len; /* Make sure we have enough room */ - len = min(len, (size_t)(PAGE_SIZE - wl->fwlog_size)); + len = min(maxlen, (size_t)(PAGE_SIZE - wl->fwlog_size)); /* Fill the FW log file, consumed by the sysfs fwlog entry */ memcpy(wl->fwlog + wl->fwlog_size, memblock, len); @@ -808,10 +813,9 @@ size_t wl12xx_copy_fwlog(struct wl1271 *wl, u8 *memblock, size_t maxlen) return len; } -#define WLCORE_FW_LOG_END 0x2000000 - static void wl12xx_read_fwlog_panic(struct wl1271 *wl) { + struct wlcore_partition_set part, old_part; u32 addr; u32 offset; u32 end_of_log; @@ -824,7 +828,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) wl1271_info("Reading FW panic log"); - block = kmalloc(WL12XX_HW_BLOCK_SIZE, GFP_KERNEL); + block = kmalloc(wl->fw_mem_block_size, GFP_KERNEL); if (!block) return; @@ -850,17 +854,31 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) if (wl->conf.fwlog.mode == WL12XX_FWLOG_CONTINUOUS) { offset = sizeof(addr) + sizeof(struct wl1271_rx_descriptor); - end_of_log = WLCORE_FW_LOG_END; + end_of_log = wl->fwlog_end; } else { offset = sizeof(addr); end_of_log = addr; } + old_part = wl->curr_part; + memset(&part, 0, sizeof(part)); + /* Traverse the memory blocks linked list */ do { - memset(block, 0, WL12XX_HW_BLOCK_SIZE); - ret = wlcore_read_hwaddr(wl, addr, block, WL12XX_HW_BLOCK_SIZE, - false); + part.mem.start = wlcore_hw_convert_hwaddr(wl, addr); + part.mem.size = PAGE_SIZE; + + ret = wlcore_set_partition(wl, &part); + if (ret < 0) { + wl1271_error("%s: set_partition start=0x%X size=%d", + __func__, part.mem.start, part.mem.size); + goto out; + } + + memset(block, 0, wl->fw_mem_block_size); + ret = wlcore_read_hwaddr(wl, addr, block, + wl->fw_mem_block_size, false); + if (ret < 0) goto out; @@ -871,8 +889,9 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) * on demand mode and is equal to 0x2000000 in continuous mode. */ addr = le32_to_cpup((__le32 *)block); + if (!wl12xx_copy_fwlog(wl, block + offset, - WL12XX_HW_BLOCK_SIZE - offset)) + wl->fw_mem_block_size - offset)) break; } while (addr && (addr != end_of_log)); @@ -880,6 +899,7 @@ static void wl12xx_read_fwlog_panic(struct wl1271 *wl) out: kfree(block); + wlcore_set_partition(wl, &old_part); } static void wlcore_print_recovery(struct wl1271 *wl) @@ -924,7 +944,8 @@ static void wl1271_recovery_work(struct work_struct *work) goto out_unlock; if (!test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) { - wl12xx_read_fwlog_panic(wl); + if (wl->conf.fwlog.output == WL12XX_FWLOG_OUTPUT_HOST) + wl12xx_read_fwlog_panic(wl); wlcore_print_recovery(wl); } @@ -1062,7 +1083,8 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode) static const char* const PLT_MODE[] = { "PLT_OFF", "PLT_ON", - "PLT_FEM_DETECT" + "PLT_FEM_DETECT", + "PLT_CHIP_AWAKE" }; int ret; @@ -1088,9 +1110,11 @@ int wl1271_plt_start(struct wl1271 *wl, const enum plt_mode plt_mode) if (ret < 0) goto power_off; - ret = wl->ops->plt_init(wl); - if (ret < 0) - goto power_off; + if (plt_mode != PLT_CHIP_AWAKE) { + ret = wl->ops->plt_init(wl); + if (ret < 0) + goto power_off; + } wl->state = WLCORE_STATE_ON; wl1271_notice("firmware booted in PLT mode %s (%s)", @@ -1925,8 +1949,10 @@ static void wlcore_op_stop_locked(struct wl1271 *wl) /* * FW channels must be re-calibrated after recovery, - * clear the last Reg-Domain channel configuration. + * save current Reg-Domain channel configuration and clear it. */ + memcpy(wl->reg_ch_conf_pending, wl->reg_ch_conf_last, + sizeof(wl->reg_ch_conf_pending)); memset(wl->reg_ch_conf_last, 0, sizeof(wl->reg_ch_conf_last)); } @@ -2008,6 +2034,47 @@ out: mutex_unlock(&wl->mutex); } +static void wlcore_pending_auth_complete_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wl1271 *wl; + struct wl12xx_vif *wlvif; + unsigned long time_spare; + int ret; + + dwork = container_of(work, struct delayed_work, work); + wlvif = container_of(dwork, struct wl12xx_vif, + pending_auth_complete_work); + wl = wlvif->wl; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + /* + * Make sure a second really passed since the last auth reply. Maybe + * a second auth reply arrived while we were stuck on the mutex. + * Check for a little less than the timeout to protect from scheduler + * irregularities. + */ + time_spare = jiffies + + msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT - 50); + if (!time_after(time_spare, wlvif->pending_auth_reply_time)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + /* cancel the ROC if active */ + wlcore_update_inconn_sta(wl, wlvif, NULL, false); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static int wl12xx_allocate_rate_policy(struct wl1271 *wl, u8 *idx) { u8 policy = find_first_zero_bit(wl->rate_policies_map, @@ -2159,6 +2226,8 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) wlcore_channel_switch_work); INIT_DELAYED_WORK(&wlvif->connection_loss_work, wlcore_connection_loss_work); + INIT_DELAYED_WORK(&wlvif->pending_auth_complete_work, + wlcore_pending_auth_complete_work); INIT_LIST_HEAD(&wlvif->list); setup_timer(&wlvif->rx_streaming_timer, wl1271_rx_streaming_timer, @@ -2376,6 +2445,11 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, int ret = 0; u8 role_type; + if (wl->plt) { + wl1271_error("Adding Interface not allowed while in PLT mode"); + return -EBUSY; + } + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | IEEE80211_VIF_SUPPORTS_CQM_RSSI; @@ -2572,6 +2646,12 @@ deinit: !test_bit(WL1271_FLAG_INTENDED_FW_RECOVERY, &wl->flags)) goto unlock; + if (wl->ap_count == 0 && is_ap) { + /* mask ap events */ + wl->event_mask &= ~wl->ap_event_mask; + wl1271_event_unmask(wl); + } + if (wl->ap_count == 0 && is_ap && wl->sta_count) { u8 sta_auth = wl->conf.conn.sta_sleep_auth; /* Configure for power according to debugfs */ @@ -2590,6 +2670,7 @@ unlock: cancel_work_sync(&wlvif->rx_streaming_disable_work); cancel_delayed_work_sync(&wlvif->connection_loss_work); cancel_delayed_work_sync(&wlvif->channel_switch_work); + cancel_delayed_work_sync(&wlvif->pending_auth_complete_work); mutex_lock(&wl->mutex); } @@ -2875,6 +2956,25 @@ static void wl1271_set_band_rate(struct wl1271 *wl, struct wl12xx_vif *wlvif) wlvif->rate_set = wlvif->basic_rate_set; } +static void wl1271_sta_handle_idle(struct wl1271 *wl, struct wl12xx_vif *wlvif, + bool idle) +{ + bool cur_idle = !test_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags); + + if (idle == cur_idle) + return; + + if (idle) { + clear_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags); + } else { + /* The current firmware only supports sched_scan in idle */ + if (wl->sched_vif == wlvif) + wl->ops->sched_scan_stop(wl, wlvif); + + set_bit(WLVIF_FLAG_ACTIVE, &wlvif->flags); + } +} + static int wl12xx_config_vif(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_conf *conf, u32 changed) { @@ -3969,6 +4069,13 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, } } else { if (test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) { + /* + * AP might be in ROC in case we have just + * sent auth reply. handle it. + */ + if (test_bit(wlvif->role_id, wl->roc_map)) + wl12xx_croc(wl, wlvif->role_id); + ret = wl12xx_cmd_role_stop_ap(wl, wlvif); if (ret < 0) goto out; @@ -4120,6 +4227,9 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, do_join = true; } + if (changed & BSS_CHANGED_IDLE && !is_ibss) + wl1271_sta_handle_idle(wl, wlvif, bss_conf->idle); + if (changed & BSS_CHANGED_CQM) { bool enable = false; if (bss_conf->cqm_rssi_thold) @@ -4656,29 +4766,49 @@ static void wlcore_roc_if_possible(struct wl1271 *wl, wl12xx_roc(wl, wlvif, wlvif->role_id, wlvif->band, wlvif->channel); } -static void wlcore_update_inconn_sta(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct wl1271_station *wl_sta, - bool in_connection) +/* + * when wl_sta is NULL, we treat this call as if coming from a + * pending auth reply. + * wl->mutex must be taken and the FW must be awake when the call + * takes place. + */ +void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct wl1271_station *wl_sta, bool in_conn) { - if (in_connection) { - if (WARN_ON(wl_sta->in_connection)) + if (in_conn) { + if (WARN_ON(wl_sta && wl_sta->in_connection)) return; - wl_sta->in_connection = true; - if (!wlvif->inconn_count++) + + if (!wlvif->ap_pending_auth_reply && + !wlvif->inconn_count) wlcore_roc_if_possible(wl, wlvif); + + if (wl_sta) { + wl_sta->in_connection = true; + wlvif->inconn_count++; + } else { + wlvif->ap_pending_auth_reply = true; + } } else { - if (!wl_sta->in_connection) + if (wl_sta && !wl_sta->in_connection) return; - wl_sta->in_connection = false; - wlvif->inconn_count--; - if (WARN_ON(wlvif->inconn_count < 0)) + if (WARN_ON(!wl_sta && !wlvif->ap_pending_auth_reply)) return; - if (!wlvif->inconn_count) - if (test_bit(wlvif->role_id, wl->roc_map)) - wl12xx_croc(wl, wlvif->role_id); + if (WARN_ON(wl_sta && !wlvif->inconn_count)) + return; + + if (wl_sta) { + wl_sta->in_connection = false; + wlvif->inconn_count--; + } else { + wlvif->ap_pending_auth_reply = false; + } + + if (!wlvif->inconn_count && !wlvif->ap_pending_auth_reply && + test_bit(wlvif->role_id, wl->roc_map)) + wl12xx_croc(wl, wlvif->role_id); } } @@ -5313,10 +5443,7 @@ static struct ieee80211_rate wl1271_rates_5ghz[] = { /* 5 GHz band channels for WL1273 */ static struct ieee80211_channel wl1271_channels_5ghz[] = { - { .hw_value = 7, .center_freq = 5035, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 8, .center_freq = 5040, .max_power = WLCORE_MAX_TXPWR }, - { .hw_value = 9, .center_freq = 5045, .max_power = WLCORE_MAX_TXPWR }, - { .hw_value = 11, .center_freq = 5055, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 12, .center_freq = 5060, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 16, .center_freq = 5080, .max_power = WLCORE_MAX_TXPWR }, { .hw_value = 34, .center_freq = 5170, .max_power = WLCORE_MAX_TXPWR }, @@ -5896,14 +6023,20 @@ static const struct wiphy_wowlan_support wlcore_wowlan_support = { }; #endif +static irqreturn_t wlcore_hardirq(int irq, void *cookie) +{ + return IRQ_WAKE_THREAD; +} + static void wlcore_nvs_cb(const struct firmware *fw, void *context) { struct wl1271 *wl = context; struct platform_device *pdev = wl->pdev; - struct wlcore_platdev_data *pdev_data = pdev->dev.platform_data; + struct wlcore_platdev_data *pdev_data = dev_get_platdata(&pdev->dev); struct wl12xx_platform_data *pdata = pdev_data->pdata; unsigned long irqflags; int ret; + irq_handler_t hardirq_fn = NULL; if (fw) { wl->nvs = kmemdup(fw->data, fw->size, GFP_KERNEL); @@ -5932,12 +6065,14 @@ static void wlcore_nvs_cb(const struct firmware *fw, void *context) wl->platform_quirks = pdata->platform_quirks; wl->if_ops = pdev_data->if_ops; - if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) + if (wl->platform_quirks & WL12XX_PLATFORM_QUIRK_EDGE_IRQ) { irqflags = IRQF_TRIGGER_RISING; - else + hardirq_fn = wlcore_hardirq; + } else { irqflags = IRQF_TRIGGER_HIGH | IRQF_ONESHOT; + } - ret = request_threaded_irq(wl->irq, NULL, wlcore_irq, + ret = request_threaded_irq(wl->irq, hardirq_fn, wlcore_irq, irqflags, pdev->name, wl); if (ret < 0) { wl1271_error("request_irq() failed: %d", ret); @@ -6046,6 +6181,9 @@ module_param_named(fwlog, fwlog_param, charp, 0); MODULE_PARM_DESC(fwlog, "FW logger options: continuous, ondemand, dbgpins or disable"); +module_param(fwlog_mem_blocks, int, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(fwlog_mem_blocks, "fwlog mem_blocks"); + module_param(bug_on_recovery, int, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery"); diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index 98066d40c2ad107943384509abcbc10b82d82019..26bfc365ba70bf03e215ddc0be7df0d01c13512b 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -83,6 +83,10 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) struct wl12xx_vif *wlvif; u32 timeout; + /* We do not enter elp sleep in PLT mode */ + if (wl->plt) + return; + if (wl->sleep_auth != WL1271_PSM_ELP) return; diff --git a/drivers/net/wireless/ti/wlcore/scan.c b/drivers/net/wireless/ti/wlcore/scan.c index f407101e525b60a9c4bff3c402865d12f3d6afde..7ed86203304b700c87e4b1ca48de127f7466420f 100644 --- a/drivers/net/wireless/ti/wlcore/scan.c +++ b/drivers/net/wireless/ti/wlcore/scan.c @@ -92,9 +92,31 @@ out: static void wlcore_started_vifs_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + bool active = false; int *count = (int *)data; - if (!vif->bss_conf.idle) + /* + * count active interfaces according to interface type. + * checking only bss_conf.idle is bad for some cases, e.g. + * we don't want to count sta in p2p_find as active interface. + */ + switch (wlvif->bss_type) { + case BSS_TYPE_STA_BSS: + if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + active = true; + break; + + case BSS_TYPE_AP_BSS: + if (wlvif->wl->active_sta_count > 0) + active = true; + break; + + default: + break; + } + + if (active) (*count)++; } @@ -174,17 +196,7 @@ wlcore_scan_get_channels(struct wl1271 *wl, /* if radar is set, we ignore the passive flag */ (radar || !!(flags & IEEE80211_CHAN_PASSIVE_SCAN) == passive)) { - wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", - req_channels[i]->band, - req_channels[i]->center_freq); - wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X", - req_channels[i]->hw_value, - req_channels[i]->flags); - wl1271_debug(DEBUG_SCAN, "max_power %d", - req_channels[i]->max_power); - wl1271_debug(DEBUG_SCAN, "min_dwell_time %d max dwell time %d", - min_dwell_time_active, - max_dwell_time_active); + if (flags & IEEE80211_CHAN_RADAR) { channels[j].flags |= SCAN_CHANNEL_FLAGS_DFS; @@ -222,6 +234,17 @@ wlcore_scan_get_channels(struct wl1271 *wl, *n_pactive_ch); } + wl1271_debug(DEBUG_SCAN, "freq %d, ch. %d, flags 0x%x, power %d, min/max_dwell %d/%d%s%s", + req_channels[i]->center_freq, + req_channels[i]->hw_value, + req_channels[i]->flags, + req_channels[i]->max_power, + min_dwell_time_active, + max_dwell_time_active, + flags & IEEE80211_CHAN_RADAR ? + ", DFS" : "", + flags & IEEE80211_CHAN_PASSIVE_SCAN ? + ", PASSIVE" : ""); j++; } } @@ -364,7 +387,7 @@ wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, struct cfg80211_ssid *ssids = req->ssids; int ret = 0, type, i, j, n_match_ssids = 0; - wl1271_debug(DEBUG_CMD, "cmd sched scan ssid list"); + wl1271_debug((DEBUG_CMD | DEBUG_SCAN), "cmd sched scan ssid list"); /* count the match sets that contain SSIDs */ for (i = 0; i < req->n_match_sets; i++) @@ -442,8 +465,6 @@ wlcore_scan_sched_scan_ssid_list(struct wl1271 *wl, } } - wl1271_dump(DEBUG_SCAN, "SSID_LIST: ", cmd, sizeof(*cmd)); - ret = wl1271_cmd_send(wl, CMD_CONNECTION_SCAN_SSID_CFG, cmd, sizeof(*cmd), 0); if (ret < 0) { diff --git a/drivers/net/wireless/ti/wlcore/spi.c b/drivers/net/wireless/ti/wlcore/spi.c index 1b0cd98e35f187f8be9f0525e152e5960742471d..b2c018dccf1887bb8dbdd5b85d9a88e0d30ff46e 100644 --- a/drivers/net/wireless/ti/wlcore/spi.c +++ b/drivers/net/wireless/ti/wlcore/spi.c @@ -335,7 +335,7 @@ static int wl1271_probe(struct spi_device *spi) if (!pdev_data) goto out; - pdev_data->pdata = spi->dev.platform_data; + pdev_data->pdata = dev_get_platdata(&spi->dev); if (!pdev_data->pdata) { dev_err(&spi->dev, "no platform data\n"); ret = -ENODEV; diff --git a/drivers/net/wireless/ti/wlcore/testmode.c b/drivers/net/wireless/ti/wlcore/testmode.c index 527590f2adfbe66b088a99c48d5ce382ff030efd..ddad58f614da4fa862a0e3f2b776fe0e994a742d 100644 --- a/drivers/net/wireless/ti/wlcore/testmode.c +++ b/drivers/net/wireless/ti/wlcore/testmode.c @@ -179,7 +179,8 @@ static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[]) goto out_sleep; } - ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd)); + ret = wl1271_cmd_interrogate(wl, ie_id, cmd, + sizeof(struct acx_header), sizeof(*cmd)); if (ret < 0) { wl1271_warning("testmode cmd interrogate failed: %d", ret); goto out_free; @@ -297,7 +298,8 @@ static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[]) ret = wl1271_plt_stop(wl); break; case PLT_ON: - ret = wl1271_plt_start(wl, PLT_ON); + case PLT_CHIP_AWAKE: + ret = wl1271_plt_start(wl, val); break; case PLT_FEM_DETECT: ret = wl1271_tm_detect_fem(wl, tb); @@ -361,6 +363,7 @@ int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct wl1271 *wl = hw->priv; struct nlattr *tb[WL1271_TM_ATTR_MAX + 1]; + u32 nla_cmd; int err; err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy); @@ -370,7 +373,14 @@ int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (!tb[WL1271_TM_ATTR_CMD_ID]) return -EINVAL; - switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) { + nla_cmd = nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID]); + + /* Only SET_PLT_MODE is allowed in case of mode PLT_CHIP_AWAKE */ + if (wl->plt_mode == PLT_CHIP_AWAKE && + nla_cmd != WL1271_TM_CMD_SET_PLT_MODE) + return -EOPNOTSUPP; + + switch (nla_cmd) { case WL1271_TM_CMD_TEST: return wl1271_tm_cmd_test(wl, tb); case WL1271_TM_CMD_INTERROGATE: diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 7e93fe63a2c74a215b4948a91a95c179a8d78592..87cd707affa240390f6b34ea0d1b28caf23ffb83 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -86,19 +86,34 @@ void wl1271_free_tx_id(struct wl1271 *wl, int id) EXPORT_SYMBOL(wl1271_free_tx_id); static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, + struct wl12xx_vif *wlvif, struct sk_buff *skb) { struct ieee80211_hdr *hdr; + hdr = (struct ieee80211_hdr *)(skb->data + + sizeof(struct wl1271_tx_hw_descr)); + if (!ieee80211_is_auth(hdr->frame_control)) + return; + /* * add the station to the known list before transmitting the * authentication response. this way it won't get de-authed by FW * when transmitting too soon. */ - hdr = (struct ieee80211_hdr *)(skb->data + - sizeof(struct wl1271_tx_hw_descr)); - if (ieee80211_is_auth(hdr->frame_control)) - wl1271_acx_set_inconnection_sta(wl, hdr->addr1); + wl1271_acx_set_inconnection_sta(wl, hdr->addr1); + + /* + * ROC for 1 second on the AP channel for completing the connection. + * Note the ROC will be continued by the update_sta_state callbacks + * once the station reaches the associated state. + */ + wlcore_update_inconn_sta(wl, wlvif, NULL, true); + wlvif->pending_auth_reply_time = jiffies; + cancel_delayed_work(&wlvif->pending_auth_complete_work); + ieee80211_queue_delayed_work(wl->hw, + &wlvif->pending_auth_complete_work, + msecs_to_jiffies(WLCORE_PEND_AUTH_ROC_TIMEOUT)); } static void wl1271_tx_regulate_link(struct wl1271 *wl, @@ -386,7 +401,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, is_wep = (cipher == WLAN_CIPHER_SUITE_WEP40) || (cipher == WLAN_CIPHER_SUITE_WEP104); - if (WARN_ON(is_wep && wlvif->default_key != idx)) { + if (WARN_ON(is_wep && wlvif && wlvif->default_key != idx)) { ret = wl1271_set_default_wep_key(wl, wlvif, idx); if (ret < 0) return ret; @@ -404,7 +419,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif, wl1271_tx_fill_hdr(wl, wlvif, skb, extra, info, hlid); if (!is_dummy && wlvif && wlvif->bss_type == BSS_TYPE_AP_BSS) { - wl1271_tx_ap_update_inconnection_sta(wl, skb); + wl1271_tx_ap_update_inconnection_sta(wl, wlvif, skb); wl1271_tx_regulate_link(wl, wlvif, hlid); } diff --git a/drivers/net/wireless/ti/wlcore/tx.h b/drivers/net/wireless/ti/wlcore/tx.h index 55aa4acf9105a4e703036b0b12250ae19a9a7f81..35489c300da17bfefe3b35fc7515dc58aeeaa196 100644 --- a/drivers/net/wireless/ti/wlcore/tx.h +++ b/drivers/net/wireless/ti/wlcore/tx.h @@ -56,6 +56,9 @@ /* Used for management frames and dummy packets */ #define WL1271_TID_MGMT 7 +/* stop a ROC for pending authentication reply after this time (ms) */ +#define WLCORE_PEND_AUTH_ROC_TIMEOUT 1000 + struct wl127x_tx_mem { /* * Number of extra memory blocks to allocate for this packet diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 0034979e97cbaa0316dfd2a62db2e5be59b7f058..06efc12a39e5175dfde449843ffbb5a7c50b157d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -110,6 +110,7 @@ struct wlcore_ops { struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, u32 rate_set, u8 hlid); + u32 (*convert_hwaddr)(struct wl1271 *wl, u32 hwaddr); bool (*lnk_high_prio)(struct wl1271 *wl, u8 hlid, struct wl1271_link *lnk); bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid, @@ -290,6 +291,12 @@ struct wl1271 { /* Number of valid bytes in the FW log buffer */ ssize_t fwlog_size; + /* FW log end marker */ + u32 fwlog_end; + + /* FW memory block size */ + u32 fw_mem_block_size; + /* Sysfs FW log entry readers wait queue */ wait_queue_head_t fwlog_waitq; @@ -307,6 +314,8 @@ struct wl1271 { /* The mbox event mask */ u32 event_mask; + /* events to unmask only when ap interface is up */ + u32 ap_event_mask; /* Mailbox pointers */ u32 mbox_size; @@ -481,6 +490,8 @@ int wlcore_set_key(struct wl1271 *wl, enum set_key_cmd cmd, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf); void wlcore_regdomain_config(struct wl1271 *wl); +void wlcore_update_inconn_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, + struct wl1271_station *wl_sta, bool in_conn); static inline void wlcore_set_ht_cap(struct wl1271 *wl, enum ieee80211_band band, diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index e5e146435fe77e34bc04ac7bca36a1e8e553a17a..ce7261ce8b59a244837618a07ecf174762f04946 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -255,6 +255,7 @@ enum wl12xx_vif_flags { WLVIF_FLAG_CS_PROGRESS, WLVIF_FLAG_AP_PROBE_RESP_SET, WLVIF_FLAG_IN_USE, + WLVIF_FLAG_ACTIVE, }; struct wl12xx_vif; @@ -307,6 +308,7 @@ enum plt_mode { PLT_OFF = 0, PLT_ON = 1, PLT_FEM_DETECT = 2, + PLT_CHIP_AWAKE = 3 }; struct wl12xx_rx_filter_field { @@ -456,6 +458,15 @@ struct wl12xx_vif { */ int hw_queue_base; + /* do we have a pending auth reply? (and ROC) */ + bool ap_pending_auth_reply; + + /* time when we sent the pending auth reply */ + unsigned long pending_auth_reply_time; + + /* work for canceling ROC after pending auth reply */ + struct delayed_work pending_auth_complete_work; + /* * This struct must be last! * data that has to be saved acrossed reconfigs (e.g. recovery) @@ -539,6 +550,4 @@ void wl1271_rx_filter_flatten_fields(struct wl12xx_rx_filter *filter, #define HW_HT_RATES_OFFSET 16 #define HW_MIMO_RATES_OFFSET 24 -#define WL12XX_HW_BLOCK_SIZE 256 - #endif /* __WLCORE_I_H__ */ diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 400fea1de080c163f18fbe8efa3048a47abe6ec1..08ae01b41c832d7144bdeceb70b976adaf281d4c 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -87,9 +87,13 @@ struct pending_tx_info { struct xenvif_rx_meta { int id; int size; + int gso_type; int gso_size; }; +#define GSO_BIT(type) \ + (1 << XEN_NETIF_GSO_TYPE_ ## type) + /* Discriminate from any valid pending_idx value. */ #define INVALID_PENDING_IDX 0xFFFF @@ -150,10 +154,12 @@ struct xenvif { u8 fe_dev_addr[6]; /* Frontend feature information. */ + int gso_mask; + int gso_prefix_mask; + u8 can_sg:1; - u8 gso:1; - u8 gso_prefix:1; - u8 csum:1; + u8 ip_csum:1; + u8 ipv6_csum:1; /* Internal feature information. */ u8 can_queue:1; /* can queue packets for receiver? */ diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 459935a6bfae3ab31c634feff974e64f8f93cf3d..b78ee10a956a199e8d5e1a1615d5a6e1cbe94acd 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -214,10 +214,14 @@ static netdev_features_t xenvif_fix_features(struct net_device *dev, if (!vif->can_sg) features &= ~NETIF_F_SG; - if (!vif->gso && !vif->gso_prefix) + if (~(vif->gso_mask | vif->gso_prefix_mask) & GSO_BIT(TCPV4)) features &= ~NETIF_F_TSO; - if (!vif->csum) + if (~(vif->gso_mask | vif->gso_prefix_mask) & GSO_BIT(TCPV6)) + features &= ~NETIF_F_TSO6; + if (!vif->ip_csum) features &= ~NETIF_F_IP_CSUM; + if (!vif->ipv6_csum) + features &= ~NETIF_F_IPV6_CSUM; return features; } @@ -306,7 +310,7 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, vif->domid = domid; vif->handle = handle; vif->can_sg = 1; - vif->csum = 1; + vif->ip_csum = 1; vif->dev = dev; vif->credit_bytes = vif->remaining_credit = ~0UL; @@ -315,8 +319,10 @@ struct xenvif *xenvif_alloc(struct device *parent, domid_t domid, vif->credit_window_start = get_jiffies_64(); dev->netdev_ops = &xenvif_netdev_ops; - dev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; - dev->features = dev->hw_features; + dev->hw_features = NETIF_F_SG | + NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_TSO | NETIF_F_TSO6; + dev->features = dev->hw_features | NETIF_F_RXCSUM; SET_ETHTOOL_OPS(dev, &xenvif_ethtool_ops); dev->tx_queue_len = XENVIF_QUEUE_LENGTH; diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 900da4b243ad7a0a4eb21ae992d52912650f4c5c..919b6509455cfbaf45ba63c7d8b378fa13a8e758 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -109,15 +109,12 @@ static inline unsigned long idx_to_kaddr(struct xenvif *vif, return (unsigned long)pfn_to_kaddr(idx_to_pfn(vif, idx)); } -/* - * This is the amount of packet we copy rather than map, so that the - * guest can't fiddle with the contents of the headers while we do - * packet processing on them (netfilter, routing, etc). +/* This is a miniumum size for the linear area to avoid lots of + * calls to __pskb_pull_tail() as we set up checksum offsets. The + * value 128 was chosen as it covers all IPv4 and most likely + * IPv6 headers. */ -#define PKT_PROT_LEN (ETH_HLEN + \ - VLAN_HLEN + \ - sizeof(struct iphdr) + MAX_IPOPTLEN + \ - sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE) +#define PKT_PROT_LEN 128 static u16 frag_get_pending_idx(skb_frag_t *frag) { @@ -145,7 +142,7 @@ static int max_required_rx_slots(struct xenvif *vif) int max = DIV_ROUND_UP(vif->dev->mtu, PAGE_SIZE); /* XXX FIXME: RX path dependent on MAX_SKB_FRAGS */ - if (vif->can_sg || vif->gso || vif->gso_prefix) + if (vif->can_sg || vif->gso_mask || vif->gso_prefix_mask) max += MAX_SKB_FRAGS + 1; /* extra_info + frags */ return max; @@ -317,6 +314,7 @@ static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif *vif, req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++); meta = npo->meta + npo->meta_prod++; + meta->gso_type = XEN_NETIF_GSO_TYPE_NONE; meta->gso_size = 0; meta->size = 0; meta->id = req->id; @@ -339,6 +337,7 @@ static void xenvif_gop_frag_copy(struct xenvif *vif, struct sk_buff *skb, struct gnttab_copy *copy_gop; struct xenvif_rx_meta *meta; unsigned long bytes; + int gso_type; /* Data must not cross a page boundary. */ BUG_ON(size + offset > PAGE_SIZE<gso_size && !vif->gso_prefix) + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) + gso_type = XEN_NETIF_GSO_TYPE_TCPV4; + else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) + gso_type = XEN_NETIF_GSO_TYPE_TCPV6; + else + gso_type = XEN_NETIF_GSO_TYPE_NONE; + + if (*head && ((1 << gso_type) & vif->gso_mask)) vif->rx.req_cons++; *head = 0; /* There must be something in this buffer now. */ @@ -428,14 +434,28 @@ static int xenvif_gop_skb(struct sk_buff *skb, unsigned char *data; int head = 1; int old_meta_prod; + int gso_type; + int gso_size; old_meta_prod = npo->meta_prod; + if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { + gso_type = XEN_NETIF_GSO_TYPE_TCPV4; + gso_size = skb_shinfo(skb)->gso_size; + } else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { + gso_type = XEN_NETIF_GSO_TYPE_TCPV6; + gso_size = skb_shinfo(skb)->gso_size; + } else { + gso_type = XEN_NETIF_GSO_TYPE_NONE; + gso_size = 0; + } + /* Set up a GSO prefix descriptor, if necessary */ - if (skb_shinfo(skb)->gso_size && vif->gso_prefix) { + if ((1 << skb_shinfo(skb)->gso_type) & vif->gso_prefix_mask) { req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++); meta = npo->meta + npo->meta_prod++; - meta->gso_size = skb_shinfo(skb)->gso_size; + meta->gso_type = gso_type; + meta->gso_size = gso_size; meta->size = 0; meta->id = req->id; } @@ -443,10 +463,13 @@ static int xenvif_gop_skb(struct sk_buff *skb, req = RING_GET_REQUEST(&vif->rx, vif->rx.req_cons++); meta = npo->meta + npo->meta_prod++; - if (!vif->gso_prefix) - meta->gso_size = skb_shinfo(skb)->gso_size; - else + if ((1 << gso_type) & vif->gso_mask) { + meta->gso_type = gso_type; + meta->gso_size = gso_size; + } else { + meta->gso_type = XEN_NETIF_GSO_TYPE_NONE; meta->gso_size = 0; + } meta->size = 0; meta->id = req->id; @@ -592,7 +615,8 @@ void xenvif_rx_action(struct xenvif *vif) vif = netdev_priv(skb->dev); - if (vif->meta[npo.meta_cons].gso_size && vif->gso_prefix) { + if ((1 << vif->meta[npo.meta_cons].gso_type) & + vif->gso_prefix_mask) { resp = RING_GET_RESPONSE(&vif->rx, vif->rx.rsp_prod_pvt++); @@ -629,7 +653,8 @@ void xenvif_rx_action(struct xenvif *vif) vif->meta[npo.meta_cons].size, flags); - if (vif->meta[npo.meta_cons].gso_size && !vif->gso_prefix) { + if ((1 << vif->meta[npo.meta_cons].gso_type) & + vif->gso_mask) { struct xen_netif_extra_info *gso = (struct xen_netif_extra_info *) RING_GET_RESPONSE(&vif->rx, @@ -637,8 +662,8 @@ void xenvif_rx_action(struct xenvif *vif) resp->flags |= XEN_NETRXF_extra_info; + gso->u.gso.type = vif->meta[npo.meta_cons].gso_type; gso->u.gso.size = vif->meta[npo.meta_cons].gso_size; - gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4; gso->u.gso.pad = 0; gso->u.gso.features = 0; @@ -1101,15 +1126,20 @@ static int xenvif_set_skb_gso(struct xenvif *vif, return -EINVAL; } - /* Currently only TCPv4 S.O. is supported. */ - if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) { + switch (gso->u.gso.type) { + case XEN_NETIF_GSO_TYPE_TCPV4: + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + break; + case XEN_NETIF_GSO_TYPE_TCPV6: + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; + break; + default: netdev_err(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type); xenvif_fatal_tx_err(vif); return -EINVAL; } skb_shinfo(skb)->gso_size = gso->u.gso.size; - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; /* Header must be checked, and gso_segs computed. */ skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; @@ -1118,61 +1148,74 @@ static int xenvif_set_skb_gso(struct xenvif *vif, return 0; } -static int checksum_setup(struct xenvif *vif, struct sk_buff *skb) +static inline void maybe_pull_tail(struct sk_buff *skb, unsigned int len) +{ + if (skb_is_nonlinear(skb) && skb_headlen(skb) < len) { + /* If we need to pullup then pullup to the max, so we + * won't need to do it again. + */ + int target = min_t(int, skb->len, MAX_TCP_HEADER); + __pskb_pull_tail(skb, target - skb_headlen(skb)); + } +} + +static int checksum_setup_ip(struct xenvif *vif, struct sk_buff *skb, + int recalculate_partial_csum) { - struct iphdr *iph; + struct iphdr *iph = (void *)skb->data; + unsigned int header_size; + unsigned int off; int err = -EPROTO; - int recalculate_partial_csum = 0; - /* - * A GSO SKB must be CHECKSUM_PARTIAL. However some buggy - * peers can fail to set NETRXF_csum_blank when sending a GSO - * frame. In this case force the SKB to CHECKSUM_PARTIAL and - * recalculate the partial checksum. - */ - if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) { - vif->rx_gso_checksum_fixup++; - skb->ip_summed = CHECKSUM_PARTIAL; - recalculate_partial_csum = 1; - } + off = sizeof(struct iphdr); - /* A non-CHECKSUM_PARTIAL SKB does not require setup. */ - if (skb->ip_summed != CHECKSUM_PARTIAL) - return 0; + header_size = skb->network_header + off + MAX_IPOPTLEN; + maybe_pull_tail(skb, header_size); - if (skb->protocol != htons(ETH_P_IP)) - goto out; + off = iph->ihl * 4; - iph = (void *)skb->data; switch (iph->protocol) { case IPPROTO_TCP: - if (!skb_partial_csum_set(skb, 4 * iph->ihl, + if (!skb_partial_csum_set(skb, off, offsetof(struct tcphdr, check))) goto out; if (recalculate_partial_csum) { struct tcphdr *tcph = tcp_hdr(skb); + + header_size = skb->network_header + + off + + sizeof(struct tcphdr); + maybe_pull_tail(skb, header_size); + tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, - skb->len - iph->ihl*4, + skb->len - off, IPPROTO_TCP, 0); } break; case IPPROTO_UDP: - if (!skb_partial_csum_set(skb, 4 * iph->ihl, + if (!skb_partial_csum_set(skb, off, offsetof(struct udphdr, check))) goto out; if (recalculate_partial_csum) { struct udphdr *udph = udp_hdr(skb); + + header_size = skb->network_header + + off + + sizeof(struct udphdr); + maybe_pull_tail(skb, header_size); + udph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, - skb->len - iph->ihl*4, + skb->len - off, IPPROTO_UDP, 0); } break; default: if (net_ratelimit()) netdev_err(vif->dev, - "Attempting to checksum a non-TCP/UDP packet, dropping a protocol %d packet\n", + "Attempting to checksum a non-TCP/UDP packet, " + "dropping a protocol %d packet\n", iph->protocol); goto out; } @@ -1183,6 +1226,158 @@ out: return err; } +static int checksum_setup_ipv6(struct xenvif *vif, struct sk_buff *skb, + int recalculate_partial_csum) +{ + int err = -EPROTO; + struct ipv6hdr *ipv6h = (void *)skb->data; + u8 nexthdr; + unsigned int header_size; + unsigned int off; + bool fragment; + bool done; + + done = false; + + off = sizeof(struct ipv6hdr); + + header_size = skb->network_header + off; + maybe_pull_tail(skb, header_size); + + nexthdr = ipv6h->nexthdr; + + while ((off <= sizeof(struct ipv6hdr) + ntohs(ipv6h->payload_len)) && + !done) { + switch (nexthdr) { + case IPPROTO_DSTOPTS: + case IPPROTO_HOPOPTS: + case IPPROTO_ROUTING: { + struct ipv6_opt_hdr *hp = (void *)(skb->data + off); + + header_size = skb->network_header + + off + + sizeof(struct ipv6_opt_hdr); + maybe_pull_tail(skb, header_size); + + nexthdr = hp->nexthdr; + off += ipv6_optlen(hp); + break; + } + case IPPROTO_AH: { + struct ip_auth_hdr *hp = (void *)(skb->data + off); + + header_size = skb->network_header + + off + + sizeof(struct ip_auth_hdr); + maybe_pull_tail(skb, header_size); + + nexthdr = hp->nexthdr; + off += (hp->hdrlen+2)<<2; + break; + } + case IPPROTO_FRAGMENT: + fragment = true; + /* fall through */ + default: + done = true; + break; + } + } + + if (!done) { + if (net_ratelimit()) + netdev_err(vif->dev, "Failed to parse packet header\n"); + goto out; + } + + if (fragment) { + if (net_ratelimit()) + netdev_err(vif->dev, "Packet is a fragment!\n"); + goto out; + } + + switch (nexthdr) { + case IPPROTO_TCP: + if (!skb_partial_csum_set(skb, off, + offsetof(struct tcphdr, check))) + goto out; + + if (recalculate_partial_csum) { + struct tcphdr *tcph = tcp_hdr(skb); + + header_size = skb->network_header + + off + + sizeof(struct tcphdr); + maybe_pull_tail(skb, header_size); + + tcph->check = ~csum_ipv6_magic(&ipv6h->saddr, + &ipv6h->daddr, + skb->len - off, + IPPROTO_TCP, 0); + } + break; + case IPPROTO_UDP: + if (!skb_partial_csum_set(skb, off, + offsetof(struct udphdr, check))) + goto out; + + if (recalculate_partial_csum) { + struct udphdr *udph = udp_hdr(skb); + + header_size = skb->network_header + + off + + sizeof(struct udphdr); + maybe_pull_tail(skb, header_size); + + udph->check = ~csum_ipv6_magic(&ipv6h->saddr, + &ipv6h->daddr, + skb->len - off, + IPPROTO_UDP, 0); + } + break; + default: + if (net_ratelimit()) + netdev_err(vif->dev, + "Attempting to checksum a non-TCP/UDP packet, " + "dropping a protocol %d packet\n", + nexthdr); + goto out; + } + + err = 0; + +out: + return err; +} + +static int checksum_setup(struct xenvif *vif, struct sk_buff *skb) +{ + int err = -EPROTO; + int recalculate_partial_csum = 0; + + /* A GSO SKB must be CHECKSUM_PARTIAL. However some buggy + * peers can fail to set NETRXF_csum_blank when sending a GSO + * frame. In this case force the SKB to CHECKSUM_PARTIAL and + * recalculate the partial checksum. + */ + if (skb->ip_summed != CHECKSUM_PARTIAL && skb_is_gso(skb)) { + vif->rx_gso_checksum_fixup++; + skb->ip_summed = CHECKSUM_PARTIAL; + recalculate_partial_csum = 1; + } + + /* A non-CHECKSUM_PARTIAL SKB does not require setup. */ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + if (skb->protocol == htons(ETH_P_IP)) + err = checksum_setup_ip(vif, skb, recalculate_partial_csum); + else if (skb->protocol == htons(ETH_P_IPV6)) + err = checksum_setup_ipv6(vif, skb, recalculate_partial_csum); + + return err; +} + static bool tx_credit_exceeded(struct xenvif *vif, unsigned size) { u64 now = get_jiffies_64(); @@ -1428,12 +1623,7 @@ static int xenvif_tx_submit(struct xenvif *vif, int budget) xenvif_fill_frags(vif, skb); - /* - * If the initial fragment was < PKT_PROT_LEN then - * pull through some bytes from the other fragments to - * increase the linear region to PKT_PROT_LEN bytes. - */ - if (skb_headlen(skb) < PKT_PROT_LEN && skb_is_nonlinear(skb)) { + if (skb_is_nonlinear(skb) && skb_headlen(skb) < PKT_PROT_LEN) { int target = min_t(int, skb->len, PKT_PROT_LEN); __pskb_pull_tail(skb, target - skb_headlen(skb)); } diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index 1b08d879837231256f4cc954bbe8fbd0c4aa9391..f0358992b04fa820cb083300765271b92823320a 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -105,6 +105,22 @@ static int netback_probe(struct xenbus_device *dev, goto abort_transaction; } + err = xenbus_printf(xbt, dev->nodename, "feature-gso-tcpv6", + "%d", sg); + if (err) { + message = "writing feature-gso-tcpv6"; + goto abort_transaction; + } + + /* We support partial checksum setup for IPv6 packets */ + err = xenbus_printf(xbt, dev->nodename, + "feature-ipv6-csum-offload", + "%d", 1); + if (err) { + message = "writing feature-ipv6-csum-offload"; + goto abort_transaction; + } + /* We support rx-copy path. */ err = xenbus_printf(xbt, dev->nodename, "feature-rx-copy", "%d", 1); @@ -561,20 +577,50 @@ static int connect_rings(struct backend_info *be) val = 0; vif->can_sg = !!val; + vif->gso_mask = 0; + vif->gso_prefix_mask = 0; + if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv4", "%d", &val) < 0) val = 0; - vif->gso = !!val; + if (val) + vif->gso_mask |= GSO_BIT(TCPV4); if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv4-prefix", "%d", &val) < 0) val = 0; - vif->gso_prefix = !!val; + if (val) + vif->gso_prefix_mask |= GSO_BIT(TCPV4); + + if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv6", + "%d", &val) < 0) + val = 0; + if (val) + vif->gso_mask |= GSO_BIT(TCPV6); + + if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-gso-tcpv6-prefix", + "%d", &val) < 0) + val = 0; + if (val) + vif->gso_prefix_mask |= GSO_BIT(TCPV6); + + if (vif->gso_mask & vif->gso_prefix_mask) { + xenbus_dev_fatal(dev, err, + "%s: gso and gso prefix flags are not " + "mutually exclusive", + dev->otherend); + return -EOPNOTSUPP; + } if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-no-csum-offload", "%d", &val) < 0) val = 0; - vif->csum = !val; + vif->ip_csum = !val; + + if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-ipv6-csum-offload", + "%d", &val) < 0) + val = 0; + vif->ipv6_csum = !!val; /* Map the shared frame, irq etc. */ err = xenvif_connect(vif, tx_ring_ref, rx_ring_ref, diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 36808bf256770a5e02e7674002ed389c7fd8ac19..dd1011e55cb598096ef7c2ad64981a2572b760d7 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -952,7 +952,7 @@ static int handle_incoming_queue(struct net_device *dev, u64_stats_update_end(&stats->syncp); /* Pass it up. */ - netif_receive_skb(skb); + napi_gro_receive(&np->napi, skb); } return packets_dropped; @@ -1051,6 +1051,8 @@ err: if (work_done < budget) { int more_to_do = 0; + napi_gro_flush(napi, false); + local_irq_save(flags); RING_FINAL_CHECK_FOR_RESPONSES(&np->rx, more_to_do); diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index b0b64ccb7d7d5d2581047bbd8ff151dabbf3492c..c1fb20603338299eb2eecfd085653480dd726428 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -46,6 +46,16 @@ config NFC_SIM If unsure, say N. +config NFC_PORT100 + tristate "Sony NFC Port-100 Series USB device support" + depends on USB + depends on NFC_DIGITAL + help + This adds support for Sony Port-100 chip based USB devices such as the + RC-S380 dongle. + + If unsure, say N. + source "drivers/nfc/pn544/Kconfig" source "drivers/nfc/microread/Kconfig" diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index be7636abcb3fa2c79076f533b54ab4769251fbb3..c715fe8582a8075503f7db5de758fa250d2905bb 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -8,5 +8,6 @@ obj-$(CONFIG_NFC_PN533) += pn533.o obj-$(CONFIG_NFC_WILINK) += nfcwilink.o obj-$(CONFIG_NFC_MEI_PHY) += mei_phy.o obj-$(CONFIG_NFC_SIM) += nfcsim.o +obj-$(CONFIG_NFC_PORT100) += port100.o ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index 606bf55e76ec5941bf69443f8113a38266b4c000..85f90090cc1d1e167de6391e3481b597451555bd 100644 --- a/drivers/nfc/mei_phy.c +++ b/drivers/nfc/mei_phy.c @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -60,13 +62,13 @@ int nfc_mei_phy_enable(void *phy_id) r = mei_cl_enable_device(phy->device); if (r < 0) { - pr_err("MEI_PHY: Could not enable device\n"); + pr_err("Could not enable device\n"); return r; } r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy); if (r) { - pr_err("MEY_PHY: Event cb registration failed\n"); + pr_err("Event cb registration failed\n"); mei_cl_disable_device(phy->device); phy->powered = 0; diff --git a/drivers/nfc/microread/i2c.c b/drivers/nfc/microread/i2c.c index 101089495bf81f6b5ee115f219f8b13f3f5cb667..696e3467eccc33ab1ee97c6da2a4940c489ee086 100644 --- a/drivers/nfc/microread/i2c.c +++ b/drivers/nfc/microread/i2c.c @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -95,12 +97,8 @@ static int check_crc(struct sk_buff *skb) crc = crc ^ skb->data[i]; if (crc != skb->data[skb->len-1]) { - pr_err(MICROREAD_I2C_DRIVER_NAME - ": CRC error 0x%x != 0x%x\n", - crc, skb->data[skb->len-1]); - - pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__); - + pr_err("CRC error 0x%x != 0x%x\n", crc, skb->data[skb->len-1]); + pr_info("%s: BAD CRC\n", __func__); return -EPERM; } @@ -160,18 +158,15 @@ static int microread_i2c_read(struct microread_i2c_phy *phy, u8 tmp[MICROREAD_I2C_LLC_MAX_SIZE - 1]; struct i2c_client *client = phy->i2c_dev; - pr_debug("%s\n", __func__); - r = i2c_master_recv(client, &len, 1); if (r != 1) { - dev_err(&client->dev, "cannot read len byte\n"); + nfc_err(&client->dev, "cannot read len byte\n"); return -EREMOTEIO; } if ((len < MICROREAD_I2C_LLC_MIN_SIZE) || (len > MICROREAD_I2C_LLC_MAX_SIZE)) { - dev_err(&client->dev, "invalid len byte\n"); - pr_err("invalid len byte\n"); + nfc_err(&client->dev, "invalid len byte\n"); r = -EBADMSG; goto flush; } @@ -228,7 +223,6 @@ static irqreturn_t microread_i2c_irq_thread_fn(int irq, void *phy_id) } client = phy->i2c_dev; - dev_dbg(&client->dev, "IRQ\n"); if (phy->hard_fault != 0) return IRQ_HANDLED; @@ -263,20 +257,18 @@ static int microread_i2c_probe(struct i2c_client *client, dev_get_platdata(&client->dev); int r; - dev_dbg(&client->dev, "client %p", client); + dev_dbg(&client->dev, "client %p\n", client); if (!pdata) { - dev_err(&client->dev, "client %p: missing platform data", + nfc_err(&client->dev, "client %p: missing platform data\n", client); return -EINVAL; } phy = devm_kzalloc(&client->dev, sizeof(struct microread_i2c_phy), GFP_KERNEL); - if (!phy) { - dev_err(&client->dev, "Can't allocate microread phy"); + if (!phy) return -ENOMEM; - } i2c_set_clientdata(client, phy); phy->i2c_dev = client; @@ -285,7 +277,7 @@ static int microread_i2c_probe(struct i2c_client *client, IRQF_TRIGGER_RISING | IRQF_ONESHOT, MICROREAD_I2C_DRIVER_NAME, phy); if (r) { - dev_err(&client->dev, "Unable to register IRQ handler"); + nfc_err(&client->dev, "Unable to register IRQ handler\n"); return r; } @@ -296,7 +288,7 @@ static int microread_i2c_probe(struct i2c_client *client, if (r < 0) goto err_irq; - dev_info(&client->dev, "Probed"); + nfc_info(&client->dev, "Probed"); return 0; @@ -310,8 +302,6 @@ static int microread_i2c_remove(struct i2c_client *client) { struct microread_i2c_phy *phy = i2c_get_clientdata(client); - dev_dbg(&client->dev, "%s\n", __func__); - microread_remove(phy->hdev); free_irq(client->irq, phy); diff --git a/drivers/nfc/microread/mei.c b/drivers/nfc/microread/mei.c index cdf1bc53b257f6ae34e3f2148de97b47f53521b7..72fafec3d46058908ea841f2c89273c179b6d30d 100644 --- a/drivers/nfc/microread/mei.c +++ b/drivers/nfc/microread/mei.c @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -59,8 +61,6 @@ static int microread_mei_remove(struct mei_cl_device *device) { struct nfc_mei_phy *phy = mei_cl_get_drvdata(device); - pr_info("Removing microread\n"); - microread_remove(phy->hdev); nfc_mei_phy_free(phy); diff --git a/drivers/nfc/microread/microread.c b/drivers/nfc/microread/microread.c index cdb9f6de132a5c16b564e2a1228bbc3ae594ec2f..970ded6bfcf562b278e43b03e5112042c798fd0c 100644 --- a/drivers/nfc/microread/microread.c +++ b/drivers/nfc/microread/microread.c @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -546,7 +548,7 @@ exit: kfree_skb(skb); if (r) - pr_err("Failed to handle discovered target err=%d", r); + pr_err("Failed to handle discovered target err=%d\n", r); } static int microread_event_received(struct nfc_hci_dev *hdev, u8 gate, @@ -656,7 +658,6 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, info = kzalloc(sizeof(struct microread_info), GFP_KERNEL); if (!info) { - pr_err("Cannot allocate memory for microread_info.\n"); r = -ENOMEM; goto err_info_alloc; } @@ -686,7 +687,7 @@ int microread_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, MICROREAD_CMD_TAILROOM, phy_payload); if (!info->hdev) { - pr_err("Cannot allocate nfc hdev.\n"); + pr_err("Cannot allocate nfc hdev\n"); r = -ENOMEM; goto err_alloc_hdev; } diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c index 9a53f13c88df50a705c84b89e4fe7c081dbe3c88..93111fa8d2829735ec1ddea6c09c6da86bbb9459 100644 --- a/drivers/nfc/nfcsim.c +++ b/drivers/nfc/nfcsim.c @@ -19,10 +19,10 @@ #include #include -#define DEV_ERR(_dev, fmt, args...) nfc_dev_err(&_dev->nfc_dev->dev, \ +#define DEV_ERR(_dev, fmt, args...) nfc_err(&_dev->nfc_dev->dev, \ "%s: " fmt, __func__, ## args) -#define DEV_DBG(_dev, fmt, args...) nfc_dev_dbg(&_dev->nfc_dev->dev, \ +#define DEV_DBG(_dev, fmt, args...) dev_dbg(&_dev->nfc_dev->dev, \ "%s: " fmt, __func__, ## args) #define NFCSIM_VERSION "0.1" @@ -64,7 +64,7 @@ static struct workqueue_struct *wq; static void nfcsim_cleanup_dev(struct nfcsim *dev, u8 shutdown) { - DEV_DBG(dev, "shutdown=%d", shutdown); + DEV_DBG(dev, "shutdown=%d\n", shutdown); mutex_lock(&dev->lock); @@ -84,7 +84,7 @@ static int nfcsim_target_found(struct nfcsim *dev) { struct nfc_target nfc_tgt; - DEV_DBG(dev, ""); + DEV_DBG(dev, "\n"); memset(&nfc_tgt, 0, sizeof(struct nfc_target)); @@ -98,7 +98,7 @@ static int nfcsim_dev_up(struct nfc_dev *nfc_dev) { struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - DEV_DBG(dev, ""); + DEV_DBG(dev, "\n"); mutex_lock(&dev->lock); @@ -113,7 +113,7 @@ static int nfcsim_dev_down(struct nfc_dev *nfc_dev) { struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - DEV_DBG(dev, ""); + DEV_DBG(dev, "\n"); mutex_lock(&dev->lock); @@ -143,7 +143,7 @@ static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev, remote_gb = nfc_get_local_general_bytes(peer->nfc_dev, &remote_gb_len); if (!remote_gb) { - DEV_ERR(peer, "Can't get remote general bytes"); + DEV_ERR(peer, "Can't get remote general bytes\n"); mutex_unlock(&peer->lock); return -EINVAL; @@ -155,7 +155,7 @@ static int nfcsim_dep_link_up(struct nfc_dev *nfc_dev, rc = nfc_set_remote_general_bytes(nfc_dev, remote_gb, remote_gb_len); if (rc) { - DEV_ERR(dev, "Can't set remote general bytes"); + DEV_ERR(dev, "Can't set remote general bytes\n"); mutex_unlock(&dev->lock); return rc; } @@ -172,7 +172,7 @@ static int nfcsim_dep_link_down(struct nfc_dev *nfc_dev) { struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - DEV_DBG(dev, ""); + DEV_DBG(dev, "\n"); nfcsim_cleanup_dev(dev, 0); @@ -188,7 +188,7 @@ static int nfcsim_start_poll(struct nfc_dev *nfc_dev, mutex_lock(&dev->lock); if (dev->polling_mode != NFCSIM_POLL_NONE) { - DEV_ERR(dev, "Already in polling mode"); + DEV_ERR(dev, "Already in polling mode\n"); rc = -EBUSY; goto exit; } @@ -200,7 +200,7 @@ static int nfcsim_start_poll(struct nfc_dev *nfc_dev, dev->polling_mode |= NFCSIM_POLL_TARGET; if (dev->polling_mode == NFCSIM_POLL_NONE) { - DEV_ERR(dev, "Unsupported polling mode"); + DEV_ERR(dev, "Unsupported polling mode\n"); rc = -EINVAL; goto exit; } @@ -210,7 +210,7 @@ static int nfcsim_start_poll(struct nfc_dev *nfc_dev, queue_delayed_work(wq, &dev->poll_work, 0); - DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X", im_protocols, + DEV_DBG(dev, "Start polling: im: 0x%X, tm: 0x%X\n", im_protocols, tm_protocols); rc = 0; @@ -224,7 +224,7 @@ static void nfcsim_stop_poll(struct nfc_dev *nfc_dev) { struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - DEV_DBG(dev, "Stop poll"); + DEV_DBG(dev, "Stop poll\n"); mutex_lock(&dev->lock); @@ -240,7 +240,7 @@ static int nfcsim_activate_target(struct nfc_dev *nfc_dev, { struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - DEV_DBG(dev, ""); + DEV_DBG(dev, "\n"); return -ENOTSUPP; } @@ -250,7 +250,7 @@ static void nfcsim_deactivate_target(struct nfc_dev *nfc_dev, { struct nfcsim *dev = nfc_get_drvdata(nfc_dev); - DEV_DBG(dev, ""); + DEV_DBG(dev, "\n"); } static void nfcsim_wq_recv(struct work_struct *work) @@ -267,7 +267,7 @@ static void nfcsim_wq_recv(struct work_struct *work) if (dev->initiator) { if (!dev->cb) { - DEV_ERR(dev, "Null recv callback"); + DEV_ERR(dev, "Null recv callback\n"); dev_kfree_skb(dev->clone_skb); goto exit; } @@ -310,7 +310,7 @@ static int nfcsim_tx(struct nfc_dev *nfc_dev, struct nfc_target *target, peer->clone_skb = skb_clone(skb, GFP_KERNEL); if (!peer->clone_skb) { - DEV_ERR(dev, "skb_clone failed"); + DEV_ERR(dev, "skb_clone failed\n"); mutex_unlock(&peer->lock); err = -ENOMEM; goto exit; @@ -397,13 +397,13 @@ static void nfcsim_wq_poll(struct work_struct *work) nfcsim_set_polling_mode(dev); if (dev->curr_polling_mode == NFCSIM_POLL_NONE) { - DEV_DBG(dev, "Not polling"); + DEV_DBG(dev, "Not polling\n"); goto unlock; } DEV_DBG(dev, "Polling as %s", dev->curr_polling_mode == NFCSIM_POLL_INITIATOR ? - "initiator" : "target"); + "initiator\n" : "target\n"); if (dev->curr_polling_mode == NFCSIM_POLL_TARGET) goto sched_work; diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c index 59f95d8fc98c887228d15e0421c69906bdd85154..71308645593f638a2879df4348f1349f0e031ce4 100644 --- a/drivers/nfc/nfcwilink.c +++ b/drivers/nfc/nfcwilink.c @@ -146,13 +146,11 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name) unsigned long comp_ret; int rc; - nfc_dev_dbg(&drv->pdev->dev, "get_bts_file_name entry"); - skb = nfcwilink_skb_alloc(sizeof(struct nci_vs_nfcc_info_cmd), GFP_KERNEL); if (!skb) { - nfc_dev_err(&drv->pdev->dev, - "no memory for nci_vs_nfcc_info_cmd"); + nfc_err(&drv->pdev->dev, + "no memory for nci_vs_nfcc_info_cmd\n"); return -ENOMEM; } @@ -170,21 +168,19 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name) comp_ret = wait_for_completion_timeout(&drv->completed, msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); - nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld", - comp_ret); + dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n", + comp_ret); if (comp_ret == 0) { - nfc_dev_err(&drv->pdev->dev, - "timeout on wait_for_completion_timeout"); + nfc_err(&drv->pdev->dev, + "timeout on wait_for_completion_timeout\n"); return -ETIMEDOUT; } - nfc_dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d", - drv->nfcc_info.plen, - drv->nfcc_info.status); + dev_dbg(&drv->pdev->dev, "nci_vs_nfcc_info_rsp: plen %d, status %d\n", + drv->nfcc_info.plen, drv->nfcc_info.status); if ((drv->nfcc_info.plen != 5) || (drv->nfcc_info.status != 0)) { - nfc_dev_err(&drv->pdev->dev, - "invalid nci_vs_nfcc_info_rsp"); + nfc_err(&drv->pdev->dev, "invalid nci_vs_nfcc_info_rsp\n"); return -EINVAL; } @@ -195,7 +191,7 @@ static int nfcwilink_get_bts_file_name(struct nfcwilink *drv, char *file_name) drv->nfcc_info.sw_ver_z, drv->nfcc_info.patch_id); - nfc_dev_info(&drv->pdev->dev, "nfcwilink FW file name: %s", file_name); + nfc_info(&drv->pdev->dev, "nfcwilink FW file name: %s\n", file_name); return 0; } @@ -207,15 +203,13 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len) unsigned long comp_ret; int rc; - nfc_dev_dbg(&drv->pdev->dev, "send_bts_cmd entry"); - /* verify valid cmd for the NFC channel */ if ((len <= sizeof(struct nfcwilink_hdr)) || (len > BTS_FILE_CMD_MAX_LEN) || (hdr->chnl != NFCWILINK_CHNL) || (hdr->opcode != NFCWILINK_OPCODE)) { - nfc_dev_err(&drv->pdev->dev, - "ignoring invalid bts cmd, len %d, chnl %d, opcode %d", + nfc_err(&drv->pdev->dev, + "ignoring invalid bts cmd, len %d, chnl %d, opcode %d\n", len, hdr->chnl, hdr->opcode); return 0; } @@ -226,7 +220,7 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len) skb = nfcwilink_skb_alloc(len, GFP_KERNEL); if (!skb) { - nfc_dev_err(&drv->pdev->dev, "no memory for bts cmd"); + nfc_err(&drv->pdev->dev, "no memory for bts cmd\n"); return -ENOMEM; } @@ -238,11 +232,11 @@ static int nfcwilink_send_bts_cmd(struct nfcwilink *drv, __u8 *data, int len) comp_ret = wait_for_completion_timeout(&drv->completed, msecs_to_jiffies(NFCWILINK_CMD_TIMEOUT)); - nfc_dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld", - comp_ret); + dev_dbg(&drv->pdev->dev, "wait_for_completion_timeout returned %ld\n", + comp_ret); if (comp_ret == 0) { - nfc_dev_err(&drv->pdev->dev, - "timeout on wait_for_completion_timeout"); + nfc_err(&drv->pdev->dev, + "timeout on wait_for_completion_timeout\n"); return -ETIMEDOUT; } @@ -257,8 +251,6 @@ static int nfcwilink_download_fw(struct nfcwilink *drv) __u8 *ptr; int len, rc; - nfc_dev_dbg(&drv->pdev->dev, "download_fw entry"); - set_bit(NFCWILINK_FW_DOWNLOAD, &drv->flags); rc = nfcwilink_get_bts_file_name(drv, file_name); @@ -267,7 +259,7 @@ static int nfcwilink_download_fw(struct nfcwilink *drv) rc = request_firmware(&fw, file_name, &drv->pdev->dev); if (rc) { - nfc_dev_err(&drv->pdev->dev, "request_firmware failed %d", rc); + nfc_err(&drv->pdev->dev, "request_firmware failed %d\n", rc); /* if the file is not found, don't exit with failure */ if (rc == -ENOENT) @@ -280,14 +272,14 @@ static int nfcwilink_download_fw(struct nfcwilink *drv) ptr = (__u8 *)fw->data; if ((len == 0) || (ptr == NULL)) { - nfc_dev_dbg(&drv->pdev->dev, - "request_firmware returned size %d", len); + dev_dbg(&drv->pdev->dev, + "request_firmware returned size %d\n", len); goto release_fw; } if (__le32_to_cpu(((struct bts_file_hdr *)ptr)->magic) != BTS_FILE_HDR_MAGIC) { - nfc_dev_err(&drv->pdev->dev, "wrong bts magic number"); + nfc_err(&drv->pdev->dev, "wrong bts magic number\n"); rc = -EINVAL; goto release_fw; } @@ -302,8 +294,8 @@ static int nfcwilink_download_fw(struct nfcwilink *drv) action_len = __le16_to_cpu(((struct bts_file_action *)ptr)->len); - nfc_dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d", - action_type, action_len); + dev_dbg(&drv->pdev->dev, "bts_file_action type %d, len %d\n", + action_type, action_len); switch (action_type) { case BTS_FILE_ACTION_TYPE_SEND_CMD: @@ -333,8 +325,6 @@ static void nfcwilink_register_complete(void *priv_data, char data) { struct nfcwilink *drv = priv_data; - nfc_dev_dbg(&drv->pdev->dev, "register_complete entry"); - /* store ST registration status */ drv->st_register_cb_status = data; @@ -356,7 +346,7 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) return -EFAULT; } - nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len); + dev_dbg(&drv->pdev->dev, "receive entry, len %d\n", skb->len); /* strip the ST header (apart for the chnl byte, which is not received in the hdr) */ @@ -370,7 +360,7 @@ static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) /* Forward skb to NCI core layer */ rc = nci_recv_frame(drv->ndev, skb); if (rc < 0) { - nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc); + nfc_err(&drv->pdev->dev, "nci_recv_frame failed %d\n", rc); return rc; } @@ -396,8 +386,6 @@ static int nfcwilink_open(struct nci_dev *ndev) unsigned long comp_ret; int rc; - nfc_dev_dbg(&drv->pdev->dev, "open entry"); - if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) { rc = -EBUSY; goto exit; @@ -415,9 +403,9 @@ static int nfcwilink_open(struct nci_dev *ndev) &drv->completed, msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT)); - nfc_dev_dbg(&drv->pdev->dev, - "wait_for_completion_timeout returned %ld", - comp_ret); + dev_dbg(&drv->pdev->dev, + "wait_for_completion_timeout returned %ld\n", + comp_ret); if (comp_ret == 0) { /* timeout */ @@ -425,13 +413,12 @@ static int nfcwilink_open(struct nci_dev *ndev) goto clear_exit; } else if (drv->st_register_cb_status != 0) { rc = drv->st_register_cb_status; - nfc_dev_err(&drv->pdev->dev, - "st_register_cb failed %d", rc); + nfc_err(&drv->pdev->dev, + "st_register_cb failed %d\n", rc); goto clear_exit; } } else { - nfc_dev_err(&drv->pdev->dev, - "st_register failed %d", rc); + nfc_err(&drv->pdev->dev, "st_register failed %d\n", rc); goto clear_exit; } } @@ -441,8 +428,8 @@ static int nfcwilink_open(struct nci_dev *ndev) drv->st_write = nfcwilink_proto.write; if (nfcwilink_download_fw(drv)) { - nfc_dev_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d", - rc); + nfc_err(&drv->pdev->dev, "nfcwilink_download_fw failed %d\n", + rc); /* open should succeed, even if the FW download failed */ } @@ -460,14 +447,12 @@ static int nfcwilink_close(struct nci_dev *ndev) struct nfcwilink *drv = nci_get_drvdata(ndev); int rc; - nfc_dev_dbg(&drv->pdev->dev, "close entry"); - if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags)) return 0; rc = st_unregister(&nfcwilink_proto); if (rc) - nfc_dev_err(&drv->pdev->dev, "st_unregister failed %d", rc); + nfc_err(&drv->pdev->dev, "st_unregister failed %d\n", rc); drv->st_write = NULL; @@ -480,7 +465,7 @@ static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb) struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000}; long len; - nfc_dev_dbg(&drv->pdev->dev, "send entry, len %d", skb->len); + dev_dbg(&drv->pdev->dev, "send entry, len %d\n", skb->len); if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) { kfree_skb(skb); @@ -498,7 +483,7 @@ static int nfcwilink_send(struct nci_dev *ndev, struct sk_buff *skb) len = drv->st_write(skb); if (len < 0) { kfree_skb(skb); - nfc_dev_err(&drv->pdev->dev, "st_write failed %ld", len); + nfc_err(&drv->pdev->dev, "st_write failed %ld\n", len); return -EFAULT; } @@ -517,8 +502,6 @@ static int nfcwilink_probe(struct platform_device *pdev) int rc; __u32 protocols; - nfc_dev_dbg(&pdev->dev, "probe entry"); - drv = devm_kzalloc(&pdev->dev, sizeof(struct nfcwilink), GFP_KERNEL); if (!drv) { rc = -ENOMEM; @@ -538,7 +521,7 @@ static int nfcwilink_probe(struct platform_device *pdev) NFCWILINK_HDR_LEN, 0); if (!drv->ndev) { - nfc_dev_err(&pdev->dev, "nci_allocate_device failed"); + nfc_err(&pdev->dev, "nci_allocate_device failed\n"); rc = -ENOMEM; goto exit; } @@ -548,7 +531,7 @@ static int nfcwilink_probe(struct platform_device *pdev) rc = nci_register_device(drv->ndev); if (rc < 0) { - nfc_dev_err(&pdev->dev, "nci_register_device failed %d", rc); + nfc_err(&pdev->dev, "nci_register_device failed %d\n", rc); goto free_dev_exit; } @@ -568,8 +551,6 @@ static int nfcwilink_remove(struct platform_device *pdev) struct nfcwilink *drv = dev_get_drvdata(&pdev->dev); struct nci_dev *ndev; - nfc_dev_dbg(&pdev->dev, "remove entry"); - if (!drv) return -EFAULT; @@ -578,8 +559,6 @@ static int nfcwilink_remove(struct platform_device *pdev) nci_unregister_device(ndev); nci_free_device(ndev); - dev_set_drvdata(&pdev->dev, NULL); - return 0; } diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index 5df730be88a388ba28f705fde82fdce04b1ff88e..2daf04c073383c53ae4374d8a9613546fe13d09b 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -150,6 +150,7 @@ MODULE_DEVICE_TABLE(usb, pn533_table); #define PN533_CMD_TG_INIT_AS_TARGET 0x8c #define PN533_CMD_TG_GET_DATA 0x86 #define PN533_CMD_TG_SET_DATA 0x8e +#define PN533_CMD_TG_SET_META_DATA 0x94 #define PN533_CMD_UNDEF 0xff #define PN533_CMD_RESPONSE(cmd) (cmd + 1) @@ -373,6 +374,8 @@ struct pn533 { struct delayed_work poll_work; struct work_struct mi_rx_work; struct work_struct mi_tx_work; + struct work_struct mi_tm_rx_work; + struct work_struct mi_tm_tx_work; struct work_struct tg_work; struct work_struct rf_work; @@ -387,6 +390,7 @@ struct pn533 { struct pn533_poll_modulations *poll_mod_active[PN533_POLL_MOD_MAX + 1]; u8 poll_mod_count; u8 poll_mod_curr; + u8 poll_dep; u32 poll_protocols; u32 listen_protocols; struct timer_list listen_timer; @@ -722,32 +726,32 @@ static void pn533_recv_response(struct urb *urb) break; /* success */ case -ECONNRESET: case -ENOENT: - nfc_dev_dbg(&dev->interface->dev, - "The urb has been canceled (status %d)", - urb->status); + dev_dbg(&dev->interface->dev, + "The urb has been canceled (status %d)\n", + urb->status); goto sched_wq; case -ESHUTDOWN: default: - nfc_dev_err(&dev->interface->dev, - "Urb failure (status %d)", urb->status); + nfc_err(&dev->interface->dev, + "Urb failure (status %d)\n", urb->status); goto sched_wq; } in_frame = dev->in_urb->transfer_buffer; - nfc_dev_dbg(&dev->interface->dev, "Received a frame."); + dev_dbg(&dev->interface->dev, "Received a frame\n"); print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame, dev->ops->rx_frame_size(in_frame), false); if (!dev->ops->rx_is_frame_valid(in_frame, dev)) { - nfc_dev_err(&dev->interface->dev, "Received an invalid frame"); + nfc_err(&dev->interface->dev, "Received an invalid frame\n"); cmd->status = -EIO; goto sched_wq; } if (!pn533_rx_frame_is_cmd_response(dev, in_frame)) { - nfc_dev_err(&dev->interface->dev, - "It it not the response to the last command"); + nfc_err(&dev->interface->dev, + "It it not the response to the last command\n"); cmd->status = -EIO; goto sched_wq; } @@ -777,29 +781,29 @@ static void pn533_recv_ack(struct urb *urb) break; /* success */ case -ECONNRESET: case -ENOENT: - nfc_dev_dbg(&dev->interface->dev, - "The urb has been stopped (status %d)", - urb->status); + dev_dbg(&dev->interface->dev, + "The urb has been stopped (status %d)\n", + urb->status); goto sched_wq; case -ESHUTDOWN: default: - nfc_dev_err(&dev->interface->dev, - "Urb failure (status %d)", urb->status); + nfc_err(&dev->interface->dev, + "Urb failure (status %d)\n", urb->status); goto sched_wq; } in_frame = dev->in_urb->transfer_buffer; if (!pn533_std_rx_frame_is_ack(in_frame)) { - nfc_dev_err(&dev->interface->dev, "Received an invalid ack"); + nfc_err(&dev->interface->dev, "Received an invalid ack\n"); cmd->status = -EIO; goto sched_wq; } rc = pn533_submit_urb_for_response(dev, GFP_ATOMIC); if (rc) { - nfc_dev_err(&dev->interface->dev, - "usb_submit_urb failed with result %d", rc); + nfc_err(&dev->interface->dev, + "usb_submit_urb failed with result %d\n", rc); cmd->status = rc; goto sched_wq; } @@ -823,8 +827,6 @@ static int pn533_send_ack(struct pn533 *dev, gfp_t flags) /* spec 7.1.1.3: Preamble, SoPC (2), ACK Code (2), Postamble */ int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - dev->out_urb->transfer_buffer = ack; dev->out_urb->transfer_buffer_length = sizeof(ack); rc = usb_submit_urb(dev->out_urb, flags); @@ -927,7 +929,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, struct pn533_cmd *cmd; int rc = 0; - nfc_dev_dbg(&dev->interface->dev, "Sending command 0x%x", cmd_code); + dev_dbg(&dev->interface->dev, "Sending command 0x%x\n", cmd_code); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) @@ -954,8 +956,8 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, goto unlock; } - nfc_dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x", __func__, - cmd_code); + dev_dbg(&dev->interface->dev, "%s Queueing command 0x%x\n", + __func__, cmd_code); INIT_LIST_HEAD(&cmd->queue); list_add_tail(&cmd->queue, &dev->cmd_queue); @@ -1168,14 +1170,14 @@ static void pn533_send_complete(struct urb *urb) break; /* success */ case -ECONNRESET: case -ENOENT: - nfc_dev_dbg(&dev->interface->dev, - "The urb has been stopped (status %d)", - urb->status); + dev_dbg(&dev->interface->dev, + "The urb has been stopped (status %d)\n", + urb->status); break; case -ESHUTDOWN: default: - nfc_dev_err(&dev->interface->dev, - "Urb failure (status %d)", urb->status); + nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", + urb->status); } } @@ -1452,8 +1454,8 @@ static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata, struct nfc_target nfc_tgt; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s - modulation=%d", __func__, - dev->poll_mod_curr); + dev_dbg(&dev->interface->dev, "%s: modulation=%d\n", + __func__, dev->poll_mod_curr); if (tg != 1) return -EPROTO; @@ -1475,8 +1477,8 @@ static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata, rc = pn533_target_found_type_b(&nfc_tgt, tgdata, tgdata_len); break; default: - nfc_dev_err(&dev->interface->dev, - "Unknown current poll modulation"); + nfc_err(&dev->interface->dev, + "Unknown current poll modulation\n"); return -EPROTO; } @@ -1484,14 +1486,14 @@ static int pn533_target_found(struct pn533 *dev, u8 tg, u8 *tgdata, return rc; if (!(nfc_tgt.supported_protocols & dev->poll_protocols)) { - nfc_dev_dbg(&dev->interface->dev, - "The Tg found doesn't have the desired protocol"); + dev_dbg(&dev->interface->dev, + "The Tg found doesn't have the desired protocol\n"); return -EAGAIN; } - nfc_dev_dbg(&dev->interface->dev, - "Target found - supported protocols: 0x%x", - nfc_tgt.supported_protocols); + dev_dbg(&dev->interface->dev, + "Target found - supported protocols: 0x%x\n", + nfc_tgt.supported_protocols); dev->tgt_available_prots = nfc_tgt.supported_protocols; @@ -1548,7 +1550,8 @@ static int pn533_start_poll_complete(struct pn533 *dev, struct sk_buff *resp) u8 nbtg, tg, *tgdata; int rc, tgdata_len; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + /* Toggle the DEP polling */ + dev->poll_dep = 1; nbtg = resp->data[0]; tg = resp->data[1]; @@ -1624,37 +1627,130 @@ static struct sk_buff *pn533_alloc_poll_tg_frame(struct pn533 *dev) #define PN533_CMD_DATAEXCH_HEAD_LEN 1 #define PN533_CMD_DATAEXCH_DATA_MAXLEN 262 +static void pn533_wq_tm_mi_recv(struct work_struct *work); +static struct sk_buff *pn533_build_response(struct pn533 *dev); + static int pn533_tm_get_data_complete(struct pn533 *dev, void *arg, struct sk_buff *resp) { - u8 status; + struct sk_buff *skb; + u8 status, ret, mi; + int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); - if (IS_ERR(resp)) + if (IS_ERR(resp)) { + skb_queue_purge(&dev->resp_q); return PTR_ERR(resp); + } status = resp->data[0]; + + ret = status & PN533_CMD_RET_MASK; + mi = status & PN533_CMD_MI_MASK; + skb_pull(resp, sizeof(status)); - if (status != 0) { - nfc_tm_deactivated(dev->nfc_dev); - dev->tgt_mode = 0; - dev_kfree_skb(resp); - return 0; + if (ret != PN533_CMD_RET_SUCCESS) { + rc = -EIO; + goto error; } - return nfc_tm_data_received(dev->nfc_dev, resp); + skb_queue_tail(&dev->resp_q, resp); + + if (mi) { + queue_work(dev->wq, &dev->mi_tm_rx_work); + return -EINPROGRESS; + } + + skb = pn533_build_response(dev); + if (!skb) { + rc = -EIO; + goto error; + } + + return nfc_tm_data_received(dev->nfc_dev, skb); + +error: + nfc_tm_deactivated(dev->nfc_dev); + dev->tgt_mode = 0; + skb_queue_purge(&dev->resp_q); + dev_kfree_skb(resp); + + return rc; +} + +static void pn533_wq_tm_mi_recv(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, mi_tm_rx_work); + struct sk_buff *skb; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + skb = pn533_alloc_skb(dev, 0); + if (!skb) + return; + + rc = pn533_send_cmd_direct_async(dev, + PN533_CMD_TG_GET_DATA, + skb, + pn533_tm_get_data_complete, + NULL); + + if (rc < 0) + dev_kfree_skb(skb); + + return; +} + +static int pn533_tm_send_complete(struct pn533 *dev, void *arg, + struct sk_buff *resp); +static void pn533_wq_tm_mi_send(struct work_struct *work) +{ + struct pn533 *dev = container_of(work, struct pn533, mi_tm_tx_work); + struct sk_buff *skb; + int rc; + + dev_dbg(&dev->interface->dev, "%s\n", __func__); + + /* Grab the first skb in the queue */ + skb = skb_dequeue(&dev->fragment_skb); + if (skb == NULL) { /* No more data */ + /* Reset the queue for future use */ + skb_queue_head_init(&dev->fragment_skb); + goto error; + } + + /* last entry - remove MI bit */ + if (skb_queue_len(&dev->fragment_skb) == 0) { + rc = pn533_send_cmd_direct_async(dev, PN533_CMD_TG_SET_DATA, + skb, pn533_tm_send_complete, NULL); + } else + rc = pn533_send_cmd_direct_async(dev, + PN533_CMD_TG_SET_META_DATA, + skb, pn533_tm_send_complete, NULL); + + if (rc == 0) /* success */ + return; + + dev_err(&dev->interface->dev, + "Error %d when trying to perform set meta data_exchange", rc); + + dev_kfree_skb(skb); + +error: + pn533_send_ack(dev, GFP_KERNEL); + queue_work(dev->wq, &dev->cmd_work); } static void pn533_wq_tg_get_data(struct work_struct *work) { struct pn533 *dev = container_of(work, struct pn533, tg_work); - struct sk_buff *skb; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); skb = pn533_alloc_skb(dev, 0); if (!skb) @@ -1676,7 +1772,7 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp) size_t gb_len; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); if (resp->len < ATR_REQ_GB_OFFSET + 1) return -EINVAL; @@ -1684,8 +1780,8 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp) mode = resp->data[0]; cmd = &resp->data[1]; - nfc_dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n", - mode, resp->len); + dev_dbg(&dev->interface->dev, "Target mode 0x%x len %d\n", + mode, resp->len); if ((mode & PN533_INIT_TARGET_RESP_FRAME_MASK) == PN533_INIT_TARGET_RESP_ACTIVE) @@ -1700,8 +1796,8 @@ static int pn533_init_target_complete(struct pn533 *dev, struct sk_buff *resp) rc = nfc_tm_activated(dev->nfc_dev, NFC_PROTO_NFC_DEP_MASK, comm_mode, gb, gb_len); if (rc < 0) { - nfc_dev_err(&dev->interface->dev, - "Error when signaling target activation"); + nfc_err(&dev->interface->dev, + "Error when signaling target activation\n"); return rc; } @@ -1715,7 +1811,7 @@ static void pn533_listen_mode_timer(unsigned long data) { struct pn533 *dev = (struct pn533 *)data; - nfc_dev_dbg(&dev->interface->dev, "Listen mode timeout"); + dev_dbg(&dev->interface->dev, "Listen mode timeout\n"); dev->cancel_listen = 1; @@ -1730,13 +1826,12 @@ static int pn533_rf_complete(struct pn533 *dev, void *arg, { int rc = 0; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); if (IS_ERR(resp)) { rc = PTR_ERR(resp); - nfc_dev_err(&dev->interface->dev, "%s RF setting error %d", - __func__, rc); + nfc_err(&dev->interface->dev, "RF setting error %d", rc); return rc; } @@ -1754,7 +1849,7 @@ static void pn533_wq_rf(struct work_struct *work) struct sk_buff *skb; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); skb = pn533_alloc_skb(dev, 2); if (!skb) @@ -1767,25 +1862,136 @@ static void pn533_wq_rf(struct work_struct *work) pn533_rf_complete, NULL); if (rc < 0) { dev_kfree_skb(skb); - nfc_dev_err(&dev->interface->dev, "RF setting error %d", rc); + nfc_err(&dev->interface->dev, "RF setting error %d\n", rc); } return; } +static int pn533_poll_dep_complete(struct pn533 *dev, void *arg, + struct sk_buff *resp) +{ + struct pn533_cmd_jump_dep_response *rsp; + struct nfc_target nfc_target; + u8 target_gt_len; + int rc; + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rsp = (struct pn533_cmd_jump_dep_response *)resp->data; + + rc = rsp->status & PN533_CMD_RET_MASK; + if (rc != PN533_CMD_RET_SUCCESS) { + /* Not target found, turn radio off */ + queue_work(dev->wq, &dev->rf_work); + + dev_kfree_skb(resp); + return 0; + } + + dev_dbg(&dev->interface->dev, "Creating new target"); + + nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK; + nfc_target.nfcid1_len = 10; + memcpy(nfc_target.nfcid1, rsp->nfcid3t, nfc_target.nfcid1_len); + rc = nfc_targets_found(dev->nfc_dev, &nfc_target, 1); + if (rc) + goto error; + + dev->tgt_available_prots = 0; + dev->tgt_active_prot = NFC_PROTO_NFC_DEP; + + /* ATR_RES general bytes are located at offset 17 */ + target_gt_len = resp->len - 17; + rc = nfc_set_remote_general_bytes(dev->nfc_dev, + rsp->gt, target_gt_len); + if (!rc) { + rc = nfc_dep_link_is_up(dev->nfc_dev, + dev->nfc_dev->targets[0].idx, + 0, NFC_RF_INITIATOR); + + if (!rc) + pn533_poll_reset_mod_list(dev); + } +error: + dev_kfree_skb(resp); + return rc; +} + +#define PASSIVE_DATA_LEN 5 +static int pn533_poll_dep(struct nfc_dev *nfc_dev) +{ + struct pn533 *dev = nfc_get_drvdata(nfc_dev); + struct sk_buff *skb; + int rc, skb_len; + u8 *next, nfcid3[NFC_NFCID3_MAXSIZE]; + u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3}; + + dev_dbg(&dev->interface->dev, "%s", __func__); + + if (!dev->gb) { + dev->gb = nfc_get_local_general_bytes(nfc_dev, &dev->gb_len); + + if (!dev->gb || !dev->gb_len) { + dev->poll_dep = 0; + queue_work(dev->wq, &dev->rf_work); + } + } + + skb_len = 3 + dev->gb_len; /* ActPass + BR + Next */ + skb_len += PASSIVE_DATA_LEN; + + /* NFCID3 */ + skb_len += NFC_NFCID3_MAXSIZE; + nfcid3[0] = 0x1; + nfcid3[1] = 0xfe; + get_random_bytes(nfcid3 + 2, 6); + + skb = pn533_alloc_skb(dev, skb_len); + if (!skb) + return -ENOMEM; + + *skb_put(skb, 1) = 0x01; /* Active */ + *skb_put(skb, 1) = 0x02; /* 424 kbps */ + + next = skb_put(skb, 1); /* Next */ + *next = 0; + + /* Copy passive data */ + memcpy(skb_put(skb, PASSIVE_DATA_LEN), passive_data, PASSIVE_DATA_LEN); + *next |= 1; + + /* Copy NFCID3 (which is NFCID2 from SENSF_RES) */ + memcpy(skb_put(skb, NFC_NFCID3_MAXSIZE), nfcid3, + NFC_NFCID3_MAXSIZE); + *next |= 2; + + memcpy(skb_put(skb, dev->gb_len), dev->gb, dev->gb_len); + *next |= 4; /* We have some Gi */ + + rc = pn533_send_cmd_async(dev, PN533_CMD_IN_JUMP_FOR_DEP, skb, + pn533_poll_dep_complete, NULL); + + if (rc < 0) + dev_kfree_skb(skb); + + return rc; +} + static int pn533_poll_complete(struct pn533 *dev, void *arg, struct sk_buff *resp) { struct pn533_poll_modulations *cur_mod; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); if (IS_ERR(resp)) { rc = PTR_ERR(resp); - nfc_dev_err(&dev->interface->dev, "%s Poll complete error %d", - __func__, rc); + nfc_err(&dev->interface->dev, "%s Poll complete error %d\n", + __func__, rc); if (rc == -ENOENT) { if (dev->poll_mod_count != 0) @@ -1793,8 +1999,8 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg, else goto stop_poll; } else if (rc < 0) { - nfc_dev_err(&dev->interface->dev, - "Error %d when running poll", rc); + nfc_err(&dev->interface->dev, + "Error %d when running poll\n", rc); goto stop_poll; } } @@ -1813,7 +2019,7 @@ static int pn533_poll_complete(struct pn533 *dev, void *arg, goto done; if (!dev->poll_mod_count) { - nfc_dev_dbg(&dev->interface->dev, "Polling has been stopped."); + dev_dbg(&dev->interface->dev, "Polling has been stopped\n"); goto done; } @@ -1826,7 +2032,7 @@ done: return rc; stop_poll: - nfc_dev_err(&dev->interface->dev, "Polling operation has been stopped"); + nfc_err(&dev->interface->dev, "Polling operation has been stopped\n"); pn533_poll_reset_mod_list(dev); dev->poll_protocols = 0; @@ -1856,8 +2062,13 @@ static int pn533_send_poll_frame(struct pn533 *dev) mod = dev->poll_mod_active[dev->poll_mod_curr]; - nfc_dev_dbg(&dev->interface->dev, "%s mod len %d\n", - __func__, mod->len); + dev_dbg(&dev->interface->dev, "%s mod len %d\n", + __func__, mod->len); + + if (dev->poll_dep) { + dev->poll_dep = 0; + return pn533_poll_dep(dev->nfc_dev); + } if (mod->len == 0) { /* Listen mode */ cmd_code = PN533_CMD_TG_INIT_AS_TARGET; @@ -1868,7 +2079,7 @@ static int pn533_send_poll_frame(struct pn533 *dev) } if (!skb) { - nfc_dev_err(&dev->interface->dev, "Failed to allocate skb."); + nfc_err(&dev->interface->dev, "Failed to allocate skb\n"); return -ENOMEM; } @@ -1876,7 +2087,7 @@ static int pn533_send_poll_frame(struct pn533 *dev) NULL); if (rc < 0) { dev_kfree_skb(skb); - nfc_dev_err(&dev->interface->dev, "Polling loop error %d", rc); + nfc_err(&dev->interface->dev, "Polling loop error %d\n", rc); } return rc; @@ -1890,9 +2101,9 @@ static void pn533_wq_poll(struct work_struct *work) cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; - nfc_dev_dbg(&dev->interface->dev, - "%s cancel_listen %d modulation len %d", - __func__, dev->cancel_listen, cur_mod->len); + dev_dbg(&dev->interface->dev, + "%s cancel_listen %d modulation len %d\n", + __func__, dev->cancel_listen, cur_mod->len); if (dev->cancel_listen == 1) { dev->cancel_listen = 0; @@ -1913,21 +2124,23 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev, u32 im_protocols, u32 tm_protocols) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); + struct pn533_poll_modulations *cur_mod; u8 rand_mod; + int rc; - nfc_dev_dbg(&dev->interface->dev, - "%s: im protocols 0x%x tm protocols 0x%x", - __func__, im_protocols, tm_protocols); + dev_dbg(&dev->interface->dev, + "%s: im protocols 0x%x tm protocols 0x%x\n", + __func__, im_protocols, tm_protocols); if (dev->tgt_active_prot) { - nfc_dev_err(&dev->interface->dev, - "Cannot poll with a target already activated"); + nfc_err(&dev->interface->dev, + "Cannot poll with a target already activated\n"); return -EBUSY; } if (dev->tgt_mode) { - nfc_dev_err(&dev->interface->dev, - "Cannot poll while already being activated"); + nfc_err(&dev->interface->dev, + "Cannot poll while already being activated\n"); return -EBUSY; } @@ -1946,20 +2159,26 @@ static int pn533_start_poll(struct nfc_dev *nfc_dev, rand_mod %= dev->poll_mod_count; dev->poll_mod_curr = rand_mod; - return pn533_send_poll_frame(dev); + cur_mod = dev->poll_mod_active[dev->poll_mod_curr]; + + rc = pn533_send_poll_frame(dev); + + /* Start listen timer */ + if (!rc && cur_mod->len == 0 && dev->poll_mod_count > 1) + mod_timer(&dev->listen_timer, jiffies + PN533_LISTEN_TIME * HZ); + + return rc; } static void pn533_stop_poll(struct nfc_dev *nfc_dev) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); - del_timer(&dev->listen_timer); if (!dev->poll_mod_count) { - nfc_dev_dbg(&dev->interface->dev, - "Polling operation was not running"); + dev_dbg(&dev->interface->dev, + "Polling operation was not running\n"); return; } @@ -1973,11 +2192,10 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev) struct pn533_cmd_activate_response *rsp; u16 gt_len; int rc; - struct sk_buff *skb; struct sk_buff *resp; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); skb = pn533_alloc_skb(dev, sizeof(u8) * 2); /*TG + Next*/ if (!skb) @@ -1993,8 +2211,8 @@ static int pn533_activate_target_nfcdep(struct pn533 *dev) rsp = (struct pn533_cmd_activate_response *)resp->data; rc = rsp->status & PN533_CMD_RET_MASK; if (rc != PN533_CMD_RET_SUCCESS) { - nfc_dev_err(&dev->interface->dev, - "Target activation failed (error 0x%x)", rc); + nfc_err(&dev->interface->dev, + "Target activation failed (error 0x%x)\n", rc); dev_kfree_skb(resp); return -EIO; } @@ -2013,39 +2231,38 @@ static int pn533_activate_target(struct nfc_dev *nfc_dev, struct pn533 *dev = nfc_get_drvdata(nfc_dev); int rc; - nfc_dev_dbg(&dev->interface->dev, "%s - protocol=%u", __func__, - protocol); + dev_dbg(&dev->interface->dev, "%s: protocol=%u\n", __func__, protocol); if (dev->poll_mod_count) { - nfc_dev_err(&dev->interface->dev, - "Cannot activate while polling"); + nfc_err(&dev->interface->dev, + "Cannot activate while polling\n"); return -EBUSY; } if (dev->tgt_active_prot) { - nfc_dev_err(&dev->interface->dev, - "There is already an active target"); + nfc_err(&dev->interface->dev, + "There is already an active target\n"); return -EBUSY; } if (!dev->tgt_available_prots) { - nfc_dev_err(&dev->interface->dev, - "There is no available target to activate"); + nfc_err(&dev->interface->dev, + "There is no available target to activate\n"); return -EINVAL; } if (!(dev->tgt_available_prots & (1 << protocol))) { - nfc_dev_err(&dev->interface->dev, - "Target doesn't support requested proto %u", - protocol); + nfc_err(&dev->interface->dev, + "Target doesn't support requested proto %u\n", + protocol); return -EINVAL; } if (protocol == NFC_PROTO_NFC_DEP) { rc = pn533_activate_target_nfcdep(dev); if (rc) { - nfc_dev_err(&dev->interface->dev, - "Activating target with DEP failed %d", rc); + nfc_err(&dev->interface->dev, + "Activating target with DEP failed %d\n", rc); return rc; } } @@ -2060,16 +2277,14 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, struct nfc_target *target) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); - struct sk_buff *skb; struct sk_buff *resp; - int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); if (!dev->tgt_active_prot) { - nfc_dev_err(&dev->interface->dev, "There is no active target"); + nfc_err(&dev->interface->dev, "There is no active target\n"); return; } @@ -2088,8 +2303,8 @@ static void pn533_deactivate_target(struct nfc_dev *nfc_dev, rc = resp->data[0] & PN533_CMD_RET_MASK; if (rc != PN533_CMD_RET_SUCCESS) - nfc_dev_err(&dev->interface->dev, - "Error 0x%x when releasing the target", rc); + nfc_err(&dev->interface->dev, + "Error 0x%x when releasing the target\n", rc); dev_kfree_skb(resp); return; @@ -2111,8 +2326,8 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, if (dev->tgt_available_prots && !(dev->tgt_available_prots & (1 << NFC_PROTO_NFC_DEP))) { - nfc_dev_err(&dev->interface->dev, - "The target does not support DEP"); + nfc_err(&dev->interface->dev, + "The target does not support DEP\n"); rc = -EINVAL; goto error; } @@ -2121,15 +2336,15 @@ static int pn533_in_dep_link_up_complete(struct pn533 *dev, void *arg, rc = rsp->status & PN533_CMD_RET_MASK; if (rc != PN533_CMD_RET_SUCCESS) { - nfc_dev_err(&dev->interface->dev, - "Bringing DEP link up failed (error 0x%x)", rc); + nfc_err(&dev->interface->dev, + "Bringing DEP link up failed (error 0x%x)\n", rc); goto error; } if (!dev->tgt_available_prots) { struct nfc_target nfc_target; - nfc_dev_dbg(&dev->interface->dev, "Creating new target"); + dev_dbg(&dev->interface->dev, "Creating new target\n"); nfc_target.supported_protocols = NFC_PROTO_NFC_DEP_MASK; nfc_target.nfcid1_len = 10; @@ -2158,7 +2373,6 @@ error: } static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf); -#define PASSIVE_DATA_LEN 5 static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, u8 comm_mode, u8 *gb, size_t gb_len) { @@ -2166,20 +2380,19 @@ static int pn533_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target, struct sk_buff *skb; int rc, skb_len; u8 *next, *arg, nfcid3[NFC_NFCID3_MAXSIZE]; - u8 passive_data[PASSIVE_DATA_LEN] = {0x00, 0xff, 0xff, 0x00, 0x3}; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); if (dev->poll_mod_count) { - nfc_dev_err(&dev->interface->dev, - "Cannot bring the DEP link up while polling"); + nfc_err(&dev->interface->dev, + "Cannot bring the DEP link up while polling\n"); return -EBUSY; } if (dev->tgt_active_prot) { - nfc_dev_err(&dev->interface->dev, - "There is already an active target"); + nfc_err(&dev->interface->dev, + "There is already an active target\n"); return -EBUSY; } @@ -2249,7 +2462,7 @@ static int pn533_dep_link_down(struct nfc_dev *nfc_dev) { struct pn533 *dev = nfc_get_drvdata(nfc_dev); - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); pn533_poll_reset_mod_list(dev); @@ -2274,7 +2487,7 @@ static struct sk_buff *pn533_build_response(struct pn533 *dev) struct sk_buff *skb, *tmp, *t; unsigned int skb_len = 0, tmp_len = 0; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); if (skb_queue_empty(&dev->resp_q)) return NULL; @@ -2287,8 +2500,8 @@ static struct sk_buff *pn533_build_response(struct pn533 *dev) skb_queue_walk_safe(&dev->resp_q, tmp, t) skb_len += tmp->len; - nfc_dev_dbg(&dev->interface->dev, "%s total length %d\n", - __func__, skb_len); + dev_dbg(&dev->interface->dev, "%s total length %d\n", + __func__, skb_len); skb = alloc_skb(skb_len, GFP_KERNEL); if (skb == NULL) @@ -2315,7 +2528,7 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, int rc = 0; u8 status, ret, mi; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); if (IS_ERR(resp)) { rc = PTR_ERR(resp); @@ -2329,8 +2542,8 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, skb_pull(resp, sizeof(status)); if (ret != PN533_CMD_RET_SUCCESS) { - nfc_dev_err(&dev->interface->dev, - "Exchanging data failed (error 0x%x)", ret); + nfc_err(&dev->interface->dev, + "Exchanging data failed (error 0x%x)\n", ret); rc = -EIO; goto error; } @@ -2388,14 +2601,17 @@ static int pn533_fill_fragment_skbs(struct pn533 *dev, struct sk_buff *skb) break; } - /* Reserve the TG/MI byte */ - skb_reserve(frag, 1); + if (!dev->tgt_mode) { + /* Reserve the TG/MI byte */ + skb_reserve(frag, 1); - /* MI + TG */ - if (frag_size == PN533_CMD_DATAFRAME_MAXLEN) - *skb_push(frag, sizeof(u8)) = (PN533_CMD_MI_MASK | 1); - else - *skb_push(frag, sizeof(u8)) = 1; /* TG */ + /* MI + TG */ + if (frag_size == PN533_CMD_DATAFRAME_MAXLEN) + *skb_push(frag, sizeof(u8)) = + (PN533_CMD_MI_MASK | 1); + else + *skb_push(frag, sizeof(u8)) = 1; /* TG */ + } memcpy(skb_put(frag, frag_size), skb->data, frag_size); @@ -2420,11 +2636,11 @@ static int pn533_transceive(struct nfc_dev *nfc_dev, struct pn533_data_exchange_arg *arg = NULL; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); if (!dev->tgt_active_prot) { - nfc_dev_err(&dev->interface->dev, - "Can't exchange data if there is no active target"); + nfc_err(&dev->interface->dev, + "Can't exchange data if there is no active target\n"); rc = -EINVAL; goto error; } @@ -2487,13 +2703,18 @@ static int pn533_tm_send_complete(struct pn533 *dev, void *arg, { u8 status; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); if (IS_ERR(resp)) return PTR_ERR(resp); status = resp->data[0]; + /* Prepare for the next round */ + if (skb_queue_len(&dev->fragment_skb) > 0) { + queue_work(dev->wq, &dev->mi_tm_tx_work); + return -EINPROGRESS; + } dev_kfree_skb(resp); if (status != 0) { @@ -2514,19 +2735,34 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) struct pn533 *dev = nfc_get_drvdata(nfc_dev); int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); + /* let's split in multiple chunks if size's too big */ if (skb->len > PN533_CMD_DATAEXCH_DATA_MAXLEN) { - nfc_dev_err(&dev->interface->dev, - "Data length greater than the max allowed: %d", - PN533_CMD_DATAEXCH_DATA_MAXLEN); - return -ENOSYS; + rc = pn533_fill_fragment_skbs(dev, skb); + if (rc <= 0) + goto error; + + /* get the first skb */ + skb = skb_dequeue(&dev->fragment_skb); + if (!skb) { + rc = -EIO; + goto error; + } + + rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_META_DATA, skb, + pn533_tm_send_complete, NULL); + } else { + /* Send th skb */ + rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb, + pn533_tm_send_complete, NULL); } - rc = pn533_send_data_async(dev, PN533_CMD_TG_SET_DATA, skb, - pn533_tm_send_complete, NULL); - if (rc < 0) +error: + if (rc < 0) { dev_kfree_skb(skb); + skb_queue_purge(&dev->fragment_skb); + } return rc; } @@ -2534,11 +2770,10 @@ static int pn533_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb) static void pn533_wq_mi_recv(struct work_struct *work) { struct pn533 *dev = container_of(work, struct pn533, mi_rx_work); - struct sk_buff *skb; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); skb = pn533_alloc_skb(dev, PN533_CMD_DATAEXCH_HEAD_LEN); if (!skb) @@ -2570,8 +2805,8 @@ static void pn533_wq_mi_recv(struct work_struct *work) if (rc == 0) /* success */ return; - nfc_dev_err(&dev->interface->dev, - "Error %d when trying to perform data_exchange", rc); + nfc_err(&dev->interface->dev, + "Error %d when trying to perform data_exchange\n", rc); dev_kfree_skb(skb); kfree(dev->cmd_complete_mi_arg); @@ -2587,7 +2822,7 @@ static void pn533_wq_mi_send(struct work_struct *work) struct sk_buff *skb; int rc; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); /* Grab the first skb in the queue */ skb = skb_dequeue(&dev->fragment_skb); @@ -2625,8 +2860,8 @@ static void pn533_wq_mi_send(struct work_struct *work) if (rc == 0) /* success */ return; - nfc_dev_err(&dev->interface->dev, - "Error %d when trying to perform data_exchange", rc); + nfc_err(&dev->interface->dev, + "Error %d when trying to perform data_exchange\n", rc); dev_kfree_skb(skb); kfree(dev->cmd_complete_dep_arg); @@ -2641,10 +2876,9 @@ static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, { struct sk_buff *skb; struct sk_buff *resp; - int skb_len; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); skb_len = sizeof(cfgitem) + cfgdata_len; /* cfgitem + cfgdata */ @@ -2691,7 +2925,7 @@ static int pn533_pasori_fw_reset(struct pn533 *dev) struct sk_buff *skb; struct sk_buff *resp; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); skb = pn533_alloc_skb(dev, sizeof(u8)); if (!skb) @@ -2717,7 +2951,7 @@ static void pn533_acr122_poweron_rdr_resp(struct urb *urb) { struct pn533_acr122_poweron_rdr_arg *arg = urb->context; - nfc_dev_dbg(&urb->dev->dev, "%s", __func__); + dev_dbg(&urb->dev->dev, "%s\n", __func__); print_hex_dump_debug("ACR122 RX: ", DUMP_PREFIX_NONE, 16, 1, urb->transfer_buffer, urb->transfer_buffer_length, @@ -2737,7 +2971,7 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev) void *cntx; struct pn533_acr122_poweron_rdr_arg arg; - nfc_dev_dbg(&dev->interface->dev, "%s", __func__); + dev_dbg(&dev->interface->dev, "%s\n", __func__); init_completion(&arg.done); cntx = dev->in_urb->context; /* backup context */ @@ -2755,16 +2989,15 @@ static int pn533_acr122_poweron_rdr(struct pn533 *dev) rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); if (rc) { - nfc_dev_err(&dev->interface->dev, - "Reader power on cmd error %d", rc); + nfc_err(&dev->interface->dev, + "Reader power on cmd error %d\n", rc); return rc; } rc = usb_submit_urb(dev->in_urb, GFP_KERNEL); if (rc) { - nfc_dev_err(&dev->interface->dev, - "Can't submit for reader power on cmd response %d", - rc); + nfc_err(&dev->interface->dev, + "Can't submit reader poweron cmd response %d\n", rc); return rc; } @@ -2785,20 +3018,19 @@ static int pn533_rf_field(struct nfc_dev *nfc_dev, u8 rf) rc = pn533_set_configuration(dev, PN533_CFGITEM_RF_FIELD, (u8 *)&rf_field, 1); if (rc) { - nfc_dev_err(&dev->interface->dev, - "Error on setting RF field"); + nfc_err(&dev->interface->dev, "Error on setting RF field\n"); return rc; } return rc; } -int pn533_dev_up(struct nfc_dev *nfc_dev) +static int pn533_dev_up(struct nfc_dev *nfc_dev) { return pn533_rf_field(nfc_dev, 1); } -int pn533_dev_down(struct nfc_dev *nfc_dev) +static int pn533_dev_down(struct nfc_dev *nfc_dev) { return pn533_rf_field(nfc_dev, 0); } @@ -2839,16 +3071,16 @@ static int pn533_setup(struct pn533 *dev) break; default: - nfc_dev_err(&dev->interface->dev, "Unknown device type %d\n", - dev->device_type); + nfc_err(&dev->interface->dev, "Unknown device type %d\n", + dev->device_type); return -EINVAL; } rc = pn533_set_configuration(dev, PN533_CFGITEM_MAX_RETRIES, (u8 *)&max_retries, sizeof(max_retries)); if (rc) { - nfc_dev_err(&dev->interface->dev, - "Error on setting MAX_RETRIES config"); + nfc_err(&dev->interface->dev, + "Error on setting MAX_RETRIES config\n"); return rc; } @@ -2856,8 +3088,7 @@ static int pn533_setup(struct pn533 *dev) rc = pn533_set_configuration(dev, PN533_CFGITEM_TIMING, (u8 *)&timing, sizeof(timing)); if (rc) { - nfc_dev_err(&dev->interface->dev, - "Error on setting RF timings"); + nfc_err(&dev->interface->dev, "Error on setting RF timings\n"); return rc; } @@ -2871,8 +3102,8 @@ static int pn533_setup(struct pn533 *dev) rc = pn533_set_configuration(dev, PN533_CFGITEM_PASORI, pasori_cfg, 3); if (rc) { - nfc_dev_err(&dev->interface->dev, - "Error while settings PASORI config"); + nfc_err(&dev->interface->dev, + "Error while settings PASORI config\n"); return rc; } @@ -2917,8 +3148,8 @@ static int pn533_probe(struct usb_interface *interface, } if (!in_endpoint || !out_endpoint) { - nfc_dev_err(&interface->dev, - "Could not find bulk-in or bulk-out endpoint"); + nfc_err(&interface->dev, + "Could not find bulk-in or bulk-out endpoint\n"); rc = -ENODEV; goto error; } @@ -2941,6 +3172,8 @@ static int pn533_probe(struct usb_interface *interface, INIT_WORK(&dev->mi_rx_work, pn533_wq_mi_recv); INIT_WORK(&dev->mi_tx_work, pn533_wq_mi_send); INIT_WORK(&dev->tg_work, pn533_wq_tg_get_data); + INIT_WORK(&dev->mi_tm_rx_work, pn533_wq_tm_mi_recv); + INIT_WORK(&dev->mi_tm_tx_work, pn533_wq_tm_mi_send); INIT_DELAYED_WORK(&dev->poll_work, pn533_wq_poll); INIT_WORK(&dev->rf_work, pn533_wq_rf); dev->wq = alloc_ordered_workqueue("pn533", 0); @@ -2978,16 +3211,15 @@ static int pn533_probe(struct usb_interface *interface, rc = pn533_acr122_poweron_rdr(dev); if (rc < 0) { - nfc_dev_err(&dev->interface->dev, - "Couldn't poweron the reader (error %d)", - rc); + nfc_err(&dev->interface->dev, + "Couldn't poweron the reader (error %d)\n", rc); goto destroy_wq; } break; default: - nfc_dev_err(&dev->interface->dev, "Unknown device type %d\n", - dev->device_type); + nfc_err(&dev->interface->dev, "Unknown device type %d\n", + dev->device_type); rc = -EINVAL; goto destroy_wq; } @@ -2997,9 +3229,9 @@ static int pn533_probe(struct usb_interface *interface, if (rc < 0) goto destroy_wq; - nfc_dev_info(&dev->interface->dev, - "NXP PN5%02X firmware ver %d.%d now attached", - fw_ver.ic, fw_ver.ver, fw_ver.rev); + nfc_info(&dev->interface->dev, + "NXP PN5%02X firmware ver %d.%d now attached\n", + fw_ver.ic, fw_ver.ver, fw_ver.rev); dev->nfc_dev = nfc_allocate_device(&pn533_nfc_ops, protocols, @@ -3070,7 +3302,7 @@ static void pn533_disconnect(struct usb_interface *interface) usb_free_urb(dev->out_urb); kfree(dev); - nfc_dev_info(&interface->dev, "NXP PN533 NFC device disconnected"); + nfc_info(&interface->dev, "NXP PN533 NFC device disconnected\n"); } static struct usb_driver pn533_driver = { diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index 01e27d4bdd0d7abcfdc4d91c7f6963974f3b4e48..b158ee1c2ac69fc8ec0ff3fc667df10e5eb3f70e 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -151,8 +153,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) char rset_cmd[] = { 0x05, 0xF9, 0x04, 0x00, 0xC3, 0xE5 }; int count = sizeof(rset_cmd); - pr_info(DRIVER_DESC ": %s\n", __func__); - dev_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n"); + nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n"); /* Disable fw download */ gpio_set_value(phy->gpio_fw, 0); @@ -173,7 +174,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) dev_dbg(&phy->i2c_dev->dev, "Sending reset cmd\n"); ret = i2c_master_send(phy->i2c_dev, rset_cmd, count); if (ret == count) { - dev_info(&phy->i2c_dev->dev, + nfc_info(&phy->i2c_dev->dev, "nfc_en polarity : active %s\n", (polarity == 0 ? "low" : "high")); goto out; @@ -181,7 +182,7 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) } } - dev_err(&phy->i2c_dev->dev, + nfc_err(&phy->i2c_dev->dev, "Could not detect nfc_en polarity, fallback to active high\n"); out: @@ -201,7 +202,7 @@ static int pn544_hci_i2c_enable(void *phy_id) { struct pn544_i2c_phy *phy = phy_id; - pr_info(DRIVER_DESC ": %s\n", __func__); + pr_info("%s\n", __func__); pn544_hci_i2c_enable_mode(phy, PN544_HCI_MODE); @@ -214,8 +215,6 @@ static void pn544_hci_i2c_disable(void *phy_id) { struct pn544_i2c_phy *phy = phy_id; - pr_info(DRIVER_DESC ": %s\n", __func__); - gpio_set_value(phy->gpio_fw, 0); gpio_set_value(phy->gpio_en, !phy->en_polarity); usleep_range(10000, 15000); @@ -298,11 +297,9 @@ static int check_crc(u8 *buf, int buflen) crc = ~crc; if (buf[len - 2] != (crc & 0xff) || buf[len - 1] != (crc >> 8)) { - pr_err(PN544_HCI_I2C_DRIVER_NAME - ": CRC error 0x%x != 0x%x 0x%x\n", + pr_err("CRC error 0x%x != 0x%x 0x%x\n", crc, buf[len - 1], buf[len - 2]); - - pr_info(DRIVER_DESC ": %s : BAD CRC\n", __func__); + pr_info("%s: BAD CRC\n", __func__); print_hex_dump(KERN_DEBUG, "crc: ", DUMP_PREFIX_NONE, 16, 2, buf, buflen, false); return -EPERM; @@ -328,13 +325,13 @@ static int pn544_hci_i2c_read(struct pn544_i2c_phy *phy, struct sk_buff **skb) r = i2c_master_recv(client, &len, 1); if (r != 1) { - dev_err(&client->dev, "cannot read len byte\n"); + nfc_err(&client->dev, "cannot read len byte\n"); return -EREMOTEIO; } if ((len < (PN544_HCI_I2C_LLC_MIN_SIZE - 1)) || (len > (PN544_HCI_I2C_LLC_MAX_SIZE - 1))) { - dev_err(&client->dev, "invalid len byte\n"); + nfc_err(&client->dev, "invalid len byte\n"); r = -EBADMSG; goto flush; } @@ -386,7 +383,7 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy) r = i2c_master_recv(client, (char *) &response, sizeof(response)); if (r != sizeof(response)) { - dev_err(&client->dev, "cannot read fw status\n"); + nfc_err(&client->dev, "cannot read fw status\n"); return -EIO; } @@ -478,8 +475,7 @@ static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name) { struct pn544_i2c_phy *phy = phy_id; - pr_info(DRIVER_DESC ": Starting Firmware Download (%s)\n", - firmware_name); + pr_info("Starting Firmware Download (%s)\n", firmware_name); strcpy(phy->firmware_name, firmware_name); @@ -493,7 +489,7 @@ static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name) static void pn544_hci_i2c_fw_work_complete(struct pn544_i2c_phy *phy, int result) { - pr_info(DRIVER_DESC ": Firmware Download Complete, result=%d\n", result); + pr_info("Firmware Download Complete, result=%d\n", result); pn544_hci_i2c_disable(phy); @@ -694,14 +690,14 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, dev_dbg(&client->dev, "IRQ: %d\n", client->irq); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_err(&client->dev, "Need I2C_FUNC_I2C\n"); + nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); return -ENODEV; } phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy), GFP_KERNEL); if (!phy) { - dev_err(&client->dev, + nfc_err(&client->dev, "Cannot allocate memory for pn544 i2c phy.\n"); return -ENOMEM; } @@ -714,18 +710,18 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, pdata = client->dev.platform_data; if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); + nfc_err(&client->dev, "No platform data\n"); return -EINVAL; } if (pdata->request_resources == NULL) { - dev_err(&client->dev, "request_resources() missing\n"); + nfc_err(&client->dev, "request_resources() missing\n"); return -EINVAL; } r = pdata->request_resources(client); if (r) { - dev_err(&client->dev, "Cannot get platform resources\n"); + nfc_err(&client->dev, "Cannot get platform resources\n"); return r; } @@ -739,7 +735,7 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, IRQF_TRIGGER_RISING | IRQF_ONESHOT, PN544_HCI_I2C_DRIVER_NAME, phy); if (r < 0) { - dev_err(&client->dev, "Unable to register IRQ handler\n"); + nfc_err(&client->dev, "Unable to register IRQ handler\n"); goto err_rti; } diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 078e62feba1715e9c4cc3bfd246e6c947082735b..74cfa0a88b9e7c0ae6cdedd66d77deedd15e9872 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -18,6 +18,8 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include #include #include @@ -41,6 +43,7 @@ enum pn544_state { /* Proprietary commands */ #define PN544_WRITE 0x3f +#define PN544_TEST_SWP 0x21 /* Proprietary gates, events, commands and registers */ @@ -81,14 +84,17 @@ enum pn544_state { #define PN544_PL_NFCT_DEACTIVATED 0x09 #define PN544_SWP_MGMT_GATE 0xA0 +#define PN544_SWP_DEFAULT_MODE 0x01 #define PN544_NFC_WI_MGMT_GATE 0xA1 +#define PN544_NFC_ESE_DEFAULT_MODE 0x01 #define PN544_HCI_EVT_SND_DATA 0x01 #define PN544_HCI_EVT_ACTIVATED 0x02 #define PN544_HCI_EVT_DEACTIVATED 0x03 #define PN544_HCI_EVT_RCV_DATA 0x04 #define PN544_HCI_EVT_CONTINUE_MI 0x05 +#define PN544_HCI_EVT_SWITCH_MODE 0x03 #define PN544_HCI_CMD_ATTREQUEST 0x12 #define PN544_HCI_CMD_CONTINUE_ACTIVATION 0x13 @@ -187,13 +193,6 @@ static int pn544_hci_ready(struct nfc_hci_dev *hdev) {{0x9e, 0xb4}, 0x00}, - {{0x9e, 0xd9}, 0xff}, - {{0x9e, 0xda}, 0xff}, - {{0x9e, 0xdb}, 0x23}, - {{0x9e, 0xdc}, 0x21}, - {{0x9e, 0xdd}, 0x22}, - {{0x9e, 0xde}, 0x24}, - {{0x9c, 0x01}, 0x08}, {{0x9e, 0xaa}, 0x01}, @@ -394,7 +393,7 @@ static int pn544_hci_start_poll(struct nfc_hci_dev *hdev, if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) { hdev->gb = nfc_get_local_general_bytes(hdev->ndev, &hdev->gb_len); - pr_debug("generate local bytes %p", hdev->gb); + pr_debug("generate local bytes %p\n", hdev->gb); if (hdev->gb == NULL || hdev->gb_len == 0) { im_protocols &= ~NFC_PROTO_NFC_DEP_MASK; tm_protocols &= ~NFC_PROTO_NFC_DEP_MASK; @@ -696,7 +695,7 @@ static int pn544_hci_tm_send(struct nfc_hci_dev *hdev, struct sk_buff *skb) static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, struct nfc_target *target) { - pr_debug("supported protocol %d", target->supported_protocols); + pr_debug("supported protocol %d\b", target->supported_protocols); if (target->supported_protocols & (NFC_PROTO_ISO14443_MASK | NFC_PROTO_ISO14443_B_MASK)) { return nfc_hci_send_cmd(hdev, target->hci_reader_gate, @@ -733,7 +732,7 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, struct sk_buff *rgb_skb = NULL; int r; - pr_debug("hci event %d", event); + pr_debug("hci event %d\n", event); switch (event) { case PN544_HCI_EVT_ACTIVATED: if (gate == PN544_RF_READER_NFCIP1_INITIATOR_GATE) { @@ -764,7 +763,7 @@ static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, } if (skb->data[0] != 0) { - pr_debug("data0 %d", skb->data[0]); + pr_debug("data0 %d\n", skb->data[0]); r = -EPROTO; goto exit; } @@ -792,6 +791,108 @@ static int pn544_hci_fw_download(struct nfc_hci_dev *hdev, return info->fw_download(info->phy_id, firmware_name); } +static int pn544_hci_discover_se(struct nfc_hci_dev *hdev) +{ + u32 se_idx = 0; + u8 ese_mode = 0x01; /* Default mode */ + struct sk_buff *res_skb; + int r; + + r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, PN544_TEST_SWP, + NULL, 0, &res_skb); + + if (r == 0) { + if (res_skb->len == 2 && res_skb->data[0] == 0x00) + nfc_add_se(hdev->ndev, se_idx++, NFC_SE_UICC); + + kfree_skb(res_skb); + } + + r = nfc_hci_send_event(hdev, PN544_NFC_WI_MGMT_GATE, + PN544_HCI_EVT_SWITCH_MODE, + &ese_mode, 1); + if (r == 0) + nfc_add_se(hdev->ndev, se_idx++, NFC_SE_EMBEDDED); + + return !se_idx; +} + +#define PN544_SE_MODE_OFF 0x00 +#define PN544_SE_MODE_ON 0x01 +static int pn544_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + struct nfc_se *se; + u8 enable = PN544_SE_MODE_ON; + static struct uicc_gatelist { + u8 head; + u8 adr[2]; + u8 value; + } uicc_gatelist[] = { + {0x00, {0x9e, 0xd9}, 0x23}, + {0x00, {0x9e, 0xda}, 0x21}, + {0x00, {0x9e, 0xdb}, 0x22}, + {0x00, {0x9e, 0xdc}, 0x24}, + }; + struct uicc_gatelist *p = uicc_gatelist; + int count = ARRAY_SIZE(uicc_gatelist); + struct sk_buff *res_skb; + int r; + + se = nfc_find_se(hdev->ndev, se_idx); + + switch (se->type) { + case NFC_SE_UICC: + while (count--) { + r = nfc_hci_send_cmd(hdev, PN544_SYS_MGMT_GATE, + PN544_WRITE, (u8 *)p, 4, &res_skb); + if (r < 0) + return r; + + if (res_skb->len != 1) { + kfree_skb(res_skb); + return -EPROTO; + } + + if (res_skb->data[0] != p->value) { + kfree_skb(res_skb); + return -EIO; + } + + kfree_skb(res_skb); + + p++; + } + + return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE, + PN544_SWP_DEFAULT_MODE, &enable, 1); + case NFC_SE_EMBEDDED: + return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE, + PN544_NFC_ESE_DEFAULT_MODE, &enable, 1); + + default: + return -EINVAL; + } +} + +static int pn544_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + struct nfc_se *se; + u8 disable = PN544_SE_MODE_OFF; + + se = nfc_find_se(hdev->ndev, se_idx); + + switch (se->type) { + case NFC_SE_UICC: + return nfc_hci_set_param(hdev, PN544_SWP_MGMT_GATE, + PN544_SWP_DEFAULT_MODE, &disable, 1); + case NFC_SE_EMBEDDED: + return nfc_hci_set_param(hdev, PN544_NFC_WI_MGMT_GATE, + PN544_NFC_ESE_DEFAULT_MODE, &disable, 1); + default: + return -EINVAL; + } +} + static struct nfc_hci_ops pn544_hci_ops = { .open = pn544_hci_open, .close = pn544_hci_close, @@ -807,6 +908,9 @@ static struct nfc_hci_ops pn544_hci_ops = { .check_presence = pn544_hci_check_presence, .event_received = pn544_hci_event_received, .fw_download = pn544_hci_fw_download, + .discover_se = pn544_hci_discover_se, + .enable_se = pn544_hci_enable_se, + .disable_se = pn544_hci_disable_se, }; int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, @@ -820,7 +924,6 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, info = kzalloc(sizeof(struct pn544_hci_info), GFP_KERNEL); if (!info) { - pr_err("Cannot allocate memory for pn544_hci_info.\n"); r = -ENOMEM; goto err_info_alloc; } @@ -853,7 +956,7 @@ int pn544_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, phy_headroom + PN544_CMDS_HEADROOM, phy_tailroom, phy_payload); if (!info->hdev) { - pr_err("Cannot allocate nfc hdev.\n"); + pr_err("Cannot allocate nfc hdev\n"); r = -ENOMEM; goto err_alloc_hdev; } diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c new file mode 100644 index 0000000000000000000000000000000000000000..8a0571eb2627e42becfeb424e33d8099255037c7 --- /dev/null +++ b/drivers/nfc/port100.c @@ -0,0 +1,1529 @@ +/* + * Sony NFC Port-100 Series driver + * Copyright (c) 2013, Intel Corporation. + * + * Partly based/Inspired by Stephen Tiedemann's nfcpy + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include +#include +#include + +#define VERSION "0.1" + +#define SONY_VENDOR_ID 0x054c +#define RCS380_PRODUCT_ID 0x06c1 + +#define PORT100_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ + NFC_PROTO_MIFARE_MASK | \ + NFC_PROTO_FELICA_MASK | \ + NFC_PROTO_NFC_DEP_MASK) + +#define PORT100_CAPABILITIES (NFC_DIGITAL_DRV_CAPS_IN_CRC | \ + NFC_DIGITAL_DRV_CAPS_TG_CRC) + +/* Standard port100 frame definitions */ +#define PORT100_FRAME_HEADER_LEN (sizeof(struct port100_frame) \ + + 2) /* data[0] CC, data[1] SCC */ +#define PORT100_FRAME_TAIL_LEN 2 /* data[len] DCS, data[len + 1] postamble*/ + +#define PORT100_COMM_RF_HEAD_MAX_LEN (sizeof(struct port100_tg_comm_rf_cmd)) + +/* + * Max extended frame payload len, excluding CC and SCC + * which are already in PORT100_FRAME_HEADER_LEN. + */ +#define PORT100_FRAME_MAX_PAYLOAD_LEN 1001 + +#define PORT100_FRAME_ACK_SIZE 6 /* Preamble (1), SoPC (2), ACK Code (2), + Postamble (1) */ +static u8 ack_frame[PORT100_FRAME_ACK_SIZE] = { + 0x00, 0x00, 0xff, 0x00, 0xff, 0x00 +}; + +#define PORT100_FRAME_CHECKSUM(f) (f->data[le16_to_cpu(f->datalen)]) +#define PORT100_FRAME_POSTAMBLE(f) (f->data[le16_to_cpu(f->datalen) + 1]) + +/* start of frame */ +#define PORT100_FRAME_SOF 0x00FF +#define PORT100_FRAME_EXT 0xFFFF +#define PORT100_FRAME_ACK 0x00FF + +/* Port-100 command: in or out */ +#define PORT100_FRAME_DIRECTION(f) (f->data[0]) /* CC */ +#define PORT100_FRAME_DIR_OUT 0xD6 +#define PORT100_FRAME_DIR_IN 0xD7 + +/* Port-100 sub-command */ +#define PORT100_FRAME_CMD(f) (f->data[1]) /* SCC */ + +#define PORT100_CMD_GET_FIRMWARE_VERSION 0x20 +#define PORT100_CMD_GET_COMMAND_TYPE 0x28 +#define PORT100_CMD_SET_COMMAND_TYPE 0x2A + +#define PORT100_CMD_IN_SET_RF 0x00 +#define PORT100_CMD_IN_SET_PROTOCOL 0x02 +#define PORT100_CMD_IN_COMM_RF 0x04 + +#define PORT100_CMD_TG_SET_RF 0x40 +#define PORT100_CMD_TG_SET_PROTOCOL 0x42 +#define PORT100_CMD_TG_SET_RF_OFF 0x46 +#define PORT100_CMD_TG_COMM_RF 0x48 + +#define PORT100_CMD_SWITCH_RF 0x06 + +#define PORT100_CMD_RESPONSE(cmd) (cmd + 1) + +#define PORT100_CMD_TYPE_IS_SUPPORTED(mask, cmd_type) \ + ((mask) & (0x01 << (cmd_type))) +#define PORT100_CMD_TYPE_0 0 +#define PORT100_CMD_TYPE_1 1 + +#define PORT100_CMD_STATUS_OK 0x00 +#define PORT100_CMD_STATUS_TIMEOUT 0x80 + +#define PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK 0x01 +#define PORT100_MDAA_TGT_WAS_ACTIVATED_MASK 0x02 + +struct port100; + +typedef void (*port100_send_async_complete_t)(struct port100 *dev, void *arg, + struct sk_buff *resp); + +/** + * Setting sets structure for in_set_rf command + * + * @in_*_set_number: Represent the entry indexes in the port-100 RF Base Table. + * This table contains multiple RF setting sets required for RF + * communication. + * + * @in_*_comm_type: Theses fields set the communication type to be used. + */ +struct port100_in_rf_setting { + u8 in_send_set_number; + u8 in_send_comm_type; + u8 in_recv_set_number; + u8 in_recv_comm_type; +} __packed; + +#define PORT100_COMM_TYPE_IN_212F 0x01 +#define PORT100_COMM_TYPE_IN_424F 0x02 +#define PORT100_COMM_TYPE_IN_106A 0x03 + +static const struct port100_in_rf_setting in_rf_settings[] = { + [NFC_DIGITAL_RF_TECH_212F] = { + .in_send_set_number = 1, + .in_send_comm_type = PORT100_COMM_TYPE_IN_212F, + .in_recv_set_number = 15, + .in_recv_comm_type = PORT100_COMM_TYPE_IN_212F, + }, + [NFC_DIGITAL_RF_TECH_424F] = { + .in_send_set_number = 1, + .in_send_comm_type = PORT100_COMM_TYPE_IN_424F, + .in_recv_set_number = 15, + .in_recv_comm_type = PORT100_COMM_TYPE_IN_424F, + }, + [NFC_DIGITAL_RF_TECH_106A] = { + .in_send_set_number = 2, + .in_send_comm_type = PORT100_COMM_TYPE_IN_106A, + .in_recv_set_number = 15, + .in_recv_comm_type = PORT100_COMM_TYPE_IN_106A, + }, +}; + +/** + * Setting sets structure for tg_set_rf command + * + * @tg_set_number: Represents the entry index in the port-100 RF Base Table. + * This table contains multiple RF setting sets required for RF + * communication. this field is used for both send and receive + * settings. + * + * @tg_comm_type: Sets the communication type to be used to send and receive + * data. + */ +struct port100_tg_rf_setting { + u8 tg_set_number; + u8 tg_comm_type; +} __packed; + +#define PORT100_COMM_TYPE_TG_106A 0x0B +#define PORT100_COMM_TYPE_TG_212F 0x0C +#define PORT100_COMM_TYPE_TG_424F 0x0D + +static const struct port100_tg_rf_setting tg_rf_settings[] = { + [NFC_DIGITAL_RF_TECH_106A] = { + .tg_set_number = 8, + .tg_comm_type = PORT100_COMM_TYPE_TG_106A, + }, + [NFC_DIGITAL_RF_TECH_212F] = { + .tg_set_number = 8, + .tg_comm_type = PORT100_COMM_TYPE_TG_212F, + }, + [NFC_DIGITAL_RF_TECH_424F] = { + .tg_set_number = 8, + .tg_comm_type = PORT100_COMM_TYPE_TG_424F, + }, +}; + +#define PORT100_IN_PROT_INITIAL_GUARD_TIME 0x00 +#define PORT100_IN_PROT_ADD_CRC 0x01 +#define PORT100_IN_PROT_CHECK_CRC 0x02 +#define PORT100_IN_PROT_MULTI_CARD 0x03 +#define PORT100_IN_PROT_ADD_PARITY 0x04 +#define PORT100_IN_PROT_CHECK_PARITY 0x05 +#define PORT100_IN_PROT_BITWISE_AC_RECV_MODE 0x06 +#define PORT100_IN_PROT_VALID_BIT_NUMBER 0x07 +#define PORT100_IN_PROT_CRYPTO1 0x08 +#define PORT100_IN_PROT_ADD_SOF 0x09 +#define PORT100_IN_PROT_CHECK_SOF 0x0A +#define PORT100_IN_PROT_ADD_EOF 0x0B +#define PORT100_IN_PROT_CHECK_EOF 0x0C +#define PORT100_IN_PROT_DEAF_TIME 0x0E +#define PORT100_IN_PROT_CRM 0x0F +#define PORT100_IN_PROT_CRM_MIN_LEN 0x10 +#define PORT100_IN_PROT_T1_TAG_FRAME 0x11 +#define PORT100_IN_PROT_RFCA 0x12 +#define PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR 0x13 +#define PORT100_IN_PROT_END 0x14 + +#define PORT100_IN_MAX_NUM_PROTOCOLS 19 + +#define PORT100_TG_PROT_TU 0x00 +#define PORT100_TG_PROT_RF_OFF 0x01 +#define PORT100_TG_PROT_CRM 0x02 +#define PORT100_TG_PROT_END 0x03 + +#define PORT100_TG_MAX_NUM_PROTOCOLS 3 + +struct port100_protocol { + u8 number; + u8 value; +} __packed; + +static struct port100_protocol +in_protocols[][PORT100_IN_MAX_NUM_PROTOCOLS + 1] = { + [NFC_DIGITAL_FRAMING_NFCA_SHORT] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 6 }, + { PORT100_IN_PROT_ADD_CRC, 0 }, + { PORT100_IN_PROT_CHECK_CRC, 0 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 0 }, + { PORT100_IN_PROT_CHECK_PARITY, 1 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 7 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 0 }, + { PORT100_IN_PROT_CHECK_SOF, 0 }, + { PORT100_IN_PROT_ADD_EOF, 0 }, + { PORT100_IN_PROT_CHECK_EOF, 0 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 6 }, + { PORT100_IN_PROT_ADD_CRC, 0 }, + { PORT100_IN_PROT_CHECK_CRC, 0 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 1 }, + { PORT100_IN_PROT_CHECK_PARITY, 1 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 0 }, + { PORT100_IN_PROT_CHECK_SOF, 0 }, + { PORT100_IN_PROT_ADD_EOF, 0 }, + { PORT100_IN_PROT_CHECK_EOF, 0 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 6 }, + { PORT100_IN_PROT_ADD_CRC, 1 }, + { PORT100_IN_PROT_CHECK_CRC, 1 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 1 }, + { PORT100_IN_PROT_CHECK_PARITY, 1 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 0 }, + { PORT100_IN_PROT_CHECK_SOF, 0 }, + { PORT100_IN_PROT_ADD_EOF, 0 }, + { PORT100_IN_PROT_CHECK_EOF, 0 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_T1T] = { + /* nfc_digital_framing_nfca_short */ + { PORT100_IN_PROT_ADD_CRC, 2 }, + { PORT100_IN_PROT_CHECK_CRC, 2 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 2 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_T2T] = { + /* nfc_digital_framing_nfca_standard */ + { PORT100_IN_PROT_ADD_CRC, 1 }, + { PORT100_IN_PROT_CHECK_CRC, 0 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = { + /* nfc_digital_framing_nfca_standard */ + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF] = { + { PORT100_IN_PROT_INITIAL_GUARD_TIME, 18 }, + { PORT100_IN_PROT_ADD_CRC, 1 }, + { PORT100_IN_PROT_CHECK_CRC, 1 }, + { PORT100_IN_PROT_MULTI_CARD, 0 }, + { PORT100_IN_PROT_ADD_PARITY, 0 }, + { PORT100_IN_PROT_CHECK_PARITY, 0 }, + { PORT100_IN_PROT_BITWISE_AC_RECV_MODE, 0 }, + { PORT100_IN_PROT_VALID_BIT_NUMBER, 8 }, + { PORT100_IN_PROT_CRYPTO1, 0 }, + { PORT100_IN_PROT_ADD_SOF, 0 }, + { PORT100_IN_PROT_CHECK_SOF, 0 }, + { PORT100_IN_PROT_ADD_EOF, 0 }, + { PORT100_IN_PROT_CHECK_EOF, 0 }, + { PORT100_IN_PROT_DEAF_TIME, 4 }, + { PORT100_IN_PROT_CRM, 0 }, + { PORT100_IN_PROT_CRM_MIN_LEN, 0 }, + { PORT100_IN_PROT_T1_TAG_FRAME, 0 }, + { PORT100_IN_PROT_RFCA, 0 }, + { PORT100_IN_PROT_GUARD_TIME_AT_INITIATOR, 6 }, + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF_T3T] = { + /* nfc_digital_framing_nfcf */ + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = { + /* nfc_digital_framing_nfcf */ + { PORT100_IN_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = { + { PORT100_IN_PROT_END, 0 }, + }, +}; + +static struct port100_protocol +tg_protocols[][PORT100_TG_MAX_NUM_PROTOCOLS + 1] = { + [NFC_DIGITAL_FRAMING_NFCA_SHORT] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_STANDARD] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_T1T] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_T2T] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCA_NFC_DEP] = { + { PORT100_TG_PROT_TU, 1 }, + { PORT100_TG_PROT_RF_OFF, 0 }, + { PORT100_TG_PROT_CRM, 7 }, + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF_T3T] = { + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFCF_NFC_DEP] = { + { PORT100_TG_PROT_TU, 1 }, + { PORT100_TG_PROT_RF_OFF, 0 }, + { PORT100_TG_PROT_CRM, 7 }, + { PORT100_TG_PROT_END, 0 }, + }, + [NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED] = { + { PORT100_TG_PROT_RF_OFF, 1 }, + { PORT100_TG_PROT_END, 0 }, + }, +}; + +struct port100 { + struct nfc_digital_dev *nfc_digital_dev; + + int skb_headroom; + int skb_tailroom; + + struct usb_device *udev; + struct usb_interface *interface; + + struct urb *out_urb; + struct urb *in_urb; + + struct work_struct cmd_complete_work; + + u8 cmd_type; + + /* The digital stack serializes commands to be sent. There is no need + * for any queuing/locking mechanism at driver level. + */ + struct port100_cmd *cmd; +}; + +struct port100_cmd { + u8 code; + int status; + struct sk_buff *req; + struct sk_buff *resp; + int resp_len; + port100_send_async_complete_t complete_cb; + void *complete_cb_context; +}; + +struct port100_frame { + u8 preamble; + __be16 start_frame; + __be16 extended_frame; + __le16 datalen; + u8 datalen_checksum; + u8 data[]; +} __packed; + +struct port100_ack_frame { + u8 preamble; + __be16 start_frame; + __be16 ack_frame; + u8 postambule; +} __packed; + +struct port100_cb_arg { + nfc_digital_cmd_complete_t complete_cb; + void *complete_arg; + u8 mdaa; +}; + +struct port100_tg_comm_rf_cmd { + __le16 guard_time; + __le16 send_timeout; + u8 mdaa; + u8 nfca_param[6]; + u8 nfcf_param[18]; + u8 mf_halted; + u8 arae_flag; + __le16 recv_timeout; + u8 data[]; +} __packed; + +struct port100_tg_comm_rf_res { + u8 comm_type; + u8 ar_status; + u8 target_activated; + __le32 status; + u8 data[]; +} __packed; + +/* The rule: value + checksum = 0 */ +static inline u8 port100_checksum(u16 value) +{ + return ~(((u8 *)&value)[0] + ((u8 *)&value)[1]) + 1; +} + +/* The rule: sum(data elements) + checksum = 0 */ +static u8 port100_data_checksum(u8 *data, int datalen) +{ + u8 sum = 0; + int i; + + for (i = 0; i < datalen; i++) + sum += data[i]; + + return port100_checksum(sum); +} + +static void port100_tx_frame_init(void *_frame, u8 cmd_code) +{ + struct port100_frame *frame = _frame; + + frame->preamble = 0; + frame->start_frame = cpu_to_be16(PORT100_FRAME_SOF); + frame->extended_frame = cpu_to_be16(PORT100_FRAME_EXT); + PORT100_FRAME_DIRECTION(frame) = PORT100_FRAME_DIR_OUT; + PORT100_FRAME_CMD(frame) = cmd_code; + frame->datalen = cpu_to_le16(2); +} + +static void port100_tx_frame_finish(void *_frame) +{ + struct port100_frame *frame = _frame; + + frame->datalen_checksum = port100_checksum(le16_to_cpu(frame->datalen)); + + PORT100_FRAME_CHECKSUM(frame) = + port100_data_checksum(frame->data, le16_to_cpu(frame->datalen)); + + PORT100_FRAME_POSTAMBLE(frame) = 0; +} + +static void port100_tx_update_payload_len(void *_frame, int len) +{ + struct port100_frame *frame = _frame; + + frame->datalen = cpu_to_le16(le16_to_cpu(frame->datalen) + len); +} + +static bool port100_rx_frame_is_valid(void *_frame) +{ + u8 checksum; + struct port100_frame *frame = _frame; + + if (frame->start_frame != cpu_to_be16(PORT100_FRAME_SOF) || + frame->extended_frame != cpu_to_be16(PORT100_FRAME_EXT)) + return false; + + checksum = port100_checksum(le16_to_cpu(frame->datalen)); + if (checksum != frame->datalen_checksum) + return false; + + checksum = port100_data_checksum(frame->data, + le16_to_cpu(frame->datalen)); + if (checksum != PORT100_FRAME_CHECKSUM(frame)) + return false; + + return true; +} + +static bool port100_rx_frame_is_ack(struct port100_ack_frame *frame) +{ + return (frame->start_frame == cpu_to_be16(PORT100_FRAME_SOF) && + frame->ack_frame == cpu_to_be16(PORT100_FRAME_ACK)); +} + +static inline int port100_rx_frame_size(void *frame) +{ + struct port100_frame *f = frame; + + return sizeof(struct port100_frame) + le16_to_cpu(f->datalen) + + PORT100_FRAME_TAIL_LEN; +} + +static bool port100_rx_frame_is_cmd_response(struct port100 *dev, void *frame) +{ + struct port100_frame *f = frame; + + return (PORT100_FRAME_CMD(f) == PORT100_CMD_RESPONSE(dev->cmd->code)); +} + +static void port100_recv_response(struct urb *urb) +{ + struct port100 *dev = urb->context; + struct port100_cmd *cmd = dev->cmd; + u8 *in_frame; + + cmd->status = urb->status; + + switch (urb->status) { + case 0: + break; /* success */ + case -ECONNRESET: + case -ENOENT: + nfc_err(&dev->interface->dev, + "The urb has been canceled (status %d)", urb->status); + goto sched_wq; + case -ESHUTDOWN: + default: + nfc_err(&dev->interface->dev, "Urb failure (status %d)", + urb->status); + goto sched_wq; + } + + in_frame = dev->in_urb->transfer_buffer; + + if (!port100_rx_frame_is_valid(in_frame)) { + nfc_err(&dev->interface->dev, "Received an invalid frame"); + cmd->status = -EIO; + goto sched_wq; + } + + print_hex_dump_debug("PORT100 RX: ", DUMP_PREFIX_NONE, 16, 1, in_frame, + port100_rx_frame_size(in_frame), false); + + if (!port100_rx_frame_is_cmd_response(dev, in_frame)) { + nfc_err(&dev->interface->dev, + "It's not the response to the last command"); + cmd->status = -EIO; + goto sched_wq; + } + +sched_wq: + schedule_work(&dev->cmd_complete_work); +} + +static int port100_submit_urb_for_response(struct port100 *dev, gfp_t flags) +{ + dev->in_urb->complete = port100_recv_response; + + return usb_submit_urb(dev->in_urb, flags); +} + +static void port100_recv_ack(struct urb *urb) +{ + struct port100 *dev = urb->context; + struct port100_cmd *cmd = dev->cmd; + struct port100_ack_frame *in_frame; + int rc; + + cmd->status = urb->status; + + switch (urb->status) { + case 0: + break; /* success */ + case -ECONNRESET: + case -ENOENT: + nfc_err(&dev->interface->dev, + "The urb has been stopped (status %d)", urb->status); + goto sched_wq; + case -ESHUTDOWN: + default: + nfc_err(&dev->interface->dev, "Urb failure (status %d)", + urb->status); + goto sched_wq; + } + + in_frame = dev->in_urb->transfer_buffer; + + if (!port100_rx_frame_is_ack(in_frame)) { + nfc_err(&dev->interface->dev, "Received an invalid ack"); + cmd->status = -EIO; + goto sched_wq; + } + + rc = port100_submit_urb_for_response(dev, GFP_ATOMIC); + if (rc) { + nfc_err(&dev->interface->dev, + "usb_submit_urb failed with result %d", rc); + cmd->status = rc; + goto sched_wq; + } + + return; + +sched_wq: + schedule_work(&dev->cmd_complete_work); +} + +static int port100_submit_urb_for_ack(struct port100 *dev, gfp_t flags) +{ + dev->in_urb->complete = port100_recv_ack; + + return usb_submit_urb(dev->in_urb, flags); +} + +static int port100_send_ack(struct port100 *dev) +{ + int rc; + + dev->out_urb->transfer_buffer = ack_frame; + dev->out_urb->transfer_buffer_length = sizeof(ack_frame); + rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); + + return rc; +} + +static int port100_send_frame_async(struct port100 *dev, struct sk_buff *out, + struct sk_buff *in, int in_len) +{ + int rc; + + dev->out_urb->transfer_buffer = out->data; + dev->out_urb->transfer_buffer_length = out->len; + + dev->in_urb->transfer_buffer = in->data; + dev->in_urb->transfer_buffer_length = in_len; + + print_hex_dump_debug("PORT100 TX: ", DUMP_PREFIX_NONE, 16, 1, + out->data, out->len, false); + + rc = usb_submit_urb(dev->out_urb, GFP_KERNEL); + if (rc) + return rc; + + rc = port100_submit_urb_for_ack(dev, GFP_KERNEL); + if (rc) + goto error; + + return 0; + +error: + usb_unlink_urb(dev->out_urb); + return rc; +} + +static void port100_build_cmd_frame(struct port100 *dev, u8 cmd_code, + struct sk_buff *skb) +{ + /* payload is already there, just update datalen */ + int payload_len = skb->len; + + skb_push(skb, PORT100_FRAME_HEADER_LEN); + skb_put(skb, PORT100_FRAME_TAIL_LEN); + + port100_tx_frame_init(skb->data, cmd_code); + port100_tx_update_payload_len(skb->data, payload_len); + port100_tx_frame_finish(skb->data); +} + +static void port100_send_async_complete(struct port100 *dev) +{ + struct port100_cmd *cmd = dev->cmd; + int status = cmd->status; + + struct sk_buff *req = cmd->req; + struct sk_buff *resp = cmd->resp; + + dev_kfree_skb(req); + + dev->cmd = NULL; + + if (status < 0) { + cmd->complete_cb(dev, cmd->complete_cb_context, + ERR_PTR(status)); + dev_kfree_skb(resp); + goto done; + } + + skb_put(resp, port100_rx_frame_size(resp->data)); + skb_pull(resp, PORT100_FRAME_HEADER_LEN); + skb_trim(resp, resp->len - PORT100_FRAME_TAIL_LEN); + + cmd->complete_cb(dev, cmd->complete_cb_context, resp); + +done: + kfree(cmd); +} + +static int port100_send_cmd_async(struct port100 *dev, u8 cmd_code, + struct sk_buff *req, + port100_send_async_complete_t complete_cb, + void *complete_cb_context) +{ + struct port100_cmd *cmd; + struct sk_buff *resp; + int rc; + int resp_len = PORT100_FRAME_HEADER_LEN + + PORT100_FRAME_MAX_PAYLOAD_LEN + + PORT100_FRAME_TAIL_LEN; + + resp = alloc_skb(resp_len, GFP_KERNEL); + if (!resp) + return -ENOMEM; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + dev_kfree_skb(resp); + return -ENOMEM; + } + + cmd->code = cmd_code; + cmd->req = req; + cmd->resp = resp; + cmd->resp_len = resp_len; + cmd->complete_cb = complete_cb; + cmd->complete_cb_context = complete_cb_context; + + port100_build_cmd_frame(dev, cmd_code, req); + + dev->cmd = cmd; + + rc = port100_send_frame_async(dev, req, resp, resp_len); + if (rc) { + kfree(cmd); + dev_kfree_skb(resp); + dev->cmd = NULL; + } + + return rc; +} + +struct port100_sync_cmd_response { + struct sk_buff *resp; + struct completion done; +}; + +static void port100_wq_cmd_complete(struct work_struct *work) +{ + struct port100 *dev = container_of(work, struct port100, + cmd_complete_work); + + port100_send_async_complete(dev); +} + +static void port100_send_sync_complete(struct port100 *dev, void *_arg, + struct sk_buff *resp) +{ + struct port100_sync_cmd_response *arg = _arg; + + arg->resp = resp; + complete(&arg->done); +} + +static struct sk_buff *port100_send_cmd_sync(struct port100 *dev, u8 cmd_code, + struct sk_buff *req) +{ + int rc; + struct port100_sync_cmd_response arg; + + init_completion(&arg.done); + + rc = port100_send_cmd_async(dev, cmd_code, req, + port100_send_sync_complete, &arg); + if (rc) { + dev_kfree_skb(req); + return ERR_PTR(rc); + } + + wait_for_completion(&arg.done); + + return arg.resp; +} + +static void port100_send_complete(struct urb *urb) +{ + struct port100 *dev = urb->context; + + switch (urb->status) { + case 0: + break; /* success */ + case -ECONNRESET: + case -ENOENT: + nfc_err(&dev->interface->dev, + "The urb has been stopped (status %d)", urb->status); + break; + case -ESHUTDOWN: + default: + nfc_err(&dev->interface->dev, "Urb failure (status %d)", + urb->status); + } +} + +static void port100_abort_cmd(struct nfc_digital_dev *ddev) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + + /* An ack will cancel the last issued command */ + port100_send_ack(dev); + + /* cancel the urb request */ + usb_kill_urb(dev->in_urb); +} + +static struct sk_buff *port100_alloc_skb(struct port100 *dev, unsigned int size) +{ + struct sk_buff *skb; + + skb = alloc_skb(dev->skb_headroom + dev->skb_tailroom + size, + GFP_KERNEL); + if (skb) + skb_reserve(skb, dev->skb_headroom); + + return skb; +} + +static int port100_set_command_type(struct port100 *dev, u8 command_type) +{ + struct sk_buff *skb; + struct sk_buff *resp; + int rc; + + skb = port100_alloc_skb(dev, 1); + if (!skb) + return -ENOMEM; + + *skb_put(skb, sizeof(u8)) = command_type; + + resp = port100_send_cmd_sync(dev, PORT100_CMD_SET_COMMAND_TYPE, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static u64 port100_get_command_type_mask(struct port100 *dev) +{ + struct sk_buff *skb; + struct sk_buff *resp; + u64 mask; + + skb = port100_alloc_skb(dev, 0); + if (!skb) + return -ENOMEM; + + resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_COMMAND_TYPE, skb); + if (IS_ERR(resp)) + return PTR_ERR(resp); + + if (resp->len < 8) + mask = 0; + else + mask = be64_to_cpu(*(__be64 *)resp->data); + + dev_kfree_skb(resp); + + return mask; +} + +static u16 port100_get_firmware_version(struct port100 *dev) +{ + struct sk_buff *skb; + struct sk_buff *resp; + u16 fw_ver; + + skb = port100_alloc_skb(dev, 0); + if (!skb) + return 0; + + resp = port100_send_cmd_sync(dev, PORT100_CMD_GET_FIRMWARE_VERSION, + skb); + if (IS_ERR(resp)) + return 0; + + fw_ver = le16_to_cpu(*(__le16 *)resp->data); + + dev_kfree_skb(resp); + + return fw_ver; +} + +static int port100_switch_rf(struct nfc_digital_dev *ddev, bool on) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct sk_buff *skb, *resp; + + skb = port100_alloc_skb(dev, 1); + if (!skb) + return -ENOMEM; + + *skb_put(skb, 1) = on ? 1 : 0; + + resp = port100_send_cmd_sync(dev, PORT100_CMD_SWITCH_RF, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + dev_kfree_skb(resp); + + return 0; +} + +static int port100_in_set_rf(struct nfc_digital_dev *ddev, u8 rf) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct sk_buff *skb; + struct sk_buff *resp; + int rc; + + if (rf >= NFC_DIGITAL_RF_TECH_LAST) + return -EINVAL; + + skb = port100_alloc_skb(dev, sizeof(struct port100_in_rf_setting)); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, sizeof(struct port100_in_rf_setting)), + &in_rf_settings[rf], + sizeof(struct port100_in_rf_setting)); + + resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_RF, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static int port100_in_set_framing(struct nfc_digital_dev *ddev, int param) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_protocol *protocols; + struct sk_buff *skb; + struct sk_buff *resp; + int num_protocols; + size_t size; + int rc; + + if (param >= NFC_DIGITAL_FRAMING_LAST) + return -EINVAL; + + protocols = in_protocols[param]; + + num_protocols = 0; + while (protocols[num_protocols].number != PORT100_IN_PROT_END) + num_protocols++; + + if (!num_protocols) + return 0; + + size = sizeof(struct port100_protocol) * num_protocols; + + skb = port100_alloc_skb(dev, size); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, size), protocols, size); + + resp = port100_send_cmd_sync(dev, PORT100_CMD_IN_SET_PROTOCOL, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static int port100_in_configure_hw(struct nfc_digital_dev *ddev, int type, + int param) +{ + if (type == NFC_DIGITAL_CONFIG_RF_TECH) + return port100_in_set_rf(ddev, param); + + if (type == NFC_DIGITAL_CONFIG_FRAMING) + return port100_in_set_framing(ddev, param); + + return -EINVAL; +} + +static void port100_in_comm_rf_complete(struct port100 *dev, void *arg, + struct sk_buff *resp) +{ + struct port100_cb_arg *cb_arg = arg; + nfc_digital_cmd_complete_t cb = cb_arg->complete_cb; + u32 status; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + goto exit; + } + + if (resp->len < 4) { + nfc_err(&dev->interface->dev, + "Invalid packet length received.\n"); + rc = -EIO; + goto error; + } + + status = le32_to_cpu(*(__le32 *)resp->data); + + skb_pull(resp, sizeof(u32)); + + if (status == PORT100_CMD_STATUS_TIMEOUT) { + rc = -ETIMEDOUT; + goto error; + } + + if (status != PORT100_CMD_STATUS_OK) { + nfc_err(&dev->interface->dev, + "in_comm_rf failed with status 0x%08x\n", status); + rc = -EIO; + goto error; + } + + /* Remove collision bits byte */ + skb_pull(resp, 1); + + goto exit; + +error: + kfree_skb(resp); + resp = ERR_PTR(rc); + +exit: + cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp); + + kfree(cb_arg); +} + +static int port100_in_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 _timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_cb_arg *cb_arg; + __le16 timeout; + + cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL); + if (!cb_arg) + return -ENOMEM; + + cb_arg->complete_cb = cb; + cb_arg->complete_arg = arg; + + timeout = cpu_to_le16(_timeout * 10); + + memcpy(skb_push(skb, sizeof(__le16)), &timeout, sizeof(__le16)); + + return port100_send_cmd_async(dev, PORT100_CMD_IN_COMM_RF, skb, + port100_in_comm_rf_complete, cb_arg); +} + +static int port100_tg_set_rf(struct nfc_digital_dev *ddev, u8 rf) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct sk_buff *skb; + struct sk_buff *resp; + int rc; + + if (rf >= NFC_DIGITAL_RF_TECH_LAST) + return -EINVAL; + + skb = port100_alloc_skb(dev, sizeof(struct port100_tg_rf_setting)); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, sizeof(struct port100_tg_rf_setting)), + &tg_rf_settings[rf], + sizeof(struct port100_tg_rf_setting)); + + resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_RF, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static int port100_tg_set_framing(struct nfc_digital_dev *ddev, int param) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_protocol *protocols; + struct sk_buff *skb; + struct sk_buff *resp; + int rc; + int num_protocols; + size_t size; + + if (param >= NFC_DIGITAL_FRAMING_LAST) + return -EINVAL; + + protocols = tg_protocols[param]; + + num_protocols = 0; + while (protocols[num_protocols].number != PORT100_TG_PROT_END) + num_protocols++; + + if (!num_protocols) + return 0; + + size = sizeof(struct port100_protocol) * num_protocols; + + skb = port100_alloc_skb(dev, size); + if (!skb) + return -ENOMEM; + + memcpy(skb_put(skb, size), protocols, size); + + resp = port100_send_cmd_sync(dev, PORT100_CMD_TG_SET_PROTOCOL, skb); + + if (IS_ERR(resp)) + return PTR_ERR(resp); + + rc = resp->data[0]; + + dev_kfree_skb(resp); + + return rc; +} + +static int port100_tg_configure_hw(struct nfc_digital_dev *ddev, int type, + int param) +{ + if (type == NFC_DIGITAL_CONFIG_RF_TECH) + return port100_tg_set_rf(ddev, param); + + if (type == NFC_DIGITAL_CONFIG_FRAMING) + return port100_tg_set_framing(ddev, param); + + return -EINVAL; +} + +static bool port100_tg_target_activated(struct port100 *dev, u8 tgt_activated) +{ + u8 mask; + + switch (dev->cmd_type) { + case PORT100_CMD_TYPE_0: + mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK; + break; + case PORT100_CMD_TYPE_1: + mask = PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK | + PORT100_MDAA_TGT_WAS_ACTIVATED_MASK; + break; + default: + nfc_err(&dev->interface->dev, "Unknonwn command type.\n"); + return false; + } + + return ((tgt_activated & mask) == mask); +} + +static void port100_tg_comm_rf_complete(struct port100 *dev, void *arg, + struct sk_buff *resp) +{ + u32 status; + struct port100_cb_arg *cb_arg = arg; + nfc_digital_cmd_complete_t cb = cb_arg->complete_cb; + struct port100_tg_comm_rf_res *hdr; + + if (IS_ERR(resp)) + goto exit; + + hdr = (struct port100_tg_comm_rf_res *)resp->data; + + status = le32_to_cpu(hdr->status); + + if (cb_arg->mdaa && + !port100_tg_target_activated(dev, hdr->target_activated)) { + kfree_skb(resp); + resp = ERR_PTR(-ETIMEDOUT); + + goto exit; + } + + skb_pull(resp, sizeof(struct port100_tg_comm_rf_res)); + + if (status != PORT100_CMD_STATUS_OK) { + kfree_skb(resp); + + if (status == PORT100_CMD_STATUS_TIMEOUT) + resp = ERR_PTR(-ETIMEDOUT); + else + resp = ERR_PTR(-EIO); + } + +exit: + cb(dev->nfc_digital_dev, cb_arg->complete_arg, resp); + + kfree(cb_arg); +} + +static int port100_tg_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_tg_comm_rf_cmd *hdr; + struct port100_cb_arg *cb_arg; + + cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL); + if (!cb_arg) + return -ENOMEM; + + cb_arg->complete_cb = cb; + cb_arg->complete_arg = arg; + + skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd)); + + hdr = (struct port100_tg_comm_rf_cmd *)skb->data; + + memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd)); + hdr->guard_time = cpu_to_le16(500); + hdr->send_timeout = cpu_to_le16(0xFFFF); + hdr->recv_timeout = cpu_to_le16(timeout); + + return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb, + port100_tg_comm_rf_complete, cb_arg); +} + +static int port100_listen_mdaa(struct nfc_digital_dev *ddev, + struct digital_tg_mdaa_params *params, + u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct port100_tg_comm_rf_cmd *hdr; + struct port100_cb_arg *cb_arg; + struct sk_buff *skb; + int rc; + + rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, + NFC_DIGITAL_RF_TECH_106A); + if (rc) + return rc; + + rc = port100_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_NFC_DEP); + if (rc) + return rc; + + cb_arg = kzalloc(sizeof(struct port100_cb_arg), GFP_KERNEL); + if (!cb_arg) + return -ENOMEM; + + cb_arg->complete_cb = cb; + cb_arg->complete_arg = arg; + cb_arg->mdaa = 1; + + skb = port100_alloc_skb(dev, 0); + if (!skb) { + kfree(cb_arg); + return -ENOMEM; + } + + skb_push(skb, sizeof(struct port100_tg_comm_rf_cmd)); + hdr = (struct port100_tg_comm_rf_cmd *)skb->data; + + memset(hdr, 0, sizeof(struct port100_tg_comm_rf_cmd)); + + hdr->guard_time = 0; + hdr->send_timeout = cpu_to_le16(0xFFFF); + hdr->mdaa = 1; + hdr->nfca_param[0] = (params->sens_res >> 8) & 0xFF; + hdr->nfca_param[1] = params->sens_res & 0xFF; + memcpy(hdr->nfca_param + 2, params->nfcid1, 3); + hdr->nfca_param[5] = params->sel_res; + memcpy(hdr->nfcf_param, params->nfcid2, 8); + hdr->nfcf_param[16] = (params->sc >> 8) & 0xFF; + hdr->nfcf_param[17] = params->sc & 0xFF; + hdr->recv_timeout = cpu_to_le16(timeout); + + return port100_send_cmd_async(dev, PORT100_CMD_TG_COMM_RF, skb, + port100_tg_comm_rf_complete, cb_arg); +} + +static int port100_listen(struct nfc_digital_dev *ddev, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + struct port100 *dev = nfc_digital_get_drvdata(ddev); + struct sk_buff *skb; + + skb = port100_alloc_skb(dev, 0); + if (!skb) + return -ENOMEM; + + return port100_tg_send_cmd(ddev, skb, timeout, cb, arg); +} + +static struct nfc_digital_ops port100_digital_ops = { + .in_configure_hw = port100_in_configure_hw, + .in_send_cmd = port100_in_send_cmd, + + .tg_listen_mdaa = port100_listen_mdaa, + .tg_listen = port100_listen, + .tg_configure_hw = port100_tg_configure_hw, + .tg_send_cmd = port100_tg_send_cmd, + + .switch_rf = port100_switch_rf, + .abort_cmd = port100_abort_cmd, +}; + +static const struct usb_device_id port100_table[] = { + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE, + .idVendor = SONY_VENDOR_ID, + .idProduct = RCS380_PRODUCT_ID, + }, + { } +}; +MODULE_DEVICE_TABLE(usb, port100_table); + +static int port100_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct port100 *dev; + int rc; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int in_endpoint; + int out_endpoint; + u16 fw_version; + u64 cmd_type_mask; + int i; + + dev = devm_kzalloc(&interface->dev, sizeof(struct port100), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + dev->interface = interface; + usb_set_intfdata(interface, dev); + + in_endpoint = out_endpoint = 0; + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!in_endpoint && usb_endpoint_is_bulk_in(endpoint)) + in_endpoint = endpoint->bEndpointAddress; + + if (!out_endpoint && usb_endpoint_is_bulk_out(endpoint)) + out_endpoint = endpoint->bEndpointAddress; + } + + if (!in_endpoint || !out_endpoint) { + nfc_err(&interface->dev, + "Could not find bulk-in or bulk-out endpoint\n"); + rc = -ENODEV; + goto error; + } + + dev->in_urb = usb_alloc_urb(0, GFP_KERNEL); + dev->out_urb = usb_alloc_urb(0, GFP_KERNEL); + + if (!dev->in_urb || !dev->out_urb) { + nfc_err(&interface->dev, "Could not allocate USB URBs\n"); + rc = -ENOMEM; + goto error; + } + + usb_fill_bulk_urb(dev->in_urb, dev->udev, + usb_rcvbulkpipe(dev->udev, in_endpoint), + NULL, 0, NULL, dev); + usb_fill_bulk_urb(dev->out_urb, dev->udev, + usb_sndbulkpipe(dev->udev, out_endpoint), + NULL, 0, port100_send_complete, dev); + + dev->skb_headroom = PORT100_FRAME_HEADER_LEN + + PORT100_COMM_RF_HEAD_MAX_LEN; + dev->skb_tailroom = PORT100_FRAME_TAIL_LEN; + + INIT_WORK(&dev->cmd_complete_work, port100_wq_cmd_complete); + + /* The first thing to do with the Port-100 is to set the command type + * to be used. If supported we use command type 1. 0 otherwise. + */ + cmd_type_mask = port100_get_command_type_mask(dev); + if (!cmd_type_mask) { + nfc_err(&interface->dev, + "Could not get supported command types.\n"); + rc = -ENODEV; + goto error; + } + + if (PORT100_CMD_TYPE_IS_SUPPORTED(cmd_type_mask, PORT100_CMD_TYPE_1)) + dev->cmd_type = PORT100_CMD_TYPE_1; + else + dev->cmd_type = PORT100_CMD_TYPE_0; + + rc = port100_set_command_type(dev, dev->cmd_type); + if (rc) { + nfc_err(&interface->dev, + "The device does not support command type %u.\n", + dev->cmd_type); + goto error; + } + + fw_version = port100_get_firmware_version(dev); + if (!fw_version) + nfc_err(&interface->dev, + "Could not get device firmware version.\n"); + + nfc_info(&interface->dev, + "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n", + (fw_version & 0xFF00) >> 8, fw_version & 0xFF); + + dev->nfc_digital_dev = nfc_digital_allocate_device(&port100_digital_ops, + PORT100_PROTOCOLS, + PORT100_CAPABILITIES, + dev->skb_headroom, + dev->skb_tailroom); + if (!dev->nfc_digital_dev) { + nfc_err(&interface->dev, + "Could not allocate nfc_digital_dev.\n"); + rc = -ENOMEM; + goto error; + } + + nfc_digital_set_parent_dev(dev->nfc_digital_dev, &interface->dev); + nfc_digital_set_drvdata(dev->nfc_digital_dev, dev); + + rc = nfc_digital_register_device(dev->nfc_digital_dev); + if (rc) { + nfc_err(&interface->dev, + "Could not register digital device.\n"); + goto free_nfc_dev; + } + + return 0; + +free_nfc_dev: + nfc_digital_free_device(dev->nfc_digital_dev); + +error: + usb_free_urb(dev->in_urb); + usb_free_urb(dev->out_urb); + usb_put_dev(dev->udev); + + return rc; +} + +static void port100_disconnect(struct usb_interface *interface) +{ + struct port100 *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + nfc_digital_unregister_device(dev->nfc_digital_dev); + nfc_digital_free_device(dev->nfc_digital_dev); + + usb_kill_urb(dev->in_urb); + usb_kill_urb(dev->out_urb); + + usb_free_urb(dev->in_urb); + usb_free_urb(dev->out_urb); + + kfree(dev->cmd); + + nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected"); +} + +static struct usb_driver port100_driver = { + .name = "port100", + .probe = port100_probe, + .disconnect = port100_disconnect, + .id_table = port100_table, +}; + +module_usb_driver(port100_driver); + +MODULE_DESCRIPTION("NFC Port-100 series usb driver ver " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index d7b66a28fe75577dd6ccaef41619eb19b327df34..eb4e1f809feb38087efeec241686254342f70514 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4451,7 +4451,7 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata) struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; struct qeth_snmp_ureq *ureq; - int req_len; + unsigned int req_len; struct qeth_arp_query_info qinfo = {0, }; int rc = 0; @@ -4467,6 +4467,10 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata) /* skip 4 bytes (data_len struct member) to get req_len */ if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int))) return -EFAULT; + if (req_len > (QETH_BUFSIZE - IPA_PDU_HEADER_SIZE - + sizeof(struct qeth_ipacmd_hdr) - + sizeof(struct qeth_ipacmd_setadpparms_hdr))) + return -EINVAL; ureq = memdup_user(udata, req_len + sizeof(struct qeth_snmp_ureq_hdr)); if (IS_ERR(ureq)) { QETH_CARD_TEXT(card, 2, "snmpnome"); diff --git a/include/linux/crc32.h b/include/linux/crc32.h index 68267b64bb983cd103a34f499f6474ef5c4ef2e3..7d275c4fc01108f508edc2da38a6c60500cd331c 100644 --- a/include/linux/crc32.h +++ b/include/linux/crc32.h @@ -11,8 +11,48 @@ extern u32 crc32_le(u32 crc, unsigned char const *p, size_t len); extern u32 crc32_be(u32 crc, unsigned char const *p, size_t len); +/** + * crc32_le_combine - Combine two crc32 check values into one. For two + * sequences of bytes, seq1 and seq2 with lengths len1 + * and len2, crc32_le() check values were calculated + * for each, crc1 and crc2. + * + * @crc1: crc32 of the first block + * @crc2: crc32 of the second block + * @len2: length of the second block + * + * Return: The crc32_le() check value of seq1 and seq2 concatenated, + * requiring only crc1, crc2, and len2. Note: If seq_full denotes + * the concatenated memory area of seq1 with seq2, and crc_full + * the crc32_le() value of seq_full, then crc_full == + * crc32_le_combine(crc1, crc2, len2) when crc_full was seeded + * with the same initializer as crc1, and crc2 seed was 0. See + * also crc32_combine_test(). + */ +extern u32 crc32_le_combine(u32 crc1, u32 crc2, size_t len2); + extern u32 __crc32c_le(u32 crc, unsigned char const *p, size_t len); +/** + * __crc32c_le_combine - Combine two crc32c check values into one. For two + * sequences of bytes, seq1 and seq2 with lengths len1 + * and len2, __crc32c_le() check values were calculated + * for each, crc1 and crc2. + * + * @crc1: crc32c of the first block + * @crc2: crc32c of the second block + * @len2: length of the second block + * + * Return: The __crc32c_le() check value of seq1 and seq2 concatenated, + * requiring only crc1, crc2, and len2. Note: If seq_full denotes + * the concatenated memory area of seq1 with seq2, and crc_full + * the __crc32c_le() value of seq_full, then crc_full == + * __crc32c_le_combine(crc1, crc2, len2) when crc_full was + * seeded with the same initializer as crc1, and crc2 seed + * was 0. See also crc32c_combine_test(). + */ +extern u32 __crc32c_le_combine(u32 crc1, u32 crc2, size_t len2); + #define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)(data), length) /* diff --git a/include/linux/etherdevice.h b/include/linux/etherdevice.h index d8b512496e50c155e99712f5226e115159beabc1..fc4a9aa7dd82c7a26e69ac21cce27bdc399dc9b1 100644 --- a/include/linux/etherdevice.h +++ b/include/linux/etherdevice.h @@ -28,27 +28,24 @@ #include #ifdef __KERNEL__ -extern __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev); +__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev); extern const struct header_ops eth_header_ops; -extern int eth_header(struct sk_buff *skb, struct net_device *dev, - unsigned short type, - const void *daddr, const void *saddr, unsigned len); -extern int eth_rebuild_header(struct sk_buff *skb); -extern int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr); -extern int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, __be16 type); -extern void eth_header_cache_update(struct hh_cache *hh, - const struct net_device *dev, - const unsigned char *haddr); -extern int eth_prepare_mac_addr_change(struct net_device *dev, void *p); -extern void eth_commit_mac_addr_change(struct net_device *dev, void *p); -extern int eth_mac_addr(struct net_device *dev, void *p); -extern int eth_change_mtu(struct net_device *dev, int new_mtu); -extern int eth_validate_addr(struct net_device *dev); - - - -extern struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs, +int eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, + const void *daddr, const void *saddr, unsigned len); +int eth_rebuild_header(struct sk_buff *skb); +int eth_header_parse(const struct sk_buff *skb, unsigned char *haddr); +int eth_header_cache(const struct neighbour *neigh, struct hh_cache *hh, + __be16 type); +void eth_header_cache_update(struct hh_cache *hh, const struct net_device *dev, + const unsigned char *haddr); +int eth_prepare_mac_addr_change(struct net_device *dev, void *p); +void eth_commit_mac_addr_change(struct net_device *dev, void *p); +int eth_mac_addr(struct net_device *dev, void *p); +int eth_change_mtu(struct net_device *dev, int new_mtu); +int eth_validate_addr(struct net_device *dev); + +struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs, unsigned int rxqs); #define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1) #define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count) diff --git a/include/linux/fcdevice.h b/include/linux/fcdevice.h index e460ef8319841dd83d6f6f1eb8653dfd5452d6e0..5009fa16b5d8f08369ccdcb6537bdeb426598d0a 100644 --- a/include/linux/fcdevice.h +++ b/include/linux/fcdevice.h @@ -27,7 +27,7 @@ #include #ifdef __KERNEL__ -extern struct net_device *alloc_fcdev(int sizeof_priv); +struct net_device *alloc_fcdev(int sizeof_priv); #endif #endif /* _LINUX_FCDEVICE_H */ diff --git a/include/linux/fddidevice.h b/include/linux/fddidevice.h index 155bafd9e886607f5ee6b9bd824c2a1fa8ec6c32..9a79f0106da1a66ea0f7f647468fc3fd5c351108 100644 --- a/include/linux/fddidevice.h +++ b/include/linux/fddidevice.h @@ -25,10 +25,9 @@ #include #ifdef __KERNEL__ -extern __be16 fddi_type_trans(struct sk_buff *skb, - struct net_device *dev); -extern int fddi_change_mtu(struct net_device *dev, int new_mtu); -extern struct net_device *alloc_fddidev(int sizeof_priv); +__be16 fddi_type_trans(struct sk_buff *skb, struct net_device *dev); +int fddi_change_mtu(struct net_device *dev, int new_mtu); +struct net_device *alloc_fddidev(int sizeof_priv); #endif #endif /* _LINUX_FDDIDEVICE_H */ diff --git a/include/linux/hippidevice.h b/include/linux/hippidevice.h index f148e49084106fca84ae61b2880420dc0bc2798d..8ec23fb0b412290c767ab444b564029911ba7786 100644 --- a/include/linux/hippidevice.h +++ b/include/linux/hippidevice.h @@ -31,11 +31,11 @@ struct hippi_cb { __u32 ifield; }; -extern __be16 hippi_type_trans(struct sk_buff *skb, struct net_device *dev); -extern int hippi_change_mtu(struct net_device *dev, int new_mtu); -extern int hippi_mac_addr(struct net_device *dev, void *p); -extern int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p); -extern struct net_device *alloc_hippi_dev(int sizeof_priv); +__be16 hippi_type_trans(struct sk_buff *skb, struct net_device *dev); +int hippi_change_mtu(struct net_device *dev, int new_mtu); +int hippi_mac_addr(struct net_device *dev, void *p); +int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p); +struct net_device *alloc_hippi_dev(int sizeof_priv); #endif #endif /* _LINUX_HIPPIDEVICE_H */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index a5b598a79becb99eb3554886cbec7708ee426309..8c3b26a215745b755a5018764e4c0ca1f1bd52b7 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -696,6 +696,18 @@ struct ieee80211_sec_chan_offs_ie { u8 sec_chan_offs; } __packed; +/** + * struct ieee80211_mesh_chansw_params_ie - mesh channel switch parameters IE + * + * This structure represents the "Mesh Channel Switch Paramters element" + */ +struct ieee80211_mesh_chansw_params_ie { + u8 mesh_ttl; + u8 mesh_flags; + __le16 mesh_reason; + __le16 mesh_pre_value; +} __packed; + /** * struct ieee80211_wide_bw_chansw_ie - wide bandwidth channel switch IE */ @@ -750,6 +762,14 @@ enum mesh_config_capab_flags { IEEE80211_MESHCONF_CAPAB_POWER_SAVE_LEVEL = 0x40, }; +/** + * mesh channel switch parameters element's flag indicator + * + */ +#define WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT BIT(0) +#define WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR BIT(1) +#define WLAN_EID_CHAN_SWITCH_PARAM_REASON BIT(2) + /** * struct ieee80211_rann_ie * @@ -1391,8 +1411,8 @@ struct ieee80211_vht_operation { #define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700 #define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800 #define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000 -#define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX 0x00006000 -#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00030000 +#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX 0x0000e000 +#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00070000 #define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000 #define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000 #define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000 diff --git a/include/linux/if_macvlan.h b/include/linux/if_macvlan.h index ddd33fd5904dbb32d428c449543ecc6e3dc8a010..c2702856295ebaaa06db6dddb7106a53d2e71315 100644 --- a/include/linux/if_macvlan.h +++ b/include/linux/if_macvlan.h @@ -61,6 +61,7 @@ struct macvlan_dev { struct hlist_node hlist; struct macvlan_port *port; struct net_device *lowerdev; + void *fwd_priv; struct macvlan_pcpu_stats __percpu *pcpu_stats; DECLARE_BITMAP(mc_filter, MACVLAN_MC_FILTER_SZ); diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h index 715c343f7c0009554d430a8e9cf613f9318263a5..f252deb99454fa06e4892ab1100e41aea0968d84 100644 --- a/include/linux/if_vlan.h +++ b/include/linux/if_vlan.h @@ -89,6 +89,101 @@ extern struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, extern struct net_device *vlan_dev_real_dev(const struct net_device *dev); extern u16 vlan_dev_vlan_id(const struct net_device *dev); +/** + * struct vlan_priority_tci_mapping - vlan egress priority mappings + * @priority: skb priority + * @vlan_qos: vlan priority: (skb->priority << 13) & 0xE000 + * @next: pointer to next struct + */ +struct vlan_priority_tci_mapping { + u32 priority; + u16 vlan_qos; + struct vlan_priority_tci_mapping *next; +}; + +/** + * struct vlan_pcpu_stats - VLAN percpu rx/tx stats + * @rx_packets: number of received packets + * @rx_bytes: number of received bytes + * @rx_multicast: number of received multicast packets + * @tx_packets: number of transmitted packets + * @tx_bytes: number of transmitted bytes + * @syncp: synchronization point for 64bit counters + * @rx_errors: number of rx errors + * @tx_dropped: number of tx drops + */ +struct vlan_pcpu_stats { + u64 rx_packets; + u64 rx_bytes; + u64 rx_multicast; + u64 tx_packets; + u64 tx_bytes; + struct u64_stats_sync syncp; + u32 rx_errors; + u32 tx_dropped; +}; + +struct proc_dir_entry; +struct netpoll; + +/** + * struct vlan_dev_priv - VLAN private device data + * @nr_ingress_mappings: number of ingress priority mappings + * @ingress_priority_map: ingress priority mappings + * @nr_egress_mappings: number of egress priority mappings + * @egress_priority_map: hash of egress priority mappings + * @vlan_proto: VLAN encapsulation protocol + * @vlan_id: VLAN identifier + * @flags: device flags + * @real_dev: underlying netdevice + * @real_dev_addr: address of underlying netdevice + * @dent: proc dir entry + * @vlan_pcpu_stats: ptr to percpu rx stats + */ +struct vlan_dev_priv { + unsigned int nr_ingress_mappings; + u32 ingress_priority_map[8]; + unsigned int nr_egress_mappings; + struct vlan_priority_tci_mapping *egress_priority_map[16]; + + __be16 vlan_proto; + u16 vlan_id; + u16 flags; + + struct net_device *real_dev; + unsigned char real_dev_addr[ETH_ALEN]; + + struct proc_dir_entry *dent; + struct vlan_pcpu_stats __percpu *vlan_pcpu_stats; +#ifdef CONFIG_NET_POLL_CONTROLLER + struct netpoll *netpoll; +#endif +}; + +static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev) +{ + return netdev_priv(dev); +} + +static inline u16 +vlan_dev_get_egress_qos_mask(struct net_device *dev, u32 skprio) +{ + struct vlan_priority_tci_mapping *mp; + + smp_rmb(); /* coupled with smp_wmb() in vlan_dev_set_egress_priority() */ + + mp = vlan_dev_priv(dev)->egress_priority_map[(skprio & 0xF)]; + while (mp) { + if (mp->priority == skprio) { + return mp->vlan_qos; /* This should already be shifted + * to mask correctly with the + * VLAN's TCI */ + } + mp = mp->next; + } + return 0; +} + extern bool vlan_do_receive(struct sk_buff **skb); extern struct sk_buff *vlan_untag(struct sk_buff *skb); @@ -121,6 +216,12 @@ static inline u16 vlan_dev_vlan_id(const struct net_device *dev) return 0; } +static inline u16 vlan_dev_get_egress_qos_mask(struct net_device *dev, + u32 skprio) +{ + return 0; +} + static inline bool vlan_do_receive(struct sk_buff **skb) { return false; diff --git a/include/linux/inetdevice.h b/include/linux/inetdevice.h index 79640e015a86c6eb4a8bab2c0d2ee78eaf241558..0d678aefe69df6155b683ce039a42e4b7c4a1cab 100644 --- a/include/linux/inetdevice.h +++ b/include/linux/inetdevice.h @@ -147,25 +147,27 @@ struct in_ifaddr { unsigned long ifa_tstamp; /* updated timestamp */ }; -extern int register_inetaddr_notifier(struct notifier_block *nb); -extern int unregister_inetaddr_notifier(struct notifier_block *nb); +int register_inetaddr_notifier(struct notifier_block *nb); +int unregister_inetaddr_notifier(struct notifier_block *nb); -extern void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, - struct ipv4_devconf *devconf); +void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, + struct ipv4_devconf *devconf); -extern struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref); +struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref); static inline struct net_device *ip_dev_find(struct net *net, __be32 addr) { return __ip_dev_find(net, addr, true); } -extern int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b); -extern int devinet_ioctl(struct net *net, unsigned int cmd, void __user *); -extern void devinet_init(void); -extern struct in_device *inetdev_by_index(struct net *, int); -extern __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope); -extern __be32 inet_confirm_addr(struct in_device *in_dev, __be32 dst, __be32 local, int scope); -extern struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, __be32 mask); +int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b); +int devinet_ioctl(struct net *net, unsigned int cmd, void __user *); +void devinet_init(void); +struct in_device *inetdev_by_index(struct net *, int); +__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope); +__be32 inet_confirm_addr(struct in_device *in_dev, __be32 dst, __be32 local, + int scope); +struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, + __be32 mask); static __inline__ int inet_ifa_match(__be32 addr, struct in_ifaddr *ifa) { @@ -218,7 +220,7 @@ static inline struct in_device *__in_dev_get_rtnl(const struct net_device *dev) return rtnl_dereference(dev->ip_ptr); } -extern void in_dev_finish_destroy(struct in_device *idev); +void in_dev_finish_destroy(struct in_device *idev); static inline void in_dev_put(struct in_device *idev) { diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 28ea38439313226861c5c6df902b7b92c3cb5969..5d89d1b808a6cc896f22332c477126c3d2f2b563 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -21,13 +21,11 @@ struct ipv6_devconf { __s32 force_mld_version; __s32 mldv1_unsolicited_report_interval; __s32 mldv2_unsolicited_report_interval; -#ifdef CONFIG_IPV6_PRIVACY __s32 use_tempaddr; __s32 temp_valid_lft; __s32 temp_prefered_lft; __s32 regen_max_retry; __s32 max_desync_factor; -#endif __s32 max_addresses; __s32 accept_ra_defrtr; __s32 accept_ra_pinfo; @@ -115,16 +113,8 @@ static inline int inet6_iif(const struct sk_buff *skb) return IP6CB(skb)->iif; } -struct inet6_request_sock { - struct in6_addr loc_addr; - struct in6_addr rmt_addr; - struct sk_buff *pktopts; - int iif; -}; - struct tcp6_request_sock { struct tcp_request_sock tcp6rsk_tcp; - struct inet6_request_sock tcp6rsk_inet6; }; struct ipv6_mc_socklist; @@ -141,8 +131,6 @@ struct ipv6_fl_socklist; */ struct ipv6_pinfo { struct in6_addr saddr; - struct in6_addr rcv_saddr; - struct in6_addr daddr; struct in6_pktinfo sticky_pktinfo; const struct in6_addr *daddr_cache; #ifdef CONFIG_IPV6_SUBTREES @@ -256,48 +244,22 @@ struct tcp6_sock { extern int inet6_sk_rebuild_header(struct sock *sk); -struct inet6_timewait_sock { - struct in6_addr tw_v6_daddr; - struct in6_addr tw_v6_rcv_saddr; -}; - struct tcp6_timewait_sock { struct tcp_timewait_sock tcp6tw_tcp; - struct inet6_timewait_sock tcp6tw_inet6; }; -static inline struct inet6_timewait_sock *inet6_twsk(const struct sock *sk) -{ - return (struct inet6_timewait_sock *)(((u8 *)sk) + - inet_twsk(sk)->tw_ipv6_offset); -} - #if IS_ENABLED(CONFIG_IPV6) static inline struct ipv6_pinfo * inet6_sk(const struct sock *__sk) { return inet_sk(__sk)->pinet6; } -static inline struct inet6_request_sock * - inet6_rsk(const struct request_sock *rsk) -{ - return (struct inet6_request_sock *)(((u8 *)rsk) + - inet_rsk(rsk)->inet6_rsk_offset); -} - -static inline u32 inet6_rsk_offset(struct request_sock *rsk) -{ - return rsk->rsk_ops->obj_size - sizeof(struct inet6_request_sock); -} - static inline struct request_sock *inet6_reqsk_alloc(struct request_sock_ops *ops) { struct request_sock *req = reqsk_alloc(ops); - if (req != NULL) { - inet_rsk(req)->inet6_rsk_offset = inet6_rsk_offset(req); - inet6_rsk(req)->pktopts = NULL; - } + if (req) + inet_rsk(req)->pktopts = NULL; return req; } @@ -321,21 +283,11 @@ static inline void inet_sk_copy_descendant(struct sock *sk_to, #define __ipv6_only_sock(sk) (inet6_sk(sk)->ipv6only) #define ipv6_only_sock(sk) ((sk)->sk_family == PF_INET6 && __ipv6_only_sock(sk)) -static inline u16 inet6_tw_offset(const struct proto *prot) +static inline const struct in6_addr *inet6_rcv_saddr(const struct sock *sk) { - return prot->twsk_prot->twsk_obj_size - - sizeof(struct inet6_timewait_sock); -} - -static inline struct in6_addr *__inet6_rcv_saddr(const struct sock *sk) -{ - return likely(sk->sk_state != TCP_TIME_WAIT) ? - &inet6_sk(sk)->rcv_saddr : &inet6_twsk(sk)->tw_v6_rcv_saddr; -} - -static inline struct in6_addr *inet6_rcv_saddr(const struct sock *sk) -{ - return sk->sk_family == AF_INET6 ? __inet6_rcv_saddr(sk) : NULL; + if (sk->sk_family == AF_INET6) + return &sk->sk_v6_rcv_saddr; + return NULL; } static inline int inet_v6_ipv6only(const struct sock *sk) @@ -363,28 +315,18 @@ static inline struct raw6_sock *raw6_sk(const struct sock *sk) return NULL; } -#define __inet6_rcv_saddr(__sk) NULL #define inet6_rcv_saddr(__sk) NULL #define tcp_twsk_ipv6only(__sk) 0 #define inet_v6_ipv6only(__sk) 0 #endif /* IS_ENABLED(CONFIG_IPV6) */ #define INET6_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif) \ - ((inet_sk(__sk)->inet_portpair == (__ports)) && \ + (((__sk)->sk_portpair == (__ports)) && \ ((__sk)->sk_family == AF_INET6) && \ - ipv6_addr_equal(&inet6_sk(__sk)->daddr, (__saddr)) && \ - ipv6_addr_equal(&inet6_sk(__sk)->rcv_saddr, (__daddr)) && \ + ipv6_addr_equal(&(__sk)->sk_v6_daddr, (__saddr)) && \ + ipv6_addr_equal(&(__sk)->sk_v6_rcv_saddr, (__daddr)) && \ (!(__sk)->sk_bound_dev_if || \ ((__sk)->sk_bound_dev_if == (__dif))) && \ net_eq(sock_net(__sk), (__net))) -#define INET6_TW_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif) \ - ((inet_twsk(__sk)->tw_portpair == (__ports)) && \ - ((__sk)->sk_family == AF_INET6) && \ - ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_daddr, (__saddr)) && \ - ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_rcv_saddr, (__daddr)) && \ - (!(__sk)->sk_bound_dev_if || \ - ((__sk)->sk_bound_dev_if == (__dif))) && \ - net_eq(sock_net(__sk), (__net))) - #endif /* _IPV6_H */ diff --git a/include/linux/jump_label.h b/include/linux/jump_label.h index cf08540d62048db2a222b7ea35e8ccfe9632ece0..39999775b92221fa993120954496d15a8a1298b6 100644 --- a/include/linux/jump_label.h +++ b/include/linux/jump_label.h @@ -48,6 +48,13 @@ #include #include +#include + +extern bool static_key_initialized; + +#define STATIC_KEY_CHECK_USE() WARN(!static_key_initialized, \ + "%s used before call to jump_label_init", \ + __func__) #if defined(CC_HAVE_ASM_GOTO) && defined(CONFIG_JUMP_LABEL) @@ -128,6 +135,7 @@ struct static_key { static __always_inline void jump_label_init(void) { + static_key_initialized = true; } static __always_inline bool static_key_false(struct static_key *key) @@ -146,11 +154,13 @@ static __always_inline bool static_key_true(struct static_key *key) static inline void static_key_slow_inc(struct static_key *key) { + STATIC_KEY_CHECK_USE(); atomic_inc(&key->enabled); } static inline void static_key_slow_dec(struct static_key *key) { + STATIC_KEY_CHECK_USE(); atomic_dec(&key->enabled); } diff --git a/include/linux/jump_label_ratelimit.h b/include/linux/jump_label_ratelimit.h index 113788389b3dc25c2de62c32268da828719c7d68..089f70f83e97c9a1adf1087f10d095a02bd3e152 100644 --- a/include/linux/jump_label_ratelimit.h +++ b/include/linux/jump_label_ratelimit.h @@ -23,12 +23,14 @@ struct static_key_deferred { }; static inline void static_key_slow_dec_deferred(struct static_key_deferred *key) { + STATIC_KEY_CHECK_USE(); static_key_slow_dec(&key->key); } static inline void jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl) { + STATIC_KEY_CHECK_USE(); } #endif /* HAVE_JUMP_LABEL */ #endif /* _LINUX_JUMP_LABEL_RATELIMIT_H */ diff --git a/include/linux/mlx4/cmd.h b/include/linux/mlx4/cmd.h index cd1fdf75103b7bd26350490cffc7de8e054bc366..8df61bc5da00ff9d751727c42ee93b2c6ad7de9f 100644 --- a/include/linux/mlx4/cmd.h +++ b/include/linux/mlx4/cmd.h @@ -154,10 +154,6 @@ enum { MLX4_CMD_QUERY_IF_STAT = 0X54, MLX4_CMD_SET_IF_STAT = 0X55, - /* set port opcode modifiers */ - MLX4_SET_PORT_PRIO2TC = 0x8, - MLX4_SET_PORT_SCHEDULER = 0x9, - /* register/delete flow steering network rules */ MLX4_QP_FLOW_STEERING_ATTACH = 0x65, MLX4_QP_FLOW_STEERING_DETACH = 0x66, @@ -182,6 +178,8 @@ enum { MLX4_SET_PORT_VLAN_TABLE = 0x3, MLX4_SET_PORT_PRIO_MAP = 0x4, MLX4_SET_PORT_GID_TABLE = 0x5, + MLX4_SET_PORT_PRIO2TC = 0x8, + MLX4_SET_PORT_SCHEDULER = 0x9, }; enum { diff --git a/include/linux/mlx4/device.h b/include/linux/mlx4/device.h index 24ce6bdd540ef65f1a8560ae6f089a0df1e5aeb1..7d3a523160ba4906aaa3c03290bcea20920c4b59 100644 --- a/include/linux/mlx4/device.h +++ b/include/linux/mlx4/device.h @@ -54,6 +54,7 @@ enum { MLX4_FLAG_MASTER = 1 << 2, MLX4_FLAG_SLAVE = 1 << 3, MLX4_FLAG_SRIOV = 1 << 4, + MLX4_FLAG_OLD_REG_MAC = 1 << 6, }; enum { @@ -155,7 +156,7 @@ enum { MLX4_DEV_CAP_FLAG2_RSS_TOP = 1LL << 1, MLX4_DEV_CAP_FLAG2_RSS_XOR = 1LL << 2, MLX4_DEV_CAP_FLAG2_FS_EN = 1LL << 3, - MLX4_DEV_CAP_FLAGS2_REASSIGN_MAC_EN = 1LL << 4, + MLX4_DEV_CAP_FLAG2_REASSIGN_MAC_EN = 1LL << 4, MLX4_DEV_CAP_FLAG2_TS = 1LL << 5, MLX4_DEV_CAP_FLAG2_VLAN_CONTROL = 1LL << 6, MLX4_DEV_CAP_FLAG2_FSM = 1LL << 7, @@ -640,16 +641,28 @@ struct mlx4_counter { __be64 tx_bytes; }; +struct mlx4_quotas { + int qp; + int cq; + int srq; + int mpt; + int mtt; + int counter; + int xrcd; +}; + struct mlx4_dev { struct pci_dev *pdev; unsigned long flags; unsigned long num_slaves; struct mlx4_caps caps; struct mlx4_phys_caps phys_caps; + struct mlx4_quotas quotas; struct radix_tree_root qp_table_tree; u8 rev_id; char board_id[MLX4_BOARD_ID_LEN]; int num_vfs; + int numa_node; int oper_log_mgm_entry_size; u64 regid_promisc_array[MLX4_MAX_PORTS + 1]; u64 regid_allmulti_array[MLX4_MAX_PORTS + 1]; @@ -771,6 +784,12 @@ static inline int mlx4_is_master(struct mlx4_dev *dev) return dev->flags & MLX4_FLAG_MASTER; } +static inline int mlx4_num_reserved_sqps(struct mlx4_dev *dev) +{ + return dev->phys_caps.base_sqpn + 8 + + 16 * MLX4_MFUNC_MAX * !!mlx4_is_master(dev); +} + static inline int mlx4_is_qp_reserved(struct mlx4_dev *dev, u32 qpn) { return (qpn < dev->phys_caps.base_sqpn + 8 + @@ -816,7 +835,7 @@ void mlx4_xrcd_free(struct mlx4_dev *dev, u32 xrcdn); int mlx4_uar_alloc(struct mlx4_dev *dev, struct mlx4_uar *uar); void mlx4_uar_free(struct mlx4_dev *dev, struct mlx4_uar *uar); -int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf); +int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf, int node); void mlx4_bf_free(struct mlx4_dev *dev, struct mlx4_bf *bf); int mlx4_mtt_init(struct mlx4_dev *dev, int npages, int page_shift, @@ -1078,7 +1097,7 @@ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, u8 *pg, u16 *ratelimit); int mlx4_find_cached_vlan(struct mlx4_dev *dev, u8 port, u16 vid, int *idx); int mlx4_register_vlan(struct mlx4_dev *dev, u8 port, u16 vlan, int *index); -void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, int index); +void mlx4_unregister_vlan(struct mlx4_dev *dev, u8 port, u16 vlan); int mlx4_map_phys_fmr(struct mlx4_dev *dev, struct mlx4_fmr *fmr, u64 *page_list, int npages, u64 iova, u32 *lkey, u32 *rkey); diff --git a/include/linux/net.h b/include/linux/net.h index 4f27575ce1d67ebe74d1bba4f40a70f6edbdea11..b292a04355717a503b093e2d2c424cf92951415f 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -24,6 +24,7 @@ #include /* For O_CLOEXEC and O_NONBLOCK */ #include #include +#include #include struct poll_table_struct; @@ -195,27 +196,23 @@ enum { SOCK_WAKE_URG, }; -extern int sock_wake_async(struct socket *sk, int how, int band); -extern int sock_register(const struct net_proto_family *fam); -extern void sock_unregister(int family); -extern int __sock_create(struct net *net, int family, int type, int proto, - struct socket **res, int kern); -extern int sock_create(int family, int type, int proto, - struct socket **res); -extern int sock_create_kern(int family, int type, int proto, - struct socket **res); -extern int sock_create_lite(int family, int type, int proto, - struct socket **res); -extern void sock_release(struct socket *sock); -extern int sock_sendmsg(struct socket *sock, struct msghdr *msg, - size_t len); -extern int sock_recvmsg(struct socket *sock, struct msghdr *msg, - size_t size, int flags); -extern struct file *sock_alloc_file(struct socket *sock, int flags, const char *dname); -extern struct socket *sockfd_lookup(int fd, int *err); -extern struct socket *sock_from_file(struct file *file, int *err); +int sock_wake_async(struct socket *sk, int how, int band); +int sock_register(const struct net_proto_family *fam); +void sock_unregister(int family); +int __sock_create(struct net *net, int family, int type, int proto, + struct socket **res, int kern); +int sock_create(int family, int type, int proto, struct socket **res); +int sock_create_kern(int family, int type, int proto, struct socket **res); +int sock_create_lite(int family, int type, int proto, struct socket **res); +void sock_release(struct socket *sock); +int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len); +int sock_recvmsg(struct socket *sock, struct msghdr *msg, size_t size, + int flags); +struct file *sock_alloc_file(struct socket *sock, int flags, const char *dname); +struct socket *sockfd_lookup(int fd, int *err); +struct socket *sock_from_file(struct file *file, int *err); #define sockfd_put(sock) fput(sock->file) -extern int net_ratelimit(void); +int net_ratelimit(void); #define net_ratelimited_function(function, ...) \ do { \ @@ -243,32 +240,52 @@ do { \ #define net_random() prandom_u32() #define net_srandom(seed) prandom_seed((__force u32)(seed)) -extern int kernel_sendmsg(struct socket *sock, struct msghdr *msg, - struct kvec *vec, size_t num, size_t len); -extern int kernel_recvmsg(struct socket *sock, struct msghdr *msg, - struct kvec *vec, size_t num, - size_t len, int flags); - -extern int kernel_bind(struct socket *sock, struct sockaddr *addr, - int addrlen); -extern int kernel_listen(struct socket *sock, int backlog); -extern int kernel_accept(struct socket *sock, struct socket **newsock, - int flags); -extern int kernel_connect(struct socket *sock, struct sockaddr *addr, - int addrlen, int flags); -extern int kernel_getsockname(struct socket *sock, struct sockaddr *addr, - int *addrlen); -extern int kernel_getpeername(struct socket *sock, struct sockaddr *addr, - int *addrlen); -extern int kernel_getsockopt(struct socket *sock, int level, int optname, - char *optval, int *optlen); -extern int kernel_setsockopt(struct socket *sock, int level, int optname, - char *optval, unsigned int optlen); -extern int kernel_sendpage(struct socket *sock, struct page *page, int offset, - size_t size, int flags); -extern int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); -extern int kernel_sock_shutdown(struct socket *sock, - enum sock_shutdown_cmd how); +bool __net_get_random_once(void *buf, int nbytes, bool *done, + struct static_key *done_key); + +#ifdef HAVE_JUMP_LABEL +#define ___NET_RANDOM_STATIC_KEY_INIT ((struct static_key) \ + { .enabled = ATOMIC_INIT(0), .entries = (void *)1 }) +#else /* !HAVE_JUMP_LABEL */ +#define ___NET_RANDOM_STATIC_KEY_INIT STATIC_KEY_INIT_FALSE +#endif /* HAVE_JUMP_LABEL */ + +#define net_get_random_once(buf, nbytes) \ + ({ \ + bool ___ret = false; \ + static bool ___done = false; \ + static struct static_key ___done_key = \ + ___NET_RANDOM_STATIC_KEY_INIT; \ + if (!static_key_true(&___done_key)) \ + ___ret = __net_get_random_once(buf, \ + nbytes, \ + &___done, \ + &___done_key); \ + ___ret; \ + }) + +int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, + size_t num, size_t len); +int kernel_recvmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, + size_t num, size_t len, int flags); + +int kernel_bind(struct socket *sock, struct sockaddr *addr, int addrlen); +int kernel_listen(struct socket *sock, int backlog); +int kernel_accept(struct socket *sock, struct socket **newsock, int flags); +int kernel_connect(struct socket *sock, struct sockaddr *addr, int addrlen, + int flags); +int kernel_getsockname(struct socket *sock, struct sockaddr *addr, + int *addrlen); +int kernel_getpeername(struct socket *sock, struct sockaddr *addr, + int *addrlen); +int kernel_getsockopt(struct socket *sock, int level, int optname, char *optval, + int *optlen); +int kernel_setsockopt(struct socket *sock, int level, int optname, char *optval, + unsigned int optlen); +int kernel_sendpage(struct socket *sock, struct page *page, int offset, + size_t size, int flags); +int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); +int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); #define MODULE_ALIAS_NETPROTO(proto) \ MODULE_ALIAS("net-pf-" __stringify(proto)) diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index a2a89a5c7be55b15baec6271a8cc4329b6f642bd..1005ebf175752774ada359369a313379a82dc85a 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -42,6 +42,8 @@ enum { NETIF_F_TSO6_BIT, /* ... TCPv6 segmentation */ NETIF_F_FSO_BIT, /* ... FCoE segmentation */ NETIF_F_GSO_GRE_BIT, /* ... GRE with TSO */ + NETIF_F_GSO_IPIP_BIT, /* ... IPIP tunnel with TSO */ + NETIF_F_GSO_SIT_BIT, /* ... SIT tunnel with TSO */ NETIF_F_GSO_UDP_TUNNEL_BIT, /* ... UDP TUNNEL with TSO */ NETIF_F_GSO_MPLS_BIT, /* ... MPLS segmentation */ /**/NETIF_F_GSO_LAST = /* last bit, see GSO_MASK */ @@ -60,6 +62,7 @@ enum { NETIF_F_HW_VLAN_STAG_TX_BIT, /* Transmit VLAN STAG HW acceleration */ NETIF_F_HW_VLAN_STAG_RX_BIT, /* Receive VLAN STAG HW acceleration */ NETIF_F_HW_VLAN_STAG_FILTER_BIT,/* Receive filtering on VLAN STAGs */ + NETIF_F_HW_L2FW_DOFFLOAD_BIT, /* Allow L2 Forwarding in Hardware */ /* * Add your fresh new feature above and remember to update @@ -107,11 +110,14 @@ enum { #define NETIF_F_RXFCS __NETIF_F(RXFCS) #define NETIF_F_RXALL __NETIF_F(RXALL) #define NETIF_F_GSO_GRE __NETIF_F(GSO_GRE) +#define NETIF_F_GSO_IPIP __NETIF_F(GSO_IPIP) +#define NETIF_F_GSO_SIT __NETIF_F(GSO_SIT) #define NETIF_F_GSO_UDP_TUNNEL __NETIF_F(GSO_UDP_TUNNEL) #define NETIF_F_GSO_MPLS __NETIF_F(GSO_MPLS) #define NETIF_F_HW_VLAN_STAG_FILTER __NETIF_F(HW_VLAN_STAG_FILTER) #define NETIF_F_HW_VLAN_STAG_RX __NETIF_F(HW_VLAN_STAG_RX) #define NETIF_F_HW_VLAN_STAG_TX __NETIF_F(HW_VLAN_STAG_TX) +#define NETIF_F_HW_L2FW_DOFFLOAD __NETIF_F(HW_L2FW_DOFFLOAD) /* Features valid for ethtool to change */ /* = all defined minus driver/device-class-related */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index adf4070586d64be47fe5d18ed8f554e0c66670e1..8b3de7cc2ffc24f12ade53f8680d2851d7bf031b 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -60,8 +60,8 @@ struct wireless_dev; #define SET_ETHTOOL_OPS(netdev,ops) \ ( (netdev)->ethtool_ops = (ops) ) -extern void netdev_set_default_ethtool_ops(struct net_device *dev, - const struct ethtool_ops *ops); +void netdev_set_default_ethtool_ops(struct net_device *dev, + const struct ethtool_ops *ops); /* hardware address assignment types */ #define NET_ADDR_PERM 0 /* address is permanent (default) */ @@ -298,7 +298,7 @@ struct netdev_boot_setup { }; #define NETDEV_BOOT_SETUP_MAX 8 -extern int __init netdev_boot_setup(char *str); +int __init netdev_boot_setup(char *str); /* * Structure for NAPI scheduling similar to tasklet but with weighting @@ -394,7 +394,7 @@ enum rx_handler_result { typedef enum rx_handler_result rx_handler_result_t; typedef rx_handler_result_t rx_handler_func_t(struct sk_buff **pskb); -extern void __napi_schedule(struct napi_struct *n); +void __napi_schedule(struct napi_struct *n); static inline bool napi_disable_pending(struct napi_struct *n) { @@ -445,8 +445,8 @@ static inline bool napi_reschedule(struct napi_struct *napi) * * Mark NAPI processing as complete. */ -extern void __napi_complete(struct napi_struct *n); -extern void napi_complete(struct napi_struct *n); +void __napi_complete(struct napi_struct *n); +void napi_complete(struct napi_struct *n); /** * napi_by_id - lookup a NAPI by napi_id @@ -455,7 +455,7 @@ extern void napi_complete(struct napi_struct *n); * lookup @napi_id in napi_hash table * must be called under rcu_read_lock() */ -extern struct napi_struct *napi_by_id(unsigned int napi_id); +struct napi_struct *napi_by_id(unsigned int napi_id); /** * napi_hash_add - add a NAPI to global hashtable @@ -463,7 +463,7 @@ extern struct napi_struct *napi_by_id(unsigned int napi_id); * * generate a new napi_id and store a @napi under it in napi_hash */ -extern void napi_hash_add(struct napi_struct *napi); +void napi_hash_add(struct napi_struct *napi); /** * napi_hash_del - remove a NAPI from global table @@ -472,7 +472,7 @@ extern void napi_hash_add(struct napi_struct *napi); * Warning: caller must observe rcu grace period * before freeing memory containing @napi */ -extern void napi_hash_del(struct napi_struct *napi); +void napi_hash_del(struct napi_struct *napi); /** * napi_disable - prevent NAPI from scheduling @@ -483,6 +483,7 @@ extern void napi_hash_del(struct napi_struct *napi); */ static inline void napi_disable(struct napi_struct *n) { + might_sleep(); set_bit(NAPI_STATE_DISABLE, &n->state); while (test_and_set_bit(NAPI_STATE_SCHED, &n->state)) msleep(1); @@ -664,8 +665,8 @@ static inline void rps_reset_sock_flow(struct rps_sock_flow_table *table, extern struct rps_sock_flow_table __rcu *rps_sock_flow_table; #ifdef CONFIG_RFS_ACCEL -extern bool rps_may_expire_flow(struct net_device *dev, u16 rxq_index, - u32 flow_id, u16 filter_id); +bool rps_may_expire_flow(struct net_device *dev, u16 rxq_index, u32 flow_id, + u16 filter_id); #endif /* This structure contains an instance of an RX queue. */ @@ -961,6 +962,25 @@ struct netdev_phys_port_id { * Called by vxlan to notify the driver about a UDP port and socket * address family that vxlan is not listening to anymore. The operation * is protected by the vxlan_net->sock_lock. + * + * void* (*ndo_dfwd_add_station)(struct net_device *pdev, + * struct net_device *dev) + * Called by upper layer devices to accelerate switching or other + * station functionality into hardware. 'pdev is the lowerdev + * to use for the offload and 'dev' is the net device that will + * back the offload. Returns a pointer to the private structure + * the upper layer will maintain. + * void (*ndo_dfwd_del_station)(struct net_device *pdev, void *priv) + * Called by upper layer device to delete the station created + * by 'ndo_dfwd_add_station'. 'pdev' is the net device backing + * the station and priv is the structure returned by the add + * operation. + * netdev_tx_t (*ndo_dfwd_start_xmit)(struct sk_buff *skb, + * struct net_device *dev, + * void *priv); + * Callback to use for xmit over the accelerated station. This + * is used in place of ndo_start_xmit on accelerated net + * devices. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1097,6 +1117,15 @@ struct net_device_ops { void (*ndo_del_vxlan_port)(struct net_device *dev, sa_family_t sa_family, __be16 port); + + void* (*ndo_dfwd_add_station)(struct net_device *pdev, + struct net_device *dev); + void (*ndo_dfwd_del_station)(struct net_device *pdev, + void *priv); + + netdev_tx_t (*ndo_dfwd_start_xmit) (struct sk_buff *skb, + struct net_device *dev, + void *priv); }; /* @@ -1131,7 +1160,7 @@ struct net_device { unsigned long mem_end; /* shared mem end */ unsigned long mem_start; /* shared mem start */ unsigned long base_addr; /* device I/O address */ - unsigned int irq; /* device IRQ number */ + int irq; /* device IRQ number */ /* * Some hardware also needs these fields, but they are not @@ -1143,8 +1172,19 @@ struct net_device { struct list_head dev_list; struct list_head napi_list; struct list_head unreg_list; - struct list_head upper_dev_list; /* List of upper devices */ - struct list_head lower_dev_list; + struct list_head close_list; + + /* directly linked devices, like slaves for bonding */ + struct { + struct list_head upper; + struct list_head lower; + } adj_list; + + /* all linked devices, *including* neighbours */ + struct { + struct list_head upper; + struct list_head lower; + } all_adj_list; /* currently active device features */ @@ -1183,6 +1223,7 @@ struct net_device { /* Management operations */ const struct net_device_ops *netdev_ops; const struct ethtool_ops *ethtool_ops; + const struct forwarding_accel_ops *fwd_ops; /* Hardware header description */ const struct header_ops *header_ops; @@ -1487,9 +1528,9 @@ static inline void netdev_for_each_tx_queue(struct net_device *dev, f(dev, &dev->_tx[i], arg); } -extern struct netdev_queue *netdev_pick_tx(struct net_device *dev, - struct sk_buff *skb); -extern u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb); +struct netdev_queue *netdev_pick_tx(struct net_device *dev, + struct sk_buff *skb); +u16 __netdev_pick_tx(struct net_device *dev, struct sk_buff *skb); /* * Net namespace inlines @@ -1673,8 +1714,8 @@ struct packet_offload { #define NETDEV_CHANGEUPPER 0x0015 #define NETDEV_RESEND_IGMP 0x0016 -extern int register_netdevice_notifier(struct notifier_block *nb); -extern int unregister_netdevice_notifier(struct notifier_block *nb); +int register_netdevice_notifier(struct notifier_block *nb); +int unregister_netdevice_notifier(struct notifier_block *nb); struct netdev_notifier_info { struct net_device *dev; @@ -1697,9 +1738,9 @@ netdev_notifier_info_to_dev(const struct netdev_notifier_info *info) return info->dev; } -extern int call_netdevice_notifiers_info(unsigned long val, struct net_device *dev, - struct netdev_notifier_info *info); -extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev); +int call_netdevice_notifiers_info(unsigned long val, struct net_device *dev, + struct netdev_notifier_info *info); +int call_netdevice_notifiers(unsigned long val, struct net_device *dev); extern rwlock_t dev_base_lock; /* Device list lock */ @@ -1754,54 +1795,53 @@ static inline struct net_device *first_net_device_rcu(struct net *net) return lh == &net->dev_base_head ? NULL : net_device_entry(lh); } -extern int netdev_boot_setup_check(struct net_device *dev); -extern unsigned long netdev_boot_base(const char *prefix, int unit); -extern struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type, - const char *hwaddr); -extern struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type); -extern struct net_device *__dev_getfirstbyhwtype(struct net *net, unsigned short type); -extern void dev_add_pack(struct packet_type *pt); -extern void dev_remove_pack(struct packet_type *pt); -extern void __dev_remove_pack(struct packet_type *pt); -extern void dev_add_offload(struct packet_offload *po); -extern void dev_remove_offload(struct packet_offload *po); -extern void __dev_remove_offload(struct packet_offload *po); - -extern struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short flags, - unsigned short mask); -extern struct net_device *dev_get_by_name(struct net *net, const char *name); -extern struct net_device *dev_get_by_name_rcu(struct net *net, const char *name); -extern struct net_device *__dev_get_by_name(struct net *net, const char *name); -extern int dev_alloc_name(struct net_device *dev, const char *name); -extern int dev_open(struct net_device *dev); -extern int dev_close(struct net_device *dev); -extern void dev_disable_lro(struct net_device *dev); -extern int dev_loopback_xmit(struct sk_buff *newskb); -extern int dev_queue_xmit(struct sk_buff *skb); -extern int register_netdevice(struct net_device *dev); -extern void unregister_netdevice_queue(struct net_device *dev, - struct list_head *head); -extern void unregister_netdevice_many(struct list_head *head); +int netdev_boot_setup_check(struct net_device *dev); +unsigned long netdev_boot_base(const char *prefix, int unit); +struct net_device *dev_getbyhwaddr_rcu(struct net *net, unsigned short type, + const char *hwaddr); +struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type); +struct net_device *__dev_getfirstbyhwtype(struct net *net, unsigned short type); +void dev_add_pack(struct packet_type *pt); +void dev_remove_pack(struct packet_type *pt); +void __dev_remove_pack(struct packet_type *pt); +void dev_add_offload(struct packet_offload *po); +void dev_remove_offload(struct packet_offload *po); +void __dev_remove_offload(struct packet_offload *po); + +struct net_device *dev_get_by_flags_rcu(struct net *net, unsigned short flags, + unsigned short mask); +struct net_device *dev_get_by_name(struct net *net, const char *name); +struct net_device *dev_get_by_name_rcu(struct net *net, const char *name); +struct net_device *__dev_get_by_name(struct net *net, const char *name); +int dev_alloc_name(struct net_device *dev, const char *name); +int dev_open(struct net_device *dev); +int dev_close(struct net_device *dev); +void dev_disable_lro(struct net_device *dev); +int dev_loopback_xmit(struct sk_buff *newskb); +int dev_queue_xmit(struct sk_buff *skb); +int register_netdevice(struct net_device *dev); +void unregister_netdevice_queue(struct net_device *dev, struct list_head *head); +void unregister_netdevice_many(struct list_head *head); static inline void unregister_netdevice(struct net_device *dev) { unregister_netdevice_queue(dev, NULL); } -extern int netdev_refcnt_read(const struct net_device *dev); -extern void free_netdev(struct net_device *dev); -extern void synchronize_net(void); -extern int init_dummy_netdev(struct net_device *dev); +int netdev_refcnt_read(const struct net_device *dev); +void free_netdev(struct net_device *dev); +void netdev_freemem(struct net_device *dev); +void synchronize_net(void); +int init_dummy_netdev(struct net_device *dev); -extern struct net_device *dev_get_by_index(struct net *net, int ifindex); -extern struct net_device *__dev_get_by_index(struct net *net, int ifindex); -extern struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex); -extern int netdev_get_name(struct net *net, char *name, int ifindex); -extern int dev_restart(struct net_device *dev); +struct net_device *dev_get_by_index(struct net *net, int ifindex); +struct net_device *__dev_get_by_index(struct net *net, int ifindex); +struct net_device *dev_get_by_index_rcu(struct net *net, int ifindex); +int netdev_get_name(struct net *net, char *name, int ifindex); +int dev_restart(struct net_device *dev); #ifdef CONFIG_NETPOLL_TRAP -extern int netpoll_trap(void); +int netpoll_trap(void); #endif -extern int skb_gro_receive(struct sk_buff **head, - struct sk_buff *skb); +int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb); static inline unsigned int skb_gro_offset(const struct sk_buff *skb) { @@ -1873,7 +1913,7 @@ static inline int dev_parse_header(const struct sk_buff *skb, } typedef int gifconf_func_t(struct net_device * dev, char __user * bufptr, int len); -extern int register_gifconf(unsigned int family, gifconf_func_t * gifconf); +int register_gifconf(unsigned int family, gifconf_func_t *gifconf); static inline int unregister_gifconf(unsigned int family) { return register_gifconf(family, NULL); @@ -1944,7 +1984,7 @@ static inline void input_queue_tail_incr_save(struct softnet_data *sd, DECLARE_PER_CPU_ALIGNED(struct softnet_data, softnet_data); -extern void __netif_schedule(struct Qdisc *q); +void __netif_schedule(struct Qdisc *q); static inline void netif_schedule_queue(struct netdev_queue *txq) { @@ -2264,9 +2304,8 @@ static inline void netif_wake_subqueue(struct net_device *dev, u16 queue_index) } #ifdef CONFIG_XPS -extern int netif_set_xps_queue(struct net_device *dev, - const struct cpumask *mask, - u16 index); +int netif_set_xps_queue(struct net_device *dev, const struct cpumask *mask, + u16 index); #else static inline int netif_set_xps_queue(struct net_device *dev, const struct cpumask *mask, @@ -2297,12 +2336,10 @@ static inline bool netif_is_multiqueue(const struct net_device *dev) return dev->num_tx_queues > 1; } -extern int netif_set_real_num_tx_queues(struct net_device *dev, - unsigned int txq); +int netif_set_real_num_tx_queues(struct net_device *dev, unsigned int txq); #ifdef CONFIG_RPS -extern int netif_set_real_num_rx_queues(struct net_device *dev, - unsigned int rxq); +int netif_set_real_num_rx_queues(struct net_device *dev, unsigned int rxq); #else static inline int netif_set_real_num_rx_queues(struct net_device *dev, unsigned int rxq) @@ -2329,28 +2366,27 @@ static inline int netif_copy_real_num_queues(struct net_device *to_dev, } #define DEFAULT_MAX_NUM_RSS_QUEUES (8) -extern int netif_get_num_default_rss_queues(void); +int netif_get_num_default_rss_queues(void); /* Use this variant when it is known for sure that it * is executing from hardware interrupt context or with hardware interrupts * disabled. */ -extern void dev_kfree_skb_irq(struct sk_buff *skb); +void dev_kfree_skb_irq(struct sk_buff *skb); /* Use this variant in places where it could be invoked * from either hardware interrupt or other context, with hardware interrupts * either disabled or enabled. */ -extern void dev_kfree_skb_any(struct sk_buff *skb); +void dev_kfree_skb_any(struct sk_buff *skb); -extern int netif_rx(struct sk_buff *skb); -extern int netif_rx_ni(struct sk_buff *skb); -extern int netif_receive_skb(struct sk_buff *skb); -extern gro_result_t napi_gro_receive(struct napi_struct *napi, - struct sk_buff *skb); -extern void napi_gro_flush(struct napi_struct *napi, bool flush_old); -extern struct sk_buff * napi_get_frags(struct napi_struct *napi); -extern gro_result_t napi_gro_frags(struct napi_struct *napi); +int netif_rx(struct sk_buff *skb); +int netif_rx_ni(struct sk_buff *skb); +int netif_receive_skb(struct sk_buff *skb); +gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb); +void napi_gro_flush(struct napi_struct *napi, bool flush_old); +struct sk_buff *napi_get_frags(struct napi_struct *napi); +gro_result_t napi_gro_frags(struct napi_struct *napi); static inline void napi_free_frags(struct napi_struct *napi) { @@ -2358,40 +2394,36 @@ static inline void napi_free_frags(struct napi_struct *napi) napi->skb = NULL; } -extern int netdev_rx_handler_register(struct net_device *dev, - rx_handler_func_t *rx_handler, - void *rx_handler_data); -extern void netdev_rx_handler_unregister(struct net_device *dev); - -extern bool dev_valid_name(const char *name); -extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); -extern int dev_ethtool(struct net *net, struct ifreq *); -extern unsigned int dev_get_flags(const struct net_device *); -extern int __dev_change_flags(struct net_device *, unsigned int flags); -extern int dev_change_flags(struct net_device *, unsigned int); -extern void __dev_notify_flags(struct net_device *, unsigned int old_flags); -extern int dev_change_name(struct net_device *, const char *); -extern int dev_set_alias(struct net_device *, const char *, size_t); -extern int dev_change_net_namespace(struct net_device *, - struct net *, const char *); -extern int dev_set_mtu(struct net_device *, int); -extern void dev_set_group(struct net_device *, int); -extern int dev_set_mac_address(struct net_device *, - struct sockaddr *); -extern int dev_change_carrier(struct net_device *, - bool new_carrier); -extern int dev_get_phys_port_id(struct net_device *dev, - struct netdev_phys_port_id *ppid); -extern int dev_hard_start_xmit(struct sk_buff *skb, - struct net_device *dev, - struct netdev_queue *txq); -extern int dev_forward_skb(struct net_device *dev, - struct sk_buff *skb); +int netdev_rx_handler_register(struct net_device *dev, + rx_handler_func_t *rx_handler, + void *rx_handler_data); +void netdev_rx_handler_unregister(struct net_device *dev); + +bool dev_valid_name(const char *name); +int dev_ioctl(struct net *net, unsigned int cmd, void __user *); +int dev_ethtool(struct net *net, struct ifreq *); +unsigned int dev_get_flags(const struct net_device *); +int __dev_change_flags(struct net_device *, unsigned int flags); +int dev_change_flags(struct net_device *, unsigned int); +void __dev_notify_flags(struct net_device *, unsigned int old_flags, + unsigned int gchanges); +int dev_change_name(struct net_device *, const char *); +int dev_set_alias(struct net_device *, const char *, size_t); +int dev_change_net_namespace(struct net_device *, struct net *, const char *); +int dev_set_mtu(struct net_device *, int); +void dev_set_group(struct net_device *, int); +int dev_set_mac_address(struct net_device *, struct sockaddr *); +int dev_change_carrier(struct net_device *, bool new_carrier); +int dev_get_phys_port_id(struct net_device *dev, + struct netdev_phys_port_id *ppid); +int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, + struct netdev_queue *txq, void *accel_priv); +int dev_forward_skb(struct net_device *dev, struct sk_buff *skb); extern int netdev_budget; /* Called by rtnetlink.c:rtnl_unlock() */ -extern void netdev_run_todo(void); +void netdev_run_todo(void); /** * dev_put - release reference to device @@ -2424,9 +2456,9 @@ static inline void dev_hold(struct net_device *dev) * kind of lower layer not just hardware media. */ -extern void linkwatch_init_dev(struct net_device *dev); -extern void linkwatch_fire_event(struct net_device *dev); -extern void linkwatch_forget_dev(struct net_device *dev); +void linkwatch_init_dev(struct net_device *dev); +void linkwatch_fire_event(struct net_device *dev); +void linkwatch_forget_dev(struct net_device *dev); /** * netif_carrier_ok - test if carrier present @@ -2439,13 +2471,13 @@ static inline bool netif_carrier_ok(const struct net_device *dev) return !test_bit(__LINK_STATE_NOCARRIER, &dev->state); } -extern unsigned long dev_trans_start(struct net_device *dev); +unsigned long dev_trans_start(struct net_device *dev); -extern void __netdev_watchdog_up(struct net_device *dev); +void __netdev_watchdog_up(struct net_device *dev); -extern void netif_carrier_on(struct net_device *dev); +void netif_carrier_on(struct net_device *dev); -extern void netif_carrier_off(struct net_device *dev); +void netif_carrier_off(struct net_device *dev); /** * netif_dormant_on - mark device as dormant. @@ -2513,9 +2545,9 @@ static inline bool netif_device_present(struct net_device *dev) return test_bit(__LINK_STATE_PRESENT, &dev->state); } -extern void netif_device_detach(struct net_device *dev); +void netif_device_detach(struct net_device *dev); -extern void netif_device_attach(struct net_device *dev); +void netif_device_attach(struct net_device *dev); /* * Network interface message level settings @@ -2724,119 +2756,138 @@ static inline void netif_addr_unlock_bh(struct net_device *dev) /* These functions live elsewhere (drivers/net/net_init.c, but related) */ -extern void ether_setup(struct net_device *dev); +void ether_setup(struct net_device *dev); /* Support for loadable net-drivers */ -extern struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, - void (*setup)(struct net_device *), - unsigned int txqs, unsigned int rxqs); +struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, + void (*setup)(struct net_device *), + unsigned int txqs, unsigned int rxqs); #define alloc_netdev(sizeof_priv, name, setup) \ alloc_netdev_mqs(sizeof_priv, name, setup, 1, 1) #define alloc_netdev_mq(sizeof_priv, name, setup, count) \ alloc_netdev_mqs(sizeof_priv, name, setup, count, count) -extern int register_netdev(struct net_device *dev); -extern void unregister_netdev(struct net_device *dev); +int register_netdev(struct net_device *dev); +void unregister_netdev(struct net_device *dev); /* General hardware address lists handling functions */ -extern int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len, unsigned char addr_type); -extern void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len, unsigned char addr_type); -extern int __hw_addr_sync(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len); -extern void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, - struct netdev_hw_addr_list *from_list, - int addr_len); -extern void __hw_addr_flush(struct netdev_hw_addr_list *list); -extern void __hw_addr_init(struct netdev_hw_addr_list *list); +int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, unsigned char addr_type); +void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, + int addr_len, unsigned char addr_type); +int __hw_addr_sync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, int addr_len); +void __hw_addr_unsync(struct netdev_hw_addr_list *to_list, + struct netdev_hw_addr_list *from_list, int addr_len); +void __hw_addr_flush(struct netdev_hw_addr_list *list); +void __hw_addr_init(struct netdev_hw_addr_list *list); /* Functions used for device addresses handling */ -extern int dev_addr_add(struct net_device *dev, const unsigned char *addr, - unsigned char addr_type); -extern int dev_addr_del(struct net_device *dev, const unsigned char *addr, - unsigned char addr_type); -extern int dev_addr_add_multiple(struct net_device *to_dev, - struct net_device *from_dev, - unsigned char addr_type); -extern int dev_addr_del_multiple(struct net_device *to_dev, - struct net_device *from_dev, - unsigned char addr_type); -extern void dev_addr_flush(struct net_device *dev); -extern int dev_addr_init(struct net_device *dev); +int dev_addr_add(struct net_device *dev, const unsigned char *addr, + unsigned char addr_type); +int dev_addr_del(struct net_device *dev, const unsigned char *addr, + unsigned char addr_type); +int dev_addr_add_multiple(struct net_device *to_dev, + struct net_device *from_dev, unsigned char addr_type); +int dev_addr_del_multiple(struct net_device *to_dev, + struct net_device *from_dev, unsigned char addr_type); +void dev_addr_flush(struct net_device *dev); +int dev_addr_init(struct net_device *dev); /* Functions used for unicast addresses handling */ -extern int dev_uc_add(struct net_device *dev, const unsigned char *addr); -extern int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr); -extern int dev_uc_del(struct net_device *dev, const unsigned char *addr); -extern int dev_uc_sync(struct net_device *to, struct net_device *from); -extern int dev_uc_sync_multiple(struct net_device *to, struct net_device *from); -extern void dev_uc_unsync(struct net_device *to, struct net_device *from); -extern void dev_uc_flush(struct net_device *dev); -extern void dev_uc_init(struct net_device *dev); +int dev_uc_add(struct net_device *dev, const unsigned char *addr); +int dev_uc_add_excl(struct net_device *dev, const unsigned char *addr); +int dev_uc_del(struct net_device *dev, const unsigned char *addr); +int dev_uc_sync(struct net_device *to, struct net_device *from); +int dev_uc_sync_multiple(struct net_device *to, struct net_device *from); +void dev_uc_unsync(struct net_device *to, struct net_device *from); +void dev_uc_flush(struct net_device *dev); +void dev_uc_init(struct net_device *dev); /* Functions used for multicast addresses handling */ -extern int dev_mc_add(struct net_device *dev, const unsigned char *addr); -extern int dev_mc_add_global(struct net_device *dev, const unsigned char *addr); -extern int dev_mc_add_excl(struct net_device *dev, const unsigned char *addr); -extern int dev_mc_del(struct net_device *dev, const unsigned char *addr); -extern int dev_mc_del_global(struct net_device *dev, const unsigned char *addr); -extern int dev_mc_sync(struct net_device *to, struct net_device *from); -extern int dev_mc_sync_multiple(struct net_device *to, struct net_device *from); -extern void dev_mc_unsync(struct net_device *to, struct net_device *from); -extern void dev_mc_flush(struct net_device *dev); -extern void dev_mc_init(struct net_device *dev); +int dev_mc_add(struct net_device *dev, const unsigned char *addr); +int dev_mc_add_global(struct net_device *dev, const unsigned char *addr); +int dev_mc_add_excl(struct net_device *dev, const unsigned char *addr); +int dev_mc_del(struct net_device *dev, const unsigned char *addr); +int dev_mc_del_global(struct net_device *dev, const unsigned char *addr); +int dev_mc_sync(struct net_device *to, struct net_device *from); +int dev_mc_sync_multiple(struct net_device *to, struct net_device *from); +void dev_mc_unsync(struct net_device *to, struct net_device *from); +void dev_mc_flush(struct net_device *dev); +void dev_mc_init(struct net_device *dev); /* Functions used for secondary unicast and multicast support */ -extern void dev_set_rx_mode(struct net_device *dev); -extern void __dev_set_rx_mode(struct net_device *dev); -extern int dev_set_promiscuity(struct net_device *dev, int inc); -extern int dev_set_allmulti(struct net_device *dev, int inc); -extern void netdev_state_change(struct net_device *dev); -extern void netdev_notify_peers(struct net_device *dev); -extern void netdev_features_change(struct net_device *dev); +void dev_set_rx_mode(struct net_device *dev); +void __dev_set_rx_mode(struct net_device *dev); +int dev_set_promiscuity(struct net_device *dev, int inc); +int dev_set_allmulti(struct net_device *dev, int inc); +void netdev_state_change(struct net_device *dev); +void netdev_notify_peers(struct net_device *dev); +void netdev_features_change(struct net_device *dev); /* Load a device via the kmod */ -extern void dev_load(struct net *net, const char *name); -extern struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, - struct rtnl_link_stats64 *storage); -extern void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, - const struct net_device_stats *netdev_stats); +void dev_load(struct net *net, const char *name); +struct rtnl_link_stats64 *dev_get_stats(struct net_device *dev, + struct rtnl_link_stats64 *storage); +void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64, + const struct net_device_stats *netdev_stats); extern int netdev_max_backlog; extern int netdev_tstamp_prequeue; extern int weight_p; extern int bpf_jit_enable; -extern bool netdev_has_upper_dev(struct net_device *dev, - struct net_device *upper_dev); -extern bool netdev_has_any_upper_dev(struct net_device *dev); -extern struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev, - struct list_head **iter); +bool netdev_has_upper_dev(struct net_device *dev, struct net_device *upper_dev); +bool netdev_has_any_upper_dev(struct net_device *dev); +struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev, + struct list_head **iter); /* iterate through upper list, must be called under RCU read lock */ -#define netdev_for_each_upper_dev_rcu(dev, upper, iter) \ - for (iter = &(dev)->upper_dev_list, \ - upper = netdev_upper_get_next_dev_rcu(dev, &(iter)); \ - upper; \ - upper = netdev_upper_get_next_dev_rcu(dev, &(iter))) - -extern struct net_device *netdev_master_upper_dev_get(struct net_device *dev); -extern struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev); -extern int netdev_upper_dev_link(struct net_device *dev, +#define netdev_for_each_all_upper_dev_rcu(dev, updev, iter) \ + for (iter = &(dev)->all_adj_list.upper, \ + updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter)); \ + updev; \ + updev = netdev_all_upper_get_next_dev_rcu(dev, &(iter))) + +void *netdev_lower_get_next_private(struct net_device *dev, + struct list_head **iter); +void *netdev_lower_get_next_private_rcu(struct net_device *dev, + struct list_head **iter); + +#define netdev_for_each_lower_private(dev, priv, iter) \ + for (iter = (dev)->adj_list.lower.next, \ + priv = netdev_lower_get_next_private(dev, &(iter)); \ + priv; \ + priv = netdev_lower_get_next_private(dev, &(iter))) + +#define netdev_for_each_lower_private_rcu(dev, priv, iter) \ + for (iter = &(dev)->adj_list.lower, \ + priv = netdev_lower_get_next_private_rcu(dev, &(iter)); \ + priv; \ + priv = netdev_lower_get_next_private_rcu(dev, &(iter))) + +void *netdev_adjacent_get_private(struct list_head *adj_list); +struct net_device *netdev_master_upper_dev_get(struct net_device *dev); +struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev); +int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev); +int netdev_master_upper_dev_link(struct net_device *dev, struct net_device *upper_dev); -extern int netdev_master_upper_dev_link(struct net_device *dev, - struct net_device *upper_dev); -extern void netdev_upper_dev_unlink(struct net_device *dev, - struct net_device *upper_dev); -extern int skb_checksum_help(struct sk_buff *skb); -extern struct sk_buff *__skb_gso_segment(struct sk_buff *skb, - netdev_features_t features, bool tx_path); -extern struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, - netdev_features_t features); +int netdev_master_upper_dev_link_private(struct net_device *dev, + struct net_device *upper_dev, + void *private); +void netdev_upper_dev_unlink(struct net_device *dev, + struct net_device *upper_dev); +void *netdev_lower_dev_get_private_rcu(struct net_device *dev, + struct net_device *lower_dev); +void *netdev_lower_dev_get_private(struct net_device *dev, + struct net_device *lower_dev); +int skb_checksum_help(struct sk_buff *skb); +struct sk_buff *__skb_gso_segment(struct sk_buff *skb, + netdev_features_t features, bool tx_path); +struct sk_buff *skb_mac_gso_segment(struct sk_buff *skb, + netdev_features_t features); static inline struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features) @@ -2858,26 +2909,26 @@ static inline bool can_checksum_protocol(netdev_features_t features, } #ifdef CONFIG_BUG -extern void netdev_rx_csum_fault(struct net_device *dev); +void netdev_rx_csum_fault(struct net_device *dev); #else static inline void netdev_rx_csum_fault(struct net_device *dev) { } #endif /* rx skb timestamps */ -extern void net_enable_timestamp(void); -extern void net_disable_timestamp(void); +void net_enable_timestamp(void); +void net_disable_timestamp(void); #ifdef CONFIG_PROC_FS -extern int __init dev_proc_init(void); +int __init dev_proc_init(void); #else #define dev_proc_init() 0 #endif -extern int netdev_class_create_file_ns(struct class_attribute *class_attr, - const void *ns); -extern void netdev_class_remove_file_ns(struct class_attribute *class_attr, - const void *ns); +int netdev_class_create_file_ns(struct class_attribute *class_attr, + const void *ns); +void netdev_class_remove_file_ns(struct class_attribute *class_attr, + const void *ns); static inline int netdev_class_create_file(struct class_attribute *class_attr) { @@ -2891,9 +2942,9 @@ static inline void netdev_class_remove_file(struct class_attribute *class_attr) extern struct kobj_ns_type_operations net_ns_type_operations; -extern const char *netdev_drivername(const struct net_device *dev); +const char *netdev_drivername(const struct net_device *dev); -extern void linkwatch_run_queue(void); +void linkwatch_run_queue(void); static inline netdev_features_t netdev_get_wanted_features( struct net_device *dev) @@ -2957,6 +3008,11 @@ static inline void netif_set_gso_max_size(struct net_device *dev, dev->gso_max_size = size; } +static inline bool netif_is_macvlan(struct net_device *dev) +{ + return dev->priv_flags & IFF_MACVLAN; +} + static inline bool netif_is_bond_master(struct net_device *dev) { return dev->flags & IFF_MASTER && dev->priv_flags & IFF_BONDING; @@ -2985,22 +3041,22 @@ static inline const char *netdev_name(const struct net_device *dev) return dev->name; } -extern __printf(3, 4) +__printf(3, 4) int netdev_printk(const char *level, const struct net_device *dev, const char *format, ...); -extern __printf(2, 3) +__printf(2, 3) int netdev_emerg(const struct net_device *dev, const char *format, ...); -extern __printf(2, 3) +__printf(2, 3) int netdev_alert(const struct net_device *dev, const char *format, ...); -extern __printf(2, 3) +__printf(2, 3) int netdev_crit(const struct net_device *dev, const char *format, ...); -extern __printf(2, 3) +__printf(2, 3) int netdev_err(const struct net_device *dev, const char *format, ...); -extern __printf(2, 3) +__printf(2, 3) int netdev_warn(const struct net_device *dev, const char *format, ...); -extern __printf(2, 3) +__printf(2, 3) int netdev_notice(const struct net_device *dev, const char *format, ...); -extern __printf(2, 3) +__printf(2, 3) int netdev_info(const struct net_device *dev, const char *format, ...); #define MODULE_ALIAS_NETDEV(device) \ @@ -3041,7 +3097,7 @@ do { \ * file/line information and a backtrace. */ #define netdev_WARN(dev, format, args...) \ - WARN(1, "netdevice: %s\n" format, netdev_name(dev), ##args); + WARN(1, "netdevice: %s\n" format, netdev_name(dev), ##args) /* netif printk helpers, similar to netdev_printk */ diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 708fe72ab913ba71e5421b080d0c9a8c90c7a3d9..2077489f98873bcbe4ca083cd1f0eb1b99eeab5e 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -35,14 +35,15 @@ static inline void nf_inet_addr_mask(const union nf_inet_addr *a1, result->all[3] = a1->all[3] & mask->all[3]; } -extern int netfilter_init(void); +int netfilter_init(void); /* Largest hook number + 1 */ #define NF_MAX_HOOKS 8 struct sk_buff; -typedef unsigned int nf_hookfn(unsigned int hooknum, +struct nf_hook_ops; +typedef unsigned int nf_hookfn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -52,12 +53,13 @@ struct nf_hook_ops { struct list_head list; /* User fills in from here down. */ - nf_hookfn *hook; - struct module *owner; - u_int8_t pf; - unsigned int hooknum; + nf_hookfn *hook; + struct module *owner; + void *priv; + u_int8_t pf; + unsigned int hooknum; /* Hooks are ordered in ascending priority. */ - int priority; + int priority; }; struct nf_sockopt_ops { @@ -208,7 +210,7 @@ int compat_nf_getsockopt(struct sock *sk, u_int8_t pf, int optval, /* Call this before modifying an existing packet: ensures it is modifiable and linear to the point you care about (writable_len). Returns true or false. */ -extern int skb_make_writable(struct sk_buff *skb, unsigned int writable_len); +int skb_make_writable(struct sk_buff *skb, unsigned int writable_len); struct flowi; struct nf_queue_entry; @@ -269,8 +271,8 @@ nf_checksum_partial(struct sk_buff *skb, unsigned int hook, return csum; } -extern int nf_register_afinfo(const struct nf_afinfo *afinfo); -extern void nf_unregister_afinfo(const struct nf_afinfo *afinfo); +int nf_register_afinfo(const struct nf_afinfo *afinfo); +void nf_unregister_afinfo(const struct nf_afinfo *afinfo); #include extern void (*nf_nat_decode_session_hook)(struct sk_buff *, struct flowi *); @@ -315,7 +317,7 @@ nf_nat_decode_session(struct sk_buff *skb, struct flowi *fl, u_int8_t family) #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) extern void (*ip_ct_attach)(struct sk_buff *, const struct sk_buff *) __rcu; -extern void nf_ct_attach(struct sk_buff *, const struct sk_buff *); +void nf_ct_attach(struct sk_buff *, const struct sk_buff *); extern void (*nf_ct_destroy)(struct nf_conntrack *) __rcu; struct nf_conn; diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index 9ac9fbde7b61a38795d631ab545b9bd3140fe6f6..c7174b8166749501739684d2a938b031b9d4784d 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -49,31 +49,68 @@ enum ip_set_feature { /* Set extensions */ enum ip_set_extension { - IPSET_EXT_NONE = 0, - IPSET_EXT_BIT_TIMEOUT = 1, + IPSET_EXT_BIT_TIMEOUT = 0, IPSET_EXT_TIMEOUT = (1 << IPSET_EXT_BIT_TIMEOUT), - IPSET_EXT_BIT_COUNTER = 2, + IPSET_EXT_BIT_COUNTER = 1, IPSET_EXT_COUNTER = (1 << IPSET_EXT_BIT_COUNTER), -}; - -/* Extension offsets */ -enum ip_set_offset { - IPSET_OFFSET_TIMEOUT = 0, - IPSET_OFFSET_COUNTER, - IPSET_OFFSET_MAX, + IPSET_EXT_BIT_COMMENT = 2, + IPSET_EXT_COMMENT = (1 << IPSET_EXT_BIT_COMMENT), + /* Mark set with an extension which needs to call destroy */ + IPSET_EXT_BIT_DESTROY = 7, + IPSET_EXT_DESTROY = (1 << IPSET_EXT_BIT_DESTROY), }; #define SET_WITH_TIMEOUT(s) ((s)->extensions & IPSET_EXT_TIMEOUT) #define SET_WITH_COUNTER(s) ((s)->extensions & IPSET_EXT_COUNTER) +#define SET_WITH_COMMENT(s) ((s)->extensions & IPSET_EXT_COMMENT) + +/* Extension id, in size order */ +enum ip_set_ext_id { + IPSET_EXT_ID_COUNTER = 0, + IPSET_EXT_ID_TIMEOUT, + IPSET_EXT_ID_COMMENT, + IPSET_EXT_ID_MAX, +}; + +/* Extension type */ +struct ip_set_ext_type { + /* Destroy extension private data (can be NULL) */ + void (*destroy)(void *ext); + enum ip_set_extension type; + enum ipset_cadt_flags flag; + /* Size and minimal alignment */ + u8 len; + u8 align; +}; + +extern const struct ip_set_ext_type ip_set_extensions[]; struct ip_set_ext { - unsigned long timeout; u64 packets; u64 bytes; + u32 timeout; + char *comment; +}; + +struct ip_set_counter { + atomic64_t bytes; + atomic64_t packets; +}; + +struct ip_set_comment { + char *str; }; struct ip_set; +#define ext_timeout(e, s) \ +(unsigned long *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_TIMEOUT]) +#define ext_counter(e, s) \ +(struct ip_set_counter *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COUNTER]) +#define ext_comment(e, s) \ +(struct ip_set_comment *)(((void *)(e)) + (s)->offset[IPSET_EXT_ID_COMMENT]) + + typedef int (*ipset_adtfn)(struct ip_set *set, void *value, const struct ip_set_ext *ext, struct ip_set_ext *mext, u32 cmdflags); @@ -147,7 +184,8 @@ struct ip_set_type { u8 revision_min, revision_max; /* Create set */ - int (*create)(struct ip_set *set, struct nlattr *tb[], u32 flags); + int (*create)(struct net *net, struct ip_set *set, + struct nlattr *tb[], u32 flags); /* Attribute policies */ const struct nla_policy create_policy[IPSET_ATTR_CREATE_MAX + 1]; @@ -179,14 +217,45 @@ struct ip_set { u8 revision; /* Extensions */ u8 extensions; + /* Default timeout value, if enabled */ + u32 timeout; + /* Element data size */ + size_t dsize; + /* Offsets to extensions in elements */ + size_t offset[IPSET_EXT_ID_MAX]; /* The type specific data */ void *data; }; -struct ip_set_counter { - atomic64_t bytes; - atomic64_t packets; -}; +static inline void +ip_set_ext_destroy(struct ip_set *set, void *data) +{ + /* Check that the extension is enabled for the set and + * call it's destroy function for its extension part in data. + */ + if (SET_WITH_COMMENT(set)) + ip_set_extensions[IPSET_EXT_ID_COMMENT].destroy( + ext_comment(data, set)); +} + +static inline int +ip_set_put_flags(struct sk_buff *skb, struct ip_set *set) +{ + u32 cadt_flags = 0; + + if (SET_WITH_TIMEOUT(set)) + if (unlikely(nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(set->timeout)))) + return -EMSGSIZE; + if (SET_WITH_COUNTER(set)) + cadt_flags |= IPSET_FLAG_WITH_COUNTERS; + if (SET_WITH_COMMENT(set)) + cadt_flags |= IPSET_FLAG_WITH_COMMENT; + + if (!cadt_flags) + return 0; + return nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(cadt_flags)); +} static inline void ip_set_add_bytes(u64 bytes, struct ip_set_counter *counter) @@ -247,13 +316,24 @@ ip_set_init_counter(struct ip_set_counter *counter, atomic64_set(&(counter)->packets, (long long)(ext->packets)); } +/* Netlink CB args */ +enum { + IPSET_CB_NET = 0, + IPSET_CB_DUMP, + IPSET_CB_INDEX, + IPSET_CB_ARG0, + IPSET_CB_ARG1, + IPSET_CB_ARG2, +}; + /* register and unregister set references */ -extern ip_set_id_t ip_set_get_byname(const char *name, struct ip_set **set); -extern void ip_set_put_byindex(ip_set_id_t index); -extern const char *ip_set_name_byindex(ip_set_id_t index); -extern ip_set_id_t ip_set_nfnl_get(const char *name); -extern ip_set_id_t ip_set_nfnl_get_byindex(ip_set_id_t index); -extern void ip_set_nfnl_put(ip_set_id_t index); +extern ip_set_id_t ip_set_get_byname(struct net *net, + const char *name, struct ip_set **set); +extern void ip_set_put_byindex(struct net *net, ip_set_id_t index); +extern const char *ip_set_name_byindex(struct net *net, ip_set_id_t index); +extern ip_set_id_t ip_set_nfnl_get(struct net *net, const char *name); +extern ip_set_id_t ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index); +extern void ip_set_nfnl_put(struct net *net, ip_set_id_t index); /* API for iptables set match, and SET target */ @@ -272,6 +352,8 @@ extern void *ip_set_alloc(size_t size); extern void ip_set_free(void *members); extern int ip_set_get_ipaddr4(struct nlattr *nla, __be32 *ipaddr); extern int ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr); +extern size_t ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], + size_t len); extern int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], struct ip_set_ext *ext); @@ -389,13 +471,40 @@ bitmap_bytes(u32 a, u32 b) } #include +#include + +static inline int +ip_set_put_extensions(struct sk_buff *skb, const struct ip_set *set, + const void *e, bool active) +{ + if (SET_WITH_TIMEOUT(set)) { + unsigned long *timeout = ext_timeout(e, set); + + if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, + htonl(active ? ip_set_timeout_get(timeout) + : *timeout))) + return -EMSGSIZE; + } + if (SET_WITH_COUNTER(set) && + ip_set_put_counter(skb, ext_counter(e, set))) + return -EMSGSIZE; + if (SET_WITH_COMMENT(set) && + ip_set_put_comment(skb, ext_comment(e, set))) + return -EMSGSIZE; + return 0; +} -#define IP_SET_INIT_KEXT(skb, opt, map) \ +#define IP_SET_INIT_KEXT(skb, opt, set) \ { .bytes = (skb)->len, .packets = 1, \ - .timeout = ip_set_adt_opt_timeout(opt, map) } + .timeout = ip_set_adt_opt_timeout(opt, set) } -#define IP_SET_INIT_UEXT(map) \ +#define IP_SET_INIT_UEXT(set) \ { .bytes = ULLONG_MAX, .packets = ULLONG_MAX, \ - .timeout = (map)->timeout } + .timeout = (set)->timeout } + +#define IP_SET_INIT_CIDR(a, b) ((a) ? (a) : (b)) + +#define IPSET_CONCAT(a, b) a##b +#define IPSET_TOKEN(a, b) IPSET_CONCAT(a, b) #endif /*_IP_SET_H */ diff --git a/include/linux/netfilter/ipset/ip_set_comment.h b/include/linux/netfilter/ipset/ip_set_comment.h new file mode 100644 index 0000000000000000000000000000000000000000..21217ea008d79611051c17eb7aa0900c3f3e6663 --- /dev/null +++ b/include/linux/netfilter/ipset/ip_set_comment.h @@ -0,0 +1,57 @@ +#ifndef _IP_SET_COMMENT_H +#define _IP_SET_COMMENT_H + +/* Copyright (C) 2013 Oliver Smith + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifdef __KERNEL__ + +static inline char* +ip_set_comment_uget(struct nlattr *tb) +{ + return nla_data(tb); +} + +static inline void +ip_set_init_comment(struct ip_set_comment *comment, + const struct ip_set_ext *ext) +{ + size_t len = ext->comment ? strlen(ext->comment) : 0; + + if (unlikely(comment->str)) { + kfree(comment->str); + comment->str = NULL; + } + if (!len) + return; + if (unlikely(len > IPSET_MAX_COMMENT_SIZE)) + len = IPSET_MAX_COMMENT_SIZE; + comment->str = kzalloc(len + 1, GFP_ATOMIC); + if (unlikely(!comment->str)) + return; + strlcpy(comment->str, ext->comment, len + 1); +} + +static inline int +ip_set_put_comment(struct sk_buff *skb, struct ip_set_comment *comment) +{ + if (!comment->str) + return 0; + return nla_put_string(skb, IPSET_ATTR_COMMENT, comment->str); +} + +static inline void +ip_set_comment_free(struct ip_set_comment *comment) +{ + if (unlikely(!comment->str)) + return; + kfree(comment->str); + comment->str = NULL; +} + +#endif +#endif diff --git a/include/linux/netfilter/ipset/ip_set_timeout.h b/include/linux/netfilter/ipset/ip_set_timeout.h index 3aac04167ca70699a60d63c50486eff9d0cc910a..83c2f9e0886cc537f57bf1db5cbfc035fbe93596 100644 --- a/include/linux/netfilter/ipset/ip_set_timeout.h +++ b/include/linux/netfilter/ipset/ip_set_timeout.h @@ -23,8 +23,8 @@ /* Set is defined with timeout support: timeout value may be 0 */ #define IPSET_NO_TIMEOUT UINT_MAX -#define ip_set_adt_opt_timeout(opt, map) \ -((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (map)->timeout) +#define ip_set_adt_opt_timeout(opt, set) \ +((opt)->ext.timeout != IPSET_NO_TIMEOUT ? (opt)->ext.timeout : (set)->timeout) static inline unsigned int ip_set_timeout_uget(struct nlattr *tb) diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h index 127d0b90604fa08486a88437fbd9a8e09b315148..275505792664ae4e835401a0503d5d3d9fa66808 100644 --- a/include/linux/netfilter/nf_conntrack_common.h +++ b/include/linux/netfilter/nf_conntrack_common.h @@ -23,6 +23,6 @@ struct ip_conntrack_stat { }; /* call to create an explicit dependency on nf_conntrack. */ -extern void need_conntrack(void); +void need_conntrack(void); #endif /* _NF_CONNTRACK_COMMON_H */ diff --git a/include/linux/netfilter/nf_conntrack_h323.h b/include/linux/netfilter/nf_conntrack_h323.h index f381020eee92835fa68ae19858e1a4ca0437138c..858d9b214053ff141e5e856ce98edbf50412bc00 100644 --- a/include/linux/netfilter/nf_conntrack_h323.h +++ b/include/linux/netfilter/nf_conntrack_h323.h @@ -29,13 +29,13 @@ struct nf_ct_h323_master { struct nf_conn; -extern int get_h225_addr(struct nf_conn *ct, unsigned char *data, - TransportAddress *taddr, - union nf_inet_addr *addr, __be16 *port); -extern void nf_conntrack_h245_expect(struct nf_conn *new, - struct nf_conntrack_expect *this); -extern void nf_conntrack_q931_expect(struct nf_conn *new, - struct nf_conntrack_expect *this); +int get_h225_addr(struct nf_conn *ct, unsigned char *data, + TransportAddress *taddr, union nf_inet_addr *addr, + __be16 *port); +void nf_conntrack_h245_expect(struct nf_conn *new, + struct nf_conntrack_expect *this); +void nf_conntrack_q931_expect(struct nf_conn *new, + struct nf_conntrack_expect *this); extern int (*set_h245_addr_hook) (struct sk_buff *skb, unsigned int protoff, unsigned char **data, int dataoff, H245_TransportAddress *taddr, diff --git a/include/linux/netfilter/nf_conntrack_proto_gre.h b/include/linux/netfilter/nf_conntrack_proto_gre.h index 6a0664c0c45197a7abece81ecb35fae6e638d8ae..ec2ffaf418c8e8d31e1cca95fea02a85aa48089f 100644 --- a/include/linux/netfilter/nf_conntrack_proto_gre.h +++ b/include/linux/netfilter/nf_conntrack_proto_gre.h @@ -87,8 +87,8 @@ int nf_ct_gre_keymap_add(struct nf_conn *ct, enum ip_conntrack_dir dir, /* delete keymap entries */ void nf_ct_gre_keymap_destroy(struct nf_conn *ct); -extern void nf_ct_gre_keymap_flush(struct net *net); -extern void nf_nat_need_gre(void); +void nf_ct_gre_keymap_flush(struct net *net); +void nf_nat_need_gre(void); #endif /* __KERNEL__ */ #endif /* _CONNTRACK_PROTO_GRE_H */ diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h index ba7f571a2b1cba1f2381ee50c7030946543f31ac..d5af3c27fb7de0385b11396ac241991bacb3684e 100644 --- a/include/linux/netfilter/nf_conntrack_sip.h +++ b/include/linux/netfilter/nf_conntrack_sip.h @@ -107,85 +107,93 @@ enum sdp_header_types { SDP_HDR_MEDIA, }; -extern unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, - unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen); -extern void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, - unsigned int protoff, s16 off); -extern unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, - unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - struct nf_conntrack_expect *exp, - unsigned int matchoff, - unsigned int matchlen); -extern unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, - unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - unsigned int sdpoff, - enum sdp_header_types type, - enum sdp_header_types term, - const union nf_inet_addr *addr); -extern unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, - unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - unsigned int matchoff, - unsigned int matchlen, - u_int16_t port); -extern unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, - unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - unsigned int sdpoff, - const union nf_inet_addr *addr); -extern unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, - unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - struct nf_conntrack_expect *rtp_exp, - struct nf_conntrack_expect *rtcp_exp, - unsigned int mediaoff, - unsigned int medialen, - union nf_inet_addr *rtp_addr); - -extern int ct_sip_parse_request(const struct nf_conn *ct, - const char *dptr, unsigned int datalen, - unsigned int *matchoff, unsigned int *matchlen, - union nf_inet_addr *addr, __be16 *port); -extern int ct_sip_get_header(const struct nf_conn *ct, const char *dptr, - unsigned int dataoff, unsigned int datalen, - enum sip_header_types type, - unsigned int *matchoff, unsigned int *matchlen); -extern int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, - unsigned int *dataoff, unsigned int datalen, - enum sip_header_types type, int *in_header, - unsigned int *matchoff, unsigned int *matchlen, - union nf_inet_addr *addr, __be16 *port); -extern int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, - unsigned int dataoff, unsigned int datalen, - const char *name, - unsigned int *matchoff, unsigned int *matchlen, - union nf_inet_addr *addr, bool delim); -extern int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, - unsigned int off, unsigned int datalen, - const char *name, - unsigned int *matchoff, unsigned int *matchen, - unsigned int *val); - -extern int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, - unsigned int dataoff, unsigned int datalen, +struct nf_nat_sip_hooks { + unsigned int (*msg)(struct sk_buff *skb, + unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen); + + void (*seq_adjust)(struct sk_buff *skb, + unsigned int protoff, s16 off); + + unsigned int (*expect)(struct sk_buff *skb, + unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + struct nf_conntrack_expect *exp, + unsigned int matchoff, + unsigned int matchlen); + + unsigned int (*sdp_addr)(struct sk_buff *skb, + unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + unsigned int sdpoff, enum sdp_header_types type, enum sdp_header_types term, - unsigned int *matchoff, unsigned int *matchlen); + const union nf_inet_addr *addr); + + unsigned int (*sdp_port)(struct sk_buff *skb, + unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + unsigned int matchoff, + unsigned int matchlen, + u_int16_t port); + + unsigned int (*sdp_session)(struct sk_buff *skb, + unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + unsigned int sdpoff, + const union nf_inet_addr *addr); + + unsigned int (*sdp_media)(struct sk_buff *skb, + unsigned int protoff, + unsigned int dataoff, + const char **dptr, + unsigned int *datalen, + struct nf_conntrack_expect *rtp_exp, + struct nf_conntrack_expect *rtcp_exp, + unsigned int mediaoff, + unsigned int medialen, + union nf_inet_addr *rtp_addr); +}; +extern const struct nf_nat_sip_hooks *nf_nat_sip_hooks; + +int ct_sip_parse_request(const struct nf_conn *ct, const char *dptr, + unsigned int datalen, unsigned int *matchoff, + unsigned int *matchlen, union nf_inet_addr *addr, + __be16 *port); +int ct_sip_get_header(const struct nf_conn *ct, const char *dptr, + unsigned int dataoff, unsigned int datalen, + enum sip_header_types type, unsigned int *matchoff, + unsigned int *matchlen); +int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr, + unsigned int *dataoff, unsigned int datalen, + enum sip_header_types type, int *in_header, + unsigned int *matchoff, unsigned int *matchlen, + union nf_inet_addr *addr, __be16 *port); +int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr, + unsigned int dataoff, unsigned int datalen, + const char *name, unsigned int *matchoff, + unsigned int *matchlen, union nf_inet_addr *addr, + bool delim); +int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr, + unsigned int off, unsigned int datalen, + const char *name, unsigned int *matchoff, + unsigned int *matchen, unsigned int *val); + +int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr, + unsigned int dataoff, unsigned int datalen, + enum sdp_header_types type, + enum sdp_header_types term, + unsigned int *matchoff, unsigned int *matchlen); #endif /* __KERNEL__ */ #endif /* __NF_CONNTRACK_SIP_H__ */ diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h index cadb7402d7a713fdcedbe4b3cb77c6095854f3ff..28c74367e900ac679aa5feba98b7629ea6338af3 100644 --- a/include/linux/netfilter/nfnetlink.h +++ b/include/linux/netfilter/nfnetlink.h @@ -14,6 +14,9 @@ struct nfnl_callback { int (*call_rcu)(struct sock *nl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const cda[]); + int (*call_batch)(struct sock *nl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]); const struct nla_policy *policy; /* netlink attribute policy */ const u_int16_t attr_count; /* number of nlattr's */ }; @@ -23,22 +26,24 @@ struct nfnetlink_subsystem { __u8 subsys_id; /* nfnetlink subsystem ID */ __u8 cb_count; /* number of callbacks */ const struct nfnl_callback *cb; /* callback for individual types */ + int (*commit)(struct sk_buff *skb); + int (*abort)(struct sk_buff *skb); }; -extern int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n); -extern int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); +int nfnetlink_subsys_register(const struct nfnetlink_subsystem *n); +int nfnetlink_subsys_unregister(const struct nfnetlink_subsystem *n); -extern int nfnetlink_has_listeners(struct net *net, unsigned int group); -extern struct sk_buff *nfnetlink_alloc_skb(struct net *net, unsigned int size, - u32 dst_portid, gfp_t gfp_mask); -extern int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid, - unsigned int group, int echo, gfp_t flags); -extern int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error); -extern int nfnetlink_unicast(struct sk_buff *skb, struct net *net, - u32 portid, int flags); +int nfnetlink_has_listeners(struct net *net, unsigned int group); +struct sk_buff *nfnetlink_alloc_skb(struct net *net, unsigned int size, + u32 dst_portid, gfp_t gfp_mask); +int nfnetlink_send(struct sk_buff *skb, struct net *net, u32 portid, + unsigned int group, int echo, gfp_t flags); +int nfnetlink_set_err(struct net *net, u32 portid, u32 group, int error); +int nfnetlink_unicast(struct sk_buff *skb, struct net *net, u32 portid, + int flags); -extern void nfnl_lock(__u8 subsys_id); -extern void nfnl_unlock(__u8 subsys_id); +void nfnl_lock(__u8 subsys_id); +void nfnl_unlock(__u8 subsys_id); #define MODULE_ALIAS_NFNL_SUBSYS(subsys) \ MODULE_ALIAS("nfnetlink-subsys-" __stringify(subsys)) diff --git a/include/linux/netfilter/nfnetlink_acct.h b/include/linux/netfilter/nfnetlink_acct.h index bb4bbc9b7a18c4834724eabe574b76670806c8c4..b2e85e59f76085cb0e56294c78b63d3a04a0762a 100644 --- a/include/linux/netfilter/nfnetlink_acct.h +++ b/include/linux/netfilter/nfnetlink_acct.h @@ -6,8 +6,8 @@ struct nf_acct; -extern struct nf_acct *nfnl_acct_find_get(const char *filter_name); -extern void nfnl_acct_put(struct nf_acct *acct); -extern void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct); +struct nf_acct *nfnl_acct_find_get(const char *filter_name); +void nfnl_acct_put(struct nf_acct *acct); +void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct); #endif /* _NFNL_ACCT_H */ diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h index dd49566315c616f1d4db4cba47ee1dc9ecaefa26..a3e215bb0241d47379bce4ff6e81ba2b4995d3c3 100644 --- a/include/linux/netfilter/x_tables.h +++ b/include/linux/netfilter/x_tables.h @@ -229,50 +229,48 @@ struct xt_table_info { #define XT_TABLE_INFO_SZ (offsetof(struct xt_table_info, entries) \ + nr_cpu_ids * sizeof(char *)) -extern int xt_register_target(struct xt_target *target); -extern void xt_unregister_target(struct xt_target *target); -extern int xt_register_targets(struct xt_target *target, unsigned int n); -extern void xt_unregister_targets(struct xt_target *target, unsigned int n); - -extern int xt_register_match(struct xt_match *target); -extern void xt_unregister_match(struct xt_match *target); -extern int xt_register_matches(struct xt_match *match, unsigned int n); -extern void xt_unregister_matches(struct xt_match *match, unsigned int n); - -extern int xt_check_match(struct xt_mtchk_param *, - unsigned int size, u_int8_t proto, bool inv_proto); -extern int xt_check_target(struct xt_tgchk_param *, - unsigned int size, u_int8_t proto, bool inv_proto); - -extern struct xt_table *xt_register_table(struct net *net, - const struct xt_table *table, - struct xt_table_info *bootstrap, - struct xt_table_info *newinfo); -extern void *xt_unregister_table(struct xt_table *table); - -extern struct xt_table_info *xt_replace_table(struct xt_table *table, - unsigned int num_counters, - struct xt_table_info *newinfo, - int *error); - -extern struct xt_match *xt_find_match(u8 af, const char *name, u8 revision); -extern struct xt_target *xt_find_target(u8 af, const char *name, u8 revision); -extern struct xt_match *xt_request_find_match(u8 af, const char *name, - u8 revision); -extern struct xt_target *xt_request_find_target(u8 af, const char *name, - u8 revision); -extern int xt_find_revision(u8 af, const char *name, u8 revision, - int target, int *err); - -extern struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, - const char *name); -extern void xt_table_unlock(struct xt_table *t); - -extern int xt_proto_init(struct net *net, u_int8_t af); -extern void xt_proto_fini(struct net *net, u_int8_t af); - -extern struct xt_table_info *xt_alloc_table_info(unsigned int size); -extern void xt_free_table_info(struct xt_table_info *info); +int xt_register_target(struct xt_target *target); +void xt_unregister_target(struct xt_target *target); +int xt_register_targets(struct xt_target *target, unsigned int n); +void xt_unregister_targets(struct xt_target *target, unsigned int n); + +int xt_register_match(struct xt_match *target); +void xt_unregister_match(struct xt_match *target); +int xt_register_matches(struct xt_match *match, unsigned int n); +void xt_unregister_matches(struct xt_match *match, unsigned int n); + +int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto, + bool inv_proto); +int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto, + bool inv_proto); + +struct xt_table *xt_register_table(struct net *net, + const struct xt_table *table, + struct xt_table_info *bootstrap, + struct xt_table_info *newinfo); +void *xt_unregister_table(struct xt_table *table); + +struct xt_table_info *xt_replace_table(struct xt_table *table, + unsigned int num_counters, + struct xt_table_info *newinfo, + int *error); + +struct xt_match *xt_find_match(u8 af, const char *name, u8 revision); +struct xt_target *xt_find_target(u8 af, const char *name, u8 revision); +struct xt_match *xt_request_find_match(u8 af, const char *name, u8 revision); +struct xt_target *xt_request_find_target(u8 af, const char *name, u8 revision); +int xt_find_revision(u8 af, const char *name, u8 revision, int target, + int *err); + +struct xt_table *xt_find_table_lock(struct net *net, u_int8_t af, + const char *name); +void xt_table_unlock(struct xt_table *t); + +int xt_proto_init(struct net *net, u_int8_t af); +void xt_proto_fini(struct net *net, u_int8_t af); + +struct xt_table_info *xt_alloc_table_info(unsigned int size); +void xt_free_table_info(struct xt_table_info *info); /** * xt_recseq - recursive seqcount for netfilter use @@ -353,8 +351,8 @@ static inline unsigned long ifname_compare_aligned(const char *_a, return ret; } -extern struct nf_hook_ops *xt_hook_link(const struct xt_table *, nf_hookfn *); -extern void xt_hook_unlink(const struct xt_table *, struct nf_hook_ops *); +struct nf_hook_ops *xt_hook_link(const struct xt_table *, nf_hookfn *); +void xt_hook_unlink(const struct xt_table *, struct nf_hook_ops *); #ifdef CONFIG_COMPAT #include @@ -414,25 +412,25 @@ struct _compat_xt_align { #define COMPAT_XT_ALIGN(s) __ALIGN_KERNEL((s), __alignof__(struct _compat_xt_align)) -extern void xt_compat_lock(u_int8_t af); -extern void xt_compat_unlock(u_int8_t af); - -extern int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta); -extern void xt_compat_flush_offsets(u_int8_t af); -extern void xt_compat_init_offsets(u_int8_t af, unsigned int number); -extern int xt_compat_calc_jump(u_int8_t af, unsigned int offset); - -extern int xt_compat_match_offset(const struct xt_match *match); -extern int xt_compat_match_from_user(struct xt_entry_match *m, - void **dstptr, unsigned int *size); -extern int xt_compat_match_to_user(const struct xt_entry_match *m, - void __user **dstptr, unsigned int *size); - -extern int xt_compat_target_offset(const struct xt_target *target); -extern void xt_compat_target_from_user(struct xt_entry_target *t, - void **dstptr, unsigned int *size); -extern int xt_compat_target_to_user(const struct xt_entry_target *t, - void __user **dstptr, unsigned int *size); +void xt_compat_lock(u_int8_t af); +void xt_compat_unlock(u_int8_t af); + +int xt_compat_add_offset(u_int8_t af, unsigned int offset, int delta); +void xt_compat_flush_offsets(u_int8_t af); +void xt_compat_init_offsets(u_int8_t af, unsigned int number); +int xt_compat_calc_jump(u_int8_t af, unsigned int offset); + +int xt_compat_match_offset(const struct xt_match *match); +int xt_compat_match_from_user(struct xt_entry_match *m, void **dstptr, + unsigned int *size); +int xt_compat_match_to_user(const struct xt_entry_match *m, + void __user **dstptr, unsigned int *size); + +int xt_compat_target_offset(const struct xt_target *target); +void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr, + unsigned int *size); +int xt_compat_target_to_user(const struct xt_entry_target *t, + void __user **dstptr, unsigned int *size); #endif /* CONFIG_COMPAT */ #endif /* _X_TABLES_H */ diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index dfb4d9e52bcb3a1775eebd627d1d758aaaed6c4b..8ab1c278b66da77229647e08ef8e0d948d4e6122 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -25,7 +25,7 @@ enum nf_br_hook_priorities { #define BRNF_PPPoE 0x20 /* Only used in br_forward.c */ -extern int nf_bridge_copy_header(struct sk_buff *skb); +int nf_bridge_copy_header(struct sk_buff *skb); static inline int nf_bridge_maybe_copy_header(struct sk_buff *skb) { if (skb->nf_bridge && @@ -53,7 +53,7 @@ static inline unsigned int nf_bridge_mtu_reduction(const struct sk_buff *skb) return 0; } -extern int br_handle_frame_finish(struct sk_buff *skb); +int br_handle_frame_finish(struct sk_buff *skb); /* Only used in br_device.c */ static inline int br_nf_pre_routing_finish_bridge_slow(struct sk_buff *skb) { diff --git a/include/linux/netfilter_ipv4.h b/include/linux/netfilter_ipv4.h index dfaf116b3e8125a3f36511bfe35b64fc81d463e3..6e4591bb54d495d2f4ee3f82058305be7539d025 100644 --- a/include/linux/netfilter_ipv4.h +++ b/include/linux/netfilter_ipv4.h @@ -6,7 +6,7 @@ #include -extern int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type); -extern __sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook, - unsigned int dataoff, u_int8_t protocol); +int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type); +__sum16 nf_ip_checksum(struct sk_buff *skb, unsigned int hook, + unsigned int dataoff, u_int8_t protocol); #endif /*__LINUX_IP_NETFILTER_H*/ diff --git a/include/linux/netfilter_ipv6.h b/include/linux/netfilter_ipv6.h index 2d4df6ce043efab2f9017bc5359b90846f085fb7..64dad1cc1a4bc86d391e8e9cbffea5091b9bfa53 100644 --- a/include/linux/netfilter_ipv6.h +++ b/include/linux/netfilter_ipv6.h @@ -11,12 +11,12 @@ #ifdef CONFIG_NETFILTER -extern int ip6_route_me_harder(struct sk_buff *skb); -extern __sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, - unsigned int dataoff, u_int8_t protocol); +int ip6_route_me_harder(struct sk_buff *skb); +__sum16 nf_ip6_checksum(struct sk_buff *skb, unsigned int hook, + unsigned int dataoff, u_int8_t protocol); -extern int ipv6_netfilter_init(void); -extern void ipv6_netfilter_fini(void); +int ipv6_netfilter_init(void); +void ipv6_netfilter_fini(void); /* * Hook functions for ipv6 to allow xt_* modules to be built-in even diff --git a/include/linux/random.h b/include/linux/random.h index 6312dd9ba449b4d65f5bb6bcdc01d606fc2fdfa8..4002b3df4c8566d4497a9dae222e6fc1e6e37b66 100644 --- a/include/linux/random.h +++ b/include/linux/random.h @@ -29,8 +29,13 @@ unsigned long randomize_range(unsigned long start, unsigned long end, unsigned l u32 prandom_u32(void); void prandom_bytes(void *buf, int nbytes); void prandom_seed(u32 seed); +void prandom_reseed_late(void); -u32 prandom_u32_state(struct rnd_state *); +struct rnd_state { + __u32 s1, s2, s3, s4; +}; + +u32 prandom_u32_state(struct rnd_state *state); void prandom_bytes_state(struct rnd_state *state, void *buf, int nbytes); /* @@ -50,9 +55,10 @@ static inline void prandom_seed_state(struct rnd_state *state, u64 seed) { u32 i = (seed >> 32) ^ (seed << 10) ^ seed; - state->s1 = __seed(i, 1); - state->s2 = __seed(i, 7); - state->s3 = __seed(i, 15); + state->s1 = __seed(i, 2U); + state->s2 = __seed(i, 8U); + state->s3 = __seed(i, 16U); + state->s4 = __seed(i, 128U); } #ifdef CONFIG_ARCH_RANDOM diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index f28544b2f9af02b4dd2ebcfe5f9bf28a088e1f0d..939428ad25acb506297c1726faaee83d60d047c4 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -15,7 +15,7 @@ extern int rtnetlink_put_metrics(struct sk_buff *skb, u32 *metrics); extern int rtnl_put_cacheinfo(struct sk_buff *skb, struct dst_entry *dst, u32 id, long expires, u32 error); -extern void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change); +void rtmsg_ifinfo(int type, struct net_device *dev, unsigned change, gfp_t flags); /* RTNL is used as a global lock for all changes to network configuration */ extern void rtnl_lock(void); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index c2d89335f6370d6e72a06470290a2a6e8a4cac7b..215b5ea1cb302c43f0e8b9532fbe3ce59ff0f612 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -318,9 +318,13 @@ enum { SKB_GSO_GRE = 1 << 6, - SKB_GSO_UDP_TUNNEL = 1 << 7, + SKB_GSO_IPIP = 1 << 7, - SKB_GSO_MPLS = 1 << 8, + SKB_GSO_SIT = 1 << 8, + + SKB_GSO_UDP_TUNNEL = 1 << 9, + + SKB_GSO_MPLS = 1 << 10, }; #if BITS_PER_LONG > 32 @@ -333,11 +337,6 @@ typedef unsigned int sk_buff_data_t; typedef unsigned char *sk_buff_data_t; #endif -#if defined(CONFIG_NF_DEFRAG_IPV4) || defined(CONFIG_NF_DEFRAG_IPV4_MODULE) || \ - defined(CONFIG_NF_DEFRAG_IPV6) || defined(CONFIG_NF_DEFRAG_IPV6_MODULE) -#define NET_SKBUFF_NF_DEFRAG_NEEDED 1 -#endif - /** * struct sk_buff - socket buffer * @next: Next buffer in list @@ -370,7 +369,6 @@ typedef unsigned char *sk_buff_data_t; * @protocol: Packet protocol from driver * @destructor: Destruct function * @nfct: Associated connection, if any - * @nfct_reasm: netfilter conntrack re-assembly pointer * @nf_bridge: Saved data about a bridged frame - see br_netfilter.c * @skb_iif: ifindex of device we arrived on * @tc_index: Traffic control index @@ -459,9 +457,6 @@ struct sk_buff { #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct nf_conntrack *nfct; #endif -#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED - struct sk_buff *nfct_reasm; -#endif #ifdef CONFIG_BRIDGE_NETFILTER struct nf_bridge_info *nf_bridge; #endif @@ -585,8 +580,8 @@ static inline void skb_dst_set(struct sk_buff *skb, struct dst_entry *dst) skb->_skb_refdst = (unsigned long)dst; } -extern void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst, - bool force); +void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst, + bool force); /** * skb_dst_set_noref - sets skb dst, hopefully, without taking reference @@ -634,20 +629,20 @@ static inline struct rtable *skb_rtable(const struct sk_buff *skb) return (struct rtable *)skb_dst(skb); } -extern void kfree_skb(struct sk_buff *skb); -extern void kfree_skb_list(struct sk_buff *segs); -extern void skb_tx_error(struct sk_buff *skb); -extern void consume_skb(struct sk_buff *skb); -extern void __kfree_skb(struct sk_buff *skb); +void kfree_skb(struct sk_buff *skb); +void kfree_skb_list(struct sk_buff *segs); +void skb_tx_error(struct sk_buff *skb); +void consume_skb(struct sk_buff *skb); +void __kfree_skb(struct sk_buff *skb); extern struct kmem_cache *skbuff_head_cache; -extern void kfree_skb_partial(struct sk_buff *skb, bool head_stolen); -extern bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, - bool *fragstolen, int *delta_truesize); +void kfree_skb_partial(struct sk_buff *skb, bool head_stolen); +bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, + bool *fragstolen, int *delta_truesize); -extern struct sk_buff *__alloc_skb(unsigned int size, - gfp_t priority, int flags, int node); -extern struct sk_buff *build_skb(void *data, unsigned int frag_size); +struct sk_buff *__alloc_skb(unsigned int size, gfp_t priority, int flags, + int node); +struct sk_buff *build_skb(void *data, unsigned int frag_size); static inline struct sk_buff *alloc_skb(unsigned int size, gfp_t priority) { @@ -660,41 +655,33 @@ static inline struct sk_buff *alloc_skb_fclone(unsigned int size, return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE); } -extern struct sk_buff *__alloc_skb_head(gfp_t priority, int node); +struct sk_buff *__alloc_skb_head(gfp_t priority, int node); static inline struct sk_buff *alloc_skb_head(gfp_t priority) { return __alloc_skb_head(priority, -1); } -extern struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src); -extern int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask); -extern struct sk_buff *skb_clone(struct sk_buff *skb, - gfp_t priority); -extern struct sk_buff *skb_copy(const struct sk_buff *skb, - gfp_t priority); -extern struct sk_buff *__pskb_copy(struct sk_buff *skb, - int headroom, gfp_t gfp_mask); - -extern int pskb_expand_head(struct sk_buff *skb, - int nhead, int ntail, - gfp_t gfp_mask); -extern struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, - unsigned int headroom); -extern struct sk_buff *skb_copy_expand(const struct sk_buff *skb, - int newheadroom, int newtailroom, - gfp_t priority); -extern int skb_to_sgvec(struct sk_buff *skb, - struct scatterlist *sg, int offset, - int len); -extern int skb_cow_data(struct sk_buff *skb, int tailbits, - struct sk_buff **trailer); -extern int skb_pad(struct sk_buff *skb, int pad); +struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src); +int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask); +struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority); +struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t priority); +struct sk_buff *__pskb_copy(struct sk_buff *skb, int headroom, gfp_t gfp_mask); + +int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, gfp_t gfp_mask); +struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, + unsigned int headroom); +struct sk_buff *skb_copy_expand(const struct sk_buff *skb, int newheadroom, + int newtailroom, gfp_t priority); +int skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, + int len); +int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer); +int skb_pad(struct sk_buff *skb, int pad); #define dev_kfree_skb(a) consume_skb(a) -extern int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, - int getfrag(void *from, char *to, int offset, - int len,int odd, struct sk_buff *skb), - void *from, int length); +int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, + int getfrag(void *from, char *to, int offset, + int len, int odd, struct sk_buff *skb), + void *from, int length); struct skb_seq_state { __u32 lower_offset; @@ -706,18 +693,17 @@ struct skb_seq_state { __u8 *frag_data; }; -extern void skb_prepare_seq_read(struct sk_buff *skb, - unsigned int from, unsigned int to, - struct skb_seq_state *st); -extern unsigned int skb_seq_read(unsigned int consumed, const u8 **data, - struct skb_seq_state *st); -extern void skb_abort_seq_read(struct skb_seq_state *st); +void skb_prepare_seq_read(struct sk_buff *skb, unsigned int from, + unsigned int to, struct skb_seq_state *st); +unsigned int skb_seq_read(unsigned int consumed, const u8 **data, + struct skb_seq_state *st); +void skb_abort_seq_read(struct skb_seq_state *st); -extern unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, - unsigned int to, struct ts_config *config, - struct ts_state *state); +unsigned int skb_find_text(struct sk_buff *skb, unsigned int from, + unsigned int to, struct ts_config *config, + struct ts_state *state); -extern void __skb_get_rxhash(struct sk_buff *skb); +void __skb_get_rxhash(struct sk_buff *skb); static inline __u32 skb_get_rxhash(struct sk_buff *skb) { if (!skb->l4_rxhash) @@ -1095,7 +1081,8 @@ static inline void skb_queue_head_init_class(struct sk_buff_head *list, * The "__skb_xxxx()" functions are the non-atomic ones that * can only be called with interrupts disabled. */ -extern void skb_insert(struct sk_buff *old, struct sk_buff *newsk, struct sk_buff_head *list); +void skb_insert(struct sk_buff *old, struct sk_buff *newsk, + struct sk_buff_head *list); static inline void __skb_insert(struct sk_buff *newsk, struct sk_buff *prev, struct sk_buff *next, struct sk_buff_head *list) @@ -1201,8 +1188,8 @@ static inline void __skb_queue_after(struct sk_buff_head *list, __skb_insert(newsk, prev, prev->next, list); } -extern void skb_append(struct sk_buff *old, struct sk_buff *newsk, - struct sk_buff_head *list); +void skb_append(struct sk_buff *old, struct sk_buff *newsk, + struct sk_buff_head *list); static inline void __skb_queue_before(struct sk_buff_head *list, struct sk_buff *next, @@ -1221,7 +1208,7 @@ static inline void __skb_queue_before(struct sk_buff_head *list, * * A buffer cannot be placed on two lists at the same time. */ -extern void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk); +void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk); static inline void __skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk) { @@ -1238,7 +1225,7 @@ static inline void __skb_queue_head(struct sk_buff_head *list, * * A buffer cannot be placed on two lists at the same time. */ -extern void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk); +void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk); static inline void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk) { @@ -1249,7 +1236,7 @@ static inline void __skb_queue_tail(struct sk_buff_head *list, * remove sk_buff from list. _Must_ be called atomically, and with * the list known.. */ -extern void skb_unlink(struct sk_buff *skb, struct sk_buff_head *list); +void skb_unlink(struct sk_buff *skb, struct sk_buff_head *list); static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list) { struct sk_buff *next, *prev; @@ -1270,7 +1257,7 @@ static inline void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list) * so must be used with appropriate locks held only. The head item is * returned or %NULL if the list is empty. */ -extern struct sk_buff *skb_dequeue(struct sk_buff_head *list); +struct sk_buff *skb_dequeue(struct sk_buff_head *list); static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list) { struct sk_buff *skb = skb_peek(list); @@ -1287,7 +1274,7 @@ static inline struct sk_buff *__skb_dequeue(struct sk_buff_head *list) * so must be used with appropriate locks held only. The tail item is * returned or %NULL if the list is empty. */ -extern struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list); +struct sk_buff *skb_dequeue_tail(struct sk_buff_head *list); static inline struct sk_buff *__skb_dequeue_tail(struct sk_buff_head *list) { struct sk_buff *skb = skb_peek_tail(list); @@ -1361,7 +1348,7 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i, * @size: the length of the data * * As per __skb_fill_page_desc() -- initialises the @i'th fragment of - * @skb to point to &size bytes at offset @off within @page. In + * @skb to point to @size bytes at offset @off within @page. In * addition updates @skb such that @i is the last fragment. * * Does not take any additional reference on the fragment. @@ -1373,8 +1360,11 @@ static inline void skb_fill_page_desc(struct sk_buff *skb, int i, skb_shinfo(skb)->nr_frags = i + 1; } -extern void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, - int off, int size, unsigned int truesize); +void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, + int size, unsigned int truesize); + +void skb_coalesce_rx_frag(struct sk_buff *skb, int i, int size, + unsigned int truesize); #define SKB_PAGE_ASSERT(skb) BUG_ON(skb_shinfo(skb)->nr_frags) #define SKB_FRAG_ASSERT(skb) BUG_ON(skb_has_frag_list(skb)) @@ -1418,7 +1408,8 @@ static inline void skb_set_tail_pointer(struct sk_buff *skb, const int offset) /* * Add data to an sk_buff */ -extern unsigned char *skb_put(struct sk_buff *skb, unsigned int len); +unsigned char *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len); +unsigned char *skb_put(struct sk_buff *skb, unsigned int len); static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len) { unsigned char *tmp = skb_tail_pointer(skb); @@ -1428,7 +1419,7 @@ static inline unsigned char *__skb_put(struct sk_buff *skb, unsigned int len) return tmp; } -extern unsigned char *skb_push(struct sk_buff *skb, unsigned int len); +unsigned char *skb_push(struct sk_buff *skb, unsigned int len); static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len) { skb->data -= len; @@ -1436,7 +1427,7 @@ static inline unsigned char *__skb_push(struct sk_buff *skb, unsigned int len) return skb->data; } -extern unsigned char *skb_pull(struct sk_buff *skb, unsigned int len); +unsigned char *skb_pull(struct sk_buff *skb, unsigned int len); static inline unsigned char *__skb_pull(struct sk_buff *skb, unsigned int len) { skb->len -= len; @@ -1449,7 +1440,7 @@ static inline unsigned char *skb_pull_inline(struct sk_buff *skb, unsigned int l return unlikely(len > skb->len) ? NULL : __skb_pull(skb, len); } -extern unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta); +unsigned char *__pskb_pull_tail(struct sk_buff *skb, int delta); static inline unsigned char *__pskb_pull(struct sk_buff *skb, unsigned int len) { @@ -1753,7 +1744,7 @@ static inline int pskb_network_may_pull(struct sk_buff *skb, unsigned int len) #define NET_SKB_PAD max(32, L1_CACHE_BYTES) #endif -extern int ___pskb_trim(struct sk_buff *skb, unsigned int len); +int ___pskb_trim(struct sk_buff *skb, unsigned int len); static inline void __skb_trim(struct sk_buff *skb, unsigned int len) { @@ -1765,7 +1756,7 @@ static inline void __skb_trim(struct sk_buff *skb, unsigned int len) skb_set_tail_pointer(skb, len); } -extern void skb_trim(struct sk_buff *skb, unsigned int len); +void skb_trim(struct sk_buff *skb, unsigned int len); static inline int __pskb_trim(struct sk_buff *skb, unsigned int len) { @@ -1838,7 +1829,7 @@ static inline int skb_orphan_frags(struct sk_buff *skb, gfp_t gfp_mask) * the list and one reference dropped. This function does not take the * list lock and the caller must hold the relevant locks to use it. */ -extern void skb_queue_purge(struct sk_buff_head *list); +void skb_queue_purge(struct sk_buff_head *list); static inline void __skb_queue_purge(struct sk_buff_head *list) { struct sk_buff *skb; @@ -1850,11 +1841,10 @@ static inline void __skb_queue_purge(struct sk_buff_head *list) #define NETDEV_FRAG_PAGE_MAX_SIZE (PAGE_SIZE << NETDEV_FRAG_PAGE_MAX_ORDER) #define NETDEV_PAGECNT_MAX_BIAS NETDEV_FRAG_PAGE_MAX_SIZE -extern void *netdev_alloc_frag(unsigned int fragsz); +void *netdev_alloc_frag(unsigned int fragsz); -extern struct sk_buff *__netdev_alloc_skb(struct net_device *dev, - unsigned int length, - gfp_t gfp_mask); +struct sk_buff *__netdev_alloc_skb(struct net_device *dev, unsigned int length, + gfp_t gfp_mask); /** * netdev_alloc_skb - allocate an skbuff for rx on a specific device @@ -2071,6 +2061,8 @@ static inline void skb_frag_set_page(struct sk_buff *skb, int f, __skb_frag_set_page(&skb_shinfo(skb)->frags[f], page); } +bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t prio); + /** * skb_frag_dma_map - maps a paged fragment via the DMA API * @dev: the device to map the fragment to @@ -2342,60 +2334,49 @@ static inline void skb_frag_add_head(struct sk_buff *skb, struct sk_buff *frag) #define skb_walk_frags(skb, iter) \ for (iter = skb_shinfo(skb)->frag_list; iter; iter = iter->next) -extern struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, - int *peeked, int *off, int *err); -extern struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, - int noblock, int *err); -extern unsigned int datagram_poll(struct file *file, struct socket *sock, - struct poll_table_struct *wait); -extern int skb_copy_datagram_iovec(const struct sk_buff *from, - int offset, struct iovec *to, - int size); -extern int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, - int hlen, - struct iovec *iov); -extern int skb_copy_datagram_from_iovec(struct sk_buff *skb, - int offset, - const struct iovec *from, - int from_offset, - int len); -extern int zerocopy_sg_from_iovec(struct sk_buff *skb, - const struct iovec *frm, - int offset, - size_t count); -extern int skb_copy_datagram_const_iovec(const struct sk_buff *from, - int offset, - const struct iovec *to, - int to_offset, - int size); -extern void skb_free_datagram(struct sock *sk, struct sk_buff *skb); -extern void skb_free_datagram_locked(struct sock *sk, - struct sk_buff *skb); -extern int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, - unsigned int flags); -extern __wsum skb_checksum(const struct sk_buff *skb, int offset, - int len, __wsum csum); -extern int skb_copy_bits(const struct sk_buff *skb, int offset, - void *to, int len); -extern int skb_store_bits(struct sk_buff *skb, int offset, - const void *from, int len); -extern __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, - int offset, u8 *to, int len, - __wsum csum); -extern int skb_splice_bits(struct sk_buff *skb, - unsigned int offset, - struct pipe_inode_info *pipe, - unsigned int len, - unsigned int flags); -extern void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); -extern void skb_split(struct sk_buff *skb, - struct sk_buff *skb1, const u32 len); -extern int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, - int shiftlen); -extern void skb_scrub_packet(struct sk_buff *skb, bool xnet); - -extern struct sk_buff *skb_segment(struct sk_buff *skb, - netdev_features_t features); +struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned flags, + int *peeked, int *off, int *err); +struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, + int *err); +unsigned int datagram_poll(struct file *file, struct socket *sock, + struct poll_table_struct *wait); +int skb_copy_datagram_iovec(const struct sk_buff *from, int offset, + struct iovec *to, int size); +int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, int hlen, + struct iovec *iov); +int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, + const struct iovec *from, int from_offset, + int len); +int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *frm, + int offset, size_t count); +int skb_copy_datagram_const_iovec(const struct sk_buff *from, int offset, + const struct iovec *to, int to_offset, + int size); +void skb_free_datagram(struct sock *sk, struct sk_buff *skb); +void skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb); +int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags); +int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len); +int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len); +__wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, + int len, __wsum csum); +int skb_splice_bits(struct sk_buff *skb, unsigned int offset, + struct pipe_inode_info *pipe, unsigned int len, + unsigned int flags); +void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); +void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len); +int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen); +void skb_scrub_packet(struct sk_buff *skb, bool xnet); +struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features); + +struct skb_checksum_ops { + __wsum (*update)(const void *mem, int len, __wsum wsum); + __wsum (*combine)(__wsum csum, __wsum csum2, int offset, int len); +}; + +__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len, + __wsum csum, const struct skb_checksum_ops *ops); +__wsum skb_checksum(const struct sk_buff *skb, int offset, int len, + __wsum csum); static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) @@ -2440,7 +2421,7 @@ static inline void skb_copy_to_linear_data_offset(struct sk_buff *skb, memcpy(skb->data + offset, from, len); } -extern void skb_init(void); +void skb_init(void); static inline ktime_t skb_get_ktime(const struct sk_buff *skb) { @@ -2483,12 +2464,12 @@ static inline ktime_t net_invalid_timestamp(void) return ktime_set(0, 0); } -extern void skb_timestamping_init(void); +void skb_timestamping_init(void); #ifdef CONFIG_NETWORK_PHY_TIMESTAMPING -extern void skb_clone_tx_timestamp(struct sk_buff *skb); -extern bool skb_defer_rx_timestamp(struct sk_buff *skb); +void skb_clone_tx_timestamp(struct sk_buff *skb); +bool skb_defer_rx_timestamp(struct sk_buff *skb); #else /* CONFIG_NETWORK_PHY_TIMESTAMPING */ @@ -2529,8 +2510,8 @@ void skb_complete_tx_timestamp(struct sk_buff *skb, * generates a software time stamp (otherwise), then queues the clone * to the error queue of the socket. Errors are silently ignored. */ -extern void skb_tstamp_tx(struct sk_buff *orig_skb, - struct skb_shared_hwtstamps *hwtstamps); +void skb_tstamp_tx(struct sk_buff *orig_skb, + struct skb_shared_hwtstamps *hwtstamps); static inline void sw_tx_timestamp(struct sk_buff *skb) { @@ -2562,8 +2543,8 @@ static inline void skb_tx_timestamp(struct sk_buff *skb) */ void skb_complete_wifi_ack(struct sk_buff *skb, bool acked); -extern __sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); -extern __sum16 __skb_checksum_complete(struct sk_buff *skb); +__sum16 __skb_checksum_complete_head(struct sk_buff *skb, int len); +__sum16 __skb_checksum_complete(struct sk_buff *skb); static inline int skb_csum_unnecessary(const struct sk_buff *skb) { @@ -2593,7 +2574,7 @@ static inline __sum16 skb_checksum_complete(struct sk_buff *skb) } #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) -extern void nf_conntrack_destroy(struct nf_conntrack *nfct); +void nf_conntrack_destroy(struct nf_conntrack *nfct); static inline void nf_conntrack_put(struct nf_conntrack *nfct) { if (nfct && atomic_dec_and_test(&nfct->use)) @@ -2605,18 +2586,6 @@ static inline void nf_conntrack_get(struct nf_conntrack *nfct) atomic_inc(&nfct->use); } #endif -#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED -static inline void nf_conntrack_get_reasm(struct sk_buff *skb) -{ - if (skb) - atomic_inc(&skb->users); -} -static inline void nf_conntrack_put_reasm(struct sk_buff *skb) -{ - if (skb) - kfree_skb(skb); -} -#endif #ifdef CONFIG_BRIDGE_NETFILTER static inline void nf_bridge_put(struct nf_bridge_info *nf_bridge) { @@ -2635,10 +2604,6 @@ static inline void nf_reset(struct sk_buff *skb) nf_conntrack_put(skb->nfct); skb->nfct = NULL; #endif -#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED - nf_conntrack_put_reasm(skb->nfct_reasm); - skb->nfct_reasm = NULL; -#endif #ifdef CONFIG_BRIDGE_NETFILTER nf_bridge_put(skb->nf_bridge); skb->nf_bridge = NULL; @@ -2660,10 +2625,6 @@ static inline void __nf_copy(struct sk_buff *dst, const struct sk_buff *src) nf_conntrack_get(src->nfct); dst->nfctinfo = src->nfctinfo; #endif -#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED - dst->nfct_reasm = src->nfct_reasm; - nf_conntrack_get_reasm(src->nfct_reasm); -#endif #ifdef CONFIG_BRIDGE_NETFILTER dst->nf_bridge = src->nf_bridge; nf_bridge_get(src->nf_bridge); @@ -2675,9 +2636,6 @@ static inline void nf_copy(struct sk_buff *dst, const struct sk_buff *src) #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) nf_conntrack_put(dst->nfct); #endif -#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED - nf_conntrack_put_reasm(dst->nfct_reasm); -#endif #ifdef CONFIG_BRIDGE_NETFILTER nf_bridge_put(dst->nf_bridge); #endif @@ -2732,28 +2690,27 @@ static inline bool skb_rx_queue_recorded(const struct sk_buff *skb) return skb->queue_mapping != 0; } -extern u16 __skb_tx_hash(const struct net_device *dev, - const struct sk_buff *skb, - unsigned int num_tx_queues); +u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, + unsigned int num_tx_queues); -#ifdef CONFIG_XFRM static inline struct sec_path *skb_sec_path(struct sk_buff *skb) { +#ifdef CONFIG_XFRM return skb->sp; -} #else -static inline struct sec_path *skb_sec_path(struct sk_buff *skb) -{ return NULL; -} #endif +} /* Keeps track of mac header offset relative to skb->head. * It is useful for TSO of Tunneling protocol. e.g. GRE. * For non-tunnel skb it points to skb_mac_header() and for - * tunnel skb it points to outer mac header. */ + * tunnel skb it points to outer mac header. + * Keeps track of level of encapsulation of network headers. + */ struct skb_gso_cb { - int mac_offset; + int mac_offset; + int encap_level; }; #define SKB_GSO_CB(skb) ((struct skb_gso_cb *)(skb)->cb) @@ -2783,12 +2740,13 @@ static inline bool skb_is_gso(const struct sk_buff *skb) return skb_shinfo(skb)->gso_size; } +/* Note: Should be called only if skb_is_gso(skb) is true */ static inline bool skb_is_gso_v6(const struct sk_buff *skb) { return skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6; } -extern void __skb_warn_lro_forwarding(const struct sk_buff *skb); +void __skb_warn_lro_forwarding(const struct sk_buff *skb); static inline bool skb_warn_if_lro(const struct sk_buff *skb) { diff --git a/include/linux/ssb/ssb_driver_gige.h b/include/linux/ssb/ssb_driver_gige.h index 86a12b0cb239850d903e53b52091ad53fd179115..0688472500bbabb1274a55c1ab0720d39b10922b 100644 --- a/include/linux/ssb/ssb_driver_gige.h +++ b/include/linux/ssb/ssb_driver_gige.h @@ -108,6 +108,16 @@ static inline int ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) return 0; } +/* Get the device phy address */ +static inline int ssb_gige_get_phyaddr(struct pci_dev *pdev) +{ + struct ssb_gige *dev = pdev_to_ssb_gige(pdev); + if (!dev) + return -ENODEV; + + return dev->dev->bus->sprom.et0phyaddr; +} + extern int ssb_gige_pcibios_plat_dev_init(struct ssb_device *sdev, struct pci_dev *pdev); extern int ssb_gige_map_irq(struct ssb_device *sdev, @@ -174,6 +184,10 @@ static inline int ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr) { return -ENODEV; } +static inline int ssb_gige_get_phyaddr(struct pci_dev *pdev) +{ + return -ENODEV; +} #endif /* CONFIG_SSB_DRIVER_GIGE */ #endif /* LINUX_SSB_DRIVER_GIGE_H_ */ diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index cc25b70af33c0b641caa53422c5faacc929809a7..c3fa807459967286c0407c18da294ca6d95ced42 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -36,6 +36,9 @@ * SUCH DAMAGE. */ +#ifndef __LINUX_USB_CDC_NCM_H +#define __LINUX_USB_CDC_NCM_H + #define CDC_NCM_COMM_ALTSETTING_NCM 0 #define CDC_NCM_COMM_ALTSETTING_MBIM 1 @@ -85,22 +88,13 @@ #define cdc_ncm_data_intf_is_mbim(x) ((x)->desc.bInterfaceProtocol == USB_CDC_MBIM_PROTO_NTB) struct cdc_ncm_ctx { - struct usb_cdc_ncm_ntb_parameters ncm_parm; struct hrtimer tx_timer; struct tasklet_struct bh; const struct usb_cdc_ncm_desc *func_desc; - const struct usb_cdc_mbim_desc *mbim_desc; - const struct usb_cdc_header_desc *header_desc; - const struct usb_cdc_union_desc *union_desc; + const struct usb_cdc_mbim_desc *mbim_desc; const struct usb_cdc_ether_desc *ether_desc; - struct net_device *netdev; - struct usb_device *udev; - struct usb_host_endpoint *in_ep; - struct usb_host_endpoint *out_ep; - struct usb_host_endpoint *status_ep; - struct usb_interface *intf; struct usb_interface *control; struct usb_interface *data; @@ -113,8 +107,6 @@ struct cdc_ncm_ctx { u32 tx_timer_pending; u32 tx_curr_frame_num; - u32 rx_speed; - u32 tx_speed; u32 rx_max; u32 tx_max; u32 max_datagram_size; @@ -127,9 +119,14 @@ struct cdc_ncm_ctx { u16 connected; }; -extern u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf); -extern int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting); -extern void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); -extern struct sk_buff *cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign); -extern int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); -extern int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset); +u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf); +int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting); +void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); +struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); +int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); +int cdc_ncm_rx_verify_ndp16(struct sk_buff *skb_in, int ndpoffset); +struct sk_buff * +cdc_ncm_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags); +int cdc_ncm_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in); + +#endif /* __LINUX_USB_CDC_NCM_H */ diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 10d43d8c7037ac58bea44788af5c7086dbf40f90..2a628b28249ffd00f8b4bd36d4e024583599ad67 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -197,8 +197,8 @@ static inline bool bdaddr_type_is_le(__u8 type) return false; } -#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0} }) -#define BDADDR_LOCAL (&(bdaddr_t) {{0, 0, 0, 0xff, 0xff, 0xff} }) +#define BDADDR_ANY (&(bdaddr_t) {{0, 0, 0, 0, 0, 0}}) +#define BDADDR_NONE (&(bdaddr_t) {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}) /* Copy, swap, convert BD Address */ static inline int bacmp(const bdaddr_t *ba1, const bdaddr_t *ba2) @@ -218,11 +218,10 @@ void baswap(bdaddr_t *dst, bdaddr_t *src); struct bt_sock { struct sock sk; - bdaddr_t src; - bdaddr_t dst; struct list_head accept_q; struct sock *parent; unsigned long flags; + void (*skb_msg_name)(struct sk_buff *, void *, int *); }; enum { @@ -249,6 +248,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock, uint bt_sock_poll(struct file *file, struct socket *sock, poll_table *wait); int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo); +int bt_sock_wait_ready(struct sock *sk, unsigned long flags); void bt_accept_enqueue(struct sock *parent, struct sock *sk); void bt_accept_unlink(struct sock *sk); @@ -282,8 +282,11 @@ struct bt_skb_cb { __u8 incoming; __u16 expect; __u8 force_active; + struct l2cap_chan *chan; struct l2cap_ctrl control; struct hci_req_ctrl req; + bdaddr_t bdaddr; + __le16 psm; }; #define bt_cb(skb) ((struct bt_skb_cb *)((skb)->cb)) @@ -331,16 +334,16 @@ out: int bt_to_errno(__u16 code); -extern int hci_sock_init(void); -extern void hci_sock_cleanup(void); +int hci_sock_init(void); +void hci_sock_cleanup(void); -extern int bt_sysfs_init(void); -extern void bt_sysfs_cleanup(void); +int bt_sysfs_init(void); +void bt_sysfs_cleanup(void); -extern int bt_procfs_init(struct net *net, const char *name, - struct bt_sock_list* sk_list, - int (* seq_show)(struct seq_file *, void *)); -extern void bt_procfs_cleanup(struct net *net, const char *name); +int bt_procfs_init(struct net *net, const char *name, + struct bt_sock_list *sk_list, + int (*seq_show)(struct seq_file *, void *)); +void bt_procfs_cleanup(struct net *net, const char *name); extern struct dentry *bt_debugfs; diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 15f10841e2b5ddedb94dadfe166e2fe19b09cfee..1784c48699f04dd425027d2e51d0aabc3fd87f4a 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -35,6 +35,8 @@ #define HCI_MAX_AMP_ASSOC_SIZE 672 +#define HCI_MAX_CSB_DATA_SIZE 252 + /* HCI dev events */ #define HCI_DEV_REG 1 #define HCI_DEV_UNREG 2 @@ -62,16 +64,20 @@ #define HCI_AMP 0x01 /* First BR/EDR Controller shall have ID = 0 */ -#define HCI_BREDR_ID 0 +#define AMP_ID_BREDR 0x00 + +/* AMP controller types */ +#define AMP_TYPE_BREDR 0x00 +#define AMP_TYPE_80211 0x01 /* AMP controller status */ -#define AMP_CTRL_POWERED_DOWN 0x00 -#define AMP_CTRL_BLUETOOTH_ONLY 0x01 -#define AMP_CTRL_NO_CAPACITY 0x02 -#define AMP_CTRL_LOW_CAPACITY 0x03 -#define AMP_CTRL_MEDIUM_CAPACITY 0x04 -#define AMP_CTRL_HIGH_CAPACITY 0x05 -#define AMP_CTRL_FULL_CAPACITY 0x06 +#define AMP_STATUS_POWERED_DOWN 0x00 +#define AMP_STATUS_BLUETOOTH_ONLY 0x01 +#define AMP_STATUS_NO_CAPACITY 0x02 +#define AMP_STATUS_LOW_CAPACITY 0x03 +#define AMP_STATUS_MEDIUM_CAPACITY 0x04 +#define AMP_STATUS_HIGH_CAPACITY 0x05 +#define AMP_STATUS_FULL_CAPACITY 0x06 /* HCI device quirks */ enum { @@ -109,18 +115,22 @@ enum { HCI_PAIRABLE, HCI_SERVICE_CACHE, HCI_DEBUG_KEYS, + HCI_DUT_MODE, HCI_UNREGISTER, + HCI_USER_CHANNEL, HCI_LE_SCAN, HCI_SSP_ENABLED, HCI_HS_ENABLED, HCI_LE_ENABLED, - HCI_LE_PERIPHERAL, + HCI_ADVERTISING, HCI_CONNECTABLE, HCI_DISCOVERABLE, + HCI_LIMITED_DISCOVERABLE, HCI_LINK_SECURITY, HCI_PERIODIC_INQ, HCI_FAST_CONNECTABLE, + HCI_BREDR_ENABLED, }; /* A mask for the flags that are supposed to remain when a reset happens @@ -624,6 +634,24 @@ struct hci_rp_logical_link_cancel { __u8 flow_spec_id; } __packed; +#define HCI_OP_SET_CSB 0x0441 +struct hci_cp_set_csb { + __u8 enable; + __u8 lt_addr; + __u8 lpo_allowed; + __le16 packet_type; + __le16 interval_min; + __le16 interval_max; + __le16 csb_sv_tout; +} __packed; +struct hci_rp_set_csb { + __u8 status; + __u8 lt_addr; + __le16 interval; +} __packed; + +#define HCI_OP_START_SYNC_TRAIN 0x0443 + #define HCI_OP_SNIFF_MODE 0x0803 struct hci_cp_sniff_mode { __le16 handle; @@ -694,9 +722,6 @@ struct hci_cp_sniff_subrate { } __packed; #define HCI_OP_SET_EVENT_MASK 0x0c01 -struct hci_cp_set_event_mask { - __u8 mask[8]; -} __packed; #define HCI_OP_RESET 0x0c03 @@ -792,6 +817,20 @@ struct hci_cp_host_buffer_size { __le16 sco_max_pkt; } __packed; +#define HCI_OP_READ_NUM_SUPPORTED_IAC 0x0c38 +struct hci_rp_read_num_supported_iac { + __u8 status; + __u8 num_iac; +} __packed; + +#define HCI_OP_READ_CURRENT_IAC_LAP 0x0c39 + +#define HCI_OP_WRITE_CURRENT_IAC_LAP 0x0c3a +struct hci_cp_write_current_iac_lap { + __u8 num_iac; + __u8 iac_lap[6]; +} __packed; + #define HCI_OP_WRITE_INQUIRY_MODE 0x0c45 #define HCI_MAX_EIR_LENGTH 240 @@ -826,6 +865,10 @@ struct hci_rp_read_inq_rsp_tx_power { __s8 tx_power; } __packed; +#define HCI_OP_SET_EVENT_MASK_PAGE_2 0x0c63 + +#define HCI_OP_READ_LOCATION_DATA 0x0c64 + #define HCI_OP_READ_FLOW_CONTROL_MODE 0x0c66 struct hci_rp_read_flow_control_mode { __u8 status; @@ -838,6 +881,50 @@ struct hci_cp_write_le_host_supported { __u8 simul; } __packed; +#define HCI_OP_SET_RESERVED_LT_ADDR 0x0c74 +struct hci_cp_set_reserved_lt_addr { + __u8 lt_addr; +} __packed; +struct hci_rp_set_reserved_lt_addr { + __u8 status; + __u8 lt_addr; +} __packed; + +#define HCI_OP_DELETE_RESERVED_LT_ADDR 0x0c75 +struct hci_cp_delete_reserved_lt_addr { + __u8 lt_addr; +} __packed; +struct hci_rp_delete_reserved_lt_addr { + __u8 status; + __u8 lt_addr; +} __packed; + +#define HCI_OP_SET_CSB_DATA 0x0c76 +struct hci_cp_set_csb_data { + __u8 lt_addr; + __u8 fragment; + __u8 data_length; + __u8 data[HCI_MAX_CSB_DATA_SIZE]; +} __packed; +struct hci_rp_set_csb_data { + __u8 status; + __u8 lt_addr; +} __packed; + +#define HCI_OP_READ_SYNC_TRAIN_PARAMS 0x0c77 + +#define HCI_OP_WRITE_SYNC_TRAIN_PARAMS 0x0c78 +struct hci_cp_write_sync_train_params { + __le16 interval_min; + __le16 interval_max; + __le32 sync_train_tout; + __u8 service_data; +} __packed; +struct hci_rp_write_sync_train_params { + __u8 status; + __le16 sync_train_int; +} __packed; + #define HCI_OP_READ_LOCAL_VERSION 0x1001 struct hci_rp_read_local_version { __u8 status; @@ -957,6 +1044,10 @@ struct hci_rp_write_remote_amp_assoc { __u8 phy_handle; } __packed; +#define HCI_OP_ENABLE_DUT_MODE 0x1803 + +#define HCI_OP_WRITE_SSP_DEBUG_MODE 0x1804 + #define HCI_OP_LE_SET_EVENT_MASK 0x2001 struct hci_cp_le_set_event_mask { __u8 mask[8]; @@ -975,6 +1066,20 @@ struct hci_rp_le_read_local_features { __u8 features[8]; } __packed; +#define HCI_OP_LE_SET_RANDOM_ADDR 0x2005 + +#define HCI_OP_LE_SET_ADV_PARAM 0x2006 +struct hci_cp_le_set_adv_param { + __le16 min_interval; + __le16 max_interval; + __u8 type; + __u8 own_address_type; + __u8 direct_addr_type; + bdaddr_t direct_addr; + __u8 channel_map; + __u8 filter_policy; +} __packed; + #define HCI_OP_LE_READ_ADV_TX_POWER 0x2007 struct hci_rp_le_read_adv_tx_power { __u8 status; @@ -989,6 +1094,12 @@ struct hci_cp_le_set_adv_data { __u8 data[HCI_MAX_AD_LENGTH]; } __packed; +#define HCI_OP_LE_SET_SCAN_RSP_DATA 0x2009 +struct hci_cp_le_set_scan_rsp_data { + __u8 length; + __u8 data[HCI_MAX_AD_LENGTH]; +} __packed; + #define HCI_OP_LE_SET_ADV_ENABLE 0x200a #define LE_SCAN_PASSIVE 0x00 @@ -1438,6 +1549,13 @@ struct hci_ev_num_comp_blocks { struct hci_comp_blocks_info handles[0]; } __packed; +#define HCI_EV_SYNC_TRAIN_COMPLETE 0x4F +struct hci_ev_sync_train_complete { + __u8 status; +} __packed; + +#define HCI_EV_SLAVE_PAGE_RESP_TIMEOUT 0x54 + /* Low energy meta events */ #define LE_CONN_ROLE_MASTER 0x00 @@ -1462,11 +1580,11 @@ struct hci_ev_le_ltk_req { } __packed; /* Advertising report event types */ -#define ADV_IND 0x00 -#define ADV_DIRECT_IND 0x01 -#define ADV_SCAN_IND 0x02 -#define ADV_NONCONN_IND 0x03 -#define ADV_SCAN_RSP 0x04 +#define LE_ADV_IND 0x00 +#define LE_ADV_DIRECT_IND 0x01 +#define LE_ADV_SCAN_IND 0x02 +#define LE_ADV_NONCONN_IND 0x03 +#define LE_ADV_SCAN_RSP 0x04 #define ADDR_LE_DEV_PUBLIC 0x00 #define ADDR_LE_DEV_RANDOM 0x01 @@ -1571,6 +1689,7 @@ struct sockaddr_hci { #define HCI_DEV_NONE 0xffff #define HCI_CHANNEL_RAW 0 +#define HCI_CHANNEL_USER 1 #define HCI_CHANNEL_MONITOR 2 #define HCI_CHANNEL_CONTROL 3 @@ -1673,6 +1792,4 @@ struct hci_inquiry_req { }; #define IREQ_CACHE_FLUSH 0x0001 -extern bool enable_hs; - #endif /* __HCI_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3ede820d328f9798a12e43ef77db50d0a572d29f..f8555ad7b10485925b07393019579c1ee706b2b0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -81,6 +81,7 @@ struct hci_conn_hash { struct bdaddr_list { struct list_head list; bdaddr_t bdaddr; + u8 bdaddr_type; }; struct bt_uuid { @@ -140,6 +141,8 @@ struct hci_dev { __u8 bus; __u8 dev_type; bdaddr_t bdaddr; + bdaddr_t static_addr; + __u8 own_addr_type; __u8 dev_name[HCI_MAX_NAME_LENGTH]; __u8 short_name[HCI_MAX_SHORT_NAME_LENGTH]; __u8 eir[HCI_MAX_EIR_LENGTH]; @@ -158,11 +161,17 @@ struct hci_dev { __u16 manufacturer; __u16 lmp_subver; __u16 voice_setting; + __u8 num_iac; __u8 io_capability; __s8 inq_tx_power; __u16 page_scan_interval; __u16 page_scan_window; __u8 page_scan_type; + __u16 le_scan_interval; + __u16 le_scan_window; + __u16 le_conn_min_interval; + __u16 le_conn_max_interval; + __u8 ssp_debug_mode; __u16 devid_source; __u16 devid_vendor; @@ -279,14 +288,15 @@ struct hci_dev { __s8 adv_tx_power; __u8 adv_data[HCI_MAX_AD_LENGTH]; __u8 adv_data_len; + __u8 scan_rsp_data[HCI_MAX_AD_LENGTH]; + __u8 scan_rsp_data_len; int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); int (*setup)(struct hci_dev *hdev); - int (*send)(struct sk_buff *skb); + int (*send)(struct hci_dev *hdev, struct sk_buff *skb); void (*notify)(struct hci_dev *hdev, unsigned int evt); - int (*ioctl)(struct hci_dev *hdev, unsigned int cmd, unsigned long arg); }; #define HCI_PHY_HANDLE(handle) (handle & 0xff) @@ -298,6 +308,8 @@ struct hci_conn { bdaddr_t dst; __u8 dst_type; + bdaddr_t src; + __u8 src_type; __u16 handle; __u16 state; __u8 mode; @@ -306,7 +318,6 @@ struct hci_conn { __u8 attempt; __u8 dev_class[3]; __u8 features[HCI_MAX_PAGES][8]; - __u16 interval; __u16 pkt_type; __u16 link_policy; __u32 link_mode; @@ -334,8 +345,8 @@ struct hci_conn { struct list_head chan_list; struct delayed_work disc_work; - struct timer_list idle_timer; - struct timer_list auto_accept_timer; + struct delayed_work auto_accept_work; + struct delayed_work idle_work; struct device dev; @@ -367,18 +378,17 @@ extern rwlock_t hci_dev_list_lock; extern rwlock_t hci_cb_list_lock; /* ----- HCI interface to upper protocols ----- */ -extern int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); -extern void l2cap_connect_cfm(struct hci_conn *hcon, u8 status); -extern int l2cap_disconn_ind(struct hci_conn *hcon); -extern void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason); -extern int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt); -extern int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, - u16 flags); - -extern int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags); -extern void sco_connect_cfm(struct hci_conn *hcon, __u8 status); -extern void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason); -extern int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); +int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr); +void l2cap_connect_cfm(struct hci_conn *hcon, u8 status); +int l2cap_disconn_ind(struct hci_conn *hcon); +void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason); +int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt); +int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags); + +int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags); +void sco_connect_cfm(struct hci_conn *hcon, __u8 status); +void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason); +int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb); /* ----- Inquiry cache ----- */ #define INQUIRY_CACHE_AGE_MAX (HZ*30) /* 30 seconds */ @@ -644,7 +654,7 @@ static inline void hci_conn_drop(struct hci_conn *conn) switch (conn->type) { case ACL_LINK: case LE_LINK: - del_timer(&conn->idle_timer); + cancel_delayed_work(&conn->idle_work); if (conn->state == BT_CONNECTED) { timeo = conn->disc_timeout; if (!conn->out) @@ -703,19 +713,6 @@ static inline void hci_set_drvdata(struct hci_dev *hdev, void *data) dev_set_drvdata(&hdev->dev, data); } -/* hci_dev_list shall be locked */ -static inline uint8_t __hci_num_ctrl(void) -{ - uint8_t count = 0; - struct list_head *p; - - list_for_each(p, &hci_dev_list) { - count++; - } - - return count; -} - struct hci_dev *hci_dev_get(int index); struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src); @@ -738,7 +735,7 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg); int hci_inquiry(void __user *arg); struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, - bdaddr_t *bdaddr); + bdaddr_t *bdaddr, u8 type); int hci_blacklist_clear(struct hci_dev *hdev); int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); @@ -768,13 +765,11 @@ int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); -int hci_recv_frame(struct sk_buff *skb); +int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count); int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count); void hci_init_sysfs(struct hci_dev *hdev); -int hci_add_sysfs(struct hci_dev *hdev); -void hci_del_sysfs(struct hci_dev *hdev); void hci_conn_init_sysfs(struct hci_conn *conn); void hci_conn_add_sysfs(struct hci_conn *conn); void hci_conn_del_sysfs(struct hci_conn *conn); @@ -807,22 +802,6 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define lmp_host_le_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE)) #define lmp_host_le_br_capable(dev) (!!((dev)->features[1][0] & LMP_HOST_LE_BREDR)) -/* returns true if at least one AMP active */ -static inline bool hci_amp_capable(void) -{ - struct hci_dev *hdev; - bool ret = false; - - read_lock(&hci_dev_list_lock); - list_for_each_entry(hdev, &hci_dev_list, list) - if (hdev->amp_type == HCI_AMP && - test_bit(HCI_UP, &hdev->flags)) - ret = true; - read_unlock(&hci_dev_list_lock); - - return ret; -} - /* ----- HCI protocols ----- */ #define HCI_PROTO_DEFER 0x01 @@ -1033,34 +1012,6 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) return false; } -static inline size_t eir_get_length(u8 *eir, size_t eir_len) -{ - size_t parsed = 0; - - while (parsed < eir_len) { - u8 field_len = eir[0]; - - if (field_len == 0) - return parsed; - - parsed += field_len + 1; - eir += field_len + 1; - } - - return eir_len; -} - -static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, - u8 data_len) -{ - eir[eir_len++] = sizeof(type) + data_len; - eir[eir_len++] = type; - memcpy(&eir[eir_len], data, data_len); - eir_len += data_len; - - return eir_len; -} - int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); @@ -1120,29 +1071,30 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event); #define DISCOV_BREDR_INQUIRY_LEN 0x08 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); -int mgmt_index_added(struct hci_dev *hdev); -int mgmt_index_removed(struct hci_dev *hdev); -int mgmt_set_powered_failed(struct hci_dev *hdev, int err); +void mgmt_index_added(struct hci_dev *hdev); +void mgmt_index_removed(struct hci_dev *hdev); +void mgmt_set_powered_failed(struct hci_dev *hdev, int err); int mgmt_powered(struct hci_dev *hdev, u8 powered); -int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); -int mgmt_connectable(struct hci_dev *hdev, u8 connectable); -int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); -int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, - bool persistent); -int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u32 flags, u8 *name, u8 name_len, - u8 *dev_class); -int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 reason); -int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status); -int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status); -int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure); -int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status); -int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status); +void mgmt_discoverable_timeout(struct hci_dev *hdev); +void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); +void mgmt_connectable(struct hci_dev *hdev, u8 connectable); +void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); +void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, + bool persistent); +void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u32 flags, u8 *name, u8 name_len, + u8 *dev_class); +void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 reason); +void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status); +void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 status); +void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure); +void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status); +void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status); int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, __le32 value, u8 confirm_hint); @@ -1159,26 +1111,25 @@ int mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, u32 passkey, u8 entered); -int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status); -int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status); -int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); -int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, - u8 status); -int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); -int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, - u8 *randomizer, u8 status); -int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); -int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, - u8 ssp, u8 *eir, u16 eir_len); -int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, s8 rssi, u8 *name, u8 name_len); -int mgmt_discovering(struct hci_dev *hdev, u8 discovering); +void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 status); +void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status); +void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status); +void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, + u8 status); +void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); +void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, + u8 *randomizer, u8 status); +void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, + u8 ssp, u8 *eir, u16 eir_len); +void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, s8 rssi, u8 *name, u8 name_len); +void mgmt_discovering(struct hci_dev *hdev, u8 discovering); int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -bool mgmt_valid_hdev(struct hci_dev *hdev); -int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent); +void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent); +void mgmt_reenable_advertising(struct hci_dev *hdev); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) @@ -1208,15 +1159,11 @@ struct hci_sec_filter { #define hci_req_lock(d) mutex_lock(&d->req_lock) #define hci_req_unlock(d) mutex_unlock(&d->req_lock) -void hci_update_ad(struct hci_request *req); - void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], __u8 ltk[16]); -u8 bdaddr_to_le(u8 bdaddr_type); - #define SCO_AIRMODE_MASK 0x0003 #define SCO_AIRMODE_CVSD 0x0000 #define SCO_AIRMODE_TRANSP 0x0003 diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1a966afbbfa8408d159503f711f00855e0957d4b..51329905bfaafda1c290a26103914d5879144a01 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -131,6 +131,7 @@ struct l2cap_conninfo { /* L2CAP fixed channels */ #define L2CAP_FC_L2CAP 0x02 +#define L2CAP_FC_CONNLESS 0x04 #define L2CAP_FC_A2MP 0x08 /* L2CAP Control Field bit masks */ @@ -237,6 +238,7 @@ struct l2cap_conn_rsp { /* protocol/service multiplexer (PSM) */ #define L2CAP_PSM_SDP 0x0001 #define L2CAP_PSM_RFCOMM 0x0003 +#define L2CAP_PSM_3DSP 0x0021 /* channel indentifier */ #define L2CAP_CID_SIGNALING 0x0001 @@ -433,8 +435,6 @@ struct l2cap_seq_list { #define L2CAP_SEQ_LIST_TAIL 0x8000 struct l2cap_chan { - struct sock *sk; - struct l2cap_conn *conn; struct hci_conn *hs_hcon; struct hci_chan *hs_hchan; @@ -442,7 +442,12 @@ struct l2cap_chan { __u8 state; + bdaddr_t dst; + __u8 dst_type; + bdaddr_t src; + __u8 src_type; __le16 psm; + __le16 sport; __u16 dcid; __u16 scid; @@ -453,8 +458,6 @@ struct l2cap_chan { __u8 chan_type; __u8 chan_policy; - __le16 sport; - __u8 sec_level; __u8 ident; @@ -546,9 +549,12 @@ struct l2cap_ops { void (*teardown) (struct l2cap_chan *chan, int err); void (*close) (struct l2cap_chan *chan); void (*state_change) (struct l2cap_chan *chan, - int state); + int state, int err); void (*ready) (struct l2cap_chan *chan); void (*defer) (struct l2cap_chan *chan); + void (*resume) (struct l2cap_chan *chan); + void (*set_shutdown) (struct l2cap_chan *chan); + long (*get_sndtimeo) (struct l2cap_chan *chan); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, unsigned long len, int nb); }; @@ -557,13 +563,11 @@ struct l2cap_conn { struct hci_conn *hcon; struct hci_chan *hchan; - bdaddr_t *dst; - bdaddr_t *src; - unsigned int mtu; __u32 feat_mask; __u8 fixed_chan_mask; + bool hs_enabled; __u8 info_state; __u8 info_ident; @@ -649,6 +653,7 @@ enum { FLAG_FLUSHABLE, FLAG_EXT_CTRL, FLAG_EFS_ENABLE, + FLAG_DEFER_SETUP, }; enum { @@ -790,6 +795,19 @@ static inline void l2cap_chan_no_defer(struct l2cap_chan *chan) { } +static inline void l2cap_chan_no_resume(struct l2cap_chan *chan) +{ +} + +static inline void l2cap_chan_no_set_shutdown(struct l2cap_chan *chan) +{ +} + +static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan) +{ + return 0; +} + extern bool disable_ertm; int l2cap_init_sockets(void); @@ -797,7 +815,6 @@ void l2cap_cleanup_sockets(void); bool l2cap_is_socket(struct socket *sock); void __l2cap_connect_rsp_defer(struct l2cap_chan *chan); -int __l2cap_wait_ack(struct sock *sk); int l2cap_add_psm(struct l2cap_chan *chan, bdaddr_t *src, __le16 psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 9944c3e68c5d1dd57f40f0b72012a0fbfcdf1a4b..518c5c84e39a67ef4c9789eacd8d88117bf6ccbd 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -93,6 +93,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_BREDR 0x00000080 #define MGMT_SETTING_HS 0x00000100 #define MGMT_SETTING_LE 0x00000200 +#define MGMT_SETTING_ADVERTISING 0x00000400 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 @@ -351,6 +352,23 @@ struct mgmt_cp_set_device_id { } __packed; #define MGMT_SET_DEVICE_ID_SIZE 8 +#define MGMT_OP_SET_ADVERTISING 0x0029 + +#define MGMT_OP_SET_BREDR 0x002A + +#define MGMT_OP_SET_STATIC_ADDRESS 0x002B +struct mgmt_cp_set_static_address { + bdaddr_t bdaddr; +} __packed; +#define MGMT_SET_STATIC_ADDRESS_SIZE 6 + +#define MGMT_OP_SET_SCAN_PARAMS 0x002C +struct mgmt_cp_set_scan_params { + __le16 interval; + __le16 window; +} __packed; +#define MGMT_SET_SCAN_PARAMS_SIZE 4 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index 7afd4199d6b6cfab22c74fb6bf0544a887bbd5af..486213a1aed8d07ad63aaba57957e7a651657696 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -256,8 +256,8 @@ static inline void rfcomm_dlc_put(struct rfcomm_dlc *d) rfcomm_dlc_free(d); } -extern void __rfcomm_dlc_throttle(struct rfcomm_dlc *d); -extern void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d); +void __rfcomm_dlc_throttle(struct rfcomm_dlc *d); +void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d); static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d) { @@ -300,6 +300,8 @@ struct rfcomm_conninfo { struct rfcomm_pinfo { struct bt_sock bt; + bdaddr_t src; + bdaddr_t dst; struct rfcomm_dlc *dlc; u8 channel; u8 sec_level; diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index e252a31ee6b6389f54d1a2483a5f143d6032d57f..2019d1a0996a80d9d2fc9ef29fb33e102f2864bd 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -55,9 +55,6 @@ struct sco_conninfo { struct sco_conn { struct hci_conn *hcon; - bdaddr_t *dst; - bdaddr_t *src; - spinlock_t lock; struct sock *sk; @@ -72,6 +69,8 @@ struct sco_conn { struct sco_pinfo { struct bt_sock bt; + bdaddr_t src; + bdaddr_t dst; __u32 flags; __u16 setting; struct sco_conn *conn; diff --git a/include/net/caif/caif_hsi.h b/include/net/caif/caif_hsi.h index 4795e817afe508b6f404aebbd71e6ebe4659cb62..097f69cfaa75fbe2ce56c2ae33f4c96351b3add6 100644 --- a/include/net/caif/caif_hsi.h +++ b/include/net/caif/caif_hsi.h @@ -195,6 +195,6 @@ enum ifla_caif_hsi { __IFLA_CAIF_HSI_MAX }; -extern struct cfhsi_ops *cfhsi_get_ops(void); +struct cfhsi_ops *cfhsi_get_ops(void); #endif /* CAIF_HSI_H_ */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index cb710913d5c8db68e20bd3cca10f72710d1ef1ae..3eae46cb1acfecadbff979ee17e17aeab40971e7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -436,6 +436,15 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, u32 prohibited_flags); +/** + * cfg80211_chandef_dfs_required - checks if radar detection is required + * @wiphy: the wiphy to validate against + * @chandef: the channel definition to check + * Return: 1 if radar detection is required, 0 if it is not, < 0 on error + */ +int cfg80211_chandef_dfs_required(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef); + /** * ieee80211_chandef_rate_flags - returns rate flags for a channel * @@ -735,6 +744,10 @@ enum station_parameters_apply_mask { * @capability: station capability * @ext_capab: extended capabilities of the station * @ext_capab_len: number of extended capabilities + * @supported_channels: supported channels in IEEE 802.11 format + * @supported_channels_len: number of supported channels + * @supported_oper_classes: supported oper classes in IEEE 802.11 format + * @supported_oper_classes_len: number of supported operating classes */ struct station_parameters { const u8 *supported_rates; @@ -754,6 +767,10 @@ struct station_parameters { u16 capability; const u8 *ext_capab; u8 ext_capab_len; + const u8 *supported_channels; + u8 supported_channels_len; + const u8 *supported_oper_classes; + u8 supported_oper_classes_len; }; /** @@ -1647,6 +1664,9 @@ struct cfg80211_disassoc_request { * sets/clears %NL80211_STA_FLAG_AUTHORIZED. If true, the driver is * required to assume that the port is unauthorized until authorized by * user space. Otherwise, port is marked authorized by default. + * @userspace_handles_dfs: whether user space controls DFS operation, i.e. + * changes the channel when a radar is detected. This is required + * to operate on DFS channels. * @basic_rates: bitmap of basic rates to use when creating the IBSS * @mcast_rate: per-band multicast rate index + 1 (0: disabled) * @ht_capa: HT Capabilities over-rides. Values set in ht_capa_mask @@ -1664,6 +1684,7 @@ struct cfg80211_ibss_params { bool channel_fixed; bool privacy; bool control_port; + bool userspace_handles_dfs; int mcast_rate[IEEE80211_NUM_BANDS]; struct ieee80211_ht_cap ht_capa; struct ieee80211_ht_cap ht_capa_mask; @@ -3044,6 +3065,7 @@ struct cfg80211_cached_keys; * @conn: (private) cfg80211 software SME connection state machine data * @connect_keys: (private) keys to set after connection is established * @ibss_fixed: (private) IBSS is using fixed BSSID + * @ibss_dfs_possible: (private) IBSS may change to a DFS channel * @event_list: (private) list for internal event processing * @event_lock: (private) lock for event list */ @@ -3082,6 +3104,7 @@ struct wireless_dev { struct ieee80211_channel *channel; bool ibss_fixed; + bool ibss_dfs_possible; bool ps; int ps_timeout; @@ -3474,6 +3497,15 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy, const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, u32 center_freq); +/** + * reg_initiator_name - map regulatory request initiator enum to name + * @initiator: the regulatory request initiator + * + * You can use this to map the regulatory request initiator enum to a + * proper string representation. + */ +const char *reg_initiator_name(enum nl80211_reg_initiator initiator); + /* * callbacks for asynchronous cfg80211 methods, notification * functions and BSS handling helpers diff --git a/include/net/checksum.h b/include/net/checksum.h index 8f59ca50477c6424c6a21d8a5f5ca60af62c7d32..37a0e24adbe72a995e341c1c0113fbed0c5fd69b 100644 --- a/include/net/checksum.h +++ b/include/net/checksum.h @@ -78,6 +78,12 @@ csum_block_add(__wsum csum, __wsum csum2, int offset) return csum_add(csum, (__force __wsum)sum); } +static inline __wsum +csum_block_add_ext(__wsum csum, __wsum csum2, int offset, int len) +{ + return csum_block_add(csum, csum2, offset); +} + static inline __wsum csum_block_sub(__wsum csum, __wsum csum2, int offset) { @@ -92,6 +98,11 @@ static inline __wsum csum_unfold(__sum16 n) return (__force __wsum)n; } +static inline __wsum csum_partial_ext(const void *buff, int len, __wsum sum) +{ + return csum_partial(buff, len, sum); +} + #define CSUM_MANGLED_0 ((__force __sum16)0xffff) static inline void csum_replace4(__sum16 *sum, __be32 from, __be32 to) diff --git a/include/net/codel.h b/include/net/codel.h index 389cf621161d6903510c023e04e57a4ccd03072c..3b04ff5f6f8d60489a13171d3071ffa597c2c140 100644 --- a/include/net/codel.h +++ b/include/net/codel.h @@ -72,10 +72,21 @@ static inline codel_time_t codel_get_time(void) return ns >> CODEL_SHIFT; } -#define codel_time_after(a, b) ((s32)(a) - (s32)(b) > 0) -#define codel_time_after_eq(a, b) ((s32)(a) - (s32)(b) >= 0) -#define codel_time_before(a, b) ((s32)(a) - (s32)(b) < 0) -#define codel_time_before_eq(a, b) ((s32)(a) - (s32)(b) <= 0) +/* Dealing with timer wrapping, according to RFC 1982, as desc in wikipedia: + * https://en.wikipedia.org/wiki/Serial_number_arithmetic#General_Solution + * codel_time_after(a,b) returns true if the time a is after time b. + */ +#define codel_time_after(a, b) \ + (typecheck(codel_time_t, a) && \ + typecheck(codel_time_t, b) && \ + ((s32)((a) - (b)) > 0)) +#define codel_time_before(a, b) codel_time_after(b, a) + +#define codel_time_after_eq(a, b) \ + (typecheck(codel_time_t, a) && \ + typecheck(codel_time_t, b) && \ + ((s32)((a) - (b)) >= 0)) +#define codel_time_before_eq(a, b) codel_time_after_eq(b, a) /* Qdiscs using codel plugin must use codel_skb_cb in their own cb[] */ struct codel_skb_cb { diff --git a/include/net/compat.h b/include/net/compat.h index 6e9565324989dd0ad4eb92809af943f61055a047..3b603b199c01c5d554202323dde4253e01f7a4ab 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -29,8 +29,8 @@ struct compat_cmsghdr { compat_int_t cmsg_type; }; -extern int compat_sock_get_timestamp(struct sock *, struct timeval __user *); -extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *); +int compat_sock_get_timestamp(struct sock *, struct timeval __user *); +int compat_sock_get_timestampns(struct sock *, struct timespec __user *); #else /* defined(CONFIG_COMPAT) */ /* @@ -40,24 +40,30 @@ extern int compat_sock_get_timestampns(struct sock *, struct timespec __user *); #define compat_mmsghdr mmsghdr #endif /* defined(CONFIG_COMPAT) */ -extern int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *); -extern int verify_compat_iovec(struct msghdr *, struct iovec *, struct sockaddr_storage *, int); -extern asmlinkage long compat_sys_sendmsg(int,struct compat_msghdr __user *,unsigned int); -extern asmlinkage long compat_sys_sendmmsg(int, struct compat_mmsghdr __user *, - unsigned int, unsigned int); -extern asmlinkage long compat_sys_recvmsg(int,struct compat_msghdr __user *,unsigned int); -extern asmlinkage long compat_sys_recvmmsg(int, struct compat_mmsghdr __user *, - unsigned int, unsigned int, - struct compat_timespec __user *); -extern asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, int __user *); -extern int put_cmsg_compat(struct msghdr*, int, int, int, void *); - -extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, unsigned char *, int); - -extern int compat_mc_setsockopt(struct sock *, int, int, char __user *, unsigned int, - int (*)(struct sock *, int, int, char __user *, unsigned int)); -extern int compat_mc_getsockopt(struct sock *, int, int, char __user *, - int __user *, int (*)(struct sock *, int, int, char __user *, - int __user *)); +int get_compat_msghdr(struct msghdr *, struct compat_msghdr __user *); +int verify_compat_iovec(struct msghdr *, struct iovec *, + struct sockaddr_storage *, int); +asmlinkage long compat_sys_sendmsg(int, struct compat_msghdr __user *, + unsigned int); +asmlinkage long compat_sys_sendmmsg(int, struct compat_mmsghdr __user *, + unsigned int, unsigned int); +asmlinkage long compat_sys_recvmsg(int, struct compat_msghdr __user *, + unsigned int); +asmlinkage long compat_sys_recvmmsg(int, struct compat_mmsghdr __user *, + unsigned int, unsigned int, + struct compat_timespec __user *); +asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, + int __user *); +int put_cmsg_compat(struct msghdr*, int, int, int, void *); + +int cmsghdr_from_user_compat_to_kern(struct msghdr *, struct sock *, + unsigned char *, int); + +int compat_mc_setsockopt(struct sock *, int, int, char __user *, unsigned int, + int (*)(struct sock *, int, int, char __user *, + unsigned int)); +int compat_mc_getsockopt(struct sock *, int, int, char __user *, int __user *, + int (*)(struct sock *, int, int, char __user *, + int __user *)); #endif /* NET_COMPAT_H */ diff --git a/include/net/dcbevent.h b/include/net/dcbevent.h index 443626ed4cbc4daba803c58dfc76128c5c0f5b67..d2f3041c0dfaa685b2f32a633ee0931bad036329 100644 --- a/include/net/dcbevent.h +++ b/include/net/dcbevent.h @@ -25,9 +25,9 @@ enum dcbevent_notif_type { }; #ifdef CONFIG_DCB -extern int register_dcbevent_notifier(struct notifier_block *nb); -extern int unregister_dcbevent_notifier(struct notifier_block *nb); -extern int call_dcbevent_notifiers(unsigned long val, void *v); +int register_dcbevent_notifier(struct notifier_block *nb); +int unregister_dcbevent_notifier(struct notifier_block *nb); +int call_dcbevent_notifiers(unsigned long val, void *v); #else static inline int register_dcbevent_notifier(struct notifier_block *nb) diff --git a/include/net/dn.h b/include/net/dn.h index c88bf4ebd330723de80a330b11baf65507e94d24..ccc15588d108ecc2a33d5b2544b8e0e1feb33069 100644 --- a/include/net/dn.h +++ b/include/net/dn.h @@ -199,24 +199,26 @@ static inline void dn_sk_ports_copy(struct flowidn *fld, struct dn_scp *scp) fld->fld_dport = scp->addrrem; } -extern unsigned int dn_mss_from_pmtu(struct net_device *dev, int mtu); +unsigned int dn_mss_from_pmtu(struct net_device *dev, int mtu); #define DN_MENUVER_ACC 0x01 #define DN_MENUVER_USR 0x02 #define DN_MENUVER_PRX 0x04 #define DN_MENUVER_UIC 0x08 -extern struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr); -extern struct sock *dn_find_by_skb(struct sk_buff *skb); +struct sock *dn_sklist_find_listener(struct sockaddr_dn *addr); +struct sock *dn_find_by_skb(struct sk_buff *skb); #define DN_ASCBUF_LEN 9 -extern char *dn_addr2asc(__u16, char *); -extern int dn_destroy_timer(struct sock *sk); +char *dn_addr2asc(__u16, char *); +int dn_destroy_timer(struct sock *sk); -extern int dn_sockaddr2username(struct sockaddr_dn *addr, unsigned char *buf, unsigned char type); -extern int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *addr, unsigned char *type); +int dn_sockaddr2username(struct sockaddr_dn *addr, unsigned char *buf, + unsigned char type); +int dn_username2sockaddr(unsigned char *data, int len, struct sockaddr_dn *addr, + unsigned char *type); -extern void dn_start_slow_timer(struct sock *sk); -extern void dn_stop_slow_timer(struct sock *sk); +void dn_start_slow_timer(struct sock *sk); +void dn_stop_slow_timer(struct sock *sk); extern __le16 decnet_address; extern int decnet_debug_level; diff --git a/include/net/dn_dev.h b/include/net/dn_dev.h index b9e32db03f2040e76d7d73295406772de0029930..20b5ab06032d35dc1f5f3175d9b88d59db2e2232 100644 --- a/include/net/dn_dev.h +++ b/include/net/dn_dev.h @@ -148,27 +148,27 @@ struct rtnode_hello_message { } __packed; -extern void dn_dev_init(void); -extern void dn_dev_cleanup(void); +void dn_dev_init(void); +void dn_dev_cleanup(void); -extern int dn_dev_ioctl(unsigned int cmd, void __user *arg); +int dn_dev_ioctl(unsigned int cmd, void __user *arg); -extern void dn_dev_devices_off(void); -extern void dn_dev_devices_on(void); +void dn_dev_devices_off(void); +void dn_dev_devices_on(void); -extern void dn_dev_init_pkt(struct sk_buff *skb); -extern void dn_dev_veri_pkt(struct sk_buff *skb); -extern void dn_dev_hello(struct sk_buff *skb); +void dn_dev_init_pkt(struct sk_buff *skb); +void dn_dev_veri_pkt(struct sk_buff *skb); +void dn_dev_hello(struct sk_buff *skb); -extern void dn_dev_up(struct net_device *); -extern void dn_dev_down(struct net_device *); +void dn_dev_up(struct net_device *); +void dn_dev_down(struct net_device *); -extern int dn_dev_set_default(struct net_device *dev, int force); -extern struct net_device *dn_dev_get_default(void); -extern int dn_dev_bind_default(__le16 *addr); +int dn_dev_set_default(struct net_device *dev, int force); +struct net_device *dn_dev_get_default(void); +int dn_dev_bind_default(__le16 *addr); -extern int register_dnaddr_notifier(struct notifier_block *nb); -extern int unregister_dnaddr_notifier(struct notifier_block *nb); +int register_dnaddr_notifier(struct notifier_block *nb); +int unregister_dnaddr_notifier(struct notifier_block *nb); static inline int dn_dev_islocal(struct net_device *dev, __le16 addr) { diff --git a/include/net/dn_fib.h b/include/net/dn_fib.h index 74004af31c482c2bef8c9240b47e82c2f4a9bfbd..f2ca135ddcc979b07260c5d5996dc79bf7659536 100644 --- a/include/net/dn_fib.h +++ b/include/net/dn_fib.h @@ -95,41 +95,38 @@ struct dn_fib_table { /* * dn_fib.c */ -extern void dn_fib_init(void); -extern void dn_fib_cleanup(void); - -extern int dn_fib_ioctl(struct socket *sock, unsigned int cmd, - unsigned long arg); -extern struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, - struct nlattr *attrs[], - const struct nlmsghdr *nlh, int *errp); -extern int dn_fib_semantic_match(int type, struct dn_fib_info *fi, - const struct flowidn *fld, - struct dn_fib_res *res); -extern void dn_fib_release_info(struct dn_fib_info *fi); -extern void dn_fib_flush(void); -extern void dn_fib_select_multipath(const struct flowidn *fld, - struct dn_fib_res *res); +void dn_fib_init(void); +void dn_fib_cleanup(void); + +int dn_fib_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); +struct dn_fib_info *dn_fib_create_info(const struct rtmsg *r, + struct nlattr *attrs[], + const struct nlmsghdr *nlh, int *errp); +int dn_fib_semantic_match(int type, struct dn_fib_info *fi, + const struct flowidn *fld, struct dn_fib_res *res); +void dn_fib_release_info(struct dn_fib_info *fi); +void dn_fib_flush(void); +void dn_fib_select_multipath(const struct flowidn *fld, struct dn_fib_res *res); /* * dn_tables.c */ -extern struct dn_fib_table *dn_fib_get_table(u32 n, int creat); -extern struct dn_fib_table *dn_fib_empty_table(void); -extern void dn_fib_table_init(void); -extern void dn_fib_table_cleanup(void); +struct dn_fib_table *dn_fib_get_table(u32 n, int creat); +struct dn_fib_table *dn_fib_empty_table(void); +void dn_fib_table_init(void); +void dn_fib_table_cleanup(void); /* * dn_rules.c */ -extern void dn_fib_rules_init(void); -extern void dn_fib_rules_cleanup(void); -extern unsigned int dnet_addr_type(__le16 addr); -extern int dn_fib_lookup(struct flowidn *fld, struct dn_fib_res *res); +void dn_fib_rules_init(void); +void dn_fib_rules_cleanup(void); +unsigned int dnet_addr_type(__le16 addr); +int dn_fib_lookup(struct flowidn *fld, struct dn_fib_res *res); -extern int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb); +int dn_fib_dump(struct sk_buff *skb, struct netlink_callback *cb); -extern void dn_fib_free_info(struct dn_fib_info *fi); +void dn_fib_free_info(struct dn_fib_info *fi); static inline void dn_fib_info_put(struct dn_fib_info *fi) { diff --git a/include/net/dn_neigh.h b/include/net/dn_neigh.h index 4cb4ae7fb81fddc3915402e26b90a4ba8ac28c78..fac4e3f4a6d3c0ede92af3d4df4c24319a94c0a0 100644 --- a/include/net/dn_neigh.h +++ b/include/net/dn_neigh.h @@ -16,12 +16,12 @@ struct dn_neigh { __u8 priority; }; -extern void dn_neigh_init(void); -extern void dn_neigh_cleanup(void); -extern int dn_neigh_router_hello(struct sk_buff *skb); -extern int dn_neigh_endnode_hello(struct sk_buff *skb); -extern void dn_neigh_pointopoint_hello(struct sk_buff *skb); -extern int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n); +void dn_neigh_init(void); +void dn_neigh_cleanup(void); +int dn_neigh_router_hello(struct sk_buff *skb); +int dn_neigh_endnode_hello(struct sk_buff *skb); +void dn_neigh_pointopoint_hello(struct sk_buff *skb); +int dn_neigh_elist(struct net_device *dev, unsigned char *ptr, int n); extern struct neigh_table dn_neigh_table; diff --git a/include/net/dn_nsp.h b/include/net/dn_nsp.h index e43a2893f132f0767849271f3eac0f625dbd8828..3a3e33d184562ae778db49b0cabb1e524c2ee548 100644 --- a/include/net/dn_nsp.h +++ b/include/net/dn_nsp.h @@ -15,29 +15,32 @@ *******************************************************************************/ /* dn_nsp.c functions prototyping */ -extern void dn_nsp_send_data_ack(struct sock *sk); -extern void dn_nsp_send_oth_ack(struct sock *sk); -extern void dn_nsp_delayed_ack(struct sock *sk); -extern void dn_send_conn_ack(struct sock *sk); -extern void dn_send_conn_conf(struct sock *sk, gfp_t gfp); -extern void dn_nsp_send_disc(struct sock *sk, unsigned char type, - unsigned short reason, gfp_t gfp); -extern void dn_nsp_return_disc(struct sk_buff *skb, unsigned char type, - unsigned short reason); -extern void dn_nsp_send_link(struct sock *sk, unsigned char lsflags, char fcval); -extern void dn_nsp_send_conninit(struct sock *sk, unsigned char flags); - -extern void dn_nsp_output(struct sock *sk); -extern int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, struct sk_buff_head *q, unsigned short acknum); -extern void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, gfp_t gfp, int oob); -extern unsigned long dn_nsp_persist(struct sock *sk); -extern int dn_nsp_xmit_timeout(struct sock *sk); - -extern int dn_nsp_rx(struct sk_buff *); -extern int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb); - -extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri); -extern struct sk_buff *dn_alloc_send_skb(struct sock *sk, size_t *size, int noblock, long timeo, int *err); +void dn_nsp_send_data_ack(struct sock *sk); +void dn_nsp_send_oth_ack(struct sock *sk); +void dn_nsp_delayed_ack(struct sock *sk); +void dn_send_conn_ack(struct sock *sk); +void dn_send_conn_conf(struct sock *sk, gfp_t gfp); +void dn_nsp_send_disc(struct sock *sk, unsigned char type, + unsigned short reason, gfp_t gfp); +void dn_nsp_return_disc(struct sk_buff *skb, unsigned char type, + unsigned short reason); +void dn_nsp_send_link(struct sock *sk, unsigned char lsflags, char fcval); +void dn_nsp_send_conninit(struct sock *sk, unsigned char flags); + +void dn_nsp_output(struct sock *sk); +int dn_nsp_check_xmit_queue(struct sock *sk, struct sk_buff *skb, + struct sk_buff_head *q, unsigned short acknum); +void dn_nsp_queue_xmit(struct sock *sk, struct sk_buff *skb, gfp_t gfp, + int oob); +unsigned long dn_nsp_persist(struct sock *sk); +int dn_nsp_xmit_timeout(struct sock *sk); + +int dn_nsp_rx(struct sk_buff *); +int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb); + +struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri); +struct sk_buff *dn_alloc_send_skb(struct sock *sk, size_t *size, int noblock, + long timeo, int *err); #define NSP_REASON_OK 0 /* No error */ #define NSP_REASON_NR 1 /* No resources */ diff --git a/include/net/dn_route.h b/include/net/dn_route.h index 2e9d317c82dcc7164ef9c3cfbfc8429c6364031a..b409ad6b8d7adbb5bd25ad3b71cbea5c8723b70e 100644 --- a/include/net/dn_route.h +++ b/include/net/dn_route.h @@ -15,10 +15,11 @@ GNU General Public License for more details. *******************************************************************************/ -extern struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri); -extern int dn_route_output_sock(struct dst_entry __rcu **pprt, struct flowidn *, struct sock *sk, int flags); -extern int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb); -extern void dn_rt_cache_flush(int delay); +struct sk_buff *dn_alloc_skb(struct sock *sk, int size, gfp_t pri); +int dn_route_output_sock(struct dst_entry __rcu **pprt, struct flowidn *, + struct sock *sk, int flags); +int dn_cache_dump(struct sk_buff *skb, struct netlink_callback *cb); +void dn_rt_cache_flush(int delay); /* Masks for flags field */ #define DN_RT_F_PID 0x07 /* Mask for packet type */ @@ -92,8 +93,8 @@ static inline bool dn_is_output_route(struct dn_route *rt) return rt->fld.flowidn_iif == 0; } -extern void dn_route_init(void); -extern void dn_route_cleanup(void); +void dn_route_init(void); +void dn_route_cleanup(void); #include #include diff --git a/include/net/dst.h b/include/net/dst.h index 3c4c944096c9e5133695a415b530c80908980f39..44995c13e941df814db2433819d2934869df4d0f 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -106,7 +106,7 @@ struct dst_entry { }; }; -extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); +u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old); extern const u32 dst_default_metrics[]; #define DST_METRICS_READ_ONLY 0x1UL @@ -119,7 +119,7 @@ static inline bool dst_metrics_read_only(const struct dst_entry *dst) return dst->_metrics & DST_METRICS_READ_ONLY; } -extern void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old); +void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old); static inline void dst_destroy_metrics_generic(struct dst_entry *dst) { @@ -262,7 +262,7 @@ static inline struct dst_entry *dst_clone(struct dst_entry *dst) return dst; } -extern void dst_release(struct dst_entry *dst); +void dst_release(struct dst_entry *dst); static inline void refdst_drop(unsigned long refdst) { @@ -362,12 +362,11 @@ static inline struct dst_entry *skb_dst_pop(struct sk_buff *skb) return child; } -extern int dst_discard(struct sk_buff *skb); -extern void *dst_alloc(struct dst_ops *ops, struct net_device *dev, - int initial_ref, int initial_obsolete, - unsigned short flags); -extern void __dst_free(struct dst_entry *dst); -extern struct dst_entry *dst_destroy(struct dst_entry *dst); +int dst_discard(struct sk_buff *skb); +void *dst_alloc(struct dst_ops *ops, struct net_device *dev, int initial_ref, + int initial_obsolete, unsigned short flags); +void __dst_free(struct dst_entry *dst); +struct dst_entry *dst_destroy(struct dst_entry *dst); static inline void dst_free(struct dst_entry *dst) { @@ -463,7 +462,7 @@ static inline struct dst_entry *dst_check(struct dst_entry *dst, u32 cookie) return dst; } -extern void dst_init(void); +void dst_init(void); /* Flags for xfrm_lookup flags argument. */ enum { @@ -486,9 +485,9 @@ static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst) } #else -extern struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, - const struct flowi *fl, struct sock *sk, - int flags); +struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, + const struct flowi *fl, struct sock *sk, + int flags); /* skb attached with this dst needs transformation if dst->xfrm is valid */ static inline struct xfrm_state *dst_xfrm(const struct dst_entry *dst) diff --git a/include/net/esp.h b/include/net/esp.h index d58451331dbde8a0d9b4231a61bd4957f62cdeec..a43be85aedc44594265695e98452ae491b852d83 100644 --- a/include/net/esp.h +++ b/include/net/esp.h @@ -3,18 +3,6 @@ #include -struct crypto_aead; - -struct esp_data { - /* 0..255 */ - int padlen; - - /* Confidentiality & Integrity */ - struct crypto_aead *aead; -}; - -extern void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len); - struct ip_esp_hdr; static inline struct ip_esp_hdr *ip_esp_hdr(const struct sk_buff *skb) diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index 4b2b557fb0e8c2d709e1733a13e4ca3dbc02ff0e..e584de16e4c3629ccebef06d000b47166e5cc65d 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -115,14 +115,13 @@ static inline u32 frh_get_table(struct fib_rule_hdr *frh, struct nlattr **nla) return frh->table; } -extern struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, struct net *); -extern void fib_rules_unregister(struct fib_rules_ops *); +struct fib_rules_ops *fib_rules_register(const struct fib_rules_ops *, + struct net *); +void fib_rules_unregister(struct fib_rules_ops *); -extern int fib_rules_lookup(struct fib_rules_ops *, - struct flowi *, int flags, - struct fib_lookup_arg *); -extern int fib_default_rule_add(struct fib_rules_ops *, - u32 pref, u32 table, - u32 flags); -extern u32 fib_default_rule_pref(struct fib_rules_ops *ops); +int fib_rules_lookup(struct fib_rules_ops *, struct flowi *, int flags, + struct fib_lookup_arg *); +int fib_default_rule_add(struct fib_rules_ops *, u32 pref, u32 table, + u32 flags); +u32 fib_default_rule_pref(struct fib_rules_ops *ops); #endif diff --git a/include/net/flow.h b/include/net/flow.h index 628e11b98c580d7f66ace375f5a4e0598d03b471..65ce471d2ab577f81cc62040018aff980ea82f65 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -215,12 +215,13 @@ typedef struct flow_cache_object *(*flow_resolve_t)( struct net *net, const struct flowi *key, u16 family, u8 dir, struct flow_cache_object *oldobj, void *ctx); -extern struct flow_cache_object *flow_cache_lookup( - struct net *net, const struct flowi *key, u16 family, - u8 dir, flow_resolve_t resolver, void *ctx); +struct flow_cache_object *flow_cache_lookup(struct net *net, + const struct flowi *key, u16 family, + u8 dir, flow_resolve_t resolver, + void *ctx); -extern void flow_cache_flush(void); -extern void flow_cache_flush_deferred(void); +void flow_cache_flush(void); +void flow_cache_flush_deferred(void); extern atomic_t flow_cache_genid; #endif diff --git a/include/net/flow_keys.h b/include/net/flow_keys.h index bb8271d487b7bea10861c6548dde50d0964c4a20..7e64bd8bbda941319b494bcb7b66e79b7f52370a 100644 --- a/include/net/flow_keys.h +++ b/include/net/flow_keys.h @@ -13,5 +13,6 @@ struct flow_keys { u8 ip_proto; }; -extern bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow); +bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow); +__be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto); #endif diff --git a/include/net/garp.h b/include/net/garp.h index 834d8add9e5fd51112f08cde966e0044604049cb..abf33bbd2e6a0413193769d353bb9ce82a10ebec 100644 --- a/include/net/garp.h +++ b/include/net/garp.h @@ -112,19 +112,18 @@ struct garp_port { struct rcu_head rcu; }; -extern int garp_register_application(struct garp_application *app); -extern void garp_unregister_application(struct garp_application *app); - -extern int garp_init_applicant(struct net_device *dev, - struct garp_application *app); -extern void garp_uninit_applicant(struct net_device *dev, - struct garp_application *app); - -extern int garp_request_join(const struct net_device *dev, - const struct garp_application *app, - const void *data, u8 len, u8 type); -extern void garp_request_leave(const struct net_device *dev, - const struct garp_application *app, - const void *data, u8 len, u8 type); +int garp_register_application(struct garp_application *app); +void garp_unregister_application(struct garp_application *app); + +int garp_init_applicant(struct net_device *dev, struct garp_application *app); +void garp_uninit_applicant(struct net_device *dev, + struct garp_application *app); + +int garp_request_join(const struct net_device *dev, + const struct garp_application *app, const void *data, + u8 len, u8 type); +void garp_request_leave(const struct net_device *dev, + const struct garp_application *app, + const void *data, u8 len, u8 type); #endif /* _NET_GARP_H */ diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h index cf8439ba4d11062a326919fa885c3ae5a24e62c5..ea4271dceff0da2df04497e424e49c18b9a6d6f5 100644 --- a/include/net/gen_stats.h +++ b/include/net/gen_stats.h @@ -19,32 +19,31 @@ struct gnet_dump { struct tc_stats tc_stats; }; -extern int gnet_stats_start_copy(struct sk_buff *skb, int type, +int gnet_stats_start_copy(struct sk_buff *skb, int type, spinlock_t *lock, + struct gnet_dump *d); + +int gnet_stats_start_copy_compat(struct sk_buff *skb, int type, + int tc_stats_type, int xstats_type, spinlock_t *lock, struct gnet_dump *d); -extern int gnet_stats_start_copy_compat(struct sk_buff *skb, int type, - int tc_stats_type,int xstats_type, - spinlock_t *lock, struct gnet_dump *d); - -extern int gnet_stats_copy_basic(struct gnet_dump *d, - struct gnet_stats_basic_packed *b); -extern int gnet_stats_copy_rate_est(struct gnet_dump *d, - const struct gnet_stats_basic_packed *b, - struct gnet_stats_rate_est64 *r); -extern int gnet_stats_copy_queue(struct gnet_dump *d, - struct gnet_stats_queue *q); -extern int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len); - -extern int gnet_stats_finish_copy(struct gnet_dump *d); - -extern int gen_new_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est64 *rate_est, - spinlock_t *stats_lock, struct nlattr *opt); -extern void gen_kill_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est64 *rate_est); -extern int gen_replace_estimator(struct gnet_stats_basic_packed *bstats, - struct gnet_stats_rate_est64 *rate_est, - spinlock_t *stats_lock, struct nlattr *opt); -extern bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats, - const struct gnet_stats_rate_est64 *rate_est); +int gnet_stats_copy_basic(struct gnet_dump *d, + struct gnet_stats_basic_packed *b); +int gnet_stats_copy_rate_est(struct gnet_dump *d, + const struct gnet_stats_basic_packed *b, + struct gnet_stats_rate_est64 *r); +int gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q); +int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len); + +int gnet_stats_finish_copy(struct gnet_dump *d); + +int gen_new_estimator(struct gnet_stats_basic_packed *bstats, + struct gnet_stats_rate_est64 *rate_est, + spinlock_t *stats_lock, struct nlattr *opt); +void gen_kill_estimator(struct gnet_stats_basic_packed *bstats, + struct gnet_stats_rate_est64 *rate_est); +int gen_replace_estimator(struct gnet_stats_basic_packed *bstats, + struct gnet_stats_rate_est64 *rate_est, + spinlock_t *stats_lock, struct nlattr *opt); +bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats, + const struct gnet_stats_rate_est64 *rate_est); #endif diff --git a/include/net/genetlink.h b/include/net/genetlink.h index 8e0b6c856a1302227812acbc10cb5e6b1f230bf7..9b787b62cf16066e6f2335301865849fa674f7a1 100644 --- a/include/net/genetlink.h +++ b/include/net/genetlink.h @@ -122,7 +122,7 @@ struct genl_ops { struct list_head ops_list; }; -extern int __genl_register_family(struct genl_family *family); +int __genl_register_family(struct genl_family *family); static inline int genl_register_family(struct genl_family *family) { @@ -130,8 +130,8 @@ static inline int genl_register_family(struct genl_family *family) return __genl_register_family(family); } -extern int __genl_register_family_with_ops(struct genl_family *family, - struct genl_ops *ops, size_t n_ops); +int __genl_register_family_with_ops(struct genl_family *family, + struct genl_ops *ops, size_t n_ops); static inline int genl_register_family_with_ops(struct genl_family *family, struct genl_ops *ops, size_t n_ops) @@ -140,18 +140,18 @@ static inline int genl_register_family_with_ops(struct genl_family *family, return __genl_register_family_with_ops(family, ops, n_ops); } -extern int genl_unregister_family(struct genl_family *family); -extern int genl_register_ops(struct genl_family *, struct genl_ops *ops); -extern int genl_unregister_ops(struct genl_family *, struct genl_ops *ops); -extern int genl_register_mc_group(struct genl_family *family, - struct genl_multicast_group *grp); -extern void genl_unregister_mc_group(struct genl_family *family, - struct genl_multicast_group *grp); -extern void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, - u32 group, struct nlmsghdr *nlh, gfp_t flags); +int genl_unregister_family(struct genl_family *family); +int genl_register_ops(struct genl_family *, struct genl_ops *ops); +int genl_unregister_ops(struct genl_family *, struct genl_ops *ops); +int genl_register_mc_group(struct genl_family *family, + struct genl_multicast_group *grp); +void genl_unregister_mc_group(struct genl_family *family, + struct genl_multicast_group *grp); +void genl_notify(struct sk_buff *skb, struct net *net, u32 portid, + u32 group, struct nlmsghdr *nlh, gfp_t flags); void *genlmsg_put(struct sk_buff *skb, u32 portid, u32 seq, - struct genl_family *family, int flags, u8 cmd); + struct genl_family *family, int flags, u8 cmd); /** * genlmsg_nlhdr - Obtain netlink header from user specified header diff --git a/include/net/gre.h b/include/net/gre.h index 57e4afdf7879ca9436fa139c31e3f162a407e502..dcd9ae3270d386b06d7565cfb8b7c4bb59dde740 100644 --- a/include/net/gre.h +++ b/include/net/gre.h @@ -38,7 +38,13 @@ void gre_offload_exit(void); void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, int hdr_len); -struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum); + +static inline struct sk_buff *gre_handle_offloads(struct sk_buff *skb, + bool gre_csum) +{ + return iptunnel_handle_offloads(skb, gre_csum, SKB_GSO_GRE); +} + static inline int ip_gre_calc_hlen(__be16 o_flags) { diff --git a/include/net/icmp.h b/include/net/icmp.h index 081439fd070ed7ce932ff1e1877398d1d119207c..970028e13382807108764f8784d9b1f11d7078d7 100644 --- a/include/net/icmp.h +++ b/include/net/icmp.h @@ -39,10 +39,10 @@ struct net_proto_family; struct sk_buff; struct net; -extern void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info); -extern int icmp_rcv(struct sk_buff *skb); -extern void icmp_err(struct sk_buff *, u32 info); -extern int icmp_init(void); -extern void icmp_out_count(struct net *net, unsigned char type); +void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info); +int icmp_rcv(struct sk_buff *skb); +void icmp_err(struct sk_buff *skb, u32 info); +int icmp_init(void); +void icmp_out_count(struct net *net, unsigned char type); #endif /* _ICMP_H */ diff --git a/include/net/if_inet6.h b/include/net/if_inet6.h index 02ef7727bb5525246a6303206ce66d8e32afa689..76d54270f2e27c175836b137f363014381e91687 100644 --- a/include/net/if_inet6.h +++ b/include/net/if_inet6.h @@ -66,11 +66,10 @@ struct inet6_ifaddr { struct hlist_node addr_lst; struct list_head if_list; -#ifdef CONFIG_IPV6_PRIVACY struct list_head tmp_list; struct inet6_ifaddr *ifpub; int regen_count; -#endif + bool tokenized; struct rcu_head rcu; @@ -192,11 +191,9 @@ struct inet6_dev { __u32 if_flags; int dead; -#ifdef CONFIG_IPV6_PRIVACY u8 rndid[8]; struct timer_list regen_timer; struct list_head tempaddr_list; -#endif struct in6_addr token; diff --git a/include/net/inet6_connection_sock.h b/include/net/inet6_connection_sock.h index 04642c920431151fd09f00bc1400c164e0a2f9c0..f981ba7adeed4c5dbaba8d5e662768d3503bfec7 100644 --- a/include/net/inet6_connection_sock.h +++ b/include/net/inet6_connection_sock.h @@ -22,27 +22,25 @@ struct sk_buff; struct sock; struct sockaddr; -extern int inet6_csk_bind_conflict(const struct sock *sk, - const struct inet_bind_bucket *tb, bool relax); +int inet6_csk_bind_conflict(const struct sock *sk, + const struct inet_bind_bucket *tb, bool relax); -extern struct dst_entry* inet6_csk_route_req(struct sock *sk, - struct flowi6 *fl6, - const struct request_sock *req); +struct dst_entry *inet6_csk_route_req(struct sock *sk, struct flowi6 *fl6, + const struct request_sock *req); -extern struct request_sock *inet6_csk_search_req(const struct sock *sk, - struct request_sock ***prevp, - const __be16 rport, - const struct in6_addr *raddr, - const struct in6_addr *laddr, - const int iif); +struct request_sock *inet6_csk_search_req(const struct sock *sk, + struct request_sock ***prevp, + const __be16 rport, + const struct in6_addr *raddr, + const struct in6_addr *laddr, + const int iif); -extern void inet6_csk_reqsk_queue_hash_add(struct sock *sk, - struct request_sock *req, - const unsigned long timeout); +void inet6_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, + const unsigned long timeout); -extern void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr); +void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr); -extern int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl); +int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl); -extern struct dst_entry *inet6_csk_update_pmtu(struct sock *sk, u32 mtu); +struct dst_entry *inet6_csk_update_pmtu(struct sock *sk, u32 mtu); #endif /* _INET6_CONNECTION_SOCK_H */ diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index fd4ee016ba5cd5e4dc87b08cf06aef17a6b9cc27..ae061354430836687225be9ae1a8c1f09a35ed8a 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h @@ -28,32 +28,17 @@ struct inet_hashinfo; -static inline unsigned int inet6_ehashfn(struct net *net, - const struct in6_addr *laddr, const u16 lport, - const struct in6_addr *faddr, const __be16 fport) +static inline unsigned int __inet6_ehashfn(const u32 lhash, + const u16 lport, + const u32 fhash, + const __be16 fport, + const u32 initval) { - u32 ports = (((u32)lport) << 16) | (__force u32)fport; - - return jhash_3words((__force u32)laddr->s6_addr32[3], - ipv6_addr_jhash(faddr), - ports, - inet_ehash_secret + net_hash_mix(net)); -} - -static inline int inet6_sk_ehashfn(const struct sock *sk) -{ - const struct inet_sock *inet = inet_sk(sk); - const struct ipv6_pinfo *np = inet6_sk(sk); - const struct in6_addr *laddr = &np->rcv_saddr; - const struct in6_addr *faddr = &np->daddr; - const __u16 lport = inet->inet_num; - const __be16 fport = inet->inet_dport; - struct net *net = sock_net(sk); - - return inet6_ehashfn(net, laddr, lport, faddr, fport); + const u32 ports = (((u32)lport) << 16) | (__force u32)fport; + return jhash_3words(lhash, fhash, ports, initval); } -extern int __inet6_hash(struct sock *sk, struct inet_timewait_sock *twp); +int __inet6_hash(struct sock *sk, struct inet_timewait_sock *twp); /* * Sockets in TCP_CLOSE state are _always_ taken out of the hash, so @@ -61,21 +46,19 @@ extern int __inet6_hash(struct sock *sk, struct inet_timewait_sock *twp); * * The sockhash lock must be held as a reader here. */ -extern struct sock *__inet6_lookup_established(struct net *net, - struct inet_hashinfo *hashinfo, - const struct in6_addr *saddr, - const __be16 sport, - const struct in6_addr *daddr, - const u16 hnum, - const int dif); - -extern struct sock *inet6_lookup_listener(struct net *net, - struct inet_hashinfo *hashinfo, - const struct in6_addr *saddr, - const __be16 sport, - const struct in6_addr *daddr, - const unsigned short hnum, - const int dif); +struct sock *__inet6_lookup_established(struct net *net, + struct inet_hashinfo *hashinfo, + const struct in6_addr *saddr, + const __be16 sport, + const struct in6_addr *daddr, + const u16 hnum, const int dif); + +struct sock *inet6_lookup_listener(struct net *net, + struct inet_hashinfo *hashinfo, + const struct in6_addr *saddr, + const __be16 sport, + const struct in6_addr *daddr, + const unsigned short hnum, const int dif); static inline struct sock *__inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo, @@ -110,9 +93,9 @@ static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo, inet6_iif(skb)); } -extern struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo, - const struct in6_addr *saddr, const __be16 sport, - const struct in6_addr *daddr, const __be16 dport, - const int dif); +struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo, + const struct in6_addr *saddr, const __be16 sport, + const struct in6_addr *daddr, const __be16 dport, + const int dif); #endif /* IS_ENABLED(CONFIG_IPV6) */ #endif /* _INET6_HASHTABLES_H */ diff --git a/include/net/inet_common.h b/include/net/inet_common.h index 234008782c8cca00e2af6394c17391189abd974a..fe7994c48b75685174134e7817bbb20ef375f890 100644 --- a/include/net/inet_common.h +++ b/include/net/inet_common.h @@ -13,30 +13,30 @@ struct sock; struct sockaddr; struct socket; -extern int inet_release(struct socket *sock); -extern int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags); -extern int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags); -extern int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags); -extern int inet_accept(struct socket *sock, struct socket *newsock, int flags); -extern int inet_sendmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size); -extern ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, - size_t size, int flags); -extern int inet_recvmsg(struct kiocb *iocb, struct socket *sock, - struct msghdr *msg, size_t size, int flags); -extern int inet_shutdown(struct socket *sock, int how); -extern int inet_listen(struct socket *sock, int backlog); -extern void inet_sock_destruct(struct sock *sk); -extern int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); -extern int inet_getname(struct socket *sock, struct sockaddr *uaddr, - int *uaddr_len, int peer); -extern int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); -extern int inet_ctl_sock_create(struct sock **sk, unsigned short family, - unsigned short type, unsigned char protocol, - struct net *net); +int inet_release(struct socket *sock); +int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags); +int __inet_stream_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags); +int inet_dgram_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags); +int inet_accept(struct socket *sock, struct socket *newsock, int flags); +int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, + size_t size); +ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, + size_t size, int flags); +int inet_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, + size_t size, int flags); +int inet_shutdown(struct socket *sock, int how); +int inet_listen(struct socket *sock, int backlog); +void inet_sock_destruct(struct sock *sk); +int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); +int inet_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, + int peer); +int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); +int inet_ctl_sock_create(struct sock **sk, unsigned short family, + unsigned short type, unsigned char protocol, + struct net *net); static inline void inet_ctl_sock_destroy(struct sock *sk) { diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h index de2c78529afaf81f00abff62bb29708f7f6941a2..c55aeed41acea4ce12b49b2d77862a4cf609363a 100644 --- a/include/net/inet_connection_sock.h +++ b/include/net/inet_connection_sock.h @@ -146,9 +146,9 @@ static inline void *inet_csk_ca(const struct sock *sk) return (void *)inet_csk(sk)->icsk_ca_priv; } -extern struct sock *inet_csk_clone_lock(const struct sock *sk, - const struct request_sock *req, - const gfp_t priority); +struct sock *inet_csk_clone_lock(const struct sock *sk, + const struct request_sock *req, + const gfp_t priority); enum inet_csk_ack_state_t { ICSK_ACK_SCHED = 1, @@ -157,11 +157,11 @@ enum inet_csk_ack_state_t { ICSK_ACK_PUSHED2 = 8 }; -extern void inet_csk_init_xmit_timers(struct sock *sk, - void (*retransmit_handler)(unsigned long), - void (*delack_handler)(unsigned long), - void (*keepalive_handler)(unsigned long)); -extern void inet_csk_clear_xmit_timers(struct sock *sk); +void inet_csk_init_xmit_timers(struct sock *sk, + void (*retransmit_handler)(unsigned long), + void (*delack_handler)(unsigned long), + void (*keepalive_handler)(unsigned long)); +void inet_csk_clear_xmit_timers(struct sock *sk); static inline void inet_csk_schedule_ack(struct sock *sk) { @@ -178,8 +178,8 @@ static inline void inet_csk_delack_init(struct sock *sk) memset(&inet_csk(sk)->icsk_ack, 0, sizeof(inet_csk(sk)->icsk_ack)); } -extern void inet_csk_delete_keepalive_timer(struct sock *sk); -extern void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long timeout); +void inet_csk_delete_keepalive_timer(struct sock *sk); +void inet_csk_reset_keepalive_timer(struct sock *sk, unsigned long timeout); #ifdef INET_CSK_DEBUG extern const char inet_csk_timer_bug_msg[]; @@ -241,23 +241,21 @@ static inline void inet_csk_reset_xmit_timer(struct sock *sk, const int what, #endif } -extern struct sock *inet_csk_accept(struct sock *sk, int flags, int *err); +struct sock *inet_csk_accept(struct sock *sk, int flags, int *err); -extern struct request_sock *inet_csk_search_req(const struct sock *sk, - struct request_sock ***prevp, - const __be16 rport, - const __be32 raddr, - const __be32 laddr); -extern int inet_csk_bind_conflict(const struct sock *sk, - const struct inet_bind_bucket *tb, bool relax); -extern int inet_csk_get_port(struct sock *sk, unsigned short snum); +struct request_sock *inet_csk_search_req(const struct sock *sk, + struct request_sock ***prevp, + const __be16 rport, + const __be32 raddr, + const __be32 laddr); +int inet_csk_bind_conflict(const struct sock *sk, + const struct inet_bind_bucket *tb, bool relax); +int inet_csk_get_port(struct sock *sk, unsigned short snum); -extern struct dst_entry* inet_csk_route_req(struct sock *sk, - struct flowi4 *fl4, +struct dst_entry *inet_csk_route_req(struct sock *sk, struct flowi4 *fl4, + const struct request_sock *req); +struct dst_entry *inet_csk_route_child_sock(struct sock *sk, struct sock *newsk, const struct request_sock *req); -extern struct dst_entry* inet_csk_route_child_sock(struct sock *sk, - struct sock *newsk, - const struct request_sock *req); static inline void inet_csk_reqsk_queue_add(struct sock *sk, struct request_sock *req, @@ -266,9 +264,8 @@ static inline void inet_csk_reqsk_queue_add(struct sock *sk, reqsk_queue_add(&inet_csk(sk)->icsk_accept_queue, req, sk, child); } -extern void inet_csk_reqsk_queue_hash_add(struct sock *sk, - struct request_sock *req, - unsigned long timeout); +void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, + unsigned long timeout); static inline void inet_csk_reqsk_queue_removed(struct sock *sk, struct request_sock *req) @@ -315,13 +312,13 @@ static inline void inet_csk_reqsk_queue_drop(struct sock *sk, reqsk_free(req); } -extern void inet_csk_reqsk_queue_prune(struct sock *parent, - const unsigned long interval, - const unsigned long timeout, - const unsigned long max_rto); +void inet_csk_reqsk_queue_prune(struct sock *parent, + const unsigned long interval, + const unsigned long timeout, + const unsigned long max_rto); -extern void inet_csk_destroy_sock(struct sock *sk); -extern void inet_csk_prepare_forced_close(struct sock *sk); +void inet_csk_destroy_sock(struct sock *sk); +void inet_csk_prepare_forced_close(struct sock *sk); /* * LISTEN is a special case for poll.. @@ -332,15 +329,15 @@ static inline unsigned int inet_csk_listen_poll(const struct sock *sk) (POLLIN | POLLRDNORM) : 0; } -extern int inet_csk_listen_start(struct sock *sk, const int nr_table_entries); -extern void inet_csk_listen_stop(struct sock *sk); +int inet_csk_listen_start(struct sock *sk, const int nr_table_entries); +void inet_csk_listen_stop(struct sock *sk); -extern void inet_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr); +void inet_csk_addr2sockaddr(struct sock *sk, struct sockaddr *uaddr); -extern int inet_csk_compat_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen); -extern int inet_csk_compat_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen); +int inet_csk_compat_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); +int inet_csk_compat_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); -extern struct dst_entry *inet_csk_update_pmtu(struct sock *sk, u32 mtu); +struct dst_entry *inet_csk_update_pmtu(struct sock *sk, u32 mtu); #endif /* _INET_CONNECTION_SOCK_H */ diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index bfcbc0017950372ddd39dd540ff3f6da99f2bee6..6f59de98dabde3be0daa202f8be4528d49676f7f 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -64,6 +64,10 @@ struct inet_frags { rwlock_t lock ____cacheline_aligned_in_smp; int secret_interval; struct timer_list secret_timer; + + /* The first call to hashfn is responsible to initialize + * rnd. This is best done with net_get_random_once. + */ u32 rnd; int qsize; diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index ef83d9e844b54f0468034ce54da3d4552868f7e9..1bdb47715def0e21496ae89a59d3d9bd5f1f2c81 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -37,12 +37,11 @@ #include /* This is for all connections with a full identity, no wildcards. - * One chain is dedicated to TIME_WAIT sockets. - * I'll experiment with dynamic table growth later. + * The 'e' prefix stands for Establish, but we really put all sockets + * but LISTEN ones. */ struct inet_ehash_bucket { struct hlist_nulls_head chain; - struct hlist_nulls_head twchain; }; /* There are a few simple rules, which allow for local port reuse by @@ -123,7 +122,6 @@ struct inet_hashinfo { * * TCP_ESTABLISHED <= sk->sk_state < TCP_CLOSE * - * TIME_WAIT sockets use a separate chain (twchain). */ struct inet_ehash_bucket *ehash; spinlock_t *ehash_locks; @@ -218,22 +216,21 @@ static inline void inet_ehash_locks_free(struct inet_hashinfo *hashinfo) } } -extern struct inet_bind_bucket * - inet_bind_bucket_create(struct kmem_cache *cachep, - struct net *net, - struct inet_bind_hashbucket *head, - const unsigned short snum); -extern void inet_bind_bucket_destroy(struct kmem_cache *cachep, - struct inet_bind_bucket *tb); +struct inet_bind_bucket * +inet_bind_bucket_create(struct kmem_cache *cachep, struct net *net, + struct inet_bind_hashbucket *head, + const unsigned short snum); +void inet_bind_bucket_destroy(struct kmem_cache *cachep, + struct inet_bind_bucket *tb); -static inline int inet_bhashfn(struct net *net, - const __u16 lport, const int bhash_size) +static inline int inet_bhashfn(struct net *net, const __u16 lport, + const int bhash_size) { return (lport + net_hash_mix(net)) & (bhash_size - 1); } -extern void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, - const unsigned short snum); +void inet_bind_hash(struct sock *sk, struct inet_bind_bucket *tb, + const unsigned short snum); /* These can have wildcards, don't try too hard. */ static inline int inet_lhashfn(struct net *net, const unsigned short num) @@ -247,23 +244,22 @@ static inline int inet_sk_listen_hashfn(const struct sock *sk) } /* Caller must disable local BH processing. */ -extern int __inet_inherit_port(struct sock *sk, struct sock *child); +int __inet_inherit_port(struct sock *sk, struct sock *child); -extern void inet_put_port(struct sock *sk); +void inet_put_port(struct sock *sk); void inet_hashinfo_init(struct inet_hashinfo *h); -extern int __inet_hash_nolisten(struct sock *sk, struct inet_timewait_sock *tw); -extern void inet_hash(struct sock *sk); -extern void inet_unhash(struct sock *sk); +int __inet_hash_nolisten(struct sock *sk, struct inet_timewait_sock *tw); +void inet_hash(struct sock *sk); +void inet_unhash(struct sock *sk); -extern struct sock *__inet_lookup_listener(struct net *net, - struct inet_hashinfo *hashinfo, - const __be32 saddr, - const __be16 sport, - const __be32 daddr, - const unsigned short hnum, - const int dif); +struct sock *__inet_lookup_listener(struct net *net, + struct inet_hashinfo *hashinfo, + const __be32 saddr, const __be16 sport, + const __be32 daddr, + const unsigned short hnum, + const int dif); static inline struct sock *inet_lookup_listener(struct net *net, struct inet_hashinfo *hashinfo, @@ -304,30 +300,17 @@ static inline struct sock *inet_lookup_listener(struct net *net, ((__force __u64)(__be32)(__saddr))); #endif /* __BIG_ENDIAN */ #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ - ((inet_sk(__sk)->inet_portpair == (__ports)) && \ - (inet_sk(__sk)->inet_addrpair == (__cookie)) && \ + (((__sk)->sk_portpair == (__ports)) && \ + ((__sk)->sk_addrpair == (__cookie)) && \ (!(__sk)->sk_bound_dev_if || \ ((__sk)->sk_bound_dev_if == (__dif))) && \ net_eq(sock_net(__sk), (__net))) -#define INET_TW_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif)\ - ((inet_twsk(__sk)->tw_portpair == (__ports)) && \ - (inet_twsk(__sk)->tw_addrpair == (__cookie)) && \ - (!(__sk)->sk_bound_dev_if || \ - ((__sk)->sk_bound_dev_if == (__dif))) && \ - net_eq(sock_net(__sk), (__net))) #else /* 32-bit arch */ #define INET_ADDR_COOKIE(__name, __saddr, __daddr) #define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ - ((inet_sk(__sk)->inet_portpair == (__ports)) && \ - (inet_sk(__sk)->inet_daddr == (__saddr)) && \ - (inet_sk(__sk)->inet_rcv_saddr == (__daddr)) && \ - (!(__sk)->sk_bound_dev_if || \ - ((__sk)->sk_bound_dev_if == (__dif))) && \ - net_eq(sock_net(__sk), (__net))) -#define INET_TW_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ - ((inet_twsk(__sk)->tw_portpair == (__ports)) && \ - (inet_twsk(__sk)->tw_daddr == (__saddr)) && \ - (inet_twsk(__sk)->tw_rcv_saddr == (__daddr)) && \ + (((__sk)->sk_portpair == (__ports)) && \ + ((__sk)->sk_daddr == (__saddr)) && \ + ((__sk)->sk_rcv_saddr == (__daddr)) && \ (!(__sk)->sk_bound_dev_if || \ ((__sk)->sk_bound_dev_if == (__dif))) && \ net_eq(sock_net(__sk), (__net))) @@ -339,10 +322,11 @@ static inline struct sock *inet_lookup_listener(struct net *net, * * Local BH must be disabled here. */ -extern struct sock * __inet_lookup_established(struct net *net, - struct inet_hashinfo *hashinfo, - const __be32 saddr, const __be16 sport, - const __be32 daddr, const u16 hnum, const int dif); +struct sock *__inet_lookup_established(struct net *net, + struct inet_hashinfo *hashinfo, + const __be32 saddr, const __be16 sport, + const __be32 daddr, const u16 hnum, + const int dif); static inline struct sock * inet_lookup_established(struct net *net, struct inet_hashinfo *hashinfo, @@ -399,13 +383,14 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo, iph->daddr, dport, inet_iif(skb)); } -extern int __inet_hash_connect(struct inet_timewait_death_row *death_row, - struct sock *sk, - u32 port_offset, - int (*check_established)(struct inet_timewait_death_row *, - struct sock *, __u16, struct inet_timewait_sock **), - int (*hash)(struct sock *sk, struct inet_timewait_sock *twp)); +int __inet_hash_connect(struct inet_timewait_death_row *death_row, + struct sock *sk, u32 port_offset, + int (*check_established)(struct inet_timewait_death_row *, + struct sock *, __u16, + struct inet_timewait_sock **), + int (*hash)(struct sock *sk, + struct inet_timewait_sock *twp)); -extern int inet_hash_connect(struct inet_timewait_death_row *death_row, - struct sock *sk); +int inet_hash_connect(struct inet_timewait_death_row *death_row, + struct sock *sk); #endif /* _INET_HASHTABLES_H */ diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index b21a7f06d6a4955910b577a23b04eb904207265a..1833c3f389ee64a0c6b3862d4f2fbc6db0984b0a 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -70,13 +70,14 @@ struct ip_options_data { struct inet_request_sock { struct request_sock req; -#if IS_ENABLED(CONFIG_IPV6) - u16 inet6_rsk_offset; -#endif - __be16 loc_port; - __be32 loc_addr; - __be32 rmt_addr; - __be16 rmt_port; +#define ir_loc_addr req.__req_common.skc_rcv_saddr +#define ir_rmt_addr req.__req_common.skc_daddr +#define ir_num req.__req_common.skc_num +#define ir_rmt_port req.__req_common.skc_dport +#define ir_v6_rmt_addr req.__req_common.skc_v6_daddr +#define ir_v6_loc_addr req.__req_common.skc_v6_rcv_saddr +#define ir_iif req.__req_common.skc_bound_dev_if + kmemcheck_bitfield_begin(flags); u16 snd_wscale : 4, rcv_wscale : 4, @@ -88,6 +89,7 @@ struct inet_request_sock { no_srccheck: 1; kmemcheck_bitfield_end(flags); struct ip_options_rcu *opt; + struct sk_buff *pktopts; }; static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) @@ -103,6 +105,9 @@ struct inet_cork { int length; /* Total length of all frames */ struct dst_entry *dst; u8 tx_flags; + __u8 ttl; + __s16 tos; + char priority; }; struct inet_cork_full { @@ -143,10 +148,8 @@ struct inet_sock { /* Socket demultiplex comparisons on incoming packets. */ #define inet_daddr sk.__sk_common.skc_daddr #define inet_rcv_saddr sk.__sk_common.skc_rcv_saddr -#define inet_addrpair sk.__sk_common.skc_addrpair #define inet_dport sk.__sk_common.skc_dport #define inet_num sk.__sk_common.skc_num -#define inet_portpair sk.__sk_common.skc_portpair __be32 inet_saddr; __s16 uc_ttl; @@ -199,32 +202,18 @@ static inline void inet_sk_copy_descendant(struct sock *sk_to, } #endif -extern int inet_sk_rebuild_header(struct sock *sk); - -extern u32 inet_ehash_secret; -extern u32 ipv6_hash_secret; -extern void build_ehash_secret(void); +int inet_sk_rebuild_header(struct sock *sk); -static inline unsigned int inet_ehashfn(struct net *net, - const __be32 laddr, const __u16 lport, - const __be32 faddr, const __be16 fport) +static inline unsigned int __inet_ehashfn(const __be32 laddr, + const __u16 lport, + const __be32 faddr, + const __be16 fport, + u32 initval) { return jhash_3words((__force __u32) laddr, (__force __u32) faddr, ((__u32) lport) << 16 | (__force __u32)fport, - inet_ehash_secret + net_hash_mix(net)); -} - -static inline int inet_sk_ehashfn(const struct sock *sk) -{ - const struct inet_sock *inet = inet_sk(sk); - const __be32 laddr = inet->inet_rcv_saddr; - const __u16 lport = inet->inet_num; - const __be32 faddr = inet->inet_daddr; - const __be16 fport = inet->inet_dport; - struct net *net = sock_net(sk); - - return inet_ehashfn(net, laddr, lport, faddr, fport); + initval); } static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops) diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index f908dfc06505850b54e6868c53da957ff71c3527..71c6e264e5b521bfa43033b13a0524ac2556d002 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -58,6 +58,11 @@ struct inet_hashinfo; # define INET_TWDR_RECYCLE_TICK (12 + 2 - INET_TWDR_RECYCLE_SLOTS_LOG) #endif +static inline u32 inet_tw_time_stamp(void) +{ + return jiffies; +} + /* TIME_WAIT reaping mechanism. */ #define INET_TWDR_TWKILL_SLOTS 8 /* Please keep this a power of 2. */ @@ -83,9 +88,9 @@ struct inet_timewait_death_row { int sysctl_max_tw_buckets; }; -extern void inet_twdr_hangman(unsigned long data); -extern void inet_twdr_twkill_work(struct work_struct *work); -extern void inet_twdr_twcal_tick(unsigned long data); +void inet_twdr_hangman(unsigned long data); +void inet_twdr_twkill_work(struct work_struct *work); +void inet_twdr_twcal_tick(unsigned long data); struct inet_bind_bucket; @@ -111,11 +116,11 @@ struct inet_timewait_sock { #define tw_prot __tw_common.skc_prot #define tw_net __tw_common.skc_net #define tw_daddr __tw_common.skc_daddr +#define tw_v6_daddr __tw_common.skc_v6_daddr #define tw_rcv_saddr __tw_common.skc_rcv_saddr -#define tw_addrpair __tw_common.skc_addrpair +#define tw_v6_rcv_saddr __tw_common.skc_v6_rcv_saddr #define tw_dport __tw_common.skc_dport #define tw_num __tw_common.skc_num -#define tw_portpair __tw_common.skc_portpair int tw_timeout; volatile unsigned char tw_substate; @@ -130,26 +135,14 @@ struct inet_timewait_sock { tw_transparent : 1, tw_pad : 6, /* 6 bits hole */ tw_tos : 8, - tw_ipv6_offset : 16; + tw_pad2 : 16; /* 16 bits hole */ kmemcheck_bitfield_end(flags); - unsigned long tw_ttd; + u32 tw_ttd; struct inet_bind_bucket *tw_tb; struct hlist_node tw_death_node; }; #define tw_tclass tw_tos -static inline void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw, - struct hlist_nulls_head *list) -{ - hlist_nulls_add_head_rcu(&tw->tw_node, list); -} - -static inline void inet_twsk_add_bind_node(struct inet_timewait_sock *tw, - struct hlist_head *list) -{ - hlist_add_head(&tw->tw_bind_node, list); -} - static inline int inet_twsk_dead_hashed(const struct inet_timewait_sock *tw) { return !hlist_unhashed(&tw->tw_death_node); @@ -189,34 +182,28 @@ static inline struct inet_timewait_sock *inet_twsk(const struct sock *sk) return (struct inet_timewait_sock *)sk; } -static inline __be32 sk_rcv_saddr(const struct sock *sk) -{ -/* both inet_sk() and inet_twsk() store rcv_saddr in skc_rcv_saddr */ - return sk->__sk_common.skc_rcv_saddr; -} - -extern void inet_twsk_put(struct inet_timewait_sock *tw); +void inet_twsk_free(struct inet_timewait_sock *tw); +void inet_twsk_put(struct inet_timewait_sock *tw); -extern int inet_twsk_unhash(struct inet_timewait_sock *tw); +int inet_twsk_unhash(struct inet_timewait_sock *tw); -extern int inet_twsk_bind_unhash(struct inet_timewait_sock *tw, - struct inet_hashinfo *hashinfo); +int inet_twsk_bind_unhash(struct inet_timewait_sock *tw, + struct inet_hashinfo *hashinfo); -extern struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, - const int state); +struct inet_timewait_sock *inet_twsk_alloc(const struct sock *sk, + const int state); -extern void __inet_twsk_hashdance(struct inet_timewait_sock *tw, - struct sock *sk, - struct inet_hashinfo *hashinfo); +void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, + struct inet_hashinfo *hashinfo); -extern void inet_twsk_schedule(struct inet_timewait_sock *tw, - struct inet_timewait_death_row *twdr, - const int timeo, const int timewait_len); -extern void inet_twsk_deschedule(struct inet_timewait_sock *tw, - struct inet_timewait_death_row *twdr); +void inet_twsk_schedule(struct inet_timewait_sock *tw, + struct inet_timewait_death_row *twdr, + const int timeo, const int timewait_len); +void inet_twsk_deschedule(struct inet_timewait_sock *tw, + struct inet_timewait_death_row *twdr); -extern void inet_twsk_purge(struct inet_hashinfo *hashinfo, - struct inet_timewait_death_row *twdr, int family); +void inet_twsk_purge(struct inet_hashinfo *hashinfo, + struct inet_timewait_death_row *twdr, int family); static inline struct net *twsk_net(const struct inet_timewait_sock *twsk) diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index 53f464d7cddcd6ce18925c521b99bfaefac66f2f..f4e127af4e179dd022007ed4a416ab32c9146533 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -120,9 +120,9 @@ static inline void inetpeer_transfer_peer(unsigned long *to, unsigned long *from } } -extern void inet_peer_base_init(struct inet_peer_base *); +void inet_peer_base_init(struct inet_peer_base *); -void inet_initpeers(void) __init; +void inet_initpeers(void) __init; #define INETPEER_METRICS_NEW (~(u32) 0) @@ -159,11 +159,11 @@ static inline struct inet_peer *inet_getpeer_v6(struct inet_peer_base *base, } /* can be called from BH context or outside */ -extern void inet_putpeer(struct inet_peer *p); -extern bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); +void inet_putpeer(struct inet_peer *p); +bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout); -extern void inetpeer_invalidate_tree(struct inet_peer_base *); -extern void inetpeer_invalidate_family(int family); +void inetpeer_invalidate_tree(struct inet_peer_base *); +void inetpeer_invalidate_family(int family); /* * temporary check to make sure we dont access rid, ip_id_count, tcp_ts, diff --git a/include/net/ip.h b/include/net/ip.h index 5e5268807a1ceb521f673ca05ec3233389a97f61..217bc5bfc6c6e99cf52117312ceda95c98f005a6 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -56,6 +57,9 @@ struct ipcm_cookie { int oif; struct ip_options_rcu *opt; __u8 tx_flags; + __u8 ttl; + __s16 tos; + char priority; }; #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb)) @@ -86,64 +90,71 @@ struct packet_type; struct rtable; struct sockaddr; -extern int igmp_mc_proc_init(void); +int igmp_mc_proc_init(void); /* * Functions provided by ip.c */ -extern int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, - __be32 saddr, __be32 daddr, - struct ip_options_rcu *opt); -extern int ip_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev); -extern int ip_local_deliver(struct sk_buff *skb); -extern int ip_mr_input(struct sk_buff *skb); -extern int ip_output(struct sk_buff *skb); -extern int ip_mc_output(struct sk_buff *skb); -extern int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)); -extern int ip_do_nat(struct sk_buff *skb); -extern void ip_send_check(struct iphdr *ip); -extern int __ip_local_out(struct sk_buff *skb); -extern int ip_local_out(struct sk_buff *skb); -extern int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl); -extern void ip_init(void); -extern int ip_append_data(struct sock *sk, struct flowi4 *fl4, - int getfrag(void *from, char *to, int offset, int len, - int odd, struct sk_buff *skb), - void *from, int len, int protolen, - struct ipcm_cookie *ipc, - struct rtable **rt, - unsigned int flags); -extern int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb); -extern ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, - int offset, size_t size, int flags); -extern struct sk_buff *__ip_make_skb(struct sock *sk, - struct flowi4 *fl4, - struct sk_buff_head *queue, - struct inet_cork *cork); -extern int ip_send_skb(struct net *net, struct sk_buff *skb); -extern int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4); -extern void ip_flush_pending_frames(struct sock *sk); -extern struct sk_buff *ip_make_skb(struct sock *sk, - struct flowi4 *fl4, - int getfrag(void *from, char *to, int offset, int len, - int odd, struct sk_buff *skb), - void *from, int length, int transhdrlen, - struct ipcm_cookie *ipc, - struct rtable **rtp, - unsigned int flags); +int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk, + __be32 saddr, __be32 daddr, + struct ip_options_rcu *opt); +int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, + struct net_device *orig_dev); +int ip_local_deliver(struct sk_buff *skb); +int ip_mr_input(struct sk_buff *skb); +int ip_output(struct sk_buff *skb); +int ip_mc_output(struct sk_buff *skb); +int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)); +int ip_do_nat(struct sk_buff *skb); +void ip_send_check(struct iphdr *ip); +int __ip_local_out(struct sk_buff *skb); +int ip_local_out(struct sk_buff *skb); +int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl); +void ip_init(void); +int ip_append_data(struct sock *sk, struct flowi4 *fl4, + int getfrag(void *from, char *to, int offset, int len, + int odd, struct sk_buff *skb), + void *from, int len, int protolen, + struct ipcm_cookie *ipc, + struct rtable **rt, + unsigned int flags); +int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, + struct sk_buff *skb); +ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, + int offset, size_t size, int flags); +struct sk_buff *__ip_make_skb(struct sock *sk, struct flowi4 *fl4, + struct sk_buff_head *queue, + struct inet_cork *cork); +int ip_send_skb(struct net *net, struct sk_buff *skb); +int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4); +void ip_flush_pending_frames(struct sock *sk); +struct sk_buff *ip_make_skb(struct sock *sk, struct flowi4 *fl4, + int getfrag(void *from, char *to, int offset, + int len, int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, + struct ipcm_cookie *ipc, struct rtable **rtp, + unsigned int flags); static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4) { return __ip_make_skb(sk, fl4, &sk->sk_write_queue, &inet_sk(sk)->cork.base); } +static inline __u8 get_rttos(struct ipcm_cookie* ipc, struct inet_sock *inet) +{ + return (ipc->tos != -1) ? RT_TOS(ipc->tos) : RT_TOS(inet->tos); +} + +static inline __u8 get_rtconn_flags(struct ipcm_cookie* ipc, struct sock* sk) +{ + return (ipc->tos != -1) ? RT_CONN_FLAGS_TOS(sk, ipc->tos) : RT_CONN_FLAGS(sk); +} + /* datagram.c */ -extern int ip4_datagram_connect(struct sock *sk, - struct sockaddr *uaddr, int addr_len); +int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); -extern void ip4_datagram_release_cb(struct sock *sk); +void ip4_datagram_release_cb(struct sock *sk); struct ip_reply_arg { struct kvec iov[1]; @@ -184,16 +195,16 @@ extern struct ipv4_config ipv4_config; #define NET_ADD_STATS_BH(net, field, adnd) SNMP_ADD_STATS_BH((net)->mib.net_statistics, field, adnd) #define NET_ADD_STATS_USER(net, field, adnd) SNMP_ADD_STATS_USER((net)->mib.net_statistics, field, adnd) -extern unsigned long snmp_fold_field(void __percpu *mib[], int offt); +unsigned long snmp_fold_field(void __percpu *mib[], int offt); #if BITS_PER_LONG==32 -extern u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t sync_off); +u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t sync_off); #else static inline u64 snmp_fold_field64(void __percpu *mib[], int offt, size_t syncp_off) { return snmp_fold_field(mib, offt); } #endif -extern int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align); +int snmp_mib_init(void __percpu *ptr[2], size_t mibsize, size_t align); static inline void snmp_mib_free(void __percpu *ptr[SNMP_ARRAY_SZ]) { @@ -206,11 +217,7 @@ static inline void snmp_mib_free(void __percpu *ptr[SNMP_ARRAY_SZ]) } } -extern struct local_ports { - seqlock_t lock; - int range[2]; -} sysctl_local_ports; -extern void inet_get_local_port_range(int *low, int *high); +void inet_get_local_port_range(struct net *net, int *low, int *high); extern unsigned long *sysctl_local_reserved_ports; static inline int inet_is_reserved_local_port(int port) @@ -231,9 +238,9 @@ extern int sysctl_ip_early_demux; /* From ip_output.c */ extern int sysctl_ip_dynaddr; -extern void ipfrag_init(void); +void ipfrag_init(void); -extern void ip_static_sysctl_init(void); +void ip_static_sysctl_init(void); static inline bool ip_is_fragment(const struct iphdr *iph) { @@ -262,7 +269,7 @@ int ip_dont_fragment(struct sock *sk, struct dst_entry *dst) !(dst_metric_locked(dst, RTAX_MTU))); } -extern void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more); +void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more); static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk) { @@ -367,7 +374,7 @@ static __inline__ void inet_reset_saddr(struct sock *sk) struct ipv6_pinfo *np = inet6_sk(sk); memset(&np->saddr, 0, sizeof(np->saddr)); - memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr)); + memset(&sk->sk_v6_rcv_saddr, 0, sizeof(sk->sk_v6_rcv_saddr)); } #endif } @@ -390,7 +397,7 @@ static inline int sk_mc_loop(struct sock *sk) return 1; } -extern bool ip_call_ra_chain(struct sk_buff *skb); +bool ip_call_ra_chain(struct sk_buff *skb); /* * Functions provided by ip_fragment.c @@ -428,50 +435,52 @@ int ip_frag_nqueues(struct net *net); * Functions provided by ip_forward.c */ -extern int ip_forward(struct sk_buff *skb); +int ip_forward(struct sk_buff *skb); /* * Functions provided by ip_options.c */ -extern void ip_options_build(struct sk_buff *skb, struct ip_options *opt, - __be32 daddr, struct rtable *rt, int is_frag); -extern int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb); -extern void ip_options_fragment(struct sk_buff *skb); -extern int ip_options_compile(struct net *net, - struct ip_options *opt, struct sk_buff *skb); -extern int ip_options_get(struct net *net, struct ip_options_rcu **optp, - unsigned char *data, int optlen); -extern int ip_options_get_from_user(struct net *net, struct ip_options_rcu **optp, - unsigned char __user *data, int optlen); -extern void ip_options_undo(struct ip_options * opt); -extern void ip_forward_options(struct sk_buff *skb); -extern int ip_options_rcv_srr(struct sk_buff *skb); +void ip_options_build(struct sk_buff *skb, struct ip_options *opt, + __be32 daddr, struct rtable *rt, int is_frag); +int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb); +void ip_options_fragment(struct sk_buff *skb); +int ip_options_compile(struct net *net, struct ip_options *opt, + struct sk_buff *skb); +int ip_options_get(struct net *net, struct ip_options_rcu **optp, + unsigned char *data, int optlen); +int ip_options_get_from_user(struct net *net, struct ip_options_rcu **optp, + unsigned char __user *data, int optlen); +void ip_options_undo(struct ip_options *opt); +void ip_forward_options(struct sk_buff *skb); +int ip_options_rcv_srr(struct sk_buff *skb); /* * Functions provided by ip_sockglue.c */ -extern void ipv4_pktinfo_prepare(struct sk_buff *skb); -extern void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb); -extern int ip_cmsg_send(struct net *net, - struct msghdr *msg, struct ipcm_cookie *ipc); -extern int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen); -extern int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); -extern int compat_ip_setsockopt(struct sock *sk, int level, - int optname, char __user *optval, unsigned int optlen); -extern int compat_ip_getsockopt(struct sock *sk, int level, - int optname, char __user *optval, int __user *optlen); -extern int ip_ra_control(struct sock *sk, unsigned char on, void (*destructor)(struct sock *)); - -extern int ip_recv_error(struct sock *sk, struct msghdr *msg, int len); -extern void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, - __be16 port, u32 info, u8 *payload); -extern void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport, - u32 info); +void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb); +void ip_cmsg_recv(struct msghdr *msg, struct sk_buff *skb); +int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc); +int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, + unsigned int optlen); +int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval, + int __user *optlen); +int compat_ip_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); +int compat_ip_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); +int ip_ra_control(struct sock *sk, unsigned char on, + void (*destructor)(struct sock *)); + +int ip_recv_error(struct sock *sk, struct msghdr *msg, int len); +void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, + u32 info, u8 *payload); +void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport, + u32 info); #ifdef CONFIG_PROC_FS -extern int ip_misc_proc_init(void); +int ip_misc_proc_init(void); #endif #endif /* _IP_H */ diff --git a/include/net/ip6_checksum.h b/include/net/ip6_checksum.h index 7686e3f5033d8364da7aad69be58b86018af7589..9e3c540c1b110c71b65003a6aac22cc6c333be5a 100644 --- a/include/net/ip6_checksum.h +++ b/include/net/ip6_checksum.h @@ -66,12 +66,14 @@ static inline void __tcp_v6_send_check(struct sk_buff *skb, } } +#if IS_ENABLED(CONFIG_IPV6) static inline void tcp_v6_send_check(struct sock *sk, struct sk_buff *skb) { struct ipv6_pinfo *np = inet6_sk(sk); - __tcp_v6_send_check(skb, &np->saddr, &np->daddr); + __tcp_v6_send_check(skb, &np->saddr, &sk->sk_v6_daddr); } +#endif int udp6_csum_init(struct sk_buff *skb, struct udphdr *uh, int proto); #endif diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 5e661a979694e87d2679e395aa0dce0da033942f..2182525e4d742ccf225ec5832e1958560f2f2238 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -268,48 +268,39 @@ typedef struct rt6_info *(*pol_lookup_t)(struct net *, * exported functions */ -extern struct fib6_table *fib6_get_table(struct net *net, u32 id); -extern struct fib6_table *fib6_new_table(struct net *net, u32 id); -extern struct dst_entry *fib6_rule_lookup(struct net *net, - struct flowi6 *fl6, int flags, - pol_lookup_t lookup); +struct fib6_table *fib6_get_table(struct net *net, u32 id); +struct fib6_table *fib6_new_table(struct net *net, u32 id); +struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, + int flags, pol_lookup_t lookup); -extern struct fib6_node *fib6_lookup(struct fib6_node *root, - const struct in6_addr *daddr, - const struct in6_addr *saddr); +struct fib6_node *fib6_lookup(struct fib6_node *root, + const struct in6_addr *daddr, + const struct in6_addr *saddr); -struct fib6_node *fib6_locate(struct fib6_node *root, - const struct in6_addr *daddr, int dst_len, - const struct in6_addr *saddr, int src_len); +struct fib6_node *fib6_locate(struct fib6_node *root, + const struct in6_addr *daddr, int dst_len, + const struct in6_addr *saddr, int src_len); -extern void fib6_clean_all_ro(struct net *net, - int (*func)(struct rt6_info *, void *arg), - int prune, void *arg); +void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), + int prune, void *arg); -extern void fib6_clean_all(struct net *net, - int (*func)(struct rt6_info *, void *arg), - int prune, void *arg); +int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info); -extern int fib6_add(struct fib6_node *root, - struct rt6_info *rt, - struct nl_info *info); +int fib6_del(struct rt6_info *rt, struct nl_info *info); -extern int fib6_del(struct rt6_info *rt, - struct nl_info *info); +void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info); -extern void inet6_rt_notify(int event, struct rt6_info *rt, - struct nl_info *info); +void fib6_run_gc(unsigned long expires, struct net *net, bool force); -extern void fib6_run_gc(unsigned long expires, - struct net *net, bool force); +void fib6_gc_cleanup(void); -extern void fib6_gc_cleanup(void); +int fib6_init(void); -extern int fib6_init(void); +int ipv6_route_open(struct inode *inode, struct file *file); #ifdef CONFIG_IPV6_MULTIPLE_TABLES -extern int fib6_rules_init(void); -extern void fib6_rules_cleanup(void); +int fib6_rules_init(void); +void fib6_rules_cleanup(void); #else static inline int fib6_rules_init(void) { diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 2b786b7e35850fe4b4b147a8a0cdcf0b010d2cb9..733747ce163c1a08a91a24eb5277832f5c04d6ee 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -51,7 +51,7 @@ static inline unsigned int rt6_flags2srcprefs(int flags) return (flags >> 3) & 7; } -extern void rt6_bind_peer(struct rt6_info *rt, int create); +void rt6_bind_peer(struct rt6_info *rt, int create); static inline struct inet_peer *__rt6_get_peer(struct rt6_info *rt, int create) { @@ -72,70 +72,58 @@ static inline struct inet_peer *rt6_get_peer_create(struct rt6_info *rt) return __rt6_get_peer(rt, 1); } -extern void ip6_route_input(struct sk_buff *skb); +void ip6_route_input(struct sk_buff *skb); -extern struct dst_entry * ip6_route_output(struct net *net, - const struct sock *sk, - struct flowi6 *fl6); -extern struct dst_entry * ip6_route_lookup(struct net *net, - struct flowi6 *fl6, int flags); +struct dst_entry *ip6_route_output(struct net *net, const struct sock *sk, + struct flowi6 *fl6); +struct dst_entry *ip6_route_lookup(struct net *net, struct flowi6 *fl6, + int flags); -extern int ip6_route_init(void); -extern void ip6_route_cleanup(void); +int ip6_route_init(void); +void ip6_route_cleanup(void); -extern int ipv6_route_ioctl(struct net *net, - unsigned int cmd, - void __user *arg); +int ipv6_route_ioctl(struct net *net, unsigned int cmd, void __user *arg); -extern int ip6_route_add(struct fib6_config *cfg); -extern int ip6_ins_rt(struct rt6_info *); -extern int ip6_del_rt(struct rt6_info *); +int ip6_route_add(struct fib6_config *cfg); +int ip6_ins_rt(struct rt6_info *); +int ip6_del_rt(struct rt6_info *); -extern int ip6_route_get_saddr(struct net *net, - struct rt6_info *rt, - const struct in6_addr *daddr, - unsigned int prefs, - struct in6_addr *saddr); +int ip6_route_get_saddr(struct net *net, struct rt6_info *rt, + const struct in6_addr *daddr, unsigned int prefs, + struct in6_addr *saddr); -extern struct rt6_info *rt6_lookup(struct net *net, - const struct in6_addr *daddr, - const struct in6_addr *saddr, - int oif, int flags); +struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, + const struct in6_addr *saddr, int oif, int flags); -extern struct dst_entry *icmp6_dst_alloc(struct net_device *dev, - struct flowi6 *fl6); -extern int icmp6_dst_gc(void); +struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6); +int icmp6_dst_gc(void); -extern void fib6_force_start_gc(struct net *net); +void fib6_force_start_gc(struct net *net); -extern struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, - const struct in6_addr *addr, - bool anycast); +struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, + const struct in6_addr *addr, bool anycast); /* * support functions for ND * */ -extern struct rt6_info * rt6_get_dflt_router(const struct in6_addr *addr, - struct net_device *dev); -extern struct rt6_info * rt6_add_dflt_router(const struct in6_addr *gwaddr, - struct net_device *dev, - unsigned int pref); - -extern void rt6_purge_dflt_routers(struct net *net); - -extern int rt6_route_rcv(struct net_device *dev, - u8 *opt, int len, - const struct in6_addr *gwaddr); - -extern void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, - int oif, u32 mark); -extern void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, - __be32 mtu); -extern void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark); -extern void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, - u32 mark); -extern void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk); +struct rt6_info *rt6_get_dflt_router(const struct in6_addr *addr, + struct net_device *dev); +struct rt6_info *rt6_add_dflt_router(const struct in6_addr *gwaddr, + struct net_device *dev, unsigned int pref); + +void rt6_purge_dflt_routers(struct net *net); + +int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, + const struct in6_addr *gwaddr); + +void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, int oif, + u32 mark); +void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, __be32 mtu); +void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark); +void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, + u32 mark); +void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk); struct netlink_callback; @@ -145,10 +133,10 @@ struct rt6_rtnl_dump_arg { struct net *net; }; -extern int rt6_dump_route(struct rt6_info *rt, void *p_arg); -extern void rt6_ifdown(struct net *net, struct net_device *dev); -extern void rt6_mtu_change(struct net_device *dev, unsigned int mtu); -extern void rt6_remove_prefsrc(struct inet6_ifaddr *ifp); +int rt6_dump_route(struct rt6_info *rt, void *p_arg); +void rt6_ifdown(struct net *net, struct net_device *dev); +void rt6_mtu_change(struct net_device *dev, unsigned int mtu); +void rt6_remove_prefsrc(struct inet6_ifaddr *ifp); /* diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index cbf2be37c91ac603dc883fa3890e62ec1b5c7a80..9922093f575e20fa2e53a903a68da7773469c600 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -165,7 +165,7 @@ struct fib_result_nl { #define FIB_TABLE_HASHSZ 2 #endif -extern __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh); +__be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh); #define FIB_RES_SADDR(net, res) \ ((FIB_RES_NH(res).nh_saddr_genid == \ @@ -187,14 +187,14 @@ struct fib_table { unsigned long tb_data[0]; }; -extern int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, - struct fib_result *res, int fib_flags); -extern int fib_table_insert(struct fib_table *, struct fib_config *); -extern int fib_table_delete(struct fib_table *, struct fib_config *); -extern int fib_table_dump(struct fib_table *table, struct sk_buff *skb, - struct netlink_callback *cb); -extern int fib_table_flush(struct fib_table *table); -extern void fib_free_table(struct fib_table *tb); +int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp, + struct fib_result *res, int fib_flags); +int fib_table_insert(struct fib_table *, struct fib_config *); +int fib_table_delete(struct fib_table *, struct fib_config *); +int fib_table_dump(struct fib_table *table, struct sk_buff *skb, + struct netlink_callback *cb); +int fib_table_flush(struct fib_table *table); +void fib_free_table(struct fib_table *tb); @@ -234,14 +234,13 @@ static inline int fib_lookup(struct net *net, const struct flowi4 *flp, } #else /* CONFIG_IP_MULTIPLE_TABLES */ -extern int __net_init fib4_rules_init(struct net *net); -extern void __net_exit fib4_rules_exit(struct net *net); +int __net_init fib4_rules_init(struct net *net); +void __net_exit fib4_rules_exit(struct net *net); -extern struct fib_table *fib_new_table(struct net *net, u32 id); -extern struct fib_table *fib_get_table(struct net *net, u32 id); +struct fib_table *fib_new_table(struct net *net, u32 id); +struct fib_table *fib_get_table(struct net *net, u32 id); -extern int __fib_lookup(struct net *net, struct flowi4 *flp, - struct fib_result *res); +int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res); static inline int fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res) @@ -269,12 +268,12 @@ static inline int fib_lookup(struct net *net, struct flowi4 *flp, /* Exported by fib_frontend.c */ extern const struct nla_policy rtm_ipv4_policy[]; -extern void ip_fib_init(void); -extern __be32 fib_compute_spec_dst(struct sk_buff *skb); -extern int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, - u8 tos, int oif, struct net_device *dev, - struct in_device *idev, u32 *itag); -extern void fib_select_default(struct fib_result *res); +void ip_fib_init(void); +__be32 fib_compute_spec_dst(struct sk_buff *skb); +int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst, + u8 tos, int oif, struct net_device *dev, + struct in_device *idev, u32 *itag); +void fib_select_default(struct fib_result *res); #ifdef CONFIG_IP_ROUTE_CLASSID static inline int fib_num_tclassid_users(struct net *net) { @@ -288,15 +287,15 @@ static inline int fib_num_tclassid_users(struct net *net) #endif /* Exported by fib_semantics.c */ -extern int ip_fib_check_default(__be32 gw, struct net_device *dev); -extern int fib_sync_down_dev(struct net_device *dev, int force); -extern int fib_sync_down_addr(struct net *net, __be32 local); -extern int fib_sync_up(struct net_device *dev); -extern void fib_select_multipath(struct fib_result *res); +int ip_fib_check_default(__be32 gw, struct net_device *dev); +int fib_sync_down_dev(struct net_device *dev, int force); +int fib_sync_down_addr(struct net *net, __be32 local); +int fib_sync_up(struct net_device *dev); +void fib_select_multipath(struct fib_result *res); /* Exported by fib_trie.c */ -extern void fib_trie_init(void); -extern struct fib_table *fib_trie_table(u32 id); +void fib_trie_init(void); +struct fib_table *fib_trie_table(u32 id); static inline void fib_combine_itag(u32 *itag, const struct fib_result *res) { @@ -314,7 +313,7 @@ static inline void fib_combine_itag(u32 *itag, const struct fib_result *res) #endif } -extern void free_fib_info(struct fib_info *fi); +void free_fib_info(struct fib_info *fi); static inline void fib_info_put(struct fib_info *fi) { @@ -323,8 +322,8 @@ static inline void fib_info_put(struct fib_info *fi) } #ifdef CONFIG_PROC_FS -extern int __net_init fib_proc_init(struct net *net); -extern void __net_exit fib_proc_exit(struct net *net); +int __net_init fib_proc_init(struct net *net); +void __net_exit fib_proc_exit(struct net *net); #else static inline int fib_proc_init(struct net *net) { diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index a0a4a100f5c9a74ce544097dee5f3d25e68e8b9b..732f8c6ae975877ccd9c5873369f9be6490f06e9 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -150,6 +150,9 @@ int iptunnel_xmit(struct rtable *rt, struct sk_buff *skb, __be32 src, __be32 dst, __u8 proto, __u8 tos, __u8 ttl, __be16 df, bool xnet); +struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, bool gre_csum, + int gso_type_mask); + static inline void iptunnel_xmit_stats(int err, struct net_device_stats *err_stats, struct pcpu_tstats __percpu *stats) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 9c4d37ec45a1e5dd0536dcbef7fdfa8eac684401..5679d927562be7b0225c68468a498ded903bd36a 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -109,7 +109,6 @@ extern int ip_vs_conn_tab_size; struct ip_vs_iphdr { __u32 len; /* IPv4 simply where L4 starts IPv6 where L4 Transport Header starts */ - __u32 thoff_reasm; /* Transport Header Offset in nfct_reasm skb */ __u16 fragoffs; /* IPv6 fragment offset, 0 if first frag (or not frag)*/ __s16 protocol; __s32 flags; @@ -117,34 +116,12 @@ struct ip_vs_iphdr { union nf_inet_addr daddr; }; -/* Dependency to module: nf_defrag_ipv6 */ -#if defined(CONFIG_NF_DEFRAG_IPV6) || defined(CONFIG_NF_DEFRAG_IPV6_MODULE) -static inline struct sk_buff *skb_nfct_reasm(const struct sk_buff *skb) -{ - return skb->nfct_reasm; -} static inline void *frag_safe_skb_hp(const struct sk_buff *skb, int offset, int len, void *buffer, const struct ip_vs_iphdr *ipvsh) { - if (unlikely(ipvsh->fragoffs && skb_nfct_reasm(skb))) - return skb_header_pointer(skb_nfct_reasm(skb), - ipvsh->thoff_reasm, len, buffer); - return skb_header_pointer(skb, offset, len, buffer); } -#else -static inline struct sk_buff *skb_nfct_reasm(const struct sk_buff *skb) -{ - return NULL; -} -static inline void *frag_safe_skb_hp(const struct sk_buff *skb, int offset, - int len, void *buffer, - const struct ip_vs_iphdr *ipvsh) -{ - return skb_header_pointer(skb, offset, len, buffer); -} -#endif static inline void ip_vs_fill_ip4hdr(const void *nh, struct ip_vs_iphdr *iphdr) @@ -171,19 +148,12 @@ ip_vs_fill_iph_skb(int af, const struct sk_buff *skb, struct ip_vs_iphdr *iphdr) (struct ipv6hdr *)skb_network_header(skb); iphdr->saddr.in6 = iph->saddr; iphdr->daddr.in6 = iph->daddr; - /* ipv6_find_hdr() updates len, flags, thoff_reasm */ - iphdr->thoff_reasm = 0; + /* ipv6_find_hdr() updates len, flags */ iphdr->len = 0; iphdr->flags = 0; iphdr->protocol = ipv6_find_hdr(skb, &iphdr->len, -1, &iphdr->fragoffs, &iphdr->flags); - /* get proto from re-assembled packet and it's offset */ - if (skb_nfct_reasm(skb)) - iphdr->protocol = ipv6_find_hdr(skb_nfct_reasm(skb), - &iphdr->thoff_reasm, - -1, NULL, NULL); - } else #endif { @@ -236,7 +206,7 @@ static inline int ip_vs_addr_equal(int af, const union nf_inet_addr *a, #ifdef CONFIG_IP_VS_DEBUG #include -extern int ip_vs_get_debug_level(void); +int ip_vs_get_debug_level(void); static inline const char *ip_vs_dbg_addr(int af, char *buf, size_t buf_len, const union nf_inet_addr *addr, @@ -532,9 +502,9 @@ struct ip_vs_proto_data { struct tcp_states_t *tcp_state_table; }; -extern struct ip_vs_protocol *ip_vs_proto_get(unsigned short proto); -extern struct ip_vs_proto_data *ip_vs_proto_data_get(struct net *net, - unsigned short proto); +struct ip_vs_protocol *ip_vs_proto_get(unsigned short proto); +struct ip_vs_proto_data *ip_vs_proto_data_get(struct net *net, + unsigned short proto); struct ip_vs_conn_param { struct net *net; @@ -1173,8 +1143,8 @@ static inline int sysctl_backup_only(struct netns_ipvs *ipvs) * IPVS core functions * (from ip_vs_core.c) */ -extern const char *ip_vs_proto_name(unsigned int proto); -extern void ip_vs_init_hash_table(struct list_head *table, int rows); +const char *ip_vs_proto_name(unsigned int proto); +void ip_vs_init_hash_table(struct list_head *table, int rows); #define IP_VS_INIT_HASH_TABLE(t) ip_vs_init_hash_table((t), ARRAY_SIZE((t))) #define IP_VS_APP_TYPE_FTP 1 @@ -1237,22 +1207,22 @@ static inline void __ip_vs_conn_put(struct ip_vs_conn *cp) smp_mb__before_atomic_dec(); atomic_dec(&cp->refcnt); } -extern void ip_vs_conn_put(struct ip_vs_conn *cp); -extern void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport); +void ip_vs_conn_put(struct ip_vs_conn *cp); +void ip_vs_conn_fill_cport(struct ip_vs_conn *cp, __be16 cport); struct ip_vs_conn *ip_vs_conn_new(const struct ip_vs_conn_param *p, const union nf_inet_addr *daddr, __be16 dport, unsigned int flags, struct ip_vs_dest *dest, __u32 fwmark); -extern void ip_vs_conn_expire_now(struct ip_vs_conn *cp); +void ip_vs_conn_expire_now(struct ip_vs_conn *cp); -extern const char * ip_vs_state_name(__u16 proto, int state); +const char *ip_vs_state_name(__u16 proto, int state); -extern void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp); -extern int ip_vs_check_template(struct ip_vs_conn *ct); -extern void ip_vs_random_dropentry(struct net *net); -extern int ip_vs_conn_init(void); -extern void ip_vs_conn_cleanup(void); +void ip_vs_tcp_conn_listen(struct net *net, struct ip_vs_conn *cp); +int ip_vs_check_template(struct ip_vs_conn *ct); +void ip_vs_random_dropentry(struct net *net); +int ip_vs_conn_init(void); +void ip_vs_conn_cleanup(void); static inline void ip_vs_control_del(struct ip_vs_conn *cp) { @@ -1317,37 +1287,36 @@ ip_vs_control_add(struct ip_vs_conn *cp, struct ip_vs_conn *ctl_cp) /* * IPVS netns init & cleanup functions */ -extern int ip_vs_estimator_net_init(struct net *net); -extern int ip_vs_control_net_init(struct net *net); -extern int ip_vs_protocol_net_init(struct net *net); -extern int ip_vs_app_net_init(struct net *net); -extern int ip_vs_conn_net_init(struct net *net); -extern int ip_vs_sync_net_init(struct net *net); -extern void ip_vs_conn_net_cleanup(struct net *net); -extern void ip_vs_app_net_cleanup(struct net *net); -extern void ip_vs_protocol_net_cleanup(struct net *net); -extern void ip_vs_control_net_cleanup(struct net *net); -extern void ip_vs_estimator_net_cleanup(struct net *net); -extern void ip_vs_sync_net_cleanup(struct net *net); -extern void ip_vs_service_net_cleanup(struct net *net); +int ip_vs_estimator_net_init(struct net *net); +int ip_vs_control_net_init(struct net *net); +int ip_vs_protocol_net_init(struct net *net); +int ip_vs_app_net_init(struct net *net); +int ip_vs_conn_net_init(struct net *net); +int ip_vs_sync_net_init(struct net *net); +void ip_vs_conn_net_cleanup(struct net *net); +void ip_vs_app_net_cleanup(struct net *net); +void ip_vs_protocol_net_cleanup(struct net *net); +void ip_vs_control_net_cleanup(struct net *net); +void ip_vs_estimator_net_cleanup(struct net *net); +void ip_vs_sync_net_cleanup(struct net *net); +void ip_vs_service_net_cleanup(struct net *net); /* * IPVS application functions * (from ip_vs_app.c) */ #define IP_VS_APP_MAX_PORTS 8 -extern struct ip_vs_app *register_ip_vs_app(struct net *net, - struct ip_vs_app *app); -extern void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app); -extern int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp); -extern void ip_vs_unbind_app(struct ip_vs_conn *cp); -extern int register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app, - __u16 proto, __u16 port); -extern int ip_vs_app_inc_get(struct ip_vs_app *inc); -extern void ip_vs_app_inc_put(struct ip_vs_app *inc); - -extern int ip_vs_app_pkt_out(struct ip_vs_conn *, struct sk_buff *skb); -extern int ip_vs_app_pkt_in(struct ip_vs_conn *, struct sk_buff *skb); +struct ip_vs_app *register_ip_vs_app(struct net *net, struct ip_vs_app *app); +void unregister_ip_vs_app(struct net *net, struct ip_vs_app *app); +int ip_vs_bind_app(struct ip_vs_conn *cp, struct ip_vs_protocol *pp); +void ip_vs_unbind_app(struct ip_vs_conn *cp); +int register_ip_vs_app_inc(struct net *net, struct ip_vs_app *app, __u16 proto, + __u16 port); +int ip_vs_app_inc_get(struct ip_vs_app *inc); +void ip_vs_app_inc_put(struct ip_vs_app *inc); + +int ip_vs_app_pkt_out(struct ip_vs_conn *, struct sk_buff *skb); +int ip_vs_app_pkt_in(struct ip_vs_conn *, struct sk_buff *skb); int register_ip_vs_pe(struct ip_vs_pe *pe); int unregister_ip_vs_pe(struct ip_vs_pe *pe); @@ -1368,17 +1337,15 @@ struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name); /* * IPVS protocol functions (from ip_vs_proto.c) */ -extern int ip_vs_protocol_init(void); -extern void ip_vs_protocol_cleanup(void); -extern void ip_vs_protocol_timeout_change(struct netns_ipvs *ipvs, int flags); -extern int *ip_vs_create_timeout_table(int *table, int size); -extern int -ip_vs_set_state_timeout(int *table, int num, const char *const *names, - const char *name, int to); -extern void -ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, - const struct sk_buff *skb, - int offset, const char *msg); +int ip_vs_protocol_init(void); +void ip_vs_protocol_cleanup(void); +void ip_vs_protocol_timeout_change(struct netns_ipvs *ipvs, int flags); +int *ip_vs_create_timeout_table(int *table, int size); +int ip_vs_set_state_timeout(int *table, int num, const char *const *names, + const char *name, int to); +void ip_vs_tcpudp_debug_packet(int af, struct ip_vs_protocol *pp, + const struct sk_buff *skb, int offset, + const char *msg); extern struct ip_vs_protocol ip_vs_protocol_tcp; extern struct ip_vs_protocol ip_vs_protocol_udp; @@ -1391,22 +1358,22 @@ extern struct ip_vs_protocol ip_vs_protocol_sctp; * Registering/unregistering scheduler functions * (from ip_vs_sched.c) */ -extern int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler); -extern int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler); -extern int ip_vs_bind_scheduler(struct ip_vs_service *svc, - struct ip_vs_scheduler *scheduler); -extern void ip_vs_unbind_scheduler(struct ip_vs_service *svc, - struct ip_vs_scheduler *sched); -extern struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name); -extern void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler); -extern struct ip_vs_conn * +int register_ip_vs_scheduler(struct ip_vs_scheduler *scheduler); +int unregister_ip_vs_scheduler(struct ip_vs_scheduler *scheduler); +int ip_vs_bind_scheduler(struct ip_vs_service *svc, + struct ip_vs_scheduler *scheduler); +void ip_vs_unbind_scheduler(struct ip_vs_service *svc, + struct ip_vs_scheduler *sched); +struct ip_vs_scheduler *ip_vs_scheduler_get(const char *sched_name); +void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler); +struct ip_vs_conn * ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, struct ip_vs_proto_data *pd, int *ignored, struct ip_vs_iphdr *iph); -extern int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, - struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph); +int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, + struct ip_vs_proto_data *pd, struct ip_vs_iphdr *iph); -extern void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg); +void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg); /* @@ -1415,25 +1382,24 @@ extern void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg); extern struct ip_vs_stats ip_vs_stats; extern int sysctl_ip_vs_sync_ver; -extern struct ip_vs_service * +struct ip_vs_service * ip_vs_service_find(struct net *net, int af, __u32 fwmark, __u16 protocol, const union nf_inet_addr *vaddr, __be16 vport); -extern bool -ip_vs_has_real_service(struct net *net, int af, __u16 protocol, - const union nf_inet_addr *daddr, __be16 dport); - -extern int ip_vs_use_count_inc(void); -extern void ip_vs_use_count_dec(void); -extern int ip_vs_register_nl_ioctl(void); -extern void ip_vs_unregister_nl_ioctl(void); -extern int ip_vs_control_init(void); -extern void ip_vs_control_cleanup(void); -extern struct ip_vs_dest * +bool ip_vs_has_real_service(struct net *net, int af, __u16 protocol, + const union nf_inet_addr *daddr, __be16 dport); + +int ip_vs_use_count_inc(void); +void ip_vs_use_count_dec(void); +int ip_vs_register_nl_ioctl(void); +void ip_vs_unregister_nl_ioctl(void); +int ip_vs_control_init(void); +void ip_vs_control_cleanup(void); +struct ip_vs_dest * ip_vs_find_dest(struct net *net, int af, const union nf_inet_addr *daddr, __be16 dport, const union nf_inet_addr *vaddr, __be16 vport, __u16 protocol, __u32 fwmark, __u32 flags); -extern void ip_vs_try_bind_dest(struct ip_vs_conn *cp); +void ip_vs_try_bind_dest(struct ip_vs_conn *cp); static inline void ip_vs_dest_hold(struct ip_vs_dest *dest) { @@ -1446,60 +1412,59 @@ static inline void ip_vs_dest_put(struct ip_vs_dest *dest) atomic_dec(&dest->refcnt); } +static inline void ip_vs_dest_put_and_free(struct ip_vs_dest *dest) +{ + if (atomic_dec_return(&dest->refcnt) < 0) + kfree(dest); +} + /* * IPVS sync daemon data and function prototypes * (from ip_vs_sync.c) */ -extern int start_sync_thread(struct net *net, int state, char *mcast_ifn, - __u8 syncid); -extern int stop_sync_thread(struct net *net, int state); -extern void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts); - +int start_sync_thread(struct net *net, int state, char *mcast_ifn, __u8 syncid); +int stop_sync_thread(struct net *net, int state); +void ip_vs_sync_conn(struct net *net, struct ip_vs_conn *cp, int pkts); /* * IPVS rate estimator prototypes (from ip_vs_est.c) */ -extern void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats); -extern void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats); -extern void ip_vs_zero_estimator(struct ip_vs_stats *stats); -extern void ip_vs_read_estimator(struct ip_vs_stats_user *dst, - struct ip_vs_stats *stats); +void ip_vs_start_estimator(struct net *net, struct ip_vs_stats *stats); +void ip_vs_stop_estimator(struct net *net, struct ip_vs_stats *stats); +void ip_vs_zero_estimator(struct ip_vs_stats *stats); +void ip_vs_read_estimator(struct ip_vs_stats_user *dst, + struct ip_vs_stats *stats); /* * Various IPVS packet transmitters (from ip_vs_xmit.c) */ -extern int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); -extern int ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, - struct ip_vs_iphdr *iph); -extern int ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); -extern int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, - struct ip_vs_iphdr *iph); -extern int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); -extern int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, - unsigned int hooknum, struct ip_vs_iphdr *iph); -extern void ip_vs_dest_dst_rcu_free(struct rcu_head *head); +int ip_vs_null_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); +int ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); +int ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); +int ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); +int ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); +int ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, int offset, + unsigned int hooknum, struct ip_vs_iphdr *iph); +void ip_vs_dest_dst_rcu_free(struct rcu_head *head); #ifdef CONFIG_IP_VS_IPV6 -extern int ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, - struct ip_vs_iphdr *iph); -extern int ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, - struct ip_vs_iphdr *iph); -extern int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, - struct ip_vs_iphdr *iph); -extern int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); -extern int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, - struct ip_vs_protocol *pp, int offset, - unsigned int hooknum, struct ip_vs_iphdr *iph); +int ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); +int ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); +int ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); +int ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, struct ip_vs_iphdr *iph); +int ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, + struct ip_vs_protocol *pp, int offset, + unsigned int hooknum, struct ip_vs_iphdr *iph); #endif #ifdef CONFIG_SYSCTL @@ -1548,15 +1513,15 @@ static inline char ip_vs_fwd_tag(struct ip_vs_conn *cp) return fwd; } -extern void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int dir); +void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir); #ifdef CONFIG_IP_VS_IPV6 -extern void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, - struct ip_vs_conn *cp, int dir); +void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, + struct ip_vs_conn *cp, int dir); #endif -extern __sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); +__sum16 ip_vs_checksum_complete(struct sk_buff *skb, int offset); static inline __wsum ip_vs_check_diff4(__be32 old, __be32 new, __wsum oldsum) { @@ -1615,13 +1580,13 @@ static inline int ip_vs_conntrack_enabled(struct netns_ipvs *ipvs) #endif } -extern void ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp, - int outin); -extern int ip_vs_confirm_conntrack(struct sk_buff *skb); -extern void ip_vs_nfct_expect_related(struct sk_buff *skb, struct nf_conn *ct, - struct ip_vs_conn *cp, u_int8_t proto, - const __be16 port, int from_rs); -extern void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp); +void ip_vs_update_conntrack(struct sk_buff *skb, struct ip_vs_conn *cp, + int outin); +int ip_vs_confirm_conntrack(struct sk_buff *skb); +void ip_vs_nfct_expect_related(struct sk_buff *skb, struct nf_conn *ct, + struct ip_vs_conn *cp, u_int8_t proto, + const __be16 port, int from_rs); +void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp); #else diff --git a/include/net/ipv6.h b/include/net/ipv6.h index bbf1c8fb8511c70220c4a2b7c046f6007cf525b5..2a5f668cd6836fae6ff45e551f8dacfd93dacd8e 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -244,14 +244,15 @@ struct ipv6_fl_socklist { struct rcu_head rcu; }; -extern struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label); -extern struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions * opt_space, - struct ip6_flowlabel * fl, - struct ipv6_txoptions * fopt); -extern void fl6_free_socklist(struct sock *sk); -extern int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen); -extern int ip6_flowlabel_init(void); -extern void ip6_flowlabel_cleanup(void); +struct ip6_flowlabel *fl6_sock_lookup(struct sock *sk, __be32 label); +struct ipv6_txoptions *fl6_merge_options(struct ipv6_txoptions *opt_space, + struct ip6_flowlabel *fl, + struct ipv6_txoptions *fopt); +void fl6_free_socklist(struct sock *sk); +int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen); +int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq); +int ip6_flowlabel_init(void); +void ip6_flowlabel_cleanup(void); static inline void fl6_sock_release(struct ip6_flowlabel *fl) { @@ -259,7 +260,7 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl) atomic_dec(&fl->users); } -extern void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info); +void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info); int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct icmp6hdr *thdr, int len); @@ -267,19 +268,21 @@ int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, struct sock *sk, struct flowi6 *fl6); -extern int ip6_ra_control(struct sock *sk, int sel); +int ip6_ra_control(struct sock *sk, int sel); -extern int ipv6_parse_hopopts(struct sk_buff *skb); +int ipv6_parse_hopopts(struct sk_buff *skb); -extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt); -extern struct ipv6_txoptions * ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, - int newtype, - struct ipv6_opt_hdr __user *newopt, - int newoptlen); +struct ipv6_txoptions *ipv6_dup_options(struct sock *sk, + struct ipv6_txoptions *opt); +struct ipv6_txoptions *ipv6_renew_options(struct sock *sk, + struct ipv6_txoptions *opt, + int newtype, + struct ipv6_opt_hdr __user *newopt, + int newoptlen); struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, struct ipv6_txoptions *opt); -extern bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb); +bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb); static inline bool ipv6_accept_ra(struct inet6_dev *idev) { @@ -306,7 +309,7 @@ static inline int ip6_frag_mem(struct net *net) #define IPV6_FRAG_LOW_THRESH (3 * 1024*1024) /* 3145728 */ #define IPV6_FRAG_TIMEOUT (60 * HZ) /* 60 seconds */ -extern int __ipv6_addr_type(const struct in6_addr *addr); +int __ipv6_addr_type(const struct in6_addr *addr); static inline int ipv6_addr_type(const struct in6_addr *addr) { return __ipv6_addr_type(addr) & 0xffff; @@ -537,14 +540,14 @@ static inline u32 ipv6_addr_hash(const struct in6_addr *a) } /* more secured version of ipv6_addr_hash() */ -static inline u32 ipv6_addr_jhash(const struct in6_addr *a) +static inline u32 __ipv6_addr_jhash(const struct in6_addr *a, const u32 initval) { u32 v = (__force u32)a->s6_addr32[0] ^ (__force u32)a->s6_addr32[1]; return jhash_3words(v, (__force u32)a->s6_addr32[2], (__force u32)a->s6_addr32[3], - ipv6_hash_secret); + initval); } static inline bool ipv6_addr_loopback(const struct in6_addr *a) @@ -656,9 +659,9 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr)); } -extern void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt); +void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt); -extern int ip6_dst_hoplimit(struct dst_entry *dst); +int ip6_dst_hoplimit(struct dst_entry *dst); /* * Header manipulation @@ -682,83 +685,65 @@ static inline __be32 ip6_flowinfo(const struct ipv6hdr *hdr) * rcv function (called from netdevice level) */ -extern int ipv6_rcv(struct sk_buff *skb, - struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev); +int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev); -extern int ip6_rcv_finish(struct sk_buff *skb); +int ip6_rcv_finish(struct sk_buff *skb); /* * upper-layer output functions */ -extern int ip6_xmit(struct sock *sk, - struct sk_buff *skb, - struct flowi6 *fl6, - struct ipv6_txoptions *opt, - int tclass); - -extern int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr); - -extern int ip6_append_data(struct sock *sk, - int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), - void *from, - int length, - int transhdrlen, - int hlimit, - int tclass, - struct ipv6_txoptions *opt, - struct flowi6 *fl6, - struct rt6_info *rt, - unsigned int flags, - int dontfrag); - -extern int ip6_push_pending_frames(struct sock *sk); - -extern void ip6_flush_pending_frames(struct sock *sk); - -extern int ip6_dst_lookup(struct sock *sk, - struct dst_entry **dst, - struct flowi6 *fl6); -extern struct dst_entry * ip6_dst_lookup_flow(struct sock *sk, - struct flowi6 *fl6, - const struct in6_addr *final_dst, - bool can_sleep); -extern struct dst_entry * ip6_sk_dst_lookup_flow(struct sock *sk, - struct flowi6 *fl6, - const struct in6_addr *final_dst, - bool can_sleep); -extern struct dst_entry * ip6_blackhole_route(struct net *net, - struct dst_entry *orig_dst); +int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi6 *fl6, + struct ipv6_txoptions *opt, int tclass); + +int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr); + +int ip6_append_data(struct sock *sk, + int getfrag(void *from, char *to, int offset, int len, + int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, int hlimit, + int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6, + struct rt6_info *rt, unsigned int flags, int dontfrag); + +int ip6_push_pending_frames(struct sock *sk); + +void ip6_flush_pending_frames(struct sock *sk); + +int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi6 *fl6); +struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, + const struct in6_addr *final_dst, + bool can_sleep); +struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, + const struct in6_addr *final_dst, + bool can_sleep); +struct dst_entry *ip6_blackhole_route(struct net *net, + struct dst_entry *orig_dst); /* * skb processing functions */ -extern int ip6_output(struct sk_buff *skb); -extern int ip6_forward(struct sk_buff *skb); -extern int ip6_input(struct sk_buff *skb); -extern int ip6_mc_input(struct sk_buff *skb); +int ip6_output(struct sk_buff *skb); +int ip6_forward(struct sk_buff *skb); +int ip6_input(struct sk_buff *skb); +int ip6_mc_input(struct sk_buff *skb); -extern int __ip6_local_out(struct sk_buff *skb); -extern int ip6_local_out(struct sk_buff *skb); +int __ip6_local_out(struct sk_buff *skb); +int ip6_local_out(struct sk_buff *skb); /* * Extension header (options) processing */ -extern void ipv6_push_nfrag_opts(struct sk_buff *skb, - struct ipv6_txoptions *opt, - u8 *proto, - struct in6_addr **daddr_p); -extern void ipv6_push_frag_opts(struct sk_buff *skb, - struct ipv6_txoptions *opt, - u8 *proto); +void ipv6_push_nfrag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, + u8 *proto, struct in6_addr **daddr_p); +void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, + u8 *proto); -extern int ipv6_skip_exthdr(const struct sk_buff *, int start, - u8 *nexthdrp, __be16 *frag_offp); +int ipv6_skip_exthdr(const struct sk_buff *, int start, u8 *nexthdrp, + __be16 *frag_offp); -extern bool ipv6_ext_hdr(u8 nexthdr); +bool ipv6_ext_hdr(u8 nexthdr); enum { IP6_FH_F_FRAG = (1 << 0), @@ -767,57 +752,44 @@ enum { }; /* find specified header and get offset to it */ -extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, - int target, unsigned short *fragoff, int *fragflg); +int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, + unsigned short *fragoff, int *fragflg); -extern int ipv6_find_tlv(struct sk_buff *skb, int offset, int type); +int ipv6_find_tlv(struct sk_buff *skb, int offset, int type); -extern struct in6_addr *fl6_update_dst(struct flowi6 *fl6, - const struct ipv6_txoptions *opt, - struct in6_addr *orig); +struct in6_addr *fl6_update_dst(struct flowi6 *fl6, + const struct ipv6_txoptions *opt, + struct in6_addr *orig); /* * socket options (ipv6_sockglue.c) */ -extern int ipv6_setsockopt(struct sock *sk, int level, - int optname, - char __user *optval, - unsigned int optlen); -extern int ipv6_getsockopt(struct sock *sk, int level, - int optname, - char __user *optval, - int __user *optlen); -extern int compat_ipv6_setsockopt(struct sock *sk, - int level, - int optname, - char __user *optval, - unsigned int optlen); -extern int compat_ipv6_getsockopt(struct sock *sk, - int level, - int optname, - char __user *optval, - int __user *optlen); - -extern int ip6_datagram_connect(struct sock *sk, - struct sockaddr *addr, int addr_len); - -extern int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len); -extern int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len); -extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, - u32 info, u8 *payload); -extern void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info); -extern void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu); - -extern int inet6_release(struct socket *sock); -extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr, - int addr_len); -extern int inet6_getname(struct socket *sock, struct sockaddr *uaddr, - int *uaddr_len, int peer); -extern int inet6_ioctl(struct socket *sock, unsigned int cmd, - unsigned long arg); - -extern int inet6_hash_connect(struct inet_timewait_death_row *death_row, +int ipv6_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); +int ipv6_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); +int compat_ipv6_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); +int compat_ipv6_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); + +int ip6_datagram_connect(struct sock *sk, struct sockaddr *addr, int addr_len); + +int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len); +int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len); +void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port, + u32 info, u8 *payload); +void ipv6_local_error(struct sock *sk, int err, struct flowi6 *fl6, u32 info); +void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu); + +int inet6_release(struct socket *sock); +int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len); +int inet6_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, + int peer); +int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg); + +int inet6_hash_connect(struct inet_timewait_death_row *death_row, struct sock *sk); /* @@ -829,30 +801,27 @@ extern const struct proto_ops inet6_dgram_ops; struct group_source_req; struct group_filter; -extern int ip6_mc_source(int add, int omode, struct sock *sk, - struct group_source_req *pgsr); -extern int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf); -extern int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, - struct group_filter __user *optval, - int __user *optlen); -extern unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr, - const struct in6_addr *daddr, u32 rnd); +int ip6_mc_source(int add, int omode, struct sock *sk, + struct group_source_req *pgsr); +int ip6_mc_msfilter(struct sock *sk, struct group_filter *gsf); +int ip6_mc_msfget(struct sock *sk, struct group_filter *gsf, + struct group_filter __user *optval, int __user *optlen); #ifdef CONFIG_PROC_FS -extern int ac6_proc_init(struct net *net); -extern void ac6_proc_exit(struct net *net); -extern int raw6_proc_init(void); -extern void raw6_proc_exit(void); -extern int tcp6_proc_init(struct net *net); -extern void tcp6_proc_exit(struct net *net); -extern int udp6_proc_init(struct net *net); -extern void udp6_proc_exit(struct net *net); -extern int udplite6_proc_init(void); -extern void udplite6_proc_exit(void); -extern int ipv6_misc_proc_init(void); -extern void ipv6_misc_proc_exit(void); -extern int snmp6_register_dev(struct inet6_dev *idev); -extern int snmp6_unregister_dev(struct inet6_dev *idev); +int ac6_proc_init(struct net *net); +void ac6_proc_exit(struct net *net); +int raw6_proc_init(void); +void raw6_proc_exit(void); +int tcp6_proc_init(struct net *net); +void tcp6_proc_exit(struct net *net); +int udp6_proc_init(struct net *net); +void udp6_proc_exit(struct net *net); +int udplite6_proc_init(void); +void udplite6_proc_exit(void); +int ipv6_misc_proc_init(void); +void ipv6_misc_proc_exit(void); +int snmp6_register_dev(struct inet6_dev *idev); +int snmp6_unregister_dev(struct inet6_dev *idev); #else static inline int ac6_proc_init(struct net *net) { return 0; } @@ -865,10 +834,10 @@ static inline int snmp6_unregister_dev(struct inet6_dev *idev) { return 0; } extern struct ctl_table ipv6_route_table_template[]; extern struct ctl_table ipv6_icmp_table_template[]; -extern struct ctl_table *ipv6_icmp_sysctl_init(struct net *net); -extern struct ctl_table *ipv6_route_sysctl_init(struct net *net); -extern int ipv6_sysctl_register(void); -extern void ipv6_sysctl_unregister(void); +struct ctl_table *ipv6_icmp_sysctl_init(struct net *net); +struct ctl_table *ipv6_route_sysctl_init(struct net *net); +int ipv6_sysctl_register(void); +void ipv6_sysctl_unregister(void); #endif #endif /* _NET_IPV6_H */ diff --git a/include/net/ipx.h b/include/net/ipx.h index c1fec6b464cc7b3926bfa12e55893994d772cc32..9e9e35465bafbaff131c893814abd44d99be712f 100644 --- a/include/net/ipx.h +++ b/include/net/ipx.h @@ -123,23 +123,23 @@ extern struct list_head ipx_routes; extern rwlock_t ipx_routes_lock; extern struct list_head ipx_interfaces; -extern struct ipx_interface *ipx_interfaces_head(void); +struct ipx_interface *ipx_interfaces_head(void); extern spinlock_t ipx_interfaces_lock; extern struct ipx_interface *ipx_primary_net; -extern int ipx_proc_init(void); -extern void ipx_proc_exit(void); +int ipx_proc_init(void); +void ipx_proc_exit(void); -extern const char *ipx_frame_name(__be16); -extern const char *ipx_device_name(struct ipx_interface *intrfc); +const char *ipx_frame_name(__be16); +const char *ipx_device_name(struct ipx_interface *intrfc); static __inline__ void ipxitf_hold(struct ipx_interface *intrfc) { atomic_inc(&intrfc->refcnt); } -extern void ipxitf_down(struct ipx_interface *intrfc); +void ipxitf_down(struct ipx_interface *intrfc); static __inline__ void ipxitf_put(struct ipx_interface *intrfc) { diff --git a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h index 80ffde3bb164e2a3a856838f0c8c4c2906bfeaf6..0224402260a77a3653a276e0c33f2e9281601f80 100644 --- a/include/net/irda/ircomm_tty.h +++ b/include/net/irda/ircomm_tty.h @@ -105,13 +105,13 @@ struct ircomm_tty_cb { void ircomm_tty_start(struct tty_struct *tty); void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self); -extern int ircomm_tty_tiocmget(struct tty_struct *tty); -extern int ircomm_tty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear); -extern int ircomm_tty_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg); -extern void ircomm_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios); +int ircomm_tty_tiocmget(struct tty_struct *tty); +int ircomm_tty_tiocmset(struct tty_struct *tty, unsigned int set, + unsigned int clear); +int ircomm_tty_ioctl(struct tty_struct *tty, unsigned int cmd, + unsigned long arg); +void ircomm_tty_set_termios(struct tty_struct *tty, + struct ktermios *old_termios); #endif diff --git a/include/net/irda/irda.h b/include/net/irda/irda.h index 3bed61d379a8aa1de70fe25935810c830cd70ac2..a059465101ff3b5a35120d09782b6f10db9c98fa 100644 --- a/include/net/irda/irda.h +++ b/include/net/irda/irda.h @@ -112,20 +112,19 @@ do { if(!(expr)) { \ struct net_device; struct packet_type; -extern void irda_proc_register(void); -extern void irda_proc_unregister(void); +void irda_proc_register(void); +void irda_proc_unregister(void); -extern int irda_sysctl_register(void); -extern void irda_sysctl_unregister(void); +int irda_sysctl_register(void); +void irda_sysctl_unregister(void); -extern int irsock_init(void); -extern void irsock_cleanup(void); +int irsock_init(void); +void irsock_cleanup(void); -extern int irda_nl_register(void); -extern void irda_nl_unregister(void); +int irda_nl_register(void); +void irda_nl_unregister(void); -extern int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *ptype, - struct net_device *orig_dev); +int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *ptype, struct net_device *orig_dev); #endif /* NET_IRDA_H */ diff --git a/include/net/irda/irda_device.h b/include/net/irda/irda_device.h index 94c852d47d0f8d5c7d033279bf2f67ca3fd861dd..11417475a6c31059aa28d5db55ce90c2a536a703 100644 --- a/include/net/irda/irda_device.h +++ b/include/net/irda/irda_device.h @@ -162,7 +162,7 @@ typedef struct { int irq, irq2; /* Interrupts used */ int dma, dma2; /* DMA channel(s) used */ int fifo_size; /* FIFO size */ - int irqflags; /* interrupt flags (ie, IRQF_SHARED|IRQF_DISABLED) */ + int irqflags; /* interrupt flags (ie, IRQF_SHARED) */ int direction; /* Link direction, used by some FIR drivers */ int enabled; /* Powered on? */ int suspended; /* Suspended by APM */ diff --git a/include/net/irda/irlap_event.h b/include/net/irda/irlap_event.h index 4c90824c50fb1ee002aa6e82f50f32fe0c2b940a..f9d88da97af2c15844caf5b9ba875cffb8a5f8c3 100644 --- a/include/net/irda/irlap_event.h +++ b/include/net/irda/irlap_event.h @@ -126,6 +126,6 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, struct sk_buff *skb, struct irlap_info *info); void irlap_print_event(IRLAP_EVENT event); -extern int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb); +int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb); #endif diff --git a/include/net/irda/irlap_frame.h b/include/net/irda/irlap_frame.h index 6b1dc4f8eca58a231ff481ee6fd5be8ac1a8f15c..57173ae398aed4096dd8b2cc7374bd4645edc3e7 100644 --- a/include/net/irda/irlap_frame.h +++ b/include/net/irda/irlap_frame.h @@ -163,7 +163,7 @@ void irlap_resend_rejected_frame(struct irlap_cb *self, int command); void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb, __u8 caddr, int command); -extern int irlap_insert_qos_negotiation_params(struct irlap_cb *self, - struct sk_buff *skb); +int irlap_insert_qos_negotiation_params(struct irlap_cb *self, + struct sk_buff *skb); #endif diff --git a/include/net/iw_handler.h b/include/net/iw_handler.h index 5d5a6a4732effd8865918fe31c390c5067505534..a830b01baba4b08a1d5054557c540312084b8f50 100644 --- a/include/net/iw_handler.h +++ b/include/net/iw_handler.h @@ -432,44 +432,32 @@ struct iw_public_data { /* First : function strictly used inside the kernel */ /* Handle /proc/net/wireless, called in net/code/dev.c */ -extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, - int length); +int dev_get_wireless_info(char *buffer, char **start, off_t offset, int length); /* Second : functions that may be called by driver modules */ /* Send a single event to user space */ -extern void wireless_send_event(struct net_device * dev, - unsigned int cmd, - union iwreq_data * wrqu, - const char * extra); +void wireless_send_event(struct net_device *dev, unsigned int cmd, + union iwreq_data *wrqu, const char *extra); /* We may need a function to send a stream of events to user space. * More on that later... */ /* Standard handler for SIOCSIWSPY */ -extern int iw_handler_set_spy(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra); +int iw_handler_set_spy(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); /* Standard handler for SIOCGIWSPY */ -extern int iw_handler_get_spy(struct net_device * dev, - struct iw_request_info * info, - union iwreq_data * wrqu, - char * extra); +int iw_handler_get_spy(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); /* Standard handler for SIOCSIWTHRSPY */ -extern int iw_handler_set_thrspy(struct net_device * dev, - struct iw_request_info *info, - union iwreq_data * wrqu, - char * extra); +int iw_handler_set_thrspy(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); /* Standard handler for SIOCGIWTHRSPY */ -extern int iw_handler_get_thrspy(struct net_device * dev, - struct iw_request_info *info, - union iwreq_data * wrqu, - char * extra); +int iw_handler_get_thrspy(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra); /* Driver call to update spy records */ -extern void wireless_spy_update(struct net_device * dev, - unsigned char * address, - struct iw_quality * wstats); +void wireless_spy_update(struct net_device *dev, unsigned char *address, + struct iw_quality *wstats); /************************* INLINE FUNTIONS *************************/ /* diff --git a/include/net/lapb.h b/include/net/lapb.h index df892a94f2c6570b642a33391fb9301ee99bcedb..9510f8725f03c316ab4eb61e9ee503218d2056c8 100644 --- a/include/net/lapb.h +++ b/include/net/lapb.h @@ -105,40 +105,40 @@ struct lapb_cb { }; /* lapb_iface.c */ -extern void lapb_connect_confirmation(struct lapb_cb *lapb, int); -extern void lapb_connect_indication(struct lapb_cb *lapb, int); -extern void lapb_disconnect_confirmation(struct lapb_cb *lapb, int); -extern void lapb_disconnect_indication(struct lapb_cb *lapb, int); -extern int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *); -extern int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *); +void lapb_connect_confirmation(struct lapb_cb *lapb, int); +void lapb_connect_indication(struct lapb_cb *lapb, int); +void lapb_disconnect_confirmation(struct lapb_cb *lapb, int); +void lapb_disconnect_indication(struct lapb_cb *lapb, int); +int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *); +int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *); /* lapb_in.c */ -extern void lapb_data_input(struct lapb_cb *lapb, struct sk_buff *); +void lapb_data_input(struct lapb_cb *lapb, struct sk_buff *); /* lapb_out.c */ -extern void lapb_kick(struct lapb_cb *lapb); -extern void lapb_transmit_buffer(struct lapb_cb *lapb, struct sk_buff *, int); -extern void lapb_establish_data_link(struct lapb_cb *lapb); -extern void lapb_enquiry_response(struct lapb_cb *lapb); -extern void lapb_timeout_response(struct lapb_cb *lapb); -extern void lapb_check_iframes_acked(struct lapb_cb *lapb, unsigned short); -extern void lapb_check_need_response(struct lapb_cb *lapb, int, int); +void lapb_kick(struct lapb_cb *lapb); +void lapb_transmit_buffer(struct lapb_cb *lapb, struct sk_buff *, int); +void lapb_establish_data_link(struct lapb_cb *lapb); +void lapb_enquiry_response(struct lapb_cb *lapb); +void lapb_timeout_response(struct lapb_cb *lapb); +void lapb_check_iframes_acked(struct lapb_cb *lapb, unsigned short); +void lapb_check_need_response(struct lapb_cb *lapb, int, int); /* lapb_subr.c */ -extern void lapb_clear_queues(struct lapb_cb *lapb); -extern void lapb_frames_acked(struct lapb_cb *lapb, unsigned short); -extern void lapb_requeue_frames(struct lapb_cb *lapb); -extern int lapb_validate_nr(struct lapb_cb *lapb, unsigned short); -extern int lapb_decode(struct lapb_cb *lapb, struct sk_buff *, struct lapb_frame *); -extern void lapb_send_control(struct lapb_cb *lapb, int, int, int); -extern void lapb_transmit_frmr(struct lapb_cb *lapb); +void lapb_clear_queues(struct lapb_cb *lapb); +void lapb_frames_acked(struct lapb_cb *lapb, unsigned short); +void lapb_requeue_frames(struct lapb_cb *lapb); +int lapb_validate_nr(struct lapb_cb *lapb, unsigned short); +int lapb_decode(struct lapb_cb *lapb, struct sk_buff *, struct lapb_frame *); +void lapb_send_control(struct lapb_cb *lapb, int, int, int); +void lapb_transmit_frmr(struct lapb_cb *lapb); /* lapb_timer.c */ -extern void lapb_start_t1timer(struct lapb_cb *lapb); -extern void lapb_start_t2timer(struct lapb_cb *lapb); -extern void lapb_stop_t1timer(struct lapb_cb *lapb); -extern void lapb_stop_t2timer(struct lapb_cb *lapb); -extern int lapb_t1timer_running(struct lapb_cb *lapb); +void lapb_start_t1timer(struct lapb_cb *lapb); +void lapb_start_t2timer(struct lapb_cb *lapb); +void lapb_stop_t1timer(struct lapb_cb *lapb); +void lapb_stop_t2timer(struct lapb_cb *lapb); +int lapb_t1timer_running(struct lapb_cb *lapb); /* * Debug levels. diff --git a/include/net/llc.h b/include/net/llc.h index 9e7d7f08ef77c5539ea887b483b274fc0ddb91fd..68490cbc8a659de87fefbfe1d80f8b29bed85dbc 100644 --- a/include/net/llc.h +++ b/include/net/llc.h @@ -95,29 +95,29 @@ struct hlist_nulls_head *llc_sk_laddr_hash(struct llc_sap *sap, extern struct list_head llc_sap_list; extern spinlock_t llc_sap_list_lock; -extern int llc_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev); +int llc_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, + struct net_device *orig_dev); -extern int llc_mac_hdr_init(struct sk_buff *skb, - const unsigned char *sa, const unsigned char *da); +int llc_mac_hdr_init(struct sk_buff *skb, const unsigned char *sa, + const unsigned char *da); -extern void llc_add_pack(int type, void (*handler)(struct llc_sap *sap, - struct sk_buff *skb)); -extern void llc_remove_pack(int type); +void llc_add_pack(int type, + void (*handler)(struct llc_sap *sap, struct sk_buff *skb)); +void llc_remove_pack(int type); -extern void llc_set_station_handler(void (*handler)(struct sk_buff *skb)); +void llc_set_station_handler(void (*handler)(struct sk_buff *skb)); -extern struct llc_sap *llc_sap_open(unsigned char lsap, - int (*rcv)(struct sk_buff *skb, - struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev)); +struct llc_sap *llc_sap_open(unsigned char lsap, + int (*rcv)(struct sk_buff *skb, + struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev)); static inline void llc_sap_hold(struct llc_sap *sap) { atomic_inc(&sap->refcnt); } -extern void llc_sap_close(struct llc_sap *sap); +void llc_sap_close(struct llc_sap *sap); static inline void llc_sap_put(struct llc_sap *sap) { @@ -125,27 +125,27 @@ static inline void llc_sap_put(struct llc_sap *sap) llc_sap_close(sap); } -extern struct llc_sap *llc_sap_find(unsigned char sap_value); +struct llc_sap *llc_sap_find(unsigned char sap_value); -extern int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb, - unsigned char *dmac, unsigned char dsap); +int llc_build_and_send_ui_pkt(struct llc_sap *sap, struct sk_buff *skb, + unsigned char *dmac, unsigned char dsap); -extern void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb); -extern void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb); +void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb); +void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb); -extern void llc_station_init(void); -extern void llc_station_exit(void); +void llc_station_init(void); +void llc_station_exit(void); #ifdef CONFIG_PROC_FS -extern int llc_proc_init(void); -extern void llc_proc_exit(void); +int llc_proc_init(void); +void llc_proc_exit(void); #else #define llc_proc_init() (0) #define llc_proc_exit() do { } while(0) #endif /* CONFIG_PROC_FS */ #ifdef CONFIG_SYSCTL -extern int llc_sysctl_init(void); -extern void llc_sysctl_exit(void); +int llc_sysctl_init(void); +void llc_sysctl_exit(void); extern int sysctl_llc2_ack_timeout; extern int sysctl_llc2_busy_timeout; diff --git a/include/net/llc_c_ac.h b/include/net/llc_c_ac.h index df83f69d2de41903bc6b21467475853322828bdc..f3be818e73c1fbe1034d63bf6f2d132848060908 100644 --- a/include/net/llc_c_ac.h +++ b/include/net/llc_c_ac.h @@ -89,114 +89,92 @@ typedef int (*llc_conn_action_t)(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ac_clear_remote_busy(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ac_conn_ind(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ac_conn_confirm(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_data_ind(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_disc_ind(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_rst_ind(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_rst_confirm(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_clear_remote_busy_if_f_eq_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_disc_cmd_p_set_x(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_dm_rsp_f_set_p(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_dm_rsp_f_set_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_frmr_rsp_f_set_x(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_resend_frmr_rsp_f_set_0(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_resend_frmr_rsp_f_set_p(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_i_cmd_p_set_1(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_send_i_xxx_x_set_0(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_resend_i_xxx_x_set_0(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_resend_i_rsp_f_set_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_rej_cmd_p_set_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_rej_rsp_f_set_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_rej_xxx_x_set_0(struct sock* sk, +int llc_conn_ac_clear_remote_busy(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_conn_ind(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_conn_confirm(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_data_ind(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_disc_ind(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_rst_ind(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_rst_confirm(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_clear_remote_busy_if_f_eq_1(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ac_send_rnr_cmd_p_set_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_rnr_rsp_f_set_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_rnr_xxx_x_set_0(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_set_remote_busy(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_opt_send_rnr_xxx_x_set_0(struct sock* sk, +int llc_conn_ac_stop_rej_tmr_if_data_flag_eq_2(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ac_send_disc_cmd_p_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_dm_rsp_f_set_p(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_dm_rsp_f_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_frmr_rsp_f_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_resend_frmr_rsp_f_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_resend_frmr_rsp_f_set_p(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_i_cmd_p_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_i_xxx_x_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_resend_i_xxx_x_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_resend_i_xxx_x_set_0_or_send_rr(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ac_send_rr_cmd_p_set_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_rr_rsp_f_set_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_ack_rsp_f_set_1(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_rr_xxx_x_set_0(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_ack_xxx_x_set_0(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_sabme_cmd_p_set_x(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_send_ua_rsp_f_set_p(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_set_s_flag_0(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_s_flag_1(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_start_p_timer(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_start_ack_timer(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_start_rej_timer(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_start_ack_tmr_if_not_running(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_stop_ack_timer(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_stop_p_timer(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_stop_rej_timer(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_stop_all_timers(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_stop_other_timers(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_upd_nr_received(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_inc_tx_win_size(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_dec_tx_win_size(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_upd_p_flag(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_data_flag_2(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_data_flag_0(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_data_flag_1(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_data_flag_1_if_data_flag_eq_0(struct sock* sk, - struct sk_buff *skb); -extern int llc_conn_ac_set_p_flag_0(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_remote_busy_0(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_retry_cnt_0(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_cause_flag_0(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_cause_flag_1(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_inc_retry_cnt_by_1(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_vr_0(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_inc_vr_by_1(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_vs_0(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_set_vs_nr(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_rst_vs(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_upd_vs(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_disc(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_reset(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_disc_confirm(struct sock* sk, struct sk_buff *skb); -extern u8 llc_circular_between(u8 a, u8 b, u8 c); -extern int llc_conn_ac_send_ack_if_needed(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_adjust_npta_by_rr(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_adjust_npta_by_rnr(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_rst_sendack_flag(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_send_i_rsp_as_ack(struct sock* sk, struct sk_buff *skb); -extern int llc_conn_ac_send_i_as_ack(struct sock* sk, struct sk_buff *skb); +int llc_conn_ac_resend_i_rsp_f_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_rej_cmd_p_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_rej_rsp_f_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_rej_xxx_x_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_rnr_cmd_p_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_rnr_rsp_f_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_rnr_xxx_x_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_remote_busy(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_opt_send_rnr_xxx_x_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_rr_cmd_p_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_rr_rsp_f_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_ack_rsp_f_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_rr_xxx_x_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_ack_xxx_x_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_sabme_cmd_p_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_ua_rsp_f_set_p(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_s_flag_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_s_flag_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_start_p_timer(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_start_ack_timer(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_start_rej_timer(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_start_ack_tmr_if_not_running(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ac_stop_ack_timer(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_stop_p_timer(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_stop_rej_timer(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_stop_all_timers(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_stop_other_timers(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_upd_nr_received(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_inc_tx_win_size(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_dec_tx_win_size(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_upd_p_flag(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_data_flag_2(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_data_flag_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_data_flag_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_data_flag_1_if_data_flag_eq_0(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ac_set_p_flag_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_remote_busy_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_retry_cnt_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_cause_flag_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_cause_flag_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_inc_retry_cnt_by_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_vr_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_inc_vr_by_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_vs_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_set_vs_nr(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_rst_vs(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_upd_vs(struct sock *sk, struct sk_buff *skb); +int llc_conn_disc(struct sock *sk, struct sk_buff *skb); +int llc_conn_reset(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_disc_confirm(struct sock *sk, struct sk_buff *skb); +u8 llc_circular_between(u8 a, u8 b, u8 c); +int llc_conn_ac_send_ack_if_needed(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_adjust_npta_by_rr(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_adjust_npta_by_rnr(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_rst_sendack_flag(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_i_rsp_as_ack(struct sock *sk, struct sk_buff *skb); +int llc_conn_ac_send_i_as_ack(struct sock *sk, struct sk_buff *skb); -extern void llc_conn_busy_tmr_cb(unsigned long timeout_data); -extern void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data); -extern void llc_conn_ack_tmr_cb(unsigned long timeout_data); -extern void llc_conn_rej_tmr_cb(unsigned long timeout_data); +void llc_conn_busy_tmr_cb(unsigned long timeout_data); +void llc_conn_pf_cycle_tmr_cb(unsigned long timeout_data); +void llc_conn_ack_tmr_cb(unsigned long timeout_data); +void llc_conn_rej_tmr_cb(unsigned long timeout_data); -extern void llc_conn_set_p_flag(struct sock *sk, u8 value); +void llc_conn_set_p_flag(struct sock *sk, u8 value); #endif /* LLC_C_AC_H */ diff --git a/include/net/llc_c_ev.h b/include/net/llc_c_ev.h index 6ca3113df39eed10eb2032a11c6b4a7c43abe003..3948cf111dd0d6d4948ed3b7c40740b82bfe41bc 100644 --- a/include/net/llc_c_ev.h +++ b/include/net/llc_c_ev.h @@ -128,138 +128,93 @@ static __inline__ struct llc_conn_state_ev *llc_conn_ev(struct sk_buff *skb) typedef int (*llc_conn_ev_t)(struct sock *sk, struct sk_buff *skb); typedef int (*llc_conn_ev_qfyr_t)(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_conn_req(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_data_req(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_disc_req(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_rst_req(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_local_busy_detected(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_local_busy_cleared(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_rx_bad_pdu(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_rx_disc_cmd_pbit_set_x(struct sock *sk, +int llc_conn_ev_conn_req(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_data_req(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_disc_req(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rst_req(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_local_busy_detected(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_local_busy_cleared(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_bad_pdu(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_disc_cmd_pbit_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_dm_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_frmr_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ev_rx_i_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_rx_dm_rsp_fbit_set_x(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_frmr_rsp_fbit_set_x(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_cmd_pbit_set_x_inval_ns(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_rsp_fbit_set_x(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_rsp_fbit_set_x_unexpd_ns(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rej_rsp_fbit_set_x(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_sabme_cmd_pbit_set_x(struct sock *sk, +int llc_conn_ev_rx_i_rsp_fbit_set_x_inval_ns(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ev_rx_rej_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_sabme_cmd_pbit_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_ua_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_xxx_cmd_pbit_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_xxx_rsp_fbit_set_x(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_zzz_cmd_pbit_set_x_inval_nr(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_rx_ua_rsp_fbit_set_x(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_xxx_cmd_pbit_set_x(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_xxx_rsp_fbit_set_x(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_zzz_cmd_pbit_set_x_inval_nr(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_zzz_rsp_fbit_set_x_inval_nr(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_p_tmr_exp(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_ack_tmr_exp(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_rej_tmr_exp(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_busy_tmr_exp(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_sendack_tmr_exp(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_zzz_rsp_fbit_set_x_inval_nr(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ev_p_tmr_exp(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_ack_tmr_exp(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rej_tmr_exp(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_busy_tmr_exp(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_sendack_tmr_exp(struct sock *sk, struct sk_buff *skb); /* NOT_USED functions and their variations */ -extern int llc_conn_ev_rx_xxx_cmd_pbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_xxx_rsp_fbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_cmd_pbit_set_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_cmd_pbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_rsp_fbit_set_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_i_rsp_fbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rr_cmd_pbit_set_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rr_cmd_pbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rr_rsp_fbit_set_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rr_rsp_fbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rnr_cmd_pbit_set_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rnr_cmd_pbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rnr_rsp_fbit_set_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rnr_rsp_fbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rej_cmd_pbit_set_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rej_cmd_pbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rej_rsp_fbit_set_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_rej_rsp_fbit_set_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_rx_any_frame(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_tx_buffer_full(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_init_p_f_cycle(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_xxx_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_xxx_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_i_cmd_pbit_set_0_unexpd_ns(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ev_rx_i_cmd_pbit_set_1_unexpd_ns(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ev_rx_i_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_i_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_i_rsp_fbit_set_0_unexpd_ns(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ev_rx_i_rsp_fbit_set_1_unexpd_ns(struct sock *sk, + struct sk_buff *skb); +int llc_conn_ev_rx_i_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_i_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rr_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rr_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rr_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rr_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rnr_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rnr_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rnr_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rnr_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rej_cmd_pbit_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rej_cmd_pbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rej_rsp_fbit_set_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_rej_rsp_fbit_set_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_rx_any_frame(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_tx_buffer_full(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_init_p_f_cycle(struct sock *sk, struct sk_buff *skb); /* Available connection action qualifiers */ -extern int llc_conn_ev_qlfy_data_flag_eq_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_data_flag_eq_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_data_flag_eq_2(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_p_flag_eq_1(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_qlfy_last_frame_eq_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_last_frame_eq_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_p_flag_eq_0(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_qlfy_p_flag_eq_f(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_qlfy_remote_busy_eq_0(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_remote_busy_eq_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_retry_cnt_lt_n2(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_retry_cnt_gte_n2(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_s_flag_eq_1(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_qlfy_s_flag_eq_0(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_qlfy_cause_flag_eq_1(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_cause_flag_eq_0(struct sock *sk, +int llc_conn_ev_qlfy_data_flag_eq_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_data_flag_eq_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_data_flag_eq_2(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_p_flag_eq_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_last_frame_eq_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_last_frame_eq_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_p_flag_eq_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_p_flag_eq_f(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_remote_busy_eq_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_remote_busy_eq_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_retry_cnt_lt_n2(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_retry_cnt_gte_n2(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_s_flag_eq_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_s_flag_eq_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_cause_flag_eq_1(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_cause_flag_eq_0(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_set_status_conn(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_set_status_disc(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_set_status_failed(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_set_status_remote_busy(struct sock *sk, struct sk_buff *skb); -extern int llc_conn_ev_qlfy_set_status_conn(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_set_status_disc(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_set_status_failed(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_set_status_remote_busy(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_set_status_refuse(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_set_status_conflict(struct sock *sk, - struct sk_buff *skb); -extern int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk, - struct sk_buff *skb); +int llc_conn_ev_qlfy_set_status_refuse(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_set_status_conflict(struct sock *sk, struct sk_buff *skb); +int llc_conn_ev_qlfy_set_status_rst_done(struct sock *sk, struct sk_buff *skb); static __inline__ int llc_conn_space(struct sock *sk, struct sk_buff *skb) { diff --git a/include/net/llc_conn.h b/include/net/llc_conn.h index 2f97d8ddce924dbfc61f473896cacda3cfb70456..0134681acc4cfe354c6546be1a1ffefa5c078cde 100644 --- a/include/net/llc_conn.h +++ b/include/net/llc_conn.h @@ -95,28 +95,24 @@ static __inline__ char llc_backlog_type(struct sk_buff *skb) return skb->cb[sizeof(skb->cb) - 1]; } -extern struct sock *llc_sk_alloc(struct net *net, int family, gfp_t priority, - struct proto *prot); -extern void llc_sk_free(struct sock *sk); +struct sock *llc_sk_alloc(struct net *net, int family, gfp_t priority, + struct proto *prot); +void llc_sk_free(struct sock *sk); -extern void llc_sk_reset(struct sock *sk); +void llc_sk_reset(struct sock *sk); /* Access to a connection */ -extern int llc_conn_state_process(struct sock *sk, struct sk_buff *skb); -extern void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb); -extern void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb); -extern void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, - u8 first_p_bit); -extern void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, - u8 first_f_bit); -extern int llc_conn_remove_acked_pdus(struct sock *conn, u8 nr, - u16 *how_many_unacked); -extern struct sock *llc_lookup_established(struct llc_sap *sap, - struct llc_addr *daddr, - struct llc_addr *laddr); -extern void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk); -extern void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk); +int llc_conn_state_process(struct sock *sk, struct sk_buff *skb); +void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb); +void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb); +void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr, u8 first_p_bit); +void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit); +int llc_conn_remove_acked_pdus(struct sock *conn, u8 nr, u16 *how_many_unacked); +struct sock *llc_lookup_established(struct llc_sap *sap, struct llc_addr *daddr, + struct llc_addr *laddr); +void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk); +void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk); -extern u8 llc_data_accept_state(u8 state); -extern void llc_build_offset_table(void); +u8 llc_data_accept_state(u8 state); +void llc_build_offset_table(void); #endif /* LLC_CONN_H */ diff --git a/include/net/llc_if.h b/include/net/llc_if.h index f0cb909b60eb8c73670cf969a5bf544267dd7730..8d5c543cd620037590b1ee823548a9c091936de3 100644 --- a/include/net/llc_if.h +++ b/include/net/llc_if.h @@ -62,8 +62,7 @@ #define LLC_STATUS_CONFLICT 7 /* disconnect conn */ #define LLC_STATUS_RESET_DONE 8 /* */ -extern int llc_establish_connection(struct sock *sk, u8 *lmac, - u8 *dmac, u8 dsap); -extern int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb); -extern int llc_send_disc(struct sock *sk); +int llc_establish_connection(struct sock *sk, u8 *lmac, u8 *dmac, u8 dsap); +int llc_build_and_send_pkt(struct sock *sk, struct sk_buff *skb); +int llc_send_disc(struct sock *sk); #endif /* LLC_IF_H */ diff --git a/include/net/llc_pdu.h b/include/net/llc_pdu.h index 5a93d13ac95c8177542a3f2b327ae60b26065cea..31e2de7d57c5d6c210e1b445b19551407fd31b6c 100644 --- a/include/net/llc_pdu.h +++ b/include/net/llc_pdu.h @@ -410,21 +410,20 @@ struct llc_frmr_info { u8 ind_bits; /* indicator bits set with macro */ } __packed; -extern void llc_pdu_set_cmd_rsp(struct sk_buff *skb, u8 type); -extern void llc_pdu_set_pf_bit(struct sk_buff *skb, u8 bit_value); -extern void llc_pdu_decode_pf_bit(struct sk_buff *skb, u8 *pf_bit); -extern void llc_pdu_init_as_disc_cmd(struct sk_buff *skb, u8 p_bit); -extern void llc_pdu_init_as_i_cmd(struct sk_buff *skb, u8 p_bit, u8 ns, u8 nr); -extern void llc_pdu_init_as_rej_cmd(struct sk_buff *skb, u8 p_bit, u8 nr); -extern void llc_pdu_init_as_rnr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr); -extern void llc_pdu_init_as_rr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr); -extern void llc_pdu_init_as_sabme_cmd(struct sk_buff *skb, u8 p_bit); -extern void llc_pdu_init_as_dm_rsp(struct sk_buff *skb, u8 f_bit); -extern void llc_pdu_init_as_frmr_rsp(struct sk_buff *skb, - struct llc_pdu_sn *prev_pdu, - u8 f_bit, u8 vs, u8 vr, u8 vzyxw); -extern void llc_pdu_init_as_rr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr); -extern void llc_pdu_init_as_rej_rsp(struct sk_buff *skb, u8 f_bit, u8 nr); -extern void llc_pdu_init_as_rnr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr); -extern void llc_pdu_init_as_ua_rsp(struct sk_buff *skb, u8 f_bit); +void llc_pdu_set_cmd_rsp(struct sk_buff *skb, u8 type); +void llc_pdu_set_pf_bit(struct sk_buff *skb, u8 bit_value); +void llc_pdu_decode_pf_bit(struct sk_buff *skb, u8 *pf_bit); +void llc_pdu_init_as_disc_cmd(struct sk_buff *skb, u8 p_bit); +void llc_pdu_init_as_i_cmd(struct sk_buff *skb, u8 p_bit, u8 ns, u8 nr); +void llc_pdu_init_as_rej_cmd(struct sk_buff *skb, u8 p_bit, u8 nr); +void llc_pdu_init_as_rnr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr); +void llc_pdu_init_as_rr_cmd(struct sk_buff *skb, u8 p_bit, u8 nr); +void llc_pdu_init_as_sabme_cmd(struct sk_buff *skb, u8 p_bit); +void llc_pdu_init_as_dm_rsp(struct sk_buff *skb, u8 f_bit); +void llc_pdu_init_as_frmr_rsp(struct sk_buff *skb, struct llc_pdu_sn *prev_pdu, + u8 f_bit, u8 vs, u8 vr, u8 vzyxw); +void llc_pdu_init_as_rr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr); +void llc_pdu_init_as_rej_rsp(struct sk_buff *skb, u8 f_bit, u8 nr); +void llc_pdu_init_as_rnr_rsp(struct sk_buff *skb, u8 f_bit, u8 nr); +void llc_pdu_init_as_ua_rsp(struct sk_buff *skb, u8 f_bit); #endif /* LLC_PDU_H */ diff --git a/include/net/llc_s_ac.h b/include/net/llc_s_ac.h index 37a3bbd02394b3497e91316275babc01214f5d08..a61b98c108ee2c4df241a7d2318249427b686951 100644 --- a/include/net/llc_s_ac.h +++ b/include/net/llc_s_ac.h @@ -25,15 +25,13 @@ /* All action functions must look like this */ typedef int (*llc_sap_action_t)(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_action_unitdata_ind(struct llc_sap *sap, - struct sk_buff *skb); -extern int llc_sap_action_send_ui(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_action_send_xid_c(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_action_send_xid_r(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_action_send_test_c(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_action_send_test_r(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_action_report_status(struct llc_sap *sap, - struct sk_buff *skb); -extern int llc_sap_action_xid_ind(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_action_test_ind(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_action_unitdata_ind(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_action_send_ui(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_action_send_xid_c(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_action_send_xid_r(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_action_send_test_c(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_action_send_test_r(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_action_report_status(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_action_xid_ind(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_action_test_ind(struct llc_sap *sap, struct sk_buff *skb); #endif /* LLC_S_AC_H */ diff --git a/include/net/llc_s_ev.h b/include/net/llc_s_ev.h index e3acb9329e4af4106bc221af99e94670b2393e93..84db3a59ed2880bb4b797fd64ea012beb9136ea1 100644 --- a/include/net/llc_s_ev.h +++ b/include/net/llc_s_ev.h @@ -53,15 +53,14 @@ struct llc_sap; typedef int (*llc_sap_ev_t)(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_activation_req(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_rx_ui(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_unitdata_req(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_xid_req(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_rx_xid_c(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_rx_xid_r(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_test_req(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_rx_test_c(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_rx_test_r(struct llc_sap *sap, struct sk_buff *skb); -extern int llc_sap_ev_deactivation_req(struct llc_sap *sap, - struct sk_buff *skb); +int llc_sap_ev_activation_req(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_ev_rx_ui(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_ev_unitdata_req(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_ev_xid_req(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_ev_rx_xid_c(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_ev_rx_xid_r(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_ev_test_req(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_ev_rx_test_c(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_ev_rx_test_r(struct llc_sap *sap, struct sk_buff *skb); +int llc_sap_ev_deactivation_req(struct llc_sap *sap, struct sk_buff *skb); #endif /* LLC_S_EV_H */ diff --git a/include/net/llc_sap.h b/include/net/llc_sap.h index ed25bec2f6483aa7f2b6bda6a6324eb2206ee383..1e4df9fd9fb2cf6b06acdf2b923ed41fdfad142c 100644 --- a/include/net/llc_sap.h +++ b/include/net/llc_sap.h @@ -19,18 +19,14 @@ struct net_device; struct sk_buff; struct sock; -extern void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb); -extern void llc_save_primitive(struct sock *sk, struct sk_buff* skb, - unsigned char prim); -extern struct sk_buff *llc_alloc_frame(struct sock *sk, struct net_device *dev, - u8 type, u32 data_size); +void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb); +void llc_save_primitive(struct sock *sk, struct sk_buff *skb, + unsigned char prim); +struct sk_buff *llc_alloc_frame(struct sock *sk, struct net_device *dev, + u8 type, u32 data_size); -extern void llc_build_and_send_test_pkt(struct llc_sap *sap, - struct sk_buff *skb, - unsigned char *dmac, - unsigned char dsap); -extern void llc_build_and_send_xid_pkt(struct llc_sap *sap, - struct sk_buff *skb, - unsigned char *dmac, - unsigned char dsap); +void llc_build_and_send_test_pkt(struct llc_sap *sap, struct sk_buff *skb, + unsigned char *dmac, unsigned char dsap); +void llc_build_and_send_xid_pkt(struct llc_sap *sap, struct sk_buff *skb, + unsigned char *dmac, unsigned char dsap); #endif /* LLC_SAP_H */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index cc6035f1a2f11c1b68f5a82235d4698441e8cddd..7ceed99a05bc79218a841352a454a980df48741b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -829,6 +829,15 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3 * @RX_FLAG_10MHZ: 10 MHz (half channel) was used * @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used + * @RX_FLAG_AMSDU_MORE: Some drivers may prefer to report separate A-MSDU + * subframes instead of a one huge frame for performance reasons. + * All, but the last MSDU from an A-MSDU should have this flag set. E.g. + * if an A-MSDU has 3 frames, the first 2 must have the flag set, while + * the 3rd (last) one must not have this flag set. The flag is used to + * deal with retransmission/duplication recovery properly since A-MSDU + * subframes share the same sequence number. Reported subframes can be + * either regular MSDU or singly A-MSDUs. Subframes must not be + * interleaved with other frames. */ enum mac80211_rx_flags { RX_FLAG_MMIC_ERROR = BIT(0), @@ -859,6 +868,7 @@ enum mac80211_rx_flags { RX_FLAG_STBC_MASK = BIT(26) | BIT(27), RX_FLAG_10MHZ = BIT(28), RX_FLAG_5MHZ = BIT(29), + RX_FLAG_AMSDU_MORE = BIT(30), }; #define RX_FLAG_STBC_SHIFT 26 @@ -1492,6 +1502,15 @@ struct ieee80211_tx_control { * * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames * only, to allow getting TBTT of a DTIM beacon. + * + * @IEEE80211_HW_SUPPORTS_HT_CCK_RATES: Hardware supports mixing HT/CCK rates + * and can cope with CCK rates in an aggregation session (e.g. by not + * using aggregation for such frames.) + * + * @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA) + * for a single active channel while using channel contexts. When support + * is not enabled the default action is to disconnect when getting the + * CSA frame. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -1522,6 +1541,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25, IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, + IEEE80211_HW_CHANCTX_STA_CSA = 1<<28, }; /** @@ -2666,6 +2686,10 @@ enum ieee80211_roc_type { * zero using ieee80211_csa_is_complete() after the beacon has been * transmitted and then call ieee80211_csa_finish(). * + * @join_ibss: Join an IBSS (on an IBSS interface); this is called after all + * information in bss_conf is set up and the beacon can be retrieved. A + * channel context is bound before this is called. + * @leave_ibss: Leave the IBSS again. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -2857,6 +2881,9 @@ struct ieee80211_ops { void (*channel_switch_beacon)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_chan_def *chandef); + + int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); }; /** @@ -3919,6 +3946,25 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw, struct ieee80211_vif *vif), void *data); +/** + * ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces + * + * This function iterates over the interfaces associated with a given + * hardware that are currently active and calls the callback for them. + * This version can only be used while holding the RTNL. + * + * @hw: the hardware struct of which the interfaces should be iterated over + * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags + * @iterator: the iterator function to call, cannot sleep + * @data: first argument of the iterator function + */ +void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw, + u32 iter_flags, + void (*iterator)(void *data, + u8 *mac, + struct ieee80211_vif *vif), + void *data); + /** * ieee80211_queue_work - add work onto the mac80211 workqueue * @@ -4525,4 +4571,18 @@ void ieee80211_report_wowlan_wakeup(struct ieee80211_vif *vif, struct cfg80211_wowlan_wakeup *wakeup, gfp_t gfp); +/** + * ieee80211_tx_prepare_skb - prepare an 802.11 skb for transmission + * @hw: pointer as obtained from ieee80211_alloc_hw() + * @vif: virtual interface + * @skb: frame to be sent from within the driver + * @band: the band to transmit on + * @sta: optional pointer to get the station to send the frame to + * + * Note: must be called under RCU lock + */ +bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, struct sk_buff *skb, + int band, struct ieee80211_sta **sta); + #endif /* MAC80211_H */ diff --git a/include/net/mrp.h b/include/net/mrp.h index 0f7558b638ae6ff9d0cde2b9e45e13990269005b..31912c3be772bfa5971e3f31a11f982cdd22ad2d 100644 --- a/include/net/mrp.h +++ b/include/net/mrp.h @@ -126,19 +126,17 @@ struct mrp_port { struct rcu_head rcu; }; -extern int mrp_register_application(struct mrp_application *app); -extern void mrp_unregister_application(struct mrp_application *app); - -extern int mrp_init_applicant(struct net_device *dev, - struct mrp_application *app); -extern void mrp_uninit_applicant(struct net_device *dev, - struct mrp_application *app); - -extern int mrp_request_join(const struct net_device *dev, - const struct mrp_application *app, - const void *value, u8 len, u8 type); -extern void mrp_request_leave(const struct net_device *dev, - const struct mrp_application *app, - const void *value, u8 len, u8 type); +int mrp_register_application(struct mrp_application *app); +void mrp_unregister_application(struct mrp_application *app); + +int mrp_init_applicant(struct net_device *dev, struct mrp_application *app); +void mrp_uninit_applicant(struct net_device *dev, struct mrp_application *app); + +int mrp_request_join(const struct net_device *dev, + const struct mrp_application *app, + const void *value, u8 len, u8 type); +void mrp_request_leave(const struct net_device *dev, + const struct mrp_application *app, + const void *value, u8 len, u8 type); #endif /* _NET_MRP_H */ diff --git a/include/net/ndisc.h b/include/net/ndisc.h index ea0cc26ab70e1b447a0e834992c746a9f33c9938..6bbda34d5e59d030a1e2d69e93cda754d06ae5c0 100644 --- a/include/net/ndisc.h +++ b/include/net/ndisc.h @@ -110,8 +110,8 @@ struct ndisc_options { #define NDISC_OPT_SPACE(len) (((len)+2+7)&~7) -extern struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, - struct ndisc_options *ndopts); +struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, + struct ndisc_options *ndopts); /* * Return the padding between the option length and the start of the @@ -189,60 +189,51 @@ static inline struct neighbour *__ipv6_neigh_lookup(struct net_device *dev, cons return n; } -extern int ndisc_init(void); -extern int ndisc_late_init(void); +int ndisc_init(void); +int ndisc_late_init(void); -extern void ndisc_late_cleanup(void); -extern void ndisc_cleanup(void); +void ndisc_late_cleanup(void); +void ndisc_cleanup(void); -extern int ndisc_rcv(struct sk_buff *skb); +int ndisc_rcv(struct sk_buff *skb); -extern void ndisc_send_ns(struct net_device *dev, - struct neighbour *neigh, - const struct in6_addr *solicit, - const struct in6_addr *daddr, - const struct in6_addr *saddr); +void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, + const struct in6_addr *solicit, + const struct in6_addr *daddr, const struct in6_addr *saddr); -extern void ndisc_send_rs(struct net_device *dev, - const struct in6_addr *saddr, - const struct in6_addr *daddr); -extern void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, - const struct in6_addr *daddr, - const struct in6_addr *solicited_addr, - bool router, bool solicited, bool override, - bool inc_opt); +void ndisc_send_rs(struct net_device *dev, + const struct in6_addr *saddr, const struct in6_addr *daddr); +void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, + const struct in6_addr *daddr, + const struct in6_addr *solicited_addr, + bool router, bool solicited, bool override, bool inc_opt); -extern void ndisc_send_redirect(struct sk_buff *skb, - const struct in6_addr *target); +void ndisc_send_redirect(struct sk_buff *skb, const struct in6_addr *target); -extern int ndisc_mc_map(const struct in6_addr *addr, char *buf, - struct net_device *dev, int dir); +int ndisc_mc_map(const struct in6_addr *addr, char *buf, struct net_device *dev, + int dir); /* * IGMP */ -extern int igmp6_init(void); +int igmp6_init(void); -extern void igmp6_cleanup(void); +void igmp6_cleanup(void); -extern int igmp6_event_query(struct sk_buff *skb); +int igmp6_event_query(struct sk_buff *skb); -extern int igmp6_event_report(struct sk_buff *skb); +int igmp6_event_report(struct sk_buff *skb); #ifdef CONFIG_SYSCTL -extern int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, - int write, - void __user *buffer, - size_t *lenp, - loff_t *ppos); +int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *ppos); int ndisc_ifinfo_sysctl_strategy(struct ctl_table *ctl, void __user *oldval, size_t __user *oldlenp, void __user *newval, size_t newlen); #endif -extern void inet6_ifinfo_notify(int event, - struct inet6_dev *idev); +void inet6_ifinfo_notify(int event, struct inet6_dev *idev); #endif diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 9d22f08896c6c16d5a1c26fad0ef65461980d02a..da68c9a90ac56932638feca9cdbba6a61b75ba64 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -22,6 +22,7 @@ #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) #include #endif +#include #include struct user_namespace; @@ -101,6 +102,9 @@ struct net { #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE) struct netns_ct ct; #endif +#if defined(CONFIG_NF_TABLES) || defined(CONFIG_NF_TABLES_MODULE) + struct netns_nftables nft; +#endif #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) struct netns_nf_frag nf_frag; #endif @@ -137,8 +141,8 @@ struct net { extern struct net init_net; #ifdef CONFIG_NET_NS -extern struct net *copy_net_ns(unsigned long flags, - struct user_namespace *user_ns, struct net *old_net); +struct net *copy_net_ns(unsigned long flags, struct user_namespace *user_ns, + struct net *old_net); #else /* CONFIG_NET_NS */ #include @@ -155,11 +159,11 @@ static inline struct net *copy_net_ns(unsigned long flags, extern struct list_head net_namespace_list; -extern struct net *get_net_ns_by_pid(pid_t pid); -extern struct net *get_net_ns_by_fd(int pid); +struct net *get_net_ns_by_pid(pid_t pid); +struct net *get_net_ns_by_fd(int pid); #ifdef CONFIG_NET_NS -extern void __put_net(struct net *net); +void __put_net(struct net *net); static inline struct net *get_net(struct net *net) { @@ -191,7 +195,7 @@ int net_eq(const struct net *net1, const struct net *net2) return net1 == net2; } -extern void net_drop_ns(void *); +void net_drop_ns(void *); #else @@ -308,19 +312,19 @@ struct pernet_operations { * device which caused kernel oops, and panics during network * namespace cleanup. So please don't get this wrong. */ -extern int register_pernet_subsys(struct pernet_operations *); -extern void unregister_pernet_subsys(struct pernet_operations *); -extern int register_pernet_device(struct pernet_operations *); -extern void unregister_pernet_device(struct pernet_operations *); +int register_pernet_subsys(struct pernet_operations *); +void unregister_pernet_subsys(struct pernet_operations *); +int register_pernet_device(struct pernet_operations *); +void unregister_pernet_device(struct pernet_operations *); struct ctl_table; struct ctl_table_header; #ifdef CONFIG_SYSCTL -extern int net_sysctl_init(void); -extern struct ctl_table_header *register_net_sysctl(struct net *net, - const char *path, struct ctl_table *table); -extern void unregister_net_sysctl_table(struct ctl_table_header *header); +int net_sysctl_init(void); +struct ctl_table_header *register_net_sysctl(struct net *net, const char *path, + struct ctl_table *table); +void unregister_net_sysctl_table(struct ctl_table_header *header); #else static inline int net_sysctl_init(void) { return 0; } static inline struct ctl_table_header *register_net_sysctl(struct net *net, diff --git a/include/net/netevent.h b/include/net/netevent.h index fe630dde35c3f3688d30e1e33a5fb5d06393bba6..d8bbb38584b603ac5cab30d3f86049565b855106 100644 --- a/include/net/netevent.h +++ b/include/net/netevent.h @@ -26,8 +26,8 @@ enum netevent_notif_type { NETEVENT_REDIRECT, /* arg is struct netevent_redirect ptr */ }; -extern int register_netevent_notifier(struct notifier_block *nb); -extern int unregister_netevent_notifier(struct notifier_block *nb); -extern int call_netevent_notifiers(unsigned long val, void *v); +int register_netevent_notifier(struct notifier_block *nb); +int unregister_netevent_notifier(struct notifier_block *nb); +int call_netevent_notifiers(unsigned long val, void *v); #endif diff --git a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h index 7573d52a43469a9dbc3feeb6555810ceb9e1a740..6c3d12e2949f908c3927cb4bfe29ac30c49e1e4a 100644 --- a/include/net/netfilter/ipv4/nf_conntrack_ipv4.h +++ b/include/net/netfilter/ipv4/nf_conntrack_ipv4.h @@ -16,9 +16,9 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4; extern struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp; -extern int nf_conntrack_ipv4_compat_init(void); -extern void nf_conntrack_ipv4_compat_fini(void); +int nf_conntrack_ipv4_compat_init(void); +void nf_conntrack_ipv4_compat_fini(void); -extern void need_ipv4_conntrack(void); +void need_ipv4_conntrack(void); #endif /*_NF_CONNTRACK_IPV4_H*/ diff --git a/include/net/netfilter/ipv4/nf_defrag_ipv4.h b/include/net/netfilter/ipv4/nf_defrag_ipv4.h index 6b00ea38546b8e829f5b7a97bc5c33b35b0a030a..f01ef208dff6ec151f82eb2a33626fdae7edea42 100644 --- a/include/net/netfilter/ipv4/nf_defrag_ipv4.h +++ b/include/net/netfilter/ipv4/nf_defrag_ipv4.h @@ -1,6 +1,6 @@ #ifndef _NF_DEFRAG_IPV4_H #define _NF_DEFRAG_IPV4_H -extern void nf_defrag_ipv4_enable(void); +void nf_defrag_ipv4_enable(void); #endif /* _NF_DEFRAG_IPV4_H */ diff --git a/include/net/netfilter/ipv6/nf_defrag_ipv6.h b/include/net/netfilter/ipv6/nf_defrag_ipv6.h index fd79c9a1779d19d6a5dd54ef7380b990763a00ea..27666d8a0bd07f38f644bf7c768678ade981155b 100644 --- a/include/net/netfilter/ipv6/nf_defrag_ipv6.h +++ b/include/net/netfilter/ipv6/nf_defrag_ipv6.h @@ -1,15 +1,12 @@ #ifndef _NF_DEFRAG_IPV6_H #define _NF_DEFRAG_IPV6_H -extern void nf_defrag_ipv6_enable(void); +void nf_defrag_ipv6_enable(void); -extern int nf_ct_frag6_init(void); -extern void nf_ct_frag6_cleanup(void); -extern struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user); -extern void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, - struct net_device *in, - struct net_device *out, - int (*okfn)(struct sk_buff *)); +int nf_ct_frag6_init(void); +void nf_ct_frag6_cleanup(void); +struct sk_buff *nf_ct_frag6_gather(struct sk_buff *skb, u32 user); +void nf_ct_frag6_consume_orig(struct sk_buff *skb); struct inet_frags_ctl; diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 0c1288a50e8bde05d896ebf243d0b7c3a01deb11..01ea6eed1bb1ddcc9b8c9001286ae85730bea416 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -139,15 +139,13 @@ static inline struct net *nf_ct_net(const struct nf_conn *ct) } /* Alter reply tuple (maybe alter helper). */ -extern void -nf_conntrack_alter_reply(struct nf_conn *ct, - const struct nf_conntrack_tuple *newreply); +void nf_conntrack_alter_reply(struct nf_conn *ct, + const struct nf_conntrack_tuple *newreply); /* Is this tuple taken? (ignoring any belonging to the given conntrack). */ -extern int -nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple, - const struct nf_conn *ignored_conntrack); +int nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple, + const struct nf_conn *ignored_conntrack); /* Return conntrack_info and tuple hash for given skb. */ static inline struct nf_conn * @@ -165,37 +163,34 @@ static inline void nf_ct_put(struct nf_conn *ct) } /* Protocol module loading */ -extern int nf_ct_l3proto_try_module_get(unsigned short l3proto); -extern void nf_ct_l3proto_module_put(unsigned short l3proto); +int nf_ct_l3proto_try_module_get(unsigned short l3proto); +void nf_ct_l3proto_module_put(unsigned short l3proto); /* * Allocate a hashtable of hlist_head (if nulls == 0), * or hlist_nulls_head (if nulls == 1) */ -extern void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls); +void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls); -extern void nf_ct_free_hashtable(void *hash, unsigned int size); +void nf_ct_free_hashtable(void *hash, unsigned int size); -extern struct nf_conntrack_tuple_hash * +struct nf_conntrack_tuple_hash * __nf_conntrack_find(struct net *net, u16 zone, const struct nf_conntrack_tuple *tuple); -extern int nf_conntrack_hash_check_insert(struct nf_conn *ct); +int nf_conntrack_hash_check_insert(struct nf_conn *ct); bool nf_ct_delete(struct nf_conn *ct, u32 pid, int report); -extern void nf_conntrack_flush_report(struct net *net, u32 portid, int report); +void nf_conntrack_flush_report(struct net *net, u32 portid, int report); -extern bool nf_ct_get_tuplepr(const struct sk_buff *skb, - unsigned int nhoff, u_int16_t l3num, - struct nf_conntrack_tuple *tuple); -extern bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse, - const struct nf_conntrack_tuple *orig); +bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff, + u_int16_t l3num, struct nf_conntrack_tuple *tuple); +bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse, + const struct nf_conntrack_tuple *orig); -extern void __nf_ct_refresh_acct(struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb, - unsigned long extra_jiffies, - int do_acct); +void __nf_ct_refresh_acct(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + const struct sk_buff *skb, + unsigned long extra_jiffies, int do_acct); /* Refresh conntrack for this many jiffies and do accounting */ static inline void nf_ct_refresh_acct(struct nf_conn *ct, @@ -214,10 +209,8 @@ static inline void nf_ct_refresh(struct nf_conn *ct, __nf_ct_refresh_acct(ct, 0, skb, extra_jiffies, 0); } -extern bool __nf_ct_kill_acct(struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - const struct sk_buff *skb, - int do_acct); +bool __nf_ct_kill_acct(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + const struct sk_buff *skb, int do_acct); /* kill conntrack and do accounting */ static inline bool nf_ct_kill_acct(struct nf_conn *ct, @@ -244,19 +237,17 @@ static inline struct nf_conn *nf_ct_untracked_get(void) { return &__raw_get_cpu_var(nf_conntrack_untracked); } -extern void nf_ct_untracked_status_or(unsigned long bits); +void nf_ct_untracked_status_or(unsigned long bits); /* Iterate over all conntracks: if iter returns true, it's deleted. */ -extern void -nf_ct_iterate_cleanup(struct net *net, - int (*iter)(struct nf_conn *i, void *data), - void *data, u32 portid, int report); -extern void nf_conntrack_free(struct nf_conn *ct); -extern struct nf_conn * -nf_conntrack_alloc(struct net *net, u16 zone, - const struct nf_conntrack_tuple *orig, - const struct nf_conntrack_tuple *repl, - gfp_t gfp); +void nf_ct_iterate_cleanup(struct net *net, + int (*iter)(struct nf_conn *i, void *data), + void *data, u32 portid, int report); +void nf_conntrack_free(struct nf_conn *ct); +struct nf_conn *nf_conntrack_alloc(struct net *net, u16 zone, + const struct nf_conntrack_tuple *orig, + const struct nf_conntrack_tuple *repl, + gfp_t gfp); static inline int nf_ct_is_template(const struct nf_conn *ct) { @@ -287,7 +278,7 @@ static inline bool nf_is_loopback_packet(const struct sk_buff *skb) struct kernel_param; -extern int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp); +int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp); extern unsigned int nf_conntrack_htable_size; extern unsigned int nf_conntrack_max; extern unsigned int nf_conntrack_hash_rnd; diff --git a/include/net/netfilter/nf_conntrack_acct.h b/include/net/netfilter/nf_conntrack_acct.h index 2bdb7a15fe06b102a5fc88c2cc3da5662ff53a95..79d8d16732b426d722f11ec2951ef03891159a63 100644 --- a/include/net/netfilter/nf_conntrack_acct.h +++ b/include/net/netfilter/nf_conntrack_acct.h @@ -19,17 +19,21 @@ struct nf_conn_counter { atomic64_t bytes; }; +struct nf_conn_acct { + struct nf_conn_counter counter[IP_CT_DIR_MAX]; +}; + static inline -struct nf_conn_counter *nf_conn_acct_find(const struct nf_conn *ct) +struct nf_conn_acct *nf_conn_acct_find(const struct nf_conn *ct) { return nf_ct_ext_find(ct, NF_CT_EXT_ACCT); } static inline -struct nf_conn_counter *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp) +struct nf_conn_acct *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp) { struct net *net = nf_ct_net(ct); - struct nf_conn_counter *acct; + struct nf_conn_acct *acct; if (!net->ct.sysctl_acct) return NULL; @@ -42,8 +46,8 @@ struct nf_conn_counter *nf_ct_acct_ext_add(struct nf_conn *ct, gfp_t gfp) return acct; }; -extern unsigned int -seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir); +unsigned int seq_print_acct(struct seq_file *s, const struct nf_conn *ct, + int dir); /* Check if connection tracking accounting is enabled */ static inline bool nf_ct_acct_enabled(struct net *net) @@ -57,9 +61,9 @@ static inline void nf_ct_set_acct(struct net *net, bool enable) net->ct.sysctl_acct = enable; } -extern int nf_conntrack_acct_pernet_init(struct net *net); -extern void nf_conntrack_acct_pernet_fini(struct net *net); +int nf_conntrack_acct_pernet_init(struct net *net); +void nf_conntrack_acct_pernet_fini(struct net *net); -extern int nf_conntrack_acct_init(void); -extern void nf_conntrack_acct_fini(void); +int nf_conntrack_acct_init(void); +void nf_conntrack_acct_fini(void); #endif /* _NF_CONNTRACK_ACCT_H */ diff --git a/include/net/netfilter/nf_conntrack_core.h b/include/net/netfilter/nf_conntrack_core.h index fb2b6234e9375846bdb37727717c7dbd432550d9..15308b8eb5b5218330c3043c46a02537d00779a2 100644 --- a/include/net/netfilter/nf_conntrack_core.h +++ b/include/net/netfilter/nf_conntrack_core.h @@ -20,49 +20,42 @@ /* This header is used to share core functionality between the standalone connection tracking module, and the compatibility layer's use of connection tracking. */ -extern unsigned int nf_conntrack_in(struct net *net, - u_int8_t pf, - unsigned int hooknum, - struct sk_buff *skb); - -extern int nf_conntrack_init_net(struct net *net); -extern void nf_conntrack_cleanup_net(struct net *net); -extern void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list); - -extern int nf_conntrack_proto_pernet_init(struct net *net); -extern void nf_conntrack_proto_pernet_fini(struct net *net); - -extern int nf_conntrack_proto_init(void); -extern void nf_conntrack_proto_fini(void); - -extern int nf_conntrack_init_start(void); -extern void nf_conntrack_cleanup_start(void); - -extern void nf_conntrack_init_end(void); -extern void nf_conntrack_cleanup_end(void); - -extern bool -nf_ct_get_tuple(const struct sk_buff *skb, - unsigned int nhoff, - unsigned int dataoff, - u_int16_t l3num, - u_int8_t protonum, - struct nf_conntrack_tuple *tuple, - const struct nf_conntrack_l3proto *l3proto, - const struct nf_conntrack_l4proto *l4proto); - -extern bool -nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse, - const struct nf_conntrack_tuple *orig, - const struct nf_conntrack_l3proto *l3proto, - const struct nf_conntrack_l4proto *l4proto); +unsigned int nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, + struct sk_buff *skb); + +int nf_conntrack_init_net(struct net *net); +void nf_conntrack_cleanup_net(struct net *net); +void nf_conntrack_cleanup_net_list(struct list_head *net_exit_list); + +int nf_conntrack_proto_pernet_init(struct net *net); +void nf_conntrack_proto_pernet_fini(struct net *net); + +int nf_conntrack_proto_init(void); +void nf_conntrack_proto_fini(void); + +int nf_conntrack_init_start(void); +void nf_conntrack_cleanup_start(void); + +void nf_conntrack_init_end(void); +void nf_conntrack_cleanup_end(void); + +bool nf_ct_get_tuple(const struct sk_buff *skb, unsigned int nhoff, + unsigned int dataoff, u_int16_t l3num, u_int8_t protonum, + struct nf_conntrack_tuple *tuple, + const struct nf_conntrack_l3proto *l3proto, + const struct nf_conntrack_l4proto *l4proto); + +bool nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse, + const struct nf_conntrack_tuple *orig, + const struct nf_conntrack_l3proto *l3proto, + const struct nf_conntrack_l4proto *l4proto); /* Find a connection corresponding to a tuple. */ -extern struct nf_conntrack_tuple_hash * +struct nf_conntrack_tuple_hash * nf_conntrack_find_get(struct net *net, u16 zone, const struct nf_conntrack_tuple *tuple); -extern int __nf_conntrack_confirm(struct sk_buff *skb); +int __nf_conntrack_confirm(struct sk_buff *skb); /* Confirm a connection: returns NF_DROP if packet must be dropped. */ static inline int nf_conntrack_confirm(struct sk_buff *skb) diff --git a/include/net/netfilter/nf_conntrack_ecache.h b/include/net/netfilter/nf_conntrack_ecache.h index 092dc651689f81d85a6ae686c5d761aee006918d..0e3d08e4b1d3e59fa101607d88e4c66f7664566d 100644 --- a/include/net/netfilter/nf_conntrack_ecache.h +++ b/include/net/netfilter/nf_conntrack_ecache.h @@ -68,10 +68,12 @@ struct nf_ct_event_notifier { int (*fcn)(unsigned int events, struct nf_ct_event *item); }; -extern int nf_conntrack_register_notifier(struct net *net, struct nf_ct_event_notifier *nb); -extern void nf_conntrack_unregister_notifier(struct net *net, struct nf_ct_event_notifier *nb); +int nf_conntrack_register_notifier(struct net *net, + struct nf_ct_event_notifier *nb); +void nf_conntrack_unregister_notifier(struct net *net, + struct nf_ct_event_notifier *nb); -extern void nf_ct_deliver_cached_events(struct nf_conn *ct); +void nf_ct_deliver_cached_events(struct nf_conn *ct); static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct) @@ -166,8 +168,10 @@ struct nf_exp_event_notifier { int (*fcn)(unsigned int events, struct nf_exp_event *item); }; -extern int nf_ct_expect_register_notifier(struct net *net, struct nf_exp_event_notifier *nb); -extern void nf_ct_expect_unregister_notifier(struct net *net, struct nf_exp_event_notifier *nb); +int nf_ct_expect_register_notifier(struct net *net, + struct nf_exp_event_notifier *nb); +void nf_ct_expect_unregister_notifier(struct net *net, + struct nf_exp_event_notifier *nb); static inline void nf_ct_expect_event_report(enum ip_conntrack_expect_events event, @@ -207,11 +211,11 @@ nf_ct_expect_event(enum ip_conntrack_expect_events event, nf_ct_expect_event_report(event, exp, 0, 0); } -extern int nf_conntrack_ecache_pernet_init(struct net *net); -extern void nf_conntrack_ecache_pernet_fini(struct net *net); +int nf_conntrack_ecache_pernet_init(struct net *net); +void nf_conntrack_ecache_pernet_fini(struct net *net); -extern int nf_conntrack_ecache_init(void); -extern void nf_conntrack_ecache_fini(void); +int nf_conntrack_ecache_init(void); +void nf_conntrack_ecache_fini(void); #else /* CONFIG_NF_CONNTRACK_EVENTS */ static inline void nf_conntrack_event_cache(enum ip_conntrack_events event, diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 88a1d4060d5260f7ac51e7b2d74f87ab409cd4de..956b175523ffaf726dadb2fa079219e0d12d5838 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -36,7 +36,7 @@ enum nf_ct_ext_id { #define NF_CT_EXT_HELPER_TYPE struct nf_conn_help #define NF_CT_EXT_NAT_TYPE struct nf_conn_nat #define NF_CT_EXT_SEQADJ_TYPE struct nf_conn_seqadj -#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter +#define NF_CT_EXT_ACCT_TYPE struct nf_conn_acct #define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache #define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone #define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp @@ -73,7 +73,7 @@ static inline void *__nf_ct_ext_find(const struct nf_conn *ct, u8 id) ((id##_TYPE *)__nf_ct_ext_find((ext), (id))) /* Destroy all relationships */ -extern void __nf_ct_ext_destroy(struct nf_conn *ct); +void __nf_ct_ext_destroy(struct nf_conn *ct); static inline void nf_ct_ext_destroy(struct nf_conn *ct) { if (ct->ext) diff --git a/include/net/netfilter/nf_conntrack_helper.h b/include/net/netfilter/nf_conntrack_helper.h index 26c4ae5bfbb8df49abbdcf19dc67de9273575601..6cf614bc0029326da74bdb6145d009f95f3422e5 100644 --- a/include/net/netfilter/nf_conntrack_helper.h +++ b/include/net/netfilter/nf_conntrack_helper.h @@ -52,21 +52,24 @@ struct nf_conntrack_helper { unsigned int queue_num; /* For user-space helpers. */ }; -extern struct nf_conntrack_helper * -__nf_conntrack_helper_find(const char *name, u16 l3num, u8 protonum); +struct nf_conntrack_helper *__nf_conntrack_helper_find(const char *name, + u16 l3num, u8 protonum); -extern struct nf_conntrack_helper * -nf_conntrack_helper_try_module_get(const char *name, u16 l3num, u8 protonum); +struct nf_conntrack_helper *nf_conntrack_helper_try_module_get(const char *name, + u16 l3num, + u8 protonum); -extern int nf_conntrack_helper_register(struct nf_conntrack_helper *); -extern void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); +int nf_conntrack_helper_register(struct nf_conntrack_helper *); +void nf_conntrack_helper_unregister(struct nf_conntrack_helper *); -extern struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, struct nf_conntrack_helper *helper, gfp_t gfp); +struct nf_conn_help *nf_ct_helper_ext_add(struct nf_conn *ct, + struct nf_conntrack_helper *helper, + gfp_t gfp); -extern int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, - gfp_t flags); +int __nf_ct_try_assign_helper(struct nf_conn *ct, struct nf_conn *tmpl, + gfp_t flags); -extern void nf_ct_helper_destroy(struct nf_conn *ct); +void nf_ct_helper_destroy(struct nf_conn *ct); static inline struct nf_conn_help *nfct_help(const struct nf_conn *ct) { @@ -82,17 +85,16 @@ static inline void *nfct_help_data(const struct nf_conn *ct) return (void *)help->data; } -extern int nf_conntrack_helper_pernet_init(struct net *net); -extern void nf_conntrack_helper_pernet_fini(struct net *net); +int nf_conntrack_helper_pernet_init(struct net *net); +void nf_conntrack_helper_pernet_fini(struct net *net); -extern int nf_conntrack_helper_init(void); -extern void nf_conntrack_helper_fini(void); +int nf_conntrack_helper_init(void); +void nf_conntrack_helper_fini(void); -extern int nf_conntrack_broadcast_help(struct sk_buff *skb, - unsigned int protoff, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int timeout); +int nf_conntrack_broadcast_help(struct sk_buff *skb, unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int timeout); struct nf_ct_helper_expectfn { struct list_head head; diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index 3bb89eac3fa130a477b925d87c441189d27836e1..3efab704b7eb9b303b433549bb342d923e43f67f 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -77,17 +77,17 @@ struct nf_conntrack_l3proto { extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[AF_MAX]; /* Protocol pernet registration. */ -extern int nf_ct_l3proto_pernet_register(struct net *net, - struct nf_conntrack_l3proto *proto); -extern void nf_ct_l3proto_pernet_unregister(struct net *net, - struct nf_conntrack_l3proto *proto); +int nf_ct_l3proto_pernet_register(struct net *net, + struct nf_conntrack_l3proto *proto); +void nf_ct_l3proto_pernet_unregister(struct net *net, + struct nf_conntrack_l3proto *proto); /* Protocol global registration. */ -extern int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto); -extern void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto); +int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto); +void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto); -extern struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto); -extern void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p); +struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto); +void nf_ct_l3proto_put(struct nf_conntrack_l3proto *p); /* Existing built-in protocols */ extern struct nf_conntrack_l3proto nf_conntrack_l3proto_generic; diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index b411d7b17dec40a64cc41df315f6188004ac65fa..4c8d573830b7e606098fbd8fa2b5e483111601b0 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -114,22 +114,22 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic; #define MAX_NF_CT_PROTO 256 -extern struct nf_conntrack_l4proto * -__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto); +struct nf_conntrack_l4proto *__nf_ct_l4proto_find(u_int16_t l3proto, + u_int8_t l4proto); -extern struct nf_conntrack_l4proto * -nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto); -extern void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p); +struct nf_conntrack_l4proto *nf_ct_l4proto_find_get(u_int16_t l3proto, + u_int8_t l4proto); +void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p); /* Protocol pernet registration. */ -extern int nf_ct_l4proto_pernet_register(struct net *net, - struct nf_conntrack_l4proto *proto); -extern void nf_ct_l4proto_pernet_unregister(struct net *net, - struct nf_conntrack_l4proto *proto); +int nf_ct_l4proto_pernet_register(struct net *net, + struct nf_conntrack_l4proto *proto); +void nf_ct_l4proto_pernet_unregister(struct net *net, + struct nf_conntrack_l4proto *proto); /* Protocol global registration. */ -extern int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto); -extern void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto); +int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto); +void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto); static inline void nf_ct_kfree_compat_sysctl_table(struct nf_proto_net *pn) { @@ -140,11 +140,11 @@ static inline void nf_ct_kfree_compat_sysctl_table(struct nf_proto_net *pn) } /* Generic netlink helpers */ -extern int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb, - const struct nf_conntrack_tuple *tuple); -extern int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], - struct nf_conntrack_tuple *t); -extern int nf_ct_port_nlattr_tuple_size(void); +int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb, + const struct nf_conntrack_tuple *tuple); +int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[], + struct nf_conntrack_tuple *t); +int nf_ct_port_nlattr_tuple_size(void); extern const struct nla_policy nf_ct_port_nla_policy[]; #ifdef CONFIG_SYSCTL diff --git a/include/net/netfilter/nf_conntrack_seqadj.h b/include/net/netfilter/nf_conntrack_seqadj.h index f6177a5fe0cafe6de0e00ac6d2abd1ce9856d897..4b3362991a25f9c9f09b9794c96a0d0e15e89be9 100644 --- a/include/net/netfilter/nf_conntrack_seqadj.h +++ b/include/net/netfilter/nf_conntrack_seqadj.h @@ -30,22 +30,18 @@ static inline struct nf_conn_seqadj *nfct_seqadj_ext_add(struct nf_conn *ct) return nf_ct_ext_add(ct, NF_CT_EXT_SEQADJ, GFP_ATOMIC); } -extern int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, - s32 off); -extern int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, - __be32 seq, s32 off); -extern void nf_ct_tcp_seqadj_set(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - s32 off); - -extern int nf_ct_seq_adjust(struct sk_buff *skb, - struct nf_conn *ct, enum ip_conntrack_info ctinfo, - unsigned int protoff); -extern s32 nf_ct_seq_offset(const struct nf_conn *ct, enum ip_conntrack_dir, - u32 seq); - -extern int nf_conntrack_seqadj_init(void); -extern void nf_conntrack_seqadj_fini(void); +int nf_ct_seqadj_init(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + s32 off); +int nf_ct_seqadj_set(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + __be32 seq, s32 off); +void nf_ct_tcp_seqadj_set(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, s32 off); + +int nf_ct_seq_adjust(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, unsigned int protoff); +s32 nf_ct_seq_offset(const struct nf_conn *ct, enum ip_conntrack_dir, u32 seq); + +int nf_conntrack_seqadj_init(void); +void nf_conntrack_seqadj_fini(void); #endif /* _NF_CONNTRACK_SEQADJ_H */ diff --git a/include/net/netfilter/nf_conntrack_synproxy.h b/include/net/netfilter/nf_conntrack_synproxy.h index f572f313d6f1e0d7ae61f345b9aeb15a7dfea0a2..6793614e6502a0eb51ca7efabc593e7bb6866576 100644 --- a/include/net/netfilter/nf_conntrack_synproxy.h +++ b/include/net/netfilter/nf_conntrack_synproxy.h @@ -56,22 +56,20 @@ struct synproxy_options { struct tcphdr; struct xt_synproxy_info; -extern bool synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, - const struct tcphdr *th, - struct synproxy_options *opts); -extern unsigned int synproxy_options_size(const struct synproxy_options *opts); -extern void synproxy_build_options(struct tcphdr *th, - const struct synproxy_options *opts); +bool synproxy_parse_options(const struct sk_buff *skb, unsigned int doff, + const struct tcphdr *th, + struct synproxy_options *opts); +unsigned int synproxy_options_size(const struct synproxy_options *opts); +void synproxy_build_options(struct tcphdr *th, + const struct synproxy_options *opts); -extern void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info, - struct synproxy_options *opts); -extern void synproxy_check_timestamp_cookie(struct synproxy_options *opts); +void synproxy_init_timestamp_cookie(const struct xt_synproxy_info *info, + struct synproxy_options *opts); +void synproxy_check_timestamp_cookie(struct synproxy_options *opts); -extern unsigned int synproxy_tstamp_adjust(struct sk_buff *skb, - unsigned int protoff, - struct tcphdr *th, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - const struct nf_conn_synproxy *synproxy); +unsigned int synproxy_tstamp_adjust(struct sk_buff *skb, unsigned int protoff, + struct tcphdr *th, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + const struct nf_conn_synproxy *synproxy); #endif /* _NF_CONNTRACK_SYNPROXY_H */ diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index d23aceb16d9443a865bc0e789a60089b0d9c40bc..62308713dd7fa1704409e7db22fca725cc0200b9 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -76,8 +76,8 @@ nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct, } #ifdef CONFIG_NF_CONNTRACK_TIMEOUT -extern int nf_conntrack_timeout_init(void); -extern void nf_conntrack_timeout_fini(void); +int nf_conntrack_timeout_init(void); +void nf_conntrack_timeout_fini(void); #else static inline int nf_conntrack_timeout_init(void) { diff --git a/include/net/netfilter/nf_conntrack_timestamp.h b/include/net/netfilter/nf_conntrack_timestamp.h index b00461413efd4d49ec3e11ba4a3e0786594a32f2..300ae2209f251e7e669c8c353b8de7fb50b16f55 100644 --- a/include/net/netfilter/nf_conntrack_timestamp.h +++ b/include/net/netfilter/nf_conntrack_timestamp.h @@ -48,11 +48,11 @@ static inline void nf_ct_set_tstamp(struct net *net, bool enable) } #ifdef CONFIG_NF_CONNTRACK_TIMESTAMP -extern int nf_conntrack_tstamp_pernet_init(struct net *net); -extern void nf_conntrack_tstamp_pernet_fini(struct net *net); +int nf_conntrack_tstamp_pernet_init(struct net *net); +void nf_conntrack_tstamp_pernet_fini(struct net *net); -extern int nf_conntrack_tstamp_init(void); -extern void nf_conntrack_tstamp_fini(void); +int nf_conntrack_tstamp_init(void); +void nf_conntrack_tstamp_fini(void); #else static inline int nf_conntrack_tstamp_pernet_init(struct net *net) { diff --git a/include/net/netfilter/nf_nat.h b/include/net/netfilter/nf_nat.h index 59a1924200536b9fc5847998b7129c8aefeb7060..07eaaf60409215198961cea9834c2d770a90f02e 100644 --- a/include/net/netfilter/nf_nat.h +++ b/include/net/netfilter/nf_nat.h @@ -41,13 +41,16 @@ struct nf_conn_nat { }; /* Set up the info structure to map into this range. */ -extern unsigned int nf_nat_setup_info(struct nf_conn *ct, - const struct nf_nat_range *range, - enum nf_nat_manip_type maniptype); +unsigned int nf_nat_setup_info(struct nf_conn *ct, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype); + +extern unsigned int nf_nat_alloc_null_binding(struct nf_conn *ct, + unsigned int hooknum); /* Is this tuple already taken? (not by us)*/ -extern int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, - const struct nf_conn *ignored_conntrack); +int nf_nat_used_tuple(const struct nf_conntrack_tuple *tuple, + const struct nf_conn *ignored_conntrack); static inline struct nf_conn_nat *nfct_nat(const struct nf_conn *ct) { diff --git a/include/net/netfilter/nf_nat_core.h b/include/net/netfilter/nf_nat_core.h index 972e1e47ec79819f610cf9081be732b9131aa46a..fbfd1ba4254e0c356ca382626cae9d7653e8295e 100644 --- a/include/net/netfilter/nf_nat_core.h +++ b/include/net/netfilter/nf_nat_core.h @@ -7,12 +7,10 @@ /* This header used to share core functionality between the standalone NAT module, and the compatibility layer's use of NAT for masquerading. */ -extern unsigned int nf_nat_packet(struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - struct sk_buff *skb); +unsigned int nf_nat_packet(struct nf_conn *ct, enum ip_conntrack_info ctinfo, + unsigned int hooknum, struct sk_buff *skb); -extern int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family); +int nf_xfrm_me_harder(struct sk_buff *skb, unsigned int family); static inline int nf_nat_initialized(struct nf_conn *ct, enum nf_nat_manip_type manip) diff --git a/include/net/netfilter/nf_nat_helper.h b/include/net/netfilter/nf_nat_helper.h index 404324d1d0c4c2a07cb14352ec8623a53b08b541..01bcc6bfbcc9034e399a938a6a6dd83b2bed6ef7 100644 --- a/include/net/netfilter/nf_nat_helper.h +++ b/include/net/netfilter/nf_nat_helper.h @@ -7,14 +7,11 @@ struct sk_buff; /* These return true or false. */ -extern int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len, bool adjust); +int __nf_nat_mangle_tcp_packet(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int match_offset, + unsigned int match_len, const char *rep_buffer, + unsigned int rep_len, bool adjust); static inline int nf_nat_mangle_tcp_packet(struct sk_buff *skb, struct nf_conn *ct, @@ -30,18 +27,14 @@ static inline int nf_nat_mangle_tcp_packet(struct sk_buff *skb, rep_buffer, rep_len, true); } -extern int nf_nat_mangle_udp_packet(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int match_offset, - unsigned int match_len, - const char *rep_buffer, - unsigned int rep_len); +int nf_nat_mangle_udp_packet(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int protoff, unsigned int match_offset, + unsigned int match_len, const char *rep_buffer, + unsigned int rep_len); /* Setup NAT on this expected conntrack so it follows master, but goes * to port ct->master->saved_proto. */ -extern void nf_nat_follow_master(struct nf_conn *ct, - struct nf_conntrack_expect *this); +void nf_nat_follow_master(struct nf_conn *ct, struct nf_conntrack_expect *this); #endif diff --git a/include/net/netfilter/nf_nat_l3proto.h b/include/net/netfilter/nf_nat_l3proto.h index bd3b97e02c82a86eaf8aafc20a3a94b71830fa1f..5a2919b2e09af396540348bad01f12b7deab5ef9 100644 --- a/include/net/netfilter/nf_nat_l3proto.h +++ b/include/net/netfilter/nf_nat_l3proto.h @@ -35,18 +35,15 @@ struct nf_nat_l3proto { struct nf_nat_range *range); }; -extern int nf_nat_l3proto_register(const struct nf_nat_l3proto *); -extern void nf_nat_l3proto_unregister(const struct nf_nat_l3proto *); -extern const struct nf_nat_l3proto *__nf_nat_l3proto_find(u8 l3proto); - -extern int nf_nat_icmp_reply_translation(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum); -extern int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo, - unsigned int hooknum, - unsigned int hdrlen); +int nf_nat_l3proto_register(const struct nf_nat_l3proto *); +void nf_nat_l3proto_unregister(const struct nf_nat_l3proto *); +const struct nf_nat_l3proto *__nf_nat_l3proto_find(u8 l3proto); + +int nf_nat_icmp_reply_translation(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int hooknum); +int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conn *ct, + enum ip_conntrack_info ctinfo, + unsigned int hooknum, unsigned int hdrlen); #endif /* _NF_NAT_L3PROTO_H */ diff --git a/include/net/netfilter/nf_nat_l4proto.h b/include/net/netfilter/nf_nat_l4proto.h index 24feb68d1bccd6698be75deaa15f8bb91a7bf0d8..12f4cc841b6eddba6bdfc4132e448c31781b4e84 100644 --- a/include/net/netfilter/nf_nat_l4proto.h +++ b/include/net/netfilter/nf_nat_l4proto.h @@ -42,10 +42,11 @@ struct nf_nat_l4proto { }; /* Protocol registration. */ -extern int nf_nat_l4proto_register(u8 l3proto, const struct nf_nat_l4proto *l4proto); -extern void nf_nat_l4proto_unregister(u8 l3proto, const struct nf_nat_l4proto *l4proto); +int nf_nat_l4proto_register(u8 l3proto, const struct nf_nat_l4proto *l4proto); +void nf_nat_l4proto_unregister(u8 l3proto, + const struct nf_nat_l4proto *l4proto); -extern const struct nf_nat_l4proto *__nf_nat_l4proto_find(u8 l3proto, u8 l4proto); +const struct nf_nat_l4proto *__nf_nat_l4proto_find(u8 l3proto, u8 l4proto); /* Built-in protocols. */ extern const struct nf_nat_l4proto nf_nat_l4proto_tcp; @@ -54,19 +55,18 @@ extern const struct nf_nat_l4proto nf_nat_l4proto_icmp; extern const struct nf_nat_l4proto nf_nat_l4proto_icmpv6; extern const struct nf_nat_l4proto nf_nat_l4proto_unknown; -extern bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, - enum nf_nat_manip_type maniptype, - const union nf_conntrack_man_proto *min, - const union nf_conntrack_man_proto *max); +bool nf_nat_l4proto_in_range(const struct nf_conntrack_tuple *tuple, + enum nf_nat_manip_type maniptype, + const union nf_conntrack_man_proto *min, + const union nf_conntrack_man_proto *max); -extern void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto, - struct nf_conntrack_tuple *tuple, - const struct nf_nat_range *range, - enum nf_nat_manip_type maniptype, - const struct nf_conn *ct, - u16 *rover); +void nf_nat_l4proto_unique_tuple(const struct nf_nat_l3proto *l3proto, + struct nf_conntrack_tuple *tuple, + const struct nf_nat_range *range, + enum nf_nat_manip_type maniptype, + const struct nf_conn *ct, u16 *rover); -extern int nf_nat_l4proto_nlattr_to_range(struct nlattr *tb[], - struct nf_nat_range *range); +int nf_nat_l4proto_nlattr_to_range(struct nlattr *tb[], + struct nf_nat_range *range); #endif /*_NF_NAT_L4PROTO_H*/ diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index aaba4bbcdda0e9e7b4b7adfcb4c19e3555bb4219..c1d5b3e34a211a8df6fd4eba749b4fcd9e62131f 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -28,7 +28,7 @@ struct nf_queue_handler { void nf_register_queue_handler(const struct nf_queue_handler *qh); void nf_unregister_queue_handler(void); -extern void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict); +void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict); bool nf_queue_entry_get_refs(struct nf_queue_entry *entry); void nf_queue_entry_release_refs(struct nf_queue_entry *entry); diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h new file mode 100644 index 0000000000000000000000000000000000000000..5a91abfc0c30dec50c6f83c2d6568429a676c1c5 --- /dev/null +++ b/include/net/netfilter/nf_tables.h @@ -0,0 +1,519 @@ +#ifndef _NET_NF_TABLES_H +#define _NET_NF_TABLES_H + +#include +#include +#include +#include +#include + +#define NFT_JUMP_STACK_SIZE 16 + +struct nft_pktinfo { + struct sk_buff *skb; + const struct net_device *in; + const struct net_device *out; + u8 hooknum; + u8 nhoff; + u8 thoff; + /* for x_tables compatibility */ + struct xt_action_param xt; +}; + +static inline void nft_set_pktinfo(struct nft_pktinfo *pkt, + const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out) +{ + pkt->skb = skb; + pkt->in = pkt->xt.in = in; + pkt->out = pkt->xt.out = out; + pkt->hooknum = pkt->xt.hooknum = ops->hooknum; + pkt->xt.family = ops->pf; +} + +struct nft_data { + union { + u32 data[4]; + struct { + u32 verdict; + struct nft_chain *chain; + }; + }; +} __attribute__((aligned(__alignof__(u64)))); + +static inline int nft_data_cmp(const struct nft_data *d1, + const struct nft_data *d2, + unsigned int len) +{ + return memcmp(d1->data, d2->data, len); +} + +static inline void nft_data_copy(struct nft_data *dst, + const struct nft_data *src) +{ + BUILD_BUG_ON(__alignof__(*dst) != __alignof__(u64)); + *(u64 *)&dst->data[0] = *(u64 *)&src->data[0]; + *(u64 *)&dst->data[2] = *(u64 *)&src->data[2]; +} + +static inline void nft_data_debug(const struct nft_data *data) +{ + pr_debug("data[0]=%x data[1]=%x data[2]=%x data[3]=%x\n", + data->data[0], data->data[1], + data->data[2], data->data[3]); +} + +/** + * struct nft_ctx - nf_tables rule/set context + * + * @net: net namespace + * @skb: netlink skb + * @nlh: netlink message header + * @afi: address family info + * @table: the table the chain is contained in + * @chain: the chain the rule is contained in + * @nla: netlink attributes + */ +struct nft_ctx { + struct net *net; + const struct sk_buff *skb; + const struct nlmsghdr *nlh; + const struct nft_af_info *afi; + const struct nft_table *table; + const struct nft_chain *chain; + const struct nlattr * const *nla; +}; + +struct nft_data_desc { + enum nft_data_types type; + unsigned int len; +}; + +int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data, + struct nft_data_desc *desc, const struct nlattr *nla); +void nft_data_uninit(const struct nft_data *data, enum nft_data_types type); +int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data, + enum nft_data_types type, unsigned int len); + +static inline enum nft_data_types nft_dreg_to_type(enum nft_registers reg) +{ + return reg == NFT_REG_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE; +} + +static inline enum nft_registers nft_type_to_reg(enum nft_data_types type) +{ + return type == NFT_DATA_VERDICT ? NFT_REG_VERDICT : NFT_REG_1; +} + +int nft_validate_input_register(enum nft_registers reg); +int nft_validate_output_register(enum nft_registers reg); +int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg, + const struct nft_data *data, + enum nft_data_types type); + +/** + * struct nft_set_elem - generic representation of set elements + * + * @cookie: implementation specific element cookie + * @key: element key + * @data: element data (maps only) + * @flags: element flags (end of interval) + * + * The cookie can be used to store a handle to the element for subsequent + * removal. + */ +struct nft_set_elem { + void *cookie; + struct nft_data key; + struct nft_data data; + u32 flags; +}; + +struct nft_set; +struct nft_set_iter { + unsigned int count; + unsigned int skip; + int err; + int (*fn)(const struct nft_ctx *ctx, + const struct nft_set *set, + const struct nft_set_iter *iter, + const struct nft_set_elem *elem); +}; + +/** + * struct nft_set_ops - nf_tables set operations + * + * @lookup: look up an element within the set + * @insert: insert new element into set + * @remove: remove element from set + * @walk: iterate over all set elemeennts + * @privsize: function to return size of set private data + * @init: initialize private data of new set instance + * @destroy: destroy private data of set instance + * @list: nf_tables_set_ops list node + * @owner: module reference + * @features: features supported by the implementation + */ +struct nft_set_ops { + bool (*lookup)(const struct nft_set *set, + const struct nft_data *key, + struct nft_data *data); + int (*get)(const struct nft_set *set, + struct nft_set_elem *elem); + int (*insert)(const struct nft_set *set, + const struct nft_set_elem *elem); + void (*remove)(const struct nft_set *set, + const struct nft_set_elem *elem); + void (*walk)(const struct nft_ctx *ctx, + const struct nft_set *set, + struct nft_set_iter *iter); + + unsigned int (*privsize)(const struct nlattr * const nla[]); + int (*init)(const struct nft_set *set, + const struct nlattr * const nla[]); + void (*destroy)(const struct nft_set *set); + + struct list_head list; + struct module *owner; + u32 features; +}; + +int nft_register_set(struct nft_set_ops *ops); +void nft_unregister_set(struct nft_set_ops *ops); + +/** + * struct nft_set - nf_tables set instance + * + * @list: table set list node + * @bindings: list of set bindings + * @name: name of the set + * @ktype: key type (numeric type defined by userspace, not used in the kernel) + * @dtype: data type (verdict or numeric type defined by userspace) + * @ops: set ops + * @flags: set flags + * @klen: key length + * @dlen: data length + * @data: private set data + */ +struct nft_set { + struct list_head list; + struct list_head bindings; + char name[IFNAMSIZ]; + u32 ktype; + u32 dtype; + /* runtime data below here */ + const struct nft_set_ops *ops ____cacheline_aligned; + u16 flags; + u8 klen; + u8 dlen; + unsigned char data[] + __attribute__((aligned(__alignof__(u64)))); +}; + +static inline void *nft_set_priv(const struct nft_set *set) +{ + return (void *)set->data; +} + +struct nft_set *nf_tables_set_lookup(const struct nft_table *table, + const struct nlattr *nla); + +/** + * struct nft_set_binding - nf_tables set binding + * + * @list: set bindings list node + * @chain: chain containing the rule bound to the set + * + * A set binding contains all information necessary for validation + * of new elements added to a bound set. + */ +struct nft_set_binding { + struct list_head list; + const struct nft_chain *chain; +}; + +int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding); +void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding); + + +/** + * struct nft_expr_type - nf_tables expression type + * + * @select_ops: function to select nft_expr_ops + * @ops: default ops, used when no select_ops functions is present + * @list: used internally + * @name: Identifier + * @owner: module reference + * @policy: netlink attribute policy + * @maxattr: highest netlink attribute number + */ +struct nft_expr_type { + const struct nft_expr_ops *(*select_ops)(const struct nft_ctx *, + const struct nlattr * const tb[]); + const struct nft_expr_ops *ops; + struct list_head list; + const char *name; + struct module *owner; + const struct nla_policy *policy; + unsigned int maxattr; +}; + +/** + * struct nft_expr_ops - nf_tables expression operations + * + * @eval: Expression evaluation function + * @size: full expression size, including private data size + * @init: initialization function + * @destroy: destruction function + * @dump: function to dump parameters + * @type: expression type + * @validate: validate expression, called during loop detection + * @data: extra data to attach to this expression operation + */ +struct nft_expr; +struct nft_expr_ops { + void (*eval)(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt); + unsigned int size; + + int (*init)(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]); + void (*destroy)(const struct nft_expr *expr); + int (*dump)(struct sk_buff *skb, + const struct nft_expr *expr); + int (*validate)(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data); + const struct nft_expr_type *type; + void *data; +}; + +#define NFT_EXPR_MAXATTR 16 +#define NFT_EXPR_SIZE(size) (sizeof(struct nft_expr) + \ + ALIGN(size, __alignof__(struct nft_expr))) + +/** + * struct nft_expr - nf_tables expression + * + * @ops: expression ops + * @data: expression private data + */ +struct nft_expr { + const struct nft_expr_ops *ops; + unsigned char data[]; +}; + +static inline void *nft_expr_priv(const struct nft_expr *expr) +{ + return (void *)expr->data; +} + +/** + * struct nft_rule - nf_tables rule + * + * @list: used internally + * @rcu_head: used internally for rcu + * @handle: rule handle + * @genmask: generation mask + * @dlen: length of expression data + * @data: expression data + */ +struct nft_rule { + struct list_head list; + struct rcu_head rcu_head; + u64 handle:46, + genmask:2, + dlen:16; + unsigned char data[] + __attribute__((aligned(__alignof__(struct nft_expr)))); +}; + +/** + * struct nft_rule_trans - nf_tables rule update in transaction + * + * @list: used internally + * @rule: rule that needs to be updated + * @chain: chain that this rule belongs to + * @table: table for which this chain applies + * @nlh: netlink header of the message that contain this update + * @family: family expressesed as AF_* + */ +struct nft_rule_trans { + struct list_head list; + struct nft_rule *rule; + const struct nft_chain *chain; + const struct nft_table *table; + const struct nlmsghdr *nlh; + u8 family; +}; + +static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) +{ + return (struct nft_expr *)&rule->data[0]; +} + +static inline struct nft_expr *nft_expr_next(const struct nft_expr *expr) +{ + return ((void *)expr) + expr->ops->size; +} + +static inline struct nft_expr *nft_expr_last(const struct nft_rule *rule) +{ + return (struct nft_expr *)&rule->data[rule->dlen]; +} + +/* + * The last pointer isn't really necessary, but the compiler isn't able to + * determine that the result of nft_expr_last() is always the same since it + * can't assume that the dlen value wasn't changed within calls in the loop. + */ +#define nft_rule_for_each_expr(expr, last, rule) \ + for ((expr) = nft_expr_first(rule), (last) = nft_expr_last(rule); \ + (expr) != (last); \ + (expr) = nft_expr_next(expr)) + +enum nft_chain_flags { + NFT_BASE_CHAIN = 0x1, +}; + +/** + * struct nft_chain - nf_tables chain + * + * @rules: list of rules in the chain + * @list: used internally + * @rcu_head: used internally + * @net: net namespace that this chain belongs to + * @table: table that this chain belongs to + * @handle: chain handle + * @flags: bitmask of enum nft_chain_flags + * @use: number of jump references to this chain + * @level: length of longest path to this chain + * @name: name of the chain + */ +struct nft_chain { + struct list_head rules; + struct list_head list; + struct rcu_head rcu_head; + struct net *net; + struct nft_table *table; + u64 handle; + u8 flags; + u16 use; + u16 level; + char name[NFT_CHAIN_MAXNAMELEN]; +}; + +enum nft_chain_type { + NFT_CHAIN_T_DEFAULT = 0, + NFT_CHAIN_T_ROUTE, + NFT_CHAIN_T_NAT, + NFT_CHAIN_T_MAX +}; + +struct nft_stats { + u64 bytes; + u64 pkts; +}; + +/** + * struct nft_base_chain - nf_tables base chain + * + * @ops: netfilter hook ops + * @type: chain type + * @policy: default policy + * @stats: per-cpu chain stats + * @chain: the chain + */ +struct nft_base_chain { + struct nf_hook_ops ops; + enum nft_chain_type type; + u8 policy; + struct nft_stats __percpu *stats; + struct nft_chain chain; +}; + +static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chain) +{ + return container_of(chain, struct nft_base_chain, chain); +} + +unsigned int nft_do_chain_pktinfo(struct nft_pktinfo *pkt, + const struct nf_hook_ops *ops); + +/** + * struct nft_table - nf_tables table + * + * @list: used internally + * @chains: chains in the table + * @sets: sets in the table + * @hgenerator: handle generator state + * @use: number of chain references to this table + * @flags: table flag (see enum nft_table_flags) + * @name: name of the table + */ +struct nft_table { + struct list_head list; + struct list_head chains; + struct list_head sets; + u64 hgenerator; + u32 use; + u16 flags; + char name[]; +}; + +/** + * struct nft_af_info - nf_tables address family info + * + * @list: used internally + * @family: address family + * @nhooks: number of hooks in this family + * @owner: module owner + * @tables: used internally + * @hooks: hookfn overrides for packet validation + */ +struct nft_af_info { + struct list_head list; + int family; + unsigned int nhooks; + struct module *owner; + struct list_head tables; + nf_hookfn *hooks[NF_MAX_HOOKS]; +}; + +int nft_register_afinfo(struct net *, struct nft_af_info *); +void nft_unregister_afinfo(struct nft_af_info *); + +struct nf_chain_type { + unsigned int hook_mask; + const char *name; + enum nft_chain_type type; + nf_hookfn *fn[NF_MAX_HOOKS]; + struct module *me; + int family; +}; + +int nft_register_chain_type(struct nf_chain_type *); +void nft_unregister_chain_type(struct nf_chain_type *); + +int nft_register_expr(struct nft_expr_type *); +void nft_unregister_expr(struct nft_expr_type *); + +#define MODULE_ALIAS_NFT_FAMILY(family) \ + MODULE_ALIAS("nft-afinfo-" __stringify(family)) + +#define MODULE_ALIAS_NFT_CHAIN(family, name) \ + MODULE_ALIAS("nft-chain-" __stringify(family) "-" name) + +#define MODULE_ALIAS_NFT_EXPR(name) \ + MODULE_ALIAS("nft-expr-" name) + +#define MODULE_ALIAS_NFT_SET() \ + MODULE_ALIAS("nft-set") + +#endif /* _NET_NF_TABLES_H */ diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h new file mode 100644 index 0000000000000000000000000000000000000000..cf2b7ae2b9d82b11cfa7a810130187e2c763881a --- /dev/null +++ b/include/net/netfilter/nf_tables_core.h @@ -0,0 +1,42 @@ +#ifndef _NET_NF_TABLES_CORE_H +#define _NET_NF_TABLES_CORE_H + +int nf_tables_core_module_init(void); +void nf_tables_core_module_exit(void); + +int nft_immediate_module_init(void); +void nft_immediate_module_exit(void); + +struct nft_cmp_fast_expr { + u32 data; + enum nft_registers sreg:8; + u8 len; +}; + +extern const struct nft_expr_ops nft_cmp_fast_ops; + +int nft_cmp_module_init(void); +void nft_cmp_module_exit(void); + +int nft_lookup_module_init(void); +void nft_lookup_module_exit(void); + +int nft_bitwise_module_init(void); +void nft_bitwise_module_exit(void); + +int nft_byteorder_module_init(void); +void nft_byteorder_module_exit(void); + +struct nft_payload { + enum nft_payload_bases base:8; + u8 offset; + u8 len; + enum nft_registers dreg:8; +}; + +extern const struct nft_expr_ops nft_payload_fast_ops; + +int nft_payload_module_init(void); +void nft_payload_module_exit(void); + +#endif /* _NET_NF_TABLES_CORE_H */ diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h new file mode 100644 index 0000000000000000000000000000000000000000..1be1c2c197ee2b0d8540bdb3469f8d6c0db4538e --- /dev/null +++ b/include/net/netfilter/nf_tables_ipv4.h @@ -0,0 +1,23 @@ +#ifndef _NF_TABLES_IPV4_H_ +#define _NF_TABLES_IPV4_H_ + +#include +#include + +static inline void +nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt, + const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out) +{ + struct iphdr *ip; + + nft_set_pktinfo(pkt, ops, skb, in, out); + + pkt->xt.thoff = ip_hdrlen(pkt->skb); + ip = ip_hdr(pkt->skb); + pkt->xt.fragoff = ntohs(ip->frag_off) & IP_OFFSET; +} + +#endif diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h new file mode 100644 index 0000000000000000000000000000000000000000..4a9b88a65963ee48d33589e2b544c95551c5f185 --- /dev/null +++ b/include/net/netfilter/nf_tables_ipv6.h @@ -0,0 +1,30 @@ +#ifndef _NF_TABLES_IPV6_H_ +#define _NF_TABLES_IPV6_H_ + +#include +#include + +static inline int +nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt, + const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out) +{ + int protohdr, thoff = 0; + unsigned short frag_off; + + nft_set_pktinfo(pkt, ops, skb, in, out); + + protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL); + /* If malformed, drop it */ + if (protohdr < 0) + return -1; + + pkt->xt.thoff = thoff; + pkt->xt.fragoff = frag_off; + + return 0; +} + +#endif diff --git a/include/net/netfilter/xt_rateest.h b/include/net/netfilter/xt_rateest.h index 495c71f66e7e9334b3506174dc7e5352399938f4..79f45e19f31eaa54deb8437ef4b883bec78def84 100644 --- a/include/net/netfilter/xt_rateest.h +++ b/include/net/netfilter/xt_rateest.h @@ -16,7 +16,7 @@ struct xt_rateest { struct rcu_head rcu; }; -extern struct xt_rateest *xt_rateest_lookup(const char *name); -extern void xt_rateest_put(struct xt_rateest *est); +struct xt_rateest *xt_rateest_lookup(const char *name); +void xt_rateest_put(struct xt_rateest *est); #endif /* _XT_RATEEST_H */ diff --git a/include/net/netlink.h b/include/net/netlink.h index 9690b0f6698a1d1b433ea572bdc4efb938f501c5..2b47eaadba8fad95b9bde8aeeace37fa03d1fc31 100644 --- a/include/net/netlink.h +++ b/include/net/netlink.h @@ -225,44 +225,31 @@ struct nl_info { u32 portid; }; -extern int netlink_rcv_skb(struct sk_buff *skb, - int (*cb)(struct sk_buff *, - struct nlmsghdr *)); -extern int nlmsg_notify(struct sock *sk, struct sk_buff *skb, - u32 portid, unsigned int group, int report, - gfp_t flags); - -extern int nla_validate(const struct nlattr *head, - int len, int maxtype, - const struct nla_policy *policy); -extern int nla_parse(struct nlattr **tb, int maxtype, - const struct nlattr *head, int len, - const struct nla_policy *policy); -extern int nla_policy_len(const struct nla_policy *, int); -extern struct nlattr * nla_find(const struct nlattr *head, - int len, int attrtype); -extern size_t nla_strlcpy(char *dst, const struct nlattr *nla, - size_t dstsize); -extern int nla_memcpy(void *dest, const struct nlattr *src, int count); -extern int nla_memcmp(const struct nlattr *nla, const void *data, - size_t size); -extern int nla_strcmp(const struct nlattr *nla, const char *str); -extern struct nlattr * __nla_reserve(struct sk_buff *skb, int attrtype, - int attrlen); -extern void * __nla_reserve_nohdr(struct sk_buff *skb, int attrlen); -extern struct nlattr * nla_reserve(struct sk_buff *skb, int attrtype, - int attrlen); -extern void * nla_reserve_nohdr(struct sk_buff *skb, int attrlen); -extern void __nla_put(struct sk_buff *skb, int attrtype, - int attrlen, const void *data); -extern void __nla_put_nohdr(struct sk_buff *skb, int attrlen, - const void *data); -extern int nla_put(struct sk_buff *skb, int attrtype, - int attrlen, const void *data); -extern int nla_put_nohdr(struct sk_buff *skb, int attrlen, - const void *data); -extern int nla_append(struct sk_buff *skb, int attrlen, - const void *data); +int netlink_rcv_skb(struct sk_buff *skb, + int (*cb)(struct sk_buff *, struct nlmsghdr *)); +int nlmsg_notify(struct sock *sk, struct sk_buff *skb, u32 portid, + unsigned int group, int report, gfp_t flags); + +int nla_validate(const struct nlattr *head, int len, int maxtype, + const struct nla_policy *policy); +int nla_parse(struct nlattr **tb, int maxtype, const struct nlattr *head, + int len, const struct nla_policy *policy); +int nla_policy_len(const struct nla_policy *, int); +struct nlattr *nla_find(const struct nlattr *head, int len, int attrtype); +size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize); +int nla_memcpy(void *dest, const struct nlattr *src, int count); +int nla_memcmp(const struct nlattr *nla, const void *data, size_t size); +int nla_strcmp(const struct nlattr *nla, const char *str); +struct nlattr *__nla_reserve(struct sk_buff *skb, int attrtype, int attrlen); +void *__nla_reserve_nohdr(struct sk_buff *skb, int attrlen); +struct nlattr *nla_reserve(struct sk_buff *skb, int attrtype, int attrlen); +void *nla_reserve_nohdr(struct sk_buff *skb, int attrlen); +void __nla_put(struct sk_buff *skb, int attrtype, int attrlen, + const void *data); +void __nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data); +int nla_put(struct sk_buff *skb, int attrtype, int attrlen, const void *data); +int nla_put_nohdr(struct sk_buff *skb, int attrlen, const void *data); +int nla_append(struct sk_buff *skb, int attrlen, const void *data); /************************************************************************** * Netlink Messages diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index bf2ec2202c5698b1bb73e22df60b4a3bc9d95c01..ee520cba2ec2f9f1ce1f011b985524946085fb60 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -15,6 +15,10 @@ struct fib_rules_ops; struct hlist_head; struct fib_table; struct sock; +struct local_ports { + seqlock_t lock; + int range[2]; +}; struct netns_ipv4 { #ifdef CONFIG_SYSCTL @@ -62,10 +66,11 @@ struct netns_ipv4 { int sysctl_icmp_ratemask; int sysctl_icmp_errors_use_inbound_ifaddr; + struct local_ports sysctl_local_ports; + int sysctl_tcp_ecn; kgid_t sysctl_ping_group_range[2]; - long sysctl_tcp_mem[3]; atomic_t dev_addr_genid; diff --git a/include/net/netns/nftables.h b/include/net/netns/nftables.h new file mode 100644 index 0000000000000000000000000000000000000000..15d056d534e3ba08a71cb4c796d3f98655161fce --- /dev/null +++ b/include/net/netns/nftables.h @@ -0,0 +1,19 @@ +#ifndef _NETNS_NFTABLES_H_ +#define _NETNS_NFTABLES_H_ + +#include + +struct nft_af_info; + +struct netns_nftables { + struct list_head af_info; + struct list_head commit_list; + struct nft_af_info *ipv4; + struct nft_af_info *ipv6; + struct nft_af_info *arp; + struct nft_af_info *bridge; + u8 gencursor; + u8 genctr; +}; + +#endif diff --git a/include/net/netrom.h b/include/net/netrom.h index 121dcf854db54ea418aff35a05677013d05e53bc..110350aca3dfe1c846e05f3f9a61e5cc8afe64d4 100644 --- a/include/net/netrom.h +++ b/include/net/netrom.h @@ -183,51 +183,50 @@ extern int sysctl_netrom_routing_control; extern int sysctl_netrom_link_fails_count; extern int sysctl_netrom_reset_circuit; -extern int nr_rx_frame(struct sk_buff *, struct net_device *); -extern void nr_destroy_socket(struct sock *); +int nr_rx_frame(struct sk_buff *, struct net_device *); +void nr_destroy_socket(struct sock *); /* nr_dev.c */ -extern int nr_rx_ip(struct sk_buff *, struct net_device *); -extern void nr_setup(struct net_device *); +int nr_rx_ip(struct sk_buff *, struct net_device *); +void nr_setup(struct net_device *); /* nr_in.c */ -extern int nr_process_rx_frame(struct sock *, struct sk_buff *); +int nr_process_rx_frame(struct sock *, struct sk_buff *); /* nr_loopback.c */ -extern void nr_loopback_init(void); -extern void nr_loopback_clear(void); -extern int nr_loopback_queue(struct sk_buff *); +void nr_loopback_init(void); +void nr_loopback_clear(void); +int nr_loopback_queue(struct sk_buff *); /* nr_out.c */ -extern void nr_output(struct sock *, struct sk_buff *); -extern void nr_send_nak_frame(struct sock *); -extern void nr_kick(struct sock *); -extern void nr_transmit_buffer(struct sock *, struct sk_buff *); -extern void nr_establish_data_link(struct sock *); -extern void nr_enquiry_response(struct sock *); -extern void nr_check_iframes_acked(struct sock *, unsigned short); +void nr_output(struct sock *, struct sk_buff *); +void nr_send_nak_frame(struct sock *); +void nr_kick(struct sock *); +void nr_transmit_buffer(struct sock *, struct sk_buff *); +void nr_establish_data_link(struct sock *); +void nr_enquiry_response(struct sock *); +void nr_check_iframes_acked(struct sock *, unsigned short); /* nr_route.c */ -extern void nr_rt_device_down(struct net_device *); -extern struct net_device *nr_dev_first(void); -extern struct net_device *nr_dev_get(ax25_address *); -extern int nr_rt_ioctl(unsigned int, void __user *); -extern void nr_link_failed(ax25_cb *, int); -extern int nr_route_frame(struct sk_buff *, ax25_cb *); +void nr_rt_device_down(struct net_device *); +struct net_device *nr_dev_first(void); +struct net_device *nr_dev_get(ax25_address *); +int nr_rt_ioctl(unsigned int, void __user *); +void nr_link_failed(ax25_cb *, int); +int nr_route_frame(struct sk_buff *, ax25_cb *); extern const struct file_operations nr_nodes_fops; extern const struct file_operations nr_neigh_fops; -extern void nr_rt_free(void); +void nr_rt_free(void); /* nr_subr.c */ -extern void nr_clear_queues(struct sock *); -extern void nr_frames_acked(struct sock *, unsigned short); -extern void nr_requeue_frames(struct sock *); -extern int nr_validate_nr(struct sock *, unsigned short); -extern int nr_in_rx_window(struct sock *, unsigned short); -extern void nr_write_internal(struct sock *, int); +void nr_clear_queues(struct sock *); +void nr_frames_acked(struct sock *, unsigned short); +void nr_requeue_frames(struct sock *); +int nr_validate_nr(struct sock *, unsigned short); +int nr_in_rx_window(struct sock *, unsigned short); +void nr_write_internal(struct sock *, int); -extern void __nr_transmit_reply(struct sk_buff *skb, int mine, - unsigned char cmdflags); +void __nr_transmit_reply(struct sk_buff *skb, int mine, unsigned char cmdflags); /* * This routine is called when a Connect Acknowledge with the Choke Flag @@ -247,24 +246,24 @@ do { \ __nr_transmit_reply((skb), (mine), NR_RESET); \ } while (0) -extern void nr_disconnect(struct sock *, int); +void nr_disconnect(struct sock *, int); /* nr_timer.c */ -extern void nr_init_timers(struct sock *sk); -extern void nr_start_heartbeat(struct sock *); -extern void nr_start_t1timer(struct sock *); -extern void nr_start_t2timer(struct sock *); -extern void nr_start_t4timer(struct sock *); -extern void nr_start_idletimer(struct sock *); -extern void nr_stop_heartbeat(struct sock *); -extern void nr_stop_t1timer(struct sock *); -extern void nr_stop_t2timer(struct sock *); -extern void nr_stop_t4timer(struct sock *); -extern void nr_stop_idletimer(struct sock *); -extern int nr_t1timer_running(struct sock *); +void nr_init_timers(struct sock *sk); +void nr_start_heartbeat(struct sock *); +void nr_start_t1timer(struct sock *); +void nr_start_t2timer(struct sock *); +void nr_start_t4timer(struct sock *); +void nr_start_idletimer(struct sock *); +void nr_stop_heartbeat(struct sock *); +void nr_stop_t1timer(struct sock *); +void nr_stop_t2timer(struct sock *); +void nr_stop_t4timer(struct sock *); +void nr_stop_idletimer(struct sock *); +int nr_t1timer_running(struct sock *); /* sysctl_net_netrom.c */ -extern void nr_register_sysctl(void); -extern void nr_unregister_sysctl(void); +void nr_register_sysctl(void); +void nr_unregister_sysctl(void); #endif diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h new file mode 100644 index 0000000000000000000000000000000000000000..36acecd5f06c17475d1cf21ff571155b2aaf0221 --- /dev/null +++ b/include/net/nfc/digital.h @@ -0,0 +1,227 @@ +/* + * NFC Digital Protocol stack + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __NFC_DIGITAL_H +#define __NFC_DIGITAL_H + +#include +#include + +/** + * Configuration types for in_configure_hw and tg_configure_hw. + */ +enum { + NFC_DIGITAL_CONFIG_RF_TECH = 0, + NFC_DIGITAL_CONFIG_FRAMING, +}; + +/** + * RF technology values passed as param argument to in_configure_hw and + * tg_configure_hw for NFC_DIGITAL_CONFIG_RF_TECH configuration type. + */ +enum { + NFC_DIGITAL_RF_TECH_106A = 0, + NFC_DIGITAL_RF_TECH_212F, + NFC_DIGITAL_RF_TECH_424F, + + NFC_DIGITAL_RF_TECH_LAST, +}; + +/** + * Framing configuration passed as param argument to in_configure_hw and + * tg_configure_hw for NFC_DIGITAL_CONFIG_FRAMING configuration type. + */ +enum { + NFC_DIGITAL_FRAMING_NFCA_SHORT = 0, + NFC_DIGITAL_FRAMING_NFCA_STANDARD, + NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A, + + NFC_DIGITAL_FRAMING_NFCA_T1T, + NFC_DIGITAL_FRAMING_NFCA_T2T, + NFC_DIGITAL_FRAMING_NFCA_NFC_DEP, + + NFC_DIGITAL_FRAMING_NFCF, + NFC_DIGITAL_FRAMING_NFCF_T3T, + NFC_DIGITAL_FRAMING_NFCF_NFC_DEP, + NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED, + + NFC_DIGITAL_FRAMING_LAST, +}; + +#define DIGITAL_MDAA_NFCID1_SIZE 3 + +struct digital_tg_mdaa_params { + u16 sens_res; + u8 nfcid1[DIGITAL_MDAA_NFCID1_SIZE]; + u8 sel_res; + + u8 nfcid2[NFC_NFCID2_MAXSIZE]; + u16 sc; +}; + +struct nfc_digital_dev; + +/** + * nfc_digital_cmd_complete_t - Definition of command result callback + * + * @ddev: nfc_digital_device ref + * @arg: user data + * @resp: response data + * + * resp pointer can be an error code and will be checked with IS_ERR() macro. + * The callback is responsible for freeing resp sk_buff. + */ +typedef void (*nfc_digital_cmd_complete_t)(struct nfc_digital_dev *ddev, + void *arg, struct sk_buff *resp); + +/** + * Device side NFC Digital operations + * + * Initiator mode: + * @in_configure_hw: Hardware configuration for RF technology and communication + * framing in initiator mode. This is a synchronous function. + * @in_send_cmd: Initiator mode data exchange using RF technology and framing + * previously set with in_configure_hw. The peer response is returned + * through callback cb. If an io error occurs or the peer didn't reply + * within the specified timeout (ms), the error code is passed back through + * the resp pointer. This is an asynchronous function. + * + * Target mode: Only NFC-DEP protocol is supported in target mode. + * @tg_configure_hw: Hardware configuration for RF technology and communication + * framing in target mode. This is a synchronous function. + * @tg_send_cmd: Target mode data exchange using RF technology and framing + * previously set with tg_configure_hw. The peer next command is returned + * through callback cb. If an io error occurs or the peer didn't reply + * within the specified timeout (ms), the error code is passed back through + * the resp pointer. This is an asynchronous function. + * @tg_listen: Put the device in listen mode waiting for data from the peer + * device. This is an asynchronous function. + * @tg_listen_mdaa: If supported, put the device in automatic listen mode with + * mode detection and automatic anti-collision. In this mode, the device + * automatically detects the RF technology and executes the anti-collision + * detection using the command responses specified in mdaa_params. The + * mdaa_params structure contains SENS_RES, NFCID1, and SEL_RES for 106A RF + * tech. NFCID2 and system code (sc) for 212F and 424F. The driver returns + * the NFC-DEP ATR_REQ command through cb. The digital stack deducts the RF + * tech by analyzing the SoD of the frame containing the ATR_REQ command. + * This is an asynchronous function. + * + * @switch_rf: Turns device radio on or off. The stack does not call explicitly + * switch_rf to turn the radio on. A call to in|tg_configure_hw must turn + * the device radio on. + * @abort_cmd: Discard the last sent command. + */ +struct nfc_digital_ops { + int (*in_configure_hw)(struct nfc_digital_dev *ddev, int type, + int param); + int (*in_send_cmd)(struct nfc_digital_dev *ddev, struct sk_buff *skb, + u16 timeout, nfc_digital_cmd_complete_t cb, + void *arg); + + int (*tg_configure_hw)(struct nfc_digital_dev *ddev, int type, + int param); + int (*tg_send_cmd)(struct nfc_digital_dev *ddev, struct sk_buff *skb, + u16 timeout, nfc_digital_cmd_complete_t cb, + void *arg); + int (*tg_listen)(struct nfc_digital_dev *ddev, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg); + int (*tg_listen_mdaa)(struct nfc_digital_dev *ddev, + struct digital_tg_mdaa_params *mdaa_params, + u16 timeout, nfc_digital_cmd_complete_t cb, + void *arg); + + int (*switch_rf)(struct nfc_digital_dev *ddev, bool on); + void (*abort_cmd)(struct nfc_digital_dev *ddev); +}; + +#define NFC_DIGITAL_POLL_MODE_COUNT_MAX 6 /* 106A, 212F, and 424F in & tg */ + +typedef int (*digital_poll_t)(struct nfc_digital_dev *ddev, u8 rf_tech); + +struct digital_poll_tech { + u8 rf_tech; + digital_poll_t poll_func; +}; + +/** + * Driver capabilities - bit mask made of the following values + * + * @NFC_DIGITAL_DRV_CAPS_IN_CRC: The driver handles CRC calculation in initiator + * mode. + * @NFC_DIGITAL_DRV_CAPS_TG_CRC: The driver handles CRC calculation in target + * mode. + */ +#define NFC_DIGITAL_DRV_CAPS_IN_CRC 0x0001 +#define NFC_DIGITAL_DRV_CAPS_TG_CRC 0x0002 + +struct nfc_digital_dev { + struct nfc_dev *nfc_dev; + struct nfc_digital_ops *ops; + + u32 protocols; + + int tx_headroom; + int tx_tailroom; + + u32 driver_capabilities; + void *driver_data; + + struct digital_poll_tech poll_techs[NFC_DIGITAL_POLL_MODE_COUNT_MAX]; + u8 poll_tech_count; + u8 poll_tech_index; + struct mutex poll_lock; + + struct work_struct cmd_work; + struct work_struct cmd_complete_work; + struct list_head cmd_queue; + struct mutex cmd_lock; + + struct work_struct poll_work; + + u8 curr_protocol; + u8 curr_rf_tech; + u8 curr_nfc_dep_pni; + + int (*skb_check_crc)(struct sk_buff *skb); + void (*skb_add_crc)(struct sk_buff *skb); +}; + +struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops, + __u32 supported_protocols, + __u32 driver_capabilities, + int tx_headroom, + int tx_tailroom); +void nfc_digital_free_device(struct nfc_digital_dev *ndev); +int nfc_digital_register_device(struct nfc_digital_dev *ndev); +void nfc_digital_unregister_device(struct nfc_digital_dev *ndev); + +static inline void nfc_digital_set_parent_dev(struct nfc_digital_dev *ndev, + struct device *dev) +{ + nfc_set_parent_dev(ndev->nfc_dev, dev); +} + +static inline void nfc_digital_set_drvdata(struct nfc_digital_dev *dev, + void *data) +{ + dev->driver_data = data; +} + +static inline void *nfc_digital_get_drvdata(struct nfc_digital_dev *dev) +{ + return dev->driver_data; +} + +#endif /* __NFC_DIGITAL_H */ diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index b64b7bce4b94302a8edb923bc54bc1e4b2c8b997..2eca2960ca9c54116bd187b6e1e18b70060f0282 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -24,12 +24,6 @@ #include -struct nfc_phy_ops { - int (*write)(void *dev_id, struct sk_buff *skb); - int (*enable)(void *dev_id); - void (*disable)(void *dev_id); -}; - struct nfc_hci_dev; struct nfc_hci_ops { diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h index 88785e5c6b2cf6d2c32a8af553259a9d21678025..e5aa5acafea0e0e176e83ce50d58d33529be4669 100644 --- a/include/net/nfc/nci.h +++ b/include/net/nfc/nci.h @@ -166,6 +166,10 @@ #define NCI_GID_NFCEE_MGMT 0x2 #define NCI_GID_PROPRIETARY 0xf +/* ----- NCI over SPI head/crc(tail) room needed for outgoing frames ----- */ +#define NCI_SPI_HDR_LEN 4 +#define NCI_SPI_CRC_LEN 2 + /* ---- NCI Packet structures ---- */ #define NCI_CTRL_HDR_SIZE 3 #define NCI_DATA_HDR_SIZE 3 diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index 99fc1f3a392af172b2c3d5b5b499898f5cfbae39..6126f1f992b40068923771cf0a7967395f88017e 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h @@ -207,19 +207,9 @@ int nci_to_errno(__u8 code); #define NCI_SPI_CRC_ENABLED 0x01 /* ----- NCI SPI structures ----- */ -struct nci_spi_dev; - -struct nci_spi_ops { - int (*open)(struct nci_spi_dev *ndev); - int (*close)(struct nci_spi_dev *ndev); - void (*assert_int)(struct nci_spi_dev *ndev); - void (*deassert_int)(struct nci_spi_dev *ndev); -}; - -struct nci_spi_dev { - struct nci_dev *nci_dev; +struct nci_spi { + struct nci_dev *ndev; struct spi_device *spi; - struct nci_spi_ops *ops; unsigned int xfer_udelay; /* microseconds delay between transactions */ @@ -227,31 +217,15 @@ struct nci_spi_dev { struct completion req_completion; u8 req_result; - - void *driver_data; }; -/* ----- NCI SPI Devices ----- */ -struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi, - struct nci_spi_ops *ops, - u32 supported_protocols, - u32 supported_se, - u8 acknowledge_mode, - unsigned int delay); -void nci_spi_free_device(struct nci_spi_dev *ndev); -int nci_spi_register_device(struct nci_spi_dev *ndev); -void nci_spi_unregister_device(struct nci_spi_dev *ndev); -int nci_spi_recv_frame(struct nci_spi_dev *ndev); - -static inline void nci_spi_set_drvdata(struct nci_spi_dev *ndev, - void *data) -{ - ndev->driver_data = data; -} - -static inline void *nci_spi_get_drvdata(struct nci_spi_dev *ndev) -{ - return ndev->driver_data; -} +/* ----- NCI SPI ----- */ +struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi, + u8 acknowledge_mode, unsigned int delay, + struct nci_dev *ndev); +int nci_spi_send(struct nci_spi *nspi, + struct completion *write_handshake_completion, + struct sk_buff *skb); +struct sk_buff *nci_spi_read(struct nci_spi *nspi); #endif /* __NCI_CORE_H */ diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index f68ee68e4e3e97cd055bc8cc760ab6bba9a11a4b..82fc4e43fc6e9ec156e19e18dddc4d3ec1fbe037 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h @@ -28,9 +28,14 @@ #include #include -#define nfc_dev_info(dev, fmt, arg...) dev_info((dev), "NFC: " fmt "\n", ## arg) -#define nfc_dev_err(dev, fmt, arg...) dev_err((dev), "NFC: " fmt "\n", ## arg) -#define nfc_dev_dbg(dev, fmt, arg...) dev_dbg((dev), fmt "\n", ## arg) +#define nfc_info(dev, fmt, ...) dev_info((dev), "NFC: " fmt, ##__VA_ARGS__) +#define nfc_err(dev, fmt, ...) dev_err((dev), "NFC: " fmt, ##__VA_ARGS__) + +struct nfc_phy_ops { + int (*write)(void *dev_id, struct sk_buff *skb); + int (*enable)(void *dev_id); + void (*disable)(void *dev_id); +}; struct nfc_dev; @@ -48,6 +53,8 @@ struct nfc_dev; typedef void (*data_exchange_cb_t)(void *context, struct sk_buff *skb, int err); +typedef void (*se_io_cb_t)(void *context, u8 *apdu, size_t apdu_len, int err); + struct nfc_target; struct nfc_ops { @@ -74,12 +81,23 @@ struct nfc_ops { int (*discover_se)(struct nfc_dev *dev); int (*enable_se)(struct nfc_dev *dev, u32 se_idx); int (*disable_se)(struct nfc_dev *dev, u32 se_idx); + int (*se_io) (struct nfc_dev *dev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context); }; #define NFC_TARGET_IDX_ANY -1 #define NFC_MAX_GT_LEN 48 #define NFC_ATR_RES_GT_OFFSET 15 +/** + * struct nfc_target - NFC target descriptiom + * + * @sens_res: 2 bytes describing the target SENS_RES response, if the target + * is a type A one. The %sens_res most significant byte must be byte 2 + * as described by the NFC Forum digital specification (i.e. the platform + * configuration one) while %sens_res least significant byte is byte 1. + */ struct nfc_target { u32 idx; u32 supported_protocols; @@ -243,5 +261,6 @@ void nfc_driver_failure(struct nfc_dev *dev, int err); int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type); int nfc_remove_se(struct nfc_dev *dev, u32 se_idx); +struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx); #endif /* __NET_NFC_H */ diff --git a/include/net/p8022.h b/include/net/p8022.h index 42e9fac51b3115f86d0a766d8d19c90cc812ac0e..05e41383856bdb0a10db5606c5fbd3040a297a21 100644 --- a/include/net/p8022.h +++ b/include/net/p8022.h @@ -1,13 +1,13 @@ #ifndef _NET_P8022_H #define _NET_P8022_H -extern struct datalink_proto * - register_8022_client(unsigned char type, - int (*func)(struct sk_buff *skb, - struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev)); -extern void unregister_8022_client(struct datalink_proto *proto); +struct datalink_proto * +register_8022_client(unsigned char type, + int (*func)(struct sk_buff *skb, + struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev)); +void unregister_8022_client(struct datalink_proto *proto); -extern struct datalink_proto *make_8023_client(void); -extern void destroy_8023_client(struct datalink_proto *dl); +struct datalink_proto *make_8023_client(void); +void destroy_8023_client(struct datalink_proto *dl); #endif diff --git a/include/net/ping.h b/include/net/ping.h index 5db0224b73ac47b1513ffb9361e48aa0167bb24d..3f67704f3747281b3ad0852fd5d69dcd49c9b01c 100644 --- a/include/net/ping.h +++ b/include/net/ping.h @@ -103,8 +103,8 @@ void ping_seq_stop(struct seq_file *seq, void *v); int ping_proc_register(struct net *net, struct ping_seq_afinfo *afinfo); void ping_proc_unregister(struct net *net, struct ping_seq_afinfo *afinfo); -extern int __init ping_proc_init(void); -extern void ping_proc_exit(void); +int __init ping_proc_init(void); +void ping_proc_exit(void); #endif void __init ping_init(void); diff --git a/include/net/protocol.h b/include/net/protocol.h index 047c0476c0a095c2b271713ca232f7099564558b..fbf7676c9a02e352890b66d0bc3caf1775dbb199 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h @@ -96,20 +96,20 @@ extern const struct net_offload __rcu *inet6_offloads[MAX_INET_PROTOS]; extern const struct inet6_protocol __rcu *inet6_protos[MAX_INET_PROTOS]; #endif -extern int inet_add_protocol(const struct net_protocol *prot, unsigned char num); -extern int inet_del_protocol(const struct net_protocol *prot, unsigned char num); -extern int inet_add_offload(const struct net_offload *prot, unsigned char num); -extern int inet_del_offload(const struct net_offload *prot, unsigned char num); -extern void inet_register_protosw(struct inet_protosw *p); -extern void inet_unregister_protosw(struct inet_protosw *p); +int inet_add_protocol(const struct net_protocol *prot, unsigned char num); +int inet_del_protocol(const struct net_protocol *prot, unsigned char num); +int inet_add_offload(const struct net_offload *prot, unsigned char num); +int inet_del_offload(const struct net_offload *prot, unsigned char num); +void inet_register_protosw(struct inet_protosw *p); +void inet_unregister_protosw(struct inet_protosw *p); #if IS_ENABLED(CONFIG_IPV6) -extern int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char num); -extern int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char num); -extern int inet6_register_protosw(struct inet_protosw *p); -extern void inet6_unregister_protosw(struct inet_protosw *p); +int inet6_add_protocol(const struct inet6_protocol *prot, unsigned char num); +int inet6_del_protocol(const struct inet6_protocol *prot, unsigned char num); +int inet6_register_protosw(struct inet_protosw *p); +void inet6_unregister_protosw(struct inet_protosw *p); #endif -extern int inet6_add_offload(const struct net_offload *prot, unsigned char num); -extern int inet6_del_offload(const struct net_offload *prot, unsigned char num); +int inet6_add_offload(const struct net_offload *prot, unsigned char num); +int inet6_del_offload(const struct net_offload *prot, unsigned char num); #endif /* _PROTOCOL_H */ diff --git a/include/net/psnap.h b/include/net/psnap.h index fe456c295b04400e3e05d489ea7d22349ec486b4..78db4cc1306a147ccb178265f265b9fd8e864ed5 100644 --- a/include/net/psnap.h +++ b/include/net/psnap.h @@ -1,11 +1,11 @@ #ifndef _NET_PSNAP_H #define _NET_PSNAP_H -extern struct datalink_proto * +struct datalink_proto * register_snap_client(const unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *orig_dev)); -extern void unregister_snap_client(struct datalink_proto *proto); +void unregister_snap_client(struct datalink_proto *proto); #endif diff --git a/include/net/raw.h b/include/net/raw.h index 42ce6fe7a2d519c84c0f171cebaad87f537b23b9..6a40c6562dd2a39ac5b4f6ee4a1331b0559bfb02 100644 --- a/include/net/raw.h +++ b/include/net/raw.h @@ -26,7 +26,7 @@ extern struct proto raw_prot; void raw_icmp_error(struct sk_buff *, int, u32); int raw_local_deliver(struct sk_buff *, int); -extern int raw_rcv(struct sock *, struct sk_buff *); +int raw_rcv(struct sock *, struct sk_buff *); #define RAW_HTABLE_SIZE MAX_INET_PROTOS @@ -36,8 +36,8 @@ struct raw_hashinfo { }; #ifdef CONFIG_PROC_FS -extern int raw_proc_init(void); -extern void raw_proc_exit(void); +int raw_proc_init(void); +void raw_proc_exit(void); struct raw_iter_state { struct seq_net_private p; diff --git a/include/net/rawv6.h b/include/net/rawv6.h index e7ea660e4db606139e54567d2ed7d14b93eec4f4..87783dea0791c616a9bac5a164e4421dc3ef2363 100644 --- a/include/net/rawv6.h +++ b/include/net/rawv6.h @@ -7,8 +7,7 @@ void raw6_icmp_error(struct sk_buff *, int nexthdr, u8 type, u8 code, int inner_offset, __be32); bool raw6_local_deliver(struct sk_buff *, int); -extern int rawv6_rcv(struct sock *sk, - struct sk_buff *skb); +int rawv6_rcv(struct sock *sk, struct sk_buff *skb); #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE) int rawv6_mh_filter_register(int (*filter)(struct sock *sock, diff --git a/include/net/request_sock.h b/include/net/request_sock.h index 59795e42c8b61464512184990c3a12698c9a184f..7f830ff67f08c318717e809221d1586f17948185 100644 --- a/include/net/request_sock.h +++ b/include/net/request_sock.h @@ -43,11 +43,12 @@ struct request_sock_ops { struct request_sock *req); }; -extern int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req); +int inet_rtx_syn_ack(struct sock *parent, struct request_sock *req); /* struct request_sock - mini sock to represent a connection request */ struct request_sock { + struct sock_common __req_common; struct request_sock *dl_next; u16 mss; u8 num_retrans; /* number of retransmits */ @@ -162,13 +163,13 @@ struct request_sock_queue { */ }; -extern int reqsk_queue_alloc(struct request_sock_queue *queue, - unsigned int nr_table_entries); +int reqsk_queue_alloc(struct request_sock_queue *queue, + unsigned int nr_table_entries); -extern void __reqsk_queue_destroy(struct request_sock_queue *queue); -extern void reqsk_queue_destroy(struct request_sock_queue *queue); -extern void reqsk_fastopen_remove(struct sock *sk, - struct request_sock *req, bool reset); +void __reqsk_queue_destroy(struct request_sock_queue *queue); +void reqsk_queue_destroy(struct request_sock_queue *queue); +void reqsk_fastopen_remove(struct sock *sk, struct request_sock *req, + bool reset); static inline struct request_sock * reqsk_queue_yank_acceptq(struct request_sock_queue *queue) diff --git a/include/net/rose.h b/include/net/rose.h index 555dd198aab755c9e9ef21a751992ccf653b64c7..50811fe2c585b0e9fd8ffb365971229872eb353e 100644 --- a/include/net/rose.h +++ b/include/net/rose.h @@ -160,38 +160,42 @@ extern int sysctl_rose_routing_control; extern int sysctl_rose_link_fail_timeout; extern int sysctl_rose_maximum_vcs; extern int sysctl_rose_window_size; -extern int rosecmp(rose_address *, rose_address *); -extern int rosecmpm(rose_address *, rose_address *, unsigned short); -extern char *rose2asc(char *buf, const rose_address *); -extern struct sock *rose_find_socket(unsigned int, struct rose_neigh *); -extern void rose_kill_by_neigh(struct rose_neigh *); -extern unsigned int rose_new_lci(struct rose_neigh *); -extern int rose_rx_call_request(struct sk_buff *, struct net_device *, struct rose_neigh *, unsigned int); -extern void rose_destroy_socket(struct sock *); + +int rosecmp(rose_address *, rose_address *); +int rosecmpm(rose_address *, rose_address *, unsigned short); +char *rose2asc(char *buf, const rose_address *); +struct sock *rose_find_socket(unsigned int, struct rose_neigh *); +void rose_kill_by_neigh(struct rose_neigh *); +unsigned int rose_new_lci(struct rose_neigh *); +int rose_rx_call_request(struct sk_buff *, struct net_device *, + struct rose_neigh *, unsigned int); +void rose_destroy_socket(struct sock *); /* rose_dev.c */ -extern void rose_setup(struct net_device *); +void rose_setup(struct net_device *); /* rose_in.c */ -extern int rose_process_rx_frame(struct sock *, struct sk_buff *); +int rose_process_rx_frame(struct sock *, struct sk_buff *); /* rose_link.c */ -extern void rose_start_ftimer(struct rose_neigh *); -extern void rose_stop_ftimer(struct rose_neigh *); -extern void rose_stop_t0timer(struct rose_neigh *); -extern int rose_ftimer_running(struct rose_neigh *); -extern void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, unsigned short); -extern void rose_transmit_clear_request(struct rose_neigh *, unsigned int, unsigned char, unsigned char); -extern void rose_transmit_link(struct sk_buff *, struct rose_neigh *); +void rose_start_ftimer(struct rose_neigh *); +void rose_stop_ftimer(struct rose_neigh *); +void rose_stop_t0timer(struct rose_neigh *); +int rose_ftimer_running(struct rose_neigh *); +void rose_link_rx_restart(struct sk_buff *, struct rose_neigh *, + unsigned short); +void rose_transmit_clear_request(struct rose_neigh *, unsigned int, + unsigned char, unsigned char); +void rose_transmit_link(struct sk_buff *, struct rose_neigh *); /* rose_loopback.c */ -extern void rose_loopback_init(void); -extern void rose_loopback_clear(void); -extern int rose_loopback_queue(struct sk_buff *, struct rose_neigh *); +void rose_loopback_init(void); +void rose_loopback_clear(void); +int rose_loopback_queue(struct sk_buff *, struct rose_neigh *); /* rose_out.c */ -extern void rose_kick(struct sock *); -extern void rose_enquiry_response(struct sock *); +void rose_kick(struct sock *); +void rose_enquiry_response(struct sock *); /* rose_route.c */ extern struct rose_neigh *rose_loopback_neigh; @@ -199,43 +203,45 @@ extern const struct file_operations rose_neigh_fops; extern const struct file_operations rose_nodes_fops; extern const struct file_operations rose_routes_fops; -extern void rose_add_loopback_neigh(void); -extern int __must_check rose_add_loopback_node(rose_address *); -extern void rose_del_loopback_node(rose_address *); -extern void rose_rt_device_down(struct net_device *); -extern void rose_link_device_down(struct net_device *); -extern struct net_device *rose_dev_first(void); -extern struct net_device *rose_dev_get(rose_address *); -extern struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *); -extern struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, unsigned char *, int); -extern int rose_rt_ioctl(unsigned int, void __user *); -extern void rose_link_failed(ax25_cb *, int); -extern int rose_route_frame(struct sk_buff *, ax25_cb *); -extern void rose_rt_free(void); +void rose_add_loopback_neigh(void); +int __must_check rose_add_loopback_node(rose_address *); +void rose_del_loopback_node(rose_address *); +void rose_rt_device_down(struct net_device *); +void rose_link_device_down(struct net_device *); +struct net_device *rose_dev_first(void); +struct net_device *rose_dev_get(rose_address *); +struct rose_route *rose_route_free_lci(unsigned int, struct rose_neigh *); +struct rose_neigh *rose_get_neigh(rose_address *, unsigned char *, + unsigned char *, int); +int rose_rt_ioctl(unsigned int, void __user *); +void rose_link_failed(ax25_cb *, int); +int rose_route_frame(struct sk_buff *, ax25_cb *); +void rose_rt_free(void); /* rose_subr.c */ -extern void rose_clear_queues(struct sock *); -extern void rose_frames_acked(struct sock *, unsigned short); -extern void rose_requeue_frames(struct sock *); -extern int rose_validate_nr(struct sock *, unsigned short); -extern void rose_write_internal(struct sock *, int); -extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); -extern int rose_parse_facilities(unsigned char *, unsigned int, struct rose_facilities_struct *); -extern void rose_disconnect(struct sock *, int, int, int); +void rose_clear_queues(struct sock *); +void rose_frames_acked(struct sock *, unsigned short); +void rose_requeue_frames(struct sock *); +int rose_validate_nr(struct sock *, unsigned short); +void rose_write_internal(struct sock *, int); +int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); +int rose_parse_facilities(unsigned char *, unsigned int, + struct rose_facilities_struct *); +void rose_disconnect(struct sock *, int, int, int); /* rose_timer.c */ -extern void rose_start_heartbeat(struct sock *); -extern void rose_start_t1timer(struct sock *); -extern void rose_start_t2timer(struct sock *); -extern void rose_start_t3timer(struct sock *); -extern void rose_start_hbtimer(struct sock *); -extern void rose_start_idletimer(struct sock *); -extern void rose_stop_heartbeat(struct sock *); -extern void rose_stop_timer(struct sock *); -extern void rose_stop_idletimer(struct sock *); +void rose_start_heartbeat(struct sock *); +void rose_start_t1timer(struct sock *); +void rose_start_t2timer(struct sock *); +void rose_start_t3timer(struct sock *); +void rose_start_hbtimer(struct sock *); +void rose_start_idletimer(struct sock *); +void rose_stop_heartbeat(struct sock *); +void rose_stop_timer(struct sock *); +void rose_stop_idletimer(struct sock *); /* sysctl_net_rose.c */ -extern void rose_register_sysctl(void); -extern void rose_unregister_sysctl(void); +void rose_register_sysctl(void); +void rose_unregister_sysctl(void); #endif diff --git a/include/net/route.h b/include/net/route.h index afdeeb5bec251ac773f03d8c7f0b830c9fa137e2..f68c167280a7f6138036f626cb8f82e68faf4f36 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -39,6 +39,7 @@ #define RTO_ONLINK 0x01 #define RT_CONN_FLAGS(sk) (RT_TOS(inet_sk(sk)->tos) | sock_flag(sk, SOCK_LOCALROUTE)) +#define RT_CONN_FLAGS_TOS(sk,tos) (RT_TOS(tos) | sock_flag(sk, SOCK_LOCALROUTE)) struct fib_nh; struct fib_info; @@ -87,34 +88,28 @@ struct ip_rt_acct { }; struct rt_cache_stat { - unsigned int in_hit; unsigned int in_slow_tot; unsigned int in_slow_mc; unsigned int in_no_route; unsigned int in_brd; unsigned int in_martian_dst; unsigned int in_martian_src; - unsigned int out_hit; unsigned int out_slow_tot; unsigned int out_slow_mc; - unsigned int gc_total; - unsigned int gc_ignored; - unsigned int gc_goal_miss; - unsigned int gc_dst_overflow; - unsigned int in_hlist_search; - unsigned int out_hlist_search; }; extern struct ip_rt_acct __percpu *ip_rt_acct; struct in_device; -extern int ip_rt_init(void); -extern void rt_cache_flush(struct net *net); -extern void rt_flush_dev(struct net_device *dev); -extern struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp); -extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp, - struct sock *sk); -extern struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig); + +int ip_rt_init(void); +void rt_cache_flush(struct net *net); +void rt_flush_dev(struct net_device *dev); +struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp); +struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp, + struct sock *sk); +struct dst_entry *ipv4_blackhole_route(struct net *net, + struct dst_entry *dst_orig); static inline struct rtable *ip_route_output_key(struct net *net, struct flowi4 *flp) { @@ -162,8 +157,8 @@ static inline struct rtable *ip_route_output_gre(struct net *net, struct flowi4 return ip_route_output_key(net, fl4); } -extern int ip_route_input_noref(struct sk_buff *skb, __be32 dst, __be32 src, - u8 tos, struct net_device *devin); +int ip_route_input_noref(struct sk_buff *skb, __be32 dst, __be32 src, + u8 tos, struct net_device *devin); static inline int ip_route_input(struct sk_buff *skb, __be32 dst, __be32 src, u8 tos, struct net_device *devin) @@ -179,24 +174,25 @@ static inline int ip_route_input(struct sk_buff *skb, __be32 dst, __be32 src, return err; } -extern void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, - int oif, u32 mark, u8 protocol, int flow_flags); -extern void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu); -extern void ipv4_redirect(struct sk_buff *skb, struct net *net, - int oif, u32 mark, u8 protocol, int flow_flags); -extern void ipv4_sk_redirect(struct sk_buff *skb, struct sock *sk); -extern void ip_rt_send_redirect(struct sk_buff *skb); - -extern unsigned int inet_addr_type(struct net *net, __be32 addr); -extern unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev, __be32 addr); -extern void ip_rt_multicast_event(struct in_device *); -extern int ip_rt_ioctl(struct net *, unsigned int cmd, void __user *arg); -extern void ip_rt_get_source(u8 *src, struct sk_buff *skb, struct rtable *rt); -extern int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb); +void ipv4_update_pmtu(struct sk_buff *skb, struct net *net, u32 mtu, int oif, + u32 mark, u8 protocol, int flow_flags); +void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu); +void ipv4_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark, + u8 protocol, int flow_flags); +void ipv4_sk_redirect(struct sk_buff *skb, struct sock *sk); +void ip_rt_send_redirect(struct sk_buff *skb); + +unsigned int inet_addr_type(struct net *net, __be32 addr); +unsigned int inet_dev_addr_type(struct net *net, const struct net_device *dev, + __be32 addr); +void ip_rt_multicast_event(struct in_device *); +int ip_rt_ioctl(struct net *, unsigned int cmd, void __user *arg); +void ip_rt_get_source(u8 *src, struct sk_buff *skb, struct rtable *rt); +int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb); struct in_ifaddr; -extern void fib_add_ifaddr(struct in_ifaddr *); -extern void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *); +void fib_add_ifaddr(struct in_ifaddr *); +void fib_del_ifaddr(struct in_ifaddr *, struct in_ifaddr *); static inline void ip_rt_put(struct rtable *rt) { @@ -317,12 +313,20 @@ static inline int ip4_dst_hoplimit(const struct dst_entry *dst) return hoplimit; } -static inline int ip_skb_dst_mtu(struct sk_buff *skb) +static inline bool ip_sk_accept_pmtu(const struct sock *sk) +{ + return inet_sk(sk)->pmtudisc != IP_PMTUDISC_INTERFACE; +} + +static inline bool ip_sk_use_pmtu(const struct sock *sk) { - struct inet_sock *inet = skb->sk ? inet_sk(skb->sk) : NULL; + return inet_sk(sk)->pmtudisc < IP_PMTUDISC_PROBE; +} - return (inet && inet->pmtudisc == IP_PMTUDISC_PROBE) ? - skb_dst(skb)->dev->mtu : dst_mtu(skb_dst(skb)); +static inline int ip_skb_dst_mtu(const struct sk_buff *skb) +{ + return (!skb->sk || ip_sk_use_pmtu(skb->sk)) ? + dst_mtu(skb_dst(skb)) : skb_dst(skb)->dev->mtu; } #endif /* _ROUTE_H */ diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 702664833a53d65c24e3b60316bbf37804e013c3..bb13a182fba6ed076851f58590ab662e429bf01a 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -8,14 +8,12 @@ typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *); typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *); typedef u16 (*rtnl_calcit_func)(struct sk_buff *, struct nlmsghdr *); -extern int __rtnl_register(int protocol, int msgtype, - rtnl_doit_func, rtnl_dumpit_func, - rtnl_calcit_func); -extern void rtnl_register(int protocol, int msgtype, - rtnl_doit_func, rtnl_dumpit_func, - rtnl_calcit_func); -extern int rtnl_unregister(int protocol, int msgtype); -extern void rtnl_unregister_all(int protocol); +int __rtnl_register(int protocol, int msgtype, + rtnl_doit_func, rtnl_dumpit_func, rtnl_calcit_func); +void rtnl_register(int protocol, int msgtype, + rtnl_doit_func, rtnl_dumpit_func, rtnl_calcit_func); +int rtnl_unregister(int protocol, int msgtype); +void rtnl_unregister_all(int protocol); static inline int rtnl_msg_family(const struct nlmsghdr *nlh) { @@ -83,11 +81,11 @@ struct rtnl_link_ops { unsigned int (*get_num_rx_queues)(void); }; -extern int __rtnl_link_register(struct rtnl_link_ops *ops); -extern void __rtnl_link_unregister(struct rtnl_link_ops *ops); +int __rtnl_link_register(struct rtnl_link_ops *ops); +void __rtnl_link_unregister(struct rtnl_link_ops *ops); -extern int rtnl_link_register(struct rtnl_link_ops *ops); -extern void rtnl_link_unregister(struct rtnl_link_ops *ops); +int rtnl_link_register(struct rtnl_link_ops *ops); +void rtnl_link_unregister(struct rtnl_link_ops *ops); /** * struct rtnl_af_ops - rtnetlink address family operations @@ -117,18 +115,18 @@ struct rtnl_af_ops { const struct nlattr *attr); }; -extern int __rtnl_af_register(struct rtnl_af_ops *ops); -extern void __rtnl_af_unregister(struct rtnl_af_ops *ops); +int __rtnl_af_register(struct rtnl_af_ops *ops); +void __rtnl_af_unregister(struct rtnl_af_ops *ops); -extern int rtnl_af_register(struct rtnl_af_ops *ops); -extern void rtnl_af_unregister(struct rtnl_af_ops *ops); +int rtnl_af_register(struct rtnl_af_ops *ops); +void rtnl_af_unregister(struct rtnl_af_ops *ops); +struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]); +struct net_device *rtnl_create_link(struct net *net, char *ifname, + const struct rtnl_link_ops *ops, + struct nlattr *tb[]); +int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm); -extern struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]); -extern struct net_device *rtnl_create_link(struct net *net, - char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]); -extern int rtnl_configure_link(struct net_device *dev, - const struct ifinfomsg *ifm); extern const struct nla_policy ifla_policy[IFLA_MAX+1]; #define MODULE_ALIAS_RTNL_LINK(kind) MODULE_ALIAS("rtnl-link-" kind) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index f4eb365f7dcdadbab70e60940c519e3f83ea3253..d0a6321c302ef2316628b747da27f37cada0b920 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -702,13 +702,20 @@ static inline u64 psched_l2t_ns(const struct psched_ratecfg *r, } void psched_ratecfg_precompute(struct psched_ratecfg *r, - const struct tc_ratespec *conf); + const struct tc_ratespec *conf, + u64 rate64); static inline void psched_ratecfg_getrate(struct tc_ratespec *res, const struct psched_ratecfg *r) { memset(res, 0, sizeof(*res)); - res->rate = r->rate_bytes_ps; + + /* legacy struct tc_ratespec has a 32bit @rate field + * Qdisc using 64bit rate should add new attributes + * in order to maintain compatibility. + */ + res->rate = min_t(u64, r->rate_bytes_ps, ~0U); + res->overhead = r->overhead; res->linklayer = (r->linklayer & TC_LINKLAYER_MASK); } diff --git a/include/net/scm.h b/include/net/scm.h index 8de2d37d2077f2df685baec887b1f836a8fa6514..262532d111f51e3a91a06af785b4721e8fab9d56 100644 --- a/include/net/scm.h +++ b/include/net/scm.h @@ -33,11 +33,11 @@ struct scm_cookie { #endif }; -extern void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm); -extern void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm); -extern int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm); -extern void __scm_destroy(struct scm_cookie *scm); -extern struct scm_fp_list * scm_fp_dup(struct scm_fp_list *fpl); +void scm_detach_fds(struct msghdr *msg, struct scm_cookie *scm); +void scm_detach_fds_compat(struct msghdr *msg, struct scm_cookie *scm); +int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *scm); +void __scm_destroy(struct scm_cookie *scm); +struct scm_fp_list *scm_fp_dup(struct scm_fp_list *fpl); #ifdef CONFIG_SECURITY_NETWORK static __inline__ void unix_get_peersec_dgram(struct socket *sock, struct scm_cookie *scm) diff --git a/include/net/sctp/checksum.h b/include/net/sctp/checksum.h index 259924d63ba6a6f0ab71f52ed0f9e7a62c6bfe1b..6bd44fe94c26382e1130b9b989d1ed6efb68b349 100644 --- a/include/net/sctp/checksum.h +++ b/include/net/sctp/checksum.h @@ -42,56 +42,38 @@ #include #include #include +#include -static inline __u32 sctp_crc32c(__u32 crc, u8 *buffer, u16 length) +static inline __wsum sctp_csum_update(const void *buff, int len, __wsum sum) { - return crc32c(crc, buffer, length); -} - -static inline __u32 sctp_start_cksum(__u8 *buffer, __u16 length) -{ - __u32 crc = ~(__u32)0; - __u8 zero[sizeof(__u32)] = {0}; - - /* Optimize this routine to be SCTP specific, knowing how - * to skip the checksum field of the SCTP header. + /* This uses the crypto implementation of crc32c, which is either + * implemented w/ hardware support or resolves to __crc32c_le(). */ - - /* Calculate CRC up to the checksum. */ - crc = sctp_crc32c(crc, buffer, sizeof(struct sctphdr) - sizeof(__u32)); - - /* Skip checksum field of the header. */ - crc = sctp_crc32c(crc, zero, sizeof(__u32)); - - /* Calculate the rest of the CRC. */ - crc = sctp_crc32c(crc, &buffer[sizeof(struct sctphdr)], - length - sizeof(struct sctphdr)); - return crc; -} - -static inline __u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32) -{ - return sctp_crc32c(crc32, buffer, length); + return crc32c(sum, buff, len); } -static inline __le32 sctp_end_cksum(__u32 crc32) +static inline __wsum sctp_csum_combine(__wsum csum, __wsum csum2, + int offset, int len) { - return cpu_to_le32(~crc32); + return __crc32c_le_combine(csum, csum2, len); } -/* Calculate the CRC32C checksum of an SCTP packet. */ static inline __le32 sctp_compute_cksum(const struct sk_buff *skb, unsigned int offset) { - const struct sk_buff *iter; + struct sctphdr *sh = sctp_hdr(skb); + __le32 ret, old = sh->checksum; + const struct skb_checksum_ops ops = { + .update = sctp_csum_update, + .combine = sctp_csum_combine, + }; - __u32 crc32 = sctp_start_cksum(skb->data + offset, - skb_headlen(skb) - offset); - skb_walk_frags(skb, iter) - crc32 = sctp_update_cksum((__u8 *) iter->data, - skb_headlen(iter), crc32); + sh->checksum = 0; + ret = cpu_to_le32(~__skb_checksum(skb, offset, skb->len - offset, + ~(__u32)0, &ops)); + sh->checksum = old; - return sctp_end_cksum(crc32); + return ret; } #endif /* __sctp_checksum_h__ */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 3794c5ad20fef72a960c34323dab275d6f26bac7..c5fe80697f8d442d9c52cb4aabc9b4edfff85223 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -90,12 +90,11 @@ /* * sctp/protocol.c */ -extern int sctp_copy_local_addr_list(struct net *, struct sctp_bind_addr *, - sctp_scope_t, gfp_t gfp, - int flags); -extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); -extern int sctp_register_pf(struct sctp_pf *, sa_family_t); -extern void sctp_addr_wq_mgmt(struct net *, struct sctp_sockaddr_entry *, int); +int sctp_copy_local_addr_list(struct net *, struct sctp_bind_addr *, + sctp_scope_t, gfp_t gfp, int flags); +struct sctp_pf *sctp_get_pf_specific(sa_family_t family); +int sctp_register_pf(struct sctp_pf *, sa_family_t); +void sctp_addr_wq_mgmt(struct net *, struct sctp_sockaddr_entry *, int); /* * sctp/socket.c @@ -110,7 +109,7 @@ void sctp_sock_rfree(struct sk_buff *skb); void sctp_copy_sock(struct sock *newsk, struct sock *sk, struct sctp_association *asoc); extern struct percpu_counter sctp_sockets_allocated; -extern int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *); +int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *); /* * sctp/primitive.c diff --git a/include/net/secure_seq.h b/include/net/secure_seq.h index c2e542b27a5a8316f3b105b2338bc96d5698690e..f257486f17be4bed528826544359c569e6a4b2dd 100644 --- a/include/net/secure_seq.h +++ b/include/net/secure_seq.h @@ -3,18 +3,18 @@ #include -extern __u32 secure_ip_id(__be32 daddr); -extern __u32 secure_ipv6_id(const __be32 daddr[4]); -extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); -extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, - __be16 dport); -extern __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport); -extern __u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, - __be16 sport, __be16 dport); -extern u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, - __be16 sport, __be16 dport); -extern u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr, - __be16 sport, __be16 dport); +__u32 secure_ip_id(__be32 daddr); +__u32 secure_ipv6_id(const __be32 daddr[4]); +u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport); +u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, + __be16 dport); +__u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport); +__u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, + __be16 sport, __be16 dport); +u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, + __be16 sport, __be16 dport); +u64 secure_dccpv6_sequence_number(__be32 *saddr, __be32 *daddr, + __be16 sport, __be16 dport); #endif /* _NET_SECURE_SEQ */ diff --git a/include/net/sock.h b/include/net/sock.h index 808cbc2ec6c1b38ea82198d2171079863377777d..e3a18ff0c38b58aec20413489bf7237c54a14a77 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -156,7 +156,7 @@ typedef __u64 __bitwise __addrpair; */ struct sock_common { /* skc_daddr and skc_rcv_saddr must be grouped on a 8 bytes aligned - * address on 64bit arches : cf INET_MATCH() and INET_TW_MATCH() + * address on 64bit arches : cf INET_MATCH() */ union { __addrpair skc_addrpair; @@ -191,6 +191,12 @@ struct sock_common { #ifdef CONFIG_NET_NS struct net *skc_net; #endif + +#if IS_ENABLED(CONFIG_IPV6) + struct in6_addr skc_v6_daddr; + struct in6_addr skc_v6_rcv_saddr; +#endif + /* * fields between dontcopy_begin/dontcopy_end * are not copied in sock_copy() @@ -218,7 +224,7 @@ struct cg_proto; * @sk_lock: synchronizer * @sk_rcvbuf: size of receive buffer in bytes * @sk_wq: sock wait queue and async head - * @sk_rx_dst: receive input route used by early tcp demux + * @sk_rx_dst: receive input route used by early demux * @sk_dst_cache: destination cache * @sk_dst_lock: destination cache lock * @sk_policy: flow policy @@ -233,6 +239,7 @@ struct cg_proto; * @sk_ll_usec: usecs to busypoll when there is no data * @sk_allocation: allocation mode * @sk_pacing_rate: Pacing rate (if supported by transport/packet scheduler) + * @sk_max_pacing_rate: Maximum pacing rate (%SO_MAX_PACING_RATE) * @sk_sndbuf: size of send buffer in bytes * @sk_flags: %SO_LINGER (l_onoff), %SO_BROADCAST, %SO_KEEPALIVE, * %SO_OOBINLINE settings, %SO_TIMESTAMPING settings @@ -299,6 +306,12 @@ struct sock { #define sk_dontcopy_begin __sk_common.skc_dontcopy_begin #define sk_dontcopy_end __sk_common.skc_dontcopy_end #define sk_hash __sk_common.skc_hash +#define sk_portpair __sk_common.skc_portpair +#define sk_num __sk_common.skc_num +#define sk_dport __sk_common.skc_dport +#define sk_addrpair __sk_common.skc_addrpair +#define sk_daddr __sk_common.skc_daddr +#define sk_rcv_saddr __sk_common.skc_rcv_saddr #define sk_family __sk_common.skc_family #define sk_state __sk_common.skc_state #define sk_reuse __sk_common.skc_reuse @@ -307,6 +320,9 @@ struct sock { #define sk_bind_node __sk_common.skc_bind_node #define sk_prot __sk_common.skc_prot #define sk_net __sk_common.skc_net +#define sk_v6_daddr __sk_common.skc_v6_daddr +#define sk_v6_rcv_saddr __sk_common.skc_v6_rcv_saddr + socket_lock_t sk_lock; struct sk_buff_head sk_receive_queue; /* @@ -363,6 +379,7 @@ struct sock { int sk_wmem_queued; gfp_t sk_allocation; u32 sk_pacing_rate; /* bytes per second */ + u32 sk_max_pacing_rate; netdev_features_t sk_route_caps; netdev_features_t sk_route_nocaps; int sk_gso_type; @@ -751,7 +768,7 @@ static inline int sk_stream_wspace(const struct sock *sk) return sk->sk_sndbuf - sk->sk_wmem_queued; } -extern void sk_stream_write_space(struct sock *sk); +void sk_stream_write_space(struct sock *sk); /* OOB backlog add */ static inline void __sk_add_backlog(struct sock *sk, struct sk_buff *skb) @@ -793,7 +810,7 @@ static inline __must_check int sk_add_backlog(struct sock *sk, struct sk_buff *s return 0; } -extern int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb); +int __sk_backlog_rcv(struct sock *sk, struct sk_buff *skb); static inline int sk_backlog_rcv(struct sock *sk, struct sk_buff *skb) { @@ -858,15 +875,15 @@ static inline void sock_rps_reset_rxhash(struct sock *sk) __rc; \ }) -extern int sk_stream_wait_connect(struct sock *sk, long *timeo_p); -extern int sk_stream_wait_memory(struct sock *sk, long *timeo_p); -extern void sk_stream_wait_close(struct sock *sk, long timeo_p); -extern int sk_stream_error(struct sock *sk, int flags, int err); -extern void sk_stream_kill_queues(struct sock *sk); -extern void sk_set_memalloc(struct sock *sk); -extern void sk_clear_memalloc(struct sock *sk); +int sk_stream_wait_connect(struct sock *sk, long *timeo_p); +int sk_stream_wait_memory(struct sock *sk, long *timeo_p); +void sk_stream_wait_close(struct sock *sk, long timeo_p); +int sk_stream_error(struct sock *sk, int flags, int err); +void sk_stream_kill_queues(struct sock *sk); +void sk_set_memalloc(struct sock *sk); +void sk_clear_memalloc(struct sock *sk); -extern int sk_wait_data(struct sock *sk, long *timeo); +int sk_wait_data(struct sock *sk, long *timeo); struct request_sock_ops; struct timewait_sock_ops; @@ -1019,10 +1036,10 @@ enum cg_proto_flags { struct cg_proto { void (*enter_memory_pressure)(struct sock *sk); - struct res_counter *memory_allocated; /* Current allocated memory. */ - struct percpu_counter *sockets_allocated; /* Current number of sockets. */ - int *memory_pressure; - long *sysctl_mem; + struct res_counter memory_allocated; /* Current allocated memory. */ + struct percpu_counter sockets_allocated; /* Current number of sockets. */ + int memory_pressure; + long sysctl_mem[3]; unsigned long flags; /* * memcg field is used to find which memcg we belong directly @@ -1036,8 +1053,8 @@ struct cg_proto { struct mem_cgroup *memcg; }; -extern int proto_register(struct proto *prot, int alloc_slab); -extern void proto_unregister(struct proto *prot); +int proto_register(struct proto *prot, int alloc_slab); +void proto_unregister(struct proto *prot); static inline bool memcg_proto_active(struct cg_proto *cg_proto) { @@ -1118,7 +1135,7 @@ static inline bool sk_under_memory_pressure(const struct sock *sk) return false; if (mem_cgroup_sockets_enabled && sk->sk_cgrp) - return !!*sk->sk_cgrp->memory_pressure; + return !!sk->sk_cgrp->memory_pressure; return !!*sk->sk_prot->memory_pressure; } @@ -1138,8 +1155,8 @@ static inline void sk_leave_memory_pressure(struct sock *sk) struct proto *prot = sk->sk_prot; for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto)) - if (*cg_proto->memory_pressure) - *cg_proto->memory_pressure = 0; + if (cg_proto->memory_pressure) + cg_proto->memory_pressure = 0; } } @@ -1175,7 +1192,7 @@ static inline void memcg_memory_allocated_add(struct cg_proto *prot, struct res_counter *fail; int ret; - ret = res_counter_charge_nofail(prot->memory_allocated, + ret = res_counter_charge_nofail(&prot->memory_allocated, amt << PAGE_SHIFT, &fail); if (ret < 0) *parent_status = OVER_LIMIT; @@ -1184,13 +1201,13 @@ static inline void memcg_memory_allocated_add(struct cg_proto *prot, static inline void memcg_memory_allocated_sub(struct cg_proto *prot, unsigned long amt) { - res_counter_uncharge(prot->memory_allocated, amt << PAGE_SHIFT); + res_counter_uncharge(&prot->memory_allocated, amt << PAGE_SHIFT); } static inline u64 memcg_memory_allocated_read(struct cg_proto *prot) { u64 ret; - ret = res_counter_read_u64(prot->memory_allocated, RES_USAGE); + ret = res_counter_read_u64(&prot->memory_allocated, RES_USAGE); return ret >> PAGE_SHIFT; } @@ -1238,7 +1255,7 @@ static inline void sk_sockets_allocated_dec(struct sock *sk) struct cg_proto *cg_proto = sk->sk_cgrp; for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto)) - percpu_counter_dec(cg_proto->sockets_allocated); + percpu_counter_dec(&cg_proto->sockets_allocated); } percpu_counter_dec(prot->sockets_allocated); @@ -1252,7 +1269,7 @@ static inline void sk_sockets_allocated_inc(struct sock *sk) struct cg_proto *cg_proto = sk->sk_cgrp; for (; cg_proto; cg_proto = parent_cg_proto(prot, cg_proto)) - percpu_counter_inc(cg_proto->sockets_allocated); + percpu_counter_inc(&cg_proto->sockets_allocated); } percpu_counter_inc(prot->sockets_allocated); @@ -1264,7 +1281,7 @@ sk_sockets_allocated_read_positive(struct sock *sk) struct proto *prot = sk->sk_prot; if (mem_cgroup_sockets_enabled && sk->sk_cgrp) - return percpu_counter_read_positive(sk->sk_cgrp->sockets_allocated); + return percpu_counter_read_positive(&sk->sk_cgrp->sockets_allocated); return percpu_counter_read_positive(prot->sockets_allocated); } @@ -1292,8 +1309,8 @@ proto_memory_pressure(struct proto *prot) #ifdef CONFIG_PROC_FS /* Called with local bh disabled */ -extern void sock_prot_inuse_add(struct net *net, struct proto *prot, int inc); -extern int sock_prot_inuse_get(struct net *net, struct proto *proto); +void sock_prot_inuse_add(struct net *net, struct proto *prot, int inc); +int sock_prot_inuse_get(struct net *net, struct proto *proto); #else static inline void sock_prot_inuse_add(struct net *net, struct proto *prot, int inc) @@ -1369,8 +1386,8 @@ static inline struct inode *SOCK_INODE(struct socket *socket) /* * Functions for memory accounting */ -extern int __sk_mem_schedule(struct sock *sk, int size, int kind); -extern void __sk_mem_reclaim(struct sock *sk); +int __sk_mem_schedule(struct sock *sk, int size, int kind); +void __sk_mem_reclaim(struct sock *sk); #define SK_MEM_QUANTUM ((int)PAGE_SIZE) #define SK_MEM_QUANTUM_SHIFT ilog2(SK_MEM_QUANTUM) @@ -1478,14 +1495,14 @@ do { \ lockdep_init_map(&(sk)->sk_lock.dep_map, (name), (key), 0); \ } while (0) -extern void lock_sock_nested(struct sock *sk, int subclass); +void lock_sock_nested(struct sock *sk, int subclass); static inline void lock_sock(struct sock *sk) { lock_sock_nested(sk, 0); } -extern void release_sock(struct sock *sk); +void release_sock(struct sock *sk); /* BH context may only use the following locking interface. */ #define bh_lock_sock(__sk) spin_lock(&((__sk)->sk_lock.slock)) @@ -1494,7 +1511,7 @@ extern void release_sock(struct sock *sk); SINGLE_DEPTH_NESTING) #define bh_unlock_sock(__sk) spin_unlock(&((__sk)->sk_lock.slock)) -extern bool lock_sock_fast(struct sock *sk); +bool lock_sock_fast(struct sock *sk); /** * unlock_sock_fast - complement of lock_sock_fast * @sk: socket @@ -1512,108 +1529,84 @@ static inline void unlock_sock_fast(struct sock *sk, bool slow) } -extern struct sock *sk_alloc(struct net *net, int family, - gfp_t priority, - struct proto *prot); -extern void sk_free(struct sock *sk); -extern void sk_release_kernel(struct sock *sk); -extern struct sock *sk_clone_lock(const struct sock *sk, - const gfp_t priority); - -extern struct sk_buff *sock_wmalloc(struct sock *sk, - unsigned long size, int force, - gfp_t priority); -extern struct sk_buff *sock_rmalloc(struct sock *sk, - unsigned long size, int force, - gfp_t priority); -extern void sock_wfree(struct sk_buff *skb); -extern void skb_orphan_partial(struct sk_buff *skb); -extern void sock_rfree(struct sk_buff *skb); -extern void sock_edemux(struct sk_buff *skb); - -extern int sock_setsockopt(struct socket *sock, int level, - int op, char __user *optval, - unsigned int optlen); - -extern int sock_getsockopt(struct socket *sock, int level, - int op, char __user *optval, - int __user *optlen); -extern struct sk_buff *sock_alloc_send_skb(struct sock *sk, - unsigned long size, - int noblock, - int *errcode); -extern struct sk_buff *sock_alloc_send_pskb(struct sock *sk, - unsigned long header_len, - unsigned long data_len, - int noblock, - int *errcode, - int max_page_order); -extern void *sock_kmalloc(struct sock *sk, int size, - gfp_t priority); -extern void sock_kfree_s(struct sock *sk, void *mem, int size); -extern void sk_send_sigurg(struct sock *sk); +struct sock *sk_alloc(struct net *net, int family, gfp_t priority, + struct proto *prot); +void sk_free(struct sock *sk); +void sk_release_kernel(struct sock *sk); +struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority); + +struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, + gfp_t priority); +struct sk_buff *sock_rmalloc(struct sock *sk, unsigned long size, int force, + gfp_t priority); +void sock_wfree(struct sk_buff *skb); +void skb_orphan_partial(struct sk_buff *skb); +void sock_rfree(struct sk_buff *skb); +void sock_edemux(struct sk_buff *skb); + +int sock_setsockopt(struct socket *sock, int level, int op, + char __user *optval, unsigned int optlen); + +int sock_getsockopt(struct socket *sock, int level, int op, + char __user *optval, int __user *optlen); +struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, + int noblock, int *errcode); +struct sk_buff *sock_alloc_send_pskb(struct sock *sk, unsigned long header_len, + unsigned long data_len, int noblock, + int *errcode, int max_page_order); +void *sock_kmalloc(struct sock *sk, int size, gfp_t priority); +void sock_kfree_s(struct sock *sk, void *mem, int size); +void sk_send_sigurg(struct sock *sk); /* * Functions to fill in entries in struct proto_ops when a protocol * does not implement a particular function. */ -extern int sock_no_bind(struct socket *, - struct sockaddr *, int); -extern int sock_no_connect(struct socket *, - struct sockaddr *, int, int); -extern int sock_no_socketpair(struct socket *, - struct socket *); -extern int sock_no_accept(struct socket *, - struct socket *, int); -extern int sock_no_getname(struct socket *, - struct sockaddr *, int *, int); -extern unsigned int sock_no_poll(struct file *, struct socket *, - struct poll_table_struct *); -extern int sock_no_ioctl(struct socket *, unsigned int, - unsigned long); -extern int sock_no_listen(struct socket *, int); -extern int sock_no_shutdown(struct socket *, int); -extern int sock_no_getsockopt(struct socket *, int , int, - char __user *, int __user *); -extern int sock_no_setsockopt(struct socket *, int, int, - char __user *, unsigned int); -extern int sock_no_sendmsg(struct kiocb *, struct socket *, - struct msghdr *, size_t); -extern int sock_no_recvmsg(struct kiocb *, struct socket *, - struct msghdr *, size_t, int); -extern int sock_no_mmap(struct file *file, - struct socket *sock, - struct vm_area_struct *vma); -extern ssize_t sock_no_sendpage(struct socket *sock, - struct page *page, - int offset, size_t size, - int flags); +int sock_no_bind(struct socket *, struct sockaddr *, int); +int sock_no_connect(struct socket *, struct sockaddr *, int, int); +int sock_no_socketpair(struct socket *, struct socket *); +int sock_no_accept(struct socket *, struct socket *, int); +int sock_no_getname(struct socket *, struct sockaddr *, int *, int); +unsigned int sock_no_poll(struct file *, struct socket *, + struct poll_table_struct *); +int sock_no_ioctl(struct socket *, unsigned int, unsigned long); +int sock_no_listen(struct socket *, int); +int sock_no_shutdown(struct socket *, int); +int sock_no_getsockopt(struct socket *, int , int, char __user *, int __user *); +int sock_no_setsockopt(struct socket *, int, int, char __user *, unsigned int); +int sock_no_sendmsg(struct kiocb *, struct socket *, struct msghdr *, size_t); +int sock_no_recvmsg(struct kiocb *, struct socket *, struct msghdr *, size_t, + int); +int sock_no_mmap(struct file *file, struct socket *sock, + struct vm_area_struct *vma); +ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset, + size_t size, int flags); /* * Functions to fill in entries in struct proto_ops when a protocol * uses the inet style. */ -extern int sock_common_getsockopt(struct socket *sock, int level, int optname, +int sock_common_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen); -extern int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock, +int sock_common_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t size, int flags); -extern int sock_common_setsockopt(struct socket *sock, int level, int optname, +int sock_common_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen); -extern int compat_sock_common_getsockopt(struct socket *sock, int level, +int compat_sock_common_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen); -extern int compat_sock_common_setsockopt(struct socket *sock, int level, +int compat_sock_common_setsockopt(struct socket *sock, int level, int optname, char __user *optval, unsigned int optlen); -extern void sk_common_release(struct sock *sk); +void sk_common_release(struct sock *sk); /* * Default socket callbacks and setup code */ /* Initialise core socket variables */ -extern void sock_init_data(struct socket *sock, struct sock *sk); +void sock_init_data(struct socket *sock, struct sock *sk); -extern void sk_filter_release_rcu(struct rcu_head *rcu); +void sk_filter_release_rcu(struct rcu_head *rcu); /** * sk_filter_release - release a socket filter @@ -1671,9 +1664,12 @@ static inline void sock_put(struct sock *sk) if (atomic_dec_and_test(&sk->sk_refcnt)) sk_free(sk); } +/* Generic version of sock_put(), dealing with all sockets + * (TCP_TIMEWAIT, ESTABLISHED...) + */ +void sock_gen_put(struct sock *sk); -extern int sk_receive_skb(struct sock *sk, struct sk_buff *skb, - const int nested); +int sk_receive_skb(struct sock *sk, struct sk_buff *skb, const int nested); static inline void sk_tx_queue_set(struct sock *sk, int tx_queue) { @@ -1727,8 +1723,8 @@ static inline void sock_graft(struct sock *sk, struct socket *parent) write_unlock_bh(&sk->sk_callback_lock); } -extern kuid_t sock_i_uid(struct sock *sk); -extern unsigned long sock_i_ino(struct sock *sk); +kuid_t sock_i_uid(struct sock *sk); +unsigned long sock_i_ino(struct sock *sk); static inline struct dst_entry * __sk_dst_get(struct sock *sk) @@ -1750,8 +1746,6 @@ sk_dst_get(struct sock *sk) return dst; } -extern void sk_reset_txq(struct sock *sk); - static inline void dst_negative_advice(struct sock *sk) { struct dst_entry *ndst, *dst = __sk_dst_get(sk); @@ -1761,7 +1755,7 @@ static inline void dst_negative_advice(struct sock *sk) if (ndst != dst) { rcu_assign_pointer(sk->sk_dst_cache, ndst); - sk_reset_txq(sk); + sk_tx_queue_clear(sk); } } } @@ -1803,16 +1797,16 @@ sk_dst_reset(struct sock *sk) spin_unlock(&sk->sk_dst_lock); } -extern struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie); +struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie); -extern struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie); +struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie); static inline bool sk_can_gso(const struct sock *sk) { return net_gso_ok(sk->sk_route_caps, sk->sk_gso_type); } -extern void sk_setup_caps(struct sock *sk, struct dst_entry *dst); +void sk_setup_caps(struct sock *sk, struct dst_entry *dst); static inline void sk_nocaps_add(struct sock *sk, netdev_features_t flags) { @@ -2025,14 +2019,14 @@ static inline void skb_set_owner_r(struct sk_buff *skb, struct sock *sk) sk_mem_charge(sk, skb->truesize); } -extern void sk_reset_timer(struct sock *sk, struct timer_list *timer, - unsigned long expires); +void sk_reset_timer(struct sock *sk, struct timer_list *timer, + unsigned long expires); -extern void sk_stop_timer(struct sock *sk, struct timer_list *timer); +void sk_stop_timer(struct sock *sk, struct timer_list *timer); -extern int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); +int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); -extern int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb); +int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb); /* * Recover an error report and clear atomically @@ -2100,7 +2094,7 @@ static inline struct page_frag *sk_page_frag(struct sock *sk) return &sk->sk_frag; } -extern bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag); +bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag); /* * Default write policy as shown to user space via poll/select/SIGIO @@ -2138,10 +2132,10 @@ static inline int sock_intr_errno(long timeo) return timeo == MAX_SCHEDULE_TIMEOUT ? -ERESTARTSYS : -EINTR; } -extern void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, - struct sk_buff *skb); -extern void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, - struct sk_buff *skb); +void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb); +void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb); static inline void sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) @@ -2174,8 +2168,8 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) __sock_recv_wifi_status(msg, sk, skb); } -extern void __sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, - struct sk_buff *skb); +void __sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, + struct sk_buff *skb); static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, struct sk_buff *skb) @@ -2200,7 +2194,7 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, * * Currently only depends on SOCK_TIMESTAMPING* flags. */ -extern void sock_tx_timestamp(struct sock *sk, __u8 *tx_flags); +void sock_tx_timestamp(struct sock *sk, __u8 *tx_flags); /** * sk_eat_skb - Release a skb if it is no longer needed @@ -2264,11 +2258,11 @@ static inline struct sock *skb_steal_sock(struct sk_buff *skb) return NULL; } -extern void sock_enable_timestamp(struct sock *sk, int flag); -extern int sock_get_timestamp(struct sock *, struct timeval __user *); -extern int sock_get_timestampns(struct sock *, struct timespec __user *); -extern int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len, - int level, int type); +void sock_enable_timestamp(struct sock *sk, int flag); +int sock_get_timestamp(struct sock *, struct timeval __user *); +int sock_get_timestampns(struct sock *, struct timespec __user *); +int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len, int level, + int type); /* * Enable debug/info messages diff --git a/include/net/stp.h b/include/net/stp.h index ad447f105417def47daff3e65f93de65d26f0917..3af174d70d9e8ca988122b75358703b99c0b4d4a 100644 --- a/include/net/stp.h +++ b/include/net/stp.h @@ -8,7 +8,7 @@ struct stp_proto { void *data; }; -extern int stp_proto_register(const struct stp_proto *proto); -extern void stp_proto_unregister(const struct stp_proto *proto); +int stp_proto_register(const struct stp_proto *proto); +void stp_proto_unregister(const struct stp_proto *proto); #endif /* _NET_STP_H */ diff --git a/include/net/tcp.h b/include/net/tcp.h index b1aa324c5e6512791cbf3340aca34dacc73bdfdb..70e55d200610ec6596f7c90715bff4749b462f6e 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -50,7 +50,7 @@ extern struct inet_hashinfo tcp_hashinfo; extern struct percpu_counter tcp_orphan_count; -extern void tcp_time_wait(struct sock *sk, int state, int timeo); +void tcp_time_wait(struct sock *sk, int state, int timeo); #define MAX_TCP_HEADER (128 + MAX_HEADER) #define MAX_TCP_OPTION_SPACE 40 @@ -259,6 +259,7 @@ extern int sysctl_tcp_max_orphans; extern int sysctl_tcp_fack; extern int sysctl_tcp_reordering; extern int sysctl_tcp_dsack; +extern long sysctl_tcp_mem[3]; extern int sysctl_tcp_wmem[3]; extern int sysctl_tcp_rmem[3]; extern int sysctl_tcp_app_win; @@ -274,7 +275,6 @@ extern int sysctl_tcp_mtu_probing; extern int sysctl_tcp_base_mss; extern int sysctl_tcp_workaround_signed_windows; extern int sysctl_tcp_slow_start_after_idle; -extern int sysctl_tcp_max_ssthresh; extern int sysctl_tcp_thin_linear_timeouts; extern int sysctl_tcp_thin_dupack; extern int sysctl_tcp_early_retrans; @@ -325,7 +325,7 @@ static inline bool tcp_too_many_orphans(struct sock *sk, int shift) return false; } -extern bool tcp_check_oom(struct sock *sk, int shift); +bool tcp_check_oom(struct sock *sk, int shift); /* syncookies: remember time of last synqueue overflow */ static inline void tcp_synq_overflow(struct sock *sk) @@ -348,38 +348,36 @@ extern struct proto tcp_prot; #define TCP_ADD_STATS_USER(net, field, val) SNMP_ADD_STATS_USER((net)->mib.tcp_statistics, field, val) #define TCP_ADD_STATS(net, field, val) SNMP_ADD_STATS((net)->mib.tcp_statistics, field, val) -extern void tcp_init_mem(struct net *net); - -extern void tcp_tasklet_init(void); - -extern void tcp_v4_err(struct sk_buff *skb, u32); - -extern void tcp_shutdown (struct sock *sk, int how); - -extern void tcp_v4_early_demux(struct sk_buff *skb); -extern int tcp_v4_rcv(struct sk_buff *skb); - -extern int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw); -extern int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t size); -extern int tcp_sendpage(struct sock *sk, struct page *page, int offset, - size_t size, int flags); -extern void tcp_release_cb(struct sock *sk); -extern void tcp_wfree(struct sk_buff *skb); -extern void tcp_write_timer_handler(struct sock *sk); -extern void tcp_delack_timer_handler(struct sock *sk); -extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg); -extern int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - const struct tcphdr *th, unsigned int len); -extern void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, - const struct tcphdr *th, unsigned int len); -extern void tcp_rcv_space_adjust(struct sock *sk); -extern void tcp_cleanup_rbuf(struct sock *sk, int copied); -extern int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp); -extern void tcp_twsk_destructor(struct sock *sk); -extern ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos, - struct pipe_inode_info *pipe, size_t len, - unsigned int flags); +void tcp_tasklet_init(void); + +void tcp_v4_err(struct sk_buff *skb, u32); + +void tcp_shutdown(struct sock *sk, int how); + +void tcp_v4_early_demux(struct sk_buff *skb); +int tcp_v4_rcv(struct sk_buff *skb); + +int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw); +int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t size); +int tcp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, + int flags); +void tcp_release_cb(struct sock *sk); +void tcp_wfree(struct sk_buff *skb); +void tcp_write_timer_handler(struct sock *sk); +void tcp_delack_timer_handler(struct sock *sk); +int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg); +int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, + const struct tcphdr *th, unsigned int len); +void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, + const struct tcphdr *th, unsigned int len); +void tcp_rcv_space_adjust(struct sock *sk); +void tcp_cleanup_rbuf(struct sock *sk, int copied); +int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp); +void tcp_twsk_destructor(struct sock *sk); +ssize_t tcp_splice_read(struct socket *sk, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); static inline void tcp_dec_quickack_mode(struct sock *sk, const unsigned int pkts) @@ -409,66 +407,65 @@ enum tcp_tw_status { }; -extern enum tcp_tw_status tcp_timewait_state_process(struct inet_timewait_sock *tw, - struct sk_buff *skb, - const struct tcphdr *th); -extern struct sock * tcp_check_req(struct sock *sk,struct sk_buff *skb, - struct request_sock *req, - struct request_sock **prev, - bool fastopen); -extern int tcp_child_process(struct sock *parent, struct sock *child, - struct sk_buff *skb); -extern void tcp_enter_loss(struct sock *sk, int how); -extern void tcp_clear_retrans(struct tcp_sock *tp); -extern void tcp_update_metrics(struct sock *sk); -extern void tcp_init_metrics(struct sock *sk); -extern void tcp_metrics_init(void); -extern bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, bool paws_check); -extern bool tcp_remember_stamp(struct sock *sk); -extern bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw); -extern void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst); -extern void tcp_disable_fack(struct tcp_sock *tp); -extern void tcp_close(struct sock *sk, long timeout); -extern void tcp_init_sock(struct sock *sk); -extern unsigned int tcp_poll(struct file * file, struct socket *sock, - struct poll_table_struct *wait); -extern int tcp_getsockopt(struct sock *sk, int level, int optname, +enum tcp_tw_status tcp_timewait_state_process(struct inet_timewait_sock *tw, + struct sk_buff *skb, + const struct tcphdr *th); +struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, struct request_sock **prev, + bool fastopen); +int tcp_child_process(struct sock *parent, struct sock *child, + struct sk_buff *skb); +void tcp_enter_loss(struct sock *sk, int how); +void tcp_clear_retrans(struct tcp_sock *tp); +void tcp_update_metrics(struct sock *sk); +void tcp_init_metrics(struct sock *sk); +void tcp_metrics_init(void); +bool tcp_peer_is_proven(struct request_sock *req, struct dst_entry *dst, + bool paws_check); +bool tcp_remember_stamp(struct sock *sk); +bool tcp_tw_remember_stamp(struct inet_timewait_sock *tw); +void tcp_fetch_timewait_stamp(struct sock *sk, struct dst_entry *dst); +void tcp_disable_fack(struct tcp_sock *tp); +void tcp_close(struct sock *sk, long timeout); +void tcp_init_sock(struct sock *sk); +unsigned int tcp_poll(struct file *file, struct socket *sock, + struct poll_table_struct *wait); +int tcp_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); +int tcp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); +int compat_tcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); -extern int tcp_setsockopt(struct sock *sk, int level, int optname, +int compat_tcp_setsockopt(struct sock *sk, int level, int optname, char __user *optval, unsigned int optlen); -extern int compat_tcp_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen); -extern int compat_tcp_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen); -extern void tcp_set_keepalive(struct sock *sk, int val); -extern void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req); -extern int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int nonblock, int flags, int *addr_len); -extern void tcp_parse_options(const struct sk_buff *skb, - struct tcp_options_received *opt_rx, - int estab, struct tcp_fastopen_cookie *foc); -extern const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); +void tcp_set_keepalive(struct sock *sk, int val); +void tcp_syn_ack_timeout(struct sock *sk, struct request_sock *req); +int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len, int nonblock, int flags, int *addr_len); +void tcp_parse_options(const struct sk_buff *skb, + struct tcp_options_received *opt_rx, + int estab, struct tcp_fastopen_cookie *foc); +const u8 *tcp_parse_md5sig_option(const struct tcphdr *th); /* * TCP v4 functions exported for the inet6 API */ -extern void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb); -extern int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb); -extern struct sock * tcp_create_openreq_child(struct sock *sk, - struct request_sock *req, - struct sk_buff *skb); -extern struct sock * tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct dst_entry *dst); -extern int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb); -extern int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, - int addr_len); -extern int tcp_connect(struct sock *sk); -extern struct sk_buff * tcp_make_synack(struct sock *sk, struct dst_entry *dst, - struct request_sock *req, - struct tcp_fastopen_cookie *foc); -extern int tcp_disconnect(struct sock *sk, int flags); +void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb); +int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb); +struct sock *tcp_create_openreq_child(struct sock *sk, + struct request_sock *req, + struct sk_buff *skb); +struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct dst_entry *dst); +int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb); +int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); +int tcp_connect(struct sock *sk); +struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, + struct request_sock *req, + struct tcp_fastopen_cookie *foc); +int tcp_disconnect(struct sock *sk, int flags); void tcp_connect_init(struct sock *sk); void tcp_finish_connect(struct sock *sk, struct sk_buff *skb); @@ -476,16 +473,32 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size); void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb); /* From syncookies.c */ -extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS]; -extern int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, - u32 cookie); -extern struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, - struct ip_options *opt); +int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, + u32 cookie); +struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, + struct ip_options *opt); #ifdef CONFIG_SYN_COOKIES -extern u32 __cookie_v4_init_sequence(const struct iphdr *iph, - const struct tcphdr *th, u16 *mssp); -extern __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, - __u16 *mss); +#include + +/* Syncookies use a monotonic timer which increments every 64 seconds. + * This counter is used both as a hash input and partially encoded into + * the cookie value. A cookie is only validated further if the delta + * between the current counter value and the encoded one is less than this, + * i.e. a sent cookie is valid only at most for 128 seconds (or less if + * the counter advances immediately after a cookie is generated). + */ +#define MAX_SYNCOOKIE_AGE 2 + +static inline u32 tcp_cookie_time(void) +{ + struct timespec now; + getnstimeofday(&now); + return now.tv_sec >> 6; /* 64 seconds granularity */ +} + +u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th, + u16 *mssp); +__u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mss); #else static inline __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, @@ -495,19 +508,19 @@ static inline __u32 cookie_v4_init_sequence(struct sock *sk, } #endif -extern __u32 cookie_init_timestamp(struct request_sock *req); -extern bool cookie_check_timestamp(struct tcp_options_received *opt, - struct net *net, bool *ecn_ok); +__u32 cookie_init_timestamp(struct request_sock *req); +bool cookie_check_timestamp(struct tcp_options_received *opt, struct net *net, + bool *ecn_ok); /* From net/ipv6/syncookies.c */ -extern int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th, - u32 cookie); -extern struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb); +int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th, + u32 cookie); +struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb); #ifdef CONFIG_SYN_COOKIES -extern u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph, - const struct tcphdr *th, u16 *mssp); -extern __u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, - __u16 *mss); +u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph, + const struct tcphdr *th, u16 *mssp); +__u32 cookie_v6_init_sequence(struct sock *sk, const struct sk_buff *skb, + __u16 *mss); #else static inline __u32 cookie_v6_init_sequence(struct sock *sk, struct sk_buff *skb, @@ -518,47 +531,46 @@ static inline __u32 cookie_v6_init_sequence(struct sock *sk, #endif /* tcp_output.c */ -extern void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss, - int nonagle); -extern bool tcp_may_send_now(struct sock *sk); -extern int __tcp_retransmit_skb(struct sock *, struct sk_buff *); -extern int tcp_retransmit_skb(struct sock *, struct sk_buff *); -extern void tcp_retransmit_timer(struct sock *sk); -extern void tcp_xmit_retransmit_queue(struct sock *); -extern void tcp_simple_retransmit(struct sock *); -extern int tcp_trim_head(struct sock *, struct sk_buff *, u32); -extern int tcp_fragment(struct sock *, struct sk_buff *, u32, unsigned int); - -extern void tcp_send_probe0(struct sock *); -extern void tcp_send_partial(struct sock *); -extern int tcp_write_wakeup(struct sock *); -extern void tcp_send_fin(struct sock *sk); -extern void tcp_send_active_reset(struct sock *sk, gfp_t priority); -extern int tcp_send_synack(struct sock *); -extern bool tcp_syn_flood_action(struct sock *sk, - const struct sk_buff *skb, - const char *proto); -extern void tcp_push_one(struct sock *, unsigned int mss_now); -extern void tcp_send_ack(struct sock *sk); -extern void tcp_send_delayed_ack(struct sock *sk); -extern void tcp_send_loss_probe(struct sock *sk); -extern bool tcp_schedule_loss_probe(struct sock *sk); +void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss, + int nonagle); +bool tcp_may_send_now(struct sock *sk); +int __tcp_retransmit_skb(struct sock *, struct sk_buff *); +int tcp_retransmit_skb(struct sock *, struct sk_buff *); +void tcp_retransmit_timer(struct sock *sk); +void tcp_xmit_retransmit_queue(struct sock *); +void tcp_simple_retransmit(struct sock *); +int tcp_trim_head(struct sock *, struct sk_buff *, u32); +int tcp_fragment(struct sock *, struct sk_buff *, u32, unsigned int); + +void tcp_send_probe0(struct sock *); +void tcp_send_partial(struct sock *); +int tcp_write_wakeup(struct sock *); +void tcp_send_fin(struct sock *sk); +void tcp_send_active_reset(struct sock *sk, gfp_t priority); +int tcp_send_synack(struct sock *); +bool tcp_syn_flood_action(struct sock *sk, const struct sk_buff *skb, + const char *proto); +void tcp_push_one(struct sock *, unsigned int mss_now); +void tcp_send_ack(struct sock *sk); +void tcp_send_delayed_ack(struct sock *sk); +void tcp_send_loss_probe(struct sock *sk); +bool tcp_schedule_loss_probe(struct sock *sk); /* tcp_input.c */ -extern void tcp_cwnd_application_limited(struct sock *sk); -extern void tcp_resume_early_retransmit(struct sock *sk); -extern void tcp_rearm_rto(struct sock *sk); -extern void tcp_reset(struct sock *sk); +void tcp_cwnd_application_limited(struct sock *sk); +void tcp_resume_early_retransmit(struct sock *sk); +void tcp_rearm_rto(struct sock *sk); +void tcp_reset(struct sock *sk); /* tcp_timer.c */ -extern void tcp_init_xmit_timers(struct sock *); +void tcp_init_xmit_timers(struct sock *); static inline void tcp_clear_xmit_timers(struct sock *sk) { inet_csk_clear_xmit_timers(sk); } -extern unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu); -extern unsigned int tcp_current_mss(struct sock *sk); +unsigned int tcp_sync_mss(struct sock *sk, u32 pmtu); +unsigned int tcp_current_mss(struct sock *sk); /* Bound MSS / TSO packet size with the half of the window */ static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize) @@ -584,20 +596,20 @@ static inline int tcp_bound_to_half_wnd(struct tcp_sock *tp, int pktsize) } /* tcp.c */ -extern void tcp_get_info(const struct sock *, struct tcp_info *); +void tcp_get_info(const struct sock *, struct tcp_info *); /* Read 'sendfile()'-style from a TCP socket */ typedef int (*sk_read_actor_t)(read_descriptor_t *, struct sk_buff *, unsigned int, size_t); -extern int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, - sk_read_actor_t recv_actor); +int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, + sk_read_actor_t recv_actor); -extern void tcp_initialize_rcv_mss(struct sock *sk); +void tcp_initialize_rcv_mss(struct sock *sk); -extern int tcp_mtu_to_mss(struct sock *sk, int pmtu); -extern int tcp_mss_to_mtu(struct sock *sk, int mss); -extern void tcp_mtup_init(struct sock *sk); -extern void tcp_init_buffer_space(struct sock *sk); +int tcp_mtu_to_mss(struct sock *sk, int pmtu); +int tcp_mss_to_mtu(struct sock *sk, int mss); +void tcp_mtup_init(struct sock *sk); +void tcp_init_buffer_space(struct sock *sk); static inline void tcp_bound_rto(const struct sock *sk) { @@ -610,7 +622,7 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp) return (tp->srtt >> 3) + tp->rttvar; } -extern void tcp_set_rto(struct sock *sk); +void tcp_set_rto(struct sock *sk); static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) { @@ -663,7 +675,7 @@ static inline u32 tcp_receive_window(const struct tcp_sock *tp) * scaling applied to the result. The caller does these things * if necessary. This is a "raw" window selection. */ -extern u32 __tcp_select_window(struct sock *sk); +u32 __tcp_select_window(struct sock *sk); void tcp_send_window_probe(struct sock *sk); @@ -784,7 +796,7 @@ struct tcp_congestion_ops { /* lower bound for congestion window (optional) */ u32 (*min_cwnd)(const struct sock *sk); /* do new cwnd calculation (required) */ - void (*cong_avoid)(struct sock *sk, u32 ack, u32 in_flight); + void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked, u32 in_flight); /* call before changing ca_state (optional) */ void (*set_state)(struct sock *sk, u8 new_state); /* call when cwnd event occurs (optional) */ @@ -800,24 +812,24 @@ struct tcp_congestion_ops { struct module *owner; }; -extern int tcp_register_congestion_control(struct tcp_congestion_ops *type); -extern void tcp_unregister_congestion_control(struct tcp_congestion_ops *type); +int tcp_register_congestion_control(struct tcp_congestion_ops *type); +void tcp_unregister_congestion_control(struct tcp_congestion_ops *type); -extern void tcp_init_congestion_control(struct sock *sk); -extern void tcp_cleanup_congestion_control(struct sock *sk); -extern int tcp_set_default_congestion_control(const char *name); -extern void tcp_get_default_congestion_control(char *name); -extern void tcp_get_available_congestion_control(char *buf, size_t len); -extern void tcp_get_allowed_congestion_control(char *buf, size_t len); -extern int tcp_set_allowed_congestion_control(char *allowed); -extern int tcp_set_congestion_control(struct sock *sk, const char *name); -extern void tcp_slow_start(struct tcp_sock *tp); -extern void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w); +void tcp_init_congestion_control(struct sock *sk); +void tcp_cleanup_congestion_control(struct sock *sk); +int tcp_set_default_congestion_control(const char *name); +void tcp_get_default_congestion_control(char *name); +void tcp_get_available_congestion_control(char *buf, size_t len); +void tcp_get_allowed_congestion_control(char *buf, size_t len); +int tcp_set_allowed_congestion_control(char *allowed); +int tcp_set_congestion_control(struct sock *sk, const char *name); +int tcp_slow_start(struct tcp_sock *tp, u32 acked); +void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w); extern struct tcp_congestion_ops tcp_init_congestion_ops; -extern u32 tcp_reno_ssthresh(struct sock *sk); -extern void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight); -extern u32 tcp_reno_min_cwnd(const struct sock *sk); +u32 tcp_reno_ssthresh(struct sock *sk); +void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight); +u32 tcp_reno_min_cwnd(const struct sock *sk); extern struct tcp_congestion_ops tcp_reno; static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state) @@ -936,8 +948,8 @@ static inline __u32 tcp_current_ssthresh(const struct sock *sk) /* Use define here intentionally to get WARN_ON location shown at the caller */ #define tcp_verify_left_out(tp) WARN_ON(tcp_left_out(tp) > tp->packets_out) -extern void tcp_enter_cwr(struct sock *sk, const int set_ssthresh); -extern __u32 tcp_init_cwnd(const struct tcp_sock *tp, const struct dst_entry *dst); +void tcp_enter_cwr(struct sock *sk, const int set_ssthresh); +__u32 tcp_init_cwnd(const struct tcp_sock *tp, const struct dst_entry *dst); /* The maximum number of MSS of available cwnd for which TSO defers * sending if not using sysctl_tcp_tso_win_divisor. @@ -963,7 +975,7 @@ static inline u32 tcp_wnd_end(const struct tcp_sock *tp) { return tp->snd_una + tp->snd_wnd; } -extern bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight); +bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight); static inline void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss, const struct sk_buff *skb) @@ -1028,7 +1040,7 @@ static inline void tcp_prequeue_init(struct tcp_sock *tp) #endif } -extern bool tcp_prequeue(struct sock *sk, struct sk_buff *skb); +bool tcp_prequeue(struct sock *sk, struct sk_buff *skb); #undef STATE_TRACE @@ -1039,9 +1051,9 @@ static const char *statename[]={ "Close Wait","Last ACK","Listen","Closing" }; #endif -extern void tcp_set_state(struct sock *sk, int state); +void tcp_set_state(struct sock *sk, int state); -extern void tcp_done(struct sock *sk); +void tcp_done(struct sock *sk); static inline void tcp_sack_reset(struct tcp_options_received *rx_opt) { @@ -1049,13 +1061,12 @@ static inline void tcp_sack_reset(struct tcp_options_received *rx_opt) rx_opt->num_sacks = 0; } -extern u32 tcp_default_init_rwnd(u32 mss); +u32 tcp_default_init_rwnd(u32 mss); /* Determine a window scaling and initial window to offer. */ -extern void tcp_select_initial_window(int __space, __u32 mss, - __u32 *rcv_wnd, __u32 *window_clamp, - int wscale_ok, __u8 *rcv_wscale, - __u32 init_rcv_wnd); +void tcp_select_initial_window(int __space, __u32 mss, __u32 *rcv_wnd, + __u32 *window_clamp, int wscale_ok, + __u8 *rcv_wscale, __u32 init_rcv_wnd); static inline int tcp_win_from_space(int space) { @@ -1095,11 +1106,11 @@ static inline void tcp_openreq_init(struct request_sock *req, ireq->wscale_ok = rx_opt->wscale_ok; ireq->acked = 0; ireq->ecn_ok = 0; - ireq->rmt_port = tcp_hdr(skb)->source; - ireq->loc_port = tcp_hdr(skb)->dest; + ireq->ir_rmt_port = tcp_hdr(skb)->source; + ireq->ir_num = ntohs(tcp_hdr(skb)->dest); } -extern void tcp_enter_memory_pressure(struct sock *sk); +void tcp_enter_memory_pressure(struct sock *sk); static inline int keepalive_intvl_when(const struct tcp_sock *tp) { @@ -1252,21 +1263,20 @@ struct tcp_md5sig_pool { }; /* - functions */ -extern int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, - const struct sock *sk, - const struct request_sock *req, - const struct sk_buff *skb); -extern int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, - int family, const u8 *newkey, - u8 newkeylen, gfp_t gfp); -extern int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, - int family); -extern struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, +int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, + const struct sock *sk, const struct request_sock *req, + const struct sk_buff *skb); +int tcp_md5_do_add(struct sock *sk, const union tcp_md5_addr *addr, + int family, const u8 *newkey, u8 newkeylen, gfp_t gfp); +int tcp_md5_do_del(struct sock *sk, const union tcp_md5_addr *addr, + int family); +struct tcp_md5sig_key *tcp_v4_md5_lookup(struct sock *sk, struct sock *addr_sk); #ifdef CONFIG_TCP_MD5SIG -extern struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, - const union tcp_md5_addr *addr, int family); +struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, + const union tcp_md5_addr *addr, + int family); #define tcp_twsk_md5_key(twsk) ((twsk)->tw_md5_key) #else static inline struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, @@ -1278,27 +1288,26 @@ static inline struct tcp_md5sig_key *tcp_md5_do_lookup(struct sock *sk, #define tcp_twsk_md5_key(twsk) NULL #endif -extern bool tcp_alloc_md5sig_pool(void); +bool tcp_alloc_md5sig_pool(void); -extern struct tcp_md5sig_pool *tcp_get_md5sig_pool(void); +struct tcp_md5sig_pool *tcp_get_md5sig_pool(void); static inline void tcp_put_md5sig_pool(void) { local_bh_enable(); } -extern int tcp_md5_hash_header(struct tcp_md5sig_pool *, const struct tcphdr *); -extern int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *, - unsigned int header_len); -extern int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, - const struct tcp_md5sig_key *key); +int tcp_md5_hash_header(struct tcp_md5sig_pool *, const struct tcphdr *); +int tcp_md5_hash_skb_data(struct tcp_md5sig_pool *, const struct sk_buff *, + unsigned int header_len); +int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, + const struct tcp_md5sig_key *key); /* From tcp_fastopen.c */ -extern void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, - struct tcp_fastopen_cookie *cookie, - int *syn_loss, unsigned long *last_syn_loss); -extern void tcp_fastopen_cache_set(struct sock *sk, u16 mss, - struct tcp_fastopen_cookie *cookie, - bool syn_lost); +void tcp_fastopen_cache_get(struct sock *sk, u16 *mss, + struct tcp_fastopen_cookie *cookie, int *syn_loss, + unsigned long *last_syn_loss); +void tcp_fastopen_cache_set(struct sock *sk, u16 mss, + struct tcp_fastopen_cookie *cookie, bool syn_lost); struct tcp_fastopen_request { /* Fast Open cookie. Size 0 means a cookie request */ struct tcp_fastopen_cookie cookie; @@ -1309,9 +1318,9 @@ void tcp_free_fastopen_req(struct tcp_sock *tp); extern struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; int tcp_fastopen_reset_cipher(void *key, unsigned int len); -extern void tcp_fastopen_cookie_gen(__be32 src, __be32 dst, - struct tcp_fastopen_cookie *foc); - +void tcp_fastopen_cookie_gen(__be32 src, __be32 dst, + struct tcp_fastopen_cookie *foc); +void tcp_fastopen_init_key_once(bool publish); #define TCP_FASTOPEN_KEY_LENGTH 16 /* Fastopen key context */ @@ -1507,7 +1516,6 @@ enum tcp_seq_states { TCP_SEQ_STATE_LISTENING, TCP_SEQ_STATE_OPENREQ, TCP_SEQ_STATE_ESTABLISHED, - TCP_SEQ_STATE_TIME_WAIT, }; int tcp_seq_open(struct inode *inode, struct file *file); @@ -1529,22 +1537,20 @@ struct tcp_iter_state { loff_t last_pos; }; -extern int tcp_proc_register(struct net *net, struct tcp_seq_afinfo *afinfo); -extern void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo); +int tcp_proc_register(struct net *net, struct tcp_seq_afinfo *afinfo); +void tcp_proc_unregister(struct net *net, struct tcp_seq_afinfo *afinfo); extern struct request_sock_ops tcp_request_sock_ops; extern struct request_sock_ops tcp6_request_sock_ops; -extern void tcp_v4_destroy_sock(struct sock *sk); +void tcp_v4_destroy_sock(struct sock *sk); -extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, - netdev_features_t features); -extern struct sk_buff **tcp_gro_receive(struct sk_buff **head, - struct sk_buff *skb); -extern int tcp_gro_complete(struct sk_buff *skb); +struct sk_buff *tcp_gso_segment(struct sk_buff *skb, + netdev_features_t features); +struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb); +int tcp_gro_complete(struct sk_buff *skb); -extern void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, - __be32 daddr); +void __tcp_v4_send_check(struct sk_buff *skb, __be32 saddr, __be32 daddr); static inline u32 tcp_notsent_lowat(const struct tcp_sock *tp) { @@ -1560,8 +1566,8 @@ static inline bool tcp_stream_memory_free(const struct sock *sk) } #ifdef CONFIG_PROC_FS -extern int tcp4_proc_init(void); -extern void tcp4_proc_exit(void); +int tcp4_proc_init(void); +void tcp4_proc_exit(void); #endif /* TCP af-specific functions */ @@ -1592,9 +1598,9 @@ struct tcp_request_sock_ops { #endif }; -extern int tcpv4_offload_init(void); +int tcpv4_offload_init(void); -extern void tcp_v4_init(void); -extern void tcp_init(void); +void tcp_v4_init(void); +void tcp_init(void); #endif /* _TCP_H */ diff --git a/include/net/tcp_memcontrol.h b/include/net/tcp_memcontrol.h index 7df18bc43a97fccef8478436f8f43f3a5553b997..05b94d9453de8b036f1c275caa268b6f8113eac7 100644 --- a/include/net/tcp_memcontrol.h +++ b/include/net/tcp_memcontrol.h @@ -1,19 +1,7 @@ #ifndef _TCP_MEMCG_H #define _TCP_MEMCG_H -struct tcp_memcontrol { - struct cg_proto cg_proto; - /* per-cgroup tcp memory pressure knobs */ - struct res_counter tcp_memory_allocated; - struct percpu_counter tcp_sockets_allocated; - /* those two are read-mostly, leave them at the end */ - long tcp_prot_mem[3]; - int tcp_memory_pressure; -}; - struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg); int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss); void tcp_destroy_cgroup(struct mem_cgroup *memcg); -unsigned long long tcp_max_memory(const struct mem_cgroup *memcg); -void tcp_prot_mem(struct mem_cgroup *memcg, long val, int idx); #endif /* _TCP_MEMCG_H */ diff --git a/include/net/udp.h b/include/net/udp.h index ef2e0b7843a0036c1b31d8e67e78b624bf05f44f..a24f0f3e107f67c71b256cd4fb85d04b2389fb7a 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -79,7 +79,7 @@ struct udp_table { unsigned int log; }; extern struct udp_table udp_table; -extern void udp_table_init(struct udp_table *, const char *); +void udp_table_init(struct udp_table *, const char *); static inline struct udp_hslot *udp_hashslot(struct udp_table *table, struct net *net, unsigned int num) { @@ -162,52 +162,53 @@ static inline void udp_lib_hash(struct sock *sk) BUG(); } -extern void udp_lib_unhash(struct sock *sk); -extern void udp_lib_rehash(struct sock *sk, u16 new_hash); +void udp_lib_unhash(struct sock *sk); +void udp_lib_rehash(struct sock *sk, u16 new_hash); static inline void udp_lib_close(struct sock *sk, long timeout) { sk_common_release(sk); } -extern int udp_lib_get_port(struct sock *sk, unsigned short snum, - int (*)(const struct sock *,const struct sock *), - unsigned int hash2_nulladdr); +int udp_lib_get_port(struct sock *sk, unsigned short snum, + int (*)(const struct sock *, const struct sock *), + unsigned int hash2_nulladdr); /* net/ipv4/udp.c */ -extern int udp_get_port(struct sock *sk, unsigned short snum, - int (*saddr_cmp)(const struct sock *, - const struct sock *)); -extern void udp_err(struct sk_buff *, u32); -extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len); -extern int udp_push_pending_frames(struct sock *sk); -extern void udp_flush_pending_frames(struct sock *sk); -extern void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst); -extern int udp_rcv(struct sk_buff *skb); -extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); -extern int udp_disconnect(struct sock *sk, int flags); -extern unsigned int udp_poll(struct file *file, struct socket *sock, - poll_table *wait); -extern struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, - netdev_features_t features); -extern int udp_lib_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen); -extern int udp_lib_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen, - int (*push_pending_frames)(struct sock *)); -extern struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, - __be32 daddr, __be16 dport, - int dif); -extern struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, - __be32 daddr, __be16 dport, - int dif, struct udp_table *tbl); -extern struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport, - const struct in6_addr *daddr, __be16 dport, - int dif); -extern struct sock *__udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport, - const struct in6_addr *daddr, __be16 dport, - int dif, struct udp_table *tbl); +void udp_v4_early_demux(struct sk_buff *skb); +int udp_get_port(struct sock *sk, unsigned short snum, + int (*saddr_cmp)(const struct sock *, + const struct sock *)); +void udp_err(struct sk_buff *, u32); +int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len); +int udp_push_pending_frames(struct sock *sk); +void udp_flush_pending_frames(struct sock *sk); +void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst); +int udp_rcv(struct sk_buff *skb); +int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); +int udp_disconnect(struct sock *sk, int flags); +unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait); +struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb, + netdev_features_t features); +int udp_lib_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); +int udp_lib_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen, + int (*push_pending_frames)(struct sock *)); +struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, + __be32 daddr, __be16 dport, int dif); +struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, + __be32 daddr, __be16 dport, int dif, + struct udp_table *tbl); +struct sock *udp6_lib_lookup(struct net *net, + const struct in6_addr *saddr, __be16 sport, + const struct in6_addr *daddr, __be16 dport, + int dif); +struct sock *__udp6_lib_lookup(struct net *net, + const struct in6_addr *saddr, __be16 sport, + const struct in6_addr *daddr, __be16 dport, + int dif, struct udp_table *tbl); /* * SNMP statistics for UDP and UDP-Lite @@ -229,13 +230,13 @@ extern struct sock *__udp6_lib_lookup(struct net *net, const struct in6_addr *sa } while(0) #if IS_ENABLED(CONFIG_IPV6) -#define UDPX_INC_STATS_BH(sk, field) \ - do { \ - if ((sk)->sk_family == AF_INET) \ - UDP_INC_STATS_BH(sock_net(sk), field, 0); \ - else \ - UDP6_INC_STATS_BH(sock_net(sk), field, 0); \ - } while (0); +#define UDPX_INC_STATS_BH(sk, field) \ +do { \ + if ((sk)->sk_family == AF_INET) \ + UDP_INC_STATS_BH(sock_net(sk), field, 0); \ + else \ + UDP6_INC_STATS_BH(sock_net(sk), field, 0); \ +} while (0) #else #define UDPX_INC_STATS_BH(sk, field) UDP_INC_STATS_BH(sock_net(sk), field, 0) #endif @@ -259,19 +260,19 @@ struct udp_iter_state { }; #ifdef CONFIG_PROC_FS -extern int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo); -extern void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo); +int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo); +void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo); -extern int udp4_proc_init(void); -extern void udp4_proc_exit(void); +int udp4_proc_init(void); +void udp4_proc_exit(void); #endif -extern int udpv4_offload_init(void); +int udpv4_offload_init(void); -extern void udp_init(void); +void udp_init(void); -extern void udp_encap_enable(void); +void udp_encap_enable(void); #if IS_ENABLED(CONFIG_IPV6) -extern void udpv6_encap_enable(void); +void udpv6_encap_enable(void); #endif #endif /* _UDP_H */ diff --git a/include/net/udplite.h b/include/net/udplite.h index 71375459a8843da6cbbf7062a0e71bf90a6ee716..2caadabcd07baf6552098082fcb78672f772dcb5 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h @@ -126,7 +126,7 @@ static inline __wsum udplite_csum(struct sk_buff *skb) return skb_checksum(skb, off, len, 0); } -extern void udplite4_register(void); -extern int udplite_get_port(struct sock *sk, unsigned short snum, - int (*scmp)(const struct sock *, const struct sock *)); +void udplite4_register(void); +int udplite_get_port(struct sock *sk, unsigned short snum, + int (*scmp)(const struct sock *, const struct sock *)); #endif /* _UDPLITE_H */ diff --git a/include/net/vxlan.h b/include/net/vxlan.h index 2d64d3cd4999272beecc5cab9e4890a1ac6479a6..6b6d180fb91a2b9f15007884be9b9a5801c94c02 100644 --- a/include/net/vxlan.h +++ b/include/net/vxlan.h @@ -36,5 +36,16 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, __be16 vxlan_src_port(__u16 port_min, __u16 port_max, struct sk_buff *skb); +/* IP header + UDP + VXLAN + Ethernet header */ +#define VXLAN_HEADROOM (20 + 8 + 8 + 14) +/* IPv6 header + UDP + VXLAN + Ethernet header */ +#define VXLAN6_HEADROOM (40 + 8 + 8 + 14) + +#if IS_ENABLED(CONFIG_VXLAN) void vxlan_get_rx_port(struct net_device *netdev); +#else +static inline void vxlan_get_rx_port(struct net_device *netdev) +{ +} +#endif #endif diff --git a/include/net/wext.h b/include/net/wext.h index 4f6e7423174cd55890d7845326c8352f66146f04..345911965dbb8f53289d63535828969454ae3333 100644 --- a/include/net/wext.h +++ b/include/net/wext.h @@ -6,13 +6,13 @@ struct net; #ifdef CONFIG_WEXT_CORE -extern int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, - void __user *arg); -extern int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, - unsigned long arg); +int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, + void __user *arg); +int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, + unsigned long arg); -extern struct iw_statistics *get_wireless_stats(struct net_device *dev); -extern int call_commit_handler(struct net_device *dev); +struct iw_statistics *get_wireless_stats(struct net_device *dev); +int call_commit_handler(struct net_device *dev); #else static inline int wext_handle_ioctl(struct net *net, struct ifreq *ifr, unsigned int cmd, void __user *arg) @@ -27,8 +27,8 @@ static inline int compat_wext_handle_ioctl(struct net *net, unsigned int cmd, #endif #ifdef CONFIG_WEXT_PROC -extern int wext_proc_init(struct net *net); -extern void wext_proc_exit(struct net *net); +int wext_proc_init(struct net *net); +void wext_proc_exit(struct net *net); #else static inline int wext_proc_init(struct net *net) { diff --git a/include/net/wimax.h b/include/net/wimax.h index bbb74f990cab784a5e9ffb4dc6b391977538cf49..98498e1daa060524af05c639fe74a951f2d307e8 100644 --- a/include/net/wimax.h +++ b/include/net/wimax.h @@ -438,9 +438,9 @@ struct wimax_dev { * * These functions are not exported to user space. */ -extern void wimax_dev_init(struct wimax_dev *); -extern int wimax_dev_add(struct wimax_dev *, struct net_device *); -extern void wimax_dev_rm(struct wimax_dev *); +void wimax_dev_init(struct wimax_dev *); +int wimax_dev_add(struct wimax_dev *, struct net_device *); +void wimax_dev_rm(struct wimax_dev *); static inline struct wimax_dev *net_dev_to_wimax(struct net_device *net_dev) @@ -454,8 +454,8 @@ struct device *wimax_dev_to_dev(struct wimax_dev *wimax_dev) return wimax_dev->net_dev->dev.parent; } -extern void wimax_state_change(struct wimax_dev *, enum wimax_st); -extern enum wimax_st wimax_state_get(struct wimax_dev *); +void wimax_state_change(struct wimax_dev *, enum wimax_st); +enum wimax_st wimax_state_get(struct wimax_dev *); /* * Radio Switch state reporting. @@ -463,8 +463,8 @@ extern enum wimax_st wimax_state_get(struct wimax_dev *); * enum wimax_rf_state is declared in linux/wimax.h so the exports * to user space can use it. */ -extern void wimax_report_rfkill_hw(struct wimax_dev *, enum wimax_rf_state); -extern void wimax_report_rfkill_sw(struct wimax_dev *, enum wimax_rf_state); +void wimax_report_rfkill_hw(struct wimax_dev *, enum wimax_rf_state); +void wimax_report_rfkill_sw(struct wimax_dev *, enum wimax_rf_state); /* @@ -490,15 +490,14 @@ extern void wimax_report_rfkill_sw(struct wimax_dev *, enum wimax_rf_state); * send diagnostics information that a device-specific diagnostics * tool would be interested in. */ -extern struct sk_buff *wimax_msg_alloc(struct wimax_dev *, const char *, - const void *, size_t, gfp_t); -extern int wimax_msg_send(struct wimax_dev *, struct sk_buff *); -extern int wimax_msg(struct wimax_dev *, const char *, - const void *, size_t, gfp_t); +struct sk_buff *wimax_msg_alloc(struct wimax_dev *, const char *, const void *, + size_t, gfp_t); +int wimax_msg_send(struct wimax_dev *, struct sk_buff *); +int wimax_msg(struct wimax_dev *, const char *, const void *, size_t, gfp_t); -extern const void *wimax_msg_data_len(struct sk_buff *, size_t *); -extern const void *wimax_msg_data(struct sk_buff *); -extern ssize_t wimax_msg_len(struct sk_buff *); +const void *wimax_msg_data_len(struct sk_buff *, size_t *); +const void *wimax_msg_data(struct sk_buff *); +ssize_t wimax_msg_len(struct sk_buff *); /* @@ -513,7 +512,7 @@ extern ssize_t wimax_msg_len(struct sk_buff *); * device's control structure and (as such) the 'struct wimax_dev' is * referenced by the caller. */ -extern int wimax_rfkill(struct wimax_dev *, enum wimax_rf_state); -extern int wimax_reset(struct wimax_dev *); +int wimax_rfkill(struct wimax_dev *, enum wimax_rf_state); +int wimax_reset(struct wimax_dev *); #endif /* #ifndef __NET__WIMAX_H__ */ diff --git a/include/net/x25.h b/include/net/x25.h index b4a8a8923128fbc68a9aa0d6dd2fb5dd5524de32..c383aa4edbf0c27980ef7aad22ff160c72b41bbd 100644 --- a/include/net/x25.h +++ b/include/net/x25.h @@ -187,57 +187,57 @@ extern int sysctl_x25_clear_request_timeout; extern int sysctl_x25_ack_holdback_timeout; extern int sysctl_x25_forward; -extern int x25_parse_address_block(struct sk_buff *skb, - struct x25_address *called_addr, - struct x25_address *calling_addr); - -extern int x25_addr_ntoa(unsigned char *, struct x25_address *, - struct x25_address *); -extern int x25_addr_aton(unsigned char *, struct x25_address *, - struct x25_address *); -extern struct sock *x25_find_socket(unsigned int, struct x25_neigh *); -extern void x25_destroy_socket_from_timer(struct sock *); -extern int x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned int); -extern void x25_kill_by_neigh(struct x25_neigh *); +int x25_parse_address_block(struct sk_buff *skb, + struct x25_address *called_addr, + struct x25_address *calling_addr); + +int x25_addr_ntoa(unsigned char *, struct x25_address *, struct x25_address *); +int x25_addr_aton(unsigned char *, struct x25_address *, struct x25_address *); +struct sock *x25_find_socket(unsigned int, struct x25_neigh *); +void x25_destroy_socket_from_timer(struct sock *); +int x25_rx_call_request(struct sk_buff *, struct x25_neigh *, unsigned int); +void x25_kill_by_neigh(struct x25_neigh *); /* x25_dev.c */ -extern void x25_send_frame(struct sk_buff *, struct x25_neigh *); -extern int x25_lapb_receive_frame(struct sk_buff *, struct net_device *, struct packet_type *, struct net_device *); -extern void x25_establish_link(struct x25_neigh *); -extern void x25_terminate_link(struct x25_neigh *); +void x25_send_frame(struct sk_buff *, struct x25_neigh *); +int x25_lapb_receive_frame(struct sk_buff *, struct net_device *, + struct packet_type *, struct net_device *); +void x25_establish_link(struct x25_neigh *); +void x25_terminate_link(struct x25_neigh *); /* x25_facilities.c */ -extern int x25_parse_facilities(struct sk_buff *, struct x25_facilities *, - struct x25_dte_facilities *, unsigned long *); -extern int x25_create_facilities(unsigned char *, struct x25_facilities *, - struct x25_dte_facilities *, unsigned long); -extern int x25_negotiate_facilities(struct sk_buff *, struct sock *, - struct x25_facilities *, - struct x25_dte_facilities *); -extern void x25_limit_facilities(struct x25_facilities *, struct x25_neigh *); +int x25_parse_facilities(struct sk_buff *, struct x25_facilities *, + struct x25_dte_facilities *, unsigned long *); +int x25_create_facilities(unsigned char *, struct x25_facilities *, + struct x25_dte_facilities *, unsigned long); +int x25_negotiate_facilities(struct sk_buff *, struct sock *, + struct x25_facilities *, + struct x25_dte_facilities *); +void x25_limit_facilities(struct x25_facilities *, struct x25_neigh *); /* x25_forward.c */ -extern void x25_clear_forward_by_lci(unsigned int lci); -extern void x25_clear_forward_by_dev(struct net_device *); -extern int x25_forward_data(int, struct x25_neigh *, struct sk_buff *); -extern int x25_forward_call(struct x25_address *, struct x25_neigh *, - struct sk_buff *, int); +void x25_clear_forward_by_lci(unsigned int lci); +void x25_clear_forward_by_dev(struct net_device *); +int x25_forward_data(int, struct x25_neigh *, struct sk_buff *); +int x25_forward_call(struct x25_address *, struct x25_neigh *, struct sk_buff *, + int); /* x25_in.c */ -extern int x25_process_rx_frame(struct sock *, struct sk_buff *); -extern int x25_backlog_rcv(struct sock *, struct sk_buff *); +int x25_process_rx_frame(struct sock *, struct sk_buff *); +int x25_backlog_rcv(struct sock *, struct sk_buff *); /* x25_link.c */ -extern void x25_link_control(struct sk_buff *, struct x25_neigh *, unsigned short); -extern void x25_link_device_up(struct net_device *); -extern void x25_link_device_down(struct net_device *); -extern void x25_link_established(struct x25_neigh *); -extern void x25_link_terminated(struct x25_neigh *); -extern void x25_transmit_clear_request(struct x25_neigh *, unsigned int, unsigned char); -extern void x25_transmit_link(struct sk_buff *, struct x25_neigh *); -extern int x25_subscr_ioctl(unsigned int, void __user *); -extern struct x25_neigh *x25_get_neigh(struct net_device *); -extern void x25_link_free(void); +void x25_link_control(struct sk_buff *, struct x25_neigh *, unsigned short); +void x25_link_device_up(struct net_device *); +void x25_link_device_down(struct net_device *); +void x25_link_established(struct x25_neigh *); +void x25_link_terminated(struct x25_neigh *); +void x25_transmit_clear_request(struct x25_neigh *, unsigned int, + unsigned char); +void x25_transmit_link(struct sk_buff *, struct x25_neigh *); +int x25_subscr_ioctl(unsigned int, void __user *); +struct x25_neigh *x25_get_neigh(struct net_device *); +void x25_link_free(void); /* x25_neigh.c */ static __inline__ void x25_neigh_hold(struct x25_neigh *nb) @@ -252,16 +252,16 @@ static __inline__ void x25_neigh_put(struct x25_neigh *nb) } /* x25_out.c */ -extern int x25_output(struct sock *, struct sk_buff *); -extern void x25_kick(struct sock *); -extern void x25_enquiry_response(struct sock *); +int x25_output(struct sock *, struct sk_buff *); +void x25_kick(struct sock *); +void x25_enquiry_response(struct sock *); /* x25_route.c */ -extern struct x25_route *x25_get_route(struct x25_address *addr); -extern struct net_device *x25_dev_get(char *); -extern void x25_route_device_down(struct net_device *dev); -extern int x25_route_ioctl(unsigned int, void __user *); -extern void x25_route_free(void); +struct x25_route *x25_get_route(struct x25_address *addr); +struct net_device *x25_dev_get(char *); +void x25_route_device_down(struct net_device *dev); +int x25_route_ioctl(unsigned int, void __user *); +void x25_route_free(void); static __inline__ void x25_route_hold(struct x25_route *rt) { @@ -275,30 +275,31 @@ static __inline__ void x25_route_put(struct x25_route *rt) } /* x25_subr.c */ -extern void x25_clear_queues(struct sock *); -extern void x25_frames_acked(struct sock *, unsigned short); -extern void x25_requeue_frames(struct sock *); -extern int x25_validate_nr(struct sock *, unsigned short); -extern void x25_write_internal(struct sock *, int); -extern int x25_decode(struct sock *, struct sk_buff *, int *, int *, int *, int *, int *); -extern void x25_disconnect(struct sock *, int, unsigned char, unsigned char); +void x25_clear_queues(struct sock *); +void x25_frames_acked(struct sock *, unsigned short); +void x25_requeue_frames(struct sock *); +int x25_validate_nr(struct sock *, unsigned short); +void x25_write_internal(struct sock *, int); +int x25_decode(struct sock *, struct sk_buff *, int *, int *, int *, int *, + int *); +void x25_disconnect(struct sock *, int, unsigned char, unsigned char); /* x25_timer.c */ -extern void x25_init_timers(struct sock *sk); -extern void x25_start_heartbeat(struct sock *); -extern void x25_start_t2timer(struct sock *); -extern void x25_start_t21timer(struct sock *); -extern void x25_start_t22timer(struct sock *); -extern void x25_start_t23timer(struct sock *); -extern void x25_stop_heartbeat(struct sock *); -extern void x25_stop_timer(struct sock *); -extern unsigned long x25_display_timer(struct sock *); -extern void x25_check_rbuf(struct sock *); +void x25_init_timers(struct sock *sk); +void x25_start_heartbeat(struct sock *); +void x25_start_t2timer(struct sock *); +void x25_start_t21timer(struct sock *); +void x25_start_t22timer(struct sock *); +void x25_start_t23timer(struct sock *); +void x25_stop_heartbeat(struct sock *); +void x25_stop_timer(struct sock *); +unsigned long x25_display_timer(struct sock *); +void x25_check_rbuf(struct sock *); /* sysctl_net_x25.c */ #ifdef CONFIG_SYSCTL -extern void x25_register_sysctl(void); -extern void x25_unregister_sysctl(void); +void x25_register_sysctl(void); +void x25_unregister_sysctl(void); #else static inline void x25_register_sysctl(void) {}; static inline void x25_unregister_sysctl(void) {}; @@ -318,6 +319,6 @@ extern rwlock_t x25_forward_list_lock; extern struct list_head x25_neigh_list; extern rwlock_t x25_neigh_list_lock; -extern int x25_proc_init(void); -extern void x25_proc_exit(void); +int x25_proc_init(void); +void x25_proc_exit(void); #endif diff --git a/include/net/xfrm.h b/include/net/xfrm.h index e253bf0cc7ef005721de3b7e8729cfabce52854f..6b82fdf4ba716898ea53fb0cf2e66690a66d1479 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -307,15 +307,17 @@ struct xfrm_policy_afinfo { struct dst_entry *(*blackhole_route)(struct net *net, struct dst_entry *orig); }; -extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); -extern int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo); -extern void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c); -extern void km_state_notify(struct xfrm_state *x, const struct km_event *c); +int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); +int xfrm_policy_unregister_afinfo(struct xfrm_policy_afinfo *afinfo); +void km_policy_notify(struct xfrm_policy *xp, int dir, + const struct km_event *c); +void km_state_notify(struct xfrm_state *x, const struct km_event *c); struct xfrm_tmpl; -extern int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol); -extern void km_state_expired(struct xfrm_state *x, int hard, u32 portid); -extern int __xfrm_state_delete(struct xfrm_state *x); +int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, + struct xfrm_policy *pol); +void km_state_expired(struct xfrm_state *x, int hard, u32 portid); +int __xfrm_state_delete(struct xfrm_state *x); struct xfrm_state_afinfo { unsigned int family; @@ -344,12 +346,12 @@ struct xfrm_state_afinfo { void (*local_error)(struct sk_buff *skb, u32 mtu); }; -extern int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo); -extern int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo); -extern struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); -extern void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); +int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo); +int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo); +struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family); +void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo); -extern void xfrm_state_delete_tunnel(struct xfrm_state *x); +void xfrm_state_delete_tunnel(struct xfrm_state *x); struct xfrm_type { char *description; @@ -372,8 +374,8 @@ struct xfrm_type { u32 (*get_mtu)(struct xfrm_state *, int size); }; -extern int xfrm_register_type(const struct xfrm_type *type, unsigned short family); -extern int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family); +int xfrm_register_type(const struct xfrm_type *type, unsigned short family); +int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family); struct xfrm_mode { /* @@ -434,8 +436,8 @@ enum { XFRM_MODE_FLAG_TUNNEL = 1, }; -extern int xfrm_register_mode(struct xfrm_mode *mode, int family); -extern int xfrm_unregister_mode(struct xfrm_mode *mode, int family); +int xfrm_register_mode(struct xfrm_mode *mode, int family); +int xfrm_unregister_mode(struct xfrm_mode *mode, int family); static inline int xfrm_af2proto(unsigned int family) { @@ -595,8 +597,8 @@ struct xfrm_mgr { const struct xfrm_kmaddress *k); }; -extern int xfrm_register_km(struct xfrm_mgr *km); -extern int xfrm_unregister_km(struct xfrm_mgr *km); +int xfrm_register_km(struct xfrm_mgr *km); +int xfrm_unregister_km(struct xfrm_mgr *km); /* * This structure is used for the duration where packets are being @@ -713,23 +715,23 @@ static inline void xfrm_audit_helper_usrinfo(kuid_t auid, u32 ses, u32 secid, audit_log_task_context(audit_buf); } -extern void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, - kuid_t auid, u32 ses, u32 secid); -extern void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, - kuid_t auid, u32 ses, u32 secid); -extern void xfrm_audit_state_add(struct xfrm_state *x, int result, - kuid_t auid, u32 ses, u32 secid); -extern void xfrm_audit_state_delete(struct xfrm_state *x, int result, - kuid_t auid, u32 ses, u32 secid); -extern void xfrm_audit_state_replay_overflow(struct xfrm_state *x, - struct sk_buff *skb); -extern void xfrm_audit_state_replay(struct xfrm_state *x, - struct sk_buff *skb, __be32 net_seq); -extern void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family); -extern void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family, - __be32 net_spi, __be32 net_seq); -extern void xfrm_audit_state_icvfail(struct xfrm_state *x, - struct sk_buff *skb, u8 proto); +void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, kuid_t auid, + u32 ses, u32 secid); +void xfrm_audit_policy_delete(struct xfrm_policy *xp, int result, kuid_t auid, + u32 ses, u32 secid); +void xfrm_audit_state_add(struct xfrm_state *x, int result, kuid_t auid, + u32 ses, u32 secid); +void xfrm_audit_state_delete(struct xfrm_state *x, int result, kuid_t auid, + u32 ses, u32 secid); +void xfrm_audit_state_replay_overflow(struct xfrm_state *x, + struct sk_buff *skb); +void xfrm_audit_state_replay(struct xfrm_state *x, struct sk_buff *skb, + __be32 net_seq); +void xfrm_audit_state_notfound_simple(struct sk_buff *skb, u16 family); +void xfrm_audit_state_notfound(struct sk_buff *skb, u16 family, __be32 net_spi, + __be32 net_seq); +void xfrm_audit_state_icvfail(struct xfrm_state *x, struct sk_buff *skb, + u8 proto); #else static inline void xfrm_audit_policy_add(struct xfrm_policy *xp, int result, @@ -784,7 +786,7 @@ static inline void xfrm_pol_hold(struct xfrm_policy *policy) atomic_inc(&policy->refcnt); } -extern void xfrm_policy_destroy(struct xfrm_policy *policy); +void xfrm_policy_destroy(struct xfrm_policy *policy); static inline void xfrm_pol_put(struct xfrm_policy *policy) { @@ -799,7 +801,7 @@ static inline void xfrm_pols_put(struct xfrm_policy **pols, int npols) xfrm_pol_put(pols[i]); } -extern void __xfrm_state_destroy(struct xfrm_state *); +void __xfrm_state_destroy(struct xfrm_state *); static inline void __xfrm_state_put(struct xfrm_state *x) { @@ -903,9 +905,8 @@ __be16 xfrm_flowi_dport(const struct flowi *fl, const union flowi_uli *uli) return port; } -extern bool xfrm_selector_match(const struct xfrm_selector *sel, - const struct flowi *fl, - unsigned short family); +bool xfrm_selector_match(const struct xfrm_selector *sel, + const struct flowi *fl, unsigned short family); #ifdef CONFIG_SECURITY_NETWORK_XFRM /* If neither has a context --> match @@ -975,7 +976,7 @@ static inline void xfrm_dst_destroy(struct xfrm_dst *xdst) } #endif -extern void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev); +void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev); struct sec_path { atomic_t refcnt; @@ -1000,7 +1001,7 @@ secpath_get(struct sec_path *sp) return sp; } -extern void __secpath_destroy(struct sec_path *sp); +void __secpath_destroy(struct sec_path *sp); static inline void secpath_put(struct sec_path *sp) @@ -1009,7 +1010,7 @@ secpath_put(struct sec_path *sp) __secpath_destroy(sp); } -extern struct sec_path *secpath_dup(struct sec_path *src); +struct sec_path *secpath_dup(struct sec_path *src); static inline void secpath_reset(struct sk_buff *skb) @@ -1059,7 +1060,8 @@ xfrm_state_addr_cmp(const struct xfrm_tmpl *tmpl, const struct xfrm_state *x, un } #ifdef CONFIG_XFRM -extern int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, unsigned short family); +int __xfrm_policy_check(struct sock *, int dir, struct sk_buff *skb, + unsigned short family); static inline int __xfrm_policy_check2(struct sock *sk, int dir, struct sk_buff *skb, @@ -1103,8 +1105,8 @@ static inline int xfrm6_policy_check_reverse(struct sock *sk, int dir, return __xfrm_policy_check2(sk, dir, skb, AF_INET6, 1); } -extern int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, - unsigned int family, int reverse); +int __xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, + unsigned int family, int reverse); static inline int xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned int family) @@ -1119,7 +1121,7 @@ static inline int xfrm_decode_session_reverse(struct sk_buff *skb, return __xfrm_decode_session(skb, fl, family, 1); } -extern int __xfrm_route_forward(struct sk_buff *skb, unsigned short family); +int __xfrm_route_forward(struct sk_buff *skb, unsigned short family); static inline int xfrm_route_forward(struct sk_buff *skb, unsigned short family) { @@ -1140,7 +1142,7 @@ static inline int xfrm6_route_forward(struct sk_buff *skb) return xfrm_route_forward(skb, AF_INET6); } -extern int __xfrm_sk_clone_policy(struct sock *sk); +int __xfrm_sk_clone_policy(struct sock *sk); static inline int xfrm_sk_clone_policy(struct sock *sk) { @@ -1149,7 +1151,7 @@ static inline int xfrm_sk_clone_policy(struct sock *sk) return 0; } -extern int xfrm_policy_delete(struct xfrm_policy *pol, int dir); +int xfrm_policy_delete(struct xfrm_policy *pol, int dir); static inline void xfrm_sk_free_policy(struct sock *sk) { @@ -1163,7 +1165,7 @@ static inline void xfrm_sk_free_policy(struct sock *sk) } } -extern void xfrm_garbage_collect(struct net *net); +void xfrm_garbage_collect(struct net *net); #else @@ -1355,6 +1357,12 @@ struct xfrm_tunnel { int priority; }; +struct xfrm_tunnel_notifier { + int (*handler)(struct sk_buff *skb); + struct xfrm_tunnel_notifier __rcu *next; + int priority; +}; + struct xfrm6_tunnel { int (*handler)(struct sk_buff *skb); int (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt, @@ -1363,16 +1371,16 @@ struct xfrm6_tunnel { int priority; }; -extern void xfrm_init(void); -extern void xfrm4_init(void); -extern int xfrm_state_init(struct net *net); -extern void xfrm_state_fini(struct net *net); -extern void xfrm4_state_init(void); +void xfrm_init(void); +void xfrm4_init(void); +int xfrm_state_init(struct net *net); +void xfrm_state_fini(struct net *net); +void xfrm4_state_init(void); #ifdef CONFIG_XFRM -extern int xfrm6_init(void); -extern void xfrm6_fini(void); -extern int xfrm6_state_init(void); -extern void xfrm6_state_fini(void); +int xfrm6_init(void); +void xfrm6_fini(void); +int xfrm6_state_init(void); +void xfrm6_state_fini(void); #else static inline int xfrm6_init(void) { @@ -1385,52 +1393,52 @@ static inline void xfrm6_fini(void) #endif #ifdef CONFIG_XFRM_STATISTICS -extern int xfrm_proc_init(struct net *net); -extern void xfrm_proc_fini(struct net *net); +int xfrm_proc_init(struct net *net); +void xfrm_proc_fini(struct net *net); #endif -extern int xfrm_sysctl_init(struct net *net); +int xfrm_sysctl_init(struct net *net); #ifdef CONFIG_SYSCTL -extern void xfrm_sysctl_fini(struct net *net); +void xfrm_sysctl_fini(struct net *net); #else static inline void xfrm_sysctl_fini(struct net *net) { } #endif -extern void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto); -extern int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, - int (*func)(struct xfrm_state *, int, void*), void *); -extern void xfrm_state_walk_done(struct xfrm_state_walk *walk); -extern struct xfrm_state *xfrm_state_alloc(struct net *net); -extern struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr, - const xfrm_address_t *saddr, - const struct flowi *fl, - struct xfrm_tmpl *tmpl, - struct xfrm_policy *pol, int *err, - unsigned short family); -extern struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark, - xfrm_address_t *daddr, - xfrm_address_t *saddr, - unsigned short family, - u8 mode, u8 proto, u32 reqid); -extern int xfrm_state_check_expire(struct xfrm_state *x); -extern void xfrm_state_insert(struct xfrm_state *x); -extern int xfrm_state_add(struct xfrm_state *x); -extern int xfrm_state_update(struct xfrm_state *x); -extern struct xfrm_state *xfrm_state_lookup(struct net *net, u32 mark, - const xfrm_address_t *daddr, __be32 spi, - u8 proto, unsigned short family); -extern struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark, - const xfrm_address_t *daddr, - const xfrm_address_t *saddr, - u8 proto, - unsigned short family); +void xfrm_state_walk_init(struct xfrm_state_walk *walk, u8 proto); +int xfrm_state_walk(struct net *net, struct xfrm_state_walk *walk, + int (*func)(struct xfrm_state *, int, void*), void *); +void xfrm_state_walk_done(struct xfrm_state_walk *walk); +struct xfrm_state *xfrm_state_alloc(struct net *net); +struct xfrm_state *xfrm_state_find(const xfrm_address_t *daddr, + const xfrm_address_t *saddr, + const struct flowi *fl, + struct xfrm_tmpl *tmpl, + struct xfrm_policy *pol, int *err, + unsigned short family); +struct xfrm_state *xfrm_stateonly_find(struct net *net, u32 mark, + xfrm_address_t *daddr, + xfrm_address_t *saddr, + unsigned short family, + u8 mode, u8 proto, u32 reqid); +int xfrm_state_check_expire(struct xfrm_state *x); +void xfrm_state_insert(struct xfrm_state *x); +int xfrm_state_add(struct xfrm_state *x); +int xfrm_state_update(struct xfrm_state *x); +struct xfrm_state *xfrm_state_lookup(struct net *net, u32 mark, + const xfrm_address_t *daddr, __be32 spi, + u8 proto, unsigned short family); +struct xfrm_state *xfrm_state_lookup_byaddr(struct net *net, u32 mark, + const xfrm_address_t *daddr, + const xfrm_address_t *saddr, + u8 proto, + unsigned short family); #ifdef CONFIG_XFRM_SUB_POLICY -extern int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, - int n, unsigned short family); -extern int xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, - int n, unsigned short family); +int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, + unsigned short family); +int xfrm_state_sort(struct xfrm_state **dst, struct xfrm_state **src, int n, + unsigned short family); #else static inline int xfrm_tmpl_sort(struct xfrm_tmpl **dst, struct xfrm_tmpl **src, int n, unsigned short family) @@ -1462,68 +1470,69 @@ struct xfrmk_spdinfo { u32 spdhmcnt; }; -extern struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, - u32 seq); -extern int xfrm_state_delete(struct xfrm_state *x); -extern int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info); -extern void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); -extern void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); -extern u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); -extern int xfrm_init_replay(struct xfrm_state *x); -extern int xfrm_state_mtu(struct xfrm_state *x, int mtu); -extern int __xfrm_init_state(struct xfrm_state *x, bool init_replay); -extern int xfrm_init_state(struct xfrm_state *x); -extern int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb); -extern int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, - int encap_type); -extern int xfrm_input_resume(struct sk_buff *skb, int nexthdr); -extern int xfrm_output_resume(struct sk_buff *skb, int err); -extern int xfrm_output(struct sk_buff *skb); -extern int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb); -extern void xfrm_local_error(struct sk_buff *skb, int mtu); -extern int xfrm4_extract_header(struct sk_buff *skb); -extern int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb); -extern int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, - int encap_type); -extern int xfrm4_transport_finish(struct sk_buff *skb, int async); -extern int xfrm4_rcv(struct sk_buff *skb); +struct xfrm_state *xfrm_find_acq_byseq(struct net *net, u32 mark, u32 seq); +int xfrm_state_delete(struct xfrm_state *x); +int xfrm_state_flush(struct net *net, u8 proto, struct xfrm_audit *audit_info); +void xfrm_sad_getinfo(struct net *net, struct xfrmk_sadinfo *si); +void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); +u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); +int xfrm_init_replay(struct xfrm_state *x); +int xfrm_state_mtu(struct xfrm_state *x, int mtu); +int __xfrm_init_state(struct xfrm_state *x, bool init_replay); +int xfrm_init_state(struct xfrm_state *x); +int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb); +int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type); +int xfrm_input_resume(struct sk_buff *skb, int nexthdr); +int xfrm_output_resume(struct sk_buff *skb, int err); +int xfrm_output(struct sk_buff *skb); +int xfrm_inner_extract_output(struct xfrm_state *x, struct sk_buff *skb); +void xfrm_local_error(struct sk_buff *skb, int mtu); +int xfrm4_extract_header(struct sk_buff *skb); +int xfrm4_extract_input(struct xfrm_state *x, struct sk_buff *skb); +int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi, + int encap_type); +int xfrm4_transport_finish(struct sk_buff *skb, int async); +int xfrm4_rcv(struct sk_buff *skb); static inline int xfrm4_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi) { return xfrm4_rcv_encap(skb, nexthdr, spi, 0); } -extern int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb); -extern int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb); -extern int xfrm4_output(struct sk_buff *skb); -extern int xfrm4_output_finish(struct sk_buff *skb); -extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family); -extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); -extern int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler); -extern int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler); -extern void xfrm4_local_error(struct sk_buff *skb, u32 mtu); -extern int xfrm6_extract_header(struct sk_buff *skb); -extern int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb); -extern int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi); -extern int xfrm6_transport_finish(struct sk_buff *skb, int async); -extern int xfrm6_rcv(struct sk_buff *skb); -extern int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, - xfrm_address_t *saddr, u8 proto); -extern int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family); -extern int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family); -extern __be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr); -extern __be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr); -extern int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb); -extern int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb); -extern int xfrm6_output(struct sk_buff *skb); -extern int xfrm6_output_finish(struct sk_buff *skb); -extern int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb, - u8 **prevhdr); -extern void xfrm6_local_error(struct sk_buff *skb, u32 mtu); +int xfrm4_extract_output(struct xfrm_state *x, struct sk_buff *skb); +int xfrm4_prepare_output(struct xfrm_state *x, struct sk_buff *skb); +int xfrm4_output(struct sk_buff *skb); +int xfrm4_output_finish(struct sk_buff *skb); +int xfrm4_tunnel_register(struct xfrm_tunnel *handler, unsigned short family); +int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler, unsigned short family); +void xfrm4_local_error(struct sk_buff *skb, u32 mtu); +int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler); +int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler); +int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler); +int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler); +int xfrm6_extract_header(struct sk_buff *skb); +int xfrm6_extract_input(struct xfrm_state *x, struct sk_buff *skb); +int xfrm6_rcv_spi(struct sk_buff *skb, int nexthdr, __be32 spi); +int xfrm6_transport_finish(struct sk_buff *skb, int async); +int xfrm6_rcv(struct sk_buff *skb); +int xfrm6_input_addr(struct sk_buff *skb, xfrm_address_t *daddr, + xfrm_address_t *saddr, u8 proto); +void xfrm6_local_error(struct sk_buff *skb, u32 mtu); +int xfrm6_tunnel_register(struct xfrm6_tunnel *handler, unsigned short family); +int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler, unsigned short family); +__be32 xfrm6_tunnel_alloc_spi(struct net *net, xfrm_address_t *saddr); +__be32 xfrm6_tunnel_spi_lookup(struct net *net, const xfrm_address_t *saddr); +int xfrm6_extract_output(struct xfrm_state *x, struct sk_buff *skb); +int xfrm6_prepare_output(struct xfrm_state *x, struct sk_buff *skb); +int xfrm6_output(struct sk_buff *skb); +int xfrm6_output_finish(struct sk_buff *skb); +int xfrm6_find_1stfragopt(struct xfrm_state *x, struct sk_buff *skb, + u8 **prevhdr); #ifdef CONFIG_XFRM -extern int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb); -extern int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen); +int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb); +int xfrm_user_policy(struct sock *sk, int optname, + u8 __user *optval, int optlen); #else static inline int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen) { @@ -1540,59 +1549,62 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp); -extern void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type); -extern int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk, - int (*func)(struct xfrm_policy *, int, int, void*), void *); -extern void xfrm_policy_walk_done(struct xfrm_policy_walk *walk); +void xfrm_policy_walk_init(struct xfrm_policy_walk *walk, u8 type); +int xfrm_policy_walk(struct net *net, struct xfrm_policy_walk *walk, + int (*func)(struct xfrm_policy *, int, int, void*), + void *); +void xfrm_policy_walk_done(struct xfrm_policy_walk *walk); int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl); struct xfrm_policy *xfrm_policy_bysel_ctx(struct net *net, u32 mark, u8 type, int dir, struct xfrm_selector *sel, struct xfrm_sec_ctx *ctx, int delete, int *err); -struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, u32 id, int delete, int *err); +struct xfrm_policy *xfrm_policy_byid(struct net *net, u32 mark, u8, int dir, + u32 id, int delete, int *err); int xfrm_policy_flush(struct net *net, u8 type, struct xfrm_audit *audit_info); u32 xfrm_get_acqseq(void); -extern int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); +int xfrm_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi); struct xfrm_state *xfrm_find_acq(struct net *net, const struct xfrm_mark *mark, u8 mode, u32 reqid, u8 proto, const xfrm_address_t *daddr, const xfrm_address_t *saddr, int create, unsigned short family); -extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); +int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); #ifdef CONFIG_XFRM_MIGRATE -extern int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, - const struct xfrm_migrate *m, int num_bundles, - const struct xfrm_kmaddress *k); -extern struct xfrm_state * xfrm_migrate_state_find(struct xfrm_migrate *m); -extern struct xfrm_state * xfrm_state_migrate(struct xfrm_state *x, - struct xfrm_migrate *m); -extern int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, - struct xfrm_migrate *m, int num_bundles, - struct xfrm_kmaddress *k); +int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, + const struct xfrm_migrate *m, int num_bundles, + const struct xfrm_kmaddress *k); +struct xfrm_state *xfrm_migrate_state_find(struct xfrm_migrate *m); +struct xfrm_state *xfrm_state_migrate(struct xfrm_state *x, + struct xfrm_migrate *m); +int xfrm_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, + struct xfrm_migrate *m, int num_bundles, + struct xfrm_kmaddress *k); #endif -extern int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); -extern void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid); -extern int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr); - -extern void xfrm_input_init(void); -extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq); - -extern void xfrm_probe_algs(void); -extern int xfrm_count_pfkey_auth_supported(void); -extern int xfrm_count_pfkey_enc_supported(void); -extern struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx); -extern struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx); -extern struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id); -extern struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id); -extern struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id); -extern struct xfrm_algo_desc *xfrm_aalg_get_byname(const char *name, int probe); -extern struct xfrm_algo_desc *xfrm_ealg_get_byname(const char *name, int probe); -extern struct xfrm_algo_desc *xfrm_calg_get_byname(const char *name, int probe); -extern struct xfrm_algo_desc *xfrm_aead_get_byname(const char *name, int icv_len, - int probe); +int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport); +void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 portid); +int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, + xfrm_address_t *addr); + +void xfrm_input_init(void); +int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, __be32 *spi, __be32 *seq); + +void xfrm_probe_algs(void); +int xfrm_count_pfkey_auth_supported(void); +int xfrm_count_pfkey_enc_supported(void); +struct xfrm_algo_desc *xfrm_aalg_get_byidx(unsigned int idx); +struct xfrm_algo_desc *xfrm_ealg_get_byidx(unsigned int idx); +struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id); +struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id); +struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id); +struct xfrm_algo_desc *xfrm_aalg_get_byname(const char *name, int probe); +struct xfrm_algo_desc *xfrm_ealg_get_byname(const char *name, int probe); +struct xfrm_algo_desc *xfrm_calg_get_byname(const char *name, int probe); +struct xfrm_algo_desc *xfrm_aead_get_byname(const char *name, int icv_len, + int probe); static inline bool xfrm6_addr_equal(const xfrm_address_t *a, const xfrm_address_t *b) diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index f04b69b6abf251d6f0ba720db25822b11b953489..38f14d0264c3aa78f3b62b93cba405c655e23ae6 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -78,4 +78,6 @@ #define SO_BUSY_POLL 46 +#define SO_MAX_PACING_RATE 47 + #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/include/uapi/linux/can/bcm.h b/include/uapi/linux/can/bcm.h index 3ebe387fea4d809790a26bbfc9aa3cea02f20cfb..382251a1d21403acd817577d83c21f47d0389865 100644 --- a/include/uapi/linux/can/bcm.h +++ b/include/uapi/linux/can/bcm.h @@ -7,6 +7,38 @@ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research * All rights reserved. * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. */ #ifndef CAN_BCM_H diff --git a/include/uapi/linux/can/error.h b/include/uapi/linux/can/error.h index 7b7148bded711b1346a2274a09af5b505e7af72e..b632045453202074ada263866052bc2a806e85bc 100644 --- a/include/uapi/linux/can/error.h +++ b/include/uapi/linux/can/error.h @@ -7,6 +7,38 @@ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research * All rights reserved. * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. */ #ifndef CAN_ERROR_H diff --git a/include/uapi/linux/can/gw.h b/include/uapi/linux/can/gw.h index 4e27c82b564a13a6a4b55860c3cb78b51f86dc7e..844c8964bdfee3a3f4a7308bf0fd832e82754a89 100644 --- a/include/uapi/linux/can/gw.h +++ b/include/uapi/linux/can/gw.h @@ -7,6 +7,38 @@ * Copyright (c) 2011 Volkswagen Group Electronic Research * All rights reserved. * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. */ #ifndef CAN_GW_H diff --git a/include/uapi/linux/can/netlink.h b/include/uapi/linux/can/netlink.h index 14966ddb7df1c5b056578ff622c77cd6400e8094..df944ed206a8e4bd23d9f9a3ec9e08c9e0ad6e3b 100644 --- a/include/uapi/linux/can/netlink.h +++ b/include/uapi/linux/can/netlink.h @@ -5,6 +5,14 @@ * * Copyright (c) 2009 Wolfgang Grandegger * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the version 2 of the GNU General Public License + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #ifndef CAN_NETLINK_H diff --git a/include/uapi/linux/can/raw.h b/include/uapi/linux/can/raw.h index a814062b07191819c80812b843a46bd80870344b..c7d8c334e0ce26838c7cc611bd3ad1eb5a31a6c4 100644 --- a/include/uapi/linux/can/raw.h +++ b/include/uapi/linux/can/raw.h @@ -8,6 +8,38 @@ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research * All rights reserved. * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Volkswagen nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * Alternatively, provided that this notice is retained in full, this + * software may be distributed under the terms of the GNU General + * Public License ("GPL") version 2, in which case the provisions of the + * GPL apply INSTEAD OF those given above. + * + * The provided data structures and external interfaces from this code + * are not restricted to be used by modules with a GPL compatible license. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. */ #ifndef CAN_RAW_H diff --git a/include/uapi/linux/hsr_netlink.h b/include/uapi/linux/hsr_netlink.h new file mode 100644 index 0000000000000000000000000000000000000000..2475cb8a53af0c391d31d831c7f68d3ca1695a9a --- /dev/null +++ b/include/uapi/linux/hsr_netlink.h @@ -0,0 +1,50 @@ +/* + * Copyright 2011-2013 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + */ + +#ifndef __UAPI_HSR_NETLINK_H +#define __UAPI_HSR_NETLINK_H + +/* Generic Netlink HSR family definition + */ + +/* attributes */ +enum { + HSR_A_UNSPEC, + HSR_A_NODE_ADDR, + HSR_A_IFINDEX, + HSR_A_IF1_AGE, + HSR_A_IF2_AGE, + HSR_A_NODE_ADDR_B, + HSR_A_IF1_SEQ, + HSR_A_IF2_SEQ, + HSR_A_IF1_IFINDEX, + HSR_A_IF2_IFINDEX, + HSR_A_ADDR_B_IFINDEX, + __HSR_A_MAX, +}; +#define HSR_A_MAX (__HSR_A_MAX - 1) + + +/* commands */ +enum { + HSR_C_UNSPEC, + HSR_C_RING_ERROR, + HSR_C_NODE_DOWN, + HSR_C_GET_NODE_STATUS, + HSR_C_SET_NODE_STATUS, + HSR_C_GET_NODE_LIST, + HSR_C_SET_NODE_LIST, + __HSR_C_MAX, +}; +#define HSR_C_MAX (__HSR_C_MAX - 1) + +#endif /* __UAPI_HSR_NETLINK_H */ diff --git a/include/uapi/linux/if.h b/include/uapi/linux/if.h index 1ec407b01e46fe73308547b02bd548fd150616ec..d758163b0e432f6c461d2b32335dcf138d217df7 100644 --- a/include/uapi/linux/if.h +++ b/include/uapi/linux/if.h @@ -83,6 +83,7 @@ #define IFF_SUPP_NOFCS 0x80000 /* device supports sending custom FCS */ #define IFF_LIVE_ADDR_CHANGE 0x100000 /* device supports hardware address * change when it's running */ +#define IFF_MACVLAN 0x200000 /* Macvlan device */ #define IF_GET_IFACE 0x0001 /* for querying only */ diff --git a/include/uapi/linux/if_bonding.h b/include/uapi/linux/if_bonding.h index a17edda8a7816c2ba92e7c1b7aee3cce23b93f8f..9635a62f6f89c781a9086af47df6382c59fea161 100644 --- a/include/uapi/linux/if_bonding.h +++ b/include/uapi/linux/if_bonding.h @@ -91,6 +91,8 @@ #define BOND_XMIT_POLICY_LAYER2 0 /* layer 2 (MAC only), default */ #define BOND_XMIT_POLICY_LAYER34 1 /* layer 3+4 (IP ^ (TCP || UDP)) */ #define BOND_XMIT_POLICY_LAYER23 2 /* layer 2+3 (IP ^ MAC) */ +#define BOND_XMIT_POLICY_ENCAP23 3 /* encapsulated layer 2+3 */ +#define BOND_XMIT_POLICY_ENCAP34 4 /* encapsulated layer 3+4 */ typedef struct ifbond { __s32 bond_mode; diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index ade07f1c491ae7e946ceafa796b1ab07e39c8b04..2ce0f6a78fa5b47ba6308b371cf6b0c8f21e579b 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -85,6 +85,7 @@ #define ETH_P_8021AH 0x88E7 /* 802.1ah Backbone Service Tag */ #define ETH_P_MVRP 0x88F5 /* 802.1Q MVRP */ #define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */ +#define ETH_P_PRP 0x88FB /* IEC 62439-3 PRP/HSRv0 */ #define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */ #define ETH_P_TDLS 0x890D /* TDLS */ #define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */ diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h index 80394e8dc3a348ed6ac7779a410c0bcda80ac284..b78566f59abaa54528ed86acf85eb7e7b3f305fa 100644 --- a/include/uapi/linux/if_link.h +++ b/include/uapi/linux/if_link.h @@ -325,6 +325,17 @@ struct ifla_vxlan_port_range { __be16 high; }; +/* Bonding section */ + +enum { + IFLA_BOND_UNSPEC, + IFLA_BOND_MODE, + IFLA_BOND_ACTIVE_SLAVE, + __IFLA_BOND_MAX, +}; + +#define IFLA_BOND_MAX (__IFLA_BOND_MAX - 1) + /* SR-IOV virtual function management section */ enum { @@ -470,4 +481,17 @@ enum { #define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1) + +/* HSR section */ + +enum { + IFLA_HSR_UNSPEC, + IFLA_HSR_SLAVE1, + IFLA_HSR_SLAVE2, + IFLA_HSR_MULTICAST_SPEC, + __IFLA_HSR_MAX, +}; + +#define IFLA_HSR_MAX (__IFLA_HSR_MAX - 1) + #endif /* _UAPI_LINUX_IF_LINK_H */ diff --git a/include/uapi/linux/in.h b/include/uapi/linux/in.h index f9e8e496ae5d454e72517ca5ee3e65fcef595986..393c5de09d42c69393cb228e0d401189f072deb4 100644 --- a/include/uapi/linux/in.h +++ b/include/uapi/linux/in.h @@ -115,6 +115,11 @@ struct in_addr { #define IP_PMTUDISC_WANT 1 /* Use per route hints */ #define IP_PMTUDISC_DO 2 /* Always DF */ #define IP_PMTUDISC_PROBE 3 /* Ignore dst pmtu */ +/* Always use interface mtu (ignores dst pmtu) but don't set DF flag. + * Also incoming ICMP frag_needed notifications will be ignored on + * this socket to prevent accepting spoofed ones. + */ +#define IP_PMTUDISC_INTERFACE 4 #define IP_MULTICAST_IF 32 #define IP_MULTICAST_TTL 33 diff --git a/include/uapi/linux/ip_vs.h b/include/uapi/linux/ip_vs.h index 29458223d0448c186eafa8d8024ecd5179738f35..fbcffe8041f7aff03046f0e901902f44cb094d0d 100644 --- a/include/uapi/linux/ip_vs.h +++ b/include/uapi/linux/ip_vs.h @@ -334,7 +334,7 @@ enum { __IPVS_CMD_ATTR_MAX, }; -#define IPVS_CMD_ATTR_MAX (__IPVS_SVC_ATTR_MAX - 1) +#define IPVS_CMD_ATTR_MAX (__IPVS_CMD_ATTR_MAX - 1) /* * Attributes used to describe a service diff --git a/include/uapi/linux/netfilter/Kbuild b/include/uapi/linux/netfilter/Kbuild index 174915420d3fe8231dc151519ceac551aceb148e..17c3af2c4bb958b636ec8553056709769dd9d73b 100644 --- a/include/uapi/linux/netfilter/Kbuild +++ b/include/uapi/linux/netfilter/Kbuild @@ -5,6 +5,8 @@ header-y += nf_conntrack_ftp.h header-y += nf_conntrack_sctp.h header-y += nf_conntrack_tcp.h header-y += nf_conntrack_tuple_common.h +header-y += nf_tables.h +header-y += nf_tables_compat.h header-y += nf_nat.h header-y += nfnetlink.h header-y += nfnetlink_acct.h diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h b/include/uapi/linux/netfilter/ipset/ip_set.h index 8024cdf13b700560e9bd0c1f3df1bacbdd90b900..25d3b2f79c022e92cfd8f851e0f9ae8a2f7b731a 100644 --- a/include/uapi/linux/netfilter/ipset/ip_set.h +++ b/include/uapi/linux/netfilter/ipset/ip_set.h @@ -10,12 +10,14 @@ #ifndef _UAPI_IP_SET_H #define _UAPI_IP_SET_H - #include /* The protocol version */ #define IPSET_PROTOCOL 6 +/* The maximum permissible comment length we will accept over netlink */ +#define IPSET_MAX_COMMENT_SIZE 255 + /* The max length of strings including NUL: set and type identifiers */ #define IPSET_MAXNAMELEN 32 @@ -110,6 +112,7 @@ enum { IPSET_ATTR_IFACE, IPSET_ATTR_BYTES, IPSET_ATTR_PACKETS, + IPSET_ATTR_COMMENT, __IPSET_ATTR_ADT_MAX, }; #define IPSET_ATTR_ADT_MAX (__IPSET_ATTR_ADT_MAX - 1) @@ -140,6 +143,7 @@ enum ipset_errno { IPSET_ERR_IPADDR_IPV4, IPSET_ERR_IPADDR_IPV6, IPSET_ERR_COUNTER, + IPSET_ERR_COMMENT, /* Type specific error codes */ IPSET_ERR_TYPE_SPECIFIC = 4352, @@ -176,6 +180,8 @@ enum ipset_cadt_flags { IPSET_FLAG_NOMATCH = (1 << IPSET_FLAG_BIT_NOMATCH), IPSET_FLAG_BIT_WITH_COUNTERS = 3, IPSET_FLAG_WITH_COUNTERS = (1 << IPSET_FLAG_BIT_WITH_COUNTERS), + IPSET_FLAG_BIT_WITH_COMMENT = 4, + IPSET_FLAG_WITH_COMMENT = (1 << IPSET_FLAG_BIT_WITH_COMMENT), IPSET_FLAG_CADT_MAX = 15, }; @@ -250,6 +256,14 @@ struct ip_set_req_get_set { #define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ /* Uses ip_set_req_get_set */ +#define IP_SET_OP_GET_FNAME 0x00000008 /* Get set index and family */ +struct ip_set_req_get_set_family { + unsigned int op; + unsigned int version; + unsigned int family; + union ip_set_name_index set; +}; + #define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ struct ip_set_req_version { unsigned int op; diff --git a/include/uapi/linux/netfilter/nf_conntrack_common.h b/include/uapi/linux/netfilter/nf_conntrack_common.h index 8dd803818ebe34c63db7792dadf4614632bdd93e..319f47128db8c117563efa26d4cd54e1c3ac2cc2 100644 --- a/include/uapi/linux/netfilter/nf_conntrack_common.h +++ b/include/uapi/linux/netfilter/nf_conntrack_common.h @@ -25,6 +25,10 @@ enum ip_conntrack_info { IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1 }; +#define NF_CT_STATE_INVALID_BIT (1 << 0) +#define NF_CT_STATE_BIT(ctinfo) (1 << ((ctinfo) % IP_CT_IS_REPLY + 1)) +#define NF_CT_STATE_UNTRACKED_BIT (1 << (IP_CT_NUMBER + 1)) + /* Bitset representing status of connection. */ enum ip_conntrack_status { /* It's an expected connection: bit 0 set. This bit never changed */ diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h new file mode 100644 index 0000000000000000000000000000000000000000..fbfd229a8e99e123108c6fc80dd962df683e5622 --- /dev/null +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -0,0 +1,718 @@ +#ifndef _LINUX_NF_TABLES_H +#define _LINUX_NF_TABLES_H + +#define NFT_CHAIN_MAXNAMELEN 32 + +enum nft_registers { + NFT_REG_VERDICT, + NFT_REG_1, + NFT_REG_2, + NFT_REG_3, + NFT_REG_4, + __NFT_REG_MAX +}; +#define NFT_REG_MAX (__NFT_REG_MAX - 1) + +/** + * enum nft_verdicts - nf_tables internal verdicts + * + * @NFT_CONTINUE: continue evaluation of the current rule + * @NFT_BREAK: terminate evaluation of the current rule + * @NFT_JUMP: push the current chain on the jump stack and jump to a chain + * @NFT_GOTO: jump to a chain without pushing the current chain on the jump stack + * @NFT_RETURN: return to the topmost chain on the jump stack + * + * The nf_tables verdicts share their numeric space with the netfilter verdicts. + */ +enum nft_verdicts { + NFT_CONTINUE = -1, + NFT_BREAK = -2, + NFT_JUMP = -3, + NFT_GOTO = -4, + NFT_RETURN = -5, +}; + +/** + * enum nf_tables_msg_types - nf_tables netlink message types + * + * @NFT_MSG_NEWTABLE: create a new table (enum nft_table_attributes) + * @NFT_MSG_GETTABLE: get a table (enum nft_table_attributes) + * @NFT_MSG_DELTABLE: delete a table (enum nft_table_attributes) + * @NFT_MSG_NEWCHAIN: create a new chain (enum nft_chain_attributes) + * @NFT_MSG_GETCHAIN: get a chain (enum nft_chain_attributes) + * @NFT_MSG_DELCHAIN: delete a chain (enum nft_chain_attributes) + * @NFT_MSG_NEWRULE: create a new rule (enum nft_rule_attributes) + * @NFT_MSG_GETRULE: get a rule (enum nft_rule_attributes) + * @NFT_MSG_DELRULE: delete a rule (enum nft_rule_attributes) + * @NFT_MSG_NEWSET: create a new set (enum nft_set_attributes) + * @NFT_MSG_GETSET: get a set (enum nft_set_attributes) + * @NFT_MSG_DELSET: delete a set (enum nft_set_attributes) + * @NFT_MSG_NEWSETELEM: create a new set element (enum nft_set_elem_attributes) + * @NFT_MSG_GETSETELEM: get a set element (enum nft_set_elem_attributes) + * @NFT_MSG_DELSETELEM: delete a set element (enum nft_set_elem_attributes) + */ +enum nf_tables_msg_types { + NFT_MSG_NEWTABLE, + NFT_MSG_GETTABLE, + NFT_MSG_DELTABLE, + NFT_MSG_NEWCHAIN, + NFT_MSG_GETCHAIN, + NFT_MSG_DELCHAIN, + NFT_MSG_NEWRULE, + NFT_MSG_GETRULE, + NFT_MSG_DELRULE, + NFT_MSG_NEWSET, + NFT_MSG_GETSET, + NFT_MSG_DELSET, + NFT_MSG_NEWSETELEM, + NFT_MSG_GETSETELEM, + NFT_MSG_DELSETELEM, + NFT_MSG_MAX, +}; + +/** + * enum nft_list_attributes - nf_tables generic list netlink attributes + * + * @NFTA_LIST_ELEM: list element (NLA_NESTED) + */ +enum nft_list_attributes { + NFTA_LIST_UNPEC, + NFTA_LIST_ELEM, + __NFTA_LIST_MAX +}; +#define NFTA_LIST_MAX (__NFTA_LIST_MAX - 1) + +/** + * enum nft_hook_attributes - nf_tables netfilter hook netlink attributes + * + * @NFTA_HOOK_HOOKNUM: netfilter hook number (NLA_U32) + * @NFTA_HOOK_PRIORITY: netfilter hook priority (NLA_U32) + */ +enum nft_hook_attributes { + NFTA_HOOK_UNSPEC, + NFTA_HOOK_HOOKNUM, + NFTA_HOOK_PRIORITY, + __NFTA_HOOK_MAX +}; +#define NFTA_HOOK_MAX (__NFTA_HOOK_MAX - 1) + +/** + * enum nft_table_flags - nf_tables table flags + * + * @NFT_TABLE_F_DORMANT: this table is not active + */ +enum nft_table_flags { + NFT_TABLE_F_DORMANT = 0x1, +}; + +/** + * enum nft_table_attributes - nf_tables table netlink attributes + * + * @NFTA_TABLE_NAME: name of the table (NLA_STRING) + * @NFTA_TABLE_FLAGS: bitmask of enum nft_table_flags (NLA_U32) + */ +enum nft_table_attributes { + NFTA_TABLE_UNSPEC, + NFTA_TABLE_NAME, + NFTA_TABLE_FLAGS, + __NFTA_TABLE_MAX +}; +#define NFTA_TABLE_MAX (__NFTA_TABLE_MAX - 1) + +/** + * enum nft_chain_attributes - nf_tables chain netlink attributes + * + * @NFTA_CHAIN_TABLE: name of the table containing the chain (NLA_STRING) + * @NFTA_CHAIN_HANDLE: numeric handle of the chain (NLA_U64) + * @NFTA_CHAIN_NAME: name of the chain (NLA_STRING) + * @NFTA_CHAIN_HOOK: hook specification for basechains (NLA_NESTED: nft_hook_attributes) + * @NFTA_CHAIN_POLICY: numeric policy of the chain (NLA_U32) + * @NFTA_CHAIN_USE: number of references to this chain (NLA_U32) + * @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING) + * @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes) + */ +enum nft_chain_attributes { + NFTA_CHAIN_UNSPEC, + NFTA_CHAIN_TABLE, + NFTA_CHAIN_HANDLE, + NFTA_CHAIN_NAME, + NFTA_CHAIN_HOOK, + NFTA_CHAIN_POLICY, + NFTA_CHAIN_USE, + NFTA_CHAIN_TYPE, + NFTA_CHAIN_COUNTERS, + __NFTA_CHAIN_MAX +}; +#define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) + +/** + * enum nft_rule_attributes - nf_tables rule netlink attributes + * + * @NFTA_RULE_TABLE: name of the table containing the rule (NLA_STRING) + * @NFTA_RULE_CHAIN: name of the chain containing the rule (NLA_STRING) + * @NFTA_RULE_HANDLE: numeric handle of the rule (NLA_U64) + * @NFTA_RULE_EXPRESSIONS: list of expressions (NLA_NESTED: nft_expr_attributes) + * @NFTA_RULE_COMPAT: compatibility specifications of the rule (NLA_NESTED: nft_rule_compat_attributes) + * @NFTA_RULE_POSITION: numeric handle of the previous rule (NLA_U64) + */ +enum nft_rule_attributes { + NFTA_RULE_UNSPEC, + NFTA_RULE_TABLE, + NFTA_RULE_CHAIN, + NFTA_RULE_HANDLE, + NFTA_RULE_EXPRESSIONS, + NFTA_RULE_COMPAT, + NFTA_RULE_POSITION, + __NFTA_RULE_MAX +}; +#define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1) + +/** + * enum nft_rule_compat_flags - nf_tables rule compat flags + * + * @NFT_RULE_COMPAT_F_INV: invert the check result + */ +enum nft_rule_compat_flags { + NFT_RULE_COMPAT_F_INV = (1 << 1), + NFT_RULE_COMPAT_F_MASK = NFT_RULE_COMPAT_F_INV, +}; + +/** + * enum nft_rule_compat_attributes - nf_tables rule compat attributes + * + * @NFTA_RULE_COMPAT_PROTO: numerice value of handled protocol (NLA_U32) + * @NFTA_RULE_COMPAT_FLAGS: bitmask of enum nft_rule_compat_flags (NLA_U32) + */ +enum nft_rule_compat_attributes { + NFTA_RULE_COMPAT_UNSPEC, + NFTA_RULE_COMPAT_PROTO, + NFTA_RULE_COMPAT_FLAGS, + __NFTA_RULE_COMPAT_MAX +}; +#define NFTA_RULE_COMPAT_MAX (__NFTA_RULE_COMPAT_MAX - 1) + +/** + * enum nft_set_flags - nf_tables set flags + * + * @NFT_SET_ANONYMOUS: name allocation, automatic cleanup on unlink + * @NFT_SET_CONSTANT: set contents may not change while bound + * @NFT_SET_INTERVAL: set contains intervals + * @NFT_SET_MAP: set is used as a dictionary + */ +enum nft_set_flags { + NFT_SET_ANONYMOUS = 0x1, + NFT_SET_CONSTANT = 0x2, + NFT_SET_INTERVAL = 0x4, + NFT_SET_MAP = 0x8, +}; + +/** + * enum nft_set_attributes - nf_tables set netlink attributes + * + * @NFTA_SET_TABLE: table name (NLA_STRING) + * @NFTA_SET_NAME: set name (NLA_STRING) + * @NFTA_SET_FLAGS: bitmask of enum nft_set_flags (NLA_U32) + * @NFTA_SET_KEY_TYPE: key data type, informational purpose only (NLA_U32) + * @NFTA_SET_KEY_LEN: key data length (NLA_U32) + * @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32) + * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32) + */ +enum nft_set_attributes { + NFTA_SET_UNSPEC, + NFTA_SET_TABLE, + NFTA_SET_NAME, + NFTA_SET_FLAGS, + NFTA_SET_KEY_TYPE, + NFTA_SET_KEY_LEN, + NFTA_SET_DATA_TYPE, + NFTA_SET_DATA_LEN, + __NFTA_SET_MAX +}; +#define NFTA_SET_MAX (__NFTA_SET_MAX - 1) + +/** + * enum nft_set_elem_flags - nf_tables set element flags + * + * @NFT_SET_ELEM_INTERVAL_END: element ends the previous interval + */ +enum nft_set_elem_flags { + NFT_SET_ELEM_INTERVAL_END = 0x1, +}; + +/** + * enum nft_set_elem_attributes - nf_tables set element netlink attributes + * + * @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data) + * @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes) + * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32) + */ +enum nft_set_elem_attributes { + NFTA_SET_ELEM_UNSPEC, + NFTA_SET_ELEM_KEY, + NFTA_SET_ELEM_DATA, + NFTA_SET_ELEM_FLAGS, + __NFTA_SET_ELEM_MAX +}; +#define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1) + +/** + * enum nft_set_elem_list_attributes - nf_tables set element list netlink attributes + * + * @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING) + * @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING) + * @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes) + */ +enum nft_set_elem_list_attributes { + NFTA_SET_ELEM_LIST_UNSPEC, + NFTA_SET_ELEM_LIST_TABLE, + NFTA_SET_ELEM_LIST_SET, + NFTA_SET_ELEM_LIST_ELEMENTS, + __NFTA_SET_ELEM_LIST_MAX +}; +#define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1) + +/** + * enum nft_data_types - nf_tables data types + * + * @NFT_DATA_VALUE: generic data + * @NFT_DATA_VERDICT: netfilter verdict + * + * The type of data is usually determined by the kernel directly and is not + * explicitly specified by userspace. The only difference are sets, where + * userspace specifies the key and mapping data types. + * + * The values 0xffffff00-0xffffffff are reserved for internally used types. + * The remaining range can be freely used by userspace to encode types, all + * values are equivalent to NFT_DATA_VALUE. + */ +enum nft_data_types { + NFT_DATA_VALUE, + NFT_DATA_VERDICT = 0xffffff00U, +}; + +#define NFT_DATA_RESERVED_MASK 0xffffff00U + +/** + * enum nft_data_attributes - nf_tables data netlink attributes + * + * @NFTA_DATA_VALUE: generic data (NLA_BINARY) + * @NFTA_DATA_VERDICT: nf_tables verdict (NLA_NESTED: nft_verdict_attributes) + */ +enum nft_data_attributes { + NFTA_DATA_UNSPEC, + NFTA_DATA_VALUE, + NFTA_DATA_VERDICT, + __NFTA_DATA_MAX +}; +#define NFTA_DATA_MAX (__NFTA_DATA_MAX - 1) + +/** + * enum nft_verdict_attributes - nf_tables verdict netlink attributes + * + * @NFTA_VERDICT_CODE: nf_tables verdict (NLA_U32: enum nft_verdicts) + * @NFTA_VERDICT_CHAIN: jump target chain name (NLA_STRING) + */ +enum nft_verdict_attributes { + NFTA_VERDICT_UNSPEC, + NFTA_VERDICT_CODE, + NFTA_VERDICT_CHAIN, + __NFTA_VERDICT_MAX +}; +#define NFTA_VERDICT_MAX (__NFTA_VERDICT_MAX - 1) + +/** + * enum nft_expr_attributes - nf_tables expression netlink attributes + * + * @NFTA_EXPR_NAME: name of the expression type (NLA_STRING) + * @NFTA_EXPR_DATA: type specific data (NLA_NESTED) + */ +enum nft_expr_attributes { + NFTA_EXPR_UNSPEC, + NFTA_EXPR_NAME, + NFTA_EXPR_DATA, + __NFTA_EXPR_MAX +}; +#define NFTA_EXPR_MAX (__NFTA_EXPR_MAX - 1) + +/** + * enum nft_immediate_attributes - nf_tables immediate expression netlink attributes + * + * @NFTA_IMMEDIATE_DREG: destination register to load data into (NLA_U32) + * @NFTA_IMMEDIATE_DATA: data to load (NLA_NESTED: nft_data_attributes) + */ +enum nft_immediate_attributes { + NFTA_IMMEDIATE_UNSPEC, + NFTA_IMMEDIATE_DREG, + NFTA_IMMEDIATE_DATA, + __NFTA_IMMEDIATE_MAX +}; +#define NFTA_IMMEDIATE_MAX (__NFTA_IMMEDIATE_MAX - 1) + +/** + * enum nft_bitwise_attributes - nf_tables bitwise expression netlink attributes + * + * @NFTA_BITWISE_SREG: source register (NLA_U32: nft_registers) + * @NFTA_BITWISE_DREG: destination register (NLA_U32: nft_registers) + * @NFTA_BITWISE_LEN: length of operands (NLA_U32) + * @NFTA_BITWISE_MASK: mask value (NLA_NESTED: nft_data_attributes) + * @NFTA_BITWISE_XOR: xor value (NLA_NESTED: nft_data_attributes) + * + * The bitwise expression performs the following operation: + * + * dreg = (sreg & mask) ^ xor + * + * which allow to express all bitwise operations: + * + * mask xor + * NOT: 1 1 + * OR: 0 x + * XOR: 1 x + * AND: x 0 + */ +enum nft_bitwise_attributes { + NFTA_BITWISE_UNSPEC, + NFTA_BITWISE_SREG, + NFTA_BITWISE_DREG, + NFTA_BITWISE_LEN, + NFTA_BITWISE_MASK, + NFTA_BITWISE_XOR, + __NFTA_BITWISE_MAX +}; +#define NFTA_BITWISE_MAX (__NFTA_BITWISE_MAX - 1) + +/** + * enum nft_byteorder_ops - nf_tables byteorder operators + * + * @NFT_BYTEORDER_NTOH: network to host operator + * @NFT_BYTEORDER_HTON: host to network opertaor + */ +enum nft_byteorder_ops { + NFT_BYTEORDER_NTOH, + NFT_BYTEORDER_HTON, +}; + +/** + * enum nft_byteorder_attributes - nf_tables byteorder expression netlink attributes + * + * @NFTA_BYTEORDER_SREG: source register (NLA_U32: nft_registers) + * @NFTA_BYTEORDER_DREG: destination register (NLA_U32: nft_registers) + * @NFTA_BYTEORDER_OP: operator (NLA_U32: enum nft_byteorder_ops) + * @NFTA_BYTEORDER_LEN: length of the data (NLA_U32) + * @NFTA_BYTEORDER_SIZE: data size in bytes (NLA_U32: 2 or 4) + */ +enum nft_byteorder_attributes { + NFTA_BYTEORDER_UNSPEC, + NFTA_BYTEORDER_SREG, + NFTA_BYTEORDER_DREG, + NFTA_BYTEORDER_OP, + NFTA_BYTEORDER_LEN, + NFTA_BYTEORDER_SIZE, + __NFTA_BYTEORDER_MAX +}; +#define NFTA_BYTEORDER_MAX (__NFTA_BYTEORDER_MAX - 1) + +/** + * enum nft_cmp_ops - nf_tables relational operator + * + * @NFT_CMP_EQ: equal + * @NFT_CMP_NEQ: not equal + * @NFT_CMP_LT: less than + * @NFT_CMP_LTE: less than or equal to + * @NFT_CMP_GT: greater than + * @NFT_CMP_GTE: greater than or equal to + */ +enum nft_cmp_ops { + NFT_CMP_EQ, + NFT_CMP_NEQ, + NFT_CMP_LT, + NFT_CMP_LTE, + NFT_CMP_GT, + NFT_CMP_GTE, +}; + +/** + * enum nft_cmp_attributes - nf_tables cmp expression netlink attributes + * + * @NFTA_CMP_SREG: source register of data to compare (NLA_U32: nft_registers) + * @NFTA_CMP_OP: cmp operation (NLA_U32: nft_cmp_ops) + * @NFTA_CMP_DATA: data to compare against (NLA_NESTED: nft_data_attributes) + */ +enum nft_cmp_attributes { + NFTA_CMP_UNSPEC, + NFTA_CMP_SREG, + NFTA_CMP_OP, + NFTA_CMP_DATA, + __NFTA_CMP_MAX +}; +#define NFTA_CMP_MAX (__NFTA_CMP_MAX - 1) + +/** + * enum nft_lookup_attributes - nf_tables set lookup expression netlink attributes + * + * @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING) + * @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers) + * @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers) + */ +enum nft_lookup_attributes { + NFTA_LOOKUP_UNSPEC, + NFTA_LOOKUP_SET, + NFTA_LOOKUP_SREG, + NFTA_LOOKUP_DREG, + __NFTA_LOOKUP_MAX +}; +#define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1) + +/** + * enum nft_payload_bases - nf_tables payload expression offset bases + * + * @NFT_PAYLOAD_LL_HEADER: link layer header + * @NFT_PAYLOAD_NETWORK_HEADER: network header + * @NFT_PAYLOAD_TRANSPORT_HEADER: transport header + */ +enum nft_payload_bases { + NFT_PAYLOAD_LL_HEADER, + NFT_PAYLOAD_NETWORK_HEADER, + NFT_PAYLOAD_TRANSPORT_HEADER, +}; + +/** + * enum nft_payload_attributes - nf_tables payload expression netlink attributes + * + * @NFTA_PAYLOAD_DREG: destination register to load data into (NLA_U32: nft_registers) + * @NFTA_PAYLOAD_BASE: payload base (NLA_U32: nft_payload_bases) + * @NFTA_PAYLOAD_OFFSET: payload offset relative to base (NLA_U32) + * @NFTA_PAYLOAD_LEN: payload length (NLA_U32) + */ +enum nft_payload_attributes { + NFTA_PAYLOAD_UNSPEC, + NFTA_PAYLOAD_DREG, + NFTA_PAYLOAD_BASE, + NFTA_PAYLOAD_OFFSET, + NFTA_PAYLOAD_LEN, + __NFTA_PAYLOAD_MAX +}; +#define NFTA_PAYLOAD_MAX (__NFTA_PAYLOAD_MAX - 1) + +/** + * enum nft_exthdr_attributes - nf_tables IPv6 extension header expression netlink attributes + * + * @NFTA_EXTHDR_DREG: destination register (NLA_U32: nft_registers) + * @NFTA_EXTHDR_TYPE: extension header type (NLA_U8) + * @NFTA_EXTHDR_OFFSET: extension header offset (NLA_U32) + * @NFTA_EXTHDR_LEN: extension header length (NLA_U32) + */ +enum nft_exthdr_attributes { + NFTA_EXTHDR_UNSPEC, + NFTA_EXTHDR_DREG, + NFTA_EXTHDR_TYPE, + NFTA_EXTHDR_OFFSET, + NFTA_EXTHDR_LEN, + __NFTA_EXTHDR_MAX +}; +#define NFTA_EXTHDR_MAX (__NFTA_EXTHDR_MAX - 1) + +/** + * enum nft_meta_keys - nf_tables meta expression keys + * + * @NFT_META_LEN: packet length (skb->len) + * @NFT_META_PROTOCOL: packet ethertype protocol (skb->protocol), invalid in OUTPUT + * @NFT_META_PRIORITY: packet priority (skb->priority) + * @NFT_META_MARK: packet mark (skb->mark) + * @NFT_META_IIF: packet input interface index (dev->ifindex) + * @NFT_META_OIF: packet output interface index (dev->ifindex) + * @NFT_META_IIFNAME: packet input interface name (dev->name) + * @NFT_META_OIFNAME: packet output interface name (dev->name) + * @NFT_META_IIFTYPE: packet input interface type (dev->type) + * @NFT_META_OIFTYPE: packet output interface type (dev->type) + * @NFT_META_SKUID: originating socket UID (fsuid) + * @NFT_META_SKGID: originating socket GID (fsgid) + * @NFT_META_NFTRACE: packet nftrace bit + * @NFT_META_RTCLASSID: realm value of packet's route (skb->dst->tclassid) + * @NFT_META_SECMARK: packet secmark (skb->secmark) + */ +enum nft_meta_keys { + NFT_META_LEN, + NFT_META_PROTOCOL, + NFT_META_PRIORITY, + NFT_META_MARK, + NFT_META_IIF, + NFT_META_OIF, + NFT_META_IIFNAME, + NFT_META_OIFNAME, + NFT_META_IIFTYPE, + NFT_META_OIFTYPE, + NFT_META_SKUID, + NFT_META_SKGID, + NFT_META_NFTRACE, + NFT_META_RTCLASSID, + NFT_META_SECMARK, +}; + +/** + * enum nft_meta_attributes - nf_tables meta expression netlink attributes + * + * @NFTA_META_DREG: destination register (NLA_U32) + * @NFTA_META_KEY: meta data item to load (NLA_U32: nft_meta_keys) + */ +enum nft_meta_attributes { + NFTA_META_UNSPEC, + NFTA_META_DREG, + NFTA_META_KEY, + __NFTA_META_MAX +}; +#define NFTA_META_MAX (__NFTA_META_MAX - 1) + +/** + * enum nft_ct_keys - nf_tables ct expression keys + * + * @NFT_CT_STATE: conntrack state (bitmask of enum ip_conntrack_info) + * @NFT_CT_DIRECTION: conntrack direction (enum ip_conntrack_dir) + * @NFT_CT_STATUS: conntrack status (bitmask of enum ip_conntrack_status) + * @NFT_CT_MARK: conntrack mark value + * @NFT_CT_SECMARK: conntrack secmark value + * @NFT_CT_EXPIRATION: relative conntrack expiration time in ms + * @NFT_CT_HELPER: connection tracking helper assigned to conntrack + * @NFT_CT_L3PROTOCOL: conntrack layer 3 protocol + * @NFT_CT_SRC: conntrack layer 3 protocol source (IPv4/IPv6 address) + * @NFT_CT_DST: conntrack layer 3 protocol destination (IPv4/IPv6 address) + * @NFT_CT_PROTOCOL: conntrack layer 4 protocol + * @NFT_CT_PROTO_SRC: conntrack layer 4 protocol source + * @NFT_CT_PROTO_DST: conntrack layer 4 protocol destination + */ +enum nft_ct_keys { + NFT_CT_STATE, + NFT_CT_DIRECTION, + NFT_CT_STATUS, + NFT_CT_MARK, + NFT_CT_SECMARK, + NFT_CT_EXPIRATION, + NFT_CT_HELPER, + NFT_CT_L3PROTOCOL, + NFT_CT_SRC, + NFT_CT_DST, + NFT_CT_PROTOCOL, + NFT_CT_PROTO_SRC, + NFT_CT_PROTO_DST, +}; + +/** + * enum nft_ct_attributes - nf_tables ct expression netlink attributes + * + * @NFTA_CT_DREG: destination register (NLA_U32) + * @NFTA_CT_KEY: conntrack data item to load (NLA_U32: nft_ct_keys) + * @NFTA_CT_DIRECTION: direction in case of directional keys (NLA_U8) + */ +enum nft_ct_attributes { + NFTA_CT_UNSPEC, + NFTA_CT_DREG, + NFTA_CT_KEY, + NFTA_CT_DIRECTION, + __NFTA_CT_MAX +}; +#define NFTA_CT_MAX (__NFTA_CT_MAX - 1) + +/** + * enum nft_limit_attributes - nf_tables limit expression netlink attributes + * + * @NFTA_LIMIT_RATE: refill rate (NLA_U64) + * @NFTA_LIMIT_UNIT: refill unit (NLA_U64) + */ +enum nft_limit_attributes { + NFTA_LIMIT_UNSPEC, + NFTA_LIMIT_RATE, + NFTA_LIMIT_UNIT, + __NFTA_LIMIT_MAX +}; +#define NFTA_LIMIT_MAX (__NFTA_LIMIT_MAX - 1) + +/** + * enum nft_counter_attributes - nf_tables counter expression netlink attributes + * + * @NFTA_COUNTER_BYTES: number of bytes (NLA_U64) + * @NFTA_COUNTER_PACKETS: number of packets (NLA_U64) + */ +enum nft_counter_attributes { + NFTA_COUNTER_UNSPEC, + NFTA_COUNTER_BYTES, + NFTA_COUNTER_PACKETS, + __NFTA_COUNTER_MAX +}; +#define NFTA_COUNTER_MAX (__NFTA_COUNTER_MAX - 1) + +/** + * enum nft_log_attributes - nf_tables log expression netlink attributes + * + * @NFTA_LOG_GROUP: netlink group to send messages to (NLA_U32) + * @NFTA_LOG_PREFIX: prefix to prepend to log messages (NLA_STRING) + * @NFTA_LOG_SNAPLEN: length of payload to include in netlink message (NLA_U32) + * @NFTA_LOG_QTHRESHOLD: queue threshold (NLA_U32) + */ +enum nft_log_attributes { + NFTA_LOG_UNSPEC, + NFTA_LOG_GROUP, + NFTA_LOG_PREFIX, + NFTA_LOG_SNAPLEN, + NFTA_LOG_QTHRESHOLD, + __NFTA_LOG_MAX +}; +#define NFTA_LOG_MAX (__NFTA_LOG_MAX - 1) + +/** + * enum nft_reject_types - nf_tables reject expression reject types + * + * @NFT_REJECT_ICMP_UNREACH: reject using ICMP unreachable + * @NFT_REJECT_TCP_RST: reject using TCP RST + */ +enum nft_reject_types { + NFT_REJECT_ICMP_UNREACH, + NFT_REJECT_TCP_RST, +}; + +/** + * enum nft_reject_attributes - nf_tables reject expression netlink attributes + * + * @NFTA_REJECT_TYPE: packet type to use (NLA_U32: nft_reject_types) + * @NFTA_REJECT_ICMP_CODE: ICMP code to use (NLA_U8) + */ +enum nft_reject_attributes { + NFTA_REJECT_UNSPEC, + NFTA_REJECT_TYPE, + NFTA_REJECT_ICMP_CODE, + __NFTA_REJECT_MAX +}; +#define NFTA_REJECT_MAX (__NFTA_REJECT_MAX - 1) + +/** + * enum nft_nat_types - nf_tables nat expression NAT types + * + * @NFT_NAT_SNAT: source NAT + * @NFT_NAT_DNAT: destination NAT + */ +enum nft_nat_types { + NFT_NAT_SNAT, + NFT_NAT_DNAT, +}; + +/** + * enum nft_nat_attributes - nf_tables nat expression netlink attributes + * + * @NFTA_NAT_TYPE: NAT type (NLA_U32: nft_nat_types) + * @NFTA_NAT_FAMILY: NAT family (NLA_U32) + * @NFTA_NAT_REG_ADDR_MIN: source register of address range start (NLA_U32: nft_registers) + * @NFTA_NAT_REG_ADDR_MAX: source register of address range end (NLA_U32: nft_registers) + * @NFTA_NAT_REG_PROTO_MIN: source register of proto range start (NLA_U32: nft_registers) + * @NFTA_NAT_REG_PROTO_MAX: source register of proto range end (NLA_U32: nft_registers) + */ +enum nft_nat_attributes { + NFTA_NAT_UNSPEC, + NFTA_NAT_TYPE, + NFTA_NAT_FAMILY, + NFTA_NAT_REG_ADDR_MIN, + NFTA_NAT_REG_ADDR_MAX, + NFTA_NAT_REG_PROTO_MIN, + NFTA_NAT_REG_PROTO_MAX, + __NFTA_NAT_MAX +}; +#define NFTA_NAT_MAX (__NFTA_NAT_MAX - 1) + +#endif /* _LINUX_NF_TABLES_H */ diff --git a/include/uapi/linux/netfilter/nf_tables_compat.h b/include/uapi/linux/netfilter/nf_tables_compat.h new file mode 100644 index 0000000000000000000000000000000000000000..8310f5f765519ef6da04664d323fa657f5b97a2a --- /dev/null +++ b/include/uapi/linux/netfilter/nf_tables_compat.h @@ -0,0 +1,38 @@ +#ifndef _NFT_COMPAT_NFNETLINK_H_ +#define _NFT_COMPAT_NFNETLINK_H_ + +enum nft_target_attributes { + NFTA_TARGET_UNSPEC, + NFTA_TARGET_NAME, + NFTA_TARGET_REV, + NFTA_TARGET_INFO, + __NFTA_TARGET_MAX +}; +#define NFTA_TARGET_MAX (__NFTA_TARGET_MAX - 1) + +enum nft_match_attributes { + NFTA_MATCH_UNSPEC, + NFTA_MATCH_NAME, + NFTA_MATCH_REV, + NFTA_MATCH_INFO, + __NFTA_MATCH_MAX +}; +#define NFTA_MATCH_MAX (__NFTA_MATCH_MAX - 1) + +#define NFT_COMPAT_NAME_MAX 32 + +enum { + NFNL_MSG_COMPAT_GET, + NFNL_MSG_COMPAT_MAX +}; + +enum { + NFTA_COMPAT_UNSPEC = 0, + NFTA_COMPAT_NAME, + NFTA_COMPAT_REV, + NFTA_COMPAT_TYPE, + __NFTA_COMPAT_MAX, +}; +#define NFTA_COMPAT_MAX (__NFTA_COMPAT_MAX - 1) + +#endif diff --git a/include/uapi/linux/netfilter/nfnetlink.h b/include/uapi/linux/netfilter/nfnetlink.h index 4a4efafad5f46664e1f60914eb52d8eb00805b84..596ddd45253c02b1f611c0655b649e651109485a 100644 --- a/include/uapi/linux/netfilter/nfnetlink.h +++ b/include/uapi/linux/netfilter/nfnetlink.h @@ -18,6 +18,8 @@ enum nfnetlink_groups { #define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_DESTROY, #define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY + NFNLGRP_NFTABLES, +#define NFNLGRP_NFTABLES NFNLGRP_NFTABLES __NFNLGRP_MAX, }; #define NFNLGRP_MAX (__NFNLGRP_MAX - 1) @@ -51,6 +53,12 @@ struct nfgenmsg { #define NFNL_SUBSYS_ACCT 7 #define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 #define NFNL_SUBSYS_CTHELPER 9 -#define NFNL_SUBSYS_COUNT 10 +#define NFNL_SUBSYS_NFTABLES 10 +#define NFNL_SUBSYS_NFT_COMPAT 11 +#define NFNL_SUBSYS_COUNT 12 + +/* Reserved control nfnetlink messages */ +#define NFNL_MSG_BATCH_BEGIN NLMSG_MIN_TYPE +#define NFNL_MSG_BATCH_END NLMSG_MIN_TYPE+1 #endif /* _UAPI_NFNETLINK_H */ diff --git a/include/uapi/linux/netfilter/nfnetlink_cttimeout.h b/include/uapi/linux/netfilter/nfnetlink_cttimeout.h index a2810a7c5e3002d63c9651e25830431a39d51abb..1ab0b97b3a1e68336485b2679045024e86ff5444 100644 --- a/include/uapi/linux/netfilter/nfnetlink_cttimeout.h +++ b/include/uapi/linux/netfilter/nfnetlink_cttimeout.h @@ -6,6 +6,8 @@ enum ctnl_timeout_msg_types { IPCTNL_MSG_TIMEOUT_NEW, IPCTNL_MSG_TIMEOUT_GET, IPCTNL_MSG_TIMEOUT_DELETE, + IPCTNL_MSG_TIMEOUT_DEFAULT_SET, + IPCTNL_MSG_TIMEOUT_DEFAULT_GET, IPCTNL_MSG_TIMEOUT_MAX }; diff --git a/include/uapi/linux/nfc.h b/include/uapi/linux/nfc.h index 29bed72a4ac43e4bd141b87a8b5e9d410ab21cff..6ad6cc03ccd3aeec4074f187836efac62ed31a0f 100644 --- a/include/uapi/linux/nfc.h +++ b/include/uapi/linux/nfc.h @@ -85,6 +85,7 @@ * a specific SE notifies us about the end of a transaction. The parameter * for this event is the application ID (AID). * @NFC_CMD_GET_SE: Dump all discovered secure elements from an NFC controller. + * @NFC_CMD_SE_IO: Send/Receive APDUs to/from the selected secure element. */ enum nfc_commands { NFC_CMD_UNSPEC, @@ -114,6 +115,7 @@ enum nfc_commands { NFC_EVENT_SE_CONNECTIVITY, NFC_EVENT_SE_TRANSACTION, NFC_CMD_GET_SE, + NFC_CMD_SE_IO, /* private: internal use only */ __NFC_CMD_AFTER_LAST }; @@ -147,6 +149,7 @@ enum nfc_commands { * @NFC_ATTR_SE_INDEX: Secure element index * @NFC_ATTR_SE_TYPE: Secure element type (UICC or EMBEDDED) * @NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS: Firmware download operation status + * @NFC_ATTR_APDU: Secure element APDU */ enum nfc_attrs { NFC_ATTR_UNSPEC, @@ -174,6 +177,7 @@ enum nfc_attrs { NFC_ATTR_SE_TYPE, NFC_ATTR_SE_AID, NFC_ATTR_FIRMWARE_DOWNLOAD_STATUS, + NFC_ATTR_SE_APDU, /* private: internal use only */ __NFC_ATTR_AFTER_LAST }; diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index fde2c021b26dbe3be5e00a9c47de5dbdb7d60b80..f752e9821e717ee68066f4d8467775a8bee2fd78 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -988,7 +988,7 @@ enum nl80211_commands { * to query the CRDA to retrieve one regulatory domain. This attribute can * also be used by userspace to query the kernel for the currently set * regulatory domain. We chose an alpha2 as that is also used by the - * IEEE-802.11d country information element to identify a country. + * IEEE-802.11 country information element to identify a country. * Users can also simply ask the wireless core to set regulatory domain * to a specific alpha2. * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory @@ -1496,6 +1496,18 @@ enum nl80211_commands { * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32. * As specified in the &enum nl80211_rxmgmt_flags. * + * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels. + * + * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported + * supported operating classes. + * + * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space + * controls DFS operation in IBSS mode. If the flag is included in + * %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS + * channels and reports radar events to userspace. Userspace is required + * to react to radar events, e.g. initiate a channel switch or leave the + * IBSS network. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1806,6 +1818,12 @@ enum nl80211_attrs { NL80211_ATTR_RXMGMT_FLAGS, + NL80211_ATTR_STA_SUPPORTED_CHANNELS, + + NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES, + + NL80211_ATTR_HANDLE_DFS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3860,13 +3878,12 @@ enum nl80211_radar_event { * * Channel states used by the DFS code. * - * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability + * @NL80211_DFS_USABLE: The channel can be used, but channel availability * check (CAC) must be performed before using it for AP or IBSS. - * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it + * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it * is therefore marked as not available. - * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available. + * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available. */ - enum nl80211_dfs_state { NL80211_DFS_USABLE, NL80211_DFS_UNAVAILABLE, diff --git a/include/uapi/linux/openvswitch.h b/include/uapi/linux/openvswitch.h index a74d375b439bb82369f9d31bbd868d466cf0aa7d..d120f9fe001783754bf27bcce1f4f4ebcf7e694e 100644 --- a/include/uapi/linux/openvswitch.h +++ b/include/uapi/linux/openvswitch.h @@ -63,15 +63,18 @@ enum ovs_datapath_cmd { * not be sent. * @OVS_DP_ATTR_STATS: Statistics about packets that have passed through the * datapath. Always present in notifications. + * @OVS_DP_ATTR_MEGAFLOW_STATS: Statistics about mega flow masks usage for the + * datapath. Always present in notifications. * * These attributes follow the &struct ovs_header within the Generic Netlink * payload for %OVS_DP_* commands. */ enum ovs_datapath_attr { OVS_DP_ATTR_UNSPEC, - OVS_DP_ATTR_NAME, /* name of dp_ifindex netdev */ - OVS_DP_ATTR_UPCALL_PID, /* Netlink PID to receive upcalls */ - OVS_DP_ATTR_STATS, /* struct ovs_dp_stats */ + OVS_DP_ATTR_NAME, /* name of dp_ifindex netdev */ + OVS_DP_ATTR_UPCALL_PID, /* Netlink PID to receive upcalls */ + OVS_DP_ATTR_STATS, /* struct ovs_dp_stats */ + OVS_DP_ATTR_MEGAFLOW_STATS, /* struct ovs_dp_megaflow_stats */ __OVS_DP_ATTR_MAX }; @@ -84,6 +87,14 @@ struct ovs_dp_stats { __u64 n_flows; /* Number of flows present */ }; +struct ovs_dp_megaflow_stats { + __u64 n_mask_hit; /* Number of masks used for flow lookups. */ + __u32 n_masks; /* Number of masks for the datapath. */ + __u32 pad0; /* Pad for future expension. */ + __u64 pad1; /* Pad for future expension. */ + __u64 pad2; /* Pad for future expension. */ +}; + struct ovs_vport_stats { __u64 rx_packets; /* total packets received */ __u64 tx_packets; /* total packets transmitted */ @@ -260,6 +271,7 @@ enum ovs_key_attr { OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */ OVS_KEY_ATTR_TUNNEL, /* Nested set of ovs_tunnel attributes */ OVS_KEY_ATTR_SCTP, /* struct ovs_key_sctp */ + OVS_KEY_ATTR_TCP_FLAGS, /* be16 TCP flags. */ #ifdef __KERNEL__ OVS_KEY_ATTR_IPV4_TUNNEL, /* struct ovs_key_ipv4_tunnel */ diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h index 082eafaf026bea1a1afec4b873a3001880cead62..25731dfb3fcc2f331cbba3c8f415c75614c1f46d 100644 --- a/include/uapi/linux/pkt_cls.h +++ b/include/uapi/linux/pkt_cls.h @@ -388,6 +388,20 @@ enum { #define TCA_CGROUP_MAX (__TCA_CGROUP_MAX - 1) +/* BPF classifier */ + +enum { + TCA_BPF_UNSPEC, + TCA_BPF_ACT, + TCA_BPF_POLICE, + TCA_BPF_CLASSID, + TCA_BPF_OPS_LEN, + TCA_BPF_OPS, + __TCA_BPF_MAX, +}; + +#define TCA_BPF_MAX (__TCA_BPF_MAX - 1) + /* Extended Matches */ struct tcf_ematch_tree_hdr { diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h index 9b829134d42243bdfc6816f9272b75ea82c9b934..307f293477e83684970fc5c6dc05225d750d0882 100644 --- a/include/uapi/linux/pkt_sched.h +++ b/include/uapi/linux/pkt_sched.h @@ -171,6 +171,8 @@ enum { TCA_TBF_PARMS, TCA_TBF_RTAB, TCA_TBF_PTAB, + TCA_TBF_RATE64, + TCA_TBF_PRATE64, __TCA_TBF_MAX, }; @@ -357,6 +359,8 @@ enum { TCA_HTB_CTAB, TCA_HTB_RTAB, TCA_HTB_DIRECT_QLEN, + TCA_HTB_RATE64, + TCA_HTB_CEIL64, __TCA_HTB_MAX, }; diff --git a/include/uapi/linux/random.h b/include/uapi/linux/random.h index 7471b5b3b8baa6ce7be4815744ad9ecaec6c1c4d..fff3528a078ffecd6aaa1986ac6e5be7b0f1ef42 100644 --- a/include/uapi/linux/random.h +++ b/include/uapi/linux/random.h @@ -40,11 +40,4 @@ struct rand_pool_info { __u32 buf[0]; }; -struct rnd_state { - __u32 s1, s2, s3; -}; - -/* Exported functions */ - - #endif /* _UAPI_LINUX_RANDOM_H */ diff --git a/include/xen/interface/io/netif.h b/include/xen/interface/io/netif.h index eb262e3324d2fca44da8c88bf3127fd4da65b118..c50061db609893a924160556203f7dcf73e24700 100644 --- a/include/xen/interface/io/netif.h +++ b/include/xen/interface/io/netif.h @@ -50,6 +50,20 @@ * node as before. */ +/* + * "feature-no-csum-offload" should be used to turn IPv4 TCP/UDP checksum + * offload off or on. If it is missing then the feature is assumed to be on. + * "feature-ipv6-csum-offload" should be used to turn IPv6 TCP/UDP checksum + * offload on or off. If it is missing then the feature is assumed to be off. + */ + +/* + * "feature-gso-tcpv4" and "feature-gso-tcpv6" advertise the capability to + * handle large TCP packets (in IPv4 or IPv6 form respectively). Neither + * frontends nor backends are assumed to be capable unless the flags are + * present. + */ + /* * This is the 'wire' format for packets: * Request 1: xen_netif_tx_request -- XEN_NETTXF_* (any flags) @@ -95,8 +109,10 @@ struct xen_netif_tx_request { #define _XEN_NETIF_EXTRA_FLAG_MORE (0) #define XEN_NETIF_EXTRA_FLAG_MORE (1U<<_XEN_NETIF_EXTRA_FLAG_MORE) -/* GSO types - only TCPv4 currently supported. */ +/* GSO types */ +#define XEN_NETIF_GSO_TYPE_NONE (0) #define XEN_NETIF_GSO_TYPE_TCPV4 (1) +#define XEN_NETIF_GSO_TYPE_TCPV6 (2) /* * This structure needs to fit within both netif_tx_request and diff --git a/init/main.c b/init/main.c index 67ee8ef0a6691422f62529164fe3e25c0fb492d9..6ad1a533a8c7fc439ccadfd7f87c23d4e32b1ff5 100644 --- a/init/main.c +++ b/init/main.c @@ -135,6 +135,13 @@ static char *static_command_line; static char *execute_command; static char *ramdisk_execute_command; +/* + * Used to generate warnings if static_key manipulation functions are used + * before jump_label_init is called. + */ +bool static_key_initialized __read_mostly = false; +EXPORT_SYMBOL_GPL(static_key_initialized); + /* * If set, this is an indication to the drivers that reset the underlying * device before going ahead with the initialization otherwise driver might diff --git a/kernel/jump_label.c b/kernel/jump_label.c index 297a9247a3b394f8538db111817e8a88d3c345ab..9019f15deab201127065e4d6987677fdcdd63676 100644 --- a/kernel/jump_label.c +++ b/kernel/jump_label.c @@ -58,6 +58,7 @@ static void jump_label_update(struct static_key *key, int enable); void static_key_slow_inc(struct static_key *key) { + STATIC_KEY_CHECK_USE(); if (atomic_inc_not_zero(&key->enabled)) return; @@ -103,12 +104,14 @@ static void jump_label_update_timeout(struct work_struct *work) void static_key_slow_dec(struct static_key *key) { + STATIC_KEY_CHECK_USE(); __static_key_slow_dec(key, 0, NULL); } EXPORT_SYMBOL_GPL(static_key_slow_dec); void static_key_slow_dec_deferred(struct static_key_deferred *key) { + STATIC_KEY_CHECK_USE(); __static_key_slow_dec(&key->key, key->timeout, &key->work); } EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred); @@ -116,6 +119,7 @@ EXPORT_SYMBOL_GPL(static_key_slow_dec_deferred); void jump_label_rate_limit(struct static_key_deferred *key, unsigned long rl) { + STATIC_KEY_CHECK_USE(); key->timeout = rl; INIT_DELAYED_WORK(&key->work, jump_label_update_timeout); } @@ -212,6 +216,7 @@ void __init jump_label_init(void) key->next = NULL; #endif } + static_key_initialized = true; jump_label_unlock(); } diff --git a/lib/Kconfig b/lib/Kconfig index b3c8be0da17f8472429ad79787c76d42fa62eb02..75485e163ca3bc8e807c10f7fdf2c761a7df377f 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -189,6 +189,13 @@ config AUDIT_GENERIC depends on AUDIT && !AUDIT_ARCH default y +config RANDOM32_SELFTEST + bool "PRNG perform self test on init" + default n + help + This option enables the 32 bit PRNG library functions to perform a + self test on initialization. + # # compression support is select'ed if needed # diff --git a/lib/crc32.c b/lib/crc32.c index 410093dbe51cba0e430301530eb4f567abc720ee..70f00ca5ef1e8fd2d4b38d047aa71baeaa207182 100644 --- a/lib/crc32.c +++ b/lib/crc32.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "crc32defs.h" #if CRC_LE_BITS > 8 @@ -49,6 +50,30 @@ MODULE_AUTHOR("Matt Domsch "); MODULE_DESCRIPTION("Various CRC32 calculations"); MODULE_LICENSE("GPL"); +#define GF2_DIM 32 + +static u32 gf2_matrix_times(u32 *mat, u32 vec) +{ + u32 sum = 0; + + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + + return sum; +} + +static void gf2_matrix_square(u32 *square, u32 *mat) +{ + int i; + + for (i = 0; i < GF2_DIM; i++) + square[i] = gf2_matrix_times(mat, mat[i]); +} + #if CRC_LE_BITS > 8 || CRC_BE_BITS > 8 /* implements slicing-by-4 or slicing-by-8 algorithm */ @@ -130,6 +155,52 @@ crc32_body(u32 crc, unsigned char const *buf, size_t len, const u32 (*tab)[256]) } #endif +/* For conditions of distribution and use, see copyright notice in zlib.h */ +static u32 crc32_generic_combine(u32 crc1, u32 crc2, size_t len2, + u32 polynomial) +{ + u32 even[GF2_DIM]; /* Even-power-of-two zeros operator */ + u32 odd[GF2_DIM]; /* Odd-power-of-two zeros operator */ + u32 row; + int i; + + if (len2 <= 0) + return crc1; + + /* Put operator for one zero bit in odd */ + odd[0] = polynomial; + row = 1; + for (i = 1; i < GF2_DIM; i++) { + odd[i] = row; + row <<= 1; + } + + gf2_matrix_square(even, odd); /* Put operator for two zero bits in even */ + gf2_matrix_square(odd, even); /* Put operator for four zero bits in odd */ + + /* Apply len2 zeros to crc1 (first square will put the operator for one + * zero byte, eight zero bits, in even). + */ + do { + /* Apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + /* If no more bits set, then done */ + if (len2 == 0) + break; + /* Another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + } while (len2 != 0); + + crc1 ^= crc2; + return crc1; +} + /** * crc32_le_generic() - Calculate bitwise little-endian Ethernet AUTODIN II * CRC32/CRC32C @@ -200,8 +271,19 @@ u32 __pure __crc32c_le(u32 crc, unsigned char const *p, size_t len) (const u32 (*)[256])crc32ctable_le, CRC32C_POLY_LE); } #endif +u32 __pure crc32_le_combine(u32 crc1, u32 crc2, size_t len2) +{ + return crc32_generic_combine(crc1, crc2, len2, CRCPOLY_LE); +} + +u32 __pure __crc32c_le_combine(u32 crc1, u32 crc2, size_t len2) +{ + return crc32_generic_combine(crc1, crc2, len2, CRC32C_POLY_LE); +} EXPORT_SYMBOL(crc32_le); +EXPORT_SYMBOL(crc32_le_combine); EXPORT_SYMBOL(__crc32c_le); +EXPORT_SYMBOL(__crc32c_le_combine); /** * crc32_be_generic() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 @@ -795,206 +877,106 @@ static struct crc_test { u32 crc32c_le; /* expected crc32c_le result */ } test[] = { - {0x674bf11d, 0x00000038, 0x00000542, 0x0af6d466, 0xd8b6e4c1, - 0xf6e93d6c}, - {0x35c672c6, 0x0000003a, 0x000001aa, 0xc6d3dfba, 0x28aaf3ad, - 0x0fe92aca}, - {0x496da28e, 0x00000039, 0x000005af, 0xd933660f, 0x5d57e81f, - 0x52e1ebb8}, - {0x09a9b90e, 0x00000027, 0x000001f8, 0xb45fe007, 0xf45fca9a, - 0x0798af9a}, - {0xdc97e5a9, 0x00000025, 0x000003b6, 0xf81a3562, 0xe0126ba2, - 0x18eb3152}, - {0x47c58900, 0x0000000a, 0x000000b9, 0x8e58eccf, 0xf3afc793, - 0xd00d08c7}, - {0x292561e8, 0x0000000c, 0x00000403, 0xa2ba8aaf, 0x0b797aed, - 0x8ba966bc}, - {0x415037f6, 0x00000003, 0x00000676, 0xa17d52e8, 0x7f0fdf35, - 0x11d694a2}, - {0x3466e707, 0x00000026, 0x00000042, 0x258319be, 0x75c484a2, - 0x6ab3208d}, - {0xafd1281b, 0x00000023, 0x000002ee, 0x4428eaf8, 0x06c7ad10, - 0xba4603c5}, - {0xd3857b18, 0x00000028, 0x000004a2, 0x5c430821, 0xb062b7cb, - 0xe6071c6f}, - {0x1d825a8f, 0x0000002b, 0x0000050b, 0xd2c45f0c, 0xd68634e0, - 0x179ec30a}, - {0x5033e3bc, 0x0000000b, 0x00000078, 0xa3ea4113, 0xac6d31fb, - 0x0903beb8}, - {0x94f1fb5e, 0x0000000f, 0x000003a2, 0xfbfc50b1, 0x3cfe50ed, - 0x6a7cb4fa}, - {0xc9a0fe14, 0x00000009, 0x00000473, 0x5fb61894, 0x87070591, - 0xdb535801}, - {0x88a034b1, 0x0000001c, 0x000005ad, 0xc1b16053, 0x46f95c67, - 0x92bed597}, - {0xf0f72239, 0x00000020, 0x0000026d, 0xa6fa58f3, 0xf8c2c1dd, - 0x192a3f1b}, - {0xcc20a5e3, 0x0000003b, 0x0000067a, 0x7740185a, 0x308b979a, - 0xccbaec1a}, - {0xce589c95, 0x0000002b, 0x00000641, 0xd055e987, 0x40aae25b, - 0x7eabae4d}, - {0x78edc885, 0x00000035, 0x000005be, 0xa39cb14b, 0x035b0d1f, - 0x28c72982}, - {0x9d40a377, 0x0000003b, 0x00000038, 0x1f47ccd2, 0x197fbc9d, - 0xc3cd4d18}, - {0x703d0e01, 0x0000003c, 0x000006f1, 0x88735e7c, 0xfed57c5a, - 0xbca8f0e7}, - {0x776bf505, 0x0000000f, 0x000005b2, 0x5cc4fc01, 0xf32efb97, - 0x713f60b3}, - {0x4a3e7854, 0x00000027, 0x000004b8, 0x8d923c82, 0x0cbfb4a2, - 0xebd08fd5}, - {0x209172dd, 0x0000003b, 0x00000356, 0xb89e9c2b, 0xd7868138, - 0x64406c59}, - {0x3ba4cc5b, 0x0000002f, 0x00000203, 0xe51601a9, 0x5b2a1032, - 0x7421890e}, - {0xfc62f297, 0x00000000, 0x00000079, 0x71a8e1a2, 0x5d88685f, - 0xe9347603}, - {0x64280b8b, 0x00000016, 0x000007ab, 0x0fa7a30c, 0xda3a455f, - 0x1bef9060}, - {0x97dd724b, 0x00000033, 0x000007ad, 0x5788b2f4, 0xd7326d32, - 0x34720072}, - {0x61394b52, 0x00000035, 0x00000571, 0xc66525f1, 0xcabe7fef, - 0x48310f59}, - {0x29b4faff, 0x00000024, 0x0000006e, 0xca13751e, 0x993648e0, - 0x783a4213}, - {0x29bfb1dc, 0x0000000b, 0x00000244, 0x436c43f7, 0x429f7a59, - 0x9e8efd41}, - {0x86ae934b, 0x00000035, 0x00000104, 0x0760ec93, 0x9cf7d0f4, - 0xfc3d34a5}, - {0xc4c1024e, 0x0000002e, 0x000006b1, 0x6516a3ec, 0x19321f9c, - 0x17a52ae2}, - {0x3287a80a, 0x00000026, 0x00000496, 0x0b257eb1, 0x754ebd51, - 0x886d935a}, - {0xa4db423e, 0x00000023, 0x0000045d, 0x9b3a66dc, 0x873e9f11, - 0xeaaeaeb2}, - {0x7a1078df, 0x00000015, 0x0000014a, 0x8c2484c5, 0x6a628659, - 0x8e900a4b}, - {0x6048bd5b, 0x00000006, 0x0000006a, 0x897e3559, 0xac9961af, - 0xd74662b1}, - {0xd8f9ea20, 0x0000003d, 0x00000277, 0x60eb905b, 0xed2aaf99, - 0xd26752ba}, - {0xea5ec3b4, 0x0000002a, 0x000004fe, 0x869965dc, 0x6c1f833b, - 0x8b1fcd62}, - {0x2dfb005d, 0x00000016, 0x00000345, 0x6a3b117e, 0xf05e8521, - 0xf54342fe}, - {0x5a214ade, 0x00000020, 0x000005b6, 0x467f70be, 0xcb22ccd3, - 0x5b95b988}, - {0xf0ab9cca, 0x00000032, 0x00000515, 0xed223df3, 0x7f3ef01d, - 0x2e1176be}, - {0x91b444f9, 0x0000002e, 0x000007f8, 0x84e9a983, 0x5676756f, - 0x66120546}, - {0x1b5d2ddb, 0x0000002e, 0x0000012c, 0xba638c4c, 0x3f42047b, - 0xf256a5cc}, - {0xd824d1bb, 0x0000003a, 0x000007b5, 0x6288653b, 0x3a3ebea0, - 0x4af1dd69}, - {0x0470180c, 0x00000034, 0x000001f0, 0x9d5b80d6, 0x3de08195, - 0x56f0a04a}, - {0xffaa3a3f, 0x00000036, 0x00000299, 0xf3a82ab8, 0x53e0c13d, - 0x74f6b6b2}, - {0x6406cfeb, 0x00000023, 0x00000600, 0xa920b8e8, 0xe4e2acf4, - 0x085951fd}, - {0xb24aaa38, 0x0000003e, 0x000004a1, 0x657cc328, 0x5077b2c3, - 0xc65387eb}, - {0x58b2ab7c, 0x00000039, 0x000002b4, 0x3a17ee7e, 0x9dcb3643, - 0x1ca9257b}, - {0x3db85970, 0x00000006, 0x000002b6, 0x95268b59, 0xb9812c10, - 0xfd196d76}, - {0x857830c5, 0x00000003, 0x00000590, 0x4ef439d5, 0xf042161d, - 0x5ef88339}, - {0xe1fcd978, 0x0000003e, 0x000007d8, 0xae8d8699, 0xce0a1ef5, - 0x2c3714d9}, - {0xb982a768, 0x00000016, 0x000006e0, 0x62fad3df, 0x5f8a067b, - 0x58576548}, - {0x1d581ce8, 0x0000001e, 0x0000058b, 0xf0f5da53, 0x26e39eee, - 0xfd7c57de}, - {0x2456719b, 0x00000025, 0x00000503, 0x4296ac64, 0xd50e4c14, - 0xd5fedd59}, - {0xfae6d8f2, 0x00000000, 0x0000055d, 0x057fdf2e, 0x2a31391a, - 0x1cc3b17b}, - {0xcba828e3, 0x00000039, 0x000002ce, 0xe3f22351, 0x8f00877b, - 0x270eed73}, - {0x13d25952, 0x0000000a, 0x0000072d, 0x76d4b4cc, 0x5eb67ec3, - 0x91ecbb11}, - {0x0342be3f, 0x00000015, 0x00000599, 0xec75d9f1, 0x9d4d2826, - 0x05ed8d0c}, - {0xeaa344e0, 0x00000014, 0x000004d8, 0x72a4c981, 0x2064ea06, - 0x0b09ad5b}, - {0xbbb52021, 0x0000003b, 0x00000272, 0x04af99fc, 0xaf042d35, - 0xf8d511fb}, - {0xb66384dc, 0x0000001d, 0x000007fc, 0xd7629116, 0x782bd801, - 0x5ad832cc}, - {0x616c01b6, 0x00000022, 0x000002c8, 0x5b1dab30, 0x783ce7d2, - 0x1214d196}, - {0xce2bdaad, 0x00000016, 0x0000062a, 0x932535c8, 0x3f02926d, - 0x5747218a}, - {0x00fe84d7, 0x00000005, 0x00000205, 0x850e50aa, 0x753d649c, - 0xde8f14de}, - {0xbebdcb4c, 0x00000006, 0x0000055d, 0xbeaa37a2, 0x2d8c9eba, - 0x3563b7b9}, - {0xd8b1a02a, 0x00000010, 0x00000387, 0x5017d2fc, 0x503541a5, - 0x071475d0}, - {0x3b96cad2, 0x00000036, 0x00000347, 0x1d2372ae, 0x926cd90b, - 0x54c79d60}, - {0xc94c1ed7, 0x00000005, 0x0000038b, 0x9e9fdb22, 0x144a9178, - 0x4c53eee6}, - {0x1aad454e, 0x00000025, 0x000002b2, 0xc3f6315c, 0x5c7a35b3, - 0x10137a3c}, - {0xa4fec9a6, 0x00000000, 0x000006d6, 0x90be5080, 0xa4107605, - 0xaa9d6c73}, - {0x1bbe71e2, 0x0000001f, 0x000002fd, 0x4e504c3b, 0x284ccaf1, - 0xb63d23e7}, - {0x4201c7e4, 0x00000002, 0x000002b7, 0x7822e3f9, 0x0cc912a9, - 0x7f53e9cf}, - {0x23fddc96, 0x00000003, 0x00000627, 0x8a385125, 0x07767e78, - 0x13c1cd83}, - {0xd82ba25c, 0x00000016, 0x0000063e, 0x98e4148a, 0x283330c9, - 0x49ff5867}, - {0x786f2032, 0x0000002d, 0x0000060f, 0xf201600a, 0xf561bfcd, - 0x8467f211}, - {0xfebe4e1f, 0x0000002a, 0x000004f2, 0x95e51961, 0xfd80dcab, - 0x3f9683b2}, - {0x1a6e0a39, 0x00000008, 0x00000672, 0x8af6c2a5, 0x78dd84cb, - 0x76a3f874}, - {0x56000ab8, 0x0000000e, 0x000000e5, 0x36bacb8f, 0x22ee1f77, - 0x863b702f}, - {0x4717fe0c, 0x00000000, 0x000006ec, 0x8439f342, 0x5c8e03da, - 0xdc6c58ff}, - {0xd5d5d68e, 0x0000003c, 0x000003a3, 0x46fff083, 0x177d1b39, - 0x0622cc95}, - {0xc25dd6c6, 0x00000024, 0x000006c0, 0x5ceb8eb4, 0x892b0d16, - 0xe85605cd}, - {0xe9b11300, 0x00000023, 0x00000683, 0x07a5d59a, 0x6c6a3208, - 0x31da5f06}, - {0x95cd285e, 0x00000001, 0x00000047, 0x7b3a4368, 0x0202c07e, - 0xa1f2e784}, - {0xd9245a25, 0x0000001e, 0x000003a6, 0xd33c1841, 0x1936c0d5, - 0xb07cc616}, - {0x103279db, 0x00000006, 0x0000039b, 0xca09b8a0, 0x77d62892, - 0xbf943b6c}, - {0x1cba3172, 0x00000027, 0x000001c8, 0xcb377194, 0xebe682db, - 0x2c01af1c}, - {0x8f613739, 0x0000000c, 0x000001df, 0xb4b0bc87, 0x7710bd43, - 0x0fe5f56d}, - {0x1c6aa90d, 0x0000001b, 0x0000053c, 0x70559245, 0xda7894ac, - 0xf8943b2d}, - {0xaabe5b93, 0x0000003d, 0x00000715, 0xcdbf42fa, 0x0c3b99e7, - 0xe4d89272}, - {0xf15dd038, 0x00000006, 0x000006db, 0x6e104aea, 0x8d5967f2, - 0x7c2f6bbb}, - {0x584dd49c, 0x00000020, 0x000007bc, 0x36b6cfd6, 0xad4e23b2, - 0xabbf388b}, - {0x5d8c9506, 0x00000020, 0x00000470, 0x4c62378e, 0x31d92640, - 0x1dca1f4e}, - {0xb80d17b0, 0x00000032, 0x00000346, 0x22a5bb88, 0x9a7ec89f, - 0x5c170e23}, - {0xdaf0592e, 0x00000023, 0x000007b0, 0x3cab3f99, 0x9b1fdd99, - 0xc0e9d672}, - {0x4793cc85, 0x0000000d, 0x00000706, 0xe82e04f6, 0xed3db6b7, - 0xc18bdc86}, - {0x82ebf64e, 0x00000009, 0x000007c3, 0x69d590a9, 0x9efa8499, - 0xa874fcdd}, - {0xb18a0319, 0x00000026, 0x000007db, 0x1cf98dcc, 0x8fa9ad6a, - 0x9dc0bb48}, + {0x674bf11d, 0x00000038, 0x00000542, 0x0af6d466, 0xd8b6e4c1, 0xf6e93d6c}, + {0x35c672c6, 0x0000003a, 0x000001aa, 0xc6d3dfba, 0x28aaf3ad, 0x0fe92aca}, + {0x496da28e, 0x00000039, 0x000005af, 0xd933660f, 0x5d57e81f, 0x52e1ebb8}, + {0x09a9b90e, 0x00000027, 0x000001f8, 0xb45fe007, 0xf45fca9a, 0x0798af9a}, + {0xdc97e5a9, 0x00000025, 0x000003b6, 0xf81a3562, 0xe0126ba2, 0x18eb3152}, + {0x47c58900, 0x0000000a, 0x000000b9, 0x8e58eccf, 0xf3afc793, 0xd00d08c7}, + {0x292561e8, 0x0000000c, 0x00000403, 0xa2ba8aaf, 0x0b797aed, 0x8ba966bc}, + {0x415037f6, 0x00000003, 0x00000676, 0xa17d52e8, 0x7f0fdf35, 0x11d694a2}, + {0x3466e707, 0x00000026, 0x00000042, 0x258319be, 0x75c484a2, 0x6ab3208d}, + {0xafd1281b, 0x00000023, 0x000002ee, 0x4428eaf8, 0x06c7ad10, 0xba4603c5}, + {0xd3857b18, 0x00000028, 0x000004a2, 0x5c430821, 0xb062b7cb, 0xe6071c6f}, + {0x1d825a8f, 0x0000002b, 0x0000050b, 0xd2c45f0c, 0xd68634e0, 0x179ec30a}, + {0x5033e3bc, 0x0000000b, 0x00000078, 0xa3ea4113, 0xac6d31fb, 0x0903beb8}, + {0x94f1fb5e, 0x0000000f, 0x000003a2, 0xfbfc50b1, 0x3cfe50ed, 0x6a7cb4fa}, + {0xc9a0fe14, 0x00000009, 0x00000473, 0x5fb61894, 0x87070591, 0xdb535801}, + {0x88a034b1, 0x0000001c, 0x000005ad, 0xc1b16053, 0x46f95c67, 0x92bed597}, + {0xf0f72239, 0x00000020, 0x0000026d, 0xa6fa58f3, 0xf8c2c1dd, 0x192a3f1b}, + {0xcc20a5e3, 0x0000003b, 0x0000067a, 0x7740185a, 0x308b979a, 0xccbaec1a}, + {0xce589c95, 0x0000002b, 0x00000641, 0xd055e987, 0x40aae25b, 0x7eabae4d}, + {0x78edc885, 0x00000035, 0x000005be, 0xa39cb14b, 0x035b0d1f, 0x28c72982}, + {0x9d40a377, 0x0000003b, 0x00000038, 0x1f47ccd2, 0x197fbc9d, 0xc3cd4d18}, + {0x703d0e01, 0x0000003c, 0x000006f1, 0x88735e7c, 0xfed57c5a, 0xbca8f0e7}, + {0x776bf505, 0x0000000f, 0x000005b2, 0x5cc4fc01, 0xf32efb97, 0x713f60b3}, + {0x4a3e7854, 0x00000027, 0x000004b8, 0x8d923c82, 0x0cbfb4a2, 0xebd08fd5}, + {0x209172dd, 0x0000003b, 0x00000356, 0xb89e9c2b, 0xd7868138, 0x64406c59}, + {0x3ba4cc5b, 0x0000002f, 0x00000203, 0xe51601a9, 0x5b2a1032, 0x7421890e}, + {0xfc62f297, 0x00000000, 0x00000079, 0x71a8e1a2, 0x5d88685f, 0xe9347603}, + {0x64280b8b, 0x00000016, 0x000007ab, 0x0fa7a30c, 0xda3a455f, 0x1bef9060}, + {0x97dd724b, 0x00000033, 0x000007ad, 0x5788b2f4, 0xd7326d32, 0x34720072}, + {0x61394b52, 0x00000035, 0x00000571, 0xc66525f1, 0xcabe7fef, 0x48310f59}, + {0x29b4faff, 0x00000024, 0x0000006e, 0xca13751e, 0x993648e0, 0x783a4213}, + {0x29bfb1dc, 0x0000000b, 0x00000244, 0x436c43f7, 0x429f7a59, 0x9e8efd41}, + {0x86ae934b, 0x00000035, 0x00000104, 0x0760ec93, 0x9cf7d0f4, 0xfc3d34a5}, + {0xc4c1024e, 0x0000002e, 0x000006b1, 0x6516a3ec, 0x19321f9c, 0x17a52ae2}, + {0x3287a80a, 0x00000026, 0x00000496, 0x0b257eb1, 0x754ebd51, 0x886d935a}, + {0xa4db423e, 0x00000023, 0x0000045d, 0x9b3a66dc, 0x873e9f11, 0xeaaeaeb2}, + {0x7a1078df, 0x00000015, 0x0000014a, 0x8c2484c5, 0x6a628659, 0x8e900a4b}, + {0x6048bd5b, 0x00000006, 0x0000006a, 0x897e3559, 0xac9961af, 0xd74662b1}, + {0xd8f9ea20, 0x0000003d, 0x00000277, 0x60eb905b, 0xed2aaf99, 0xd26752ba}, + {0xea5ec3b4, 0x0000002a, 0x000004fe, 0x869965dc, 0x6c1f833b, 0x8b1fcd62}, + {0x2dfb005d, 0x00000016, 0x00000345, 0x6a3b117e, 0xf05e8521, 0xf54342fe}, + {0x5a214ade, 0x00000020, 0x000005b6, 0x467f70be, 0xcb22ccd3, 0x5b95b988}, + {0xf0ab9cca, 0x00000032, 0x00000515, 0xed223df3, 0x7f3ef01d, 0x2e1176be}, + {0x91b444f9, 0x0000002e, 0x000007f8, 0x84e9a983, 0x5676756f, 0x66120546}, + {0x1b5d2ddb, 0x0000002e, 0x0000012c, 0xba638c4c, 0x3f42047b, 0xf256a5cc}, + {0xd824d1bb, 0x0000003a, 0x000007b5, 0x6288653b, 0x3a3ebea0, 0x4af1dd69}, + {0x0470180c, 0x00000034, 0x000001f0, 0x9d5b80d6, 0x3de08195, 0x56f0a04a}, + {0xffaa3a3f, 0x00000036, 0x00000299, 0xf3a82ab8, 0x53e0c13d, 0x74f6b6b2}, + {0x6406cfeb, 0x00000023, 0x00000600, 0xa920b8e8, 0xe4e2acf4, 0x085951fd}, + {0xb24aaa38, 0x0000003e, 0x000004a1, 0x657cc328, 0x5077b2c3, 0xc65387eb}, + {0x58b2ab7c, 0x00000039, 0x000002b4, 0x3a17ee7e, 0x9dcb3643, 0x1ca9257b}, + {0x3db85970, 0x00000006, 0x000002b6, 0x95268b59, 0xb9812c10, 0xfd196d76}, + {0x857830c5, 0x00000003, 0x00000590, 0x4ef439d5, 0xf042161d, 0x5ef88339}, + {0xe1fcd978, 0x0000003e, 0x000007d8, 0xae8d8699, 0xce0a1ef5, 0x2c3714d9}, + {0xb982a768, 0x00000016, 0x000006e0, 0x62fad3df, 0x5f8a067b, 0x58576548}, + {0x1d581ce8, 0x0000001e, 0x0000058b, 0xf0f5da53, 0x26e39eee, 0xfd7c57de}, + {0x2456719b, 0x00000025, 0x00000503, 0x4296ac64, 0xd50e4c14, 0xd5fedd59}, + {0xfae6d8f2, 0x00000000, 0x0000055d, 0x057fdf2e, 0x2a31391a, 0x1cc3b17b}, + {0xcba828e3, 0x00000039, 0x000002ce, 0xe3f22351, 0x8f00877b, 0x270eed73}, + {0x13d25952, 0x0000000a, 0x0000072d, 0x76d4b4cc, 0x5eb67ec3, 0x91ecbb11}, + {0x0342be3f, 0x00000015, 0x00000599, 0xec75d9f1, 0x9d4d2826, 0x05ed8d0c}, + {0xeaa344e0, 0x00000014, 0x000004d8, 0x72a4c981, 0x2064ea06, 0x0b09ad5b}, + {0xbbb52021, 0x0000003b, 0x00000272, 0x04af99fc, 0xaf042d35, 0xf8d511fb}, + {0xb66384dc, 0x0000001d, 0x000007fc, 0xd7629116, 0x782bd801, 0x5ad832cc}, + {0x616c01b6, 0x00000022, 0x000002c8, 0x5b1dab30, 0x783ce7d2, 0x1214d196}, + {0xce2bdaad, 0x00000016, 0x0000062a, 0x932535c8, 0x3f02926d, 0x5747218a}, + {0x00fe84d7, 0x00000005, 0x00000205, 0x850e50aa, 0x753d649c, 0xde8f14de}, + {0xbebdcb4c, 0x00000006, 0x0000055d, 0xbeaa37a2, 0x2d8c9eba, 0x3563b7b9}, + {0xd8b1a02a, 0x00000010, 0x00000387, 0x5017d2fc, 0x503541a5, 0x071475d0}, + {0x3b96cad2, 0x00000036, 0x00000347, 0x1d2372ae, 0x926cd90b, 0x54c79d60}, + {0xc94c1ed7, 0x00000005, 0x0000038b, 0x9e9fdb22, 0x144a9178, 0x4c53eee6}, + {0x1aad454e, 0x00000025, 0x000002b2, 0xc3f6315c, 0x5c7a35b3, 0x10137a3c}, + {0xa4fec9a6, 0x00000000, 0x000006d6, 0x90be5080, 0xa4107605, 0xaa9d6c73}, + {0x1bbe71e2, 0x0000001f, 0x000002fd, 0x4e504c3b, 0x284ccaf1, 0xb63d23e7}, + {0x4201c7e4, 0x00000002, 0x000002b7, 0x7822e3f9, 0x0cc912a9, 0x7f53e9cf}, + {0x23fddc96, 0x00000003, 0x00000627, 0x8a385125, 0x07767e78, 0x13c1cd83}, + {0xd82ba25c, 0x00000016, 0x0000063e, 0x98e4148a, 0x283330c9, 0x49ff5867}, + {0x786f2032, 0x0000002d, 0x0000060f, 0xf201600a, 0xf561bfcd, 0x8467f211}, + {0xfebe4e1f, 0x0000002a, 0x000004f2, 0x95e51961, 0xfd80dcab, 0x3f9683b2}, + {0x1a6e0a39, 0x00000008, 0x00000672, 0x8af6c2a5, 0x78dd84cb, 0x76a3f874}, + {0x56000ab8, 0x0000000e, 0x000000e5, 0x36bacb8f, 0x22ee1f77, 0x863b702f}, + {0x4717fe0c, 0x00000000, 0x000006ec, 0x8439f342, 0x5c8e03da, 0xdc6c58ff}, + {0xd5d5d68e, 0x0000003c, 0x000003a3, 0x46fff083, 0x177d1b39, 0x0622cc95}, + {0xc25dd6c6, 0x00000024, 0x000006c0, 0x5ceb8eb4, 0x892b0d16, 0xe85605cd}, + {0xe9b11300, 0x00000023, 0x00000683, 0x07a5d59a, 0x6c6a3208, 0x31da5f06}, + {0x95cd285e, 0x00000001, 0x00000047, 0x7b3a4368, 0x0202c07e, 0xa1f2e784}, + {0xd9245a25, 0x0000001e, 0x000003a6, 0xd33c1841, 0x1936c0d5, 0xb07cc616}, + {0x103279db, 0x00000006, 0x0000039b, 0xca09b8a0, 0x77d62892, 0xbf943b6c}, + {0x1cba3172, 0x00000027, 0x000001c8, 0xcb377194, 0xebe682db, 0x2c01af1c}, + {0x8f613739, 0x0000000c, 0x000001df, 0xb4b0bc87, 0x7710bd43, 0x0fe5f56d}, + {0x1c6aa90d, 0x0000001b, 0x0000053c, 0x70559245, 0xda7894ac, 0xf8943b2d}, + {0xaabe5b93, 0x0000003d, 0x00000715, 0xcdbf42fa, 0x0c3b99e7, 0xe4d89272}, + {0xf15dd038, 0x00000006, 0x000006db, 0x6e104aea, 0x8d5967f2, 0x7c2f6bbb}, + {0x584dd49c, 0x00000020, 0x000007bc, 0x36b6cfd6, 0xad4e23b2, 0xabbf388b}, + {0x5d8c9506, 0x00000020, 0x00000470, 0x4c62378e, 0x31d92640, 0x1dca1f4e}, + {0xb80d17b0, 0x00000032, 0x00000346, 0x22a5bb88, 0x9a7ec89f, 0x5c170e23}, + {0xdaf0592e, 0x00000023, 0x000007b0, 0x3cab3f99, 0x9b1fdd99, 0xc0e9d672}, + {0x4793cc85, 0x0000000d, 0x00000706, 0xe82e04f6, 0xed3db6b7, 0xc18bdc86}, + {0x82ebf64e, 0x00000009, 0x000007c3, 0x69d590a9, 0x9efa8499, 0xa874fcdd}, + {0xb18a0319, 0x00000026, 0x000007db, 0x1cf98dcc, 0x8fa9ad6a, 0x9dc0bb48}, }; #include @@ -1050,6 +1032,41 @@ static int __init crc32c_test(void) return 0; } +static int __init crc32c_combine_test(void) +{ + int i, j; + int errors = 0, runs = 0; + + for (i = 0; i < 10; i++) { + u32 crc_full; + + crc_full = __crc32c_le(test[i].crc, test_buf + test[i].start, + test[i].length); + for (j = 0; j <= test[i].length; ++j) { + u32 crc1, crc2; + u32 len1 = j, len2 = test[i].length - j; + + crc1 = __crc32c_le(test[i].crc, test_buf + + test[i].start, len1); + crc2 = __crc32c_le(0, test_buf + test[i].start + + len1, len2); + + if (!(crc_full == __crc32c_le_combine(crc1, crc2, len2) && + crc_full == test[i].crc32c_le)) + errors++; + runs++; + cond_resched(); + } + } + + if (errors) + pr_warn("crc32c_combine: %d/%d self tests failed\n", errors, runs); + else + pr_info("crc32c_combine: %d self tests passed\n", runs); + + return 0; +} + static int __init crc32_test(void) { int i; @@ -1109,10 +1126,49 @@ static int __init crc32_test(void) return 0; } +static int __init crc32_combine_test(void) +{ + int i, j; + int errors = 0, runs = 0; + + for (i = 0; i < 10; i++) { + u32 crc_full; + + crc_full = crc32_le(test[i].crc, test_buf + test[i].start, + test[i].length); + for (j = 0; j <= test[i].length; ++j) { + u32 crc1, crc2; + u32 len1 = j, len2 = test[i].length - j; + + crc1 = crc32_le(test[i].crc, test_buf + + test[i].start, len1); + crc2 = crc32_le(0, test_buf + test[i].start + + len1, len2); + + if (!(crc_full == crc32_le_combine(crc1, crc2, len2) && + crc_full == test[i].crc_le)) + errors++; + runs++; + cond_resched(); + } + } + + if (errors) + pr_warn("crc32_combine: %d/%d self tests failed\n", errors, runs); + else + pr_info("crc32_combine: %d self tests passed\n", runs); + + return 0; +} + static int __init crc32test_init(void) { crc32_test(); crc32c_test(); + + crc32_combine_test(); + crc32c_combine_test(); + return 0; } diff --git a/lib/random32.c b/lib/random32.c index 52280d5526be7e8e021dd204c662d3103e6d6726..82da4f4c3489eb0fa2a814adf477be16c14cbed4 100644 --- a/lib/random32.c +++ b/lib/random32.c @@ -2,19 +2,19 @@ This is a maximally equidistributed combined Tausworthe generator based on code from GNU Scientific Library 1.5 (30 Jun 2004) - x_n = (s1_n ^ s2_n ^ s3_n) + lfsr113 version: - s1_{n+1} = (((s1_n & 4294967294) <<12) ^ (((s1_n <<13) ^ s1_n) >>19)) - s2_{n+1} = (((s2_n & 4294967288) << 4) ^ (((s2_n << 2) ^ s2_n) >>25)) - s3_{n+1} = (((s3_n & 4294967280) <<17) ^ (((s3_n << 3) ^ s3_n) >>11)) + x_n = (s1_n ^ s2_n ^ s3_n ^ s4_n) - The period of this generator is about 2^88. + s1_{n+1} = (((s1_n & 4294967294) << 18) ^ (((s1_n << 6) ^ s1_n) >> 13)) + s2_{n+1} = (((s2_n & 4294967288) << 2) ^ (((s2_n << 2) ^ s2_n) >> 27)) + s3_{n+1} = (((s3_n & 4294967280) << 7) ^ (((s3_n << 13) ^ s3_n) >> 21)) + s4_{n+1} = (((s4_n & 4294967168) << 13) ^ (((s4_n << 3) ^ s4_n) >> 12)) - From: P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe - Generators", Mathematics of Computation, 65, 213 (1996), 203--213. - - This is available on the net from L'Ecuyer's home page, + The period of this generator is about 2^113 (see erratum paper). + From: P. L'Ecuyer, "Maximally Equidistributed Combined Tausworthe + Generators", Mathematics of Computation, 65, 213 (1996), 203--213: http://www.iro.umontreal.ca/~lecuyer/myftp/papers/tausme.ps ftp://ftp.iro.umontreal.ca/pub/simulation/lecuyer/papers/tausme.ps @@ -29,7 +29,7 @@ that paper.) This affects the seeding procedure by imposing the requirement - s1 > 1, s2 > 7, s3 > 15. + s1 > 1, s2 > 7, s3 > 15, s4 > 127. */ @@ -38,6 +38,11 @@ #include #include #include +#include + +#ifdef CONFIG_RANDOM32_SELFTEST +static void __init prandom_state_selftest(void); +#endif static DEFINE_PER_CPU(struct rnd_state, net_rand_state); @@ -52,11 +57,12 @@ u32 prandom_u32_state(struct rnd_state *state) { #define TAUSWORTHE(s,a,b,c,d) ((s&c)<>b) - state->s1 = TAUSWORTHE(state->s1, 13, 19, 4294967294UL, 12); - state->s2 = TAUSWORTHE(state->s2, 2, 25, 4294967288UL, 4); - state->s3 = TAUSWORTHE(state->s3, 3, 11, 4294967280UL, 17); + state->s1 = TAUSWORTHE(state->s1, 6U, 13U, 4294967294U, 18U); + state->s2 = TAUSWORTHE(state->s2, 2U, 27U, 4294967288U, 2U); + state->s3 = TAUSWORTHE(state->s3, 13U, 21U, 4294967280U, 7U); + state->s4 = TAUSWORTHE(state->s4, 3U, 12U, 4294967168U, 13U); - return (state->s1 ^ state->s2 ^ state->s3); + return (state->s1 ^ state->s2 ^ state->s3 ^ state->s4); } EXPORT_SYMBOL(prandom_u32_state); @@ -126,6 +132,38 @@ void prandom_bytes(void *buf, int bytes) } EXPORT_SYMBOL(prandom_bytes); +static void prandom_warmup(struct rnd_state *state) +{ + /* Calling RNG ten times to satify recurrence condition */ + prandom_u32_state(state); + prandom_u32_state(state); + prandom_u32_state(state); + prandom_u32_state(state); + prandom_u32_state(state); + prandom_u32_state(state); + prandom_u32_state(state); + prandom_u32_state(state); + prandom_u32_state(state); + prandom_u32_state(state); +} + +static void prandom_seed_very_weak(struct rnd_state *state, u32 seed) +{ + /* Note: This sort of seeding is ONLY used in test cases and + * during boot at the time from core_initcall until late_initcall + * as we don't have a stronger entropy source available yet. + * After late_initcall, we reseed entire state, we have to (!), + * otherwise an attacker just needs to search 32 bit space to + * probe for our internal 128 bit state if he knows a couple + * of prandom32 outputs! + */ +#define LCG(x) ((x) * 69069U) /* super-duper LCG */ + state->s1 = __seed(LCG(seed), 2U); + state->s2 = __seed(LCG(state->s1), 8U); + state->s3 = __seed(LCG(state->s2), 16U); + state->s4 = __seed(LCG(state->s3), 128U); +} + /** * prandom_seed - add entropy to pseudo random number generator * @seed: seed value @@ -141,7 +179,9 @@ void prandom_seed(u32 entropy) */ for_each_possible_cpu (i) { struct rnd_state *state = &per_cpu(net_rand_state, i); - state->s1 = __seed(state->s1 ^ entropy, 1); + + state->s1 = __seed(state->s1 ^ entropy, 2U); + prandom_warmup(state); } } EXPORT_SYMBOL(prandom_seed); @@ -154,46 +194,245 @@ static int __init prandom_init(void) { int i; +#ifdef CONFIG_RANDOM32_SELFTEST + prandom_state_selftest(); +#endif + for_each_possible_cpu(i) { struct rnd_state *state = &per_cpu(net_rand_state,i); -#define LCG(x) ((x) * 69069) /* super-duper LCG */ - state->s1 = __seed(LCG(i + jiffies), 1); - state->s2 = __seed(LCG(state->s1), 7); - state->s3 = __seed(LCG(state->s2), 15); - - /* "warm it up" */ - prandom_u32_state(state); - prandom_u32_state(state); - prandom_u32_state(state); - prandom_u32_state(state); - prandom_u32_state(state); - prandom_u32_state(state); + prandom_seed_very_weak(state, (i + jiffies) ^ random_get_entropy()); + prandom_warmup(state); } return 0; } core_initcall(prandom_init); +static void __prandom_timer(unsigned long dontcare); +static DEFINE_TIMER(seed_timer, __prandom_timer, 0, 0); + +static void __prandom_timer(unsigned long dontcare) +{ + u32 entropy; + + get_random_bytes(&entropy, sizeof(entropy)); + prandom_seed(entropy); + /* reseed every ~60 seconds, in [40 .. 80) interval with slack */ + seed_timer.expires = jiffies + (40 * HZ + (prandom_u32() % (40 * HZ))); + add_timer(&seed_timer); +} + +static void prandom_start_seed_timer(void) +{ + set_timer_slack(&seed_timer, HZ); + seed_timer.expires = jiffies + 40 * HZ; + add_timer(&seed_timer); +} + /* * Generate better values after random number generator * is fully initialized. */ -static int __init prandom_reseed(void) +static void __prandom_reseed(bool late) { int i; + unsigned long flags; + static bool latch = false; + static DEFINE_SPINLOCK(lock); + + /* only allow initial seeding (late == false) once */ + spin_lock_irqsave(&lock, flags); + if (latch && !late) + goto out; + latch = true; for_each_possible_cpu(i) { struct rnd_state *state = &per_cpu(net_rand_state,i); - u32 seeds[3]; + u32 seeds[4]; get_random_bytes(&seeds, sizeof(seeds)); - state->s1 = __seed(seeds[0], 1); - state->s2 = __seed(seeds[1], 7); - state->s3 = __seed(seeds[2], 15); + state->s1 = __seed(seeds[0], 2U); + state->s2 = __seed(seeds[1], 8U); + state->s3 = __seed(seeds[2], 16U); + state->s4 = __seed(seeds[3], 128U); - /* mix it in */ - prandom_u32_state(state); + prandom_warmup(state); } +out: + spin_unlock_irqrestore(&lock, flags); +} + +void prandom_reseed_late(void) +{ + __prandom_reseed(true); +} + +static int __init prandom_reseed(void) +{ + __prandom_reseed(false); + prandom_start_seed_timer(); return 0; } late_initcall(prandom_reseed); + +#ifdef CONFIG_RANDOM32_SELFTEST +static struct prandom_test1 { + u32 seed; + u32 result; +} test1[] = { + { 1U, 3484351685U }, + { 2U, 2623130059U }, + { 3U, 3125133893U }, + { 4U, 984847254U }, +}; + +static struct prandom_test2 { + u32 seed; + u32 iteration; + u32 result; +} test2[] = { + /* Test cases against taus113 from GSL library. */ + { 931557656U, 959U, 2975593782U }, + { 1339693295U, 876U, 3887776532U }, + { 1545556285U, 961U, 1615538833U }, + { 601730776U, 723U, 1776162651U }, + { 1027516047U, 687U, 511983079U }, + { 416526298U, 700U, 916156552U }, + { 1395522032U, 652U, 2222063676U }, + { 366221443U, 617U, 2992857763U }, + { 1539836965U, 714U, 3783265725U }, + { 556206671U, 994U, 799626459U }, + { 684907218U, 799U, 367789491U }, + { 2121230701U, 931U, 2115467001U }, + { 1668516451U, 644U, 3620590685U }, + { 768046066U, 883U, 2034077390U }, + { 1989159136U, 833U, 1195767305U }, + { 536585145U, 996U, 3577259204U }, + { 1008129373U, 642U, 1478080776U }, + { 1740775604U, 939U, 1264980372U }, + { 1967883163U, 508U, 10734624U }, + { 1923019697U, 730U, 3821419629U }, + { 442079932U, 560U, 3440032343U }, + { 1961302714U, 845U, 841962572U }, + { 2030205964U, 962U, 1325144227U }, + { 1160407529U, 507U, 240940858U }, + { 635482502U, 779U, 4200489746U }, + { 1252788931U, 699U, 867195434U }, + { 1961817131U, 719U, 668237657U }, + { 1071468216U, 983U, 917876630U }, + { 1281848367U, 932U, 1003100039U }, + { 582537119U, 780U, 1127273778U }, + { 1973672777U, 853U, 1071368872U }, + { 1896756996U, 762U, 1127851055U }, + { 847917054U, 500U, 1717499075U }, + { 1240520510U, 951U, 2849576657U }, + { 1685071682U, 567U, 1961810396U }, + { 1516232129U, 557U, 3173877U }, + { 1208118903U, 612U, 1613145022U }, + { 1817269927U, 693U, 4279122573U }, + { 1510091701U, 717U, 638191229U }, + { 365916850U, 807U, 600424314U }, + { 399324359U, 702U, 1803598116U }, + { 1318480274U, 779U, 2074237022U }, + { 697758115U, 840U, 1483639402U }, + { 1696507773U, 840U, 577415447U }, + { 2081979121U, 981U, 3041486449U }, + { 955646687U, 742U, 3846494357U }, + { 1250683506U, 749U, 836419859U }, + { 595003102U, 534U, 366794109U }, + { 47485338U, 558U, 3521120834U }, + { 619433479U, 610U, 3991783875U }, + { 704096520U, 518U, 4139493852U }, + { 1712224984U, 606U, 2393312003U }, + { 1318233152U, 922U, 3880361134U }, + { 855572992U, 761U, 1472974787U }, + { 64721421U, 703U, 683860550U }, + { 678931758U, 840U, 380616043U }, + { 692711973U, 778U, 1382361947U }, + { 677703619U, 530U, 2826914161U }, + { 92393223U, 586U, 1522128471U }, + { 1222592920U, 743U, 3466726667U }, + { 358288986U, 695U, 1091956998U }, + { 1935056945U, 958U, 514864477U }, + { 735675993U, 990U, 1294239989U }, + { 1560089402U, 897U, 2238551287U }, + { 70616361U, 829U, 22483098U }, + { 368234700U, 731U, 2913875084U }, + { 20221190U, 879U, 1564152970U }, + { 539444654U, 682U, 1835141259U }, + { 1314987297U, 840U, 1801114136U }, + { 2019295544U, 645U, 3286438930U }, + { 469023838U, 716U, 1637918202U }, + { 1843754496U, 653U, 2562092152U }, + { 400672036U, 809U, 4264212785U }, + { 404722249U, 965U, 2704116999U }, + { 600702209U, 758U, 584979986U }, + { 519953954U, 667U, 2574436237U }, + { 1658071126U, 694U, 2214569490U }, + { 420480037U, 749U, 3430010866U }, + { 690103647U, 969U, 3700758083U }, + { 1029424799U, 937U, 3787746841U }, + { 2012608669U, 506U, 3362628973U }, + { 1535432887U, 998U, 42610943U }, + { 1330635533U, 857U, 3040806504U }, + { 1223800550U, 539U, 3954229517U }, + { 1322411537U, 680U, 3223250324U }, + { 1877847898U, 945U, 2915147143U }, + { 1646356099U, 874U, 965988280U }, + { 805687536U, 744U, 4032277920U }, + { 1948093210U, 633U, 1346597684U }, + { 392609744U, 783U, 1636083295U }, + { 690241304U, 770U, 1201031298U }, + { 1360302965U, 696U, 1665394461U }, + { 1220090946U, 780U, 1316922812U }, + { 447092251U, 500U, 3438743375U }, + { 1613868791U, 592U, 828546883U }, + { 523430951U, 548U, 2552392304U }, + { 726692899U, 810U, 1656872867U }, + { 1364340021U, 836U, 3710513486U }, + { 1986257729U, 931U, 935013962U }, + { 407983964U, 921U, 728767059U }, +}; + +static void __init prandom_state_selftest(void) +{ + int i, j, errors = 0, runs = 0; + bool error = false; + + for (i = 0; i < ARRAY_SIZE(test1); i++) { + struct rnd_state state; + + prandom_seed_very_weak(&state, test1[i].seed); + prandom_warmup(&state); + + if (test1[i].result != prandom_u32_state(&state)) + error = true; + } + + if (error) + pr_warn("prandom: seed boundary self test failed\n"); + else + pr_info("prandom: seed boundary self test passed\n"); + + for (i = 0; i < ARRAY_SIZE(test2); i++) { + struct rnd_state state; + + prandom_seed_very_weak(&state, test2[i].seed); + prandom_warmup(&state); + + for (j = 0; j < test2[i].iteration - 1; j++) + prandom_u32_state(&state); + + if (test2[i].result != prandom_u32_state(&state)) + errors++; + + runs++; + cond_resched(); + } + + if (errors) + pr_warn("prandom: %d/%d self tests failed\n", errors, runs); + else + pr_info("prandom: %d self tests passed\n", runs); +} +#endif diff --git a/mm/memcontrol.c b/mm/memcontrol.c index f20a57b7faf2f3bd8faf5b8676ffd7a6adbf7fc7..e3cd40b2d5d926d45344bcdf2c513c2ee074c438 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -313,7 +313,7 @@ struct mem_cgroup { atomic_t dead_count; #if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_INET) - struct tcp_memcontrol tcp_mem; + struct cg_proto tcp_mem; #endif #if defined(CONFIG_MEMCG_KMEM) /* analogous to slab_common's slab_caches list. per-memcg */ @@ -575,13 +575,13 @@ struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg) if (!memcg || mem_cgroup_is_root(memcg)) return NULL; - return &memcg->tcp_mem.cg_proto; + return &memcg->tcp_mem; } EXPORT_SYMBOL(tcp_proto_cgroup); static void disarm_sock_keys(struct mem_cgroup *memcg) { - if (!memcg_proto_activated(&memcg->tcp_mem.cg_proto)) + if (!memcg_proto_activated(&memcg->tcp_mem)) return; static_key_slow_dec(&memcg_socket_limit_enabled); } diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index 61fc573f1142f707fee7d886e67153a62c91c14a..b3d17d1c49c3fbecdd5701da18c14c9767f7d969 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -98,14 +98,14 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) vlan_gvrp_request_leave(dev); vlan_group_set_device(grp, vlan->vlan_proto, vlan_id, NULL); + + netdev_upper_dev_unlink(real_dev, dev); /* Because unregister_netdevice_queue() makes sure at least one rcu * grace period is respected before device freeing, * we dont need to call synchronize_net() here. */ unregister_netdevice_queue(dev, head); - netdev_upper_dev_unlink(real_dev, dev); - if (grp->nr_vlan_devs == 0) { vlan_mvrp_uninit_applicant(real_dev); vlan_gvrp_uninit_applicant(real_dev); @@ -169,13 +169,13 @@ int register_vlan_dev(struct net_device *dev) if (err < 0) goto out_uninit_mvrp; - err = netdev_upper_dev_link(real_dev, dev); - if (err) - goto out_uninit_mvrp; - err = register_netdevice(dev); if (err < 0) - goto out_upper_dev_unlink; + goto out_uninit_mvrp; + + err = netdev_upper_dev_link(real_dev, dev); + if (err) + goto out_unregister_netdev; /* Account for reference in struct vlan_dev_priv */ dev_hold(real_dev); @@ -191,8 +191,8 @@ int register_vlan_dev(struct net_device *dev) return 0; -out_upper_dev_unlink: - netdev_upper_dev_unlink(real_dev, dev); +out_unregister_netdev: + unregister_netdevice(dev); out_uninit_mvrp: if (grp->nr_vlan_devs == 0) vlan_mvrp_uninit_applicant(real_dev); diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h index ba5983f34c42f8076f4416af164f229678ffc4e1..5704ed9c3a23bb13d1d5b41f36d6575fd3ec3ed9 100644 --- a/net/8021q/vlan.h +++ b/net/8021q/vlan.h @@ -5,83 +5,6 @@ #include #include - -/** - * struct vlan_priority_tci_mapping - vlan egress priority mappings - * @priority: skb priority - * @vlan_qos: vlan priority: (skb->priority << 13) & 0xE000 - * @next: pointer to next struct - */ -struct vlan_priority_tci_mapping { - u32 priority; - u16 vlan_qos; - struct vlan_priority_tci_mapping *next; -}; - - -/** - * struct vlan_pcpu_stats - VLAN percpu rx/tx stats - * @rx_packets: number of received packets - * @rx_bytes: number of received bytes - * @rx_multicast: number of received multicast packets - * @tx_packets: number of transmitted packets - * @tx_bytes: number of transmitted bytes - * @syncp: synchronization point for 64bit counters - * @rx_errors: number of rx errors - * @tx_dropped: number of tx drops - */ -struct vlan_pcpu_stats { - u64 rx_packets; - u64 rx_bytes; - u64 rx_multicast; - u64 tx_packets; - u64 tx_bytes; - struct u64_stats_sync syncp; - u32 rx_errors; - u32 tx_dropped; -}; - -struct netpoll; - -/** - * struct vlan_dev_priv - VLAN private device data - * @nr_ingress_mappings: number of ingress priority mappings - * @ingress_priority_map: ingress priority mappings - * @nr_egress_mappings: number of egress priority mappings - * @egress_priority_map: hash of egress priority mappings - * @vlan_proto: VLAN encapsulation protocol - * @vlan_id: VLAN identifier - * @flags: device flags - * @real_dev: underlying netdevice - * @real_dev_addr: address of underlying netdevice - * @dent: proc dir entry - * @vlan_pcpu_stats: ptr to percpu rx stats - */ -struct vlan_dev_priv { - unsigned int nr_ingress_mappings; - u32 ingress_priority_map[8]; - unsigned int nr_egress_mappings; - struct vlan_priority_tci_mapping *egress_priority_map[16]; - - __be16 vlan_proto; - u16 vlan_id; - u16 flags; - - struct net_device *real_dev; - unsigned char real_dev_addr[ETH_ALEN]; - - struct proc_dir_entry *dent; - struct vlan_pcpu_stats __percpu *vlan_pcpu_stats; -#ifdef CONFIG_NET_POLL_CONTROLLER - struct netpoll *netpoll; -#endif -}; - -static inline struct vlan_dev_priv *vlan_dev_priv(const struct net_device *dev) -{ - return netdev_priv(dev); -} - /* if this changes, algorithm will have to be reworked because this * depends on completely exhausting the VLAN identifier space. Thus * it gives constant time look-up, but in many cases it wastes memory. @@ -196,12 +119,12 @@ static inline u32 vlan_get_ingress_priority(struct net_device *dev, } #ifdef CONFIG_VLAN_8021Q_GVRP -extern int vlan_gvrp_request_join(const struct net_device *dev); -extern void vlan_gvrp_request_leave(const struct net_device *dev); -extern int vlan_gvrp_init_applicant(struct net_device *dev); -extern void vlan_gvrp_uninit_applicant(struct net_device *dev); -extern int vlan_gvrp_init(void); -extern void vlan_gvrp_uninit(void); +int vlan_gvrp_request_join(const struct net_device *dev); +void vlan_gvrp_request_leave(const struct net_device *dev); +int vlan_gvrp_init_applicant(struct net_device *dev); +void vlan_gvrp_uninit_applicant(struct net_device *dev); +int vlan_gvrp_init(void); +void vlan_gvrp_uninit(void); #else static inline int vlan_gvrp_request_join(const struct net_device *dev) { return 0; } static inline void vlan_gvrp_request_leave(const struct net_device *dev) {} @@ -212,12 +135,12 @@ static inline void vlan_gvrp_uninit(void) {} #endif #ifdef CONFIG_VLAN_8021Q_MVRP -extern int vlan_mvrp_request_join(const struct net_device *dev); -extern void vlan_mvrp_request_leave(const struct net_device *dev); -extern int vlan_mvrp_init_applicant(struct net_device *dev); -extern void vlan_mvrp_uninit_applicant(struct net_device *dev); -extern int vlan_mvrp_init(void); -extern void vlan_mvrp_uninit(void); +int vlan_mvrp_request_join(const struct net_device *dev); +void vlan_mvrp_request_leave(const struct net_device *dev); +int vlan_mvrp_init_applicant(struct net_device *dev); +void vlan_mvrp_uninit_applicant(struct net_device *dev); +int vlan_mvrp_init(void); +void vlan_mvrp_uninit(void); #else static inline int vlan_mvrp_request_join(const struct net_device *dev) { return 0; } static inline void vlan_mvrp_request_leave(const struct net_device *dev) {} @@ -229,8 +152,8 @@ static inline void vlan_mvrp_uninit(void) {} extern const char vlan_fullname[]; extern const char vlan_version[]; -extern int vlan_netlink_init(void); -extern void vlan_netlink_fini(void); +int vlan_netlink_init(void); +void vlan_netlink_fini(void); extern struct rtnl_link_ops vlan_link_ops; diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c index 09bf1c38805b75eee667726628bb1e93e8763a07..8db1b985dbf14faffe2ee246de8bf0de17d8610c 100644 --- a/net/8021q/vlan_dev.c +++ b/net/8021q/vlan_dev.c @@ -68,25 +68,6 @@ static int vlan_dev_rebuild_header(struct sk_buff *skb) return 0; } -static inline u16 -vlan_dev_get_egress_qos_mask(struct net_device *dev, struct sk_buff *skb) -{ - struct vlan_priority_tci_mapping *mp; - - smp_rmb(); /* coupled with smp_wmb() in vlan_dev_set_egress_priority() */ - - mp = vlan_dev_priv(dev)->egress_priority_map[(skb->priority & 0xF)]; - while (mp) { - if (mp->priority == skb->priority) { - return mp->vlan_qos; /* This should already be shifted - * to mask correctly with the - * VLAN's TCI */ - } - mp = mp->next; - } - return 0; -} - /* * Create the VLAN header for an arbitrary protocol layer * @@ -111,7 +92,7 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev, vhdr = (struct vlan_hdr *) skb_push(skb, VLAN_HLEN); vlan_tci = vlan->vlan_id; - vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); + vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority); vhdr->h_vlan_TCI = htons(vlan_tci); /* @@ -168,7 +149,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb, vlan->flags & VLAN_FLAG_REORDER_HDR) { u16 vlan_tci; vlan_tci = vlan->vlan_id; - vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb); + vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority); skb = __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci); } diff --git a/net/Kconfig b/net/Kconfig index b50dacc072f0026416490e534f7f6c14fa624724..0715db64a5c3dd7ea0cd12f09c3fe62ee8385f3a 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -220,6 +220,7 @@ source "net/openvswitch/Kconfig" source "net/vmw_vsock/Kconfig" source "net/netlink/Kconfig" source "net/mpls/Kconfig" +source "net/hsr/Kconfig" config RPS boolean diff --git a/net/Makefile b/net/Makefile index 9492e8cb64e9e467412aaf25a4c677d84ac6ac62..8fa2f91517f1b541094f9f46c6cc8173b1c7ea93 100644 --- a/net/Makefile +++ b/net/Makefile @@ -71,3 +71,4 @@ obj-$(CONFIG_NFC) += nfc/ obj-$(CONFIG_OPENVSWITCH) += openvswitch/ obj-$(CONFIG_VSOCKETS) += vmw_vsock/ obj-$(CONFIG_NET_MPLS_GSO) += mpls/ +obj-$(CONFIG_HSR) += hsr/ diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 4b4d2b779ec1a08202303863fea1b74c67f04383..a00123ebb0ae0705c8e45e8433a07e96a2a06fe5 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1735,7 +1735,7 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) res = -EFAULT; break; } - if (amount > AX25_NOUID_BLOCK) { + if (amount < 0 || amount > AX25_NOUID_BLOCK) { res = -EINVAL; break; } diff --git a/net/batman-adv/Makefile b/net/batman-adv/Makefile index 489bb36f1b9464381d2aa83c78740977e3899b77..4f4aabbd8eab24c4d12983ccf656966f4f7084b0 100644 --- a/net/batman-adv/Makefile +++ b/net/batman-adv/Makefile @@ -24,6 +24,7 @@ batman-adv-y += bitarray.o batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o batman-adv-y += debugfs.o batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o +batman-adv-y += fragmentation.o batman-adv-y += gateway_client.o batman-adv-y += gateway_common.o batman-adv-y += hard-interface.o @@ -37,5 +38,3 @@ batman-adv-y += send.o batman-adv-y += soft-interface.o batman-adv-y += sysfs.o batman-adv-y += translation-table.o -batman-adv-y += unicast.o -batman-adv-y += vis.o diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index 0a8a80cd4bf19819f797169a9b5c6122f3dc47b3..a2b480a908723a37d47d14bd76e63628518e6561 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -87,22 +87,198 @@ static uint8_t batadv_ring_buffer_avg(const uint8_t lq_recv[]) return (uint8_t)(sum / count); } +/** + * batadv_iv_ogm_orig_free - free the private resources allocated for this + * orig_node + * @orig_node: the orig_node for which the resources have to be free'd + */ +static void batadv_iv_ogm_orig_free(struct batadv_orig_node *orig_node) +{ + kfree(orig_node->bat_iv.bcast_own); + kfree(orig_node->bat_iv.bcast_own_sum); +} + +/** + * batadv_iv_ogm_orig_add_if - change the private structures of the orig_node to + * include the new hard-interface + * @orig_node: the orig_node that has to be changed + * @max_if_num: the current amount of interfaces + * + * Returns 0 on success, a negative error code otherwise. + */ +static int batadv_iv_ogm_orig_add_if(struct batadv_orig_node *orig_node, + int max_if_num) +{ + void *data_ptr; + size_t data_size, old_size; + int ret = -ENOMEM; + + spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock); + + data_size = max_if_num * sizeof(unsigned long) * BATADV_NUM_WORDS; + old_size = (max_if_num - 1) * sizeof(unsigned long) * BATADV_NUM_WORDS; + data_ptr = kmalloc(data_size, GFP_ATOMIC); + if (!data_ptr) + goto unlock; + + memcpy(data_ptr, orig_node->bat_iv.bcast_own, old_size); + kfree(orig_node->bat_iv.bcast_own); + orig_node->bat_iv.bcast_own = data_ptr; + + data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); + if (!data_ptr) { + kfree(orig_node->bat_iv.bcast_own); + goto unlock; + } + + memcpy(data_ptr, orig_node->bat_iv.bcast_own_sum, + (max_if_num - 1) * sizeof(uint8_t)); + kfree(orig_node->bat_iv.bcast_own_sum); + orig_node->bat_iv.bcast_own_sum = data_ptr; + + ret = 0; + +unlock: + spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock); + + return ret; +} + +/** + * batadv_iv_ogm_orig_del_if - change the private structures of the orig_node to + * exclude the removed interface + * @orig_node: the orig_node that has to be changed + * @max_if_num: the current amount of interfaces + * @del_if_num: the index of the interface being removed + * + * Returns 0 on success, a negative error code otherwise. + */ +static int batadv_iv_ogm_orig_del_if(struct batadv_orig_node *orig_node, + int max_if_num, int del_if_num) +{ + int chunk_size, ret = -ENOMEM, if_offset; + void *data_ptr = NULL; + + spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock); + + /* last interface was removed */ + if (max_if_num == 0) + goto free_bcast_own; + + chunk_size = sizeof(unsigned long) * BATADV_NUM_WORDS; + data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC); + if (!data_ptr) + goto unlock; + + /* copy first part */ + memcpy(data_ptr, orig_node->bat_iv.bcast_own, del_if_num * chunk_size); + + /* copy second part */ + memcpy((char *)data_ptr + del_if_num * chunk_size, + orig_node->bat_iv.bcast_own + ((del_if_num + 1) * chunk_size), + (max_if_num - del_if_num) * chunk_size); + +free_bcast_own: + kfree(orig_node->bat_iv.bcast_own); + orig_node->bat_iv.bcast_own = data_ptr; + + if (max_if_num == 0) + goto free_own_sum; + + data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); + if (!data_ptr) { + kfree(orig_node->bat_iv.bcast_own); + goto unlock; + } + + memcpy(data_ptr, orig_node->bat_iv.bcast_own_sum, + del_if_num * sizeof(uint8_t)); + + if_offset = (del_if_num + 1) * sizeof(uint8_t); + memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t), + orig_node->bat_iv.bcast_own_sum + if_offset, + (max_if_num - del_if_num) * sizeof(uint8_t)); + +free_own_sum: + kfree(orig_node->bat_iv.bcast_own_sum); + orig_node->bat_iv.bcast_own_sum = data_ptr; + + ret = 0; +unlock: + spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock); + + return ret; +} + +/** + * batadv_iv_ogm_orig_get - retrieve or create (if does not exist) an originator + * @bat_priv: the bat priv with all the soft interface information + * @addr: mac address of the originator + * + * Returns the originator object corresponding to the passed mac address or NULL + * on failure. + * If the object does not exists it is created an initialised. + */ +static struct batadv_orig_node * +batadv_iv_ogm_orig_get(struct batadv_priv *bat_priv, const uint8_t *addr) +{ + struct batadv_orig_node *orig_node; + int size, hash_added; + + orig_node = batadv_orig_hash_find(bat_priv, addr); + if (orig_node) + return orig_node; + + orig_node = batadv_orig_node_new(bat_priv, addr); + if (!orig_node) + return NULL; + + spin_lock_init(&orig_node->bat_iv.ogm_cnt_lock); + + size = bat_priv->num_ifaces * sizeof(unsigned long) * BATADV_NUM_WORDS; + orig_node->bat_iv.bcast_own = kzalloc(size, GFP_ATOMIC); + if (!orig_node->bat_iv.bcast_own) + goto free_orig_node; + + size = bat_priv->num_ifaces * sizeof(uint8_t); + orig_node->bat_iv.bcast_own_sum = kzalloc(size, GFP_ATOMIC); + if (!orig_node->bat_iv.bcast_own_sum) + goto free_bcast_own; + + hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig, + batadv_choose_orig, orig_node, + &orig_node->hash_entry); + if (hash_added != 0) + goto free_bcast_own; + + return orig_node; + +free_bcast_own: + kfree(orig_node->bat_iv.bcast_own); +free_orig_node: + batadv_orig_node_free_ref(orig_node); + + return NULL; +} + static struct batadv_neigh_node * batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface, const uint8_t *neigh_addr, struct batadv_orig_node *orig_node, struct batadv_orig_node *orig_neigh) { + struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct batadv_neigh_node *neigh_node; - neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr); + neigh_node = batadv_neigh_node_new(hard_iface, neigh_addr, orig_node); if (!neigh_node) goto out; - INIT_LIST_HEAD(&neigh_node->bonding_list); + spin_lock_init(&neigh_node->bat_iv.lq_update_lock); - neigh_node->orig_node = orig_neigh; - neigh_node->if_incoming = hard_iface; + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Creating new neighbor %pM for orig_node %pM on interface %s\n", + neigh_addr, orig_node->orig, hard_iface->net_dev->name); spin_lock_bh(&orig_node->neigh_list_lock); hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list); @@ -135,9 +311,8 @@ static int batadv_iv_ogm_iface_enable(struct batadv_hard_iface *hard_iface) batadv_ogm_packet->header.version = BATADV_COMPAT_VERSION; batadv_ogm_packet->header.ttl = 2; batadv_ogm_packet->flags = BATADV_NO_FLAGS; + batadv_ogm_packet->reserved = 0; batadv_ogm_packet->tq = BATADV_TQ_MAX_VALUE; - batadv_ogm_packet->tt_num_changes = 0; - batadv_ogm_packet->ttvn = 0; res = 0; @@ -207,12 +382,12 @@ static uint8_t batadv_hop_penalty(uint8_t tq, /* is there another aggregated packet here? */ static int batadv_iv_ogm_aggr_packet(int buff_pos, int packet_len, - int tt_num_changes) + __be16 tvlv_len) { int next_buff_pos = 0; next_buff_pos += buff_pos + BATADV_OGM_HLEN; - next_buff_pos += batadv_tt_len(tt_num_changes); + next_buff_pos += ntohs(tvlv_len); return (next_buff_pos <= packet_len) && (next_buff_pos <= BATADV_MAX_AGGREGATION_BYTES); @@ -240,7 +415,7 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet, /* adjust all flags and log packets */ while (batadv_iv_ogm_aggr_packet(buff_pos, forw_packet->packet_len, - batadv_ogm_packet->tt_num_changes)) { + batadv_ogm_packet->tvlv_len)) { /* we might have aggregated direct link packets with an * ordinary base packet */ @@ -256,18 +431,18 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet, fwd_str = "Sending own"; batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s, ttvn %d) on interface %s [%pM]\n", + "%s %spacket (originator %pM, seqno %u, TQ %d, TTL %d, IDF %s) on interface %s [%pM]\n", fwd_str, (packet_num > 0 ? "aggregated " : ""), batadv_ogm_packet->orig, ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->tq, batadv_ogm_packet->header.ttl, (batadv_ogm_packet->flags & BATADV_DIRECTLINK ? "on" : "off"), - batadv_ogm_packet->ttvn, hard_iface->net_dev->name, + hard_iface->net_dev->name, hard_iface->net_dev->dev_addr); buff_pos += BATADV_OGM_HLEN; - buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes); + buff_pos += ntohs(batadv_ogm_packet->tvlv_len); packet_num++; packet_pos = forw_packet->skb->data + buff_pos; batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos; @@ -601,7 +776,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node, struct batadv_hard_iface *if_incoming) { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - uint8_t tt_num_changes; + uint16_t tvlv_len; if (batadv_ogm_packet->header.ttl <= 1) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "ttl exceeded\n"); @@ -621,7 +796,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node, return; } - tt_num_changes = batadv_ogm_packet->tt_num_changes; + tvlv_len = ntohs(batadv_ogm_packet->tvlv_len); batadv_ogm_packet->header.ttl--; memcpy(batadv_ogm_packet->prev_sender, ethhdr->h_source, ETH_ALEN); @@ -642,7 +817,7 @@ static void batadv_iv_ogm_forward(struct batadv_orig_node *orig_node, batadv_ogm_packet->flags &= ~BATADV_DIRECTLINK; batadv_iv_ogm_queue_add(bat_priv, (unsigned char *)batadv_ogm_packet, - BATADV_OGM_HLEN + batadv_tt_len(tt_num_changes), + BATADV_OGM_HLEN + tvlv_len, if_incoming, 0, batadv_iv_ogm_fwd_send_time()); } @@ -662,20 +837,22 @@ batadv_iv_ogm_slide_own_bcast_window(struct batadv_hard_iface *hard_iface) uint32_t i; size_t word_index; uint8_t *w; + int if_num; for (i = 0; i < hash->size; i++) { head = &hash->table[i]; rcu_read_lock(); hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - spin_lock_bh(&orig_node->ogm_cnt_lock); + spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock); word_index = hard_iface->if_num * BATADV_NUM_WORDS; - word = &(orig_node->bcast_own[word_index]); + word = &(orig_node->bat_iv.bcast_own[word_index]); batadv_bit_get_packet(bat_priv, word, 1, 0); - w = &orig_node->bcast_own_sum[hard_iface->if_num]; + if_num = hard_iface->if_num; + w = &orig_node->bat_iv.bcast_own_sum[if_num]; *w = bitmap_weight(word, BATADV_TQ_LOCAL_WINDOW_SIZE); - spin_unlock_bh(&orig_node->ogm_cnt_lock); + spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock); } rcu_read_unlock(); } @@ -688,43 +865,29 @@ static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface) struct batadv_ogm_packet *batadv_ogm_packet; struct batadv_hard_iface *primary_if; int *ogm_buff_len = &hard_iface->bat_iv.ogm_buff_len; - int vis_server, tt_num_changes = 0; uint32_t seqno; - uint8_t bandwidth; + uint16_t tvlv_len = 0; - vis_server = atomic_read(&bat_priv->vis_mode); primary_if = batadv_primary_if_get_selected(bat_priv); - if (hard_iface == primary_if) - tt_num_changes = batadv_tt_append_diff(bat_priv, ogm_buff, - ogm_buff_len, - BATADV_OGM_HLEN); + if (hard_iface == primary_if) { + /* tt changes have to be committed before the tvlv data is + * appended as it may alter the tt tvlv container + */ + batadv_tt_local_commit_changes(bat_priv); + tvlv_len = batadv_tvlv_container_ogm_append(bat_priv, ogm_buff, + ogm_buff_len, + BATADV_OGM_HLEN); + } batadv_ogm_packet = (struct batadv_ogm_packet *)(*ogm_buff); + batadv_ogm_packet->tvlv_len = htons(tvlv_len); /* change sequence number to network order */ seqno = (uint32_t)atomic_read(&hard_iface->bat_iv.ogm_seqno); batadv_ogm_packet->seqno = htonl(seqno); atomic_inc(&hard_iface->bat_iv.ogm_seqno); - batadv_ogm_packet->ttvn = atomic_read(&bat_priv->tt.vn); - batadv_ogm_packet->tt_crc = htons(bat_priv->tt.local_crc); - if (tt_num_changes >= 0) - batadv_ogm_packet->tt_num_changes = tt_num_changes; - - if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC) - batadv_ogm_packet->flags |= BATADV_VIS_SERVER; - else - batadv_ogm_packet->flags &= ~BATADV_VIS_SERVER; - - if (hard_iface == primary_if && - atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_SERVER) { - bandwidth = (uint8_t)atomic_read(&bat_priv->gw_bandwidth); - batadv_ogm_packet->gw_flags = bandwidth; - } else { - batadv_ogm_packet->gw_flags = BATADV_NO_FLAGS; - } - batadv_iv_ogm_slide_own_bcast_window(hard_iface); batadv_iv_ogm_queue_add(bat_priv, hard_iface->bat_iv.ogm_buff, hard_iface->bat_iv.ogm_buff_len, hard_iface, 1, @@ -770,18 +933,18 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, if (dup_status != BATADV_NO_DUP) continue; - spin_lock_bh(&tmp_neigh_node->lq_update_lock); - batadv_ring_buffer_set(tmp_neigh_node->tq_recv, - &tmp_neigh_node->tq_index, 0); - tq_avg = batadv_ring_buffer_avg(tmp_neigh_node->tq_recv); - tmp_neigh_node->tq_avg = tq_avg; - spin_unlock_bh(&tmp_neigh_node->lq_update_lock); + spin_lock_bh(&tmp_neigh_node->bat_iv.lq_update_lock); + batadv_ring_buffer_set(tmp_neigh_node->bat_iv.tq_recv, + &tmp_neigh_node->bat_iv.tq_index, 0); + tq_avg = batadv_ring_buffer_avg(tmp_neigh_node->bat_iv.tq_recv); + tmp_neigh_node->bat_iv.tq_avg = tq_avg; + spin_unlock_bh(&tmp_neigh_node->bat_iv.lq_update_lock); } if (!neigh_node) { struct batadv_orig_node *orig_tmp; - orig_tmp = batadv_get_orig_node(bat_priv, ethhdr->h_source); + orig_tmp = batadv_iv_ogm_orig_get(bat_priv, ethhdr->h_source); if (!orig_tmp) goto unlock; @@ -798,80 +961,55 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv, rcu_read_unlock(); - orig_node->flags = batadv_ogm_packet->flags; neigh_node->last_seen = jiffies; - spin_lock_bh(&neigh_node->lq_update_lock); - batadv_ring_buffer_set(neigh_node->tq_recv, - &neigh_node->tq_index, + spin_lock_bh(&neigh_node->bat_iv.lq_update_lock); + batadv_ring_buffer_set(neigh_node->bat_iv.tq_recv, + &neigh_node->bat_iv.tq_index, batadv_ogm_packet->tq); - neigh_node->tq_avg = batadv_ring_buffer_avg(neigh_node->tq_recv); - spin_unlock_bh(&neigh_node->lq_update_lock); + tq_avg = batadv_ring_buffer_avg(neigh_node->bat_iv.tq_recv); + neigh_node->bat_iv.tq_avg = tq_avg; + spin_unlock_bh(&neigh_node->bat_iv.lq_update_lock); if (dup_status == BATADV_NO_DUP) { orig_node->last_ttl = batadv_ogm_packet->header.ttl; neigh_node->last_ttl = batadv_ogm_packet->header.ttl; } - batadv_bonding_candidate_add(orig_node, neigh_node); + batadv_bonding_candidate_add(bat_priv, orig_node, neigh_node); /* if this neighbor already is our next hop there is nothing * to change */ router = batadv_orig_node_get_router(orig_node); if (router == neigh_node) - goto update_tt; + goto out; /* if this neighbor does not offer a better TQ we won't consider it */ - if (router && (router->tq_avg > neigh_node->tq_avg)) - goto update_tt; + if (router && (router->bat_iv.tq_avg > neigh_node->bat_iv.tq_avg)) + goto out; /* if the TQ is the same and the link not more symmetric we * won't consider it either */ - if (router && (neigh_node->tq_avg == router->tq_avg)) { + if (router && (neigh_node->bat_iv.tq_avg == router->bat_iv.tq_avg)) { orig_node_tmp = router->orig_node; - spin_lock_bh(&orig_node_tmp->ogm_cnt_lock); + spin_lock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock); if_num = router->if_incoming->if_num; - sum_orig = orig_node_tmp->bcast_own_sum[if_num]; - spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock); + sum_orig = orig_node_tmp->bat_iv.bcast_own_sum[if_num]; + spin_unlock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock); orig_node_tmp = neigh_node->orig_node; - spin_lock_bh(&orig_node_tmp->ogm_cnt_lock); + spin_lock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock); if_num = neigh_node->if_incoming->if_num; - sum_neigh = orig_node_tmp->bcast_own_sum[if_num]; - spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock); + sum_neigh = orig_node_tmp->bat_iv.bcast_own_sum[if_num]; + spin_unlock_bh(&orig_node_tmp->bat_iv.ogm_cnt_lock); if (sum_orig >= sum_neigh) - goto update_tt; + goto out; } batadv_update_route(bat_priv, orig_node, neigh_node); - -update_tt: - /* I have to check for transtable changes only if the OGM has been - * sent through a primary interface - */ - if (((batadv_ogm_packet->orig != ethhdr->h_source) && - (batadv_ogm_packet->header.ttl > 2)) || - (batadv_ogm_packet->flags & BATADV_PRIMARIES_FIRST_HOP)) - batadv_tt_update_orig(bat_priv, orig_node, tt_buff, - batadv_ogm_packet->tt_num_changes, - batadv_ogm_packet->ttvn, - ntohs(batadv_ogm_packet->tt_crc)); - - if (orig_node->gw_flags != batadv_ogm_packet->gw_flags) - batadv_gw_node_update(bat_priv, orig_node, - batadv_ogm_packet->gw_flags); - - orig_node->gw_flags = batadv_ogm_packet->gw_flags; - - /* restart gateway selection if fast or late switching was enabled */ - if ((orig_node->gw_flags) && - (atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_CLIENT) && - (atomic_read(&bat_priv->gw_sel_class) > 2)) - batadv_gw_check_election(bat_priv, orig_node); - goto out; unlock: @@ -893,7 +1031,7 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, uint8_t total_count; uint8_t orig_eq_count, neigh_rq_count, neigh_rq_inv, tq_own; unsigned int neigh_rq_inv_cube, neigh_rq_max_cube; - int tq_asym_penalty, inv_asym_penalty, ret = 0; + int tq_asym_penalty, inv_asym_penalty, if_num, ret = 0; unsigned int combined_tq; /* find corresponding one hop neighbor */ @@ -931,10 +1069,11 @@ static int batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, orig_node->last_seen = jiffies; /* find packet count of corresponding one hop neighbor */ - spin_lock_bh(&orig_node->ogm_cnt_lock); - orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num]; - neigh_rq_count = neigh_node->real_packet_count; - spin_unlock_bh(&orig_node->ogm_cnt_lock); + spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock); + if_num = if_incoming->if_num; + orig_eq_count = orig_neigh_node->bat_iv.bcast_own_sum[if_num]; + neigh_rq_count = neigh_node->bat_iv.real_packet_count; + spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock); /* pay attention to not get a value bigger than 100 % */ if (orig_eq_count > neigh_rq_count) @@ -1016,12 +1155,13 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, uint32_t seqno = ntohl(batadv_ogm_packet->seqno); uint8_t *neigh_addr; uint8_t packet_count; + unsigned long *bitmap; - orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig); + orig_node = batadv_iv_ogm_orig_get(bat_priv, batadv_ogm_packet->orig); if (!orig_node) return BATADV_NO_DUP; - spin_lock_bh(&orig_node->ogm_cnt_lock); + spin_lock_bh(&orig_node->bat_iv.ogm_cnt_lock); seq_diff = seqno - orig_node->last_real_seqno; /* signalize caller that the packet is to be dropped. */ @@ -1036,7 +1176,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, hlist_for_each_entry_rcu(tmp_neigh_node, &orig_node->neigh_list, list) { neigh_addr = tmp_neigh_node->addr; - is_dup = batadv_test_bit(tmp_neigh_node->real_bits, + is_dup = batadv_test_bit(tmp_neigh_node->bat_iv.real_bits, orig_node->last_real_seqno, seqno); @@ -1052,13 +1192,13 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, } /* if the window moved, set the update flag. */ - need_update |= batadv_bit_get_packet(bat_priv, - tmp_neigh_node->real_bits, + bitmap = tmp_neigh_node->bat_iv.real_bits; + need_update |= batadv_bit_get_packet(bat_priv, bitmap, seq_diff, set_mark); - packet_count = bitmap_weight(tmp_neigh_node->real_bits, + packet_count = bitmap_weight(tmp_neigh_node->bat_iv.real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); - tmp_neigh_node->real_packet_count = packet_count; + tmp_neigh_node->bat_iv.real_packet_count = packet_count; } rcu_read_unlock(); @@ -1070,7 +1210,7 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr, } out: - spin_unlock_bh(&orig_node->ogm_cnt_lock); + spin_unlock_bh(&orig_node->bat_iv.ogm_cnt_lock); batadv_orig_node_free_ref(orig_node); return ret; } @@ -1082,7 +1222,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, { struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct batadv_hard_iface *hard_iface; - struct batadv_orig_node *orig_neigh_node, *orig_node; + struct batadv_orig_node *orig_neigh_node, *orig_node, *orig_node_tmp; struct batadv_neigh_node *router = NULL, *router_router = NULL; struct batadv_neigh_node *orig_neigh_router = NULL; int has_directlink_flag; @@ -1122,13 +1262,11 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, is_single_hop_neigh = true; batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, ttvn %u, crc %#.4x, changes %u, tq %d, TTL %d, V %d, IDF %d)\n", + "Received BATMAN packet via NB: %pM, IF: %s [%pM] (from OG: %pM, via prev OG: %pM, seqno %u, tq %d, TTL %d, V %d, IDF %d)\n", ethhdr->h_source, if_incoming->net_dev->name, if_incoming->net_dev->dev_addr, batadv_ogm_packet->orig, batadv_ogm_packet->prev_sender, - ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->ttvn, - ntohs(batadv_ogm_packet->tt_crc), - batadv_ogm_packet->tt_num_changes, batadv_ogm_packet->tq, + ntohl(batadv_ogm_packet->seqno), batadv_ogm_packet->tq, batadv_ogm_packet->header.ttl, batadv_ogm_packet->header.version, has_directlink_flag); @@ -1168,8 +1306,8 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, int16_t if_num; uint8_t *weight; - orig_neigh_node = batadv_get_orig_node(bat_priv, - ethhdr->h_source); + orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv, + ethhdr->h_source); if (!orig_neigh_node) return; @@ -1183,15 +1321,15 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, if_num = if_incoming->if_num; offset = if_num * BATADV_NUM_WORDS; - spin_lock_bh(&orig_neigh_node->ogm_cnt_lock); - word = &(orig_neigh_node->bcast_own[offset]); + spin_lock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock); + word = &(orig_neigh_node->bat_iv.bcast_own[offset]); bit_pos = if_incoming_seqno - 2; bit_pos -= ntohl(batadv_ogm_packet->seqno); batadv_set_bit(word, bit_pos); - weight = &orig_neigh_node->bcast_own_sum[if_num]; + weight = &orig_neigh_node->bat_iv.bcast_own_sum[if_num]; *weight = bitmap_weight(word, BATADV_TQ_LOCAL_WINDOW_SIZE); - spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock); + spin_unlock_bh(&orig_neigh_node->bat_iv.ogm_cnt_lock); } batadv_dbg(BATADV_DBG_BATMAN, bat_priv, @@ -1214,7 +1352,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, return; } - orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig); + orig_node = batadv_iv_ogm_orig_get(bat_priv, batadv_ogm_packet->orig); if (!orig_node) return; @@ -1235,10 +1373,12 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, } router = batadv_orig_node_get_router(orig_node); - if (router) - router_router = batadv_orig_node_get_router(router->orig_node); + if (router) { + orig_node_tmp = router->orig_node; + router_router = batadv_orig_node_get_router(orig_node_tmp); + } - if ((router && router->tq_avg != 0) && + if ((router && router->bat_iv.tq_avg != 0) && (batadv_compare_eth(router->addr, ethhdr->h_source))) is_from_best_next_hop = true; @@ -1254,14 +1394,16 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr, goto out; } + batadv_tvlv_ogm_receive(bat_priv, batadv_ogm_packet, orig_node); + /* if sender is a direct neighbor the sender mac equals * originator mac */ if (is_single_hop_neigh) orig_neigh_node = orig_node; else - orig_neigh_node = batadv_get_orig_node(bat_priv, - ethhdr->h_source); + orig_neigh_node = batadv_iv_ogm_orig_get(bat_priv, + ethhdr->h_source); if (!orig_neigh_node) goto out; @@ -1350,9 +1492,9 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, struct batadv_ogm_packet *batadv_ogm_packet; struct ethhdr *ethhdr; int buff_pos = 0, packet_len; - unsigned char *tt_buff, *packet_buff; - bool ret; + unsigned char *tvlv_buff, *packet_buff; uint8_t *packet_pos; + bool ret; ret = batadv_check_management_packet(skb, if_incoming, BATADV_OGM_HLEN); if (!ret) @@ -1375,14 +1517,14 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, /* unpack the aggregated packets and process them one by one */ while (batadv_iv_ogm_aggr_packet(buff_pos, packet_len, - batadv_ogm_packet->tt_num_changes)) { - tt_buff = packet_buff + buff_pos + BATADV_OGM_HLEN; + batadv_ogm_packet->tvlv_len)) { + tvlv_buff = packet_buff + buff_pos + BATADV_OGM_HLEN; - batadv_iv_ogm_process(ethhdr, batadv_ogm_packet, tt_buff, - if_incoming); + batadv_iv_ogm_process(ethhdr, batadv_ogm_packet, + tvlv_buff, if_incoming); buff_pos += BATADV_OGM_HLEN; - buff_pos += batadv_tt_len(batadv_ogm_packet->tt_num_changes); + buff_pos += ntohs(batadv_ogm_packet->tvlv_len); packet_pos = packet_buff + buff_pos; batadv_ogm_packet = (struct batadv_ogm_packet *)packet_pos; @@ -1392,6 +1534,106 @@ static int batadv_iv_ogm_receive(struct sk_buff *skb, return NET_RX_SUCCESS; } +/** + * batadv_iv_ogm_orig_print - print the originator table + * @bat_priv: the bat priv with all the soft interface information + * @seq: debugfs table seq_file struct + */ +static void batadv_iv_ogm_orig_print(struct batadv_priv *bat_priv, + struct seq_file *seq) +{ + struct batadv_neigh_node *neigh_node, *neigh_node_tmp; + struct batadv_hashtable *hash = bat_priv->orig_hash; + int last_seen_msecs, last_seen_secs; + struct batadv_orig_node *orig_node; + unsigned long last_seen_jiffies; + struct hlist_head *head; + int batman_count = 0; + uint32_t i; + + seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n", + "Originator", "last-seen", "#", BATADV_TQ_MAX_VALUE, + "Nexthop", "outgoingIF", "Potential nexthops"); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, head, hash_entry) { + neigh_node = batadv_orig_node_get_router(orig_node); + if (!neigh_node) + continue; + + if (neigh_node->bat_iv.tq_avg == 0) + goto next; + + last_seen_jiffies = jiffies - orig_node->last_seen; + last_seen_msecs = jiffies_to_msecs(last_seen_jiffies); + last_seen_secs = last_seen_msecs / 1000; + last_seen_msecs = last_seen_msecs % 1000; + + seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:", + orig_node->orig, last_seen_secs, + last_seen_msecs, neigh_node->bat_iv.tq_avg, + neigh_node->addr, + neigh_node->if_incoming->net_dev->name); + + hlist_for_each_entry_rcu(neigh_node_tmp, + &orig_node->neigh_list, list) { + seq_printf(seq, " %pM (%3i)", + neigh_node_tmp->addr, + neigh_node_tmp->bat_iv.tq_avg); + } + + seq_puts(seq, "\n"); + batman_count++; + +next: + batadv_neigh_node_free_ref(neigh_node); + } + rcu_read_unlock(); + } + + if (batman_count == 0) + seq_puts(seq, "No batman nodes in range ...\n"); +} + +/** + * batadv_iv_ogm_neigh_cmp - compare the metrics of two neighbors + * @neigh1: the first neighbor object of the comparison + * @neigh2: the second neighbor object of the comparison + * + * Returns a value less, equal to or greater than 0 if the metric via neigh1 is + * lower, the same as or higher than the metric via neigh2 + */ +static int batadv_iv_ogm_neigh_cmp(struct batadv_neigh_node *neigh1, + struct batadv_neigh_node *neigh2) +{ + uint8_t tq1, tq2; + + tq1 = neigh1->bat_iv.tq_avg; + tq2 = neigh2->bat_iv.tq_avg; + + return tq1 - tq2; +} + +/** + * batadv_iv_ogm_neigh_is_eob - check if neigh1 is equally good or better than + * neigh2 from the metric prospective + * @neigh1: the first neighbor object of the comparison + * @neigh2: the second neighbor object of the comparison + * + * Returns true if the metric via neigh1 is equally good or better than the + * metric via neigh2, false otherwise. + */ +static bool batadv_iv_ogm_neigh_is_eob(struct batadv_neigh_node *neigh1, + struct batadv_neigh_node *neigh2) +{ + int diff = batadv_iv_ogm_neigh_cmp(neigh1, neigh2); + + return diff > -BATADV_TQ_SIMILARITY_THRESHOLD; +} + static struct batadv_algo_ops batadv_batman_iv __read_mostly = { .name = "BATMAN_IV", .bat_iface_enable = batadv_iv_ogm_iface_enable, @@ -1400,6 +1642,12 @@ static struct batadv_algo_ops batadv_batman_iv __read_mostly = { .bat_primary_iface_set = batadv_iv_ogm_primary_iface_set, .bat_ogm_schedule = batadv_iv_ogm_schedule, .bat_ogm_emit = batadv_iv_ogm_emit, + .bat_neigh_cmp = batadv_iv_ogm_neigh_cmp, + .bat_neigh_is_equiv_or_better = batadv_iv_ogm_neigh_is_eob, + .bat_orig_print = batadv_iv_ogm_orig_print, + .bat_orig_free = batadv_iv_ogm_orig_free, + .bat_orig_add_if = batadv_iv_ogm_orig_add_if, + .bat_orig_del_if = batadv_iv_ogm_orig_del_if, }; int __init batadv_iv_init(void) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 264de88db3208290b940cc27b09e44f79e1616c9..28eb5e6d0a02510a0a7b4e268f7dc34a26bc182a 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -411,10 +411,10 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, uint8_t *orig, return NULL; } - /* this is a gateway now, remove any tt entries */ + /* this is a gateway now, remove any TT entry on this VLAN */ orig_node = batadv_orig_hash_find(bat_priv, orig); if (orig_node) { - batadv_tt_global_del_orig(bat_priv, orig_node, + batadv_tt_global_del_orig(bat_priv, orig_node, vid, "became a backbone gateway"); batadv_orig_node_free_ref(orig_node); } @@ -858,30 +858,28 @@ static int batadv_bla_process_claim(struct batadv_priv *bat_priv, struct batadv_hard_iface *primary_if, struct sk_buff *skb) { - struct ethhdr *ethhdr; + struct batadv_bla_claim_dst *bla_dst; + uint8_t *hw_src, *hw_dst; struct vlan_ethhdr *vhdr; + struct ethhdr *ethhdr; struct arphdr *arphdr; - uint8_t *hw_src, *hw_dst; - struct batadv_bla_claim_dst *bla_dst; - uint16_t proto; + unsigned short vid; + __be16 proto; int headlen; - unsigned short vid = BATADV_NO_FLAGS; int ret; + vid = batadv_get_vid(skb, 0); ethhdr = eth_hdr(skb); - if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { + proto = ethhdr->h_proto; + headlen = ETH_HLEN; + if (vid & BATADV_VLAN_HAS_TAG) { vhdr = (struct vlan_ethhdr *)ethhdr; - vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; - vid |= BATADV_VLAN_HAS_TAG; - proto = ntohs(vhdr->h_vlan_encapsulated_proto); - headlen = sizeof(*vhdr); - } else { - proto = ntohs(ethhdr->h_proto); - headlen = ETH_HLEN; + proto = vhdr->h_vlan_encapsulated_proto; + headlen += VLAN_HLEN; } - if (proto != ETH_P_ARP) + if (proto != htons(ETH_P_ARP)) return 0; /* not a claim frame */ /* this must be a ARP frame. check if it is a claim. */ @@ -1317,12 +1315,14 @@ out: /* @bat_priv: the bat priv with all the soft interface information * @orig: originator mac address + * @vid: VLAN identifier * - * check if the originator is a gateway for any VLAN ID. + * Check if the originator is a gateway for the VLAN identified by vid. * - * returns 1 if it is found, 0 otherwise + * Returns true if orig is a backbone for this vid, false otherwise. */ -int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig) +bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig, + unsigned short vid) { struct batadv_hashtable *hash = bat_priv->bla.backbone_hash; struct hlist_head *head; @@ -1330,25 +1330,26 @@ int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig) int i; if (!atomic_read(&bat_priv->bridge_loop_avoidance)) - return 0; + return false; if (!hash) - return 0; + return false; for (i = 0; i < hash->size; i++) { head = &hash->table[i]; rcu_read_lock(); hlist_for_each_entry_rcu(backbone_gw, head, hash_entry) { - if (batadv_compare_eth(backbone_gw->orig, orig)) { + if (batadv_compare_eth(backbone_gw->orig, orig) && + backbone_gw->vid == vid) { rcu_read_unlock(); - return 1; + return true; } } rcu_read_unlock(); } - return 0; + return false; } @@ -1365,10 +1366,8 @@ int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig) int batadv_bla_is_backbone_gw(struct sk_buff *skb, struct batadv_orig_node *orig_node, int hdr_size) { - struct ethhdr *ethhdr; - struct vlan_ethhdr *vhdr; struct batadv_bla_backbone_gw *backbone_gw; - unsigned short vid = BATADV_NO_FLAGS; + unsigned short vid; if (!atomic_read(&orig_node->bat_priv->bridge_loop_avoidance)) return 0; @@ -1377,16 +1376,7 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb, if (!pskb_may_pull(skb, hdr_size + ETH_HLEN)) return 0; - ethhdr = (struct ethhdr *)(((uint8_t *)skb->data) + hdr_size); - - if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { - if (!pskb_may_pull(skb, hdr_size + sizeof(struct vlan_ethhdr))) - return 0; - - vhdr = (struct vlan_ethhdr *)(skb->data + hdr_size); - vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; - vid |= BATADV_VLAN_HAS_TAG; - } + vid = batadv_get_vid(skb, hdr_size); /* see if this originator is a backbone gw for this VLAN */ backbone_gw = batadv_backbone_hash_find(orig_node->bat_priv, diff --git a/net/batman-adv/bridge_loop_avoidance.h b/net/batman-adv/bridge_loop_avoidance.h index 4b102e71e5bd63c2bee71f14bcdb0b799398ef5e..da173e760e775caab684d63d2a1749cb55e0b930 100644 --- a/net/batman-adv/bridge_loop_avoidance.h +++ b/net/batman-adv/bridge_loop_avoidance.h @@ -30,7 +30,8 @@ int batadv_bla_is_backbone_gw(struct sk_buff *skb, int batadv_bla_claim_table_seq_print_text(struct seq_file *seq, void *offset); int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, void *offset); -int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig); +bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, uint8_t *orig, + unsigned short vid); int batadv_bla_check_bcast_duplist(struct batadv_priv *bat_priv, struct sk_buff *skb); void batadv_bla_update_orig_address(struct batadv_priv *bat_priv, @@ -74,10 +75,11 @@ static inline int batadv_bla_backbone_table_seq_print_text(struct seq_file *seq, return 0; } -static inline int batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, - uint8_t *orig) +static inline bool batadv_bla_is_backbone_gw_orig(struct batadv_priv *bat_priv, + uint8_t *orig, + unsigned short vid) { - return 0; + return false; } static inline int diff --git a/net/batman-adv/debugfs.c b/net/batman-adv/debugfs.c index f186a55b23c3fbd1eda3a2d5877b5b781fd647eb..049a7a2ac5b69b83422a3292ee7ebb2183e36114 100644 --- a/net/batman-adv/debugfs.c +++ b/net/batman-adv/debugfs.c @@ -28,7 +28,6 @@ #include "gateway_common.h" #include "gateway_client.h" #include "soft-interface.h" -#include "vis.h" #include "icmp_socket.h" #include "bridge_loop_avoidance.h" #include "distributed-arp-table.h" @@ -300,12 +299,6 @@ static int batadv_transtable_local_open(struct inode *inode, struct file *file) return single_open(file, batadv_tt_local_seq_print_text, net_dev); } -static int batadv_vis_data_open(struct inode *inode, struct file *file) -{ - struct net_device *net_dev = (struct net_device *)inode->i_private; - return single_open(file, batadv_vis_seq_print_text, net_dev); -} - struct batadv_debuginfo { struct attribute attr; const struct file_operations fops; @@ -356,7 +349,6 @@ static BATADV_DEBUGINFO(dat_cache, S_IRUGO, batadv_dat_cache_open); #endif static BATADV_DEBUGINFO(transtable_local, S_IRUGO, batadv_transtable_local_open); -static BATADV_DEBUGINFO(vis_data, S_IRUGO, batadv_vis_data_open); #ifdef CONFIG_BATMAN_ADV_NC static BATADV_DEBUGINFO(nc_nodes, S_IRUGO, batadv_nc_nodes_open); #endif @@ -373,7 +365,6 @@ static struct batadv_debuginfo *batadv_mesh_debuginfos[] = { &batadv_debuginfo_dat_cache, #endif &batadv_debuginfo_transtable_local, - &batadv_debuginfo_vis_data, #ifdef CONFIG_BATMAN_ADV_NC &batadv_debuginfo_nc_nodes, #endif diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 06345d401588c949762edfa14aaeb3f018a8e294..6c8c3934bd7b44bac8643683683c494e003f1ed2 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -19,6 +19,7 @@ #include #include +#include #include #include "main.h" @@ -29,7 +30,6 @@ #include "send.h" #include "types.h" #include "translation-table.h" -#include "unicast.h" static void batadv_dat_purge(struct work_struct *work); @@ -206,15 +206,11 @@ static __be32 batadv_arp_ip_dst(struct sk_buff *skb, int hdr_size) */ static uint32_t batadv_hash_dat(const void *data, uint32_t size) { - const unsigned char *key = data; uint32_t hash = 0; - size_t i; + const struct batadv_dat_entry *dat = data; - for (i = 0; i < 4; i++) { - hash += key[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } + hash = batadv_hash_bytes(hash, &dat->ip, sizeof(dat->ip)); + hash = batadv_hash_bytes(hash, &dat->vid, sizeof(dat->vid)); hash += (hash << 3); hash ^= (hash >> 11); @@ -228,21 +224,26 @@ static uint32_t batadv_hash_dat(const void *data, uint32_t size) * table * @bat_priv: the bat priv with all the soft interface information * @ip: search key + * @vid: VLAN identifier * * Returns the dat_entry if found, NULL otherwise. */ static struct batadv_dat_entry * -batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip) +batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip, + unsigned short vid) { struct hlist_head *head; - struct batadv_dat_entry *dat_entry, *dat_entry_tmp = NULL; + struct batadv_dat_entry to_find, *dat_entry, *dat_entry_tmp = NULL; struct batadv_hashtable *hash = bat_priv->dat.hash; uint32_t index; if (!hash) return NULL; - index = batadv_hash_dat(&ip, hash->size); + to_find.ip = ip; + to_find.vid = vid; + + index = batadv_hash_dat(&to_find, hash->size); head = &hash->table[index]; rcu_read_lock(); @@ -266,22 +267,24 @@ batadv_dat_entry_hash_find(struct batadv_priv *bat_priv, __be32 ip) * @bat_priv: the bat priv with all the soft interface information * @ip: ipv4 to add/edit * @mac_addr: mac address to assign to the given ipv4 + * @vid: VLAN identifier */ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, - uint8_t *mac_addr) + uint8_t *mac_addr, unsigned short vid) { struct batadv_dat_entry *dat_entry; int hash_added; - dat_entry = batadv_dat_entry_hash_find(bat_priv, ip); + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip, vid); /* if this entry is already known, just update it */ if (dat_entry) { if (!batadv_compare_eth(dat_entry->mac_addr, mac_addr)) memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN); dat_entry->last_update = jiffies; batadv_dbg(BATADV_DBG_DAT, bat_priv, - "Entry updated: %pI4 %pM\n", &dat_entry->ip, - dat_entry->mac_addr); + "Entry updated: %pI4 %pM (vid: %d)\n", + &dat_entry->ip, dat_entry->mac_addr, + BATADV_PRINT_VID(vid)); goto out; } @@ -290,12 +293,13 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, goto out; dat_entry->ip = ip; + dat_entry->vid = vid; memcpy(dat_entry->mac_addr, mac_addr, ETH_ALEN); dat_entry->last_update = jiffies; atomic_set(&dat_entry->refcount, 2); hash_added = batadv_hash_add(bat_priv->dat.hash, batadv_compare_dat, - batadv_hash_dat, &dat_entry->ip, + batadv_hash_dat, dat_entry, &dat_entry->hash_entry); if (unlikely(hash_added != 0)) { @@ -304,8 +308,8 @@ static void batadv_dat_entry_add(struct batadv_priv *bat_priv, __be32 ip, goto out; } - batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM\n", - &dat_entry->ip, dat_entry->mac_addr); + batadv_dbg(BATADV_DBG_DAT, bat_priv, "New entry added: %pI4 %pM (vid: %d)\n", + &dat_entry->ip, dat_entry->mac_addr, BATADV_PRINT_VID(vid)); out: if (dat_entry) @@ -419,6 +423,10 @@ static bool batadv_is_orig_node_eligible(struct batadv_dat_candidate *res, bool ret = false; int j; + /* check if orig node candidate is running DAT */ + if (!(candidate->capabilities & BATADV_ORIG_CAPA_HAS_DAT)) + goto out; + /* Check if this node has already been selected... */ for (j = 0; j < select; j++) if (res[j].orig_node == candidate) @@ -588,9 +596,9 @@ static bool batadv_dat_send_data(struct batadv_priv *bat_priv, goto free_orig; tmp_skb = pskb_copy(skb, GFP_ATOMIC); - if (!batadv_unicast_4addr_prepare_skb(bat_priv, tmp_skb, - cand[i].orig_node, - packet_subtype)) { + if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, tmp_skb, + cand[i].orig_node, + packet_subtype)) { kfree_skb(tmp_skb); goto free_neigh; } @@ -625,6 +633,59 @@ out: return ret; } +/** + * batadv_dat_tvlv_container_update - update the dat tvlv container after dat + * setting change + * @bat_priv: the bat priv with all the soft interface information + */ +static void batadv_dat_tvlv_container_update(struct batadv_priv *bat_priv) +{ + char dat_mode; + + dat_mode = atomic_read(&bat_priv->distributed_arp_table); + + switch (dat_mode) { + case 0: + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1); + break; + case 1: + batadv_tvlv_container_register(bat_priv, BATADV_TVLV_DAT, 1, + NULL, 0); + break; + } +} + +/** + * batadv_dat_status_update - update the dat tvlv container after dat + * setting change + * @net_dev: the soft interface net device + */ +void batadv_dat_status_update(struct net_device *net_dev) +{ + struct batadv_priv *bat_priv = netdev_priv(net_dev); + batadv_dat_tvlv_container_update(bat_priv); +} + +/** + * batadv_gw_tvlv_ogm_handler_v1 - process incoming dat tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node of the ogm + * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) + * @tvlv_value: tvlv buffer containing the gateway data + * @tvlv_value_len: tvlv buffer length + */ +static void batadv_dat_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t flags, + void *tvlv_value, + uint16_t tvlv_value_len) +{ + if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) + orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_DAT; + else + orig->capabilities |= BATADV_ORIG_CAPA_HAS_DAT; +} + /** * batadv_dat_hash_free - free the local DAT hash table * @bat_priv: the bat priv with all the soft interface information @@ -657,6 +718,10 @@ int batadv_dat_init(struct batadv_priv *bat_priv) batadv_dat_start_timer(bat_priv); + batadv_tvlv_handler_register(bat_priv, batadv_dat_tvlv_ogm_handler_v1, + NULL, BATADV_TVLV_DAT, 1, + BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + batadv_dat_tvlv_container_update(bat_priv); return 0; } @@ -666,6 +731,9 @@ int batadv_dat_init(struct batadv_priv *bat_priv) */ void batadv_dat_free(struct batadv_priv *bat_priv) { + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_DAT, 1); + batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_DAT, 1); + cancel_delayed_work_sync(&bat_priv->dat.work); batadv_dat_hash_free(bat_priv); @@ -693,8 +761,8 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset) goto out; seq_printf(seq, "Distributed ARP Table (%s):\n", net_dev->name); - seq_printf(seq, " %-7s %-13s %5s\n", "IPv4", "MAC", - "last-seen"); + seq_printf(seq, " %-7s %-9s %4s %11s\n", "IPv4", + "MAC", "VID", "last-seen"); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@ -707,8 +775,9 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset) last_seen_msecs = last_seen_msecs % 60000; last_seen_secs = last_seen_msecs / 1000; - seq_printf(seq, " * %15pI4 %14pM %6i:%02i\n", + seq_printf(seq, " * %15pI4 %14pM %4i %6i:%02i\n", &dat_entry->ip, dat_entry->mac_addr, + BATADV_PRINT_VID(dat_entry->vid), last_seen_mins, last_seen_secs); } rcu_read_unlock(); @@ -794,6 +863,31 @@ out: return type; } +/** + * batadv_dat_get_vid - extract the VLAN identifier from skb if any + * @skb: the buffer containing the packet to extract the VID from + * @hdr_size: the size of the batman-adv header encapsulating the packet + * + * If the packet embedded in the skb is vlan tagged this function returns the + * VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned. + */ +static unsigned short batadv_dat_get_vid(struct sk_buff *skb, int *hdr_size) +{ + unsigned short vid; + + vid = batadv_get_vid(skb, *hdr_size); + + /* ARP parsing functions jump forward of hdr_size + ETH_HLEN. + * If the header contained in the packet is a VLAN one (which is longer) + * hdr_size is updated so that the functions will still skip the + * correct amount of bytes. + */ + if (vid & BATADV_VLAN_HAS_TAG) + *hdr_size += VLAN_HLEN; + + return vid; +} + /** * batadv_dat_snoop_outgoing_arp_request - snoop the ARP request and try to * answer using DAT @@ -813,26 +907,31 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, bool ret = false; struct batadv_dat_entry *dat_entry = NULL; struct sk_buff *skb_new; + int hdr_size = 0; + unsigned short vid; if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; - type = batadv_arp_get_type(bat_priv, skb, 0); + vid = batadv_dat_get_vid(skb, &hdr_size); + + type = batadv_arp_get_type(bat_priv, skb, hdr_size); /* If the node gets an ARP_REQUEST it has to send a DHT_GET unicast * message to the selected DHT candidates */ if (type != ARPOP_REQUEST) goto out; - batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REQUEST"); + batadv_dbg_arp(bat_priv, skb, type, hdr_size, + "Parsing outgoing ARP REQUEST"); - ip_src = batadv_arp_ip_src(skb, 0); - hw_src = batadv_arp_hw_src(skb, 0); - ip_dst = batadv_arp_ip_dst(skb, 0); + ip_src = batadv_arp_ip_src(skb, hdr_size); + hw_src = batadv_arp_hw_src(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); - batadv_dat_entry_add(bat_priv, ip_src, hw_src); + batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); - dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid); if (dat_entry) { /* If the ARP request is destined for a local client the local * client will answer itself. DAT would only generate a @@ -842,7 +941,8 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, * additional DAT answer may trigger kernel warnings about * a packet coming from the wrong port. */ - if (batadv_is_my_client(bat_priv, dat_entry->mac_addr)) { + if (batadv_is_my_client(bat_priv, dat_entry->mac_addr, + BATADV_NO_FLAGS)) { ret = true; goto out; } @@ -853,11 +953,15 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, if (!skb_new) goto out; + if (vid & BATADV_VLAN_HAS_TAG) + skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q), + vid & VLAN_VID_MASK); + skb_reset_mac_header(skb_new); skb_new->protocol = eth_type_trans(skb_new, bat_priv->soft_iface); bat_priv->stats.rx_packets++; - bat_priv->stats.rx_bytes += skb->len + ETH_HLEN; + bat_priv->stats.rx_bytes += skb->len + ETH_HLEN + hdr_size; bat_priv->soft_iface->last_rx = jiffies; netif_rx(skb_new); @@ -892,11 +996,14 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, struct sk_buff *skb_new; struct batadv_dat_entry *dat_entry = NULL; bool ret = false; + unsigned short vid; int err; if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); + type = batadv_arp_get_type(bat_priv, skb, hdr_size); if (type != ARPOP_REQUEST) goto out; @@ -908,9 +1015,9 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, batadv_dbg_arp(bat_priv, skb, type, hdr_size, "Parsing incoming ARP REQUEST"); - batadv_dat_entry_add(bat_priv, ip_src, hw_src); + batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); - dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid); if (!dat_entry) goto out; @@ -921,17 +1028,22 @@ bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, if (!skb_new) goto out; + if (vid & BATADV_VLAN_HAS_TAG) + skb_new = vlan_insert_tag(skb_new, htons(ETH_P_8021Q), + vid & VLAN_VID_MASK); + /* To preserve backwards compatibility, the node has choose the outgoing * format based on the incoming request packet type. The assumption is * that a node not using the 4addr packet format doesn't support it. */ if (hdr_size == sizeof(struct batadv_unicast_4addr_packet)) - err = batadv_unicast_4addr_send_skb(bat_priv, skb_new, - BATADV_P_DAT_CACHE_REPLY); + err = batadv_send_skb_via_tt_4addr(bat_priv, skb_new, + BATADV_P_DAT_CACHE_REPLY, + vid); else - err = batadv_unicast_send_skb(bat_priv, skb_new); + err = batadv_send_skb_via_tt(bat_priv, skb_new, vid); - if (!err) { + if (err != NET_XMIT_DROP) { batadv_inc_counter(bat_priv, BATADV_CNT_DAT_CACHED_REPLY_TX); ret = true; } @@ -954,23 +1066,28 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, uint16_t type; __be32 ip_src, ip_dst; uint8_t *hw_src, *hw_dst; + int hdr_size = 0; + unsigned short vid; if (!atomic_read(&bat_priv->distributed_arp_table)) return; - type = batadv_arp_get_type(bat_priv, skb, 0); + vid = batadv_dat_get_vid(skb, &hdr_size); + + type = batadv_arp_get_type(bat_priv, skb, hdr_size); if (type != ARPOP_REPLY) return; - batadv_dbg_arp(bat_priv, skb, type, 0, "Parsing outgoing ARP REPLY"); + batadv_dbg_arp(bat_priv, skb, type, hdr_size, + "Parsing outgoing ARP REPLY"); - hw_src = batadv_arp_hw_src(skb, 0); - ip_src = batadv_arp_ip_src(skb, 0); - hw_dst = batadv_arp_hw_dst(skb, 0); - ip_dst = batadv_arp_ip_dst(skb, 0); + hw_src = batadv_arp_hw_src(skb, hdr_size); + ip_src = batadv_arp_ip_src(skb, hdr_size); + hw_dst = batadv_arp_hw_dst(skb, hdr_size); + ip_dst = batadv_arp_ip_dst(skb, hdr_size); - batadv_dat_entry_add(bat_priv, ip_src, hw_src); - batadv_dat_entry_add(bat_priv, ip_dst, hw_dst); + batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); + batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid); /* Send the ARP reply to the candidates for both the IP addresses that * the node obtained from the ARP reply @@ -992,10 +1109,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, __be32 ip_src, ip_dst; uint8_t *hw_src, *hw_dst; bool ret = false; + unsigned short vid; if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; + vid = batadv_dat_get_vid(skb, &hdr_size); + type = batadv_arp_get_type(bat_priv, skb, hdr_size); if (type != ARPOP_REPLY) goto out; @@ -1011,13 +1131,13 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, /* Update our internal cache with both the IP addresses the node got * within the ARP reply */ - batadv_dat_entry_add(bat_priv, ip_src, hw_src); - batadv_dat_entry_add(bat_priv, ip_dst, hw_dst); + batadv_dat_entry_add(bat_priv, ip_src, hw_src, vid); + batadv_dat_entry_add(bat_priv, ip_dst, hw_dst, vid); /* if this REPLY is directed to a client of mine, let's deliver the * packet to the interface */ - ret = !batadv_is_my_client(bat_priv, hw_dst); + ret = !batadv_is_my_client(bat_priv, hw_dst, vid); out: if (ret) kfree_skb(skb); @@ -1040,7 +1160,8 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, __be32 ip_dst; struct batadv_dat_entry *dat_entry = NULL; bool ret = false; - const size_t bcast_len = sizeof(struct batadv_bcast_packet); + int hdr_size = sizeof(struct batadv_bcast_packet); + unsigned short vid; if (!atomic_read(&bat_priv->distributed_arp_table)) goto out; @@ -1051,12 +1172,14 @@ bool batadv_dat_drop_broadcast_packet(struct batadv_priv *bat_priv, if (forw_packet->num_packets) goto out; - type = batadv_arp_get_type(bat_priv, forw_packet->skb, bcast_len); + vid = batadv_dat_get_vid(forw_packet->skb, &hdr_size); + + type = batadv_arp_get_type(bat_priv, forw_packet->skb, hdr_size); if (type != ARPOP_REQUEST) goto out; - ip_dst = batadv_arp_ip_dst(forw_packet->skb, bcast_len); - dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst); + ip_dst = batadv_arp_ip_dst(forw_packet->skb, hdr_size); + dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst, vid); /* check if the node already got this entry */ if (!dat_entry) { batadv_dbg(BATADV_DBG_DAT, bat_priv, diff --git a/net/batman-adv/distributed-arp-table.h b/net/batman-adv/distributed-arp-table.h index 125c8c6fcfadfed4d8b388d3f7775877a5b6ff93..60d853beb8d8214c78d25f191aff77eafaabaefd 100644 --- a/net/batman-adv/distributed-arp-table.h +++ b/net/batman-adv/distributed-arp-table.h @@ -29,6 +29,7 @@ #define BATADV_DAT_ADDR_MAX ((batadv_dat_addr_t)~(batadv_dat_addr_t)0) +void batadv_dat_status_update(struct net_device *net_dev); bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, struct sk_buff *skb); bool batadv_dat_snoop_incoming_arp_request(struct batadv_priv *bat_priv, @@ -98,6 +99,10 @@ static inline void batadv_dat_inc_counter(struct batadv_priv *bat_priv, #else +static inline void batadv_dat_status_update(struct net_device *net_dev) +{ +} + static inline bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv, struct sk_buff *skb) diff --git a/net/batman-adv/fragmentation.c b/net/batman-adv/fragmentation.c new file mode 100644 index 0000000000000000000000000000000000000000..271d321b3a04063b4f9dfe476c7c07d9e2e50048 --- /dev/null +++ b/net/batman-adv/fragmentation.c @@ -0,0 +1,491 @@ +/* Copyright (C) 2013 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "main.h" +#include "fragmentation.h" +#include "send.h" +#include "originator.h" +#include "routing.h" +#include "hard-interface.h" +#include "soft-interface.h" + + +/** + * batadv_frag_clear_chain - delete entries in the fragment buffer chain + * @head: head of chain with entries. + * + * Free fragments in the passed hlist. Should be called with appropriate lock. + */ +static void batadv_frag_clear_chain(struct hlist_head *head) +{ + struct batadv_frag_list_entry *entry; + struct hlist_node *node; + + hlist_for_each_entry_safe(entry, node, head, list) { + hlist_del(&entry->list); + kfree_skb(entry->skb); + kfree(entry); + } +} + +/** + * batadv_frag_purge_orig - free fragments associated to an orig + * @orig_node: originator to free fragments from + * @check_cb: optional function to tell if an entry should be purged + */ +void batadv_frag_purge_orig(struct batadv_orig_node *orig_node, + bool (*check_cb)(struct batadv_frag_table_entry *)) +{ + struct batadv_frag_table_entry *chain; + uint8_t i; + + for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) { + chain = &orig_node->fragments[i]; + spin_lock_bh(&orig_node->fragments[i].lock); + + if (!check_cb || check_cb(chain)) { + batadv_frag_clear_chain(&orig_node->fragments[i].head); + orig_node->fragments[i].size = 0; + } + + spin_unlock_bh(&orig_node->fragments[i].lock); + } +} + +/** + * batadv_frag_size_limit - maximum possible size of packet to be fragmented + * + * Returns the maximum size of payload that can be fragmented. + */ +static int batadv_frag_size_limit(void) +{ + int limit = BATADV_FRAG_MAX_FRAG_SIZE; + + limit -= sizeof(struct batadv_frag_packet); + limit *= BATADV_FRAG_MAX_FRAGMENTS; + + return limit; +} + +/** + * batadv_frag_init_chain - check and prepare fragment chain for new fragment + * @chain: chain in fragments table to init + * @seqno: sequence number of the received fragment + * + * Make chain ready for a fragment with sequence number "seqno". Delete existing + * entries if they have an "old" sequence number. + * + * Caller must hold chain->lock. + * + * Returns true if chain is empty and caller can just insert the new fragment + * without searching for the right position. + */ +static bool batadv_frag_init_chain(struct batadv_frag_table_entry *chain, + uint16_t seqno) +{ + if (chain->seqno == seqno) + return false; + + if (!hlist_empty(&chain->head)) + batadv_frag_clear_chain(&chain->head); + + chain->size = 0; + chain->seqno = seqno; + + return true; +} + +/** + * batadv_frag_insert_packet - insert a fragment into a fragment chain + * @orig_node: originator that the fragment was received from + * @skb: skb to insert + * @chain_out: list head to attach complete chains of fragments to + * + * Insert a new fragment into the reverse ordered chain in the right table + * entry. The hash table entry is cleared if "old" fragments exist in it. + * + * Returns true if skb is buffered, false on error. If the chain has all the + * fragments needed to merge the packet, the chain is moved to the passed head + * to avoid locking the chain in the table. + */ +static bool batadv_frag_insert_packet(struct batadv_orig_node *orig_node, + struct sk_buff *skb, + struct hlist_head *chain_out) +{ + struct batadv_frag_table_entry *chain; + struct batadv_frag_list_entry *frag_entry_new = NULL, *frag_entry_curr; + struct batadv_frag_packet *frag_packet; + uint8_t bucket; + uint16_t seqno, hdr_size = sizeof(struct batadv_frag_packet); + bool ret = false; + + /* Linearize packet to avoid linearizing 16 packets in a row when doing + * the later merge. Non-linear merge should be added to remove this + * linearization. + */ + if (skb_linearize(skb) < 0) + goto err; + + frag_packet = (struct batadv_frag_packet *)skb->data; + seqno = ntohs(frag_packet->seqno); + bucket = seqno % BATADV_FRAG_BUFFER_COUNT; + + frag_entry_new = kmalloc(sizeof(*frag_entry_new), GFP_ATOMIC); + if (!frag_entry_new) + goto err; + + frag_entry_new->skb = skb; + frag_entry_new->no = frag_packet->no; + + /* Select entry in the "chain table" and delete any prior fragments + * with another sequence number. batadv_frag_init_chain() returns true, + * if the list is empty at return. + */ + chain = &orig_node->fragments[bucket]; + spin_lock_bh(&chain->lock); + if (batadv_frag_init_chain(chain, seqno)) { + hlist_add_head(&frag_entry_new->list, &chain->head); + chain->size = skb->len - hdr_size; + chain->timestamp = jiffies; + ret = true; + goto out; + } + + /* Find the position for the new fragment. */ + hlist_for_each_entry(frag_entry_curr, &chain->head, list) { + /* Drop packet if fragment already exists. */ + if (frag_entry_curr->no == frag_entry_new->no) + goto err_unlock; + + /* Order fragments from highest to lowest. */ + if (frag_entry_curr->no < frag_entry_new->no) { + hlist_add_before(&frag_entry_new->list, + &frag_entry_curr->list); + chain->size += skb->len - hdr_size; + chain->timestamp = jiffies; + ret = true; + goto out; + } + } + + /* Reached the end of the list, so insert after 'frag_entry_curr'. */ + if (likely(frag_entry_curr)) { + hlist_add_after(&frag_entry_curr->list, &frag_entry_new->list); + chain->size += skb->len - hdr_size; + chain->timestamp = jiffies; + ret = true; + } + +out: + if (chain->size > batadv_frag_size_limit() || + ntohs(frag_packet->total_size) > batadv_frag_size_limit()) { + /* Clear chain if total size of either the list or the packet + * exceeds the maximum size of one merged packet. + */ + batadv_frag_clear_chain(&chain->head); + chain->size = 0; + } else if (ntohs(frag_packet->total_size) == chain->size) { + /* All fragments received. Hand over chain to caller. */ + hlist_move_list(&chain->head, chain_out); + chain->size = 0; + } + +err_unlock: + spin_unlock_bh(&chain->lock); + +err: + if (!ret) + kfree(frag_entry_new); + + return ret; +} + +/** + * batadv_frag_merge_packets - merge a chain of fragments + * @chain: head of chain with fragments + * @skb: packet with total size of skb after merging + * + * Expand the first skb in the chain and copy the content of the remaining + * skb's into the expanded one. After doing so, clear the chain. + * + * Returns the merged skb or NULL on error. + */ +static struct sk_buff * +batadv_frag_merge_packets(struct hlist_head *chain, struct sk_buff *skb) +{ + struct batadv_frag_packet *packet; + struct batadv_frag_list_entry *entry; + struct sk_buff *skb_out = NULL; + int size, hdr_size = sizeof(struct batadv_frag_packet); + + /* Make sure incoming skb has non-bogus data. */ + packet = (struct batadv_frag_packet *)skb->data; + size = ntohs(packet->total_size); + if (size > batadv_frag_size_limit()) + goto free; + + /* Remove first entry, as this is the destination for the rest of the + * fragments. + */ + entry = hlist_entry(chain->first, struct batadv_frag_list_entry, list); + hlist_del(&entry->list); + skb_out = entry->skb; + kfree(entry); + + /* Make room for the rest of the fragments. */ + if (pskb_expand_head(skb_out, 0, size - skb->len, GFP_ATOMIC) < 0) { + kfree_skb(skb_out); + skb_out = NULL; + goto free; + } + + /* Move the existing MAC header to just before the payload. (Override + * the fragment header.) + */ + skb_pull_rcsum(skb_out, hdr_size); + memmove(skb_out->data - ETH_HLEN, skb_mac_header(skb_out), ETH_HLEN); + skb_set_mac_header(skb_out, -ETH_HLEN); + skb_reset_network_header(skb_out); + skb_reset_transport_header(skb_out); + + /* Copy the payload of the each fragment into the last skb */ + hlist_for_each_entry(entry, chain, list) { + size = entry->skb->len - hdr_size; + memcpy(skb_put(skb_out, size), entry->skb->data + hdr_size, + size); + } + +free: + /* Locking is not needed, because 'chain' is not part of any orig. */ + batadv_frag_clear_chain(chain); + return skb_out; +} + +/** + * batadv_frag_skb_buffer - buffer fragment for later merge + * @skb: skb to buffer + * @orig_node_src: originator that the skb is received from + * + * Add fragment to buffer and merge fragments if possible. + * + * There are three possible outcomes: 1) Packet is merged: Return true and + * set *skb to merged packet; 2) Packet is buffered: Return true and set *skb + * to NULL; 3) Error: Return false and leave skb as is. + */ +bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node_src) +{ + struct sk_buff *skb_out = NULL; + struct hlist_head head = HLIST_HEAD_INIT; + bool ret = false; + + /* Add packet to buffer and table entry if merge is possible. */ + if (!batadv_frag_insert_packet(orig_node_src, *skb, &head)) + goto out_err; + + /* Leave if more fragments are needed to merge. */ + if (hlist_empty(&head)) + goto out; + + skb_out = batadv_frag_merge_packets(&head, *skb); + if (!skb_out) + goto out_err; + +out: + *skb = skb_out; + ret = true; +out_err: + return ret; +} + +/** + * batadv_frag_skb_fwd - forward fragments that would exceed MTU when merged + * @skb: skb to forward + * @recv_if: interface that the skb is received on + * @orig_node_src: originator that the skb is received from + * + * Look up the next-hop of the fragments payload and check if the merged packet + * will exceed the MTU towards the next-hop. If so, the fragment is forwarded + * without merging it. + * + * Returns true if the fragment is consumed/forwarded, false otherwise. + */ +bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, + struct batadv_orig_node *orig_node_src) +{ + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node_dst = NULL; + struct batadv_neigh_node *neigh_node = NULL; + struct batadv_frag_packet *packet; + uint16_t total_size; + bool ret = false; + + packet = (struct batadv_frag_packet *)skb->data; + orig_node_dst = batadv_orig_hash_find(bat_priv, packet->dest); + if (!orig_node_dst) + goto out; + + neigh_node = batadv_find_router(bat_priv, orig_node_dst, recv_if); + if (!neigh_node) + goto out; + + /* Forward the fragment, if the merged packet would be too big to + * be assembled. + */ + total_size = ntohs(packet->total_size); + if (total_size > neigh_node->if_incoming->net_dev->mtu) { + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_FWD); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_FWD_BYTES, + skb->len + ETH_HLEN); + + packet->header.ttl--; + batadv_send_skb_packet(skb, neigh_node->if_incoming, + neigh_node->addr); + ret = true; + } + +out: + if (orig_node_dst) + batadv_orig_node_free_ref(orig_node_dst); + if (neigh_node) + batadv_neigh_node_free_ref(neigh_node); + return ret; +} + +/** + * batadv_frag_create - create a fragment from skb + * @skb: skb to create fragment from + * @frag_head: header to use in new fragment + * @mtu: size of new fragment + * + * Split the passed skb into two fragments: A new one with size matching the + * passed mtu and the old one with the rest. The new skb contains data from the + * tail of the old skb. + * + * Returns the new fragment, NULL on error. + */ +static struct sk_buff *batadv_frag_create(struct sk_buff *skb, + struct batadv_frag_packet *frag_head, + unsigned int mtu) +{ + struct sk_buff *skb_fragment; + unsigned header_size = sizeof(*frag_head); + unsigned fragment_size = mtu - header_size; + + skb_fragment = netdev_alloc_skb(NULL, mtu + ETH_HLEN); + if (!skb_fragment) + goto err; + + skb->priority = TC_PRIO_CONTROL; + + /* Eat the last mtu-bytes of the skb */ + skb_reserve(skb_fragment, header_size + ETH_HLEN); + skb_split(skb, skb_fragment, skb->len - fragment_size); + + /* Add the header */ + skb_push(skb_fragment, header_size); + memcpy(skb_fragment->data, frag_head, header_size); + +err: + return skb_fragment; +} + +/** + * batadv_frag_send_packet - create up to 16 fragments from the passed skb + * @skb: skb to create fragments from + * @orig_node: final destination of the created fragments + * @neigh_node: next-hop of the created fragments + * + * Returns true on success, false otherwise. + */ +bool batadv_frag_send_packet(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_neigh_node *neigh_node) +{ + struct batadv_priv *bat_priv; + struct batadv_hard_iface *primary_if; + struct batadv_frag_packet frag_header; + struct sk_buff *skb_fragment; + unsigned mtu = neigh_node->if_incoming->net_dev->mtu; + unsigned header_size = sizeof(frag_header); + unsigned max_fragment_size, max_packet_size; + + /* To avoid merge and refragmentation at next-hops we never send + * fragments larger than BATADV_FRAG_MAX_FRAG_SIZE + */ + mtu = min_t(unsigned, mtu, BATADV_FRAG_MAX_FRAG_SIZE); + max_fragment_size = (mtu - header_size - ETH_HLEN); + max_packet_size = max_fragment_size * BATADV_FRAG_MAX_FRAGMENTS; + + /* Don't even try to fragment, if we need more than 16 fragments */ + if (skb->len > max_packet_size) + goto out_err; + + bat_priv = orig_node->bat_priv; + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out_err; + + /* Create one header to be copied to all fragments */ + frag_header.header.packet_type = BATADV_UNICAST_FRAG; + frag_header.header.version = BATADV_COMPAT_VERSION; + frag_header.header.ttl = BATADV_TTL; + frag_header.seqno = htons(atomic_inc_return(&bat_priv->frag_seqno)); + frag_header.reserved = 0; + frag_header.no = 0; + frag_header.total_size = htons(skb->len); + memcpy(frag_header.orig, primary_if->net_dev->dev_addr, ETH_ALEN); + memcpy(frag_header.dest, orig_node->orig, ETH_ALEN); + + /* Eat and send fragments from the tail of skb */ + while (skb->len > max_fragment_size) { + skb_fragment = batadv_frag_create(skb, &frag_header, mtu); + if (!skb_fragment) + goto out_err; + + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, + skb_fragment->len + ETH_HLEN); + batadv_send_skb_packet(skb_fragment, neigh_node->if_incoming, + neigh_node->addr); + frag_header.no++; + + /* The initial check in this function should cover this case */ + if (frag_header.no == BATADV_FRAG_MAX_FRAGMENTS - 1) + goto out_err; + } + + /* Make room for the fragment header. */ + if (batadv_skb_head_push(skb, header_size) < 0 || + pskb_expand_head(skb, header_size + ETH_HLEN, 0, GFP_ATOMIC) < 0) + goto out_err; + + memcpy(skb->data, &frag_header, header_size); + + /* Send the last fragment */ + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_TX); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_TX_BYTES, + skb->len + ETH_HLEN); + batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + + return true; +out_err: + return false; +} diff --git a/net/batman-adv/fragmentation.h b/net/batman-adv/fragmentation.h new file mode 100644 index 0000000000000000000000000000000000000000..ca029e2676e704fae6c8f3e30f1cbfdbf38b7938 --- /dev/null +++ b/net/batman-adv/fragmentation.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2013 B.A.T.M.A.N. contributors: + * + * Martin Hundebøll + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _NET_BATMAN_ADV_FRAGMENTATION_H_ +#define _NET_BATMAN_ADV_FRAGMENTATION_H_ + +void batadv_frag_purge_orig(struct batadv_orig_node *orig, + bool (*check_cb)(struct batadv_frag_table_entry *)); +bool batadv_frag_skb_fwd(struct sk_buff *skb, + struct batadv_hard_iface *recv_if, + struct batadv_orig_node *orig_node_src); +bool batadv_frag_skb_buffer(struct sk_buff **skb, + struct batadv_orig_node *orig_node); +bool batadv_frag_send_packet(struct sk_buff *skb, + struct batadv_orig_node *orig_node, + struct batadv_neigh_node *neigh_node); + +/** + * batadv_frag_check_entry - check if a list of fragments has timed out + * @frags_entry: table entry to check + * + * Returns true if the frags entry has timed out, false otherwise. + */ +static inline bool +batadv_frag_check_entry(struct batadv_frag_table_entry *frags_entry) +{ + if (!hlist_empty(&frags_entry->head) && + batadv_has_timed_out(frags_entry->timestamp, BATADV_FRAG_TIMEOUT)) + return true; + else + return false; +} + +#endif /* _NET_BATMAN_ADV_FRAGMENTATION_H_ */ diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 1ce4b8763ef289f3679177c5a1fdc96df6e6126f..2449afaa7638358500c350af3bc39fb5ccc2d72e 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -118,7 +118,6 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) uint32_t max_gw_factor = 0, tmp_gw_factor = 0; uint32_t gw_divisor; uint8_t max_tq = 0; - int down, up; uint8_t tq_avg; struct batadv_orig_node *orig_node; @@ -138,14 +137,13 @@ batadv_gw_get_best_gw_node(struct batadv_priv *bat_priv) if (!atomic_inc_not_zero(&gw_node->refcount)) goto next; - tq_avg = router->tq_avg; + tq_avg = router->bat_iv.tq_avg; switch (atomic_read(&bat_priv->gw_sel_class)) { case 1: /* fast connection */ - batadv_gw_bandwidth_to_kbit(orig_node->gw_flags, - &down, &up); - - tmp_gw_factor = tq_avg * tq_avg * down * 100 * 100; + tmp_gw_factor = tq_avg * tq_avg; + tmp_gw_factor *= gw_node->bandwidth_down; + tmp_gw_factor *= 100 * 100; tmp_gw_factor /= gw_divisor; if ((tmp_gw_factor > max_gw_factor) || @@ -223,11 +221,6 @@ void batadv_gw_election(struct batadv_priv *bat_priv) struct batadv_neigh_node *router = NULL; char gw_addr[18] = { '\0' }; - /* The batman daemon checks here if we already passed a full originator - * cycle in order to make sure we don't choose the first gateway we - * hear about. This check is based on the daemon's uptime which we - * don't have. - */ if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT) goto out; @@ -258,16 +251,22 @@ void batadv_gw_election(struct batadv_priv *bat_priv) NULL); } else if ((!curr_gw) && (next_gw)) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Adding route to gateway %pM (gw_flags: %i, tq: %i)\n", + "Adding route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n", next_gw->orig_node->orig, - next_gw->orig_node->gw_flags, router->tq_avg); + next_gw->bandwidth_down / 10, + next_gw->bandwidth_down % 10, + next_gw->bandwidth_up / 10, + next_gw->bandwidth_up % 10, router->bat_iv.tq_avg); batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_ADD, gw_addr); } else { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Changing route to gateway %pM (gw_flags: %i, tq: %i)\n", + "Changing route to gateway %pM (bandwidth: %u.%u/%u.%u MBit, tq: %i)\n", next_gw->orig_node->orig, - next_gw->orig_node->gw_flags, router->tq_avg); + next_gw->bandwidth_down / 10, + next_gw->bandwidth_down % 10, + next_gw->bandwidth_up / 10, + next_gw->bandwidth_up % 10, router->bat_iv.tq_avg); batadv_throw_uevent(bat_priv, BATADV_UEV_GW, BATADV_UEV_CHANGE, gw_addr); } @@ -306,8 +305,8 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, if (!router_orig) goto out; - gw_tq_avg = router_gw->tq_avg; - orig_tq_avg = router_orig->tq_avg; + gw_tq_avg = router_gw->bat_iv.tq_avg; + orig_tq_avg = router_orig->bat_iv.tq_avg; /* the TQ value has to be better */ if (orig_tq_avg < gw_tq_avg) @@ -337,12 +336,20 @@ out: return; } +/** + * batadv_gw_node_add - add gateway node to list of available gateways + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: originator announcing gateway capabilities + * @gateway: announced bandwidth information + */ static void batadv_gw_node_add(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - uint8_t new_gwflags) + struct batadv_tvlv_gateway_data *gateway) { struct batadv_gw_node *gw_node; - int down, up; + + if (gateway->bandwidth_down == 0) + return; gw_node = kzalloc(sizeof(*gw_node), GFP_ATOMIC); if (!gw_node) @@ -356,73 +363,116 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv, hlist_add_head_rcu(&gw_node->list, &bat_priv->gw.list); spin_unlock_bh(&bat_priv->gw.list_lock); - batadv_gw_bandwidth_to_kbit(new_gwflags, &down, &up); batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Found new gateway %pM -> gw_class: %i - %i%s/%i%s\n", - orig_node->orig, new_gwflags, - (down > 2048 ? down / 1024 : down), - (down > 2048 ? "MBit" : "KBit"), - (up > 2048 ? up / 1024 : up), - (up > 2048 ? "MBit" : "KBit")); + "Found new gateway %pM -> gw bandwidth: %u.%u/%u.%u MBit\n", + orig_node->orig, + ntohl(gateway->bandwidth_down) / 10, + ntohl(gateway->bandwidth_down) % 10, + ntohl(gateway->bandwidth_up) / 10, + ntohl(gateway->bandwidth_up) % 10); } -void batadv_gw_node_update(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node, - uint8_t new_gwflags) +/** + * batadv_gw_node_get - retrieve gateway node from list of available gateways + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: originator announcing gateway capabilities + * + * Returns gateway node if found or NULL otherwise. + */ +static struct batadv_gw_node * +batadv_gw_node_get(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node) { - struct batadv_gw_node *gw_node, *curr_gw; - - /* Note: We don't need a NULL check here, since curr_gw never gets - * dereferenced. If curr_gw is NULL we also should not exit as we may - * have this gateway in our list (duplication check!) even though we - * have no currently selected gateway. - */ - curr_gw = batadv_gw_get_selected_gw_node(bat_priv); + struct batadv_gw_node *gw_node_tmp, *gw_node = NULL; rcu_read_lock(); - hlist_for_each_entry_rcu(gw_node, &bat_priv->gw.list, list) { - if (gw_node->orig_node != orig_node) + hlist_for_each_entry_rcu(gw_node_tmp, &bat_priv->gw.list, list) { + if (gw_node_tmp->orig_node != orig_node) continue; - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Gateway class of originator %pM changed from %i to %i\n", - orig_node->orig, gw_node->orig_node->gw_flags, - new_gwflags); + if (gw_node_tmp->deleted) + continue; - gw_node->deleted = 0; + if (!atomic_inc_not_zero(&gw_node_tmp->refcount)) + continue; - if (new_gwflags == BATADV_NO_FLAGS) { - gw_node->deleted = jiffies; - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Gateway %pM removed from gateway list\n", - orig_node->orig); + gw_node = gw_node_tmp; + break; + } + rcu_read_unlock(); - if (gw_node == curr_gw) - goto deselect; - } + return gw_node; +} - goto unlock; +/** + * batadv_gw_node_update - update list of available gateways with changed + * bandwidth information + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: originator announcing gateway capabilities + * @gateway: announced bandwidth information + */ +void batadv_gw_node_update(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + struct batadv_tvlv_gateway_data *gateway) +{ + struct batadv_gw_node *gw_node, *curr_gw = NULL; + + gw_node = batadv_gw_node_get(bat_priv, orig_node); + if (!gw_node) { + batadv_gw_node_add(bat_priv, orig_node, gateway); + goto out; } - if (new_gwflags == BATADV_NO_FLAGS) - goto unlock; + if ((gw_node->bandwidth_down == ntohl(gateway->bandwidth_down)) && + (gw_node->bandwidth_up == ntohl(gateway->bandwidth_up))) + goto out; - batadv_gw_node_add(bat_priv, orig_node, new_gwflags); - goto unlock; + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Gateway bandwidth of originator %pM changed from %u.%u/%u.%u MBit to %u.%u/%u.%u MBit\n", + orig_node->orig, + gw_node->bandwidth_down / 10, + gw_node->bandwidth_down % 10, + gw_node->bandwidth_up / 10, + gw_node->bandwidth_up % 10, + ntohl(gateway->bandwidth_down) / 10, + ntohl(gateway->bandwidth_down) % 10, + ntohl(gateway->bandwidth_up) / 10, + ntohl(gateway->bandwidth_up) % 10); + + gw_node->bandwidth_down = ntohl(gateway->bandwidth_down); + gw_node->bandwidth_up = ntohl(gateway->bandwidth_up); + + gw_node->deleted = 0; + if (ntohl(gateway->bandwidth_down) == 0) { + gw_node->deleted = jiffies; + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Gateway %pM removed from gateway list\n", + orig_node->orig); -deselect: - batadv_gw_deselect(bat_priv); -unlock: - rcu_read_unlock(); + /* Note: We don't need a NULL check here, since curr_gw never + * gets dereferenced. + */ + curr_gw = batadv_gw_get_selected_gw_node(bat_priv); + if (gw_node == curr_gw) + batadv_gw_deselect(bat_priv); + } +out: if (curr_gw) batadv_gw_node_free_ref(curr_gw); + if (gw_node) + batadv_gw_node_free_ref(gw_node); } void batadv_gw_node_delete(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node) { - batadv_gw_node_update(bat_priv, orig_node, 0); + struct batadv_tvlv_gateway_data gateway; + + gateway.bandwidth_down = 0; + gateway.bandwidth_up = 0; + + batadv_gw_node_update(bat_priv, orig_node, &gateway); } void batadv_gw_node_purge(struct batadv_priv *bat_priv) @@ -467,9 +517,7 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv, { struct batadv_gw_node *curr_gw; struct batadv_neigh_node *router; - int down, up, ret = -1; - - batadv_gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); + int ret = -1; router = batadv_orig_node_get_router(gw_node->orig_node); if (!router) @@ -477,16 +525,15 @@ static int batadv_write_buffer_text(struct batadv_priv *bat_priv, curr_gw = batadv_gw_get_selected_gw_node(bat_priv); - ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", + ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %u.%u/%u.%u MBit\n", (curr_gw == gw_node ? "=>" : " "), gw_node->orig_node->orig, - router->tq_avg, router->addr, + router->bat_iv.tq_avg, router->addr, router->if_incoming->net_dev->name, - gw_node->orig_node->gw_flags, - (down > 2048 ? down / 1024 : down), - (down > 2048 ? "MBit" : "KBit"), - (up > 2048 ? up / 1024 : up), - (up > 2048 ? "MBit" : "KBit")); + gw_node->bandwidth_down / 10, + gw_node->bandwidth_down % 10, + gw_node->bandwidth_up / 10, + gw_node->bandwidth_up % 10); batadv_neigh_node_free_ref(router); if (curr_gw) @@ -508,7 +555,7 @@ int batadv_gw_client_seq_print_text(struct seq_file *seq, void *offset) goto out; seq_printf(seq, - " %-12s (%s/%i) %17s [%10s]: gw_class ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", + " %-12s (%s/%i) %17s [%10s]: advertised uplink bandwidth ... [B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", "Gateway", "#", BATADV_TQ_MAX_VALUE, "Nexthop", "outgoingIF", BATADV_SOURCE_VERSION, primary_if->net_dev->name, primary_if->net_dev->dev_addr, net_dev->name); @@ -603,24 +650,29 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) struct iphdr *iphdr; struct ipv6hdr *ipv6hdr; struct udphdr *udphdr; + struct vlan_ethhdr *vhdr; + __be16 proto; /* check for ethernet header */ if (!pskb_may_pull(skb, *header_len + ETH_HLEN)) return false; ethhdr = (struct ethhdr *)skb->data; + proto = ethhdr->h_proto; *header_len += ETH_HLEN; /* check for initial vlan header */ - if (ntohs(ethhdr->h_proto) == ETH_P_8021Q) { + if (proto == htons(ETH_P_8021Q)) { if (!pskb_may_pull(skb, *header_len + VLAN_HLEN)) return false; - ethhdr = (struct ethhdr *)(skb->data + VLAN_HLEN); + + vhdr = (struct vlan_ethhdr *)skb->data; + proto = vhdr->h_vlan_encapsulated_proto; *header_len += VLAN_HLEN; } /* check for ip header */ - switch (ntohs(ethhdr->h_proto)) { - case ETH_P_IP: + switch (proto) { + case htons(ETH_P_IP): if (!pskb_may_pull(skb, *header_len + sizeof(*iphdr))) return false; iphdr = (struct iphdr *)(skb->data + *header_len); @@ -631,7 +683,7 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) return false; break; - case ETH_P_IPV6: + case htons(ETH_P_IPV6): if (!pskb_may_pull(skb, *header_len + sizeof(*ipv6hdr))) return false; ipv6hdr = (struct ipv6hdr *)(skb->data + *header_len); @@ -658,28 +710,44 @@ bool batadv_gw_is_dhcp_target(struct sk_buff *skb, unsigned int *header_len) *header_len += sizeof(*udphdr); /* check for bootp port */ - if ((ntohs(ethhdr->h_proto) == ETH_P_IP) && - (ntohs(udphdr->dest) != 67)) + if ((proto == htons(ETH_P_IP)) && + (udphdr->dest != htons(67))) return false; - if ((ntohs(ethhdr->h_proto) == ETH_P_IPV6) && - (ntohs(udphdr->dest) != 547)) + if ((proto == htons(ETH_P_IPV6)) && + (udphdr->dest != htons(547))) return false; return true; } -/* this call might reallocate skb data */ +/** + * batadv_gw_out_of_range - check if the dhcp request destination is the best gw + * @bat_priv: the bat priv with all the soft interface information + * @skb: the outgoing packet + * + * Check if the skb is a DHCP request and if it is sent to the current best GW + * server. Due to topology changes it may be the case that the GW server + * previously selected is not the best one anymore. + * + * Returns true if the packet destination is unicast and it is not the best gw, + * false otherwise. + * + * This call might reallocate skb data. + */ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, struct sk_buff *skb) { struct batadv_neigh_node *neigh_curr = NULL, *neigh_old = NULL; struct batadv_orig_node *orig_dst_node = NULL; - struct batadv_gw_node *curr_gw = NULL; + struct batadv_gw_node *gw_node = NULL, *curr_gw = NULL; struct ethhdr *ethhdr; bool ret, out_of_range = false; unsigned int header_len = 0; uint8_t curr_tq_avg; + unsigned short vid; + + vid = batadv_get_vid(skb, 0); ret = batadv_gw_is_dhcp_target(skb, &header_len); if (!ret) @@ -687,11 +755,12 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, ethhdr = (struct ethhdr *)skb->data; orig_dst_node = batadv_transtable_search(bat_priv, ethhdr->h_source, - ethhdr->h_dest); + ethhdr->h_dest, vid); if (!orig_dst_node) goto out; - if (!orig_dst_node->gw_flags) + gw_node = batadv_gw_node_get(bat_priv, orig_dst_node); + if (!gw_node->bandwidth_down == 0) goto out; ret = batadv_is_type_dhcprequest(skb, header_len); @@ -723,7 +792,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, if (!neigh_curr) goto out; - curr_tq_avg = neigh_curr->tq_avg; + curr_tq_avg = neigh_curr->bat_iv.tq_avg; break; case BATADV_GW_MODE_OFF: default: @@ -734,7 +803,7 @@ bool batadv_gw_out_of_range(struct batadv_priv *bat_priv, if (!neigh_old) goto out; - if (curr_tq_avg - neigh_old->tq_avg > BATADV_GW_THRESHOLD) + if (curr_tq_avg - neigh_old->bat_iv.tq_avg > BATADV_GW_THRESHOLD) out_of_range = true; out: @@ -742,6 +811,8 @@ out: batadv_orig_node_free_ref(orig_dst_node); if (curr_gw) batadv_gw_node_free_ref(curr_gw); + if (gw_node) + batadv_gw_node_free_ref(gw_node); if (neigh_old) batadv_neigh_node_free_ref(neigh_old); if (neigh_curr) diff --git a/net/batman-adv/gateway_client.h b/net/batman-adv/gateway_client.h index ceef4ebe8bcd6a89711000c0d52b20e536c5375a..d95c2d23195ee962496667d449a25d7b58b2741c 100644 --- a/net/batman-adv/gateway_client.h +++ b/net/batman-adv/gateway_client.h @@ -29,7 +29,7 @@ void batadv_gw_check_election(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node); void batadv_gw_node_update(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - uint8_t new_gwflags); + struct batadv_tvlv_gateway_data *gateway); void batadv_gw_node_delete(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node); void batadv_gw_node_purge(struct batadv_priv *bat_priv); diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c index 84bb2b18d7110a4597dc71c7b2f68c032d7750a9..b211b0f9cb788efa2b476616dd534675fb746b66 100644 --- a/net/batman-adv/gateway_common.c +++ b/net/batman-adv/gateway_common.c @@ -21,64 +21,23 @@ #include "gateway_common.h" #include "gateway_client.h" -/* calculates the gateway class from kbit */ -static void batadv_kbit_to_gw_bandwidth(int down, int up, long *gw_srv_class) -{ - int mdown = 0, tdown, tup, difference; - uint8_t sbit, part; - - *gw_srv_class = 0; - difference = 0x0FFFFFFF; - - /* test all downspeeds */ - for (sbit = 0; sbit < 2; sbit++) { - for (part = 0; part < 16; part++) { - tdown = 32 * (sbit + 2) * (1 << part); - - if (abs(tdown - down) < difference) { - *gw_srv_class = (sbit << 7) + (part << 3); - difference = abs(tdown - down); - mdown = tdown; - } - } - } - - /* test all upspeeds */ - difference = 0x0FFFFFFF; - - for (part = 0; part < 8; part++) { - tup = ((part + 1) * (mdown)) / 8; - - if (abs(tup - up) < difference) { - *gw_srv_class = (*gw_srv_class & 0xF8) | part; - difference = abs(tup - up); - } - } -} - -/* returns the up and downspeeds in kbit, calculated from the class */ -void batadv_gw_bandwidth_to_kbit(uint8_t gw_srv_class, int *down, int *up) -{ - int sbit = (gw_srv_class & 0x80) >> 7; - int dpart = (gw_srv_class & 0x78) >> 3; - int upart = (gw_srv_class & 0x07); - - if (!gw_srv_class) { - *down = 0; - *up = 0; - return; - } - - *down = 32 * (sbit + 2) * (1 << dpart); - *up = ((upart + 1) * (*down)) / 8; -} - +/** + * batadv_parse_gw_bandwidth - parse supplied string buffer to extract download + * and upload bandwidth information + * @net_dev: the soft interface net device + * @buff: string buffer to parse + * @down: pointer holding the returned download bandwidth information + * @up: pointer holding the returned upload bandwidth information + * + * Returns false on parse error and true otherwise. + */ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, - int *up, int *down) + uint32_t *down, uint32_t *up) { - int ret, multi = 1; + enum batadv_bandwidth_units bw_unit_type = BATADV_BW_UNIT_KBIT; char *slash_ptr, *tmp_ptr; long ldown, lup; + int ret; slash_ptr = strchr(buff, '/'); if (slash_ptr) @@ -88,10 +47,10 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, tmp_ptr = buff + strlen(buff) - 4; if (strnicmp(tmp_ptr, "mbit", 4) == 0) - multi = 1024; + bw_unit_type = BATADV_BW_UNIT_MBIT; if ((strnicmp(tmp_ptr, "kbit", 4) == 0) || - (multi > 1)) + (bw_unit_type == BATADV_BW_UNIT_MBIT)) *tmp_ptr = '\0'; } @@ -103,20 +62,28 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, return false; } - *down = ldown * multi; + switch (bw_unit_type) { + case BATADV_BW_UNIT_MBIT: + *down = ldown * 10; + break; + case BATADV_BW_UNIT_KBIT: + default: + *down = ldown / 100; + break; + } /* we also got some upload info */ if (slash_ptr) { - multi = 1; + bw_unit_type = BATADV_BW_UNIT_KBIT; if (strlen(slash_ptr + 1) > 4) { tmp_ptr = slash_ptr + 1 - 4 + strlen(slash_ptr + 1); if (strnicmp(tmp_ptr, "mbit", 4) == 0) - multi = 1024; + bw_unit_type = BATADV_BW_UNIT_MBIT; if ((strnicmp(tmp_ptr, "kbit", 4) == 0) || - (multi > 1)) + (bw_unit_type == BATADV_BW_UNIT_MBIT)) *tmp_ptr = '\0'; } @@ -128,52 +95,149 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff, return false; } - *up = lup * multi; + switch (bw_unit_type) { + case BATADV_BW_UNIT_MBIT: + *up = lup * 10; + break; + case BATADV_BW_UNIT_KBIT: + default: + *up = lup / 100; + break; + } } return true; } +/** + * batadv_gw_tvlv_container_update - update the gw tvlv container after gateway + * setting change + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv) +{ + struct batadv_tvlv_gateway_data gw; + uint32_t down, up; + char gw_mode; + + gw_mode = atomic_read(&bat_priv->gw_mode); + + switch (gw_mode) { + case BATADV_GW_MODE_OFF: + case BATADV_GW_MODE_CLIENT: + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1); + break; + case BATADV_GW_MODE_SERVER: + down = atomic_read(&bat_priv->gw.bandwidth_down); + up = atomic_read(&bat_priv->gw.bandwidth_up); + gw.bandwidth_down = htonl(down); + gw.bandwidth_up = htonl(up); + batadv_tvlv_container_register(bat_priv, BATADV_TVLV_GW, 1, + &gw, sizeof(gw)); + break; + } +} + ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff, size_t count) { struct batadv_priv *bat_priv = netdev_priv(net_dev); - long gw_bandwidth_tmp = 0; - int up = 0, down = 0; + uint32_t down_curr, up_curr, down_new = 0, up_new = 0; bool ret; - ret = batadv_parse_gw_bandwidth(net_dev, buff, &up, &down); + down_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_down); + up_curr = (unsigned int)atomic_read(&bat_priv->gw.bandwidth_up); + + ret = batadv_parse_gw_bandwidth(net_dev, buff, &down_new, &up_new); if (!ret) goto end; - if ((!down) || (down < 256)) - down = 2000; - - if (!up) - up = down / 5; + if (!down_new) + down_new = 1; - batadv_kbit_to_gw_bandwidth(down, up, &gw_bandwidth_tmp); + if (!up_new) + up_new = down_new / 5; - /* the gw bandwidth we guessed above might not match the given - * speeds, hence we need to calculate it back to show the number - * that is going to be propagated - */ - batadv_gw_bandwidth_to_kbit((uint8_t)gw_bandwidth_tmp, &down, &up); + if (!up_new) + up_new = 1; - if (atomic_read(&bat_priv->gw_bandwidth) == gw_bandwidth_tmp) + if ((down_curr == down_new) && (up_curr == up_new)) return count; batadv_gw_deselect(bat_priv); batadv_info(net_dev, - "Changing gateway bandwidth from: '%i' to: '%ld' (propagating: %d%s/%d%s)\n", - atomic_read(&bat_priv->gw_bandwidth), gw_bandwidth_tmp, - (down > 2048 ? down / 1024 : down), - (down > 2048 ? "MBit" : "KBit"), - (up > 2048 ? up / 1024 : up), - (up > 2048 ? "MBit" : "KBit")); + "Changing gateway bandwidth from: '%u.%u/%u.%u MBit' to: '%u.%u/%u.%u MBit'\n", + down_curr / 10, down_curr % 10, up_curr / 10, up_curr % 10, + down_new / 10, down_new % 10, up_new / 10, up_new % 10); - atomic_set(&bat_priv->gw_bandwidth, gw_bandwidth_tmp); + atomic_set(&bat_priv->gw.bandwidth_down, down_new); + atomic_set(&bat_priv->gw.bandwidth_up, up_new); + batadv_gw_tvlv_container_update(bat_priv); end: return count; } + +/** + * batadv_gw_tvlv_ogm_handler_v1 - process incoming gateway tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node of the ogm + * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) + * @tvlv_value: tvlv buffer containing the gateway data + * @tvlv_value_len: tvlv buffer length + */ +static void batadv_gw_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t flags, + void *tvlv_value, + uint16_t tvlv_value_len) +{ + struct batadv_tvlv_gateway_data gateway, *gateway_ptr; + + /* only fetch the tvlv value if the handler wasn't called via the + * CIFNOTFND flag and if there is data to fetch + */ + if ((flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) || + (tvlv_value_len < sizeof(gateway))) { + gateway.bandwidth_down = 0; + gateway.bandwidth_up = 0; + } else { + gateway_ptr = tvlv_value; + gateway.bandwidth_down = gateway_ptr->bandwidth_down; + gateway.bandwidth_up = gateway_ptr->bandwidth_up; + if ((gateway.bandwidth_down == 0) || + (gateway.bandwidth_up == 0)) { + gateway.bandwidth_down = 0; + gateway.bandwidth_up = 0; + } + } + + batadv_gw_node_update(bat_priv, orig, &gateway); + + /* restart gateway selection if fast or late switching was enabled */ + if ((gateway.bandwidth_down != 0) && + (atomic_read(&bat_priv->gw_mode) == BATADV_GW_MODE_CLIENT) && + (atomic_read(&bat_priv->gw_sel_class) > 2)) + batadv_gw_check_election(bat_priv, orig); +} + +/** + * batadv_gw_init - initialise the gateway handling internals + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_gw_init(struct batadv_priv *bat_priv) +{ + batadv_tvlv_handler_register(bat_priv, batadv_gw_tvlv_ogm_handler_v1, + NULL, BATADV_TVLV_GW, 1, + BATADV_TVLV_HANDLER_OGM_CIFNOTFND); +} + +/** + * batadv_gw_free - free the gateway handling internals + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_gw_free(struct batadv_priv *bat_priv) +{ + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_GW, 1); + batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_GW, 1); +} diff --git a/net/batman-adv/gateway_common.h b/net/batman-adv/gateway_common.h index 509b2bf8c2f4fa6388e095c998dd0006ec3d9b6e..56384a4cd18c98a2ac9010ea14bb89acf3a2d06d 100644 --- a/net/batman-adv/gateway_common.h +++ b/net/batman-adv/gateway_common.h @@ -26,12 +26,24 @@ enum batadv_gw_modes { BATADV_GW_MODE_SERVER, }; +/** + * enum batadv_bandwidth_units - bandwidth unit types + * @BATADV_BW_UNIT_KBIT: unit type kbit + * @BATADV_BW_UNIT_MBIT: unit type mbit + */ +enum batadv_bandwidth_units { + BATADV_BW_UNIT_KBIT, + BATADV_BW_UNIT_MBIT, +}; + #define BATADV_GW_MODE_OFF_NAME "off" #define BATADV_GW_MODE_CLIENT_NAME "client" #define BATADV_GW_MODE_SERVER_NAME "server" -void batadv_gw_bandwidth_to_kbit(uint8_t gw_class, int *down, int *up); ssize_t batadv_gw_bandwidth_set(struct net_device *net_dev, char *buff, size_t count); +void batadv_gw_tvlv_container_update(struct batadv_priv *bat_priv); +void batadv_gw_init(struct batadv_priv *bat_priv); +void batadv_gw_free(struct batadv_priv *bat_priv); #endif /* _NET_BATMAN_ADV_GATEWAY_COMMON_H_ */ diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index c478e6bcf89b8bd570a54a9a811b4309c7a2e5f0..57c2a19dcb5c8e19c410b27528f22eb671ed528a 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -28,6 +28,7 @@ #include "originator.h" #include "hash.h" #include "bridge_loop_avoidance.h" +#include "gateway_client.h" #include #include @@ -124,8 +125,11 @@ static int batadv_is_valid_iface(const struct net_device *net_dev) * * Returns true if the net device is a 802.11 wireless device, false otherwise. */ -static bool batadv_is_wifi_netdev(struct net_device *net_device) +bool batadv_is_wifi_netdev(struct net_device *net_device) { + if (!net_device) + return false; + #ifdef CONFIG_WIRELESS_EXT /* pre-cfg80211 drivers have to implement WEXT, so it is possible to * check for wireless_handlers != NULL @@ -141,34 +145,6 @@ static bool batadv_is_wifi_netdev(struct net_device *net_device) return false; } -/** - * batadv_is_wifi_iface - check if the given interface represented by ifindex - * is a wifi interface - * @ifindex: interface index to check - * - * Returns true if the interface represented by ifindex is a 802.11 wireless - * device, false otherwise. - */ -bool batadv_is_wifi_iface(int ifindex) -{ - struct net_device *net_device = NULL; - bool ret = false; - - if (ifindex == BATADV_NULL_IFINDEX) - goto out; - - net_device = dev_get_by_index(&init_net, ifindex); - if (!net_device) - goto out; - - ret = batadv_is_wifi_netdev(net_device); - -out: - if (net_device) - dev_put(net_device); - return ret; -} - static struct batadv_hard_iface * batadv_hardif_get_active(const struct net_device *soft_iface) { @@ -194,22 +170,13 @@ out: static void batadv_primary_if_update_addr(struct batadv_priv *bat_priv, struct batadv_hard_iface *oldif) { - struct batadv_vis_packet *vis_packet; struct batadv_hard_iface *primary_if; - struct sk_buff *skb; primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) goto out; batadv_dat_init_own_addr(bat_priv, primary_if); - - skb = bat_priv->vis.my_info->skb_packet; - vis_packet = (struct batadv_vis_packet *)skb->data; - memcpy(vis_packet->vis_orig, primary_if->net_dev->dev_addr, ETH_ALEN); - memcpy(vis_packet->sender_orig, - primary_if->net_dev->dev_addr, ETH_ALEN); - batadv_bla_update_orig_address(bat_priv, primary_if, oldif); out: if (primary_if) @@ -275,16 +242,10 @@ static void batadv_check_known_mac_addr(const struct net_device *net_dev) int batadv_hardif_min_mtu(struct net_device *soft_iface) { - const struct batadv_priv *bat_priv = netdev_priv(soft_iface); + struct batadv_priv *bat_priv = netdev_priv(soft_iface); const struct batadv_hard_iface *hard_iface; - /* allow big frames if all devices are capable to do so - * (have MTU > 1500 + BAT_HEADER_LEN) - */ int min_mtu = ETH_DATA_LEN; - if (atomic_read(&bat_priv->fragmentation)) - goto out; - rcu_read_lock(); list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) { if ((hard_iface->if_status != BATADV_IF_ACTIVE) && @@ -294,23 +255,40 @@ int batadv_hardif_min_mtu(struct net_device *soft_iface) if (hard_iface->soft_iface != soft_iface) continue; - min_mtu = min_t(int, - hard_iface->net_dev->mtu - BATADV_HEADER_LEN, - min_mtu); + min_mtu = min_t(int, hard_iface->net_dev->mtu, min_mtu); } rcu_read_unlock(); + + atomic_set(&bat_priv->packet_size_max, min_mtu); + + if (atomic_read(&bat_priv->fragmentation) == 0) + goto out; + + /* with fragmentation enabled the maximum size of internally generated + * packets such as translation table exchanges or tvlv containers, etc + * has to be calculated + */ + min_mtu = min_t(int, min_mtu, BATADV_FRAG_MAX_FRAG_SIZE); + min_mtu -= sizeof(struct batadv_frag_packet); + min_mtu *= BATADV_FRAG_MAX_FRAGMENTS; + atomic_set(&bat_priv->packet_size_max, min_mtu); + + /* with fragmentation enabled we can fragment external packets easily */ + min_mtu = min_t(int, min_mtu, ETH_DATA_LEN); + out: - return min_mtu; + return min_mtu - batadv_max_header_len(); } /* adjusts the MTU if a new interface with a smaller MTU appeared. */ void batadv_update_min_mtu(struct net_device *soft_iface) { - int min_mtu; + soft_iface->mtu = batadv_hardif_min_mtu(soft_iface); - min_mtu = batadv_hardif_min_mtu(soft_iface); - if (soft_iface->mtu != min_mtu) - soft_iface->mtu = min_mtu; + /* Check if the local translate table should be cleaned up to match a + * new (and smaller) MTU. + */ + batadv_tt_local_resize_to_mtu(soft_iface); } static void @@ -388,7 +366,8 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, { struct batadv_priv *bat_priv; struct net_device *soft_iface, *master; - __be16 ethertype = __constant_htons(ETH_P_BATMAN); + __be16 ethertype = htons(ETH_P_BATMAN); + int max_header_len = batadv_max_header_len(); int ret; if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) @@ -453,23 +432,22 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, hard_iface->batman_adv_ptype.dev = hard_iface->net_dev; dev_add_pack(&hard_iface->batman_adv_ptype); - atomic_set(&hard_iface->frag_seqno, 1); batadv_info(hard_iface->soft_iface, "Adding interface: %s\n", hard_iface->net_dev->name); if (atomic_read(&bat_priv->fragmentation) && - hard_iface->net_dev->mtu < ETH_DATA_LEN + BATADV_HEADER_LEN) + hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len) batadv_info(hard_iface->soft_iface, - "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %zi would solve the problem.\n", + "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to %i would solve the problem.\n", hard_iface->net_dev->name, hard_iface->net_dev->mtu, - ETH_DATA_LEN + BATADV_HEADER_LEN); + ETH_DATA_LEN + max_header_len); if (!atomic_read(&bat_priv->fragmentation) && - hard_iface->net_dev->mtu < ETH_DATA_LEN + BATADV_HEADER_LEN) + hard_iface->net_dev->mtu < ETH_DATA_LEN + max_header_len) batadv_info(hard_iface->soft_iface, - "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %zi.\n", + "The MTU of interface %s is too small (%i) to handle the transport of batman-adv packets. If you experience problems getting traffic through try increasing the MTU to %i.\n", hard_iface->net_dev->name, hard_iface->net_dev->mtu, - ETH_DATA_LEN + BATADV_HEADER_LEN); + ETH_DATA_LEN + max_header_len); if (batadv_hardif_is_iface_up(hard_iface)) batadv_hardif_activate_interface(hard_iface); @@ -533,8 +511,12 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface, dev_put(hard_iface->soft_iface); /* nobody uses this interface anymore */ - if (!bat_priv->num_ifaces && autodel == BATADV_IF_CLEANUP_AUTO) - batadv_softif_destroy_sysfs(hard_iface->soft_iface); + if (!bat_priv->num_ifaces) { + batadv_gw_check_client_stop(bat_priv); + + if (autodel == BATADV_IF_CLEANUP_AUTO) + batadv_softif_destroy_sysfs(hard_iface->soft_iface); + } netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->soft_iface); hard_iface->soft_iface = NULL; @@ -652,6 +634,8 @@ static int batadv_hard_if_event(struct notifier_block *this, if (batadv_softif_is_valid(net_dev) && event == NETDEV_REGISTER) { batadv_sysfs_add_meshif(net_dev); + bat_priv = netdev_priv(net_dev); + batadv_softif_create_vlan(bat_priv, BATADV_NO_FLAGS); return NOTIFY_DONE; } diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index 49892881a7c5bb581fc92837617380ad5b62752f..df4c8bd45c40748a5442999012ab581adf1d1d71 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -41,6 +41,7 @@ enum batadv_hard_if_cleanup { extern struct notifier_block batadv_hard_if_notifier; +bool batadv_is_wifi_netdev(struct net_device *net_device); struct batadv_hard_iface* batadv_hardif_get_by_netdev(const struct net_device *net_dev); int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, @@ -51,7 +52,6 @@ void batadv_hardif_remove_interfaces(void); int batadv_hardif_min_mtu(struct net_device *soft_iface); void batadv_update_min_mtu(struct net_device *soft_iface); void batadv_hardif_free_rcu(struct rcu_head *rcu); -bool batadv_is_wifi_iface(int ifindex); static inline void batadv_hardif_free_ref(struct batadv_hard_iface *hard_iface) diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index 5a99bb4b6b823c8d2b042aec32cb2ff70ee3a8b9..29ae4efe3543e4bfc6ff013b9c97628b9e395209 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -29,7 +29,7 @@ static struct batadv_socket_client *batadv_socket_client_hash[256]; static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, - struct batadv_icmp_packet_rr *icmp_packet, + struct batadv_icmp_header *icmph, size_t icmp_len); void batadv_socket_init(void) @@ -155,13 +155,13 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, struct batadv_priv *bat_priv = socket_client->bat_priv; struct batadv_hard_iface *primary_if = NULL; struct sk_buff *skb; - struct batadv_icmp_packet_rr *icmp_packet; - + struct batadv_icmp_packet_rr *icmp_packet_rr; + struct batadv_icmp_header *icmp_header; struct batadv_orig_node *orig_node = NULL; struct batadv_neigh_node *neigh_node = NULL; size_t packet_len = sizeof(struct batadv_icmp_packet); - if (len < sizeof(struct batadv_icmp_packet)) { + if (len < sizeof(struct batadv_icmp_header)) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Error - can't send packet from char device: invalid packet size\n"); return -EINVAL; @@ -174,8 +174,10 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, goto out; } - if (len >= sizeof(struct batadv_icmp_packet_rr)) - packet_len = sizeof(struct batadv_icmp_packet_rr); + if (len >= BATADV_ICMP_MAX_PACKET_SIZE) + packet_len = BATADV_ICMP_MAX_PACKET_SIZE; + else + packet_len = len; skb = netdev_alloc_skb_ip_align(NULL, packet_len + ETH_HLEN); if (!skb) { @@ -185,67 +187,78 @@ static ssize_t batadv_socket_write(struct file *file, const char __user *buff, skb->priority = TC_PRIO_CONTROL; skb_reserve(skb, ETH_HLEN); - icmp_packet = (struct batadv_icmp_packet_rr *)skb_put(skb, packet_len); + icmp_header = (struct batadv_icmp_header *)skb_put(skb, packet_len); - if (copy_from_user(icmp_packet, buff, packet_len)) { + if (copy_from_user(icmp_header, buff, packet_len)) { len = -EFAULT; goto free_skb; } - if (icmp_packet->header.packet_type != BATADV_ICMP) { + if (icmp_header->header.packet_type != BATADV_ICMP) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Error - can't send packet from char device: got bogus packet type (expected: BAT_ICMP)\n"); len = -EINVAL; goto free_skb; } - if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) { + switch (icmp_header->msg_type) { + case BATADV_ECHO_REQUEST: + if (len < sizeof(struct batadv_icmp_packet)) { + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, + "Error - can't send packet from char device: invalid packet size\n"); + len = -EINVAL; + goto free_skb; + } + + if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) + goto dst_unreach; + + orig_node = batadv_orig_hash_find(bat_priv, icmp_header->dst); + if (!orig_node) + goto dst_unreach; + + neigh_node = batadv_orig_node_get_router(orig_node); + if (!neigh_node) + goto dst_unreach; + + if (!neigh_node->if_incoming) + goto dst_unreach; + + if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE) + goto dst_unreach; + + icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmp_header; + if (packet_len == sizeof(*icmp_packet_rr)) + memcpy(icmp_packet_rr->rr, + neigh_node->if_incoming->net_dev->dev_addr, + ETH_ALEN); + + break; + default: batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Error - can't send packet from char device: got bogus message type (expected: ECHO_REQUEST)\n"); + "Error - can't send packet from char device: got unknown message type\n"); len = -EINVAL; goto free_skb; } - icmp_packet->uid = socket_client->index; + icmp_header->uid = socket_client->index; - if (icmp_packet->header.version != BATADV_COMPAT_VERSION) { - icmp_packet->msg_type = BATADV_PARAMETER_PROBLEM; - icmp_packet->header.version = BATADV_COMPAT_VERSION; - batadv_socket_add_packet(socket_client, icmp_packet, + if (icmp_header->header.version != BATADV_COMPAT_VERSION) { + icmp_header->msg_type = BATADV_PARAMETER_PROBLEM; + icmp_header->header.version = BATADV_COMPAT_VERSION; + batadv_socket_add_packet(socket_client, icmp_header, packet_len); goto free_skb; } - if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) - goto dst_unreach; - - orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->dst); - if (!orig_node) - goto dst_unreach; - - neigh_node = batadv_orig_node_get_router(orig_node); - if (!neigh_node) - goto dst_unreach; - - if (!neigh_node->if_incoming) - goto dst_unreach; - - if (neigh_node->if_incoming->if_status != BATADV_IF_ACTIVE) - goto dst_unreach; - - memcpy(icmp_packet->orig, - primary_if->net_dev->dev_addr, ETH_ALEN); - - if (packet_len == sizeof(struct batadv_icmp_packet_rr)) - memcpy(icmp_packet->rr, - neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN); + memcpy(icmp_header->orig, primary_if->net_dev->dev_addr, ETH_ALEN); batadv_send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); goto out; dst_unreach: - icmp_packet->msg_type = BATADV_DESTINATION_UNREACHABLE; - batadv_socket_add_packet(socket_client, icmp_packet, packet_len); + icmp_header->msg_type = BATADV_DESTINATION_UNREACHABLE; + batadv_socket_add_packet(socket_client, icmp_header, packet_len); free_skb: kfree_skb(skb); out: @@ -298,27 +311,40 @@ err: return -ENOMEM; } +/** + * batadv_socket_receive_packet - schedule an icmp packet to be sent to userspace + * on an icmp socket. + * @socket_client: the socket this packet belongs to + * @icmph: pointer to the header of the icmp packet + * @icmp_len: total length of the icmp packet + */ static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, - struct batadv_icmp_packet_rr *icmp_packet, + struct batadv_icmp_header *icmph, size_t icmp_len) { struct batadv_socket_packet *socket_packet; + size_t len; socket_packet = kmalloc(sizeof(*socket_packet), GFP_ATOMIC); if (!socket_packet) return; + len = icmp_len; + /* check the maximum length before filling the buffer */ + if (len > sizeof(socket_packet->icmp_packet)) + len = sizeof(socket_packet->icmp_packet); + INIT_LIST_HEAD(&socket_packet->list); - memcpy(&socket_packet->icmp_packet, icmp_packet, icmp_len); - socket_packet->icmp_len = icmp_len; + memcpy(&socket_packet->icmp_packet, icmph, len); + socket_packet->icmp_len = len; spin_lock_bh(&socket_client->lock); /* while waiting for the lock the socket_client could have been * deleted */ - if (!batadv_socket_client_hash[icmp_packet->uid]) { + if (!batadv_socket_client_hash[icmph->uid]) { spin_unlock_bh(&socket_client->lock); kfree(socket_packet); return; @@ -342,12 +368,18 @@ static void batadv_socket_add_packet(struct batadv_socket_client *socket_client, wake_up(&socket_client->queue_wait); } -void batadv_socket_receive_packet(struct batadv_icmp_packet_rr *icmp_packet, +/** + * batadv_socket_receive_packet - schedule an icmp packet to be received + * locally and sent to userspace. + * @icmph: pointer to the header of the icmp packet + * @icmp_len: total length of the icmp packet + */ +void batadv_socket_receive_packet(struct batadv_icmp_header *icmph, size_t icmp_len) { struct batadv_socket_client *hash; - hash = batadv_socket_client_hash[icmp_packet->uid]; + hash = batadv_socket_client_hash[icmph->uid]; if (hash) - batadv_socket_add_packet(hash, icmp_packet, icmp_len); + batadv_socket_add_packet(hash, icmph, icmp_len); } diff --git a/net/batman-adv/icmp_socket.h b/net/batman-adv/icmp_socket.h index 1fcca37b62234d92b2e6092dcdc4ccfed4d76ba2..6665080dff7950bc904c54fef35f91dd3135946a 100644 --- a/net/batman-adv/icmp_socket.h +++ b/net/batman-adv/icmp_socket.h @@ -24,7 +24,7 @@ void batadv_socket_init(void); int batadv_socket_setup(struct batadv_priv *bat_priv); -void batadv_socket_receive_packet(struct batadv_icmp_packet_rr *icmp_packet, +void batadv_socket_receive_packet(struct batadv_icmp_header *icmph, size_t icmp_len); #endif /* _NET_BATMAN_ADV_ICMP_SOCKET_H_ */ diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 1356af660b5bf05645394a28c91cdbc54e5ad4c2..c51a5e568f0a80c08beb4ded5b34b1aff8d5cfdf 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -36,10 +36,11 @@ #include "gateway_client.h" #include "bridge_loop_avoidance.h" #include "distributed-arp-table.h" -#include "vis.h" +#include "gateway_common.h" #include "hash.h" #include "bat_algo.h" #include "network-coding.h" +#include "fragmentation.h" /* List manipulations on hardif_list have to be rtnl_lock()'ed, @@ -109,9 +110,11 @@ int batadv_mesh_init(struct net_device *soft_iface) spin_lock_init(&bat_priv->tt.req_list_lock); spin_lock_init(&bat_priv->tt.roam_list_lock); spin_lock_init(&bat_priv->tt.last_changeset_lock); + spin_lock_init(&bat_priv->tt.commit_lock); spin_lock_init(&bat_priv->gw.list_lock); - spin_lock_init(&bat_priv->vis.hash_lock); - spin_lock_init(&bat_priv->vis.list_lock); + spin_lock_init(&bat_priv->tvlv.container_list_lock); + spin_lock_init(&bat_priv->tvlv.handler_list_lock); + spin_lock_init(&bat_priv->softif_vlan_list_lock); INIT_HLIST_HEAD(&bat_priv->forw_bat_list); INIT_HLIST_HEAD(&bat_priv->forw_bcast_list); @@ -119,6 +122,9 @@ int batadv_mesh_init(struct net_device *soft_iface) INIT_LIST_HEAD(&bat_priv->tt.changes_list); INIT_LIST_HEAD(&bat_priv->tt.req_list); INIT_LIST_HEAD(&bat_priv->tt.roam_list); + INIT_HLIST_HEAD(&bat_priv->tvlv.container_list); + INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list); + INIT_HLIST_HEAD(&bat_priv->softif_vlan_list); ret = batadv_originator_init(bat_priv); if (ret < 0) @@ -128,13 +134,6 @@ int batadv_mesh_init(struct net_device *soft_iface) if (ret < 0) goto err; - batadv_tt_local_add(soft_iface, soft_iface->dev_addr, - BATADV_NULL_IFINDEX); - - ret = batadv_vis_init(bat_priv); - if (ret < 0) - goto err; - ret = batadv_bla_init(bat_priv); if (ret < 0) goto err; @@ -147,6 +146,8 @@ int batadv_mesh_init(struct net_device *soft_iface) if (ret < 0) goto err; + batadv_gw_init(bat_priv); + atomic_set(&bat_priv->gw.reselect, 0); atomic_set(&bat_priv->mesh_state, BATADV_MESH_ACTIVE); @@ -165,8 +166,6 @@ void batadv_mesh_free(struct net_device *soft_iface) batadv_purge_outstanding_packets(bat_priv, NULL); - batadv_vis_quit(bat_priv); - batadv_gw_node_purge(bat_priv); batadv_nc_mesh_free(bat_priv); batadv_dat_free(bat_priv); @@ -185,6 +184,8 @@ void batadv_mesh_free(struct net_device *soft_iface) */ batadv_originator_free(bat_priv); + batadv_gw_free(bat_priv); + free_percpu(bat_priv->bat_counters); bat_priv->bat_counters = NULL; @@ -254,6 +255,31 @@ out: return primary_if; } +/** + * batadv_max_header_len - calculate maximum encapsulation overhead for a + * payload packet + * + * Return the maximum encapsulation overhead in bytes. + */ +int batadv_max_header_len(void) +{ + int header_len = 0; + + header_len = max_t(int, header_len, + sizeof(struct batadv_unicast_packet)); + header_len = max_t(int, header_len, + sizeof(struct batadv_unicast_4addr_packet)); + header_len = max_t(int, header_len, + sizeof(struct batadv_bcast_packet)); + +#ifdef CONFIG_BATMAN_ADV_NC + header_len = max_t(int, header_len, + sizeof(struct batadv_coded_packet)); +#endif + + return header_len; +} + /** * batadv_skb_set_priority - sets skb priority according to packet content * @skb: the packet to be sent @@ -392,22 +418,31 @@ static void batadv_recv_handler_init(void) for (i = 0; i < ARRAY_SIZE(batadv_rx_handler); i++) batadv_rx_handler[i] = batadv_recv_unhandled_packet; - /* batman icmp packet */ - batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet; + for (i = BATADV_UNICAST_MIN; i <= BATADV_UNICAST_MAX; i++) + batadv_rx_handler[i] = batadv_recv_unhandled_unicast_packet; + + /* compile time checks for struct member offsets */ + BUILD_BUG_ON(offsetof(struct batadv_unicast_4addr_packet, src) != 10); + BUILD_BUG_ON(offsetof(struct batadv_unicast_packet, dest) != 4); + BUILD_BUG_ON(offsetof(struct batadv_unicast_tvlv_packet, dst) != 4); + BUILD_BUG_ON(offsetof(struct batadv_frag_packet, dest) != 4); + BUILD_BUG_ON(offsetof(struct batadv_icmp_packet, icmph.dst) != 4); + BUILD_BUG_ON(offsetof(struct batadv_icmp_packet_rr, icmph.dst) != 4); + + /* broadcast packet */ + batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet; + + /* unicast packets ... */ /* unicast with 4 addresses packet */ batadv_rx_handler[BATADV_UNICAST_4ADDR] = batadv_recv_unicast_packet; /* unicast packet */ batadv_rx_handler[BATADV_UNICAST] = batadv_recv_unicast_packet; - /* fragmented unicast packet */ - batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_ucast_frag_packet; - /* broadcast packet */ - batadv_rx_handler[BATADV_BCAST] = batadv_recv_bcast_packet; - /* vis packet */ - batadv_rx_handler[BATADV_VIS] = batadv_recv_vis_packet; - /* Translation table query (request or response) */ - batadv_rx_handler[BATADV_TT_QUERY] = batadv_recv_tt_query; - /* Roaming advertisement */ - batadv_rx_handler[BATADV_ROAM_ADV] = batadv_recv_roam_adv; + /* unicast tvlv packet */ + batadv_rx_handler[BATADV_UNICAST_TVLV] = batadv_recv_unicast_tvlv; + /* batman icmp packet */ + batadv_rx_handler[BATADV_ICMP] = batadv_recv_icmp_packet; + /* Fragmented packets */ + batadv_rx_handler[BATADV_UNICAST_FRAG] = batadv_recv_frag_packet; } int @@ -415,7 +450,12 @@ batadv_recv_handler_register(uint8_t packet_type, int (*recv_handler)(struct sk_buff *, struct batadv_hard_iface *)) { - if (batadv_rx_handler[packet_type] != &batadv_recv_unhandled_packet) + int (*curr)(struct sk_buff *, + struct batadv_hard_iface *); + curr = batadv_rx_handler[packet_type]; + + if ((curr != batadv_recv_unhandled_packet) && + (curr != batadv_recv_unhandled_unicast_packet)) return -EBUSY; batadv_rx_handler[packet_type] = recv_handler; @@ -461,7 +501,9 @@ int batadv_algo_register(struct batadv_algo_ops *bat_algo_ops) !bat_algo_ops->bat_iface_update_mac || !bat_algo_ops->bat_primary_iface_set || !bat_algo_ops->bat_ogm_schedule || - !bat_algo_ops->bat_ogm_emit) { + !bat_algo_ops->bat_ogm_emit || + !bat_algo_ops->bat_neigh_cmp || + !bat_algo_ops->bat_neigh_is_equiv_or_better) { pr_info("Routing algo '%s' does not implement required ops\n", bat_algo_ops->name); ret = -EINVAL; @@ -536,6 +578,601 @@ __be32 batadv_skb_crc32(struct sk_buff *skb, u8 *payload_ptr) return htonl(crc); } +/** + * batadv_tvlv_handler_free_ref - decrement the tvlv handler refcounter and + * possibly free it + * @tvlv_handler: the tvlv handler to free + */ +static void +batadv_tvlv_handler_free_ref(struct batadv_tvlv_handler *tvlv_handler) +{ + if (atomic_dec_and_test(&tvlv_handler->refcount)) + kfree_rcu(tvlv_handler, rcu); +} + +/** + * batadv_tvlv_handler_get - retrieve tvlv handler from the tvlv handler list + * based on the provided type and version (both need to match) + * @bat_priv: the bat priv with all the soft interface information + * @type: tvlv handler type to look for + * @version: tvlv handler version to look for + * + * Returns tvlv handler if found or NULL otherwise. + */ +static struct batadv_tvlv_handler +*batadv_tvlv_handler_get(struct batadv_priv *bat_priv, + uint8_t type, uint8_t version) +{ + struct batadv_tvlv_handler *tvlv_handler_tmp, *tvlv_handler = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tvlv_handler_tmp, + &bat_priv->tvlv.handler_list, list) { + if (tvlv_handler_tmp->type != type) + continue; + + if (tvlv_handler_tmp->version != version) + continue; + + if (!atomic_inc_not_zero(&tvlv_handler_tmp->refcount)) + continue; + + tvlv_handler = tvlv_handler_tmp; + break; + } + rcu_read_unlock(); + + return tvlv_handler; +} + +/** + * batadv_tvlv_container_free_ref - decrement the tvlv container refcounter and + * possibly free it + * @tvlv_handler: the tvlv container to free + */ +static void batadv_tvlv_container_free_ref(struct batadv_tvlv_container *tvlv) +{ + if (atomic_dec_and_test(&tvlv->refcount)) + kfree(tvlv); +} + +/** + * batadv_tvlv_container_get - retrieve tvlv container from the tvlv container + * list based on the provided type and version (both need to match) + * @bat_priv: the bat priv with all the soft interface information + * @type: tvlv container type to look for + * @version: tvlv container version to look for + * + * Has to be called with the appropriate locks being acquired + * (tvlv.container_list_lock). + * + * Returns tvlv container if found or NULL otherwise. + */ +static struct batadv_tvlv_container +*batadv_tvlv_container_get(struct batadv_priv *bat_priv, + uint8_t type, uint8_t version) +{ + struct batadv_tvlv_container *tvlv_tmp, *tvlv = NULL; + + hlist_for_each_entry(tvlv_tmp, &bat_priv->tvlv.container_list, list) { + if (tvlv_tmp->tvlv_hdr.type != type) + continue; + + if (tvlv_tmp->tvlv_hdr.version != version) + continue; + + if (!atomic_inc_not_zero(&tvlv_tmp->refcount)) + continue; + + tvlv = tvlv_tmp; + break; + } + + return tvlv; +} + +/** + * batadv_tvlv_container_list_size - calculate the size of the tvlv container + * list entries + * @bat_priv: the bat priv with all the soft interface information + * + * Has to be called with the appropriate locks being acquired + * (tvlv.container_list_lock). + * + * Returns size of all currently registered tvlv containers in bytes. + */ +static uint16_t batadv_tvlv_container_list_size(struct batadv_priv *bat_priv) +{ + struct batadv_tvlv_container *tvlv; + uint16_t tvlv_len = 0; + + hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) { + tvlv_len += sizeof(struct batadv_tvlv_hdr); + tvlv_len += ntohs(tvlv->tvlv_hdr.len); + } + + return tvlv_len; +} + +/** + * batadv_tvlv_container_remove - remove tvlv container from the tvlv container + * list + * @tvlv: the to be removed tvlv container + * + * Has to be called with the appropriate locks being acquired + * (tvlv.container_list_lock). + */ +static void batadv_tvlv_container_remove(struct batadv_tvlv_container *tvlv) +{ + if (!tvlv) + return; + + hlist_del(&tvlv->list); + + /* first call to decrement the counter, second call to free */ + batadv_tvlv_container_free_ref(tvlv); + batadv_tvlv_container_free_ref(tvlv); +} + +/** + * batadv_tvlv_container_unregister - unregister tvlv container based on the + * provided type and version (both need to match) + * @bat_priv: the bat priv with all the soft interface information + * @type: tvlv container type to unregister + * @version: tvlv container type to unregister + */ +void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv, + uint8_t type, uint8_t version) +{ + struct batadv_tvlv_container *tvlv; + + spin_lock_bh(&bat_priv->tvlv.container_list_lock); + tvlv = batadv_tvlv_container_get(bat_priv, type, version); + batadv_tvlv_container_remove(tvlv); + spin_unlock_bh(&bat_priv->tvlv.container_list_lock); +} + +/** + * batadv_tvlv_container_register - register tvlv type, version and content + * to be propagated with each (primary interface) OGM + * @bat_priv: the bat priv with all the soft interface information + * @type: tvlv container type + * @version: tvlv container version + * @tvlv_value: tvlv container content + * @tvlv_value_len: tvlv container content length + * + * If a container of the same type and version was already registered the new + * content is going to replace the old one. + */ +void batadv_tvlv_container_register(struct batadv_priv *bat_priv, + uint8_t type, uint8_t version, + void *tvlv_value, uint16_t tvlv_value_len) +{ + struct batadv_tvlv_container *tvlv_old, *tvlv_new; + + if (!tvlv_value) + tvlv_value_len = 0; + + tvlv_new = kzalloc(sizeof(*tvlv_new) + tvlv_value_len, GFP_ATOMIC); + if (!tvlv_new) + return; + + tvlv_new->tvlv_hdr.version = version; + tvlv_new->tvlv_hdr.type = type; + tvlv_new->tvlv_hdr.len = htons(tvlv_value_len); + + memcpy(tvlv_new + 1, tvlv_value, ntohs(tvlv_new->tvlv_hdr.len)); + INIT_HLIST_NODE(&tvlv_new->list); + atomic_set(&tvlv_new->refcount, 1); + + spin_lock_bh(&bat_priv->tvlv.container_list_lock); + tvlv_old = batadv_tvlv_container_get(bat_priv, type, version); + batadv_tvlv_container_remove(tvlv_old); + hlist_add_head(&tvlv_new->list, &bat_priv->tvlv.container_list); + spin_unlock_bh(&bat_priv->tvlv.container_list_lock); +} + +/** + * batadv_tvlv_realloc_packet_buff - reallocate packet buffer to accomodate + * requested packet size + * @packet_buff: packet buffer + * @packet_buff_len: packet buffer size + * @packet_min_len: requested packet minimum size + * @additional_packet_len: requested additional packet size on top of minimum + * size + * + * Returns true of the packet buffer could be changed to the requested size, + * false otherwise. + */ +static bool batadv_tvlv_realloc_packet_buff(unsigned char **packet_buff, + int *packet_buff_len, + int min_packet_len, + int additional_packet_len) +{ + unsigned char *new_buff; + + new_buff = kmalloc(min_packet_len + additional_packet_len, GFP_ATOMIC); + + /* keep old buffer if kmalloc should fail */ + if (new_buff) { + memcpy(new_buff, *packet_buff, min_packet_len); + kfree(*packet_buff); + *packet_buff = new_buff; + *packet_buff_len = min_packet_len + additional_packet_len; + return true; + } + + return false; +} + +/** + * batadv_tvlv_container_ogm_append - append tvlv container content to given + * OGM packet buffer + * @bat_priv: the bat priv with all the soft interface information + * @packet_buff: ogm packet buffer + * @packet_buff_len: ogm packet buffer size including ogm header and tvlv + * content + * @packet_min_len: ogm header size to be preserved for the OGM itself + * + * The ogm packet might be enlarged or shrunk depending on the current size + * and the size of the to-be-appended tvlv containers. + * + * Returns size of all appended tvlv containers in bytes. + */ +uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, + unsigned char **packet_buff, + int *packet_buff_len, + int packet_min_len) +{ + struct batadv_tvlv_container *tvlv; + struct batadv_tvlv_hdr *tvlv_hdr; + uint16_t tvlv_value_len; + void *tvlv_value; + bool ret; + + spin_lock_bh(&bat_priv->tvlv.container_list_lock); + tvlv_value_len = batadv_tvlv_container_list_size(bat_priv); + + ret = batadv_tvlv_realloc_packet_buff(packet_buff, packet_buff_len, + packet_min_len, tvlv_value_len); + + if (!ret) + goto end; + + if (!tvlv_value_len) + goto end; + + tvlv_value = (*packet_buff) + packet_min_len; + + hlist_for_each_entry(tvlv, &bat_priv->tvlv.container_list, list) { + tvlv_hdr = tvlv_value; + tvlv_hdr->type = tvlv->tvlv_hdr.type; + tvlv_hdr->version = tvlv->tvlv_hdr.version; + tvlv_hdr->len = tvlv->tvlv_hdr.len; + tvlv_value = tvlv_hdr + 1; + memcpy(tvlv_value, tvlv + 1, ntohs(tvlv->tvlv_hdr.len)); + tvlv_value = (uint8_t *)tvlv_value + ntohs(tvlv->tvlv_hdr.len); + } + +end: + spin_unlock_bh(&bat_priv->tvlv.container_list_lock); + return tvlv_value_len; +} + +/** + * batadv_tvlv_call_handler - parse the given tvlv buffer to call the + * appropriate handlers + * @bat_priv: the bat priv with all the soft interface information + * @tvlv_handler: tvlv callback function handling the tvlv content + * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet + * @orig_node: orig node emitting the ogm packet + * @src: source mac address of the unicast packet + * @dst: destination mac address of the unicast packet + * @tvlv_value: tvlv content + * @tvlv_value_len: tvlv content length + * + * Returns success if handler was not found or the return value of the handler + * callback. + */ +static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv, + struct batadv_tvlv_handler *tvlv_handler, + bool ogm_source, + struct batadv_orig_node *orig_node, + uint8_t *src, uint8_t *dst, + void *tvlv_value, uint16_t tvlv_value_len) +{ + if (!tvlv_handler) + return NET_RX_SUCCESS; + + if (ogm_source) { + if (!tvlv_handler->ogm_handler) + return NET_RX_SUCCESS; + + if (!orig_node) + return NET_RX_SUCCESS; + + tvlv_handler->ogm_handler(bat_priv, orig_node, + BATADV_NO_FLAGS, + tvlv_value, tvlv_value_len); + tvlv_handler->flags |= BATADV_TVLV_HANDLER_OGM_CALLED; + } else { + if (!src) + return NET_RX_SUCCESS; + + if (!dst) + return NET_RX_SUCCESS; + + if (!tvlv_handler->unicast_handler) + return NET_RX_SUCCESS; + + return tvlv_handler->unicast_handler(bat_priv, src, + dst, tvlv_value, + tvlv_value_len); + } + + return NET_RX_SUCCESS; +} + +/** + * batadv_tvlv_containers_process - parse the given tvlv buffer to call the + * appropriate handlers + * @bat_priv: the bat priv with all the soft interface information + * @ogm_source: flag indicating wether the tvlv is an ogm or a unicast packet + * @orig_node: orig node emitting the ogm packet + * @src: source mac address of the unicast packet + * @dst: destination mac address of the unicast packet + * @tvlv_value: tvlv content + * @tvlv_value_len: tvlv content length + * + * Returns success when processing an OGM or the return value of all called + * handler callbacks. + */ +int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + bool ogm_source, + struct batadv_orig_node *orig_node, + uint8_t *src, uint8_t *dst, + void *tvlv_value, uint16_t tvlv_value_len) +{ + struct batadv_tvlv_handler *tvlv_handler; + struct batadv_tvlv_hdr *tvlv_hdr; + uint16_t tvlv_value_cont_len; + uint8_t cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND; + int ret = NET_RX_SUCCESS; + + while (tvlv_value_len >= sizeof(*tvlv_hdr)) { + tvlv_hdr = tvlv_value; + tvlv_value_cont_len = ntohs(tvlv_hdr->len); + tvlv_value = tvlv_hdr + 1; + tvlv_value_len -= sizeof(*tvlv_hdr); + + if (tvlv_value_cont_len > tvlv_value_len) + break; + + tvlv_handler = batadv_tvlv_handler_get(bat_priv, + tvlv_hdr->type, + tvlv_hdr->version); + + ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler, + ogm_source, orig_node, + src, dst, tvlv_value, + tvlv_value_cont_len); + if (tvlv_handler) + batadv_tvlv_handler_free_ref(tvlv_handler); + tvlv_value = (uint8_t *)tvlv_value + tvlv_value_cont_len; + tvlv_value_len -= tvlv_value_cont_len; + } + + if (!ogm_source) + return ret; + + rcu_read_lock(); + hlist_for_each_entry_rcu(tvlv_handler, + &bat_priv->tvlv.handler_list, list) { + if ((tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) && + !(tvlv_handler->flags & BATADV_TVLV_HANDLER_OGM_CALLED)) + tvlv_handler->ogm_handler(bat_priv, orig_node, + cifnotfound, NULL, 0); + + tvlv_handler->flags &= ~BATADV_TVLV_HANDLER_OGM_CALLED; + } + rcu_read_unlock(); + + return NET_RX_SUCCESS; +} + +/** + * batadv_tvlv_ogm_receive - process an incoming ogm and call the appropriate + * handlers + * @bat_priv: the bat priv with all the soft interface information + * @batadv_ogm_packet: ogm packet containing the tvlv containers + * @orig_node: orig node emitting the ogm packet + */ +void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, + struct batadv_ogm_packet *batadv_ogm_packet, + struct batadv_orig_node *orig_node) +{ + void *tvlv_value; + uint16_t tvlv_value_len; + + if (!batadv_ogm_packet) + return; + + tvlv_value_len = ntohs(batadv_ogm_packet->tvlv_len); + if (!tvlv_value_len) + return; + + tvlv_value = batadv_ogm_packet + 1; + + batadv_tvlv_containers_process(bat_priv, true, orig_node, NULL, NULL, + tvlv_value, tvlv_value_len); +} + +/** + * batadv_tvlv_handler_register - register tvlv handler based on the provided + * type and version (both need to match) for ogm tvlv payload and/or unicast + * payload + * @bat_priv: the bat priv with all the soft interface information + * @optr: ogm tvlv handler callback function. This function receives the orig + * node, flags and the tvlv content as argument to process. + * @uptr: unicast tvlv handler callback function. This function receives the + * source & destination of the unicast packet as well as the tvlv content + * to process. + * @type: tvlv handler type to be registered + * @version: tvlv handler version to be registered + * @flags: flags to enable or disable TVLV API behavior + */ +void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, + void (*optr)(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t flags, + void *tvlv_value, + uint16_t tvlv_value_len), + int (*uptr)(struct batadv_priv *bat_priv, + uint8_t *src, uint8_t *dst, + void *tvlv_value, + uint16_t tvlv_value_len), + uint8_t type, uint8_t version, uint8_t flags) +{ + struct batadv_tvlv_handler *tvlv_handler; + + tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version); + if (tvlv_handler) { + batadv_tvlv_handler_free_ref(tvlv_handler); + return; + } + + tvlv_handler = kzalloc(sizeof(*tvlv_handler), GFP_ATOMIC); + if (!tvlv_handler) + return; + + tvlv_handler->ogm_handler = optr; + tvlv_handler->unicast_handler = uptr; + tvlv_handler->type = type; + tvlv_handler->version = version; + tvlv_handler->flags = flags; + atomic_set(&tvlv_handler->refcount, 1); + INIT_HLIST_NODE(&tvlv_handler->list); + + spin_lock_bh(&bat_priv->tvlv.handler_list_lock); + hlist_add_head_rcu(&tvlv_handler->list, &bat_priv->tvlv.handler_list); + spin_unlock_bh(&bat_priv->tvlv.handler_list_lock); +} + +/** + * batadv_tvlv_handler_unregister - unregister tvlv handler based on the + * provided type and version (both need to match) + * @bat_priv: the bat priv with all the soft interface information + * @type: tvlv handler type to be unregistered + * @version: tvlv handler version to be unregistered + */ +void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv, + uint8_t type, uint8_t version) +{ + struct batadv_tvlv_handler *tvlv_handler; + + tvlv_handler = batadv_tvlv_handler_get(bat_priv, type, version); + if (!tvlv_handler) + return; + + batadv_tvlv_handler_free_ref(tvlv_handler); + spin_lock_bh(&bat_priv->tvlv.handler_list_lock); + hlist_del_rcu(&tvlv_handler->list); + spin_unlock_bh(&bat_priv->tvlv.handler_list_lock); + batadv_tvlv_handler_free_ref(tvlv_handler); +} + +/** + * batadv_tvlv_unicast_send - send a unicast packet with tvlv payload to the + * specified host + * @bat_priv: the bat priv with all the soft interface information + * @src: source mac address of the unicast packet + * @dst: destination mac address of the unicast packet + * @type: tvlv type + * @version: tvlv version + * @tvlv_value: tvlv content + * @tvlv_value_len: tvlv content length + */ +void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, uint8_t *src, + uint8_t *dst, uint8_t type, uint8_t version, + void *tvlv_value, uint16_t tvlv_value_len) +{ + struct batadv_unicast_tvlv_packet *unicast_tvlv_packet; + struct batadv_tvlv_hdr *tvlv_hdr; + struct batadv_orig_node *orig_node; + struct sk_buff *skb = NULL; + unsigned char *tvlv_buff; + unsigned int tvlv_len; + ssize_t hdr_len = sizeof(*unicast_tvlv_packet); + bool ret = false; + + orig_node = batadv_orig_hash_find(bat_priv, dst); + if (!orig_node) + goto out; + + tvlv_len = sizeof(*tvlv_hdr) + tvlv_value_len; + + skb = netdev_alloc_skb_ip_align(NULL, ETH_HLEN + hdr_len + tvlv_len); + if (!skb) + goto out; + + skb->priority = TC_PRIO_CONTROL; + skb_reserve(skb, ETH_HLEN); + tvlv_buff = skb_put(skb, sizeof(*unicast_tvlv_packet) + tvlv_len); + unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)tvlv_buff; + unicast_tvlv_packet->header.packet_type = BATADV_UNICAST_TVLV; + unicast_tvlv_packet->header.version = BATADV_COMPAT_VERSION; + unicast_tvlv_packet->header.ttl = BATADV_TTL; + unicast_tvlv_packet->reserved = 0; + unicast_tvlv_packet->tvlv_len = htons(tvlv_len); + unicast_tvlv_packet->align = 0; + memcpy(unicast_tvlv_packet->src, src, ETH_ALEN); + memcpy(unicast_tvlv_packet->dst, dst, ETH_ALEN); + + tvlv_buff = (unsigned char *)(unicast_tvlv_packet + 1); + tvlv_hdr = (struct batadv_tvlv_hdr *)tvlv_buff; + tvlv_hdr->version = version; + tvlv_hdr->type = type; + tvlv_hdr->len = htons(tvlv_value_len); + tvlv_buff += sizeof(*tvlv_hdr); + memcpy(tvlv_buff, tvlv_value, tvlv_value_len); + + if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) + ret = true; + +out: + if (skb && !ret) + kfree_skb(skb); + if (orig_node) + batadv_orig_node_free_ref(orig_node); +} + +/** + * batadv_get_vid - extract the VLAN identifier from skb if any + * @skb: the buffer containing the packet + * @header_len: length of the batman header preceding the ethernet header + * + * If the packet embedded in the skb is vlan tagged this function returns the + * VID with the BATADV_VLAN_HAS_TAG flag. Otherwise BATADV_NO_FLAGS is returned. + */ +unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len) +{ + struct ethhdr *ethhdr = (struct ethhdr *)(skb->data + header_len); + struct vlan_ethhdr *vhdr; + unsigned short vid; + + if (ethhdr->h_proto != htons(ETH_P_8021Q)) + return BATADV_NO_FLAGS; + + if (!pskb_may_pull(skb, header_len + VLAN_ETH_HLEN)) + return BATADV_NO_FLAGS; + + vhdr = (struct vlan_ethhdr *)(skb->data + header_len); + vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; + vid |= BATADV_VLAN_HAS_TAG; + + return vid; +} + static int batadv_param_set_ra(const char *val, const struct kernel_param *kp) { struct batadv_algo_ops *bat_algo_ops; diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 24675523930fe6b17b7ee15e2b1eece635ff87fe..f94f287b867083e755bd62dcc56af88f30837b33 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -20,13 +20,13 @@ #ifndef _NET_BATMAN_ADV_MAIN_H_ #define _NET_BATMAN_ADV_MAIN_H_ -#define BATADV_DRIVER_AUTHOR "Marek Lindner , " \ - "Simon Wunderlich " +#define BATADV_DRIVER_AUTHOR "Marek Lindner , " \ + "Simon Wunderlich " #define BATADV_DRIVER_DESC "B.A.T.M.A.N. advanced" #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2013.4.0" +#define BATADV_SOURCE_VERSION "2013.5.0" #endif /* B.A.T.M.A.N. parameters */ @@ -86,7 +86,11 @@ /* numbers of originator to contact for any PUT/GET DHT operation */ #define BATADV_DAT_CANDIDATES_NUM 3 -#define BATADV_VIS_INTERVAL 5000 /* 5 seconds */ +/** + * BATADV_TQ_SIMILARITY_THRESHOLD - TQ points that a secondary metric can differ + * at most from the primary one in order to be still considered acceptable + */ +#define BATADV_TQ_SIMILARITY_THRESHOLD 50 /* how much worse secondary interfaces may be to be considered as bonding * candidates @@ -133,6 +137,15 @@ enum batadv_uev_type { #define BATADV_GW_THRESHOLD 50 +/* Number of fragment chains for each orig_node */ +#define BATADV_FRAG_BUFFER_COUNT 8 +/* Maximum number of fragments for one packet */ +#define BATADV_FRAG_MAX_FRAGMENTS 16 +/* Maxumim size of each fragment */ +#define BATADV_FRAG_MAX_FRAG_SIZE 1400 +/* Time to keep fragments while waiting for rest of the fragments */ +#define BATADV_FRAG_TIMEOUT 10000 + #define BATADV_DAT_CANDIDATE_NOT_FOUND 0 #define BATADV_DAT_CANDIDATE_ORIG 1 @@ -160,15 +173,9 @@ enum batadv_uev_type { #include #include #include -#include "types.h" +#include -/** - * batadv_vlan_flags - flags for the four MSB of any vlan ID field - * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not - */ -enum batadv_vlan_flags { - BATADV_VLAN_HAS_TAG = BIT(15), -}; +#include "types.h" #define BATADV_PRINT_VID(vid) (vid & BATADV_VLAN_HAS_TAG ? \ (int)(vid & VLAN_VID_MASK) : -1) @@ -184,6 +191,7 @@ void batadv_mesh_free(struct net_device *soft_iface); int batadv_is_my_mac(struct batadv_priv *bat_priv, const uint8_t *addr); struct batadv_hard_iface * batadv_seq_print_text_primary_if_get(struct seq_file *seq); +int batadv_max_header_len(void); void batadv_skb_set_priority(struct sk_buff *skb, int offset); int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype, @@ -326,4 +334,40 @@ static inline uint64_t batadv_sum_counter(struct batadv_priv *bat_priv, */ #define BATADV_SKB_CB(__skb) ((struct batadv_skb_cb *)&((__skb)->cb[0])) +void batadv_tvlv_container_register(struct batadv_priv *bat_priv, + uint8_t type, uint8_t version, + void *tvlv_value, uint16_t tvlv_value_len); +uint16_t batadv_tvlv_container_ogm_append(struct batadv_priv *bat_priv, + unsigned char **packet_buff, + int *packet_buff_len, + int packet_min_len); +void batadv_tvlv_ogm_receive(struct batadv_priv *bat_priv, + struct batadv_ogm_packet *batadv_ogm_packet, + struct batadv_orig_node *orig_node); +void batadv_tvlv_container_unregister(struct batadv_priv *bat_priv, + uint8_t type, uint8_t version); + +void batadv_tvlv_handler_register(struct batadv_priv *bat_priv, + void (*optr)(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t flags, + void *tvlv_value, + uint16_t tvlv_value_len), + int (*uptr)(struct batadv_priv *bat_priv, + uint8_t *src, uint8_t *dst, + void *tvlv_value, + uint16_t tvlv_value_len), + uint8_t type, uint8_t version, uint8_t flags); +void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv, + uint8_t type, uint8_t version); +int batadv_tvlv_containers_process(struct batadv_priv *bat_priv, + bool ogm_source, + struct batadv_orig_node *orig_node, + uint8_t *src, uint8_t *dst, + void *tvlv_buff, uint16_t tvlv_buff_len); +void batadv_tvlv_unicast_send(struct batadv_priv *bat_priv, uint8_t *src, + uint8_t *dst, uint8_t type, uint8_t version, + void *tvlv_value, uint16_t tvlv_value_len); +unsigned short batadv_get_vid(struct sk_buff *skb, size_t header_len); + #endif /* _NET_BATMAN_ADV_MAIN_H_ */ diff --git a/net/batman-adv/network-coding.c b/net/batman-adv/network-coding.c index 4ecc0b6bf8ab63a41b57235f6706207bd5da9e99..351e199bc0afff37aa9c1a4b3f40201956dfde91 100644 --- a/net/batman-adv/network-coding.c +++ b/net/batman-adv/network-coding.c @@ -58,6 +58,59 @@ static void batadv_nc_start_timer(struct batadv_priv *bat_priv) msecs_to_jiffies(10)); } +/** + * batadv_nc_tvlv_container_update - update the network coding tvlv container + * after network coding setting change + * @bat_priv: the bat priv with all the soft interface information + */ +static void batadv_nc_tvlv_container_update(struct batadv_priv *bat_priv) +{ + char nc_mode; + + nc_mode = atomic_read(&bat_priv->network_coding); + + switch (nc_mode) { + case 0: + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_NC, 1); + break; + case 1: + batadv_tvlv_container_register(bat_priv, BATADV_TVLV_NC, 1, + NULL, 0); + break; + } +} + +/** + * batadv_nc_status_update - update the network coding tvlv container after + * network coding setting change + * @net_dev: the soft interface net device + */ +void batadv_nc_status_update(struct net_device *net_dev) +{ + struct batadv_priv *bat_priv = netdev_priv(net_dev); + batadv_nc_tvlv_container_update(bat_priv); +} + +/** + * batadv_nc_tvlv_ogm_handler_v1 - process incoming nc tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node of the ogm + * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) + * @tvlv_value: tvlv buffer containing the gateway data + * @tvlv_value_len: tvlv buffer length + */ +static void batadv_nc_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t flags, + void *tvlv_value, + uint16_t tvlv_value_len) +{ + if (flags & BATADV_TVLV_HANDLER_OGM_CIFNOTFND) + orig->capabilities &= ~BATADV_ORIG_CAPA_HAS_NC; + else + orig->capabilities |= BATADV_ORIG_CAPA_HAS_NC; +} + /** * batadv_nc_mesh_init - initialise coding hash table and start house keeping * @bat_priv: the bat priv with all the soft interface information @@ -87,6 +140,10 @@ int batadv_nc_mesh_init(struct batadv_priv *bat_priv) INIT_DELAYED_WORK(&bat_priv->nc.work, batadv_nc_worker); batadv_nc_start_timer(bat_priv); + batadv_tvlv_handler_register(bat_priv, batadv_nc_tvlv_ogm_handler_v1, + NULL, BATADV_TVLV_NC, 1, + BATADV_TVLV_HANDLER_OGM_CIFNOTFND); + batadv_nc_tvlv_container_update(bat_priv); return 0; err: @@ -802,6 +859,10 @@ void batadv_nc_update_nc_node(struct batadv_priv *bat_priv, if (!atomic_read(&bat_priv->network_coding)) goto out; + /* check if orig node is network coding enabled */ + if (!(orig_node->capabilities & BATADV_ORIG_CAPA_HAS_NC)) + goto out; + /* accept ogms from 'good' neighbors and single hop neighbors */ if (!batadv_can_nc_with_orig(bat_priv, orig_node, ogm_packet) && !is_single_hop_neigh) @@ -942,7 +1003,7 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, struct batadv_nc_packet *nc_packet, struct batadv_neigh_node *neigh_node) { - uint8_t tq_weighted_neigh, tq_weighted_coding; + uint8_t tq_weighted_neigh, tq_weighted_coding, tq_tmp; struct sk_buff *skb_dest, *skb_src; struct batadv_unicast_packet *packet1; struct batadv_unicast_packet *packet2; @@ -967,8 +1028,10 @@ static bool batadv_nc_code_packets(struct batadv_priv *bat_priv, if (!router_coding) goto out; - tq_weighted_neigh = batadv_nc_random_weight_tq(router_neigh->tq_avg); - tq_weighted_coding = batadv_nc_random_weight_tq(router_coding->tq_avg); + tq_tmp = batadv_nc_random_weight_tq(router_neigh->bat_iv.tq_avg); + tq_weighted_neigh = tq_tmp; + tq_tmp = batadv_nc_random_weight_tq(router_coding->bat_iv.tq_avg); + tq_weighted_coding = tq_tmp; /* Select one destination for the MAC-header dst-field based on * weighted TQ-values. @@ -1735,6 +1798,8 @@ free_nc_packet: */ void batadv_nc_mesh_free(struct batadv_priv *bat_priv) { + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_NC, 1); + batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_NC, 1); cancel_delayed_work_sync(&bat_priv->nc.work); batadv_nc_purge_paths(bat_priv, bat_priv->nc.coding_hash, NULL); diff --git a/net/batman-adv/network-coding.h b/net/batman-adv/network-coding.h index ddfa618e80bf0202fdf6bc89ffe213b44cbfb28a..d4fd315b5261904c1ca990578fc562b0bae41f89 100644 --- a/net/batman-adv/network-coding.h +++ b/net/batman-adv/network-coding.h @@ -22,6 +22,7 @@ #ifdef CONFIG_BATMAN_ADV_NC +void batadv_nc_status_update(struct net_device *net_dev); int batadv_nc_init(void); int batadv_nc_mesh_init(struct batadv_priv *bat_priv); void batadv_nc_mesh_free(struct batadv_priv *bat_priv); @@ -47,6 +48,10 @@ int batadv_nc_init_debugfs(struct batadv_priv *bat_priv); #else /* ifdef CONFIG_BATMAN_ADV_NC */ +static inline void batadv_nc_status_update(struct net_device *net_dev) +{ +} + static inline int batadv_nc_init(void) { return 0; diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index f50553a7de629a411d94d307ba2ea54327e7b28f..8ab14340d10f6433c849face4852e4de1b5b9547 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -25,10 +25,10 @@ #include "routing.h" #include "gateway_client.h" #include "hard-interface.h" -#include "unicast.h" #include "soft-interface.h" #include "bridge_loop_avoidance.h" #include "network-coding.h" +#include "fragmentation.h" /* hash class keys */ static struct lock_class_key batadv_orig_hash_lock_class_key; @@ -36,7 +36,7 @@ static struct lock_class_key batadv_orig_hash_lock_class_key; static void batadv_purge_orig(struct work_struct *work); /* returns 1 if they are the same originator */ -static int batadv_compare_orig(const struct hlist_node *node, const void *data2) +int batadv_compare_orig(const struct hlist_node *node, const void *data2) { const void *data1 = container_of(node, struct batadv_orig_node, hash_entry); @@ -44,6 +44,88 @@ static int batadv_compare_orig(const struct hlist_node *node, const void *data2) return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } +/** + * batadv_orig_node_vlan_get - get an orig_node_vlan object + * @orig_node: the originator serving the VLAN + * @vid: the VLAN identifier + * + * Returns the vlan object identified by vid and belonging to orig_node or NULL + * if it does not exist. + */ +struct batadv_orig_node_vlan * +batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node, + unsigned short vid) +{ + struct batadv_orig_node_vlan *vlan = NULL, *tmp; + + rcu_read_lock(); + list_for_each_entry_rcu(tmp, &orig_node->vlan_list, list) { + if (tmp->vid != vid) + continue; + + if (!atomic_inc_not_zero(&tmp->refcount)) + continue; + + vlan = tmp; + + break; + } + rcu_read_unlock(); + + return vlan; +} + +/** + * batadv_orig_node_vlan_new - search and possibly create an orig_node_vlan + * object + * @orig_node: the originator serving the VLAN + * @vid: the VLAN identifier + * + * Returns NULL in case of failure or the vlan object identified by vid and + * belonging to orig_node otherwise. The object is created and added to the list + * if it does not exist. + * + * The object is returned with refcounter increased by 1. + */ +struct batadv_orig_node_vlan * +batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node, + unsigned short vid) +{ + struct batadv_orig_node_vlan *vlan; + + spin_lock_bh(&orig_node->vlan_list_lock); + + /* first look if an object for this vid already exists */ + vlan = batadv_orig_node_vlan_get(orig_node, vid); + if (vlan) + goto out; + + vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC); + if (!vlan) + goto out; + + atomic_set(&vlan->refcount, 2); + vlan->vid = vid; + + list_add_rcu(&vlan->list, &orig_node->vlan_list); + +out: + spin_unlock_bh(&orig_node->vlan_list_lock); + + return vlan; +} + +/** + * batadv_orig_node_vlan_free_ref - decrement the refcounter and possibly free + * the originator-vlan object + * @orig_vlan: the originator-vlan object to release + */ +void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan) +{ + if (atomic_dec_and_test(&orig_vlan->refcount)) + kfree_rcu(orig_vlan, rcu); +} + int batadv_originator_init(struct batadv_priv *bat_priv) { if (bat_priv->orig_hash) @@ -90,11 +172,20 @@ batadv_orig_node_get_router(struct batadv_orig_node *orig_node) return router; } +/** + * batadv_neigh_node_new - create and init a new neigh_node object + * @hard_iface: the interface where the neighbour is connected to + * @neigh_addr: the mac address of the neighbour interface + * @orig_node: originator object representing the neighbour + * + * Allocates a new neigh_node object and initialises all the generic fields. + * Returns the new object or NULL on failure. + */ struct batadv_neigh_node * batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, - const uint8_t *neigh_addr) + const uint8_t *neigh_addr, + struct batadv_orig_node *orig_node) { - struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct batadv_neigh_node *neigh_node; neigh_node = kzalloc(sizeof(*neigh_node), GFP_ATOMIC); @@ -104,15 +195,14 @@ batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, INIT_HLIST_NODE(&neigh_node->list); memcpy(neigh_node->addr, neigh_addr, ETH_ALEN); - spin_lock_init(&neigh_node->lq_update_lock); + neigh_node->if_incoming = hard_iface; + neigh_node->orig_node = orig_node; + + INIT_LIST_HEAD(&neigh_node->bonding_list); /* extra reference for return */ atomic_set(&neigh_node->refcount, 2); - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Creating new neighbor %pM on interface %s\n", neigh_addr, - hard_iface->net_dev->name); - out: return neigh_node; } @@ -146,13 +236,15 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu) /* Free nc_nodes */ batadv_nc_purge_orig(orig_node->bat_priv, orig_node, NULL); - batadv_frag_list_free(&orig_node->frag_list); - batadv_tt_global_del_orig(orig_node->bat_priv, orig_node, + batadv_frag_purge_orig(orig_node, NULL); + + batadv_tt_global_del_orig(orig_node->bat_priv, orig_node, -1, "originator timed out"); + if (orig_node->bat_priv->bat_algo_ops->bat_orig_free) + orig_node->bat_priv->bat_algo_ops->bat_orig_free(orig_node); + kfree(orig_node->tt_buff); - kfree(orig_node->bcast_own); - kfree(orig_node->bcast_own_sum); kfree(orig_node); } @@ -210,20 +302,22 @@ void batadv_originator_free(struct batadv_priv *bat_priv) batadv_hash_destroy(hash); } -/* this function finds or creates an originator entry for the given - * address if it does not exits +/** + * batadv_orig_node_new - creates a new orig_node + * @bat_priv: the bat priv with all the soft interface information + * @addr: the mac address of the originator + * + * Creates a new originator object and initialise all the generic fields. + * The new object is not added to the originator list. + * Returns the newly created object or NULL on failure. */ -struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, +struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, const uint8_t *addr) { struct batadv_orig_node *orig_node; - int size; - int hash_added; + struct batadv_orig_node_vlan *vlan; unsigned long reset_time; - - orig_node = batadv_orig_hash_find(bat_priv, addr); - if (orig_node) - return orig_node; + int i; batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "Creating new originator: %pM\n", addr); @@ -234,10 +328,12 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, INIT_HLIST_HEAD(&orig_node->neigh_list); INIT_LIST_HEAD(&orig_node->bond_list); - spin_lock_init(&orig_node->ogm_cnt_lock); + INIT_LIST_HEAD(&orig_node->vlan_list); spin_lock_init(&orig_node->bcast_seqno_lock); spin_lock_init(&orig_node->neigh_list_lock); spin_lock_init(&orig_node->tt_buff_lock); + spin_lock_init(&orig_node->tt_lock); + spin_lock_init(&orig_node->vlan_list_lock); batadv_nc_init_orig(orig_node); @@ -249,43 +345,32 @@ struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, memcpy(orig_node->orig, addr, ETH_ALEN); batadv_dat_init_orig_node_addr(orig_node); orig_node->router = NULL; - orig_node->tt_crc = 0; atomic_set(&orig_node->last_ttvn, 0); orig_node->tt_buff = NULL; orig_node->tt_buff_len = 0; - atomic_set(&orig_node->tt_size, 0); reset_time = jiffies - 1 - msecs_to_jiffies(BATADV_RESET_PROTECTION_MS); orig_node->bcast_seqno_reset = reset_time; orig_node->batman_seqno_reset = reset_time; atomic_set(&orig_node->bond_candidates, 0); - size = bat_priv->num_ifaces * sizeof(unsigned long) * BATADV_NUM_WORDS; - - orig_node->bcast_own = kzalloc(size, GFP_ATOMIC); - if (!orig_node->bcast_own) + /* create a vlan object for the "untagged" LAN */ + vlan = batadv_orig_node_vlan_new(orig_node, BATADV_NO_FLAGS); + if (!vlan) goto free_orig_node; + /* batadv_orig_node_vlan_new() increases the refcounter. + * Immediately release vlan since it is not needed anymore in this + * context + */ + batadv_orig_node_vlan_free_ref(vlan); - size = bat_priv->num_ifaces * sizeof(uint8_t); - orig_node->bcast_own_sum = kzalloc(size, GFP_ATOMIC); - - INIT_LIST_HEAD(&orig_node->frag_list); - orig_node->last_frag_packet = 0; - - if (!orig_node->bcast_own_sum) - goto free_bcast_own; - - hash_added = batadv_hash_add(bat_priv->orig_hash, batadv_compare_orig, - batadv_choose_orig, orig_node, - &orig_node->hash_entry); - if (hash_added != 0) - goto free_bcast_own_sum; + for (i = 0; i < BATADV_FRAG_BUFFER_COUNT; i++) { + INIT_HLIST_HEAD(&orig_node->fragments[i].head); + spin_lock_init(&orig_node->fragments[i].lock); + orig_node->fragments[i].size = 0; + } return orig_node; -free_bcast_own_sum: - kfree(orig_node->bcast_own_sum); -free_bcast_own: - kfree(orig_node->bcast_own); free_orig_node: kfree(orig_node); return NULL; @@ -294,15 +379,16 @@ free_orig_node: static bool batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - struct batadv_neigh_node **best_neigh_node) + struct batadv_neigh_node **best_neigh) { + struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; struct hlist_node *node_tmp; struct batadv_neigh_node *neigh_node; bool neigh_purged = false; unsigned long last_seen; struct batadv_hard_iface *if_incoming; - *best_neigh_node = NULL; + *best_neigh = NULL; spin_lock_bh(&orig_node->neigh_list_lock); @@ -335,9 +421,12 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv, batadv_bonding_candidate_del(orig_node, neigh_node); batadv_neigh_node_free_ref(neigh_node); } else { - if ((!*best_neigh_node) || - (neigh_node->tq_avg > (*best_neigh_node)->tq_avg)) - *best_neigh_node = neigh_node; + /* store the best_neighbour if this is the first + * iteration or if a better neighbor has been found + */ + if (!*best_neigh || + bao->bat_neigh_cmp(neigh_node, *best_neigh) > 0) + *best_neigh = neigh_node; } } @@ -388,17 +477,14 @@ static void _batadv_purge_orig(struct batadv_priv *bat_priv) hlist_for_each_entry_safe(orig_node, node_tmp, head, hash_entry) { if (batadv_purge_orig_node(bat_priv, orig_node)) { - if (orig_node->gw_flags) - batadv_gw_node_delete(bat_priv, - orig_node); + batadv_gw_node_delete(bat_priv, orig_node); hlist_del_rcu(&orig_node->hash_entry); batadv_orig_node_free_ref(orig_node); continue; } - if (batadv_has_timed_out(orig_node->last_frag_packet, - BATADV_FRAG_TIMEOUT)) - batadv_frag_list_free(&orig_node->frag_list); + batadv_frag_purge_orig(orig_node, + batadv_frag_check_entry); } spin_unlock_bh(list_lock); } @@ -429,100 +515,26 @@ int batadv_orig_seq_print_text(struct seq_file *seq, void *offset) { struct net_device *net_dev = (struct net_device *)seq->private; struct batadv_priv *bat_priv = netdev_priv(net_dev); - struct batadv_hashtable *hash = bat_priv->orig_hash; - struct hlist_head *head; struct batadv_hard_iface *primary_if; - struct batadv_orig_node *orig_node; - struct batadv_neigh_node *neigh_node, *neigh_node_tmp; - int batman_count = 0; - int last_seen_secs; - int last_seen_msecs; - unsigned long last_seen_jiffies; - uint32_t i; primary_if = batadv_seq_print_text_primary_if_get(seq); if (!primary_if) - goto out; + return 0; - seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s)]\n", + seq_printf(seq, "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%pM (%s %s)]\n", BATADV_SOURCE_VERSION, primary_if->net_dev->name, - primary_if->net_dev->dev_addr, net_dev->name); - seq_printf(seq, " %-15s %s (%s/%i) %17s [%10s]: %20s ...\n", - "Originator", "last-seen", "#", BATADV_TQ_MAX_VALUE, - "Nexthop", "outgoingIF", "Potential nexthops"); - - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - - rcu_read_lock(); - hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - neigh_node = batadv_orig_node_get_router(orig_node); - if (!neigh_node) - continue; - - if (neigh_node->tq_avg == 0) - goto next; - - last_seen_jiffies = jiffies - orig_node->last_seen; - last_seen_msecs = jiffies_to_msecs(last_seen_jiffies); - last_seen_secs = last_seen_msecs / 1000; - last_seen_msecs = last_seen_msecs % 1000; - - seq_printf(seq, "%pM %4i.%03is (%3i) %pM [%10s]:", - orig_node->orig, last_seen_secs, - last_seen_msecs, neigh_node->tq_avg, - neigh_node->addr, - neigh_node->if_incoming->net_dev->name); - - hlist_for_each_entry_rcu(neigh_node_tmp, - &orig_node->neigh_list, list) { - seq_printf(seq, " %pM (%3i)", - neigh_node_tmp->addr, - neigh_node_tmp->tq_avg); - } + primary_if->net_dev->dev_addr, net_dev->name, + bat_priv->bat_algo_ops->name); - seq_puts(seq, "\n"); - batman_count++; + batadv_hardif_free_ref(primary_if); -next: - batadv_neigh_node_free_ref(neigh_node); - } - rcu_read_unlock(); + if (!bat_priv->bat_algo_ops->bat_orig_print) { + seq_puts(seq, + "No printing function for this routing protocol\n"); + return 0; } - if (batman_count == 0) - seq_puts(seq, "No batman nodes in range ...\n"); - -out: - if (primary_if) - batadv_hardif_free_ref(primary_if); - return 0; -} - -static int batadv_orig_node_add_if(struct batadv_orig_node *orig_node, - int max_if_num) -{ - void *data_ptr; - size_t data_size, old_size; - - data_size = max_if_num * sizeof(unsigned long) * BATADV_NUM_WORDS; - old_size = (max_if_num - 1) * sizeof(unsigned long) * BATADV_NUM_WORDS; - data_ptr = kmalloc(data_size, GFP_ATOMIC); - if (!data_ptr) - return -ENOMEM; - - memcpy(data_ptr, orig_node->bcast_own, old_size); - kfree(orig_node->bcast_own); - orig_node->bcast_own = data_ptr; - - data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); - if (!data_ptr) - return -ENOMEM; - - memcpy(data_ptr, orig_node->bcast_own_sum, - (max_if_num - 1) * sizeof(uint8_t)); - kfree(orig_node->bcast_own_sum); - orig_node->bcast_own_sum = data_ptr; + bat_priv->bat_algo_ops->bat_orig_print(bat_priv, seq); return 0; } @@ -531,6 +543,7 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface, int max_if_num) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); + struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; struct batadv_hashtable *hash = bat_priv->orig_hash; struct hlist_head *head; struct batadv_orig_node *orig_node; @@ -545,10 +558,10 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface, rcu_read_lock(); hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - spin_lock_bh(&orig_node->ogm_cnt_lock); - ret = batadv_orig_node_add_if(orig_node, max_if_num); - spin_unlock_bh(&orig_node->ogm_cnt_lock); - + ret = 0; + if (bao->bat_orig_add_if) + ret = bao->bat_orig_add_if(orig_node, + max_if_num); if (ret == -ENOMEM) goto err; } @@ -562,54 +575,6 @@ err: return -ENOMEM; } -static int batadv_orig_node_del_if(struct batadv_orig_node *orig_node, - int max_if_num, int del_if_num) -{ - void *data_ptr = NULL; - int chunk_size; - - /* last interface was removed */ - if (max_if_num == 0) - goto free_bcast_own; - - chunk_size = sizeof(unsigned long) * BATADV_NUM_WORDS; - data_ptr = kmalloc(max_if_num * chunk_size, GFP_ATOMIC); - if (!data_ptr) - return -ENOMEM; - - /* copy first part */ - memcpy(data_ptr, orig_node->bcast_own, del_if_num * chunk_size); - - /* copy second part */ - memcpy((char *)data_ptr + del_if_num * chunk_size, - orig_node->bcast_own + ((del_if_num + 1) * chunk_size), - (max_if_num - del_if_num) * chunk_size); - -free_bcast_own: - kfree(orig_node->bcast_own); - orig_node->bcast_own = data_ptr; - - if (max_if_num == 0) - goto free_own_sum; - - data_ptr = kmalloc(max_if_num * sizeof(uint8_t), GFP_ATOMIC); - if (!data_ptr) - return -ENOMEM; - - memcpy(data_ptr, orig_node->bcast_own_sum, - del_if_num * sizeof(uint8_t)); - - memcpy((char *)data_ptr + del_if_num * sizeof(uint8_t), - orig_node->bcast_own_sum + ((del_if_num + 1) * sizeof(uint8_t)), - (max_if_num - del_if_num) * sizeof(uint8_t)); - -free_own_sum: - kfree(orig_node->bcast_own_sum); - orig_node->bcast_own_sum = data_ptr; - - return 0; -} - int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface, int max_if_num) { @@ -618,6 +583,7 @@ int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface, struct hlist_head *head; struct batadv_hard_iface *hard_iface_tmp; struct batadv_orig_node *orig_node; + struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; uint32_t i; int ret; @@ -629,11 +595,11 @@ int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface, rcu_read_lock(); hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - spin_lock_bh(&orig_node->ogm_cnt_lock); - ret = batadv_orig_node_del_if(orig_node, max_if_num, - hard_iface->if_num); - spin_unlock_bh(&orig_node->ogm_cnt_lock); - + ret = 0; + if (bao->bat_orig_del_if) + ret = bao->bat_orig_del_if(orig_node, + max_if_num, + hard_iface->if_num); if (ret == -ENOMEM) goto err; } diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 7887b84a9af43adbff91cb8e3695b7f29c36a399..6f77d808a91672d77a48426cd5d391abf366bfb4 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -22,16 +22,18 @@ #include "hash.h" +int batadv_compare_orig(const struct hlist_node *node, const void *data2); int batadv_originator_init(struct batadv_priv *bat_priv); void batadv_originator_free(struct batadv_priv *bat_priv); void batadv_purge_orig_ref(struct batadv_priv *bat_priv); void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node); void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node); -struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, +struct batadv_orig_node *batadv_orig_node_new(struct batadv_priv *bat_priv, const uint8_t *addr); struct batadv_neigh_node * batadv_neigh_node_new(struct batadv_hard_iface *hard_iface, - const uint8_t *neigh_addr); + const uint8_t *neigh_addr, + struct batadv_orig_node *orig_node); void batadv_neigh_node_free_ref(struct batadv_neigh_node *neigh_node); struct batadv_neigh_node * batadv_orig_node_get_router(struct batadv_orig_node *orig_node); @@ -40,6 +42,13 @@ int batadv_orig_hash_add_if(struct batadv_hard_iface *hard_iface, int max_if_num); int batadv_orig_hash_del_if(struct batadv_hard_iface *hard_iface, int max_if_num); +struct batadv_orig_node_vlan * +batadv_orig_node_vlan_new(struct batadv_orig_node *orig_node, + unsigned short vid); +struct batadv_orig_node_vlan * +batadv_orig_node_vlan_get(struct batadv_orig_node *orig_node, + unsigned short vid); +void batadv_orig_node_vlan_free_ref(struct batadv_orig_node_vlan *orig_vlan); /* hashfunction to choose an entry in a hash table of given size diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index a51ccfc39da4144fa2ccee27b0e0afca741034dd..207459b62966d0975bca70894fcf5ce266a5da29 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -20,17 +20,34 @@ #ifndef _NET_BATMAN_ADV_PACKET_H_ #define _NET_BATMAN_ADV_PACKET_H_ +/** + * enum batadv_packettype - types for batman-adv encapsulated packets + * @BATADV_IV_OGM: originator messages for B.A.T.M.A.N. IV + * @BATADV_BCAST: broadcast packets carrying broadcast payload + * @BATADV_CODED: network coded packets + * + * @BATADV_UNICAST: unicast packets carrying unicast payload traffic + * @BATADV_UNICAST_FRAG: unicast packets carrying a fragment of the original + * payload packet + * @BATADV_UNICAST_4ADDR: unicast packet including the originator address of + * the sender + * @BATADV_ICMP: unicast packet like IP ICMP used for ping or traceroute + * @BATADV_UNICAST_TVLV: unicast packet carrying TVLV containers + */ enum batadv_packettype { - BATADV_IV_OGM = 0x01, - BATADV_ICMP = 0x02, - BATADV_UNICAST = 0x03, - BATADV_BCAST = 0x04, - BATADV_VIS = 0x05, - BATADV_UNICAST_FRAG = 0x06, - BATADV_TT_QUERY = 0x07, - BATADV_ROAM_ADV = 0x08, - BATADV_UNICAST_4ADDR = 0x09, - BATADV_CODED = 0x0a, + /* 0x00 - 0x3f: local packets or special rules for handling */ + BATADV_IV_OGM = 0x00, + BATADV_BCAST = 0x01, + BATADV_CODED = 0x02, + /* 0x40 - 0x7f: unicast */ +#define BATADV_UNICAST_MIN 0x40 + BATADV_UNICAST = 0x40, + BATADV_UNICAST_FRAG = 0x41, + BATADV_UNICAST_4ADDR = 0x42, + BATADV_ICMP = 0x43, + BATADV_UNICAST_TVLV = 0x44, +#define BATADV_UNICAST_MAX 0x7f + /* 0x80 - 0xff: reserved */ }; /** @@ -48,13 +65,21 @@ enum batadv_subtype { }; /* this file is included by batctl which needs these defines */ -#define BATADV_COMPAT_VERSION 14 +#define BATADV_COMPAT_VERSION 15 +/** + * enum batadv_iv_flags - flags used in B.A.T.M.A.N. IV OGM packets + * @BATADV_NOT_BEST_NEXT_HOP: flag is set when ogm packet is forwarded and was + * previously received from someone else than the best neighbor. + * @BATADV_PRIMARIES_FIRST_HOP: flag is set when the primary interface address + * is used, and the packet travels its first hop. + * @BATADV_DIRECTLINK: flag is for the first hop or if rebroadcasted from a + * one hop neighbor on the interface where it was originally received. + */ enum batadv_iv_flags { - BATADV_NOT_BEST_NEXT_HOP = BIT(3), - BATADV_PRIMARIES_FIRST_HOP = BIT(4), - BATADV_VIS_SERVER = BIT(5), - BATADV_DIRECTLINK = BIT(6), + BATADV_NOT_BEST_NEXT_HOP = BIT(0), + BATADV_PRIMARIES_FIRST_HOP = BIT(1), + BATADV_DIRECTLINK = BIT(2), }; /* ICMP message types */ @@ -66,43 +91,44 @@ enum batadv_icmp_packettype { BATADV_PARAMETER_PROBLEM = 12, }; -/* vis defines */ -enum batadv_vis_packettype { - BATADV_VIS_TYPE_SERVER_SYNC = 0, - BATADV_VIS_TYPE_CLIENT_UPDATE = 1, -}; - -/* fragmentation defines */ -enum batadv_unicast_frag_flags { - BATADV_UNI_FRAG_HEAD = BIT(0), - BATADV_UNI_FRAG_LARGETAIL = BIT(1), -}; +/* tt data subtypes */ +#define BATADV_TT_DATA_TYPE_MASK 0x0F -/* TT_QUERY subtypes */ -#define BATADV_TT_QUERY_TYPE_MASK 0x3 - -enum batadv_tt_query_packettype { - BATADV_TT_REQUEST = 0, - BATADV_TT_RESPONSE = 1, -}; - -/* TT_QUERY flags */ -enum batadv_tt_query_flags { - BATADV_TT_FULL_TABLE = BIT(2), +/** + * enum batadv_tt_data_flags - flags for tt data tvlv + * @BATADV_TT_OGM_DIFF: TT diff propagated through OGM + * @BATADV_TT_REQUEST: TT request message + * @BATADV_TT_RESPONSE: TT response message + * @BATADV_TT_FULL_TABLE: contains full table to replace existing table + */ +enum batadv_tt_data_flags { + BATADV_TT_OGM_DIFF = BIT(0), + BATADV_TT_REQUEST = BIT(1), + BATADV_TT_RESPONSE = BIT(2), + BATADV_TT_FULL_TABLE = BIT(4), }; /* BATADV_TT_CLIENT flags. * Flags from BIT(0) to BIT(7) are sent on the wire, while flags from BIT(8) to - * BIT(15) are used for local computation only + * BIT(15) are used for local computation only. + * Flags from BIT(4) to BIT(7) are kept in sync with the rest of the network. */ enum batadv_tt_client_flags { BATADV_TT_CLIENT_DEL = BIT(0), BATADV_TT_CLIENT_ROAM = BIT(1), - BATADV_TT_CLIENT_WIFI = BIT(2), - BATADV_TT_CLIENT_TEMP = BIT(3), + BATADV_TT_CLIENT_WIFI = BIT(4), BATADV_TT_CLIENT_NOPURGE = BIT(8), BATADV_TT_CLIENT_NEW = BIT(9), BATADV_TT_CLIENT_PENDING = BIT(10), + BATADV_TT_CLIENT_TEMP = BIT(11), +}; + +/** + * batadv_vlan_flags - flags for the four MSB of any vlan ID field + * @BATADV_VLAN_HAS_TAG: whether the field contains a valid vlan tag or not + */ +enum batadv_vlan_flags { + BATADV_VLAN_HAS_TAG = BIT(15), }; /* claim frame types for the bridge loop avoidance */ @@ -113,6 +139,22 @@ enum batadv_bla_claimframe { BATADV_CLAIM_TYPE_REQUEST = 0x03, }; +/** + * enum batadv_tvlv_type - tvlv type definitions + * @BATADV_TVLV_GW: gateway tvlv + * @BATADV_TVLV_DAT: distributed arp table tvlv + * @BATADV_TVLV_NC: network coding tvlv + * @BATADV_TVLV_TT: translation table tvlv + * @BATADV_TVLV_ROAM: roaming advertisement tvlv + */ +enum batadv_tvlv_type { + BATADV_TVLV_GW = 0x01, + BATADV_TVLV_DAT = 0x02, + BATADV_TVLV_NC = 0x03, + BATADV_TVLV_TT = 0x04, + BATADV_TVLV_ROAM = 0x05, +}; + /* the destination hardware field in the ARP frame is used to * transport the claim type and the group id */ @@ -131,47 +173,74 @@ struct batadv_header { */ }; +/** + * struct batadv_ogm_packet - ogm (routing protocol) packet + * @header: common batman packet header + * @flags: contains routing relevant flags - see enum batadv_iv_flags + * @tvlv_len: length of tvlv data following the ogm header + */ struct batadv_ogm_packet { struct batadv_header header; - uint8_t flags; /* 0x40: DIRECTLINK flag, 0x20 VIS_SERVER flag... */ + uint8_t flags; __be32 seqno; uint8_t orig[ETH_ALEN]; uint8_t prev_sender[ETH_ALEN]; - uint8_t gw_flags; /* flags related to gateway class */ + uint8_t reserved; uint8_t tq; - uint8_t tt_num_changes; - uint8_t ttvn; /* translation table version number */ - __be16 tt_crc; -} __packed; + __be16 tvlv_len; + /* __packed is not needed as the struct size is divisible by 4, + * and the largest data type in this struct has a size of 4. + */ +}; #define BATADV_OGM_HLEN sizeof(struct batadv_ogm_packet) -struct batadv_icmp_packet { +/** + * batadv_icmp_header - common ICMP header + * @header: common batman header + * @msg_type: ICMP packet type + * @dst: address of the destination node + * @orig: address of the source node + * @uid: local ICMP socket identifier + */ +struct batadv_icmp_header { struct batadv_header header; uint8_t msg_type; /* see ICMP message types above */ uint8_t dst[ETH_ALEN]; uint8_t orig[ETH_ALEN]; - __be16 seqno; uint8_t uid; +}; + +/** + * batadv_icmp_packet - ICMP packet + * @icmph: common ICMP header + * @reserved: not used - useful for alignment + * @seqno: ICMP sequence number + */ +struct batadv_icmp_packet { + struct batadv_icmp_header icmph; uint8_t reserved; + __be16 seqno; }; #define BATADV_RR_LEN 16 -/* icmp_packet_rr must start with all fields from imcp_packet - * as this is assumed by code that handles ICMP packets +/** + * batadv_icmp_packet_rr - ICMP RouteRecord packet + * @icmph: common ICMP header + * @rr_cur: number of entries the rr array + * @seqno: ICMP sequence number + * @rr: route record array */ struct batadv_icmp_packet_rr { - struct batadv_header header; - uint8_t msg_type; /* see ICMP message types above */ - uint8_t dst[ETH_ALEN]; - uint8_t orig[ETH_ALEN]; - __be16 seqno; - uint8_t uid; + struct batadv_icmp_header icmph; uint8_t rr_cur; + __be16 seqno; uint8_t rr[BATADV_RR_LEN][ETH_ALEN]; }; +#define BATADV_ICMP_MAX_PACKET_SIZE sizeof(struct batadv_icmp_packet_rr) + /* All packet headers in front of an ethernet header have to be completely * divisible by 2 but not by 4 to make the payload after the ethernet * header again 4 bytes boundary aligned. @@ -209,15 +278,32 @@ struct batadv_unicast_4addr_packet { */ }; -struct batadv_unicast_frag_packet { - struct batadv_header header; - uint8_t ttvn; /* destination translation table version number */ - uint8_t dest[ETH_ALEN]; - uint8_t flags; - uint8_t align; - uint8_t orig[ETH_ALEN]; - __be16 seqno; -} __packed; +/** + * struct batadv_frag_packet - fragmented packet + * @header: common batman packet header with type, compatversion, and ttl + * @dest: final destination used when routing fragments + * @orig: originator of the fragment used when merging the packet + * @no: fragment number within this sequence + * @reserved: reserved byte for alignment + * @seqno: sequence identification + * @total_size: size of the merged packet + */ +struct batadv_frag_packet { + struct batadv_header header; +#if defined(__BIG_ENDIAN_BITFIELD) + uint8_t no:4; + uint8_t reserved:4; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + uint8_t reserved:4; + uint8_t no:4; +#else +#error "unknown bitfield endianess" +#endif + uint8_t dest[ETH_ALEN]; + uint8_t orig[ETH_ALEN]; + __be16 seqno; + __be16 total_size; +}; struct batadv_bcast_packet { struct batadv_header header; @@ -231,54 +317,6 @@ struct batadv_bcast_packet { #pragma pack() -struct batadv_vis_packet { - struct batadv_header header; - uint8_t vis_type; /* which type of vis-participant sent this? */ - __be32 seqno; /* sequence number */ - uint8_t entries; /* number of entries behind this struct */ - uint8_t reserved; - uint8_t vis_orig[ETH_ALEN]; /* originator reporting its neighbors */ - uint8_t target_orig[ETH_ALEN]; /* who should receive this packet */ - uint8_t sender_orig[ETH_ALEN]; /* who sent or forwarded this packet */ -}; - -struct batadv_tt_query_packet { - struct batadv_header header; - /* the flag field is a combination of: - * - TT_REQUEST or TT_RESPONSE - * - TT_FULL_TABLE - */ - uint8_t flags; - uint8_t dst[ETH_ALEN]; - uint8_t src[ETH_ALEN]; - /* the ttvn field is: - * if TT_REQUEST: ttvn that triggered the - * request - * if TT_RESPONSE: new ttvn for the src - * orig_node - */ - uint8_t ttvn; - /* tt_data field is: - * if TT_REQUEST: crc associated with the - * ttvn - * if TT_RESPONSE: table_size - */ - __be16 tt_data; -} __packed; - -struct batadv_roam_adv_packet { - struct batadv_header header; - uint8_t reserved; - uint8_t dst[ETH_ALEN]; - uint8_t src[ETH_ALEN]; - uint8_t client[ETH_ALEN]; -} __packed; - -struct batadv_tt_change { - uint8_t flags; - uint8_t addr[ETH_ALEN]; -} __packed; - /** * struct batadv_coded_packet - network coded packet * @header: common batman packet header and ttl of first included packet @@ -311,4 +349,96 @@ struct batadv_coded_packet { __be16 coded_len; }; +/** + * struct batadv_unicast_tvlv - generic unicast packet with tvlv payload + * @header: common batman packet header + * @reserved: reserved field (for packet alignment) + * @src: address of the source + * @dst: address of the destination + * @tvlv_len: length of tvlv data following the unicast tvlv header + * @align: 2 bytes to align the header to a 4 byte boundry + */ +struct batadv_unicast_tvlv_packet { + struct batadv_header header; + uint8_t reserved; + uint8_t dst[ETH_ALEN]; + uint8_t src[ETH_ALEN]; + __be16 tvlv_len; + uint16_t align; +}; + +/** + * struct batadv_tvlv_hdr - base tvlv header struct + * @type: tvlv container type (see batadv_tvlv_type) + * @version: tvlv container version + * @len: tvlv container length + */ +struct batadv_tvlv_hdr { + uint8_t type; + uint8_t version; + __be16 len; +}; + +/** + * struct batadv_tvlv_gateway_data - gateway data propagated through gw tvlv + * container + * @bandwidth_down: advertised uplink download bandwidth + * @bandwidth_up: advertised uplink upload bandwidth + */ +struct batadv_tvlv_gateway_data { + __be32 bandwidth_down; + __be32 bandwidth_up; +}; + +/** + * struct batadv_tvlv_tt_data - tt data propagated through the tt tvlv container + * @flags: translation table flags (see batadv_tt_data_flags) + * @ttvn: translation table version number + * @vlan_num: number of announced VLANs. In the TVLV this struct is followed by + * one batadv_tvlv_tt_vlan_data object per announced vlan + */ +struct batadv_tvlv_tt_data { + uint8_t flags; + uint8_t ttvn; + __be16 num_vlan; +}; + +/** + * struct batadv_tvlv_tt_vlan_data - vlan specific tt data propagated through + * the tt tvlv container + * @crc: crc32 checksum of the entries belonging to this vlan + * @vid: vlan identifier + * @reserved: unused, useful for alignment purposes + */ +struct batadv_tvlv_tt_vlan_data { + __be32 crc; + __be16 vid; + uint16_t reserved; +}; + +/** + * struct batadv_tvlv_tt_change - translation table diff data + * @flags: status indicators concerning the non-mesh client (see + * batadv_tt_client_flags) + * @reserved: reserved field + * @addr: mac address of non-mesh client that triggered this tt change + * @vid: VLAN identifier + */ +struct batadv_tvlv_tt_change { + uint8_t flags; + uint8_t reserved; + uint8_t addr[ETH_ALEN]; + __be16 vid; +}; + +/** + * struct batadv_tvlv_roam_adv - roaming advertisement + * @client: mac address of roaming client + * @vid: VLAN identifier + */ +struct batadv_tvlv_roam_adv { + uint8_t client[ETH_ALEN]; + __be16 vid; +}; + #endif /* _NET_BATMAN_ADV_PACKET_H_ */ diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 0439395d7ba5f3f11eea51ebc53e9760ff175111..d4114d775ad61f659b59437c67aa7054d9a88f92 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -25,11 +25,12 @@ #include "icmp_socket.h" #include "translation-table.h" #include "originator.h" -#include "vis.h" -#include "unicast.h" #include "bridge_loop_avoidance.h" #include "distributed-arp-table.h" #include "network-coding.h" +#include "fragmentation.h" + +#include static int batadv_route_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); @@ -46,7 +47,7 @@ static void _batadv_update_route(struct batadv_priv *bat_priv, if ((curr_router) && (!neigh_node)) { batadv_dbg(BATADV_DBG_ROUTES, bat_priv, "Deleting route towards: %pM\n", orig_node->orig); - batadv_tt_global_del_orig(bat_priv, orig_node, + batadv_tt_global_del_orig(bat_priv, orig_node, -1, "Deleted route towards originator"); /* route added */ @@ -114,9 +115,19 @@ out: return; } -void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node, +/** + * batadv_bonding_candidate_add - consider a new link for bonding mode towards + * the given originator + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: the target node + * @neigh_node: the neighbor representing the new link to consider for bonding + * mode + */ +void batadv_bonding_candidate_add(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, struct batadv_neigh_node *neigh_node) { + struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; struct batadv_neigh_node *tmp_neigh_node, *router = NULL; uint8_t interference_candidate = 0; @@ -131,8 +142,9 @@ void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node, if (!router) goto candidate_del; + /* ... and is good enough to be considered */ - if (neigh_node->tq_avg < router->tq_avg - BATADV_BONDING_TQ_THRESHOLD) + if (bao->bat_neigh_is_equiv_or_better(neigh_node, router)) goto candidate_del; /* check if we have another candidate with the same mac address or @@ -248,46 +260,65 @@ bool batadv_check_management_packet(struct sk_buff *skb, return true; } +/** + * batadv_recv_my_icmp_packet - receive an icmp packet locally + * @bat_priv: the bat priv with all the soft interface information + * @skb: icmp packet to process + * + * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP + * otherwise. + */ static int batadv_recv_my_icmp_packet(struct batadv_priv *bat_priv, - struct sk_buff *skb, size_t icmp_len) + struct sk_buff *skb) { struct batadv_hard_iface *primary_if = NULL; struct batadv_orig_node *orig_node = NULL; - struct batadv_icmp_packet_rr *icmp_packet; - int ret = NET_RX_DROP; + struct batadv_icmp_header *icmph; + int res, ret = NET_RX_DROP; - icmp_packet = (struct batadv_icmp_packet_rr *)skb->data; + icmph = (struct batadv_icmp_header *)skb->data; - /* add data to device queue */ - if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) { - batadv_socket_receive_packet(icmp_packet, icmp_len); - goto out; - } + switch (icmph->msg_type) { + case BATADV_ECHO_REPLY: + case BATADV_DESTINATION_UNREACHABLE: + case BATADV_TTL_EXCEEDED: + /* receive the packet */ + if (skb_linearize(skb) < 0) + break; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - goto out; + batadv_socket_receive_packet(icmph, skb->len); + break; + case BATADV_ECHO_REQUEST: + /* answer echo request (ping) */ + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; - /* answer echo request (ping) */ - /* get routing information */ - orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->orig); - if (!orig_node) - goto out; + /* get routing information */ + orig_node = batadv_orig_hash_find(bat_priv, icmph->orig); + if (!orig_node) + goto out; - /* create a copy of the skb, if needed, to modify it. */ - if (skb_cow(skb, ETH_HLEN) < 0) - goto out; + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, ETH_HLEN) < 0) + goto out; - icmp_packet = (struct batadv_icmp_packet_rr *)skb->data; + icmph = (struct batadv_icmp_header *)skb->data; - memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); - memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN); - icmp_packet->msg_type = BATADV_ECHO_REPLY; - icmp_packet->header.ttl = BATADV_TTL; + memcpy(icmph->dst, icmph->orig, ETH_ALEN); + memcpy(icmph->orig, primary_if->net_dev->dev_addr, ETH_ALEN); + icmph->msg_type = BATADV_ECHO_REPLY; + icmph->header.ttl = BATADV_TTL; - if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) - ret = NET_RX_SUCCESS; + res = batadv_send_skb_to_orig(skb, orig_node, NULL); + if (res != NET_XMIT_DROP) + ret = NET_RX_SUCCESS; + break; + default: + /* drop unknown type */ + goto out; + } out: if (primary_if) batadv_hardif_free_ref(primary_if); @@ -307,9 +338,9 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, icmp_packet = (struct batadv_icmp_packet *)skb->data; /* send TTL exceeded if packet is an echo request (traceroute) */ - if (icmp_packet->msg_type != BATADV_ECHO_REQUEST) { + if (icmp_packet->icmph.msg_type != BATADV_ECHO_REQUEST) { pr_debug("Warning - can't forward icmp packet from %pM to %pM: ttl exceeded\n", - icmp_packet->orig, icmp_packet->dst); + icmp_packet->icmph.orig, icmp_packet->icmph.dst); goto out; } @@ -318,7 +349,7 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, goto out; /* get routing information */ - orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->orig); + orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->icmph.orig); if (!orig_node) goto out; @@ -328,10 +359,11 @@ static int batadv_recv_icmp_ttl_exceeded(struct batadv_priv *bat_priv, icmp_packet = (struct batadv_icmp_packet *)skb->data; - memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); - memcpy(icmp_packet->orig, primary_if->net_dev->dev_addr, ETH_ALEN); - icmp_packet->msg_type = BATADV_TTL_EXCEEDED; - icmp_packet->header.ttl = BATADV_TTL; + memcpy(icmp_packet->icmph.dst, icmp_packet->icmph.orig, ETH_ALEN); + memcpy(icmp_packet->icmph.orig, primary_if->net_dev->dev_addr, + ETH_ALEN); + icmp_packet->icmph.msg_type = BATADV_TTL_EXCEEDED; + icmp_packet->icmph.header.ttl = BATADV_TTL; if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) ret = NET_RX_SUCCESS; @@ -349,16 +381,13 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) { struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct batadv_icmp_packet_rr *icmp_packet; + struct batadv_icmp_header *icmph; + struct batadv_icmp_packet_rr *icmp_packet_rr; struct ethhdr *ethhdr; struct batadv_orig_node *orig_node = NULL; - int hdr_size = sizeof(struct batadv_icmp_packet); + int hdr_size = sizeof(struct batadv_icmp_header); int ret = NET_RX_DROP; - /* we truncate all incoming icmp packets if they don't match our size */ - if (skb->len >= sizeof(struct batadv_icmp_packet_rr)) - hdr_size = sizeof(struct batadv_icmp_packet_rr); - /* drop packet if it has not necessary minimum size */ if (unlikely(!pskb_may_pull(skb, hdr_size))) goto out; @@ -377,26 +406,39 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest)) goto out; - icmp_packet = (struct batadv_icmp_packet_rr *)skb->data; + icmph = (struct batadv_icmp_header *)skb->data; /* add record route information if not full */ - if ((hdr_size == sizeof(struct batadv_icmp_packet_rr)) && - (icmp_packet->rr_cur < BATADV_RR_LEN)) { - memcpy(&(icmp_packet->rr[icmp_packet->rr_cur]), + if ((icmph->msg_type == BATADV_ECHO_REPLY || + icmph->msg_type == BATADV_ECHO_REQUEST) && + (skb->len >= sizeof(struct batadv_icmp_packet_rr))) { + if (skb_linearize(skb) < 0) + goto out; + + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, ETH_HLEN) < 0) + goto out; + + icmph = (struct batadv_icmp_header *)skb->data; + icmp_packet_rr = (struct batadv_icmp_packet_rr *)icmph; + if (icmp_packet_rr->rr_cur >= BATADV_RR_LEN) + goto out; + + memcpy(&(icmp_packet_rr->rr[icmp_packet_rr->rr_cur]), ethhdr->h_dest, ETH_ALEN); - icmp_packet->rr_cur++; + icmp_packet_rr->rr_cur++; } /* packet for me */ - if (batadv_is_my_mac(bat_priv, icmp_packet->dst)) - return batadv_recv_my_icmp_packet(bat_priv, skb, hdr_size); + if (batadv_is_my_mac(bat_priv, icmph->dst)) + return batadv_recv_my_icmp_packet(bat_priv, skb); /* TTL exceeded */ - if (icmp_packet->header.ttl < 2) + if (icmph->header.ttl < 2) return batadv_recv_icmp_ttl_exceeded(bat_priv, skb); /* get routing information */ - orig_node = batadv_orig_hash_find(bat_priv, icmp_packet->dst); + orig_node = batadv_orig_hash_find(bat_priv, icmph->dst); if (!orig_node) goto out; @@ -404,10 +446,10 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, if (skb_cow(skb, ETH_HLEN) < 0) goto out; - icmp_packet = (struct batadv_icmp_packet_rr *)skb->data; + icmph = (struct batadv_icmp_header *)skb->data; /* decrement ttl */ - icmp_packet->header.ttl--; + icmph->header.ttl--; /* route it */ if (batadv_send_skb_to_orig(skb, orig_node, recv_if) != NET_XMIT_DROP) @@ -474,18 +516,25 @@ out: return router; } -/* Interface Alternating: Use the best of the - * remaining candidates which are not using - * this interface. +/** + * batadv_find_ifalter_router - find the best of the remaining candidates which + * are not using this interface + * @bat_priv: the bat priv with all the soft interface information + * @primary_orig: the destination + * @recv_if: the interface that the router returned by this function has to not + * use * - * Increases the returned router's refcount + * Returns the best candidate towards primary_orig that is not using recv_if. + * Increases the returned neighbor's refcount */ static struct batadv_neigh_node * -batadv_find_ifalter_router(struct batadv_orig_node *primary_orig, +batadv_find_ifalter_router(struct batadv_priv *bat_priv, + struct batadv_orig_node *primary_orig, const struct batadv_hard_iface *recv_if) { - struct batadv_neigh_node *tmp_neigh_node; struct batadv_neigh_node *router = NULL, *first_candidate = NULL; + struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; + struct batadv_neigh_node *tmp_neigh_node; rcu_read_lock(); list_for_each_entry_rcu(tmp_neigh_node, &primary_orig->bond_list, @@ -497,7 +546,7 @@ batadv_find_ifalter_router(struct batadv_orig_node *primary_orig, if (tmp_neigh_node->if_incoming == recv_if) continue; - if (router && tmp_neigh_node->tq_avg <= router->tq_avg) + if (router && bao->bat_neigh_cmp(tmp_neigh_node, router)) continue; if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) @@ -557,126 +606,6 @@ static int batadv_check_unicast_packet(struct batadv_priv *bat_priv, return 0; } -int batadv_recv_tt_query(struct sk_buff *skb, struct batadv_hard_iface *recv_if) -{ - struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct batadv_tt_query_packet *tt_query; - uint16_t tt_size; - int hdr_size = sizeof(*tt_query); - char tt_flag; - size_t packet_size; - - if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0) - return NET_RX_DROP; - - /* I could need to modify it */ - if (skb_cow(skb, sizeof(struct batadv_tt_query_packet)) < 0) - goto out; - - tt_query = (struct batadv_tt_query_packet *)skb->data; - - switch (tt_query->flags & BATADV_TT_QUERY_TYPE_MASK) { - case BATADV_TT_REQUEST: - batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_RX); - - /* If we cannot provide an answer the tt_request is - * forwarded - */ - if (!batadv_send_tt_response(bat_priv, tt_query)) { - if (tt_query->flags & BATADV_TT_FULL_TABLE) - tt_flag = 'F'; - else - tt_flag = '.'; - - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Routing TT_REQUEST to %pM [%c]\n", - tt_query->dst, - tt_flag); - return batadv_route_unicast_packet(skb, recv_if); - } - break; - case BATADV_TT_RESPONSE: - batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_RX); - - if (batadv_is_my_mac(bat_priv, tt_query->dst)) { - /* packet needs to be linearized to access the TT - * changes - */ - if (skb_linearize(skb) < 0) - goto out; - /* skb_linearize() possibly changed skb->data */ - tt_query = (struct batadv_tt_query_packet *)skb->data; - - tt_size = batadv_tt_len(ntohs(tt_query->tt_data)); - - /* Ensure we have all the claimed data */ - packet_size = sizeof(struct batadv_tt_query_packet); - packet_size += tt_size; - if (unlikely(skb_headlen(skb) < packet_size)) - goto out; - - batadv_handle_tt_response(bat_priv, tt_query); - } else { - if (tt_query->flags & BATADV_TT_FULL_TABLE) - tt_flag = 'F'; - else - tt_flag = '.'; - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Routing TT_RESPONSE to %pM [%c]\n", - tt_query->dst, - tt_flag); - return batadv_route_unicast_packet(skb, recv_if); - } - break; - } - -out: - /* returning NET_RX_DROP will make the caller function kfree the skb */ - return NET_RX_DROP; -} - -int batadv_recv_roam_adv(struct sk_buff *skb, struct batadv_hard_iface *recv_if) -{ - struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct batadv_roam_adv_packet *roam_adv_packet; - struct batadv_orig_node *orig_node; - - if (batadv_check_unicast_packet(bat_priv, skb, - sizeof(*roam_adv_packet)) < 0) - goto out; - - batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX); - - roam_adv_packet = (struct batadv_roam_adv_packet *)skb->data; - - if (!batadv_is_my_mac(bat_priv, roam_adv_packet->dst)) - return batadv_route_unicast_packet(skb, recv_if); - - /* check if it is a backbone gateway. we don't accept - * roaming advertisement from it, as it has the same - * entries as we have. - */ - if (batadv_bla_is_backbone_gw_orig(bat_priv, roam_adv_packet->src)) - goto out; - - orig_node = batadv_orig_hash_find(bat_priv, roam_adv_packet->src); - if (!orig_node) - goto out; - - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Received ROAMING_ADV from %pM (client %pM)\n", - roam_adv_packet->src, roam_adv_packet->client); - - batadv_tt_global_add(bat_priv, orig_node, roam_adv_packet->client, - BATADV_TT_CLIENT_ROAM, - atomic_read(&orig_node->last_ttvn) + 1); - - batadv_orig_node_free_ref(orig_node); -out: - /* returning NET_RX_DROP will make the caller function kfree the skb */ - return NET_RX_DROP; -} - /* find a suitable router for this originator, and use * bonding if possible. increases the found neighbors * refcount. @@ -751,7 +680,8 @@ batadv_find_router(struct batadv_priv *bat_priv, if (bonding_enabled) router = batadv_find_bond_router(primary_orig_node, recv_if); else - router = batadv_find_ifalter_router(primary_orig_node, recv_if); + router = batadv_find_ifalter_router(bat_priv, primary_orig_node, + recv_if); return_router: if (router && router->if_incoming->if_status != BATADV_IF_ACTIVE) @@ -772,11 +702,9 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, { struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct batadv_orig_node *orig_node = NULL; - struct batadv_neigh_node *neigh_node = NULL; struct batadv_unicast_packet *unicast_packet; struct ethhdr *ethhdr = eth_hdr(skb); int res, hdr_len, ret = NET_RX_DROP; - struct sk_buff *new_skb; unicast_packet = (struct batadv_unicast_packet *)skb->data; @@ -793,46 +721,12 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, if (!orig_node) goto out; - /* find_router() increases neigh_nodes refcount if found. */ - neigh_node = batadv_find_router(bat_priv, orig_node, recv_if); - - if (!neigh_node) - goto out; - /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, ETH_HLEN) < 0) goto out; - unicast_packet = (struct batadv_unicast_packet *)skb->data; - - if (unicast_packet->header.packet_type == BATADV_UNICAST && - atomic_read(&bat_priv->fragmentation) && - skb->len > neigh_node->if_incoming->net_dev->mtu) { - ret = batadv_frag_send_skb(skb, bat_priv, - neigh_node->if_incoming, - neigh_node->addr); - goto out; - } - - if (unicast_packet->header.packet_type == BATADV_UNICAST_FRAG && - batadv_frag_can_reassemble(skb, - neigh_node->if_incoming->net_dev->mtu)) { - ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb); - - if (ret == NET_RX_DROP) - goto out; - - /* packet was buffered for late merge */ - if (!new_skb) { - ret = NET_RX_SUCCESS; - goto out; - } - - skb = new_skb; - unicast_packet = (struct batadv_unicast_packet *)skb->data; - } - /* decrement ttl */ + unicast_packet = (struct batadv_unicast_packet *)skb->data; unicast_packet->header.ttl--; switch (unicast_packet->header.packet_type) { @@ -867,8 +761,6 @@ static int batadv_route_unicast_packet(struct sk_buff *skb, } out: - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); if (orig_node) batadv_orig_node_free_ref(orig_node); return ret; @@ -879,6 +771,7 @@ out: * @bat_priv: the bat priv with all the soft interface information * @unicast_packet: the unicast header to be updated * @dst_addr: the payload destination + * @vid: VLAN identifier * * Search the translation table for dst_addr and update the unicast header with * the new corresponding information (originator address where the destination @@ -889,21 +782,22 @@ out: static bool batadv_reroute_unicast_packet(struct batadv_priv *bat_priv, struct batadv_unicast_packet *unicast_packet, - uint8_t *dst_addr) + uint8_t *dst_addr, unsigned short vid) { struct batadv_orig_node *orig_node = NULL; struct batadv_hard_iface *primary_if = NULL; bool ret = false; uint8_t *orig_addr, orig_ttvn; - if (batadv_is_my_client(bat_priv, dst_addr)) { + if (batadv_is_my_client(bat_priv, dst_addr, vid)) { primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) goto out; orig_addr = primary_if->net_dev->dev_addr; orig_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); } else { - orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr); + orig_node = batadv_transtable_search(bat_priv, NULL, dst_addr, + vid); if (!orig_node) goto out; @@ -930,11 +824,12 @@ out: static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, struct sk_buff *skb, int hdr_len) { - uint8_t curr_ttvn, old_ttvn; + struct batadv_unicast_packet *unicast_packet; + struct batadv_hard_iface *primary_if; struct batadv_orig_node *orig_node; + uint8_t curr_ttvn, old_ttvn; struct ethhdr *ethhdr; - struct batadv_hard_iface *primary_if; - struct batadv_unicast_packet *unicast_packet; + unsigned short vid; int is_old_ttvn; /* check if there is enough data before accessing it */ @@ -946,6 +841,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, return 0; unicast_packet = (struct batadv_unicast_packet *)skb->data; + vid = batadv_get_vid(skb, hdr_len); ethhdr = (struct ethhdr *)(skb->data + hdr_len); /* check if the destination client was served by this node and it is now @@ -953,9 +849,9 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, * message and that it knows the new destination in the mesh to re-route * the packet to */ - if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest)) { + if (batadv_tt_local_client_is_roaming(bat_priv, ethhdr->h_dest, vid)) { if (batadv_reroute_unicast_packet(bat_priv, unicast_packet, - ethhdr->h_dest)) + ethhdr->h_dest, vid)) net_ratelimited_function(batadv_dbg, BATADV_DBG_TT, bat_priv, "Rerouting unicast packet to %pM (dst=%pM): Local Roaming\n", @@ -1001,7 +897,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, * target host */ if (batadv_reroute_unicast_packet(bat_priv, unicast_packet, - ethhdr->h_dest)) { + ethhdr->h_dest, vid)) { net_ratelimited_function(batadv_dbg, BATADV_DBG_TT, bat_priv, "Rerouting unicast packet to %pM (dst=%pM): TTVN mismatch old_ttvn=%u new_ttvn=%u\n", unicast_packet->dest, ethhdr->h_dest, @@ -1013,7 +909,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, * currently served by this node or there is no destination at all and * it is possible to drop the packet */ - if (!batadv_is_my_client(bat_priv, ethhdr->h_dest)) + if (!batadv_is_my_client(bat_priv, ethhdr->h_dest, vid)) return 0; /* update the header in order to let the packet be delivered to this @@ -1032,6 +928,34 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv, return 1; } +/** + * batadv_recv_unhandled_unicast_packet - receive and process packets which + * are in the unicast number space but not yet known to the implementation + * @skb: unicast tvlv packet to process + * @recv_if: pointer to interface this packet was received on + * + * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP + * otherwise. + */ +int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) +{ + struct batadv_unicast_packet *unicast_packet; + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + int check, hdr_size = sizeof(*unicast_packet); + + check = batadv_check_unicast_packet(bat_priv, skb, hdr_size); + if (check < 0) + return NET_RX_DROP; + + /* we don't know about this type, drop it. */ + unicast_packet = (struct batadv_unicast_packet *)skb->data; + if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) + return NET_RX_DROP; + + return batadv_route_unicast_packet(skb, recv_if); +} + int batadv_recv_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) { @@ -1094,51 +1018,112 @@ rx_success: return batadv_route_unicast_packet(skb, recv_if); } -int batadv_recv_ucast_frag_packet(struct sk_buff *skb, - struct batadv_hard_iface *recv_if) +/** + * batadv_recv_unicast_tvlv - receive and process unicast tvlv packets + * @skb: unicast tvlv packet to process + * @recv_if: pointer to interface this packet was received on + * @dst_addr: the payload destination + * + * Returns NET_RX_SUCCESS if the packet has been consumed or NET_RX_DROP + * otherwise. + */ +int batadv_recv_unicast_tvlv(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) { struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct batadv_unicast_frag_packet *unicast_packet; - int hdr_size = sizeof(*unicast_packet); - struct sk_buff *new_skb = NULL; - int ret; + struct batadv_unicast_tvlv_packet *unicast_tvlv_packet; + unsigned char *tvlv_buff; + uint16_t tvlv_buff_len; + int hdr_size = sizeof(*unicast_tvlv_packet); + int ret = NET_RX_DROP; if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0) return NET_RX_DROP; - if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size)) + /* the header is likely to be modified while forwarding */ + if (skb_cow(skb, hdr_size) < 0) return NET_RX_DROP; - unicast_packet = (struct batadv_unicast_frag_packet *)skb->data; + /* packet needs to be linearized to access the tvlv content */ + if (skb_linearize(skb) < 0) + return NET_RX_DROP; - /* packet for me */ - if (batadv_is_my_mac(bat_priv, unicast_packet->dest)) { - ret = batadv_frag_reassemble_skb(skb, bat_priv, &new_skb); + unicast_tvlv_packet = (struct batadv_unicast_tvlv_packet *)skb->data; - if (ret == NET_RX_DROP) - return NET_RX_DROP; + tvlv_buff = (unsigned char *)(skb->data + hdr_size); + tvlv_buff_len = ntohs(unicast_tvlv_packet->tvlv_len); - /* packet was buffered for late merge */ - if (!new_skb) - return NET_RX_SUCCESS; + if (tvlv_buff_len > skb->len - hdr_size) + return NET_RX_DROP; - if (batadv_dat_snoop_incoming_arp_request(bat_priv, new_skb, - hdr_size)) - goto rx_success; - if (batadv_dat_snoop_incoming_arp_reply(bat_priv, new_skb, - hdr_size)) - goto rx_success; + ret = batadv_tvlv_containers_process(bat_priv, false, NULL, + unicast_tvlv_packet->src, + unicast_tvlv_packet->dst, + tvlv_buff, tvlv_buff_len); - batadv_interface_rx(recv_if->soft_iface, new_skb, recv_if, - sizeof(struct batadv_unicast_packet), NULL); + if (ret != NET_RX_SUCCESS) + ret = batadv_route_unicast_packet(skb, recv_if); -rx_success: - return NET_RX_SUCCESS; + return ret; +} + +/** + * batadv_recv_frag_packet - process received fragment + * @skb: the received fragment + * @recv_if: interface that the skb is received on + * + * This function does one of the three following things: 1) Forward fragment, if + * the assembled packet will exceed our MTU; 2) Buffer fragment, if we till + * lack further fragments; 3) Merge fragments, if we have all needed parts. + * + * Return NET_RX_DROP if the skb is not consumed, NET_RX_SUCCESS otherwise. + */ +int batadv_recv_frag_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if) +{ + struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); + struct batadv_orig_node *orig_node_src = NULL; + struct batadv_frag_packet *frag_packet; + int ret = NET_RX_DROP; + + if (batadv_check_unicast_packet(bat_priv, skb, + sizeof(*frag_packet)) < 0) + goto out; + + frag_packet = (struct batadv_frag_packet *)skb->data; + orig_node_src = batadv_orig_hash_find(bat_priv, frag_packet->orig); + if (!orig_node_src) + goto out; + + /* Route the fragment if it is not for us and too big to be merged. */ + if (!batadv_is_my_mac(bat_priv, frag_packet->dest) && + batadv_frag_skb_fwd(skb, recv_if, orig_node_src)) { + ret = NET_RX_SUCCESS; + goto out; } - return batadv_route_unicast_packet(skb, recv_if); -} + batadv_inc_counter(bat_priv, BATADV_CNT_FRAG_RX); + batadv_add_counter(bat_priv, BATADV_CNT_FRAG_RX_BYTES, skb->len); + + /* Add fragment to buffer and merge if possible. */ + if (!batadv_frag_skb_buffer(&skb, orig_node_src)) + goto out; + /* Deliver merged packet to the appropriate handler, if it was + * merged + */ + if (skb) + batadv_batman_skb_recv(skb, recv_if->net_dev, + &recv_if->batman_adv_ptype, NULL); + + ret = NET_RX_SUCCESS; + +out: + if (orig_node_src) + batadv_orig_node_free_ref(orig_node_src); + + return ret; +} int batadv_recv_bcast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if) @@ -1240,53 +1225,3 @@ out: batadv_orig_node_free_ref(orig_node); return ret; } - -int batadv_recv_vis_packet(struct sk_buff *skb, - struct batadv_hard_iface *recv_if) -{ - struct batadv_vis_packet *vis_packet; - struct ethhdr *ethhdr; - struct batadv_priv *bat_priv = netdev_priv(recv_if->soft_iface); - int hdr_size = sizeof(*vis_packet); - - /* keep skb linear */ - if (skb_linearize(skb) < 0) - return NET_RX_DROP; - - if (unlikely(!pskb_may_pull(skb, hdr_size))) - return NET_RX_DROP; - - vis_packet = (struct batadv_vis_packet *)skb->data; - ethhdr = eth_hdr(skb); - - /* not for me */ - if (!batadv_is_my_mac(bat_priv, ethhdr->h_dest)) - return NET_RX_DROP; - - /* ignore own packets */ - if (batadv_is_my_mac(bat_priv, vis_packet->vis_orig)) - return NET_RX_DROP; - - if (batadv_is_my_mac(bat_priv, vis_packet->sender_orig)) - return NET_RX_DROP; - - switch (vis_packet->vis_type) { - case BATADV_VIS_TYPE_SERVER_SYNC: - batadv_receive_server_sync_packet(bat_priv, vis_packet, - skb_headlen(skb)); - break; - - case BATADV_VIS_TYPE_CLIENT_UPDATE: - batadv_receive_client_update_packet(bat_priv, vis_packet, - skb_headlen(skb)); - break; - - default: /* ignore unknown packet */ - break; - } - - /* We take a copy of the data in the packet, so we should - * always free the skbuf. - */ - return NET_RX_DROP; -} diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index 72a29bde201022300194422a9caff389ab409731..19544ddb81b5abf417b69484c096aff239989a10 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@ -30,23 +30,26 @@ int batadv_recv_icmp_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); int batadv_recv_unicast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); -int batadv_recv_ucast_frag_packet(struct sk_buff *skb, - struct batadv_hard_iface *recv_if); +int batadv_recv_frag_packet(struct sk_buff *skb, + struct batadv_hard_iface *iface); int batadv_recv_bcast_packet(struct sk_buff *skb, struct batadv_hard_iface *recv_if); -int batadv_recv_vis_packet(struct sk_buff *skb, - struct batadv_hard_iface *recv_if); int batadv_recv_tt_query(struct sk_buff *skb, struct batadv_hard_iface *recv_if); int batadv_recv_roam_adv(struct sk_buff *skb, struct batadv_hard_iface *recv_if); +int batadv_recv_unicast_tvlv(struct sk_buff *skb, + struct batadv_hard_iface *recv_if); +int batadv_recv_unhandled_unicast_packet(struct sk_buff *skb, + struct batadv_hard_iface *recv_if); struct batadv_neigh_node * batadv_find_router(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const struct batadv_hard_iface *recv_if); void batadv_bonding_candidate_del(struct batadv_orig_node *orig_node, struct batadv_neigh_node *neigh_node); -void batadv_bonding_candidate_add(struct batadv_orig_node *orig_node, +void batadv_bonding_candidate_add(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, struct batadv_neigh_node *neigh_node); void batadv_bonding_save_primary(const struct batadv_orig_node *orig_node, struct batadv_orig_node *orig_neigh_node, diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 0266edd0fa7f7acb0be56f896de95ba7775108f6..c83be5ebaa285dffe64d051850e87f2fca02624b 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -24,12 +24,11 @@ #include "translation-table.h" #include "soft-interface.h" #include "hard-interface.h" -#include "vis.h" #include "gateway_common.h" +#include "gateway_client.h" #include "originator.h" #include "network-coding.h" - -#include +#include "fragmentation.h" static void batadv_send_outstanding_bcast_packet(struct work_struct *work); @@ -64,10 +63,10 @@ int batadv_send_skb_packet(struct sk_buff *skb, ethhdr = eth_hdr(skb); memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN); memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); - ethhdr->h_proto = __constant_htons(ETH_P_BATMAN); + ethhdr->h_proto = htons(ETH_P_BATMAN); skb_set_network_header(skb, ETH_HLEN); - skb->protocol = __constant_htons(ETH_P_BATMAN); + skb->protocol = htons(ETH_P_BATMAN); skb->dev = hard_iface->net_dev; @@ -109,7 +108,19 @@ int batadv_send_skb_to_orig(struct sk_buff *skb, /* batadv_find_router() increases neigh_nodes refcount if found. */ neigh_node = batadv_find_router(bat_priv, orig_node, recv_if); if (!neigh_node) - return ret; + goto out; + + /* Check if the skb is too large to send in one piece and fragment + * it if needed. + */ + if (atomic_read(&bat_priv->fragmentation) && + skb->len > neigh_node->if_incoming->net_dev->mtu) { + /* Fragment and send packet. */ + if (batadv_frag_send_packet(skb, orig_node, neigh_node)) + ret = NET_XMIT_SUCCESS; + + goto out; + } /* try to network code the packet, if it is received on an interface * (i.e. being forwarded). If the packet originates from this node or if @@ -123,11 +134,225 @@ int batadv_send_skb_to_orig(struct sk_buff *skb, ret = NET_XMIT_SUCCESS; } - batadv_neigh_node_free_ref(neigh_node); +out: + if (neigh_node) + batadv_neigh_node_free_ref(neigh_node); + + return ret; +} + +/** + * batadv_send_skb_push_fill_unicast - extend the buffer and initialize the + * common fields for unicast packets + * @skb: the skb carrying the unicast header to initialize + * @hdr_size: amount of bytes to push at the beginning of the skb + * @orig_node: the destination node + * + * Returns false if the buffer extension was not possible or true otherwise. + */ +static bool +batadv_send_skb_push_fill_unicast(struct sk_buff *skb, int hdr_size, + struct batadv_orig_node *orig_node) +{ + struct batadv_unicast_packet *unicast_packet; + uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + + if (batadv_skb_head_push(skb, hdr_size) < 0) + return false; + + unicast_packet = (struct batadv_unicast_packet *)skb->data; + unicast_packet->header.version = BATADV_COMPAT_VERSION; + /* batman packet type: unicast */ + unicast_packet->header.packet_type = BATADV_UNICAST; + /* set unicast ttl */ + unicast_packet->header.ttl = BATADV_TTL; + /* copy the destination for faster routing */ + memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); + /* set the destination tt version number */ + unicast_packet->ttvn = ttvn; + + return true; +} + +/** + * batadv_send_skb_prepare_unicast - encapsulate an skb with a unicast header + * @skb: the skb containing the payload to encapsulate + * @orig_node: the destination node + * + * Returns false if the payload could not be encapsulated or true otherwise. + */ +static bool batadv_send_skb_prepare_unicast(struct sk_buff *skb, + struct batadv_orig_node *orig_node) +{ + size_t uni_size = sizeof(struct batadv_unicast_packet); + + return batadv_send_skb_push_fill_unicast(skb, uni_size, orig_node); +} + +/** + * batadv_send_skb_prepare_unicast_4addr - encapsulate an skb with a + * unicast 4addr header + * @bat_priv: the bat priv with all the soft interface information + * @skb: the skb containing the payload to encapsulate + * @orig_node: the destination node + * @packet_subtype: the unicast 4addr packet subtype to use + * + * Returns false if the payload could not be encapsulated or true otherwise. + */ +bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv, + struct sk_buff *skb, + struct batadv_orig_node *orig, + int packet_subtype) +{ + struct batadv_hard_iface *primary_if; + struct batadv_unicast_4addr_packet *uc_4addr_packet; + bool ret = false; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; + + /* Pull the header space and fill the unicast_packet substructure. + * We can do that because the first member of the uc_4addr_packet + * is of type struct unicast_packet + */ + if (!batadv_send_skb_push_fill_unicast(skb, sizeof(*uc_4addr_packet), + orig)) + goto out; + + uc_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; + uc_4addr_packet->u.header.packet_type = BATADV_UNICAST_4ADDR; + memcpy(uc_4addr_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN); + uc_4addr_packet->subtype = packet_subtype; + uc_4addr_packet->reserved = 0; + + ret = true; +out: + if (primary_if) + batadv_hardif_free_ref(primary_if); + return ret; +} + +/** + * batadv_send_skb_unicast - encapsulate and send an skb via unicast + * @bat_priv: the bat priv with all the soft interface information + * @skb: payload to send + * @packet_type: the batman unicast packet type to use + * @packet_subtype: the unicast 4addr packet subtype (only relevant for unicast + * 4addr packets) + * @orig_node: the originator to send the packet to + * @vid: the vid to be used to search the translation table + * + * Wrap the given skb into a batman-adv unicast or unicast-4addr header + * depending on whether BATADV_UNICAST or BATADV_UNICAST_4ADDR was supplied + * as packet_type. Then send this frame to the given orig_node and release a + * reference to this orig_node. + * + * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise. + */ +static int batadv_send_skb_unicast(struct batadv_priv *bat_priv, + struct sk_buff *skb, int packet_type, + int packet_subtype, + struct batadv_orig_node *orig_node, + unsigned short vid) +{ + struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct batadv_unicast_packet *unicast_packet; + int ret = NET_XMIT_DROP; + + if (!orig_node) + goto out; + + switch (packet_type) { + case BATADV_UNICAST: + if (!batadv_send_skb_prepare_unicast(skb, orig_node)) + goto out; + break; + case BATADV_UNICAST_4ADDR: + if (!batadv_send_skb_prepare_unicast_4addr(bat_priv, skb, + orig_node, + packet_subtype)) + goto out; + break; + default: + /* this function supports UNICAST and UNICAST_4ADDR only. It + * should never be invoked with any other packet type + */ + goto out; + } + + unicast_packet = (struct batadv_unicast_packet *)skb->data; + + /* inform the destination node that we are still missing a correct route + * for this client. The destination will receive this packet and will + * try to reroute it because the ttvn contained in the header is less + * than the current one + */ + if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest, vid)) + unicast_packet->ttvn = unicast_packet->ttvn - 1; + if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) + ret = NET_XMIT_SUCCESS; + +out: + if (orig_node) + batadv_orig_node_free_ref(orig_node); + if (ret == NET_XMIT_DROP) + kfree_skb(skb); return ret; } +/** + * batadv_send_skb_via_tt_generic - send an skb via TT lookup + * @bat_priv: the bat priv with all the soft interface information + * @skb: payload to send + * @packet_type: the batman unicast packet type to use + * @packet_subtype: the unicast 4addr packet subtype (only relevant for unicast + * 4addr packets) + * @vid: the vid to be used to search the translation table + * + * Look up the recipient node for the destination address in the ethernet + * header via the translation table. Wrap the given skb into a batman-adv + * unicast or unicast-4addr header depending on whether BATADV_UNICAST or + * BATADV_UNICAST_4ADDR was supplied as packet_type. Then send this frame + * to the according destination node. + * + * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise. + */ +int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv, + struct sk_buff *skb, int packet_type, + int packet_subtype, unsigned short vid) +{ + struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct batadv_orig_node *orig_node; + + orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source, + ethhdr->h_dest, vid); + return batadv_send_skb_unicast(bat_priv, skb, packet_type, + packet_subtype, orig_node, vid); +} + +/** + * batadv_send_skb_via_gw - send an skb via gateway lookup + * @bat_priv: the bat priv with all the soft interface information + * @skb: payload to send + * @vid: the vid to be used to search the translation table + * + * Look up the currently selected gateway. Wrap the given skb into a batman-adv + * unicast header and send this frame to this gateway node. + * + * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise. + */ +int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb, + unsigned short vid) +{ + struct batadv_orig_node *orig_node; + + orig_node = batadv_gw_get_selected_orig(bat_priv); + return batadv_send_skb_unicast(bat_priv, skb, BATADV_UNICAST, 0, + orig_node, vid); +} + void batadv_schedule_bat_ogm(struct batadv_hard_iface *hard_iface) { struct batadv_priv *bat_priv = netdev_priv(hard_iface->soft_iface); diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index e7b17880fca4f46ef77783034e58ae1eeb00b641..aa2e2537a739297f76afa54b30d87d69976d35d2 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -34,5 +34,58 @@ void batadv_send_outstanding_bat_ogm_packet(struct work_struct *work); void batadv_purge_outstanding_packets(struct batadv_priv *bat_priv, const struct batadv_hard_iface *hard_iface); +bool batadv_send_skb_prepare_unicast_4addr(struct batadv_priv *bat_priv, + struct sk_buff *skb, + struct batadv_orig_node *orig_node, + int packet_subtype); +int batadv_send_skb_via_tt_generic(struct batadv_priv *bat_priv, + struct sk_buff *skb, int packet_type, + int packet_subtype, unsigned short vid); +int batadv_send_skb_via_gw(struct batadv_priv *bat_priv, struct sk_buff *skb, + unsigned short vid); + +/** + * batadv_send_skb_via_tt - send an skb via TT lookup + * @bat_priv: the bat priv with all the soft interface information + * @skb: the payload to send + * @vid: the vid to be used to search the translation table + * + * Look up the recipient node for the destination address in the ethernet + * header via the translation table. Wrap the given skb into a batman-adv + * unicast header. Then send this frame to the according destination node. + * + * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise. + */ +static inline int batadv_send_skb_via_tt(struct batadv_priv *bat_priv, + struct sk_buff *skb, + unsigned short vid) +{ + return batadv_send_skb_via_tt_generic(bat_priv, skb, BATADV_UNICAST, 0, + vid); +} + +/** + * batadv_send_skb_via_tt_4addr - send an skb via TT lookup + * @bat_priv: the bat priv with all the soft interface information + * @skb: the payload to send + * @packet_subtype: the unicast 4addr packet subtype to use + * @vid: the vid to be used to search the translation table + * + * Look up the recipient node for the destination address in the ethernet + * header via the translation table. Wrap the given skb into a batman-adv + * unicast-4addr header. Then send this frame to the according destination + * node. + * + * Returns NET_XMIT_DROP in case of error or NET_XMIT_SUCCESS otherwise. + */ +static inline int batadv_send_skb_via_tt_4addr(struct batadv_priv *bat_priv, + struct sk_buff *skb, + int packet_subtype, + unsigned short vid) +{ + return batadv_send_skb_via_tt_generic(bat_priv, skb, + BATADV_UNICAST_4ADDR, + packet_subtype, vid); +} #endif /* _NET_BATMAN_ADV_SEND_H_ */ diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 813db4e646021dea4c089d53ea65504a63ff0d2d..36f050876f8260245a008de079cf0974fa97b76e 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -34,8 +34,6 @@ #include #include #include -#include -#include "unicast.h" #include "bridge_loop_avoidance.h" #include "network-coding.h" @@ -120,9 +118,10 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p) /* only modify transtable if it has been initialized before */ if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE) { - batadv_tt_local_remove(bat_priv, old_addr, + batadv_tt_local_remove(bat_priv, old_addr, BATADV_NO_FLAGS, "mac address changed", false); - batadv_tt_local_add(dev, addr->sa_data, BATADV_NULL_IFINDEX); + batadv_tt_local_add(dev, addr->sa_data, BATADV_NO_FLAGS, + BATADV_NULL_IFINDEX); } return 0; @@ -139,36 +138,48 @@ static int batadv_interface_change_mtu(struct net_device *dev, int new_mtu) return 0; } +/** + * batadv_interface_set_rx_mode - set the rx mode of a device + * @dev: registered network device to modify + * + * We do not actually need to set any rx filters for the virtual batman + * soft interface. However a dummy handler enables a user to set static + * multicast listeners for instance. + */ +static void batadv_interface_set_rx_mode(struct net_device *dev) +{ +} + static int batadv_interface_tx(struct sk_buff *skb, struct net_device *soft_iface) { - struct ethhdr *ethhdr = (struct ethhdr *)skb->data; + struct ethhdr *ethhdr; struct batadv_priv *bat_priv = netdev_priv(soft_iface); struct batadv_hard_iface *primary_if = NULL; struct batadv_bcast_packet *bcast_packet; - struct vlan_ethhdr *vhdr; - __be16 ethertype = __constant_htons(ETH_P_BATMAN); + __be16 ethertype = htons(ETH_P_BATMAN); static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00}; static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00, 0x00, 0x00}; + struct vlan_ethhdr *vhdr; unsigned int header_len = 0; int data_len = skb->len, ret; - unsigned short vid __maybe_unused = BATADV_NO_FLAGS; - bool do_bcast = false; - uint32_t seqno; unsigned long brd_delay = 1; + bool do_bcast = false, client_added; + unsigned short vid; + uint32_t seqno; if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE) goto dropped; soft_iface->trans_start = jiffies; + vid = batadv_get_vid(skb, 0); + ethhdr = (struct ethhdr *)skb->data; switch (ntohs(ethhdr->h_proto)) { case ETH_P_8021Q: vhdr = (struct vlan_ethhdr *)skb->data; - vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; - vid |= BATADV_VLAN_HAS_TAG; if (vhdr->h_vlan_encapsulated_proto != ethertype) break; @@ -185,8 +196,12 @@ static int batadv_interface_tx(struct sk_buff *skb, ethhdr = (struct ethhdr *)skb->data; /* Register the client MAC in the transtable */ - if (!is_multicast_ether_addr(ethhdr->h_source)) - batadv_tt_local_add(soft_iface, ethhdr->h_source, skb->skb_iif); + if (!is_multicast_ether_addr(ethhdr->h_source)) { + client_added = batadv_tt_local_add(soft_iface, ethhdr->h_source, + vid, skb->skb_iif); + if (!client_added) + goto dropped; + } /* don't accept stp packets. STP does not help in meshes. * better use the bridge loop avoidance ... @@ -286,8 +301,12 @@ static int batadv_interface_tx(struct sk_buff *skb, batadv_dat_snoop_outgoing_arp_reply(bat_priv, skb); - ret = batadv_unicast_send_skb(bat_priv, skb); - if (ret != 0) + if (is_multicast_ether_addr(ethhdr->h_dest)) + ret = batadv_send_skb_via_gw(bat_priv, skb, vid); + else + ret = batadv_send_skb_via_tt(bat_priv, skb, vid); + + if (ret == NET_XMIT_DROP) goto dropped_freed; } @@ -309,12 +328,12 @@ void batadv_interface_rx(struct net_device *soft_iface, struct sk_buff *skb, struct batadv_hard_iface *recv_if, int hdr_size, struct batadv_orig_node *orig_node) { + struct batadv_header *batadv_header = (struct batadv_header *)skb->data; struct batadv_priv *bat_priv = netdev_priv(soft_iface); - struct ethhdr *ethhdr; + __be16 ethertype = htons(ETH_P_BATMAN); struct vlan_ethhdr *vhdr; - struct batadv_header *batadv_header = (struct batadv_header *)skb->data; - unsigned short vid __maybe_unused = BATADV_NO_FLAGS; - __be16 ethertype = __constant_htons(ETH_P_BATMAN); + struct ethhdr *ethhdr; + unsigned short vid; bool is_bcast; is_bcast = (batadv_header->packet_type == BATADV_BCAST); @@ -326,13 +345,12 @@ void batadv_interface_rx(struct net_device *soft_iface, skb_pull_rcsum(skb, hdr_size); skb_reset_mac_header(skb); + vid = batadv_get_vid(skb, hdr_size); ethhdr = eth_hdr(skb); switch (ntohs(ethhdr->h_proto)) { case ETH_P_8021Q: vhdr = (struct vlan_ethhdr *)skb->data; - vid = ntohs(vhdr->h_vlan_TCI) & VLAN_VID_MASK; - vid |= BATADV_VLAN_HAS_TAG; if (vhdr->h_vlan_encapsulated_proto != ethertype) break; @@ -368,9 +386,10 @@ void batadv_interface_rx(struct net_device *soft_iface, if (orig_node) batadv_tt_add_temporary_global_entry(bat_priv, orig_node, - ethhdr->h_source); + ethhdr->h_source, vid); - if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest)) + if (batadv_is_ap_isolated(bat_priv, ethhdr->h_source, ethhdr->h_dest, + vid)) goto dropped; netif_rx(skb); @@ -382,6 +401,177 @@ out: return; } +/** + * batadv_softif_vlan_free_ref - decrease the vlan object refcounter and + * possibly free it + * @softif_vlan: the vlan object to release + */ +void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan) +{ + if (atomic_dec_and_test(&softif_vlan->refcount)) + kfree_rcu(softif_vlan, rcu); +} + +/** + * batadv_softif_vlan_get - get the vlan object for a specific vid + * @bat_priv: the bat priv with all the soft interface information + * @vid: the identifier of the vlan object to retrieve + * + * Returns the private data of the vlan matching the vid passed as argument or + * NULL otherwise. The refcounter of the returned object is incremented by 1. + */ +struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv, + unsigned short vid) +{ + struct batadv_softif_vlan *vlan_tmp, *vlan = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(vlan_tmp, &bat_priv->softif_vlan_list, list) { + if (vlan_tmp->vid != vid) + continue; + + if (!atomic_inc_not_zero(&vlan_tmp->refcount)) + continue; + + vlan = vlan_tmp; + break; + } + rcu_read_unlock(); + + return vlan; +} + +/** + * batadv_create_vlan - allocate the needed resources for a new vlan + * @bat_priv: the bat priv with all the soft interface information + * @vid: the VLAN identifier + * + * Returns 0 on success, a negative error otherwise. + */ +int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid) +{ + struct batadv_softif_vlan *vlan; + int err; + + vlan = batadv_softif_vlan_get(bat_priv, vid); + if (vlan) { + batadv_softif_vlan_free_ref(vlan); + return -EEXIST; + } + + vlan = kzalloc(sizeof(*vlan), GFP_ATOMIC); + if (!vlan) + return -ENOMEM; + + vlan->vid = vid; + atomic_set(&vlan->refcount, 1); + + atomic_set(&vlan->ap_isolation, 0); + + err = batadv_sysfs_add_vlan(bat_priv->soft_iface, vlan); + if (err) { + kfree(vlan); + return err; + } + + /* add a new TT local entry. This one will be marked with the NOPURGE + * flag + */ + batadv_tt_local_add(bat_priv->soft_iface, + bat_priv->soft_iface->dev_addr, vid, + BATADV_NULL_IFINDEX); + + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_add_head_rcu(&vlan->list, &bat_priv->softif_vlan_list); + spin_unlock_bh(&bat_priv->softif_vlan_list_lock); + + return 0; +} + +/** + * batadv_softif_destroy_vlan - remove and destroy a softif_vlan object + * @bat_priv: the bat priv with all the soft interface information + * @vlan: the object to remove + */ +static void batadv_softif_destroy_vlan(struct batadv_priv *bat_priv, + struct batadv_softif_vlan *vlan) +{ + spin_lock_bh(&bat_priv->softif_vlan_list_lock); + hlist_del_rcu(&vlan->list); + spin_unlock_bh(&bat_priv->softif_vlan_list_lock); + + batadv_sysfs_del_vlan(bat_priv, vlan); + + /* explicitly remove the associated TT local entry because it is marked + * with the NOPURGE flag + */ + batadv_tt_local_remove(bat_priv, bat_priv->soft_iface->dev_addr, + vlan->vid, "vlan interface destroyed", false); + + batadv_softif_vlan_free_ref(vlan); +} + +/** + * batadv_interface_add_vid - ndo_add_vid API implementation + * @dev: the netdev of the mesh interface + * @vid: identifier of the new vlan + * + * Set up all the internal structures for handling the new vlan on top of the + * mesh interface + * + * Returns 0 on success or a negative error code in case of failure. + */ +static int batadv_interface_add_vid(struct net_device *dev, __be16 proto, + unsigned short vid) +{ + struct batadv_priv *bat_priv = netdev_priv(dev); + + /* only 802.1Q vlans are supported. + * batman-adv does not know how to handle other types + */ + if (proto != htons(ETH_P_8021Q)) + return -EINVAL; + + vid |= BATADV_VLAN_HAS_TAG; + + return batadv_softif_create_vlan(bat_priv, vid); +} + +/** + * batadv_interface_kill_vid - ndo_kill_vid API implementation + * @dev: the netdev of the mesh interface + * @vid: identifier of the deleted vlan + * + * Destroy all the internal structures used to handle the vlan identified by vid + * on top of the mesh interface + * + * Returns 0 on success, -EINVAL if the specified prototype is not ETH_P_8021Q + * or -ENOENT if the specified vlan id wasn't registered. + */ +static int batadv_interface_kill_vid(struct net_device *dev, __be16 proto, + unsigned short vid) +{ + struct batadv_priv *bat_priv = netdev_priv(dev); + struct batadv_softif_vlan *vlan; + + /* only 802.1Q vlans are supported. batman-adv does not know how to + * handle other types + */ + if (proto != htons(ETH_P_8021Q)) + return -EINVAL; + + vlan = batadv_softif_vlan_get(bat_priv, vid | BATADV_VLAN_HAS_TAG); + if (!vlan) + return -ENOENT; + + batadv_softif_destroy_vlan(bat_priv, vlan); + + /* finally free the vlan object */ + batadv_softif_vlan_free_ref(vlan); + + return 0; +} + /* batman-adv network devices have devices nesting below it and are a special * "super class" of normal network devices; split their locks off into a * separate class since they always nest. @@ -421,6 +611,7 @@ static void batadv_set_lockdep_class(struct net_device *dev) */ static void batadv_softif_destroy_finish(struct work_struct *work) { + struct batadv_softif_vlan *vlan; struct batadv_priv *bat_priv; struct net_device *soft_iface; @@ -428,6 +619,13 @@ static void batadv_softif_destroy_finish(struct work_struct *work) cleanup_work); soft_iface = bat_priv->soft_iface; + /* destroy the "untagged" VLAN */ + vlan = batadv_softif_vlan_get(bat_priv, BATADV_NO_FLAGS); + if (vlan) { + batadv_softif_destroy_vlan(bat_priv, vlan); + batadv_softif_vlan_free_ref(vlan); + } + batadv_sysfs_del_meshif(soft_iface); rtnl_lock(); @@ -444,6 +642,7 @@ static void batadv_softif_destroy_finish(struct work_struct *work) static int batadv_softif_init_late(struct net_device *dev) { struct batadv_priv *bat_priv; + uint32_t random_seqno; int ret; size_t cnt_len = sizeof(uint64_t) * BATADV_CNT_NUM; @@ -468,17 +667,17 @@ static int batadv_softif_init_late(struct net_device *dev) #ifdef CONFIG_BATMAN_ADV_DAT atomic_set(&bat_priv->distributed_arp_table, 1); #endif - atomic_set(&bat_priv->ap_isolation, 0); - atomic_set(&bat_priv->vis_mode, BATADV_VIS_TYPE_CLIENT_UPDATE); atomic_set(&bat_priv->gw_mode, BATADV_GW_MODE_OFF); atomic_set(&bat_priv->gw_sel_class, 20); - atomic_set(&bat_priv->gw_bandwidth, 41); + atomic_set(&bat_priv->gw.bandwidth_down, 100); + atomic_set(&bat_priv->gw.bandwidth_up, 20); atomic_set(&bat_priv->orig_interval, 1000); atomic_set(&bat_priv->hop_penalty, 30); #ifdef CONFIG_BATMAN_ADV_DEBUG atomic_set(&bat_priv->log_level, 0); #endif atomic_set(&bat_priv->fragmentation, 1); + atomic_set(&bat_priv->packet_size_max, ETH_DATA_LEN); atomic_set(&bat_priv->bcast_queue_left, BATADV_BCAST_QUEUE_LEN); atomic_set(&bat_priv->batman_queue_left, BATADV_BATMAN_QUEUE_LEN); @@ -493,6 +692,10 @@ static int batadv_softif_init_late(struct net_device *dev) bat_priv->tt.last_changeset = NULL; bat_priv->tt.last_changeset_len = 0; + /* randomize initial seqno to avoid collision */ + get_random_bytes(&random_seqno, sizeof(random_seqno)); + atomic_set(&bat_priv->frag_seqno, random_seqno); + bat_priv->primary_if = NULL; bat_priv->num_ifaces = 0; @@ -578,8 +781,11 @@ static const struct net_device_ops batadv_netdev_ops = { .ndo_open = batadv_interface_open, .ndo_stop = batadv_interface_release, .ndo_get_stats = batadv_interface_stats, + .ndo_vlan_rx_add_vid = batadv_interface_add_vid, + .ndo_vlan_rx_kill_vid = batadv_interface_kill_vid, .ndo_set_mac_address = batadv_interface_set_mac_addr, .ndo_change_mtu = batadv_interface_change_mtu, + .ndo_set_rx_mode = batadv_interface_set_rx_mode, .ndo_start_xmit = batadv_interface_tx, .ndo_validate_addr = eth_validate_addr, .ndo_add_slave = batadv_softif_slave_add, @@ -616,6 +822,7 @@ static void batadv_softif_init_early(struct net_device *dev) dev->netdev_ops = &batadv_netdev_ops; dev->destructor = batadv_softif_free; + dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; dev->tx_queue_len = 0; /* can't call min_mtu, because the needed variables @@ -623,7 +830,7 @@ static void batadv_softif_init_early(struct net_device *dev) */ dev->mtu = ETH_DATA_LEN; /* reserve more space in the skbuff for our header */ - dev->hard_header_len = BATADV_HEADER_LEN; + dev->hard_header_len = batadv_max_header_len(); /* generate random address */ eth_hw_addr_random(dev); @@ -760,6 +967,12 @@ static const struct { { "mgmt_tx_bytes" }, { "mgmt_rx" }, { "mgmt_rx_bytes" }, + { "frag_tx" }, + { "frag_tx_bytes" }, + { "frag_rx" }, + { "frag_rx_bytes" }, + { "frag_fwd" }, + { "frag_fwd_bytes" }, { "tt_request_tx" }, { "tt_request_rx" }, { "tt_response_tx" }, diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h index 2f2472c2ea0d91aae4b50314ee1171928c5024ee..06fc91ff5a021bf584db73e75624e7d13d2560f7 100644 --- a/net/batman-adv/soft-interface.h +++ b/net/batman-adv/soft-interface.h @@ -28,5 +28,9 @@ struct net_device *batadv_softif_create(const char *name); void batadv_softif_destroy_sysfs(struct net_device *soft_iface); int batadv_softif_is_valid(const struct net_device *net_dev); extern struct rtnl_link_ops batadv_link_ops; +int batadv_softif_create_vlan(struct batadv_priv *bat_priv, unsigned short vid); +void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *softif_vlan); +struct batadv_softif_vlan *batadv_softif_vlan_get(struct batadv_priv *bat_priv, + unsigned short vid); #endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */ diff --git a/net/batman-adv/sysfs.c b/net/batman-adv/sysfs.c index 4114b961bc2c8400acc6bd776e2732313674b0e4..6335433310aff37a0438bc61367ce142d96cb07f 100644 --- a/net/batman-adv/sysfs.c +++ b/net/batman-adv/sysfs.c @@ -21,11 +21,12 @@ #include "sysfs.h" #include "translation-table.h" #include "distributed-arp-table.h" +#include "network-coding.h" #include "originator.h" #include "hard-interface.h" +#include "soft-interface.h" #include "gateway_common.h" #include "gateway_client.h" -#include "vis.h" static struct net_device *batadv_kobj_to_netdev(struct kobject *obj) { @@ -39,6 +40,53 @@ static struct batadv_priv *batadv_kobj_to_batpriv(struct kobject *obj) return netdev_priv(net_dev); } +/** + * batadv_vlan_kobj_to_batpriv - convert a vlan kobj in the associated batpriv + * @obj: kobject to covert + * + * Returns the associated batadv_priv struct. + */ +static struct batadv_priv *batadv_vlan_kobj_to_batpriv(struct kobject *obj) +{ + /* VLAN specific attributes are located in the root sysfs folder if they + * refer to the untagged VLAN.. + */ + if (!strcmp(BATADV_SYSFS_IF_MESH_SUBDIR, obj->name)) + return batadv_kobj_to_batpriv(obj); + + /* ..while the attributes for the tagged vlans are located in + * the in the corresponding "vlan%VID" subfolder + */ + return batadv_kobj_to_batpriv(obj->parent); +} + +/** + * batadv_kobj_to_vlan - convert a kobj in the associated softif_vlan struct + * @obj: kobject to covert + * + * Returns the associated softif_vlan struct if found, NULL otherwise. + */ +static struct batadv_softif_vlan * +batadv_kobj_to_vlan(struct batadv_priv *bat_priv, struct kobject *obj) +{ + struct batadv_softif_vlan *vlan_tmp, *vlan = NULL; + + rcu_read_lock(); + hlist_for_each_entry_rcu(vlan_tmp, &bat_priv->softif_vlan_list, list) { + if (vlan_tmp->kobj != obj) + continue; + + if (!atomic_inc_not_zero(&vlan_tmp->refcount)) + continue; + + vlan = vlan_tmp; + break; + } + rcu_read_unlock(); + + return vlan; +} + #define BATADV_UEV_TYPE_VAR "BATTYPE=" #define BATADV_UEV_ACTION_VAR "BATACTION=" #define BATADV_UEV_DATA_VAR "BATDATA=" @@ -53,6 +101,15 @@ static char *batadv_uev_type_str[] = { "gw" }; +/* Use this, if you have customized show and store functions for vlan attrs */ +#define BATADV_ATTR_VLAN(_name, _mode, _show, _store) \ +struct batadv_attribute batadv_attr_vlan_##_name = { \ + .attr = {.name = __stringify(_name), \ + .mode = _mode }, \ + .show = _show, \ + .store = _store, \ +}; + /* Use this, if you have customized show and store functions */ #define BATADV_ATTR(_name, _mode, _show, _store) \ struct batadv_attribute batadv_attr_##_name = { \ @@ -122,6 +179,41 @@ ssize_t batadv_show_##_name(struct kobject *kobj, \ static BATADV_ATTR(_name, _mode, batadv_show_##_name, \ batadv_store_##_name) +#define BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \ +ssize_t batadv_store_vlan_##_name(struct kobject *kobj, \ + struct attribute *attr, char *buff, \ + size_t count) \ +{ \ + struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\ + struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \ + kobj); \ + size_t res = __batadv_store_bool_attr(buff, count, _post_func, \ + attr, &vlan->_name, \ + bat_priv->soft_iface); \ + batadv_softif_vlan_free_ref(vlan); \ + return res; \ +} + +#define BATADV_ATTR_VLAN_SHOW_BOOL(_name) \ +ssize_t batadv_show_vlan_##_name(struct kobject *kobj, \ + struct attribute *attr, char *buff) \ +{ \ + struct batadv_priv *bat_priv = batadv_vlan_kobj_to_batpriv(kobj);\ + struct batadv_softif_vlan *vlan = batadv_kobj_to_vlan(bat_priv, \ + kobj); \ + size_t res = sprintf(buff, "%s\n", \ + atomic_read(&vlan->_name) == 0 ? \ + "disabled" : "enabled"); \ + batadv_softif_vlan_free_ref(vlan); \ + return res; \ +} + +/* Use this, if you are going to turn a [name] in the vlan struct on or off */ +#define BATADV_ATTR_VLAN_BOOL(_name, _mode, _post_func) \ + static BATADV_ATTR_VLAN_STORE_BOOL(_name, _post_func) \ + static BATADV_ATTR_VLAN_SHOW_BOOL(_name) \ + static BATADV_ATTR_VLAN(_name, _mode, batadv_show_vlan_##_name, \ + batadv_store_vlan_##_name) static int batadv_store_bool_attr(char *buff, size_t count, struct net_device *net_dev, @@ -230,74 +322,6 @@ __batadv_store_uint_attr(const char *buff, size_t count, return ret; } -static ssize_t batadv_show_vis_mode(struct kobject *kobj, - struct attribute *attr, char *buff) -{ - struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); - int vis_mode = atomic_read(&bat_priv->vis_mode); - const char *mode; - - if (vis_mode == BATADV_VIS_TYPE_CLIENT_UPDATE) - mode = "client"; - else - mode = "server"; - - return sprintf(buff, "%s\n", mode); -} - -static ssize_t batadv_store_vis_mode(struct kobject *kobj, - struct attribute *attr, char *buff, - size_t count) -{ - struct net_device *net_dev = batadv_kobj_to_netdev(kobj); - struct batadv_priv *bat_priv = netdev_priv(net_dev); - unsigned long val; - int ret, vis_mode_tmp = -1; - const char *old_mode, *new_mode; - - ret = kstrtoul(buff, 10, &val); - - if (((count == 2) && (!ret) && - (val == BATADV_VIS_TYPE_CLIENT_UPDATE)) || - (strncmp(buff, "client", 6) == 0) || - (strncmp(buff, "off", 3) == 0)) - vis_mode_tmp = BATADV_VIS_TYPE_CLIENT_UPDATE; - - if (((count == 2) && (!ret) && - (val == BATADV_VIS_TYPE_SERVER_SYNC)) || - (strncmp(buff, "server", 6) == 0)) - vis_mode_tmp = BATADV_VIS_TYPE_SERVER_SYNC; - - if (vis_mode_tmp < 0) { - if (buff[count - 1] == '\n') - buff[count - 1] = '\0'; - - batadv_info(net_dev, - "Invalid parameter for 'vis mode' setting received: %s\n", - buff); - return -EINVAL; - } - - if (atomic_read(&bat_priv->vis_mode) == vis_mode_tmp) - return count; - - if (atomic_read(&bat_priv->vis_mode) == BATADV_VIS_TYPE_CLIENT_UPDATE) - old_mode = "client"; - else - old_mode = "server"; - - if (vis_mode_tmp == BATADV_VIS_TYPE_CLIENT_UPDATE) - new_mode = "client"; - else - new_mode = "server"; - - batadv_info(net_dev, "Changing vis mode from: %s to: %s\n", old_mode, - new_mode); - - atomic_set(&bat_priv->vis_mode, (unsigned int)vis_mode_tmp); - return count; -} - static ssize_t batadv_show_bat_algo(struct kobject *kobj, struct attribute *attr, char *buff) { @@ -390,6 +414,7 @@ static ssize_t batadv_store_gw_mode(struct kobject *kobj, */ batadv_gw_check_client_stop(bat_priv); atomic_set(&bat_priv->gw_mode, (unsigned int)gw_mode_tmp); + batadv_gw_tvlv_container_update(bat_priv); return count; } @@ -397,15 +422,13 @@ static ssize_t batadv_show_gw_bwidth(struct kobject *kobj, struct attribute *attr, char *buff) { struct batadv_priv *bat_priv = batadv_kobj_to_batpriv(kobj); - int down, up; - int gw_bandwidth = atomic_read(&bat_priv->gw_bandwidth); - - batadv_gw_bandwidth_to_kbit(gw_bandwidth, &down, &up); - return sprintf(buff, "%i%s/%i%s\n", - (down > 2048 ? down / 1024 : down), - (down > 2048 ? "MBit" : "KBit"), - (up > 2048 ? up / 1024 : up), - (up > 2048 ? "MBit" : "KBit")); + uint32_t down, up; + + down = atomic_read(&bat_priv->gw.bandwidth_down); + up = atomic_read(&bat_priv->gw.bandwidth_up); + + return sprintf(buff, "%u.%u/%u.%u MBit\n", down / 10, + down % 10, up / 10, up % 10); } static ssize_t batadv_store_gw_bwidth(struct kobject *kobj, @@ -426,12 +449,10 @@ BATADV_ATTR_SIF_BOOL(bonding, S_IRUGO | S_IWUSR, NULL); BATADV_ATTR_SIF_BOOL(bridge_loop_avoidance, S_IRUGO | S_IWUSR, NULL); #endif #ifdef CONFIG_BATMAN_ADV_DAT -BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR, NULL); +BATADV_ATTR_SIF_BOOL(distributed_arp_table, S_IRUGO | S_IWUSR, + batadv_dat_status_update); #endif BATADV_ATTR_SIF_BOOL(fragmentation, S_IRUGO | S_IWUSR, batadv_update_min_mtu); -BATADV_ATTR_SIF_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL); -static BATADV_ATTR(vis_mode, S_IRUGO | S_IWUSR, batadv_show_vis_mode, - batadv_store_vis_mode); static BATADV_ATTR(routing_algo, S_IRUGO, batadv_show_bat_algo, NULL); static BATADV_ATTR(gw_mode, S_IRUGO | S_IWUSR, batadv_show_gw_mode, batadv_store_gw_mode); @@ -447,7 +468,8 @@ static BATADV_ATTR(gw_bandwidth, S_IRUGO | S_IWUSR, batadv_show_gw_bwidth, BATADV_ATTR_SIF_UINT(log_level, S_IRUGO | S_IWUSR, 0, BATADV_DBG_ALL, NULL); #endif #ifdef CONFIG_BATMAN_ADV_NC -BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, NULL); +BATADV_ATTR_SIF_BOOL(network_coding, S_IRUGO | S_IWUSR, + batadv_nc_status_update); #endif static struct batadv_attribute *batadv_mesh_attrs[] = { @@ -460,8 +482,6 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { &batadv_attr_distributed_arp_table, #endif &batadv_attr_fragmentation, - &batadv_attr_ap_isolation, - &batadv_attr_vis_mode, &batadv_attr_routing_algo, &batadv_attr_gw_mode, &batadv_attr_orig_interval, @@ -477,6 +497,16 @@ static struct batadv_attribute *batadv_mesh_attrs[] = { NULL, }; +BATADV_ATTR_VLAN_BOOL(ap_isolation, S_IRUGO | S_IWUSR, NULL); + +/** + * batadv_vlan_attrs - array of vlan specific sysfs attributes + */ +static struct batadv_attribute *batadv_vlan_attrs[] = { + &batadv_attr_vlan_ap_isolation, + NULL, +}; + int batadv_sysfs_add_meshif(struct net_device *dev) { struct kobject *batif_kobject = &dev->dev.kobj; @@ -527,6 +557,80 @@ void batadv_sysfs_del_meshif(struct net_device *dev) bat_priv->mesh_obj = NULL; } +/** + * batadv_sysfs_add_vlan - add all the needed sysfs objects for the new vlan + * @dev: netdev of the mesh interface + * @vlan: private data of the newly added VLAN interface + * + * Returns 0 on success and -ENOMEM if any of the structure allocations fails. + */ +int batadv_sysfs_add_vlan(struct net_device *dev, + struct batadv_softif_vlan *vlan) +{ + char vlan_subdir[sizeof(BATADV_SYSFS_VLAN_SUBDIR_PREFIX) + 5]; + struct batadv_priv *bat_priv = netdev_priv(dev); + struct batadv_attribute **bat_attr; + int err; + + if (vlan->vid & BATADV_VLAN_HAS_TAG) { + sprintf(vlan_subdir, BATADV_SYSFS_VLAN_SUBDIR_PREFIX "%hu", + vlan->vid & VLAN_VID_MASK); + + vlan->kobj = kobject_create_and_add(vlan_subdir, + bat_priv->mesh_obj); + if (!vlan->kobj) { + batadv_err(dev, "Can't add sysfs directory: %s/%s\n", + dev->name, vlan_subdir); + goto out; + } + } else { + /* the untagged LAN uses the root folder to store its "VLAN + * specific attributes" + */ + vlan->kobj = bat_priv->mesh_obj; + kobject_get(bat_priv->mesh_obj); + } + + for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) { + err = sysfs_create_file(vlan->kobj, + &((*bat_attr)->attr)); + if (err) { + batadv_err(dev, "Can't add sysfs file: %s/%s/%s\n", + dev->name, vlan_subdir, + ((*bat_attr)->attr).name); + goto rem_attr; + } + } + + return 0; + +rem_attr: + for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) + sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr)); + + kobject_put(vlan->kobj); + vlan->kobj = NULL; +out: + return -ENOMEM; +} + +/** + * batadv_sysfs_del_vlan - remove all the sysfs objects for a given VLAN + * @bat_priv: the bat priv with all the soft interface information + * @vlan: the private data of the VLAN to destroy + */ +void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv, + struct batadv_softif_vlan *vlan) +{ + struct batadv_attribute **bat_attr; + + for (bat_attr = batadv_vlan_attrs; *bat_attr; ++bat_attr) + sysfs_remove_file(vlan->kobj, &((*bat_attr)->attr)); + + kobject_put(vlan->kobj); + vlan->kobj = NULL; +} + static ssize_t batadv_show_mesh_iface(struct kobject *kobj, struct attribute *attr, char *buff) { diff --git a/net/batman-adv/sysfs.h b/net/batman-adv/sysfs.h index 479acf4c16f47d1068698d15362099459544af79..c7d725de50ade81593c0913ab5d868b23383058c 100644 --- a/net/batman-adv/sysfs.h +++ b/net/batman-adv/sysfs.h @@ -22,6 +22,12 @@ #define BATADV_SYSFS_IF_MESH_SUBDIR "mesh" #define BATADV_SYSFS_IF_BAT_SUBDIR "batman_adv" +/** + * BATADV_SYSFS_VLAN_SUBDIR_PREFIX - prefix of the subfolder that will be + * created in the sysfs hierarchy for each VLAN interface. The subfolder will + * be named "BATADV_SYSFS_VLAN_SUBDIR_PREFIX%vid". + */ +#define BATADV_SYSFS_VLAN_SUBDIR_PREFIX "vlan" struct batadv_attribute { struct attribute attr; @@ -36,6 +42,10 @@ void batadv_sysfs_del_meshif(struct net_device *dev); int batadv_sysfs_add_hardif(struct kobject **hardif_obj, struct net_device *dev); void batadv_sysfs_del_hardif(struct kobject **hardif_obj); +int batadv_sysfs_add_vlan(struct net_device *dev, + struct batadv_softif_vlan *vlan); +void batadv_sysfs_del_vlan(struct batadv_priv *bat_priv, + struct batadv_softif_vlan *vlan); int batadv_throw_uevent(struct batadv_priv *bat_priv, enum batadv_uev_type type, enum batadv_uev_action action, const char *data); diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 34510f38708fbd192ac5be8d4911e456c5d8ab50..4add57d4857f11e5ea9edfa13aae1f46dab3e4f9 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -27,13 +27,14 @@ #include "routing.h" #include "bridge_loop_avoidance.h" -#include +#include /* hash class keys */ static struct lock_class_key batadv_tt_local_hash_lock_class_key; static struct lock_class_key batadv_tt_global_hash_lock_class_key; static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, + unsigned short vid, struct batadv_orig_node *orig_node); static void batadv_tt_purge(struct work_struct *work); static void @@ -41,7 +42,8 @@ batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry); static void batadv_tt_global_del(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, const unsigned char *addr, - const char *message, bool roaming); + unsigned short vid, const char *message, + bool roaming); /* returns 1 if they are the same mac addr */ static int batadv_compare_tt(const struct hlist_node *node, const void *data2) @@ -52,43 +54,93 @@ static int batadv_compare_tt(const struct hlist_node *node, const void *data2) return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } +/** + * batadv_choose_tt - return the index of the tt entry in the hash table + * @data: pointer to the tt_common_entry object to map + * @size: the size of the hash table + * + * Returns the hash index where the object represented by 'data' should be + * stored at. + */ +static inline uint32_t batadv_choose_tt(const void *data, uint32_t size) +{ + struct batadv_tt_common_entry *tt; + uint32_t hash = 0; + + tt = (struct batadv_tt_common_entry *)data; + hash = batadv_hash_bytes(hash, &tt->addr, ETH_ALEN); + hash = batadv_hash_bytes(hash, &tt->vid, sizeof(tt->vid)); + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash % size; +} + +/** + * batadv_tt_hash_find - look for a client in the given hash table + * @hash: the hash table to search + * @addr: the mac address of the client to look for + * @vid: VLAN identifier + * + * Returns a pointer to the tt_common struct belonging to the searched client if + * found, NULL otherwise. + */ static struct batadv_tt_common_entry * -batadv_tt_hash_find(struct batadv_hashtable *hash, const void *data) +batadv_tt_hash_find(struct batadv_hashtable *hash, const uint8_t *addr, + unsigned short vid) { struct hlist_head *head; - struct batadv_tt_common_entry *tt_common_entry; - struct batadv_tt_common_entry *tt_common_entry_tmp = NULL; + struct batadv_tt_common_entry to_search, *tt, *tt_tmp = NULL; uint32_t index; if (!hash) return NULL; - index = batadv_choose_orig(data, hash->size); + memcpy(to_search.addr, addr, ETH_ALEN); + to_search.vid = vid; + + index = batadv_choose_tt(&to_search, hash->size); head = &hash->table[index]; rcu_read_lock(); - hlist_for_each_entry_rcu(tt_common_entry, head, hash_entry) { - if (!batadv_compare_eth(tt_common_entry, data)) + hlist_for_each_entry_rcu(tt, head, hash_entry) { + if (!batadv_compare_eth(tt, addr)) + continue; + + if (tt->vid != vid) continue; - if (!atomic_inc_not_zero(&tt_common_entry->refcount)) + if (!atomic_inc_not_zero(&tt->refcount)) continue; - tt_common_entry_tmp = tt_common_entry; + tt_tmp = tt; break; } rcu_read_unlock(); - return tt_common_entry_tmp; + return tt_tmp; } +/** + * batadv_tt_local_hash_find - search the local table for a given client + * @bat_priv: the bat priv with all the soft interface information + * @addr: the mac address of the client to look for + * @vid: VLAN identifier + * + * Returns a pointer to the corresponding tt_local_entry struct if the client is + * found, NULL otherwise. + */ static struct batadv_tt_local_entry * -batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const void *data) +batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const uint8_t *addr, + unsigned short vid) { struct batadv_tt_common_entry *tt_common_entry; struct batadv_tt_local_entry *tt_local_entry = NULL; - tt_common_entry = batadv_tt_hash_find(bat_priv->tt.local_hash, data); + tt_common_entry = batadv_tt_hash_find(bat_priv->tt.local_hash, addr, + vid); if (tt_common_entry) tt_local_entry = container_of(tt_common_entry, struct batadv_tt_local_entry, @@ -96,13 +148,24 @@ batadv_tt_local_hash_find(struct batadv_priv *bat_priv, const void *data) return tt_local_entry; } +/** + * batadv_tt_global_hash_find - search the global table for a given client + * @bat_priv: the bat priv with all the soft interface information + * @addr: the mac address of the client to look for + * @vid: VLAN identifier + * + * Returns a pointer to the corresponding tt_global_entry struct if the client + * is found, NULL otherwise. + */ static struct batadv_tt_global_entry * -batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const void *data) +batadv_tt_global_hash_find(struct batadv_priv *bat_priv, const uint8_t *addr, + unsigned short vid) { struct batadv_tt_common_entry *tt_common_entry; struct batadv_tt_global_entry *tt_global_entry = NULL; - tt_common_entry = batadv_tt_hash_find(bat_priv->tt.global_hash, data); + tt_common_entry = batadv_tt_hash_find(bat_priv->tt.global_hash, addr, + vid); if (tt_common_entry) tt_global_entry = container_of(tt_common_entry, struct batadv_tt_global_entry, @@ -117,25 +180,17 @@ batadv_tt_local_entry_free_ref(struct batadv_tt_local_entry *tt_local_entry) kfree_rcu(tt_local_entry, common.rcu); } -static void batadv_tt_global_entry_free_rcu(struct rcu_head *rcu) -{ - struct batadv_tt_common_entry *tt_common_entry; - struct batadv_tt_global_entry *tt_global_entry; - - tt_common_entry = container_of(rcu, struct batadv_tt_common_entry, rcu); - tt_global_entry = container_of(tt_common_entry, - struct batadv_tt_global_entry, common); - - kfree(tt_global_entry); -} - +/** + * batadv_tt_global_entry_free_ref - decrement the refcounter for a + * tt_global_entry and possibly free it + * @tt_global_entry: the object to free + */ static void batadv_tt_global_entry_free_ref(struct batadv_tt_global_entry *tt_global_entry) { if (atomic_dec_and_test(&tt_global_entry->common.refcount)) { batadv_tt_global_del_orig_list(tt_global_entry); - call_rcu(&tt_global_entry->common.rcu, - batadv_tt_global_entry_free_rcu); + kfree_rcu(tt_global_entry, common.rcu); } } @@ -153,13 +208,107 @@ static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu) kfree(orig_entry); } +/** + * batadv_tt_local_size_mod - change the size by v of the local table identified + * by vid + * @bat_priv: the bat priv with all the soft interface information + * @vid: the VLAN identifier of the sub-table to change + * @v: the amount to sum to the local table size + */ +static void batadv_tt_local_size_mod(struct batadv_priv *bat_priv, + unsigned short vid, int v) +{ + struct batadv_softif_vlan *vlan; + + vlan = batadv_softif_vlan_get(bat_priv, vid); + if (!vlan) + return; + + atomic_add(v, &vlan->tt.num_entries); + + batadv_softif_vlan_free_ref(vlan); +} + +/** + * batadv_tt_local_size_inc - increase by one the local table size for the given + * vid + * @bat_priv: the bat priv with all the soft interface information + * @vid: the VLAN identifier + */ +static void batadv_tt_local_size_inc(struct batadv_priv *bat_priv, + unsigned short vid) +{ + batadv_tt_local_size_mod(bat_priv, vid, 1); +} + +/** + * batadv_tt_local_size_dec - decrease by one the local table size for the given + * vid + * @bat_priv: the bat priv with all the soft interface information + * @vid: the VLAN identifier + */ +static void batadv_tt_local_size_dec(struct batadv_priv *bat_priv, + unsigned short vid) +{ + batadv_tt_local_size_mod(bat_priv, vid, -1); +} + +/** + * batadv_tt_global_size_mod - change the size by v of the local table + * identified by vid + * @bat_priv: the bat priv with all the soft interface information + * @vid: the VLAN identifier + * @v: the amount to sum to the global table size + */ +static void batadv_tt_global_size_mod(struct batadv_orig_node *orig_node, + unsigned short vid, int v) +{ + struct batadv_orig_node_vlan *vlan; + + vlan = batadv_orig_node_vlan_new(orig_node, vid); + if (!vlan) + return; + + if (atomic_add_return(v, &vlan->tt.num_entries) == 0) { + spin_lock_bh(&orig_node->vlan_list_lock); + list_del_rcu(&vlan->list); + spin_unlock_bh(&orig_node->vlan_list_lock); + batadv_orig_node_vlan_free_ref(vlan); + } + + batadv_orig_node_vlan_free_ref(vlan); +} + +/** + * batadv_tt_global_size_inc - increase by one the global table size for the + * given vid + * @orig_node: the originator which global table size has to be decreased + * @vid: the vlan identifier + */ +static void batadv_tt_global_size_inc(struct batadv_orig_node *orig_node, + unsigned short vid) +{ + batadv_tt_global_size_mod(orig_node, vid, 1); +} + +/** + * batadv_tt_global_size_dec - decrease by one the global table size for the + * given vid + * @orig_node: the originator which global table size has to be decreased + * @vid: the vlan identifier + */ +static void batadv_tt_global_size_dec(struct batadv_orig_node *orig_node, + unsigned short vid) +{ + batadv_tt_global_size_mod(orig_node, vid, -1); +} + static void batadv_tt_orig_list_entry_free_ref(struct batadv_tt_orig_list_entry *orig_entry) { if (!atomic_dec_and_test(&orig_entry->refcount)) return; - /* to avoid race conditions, immediately decrease the tt counter */ - atomic_dec(&orig_entry->orig_node->tt_size); + call_rcu(&orig_entry->rcu, batadv_tt_orig_list_entry_free_rcu); } @@ -180,12 +329,13 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, bool del_op_requested, del_op_entry; tt_change_node = kmalloc(sizeof(*tt_change_node), GFP_ATOMIC); - if (!tt_change_node) return; tt_change_node->change.flags = flags; + tt_change_node->change.reserved = 0; memcpy(tt_change_node->change.addr, common->addr, ETH_ALEN); + tt_change_node->change.vid = htons(common->vid); del_op_requested = flags & BATADV_TT_CLIENT_DEL; @@ -208,6 +358,13 @@ static void batadv_tt_local_event(struct batadv_priv *bat_priv, goto del; if (del_op_requested && !del_op_entry) goto del; + + /* this is a second add in the same originator interval. It + * means that flags have been changed: update them! + */ + if (!del_op_requested && !del_op_entry) + entry->change.flags = flags; + continue; del: list_del(&entry->list); @@ -229,9 +386,55 @@ unlock: atomic_inc(&bat_priv->tt.local_changes); } -int batadv_tt_len(int changes_num) +/** + * batadv_tt_len - compute length in bytes of given number of tt changes + * @changes_num: number of tt changes + * + * Returns computed length in bytes. + */ +static int batadv_tt_len(int changes_num) +{ + return changes_num * sizeof(struct batadv_tvlv_tt_change); +} + +/** + * batadv_tt_entries - compute the number of entries fitting in tt_len bytes + * @tt_len: available space + * + * Returns the number of entries. + */ +static uint16_t batadv_tt_entries(uint16_t tt_len) +{ + return tt_len / batadv_tt_len(1); +} + +/** + * batadv_tt_local_table_transmit_size - calculates the local translation table + * size when transmitted over the air + * @bat_priv: the bat priv with all the soft interface information + * + * Returns local translation table size in bytes. + */ +static int batadv_tt_local_table_transmit_size(struct batadv_priv *bat_priv) { - return changes_num * sizeof(struct batadv_tt_change); + uint16_t num_vlan = 0, tt_local_entries = 0; + struct batadv_softif_vlan *vlan; + int hdr_size; + + rcu_read_lock(); + hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) { + num_vlan++; + tt_local_entries += atomic_read(&vlan->tt.num_entries); + } + rcu_read_unlock(); + + /* header size of tvlv encapsulated tt response payload */ + hdr_size = sizeof(struct batadv_unicast_tvlv_packet); + hdr_size += sizeof(struct batadv_tvlv_hdr); + hdr_size += sizeof(struct batadv_tvlv_tt_data); + hdr_size += num_vlan * sizeof(struct batadv_tvlv_tt_vlan_data); + + return hdr_size + batadv_tt_len(tt_local_entries); } static int batadv_tt_local_init(struct batadv_priv *bat_priv) @@ -255,33 +458,51 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv, const char *message) { batadv_dbg(BATADV_DBG_TT, bat_priv, - "Deleting global tt entry %pM: %s\n", - tt_global->common.addr, message); + "Deleting global tt entry %pM (vid: %d): %s\n", + tt_global->common.addr, + BATADV_PRINT_VID(tt_global->common.vid), message); batadv_hash_remove(bat_priv->tt.global_hash, batadv_compare_tt, - batadv_choose_orig, tt_global->common.addr); + batadv_choose_tt, &tt_global->common); batadv_tt_global_entry_free_ref(tt_global); } -void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, - int ifindex) +/** + * batadv_tt_local_add - add a new client to the local table or update an + * existing client + * @soft_iface: netdev struct of the mesh interface + * @addr: the mac address of the client to add + * @vid: VLAN identifier + * @ifindex: index of the interface where the client is connected to (useful to + * identify wireless clients) + * + * Returns true if the client was successfully added, false otherwise. + */ +bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, + unsigned short vid, int ifindex) { struct batadv_priv *bat_priv = netdev_priv(soft_iface); struct batadv_tt_local_entry *tt_local; struct batadv_tt_global_entry *tt_global; + struct net_device *in_dev = NULL; struct hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry; - int hash_added; - bool roamed_back = false; + int hash_added, table_size, packet_size_max; + bool ret = false, roamed_back = false; + uint8_t remote_flags; - tt_local = batadv_tt_local_hash_find(bat_priv, addr); - tt_global = batadv_tt_global_hash_find(bat_priv, addr); + if (ifindex != BATADV_NULL_IFINDEX) + in_dev = dev_get_by_index(&init_net, ifindex); + + tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid); + tt_global = batadv_tt_global_hash_find(bat_priv, addr, vid); if (tt_local) { tt_local->last_seen = jiffies; if (tt_local->common.flags & BATADV_TT_CLIENT_PENDING) { batadv_dbg(BATADV_DBG_TT, bat_priv, - "Re-adding pending client %pM\n", addr); + "Re-adding pending client %pM (vid: %d)\n", + addr, BATADV_PRINT_VID(vid)); /* whatever the reason why the PENDING flag was set, * this is a client which was enqueued to be removed in * this orig_interval. Since it popped up again, the @@ -293,8 +514,8 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, if (tt_local->common.flags & BATADV_TT_CLIENT_ROAM) { batadv_dbg(BATADV_DBG_TT, bat_priv, - "Roaming client %pM came back to its original location\n", - addr); + "Roaming client %pM (vid: %d) came back to its original location\n", + addr, BATADV_PRINT_VID(vid)); /* the ROAM flag is set because this client roamed away * and the node got a roaming_advertisement message. Now * that the client popped up again at its original @@ -306,12 +527,24 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, goto check_roaming; } + /* Ignore the client if we cannot send it in a full table response. */ + table_size = batadv_tt_local_table_transmit_size(bat_priv); + table_size += batadv_tt_len(1); + packet_size_max = atomic_read(&bat_priv->packet_size_max); + if (table_size > packet_size_max) { + net_ratelimited_function(batadv_info, soft_iface, + "Local translation table size (%i) exceeds maximum packet size (%i); Ignoring new local tt entry: %pM\n", + table_size, packet_size_max, addr); + goto out; + } + tt_local = kmalloc(sizeof(*tt_local), GFP_ATOMIC); if (!tt_local) goto out; batadv_dbg(BATADV_DBG_TT, bat_priv, - "Creating new local tt entry: %pM (ttvn: %d)\n", addr, + "Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n", + addr, BATADV_PRINT_VID(vid), (uint8_t)atomic_read(&bat_priv->tt.vn)); memcpy(tt_local->common.addr, addr, ETH_ALEN); @@ -320,7 +553,8 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, * (consistency check) */ tt_local->common.flags = BATADV_TT_CLIENT_NEW; - if (batadv_is_wifi_iface(ifindex)) + tt_local->common.vid = vid; + if (batadv_is_wifi_netdev(in_dev)) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; atomic_set(&tt_local->common.refcount, 2); tt_local->last_seen = jiffies; @@ -331,7 +565,7 @@ void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, tt_local->common.flags |= BATADV_TT_CLIENT_NOPURGE; hash_added = batadv_hash_add(bat_priv->tt.local_hash, batadv_compare_tt, - batadv_choose_orig, &tt_local->common, + batadv_choose_tt, &tt_local->common, &tt_local->common.hash_entry); if (unlikely(hash_added != 0)) { @@ -353,6 +587,7 @@ check_roaming: rcu_read_lock(); hlist_for_each_entry_rcu(orig_entry, head, list) { batadv_send_roam_adv(bat_priv, tt_global->common.addr, + tt_global->common.vid, orig_entry->orig_node); } rcu_read_unlock(); @@ -369,78 +604,219 @@ check_roaming: } } + /* store the current remote flags before altering them. This helps + * understanding is flags are changing or not + */ + remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK; + + if (batadv_is_wifi_netdev(in_dev)) + tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; + else + tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI; + + /* if any "dynamic" flag has been modified, resend an ADD event for this + * entry so that all the nodes can get the new flags + */ + if (remote_flags ^ (tt_local->common.flags & BATADV_TT_REMOTE_MASK)) + batadv_tt_local_event(bat_priv, tt_local, BATADV_NO_FLAGS); + + ret = true; out: + if (in_dev) + dev_put(in_dev); if (tt_local) batadv_tt_local_entry_free_ref(tt_local); if (tt_global) batadv_tt_global_entry_free_ref(tt_global); + return ret; } -static void batadv_tt_realloc_packet_buff(unsigned char **packet_buff, - int *packet_buff_len, - int min_packet_len, - int new_packet_len) +/** + * batadv_tt_prepare_tvlv_global_data - prepare the TVLV TT header to send + * within a TT Response directed to another node + * @orig_node: originator for which the TT data has to be prepared + * @tt_data: uninitialised pointer to the address of the TVLV buffer + * @tt_change: uninitialised pointer to the address of the area where the TT + * changed can be stored + * @tt_len: pointer to the length to reserve to the tt_change. if -1 this + * function reserves the amount of space needed to send the entire global TT + * table. In case of success the value is updated with the real amount of + * reserved bytes + + * Allocate the needed amount of memory for the entire TT TVLV and write its + * header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data + * objects, one per active VLAN served by the originator node. + * + * Return the size of the allocated buffer or 0 in case of failure. + */ +static uint16_t +batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, + struct batadv_tvlv_tt_data **tt_data, + struct batadv_tvlv_tt_change **tt_change, + int32_t *tt_len) { - unsigned char *new_buff; + uint16_t num_vlan = 0, num_entries = 0, change_offset, tvlv_len; + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_orig_node_vlan *vlan; + uint8_t *tt_change_ptr; + + rcu_read_lock(); + list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) { + num_vlan++; + num_entries += atomic_read(&vlan->tt.num_entries); + } + + change_offset = sizeof(**tt_data); + change_offset += num_vlan * sizeof(*tt_vlan); - new_buff = kmalloc(new_packet_len, GFP_ATOMIC); + /* if tt_len is negative, allocate the space needed by the full table */ + if (*tt_len < 0) + *tt_len = batadv_tt_len(num_entries); - /* keep old buffer if kmalloc should fail */ - if (new_buff) { - memcpy(new_buff, *packet_buff, min_packet_len); - kfree(*packet_buff); - *packet_buff = new_buff; - *packet_buff_len = new_packet_len; + tvlv_len = *tt_len; + tvlv_len += change_offset; + + *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); + if (!*tt_data) { + *tt_len = 0; + goto out; + } + + (*tt_data)->flags = BATADV_NO_FLAGS; + (*tt_data)->ttvn = atomic_read(&orig_node->last_ttvn); + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); + list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) { + tt_vlan->vid = htons(vlan->vid); + tt_vlan->crc = htonl(vlan->tt.crc); + + tt_vlan++; } + + tt_change_ptr = (uint8_t *)*tt_data + change_offset; + *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr; + +out: + rcu_read_unlock(); + return tvlv_len; } -static void batadv_tt_prepare_packet_buff(struct batadv_priv *bat_priv, - unsigned char **packet_buff, - int *packet_buff_len, - int min_packet_len) -{ - int req_len; +/** + * batadv_tt_prepare_tvlv_local_data - allocate and prepare the TT TVLV for this + * node + * @bat_priv: the bat priv with all the soft interface information + * @tt_data: uninitialised pointer to the address of the TVLV buffer + * @tt_change: uninitialised pointer to the address of the area where the TT + * changes can be stored + * @tt_len: pointer to the length to reserve to the tt_change. if -1 this + * function reserves the amount of space needed to send the entire local TT + * table. In case of success the value is updated with the real amount of + * reserved bytes + * + * Allocate the needed amount of memory for the entire TT TVLV and write its + * header made up by one tvlv_tt_data object and a series of tvlv_tt_vlan_data + * objects, one per active VLAN. + * + * Return the size of the allocated buffer or 0 in case of failure. + */ +static uint16_t +batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, + struct batadv_tvlv_tt_data **tt_data, + struct batadv_tvlv_tt_change **tt_change, + int32_t *tt_len) +{ + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_softif_vlan *vlan; + uint16_t num_vlan = 0, num_entries = 0, tvlv_len; + uint8_t *tt_change_ptr; + int change_offset; - req_len = min_packet_len; - req_len += batadv_tt_len(atomic_read(&bat_priv->tt.local_changes)); + rcu_read_lock(); + hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) { + num_vlan++; + num_entries += atomic_read(&vlan->tt.num_entries); + } - /* if we have too many changes for one packet don't send any - * and wait for the tt table request which will be fragmented - */ - if (req_len > bat_priv->soft_iface->mtu) - req_len = min_packet_len; + change_offset = sizeof(**tt_data); + change_offset += num_vlan * sizeof(*tt_vlan); + + /* if tt_len is negative, allocate the space needed by the full table */ + if (*tt_len < 0) + *tt_len = batadv_tt_len(num_entries); + + tvlv_len = *tt_len; + tvlv_len += change_offset; + + *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); + if (!*tt_data) { + tvlv_len = 0; + goto out; + } + + (*tt_data)->flags = BATADV_NO_FLAGS; + (*tt_data)->ttvn = atomic_read(&bat_priv->tt.vn); + (*tt_data)->num_vlan = htons(num_vlan); + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); + hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) { + tt_vlan->vid = htons(vlan->vid); + tt_vlan->crc = htonl(vlan->tt.crc); - batadv_tt_realloc_packet_buff(packet_buff, packet_buff_len, - min_packet_len, req_len); + tt_vlan++; + } + + tt_change_ptr = (uint8_t *)*tt_data + change_offset; + *tt_change = (struct batadv_tvlv_tt_change *)tt_change_ptr; + +out: + rcu_read_unlock(); + return tvlv_len; } -static int batadv_tt_changes_fill_buff(struct batadv_priv *bat_priv, - unsigned char **packet_buff, - int *packet_buff_len, - int min_packet_len) +/** + * batadv_tt_tvlv_container_update - update the translation table tvlv container + * after local tt changes have been committed + * @bat_priv: the bat priv with all the soft interface information + */ +static void batadv_tt_tvlv_container_update(struct batadv_priv *bat_priv) { struct batadv_tt_change_node *entry, *safe; - int count = 0, tot_changes = 0, new_len; - unsigned char *tt_buff; + struct batadv_tvlv_tt_data *tt_data; + struct batadv_tvlv_tt_change *tt_change; + int tt_diff_len, tt_change_len = 0; + int tt_diff_entries_num = 0, tt_diff_entries_count = 0; + uint16_t tvlv_len; + + tt_diff_entries_num = atomic_read(&bat_priv->tt.local_changes); + tt_diff_len = batadv_tt_len(tt_diff_entries_num); + + /* if we have too many changes for one packet don't send any + * and wait for the tt table request which will be fragmented + */ + if (tt_diff_len > bat_priv->soft_iface->mtu) + tt_diff_len = 0; - batadv_tt_prepare_packet_buff(bat_priv, packet_buff, - packet_buff_len, min_packet_len); + tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, &tt_data, + &tt_change, &tt_diff_len); + if (!tvlv_len) + return; - new_len = *packet_buff_len - min_packet_len; - tt_buff = *packet_buff + min_packet_len; + tt_data->flags = BATADV_TT_OGM_DIFF; - if (new_len > 0) - tot_changes = new_len / batadv_tt_len(1); + if (tt_diff_len == 0) + goto container_register; spin_lock_bh(&bat_priv->tt.changes_list_lock); atomic_set(&bat_priv->tt.local_changes, 0); list_for_each_entry_safe(entry, safe, &bat_priv->tt.changes_list, list) { - if (count < tot_changes) { - memcpy(tt_buff + batadv_tt_len(count), - &entry->change, sizeof(struct batadv_tt_change)); - count++; + if (tt_diff_entries_count < tt_diff_entries_num) { + memcpy(tt_change + tt_diff_entries_count, + &entry->change, + sizeof(struct batadv_tvlv_tt_change)); + tt_diff_entries_count++; } list_del(&entry->list); kfree(entry); @@ -452,20 +828,25 @@ static int batadv_tt_changes_fill_buff(struct batadv_priv *bat_priv, kfree(bat_priv->tt.last_changeset); bat_priv->tt.last_changeset_len = 0; bat_priv->tt.last_changeset = NULL; + tt_change_len = batadv_tt_len(tt_diff_entries_count); /* check whether this new OGM has no changes due to size problems */ - if (new_len > 0) { + if (tt_diff_entries_count > 0) { /* if kmalloc() fails we will reply with the full table * instead of providing the diff */ - bat_priv->tt.last_changeset = kmalloc(new_len, GFP_ATOMIC); + bat_priv->tt.last_changeset = kzalloc(tt_diff_len, GFP_ATOMIC); if (bat_priv->tt.last_changeset) { - memcpy(bat_priv->tt.last_changeset, tt_buff, new_len); - bat_priv->tt.last_changeset_len = new_len; + memcpy(bat_priv->tt.last_changeset, + tt_change, tt_change_len); + bat_priv->tt.last_changeset_len = tt_diff_len; } } spin_unlock_bh(&bat_priv->tt.last_changeset_lock); - return count; +container_register: + batadv_tvlv_container_register(bat_priv, BATADV_TVLV_TT, 1, tt_data, + tvlv_len); + kfree(tt_data); } int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) @@ -476,7 +857,9 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) struct batadv_tt_common_entry *tt_common_entry; struct batadv_tt_local_entry *tt_local; struct batadv_hard_iface *primary_if; + struct batadv_softif_vlan *vlan; struct hlist_head *head; + unsigned short vid; uint32_t i; int last_seen_secs; int last_seen_msecs; @@ -489,11 +872,10 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) goto out; seq_printf(seq, - "Locally retrieved addresses (from %s) announced via TT (TTVN: %u CRC: %#.4x):\n", - net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn), - bat_priv->tt.local_crc); - seq_printf(seq, " %-13s %-7s %-10s\n", "Client", "Flags", - "Last seen"); + "Locally retrieved addresses (from %s) announced via TT (TTVN: %u):\n", + net_dev->name, (uint8_t)atomic_read(&bat_priv->tt.vn)); + seq_printf(seq, " %-13s %s %-7s %-9s (%-10s)\n", "Client", "VID", + "Flags", "Last seen", "CRC"); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@ -504,6 +886,7 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) tt_local = container_of(tt_common_entry, struct batadv_tt_local_entry, common); + vid = tt_common_entry->vid; last_seen_jiffies = jiffies - tt_local->last_seen; last_seen_msecs = jiffies_to_msecs(last_seen_jiffies); last_seen_secs = last_seen_msecs / 1000; @@ -511,8 +894,17 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) no_purge = tt_common_entry->flags & np_flag; - seq_printf(seq, " * %pM [%c%c%c%c%c] %3u.%03u\n", + vlan = batadv_softif_vlan_get(bat_priv, vid); + if (!vlan) { + seq_printf(seq, "Cannot retrieve VLAN %d\n", + BATADV_PRINT_VID(vid)); + continue; + } + + seq_printf(seq, + " * %pM %4i [%c%c%c%c%c] %3u.%03u (%#.8x)\n", tt_common_entry->addr, + BATADV_PRINT_VID(tt_common_entry->vid), (tt_common_entry->flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), no_purge ? 'P' : '.', @@ -523,7 +915,10 @@ int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset) (tt_common_entry->flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), no_purge ? 0 : last_seen_secs, - no_purge ? 0 : last_seen_msecs); + no_purge ? 0 : last_seen_msecs, + vlan->tt.crc); + + batadv_softif_vlan_free_ref(vlan); } rcu_read_unlock(); } @@ -547,27 +942,29 @@ batadv_tt_local_set_pending(struct batadv_priv *bat_priv, tt_local_entry->common.flags |= BATADV_TT_CLIENT_PENDING; batadv_dbg(BATADV_DBG_TT, bat_priv, - "Local tt entry (%pM) pending to be removed: %s\n", - tt_local_entry->common.addr, message); + "Local tt entry (%pM, vid: %d) pending to be removed: %s\n", + tt_local_entry->common.addr, + BATADV_PRINT_VID(tt_local_entry->common.vid), message); } /** * batadv_tt_local_remove - logically remove an entry from the local table * @bat_priv: the bat priv with all the soft interface information * @addr: the MAC address of the client to remove + * @vid: VLAN identifier * @message: message to append to the log on deletion * @roaming: true if the deletion is due to a roaming event * * Returns the flags assigned to the local entry before being deleted */ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, - const uint8_t *addr, const char *message, - bool roaming) + const uint8_t *addr, unsigned short vid, + const char *message, bool roaming) { struct batadv_tt_local_entry *tt_local_entry; uint16_t flags, curr_flags = BATADV_NO_FLAGS; - tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); + tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid); if (!tt_local_entry) goto out; @@ -603,8 +1000,16 @@ out: return curr_flags; } +/** + * batadv_tt_local_purge_list - purge inactive tt local entries + * @bat_priv: the bat priv with all the soft interface information + * @head: pointer to the list containing the local tt entries + * @timeout: parameter deciding whether a given tt local entry is considered + * inactive or not + */ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv, - struct hlist_head *head) + struct hlist_head *head, + int timeout) { struct batadv_tt_local_entry *tt_local_entry; struct batadv_tt_common_entry *tt_common_entry; @@ -622,8 +1027,7 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv, if (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING) continue; - if (!batadv_has_timed_out(tt_local_entry->last_seen, - BATADV_TT_LOCAL_TIMEOUT)) + if (!batadv_has_timed_out(tt_local_entry->last_seen, timeout)) continue; batadv_tt_local_set_pending(bat_priv, tt_local_entry, @@ -631,7 +1035,14 @@ static void batadv_tt_local_purge_list(struct batadv_priv *bat_priv, } } -static void batadv_tt_local_purge(struct batadv_priv *bat_priv) +/** + * batadv_tt_local_purge - purge inactive tt local entries + * @bat_priv: the bat priv with all the soft interface information + * @timeout: parameter deciding whether a given tt local entry is considered + * inactive or not + */ +static void batadv_tt_local_purge(struct batadv_priv *bat_priv, + int timeout) { struct batadv_hashtable *hash = bat_priv->tt.local_hash; struct hlist_head *head; @@ -643,7 +1054,7 @@ static void batadv_tt_local_purge(struct batadv_priv *bat_priv) list_lock = &hash->list_locks[i]; spin_lock_bh(list_lock); - batadv_tt_local_purge_list(bat_priv, head); + batadv_tt_local_purge_list(bat_priv, head, timeout); spin_unlock_bh(list_lock); } } @@ -784,7 +1195,7 @@ batadv_tt_global_orig_entry_add(struct batadv_tt_global_entry *tt_global, INIT_HLIST_NODE(&orig_entry->list); atomic_inc(&orig_node->refcount); - atomic_inc(&orig_node->tt_size); + batadv_tt_global_size_inc(orig_node, tt_global->common.vid); orig_entry->orig_node = orig_node; orig_entry->ttvn = ttvn; atomic_set(&orig_entry->refcount, 2); @@ -803,6 +1214,7 @@ out: * @bat_priv: the bat priv with all the soft interface information * @orig_node: the originator announcing the client * @tt_addr: the mac address of the non-mesh client + * @vid: VLAN identifier * @flags: TT flags that have to be set for this non-mesh client * @ttvn: the tt version number ever announcing this non-mesh client * @@ -813,21 +1225,28 @@ out: * If a TT local entry exists for this non-mesh client remove it. * * The caller must hold orig_node refcount. + * + * Return true if the new entry has been added, false otherwise */ -int batadv_tt_global_add(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node, - const unsigned char *tt_addr, uint16_t flags, - uint8_t ttvn) +static bool batadv_tt_global_add(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + const unsigned char *tt_addr, + unsigned short vid, uint16_t flags, + uint8_t ttvn) { struct batadv_tt_global_entry *tt_global_entry; struct batadv_tt_local_entry *tt_local_entry; - int ret = 0; + bool ret = false; int hash_added; struct batadv_tt_common_entry *common; uint16_t local_flags; - tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr); - tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr); + /* ignore global entries from backbone nodes */ + if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig, vid)) + return true; + + tt_global_entry = batadv_tt_global_hash_find(bat_priv, tt_addr, vid); + tt_local_entry = batadv_tt_local_hash_find(bat_priv, tt_addr, vid); /* if the node already has a local client for this entry, it has to wait * for a roaming advertisement instead of manually messing up the global @@ -844,6 +1263,7 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv, common = &tt_global_entry->common; memcpy(common->addr, tt_addr, ETH_ALEN); + common->vid = vid; common->flags = flags; tt_global_entry->roam_at = 0; @@ -861,7 +1281,7 @@ int batadv_tt_global_add(struct batadv_priv *bat_priv, hash_added = batadv_hash_add(bat_priv->tt.global_hash, batadv_compare_tt, - batadv_choose_orig, common, + batadv_choose_tt, common, &common->hash_entry); if (unlikely(hash_added != 0)) { @@ -920,14 +1340,15 @@ add_orig_entry: batadv_tt_global_orig_entry_add(tt_global_entry, orig_node, ttvn); batadv_dbg(BATADV_DBG_TT, bat_priv, - "Creating new global tt entry: %pM (via %pM)\n", - common->addr, orig_node->orig); - ret = 1; + "Creating new global tt entry: %pM (vid: %d, via %pM)\n", + common->addr, BATADV_PRINT_VID(common->vid), + orig_node->orig); + ret = true; out_remove: /* remove address from local hash if present */ - local_flags = batadv_tt_local_remove(bat_priv, tt_addr, + local_flags = batadv_tt_local_remove(bat_priv, tt_addr, vid, "global tt received", flags & BATADV_TT_CLIENT_ROAM); tt_global_entry->common.flags |= local_flags & BATADV_TT_CLIENT_WIFI; @@ -947,18 +1368,20 @@ out: } /* batadv_transtable_best_orig - Get best originator list entry from tt entry + * @bat_priv: the bat priv with all the soft interface information * @tt_global_entry: global translation table entry to be analyzed * * This functon assumes the caller holds rcu_read_lock(). * Returns best originator list entry or NULL on errors. */ static struct batadv_tt_orig_list_entry * -batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry) +batadv_transtable_best_orig(struct batadv_priv *bat_priv, + struct batadv_tt_global_entry *tt_global_entry) { - struct batadv_neigh_node *router = NULL; + struct batadv_neigh_node *router, *best_router = NULL; + struct batadv_algo_ops *bao = bat_priv->bat_algo_ops; struct hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry, *best_entry = NULL; - int best_tq = 0; head = &tt_global_entry->orig_list; hlist_for_each_entry_rcu(orig_entry, head, list) { @@ -966,64 +1389,104 @@ batadv_transtable_best_orig(struct batadv_tt_global_entry *tt_global_entry) if (!router) continue; - if (router->tq_avg > best_tq) { - best_entry = orig_entry; - best_tq = router->tq_avg; + if (best_router && + bao->bat_neigh_cmp(router, best_router) <= 0) { + batadv_neigh_node_free_ref(router); + continue; } - batadv_neigh_node_free_ref(router); + /* release the refcount for the "old" best */ + if (best_router) + batadv_neigh_node_free_ref(best_router); + + best_entry = orig_entry; + best_router = router; } + if (best_router) + batadv_neigh_node_free_ref(best_router); + return best_entry; } /* batadv_tt_global_print_entry - print all orig nodes who announce the address * for this global entry + * @bat_priv: the bat priv with all the soft interface information * @tt_global_entry: global translation table entry to be printed * @seq: debugfs table seq_file struct * * This functon assumes the caller holds rcu_read_lock(). */ static void -batadv_tt_global_print_entry(struct batadv_tt_global_entry *tt_global_entry, +batadv_tt_global_print_entry(struct batadv_priv *bat_priv, + struct batadv_tt_global_entry *tt_global_entry, struct seq_file *seq) { - struct hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry, *best_entry; struct batadv_tt_common_entry *tt_common_entry; - uint16_t flags; + struct batadv_orig_node_vlan *vlan; + struct hlist_head *head; uint8_t last_ttvn; + uint16_t flags; tt_common_entry = &tt_global_entry->common; flags = tt_common_entry->flags; - best_entry = batadv_transtable_best_orig(tt_global_entry); + best_entry = batadv_transtable_best_orig(bat_priv, tt_global_entry); if (best_entry) { + vlan = batadv_orig_node_vlan_get(best_entry->orig_node, + tt_common_entry->vid); + if (!vlan) { + seq_printf(seq, + " * Cannot retrieve VLAN %d for originator %pM\n", + BATADV_PRINT_VID(tt_common_entry->vid), + best_entry->orig_node->orig); + goto print_list; + } + last_ttvn = atomic_read(&best_entry->orig_node->last_ttvn); seq_printf(seq, - " %c %pM (%3u) via %pM (%3u) (%#.4x) [%c%c%c]\n", + " %c %pM %4i (%3u) via %pM (%3u) (%#.8x) [%c%c%c]\n", '*', tt_global_entry->common.addr, + BATADV_PRINT_VID(tt_global_entry->common.vid), best_entry->ttvn, best_entry->orig_node->orig, - last_ttvn, best_entry->orig_node->tt_crc, + last_ttvn, vlan->tt.crc, (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.')); + + batadv_orig_node_vlan_free_ref(vlan); } +print_list: head = &tt_global_entry->orig_list; hlist_for_each_entry_rcu(orig_entry, head, list) { if (best_entry == orig_entry) continue; + vlan = batadv_orig_node_vlan_get(orig_entry->orig_node, + tt_common_entry->vid); + if (!vlan) { + seq_printf(seq, + " + Cannot retrieve VLAN %d for originator %pM\n", + BATADV_PRINT_VID(tt_common_entry->vid), + orig_entry->orig_node->orig); + continue; + } + last_ttvn = atomic_read(&orig_entry->orig_node->last_ttvn); - seq_printf(seq, " %c %pM (%3u) via %pM (%3u) [%c%c%c]\n", + seq_printf(seq, + " %c %pM %4d (%3u) via %pM (%3u) (%#.8x) [%c%c%c]\n", '+', tt_global_entry->common.addr, + BATADV_PRINT_VID(tt_global_entry->common.vid), orig_entry->ttvn, orig_entry->orig_node->orig, - last_ttvn, + last_ttvn, vlan->tt.crc, (flags & BATADV_TT_CLIENT_ROAM ? 'R' : '.'), (flags & BATADV_TT_CLIENT_WIFI ? 'W' : '.'), (flags & BATADV_TT_CLIENT_TEMP ? 'T' : '.')); + + batadv_orig_node_vlan_free_ref(vlan); } } @@ -1045,9 +1508,9 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, "Globally announced TT entries received via the mesh %s\n", net_dev->name); - seq_printf(seq, " %-13s %s %-15s %s (%-6s) %s\n", - "Client", "(TTVN)", "Originator", "(Curr TTVN)", "CRC", - "Flags"); + seq_printf(seq, " %-13s %s %s %-15s %s (%-10s) %s\n", + "Client", "VID", "(TTVN)", "Originator", "(Curr TTVN)", + "CRC", "Flags"); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@ -1058,7 +1521,7 @@ int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset) tt_global = container_of(tt_common_entry, struct batadv_tt_global_entry, common); - batadv_tt_global_print_entry(tt_global, seq); + batadv_tt_global_print_entry(bat_priv, tt_global, seq); } rcu_read_unlock(); } @@ -1080,6 +1543,8 @@ batadv_tt_global_del_orig_list(struct batadv_tt_global_entry *tt_global_entry) head = &tt_global_entry->orig_list; hlist_for_each_entry_safe(orig_entry, safe, head, list) { hlist_del_rcu(&orig_entry->list); + batadv_tt_global_size_dec(orig_entry->orig_node, + tt_global_entry->common.vid); batadv_tt_orig_list_entry_free_ref(orig_entry); } spin_unlock_bh(&tt_global_entry->list_lock); @@ -1094,16 +1559,21 @@ batadv_tt_global_del_orig_entry(struct batadv_priv *bat_priv, struct hlist_head *head; struct hlist_node *safe; struct batadv_tt_orig_list_entry *orig_entry; + unsigned short vid; spin_lock_bh(&tt_global_entry->list_lock); head = &tt_global_entry->orig_list; hlist_for_each_entry_safe(orig_entry, safe, head, list) { if (orig_entry->orig_node == orig_node) { + vid = tt_global_entry->common.vid; batadv_dbg(BATADV_DBG_TT, bat_priv, - "Deleting %pM from global tt entry %pM: %s\n", + "Deleting %pM from global tt entry %pM (vid: %d): %s\n", orig_node->orig, - tt_global_entry->common.addr, message); + tt_global_entry->common.addr, + BATADV_PRINT_VID(vid), message); hlist_del_rcu(&orig_entry->list); + batadv_tt_global_size_dec(orig_node, + tt_global_entry->common.vid); batadv_tt_orig_list_entry_free_ref(orig_entry); } } @@ -1150,17 +1620,25 @@ batadv_tt_global_del_roaming(struct batadv_priv *bat_priv, orig_node, message); } - - +/** + * batadv_tt_global_del - remove a client from the global table + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: an originator serving this client + * @addr: the mac address of the client + * @vid: VLAN identifier + * @message: a message explaining the reason for deleting the client to print + * for debugging purpose + * @roaming: true if the deletion has been triggered by a roaming event + */ static void batadv_tt_global_del(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - const unsigned char *addr, + const unsigned char *addr, unsigned short vid, const char *message, bool roaming) { struct batadv_tt_global_entry *tt_global_entry; struct batadv_tt_local_entry *local_entry = NULL; - tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr); + tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid); if (!tt_global_entry) goto out; @@ -1189,7 +1667,8 @@ static void batadv_tt_global_del(struct batadv_priv *bat_priv, * the global entry, since it is useless now. */ local_entry = batadv_tt_local_hash_find(bat_priv, - tt_global_entry->common.addr); + tt_global_entry->common.addr, + vid); if (local_entry) { /* local entry exists, case 2: client roamed to us. */ batadv_tt_global_del_orig_list(tt_global_entry); @@ -1207,8 +1686,18 @@ out: batadv_tt_local_entry_free_ref(local_entry); } +/** + * batadv_tt_global_del_orig - remove all the TT global entries belonging to the + * given originator matching the provided vid + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: the originator owning the entries to remove + * @match_vid: the VLAN identifier to match. If negative all the entries will be + * removed + * @message: debug message to print as "reason" + */ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, + int32_t match_vid, const char *message) { struct batadv_tt_global_entry *tt_global; @@ -1218,6 +1707,7 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv, struct hlist_node *safe; struct hlist_head *head; spinlock_t *list_lock; /* protects write access to the hash lists */ + unsigned short vid; if (!hash) return; @@ -1229,6 +1719,10 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv, spin_lock_bh(list_lock); hlist_for_each_entry_safe(tt_common_entry, safe, head, hash_entry) { + /* remove only matching entries */ + if (match_vid >= 0 && tt_common_entry->vid != match_vid) + continue; + tt_global = container_of(tt_common_entry, struct batadv_tt_global_entry, common); @@ -1237,9 +1731,11 @@ void batadv_tt_global_del_orig(struct batadv_priv *bat_priv, orig_node, message); if (hlist_empty(&tt_global->orig_list)) { + vid = tt_global->common.vid; batadv_dbg(BATADV_DBG_TT, bat_priv, - "Deleting global tt entry %pM: %s\n", - tt_global->common.addr, message); + "Deleting global tt entry %pM (vid: %d): %s\n", + tt_global->common.addr, + BATADV_PRINT_VID(vid), message); hlist_del_rcu(&tt_common_entry->hash_entry); batadv_tt_global_entry_free_ref(tt_global); } @@ -1297,8 +1793,10 @@ static void batadv_tt_global_purge(struct batadv_priv *bat_priv) continue; batadv_dbg(BATADV_DBG_TT, bat_priv, - "Deleting global tt entry (%pM): %s\n", - tt_global->common.addr, msg); + "Deleting global tt entry %pM (vid: %d): %s\n", + tt_global->common.addr, + BATADV_PRINT_VID(tt_global->common.vid), + msg); hlist_del_rcu(&tt_common->hash_entry); @@ -1357,23 +1855,49 @@ _batadv_is_ap_isolated(struct batadv_tt_local_entry *tt_local_entry, return ret; } +/** + * batadv_transtable_search - get the mesh destination for a given client + * @bat_priv: the bat priv with all the soft interface information + * @src: mac address of the source client + * @addr: mac address of the destination client + * @vid: VLAN identifier + * + * Returns a pointer to the originator that was selected as destination in the + * mesh for contacting the client 'addr', NULL otherwise. + * In case of multiple originators serving the same client, the function returns + * the best one (best in terms of metric towards the destination node). + * + * If the two clients are AP isolated the function returns NULL. + */ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv, const uint8_t *src, - const uint8_t *addr) + const uint8_t *addr, + unsigned short vid) { struct batadv_tt_local_entry *tt_local_entry = NULL; struct batadv_tt_global_entry *tt_global_entry = NULL; struct batadv_orig_node *orig_node = NULL; struct batadv_tt_orig_list_entry *best_entry; + bool ap_isolation_enabled = false; + struct batadv_softif_vlan *vlan; + + /* if the AP isolation is requested on a VLAN, then check for its + * setting in the proper VLAN private data structure + */ + vlan = batadv_softif_vlan_get(bat_priv, vid); + if (vlan) { + ap_isolation_enabled = atomic_read(&vlan->ap_isolation); + batadv_softif_vlan_free_ref(vlan); + } - if (src && atomic_read(&bat_priv->ap_isolation)) { - tt_local_entry = batadv_tt_local_hash_find(bat_priv, src); + if (src && ap_isolation_enabled) { + tt_local_entry = batadv_tt_local_hash_find(bat_priv, src, vid); if (!tt_local_entry || (tt_local_entry->common.flags & BATADV_TT_CLIENT_PENDING)) goto out; } - tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr); + tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid); if (!tt_global_entry) goto out; @@ -1385,7 +1909,7 @@ struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv, goto out; rcu_read_lock(); - best_entry = batadv_transtable_best_orig(tt_global_entry); + best_entry = batadv_transtable_best_orig(bat_priv, tt_global_entry); /* found anything? */ if (best_entry) orig_node = best_entry->orig_node; @@ -1402,17 +1926,40 @@ out: return orig_node; } -/* Calculates the checksum of the local table of a given orig_node */ -static uint16_t batadv_tt_global_crc(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node) +/** + * batadv_tt_global_crc - calculates the checksum of the local table belonging + * to the given orig_node + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: originator for which the CRC should be computed + * @vid: VLAN identifier for which the CRC32 has to be computed + * + * This function computes the checksum for the global table corresponding to a + * specific originator. In particular, the checksum is computed as follows: For + * each client connected to the originator the CRC32C of the MAC address and the + * VID is computed and then all the CRC32Cs of the various clients are xor'ed + * together. + * + * The idea behind is that CRC32C should be used as much as possible in order to + * produce a unique hash of the table, but since the order which is used to feed + * the CRC32C function affects the result and since every node in the network + * probably sorts the clients differently, the hash function cannot be directly + * computed over the entire table. Hence the CRC32C is used only on + * the single client entry, while all the results are then xor'ed together + * because the XOR operation can combine them all while trying to reduce the + * noise as much as possible. + * + * Returns the checksum of the global table of a given originator. + */ +static uint32_t batadv_tt_global_crc(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + unsigned short vid) { - uint16_t total = 0, total_one; struct batadv_hashtable *hash = bat_priv->tt.global_hash; struct batadv_tt_common_entry *tt_common; struct batadv_tt_global_entry *tt_global; struct hlist_head *head; - uint32_t i; - int j; + uint32_t i, crc_tmp, crc = 0; + uint8_t flags; for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@ -1422,6 +1969,12 @@ static uint16_t batadv_tt_global_crc(struct batadv_priv *bat_priv, tt_global = container_of(tt_common, struct batadv_tt_global_entry, common); + /* compute the CRC only for entries belonging to the + * VLAN identified by the vid passed as parameter + */ + if (tt_common->vid != vid) + continue; + /* Roaming clients are in the global table for * consistency only. They don't have to be * taken into account while computing the @@ -1443,48 +1996,74 @@ static uint16_t batadv_tt_global_crc(struct batadv_priv *bat_priv, orig_node)) continue; - total_one = 0; - for (j = 0; j < ETH_ALEN; j++) - total_one = crc16_byte(total_one, - tt_common->addr[j]); - total ^= total_one; + crc_tmp = crc32c(0, &tt_common->vid, + sizeof(tt_common->vid)); + + /* compute the CRC on flags that have to be kept in sync + * among nodes + */ + flags = tt_common->flags & BATADV_TT_SYNC_MASK; + crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags)); + + crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN); } rcu_read_unlock(); } - return total; + return crc; } -/* Calculates the checksum of the local table */ -static uint16_t batadv_tt_local_crc(struct batadv_priv *bat_priv) +/** + * batadv_tt_local_crc - calculates the checksum of the local table + * @bat_priv: the bat priv with all the soft interface information + * @vid: VLAN identifier for which the CRC32 has to be computed + * + * For details about the computation, please refer to the documentation for + * batadv_tt_global_crc(). + * + * Returns the checksum of the local table + */ +static uint32_t batadv_tt_local_crc(struct batadv_priv *bat_priv, + unsigned short vid) { - uint16_t total = 0, total_one; struct batadv_hashtable *hash = bat_priv->tt.local_hash; struct batadv_tt_common_entry *tt_common; struct hlist_head *head; - uint32_t i; - int j; + uint32_t i, crc_tmp, crc = 0; + uint8_t flags; for (i = 0; i < hash->size; i++) { head = &hash->table[i]; rcu_read_lock(); hlist_for_each_entry_rcu(tt_common, head, hash_entry) { + /* compute the CRC only for entries belonging to the + * VLAN identified by vid + */ + if (tt_common->vid != vid) + continue; + /* not yet committed clients have not to be taken into * account while computing the CRC */ if (tt_common->flags & BATADV_TT_CLIENT_NEW) continue; - total_one = 0; - for (j = 0; j < ETH_ALEN; j++) - total_one = crc16_byte(total_one, - tt_common->addr[j]); - total ^= total_one; + + crc_tmp = crc32c(0, &tt_common->vid, + sizeof(tt_common->vid)); + + /* compute the CRC on flags that have to be kept in sync + * among nodes + */ + flags = tt_common->flags & BATADV_TT_SYNC_MASK; + crc_tmp = crc32c(crc_tmp, &flags, sizeof(flags)); + + crc ^= crc32c(crc_tmp, tt_common->addr, ETH_ALEN); } rcu_read_unlock(); } - return total; + return crc; } static void batadv_tt_req_list_free(struct batadv_priv *bat_priv) @@ -1503,11 +2082,9 @@ static void batadv_tt_req_list_free(struct batadv_priv *bat_priv) static void batadv_tt_save_orig_buffer(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - const unsigned char *tt_buff, - uint8_t tt_num_changes) + const void *tt_buff, + uint16_t tt_buff_len) { - uint16_t tt_buff_len = batadv_tt_len(tt_num_changes); - /* Replace the old buffer only if I received something in the * last OGM (the OGM could carry no changes) */ @@ -1569,9 +2146,14 @@ unlock: return tt_req_node; } -/* data_ptr is useless here, but has to be kept to respect the prototype */ -static int batadv_tt_local_valid_entry(const void *entry_ptr, - const void *data_ptr) +/** + * batadv_tt_local_valid - verify that given tt entry is a valid one + * @entry_ptr: to be checked local tt entry + * @data_ptr: not used but definition required to satisfy the callback prototype + * + * Returns 1 if the entry is a valid, 0 otherwise. + */ +static int batadv_tt_local_valid(const void *entry_ptr, const void *data_ptr) { const struct batadv_tt_common_entry *tt_common_entry = entry_ptr; @@ -1598,41 +2180,30 @@ static int batadv_tt_global_valid(const void *entry_ptr, return batadv_tt_global_entry_has_orig(tt_global_entry, orig_node); } -static struct sk_buff * -batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, - struct batadv_hashtable *hash, - struct batadv_priv *bat_priv, - int (*valid_cb)(const void *, const void *), - void *cb_data) +/** + * batadv_tt_tvlv_generate - fill the tvlv buff with the tt entries from the + * specified tt hash + * @bat_priv: the bat priv with all the soft interface information + * @hash: hash table containing the tt entries + * @tt_len: expected tvlv tt data buffer length in number of bytes + * @tvlv_buff: pointer to the buffer to fill with the TT data + * @valid_cb: function to filter tt change entries + * @cb_data: data passed to the filter function as argument + */ +static void batadv_tt_tvlv_generate(struct batadv_priv *bat_priv, + struct batadv_hashtable *hash, + void *tvlv_buff, uint16_t tt_len, + int (*valid_cb)(const void *, const void *), + void *cb_data) { struct batadv_tt_common_entry *tt_common_entry; - struct batadv_tt_query_packet *tt_response; - struct batadv_tt_change *tt_change; + struct batadv_tvlv_tt_change *tt_change; struct hlist_head *head; - struct sk_buff *skb = NULL; - uint16_t tt_tot, tt_count; - ssize_t tt_query_size = sizeof(struct batadv_tt_query_packet); + uint16_t tt_tot, tt_num_entries = 0; uint32_t i; - size_t len; - if (tt_query_size + tt_len > bat_priv->soft_iface->mtu) { - tt_len = bat_priv->soft_iface->mtu - tt_query_size; - tt_len -= tt_len % sizeof(struct batadv_tt_change); - } - tt_tot = tt_len / sizeof(struct batadv_tt_change); - - len = tt_query_size + tt_len; - skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); - if (!skb) - goto out; - - skb->priority = TC_PRIO_CONTROL; - skb_reserve(skb, ETH_HLEN); - tt_response = (struct batadv_tt_query_packet *)skb_put(skb, len); - tt_response->ttvn = ttvn; - - tt_change = (struct batadv_tt_change *)(skb->data + tt_query_size); - tt_count = 0; + tt_tot = batadv_tt_entries(tt_len); + tt_change = (struct batadv_tvlv_tt_change *)tvlv_buff; rcu_read_lock(); for (i = 0; i < hash->size; i++) { @@ -1640,7 +2211,7 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, hlist_for_each_entry_rcu(tt_common_entry, head, hash_entry) { - if (tt_count == tt_tot) + if (tt_tot == tt_num_entries) break; if ((valid_cb) && (!valid_cb(tt_common_entry, cb_data))) @@ -1649,33 +2220,123 @@ batadv_tt_response_fill_table(uint16_t tt_len, uint8_t ttvn, memcpy(tt_change->addr, tt_common_entry->addr, ETH_ALEN); tt_change->flags = tt_common_entry->flags; + tt_change->vid = htons(tt_common_entry->vid); + tt_change->reserved = 0; - tt_count++; + tt_num_entries++; tt_change++; } } rcu_read_unlock(); +} - /* store in the message the number of entries we have successfully - * copied - */ - tt_response->tt_data = htons(tt_count); +/** + * batadv_tt_global_check_crc - check if all the CRCs are correct + * @orig_node: originator for which the CRCs have to be checked + * @tt_vlan: pointer to the first tvlv VLAN entry + * @num_vlan: number of tvlv VLAN entries + * @create: if true, create VLAN objects if not found + * + * Return true if all the received CRCs match the locally stored ones, false + * otherwise + */ +static bool batadv_tt_global_check_crc(struct batadv_orig_node *orig_node, + struct batadv_tvlv_tt_vlan_data *tt_vlan, + uint16_t num_vlan) +{ + struct batadv_tvlv_tt_vlan_data *tt_vlan_tmp; + struct batadv_orig_node_vlan *vlan; + int i; -out: - return skb; + /* check if each received CRC matches the locally stored one */ + for (i = 0; i < num_vlan; i++) { + tt_vlan_tmp = tt_vlan + i; + + /* if orig_node is a backbone node for this VLAN, don't check + * the CRC as we ignore all the global entries over it + */ + if (batadv_bla_is_backbone_gw_orig(orig_node->bat_priv, + orig_node->orig, + ntohs(tt_vlan_tmp->vid))) + continue; + + vlan = batadv_orig_node_vlan_get(orig_node, + ntohs(tt_vlan_tmp->vid)); + if (!vlan) + return false; + + if (vlan->tt.crc != ntohl(tt_vlan_tmp->crc)) + return false; + } + + return true; +} + +/** + * batadv_tt_local_update_crc - update all the local CRCs + * @bat_priv: the bat priv with all the soft interface information + */ +static void batadv_tt_local_update_crc(struct batadv_priv *bat_priv) +{ + struct batadv_softif_vlan *vlan; + + /* recompute the global CRC for each VLAN */ + rcu_read_lock(); + hlist_for_each_entry_rcu(vlan, &bat_priv->softif_vlan_list, list) { + vlan->tt.crc = batadv_tt_local_crc(bat_priv, vlan->vid); + } + rcu_read_unlock(); +} + +/** + * batadv_tt_global_update_crc - update all the global CRCs for this orig_node + * @bat_priv: the bat priv with all the soft interface information + * @orig_node: the orig_node for which the CRCs have to be updated + */ +static void batadv_tt_global_update_crc(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node) +{ + struct batadv_orig_node_vlan *vlan; + uint32_t crc; + + /* recompute the global CRC for each VLAN */ + rcu_read_lock(); + list_for_each_entry_rcu(vlan, &orig_node->vlan_list, list) { + /* if orig_node is a backbone node for this VLAN, don't compute + * the CRC as we ignore all the global entries over it + */ + if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig, + vlan->vid)) + continue; + + crc = batadv_tt_global_crc(bat_priv, orig_node, vlan->vid); + vlan->tt.crc = crc; + } + rcu_read_unlock(); } +/** + * batadv_send_tt_request - send a TT Request message to a given node + * @bat_priv: the bat priv with all the soft interface information + * @dst_orig_node: the destination of the message + * @ttvn: the version number that the source of the message is looking for + * @tt_vlan: pointer to the first tvlv VLAN object to request + * @num_vlan: number of tvlv VLAN entries + * @full_table: ask for the entire translation table if true, while only for the + * last TT diff otherwise + */ static int batadv_send_tt_request(struct batadv_priv *bat_priv, struct batadv_orig_node *dst_orig_node, - uint8_t ttvn, uint16_t tt_crc, - bool full_table) + uint8_t ttvn, + struct batadv_tvlv_tt_vlan_data *tt_vlan, + uint16_t num_vlan, bool full_table) { - struct sk_buff *skb = NULL; - struct batadv_tt_query_packet *tt_request; - struct batadv_hard_iface *primary_if; + struct batadv_tvlv_tt_data *tvlv_tt_data = NULL; struct batadv_tt_req_node *tt_req_node = NULL; - int ret = 1; - size_t tt_req_len; + struct batadv_tvlv_tt_vlan_data *tt_vlan_req; + struct batadv_hard_iface *primary_if; + bool ret = false; + int i, size; primary_if = batadv_primary_if_get_selected(bat_priv); if (!primary_if) @@ -1688,157 +2349,171 @@ static int batadv_send_tt_request(struct batadv_priv *bat_priv, if (!tt_req_node) goto out; - skb = netdev_alloc_skb_ip_align(NULL, sizeof(*tt_request) + ETH_HLEN); - if (!skb) + size = sizeof(*tvlv_tt_data) + sizeof(*tt_vlan_req) * num_vlan; + tvlv_tt_data = kzalloc(size, GFP_ATOMIC); + if (!tvlv_tt_data) goto out; - skb->priority = TC_PRIO_CONTROL; - skb_reserve(skb, ETH_HLEN); + tvlv_tt_data->flags = BATADV_TT_REQUEST; + tvlv_tt_data->ttvn = ttvn; + tvlv_tt_data->num_vlan = htons(num_vlan); - tt_req_len = sizeof(*tt_request); - tt_request = (struct batadv_tt_query_packet *)skb_put(skb, tt_req_len); + /* send all the CRCs within the request. This is needed by intermediate + * nodes to ensure they have the correct table before replying + */ + tt_vlan_req = (struct batadv_tvlv_tt_vlan_data *)(tvlv_tt_data + 1); + for (i = 0; i < num_vlan; i++) { + tt_vlan_req->vid = tt_vlan->vid; + tt_vlan_req->crc = tt_vlan->crc; - tt_request->header.packet_type = BATADV_TT_QUERY; - tt_request->header.version = BATADV_COMPAT_VERSION; - memcpy(tt_request->src, primary_if->net_dev->dev_addr, ETH_ALEN); - memcpy(tt_request->dst, dst_orig_node->orig, ETH_ALEN); - tt_request->header.ttl = BATADV_TTL; - tt_request->ttvn = ttvn; - tt_request->tt_data = htons(tt_crc); - tt_request->flags = BATADV_TT_REQUEST; + tt_vlan_req++; + tt_vlan++; + } if (full_table) - tt_request->flags |= BATADV_TT_FULL_TABLE; + tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE; batadv_dbg(BATADV_DBG_TT, bat_priv, "Sending TT_REQUEST to %pM [%c]\n", - dst_orig_node->orig, (full_table ? 'F' : '.')); + dst_orig_node->orig, full_table ? 'F' : '.'); batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_TX); - - if (batadv_send_skb_to_orig(skb, dst_orig_node, NULL) != NET_XMIT_DROP) - ret = 0; + batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr, + dst_orig_node->orig, BATADV_TVLV_TT, 1, + tvlv_tt_data, size); + ret = true; out: if (primary_if) batadv_hardif_free_ref(primary_if); - if (ret) - kfree_skb(skb); if (ret && tt_req_node) { spin_lock_bh(&bat_priv->tt.req_list_lock); list_del(&tt_req_node->list); spin_unlock_bh(&bat_priv->tt.req_list_lock); kfree(tt_req_node); } + kfree(tvlv_tt_data); return ret; } -static bool -batadv_send_other_tt_response(struct batadv_priv *bat_priv, - struct batadv_tt_query_packet *tt_request) +/** + * batadv_send_other_tt_response - send reply to tt request concerning another + * node's translation table + * @bat_priv: the bat priv with all the soft interface information + * @tt_data: tt data containing the tt request information + * @req_src: mac address of tt request sender + * @req_dst: mac address of tt request recipient + * + * Returns true if tt request reply was sent, false otherwise. + */ +static bool batadv_send_other_tt_response(struct batadv_priv *bat_priv, + struct batadv_tvlv_tt_data *tt_data, + uint8_t *req_src, uint8_t *req_dst) { struct batadv_orig_node *req_dst_orig_node; struct batadv_orig_node *res_dst_orig_node = NULL; - uint8_t orig_ttvn, req_ttvn, ttvn; - int res, ret = false; - unsigned char *tt_buff; - bool full_table; - uint16_t tt_len, tt_tot; - struct sk_buff *skb = NULL; - struct batadv_tt_query_packet *tt_response; - uint8_t *packet_pos; - size_t len; + struct batadv_tvlv_tt_change *tt_change; + struct batadv_tvlv_tt_data *tvlv_tt_data = NULL; + struct batadv_tvlv_tt_vlan_data *tt_vlan; + bool ret = false, full_table; + uint8_t orig_ttvn, req_ttvn; + uint16_t tvlv_len; + int32_t tt_len; batadv_dbg(BATADV_DBG_TT, bat_priv, "Received TT_REQUEST from %pM for ttvn: %u (%pM) [%c]\n", - tt_request->src, tt_request->ttvn, tt_request->dst, - (tt_request->flags & BATADV_TT_FULL_TABLE ? 'F' : '.')); + req_src, tt_data->ttvn, req_dst, + (tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.')); /* Let's get the orig node of the REAL destination */ - req_dst_orig_node = batadv_orig_hash_find(bat_priv, tt_request->dst); + req_dst_orig_node = batadv_orig_hash_find(bat_priv, req_dst); if (!req_dst_orig_node) goto out; - res_dst_orig_node = batadv_orig_hash_find(bat_priv, tt_request->src); + res_dst_orig_node = batadv_orig_hash_find(bat_priv, req_src); if (!res_dst_orig_node) goto out; orig_ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn); - req_ttvn = tt_request->ttvn; + req_ttvn = tt_data->ttvn; - /* I don't have the requested data */ + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1); + /* this node doesn't have the requested data */ if (orig_ttvn != req_ttvn || - tt_request->tt_data != htons(req_dst_orig_node->tt_crc)) + !batadv_tt_global_check_crc(req_dst_orig_node, tt_vlan, + ntohs(tt_data->num_vlan))) goto out; /* If the full table has been explicitly requested */ - if (tt_request->flags & BATADV_TT_FULL_TABLE || + if (tt_data->flags & BATADV_TT_FULL_TABLE || !req_dst_orig_node->tt_buff) full_table = true; else full_table = false; - /* In this version, fragmentation is not implemented, then - * I'll send only one packet with as much TT entries as I can + /* TT fragmentation hasn't been implemented yet, so send as many + * TT entries fit a single packet as possible only */ if (!full_table) { spin_lock_bh(&req_dst_orig_node->tt_buff_lock); tt_len = req_dst_orig_node->tt_buff_len; - tt_tot = tt_len / sizeof(struct batadv_tt_change); - len = sizeof(*tt_response) + tt_len; - skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); - if (!skb) + tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node, + &tvlv_tt_data, + &tt_change, + &tt_len); + if (!tt_len) goto unlock; - skb->priority = TC_PRIO_CONTROL; - skb_reserve(skb, ETH_HLEN); - packet_pos = skb_put(skb, len); - tt_response = (struct batadv_tt_query_packet *)packet_pos; - tt_response->ttvn = req_ttvn; - tt_response->tt_data = htons(tt_tot); - - tt_buff = skb->data + sizeof(*tt_response); /* Copy the last orig_node's OGM buffer */ - memcpy(tt_buff, req_dst_orig_node->tt_buff, + memcpy(tt_change, req_dst_orig_node->tt_buff, req_dst_orig_node->tt_buff_len); - spin_unlock_bh(&req_dst_orig_node->tt_buff_lock); } else { - tt_len = (uint16_t)atomic_read(&req_dst_orig_node->tt_size); - tt_len *= sizeof(struct batadv_tt_change); - ttvn = (uint8_t)atomic_read(&req_dst_orig_node->last_ttvn); - - skb = batadv_tt_response_fill_table(tt_len, ttvn, - bat_priv->tt.global_hash, - bat_priv, - batadv_tt_global_valid, - req_dst_orig_node); - if (!skb) + /* allocate the tvlv, put the tt_data and all the tt_vlan_data + * in the initial part + */ + tt_len = -1; + tvlv_len = batadv_tt_prepare_tvlv_global_data(req_dst_orig_node, + &tvlv_tt_data, + &tt_change, + &tt_len); + if (!tt_len) goto out; - tt_response = (struct batadv_tt_query_packet *)skb->data; + /* fill the rest of the tvlv with the real TT entries */ + batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.global_hash, + tt_change, tt_len, + batadv_tt_global_valid, + req_dst_orig_node); + } + + /* Don't send the response, if larger than fragmented packet. */ + tt_len = sizeof(struct batadv_unicast_tvlv_packet) + tvlv_len; + if (tt_len > atomic_read(&bat_priv->packet_size_max)) { + net_ratelimited_function(batadv_info, bat_priv->soft_iface, + "Ignoring TT_REQUEST from %pM; Response size exceeds max packet size.\n", + res_dst_orig_node->orig); + goto out; } - tt_response->header.packet_type = BATADV_TT_QUERY; - tt_response->header.version = BATADV_COMPAT_VERSION; - tt_response->header.ttl = BATADV_TTL; - memcpy(tt_response->src, req_dst_orig_node->orig, ETH_ALEN); - memcpy(tt_response->dst, tt_request->src, ETH_ALEN); - tt_response->flags = BATADV_TT_RESPONSE; + tvlv_tt_data->flags = BATADV_TT_RESPONSE; + tvlv_tt_data->ttvn = req_ttvn; if (full_table) - tt_response->flags |= BATADV_TT_FULL_TABLE; + tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE; batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending TT_RESPONSE %pM for %pM (ttvn: %u)\n", - res_dst_orig_node->orig, req_dst_orig_node->orig, req_ttvn); + "Sending TT_RESPONSE %pM for %pM [%c] (ttvn: %u)\n", + res_dst_orig_node->orig, req_dst_orig_node->orig, + full_table ? 'F' : '.', req_ttvn); batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX); - res = batadv_send_skb_to_orig(skb, res_dst_orig_node, NULL); - if (res != NET_XMIT_DROP) - ret = true; + batadv_tvlv_unicast_send(bat_priv, req_dst_orig_node->orig, + req_src, BATADV_TVLV_TT, 1, tvlv_tt_data, + tvlv_len); + ret = true; goto out; unlock: @@ -1849,37 +2524,43 @@ out: batadv_orig_node_free_ref(res_dst_orig_node); if (req_dst_orig_node) batadv_orig_node_free_ref(req_dst_orig_node); - if (!ret) - kfree_skb(skb); + kfree(tvlv_tt_data); return ret; } -static bool -batadv_send_my_tt_response(struct batadv_priv *bat_priv, - struct batadv_tt_query_packet *tt_request) +/** + * batadv_send_my_tt_response - send reply to tt request concerning this node's + * translation table + * @bat_priv: the bat priv with all the soft interface information + * @tt_data: tt data containing the tt request information + * @req_src: mac address of tt request sender + * + * Returns true if tt request reply was sent, false otherwise. + */ +static bool batadv_send_my_tt_response(struct batadv_priv *bat_priv, + struct batadv_tvlv_tt_data *tt_data, + uint8_t *req_src) { - struct batadv_orig_node *orig_node; + struct batadv_tvlv_tt_data *tvlv_tt_data = NULL; struct batadv_hard_iface *primary_if = NULL; - uint8_t my_ttvn, req_ttvn, ttvn; - int ret = false; - unsigned char *tt_buff; + struct batadv_tvlv_tt_change *tt_change; + struct batadv_orig_node *orig_node; + uint8_t my_ttvn, req_ttvn; + uint16_t tvlv_len; bool full_table; - uint16_t tt_len, tt_tot; - struct sk_buff *skb = NULL; - struct batadv_tt_query_packet *tt_response; - uint8_t *packet_pos; - size_t len; + int32_t tt_len; batadv_dbg(BATADV_DBG_TT, bat_priv, "Received TT_REQUEST from %pM for ttvn: %u (me) [%c]\n", - tt_request->src, tt_request->ttvn, - (tt_request->flags & BATADV_TT_FULL_TABLE ? 'F' : '.')); + req_src, tt_data->ttvn, + (tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.')); + spin_lock_bh(&bat_priv->tt.commit_lock); my_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); - req_ttvn = tt_request->ttvn; + req_ttvn = tt_data->ttvn; - orig_node = batadv_orig_hash_find(bat_priv, tt_request->src); + orig_node = batadv_orig_hash_find(bat_priv, req_src); if (!orig_node) goto out; @@ -1890,103 +2571,104 @@ batadv_send_my_tt_response(struct batadv_priv *bat_priv, /* If the full table has been explicitly requested or the gap * is too big send the whole local translation table */ - if (tt_request->flags & BATADV_TT_FULL_TABLE || my_ttvn != req_ttvn || + if (tt_data->flags & BATADV_TT_FULL_TABLE || my_ttvn != req_ttvn || !bat_priv->tt.last_changeset) full_table = true; else full_table = false; - /* In this version, fragmentation is not implemented, then - * I'll send only one packet with as much TT entries as I can + /* TT fragmentation hasn't been implemented yet, so send as many + * TT entries fit a single packet as possible only */ if (!full_table) { spin_lock_bh(&bat_priv->tt.last_changeset_lock); - tt_len = bat_priv->tt.last_changeset_len; - tt_tot = tt_len / sizeof(struct batadv_tt_change); - len = sizeof(*tt_response) + tt_len; - skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); - if (!skb) + tt_len = bat_priv->tt.last_changeset_len; + tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, + &tvlv_tt_data, + &tt_change, + &tt_len); + if (!tt_len) goto unlock; - skb->priority = TC_PRIO_CONTROL; - skb_reserve(skb, ETH_HLEN); - packet_pos = skb_put(skb, len); - tt_response = (struct batadv_tt_query_packet *)packet_pos; - tt_response->ttvn = req_ttvn; - tt_response->tt_data = htons(tt_tot); - - tt_buff = skb->data + sizeof(*tt_response); - memcpy(tt_buff, bat_priv->tt.last_changeset, + /* Copy the last orig_node's OGM buffer */ + memcpy(tt_change, bat_priv->tt.last_changeset, bat_priv->tt.last_changeset_len); spin_unlock_bh(&bat_priv->tt.last_changeset_lock); } else { - tt_len = (uint16_t)atomic_read(&bat_priv->tt.local_entry_num); - tt_len *= sizeof(struct batadv_tt_change); - ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); - - skb = batadv_tt_response_fill_table(tt_len, ttvn, - bat_priv->tt.local_hash, - bat_priv, - batadv_tt_local_valid_entry, - NULL); - if (!skb) + req_ttvn = (uint8_t)atomic_read(&bat_priv->tt.vn); + + /* allocate the tvlv, put the tt_data and all the tt_vlan_data + * in the initial part + */ + tt_len = -1; + tvlv_len = batadv_tt_prepare_tvlv_local_data(bat_priv, + &tvlv_tt_data, + &tt_change, + &tt_len); + if (!tt_len) goto out; - tt_response = (struct batadv_tt_query_packet *)skb->data; + /* fill the rest of the tvlv with the real TT entries */ + batadv_tt_tvlv_generate(bat_priv, bat_priv->tt.local_hash, + tt_change, tt_len, + batadv_tt_local_valid, NULL); } - tt_response->header.packet_type = BATADV_TT_QUERY; - tt_response->header.version = BATADV_COMPAT_VERSION; - tt_response->header.ttl = BATADV_TTL; - memcpy(tt_response->src, primary_if->net_dev->dev_addr, ETH_ALEN); - memcpy(tt_response->dst, tt_request->src, ETH_ALEN); - tt_response->flags = BATADV_TT_RESPONSE; + tvlv_tt_data->flags = BATADV_TT_RESPONSE; + tvlv_tt_data->ttvn = req_ttvn; if (full_table) - tt_response->flags |= BATADV_TT_FULL_TABLE; + tvlv_tt_data->flags |= BATADV_TT_FULL_TABLE; batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending TT_RESPONSE to %pM [%c]\n", - orig_node->orig, - (tt_response->flags & BATADV_TT_FULL_TABLE ? 'F' : '.')); + "Sending TT_RESPONSE to %pM [%c] (ttvn: %u)\n", + orig_node->orig, full_table ? 'F' : '.', req_ttvn); batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_TX); - if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) - ret = true; + batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr, + req_src, BATADV_TVLV_TT, 1, tvlv_tt_data, + tvlv_len); + goto out; unlock: spin_unlock_bh(&bat_priv->tt.last_changeset_lock); out: + spin_unlock_bh(&bat_priv->tt.commit_lock); if (orig_node) batadv_orig_node_free_ref(orig_node); if (primary_if) batadv_hardif_free_ref(primary_if); - if (!ret) - kfree_skb(skb); - /* This packet was for me, so it doesn't need to be re-routed */ + kfree(tvlv_tt_data); + /* The packet was for this host, so it doesn't need to be re-routed */ return true; } -bool batadv_send_tt_response(struct batadv_priv *bat_priv, - struct batadv_tt_query_packet *tt_request) +/** + * batadv_send_tt_response - send reply to tt request + * @bat_priv: the bat priv with all the soft interface information + * @tt_data: tt data containing the tt request information + * @req_src: mac address of tt request sender + * @req_dst: mac address of tt request recipient + * + * Returns true if tt request reply was sent, false otherwise. + */ +static bool batadv_send_tt_response(struct batadv_priv *bat_priv, + struct batadv_tvlv_tt_data *tt_data, + uint8_t *req_src, uint8_t *req_dst) { - if (batadv_is_my_mac(bat_priv, tt_request->dst)) { - /* don't answer backbone gws! */ - if (batadv_bla_is_backbone_gw_orig(bat_priv, tt_request->src)) - return true; - - return batadv_send_my_tt_response(bat_priv, tt_request); - } else { - return batadv_send_other_tt_response(bat_priv, tt_request); - } + if (batadv_is_my_mac(bat_priv, req_dst)) + return batadv_send_my_tt_response(bat_priv, tt_data, req_src); + else + return batadv_send_other_tt_response(bat_priv, tt_data, + req_src, req_dst); } static void _batadv_tt_update_changes(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - struct batadv_tt_change *tt_change, + struct batadv_tvlv_tt_change *tt_change, uint16_t tt_num_changes, uint8_t ttvn) { int i; @@ -1997,11 +2679,13 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv, roams = (tt_change + i)->flags & BATADV_TT_CLIENT_ROAM; batadv_tt_global_del(bat_priv, orig_node, (tt_change + i)->addr, + ntohs((tt_change + i)->vid), "tt removed by changes", roams); } else { if (!batadv_tt_global_add(bat_priv, orig_node, (tt_change + i)->addr, + ntohs((tt_change + i)->vid), (tt_change + i)->flags, ttvn)) /* In case of problem while storing a * global_entry, we stop the updating @@ -2016,21 +2700,22 @@ static void _batadv_tt_update_changes(struct batadv_priv *bat_priv, } static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv, - struct batadv_tt_query_packet *tt_response) + struct batadv_tvlv_tt_change *tt_change, + uint8_t ttvn, uint8_t *resp_src, + uint16_t num_entries) { struct batadv_orig_node *orig_node; - orig_node = batadv_orig_hash_find(bat_priv, tt_response->src); + orig_node = batadv_orig_hash_find(bat_priv, resp_src); if (!orig_node) goto out; /* Purge the old table first.. */ - batadv_tt_global_del_orig(bat_priv, orig_node, "Received full table"); + batadv_tt_global_del_orig(bat_priv, orig_node, -1, + "Received full table"); - _batadv_tt_update_changes(bat_priv, orig_node, - (struct batadv_tt_change *)(tt_response + 1), - ntohs(tt_response->tt_data), - tt_response->ttvn); + _batadv_tt_update_changes(bat_priv, orig_node, tt_change, num_entries, + ttvn); spin_lock_bh(&orig_node->tt_buff_lock); kfree(orig_node->tt_buff); @@ -2038,7 +2723,7 @@ static void batadv_tt_fill_gtable(struct batadv_priv *bat_priv, orig_node->tt_buff = NULL; spin_unlock_bh(&orig_node->tt_buff_lock); - atomic_set(&orig_node->last_ttvn, tt_response->ttvn); + atomic_set(&orig_node->last_ttvn, ttvn); out: if (orig_node) @@ -2048,22 +2733,31 @@ out: static void batadv_tt_update_changes(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, uint16_t tt_num_changes, uint8_t ttvn, - struct batadv_tt_change *tt_change) + struct batadv_tvlv_tt_change *tt_change) { _batadv_tt_update_changes(bat_priv, orig_node, tt_change, tt_num_changes, ttvn); - batadv_tt_save_orig_buffer(bat_priv, orig_node, - (unsigned char *)tt_change, tt_num_changes); + batadv_tt_save_orig_buffer(bat_priv, orig_node, tt_change, + batadv_tt_len(tt_num_changes)); atomic_set(&orig_node->last_ttvn, ttvn); } -bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr) +/** + * batadv_is_my_client - check if a client is served by the local node + * @bat_priv: the bat priv with all the soft interface information + * @addr: the mac adress of the client to check + * @vid: VLAN identifier + * + * Returns true if the client is served by this node, false otherwise. + */ +bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr, + unsigned short vid) { struct batadv_tt_local_entry *tt_local_entry; bool ret = false; - tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); + tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid); if (!tt_local_entry) goto out; /* Check if the client has been logically deleted (but is kept for @@ -2079,72 +2773,68 @@ out: return ret; } -void batadv_handle_tt_response(struct batadv_priv *bat_priv, - struct batadv_tt_query_packet *tt_response) +/** + * batadv_handle_tt_response - process incoming tt reply + * @bat_priv: the bat priv with all the soft interface information + * @tt_data: tt data containing the tt request information + * @resp_src: mac address of tt reply sender + * @num_entries: number of tt change entries appended to the tt data + */ +static void batadv_handle_tt_response(struct batadv_priv *bat_priv, + struct batadv_tvlv_tt_data *tt_data, + uint8_t *resp_src, uint16_t num_entries) { struct batadv_tt_req_node *node, *safe; struct batadv_orig_node *orig_node = NULL; - struct batadv_tt_change *tt_change; + struct batadv_tvlv_tt_change *tt_change; + uint8_t *tvlv_ptr = (uint8_t *)tt_data; + uint16_t change_offset; batadv_dbg(BATADV_DBG_TT, bat_priv, "Received TT_RESPONSE from %pM for ttvn %d t_size: %d [%c]\n", - tt_response->src, tt_response->ttvn, - ntohs(tt_response->tt_data), - (tt_response->flags & BATADV_TT_FULL_TABLE ? 'F' : '.')); - - /* we should have never asked a backbone gw */ - if (batadv_bla_is_backbone_gw_orig(bat_priv, tt_response->src)) - goto out; + resp_src, tt_data->ttvn, num_entries, + (tt_data->flags & BATADV_TT_FULL_TABLE ? 'F' : '.')); - orig_node = batadv_orig_hash_find(bat_priv, tt_response->src); + orig_node = batadv_orig_hash_find(bat_priv, resp_src); if (!orig_node) goto out; - if (tt_response->flags & BATADV_TT_FULL_TABLE) { - batadv_tt_fill_gtable(bat_priv, tt_response); + spin_lock_bh(&orig_node->tt_lock); + + change_offset = sizeof(struct batadv_tvlv_tt_vlan_data); + change_offset *= ntohs(tt_data->num_vlan); + change_offset += sizeof(*tt_data); + tvlv_ptr += change_offset; + + tt_change = (struct batadv_tvlv_tt_change *)tvlv_ptr; + if (tt_data->flags & BATADV_TT_FULL_TABLE) { + batadv_tt_fill_gtable(bat_priv, tt_change, tt_data->ttvn, + resp_src, num_entries); } else { - tt_change = (struct batadv_tt_change *)(tt_response + 1); - batadv_tt_update_changes(bat_priv, orig_node, - ntohs(tt_response->tt_data), - tt_response->ttvn, tt_change); + batadv_tt_update_changes(bat_priv, orig_node, num_entries, + tt_data->ttvn, tt_change); } + /* Recalculate the CRC for this orig_node and store it */ + batadv_tt_global_update_crc(bat_priv, orig_node); + + spin_unlock_bh(&orig_node->tt_lock); + /* Delete the tt_req_node from pending tt_requests list */ spin_lock_bh(&bat_priv->tt.req_list_lock); list_for_each_entry_safe(node, safe, &bat_priv->tt.req_list, list) { - if (!batadv_compare_eth(node->addr, tt_response->src)) + if (!batadv_compare_eth(node->addr, resp_src)) continue; list_del(&node->list); kfree(node); } - spin_unlock_bh(&bat_priv->tt.req_list_lock); - /* Recalculate the CRC for this orig_node and store it */ - orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node); + spin_unlock_bh(&bat_priv->tt.req_list_lock); out: if (orig_node) batadv_orig_node_free_ref(orig_node); } -int batadv_tt_init(struct batadv_priv *bat_priv) -{ - int ret; - - ret = batadv_tt_local_init(bat_priv); - if (ret < 0) - return ret; - - ret = batadv_tt_global_init(bat_priv); - if (ret < 0) - return ret; - - INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge); - queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work, - msecs_to_jiffies(BATADV_TT_WORK_PERIOD)); - - return 1; -} - static void batadv_tt_roam_list_free(struct batadv_priv *bat_priv) { struct batadv_tt_roam_node *node, *safe; @@ -2225,14 +2915,28 @@ unlock: return ret; } +/** + * batadv_send_roam_adv - send a roaming advertisement message + * @bat_priv: the bat priv with all the soft interface information + * @client: mac address of the roaming client + * @vid: VLAN identifier + * @orig_node: message destination + * + * Send a ROAMING_ADV message to the node which was previously serving this + * client. This is done to inform the node that from now on all traffic destined + * for this particular roamed client has to be forwarded to the sender of the + * roaming message. + */ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, + unsigned short vid, struct batadv_orig_node *orig_node) { - struct sk_buff *skb = NULL; - struct batadv_roam_adv_packet *roam_adv_packet; - int ret = 1; struct batadv_hard_iface *primary_if; - size_t len = sizeof(*roam_adv_packet); + struct batadv_tvlv_roam_adv tvlv_roam; + + primary_if = batadv_primary_if_get_selected(bat_priv); + if (!primary_if) + goto out; /* before going on we have to check whether the client has * already roamed to us too many times @@ -2240,40 +2944,22 @@ static void batadv_send_roam_adv(struct batadv_priv *bat_priv, uint8_t *client, if (!batadv_tt_check_roam_count(bat_priv, client)) goto out; - skb = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); - if (!skb) - goto out; - - skb->priority = TC_PRIO_CONTROL; - skb_reserve(skb, ETH_HLEN); - - roam_adv_packet = (struct batadv_roam_adv_packet *)skb_put(skb, len); - - roam_adv_packet->header.packet_type = BATADV_ROAM_ADV; - roam_adv_packet->header.version = BATADV_COMPAT_VERSION; - roam_adv_packet->header.ttl = BATADV_TTL; - roam_adv_packet->reserved = 0; - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - goto out; - memcpy(roam_adv_packet->src, primary_if->net_dev->dev_addr, ETH_ALEN); - batadv_hardif_free_ref(primary_if); - memcpy(roam_adv_packet->dst, orig_node->orig, ETH_ALEN); - memcpy(roam_adv_packet->client, client, ETH_ALEN); - batadv_dbg(BATADV_DBG_TT, bat_priv, - "Sending ROAMING_ADV to %pM (client %pM)\n", - orig_node->orig, client); + "Sending ROAMING_ADV to %pM (client %pM, vid: %d)\n", + orig_node->orig, client, BATADV_PRINT_VID(vid)); batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_TX); - if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) - ret = 0; + memcpy(tvlv_roam.client, client, sizeof(tvlv_roam.client)); + tvlv_roam.vid = htons(vid); + + batadv_tvlv_unicast_send(bat_priv, primary_if->net_dev->dev_addr, + orig_node->orig, BATADV_TVLV_ROAM, 1, + &tvlv_roam, sizeof(tvlv_roam)); out: - if (ret && skb) - kfree_skb(skb); - return; + if (primary_if) + batadv_hardif_free_ref(primary_if); } static void batadv_tt_purge(struct work_struct *work) @@ -2286,7 +2972,7 @@ static void batadv_tt_purge(struct work_struct *work) priv_tt = container_of(delayed_work, struct batadv_priv_tt, work); bat_priv = container_of(priv_tt, struct batadv_priv, tt); - batadv_tt_local_purge(bat_priv); + batadv_tt_local_purge(bat_priv, BATADV_TT_LOCAL_TIMEOUT); batadv_tt_global_purge(bat_priv); batadv_tt_req_purge(bat_priv); batadv_tt_roam_purge(bat_priv); @@ -2297,6 +2983,9 @@ static void batadv_tt_purge(struct work_struct *work) void batadv_tt_free(struct batadv_priv *bat_priv) { + batadv_tvlv_container_unregister(bat_priv, BATADV_TVLV_TT, 1); + batadv_tvlv_handler_unregister(bat_priv, BATADV_TVLV_TT, 1); + cancel_delayed_work_sync(&bat_priv->tt.work); batadv_tt_local_table_free(bat_priv); @@ -2308,19 +2997,25 @@ void batadv_tt_free(struct batadv_priv *bat_priv) kfree(bat_priv->tt.last_changeset); } -/* This function will enable or disable the specified flags for all the entries - * in the given hash table and returns the number of modified entries +/** + * batadv_tt_local_set_flags - set or unset the specified flags on the local + * table and possibly count them in the TT size + * @bat_priv: the bat priv with all the soft interface information + * @flags: the flag to switch + * @enable: whether to set or unset the flag + * @count: whether to increase the TT size by the number of changed entries */ -static uint16_t batadv_tt_set_flags(struct batadv_hashtable *hash, - uint16_t flags, bool enable) +static void batadv_tt_local_set_flags(struct batadv_priv *bat_priv, + uint16_t flags, bool enable, bool count) { - uint32_t i; + struct batadv_hashtable *hash = bat_priv->tt.local_hash; + struct batadv_tt_common_entry *tt_common_entry; uint16_t changed_num = 0; struct hlist_head *head; - struct batadv_tt_common_entry *tt_common_entry; + uint32_t i; if (!hash) - goto out; + return; for (i = 0; i < hash->size; i++) { head = &hash->table[i]; @@ -2338,11 +3033,15 @@ static uint16_t batadv_tt_set_flags(struct batadv_hashtable *hash, tt_common_entry->flags &= ~flags; } changed_num++; + + if (!count) + continue; + + batadv_tt_local_size_inc(bat_priv, + tt_common_entry->vid); } rcu_read_unlock(); } -out: - return changed_num; } /* Purge out all the tt local entries marked with BATADV_TT_CLIENT_PENDING */ @@ -2370,10 +3069,11 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) continue; batadv_dbg(BATADV_DBG_TT, bat_priv, - "Deleting local tt entry (%pM): pending\n", - tt_common->addr); + "Deleting local tt entry (%pM, vid: %d): pending\n", + tt_common->addr, + BATADV_PRINT_VID(tt_common->vid)); - atomic_dec(&bat_priv->tt.local_entry_num); + batadv_tt_local_size_dec(bat_priv, tt_common->vid); hlist_del_rcu(&tt_common->hash_entry); tt_local = container_of(tt_common, struct batadv_tt_local_entry, @@ -2384,22 +3084,25 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) } } -static int batadv_tt_commit_changes(struct batadv_priv *bat_priv, - unsigned char **packet_buff, - int *packet_buff_len, int packet_min_len) +/** + * batadv_tt_local_commit_changes_nolock - commit all pending local tt changes + * which have been queued in the time since the last commit + * @bat_priv: the bat priv with all the soft interface information + * + * Caller must hold tt->commit_lock. + */ +static void batadv_tt_local_commit_changes_nolock(struct batadv_priv *bat_priv) { - uint16_t changed_num = 0; - - if (atomic_read(&bat_priv->tt.local_changes) < 1) - return -ENOENT; + if (atomic_read(&bat_priv->tt.local_changes) < 1) { + if (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt)) + batadv_tt_tvlv_container_update(bat_priv); + return; + } - changed_num = batadv_tt_set_flags(bat_priv->tt.local_hash, - BATADV_TT_CLIENT_NEW, false); + batadv_tt_local_set_flags(bat_priv, BATADV_TT_CLIENT_NEW, false, true); - /* all reset entries have to be counted as local entries */ - atomic_add(changed_num, &bat_priv->tt.local_entry_num); batadv_tt_local_purge_pending_clients(bat_priv); - bat_priv->tt.local_crc = batadv_tt_local_crc(bat_priv); + batadv_tt_local_update_crc(bat_priv); /* Increment the TTVN only once per OGM interval */ atomic_inc(&bat_priv->tt.vn); @@ -2409,49 +3112,38 @@ static int batadv_tt_commit_changes(struct batadv_priv *bat_priv, /* reset the sending counter */ atomic_set(&bat_priv->tt.ogm_append_cnt, BATADV_TT_OGM_APPEND_MAX); - - return batadv_tt_changes_fill_buff(bat_priv, packet_buff, - packet_buff_len, packet_min_len); + batadv_tt_tvlv_container_update(bat_priv); } -/* when calling this function (hard_iface == primary_if) has to be true */ -int batadv_tt_append_diff(struct batadv_priv *bat_priv, - unsigned char **packet_buff, int *packet_buff_len, - int packet_min_len) +/** + * batadv_tt_local_commit_changes - commit all pending local tt changes which + * have been queued in the time since the last commit + * @bat_priv: the bat priv with all the soft interface information + */ +void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv) { - int tt_num_changes; - - /* if at least one change happened */ - tt_num_changes = batadv_tt_commit_changes(bat_priv, packet_buff, - packet_buff_len, - packet_min_len); - - /* if the changes have been sent often enough */ - if ((tt_num_changes < 0) && - (!batadv_atomic_dec_not_zero(&bat_priv->tt.ogm_append_cnt))) { - batadv_tt_realloc_packet_buff(packet_buff, packet_buff_len, - packet_min_len, packet_min_len); - tt_num_changes = 0; - } - - return tt_num_changes; + spin_lock_bh(&bat_priv->tt.commit_lock); + batadv_tt_local_commit_changes_nolock(bat_priv); + spin_unlock_bh(&bat_priv->tt.commit_lock); } bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src, - uint8_t *dst) + uint8_t *dst, unsigned short vid) { struct batadv_tt_local_entry *tt_local_entry = NULL; struct batadv_tt_global_entry *tt_global_entry = NULL; + struct batadv_softif_vlan *vlan; bool ret = false; - if (!atomic_read(&bat_priv->ap_isolation)) + vlan = batadv_softif_vlan_get(bat_priv, vid); + if (!vlan || !atomic_read(&vlan->ap_isolation)) goto out; - tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst); + tt_local_entry = batadv_tt_local_hash_find(bat_priv, dst, vid); if (!tt_local_entry) goto out; - tt_global_entry = batadv_tt_global_hash_find(bat_priv, src); + tt_global_entry = batadv_tt_global_hash_find(bat_priv, src, vid); if (!tt_global_entry) goto out; @@ -2461,6 +3153,8 @@ bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src, ret = true; out: + if (vlan) + batadv_softif_vlan_free_ref(vlan); if (tt_global_entry) batadv_tt_global_entry_free_ref(tt_global_entry); if (tt_local_entry) @@ -2468,19 +3162,29 @@ out: return ret; } -void batadv_tt_update_orig(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node, - const unsigned char *tt_buff, uint8_t tt_num_changes, - uint8_t ttvn, uint16_t tt_crc) +/** + * batadv_tt_update_orig - update global translation table with new tt + * information received via ogms + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node of the ogm + * @tt_vlan: pointer to the first tvlv VLAN entry + * @tt_num_vlan: number of tvlv VLAN entries + * @tt_change: pointer to the first entry in the TT buffer + * @tt_num_changes: number of tt changes inside the tt buffer + * @ttvn: translation table version number of this changeset + * @tt_crc: crc32 checksum of orig node's translation table + */ +static void batadv_tt_update_orig(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig_node, + const void *tt_buff, uint16_t tt_num_vlan, + struct batadv_tvlv_tt_change *tt_change, + uint16_t tt_num_changes, uint8_t ttvn) { uint8_t orig_ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); + struct batadv_tvlv_tt_vlan_data *tt_vlan; bool full_table = true; - struct batadv_tt_change *tt_change; - - /* don't care about a backbone gateways updates. */ - if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig)) - return; + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)tt_buff; /* orig table not initialised AND first diff is in the OGM OR the ttvn * increased by one -> we can apply the attached changes */ @@ -2496,7 +3200,9 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv, goto request_table; } - tt_change = (struct batadv_tt_change *)tt_buff; + spin_lock_bh(&orig_node->tt_lock); + + tt_change = (struct batadv_tvlv_tt_change *)tt_buff; batadv_tt_update_changes(bat_priv, orig_node, tt_num_changes, ttvn, tt_change); @@ -2504,7 +3210,9 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv, * prefer to recompute it to spot any possible inconsistency * in the global table */ - orig_node->tt_crc = batadv_tt_global_crc(bat_priv, orig_node); + batadv_tt_global_update_crc(bat_priv, orig_node); + + spin_unlock_bh(&orig_node->tt_lock); /* The ttvn alone is not enough to guarantee consistency * because a single value could represent different states @@ -2515,37 +3223,46 @@ void batadv_tt_update_orig(struct batadv_priv *bat_priv, * checking the CRC value is mandatory to detect the * inconsistency */ - if (orig_node->tt_crc != tt_crc) + if (!batadv_tt_global_check_crc(orig_node, tt_vlan, + tt_num_vlan)) goto request_table; } else { /* if we missed more than one change or our tables are not * in sync anymore -> request fresh tt data */ if (!orig_node->tt_initialised || ttvn != orig_ttvn || - orig_node->tt_crc != tt_crc) { + !batadv_tt_global_check_crc(orig_node, tt_vlan, + tt_num_vlan)) { request_table: batadv_dbg(BATADV_DBG_TT, bat_priv, - "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u crc: %#.4x last_crc: %#.4x num_changes: %u)\n", - orig_node->orig, ttvn, orig_ttvn, tt_crc, - orig_node->tt_crc, tt_num_changes); + "TT inconsistency for %pM. Need to retrieve the correct information (ttvn: %u last_ttvn: %u num_changes: %u)\n", + orig_node->orig, ttvn, orig_ttvn, + tt_num_changes); batadv_send_tt_request(bat_priv, orig_node, ttvn, - tt_crc, full_table); + tt_vlan, tt_num_vlan, + full_table); return; } } } -/* returns true whether we know that the client has moved from its old - * originator to another one. This entry is kept is still kept for consistency - * purposes +/** + * batadv_tt_global_client_is_roaming - check if a client is marked as roaming + * @bat_priv: the bat priv with all the soft interface information + * @addr: the mac address of the client to check + * @vid: VLAN identifier + * + * Returns true if we know that the client has moved from its old originator + * to another one. This entry is still kept for consistency purposes and will be + * deleted later by a DEL or because of timeout */ bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv, - uint8_t *addr) + uint8_t *addr, unsigned short vid) { struct batadv_tt_global_entry *tt_global_entry; bool ret = false; - tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr); + tt_global_entry = batadv_tt_global_hash_find(bat_priv, addr, vid); if (!tt_global_entry) goto out; @@ -2558,19 +3275,20 @@ out: /** * batadv_tt_local_client_is_roaming - tells whether the client is roaming * @bat_priv: the bat priv with all the soft interface information - * @addr: the MAC address of the local client to query + * @addr: the mac address of the local client to query + * @vid: VLAN identifier * * Returns true if the local client is known to be roaming (it is not served by * this node anymore) or not. If yes, the client is still present in the table * to keep the latter consistent with the node TTVN */ bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv, - uint8_t *addr) + uint8_t *addr, unsigned short vid) { struct batadv_tt_local_entry *tt_local_entry; bool ret = false; - tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr); + tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid); if (!tt_local_entry) goto out; @@ -2582,26 +3300,268 @@ out: bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - const unsigned char *addr) + const unsigned char *addr, + unsigned short vid) { bool ret = false; - /* if the originator is a backbone node (meaning it belongs to the same - * LAN of this node) the temporary client must not be added because to - * reach such destination the node must use the LAN instead of the mesh - */ - if (batadv_bla_is_backbone_gw_orig(bat_priv, orig_node->orig)) - goto out; - - if (!batadv_tt_global_add(bat_priv, orig_node, addr, + if (!batadv_tt_global_add(bat_priv, orig_node, addr, vid, BATADV_TT_CLIENT_TEMP, atomic_read(&orig_node->last_ttvn))) goto out; batadv_dbg(BATADV_DBG_TT, bat_priv, - "Added temporary global client (addr: %pM orig: %pM)\n", - addr, orig_node->orig); + "Added temporary global client (addr: %pM, vid: %d, orig: %pM)\n", + addr, BATADV_PRINT_VID(vid), orig_node->orig); ret = true; out: return ret; } + +/** + * batadv_tt_local_resize_to_mtu - resize the local translation table fit the + * maximum packet size that can be transported through the mesh + * @soft_iface: netdev struct of the mesh interface + * + * Remove entries older than 'timeout' and half timeout if more entries need + * to be removed. + */ +void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface) +{ + struct batadv_priv *bat_priv = netdev_priv(soft_iface); + int packet_size_max = atomic_read(&bat_priv->packet_size_max); + int table_size, timeout = BATADV_TT_LOCAL_TIMEOUT / 2; + bool reduced = false; + + spin_lock_bh(&bat_priv->tt.commit_lock); + + while (true) { + table_size = batadv_tt_local_table_transmit_size(bat_priv); + if (packet_size_max >= table_size) + break; + + batadv_tt_local_purge(bat_priv, timeout); + batadv_tt_local_purge_pending_clients(bat_priv); + + timeout /= 2; + reduced = true; + net_ratelimited_function(batadv_info, soft_iface, + "Forced to purge local tt entries to fit new maximum fragment MTU (%i)\n", + packet_size_max); + } + + /* commit these changes immediately, to avoid synchronization problem + * with the TTVN + */ + if (reduced) + batadv_tt_local_commit_changes_nolock(bat_priv); + + spin_unlock_bh(&bat_priv->tt.commit_lock); +} + +/** + * batadv_tt_tvlv_ogm_handler_v1 - process incoming tt tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @orig: the orig_node of the ogm + * @flags: flags indicating the tvlv state (see batadv_tvlv_handler_flags) + * @tvlv_value: tvlv buffer containing the gateway data + * @tvlv_value_len: tvlv buffer length + */ +static void batadv_tt_tvlv_ogm_handler_v1(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t flags, void *tvlv_value, + uint16_t tvlv_value_len) +{ + struct batadv_tvlv_tt_vlan_data *tt_vlan; + struct batadv_tvlv_tt_change *tt_change; + struct batadv_tvlv_tt_data *tt_data; + uint16_t num_entries, num_vlan; + + if (tvlv_value_len < sizeof(*tt_data)) + return; + + tt_data = (struct batadv_tvlv_tt_data *)tvlv_value; + tvlv_value_len -= sizeof(*tt_data); + + num_vlan = ntohs(tt_data->num_vlan); + + if (tvlv_value_len < sizeof(*tt_vlan) * num_vlan) + return; + + tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1); + tt_change = (struct batadv_tvlv_tt_change *)(tt_vlan + num_vlan); + tvlv_value_len -= sizeof(*tt_vlan) * num_vlan; + + num_entries = batadv_tt_entries(tvlv_value_len); + + batadv_tt_update_orig(bat_priv, orig, tt_vlan, num_vlan, tt_change, + num_entries, tt_data->ttvn); +} + +/** + * batadv_tt_tvlv_unicast_handler_v1 - process incoming (unicast) tt tvlv + * container + * @bat_priv: the bat priv with all the soft interface information + * @src: mac address of tt tvlv sender + * @dst: mac address of tt tvlv recipient + * @tvlv_value: tvlv buffer containing the tt data + * @tvlv_value_len: tvlv buffer length + * + * Returns NET_RX_DROP if the tt tvlv is to be re-routed, NET_RX_SUCCESS + * otherwise. + */ +static int batadv_tt_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv, + uint8_t *src, uint8_t *dst, + void *tvlv_value, + uint16_t tvlv_value_len) +{ + struct batadv_tvlv_tt_data *tt_data; + uint16_t tt_vlan_len, tt_num_entries; + char tt_flag; + bool ret; + + if (tvlv_value_len < sizeof(*tt_data)) + return NET_RX_SUCCESS; + + tt_data = (struct batadv_tvlv_tt_data *)tvlv_value; + tvlv_value_len -= sizeof(*tt_data); + + tt_vlan_len = sizeof(struct batadv_tvlv_tt_vlan_data); + tt_vlan_len *= ntohs(tt_data->num_vlan); + + if (tvlv_value_len < tt_vlan_len) + return NET_RX_SUCCESS; + + tvlv_value_len -= tt_vlan_len; + tt_num_entries = batadv_tt_entries(tvlv_value_len); + + switch (tt_data->flags & BATADV_TT_DATA_TYPE_MASK) { + case BATADV_TT_REQUEST: + batadv_inc_counter(bat_priv, BATADV_CNT_TT_REQUEST_RX); + + /* If this node cannot provide a TT response the tt_request is + * forwarded + */ + ret = batadv_send_tt_response(bat_priv, tt_data, src, dst); + if (!ret) { + if (tt_data->flags & BATADV_TT_FULL_TABLE) + tt_flag = 'F'; + else + tt_flag = '.'; + + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Routing TT_REQUEST to %pM [%c]\n", + dst, tt_flag); + /* tvlv API will re-route the packet */ + return NET_RX_DROP; + } + break; + case BATADV_TT_RESPONSE: + batadv_inc_counter(bat_priv, BATADV_CNT_TT_RESPONSE_RX); + + if (batadv_is_my_mac(bat_priv, dst)) { + batadv_handle_tt_response(bat_priv, tt_data, + src, tt_num_entries); + return NET_RX_SUCCESS; + } + + if (tt_data->flags & BATADV_TT_FULL_TABLE) + tt_flag = 'F'; + else + tt_flag = '.'; + + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Routing TT_RESPONSE to %pM [%c]\n", dst, tt_flag); + + /* tvlv API will re-route the packet */ + return NET_RX_DROP; + } + + return NET_RX_SUCCESS; +} + +/** + * batadv_roam_tvlv_unicast_handler_v1 - process incoming tt roam tvlv container + * @bat_priv: the bat priv with all the soft interface information + * @src: mac address of tt tvlv sender + * @dst: mac address of tt tvlv recipient + * @tvlv_value: tvlv buffer containing the tt data + * @tvlv_value_len: tvlv buffer length + * + * Returns NET_RX_DROP if the tt roam tvlv is to be re-routed, NET_RX_SUCCESS + * otherwise. + */ +static int batadv_roam_tvlv_unicast_handler_v1(struct batadv_priv *bat_priv, + uint8_t *src, uint8_t *dst, + void *tvlv_value, + uint16_t tvlv_value_len) +{ + struct batadv_tvlv_roam_adv *roaming_adv; + struct batadv_orig_node *orig_node = NULL; + + /* If this node is not the intended recipient of the + * roaming advertisement the packet is forwarded + * (the tvlv API will re-route the packet). + */ + if (!batadv_is_my_mac(bat_priv, dst)) + return NET_RX_DROP; + + if (tvlv_value_len < sizeof(*roaming_adv)) + goto out; + + orig_node = batadv_orig_hash_find(bat_priv, src); + if (!orig_node) + goto out; + + batadv_inc_counter(bat_priv, BATADV_CNT_TT_ROAM_ADV_RX); + roaming_adv = (struct batadv_tvlv_roam_adv *)tvlv_value; + + batadv_dbg(BATADV_DBG_TT, bat_priv, + "Received ROAMING_ADV from %pM (client %pM)\n", + src, roaming_adv->client); + + batadv_tt_global_add(bat_priv, orig_node, roaming_adv->client, + ntohs(roaming_adv->vid), BATADV_TT_CLIENT_ROAM, + atomic_read(&orig_node->last_ttvn) + 1); + +out: + if (orig_node) + batadv_orig_node_free_ref(orig_node); + return NET_RX_SUCCESS; +} + +/** + * batadv_tt_init - initialise the translation table internals + * @bat_priv: the bat priv with all the soft interface information + * + * Return 0 on success or negative error number in case of failure. + */ +int batadv_tt_init(struct batadv_priv *bat_priv) +{ + int ret; + + /* synchronized flags must be remote */ + BUILD_BUG_ON(!(BATADV_TT_SYNC_MASK & BATADV_TT_REMOTE_MASK)); + + ret = batadv_tt_local_init(bat_priv); + if (ret < 0) + return ret; + + ret = batadv_tt_global_init(bat_priv); + if (ret < 0) + return ret; + + batadv_tvlv_handler_register(bat_priv, batadv_tt_tvlv_ogm_handler_v1, + batadv_tt_tvlv_unicast_handler_v1, + BATADV_TVLV_TT, 1, BATADV_NO_FLAGS); + + batadv_tvlv_handler_register(bat_priv, NULL, + batadv_roam_tvlv_unicast_handler_v1, + BATADV_TVLV_ROAM, 1, BATADV_NO_FLAGS); + + INIT_DELAYED_WORK(&bat_priv->tt.work, batadv_tt_purge); + queue_delayed_work(batadv_event_workqueue, &bat_priv->tt.work, + msecs_to_jiffies(BATADV_TT_WORK_PERIOD)); + + return 1; +} diff --git a/net/batman-adv/translation-table.h b/net/batman-adv/translation-table.h index 659a3bb759ce87087c4d5697239f4724c815971c..026b1ffa674699ea7561df5456dd0c91f15d3521 100644 --- a/net/batman-adv/translation-table.h +++ b/net/batman-adv/translation-table.h @@ -20,49 +20,35 @@ #ifndef _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ #define _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ -int batadv_tt_len(int changes_num); int batadv_tt_init(struct batadv_priv *bat_priv); -void batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, - int ifindex); +bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, + unsigned short vid, int ifindex); uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, - const uint8_t *addr, const char *message, - bool roaming); + const uint8_t *addr, unsigned short vid, + const char *message, bool roaming); int batadv_tt_local_seq_print_text(struct seq_file *seq, void *offset); -void batadv_tt_global_add_orig(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node, - const unsigned char *tt_buff, int tt_buff_len); -int batadv_tt_global_add(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node, - const unsigned char *addr, uint16_t flags, - uint8_t ttvn); int batadv_tt_global_seq_print_text(struct seq_file *seq, void *offset); void batadv_tt_global_del_orig(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - const char *message); + int32_t match_vid, const char *message); struct batadv_orig_node *batadv_transtable_search(struct batadv_priv *bat_priv, const uint8_t *src, - const uint8_t *addr); + const uint8_t *addr, + unsigned short vid); void batadv_tt_free(struct batadv_priv *bat_priv); -bool batadv_send_tt_response(struct batadv_priv *bat_priv, - struct batadv_tt_query_packet *tt_request); -bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr); -void batadv_handle_tt_response(struct batadv_priv *bat_priv, - struct batadv_tt_query_packet *tt_response); +bool batadv_is_my_client(struct batadv_priv *bat_priv, const uint8_t *addr, + unsigned short vid); bool batadv_is_ap_isolated(struct batadv_priv *bat_priv, uint8_t *src, - uint8_t *dst); -void batadv_tt_update_orig(struct batadv_priv *bat_priv, - struct batadv_orig_node *orig_node, - const unsigned char *tt_buff, uint8_t tt_num_changes, - uint8_t ttvn, uint16_t tt_crc); -int batadv_tt_append_diff(struct batadv_priv *bat_priv, - unsigned char **packet_buff, int *packet_buff_len, - int packet_min_len); + uint8_t *dst, unsigned short vid); +void batadv_tt_local_commit_changes(struct batadv_priv *bat_priv); bool batadv_tt_global_client_is_roaming(struct batadv_priv *bat_priv, - uint8_t *addr); + uint8_t *addr, unsigned short vid); bool batadv_tt_local_client_is_roaming(struct batadv_priv *bat_priv, - uint8_t *addr); + uint8_t *addr, unsigned short vid); +void batadv_tt_local_resize_to_mtu(struct net_device *soft_iface); bool batadv_tt_add_temporary_global_entry(struct batadv_priv *bat_priv, struct batadv_orig_node *orig_node, - const unsigned char *addr); + const unsigned char *addr, + unsigned short vid); #endif /* _NET_BATMAN_ADV_TRANSLATION_TABLE_H_ */ diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index b2c94e1393191e00165e42e117ded3dd2374f70f..91dd369b0ff21790b9fae41b88005be95bf0ffe4 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -24,13 +24,6 @@ #include "bitarray.h" #include -/** - * Maximum overhead for the encapsulation for a payload packet - */ -#define BATADV_HEADER_LEN \ - (ETH_HLEN + max(sizeof(struct batadv_unicast_packet), \ - sizeof(struct batadv_bcast_packet))) - #ifdef CONFIG_BATMAN_ADV_DAT /* batadv_dat_addr_t is the type used for all DHT addresses. If it is changed, @@ -42,6 +35,18 @@ #endif /* CONFIG_BATMAN_ADV_DAT */ +/** + * BATADV_TT_REMOTE_MASK - bitmask selecting the flags that are sent over the + * wire only + */ +#define BATADV_TT_REMOTE_MASK 0x00FF + +/** + * BATADV_TT_SYNC_MASK - bitmask of the flags that need to be kept in sync + * among the nodes. These flags are used to compute the global/local CRC + */ +#define BATADV_TT_SYNC_MASK 0x00F0 + /** * struct batadv_hard_iface_bat_iv - per hard interface B.A.T.M.A.N. IV data * @ogm_buff: buffer holding the OGM packet @@ -60,7 +65,6 @@ struct batadv_hard_iface_bat_iv { * @if_num: identificator of the interface * @if_status: status of the interface for batman-adv * @net_dev: pointer to the net_device - * @frag_seqno: last fragment sequence number sent by this interface * @num_bcasts: number of payload re-broadcasts on this interface (ARQ) * @hardif_obj: kobject of the per interface sysfs "mesh" directory * @refcount: number of contexts the object is used @@ -76,7 +80,6 @@ struct batadv_hard_iface { int16_t if_num; char if_status; struct net_device *net_dev; - atomic_t frag_seqno; uint8_t num_bcasts; struct kobject *hardif_obj; atomic_t refcount; @@ -87,29 +90,98 @@ struct batadv_hard_iface { struct work_struct cleanup_work; }; +/** + * struct batadv_frag_table_entry - head in the fragment buffer table + * @head: head of list with fragments + * @lock: lock to protect the list of fragments + * @timestamp: time (jiffie) of last received fragment + * @seqno: sequence number of the fragments in the list + * @size: accumulated size of packets in list + */ +struct batadv_frag_table_entry { + struct hlist_head head; + spinlock_t lock; /* protects head */ + unsigned long timestamp; + uint16_t seqno; + uint16_t size; +}; + +/** + * struct batadv_frag_list_entry - entry in a list of fragments + * @list: list node information + * @skb: fragment + * @no: fragment number in the set + */ +struct batadv_frag_list_entry { + struct hlist_node list; + struct sk_buff *skb; + uint8_t no; +}; + +/** + * struct batadv_vlan_tt - VLAN specific TT attributes + * @crc: CRC32 checksum of the entries belonging to this vlan + * @num_entries: number of TT entries for this VLAN + */ +struct batadv_vlan_tt { + uint32_t crc; + atomic_t num_entries; +}; + +/** + * batadv_orig_node_vlan - VLAN specific data per orig_node + * @vid: the VLAN identifier + * @tt: VLAN specific TT attributes + * @list: list node for orig_node::vlan_list + * @refcount: number of context where this object is currently in use + * @rcu: struct used for freeing in a RCU-safe manner + */ +struct batadv_orig_node_vlan { + unsigned short vid; + struct batadv_vlan_tt tt; + struct list_head list; + atomic_t refcount; + struct rcu_head rcu; +}; + +/** + * struct batadv_orig_bat_iv - B.A.T.M.A.N. IV private orig_node members + * @bcast_own: bitfield containing the number of our OGMs this orig_node + * rebroadcasted "back" to us (relative to last_real_seqno) + * @bcast_own_sum: counted result of bcast_own + * @ogm_cnt_lock: lock protecting bcast_own, bcast_own_sum, + * neigh_node->bat_iv.real_bits & neigh_node->bat_iv.real_packet_count + */ +struct batadv_orig_bat_iv { + unsigned long *bcast_own; + uint8_t *bcast_own_sum; + /* ogm_cnt_lock protects: bcast_own, bcast_own_sum, + * neigh_node->bat_iv.real_bits & neigh_node->bat_iv.real_packet_count + */ + spinlock_t ogm_cnt_lock; +}; + /** * struct batadv_orig_node - structure for orig_list maintaining nodes of mesh * @orig: originator ethernet address * @primary_addr: hosts primary interface address * @router: router that should be used to reach this originator * @batadv_dat_addr_t: address of the orig node in the distributed hash - * @bcast_own: bitfield containing the number of our OGMs this orig_node - * rebroadcasted "back" to us (relative to last_real_seqno) - * @bcast_own_sum: counted result of bcast_own * @last_seen: time when last packet from this node was received * @bcast_seqno_reset: time when the broadcast seqno window was reset * @batman_seqno_reset: time when the batman seqno window was reset - * @gw_flags: flags related to gateway class - * @flags: for now only VIS_SERVER flag + * @capabilities: announced capabilities of this originator * @last_ttvn: last seen translation table version number - * @tt_crc: CRC of the translation table * @tt_buff: last tt changeset this node received from the orig node * @tt_buff_len: length of the last tt changeset this node received from the * orig node * @tt_buff_lock: lock that protects tt_buff and tt_buff_len - * @tt_size: number of global TT entries announced by the orig node * @tt_initialised: bool keeping track of whether or not this node have received * any translation table information from the orig node yet + * @tt_lock: prevents from updating the table while reading it. Table update is + * made up by two operations (data structure update and metdata -CRC/TTVN- + * recalculation) and they have to be executed atomically in order to avoid + * another thread to read the table/metadata between those. * @last_real_seqno: last and best known sequence number * @last_ttl: ttl of last received packet * @bcast_bits: bitfield containing the info which payload broadcast originated @@ -117,14 +189,9 @@ struct batadv_hard_iface { * last_bcast_seqno) * @last_bcast_seqno: last broadcast sequence number received by this host * @neigh_list: list of potential next hop neighbor towards this orig node - * @frag_list: fragmentation buffer list for fragment re-assembly - * @last_frag_packet: time when last fragmented packet from this node was - * received * @neigh_list_lock: lock protecting neigh_list, router and bonding_list * @hash_entry: hlist node for batadv_priv::orig_hash * @bat_priv: pointer to soft_iface this orig node belongs to - * @ogm_cnt_lock: lock protecting bcast_own, bcast_own_sum, - * neigh_node->real_bits & neigh_node->real_packet_count * @bcast_seqno_lock: lock protecting bcast_bits & last_bcast_seqno * @bond_candidates: how many candidates are available * @bond_list: list of bonding candidates @@ -134,6 +201,11 @@ struct batadv_hard_iface { * @out_coding_list: list of nodes that can hear this orig * @in_coding_list_lock: protects in_coding_list * @out_coding_list_lock: protects out_coding_list + * @fragments: array with heads for fragment chains + * @vlan_list: a list of orig_node_vlan structs, one per VLAN served by the + * originator represented by this object + * @vlan_list_lock: lock protecting vlan_list + * @bat_iv: B.A.T.M.A.N. IV private structure */ struct batadv_orig_node { uint8_t orig[ETH_ALEN]; @@ -142,35 +214,26 @@ struct batadv_orig_node { #ifdef CONFIG_BATMAN_ADV_DAT batadv_dat_addr_t dat_addr; #endif - unsigned long *bcast_own; - uint8_t *bcast_own_sum; unsigned long last_seen; unsigned long bcast_seqno_reset; unsigned long batman_seqno_reset; - uint8_t gw_flags; - uint8_t flags; + uint8_t capabilities; atomic_t last_ttvn; - uint16_t tt_crc; unsigned char *tt_buff; int16_t tt_buff_len; spinlock_t tt_buff_lock; /* protects tt_buff & tt_buff_len */ - atomic_t tt_size; bool tt_initialised; + /* prevents from changing the table while reading it */ + spinlock_t tt_lock; uint32_t last_real_seqno; uint8_t last_ttl; DECLARE_BITMAP(bcast_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); uint32_t last_bcast_seqno; struct hlist_head neigh_list; - struct list_head frag_list; - unsigned long last_frag_packet; /* neigh_list_lock protects: neigh_list, router & bonding_list */ spinlock_t neigh_list_lock; struct hlist_node hash_entry; struct batadv_priv *bat_priv; - /* ogm_cnt_lock protects: bcast_own, bcast_own_sum, - * neigh_node->real_bits & neigh_node->real_packet_count - */ - spinlock_t ogm_cnt_lock; /* bcast_seqno_lock protects: bcast_bits & last_bcast_seqno */ spinlock_t bcast_seqno_lock; atomic_t bond_candidates; @@ -183,12 +246,28 @@ struct batadv_orig_node { spinlock_t in_coding_list_lock; /* Protects in_coding_list */ spinlock_t out_coding_list_lock; /* Protects out_coding_list */ #endif + struct batadv_frag_table_entry fragments[BATADV_FRAG_BUFFER_COUNT]; + struct list_head vlan_list; + spinlock_t vlan_list_lock; /* protects vlan_list */ + struct batadv_orig_bat_iv bat_iv; +}; + +/** + * enum batadv_orig_capabilities - orig node capabilities + * @BATADV_ORIG_CAPA_HAS_DAT: orig node has distributed arp table enabled + * @BATADV_ORIG_CAPA_HAS_NC: orig node has network coding enabled + */ +enum batadv_orig_capabilities { + BATADV_ORIG_CAPA_HAS_DAT = BIT(0), + BATADV_ORIG_CAPA_HAS_NC = BIT(1), }; /** * struct batadv_gw_node - structure for orig nodes announcing gw capabilities * @list: list node for batadv_priv_gw::list * @orig_node: pointer to corresponding orig node + * @bandwidth_down: advertised uplink download bandwidth + * @bandwidth_up: advertised uplink upload bandwidth * @deleted: this struct is scheduled for deletion * @refcount: number of contexts the object is used * @rcu: struct used for freeing in an RCU-safe manner @@ -196,46 +275,57 @@ struct batadv_orig_node { struct batadv_gw_node { struct hlist_node list; struct batadv_orig_node *orig_node; + uint32_t bandwidth_down; + uint32_t bandwidth_up; unsigned long deleted; atomic_t refcount; struct rcu_head rcu; }; /** - * struct batadv_neigh_node - structure for single hop neighbors - * @list: list node for batadv_orig_node::neigh_list - * @addr: mac address of neigh node + * struct batadv_neigh_bat_iv - B.A.T.M.A.N. IV specific structure for single + * hop neighbors * @tq_recv: ring buffer of received TQ values from this neigh node * @tq_index: ring buffer index * @tq_avg: averaged tq of all tq values in the ring buffer (tq_recv) - * @last_ttl: last received ttl from this neigh node - * @bonding_list: list node for batadv_orig_node::bond_list - * @last_seen: when last packet via this neighbor was received * @real_bits: bitfield containing the number of OGMs received from this neigh * node (relative to orig_node->last_real_seqno) * @real_packet_count: counted result of real_bits + * @lq_update_lock: lock protecting tq_recv & tq_index + */ +struct batadv_neigh_bat_iv { + uint8_t tq_recv[BATADV_TQ_GLOBAL_WINDOW_SIZE]; + uint8_t tq_index; + uint8_t tq_avg; + DECLARE_BITMAP(real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); + uint8_t real_packet_count; + spinlock_t lq_update_lock; /* protects tq_recv & tq_index */ +}; + +/** + * struct batadv_neigh_node - structure for single hops neighbors + * @list: list node for batadv_orig_node::neigh_list * @orig_node: pointer to corresponding orig_node + * @addr: the MAC address of the neighboring interface * @if_incoming: pointer to incoming hard interface - * @lq_update_lock: lock protecting tq_recv & tq_index + * @last_seen: when last packet via this neighbor was received + * @last_ttl: last received ttl from this neigh node + * @bonding_list: list node for batadv_orig_node::bond_list * @refcount: number of contexts the object is used * @rcu: struct used for freeing in an RCU-safe manner + * @bat_iv: B.A.T.M.A.N. IV private structure */ struct batadv_neigh_node { struct hlist_node list; + struct batadv_orig_node *orig_node; uint8_t addr[ETH_ALEN]; - uint8_t tq_recv[BATADV_TQ_GLOBAL_WINDOW_SIZE]; - uint8_t tq_index; - uint8_t tq_avg; + struct batadv_hard_iface *if_incoming; + unsigned long last_seen; uint8_t last_ttl; struct list_head bonding_list; - unsigned long last_seen; - DECLARE_BITMAP(real_bits, BATADV_TQ_LOCAL_WINDOW_SIZE); - uint8_t real_packet_count; - struct batadv_orig_node *orig_node; - struct batadv_hard_iface *if_incoming; - spinlock_t lq_update_lock; /* protects tq_recv & tq_index */ atomic_t refcount; struct rcu_head rcu; + struct batadv_neigh_bat_iv bat_iv; }; /** @@ -265,6 +355,12 @@ struct batadv_bcast_duplist_entry { * @BATADV_CNT_MGMT_TX_BYTES: transmitted routing protocol traffic bytes counter * @BATADV_CNT_MGMT_RX: received routing protocol traffic packet counter * @BATADV_CNT_MGMT_RX_BYTES: received routing protocol traffic bytes counter + * @BATADV_CNT_FRAG_TX: transmitted fragment traffic packet counter + * @BATADV_CNT_FRAG_TX_BYTES: transmitted fragment traffic bytes counter + * @BATADV_CNT_FRAG_RX: received fragment traffic packet counter + * @BATADV_CNT_FRAG_RX_BYTES: received fragment traffic bytes counter + * @BATADV_CNT_FRAG_FWD: forwarded fragment traffic packet counter + * @BATADV_CNT_FRAG_FWD_BYTES: forwarded fragment traffic bytes counter * @BATADV_CNT_TT_REQUEST_TX: transmitted tt req traffic packet counter * @BATADV_CNT_TT_REQUEST_RX: received tt req traffic packet counter * @BATADV_CNT_TT_RESPONSE_TX: transmitted tt resp traffic packet counter @@ -302,6 +398,12 @@ enum batadv_counters { BATADV_CNT_MGMT_TX_BYTES, BATADV_CNT_MGMT_RX, BATADV_CNT_MGMT_RX_BYTES, + BATADV_CNT_FRAG_TX, + BATADV_CNT_FRAG_TX_BYTES, + BATADV_CNT_FRAG_RX, + BATADV_CNT_FRAG_RX_BYTES, + BATADV_CNT_FRAG_FWD, + BATADV_CNT_FRAG_FWD_BYTES, BATADV_CNT_TT_REQUEST_TX, BATADV_CNT_TT_REQUEST_RX, BATADV_CNT_TT_RESPONSE_TX, @@ -343,11 +445,14 @@ enum batadv_counters { * @changes_list_lock: lock protecting changes_list * @req_list_lock: lock protecting req_list * @roam_list_lock: lock protecting roam_list - * @local_entry_num: number of entries in the local hash table - * @local_crc: Checksum of the local table, recomputed before sending a new OGM * @last_changeset: last tt changeset this host has generated * @last_changeset_len: length of last tt changeset this host has generated * @last_changeset_lock: lock protecting last_changeset & last_changeset_len + * @commit_lock: prevents from executing a local TT commit while reading the + * local table. The local TT commit is made up by two operations (data + * structure update and metdata -CRC/TTVN- recalculation) and they have to be + * executed atomically in order to avoid another thread to read the + * table/metadata between those. * @work: work queue callback item for translation table purging */ struct batadv_priv_tt { @@ -362,12 +467,12 @@ struct batadv_priv_tt { spinlock_t changes_list_lock; /* protects changes */ spinlock_t req_list_lock; /* protects req_list */ spinlock_t roam_list_lock; /* protects roam_list */ - atomic_t local_entry_num; - uint16_t local_crc; unsigned char *last_changeset; int16_t last_changeset_len; /* protects last_changeset & last_changeset_len */ spinlock_t last_changeset_lock; + /* prevents from executing a commit while reading the table */ + spinlock_t commit_lock; struct delayed_work work; }; @@ -420,31 +525,31 @@ struct batadv_priv_debug_log { * @list: list of available gateway nodes * @list_lock: lock protecting gw_list & curr_gw * @curr_gw: pointer to currently selected gateway node + * @bandwidth_down: advertised uplink download bandwidth (if gw_mode server) + * @bandwidth_up: advertised uplink upload bandwidth (if gw_mode server) * @reselect: bool indicating a gateway re-selection is in progress */ struct batadv_priv_gw { struct hlist_head list; spinlock_t list_lock; /* protects gw_list & curr_gw */ struct batadv_gw_node __rcu *curr_gw; /* rcu protected pointer */ + atomic_t bandwidth_down; + atomic_t bandwidth_up; atomic_t reselect; }; /** - * struct batadv_priv_vis - per mesh interface vis data - * @send_list: list of batadv_vis_info packets to sent - * @hash: hash table containing vis data from other nodes in the network - * @hash_lock: lock protecting the hash table - * @list_lock: lock protecting my_info::recv_list - * @work: work queue callback item for vis packet sending - * @my_info: holds this node's vis data sent on a regular basis + * struct batadv_priv_tvlv - per mesh interface tvlv data + * @container_list: list of registered tvlv containers to be sent with each OGM + * @handler_list: list of the various tvlv content handlers + * @container_list_lock: protects tvlv container list access + * @handler_list_lock: protects handler list access */ -struct batadv_priv_vis { - struct list_head send_list; - struct batadv_hashtable *hash; - spinlock_t hash_lock; /* protects hash */ - spinlock_t list_lock; /* protects my_info::recv_list */ - struct delayed_work work; - struct batadv_vis_info *my_info; +struct batadv_priv_tvlv { + struct hlist_head container_list; + struct hlist_head handler_list; + spinlock_t container_list_lock; /* protects container_list */ + spinlock_t handler_list_lock; /* protects handler_list */ }; /** @@ -490,6 +595,26 @@ struct batadv_priv_nc { struct batadv_hashtable *decoding_hash; }; +/** + * struct batadv_softif_vlan - per VLAN attributes set + * @vid: VLAN identifier + * @kobj: kobject for sysfs vlan subdirectory + * @ap_isolation: AP isolation state + * @tt: TT private attributes (VLAN specific) + * @list: list node for bat_priv::softif_vlan_list + * @refcount: number of context where this object is currently in use + * @rcu: struct used for freeing in a RCU-safe manner + */ +struct batadv_softif_vlan { + unsigned short vid; + struct kobject *kobj; + atomic_t ap_isolation; /* boolean */ + struct batadv_vlan_tt tt; + struct hlist_node list; + atomic_t refcount; + struct rcu_head rcu; +}; + /** * struct batadv_priv - per mesh interface data * @mesh_state: current status of the mesh (inactive/active/deactivating) @@ -499,15 +624,15 @@ struct batadv_priv_nc { * @aggregated_ogms: bool indicating whether OGM aggregation is enabled * @bonding: bool indicating whether traffic bonding is enabled * @fragmentation: bool indicating whether traffic fragmentation is enabled - * @ap_isolation: bool indicating whether ap isolation is enabled + * @packet_size_max: max packet size that can be transmitted via + * multiple fragmented skbs or a single frame if fragmentation is disabled + * @frag_seqno: incremental counter to identify chains of egress fragments * @bridge_loop_avoidance: bool indicating whether bridge loop avoidance is * enabled * @distributed_arp_table: bool indicating whether distributed ARP table is * enabled - * @vis_mode: vis operation: client or server (see batadv_vis_packettype) * @gw_mode: gateway operation: off, client or server (see batadv_gw_modes) * @gw_sel_class: gateway selection class (applies if gw_mode client) - * @gw_bandwidth: gateway announced bandwidth (applies if gw_mode server) * @orig_interval: OGM broadcast interval in milliseconds * @hop_penalty: penalty which will be applied to an OGM's tq-field on every hop * @log_level: configured log level (see batadv_dbg_level) @@ -527,11 +652,14 @@ struct batadv_priv_nc { * @primary_if: one of the hard interfaces assigned to this mesh interface * becomes the primary interface * @bat_algo_ops: routing algorithm used by this mesh interface + * @softif_vlan_list: a list of softif_vlan structs, one per VLAN created on top + * of the mesh interface represented by this object + * @softif_vlan_list_lock: lock protecting softif_vlan_list * @bla: bridge loope avoidance data * @debug_log: holding debug logging relevant data * @gw: gateway data * @tt: translation table data - * @vis: vis data + * @tvlv: type-version-length-value data * @dat: distributed arp table data * @network_coding: bool indicating whether network coding is enabled * @batadv_priv_nc: network coding data @@ -544,17 +672,16 @@ struct batadv_priv { atomic_t aggregated_ogms; atomic_t bonding; atomic_t fragmentation; - atomic_t ap_isolation; + atomic_t packet_size_max; + atomic_t frag_seqno; #ifdef CONFIG_BATMAN_ADV_BLA atomic_t bridge_loop_avoidance; #endif #ifdef CONFIG_BATMAN_ADV_DAT atomic_t distributed_arp_table; #endif - atomic_t vis_mode; atomic_t gw_mode; atomic_t gw_sel_class; - atomic_t gw_bandwidth; atomic_t orig_interval; atomic_t hop_penalty; #ifdef CONFIG_BATMAN_ADV_DEBUG @@ -575,6 +702,8 @@ struct batadv_priv { struct work_struct cleanup_work; struct batadv_hard_iface __rcu *primary_if; /* rcu protected pointer */ struct batadv_algo_ops *bat_algo_ops; + struct hlist_head softif_vlan_list; + spinlock_t softif_vlan_list_lock; /* protects softif_vlan_list */ #ifdef CONFIG_BATMAN_ADV_BLA struct batadv_priv_bla bla; #endif @@ -583,7 +712,7 @@ struct batadv_priv { #endif struct batadv_priv_gw gw; struct batadv_priv_tt tt; - struct batadv_priv_vis vis; + struct batadv_priv_tvlv tvlv; #ifdef CONFIG_BATMAN_ADV_DAT struct batadv_priv_dat dat; #endif @@ -620,7 +749,7 @@ struct batadv_socket_client { struct batadv_socket_packet { struct list_head list; size_t icmp_len; - struct batadv_icmp_packet_rr icmp_packet; + uint8_t icmp_packet[BATADV_ICMP_MAX_PACKET_SIZE]; }; /** @@ -677,6 +806,7 @@ struct batadv_bla_claim { /** * struct batadv_tt_common_entry - tt local & tt global common data * @addr: mac address of non-mesh client + * @vid: VLAN identifier * @hash_entry: hlist node for batadv_priv_tt::local_hash or for * batadv_priv_tt::global_hash * @flags: various state handling flags (see batadv_tt_client_flags) @@ -686,6 +816,7 @@ struct batadv_bla_claim { */ struct batadv_tt_common_entry { uint8_t addr[ETH_ALEN]; + unsigned short vid; struct hlist_node hash_entry; uint16_t flags; unsigned long added_at; @@ -740,7 +871,7 @@ struct batadv_tt_orig_list_entry { */ struct batadv_tt_change_node { struct list_head list; - struct batadv_tt_change change; + struct batadv_tvlv_tt_change change; }; /** @@ -865,78 +996,6 @@ struct batadv_forw_packet { struct batadv_hard_iface *if_incoming; }; -/** - * struct batadv_frag_packet_list_entry - storage for fragment packet - * @list: list node for orig_node::frag_list - * @seqno: sequence number of the fragment - * @skb: fragment's skb buffer - */ -struct batadv_frag_packet_list_entry { - struct list_head list; - uint16_t seqno; - struct sk_buff *skb; -}; - -/** - * struct batadv_vis_info - local data for vis information - * @first_seen: timestamp used for purging stale vis info entries - * @recv_list: List of server-neighbors we have received this packet from. This - * packet should not be re-forward to them again. List elements are struct - * batadv_vis_recvlist_node - * @send_list: list of packets to be forwarded - * @refcount: number of contexts the object is used - * @hash_entry: hlist node for batadv_priv_vis::hash - * @bat_priv: pointer to soft_iface this orig node belongs to - * @skb_packet: contains the vis packet - */ -struct batadv_vis_info { - unsigned long first_seen; - struct list_head recv_list; - struct list_head send_list; - struct kref refcount; - struct hlist_node hash_entry; - struct batadv_priv *bat_priv; - struct sk_buff *skb_packet; -} __packed; - -/** - * struct batadv_vis_info_entry - contains link information for vis - * @src: source MAC of the link, all zero for local TT entry - * @dst: destination MAC of the link, client mac address for local TT entry - * @quality: transmission quality of the link, or 0 for local TT entry - */ -struct batadv_vis_info_entry { - uint8_t src[ETH_ALEN]; - uint8_t dest[ETH_ALEN]; - uint8_t quality; -} __packed; - -/** - * struct batadv_vis_recvlist_node - list entry for batadv_vis_info::recv_list - * @list: list node for batadv_vis_info::recv_list - * @mac: MAC address of the originator from where the vis_info was received - */ -struct batadv_vis_recvlist_node { - struct list_head list; - uint8_t mac[ETH_ALEN]; -}; - -/** - * struct batadv_vis_if_list_entry - auxiliary data for vis data generation - * @addr: MAC address of the interface - * @primary: true if this interface is the primary interface - * @list: list node the interface list - * - * While scanning for vis-entries of a particular vis-originator - * this list collects its interfaces to create a subgraph/cluster - * out of them later - */ -struct batadv_vis_if_list_entry { - uint8_t addr[ETH_ALEN]; - bool primary; - struct hlist_node list; -}; - /** * struct batadv_algo_ops - mesh algorithm callbacks * @list: list node for the batadv_algo_list @@ -948,6 +1007,16 @@ struct batadv_vis_if_list_entry { * @bat_primary_iface_set: called when primary interface is selected / changed * @bat_ogm_schedule: prepare a new outgoing OGM for the send queue * @bat_ogm_emit: send scheduled OGM + * @bat_neigh_cmp: compare the metrics of two neighbors + * @bat_neigh_is_equiv_or_better: check if neigh1 is equally good or + * better than neigh2 from the metric prospective + * @bat_orig_print: print the originator table (optional) + * @bat_orig_free: free the resources allocated by the routing algorithm for an + * orig_node object + * @bat_orig_add_if: ask the routing algorithm to apply the needed changes to + * the orig_node due to a new hard-interface being added into the mesh + * @bat_orig_del_if: ask the routing algorithm to apply the needed changes to + * the orig_node due to an hard-interface being removed from the mesh */ struct batadv_algo_ops { struct hlist_node list; @@ -958,6 +1027,17 @@ struct batadv_algo_ops { void (*bat_primary_iface_set)(struct batadv_hard_iface *hard_iface); void (*bat_ogm_schedule)(struct batadv_hard_iface *hard_iface); void (*bat_ogm_emit)(struct batadv_forw_packet *forw_packet); + int (*bat_neigh_cmp)(struct batadv_neigh_node *neigh1, + struct batadv_neigh_node *neigh2); + bool (*bat_neigh_is_equiv_or_better)(struct batadv_neigh_node *neigh1, + struct batadv_neigh_node *neigh2); + /* orig_node handling API */ + void (*bat_orig_print)(struct batadv_priv *priv, struct seq_file *seq); + void (*bat_orig_free)(struct batadv_orig_node *orig_node); + int (*bat_orig_add_if)(struct batadv_orig_node *orig_node, + int max_if_num); + int (*bat_orig_del_if)(struct batadv_orig_node *orig_node, + int max_if_num, int del_if_num); }; /** @@ -965,6 +1045,7 @@ struct batadv_algo_ops { * is used to stored ARP entries needed for the global DAT cache * @ip: the IPv4 corresponding to this DAT/ARP entry * @mac_addr: the MAC address associated to the stored IPv4 + * @vid: the vlan ID associated to this entry * @last_update: time in jiffies when this entry was refreshed last time * @hash_entry: hlist node for batadv_priv_dat::hash * @refcount: number of contexts the object is used @@ -973,6 +1054,7 @@ struct batadv_algo_ops { struct batadv_dat_entry { __be32 ip; uint8_t mac_addr[ETH_ALEN]; + unsigned short vid; unsigned long last_update; struct hlist_node hash_entry; atomic_t refcount; @@ -992,4 +1074,60 @@ struct batadv_dat_candidate { struct batadv_orig_node *orig_node; }; +/** + * struct batadv_tvlv_container - container for tvlv appended to OGMs + * @list: hlist node for batadv_priv_tvlv::container_list + * @tvlv_hdr: tvlv header information needed to construct the tvlv + * @value_len: length of the buffer following this struct which contains + * the actual tvlv payload + * @refcount: number of contexts the object is used + */ +struct batadv_tvlv_container { + struct hlist_node list; + struct batadv_tvlv_hdr tvlv_hdr; + atomic_t refcount; +}; + +/** + * struct batadv_tvlv_handler - handler for specific tvlv type and version + * @list: hlist node for batadv_priv_tvlv::handler_list + * @ogm_handler: handler callback which is given the tvlv payload to process on + * incoming OGM packets + * @unicast_handler: handler callback which is given the tvlv payload to process + * on incoming unicast tvlv packets + * @type: tvlv type this handler feels responsible for + * @version: tvlv version this handler feels responsible for + * @flags: tvlv handler flags + * @refcount: number of contexts the object is used + * @rcu: struct used for freeing in an RCU-safe manner + */ +struct batadv_tvlv_handler { + struct hlist_node list; + void (*ogm_handler)(struct batadv_priv *bat_priv, + struct batadv_orig_node *orig, + uint8_t flags, + void *tvlv_value, uint16_t tvlv_value_len); + int (*unicast_handler)(struct batadv_priv *bat_priv, + uint8_t *src, uint8_t *dst, + void *tvlv_value, uint16_t tvlv_value_len); + uint8_t type; + uint8_t version; + uint8_t flags; + atomic_t refcount; + struct rcu_head rcu; +}; + +/** + * enum batadv_tvlv_handler_flags - tvlv handler flags definitions + * @BATADV_TVLV_HANDLER_OGM_CIFNOTFND: tvlv ogm processing function will call + * this handler even if its type was not found (with no data) + * @BATADV_TVLV_HANDLER_OGM_CALLED: interval tvlv handling flag - the API marks + * a handler as being called, so it won't be called if the + * BATADV_TVLV_HANDLER_OGM_CIFNOTFND flag was set + */ +enum batadv_tvlv_handler_flags { + BATADV_TVLV_HANDLER_OGM_CIFNOTFND = BIT(1), + BATADV_TVLV_HANDLER_OGM_CALLED = BIT(2), +}; + #endif /* _NET_BATMAN_ADV_TYPES_H_ */ diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c deleted file mode 100644 index 48b31d33ce6bb58f6757f6e7f69da6260bf68c57..0000000000000000000000000000000000000000 --- a/net/batman-adv/unicast.c +++ /dev/null @@ -1,491 +0,0 @@ -/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: - * - * Andreas Langer - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - */ - -#include "main.h" -#include "unicast.h" -#include "send.h" -#include "soft-interface.h" -#include "gateway_client.h" -#include "originator.h" -#include "hash.h" -#include "translation-table.h" -#include "routing.h" -#include "hard-interface.h" - - -static struct sk_buff * -batadv_frag_merge_packet(struct list_head *head, - struct batadv_frag_packet_list_entry *tfp, - struct sk_buff *skb) -{ - struct batadv_unicast_frag_packet *up; - struct sk_buff *tmp_skb; - struct batadv_unicast_packet *unicast_packet; - int hdr_len = sizeof(*unicast_packet); - int uni_diff = sizeof(*up) - hdr_len; - uint8_t *packet_pos; - - up = (struct batadv_unicast_frag_packet *)skb->data; - /* set skb to the first part and tmp_skb to the second part */ - if (up->flags & BATADV_UNI_FRAG_HEAD) { - tmp_skb = tfp->skb; - } else { - tmp_skb = skb; - skb = tfp->skb; - } - - if (skb_linearize(skb) < 0 || skb_linearize(tmp_skb) < 0) - goto err; - - skb_pull(tmp_skb, sizeof(*up)); - if (pskb_expand_head(skb, 0, tmp_skb->len, GFP_ATOMIC) < 0) - goto err; - - /* move free entry to end */ - tfp->skb = NULL; - tfp->seqno = 0; - list_move_tail(&tfp->list, head); - - memcpy(skb_put(skb, tmp_skb->len), tmp_skb->data, tmp_skb->len); - kfree_skb(tmp_skb); - - memmove(skb->data + uni_diff, skb->data, hdr_len); - packet_pos = skb_pull(skb, uni_diff); - unicast_packet = (struct batadv_unicast_packet *)packet_pos; - unicast_packet->header.packet_type = BATADV_UNICAST; - - return skb; - -err: - /* free buffered skb, skb will be freed later */ - kfree_skb(tfp->skb); - return NULL; -} - -static void batadv_frag_create_entry(struct list_head *head, - struct sk_buff *skb) -{ - struct batadv_frag_packet_list_entry *tfp; - struct batadv_unicast_frag_packet *up; - - up = (struct batadv_unicast_frag_packet *)skb->data; - - /* free and oldest packets stand at the end */ - tfp = list_entry((head)->prev, typeof(*tfp), list); - kfree_skb(tfp->skb); - - tfp->seqno = ntohs(up->seqno); - tfp->skb = skb; - list_move(&tfp->list, head); - return; -} - -static int batadv_frag_create_buffer(struct list_head *head) -{ - int i; - struct batadv_frag_packet_list_entry *tfp; - - for (i = 0; i < BATADV_FRAG_BUFFER_SIZE; i++) { - tfp = kmalloc(sizeof(*tfp), GFP_ATOMIC); - if (!tfp) { - batadv_frag_list_free(head); - return -ENOMEM; - } - tfp->skb = NULL; - tfp->seqno = 0; - INIT_LIST_HEAD(&tfp->list); - list_add(&tfp->list, head); - } - - return 0; -} - -static struct batadv_frag_packet_list_entry * -batadv_frag_search_packet(struct list_head *head, - const struct batadv_unicast_frag_packet *up) -{ - struct batadv_frag_packet_list_entry *tfp; - struct batadv_unicast_frag_packet *tmp_up = NULL; - bool is_head_tmp, is_head; - uint16_t search_seqno; - - if (up->flags & BATADV_UNI_FRAG_HEAD) - search_seqno = ntohs(up->seqno)+1; - else - search_seqno = ntohs(up->seqno)-1; - - is_head = up->flags & BATADV_UNI_FRAG_HEAD; - - list_for_each_entry(tfp, head, list) { - if (!tfp->skb) - continue; - - if (tfp->seqno == ntohs(up->seqno)) - goto mov_tail; - - tmp_up = (struct batadv_unicast_frag_packet *)tfp->skb->data; - - if (tfp->seqno == search_seqno) { - is_head_tmp = tmp_up->flags & BATADV_UNI_FRAG_HEAD; - if (is_head_tmp != is_head) - return tfp; - else - goto mov_tail; - } - } - return NULL; - -mov_tail: - list_move_tail(&tfp->list, head); - return NULL; -} - -void batadv_frag_list_free(struct list_head *head) -{ - struct batadv_frag_packet_list_entry *pf, *tmp_pf; - - if (!list_empty(head)) { - list_for_each_entry_safe(pf, tmp_pf, head, list) { - kfree_skb(pf->skb); - list_del(&pf->list); - kfree(pf); - } - } - return; -} - -/* frag_reassemble_skb(): - * returns NET_RX_DROP if the operation failed - skb is left intact - * returns NET_RX_SUCCESS if the fragment was buffered (skb_new will be NULL) - * or the skb could be reassembled (skb_new will point to the new packet and - * skb was freed) - */ -int batadv_frag_reassemble_skb(struct sk_buff *skb, - struct batadv_priv *bat_priv, - struct sk_buff **new_skb) -{ - struct batadv_orig_node *orig_node; - struct batadv_frag_packet_list_entry *tmp_frag_entry; - int ret = NET_RX_DROP; - struct batadv_unicast_frag_packet *unicast_packet; - - unicast_packet = (struct batadv_unicast_frag_packet *)skb->data; - *new_skb = NULL; - - orig_node = batadv_orig_hash_find(bat_priv, unicast_packet->orig); - if (!orig_node) - goto out; - - orig_node->last_frag_packet = jiffies; - - if (list_empty(&orig_node->frag_list) && - batadv_frag_create_buffer(&orig_node->frag_list)) { - pr_debug("couldn't create frag buffer\n"); - goto out; - } - - tmp_frag_entry = batadv_frag_search_packet(&orig_node->frag_list, - unicast_packet); - - if (!tmp_frag_entry) { - batadv_frag_create_entry(&orig_node->frag_list, skb); - ret = NET_RX_SUCCESS; - goto out; - } - - *new_skb = batadv_frag_merge_packet(&orig_node->frag_list, - tmp_frag_entry, skb); - /* if not, merge failed */ - if (*new_skb) - ret = NET_RX_SUCCESS; - -out: - if (orig_node) - batadv_orig_node_free_ref(orig_node); - return ret; -} - -int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv, - struct batadv_hard_iface *hard_iface, - const uint8_t dstaddr[]) -{ - struct batadv_unicast_packet tmp_uc, *unicast_packet; - struct batadv_hard_iface *primary_if; - struct sk_buff *frag_skb; - struct batadv_unicast_frag_packet *frag1, *frag2; - int uc_hdr_len = sizeof(*unicast_packet); - int ucf_hdr_len = sizeof(*frag1); - int data_len = skb->len - uc_hdr_len; - int large_tail = 0, ret = NET_RX_DROP; - uint16_t seqno; - - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - goto dropped; - - frag_skb = dev_alloc_skb(data_len - (data_len / 2) + ucf_hdr_len); - if (!frag_skb) - goto dropped; - - skb->priority = TC_PRIO_CONTROL; - skb_reserve(frag_skb, ucf_hdr_len); - - unicast_packet = (struct batadv_unicast_packet *)skb->data; - memcpy(&tmp_uc, unicast_packet, uc_hdr_len); - skb_split(skb, frag_skb, data_len / 2 + uc_hdr_len); - - if (batadv_skb_head_push(skb, ucf_hdr_len - uc_hdr_len) < 0 || - batadv_skb_head_push(frag_skb, ucf_hdr_len) < 0) - goto drop_frag; - - frag1 = (struct batadv_unicast_frag_packet *)skb->data; - frag2 = (struct batadv_unicast_frag_packet *)frag_skb->data; - - memcpy(frag1, &tmp_uc, sizeof(tmp_uc)); - - frag1->header.ttl--; - frag1->header.version = BATADV_COMPAT_VERSION; - frag1->header.packet_type = BATADV_UNICAST_FRAG; - - memcpy(frag1->orig, primary_if->net_dev->dev_addr, ETH_ALEN); - memcpy(frag2, frag1, sizeof(*frag2)); - - if (data_len & 1) - large_tail = BATADV_UNI_FRAG_LARGETAIL; - - frag1->flags = BATADV_UNI_FRAG_HEAD | large_tail; - frag2->flags = large_tail; - - seqno = atomic_add_return(2, &hard_iface->frag_seqno); - frag1->seqno = htons(seqno - 1); - frag2->seqno = htons(seqno); - - batadv_send_skb_packet(skb, hard_iface, dstaddr); - batadv_send_skb_packet(frag_skb, hard_iface, dstaddr); - ret = NET_RX_SUCCESS; - goto out; - -drop_frag: - kfree_skb(frag_skb); -dropped: - kfree_skb(skb); -out: - if (primary_if) - batadv_hardif_free_ref(primary_if); - return ret; -} - -/** - * batadv_unicast_push_and_fill_skb - extends the buffer and initializes the - * common fields for unicast packets - * @skb: packet - * @hdr_size: amount of bytes to push at the beginning of the skb - * @orig_node: the destination node - * - * Returns false if the buffer extension was not possible or true otherwise - */ -static bool batadv_unicast_push_and_fill_skb(struct sk_buff *skb, int hdr_size, - struct batadv_orig_node *orig_node) -{ - struct batadv_unicast_packet *unicast_packet; - uint8_t ttvn = (uint8_t)atomic_read(&orig_node->last_ttvn); - - if (batadv_skb_head_push(skb, hdr_size) < 0) - return false; - - unicast_packet = (struct batadv_unicast_packet *)skb->data; - unicast_packet->header.version = BATADV_COMPAT_VERSION; - /* batman packet type: unicast */ - unicast_packet->header.packet_type = BATADV_UNICAST; - /* set unicast ttl */ - unicast_packet->header.ttl = BATADV_TTL; - /* copy the destination for faster routing */ - memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN); - /* set the destination tt version number */ - unicast_packet->ttvn = ttvn; - - return true; -} - -/** - * batadv_unicast_prepare_skb - encapsulate an skb with a unicast header - * @skb: the skb containing the payload to encapsulate - * @orig_node: the destination node - * - * Returns false if the payload could not be encapsulated or true otherwise. - * - * This call might reallocate skb data. - */ -static bool batadv_unicast_prepare_skb(struct sk_buff *skb, - struct batadv_orig_node *orig_node) -{ - size_t uni_size = sizeof(struct batadv_unicast_packet); - return batadv_unicast_push_and_fill_skb(skb, uni_size, orig_node); -} - -/** - * batadv_unicast_4addr_prepare_skb - encapsulate an skb with a unicast4addr - * header - * @bat_priv: the bat priv with all the soft interface information - * @skb: the skb containing the payload to encapsulate - * @orig_node: the destination node - * @packet_subtype: the batman 4addr packet subtype to use - * - * Returns false if the payload could not be encapsulated or true otherwise. - * - * This call might reallocate skb data. - */ -bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv, - struct sk_buff *skb, - struct batadv_orig_node *orig, - int packet_subtype) -{ - struct batadv_hard_iface *primary_if; - struct batadv_unicast_4addr_packet *unicast_4addr_packet; - bool ret = false; - - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - goto out; - - /* pull the header space and fill the unicast_packet substructure. - * We can do that because the first member of the unicast_4addr_packet - * is of type struct unicast_packet - */ - if (!batadv_unicast_push_and_fill_skb(skb, - sizeof(*unicast_4addr_packet), - orig)) - goto out; - - unicast_4addr_packet = (struct batadv_unicast_4addr_packet *)skb->data; - unicast_4addr_packet->u.header.packet_type = BATADV_UNICAST_4ADDR; - memcpy(unicast_4addr_packet->src, primary_if->net_dev->dev_addr, - ETH_ALEN); - unicast_4addr_packet->subtype = packet_subtype; - unicast_4addr_packet->reserved = 0; - - ret = true; -out: - if (primary_if) - batadv_hardif_free_ref(primary_if); - return ret; -} - -/** - * batadv_unicast_generic_send_skb - send an skb as unicast - * @bat_priv: the bat priv with all the soft interface information - * @skb: payload to send - * @packet_type: the batman unicast packet type to use - * @packet_subtype: the batman packet subtype. It is ignored if packet_type is - * not BATADV_UNICAT_4ADDR - * - * Returns 1 in case of error or 0 otherwise - */ -int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv, - struct sk_buff *skb, int packet_type, - int packet_subtype) -{ - struct ethhdr *ethhdr = (struct ethhdr *)skb->data; - struct batadv_unicast_packet *unicast_packet; - struct batadv_orig_node *orig_node; - struct batadv_neigh_node *neigh_node; - int data_len = skb->len; - int ret = NET_RX_DROP; - unsigned int dev_mtu, header_len; - - /* get routing information */ - if (is_multicast_ether_addr(ethhdr->h_dest)) { - orig_node = batadv_gw_get_selected_orig(bat_priv); - if (orig_node) - goto find_router; - } - - /* check for tt host - increases orig_node refcount. - * returns NULL in case of AP isolation - */ - orig_node = batadv_transtable_search(bat_priv, ethhdr->h_source, - ethhdr->h_dest); - -find_router: - /* find_router(): - * - if orig_node is NULL it returns NULL - * - increases neigh_nodes refcount if found. - */ - neigh_node = batadv_find_router(bat_priv, orig_node, NULL); - - if (!neigh_node) - goto out; - - switch (packet_type) { - case BATADV_UNICAST: - if (!batadv_unicast_prepare_skb(skb, orig_node)) - goto out; - - header_len = sizeof(struct batadv_unicast_packet); - break; - case BATADV_UNICAST_4ADDR: - if (!batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node, - packet_subtype)) - goto out; - - header_len = sizeof(struct batadv_unicast_4addr_packet); - break; - default: - /* this function supports UNICAST and UNICAST_4ADDR only. It - * should never be invoked with any other packet type - */ - goto out; - } - - ethhdr = (struct ethhdr *)(skb->data + header_len); - unicast_packet = (struct batadv_unicast_packet *)skb->data; - - /* inform the destination node that we are still missing a correct route - * for this client. The destination will receive this packet and will - * try to reroute it because the ttvn contained in the header is less - * than the current one - */ - if (batadv_tt_global_client_is_roaming(bat_priv, ethhdr->h_dest)) - unicast_packet->ttvn = unicast_packet->ttvn - 1; - - dev_mtu = neigh_node->if_incoming->net_dev->mtu; - /* fragmentation mechanism only works for UNICAST (now) */ - if (packet_type == BATADV_UNICAST && - atomic_read(&bat_priv->fragmentation) && - data_len + sizeof(*unicast_packet) > dev_mtu) { - /* send frag skb decreases ttl */ - unicast_packet->header.ttl++; - ret = batadv_frag_send_skb(skb, bat_priv, - neigh_node->if_incoming, - neigh_node->addr); - goto out; - } - - if (batadv_send_skb_to_orig(skb, orig_node, NULL) != NET_XMIT_DROP) - ret = 0; - -out: - if (neigh_node) - batadv_neigh_node_free_ref(neigh_node); - if (orig_node) - batadv_orig_node_free_ref(orig_node); - if (ret == NET_RX_DROP) - kfree_skb(skb); - return ret; -} diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h deleted file mode 100644 index 429cf8a4a31eb367f727d494af43077b10f12854..0000000000000000000000000000000000000000 --- a/net/batman-adv/unicast.h +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright (C) 2010-2013 B.A.T.M.A.N. contributors: - * - * Andreas Langer - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - */ - -#ifndef _NET_BATMAN_ADV_UNICAST_H_ -#define _NET_BATMAN_ADV_UNICAST_H_ - -#include "packet.h" - -#define BATADV_FRAG_TIMEOUT 10000 /* purge frag list entries after time in ms */ -#define BATADV_FRAG_BUFFER_SIZE 6 /* number of list elements in buffer */ - -int batadv_frag_reassemble_skb(struct sk_buff *skb, - struct batadv_priv *bat_priv, - struct sk_buff **new_skb); -void batadv_frag_list_free(struct list_head *head); -int batadv_frag_send_skb(struct sk_buff *skb, struct batadv_priv *bat_priv, - struct batadv_hard_iface *hard_iface, - const uint8_t dstaddr[]); -bool batadv_unicast_4addr_prepare_skb(struct batadv_priv *bat_priv, - struct sk_buff *skb, - struct batadv_orig_node *orig_node, - int packet_subtype); -int batadv_unicast_generic_send_skb(struct batadv_priv *bat_priv, - struct sk_buff *skb, int packet_type, - int packet_subtype); - - -/** - * batadv_unicast_send_skb - send the skb encapsulated in a unicast packet - * @bat_priv: the bat priv with all the soft interface information - * @skb: the payload to send - */ -static inline int batadv_unicast_send_skb(struct batadv_priv *bat_priv, - struct sk_buff *skb) -{ - return batadv_unicast_generic_send_skb(bat_priv, skb, BATADV_UNICAST, - 0); -} - -/** - * batadv_unicast_send_skb - send the skb encapsulated in a unicast4addr packet - * @bat_priv: the bat priv with all the soft interface information - * @skb: the payload to send - * @packet_subtype: the batman 4addr packet subtype to use - */ -static inline int batadv_unicast_4addr_send_skb(struct batadv_priv *bat_priv, - struct sk_buff *skb, - int packet_subtype) -{ - return batadv_unicast_generic_send_skb(bat_priv, skb, - BATADV_UNICAST_4ADDR, - packet_subtype); -} - -static inline int batadv_frag_can_reassemble(const struct sk_buff *skb, int mtu) -{ - const struct batadv_unicast_frag_packet *unicast_packet; - int uneven_correction = 0; - unsigned int merged_size; - - unicast_packet = (struct batadv_unicast_frag_packet *)skb->data; - - if (unicast_packet->flags & BATADV_UNI_FRAG_LARGETAIL) { - if (unicast_packet->flags & BATADV_UNI_FRAG_HEAD) - uneven_correction = 1; - else - uneven_correction = -1; - } - - merged_size = (skb->len - sizeof(*unicast_packet)) * 2; - merged_size += sizeof(struct batadv_unicast_packet) + uneven_correction; - - return merged_size <= mtu; -} - -#endif /* _NET_BATMAN_ADV_UNICAST_H_ */ diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c deleted file mode 100644 index d8ea31a58457c1cc58d7ff753b8101d3ca0b7320..0000000000000000000000000000000000000000 --- a/net/batman-adv/vis.c +++ /dev/null @@ -1,938 +0,0 @@ -/* Copyright (C) 2008-2013 B.A.T.M.A.N. contributors: - * - * Simon Wunderlich - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - */ - -#include "main.h" -#include "send.h" -#include "translation-table.h" -#include "vis.h" -#include "soft-interface.h" -#include "hard-interface.h" -#include "hash.h" -#include "originator.h" - -#define BATADV_MAX_VIS_PACKET_SIZE 1000 - -/* hash class keys */ -static struct lock_class_key batadv_vis_hash_lock_class_key; - -/* free the info */ -static void batadv_free_info(struct kref *ref) -{ - struct batadv_vis_info *info; - struct batadv_priv *bat_priv; - struct batadv_vis_recvlist_node *entry, *tmp; - - info = container_of(ref, struct batadv_vis_info, refcount); - bat_priv = info->bat_priv; - - list_del_init(&info->send_list); - spin_lock_bh(&bat_priv->vis.list_lock); - list_for_each_entry_safe(entry, tmp, &info->recv_list, list) { - list_del(&entry->list); - kfree(entry); - } - - spin_unlock_bh(&bat_priv->vis.list_lock); - kfree_skb(info->skb_packet); - kfree(info); -} - -/* Compare two vis packets, used by the hashing algorithm */ -static int batadv_vis_info_cmp(const struct hlist_node *node, const void *data2) -{ - const struct batadv_vis_info *d1, *d2; - const struct batadv_vis_packet *p1, *p2; - - d1 = container_of(node, struct batadv_vis_info, hash_entry); - d2 = data2; - p1 = (struct batadv_vis_packet *)d1->skb_packet->data; - p2 = (struct batadv_vis_packet *)d2->skb_packet->data; - return batadv_compare_eth(p1->vis_orig, p2->vis_orig); -} - -/* hash function to choose an entry in a hash table of given size - * hash algorithm from http://en.wikipedia.org/wiki/Hash_table - */ -static uint32_t batadv_vis_info_choose(const void *data, uint32_t size) -{ - const struct batadv_vis_info *vis_info = data; - const struct batadv_vis_packet *packet; - const unsigned char *key; - uint32_t hash = 0; - size_t i; - - packet = (struct batadv_vis_packet *)vis_info->skb_packet->data; - key = packet->vis_orig; - for (i = 0; i < ETH_ALEN; i++) { - hash += key[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } - - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - - return hash % size; -} - -static struct batadv_vis_info * -batadv_vis_hash_find(struct batadv_priv *bat_priv, const void *data) -{ - struct batadv_hashtable *hash = bat_priv->vis.hash; - struct hlist_head *head; - struct batadv_vis_info *vis_info, *vis_info_tmp = NULL; - uint32_t index; - - if (!hash) - return NULL; - - index = batadv_vis_info_choose(data, hash->size); - head = &hash->table[index]; - - rcu_read_lock(); - hlist_for_each_entry_rcu(vis_info, head, hash_entry) { - if (!batadv_vis_info_cmp(&vis_info->hash_entry, data)) - continue; - - vis_info_tmp = vis_info; - break; - } - rcu_read_unlock(); - - return vis_info_tmp; -} - -/* insert interface to the list of interfaces of one originator, if it - * does not already exist in the list - */ -static void batadv_vis_data_insert_interface(const uint8_t *interface, - struct hlist_head *if_list, - bool primary) -{ - struct batadv_vis_if_list_entry *entry; - - hlist_for_each_entry(entry, if_list, list) { - if (batadv_compare_eth(entry->addr, interface)) - return; - } - - /* it's a new address, add it to the list */ - entry = kmalloc(sizeof(*entry), GFP_ATOMIC); - if (!entry) - return; - memcpy(entry->addr, interface, ETH_ALEN); - entry->primary = primary; - hlist_add_head(&entry->list, if_list); -} - -static void batadv_vis_data_read_prim_sec(struct seq_file *seq, - const struct hlist_head *if_list) -{ - struct batadv_vis_if_list_entry *entry; - - hlist_for_each_entry(entry, if_list, list) { - if (entry->primary) - seq_puts(seq, "PRIMARY, "); - else - seq_printf(seq, "SEC %pM, ", entry->addr); - } -} - -/* read an entry */ -static ssize_t -batadv_vis_data_read_entry(struct seq_file *seq, - const struct batadv_vis_info_entry *entry, - const uint8_t *src, bool primary) -{ - if (primary && entry->quality == 0) - return seq_printf(seq, "TT %pM, ", entry->dest); - else if (batadv_compare_eth(entry->src, src)) - return seq_printf(seq, "TQ %pM %d, ", entry->dest, - entry->quality); - - return 0; -} - -static void -batadv_vis_data_insert_interfaces(struct hlist_head *list, - struct batadv_vis_packet *packet, - struct batadv_vis_info_entry *entries) -{ - int i; - - for (i = 0; i < packet->entries; i++) { - if (entries[i].quality == 0) - continue; - - if (batadv_compare_eth(entries[i].src, packet->vis_orig)) - continue; - - batadv_vis_data_insert_interface(entries[i].src, list, false); - } -} - -static void batadv_vis_data_read_entries(struct seq_file *seq, - struct hlist_head *list, - struct batadv_vis_packet *packet, - struct batadv_vis_info_entry *entries) -{ - int i; - struct batadv_vis_if_list_entry *entry; - - hlist_for_each_entry(entry, list, list) { - seq_printf(seq, "%pM,", entry->addr); - - for (i = 0; i < packet->entries; i++) - batadv_vis_data_read_entry(seq, &entries[i], - entry->addr, entry->primary); - - /* add primary/secondary records */ - if (batadv_compare_eth(entry->addr, packet->vis_orig)) - batadv_vis_data_read_prim_sec(seq, list); - - seq_puts(seq, "\n"); - } -} - -static void batadv_vis_seq_print_text_bucket(struct seq_file *seq, - const struct hlist_head *head) -{ - struct batadv_vis_info *info; - struct batadv_vis_packet *packet; - uint8_t *entries_pos; - struct batadv_vis_info_entry *entries; - struct batadv_vis_if_list_entry *entry; - struct hlist_node *n; - - HLIST_HEAD(vis_if_list); - - hlist_for_each_entry_rcu(info, head, hash_entry) { - packet = (struct batadv_vis_packet *)info->skb_packet->data; - entries_pos = (uint8_t *)packet + sizeof(*packet); - entries = (struct batadv_vis_info_entry *)entries_pos; - - batadv_vis_data_insert_interface(packet->vis_orig, &vis_if_list, - true); - batadv_vis_data_insert_interfaces(&vis_if_list, packet, - entries); - batadv_vis_data_read_entries(seq, &vis_if_list, packet, - entries); - - hlist_for_each_entry_safe(entry, n, &vis_if_list, list) { - hlist_del(&entry->list); - kfree(entry); - } - } -} - -int batadv_vis_seq_print_text(struct seq_file *seq, void *offset) -{ - struct batadv_hard_iface *primary_if; - struct hlist_head *head; - struct net_device *net_dev = (struct net_device *)seq->private; - struct batadv_priv *bat_priv = netdev_priv(net_dev); - struct batadv_hashtable *hash = bat_priv->vis.hash; - uint32_t i; - int ret = 0; - int vis_server = atomic_read(&bat_priv->vis_mode); - - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - goto out; - - if (vis_server == BATADV_VIS_TYPE_CLIENT_UPDATE) - goto out; - - spin_lock_bh(&bat_priv->vis.hash_lock); - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - batadv_vis_seq_print_text_bucket(seq, head); - } - spin_unlock_bh(&bat_priv->vis.hash_lock); - -out: - if (primary_if) - batadv_hardif_free_ref(primary_if); - return ret; -} - -/* add the info packet to the send list, if it was not - * already linked in. - */ -static void batadv_send_list_add(struct batadv_priv *bat_priv, - struct batadv_vis_info *info) -{ - if (list_empty(&info->send_list)) { - kref_get(&info->refcount); - list_add_tail(&info->send_list, &bat_priv->vis.send_list); - } -} - -/* delete the info packet from the send list, if it was - * linked in. - */ -static void batadv_send_list_del(struct batadv_vis_info *info) -{ - if (!list_empty(&info->send_list)) { - list_del_init(&info->send_list); - kref_put(&info->refcount, batadv_free_info); - } -} - -/* tries to add one entry to the receive list. */ -static void batadv_recv_list_add(struct batadv_priv *bat_priv, - struct list_head *recv_list, const char *mac) -{ - struct batadv_vis_recvlist_node *entry; - - entry = kmalloc(sizeof(*entry), GFP_ATOMIC); - if (!entry) - return; - - memcpy(entry->mac, mac, ETH_ALEN); - spin_lock_bh(&bat_priv->vis.list_lock); - list_add_tail(&entry->list, recv_list); - spin_unlock_bh(&bat_priv->vis.list_lock); -} - -/* returns 1 if this mac is in the recv_list */ -static int batadv_recv_list_is_in(struct batadv_priv *bat_priv, - const struct list_head *recv_list, - const char *mac) -{ - const struct batadv_vis_recvlist_node *entry; - - spin_lock_bh(&bat_priv->vis.list_lock); - list_for_each_entry(entry, recv_list, list) { - if (batadv_compare_eth(entry->mac, mac)) { - spin_unlock_bh(&bat_priv->vis.list_lock); - return 1; - } - } - spin_unlock_bh(&bat_priv->vis.list_lock); - return 0; -} - -/* try to add the packet to the vis_hash. return NULL if invalid (e.g. too old, - * broken.. ). vis hash must be locked outside. is_new is set when the packet - * is newer than old entries in the hash. - */ -static struct batadv_vis_info * -batadv_add_packet(struct batadv_priv *bat_priv, - struct batadv_vis_packet *vis_packet, int vis_info_len, - int *is_new, int make_broadcast) -{ - struct batadv_vis_info *info, *old_info; - struct batadv_vis_packet *search_packet, *old_packet; - struct batadv_vis_info search_elem; - struct batadv_vis_packet *packet; - struct sk_buff *tmp_skb; - int hash_added; - size_t len; - size_t max_entries; - - *is_new = 0; - /* sanity check */ - if (!bat_priv->vis.hash) - return NULL; - - /* see if the packet is already in vis_hash */ - search_elem.skb_packet = dev_alloc_skb(sizeof(*search_packet)); - if (!search_elem.skb_packet) - return NULL; - len = sizeof(*search_packet); - tmp_skb = search_elem.skb_packet; - search_packet = (struct batadv_vis_packet *)skb_put(tmp_skb, len); - - memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN); - old_info = batadv_vis_hash_find(bat_priv, &search_elem); - kfree_skb(search_elem.skb_packet); - - if (old_info) { - tmp_skb = old_info->skb_packet; - old_packet = (struct batadv_vis_packet *)tmp_skb->data; - if (!batadv_seq_after(ntohl(vis_packet->seqno), - ntohl(old_packet->seqno))) { - if (old_packet->seqno == vis_packet->seqno) { - batadv_recv_list_add(bat_priv, - &old_info->recv_list, - vis_packet->sender_orig); - return old_info; - } else { - /* newer packet is already in hash. */ - return NULL; - } - } - /* remove old entry */ - batadv_hash_remove(bat_priv->vis.hash, batadv_vis_info_cmp, - batadv_vis_info_choose, old_info); - batadv_send_list_del(old_info); - kref_put(&old_info->refcount, batadv_free_info); - } - - info = kmalloc(sizeof(*info), GFP_ATOMIC); - if (!info) - return NULL; - - len = sizeof(*packet) + vis_info_len; - info->skb_packet = netdev_alloc_skb_ip_align(NULL, len + ETH_HLEN); - if (!info->skb_packet) { - kfree(info); - return NULL; - } - info->skb_packet->priority = TC_PRIO_CONTROL; - skb_reserve(info->skb_packet, ETH_HLEN); - packet = (struct batadv_vis_packet *)skb_put(info->skb_packet, len); - - kref_init(&info->refcount); - INIT_LIST_HEAD(&info->send_list); - INIT_LIST_HEAD(&info->recv_list); - info->first_seen = jiffies; - info->bat_priv = bat_priv; - memcpy(packet, vis_packet, len); - - /* initialize and add new packet. */ - *is_new = 1; - - /* Make it a broadcast packet, if required */ - if (make_broadcast) - memcpy(packet->target_orig, batadv_broadcast_addr, ETH_ALEN); - - /* repair if entries is longer than packet. */ - max_entries = vis_info_len / sizeof(struct batadv_vis_info_entry); - if (packet->entries > max_entries) - packet->entries = max_entries; - - batadv_recv_list_add(bat_priv, &info->recv_list, packet->sender_orig); - - /* try to add it */ - hash_added = batadv_hash_add(bat_priv->vis.hash, batadv_vis_info_cmp, - batadv_vis_info_choose, info, - &info->hash_entry); - if (hash_added != 0) { - /* did not work (for some reason) */ - kref_put(&info->refcount, batadv_free_info); - info = NULL; - } - - return info; -} - -/* handle the server sync packet, forward if needed. */ -void batadv_receive_server_sync_packet(struct batadv_priv *bat_priv, - struct batadv_vis_packet *vis_packet, - int vis_info_len) -{ - struct batadv_vis_info *info; - int is_new, make_broadcast; - int vis_server = atomic_read(&bat_priv->vis_mode); - - make_broadcast = (vis_server == BATADV_VIS_TYPE_SERVER_SYNC); - - spin_lock_bh(&bat_priv->vis.hash_lock); - info = batadv_add_packet(bat_priv, vis_packet, vis_info_len, - &is_new, make_broadcast); - if (!info) - goto end; - - /* only if we are server ourselves and packet is newer than the one in - * hash. - */ - if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC && is_new) - batadv_send_list_add(bat_priv, info); -end: - spin_unlock_bh(&bat_priv->vis.hash_lock); -} - -/* handle an incoming client update packet and schedule forward if needed. */ -void batadv_receive_client_update_packet(struct batadv_priv *bat_priv, - struct batadv_vis_packet *vis_packet, - int vis_info_len) -{ - struct batadv_vis_info *info; - struct batadv_vis_packet *packet; - int is_new; - int vis_server = atomic_read(&bat_priv->vis_mode); - int are_target = 0; - - /* clients shall not broadcast. */ - if (is_broadcast_ether_addr(vis_packet->target_orig)) - return; - - /* Are we the target for this VIS packet? */ - if (vis_server == BATADV_VIS_TYPE_SERVER_SYNC && - batadv_is_my_mac(bat_priv, vis_packet->target_orig)) - are_target = 1; - - spin_lock_bh(&bat_priv->vis.hash_lock); - info = batadv_add_packet(bat_priv, vis_packet, vis_info_len, - &is_new, are_target); - - if (!info) - goto end; - /* note that outdated packets will be dropped at this point. */ - - packet = (struct batadv_vis_packet *)info->skb_packet->data; - - /* send only if we're the target server or ... */ - if (are_target && is_new) { - packet->vis_type = BATADV_VIS_TYPE_SERVER_SYNC; /* upgrade! */ - batadv_send_list_add(bat_priv, info); - - /* ... we're not the recipient (and thus need to forward). */ - } else if (!batadv_is_my_mac(bat_priv, packet->target_orig)) { - batadv_send_list_add(bat_priv, info); - } - -end: - spin_unlock_bh(&bat_priv->vis.hash_lock); -} - -/* Walk the originators and find the VIS server with the best tq. Set the packet - * address to its address and return the best_tq. - * - * Must be called with the originator hash locked - */ -static int batadv_find_best_vis_server(struct batadv_priv *bat_priv, - struct batadv_vis_info *info) -{ - struct batadv_hashtable *hash = bat_priv->orig_hash; - struct batadv_neigh_node *router; - struct hlist_head *head; - struct batadv_orig_node *orig_node; - struct batadv_vis_packet *packet; - int best_tq = -1; - uint32_t i; - - packet = (struct batadv_vis_packet *)info->skb_packet->data; - - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - - rcu_read_lock(); - hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - router = batadv_orig_node_get_router(orig_node); - if (!router) - continue; - - if ((orig_node->flags & BATADV_VIS_SERVER) && - (router->tq_avg > best_tq)) { - best_tq = router->tq_avg; - memcpy(packet->target_orig, orig_node->orig, - ETH_ALEN); - } - batadv_neigh_node_free_ref(router); - } - rcu_read_unlock(); - } - - return best_tq; -} - -/* Return true if the vis packet is full. */ -static bool batadv_vis_packet_full(const struct batadv_vis_info *info) -{ - const struct batadv_vis_packet *packet; - size_t num; - - packet = (struct batadv_vis_packet *)info->skb_packet->data; - num = BATADV_MAX_VIS_PACKET_SIZE / sizeof(struct batadv_vis_info_entry); - - if (num < packet->entries + 1) - return true; - return false; -} - -/* generates a packet of own vis data, - * returns 0 on success, -1 if no packet could be generated - */ -static int batadv_generate_vis_packet(struct batadv_priv *bat_priv) -{ - struct batadv_hashtable *hash = bat_priv->orig_hash; - struct hlist_head *head; - struct batadv_orig_node *orig_node; - struct batadv_neigh_node *router; - struct batadv_vis_info *info = bat_priv->vis.my_info; - struct batadv_vis_packet *packet; - struct batadv_vis_info_entry *entry; - struct batadv_tt_common_entry *tt_common_entry; - uint8_t *packet_pos; - int best_tq = -1; - uint32_t i; - - info->first_seen = jiffies; - packet = (struct batadv_vis_packet *)info->skb_packet->data; - packet->vis_type = atomic_read(&bat_priv->vis_mode); - - memcpy(packet->target_orig, batadv_broadcast_addr, ETH_ALEN); - packet->header.ttl = BATADV_TTL; - packet->seqno = htonl(ntohl(packet->seqno) + 1); - packet->entries = 0; - packet->reserved = 0; - skb_trim(info->skb_packet, sizeof(*packet)); - - if (packet->vis_type == BATADV_VIS_TYPE_CLIENT_UPDATE) { - best_tq = batadv_find_best_vis_server(bat_priv, info); - - if (best_tq < 0) - return best_tq; - } - - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - - rcu_read_lock(); - hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - router = batadv_orig_node_get_router(orig_node); - if (!router) - continue; - - if (!batadv_compare_eth(router->addr, orig_node->orig)) - goto next; - - if (router->if_incoming->if_status != BATADV_IF_ACTIVE) - goto next; - - if (router->tq_avg < 1) - goto next; - - /* fill one entry into buffer. */ - packet_pos = skb_put(info->skb_packet, sizeof(*entry)); - entry = (struct batadv_vis_info_entry *)packet_pos; - memcpy(entry->src, - router->if_incoming->net_dev->dev_addr, - ETH_ALEN); - memcpy(entry->dest, orig_node->orig, ETH_ALEN); - entry->quality = router->tq_avg; - packet->entries++; - -next: - batadv_neigh_node_free_ref(router); - - if (batadv_vis_packet_full(info)) - goto unlock; - } - rcu_read_unlock(); - } - - hash = bat_priv->tt.local_hash; - - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - - rcu_read_lock(); - hlist_for_each_entry_rcu(tt_common_entry, head, - hash_entry) { - packet_pos = skb_put(info->skb_packet, sizeof(*entry)); - entry = (struct batadv_vis_info_entry *)packet_pos; - memset(entry->src, 0, ETH_ALEN); - memcpy(entry->dest, tt_common_entry->addr, ETH_ALEN); - entry->quality = 0; /* 0 means TT */ - packet->entries++; - - if (batadv_vis_packet_full(info)) - goto unlock; - } - rcu_read_unlock(); - } - - return 0; - -unlock: - rcu_read_unlock(); - return 0; -} - -/* free old vis packets. Must be called with this vis_hash_lock - * held - */ -static void batadv_purge_vis_packets(struct batadv_priv *bat_priv) -{ - uint32_t i; - struct batadv_hashtable *hash = bat_priv->vis.hash; - struct hlist_node *node_tmp; - struct hlist_head *head; - struct batadv_vis_info *info; - - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - - hlist_for_each_entry_safe(info, node_tmp, - head, hash_entry) { - /* never purge own data. */ - if (info == bat_priv->vis.my_info) - continue; - - if (batadv_has_timed_out(info->first_seen, - BATADV_VIS_TIMEOUT)) { - hlist_del(&info->hash_entry); - batadv_send_list_del(info); - kref_put(&info->refcount, batadv_free_info); - } - } - } -} - -static void batadv_broadcast_vis_packet(struct batadv_priv *bat_priv, - struct batadv_vis_info *info) -{ - struct batadv_hashtable *hash = bat_priv->orig_hash; - struct hlist_head *head; - struct batadv_orig_node *orig_node; - struct batadv_vis_packet *packet; - struct sk_buff *skb; - uint32_t i, res; - - - packet = (struct batadv_vis_packet *)info->skb_packet->data; - - /* send to all routers in range. */ - for (i = 0; i < hash->size; i++) { - head = &hash->table[i]; - - rcu_read_lock(); - hlist_for_each_entry_rcu(orig_node, head, hash_entry) { - /* if it's a vis server and reachable, send it. */ - if (!(orig_node->flags & BATADV_VIS_SERVER)) - continue; - - /* don't send it if we already received the packet from - * this node. - */ - if (batadv_recv_list_is_in(bat_priv, &info->recv_list, - orig_node->orig)) - continue; - - memcpy(packet->target_orig, orig_node->orig, ETH_ALEN); - skb = skb_clone(info->skb_packet, GFP_ATOMIC); - if (!skb) - continue; - - res = batadv_send_skb_to_orig(skb, orig_node, NULL); - if (res == NET_XMIT_DROP) - kfree_skb(skb); - } - rcu_read_unlock(); - } -} - -static void batadv_unicast_vis_packet(struct batadv_priv *bat_priv, - struct batadv_vis_info *info) -{ - struct batadv_orig_node *orig_node; - struct sk_buff *skb; - struct batadv_vis_packet *packet; - - packet = (struct batadv_vis_packet *)info->skb_packet->data; - - orig_node = batadv_orig_hash_find(bat_priv, packet->target_orig); - if (!orig_node) - goto out; - - skb = skb_clone(info->skb_packet, GFP_ATOMIC); - if (!skb) - goto out; - - if (batadv_send_skb_to_orig(skb, orig_node, NULL) == NET_XMIT_DROP) - kfree_skb(skb); - -out: - if (orig_node) - batadv_orig_node_free_ref(orig_node); -} - -/* only send one vis packet. called from batadv_send_vis_packets() */ -static void batadv_send_vis_packet(struct batadv_priv *bat_priv, - struct batadv_vis_info *info) -{ - struct batadv_hard_iface *primary_if; - struct batadv_vis_packet *packet; - - primary_if = batadv_primary_if_get_selected(bat_priv); - if (!primary_if) - goto out; - - packet = (struct batadv_vis_packet *)info->skb_packet->data; - if (packet->header.ttl < 2) { - pr_debug("Error - can't send vis packet: ttl exceeded\n"); - goto out; - } - - memcpy(packet->sender_orig, primary_if->net_dev->dev_addr, ETH_ALEN); - packet->header.ttl--; - - if (is_broadcast_ether_addr(packet->target_orig)) - batadv_broadcast_vis_packet(bat_priv, info); - else - batadv_unicast_vis_packet(bat_priv, info); - packet->header.ttl++; /* restore TTL */ - -out: - if (primary_if) - batadv_hardif_free_ref(primary_if); -} - -/* called from timer; send (and maybe generate) vis packet. */ -static void batadv_send_vis_packets(struct work_struct *work) -{ - struct delayed_work *delayed_work; - struct batadv_priv *bat_priv; - struct batadv_priv_vis *priv_vis; - struct batadv_vis_info *info; - - delayed_work = container_of(work, struct delayed_work, work); - priv_vis = container_of(delayed_work, struct batadv_priv_vis, work); - bat_priv = container_of(priv_vis, struct batadv_priv, vis); - spin_lock_bh(&bat_priv->vis.hash_lock); - batadv_purge_vis_packets(bat_priv); - - if (batadv_generate_vis_packet(bat_priv) == 0) { - /* schedule if generation was successful */ - batadv_send_list_add(bat_priv, bat_priv->vis.my_info); - } - - while (!list_empty(&bat_priv->vis.send_list)) { - info = list_first_entry(&bat_priv->vis.send_list, - typeof(*info), send_list); - - kref_get(&info->refcount); - spin_unlock_bh(&bat_priv->vis.hash_lock); - - batadv_send_vis_packet(bat_priv, info); - - spin_lock_bh(&bat_priv->vis.hash_lock); - batadv_send_list_del(info); - kref_put(&info->refcount, batadv_free_info); - } - spin_unlock_bh(&bat_priv->vis.hash_lock); - - queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work, - msecs_to_jiffies(BATADV_VIS_INTERVAL)); -} - -/* init the vis server. this may only be called when if_list is already - * initialized (e.g. bat0 is initialized, interfaces have been added) - */ -int batadv_vis_init(struct batadv_priv *bat_priv) -{ - struct batadv_vis_packet *packet; - int hash_added; - unsigned int len; - unsigned long first_seen; - struct sk_buff *tmp_skb; - - if (bat_priv->vis.hash) - return 0; - - spin_lock_bh(&bat_priv->vis.hash_lock); - - bat_priv->vis.hash = batadv_hash_new(256); - if (!bat_priv->vis.hash) { - pr_err("Can't initialize vis_hash\n"); - goto err; - } - - batadv_hash_set_lock_class(bat_priv->vis.hash, - &batadv_vis_hash_lock_class_key); - - bat_priv->vis.my_info = kmalloc(BATADV_MAX_VIS_PACKET_SIZE, GFP_ATOMIC); - if (!bat_priv->vis.my_info) - goto err; - - len = sizeof(*packet) + BATADV_MAX_VIS_PACKET_SIZE + ETH_HLEN; - bat_priv->vis.my_info->skb_packet = netdev_alloc_skb_ip_align(NULL, - len); - if (!bat_priv->vis.my_info->skb_packet) - goto free_info; - - bat_priv->vis.my_info->skb_packet->priority = TC_PRIO_CONTROL; - skb_reserve(bat_priv->vis.my_info->skb_packet, ETH_HLEN); - tmp_skb = bat_priv->vis.my_info->skb_packet; - packet = (struct batadv_vis_packet *)skb_put(tmp_skb, sizeof(*packet)); - - /* prefill the vis info */ - first_seen = jiffies - msecs_to_jiffies(BATADV_VIS_INTERVAL); - bat_priv->vis.my_info->first_seen = first_seen; - INIT_LIST_HEAD(&bat_priv->vis.my_info->recv_list); - INIT_LIST_HEAD(&bat_priv->vis.my_info->send_list); - kref_init(&bat_priv->vis.my_info->refcount); - bat_priv->vis.my_info->bat_priv = bat_priv; - packet->header.version = BATADV_COMPAT_VERSION; - packet->header.packet_type = BATADV_VIS; - packet->header.ttl = BATADV_TTL; - packet->seqno = 0; - packet->reserved = 0; - packet->entries = 0; - - INIT_LIST_HEAD(&bat_priv->vis.send_list); - - hash_added = batadv_hash_add(bat_priv->vis.hash, batadv_vis_info_cmp, - batadv_vis_info_choose, - bat_priv->vis.my_info, - &bat_priv->vis.my_info->hash_entry); - if (hash_added != 0) { - pr_err("Can't add own vis packet into hash\n"); - /* not in hash, need to remove it manually. */ - kref_put(&bat_priv->vis.my_info->refcount, batadv_free_info); - goto err; - } - - spin_unlock_bh(&bat_priv->vis.hash_lock); - - INIT_DELAYED_WORK(&bat_priv->vis.work, batadv_send_vis_packets); - queue_delayed_work(batadv_event_workqueue, &bat_priv->vis.work, - msecs_to_jiffies(BATADV_VIS_INTERVAL)); - - return 0; - -free_info: - kfree(bat_priv->vis.my_info); - bat_priv->vis.my_info = NULL; -err: - spin_unlock_bh(&bat_priv->vis.hash_lock); - batadv_vis_quit(bat_priv); - return -ENOMEM; -} - -/* Decrease the reference count on a hash item info */ -static void batadv_free_info_ref(struct hlist_node *node, void *arg) -{ - struct batadv_vis_info *info; - - info = container_of(node, struct batadv_vis_info, hash_entry); - batadv_send_list_del(info); - kref_put(&info->refcount, batadv_free_info); -} - -/* shutdown vis-server */ -void batadv_vis_quit(struct batadv_priv *bat_priv) -{ - if (!bat_priv->vis.hash) - return; - - cancel_delayed_work_sync(&bat_priv->vis.work); - - spin_lock_bh(&bat_priv->vis.hash_lock); - /* properly remove, kill timers ... */ - batadv_hash_delete(bat_priv->vis.hash, batadv_free_info_ref, NULL); - bat_priv->vis.hash = NULL; - bat_priv->vis.my_info = NULL; - spin_unlock_bh(&bat_priv->vis.hash_lock); -} diff --git a/net/batman-adv/vis.h b/net/batman-adv/vis.h deleted file mode 100644 index ad92b0e3c230de211bfd2aca90f1c9431bc1b9c1..0000000000000000000000000000000000000000 --- a/net/batman-adv/vis.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (C) 2008-2013 B.A.T.M.A.N. contributors: - * - * Simon Wunderlich, Marek Lindner - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General Public - * License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA - */ - -#ifndef _NET_BATMAN_ADV_VIS_H_ -#define _NET_BATMAN_ADV_VIS_H_ - -/* timeout of vis packets in milliseconds */ -#define BATADV_VIS_TIMEOUT 200000 - -int batadv_vis_seq_print_text(struct seq_file *seq, void *offset); -void batadv_receive_server_sync_packet(struct batadv_priv *bat_priv, - struct batadv_vis_packet *vis_packet, - int vis_info_len); -void batadv_receive_client_update_packet(struct batadv_priv *bat_priv, - struct batadv_vis_packet *vis_packet, - int vis_info_len); -int batadv_vis_init(struct batadv_priv *bat_priv); -void batadv_vis_quit(struct batadv_priv *bat_priv); - -#endif /* _NET_BATMAN_ADV_VIS_H_ */ diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index dea6a287daca88b666eaee999043a3abf6dd853a..6a791e73e39d936f0d9958d506c540c5a2805727 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -11,3 +11,5 @@ obj-$(CONFIG_BT_HIDP) += hidp/ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \ a2mp.o amp.o + +subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 17f33a62f6db559824d9dc1b3973acb779fa9162..efcd108822c43134e3d64755e24c16410f0598d5 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -15,8 +15,9 @@ #include #include #include -#include -#include + +#include "a2mp.h" +#include "amp.h" /* Global AMP Manager list */ LIST_HEAD(amp_mgr_list); @@ -75,33 +76,26 @@ u8 __next_ident(struct amp_mgr *mgr) return mgr->ident; } -static inline void __a2mp_cl_bredr(struct a2mp_cl *cl) -{ - cl->id = 0; - cl->type = 0; - cl->status = 1; -} - /* hci_dev_list shall be locked */ -static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl, u8 num_ctrl) +static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl) { - int i = 0; struct hci_dev *hdev; + int i = 1; - __a2mp_cl_bredr(cl); + cl[0].id = AMP_ID_BREDR; + cl[0].type = AMP_TYPE_BREDR; + cl[0].status = AMP_STATUS_BLUETOOTH_ONLY; list_for_each_entry(hdev, &hci_dev_list, list) { - /* Iterate through AMP controllers */ - if (hdev->id == HCI_BREDR_ID) - continue; - - /* Starting from second entry */ - if (++i >= num_ctrl) - return; - - cl[i].id = hdev->id; - cl[i].type = hdev->amp_type; - cl[i].status = hdev->amp_status; + if (hdev->dev_type == HCI_AMP) { + cl[i].id = hdev->id; + cl[i].type = hdev->amp_type; + if (test_bit(HCI_UP, &hdev->flags)) + cl[i].status = hdev->amp_status; + else + cl[i].status = AMP_STATUS_POWERED_DOWN; + i++; + } } } @@ -129,6 +123,7 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb, struct a2mp_discov_rsp *rsp; u16 ext_feat; u8 num_ctrl; + struct hci_dev *hdev; if (len < sizeof(*req)) return -EINVAL; @@ -152,7 +147,14 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb, read_lock(&hci_dev_list_lock); - num_ctrl = __hci_num_ctrl(); + /* at minimum the BR/EDR needs to be listed */ + num_ctrl = 1; + + list_for_each_entry(hdev, &hci_dev_list, list) { + if (hdev->dev_type == HCI_AMP) + num_ctrl++; + } + len = num_ctrl * sizeof(struct a2mp_cl) + sizeof(*rsp); rsp = kmalloc(len, GFP_ATOMIC); if (!rsp) { @@ -163,7 +165,7 @@ static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb, rsp->mtu = __constant_cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU); rsp->ext_feat = 0; - __a2mp_add_cl(mgr, rsp->cl, num_ctrl); + __a2mp_add_cl(mgr, rsp->cl); read_unlock(&hci_dev_list_lock); @@ -208,7 +210,7 @@ static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type, cl->status); - if (cl->id != HCI_BREDR_ID && cl->type == HCI_AMP) { + if (cl->id != AMP_ID_BREDR && cl->type != AMP_TYPE_BREDR) { struct a2mp_info_req req; found = true; @@ -344,7 +346,7 @@ static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb, tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); hdev = hci_dev_get(req->id); - if (!hdev || hdev->amp_type == HCI_BREDR || tmp) { + if (!hdev || hdev->amp_type == AMP_TYPE_BREDR || tmp) { struct a2mp_amp_assoc_rsp rsp; rsp.id = req->id; @@ -451,7 +453,7 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, rsp.remote_id = req->local_id; hdev = hci_dev_get(req->remote_id); - if (!hdev || hdev->amp_type != HCI_AMP) { + if (!hdev || hdev->amp_type == AMP_TYPE_BREDR) { rsp.status = A2MP_STATUS_INVALID_CTRL_ID; goto send_rsp; } @@ -535,7 +537,8 @@ static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, goto send_rsp; } - hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, mgr->l2cap_conn->dst); + hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, + &mgr->l2cap_conn->hcon->dst); if (!hcon) { BT_ERR("No phys link exist"); rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS; @@ -669,7 +672,8 @@ static void a2mp_chan_close_cb(struct l2cap_chan *chan) l2cap_chan_put(chan); } -static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state) +static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state, + int err) { struct amp_mgr *mgr = chan->data; @@ -706,6 +710,9 @@ static struct l2cap_ops a2mp_chan_ops = { .teardown = l2cap_chan_no_teardown, .ready = l2cap_chan_no_ready, .defer = l2cap_chan_no_defer, + .resume = l2cap_chan_no_resume, + .set_shutdown = l2cap_chan_no_set_shutdown, + .get_sndtimeo = l2cap_chan_no_get_sndtimeo, }; static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) @@ -829,6 +836,9 @@ struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, { struct amp_mgr *mgr; + if (conn->hcon->type != ACL_LINK) + return NULL; + mgr = amp_mgr_create(conn, false); if (!mgr) { BT_ERR("Could not create AMP manager"); @@ -871,7 +881,7 @@ void a2mp_send_getinfo_rsp(struct hci_dev *hdev) rsp.id = hdev->id; rsp.status = A2MP_STATUS_INVALID_CTRL_ID; - if (hdev->amp_type != HCI_BREDR) { + if (hdev->amp_type != AMP_TYPE_BREDR) { rsp.status = 0; rsp.total_bw = cpu_to_le32(hdev->amp_total_bw); rsp.max_bw = cpu_to_le32(hdev->amp_max_bw); diff --git a/include/net/bluetooth/a2mp.h b/net/bluetooth/a2mp.h similarity index 100% rename from include/net/bluetooth/a2mp.h rename to net/bluetooth/a2mp.h diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 9096137c889c4857507fcb60828b8d7ad581f424..f6a1671ea2ff793bfca0efdd1541765803fb4d03 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -25,12 +25,13 @@ /* Bluetooth address family and sockets. */ #include +#include #include #include #include -#define VERSION "2.16" +#define VERSION "2.17" /* Bluetooth sockets */ #define BT_MAX_PROTO 8 @@ -221,12 +222,12 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, if (flags & (MSG_OOB)) return -EOPNOTSUPP; - msg->msg_namelen = 0; - skb = skb_recv_datagram(sk, flags, noblock, &err); if (!skb) { - if (sk->sk_shutdown & RCV_SHUTDOWN) + if (sk->sk_shutdown & RCV_SHUTDOWN) { + msg->msg_namelen = 0; return 0; + } return err; } @@ -238,9 +239,16 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock, skb_reset_transport_header(skb); err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); - if (err == 0) + if (err == 0) { sock_recv_ts_and_drops(msg, sk, skb); + if (bt_sk(sk)->skb_msg_name) + bt_sk(sk)->skb_msg_name(skb, msg->msg_name, + &msg->msg_namelen); + else + msg->msg_namelen = 0; + } + skb_free_datagram(sk, skb); return err ? : copied; @@ -490,6 +498,7 @@ int bt_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) } EXPORT_SYMBOL(bt_sock_ioctl); +/* This function expects the sk lock to be held when called */ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) { DECLARE_WAITQUEUE(wait, current); @@ -525,6 +534,46 @@ int bt_sock_wait_state(struct sock *sk, int state, unsigned long timeo) } EXPORT_SYMBOL(bt_sock_wait_state); +/* This function expects the sk lock to be held when called */ +int bt_sock_wait_ready(struct sock *sk, unsigned long flags) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long timeo; + int err = 0; + + BT_DBG("sk %p", sk); + + timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); + + add_wait_queue(sk_sleep(sk), &wait); + set_current_state(TASK_INTERRUPTIBLE); + while (test_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags)) { + if (!timeo) { + err = -EAGAIN; + break; + } + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + set_current_state(TASK_INTERRUPTIBLE); + + err = sock_error(sk); + if (err) + break; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(sk_sleep(sk), &wait); + + return err; +} +EXPORT_SYMBOL(bt_sock_wait_ready); + #ifdef CONFIG_PROC_FS struct bt_seq_state { struct bt_sock_list *l; @@ -563,7 +612,7 @@ static int bt_seq_show(struct seq_file *seq, void *v) struct bt_sock_list *l = s->l; if (v == SEQ_START_TOKEN) { - seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Src Dst Parent"); + seq_puts(seq ,"sk RefCnt Rmem Wmem User Inode Parent"); if (l->custom_seq_show) { seq_putc(seq, ' '); @@ -576,15 +625,13 @@ static int bt_seq_show(struct seq_file *seq, void *v) struct bt_sock *bt = bt_sk(sk); seq_printf(seq, - "%pK %-6d %-6u %-6u %-6u %-6lu %pMR %pMR %-6lu", + "%pK %-6d %-6u %-6u %-6u %-6lu %-6lu", sk, atomic_read(&sk->sk_refcnt), sk_rmem_alloc_get(sk), sk_wmem_alloc_get(sk), from_kuid(seq_user_ns(seq), sock_i_uid(sk)), sock_i_ino(sk), - &bt->src, - &bt->dst, bt->parent? sock_i_ino(bt->parent): 0LU); if (l->custom_seq_show) { @@ -662,12 +709,17 @@ static struct net_proto_family bt_sock_family_ops = { .create = bt_sock_create, }; +struct dentry *bt_debugfs; +EXPORT_SYMBOL_GPL(bt_debugfs); + static int __init bt_init(void) { int err; BT_INFO("Core ver %s", VERSION); + bt_debugfs = debugfs_create_dir("bluetooth", NULL); + err = bt_sysfs_init(); if (err < 0) return err; @@ -708,7 +760,6 @@ error: static void __exit bt_exit(void) { - sco_exit(); l2cap_exit(); @@ -718,6 +769,8 @@ static void __exit bt_exit(void) sock_unregister(PF_BLUETOOTH); bt_sysfs_cleanup(); + + debugfs_remove_recursive(bt_debugfs); } subsys_initcall(bt_init); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index d459ed43c779d776e453634db290d08f48d5a7c7..bb39509b3f065e2a0d18e1a53cfcfabc8bfe779e 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -14,10 +14,11 @@ #include #include #include -#include -#include #include +#include "a2mp.h" +#include "amp.h" + /* Remote AMP Controllers interface */ void amp_ctrl_get(struct amp_ctrl *ctrl) { @@ -110,7 +111,7 @@ static u8 __next_handle(struct amp_mgr *mgr) struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr, u8 remote_id, bool out) { - bdaddr_t *dst = mgr->l2cap_conn->dst; + bdaddr_t *dst = &mgr->l2cap_conn->hcon->dst; struct hci_conn *hcon; hcon = hci_conn_add(hdev, AMP_LINK, dst); @@ -409,7 +410,8 @@ void amp_create_logical_link(struct l2cap_chan *chan) struct hci_cp_create_accept_logical_link cp; struct hci_dev *hdev; - BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, chan->conn->dst); + BT_DBG("chan %p hs_hcon %p dst %pMR", chan, hs_hcon, + &chan->conn->hcon->dst); if (!hs_hcon) return; diff --git a/include/net/bluetooth/amp.h b/net/bluetooth/amp.h similarity index 100% rename from include/net/bluetooth/amp.h rename to net/bluetooth/amp.h diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index e430b1abcd2fabf102d15cd4f99399c983fcf9f2..a841d3e776c5e091c19efea8423750edd018e946 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -32,6 +32,7 @@ #include #include +#include #include #include "bnep.h" @@ -510,20 +511,13 @@ static int bnep_session(void *arg) static struct device *bnep_get_device(struct bnep_session *session) { - bdaddr_t *src = &bt_sk(session->sock->sk)->src; - bdaddr_t *dst = &bt_sk(session->sock->sk)->dst; - struct hci_dev *hdev; struct hci_conn *conn; - hdev = hci_get_route(dst, src); - if (!hdev) + conn = l2cap_pi(session->sock->sk)->chan->conn->hcon; + if (!conn) return NULL; - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - - hci_dev_put(hdev); - - return conn ? &conn->dev : NULL; + return &conn->dev; } static struct device_type bnep_type = { @@ -539,8 +533,8 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock) BT_DBG(""); - baswap((void *) dst, &bt_sk(sock->sk)->dst); - baswap((void *) src, &bt_sk(sock->sk)->src); + baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst); + baswap((void *) src, &l2cap_pi(sock->sk)->chan->src); /* session struct allocated as private part of net_device */ dev = alloc_netdev(sizeof(struct bnep_session), diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index e0a6ebf2baa6fecd58a0cc13ea6f2906c6614a46..67fe5e84e68f0bffb166bbcfb6cdb736f15ab05e 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -340,20 +340,20 @@ int cmtp_add_connection(struct cmtp_connadd_req *req, struct socket *sock) down_write(&cmtp_session_sem); - s = __cmtp_get_session(&bt_sk(sock->sk)->dst); + s = __cmtp_get_session(&l2cap_pi(sock->sk)->chan->dst); if (s && s->state == BT_CONNECTED) { err = -EEXIST; goto failed; } - bacpy(&session->bdaddr, &bt_sk(sock->sk)->dst); + bacpy(&session->bdaddr, &l2cap_pi(sock->sk)->chan->dst); session->mtu = min_t(uint, l2cap_pi(sock->sk)->chan->omtu, l2cap_pi(sock->sk)->chan->imtu); BT_DBG("mtu %d", session->mtu); - sprintf(session->name, "%pMR", &bt_sk(sock->sk)->dst); + sprintf(session->name, "%pMR", &session->bdaddr); session->sock = sock; session->state = BT_CONFIG; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index f0817121ec5e6b0c5f50e82493f9033376908f61..ba5366c320dacc7d4db4659aa144051baf1b035a 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -28,8 +28,9 @@ #include #include -#include -#include + +#include "smp.h" +#include "a2mp.h" struct sco_param { u16 pkt_type; @@ -49,30 +50,6 @@ static const struct sco_param sco_param_wideband[] = { { EDR_ESCO_MASK | ESCO_EV3, 0x0008 }, /* T1 */ }; -static void hci_le_create_connection(struct hci_conn *conn) -{ - struct hci_dev *hdev = conn->hdev; - struct hci_cp_le_create_conn cp; - - conn->state = BT_CONNECT; - conn->out = true; - conn->link_mode |= HCI_LM_MASTER; - conn->sec_level = BT_SECURITY_LOW; - - memset(&cp, 0, sizeof(cp)); - cp.scan_interval = __constant_cpu_to_le16(0x0060); - cp.scan_window = __constant_cpu_to_le16(0x0030); - bacpy(&cp.peer_addr, &conn->dst); - cp.peer_addr_type = conn->dst_type; - cp.conn_interval_min = __constant_cpu_to_le16(0x0028); - cp.conn_interval_max = __constant_cpu_to_le16(0x0038); - cp.supervision_timeout = __constant_cpu_to_le16(0x002a); - cp.min_ce_len = __constant_cpu_to_le16(0x0000); - cp.max_ce_len = __constant_cpu_to_le16(0x0000); - - hci_send_cmd(hdev, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); -} - static void hci_le_create_connection_cancel(struct hci_conn *conn) { hci_send_cmd(conn->hdev, HCI_OP_LE_CREATE_CONN_CANCEL, 0, NULL); @@ -340,8 +317,10 @@ static void hci_conn_timeout(struct work_struct *work) } /* Enter sniff mode */ -static void hci_conn_enter_sniff_mode(struct hci_conn *conn) +static void hci_conn_idle(struct work_struct *work) { + struct hci_conn *conn = container_of(work, struct hci_conn, + idle_work.work); struct hci_dev *hdev = conn->hdev; BT_DBG("hcon %p mode %d", conn, conn->mode); @@ -375,21 +354,12 @@ static void hci_conn_enter_sniff_mode(struct hci_conn *conn) } } -static void hci_conn_idle(unsigned long arg) -{ - struct hci_conn *conn = (void *) arg; - - BT_DBG("hcon %p mode %d", conn, conn->mode); - - hci_conn_enter_sniff_mode(conn); -} - -static void hci_conn_auto_accept(unsigned long arg) +static void hci_conn_auto_accept(struct work_struct *work) { - struct hci_conn *conn = (void *) arg; - struct hci_dev *hdev = conn->hdev; + struct hci_conn *conn = container_of(work, struct hci_conn, + auto_accept_work.work); - hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst), + hci_send_cmd(conn->hdev, HCI_OP_USER_CONFIRM_REPLY, sizeof(conn->dst), &conn->dst); } @@ -404,6 +374,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) return NULL; bacpy(&conn->dst, dst); + bacpy(&conn->src, &hdev->bdaddr); conn->hdev = hdev; conn->type = type; conn->mode = HCI_CM_ACTIVE; @@ -437,9 +408,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) INIT_LIST_HEAD(&conn->chan_list); INIT_DELAYED_WORK(&conn->disc_work, hci_conn_timeout); - setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn); - setup_timer(&conn->auto_accept_timer, hci_conn_auto_accept, - (unsigned long) conn); + INIT_DELAYED_WORK(&conn->auto_accept_work, hci_conn_auto_accept); + INIT_DELAYED_WORK(&conn->idle_work, hci_conn_idle); atomic_set(&conn->refcnt, 0); @@ -460,11 +430,9 @@ int hci_conn_del(struct hci_conn *conn) BT_DBG("%s hcon %p handle %d", hdev->name, conn, conn->handle); - del_timer(&conn->idle_timer); - cancel_delayed_work_sync(&conn->disc_work); - - del_timer(&conn->auto_accept_timer); + cancel_delayed_work_sync(&conn->auto_accept_work); + cancel_delayed_work_sync(&conn->idle_work); if (conn->type == ACL_LINK) { struct hci_conn *sco = conn->link; @@ -518,6 +486,7 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) list_for_each_entry(d, &hci_dev_list, list) { if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags) || + test_bit(HCI_USER_CHANNEL, &d->dev_flags) || d->dev_type != HCI_BREDR) continue; @@ -545,34 +514,124 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) } EXPORT_SYMBOL(hci_get_route); +static void create_le_conn_complete(struct hci_dev *hdev, u8 status) +{ + struct hci_conn *conn; + + if (status == 0) + return; + + BT_ERR("HCI request failed to create LE connection: status 0x%2.2x", + status); + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); + if (!conn) + goto done; + + conn->state = BT_CLOSED; + + mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type, + status); + + hci_proto_connect_cfm(conn, status); + + hci_conn_del(conn); + +done: + hci_dev_unlock(hdev); +} + +static int hci_create_le_conn(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_cp_le_create_conn cp; + struct hci_request req; + int err; + + hci_req_init(&req, hdev); + + memset(&cp, 0, sizeof(cp)); + cp.scan_interval = cpu_to_le16(hdev->le_scan_interval); + cp.scan_window = cpu_to_le16(hdev->le_scan_window); + bacpy(&cp.peer_addr, &conn->dst); + cp.peer_addr_type = conn->dst_type; + cp.own_address_type = conn->src_type; + cp.conn_interval_min = cpu_to_le16(hdev->le_conn_min_interval); + cp.conn_interval_max = cpu_to_le16(hdev->le_conn_max_interval); + cp.supervision_timeout = __constant_cpu_to_le16(0x002a); + cp.min_ce_len = __constant_cpu_to_le16(0x0000); + cp.max_ce_len = __constant_cpu_to_le16(0x0000); + + hci_req_add(&req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp); + + err = hci_req_run(&req, create_le_conn_complete); + if (err) { + hci_conn_del(conn); + return err; + } + + return 0; +} + static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, u8 dst_type, u8 sec_level, u8 auth_type) { - struct hci_conn *le; + struct hci_conn *conn; + int err; - if (test_bit(HCI_LE_PERIPHERAL, &hdev->flags)) + if (test_bit(HCI_ADVERTISING, &hdev->flags)) return ERR_PTR(-ENOTSUPP); - le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); - if (!le) { - le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); - if (le) - return ERR_PTR(-EBUSY); + /* Some devices send ATT messages as soon as the physical link is + * established. To be able to handle these ATT messages, the user- + * space first establishes the connection and then starts the pairing + * process. + * + * So if a hci_conn object already exists for the following connection + * attempt, we simply update pending_sec_level and auth_type fields + * and return the object found. + */ + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); + if (conn) { + conn->pending_sec_level = sec_level; + conn->auth_type = auth_type; + goto done; + } - le = hci_conn_add(hdev, LE_LINK, dst); - if (!le) - return ERR_PTR(-ENOMEM); + /* Since the controller supports only one LE connection attempt at a + * time, we return -EBUSY if there is any connection attempt running. + */ + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); + if (conn) + return ERR_PTR(-EBUSY); - le->dst_type = bdaddr_to_le(dst_type); - hci_le_create_connection(le); - } + conn = hci_conn_add(hdev, LE_LINK, dst); + if (!conn) + return ERR_PTR(-ENOMEM); - le->pending_sec_level = sec_level; - le->auth_type = auth_type; + if (dst_type == BDADDR_LE_PUBLIC) + conn->dst_type = ADDR_LE_DEV_PUBLIC; + else + conn->dst_type = ADDR_LE_DEV_RANDOM; - hci_conn_hold(le); + conn->src_type = hdev->own_addr_type; - return le; + conn->state = BT_CONNECT; + conn->out = true; + conn->link_mode |= HCI_LM_MASTER; + conn->sec_level = BT_SECURITY_LOW; + conn->pending_sec_level = sec_level; + conn->auth_type = auth_type; + + err = hci_create_le_conn(conn); + if (err) + return ERR_PTR(err); + +done: + hci_conn_hold(conn); + return conn; } static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, @@ -580,6 +639,9 @@ static struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, { struct hci_conn *acl; + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + return ERR_PTR(-ENOTSUPP); + acl = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); if (!acl) { acl = hci_conn_add(hdev, ACL_LINK, dst); @@ -846,8 +908,8 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) timer: if (hdev->idle_timeout > 0) - mod_timer(&conn->idle_timer, - jiffies + msecs_to_jiffies(hdev->idle_timeout)); + queue_delayed_work(hdev->workqueue, &conn->idle_work, + msecs_to_jiffies(hdev->idle_timeout)); } /* Drop all connection on the device */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index fb7356fcfe51e03664d7aed6458eafd3015634e5..6ccc4eb9e55e4958f3eb070894a7668b05c98b5b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -27,34 +27,615 @@ #include #include +#include +#include +#include + +#include +#include + +static void hci_rx_work(struct work_struct *work); +static void hci_cmd_work(struct work_struct *work); +static void hci_tx_work(struct work_struct *work); + +/* HCI device list */ +LIST_HEAD(hci_dev_list); +DEFINE_RWLOCK(hci_dev_list_lock); + +/* HCI callback list */ +LIST_HEAD(hci_cb_list); +DEFINE_RWLOCK(hci_cb_list_lock); + +/* HCI ID Numbering */ +static DEFINE_IDA(hci_index_ida); + +/* ---- HCI notifications ---- */ + +static void hci_notify(struct hci_dev *hdev, int event) +{ + hci_sock_dev_event(hdev, event); +} + +/* ---- HCI debugfs entries ---- */ + +static ssize_t dut_mode_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_DUT_MODE, &hdev->dev_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t dut_mode_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + struct sk_buff *skb; + char buf[32]; + size_t buf_size = min(count, (sizeof(buf)-1)); + bool enable; + int err; + + if (!test_bit(HCI_UP, &hdev->flags)) + return -ENETDOWN; + + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + if (strtobool(buf, &enable)) + return -EINVAL; + + if (enable == test_bit(HCI_DUT_MODE, &hdev->dev_flags)) + return -EALREADY; + + hci_req_lock(hdev); + if (enable) + skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL, + HCI_CMD_TIMEOUT); + else + skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, + HCI_CMD_TIMEOUT); + hci_req_unlock(hdev); + + if (IS_ERR(skb)) + return PTR_ERR(skb); + + err = -bt_to_errno(skb->data[0]); + kfree_skb(skb); + + if (err < 0) + return err; + + change_bit(HCI_DUT_MODE, &hdev->dev_flags); + + return count; +} + +static const struct file_operations dut_mode_fops = { + .open = simple_open, + .read = dut_mode_read, + .write = dut_mode_write, + .llseek = default_llseek, +}; + +static int features_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + u8 p; + + hci_dev_lock(hdev); + for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) { + seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " + "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p, + hdev->features[p][0], hdev->features[p][1], + hdev->features[p][2], hdev->features[p][3], + hdev->features[p][4], hdev->features[p][5], + hdev->features[p][6], hdev->features[p][7]); + } + if (lmp_le_capable(hdev)) + seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x " + "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", + hdev->le_features[0], hdev->le_features[1], + hdev->le_features[2], hdev->le_features[3], + hdev->le_features[4], hdev->le_features[5], + hdev->le_features[6], hdev->le_features[7]); + hci_dev_unlock(hdev); + + return 0; +} + +static int features_open(struct inode *inode, struct file *file) +{ + return single_open(file, features_show, inode->i_private); +} + +static const struct file_operations features_fops = { + .open = features_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int blacklist_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + struct bdaddr_list *b; + + hci_dev_lock(hdev); + list_for_each_entry(b, &hdev->blacklist, list) + seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); + hci_dev_unlock(hdev); + + return 0; +} + +static int blacklist_open(struct inode *inode, struct file *file) +{ + return single_open(file, blacklist_show, inode->i_private); +} + +static const struct file_operations blacklist_fops = { + .open = blacklist_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int uuids_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + struct bt_uuid *uuid; + + hci_dev_lock(hdev); + list_for_each_entry(uuid, &hdev->uuids, list) { + u8 i, val[16]; + + /* The Bluetooth UUID values are stored in big endian, + * but with reversed byte order. So convert them into + * the right order for the %pUb modifier. + */ + for (i = 0; i < 16; i++) + val[i] = uuid->uuid[15 - i]; + + seq_printf(f, "%pUb\n", val); + } + hci_dev_unlock(hdev); + + return 0; +} + +static int uuids_open(struct inode *inode, struct file *file) +{ + return single_open(file, uuids_show, inode->i_private); +} + +static const struct file_operations uuids_fops = { + .open = uuids_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int inquiry_cache_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + struct discovery_state *cache = &hdev->discovery; + struct inquiry_entry *e; + + hci_dev_lock(hdev); + + list_for_each_entry(e, &cache->all, all) { + struct inquiry_data *data = &e->data; + seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", + &data->bdaddr, + data->pscan_rep_mode, data->pscan_period_mode, + data->pscan_mode, data->dev_class[2], + data->dev_class[1], data->dev_class[0], + __le16_to_cpu(data->clock_offset), + data->rssi, data->ssp_mode, e->timestamp); + } + + hci_dev_unlock(hdev); + + return 0; +} + +static int inquiry_cache_open(struct inode *inode, struct file *file) +{ + return single_open(file, inquiry_cache_show, inode->i_private); +} + +static const struct file_operations inquiry_cache_fops = { + .open = inquiry_cache_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int link_keys_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + struct list_head *p, *n; + + hci_dev_lock(hdev); + list_for_each_safe(p, n, &hdev->link_keys) { + struct link_key *key = list_entry(p, struct link_key, list); + seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type, + HCI_LINK_KEY_SIZE, key->val, key->pin_len); + } + hci_dev_unlock(hdev); + + return 0; +} + +static int link_keys_open(struct inode *inode, struct file *file) +{ + return single_open(file, link_keys_show, inode->i_private); +} + +static const struct file_operations link_keys_fops = { + .open = link_keys_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hci_dev *hdev = file->private_data; + char buf[3]; + + buf[0] = test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static const struct file_operations use_debug_keys_fops = { + .open = simple_open, + .read = use_debug_keys_read, + .llseek = default_llseek, +}; + +static int dev_class_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + + hci_dev_lock(hdev); + seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2], + hdev->dev_class[1], hdev->dev_class[0]); + hci_dev_unlock(hdev); + + return 0; +} + +static int dev_class_open(struct inode *inode, struct file *file) +{ + return single_open(file, dev_class_show, inode->i_private); +} + +static const struct file_operations dev_class_fops = { + .open = dev_class_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int voice_setting_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->voice_setting; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get, + NULL, "0x%4.4llx\n"); + +static int auto_accept_delay_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + hdev->auto_accept_delay = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int auto_accept_delay_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->auto_accept_delay; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, + auto_accept_delay_set, "%llu\n"); + +static int ssp_debug_mode_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + struct sk_buff *skb; + __u8 mode; + int err; + + if (val != 0 && val != 1) + return -EINVAL; + + if (!test_bit(HCI_UP, &hdev->flags)) + return -ENETDOWN; + + hci_req_lock(hdev); + mode = val; + skb = __hci_cmd_sync(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(mode), + &mode, HCI_CMD_TIMEOUT); + hci_req_unlock(hdev); + + if (IS_ERR(skb)) + return PTR_ERR(skb); + + err = -bt_to_errno(skb->data[0]); + kfree_skb(skb); + + if (err < 0) + return err; + + hci_dev_lock(hdev); + hdev->ssp_debug_mode = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int ssp_debug_mode_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->ssp_debug_mode; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get, + ssp_debug_mode_set, "%llu\n"); + +static int idle_timeout_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val != 0 && (val < 500 || val > 3600000)) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->idle_timeout = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int idle_timeout_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->idle_timeout; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get, + idle_timeout_set, "%llu\n"); + +static int sniff_min_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val % 2 || val > hdev->sniff_max_interval) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->sniff_min_interval = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int sniff_min_interval_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->sniff_min_interval; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get, + sniff_min_interval_set, "%llu\n"); + +static int sniff_max_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val == 0 || val % 2 || val < hdev->sniff_min_interval) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->sniff_max_interval = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int sniff_max_interval_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->sniff_max_interval; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get, + sniff_max_interval_set, "%llu\n"); + +static int static_address_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + + hci_dev_lock(hdev); + seq_printf(f, "%pMR\n", &hdev->static_addr); + hci_dev_unlock(hdev); + + return 0; +} + +static int static_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, static_address_show, inode->i_private); +} + +static const struct file_operations static_address_fops = { + .open = static_address_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int own_address_type_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val != 0 && val != 1) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->own_addr_type = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int own_address_type_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->own_addr_type; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(own_address_type_fops, own_address_type_get, + own_address_type_set, "%llu\n"); + +static int long_term_keys_show(struct seq_file *f, void *ptr) +{ + struct hci_dev *hdev = f->private; + struct list_head *p, *n; + + hci_dev_lock(hdev); + list_for_each_safe(p, n, &hdev->link_keys) { + struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list); + seq_printf(f, "%pMR (type %u) %u %u %u %.4x %*phN %*phN\\n", + <k->bdaddr, ltk->bdaddr_type, ltk->authenticated, + ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv), + 8, ltk->rand, 16, ltk->val); + } + hci_dev_unlock(hdev); + + return 0; +} + +static int long_term_keys_open(struct inode *inode, struct file *file) +{ + return single_open(file, long_term_keys_show, inode->i_private); +} + +static const struct file_operations long_term_keys_fops = { + .open = long_term_keys_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int conn_min_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval) + return -EINVAL; -#include + hci_dev_lock(hdev); + hdev->le_conn_min_interval = val; + hci_dev_unlock(hdev); -#include -#include + return 0; +} -static void hci_rx_work(struct work_struct *work); -static void hci_cmd_work(struct work_struct *work); -static void hci_tx_work(struct work_struct *work); +static int conn_min_interval_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; -/* HCI device list */ -LIST_HEAD(hci_dev_list); -DEFINE_RWLOCK(hci_dev_list_lock); + hci_dev_lock(hdev); + *val = hdev->le_conn_min_interval; + hci_dev_unlock(hdev); -/* HCI callback list */ -LIST_HEAD(hci_cb_list); -DEFINE_RWLOCK(hci_cb_list_lock); + return 0; +} -/* HCI ID Numbering */ -static DEFINE_IDA(hci_index_ida); +DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get, + conn_min_interval_set, "%llu\n"); -/* ---- HCI notifications ---- */ +static int conn_max_interval_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; -static void hci_notify(struct hci_dev *hdev, int event) + if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_conn_max_interval = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_max_interval_get(void *data, u64 *val) { - hci_sock_dev_event(hdev, event); + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_conn_max_interval; + hci_dev_unlock(hdev); + + return 0; } +DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get, + conn_max_interval_set, "%llu\n"); + /* ---- HCI requests ---- */ static void hci_req_sync_complete(struct hci_dev *hdev, u8 result) @@ -307,11 +888,23 @@ static void amp_init(struct hci_request *req) /* Read Local Version */ hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL); + /* Read Local Supported Commands */ + hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); + + /* Read Local Supported Features */ + hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); + /* Read Local AMP Info */ hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); /* Read Data Blk size */ hci_req_add(req, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL); + + /* Read Flow Control Mode */ + hci_req_add(req, HCI_OP_READ_FLOW_CONTROL_MODE, 0, NULL); + + /* Read Location Data */ + hci_req_add(req, HCI_OP_READ_LOCATION_DATA, 0, NULL); } static void hci_init1_req(struct hci_request *req, unsigned long opt) @@ -341,6 +934,8 @@ static void hci_init1_req(struct hci_request *req, unsigned long opt) static void bredr_setup(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; + __le16 param; __u8 flt_type; @@ -356,6 +951,12 @@ static void bredr_setup(struct hci_request *req) /* Read Voice Setting */ hci_req_add(req, HCI_OP_READ_VOICE_SETTING, 0, NULL); + /* Read Number of Supported IAC */ + hci_req_add(req, HCI_OP_READ_NUM_SUPPORTED_IAC, 0, NULL); + + /* Read Current IAC LAP */ + hci_req_add(req, HCI_OP_READ_CURRENT_IAC_LAP, 0, NULL); + /* Clear Event Filters */ flt_type = HCI_FLT_CLEAR_ALL; hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &flt_type); @@ -364,8 +965,10 @@ static void bredr_setup(struct hci_request *req) param = __constant_cpu_to_le16(0x7d00); hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); - /* Read page scan parameters */ - if (req->hdev->hci_ver > BLUETOOTH_VER_1_1) { + /* AVM Berlin (31), aka "BlueFRITZ!", reports version 1.2, + * but it does not support page scan related HCI commands. + */ + if (hdev->manufacturer != 31 && hdev->hci_ver > BLUETOOTH_VER_1_1) { hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL); hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL); } @@ -519,6 +1122,8 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) if (lmp_bredr_capable(hdev)) bredr_setup(req); + else + clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); if (lmp_le_capable(hdev)) le_setup(req); @@ -532,6 +1137,14 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); if (lmp_ssp_capable(hdev)) { + /* When SSP is available, then the host features page + * should also be available as well. However some + * controllers list the max_page as 0 as long as SSP + * has not been enabled. To achieve proper debugging + * output, force the minimum max_page to 1 at least. + */ + hdev->max_page = 0x01; + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { u8 mode = 0x01; hci_req_add(req, HCI_OP_WRITE_SSP_MODE, @@ -607,6 +1220,34 @@ static void hci_set_le_support(struct hci_request *req) &cp); } +static void hci_set_event_mask_page_2(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + u8 events[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + /* If Connectionless Slave Broadcast master role is supported + * enable all necessary events for it. + */ + if (hdev->features[2][0] & 0x01) { + events[1] |= 0x40; /* Triggered Clock Capture */ + events[1] |= 0x80; /* Synchronization Train Complete */ + events[2] |= 0x10; /* Slave Page Response Timeout */ + events[2] |= 0x20; /* CSB Channel Map Change */ + } + + /* If Connectionless Slave Broadcast slave role is supported + * enable all necessary events for it. + */ + if (hdev->features[2][0] & 0x02) { + events[2] |= 0x01; /* Synchronization Train Received */ + events[2] |= 0x02; /* CSB Receive */ + events[2] |= 0x04; /* CSB Timeout */ + events[2] |= 0x08; /* Truncated Page Complete */ + } + + hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events); +} + static void hci_init3_req(struct hci_request *req, unsigned long opt) { struct hci_dev *hdev = req->hdev; @@ -634,8 +1275,17 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) hci_setup_link_policy(req); if (lmp_le_capable(hdev)) { + /* If the controller has a public BD_ADDR, then by + * default use that one. If this is a LE only + * controller without one, default to the random + * address. + */ + if (bacmp(&hdev->bdaddr, BDADDR_ANY)) + hdev->own_addr_type = ADDR_LE_DEV_PUBLIC; + else + hdev->own_addr_type = ADDR_LE_DEV_RANDOM; + hci_set_le_support(req); - hci_update_ad(req); } /* Read features beyond page 1 if available */ @@ -648,6 +1298,19 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) } } +static void hci_init4_req(struct hci_request *req, unsigned long opt) +{ + struct hci_dev *hdev = req->hdev; + + /* Set event mask page 2 if the HCI command for it is supported */ + if (hdev->commands[22] & 0x04) + hci_set_event_mask_page_2(req); + + /* Check for Synchronization Train support */ + if (hdev->features[2][0] & 0x04) + hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL); +} + static int __hci_init(struct hci_dev *hdev) { int err; @@ -656,6 +1319,14 @@ static int __hci_init(struct hci_dev *hdev) if (err < 0) return err; + /* The Device Under Test (DUT) mode is special and available for + * all controller types. So just create it early on. + */ + if (test_bit(HCI_SETUP, &hdev->dev_flags)) { + debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev, + &dut_mode_fops); + } + /* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode * BR/EDR/LE type controllers. AMP controllers only need the * first stage init. @@ -667,7 +1338,75 @@ static int __hci_init(struct hci_dev *hdev) if (err < 0) return err; - return __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT); + err = __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT); + if (err < 0) + return err; + + err = __hci_req_sync(hdev, hci_init4_req, 0, HCI_INIT_TIMEOUT); + if (err < 0) + return err; + + /* Only create debugfs entries during the initial setup + * phase and not every time the controller gets powered on. + */ + if (!test_bit(HCI_SETUP, &hdev->dev_flags)) + return 0; + + debugfs_create_file("features", 0444, hdev->debugfs, hdev, + &features_fops); + debugfs_create_u16("manufacturer", 0444, hdev->debugfs, + &hdev->manufacturer); + debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver); + debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev); + debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev, + &blacklist_fops); + debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); + + if (lmp_bredr_capable(hdev)) { + debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, + hdev, &inquiry_cache_fops); + debugfs_create_file("link_keys", 0400, hdev->debugfs, + hdev, &link_keys_fops); + debugfs_create_file("use_debug_keys", 0444, hdev->debugfs, + hdev, &use_debug_keys_fops); + debugfs_create_file("dev_class", 0444, hdev->debugfs, + hdev, &dev_class_fops); + debugfs_create_file("voice_setting", 0444, hdev->debugfs, + hdev, &voice_setting_fops); + } + + if (lmp_ssp_capable(hdev)) { + debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs, + hdev, &auto_accept_delay_fops); + debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs, + hdev, &ssp_debug_mode_fops); + } + + if (lmp_sniff_capable(hdev)) { + debugfs_create_file("idle_timeout", 0644, hdev->debugfs, + hdev, &idle_timeout_fops); + debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs, + hdev, &sniff_min_interval_fops); + debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs, + hdev, &sniff_max_interval_fops); + } + + if (lmp_le_capable(hdev)) { + debugfs_create_u8("white_list_size", 0444, hdev->debugfs, + &hdev->le_white_list_size); + debugfs_create_file("static_address", 0444, hdev->debugfs, + hdev, &static_address_fops); + debugfs_create_file("own_address_type", 0644, hdev->debugfs, + hdev, &own_address_type_fops); + debugfs_create_file("long_term_keys", 0400, hdev->debugfs, + hdev, &long_term_keys_fops); + debugfs_create_file("conn_min_interval", 0644, hdev->debugfs, + hdev, &conn_min_interval_fops); + debugfs_create_file("conn_max_interval", 0644, hdev->debugfs, + hdev, &conn_max_interval_fops); + } + + return 0; } static void hci_scan_req(struct hci_request *req, unsigned long opt) @@ -984,6 +1723,21 @@ int hci_inquiry(void __user *arg) if (!hdev) return -ENODEV; + if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + err = -EBUSY; + goto done; + } + + if (hdev->dev_type != HCI_BREDR) { + err = -EOPNOTSUPP; + goto done; + } + + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + err = -EOPNOTSUPP; + goto done; + } + hci_dev_lock(hdev); if (inquiry_cache_age(hdev) > INQUIRY_CACHE_AGE_MAX || inquiry_cache_empty(hdev) || ir.flags & IREQ_CACHE_FLUSH) { @@ -1043,100 +1797,10 @@ done: return err; } -static u8 create_ad(struct hci_dev *hdev, u8 *ptr) -{ - u8 ad_len = 0, flags = 0; - size_t name_len; - - if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) - flags |= LE_AD_GENERAL; - - if (!lmp_bredr_capable(hdev)) - flags |= LE_AD_NO_BREDR; - - if (lmp_le_br_capable(hdev)) - flags |= LE_AD_SIM_LE_BREDR_CTRL; - - if (lmp_host_le_br_capable(hdev)) - flags |= LE_AD_SIM_LE_BREDR_HOST; - - if (flags) { - BT_DBG("adv flags 0x%02x", flags); - - ptr[0] = 2; - ptr[1] = EIR_FLAGS; - ptr[2] = flags; - - ad_len += 3; - ptr += 3; - } - - if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) { - ptr[0] = 2; - ptr[1] = EIR_TX_POWER; - ptr[2] = (u8) hdev->adv_tx_power; - - ad_len += 3; - ptr += 3; - } - - name_len = strlen(hdev->dev_name); - if (name_len > 0) { - size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2; - - if (name_len > max_len) { - name_len = max_len; - ptr[1] = EIR_NAME_SHORT; - } else - ptr[1] = EIR_NAME_COMPLETE; - - ptr[0] = name_len + 1; - - memcpy(ptr + 2, hdev->dev_name, name_len); - - ad_len += (name_len + 2); - ptr += (name_len + 2); - } - - return ad_len; -} - -void hci_update_ad(struct hci_request *req) -{ - struct hci_dev *hdev = req->hdev; - struct hci_cp_le_set_adv_data cp; - u8 len; - - if (!lmp_le_capable(hdev)) - return; - - memset(&cp, 0, sizeof(cp)); - - len = create_ad(hdev, cp.data); - - if (hdev->adv_data_len == len && - memcmp(cp.data, hdev->adv_data, len) == 0) - return; - - memcpy(hdev->adv_data, cp.data, sizeof(cp.data)); - hdev->adv_data_len = len; - - cp.length = len; - - hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); -} - -/* ---- HCI ioctl helpers ---- */ - -int hci_dev_open(__u16 dev) +static int hci_dev_do_open(struct hci_dev *hdev) { - struct hci_dev *hdev; int ret = 0; - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; - BT_DBG("%s %p", hdev->name, hdev); hci_req_lock(hdev); @@ -1146,13 +1810,29 @@ int hci_dev_open(__u16 dev) goto done; } - /* Check for rfkill but allow the HCI setup stage to proceed - * (which in itself doesn't cause any RF activity). - */ - if (test_bit(HCI_RFKILLED, &hdev->dev_flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags)) { - ret = -ERFKILL; - goto done; + if (!test_bit(HCI_SETUP, &hdev->dev_flags)) { + /* Check for rfkill but allow the HCI setup stage to + * proceed (which in itself doesn't cause any RF activity). + */ + if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) { + ret = -ERFKILL; + goto done; + } + + /* Check for valid public address or a configured static + * random adddress, but let the HCI setup proceed to + * be able to determine if there is a public address + * or not. + * + * This check is only valid for BR/EDR controllers + * since AMP controllers do not have an address. + */ + if (hdev->dev_type == HCI_BREDR && + !bacmp(&hdev->bdaddr, BDADDR_ANY) && + !bacmp(&hdev->static_addr, BDADDR_ANY)) { + ret = -EADDRNOTAVAIL; + goto done; + } } if (test_bit(HCI_UP, &hdev->flags)) { @@ -1172,16 +1852,11 @@ int hci_dev_open(__u16 dev) ret = hdev->setup(hdev); if (!ret) { - /* Treat all non BR/EDR controllers as raw devices if - * enable_hs is not set. - */ - if (hdev->dev_type != HCI_BREDR && !enable_hs) - set_bit(HCI_RAW, &hdev->flags); - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) set_bit(HCI_RAW, &hdev->flags); - if (!test_bit(HCI_RAW, &hdev->flags)) + if (!test_bit(HCI_RAW, &hdev->flags) && + !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) ret = __hci_init(hdev); } @@ -1192,7 +1867,8 @@ int hci_dev_open(__u16 dev) set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && - mgmt_valid_hdev(hdev)) { + !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && + hdev->dev_type == HCI_BREDR) { hci_dev_lock(hdev); mgmt_powered(hdev, 1); hci_dev_unlock(hdev); @@ -1220,10 +1896,41 @@ int hci_dev_open(__u16 dev) done: hci_req_unlock(hdev); - hci_dev_put(hdev); return ret; } +/* ---- HCI ioctl helpers ---- */ + +int hci_dev_open(__u16 dev) +{ + struct hci_dev *hdev; + int err; + + hdev = hci_dev_get(dev); + if (!hdev) + return -ENODEV; + + /* We need to ensure that no other power on/off work is pending + * before proceeding to call hci_dev_do_open. This is + * particularly important if the setup procedure has not yet + * completed. + */ + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) + cancel_delayed_work(&hdev->power_off); + + /* After this call it is guaranteed that the setup procedure + * has finished. This means that error conditions like RFKILL + * or no valid public or static random address apply. + */ + flush_workqueue(hdev->req_workqueue); + + err = hci_dev_do_open(hdev); + + hci_dev_put(hdev); + + return err; +} + static int hci_dev_do_close(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); @@ -1247,6 +1954,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) cancel_delayed_work(&hdev->discov_off); hdev->discov_timeout = 0; clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); } if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) @@ -1268,6 +1976,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); if (!test_bit(HCI_RAW, &hdev->flags) && + !test_bit(HCI_AUTO_OFF, &hdev->dev_flags) && test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_INIT, &hdev->flags); __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT); @@ -1300,15 +2009,16 @@ static int hci_dev_do_close(struct hci_dev *hdev) hdev->flags = 0; hdev->dev_flags &= ~HCI_PERSISTENT_MASK; - if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags) && - mgmt_valid_hdev(hdev)) { - hci_dev_lock(hdev); - mgmt_powered(hdev, 0); - hci_dev_unlock(hdev); + if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { + if (hdev->dev_type == HCI_BREDR) { + hci_dev_lock(hdev); + mgmt_powered(hdev, 0); + hci_dev_unlock(hdev); + } } /* Controller radio is available but is currently powered down */ - hdev->amp_status = 0; + hdev->amp_status = AMP_STATUS_POWERED_DOWN; memset(hdev->eir, 0, sizeof(hdev->eir)); memset(hdev->dev_class, 0, sizeof(hdev->dev_class)); @@ -1328,11 +2038,17 @@ int hci_dev_close(__u16 dev) if (!hdev) return -ENODEV; + if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + err = -EBUSY; + goto done; + } + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) cancel_delayed_work(&hdev->power_off); err = hci_dev_do_close(hdev); +done: hci_dev_put(hdev); return err; } @@ -1348,8 +2064,15 @@ int hci_dev_reset(__u16 dev) hci_req_lock(hdev); - if (!test_bit(HCI_UP, &hdev->flags)) + if (!test_bit(HCI_UP, &hdev->flags)) { + ret = -ENETDOWN; goto done; + } + + if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + ret = -EBUSY; + goto done; + } /* Drop queues */ skb_queue_purge(&hdev->rx_q); @@ -1384,10 +2107,15 @@ int hci_dev_reset_stat(__u16 dev) if (!hdev) return -ENODEV; + if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + ret = -EBUSY; + goto done; + } + memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); +done: hci_dev_put(hdev); - return ret; } @@ -1404,6 +2132,21 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) if (!hdev) return -ENODEV; + if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + err = -EBUSY; + goto done; + } + + if (hdev->dev_type != HCI_BREDR) { + err = -EOPNOTSUPP; + goto done; + } + + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + err = -EOPNOTSUPP; + goto done; + } + switch (cmd) { case HCISETAUTH: err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt, @@ -1462,6 +2205,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) break; } +done: hci_dev_put(hdev); return err; } @@ -1534,7 +2278,7 @@ int hci_get_dev_info(void __user *arg) strcpy(di.name, hdev->name); di.bdaddr = hdev->bdaddr; - di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); + di.type = (hdev->bus & 0x0f) | ((hdev->dev_type & 0x03) << 4); di.flags = hdev->flags; di.pkt_type = hdev->pkt_type; if (lmp_bredr_capable(hdev)) { @@ -1570,6 +2314,9 @@ static int hci_rfkill_set_block(void *data, bool blocked) BT_DBG("%p name %s blocked %d", hdev, hdev->name, blocked); + if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) + return -EBUSY; + if (blocked) { set_bit(HCI_RFKILLED, &hdev->dev_flags); if (!test_bit(HCI_SETUP, &hdev->dev_flags)) @@ -1592,13 +2339,20 @@ static void hci_power_on(struct work_struct *work) BT_DBG("%s", hdev->name); - err = hci_dev_open(hdev->id); + err = hci_dev_do_open(hdev); if (err < 0) { mgmt_set_powered_failed(hdev, err); return; } - if (test_bit(HCI_RFKILLED, &hdev->dev_flags)) { + /* During the HCI setup phase, a few error conditions are + * ignored and they need to be checked now. If they are still + * valid, it is important to turn the device back off. + */ + if (test_bit(HCI_RFKILLED, &hdev->dev_flags) || + (hdev->dev_type == HCI_BREDR && + !bacmp(&hdev->bdaddr, BDADDR_ANY) && + !bacmp(&hdev->static_addr, BDADDR_ANY))) { clear_bit(HCI_AUTO_OFF, &hdev->dev_flags); hci_dev_do_close(hdev); } else if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { @@ -1623,19 +2377,12 @@ static void hci_power_off(struct work_struct *work) static void hci_discov_off(struct work_struct *work) { struct hci_dev *hdev; - u8 scan = SCAN_PAGE; hdev = container_of(work, struct hci_dev, discov_off.work); BT_DBG("%s", hdev->name); - hci_dev_lock(hdev); - - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); - - hdev->discov_timeout = 0; - - hci_dev_unlock(hdev); + mgmt_discoverable_timeout(hdev); } int hci_uuids_clear(struct hci_dev *hdev) @@ -1958,13 +2705,15 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, return 0; } -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) +struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *b; - list_for_each_entry(b, &hdev->blacklist, list) - if (bacmp(bdaddr, &b->bdaddr) == 0) + list_for_each_entry(b, &hdev->blacklist, list) { + if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type) return b; + } return NULL; } @@ -1974,9 +2723,7 @@ int hci_blacklist_clear(struct hci_dev *hdev) struct list_head *p, *n; list_for_each_safe(p, n, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(p, struct bdaddr_list, list); + struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list); list_del(p); kfree(b); @@ -1989,10 +2736,10 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; - if (bacmp(bdaddr, BDADDR_ANY) == 0) + if (!bacmp(bdaddr, BDADDR_ANY)) return -EBADF; - if (hci_blacklist_lookup(hdev, bdaddr)) + if (hci_blacklist_lookup(hdev, bdaddr, type)) return -EEXIST; entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); @@ -2000,6 +2747,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) return -ENOMEM; bacpy(&entry->bdaddr, bdaddr); + entry->bdaddr_type = type; list_add(&entry->list, &hdev->blacklist); @@ -2010,10 +2758,10 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; - if (bacmp(bdaddr, BDADDR_ANY) == 0) + if (!bacmp(bdaddr, BDADDR_ANY)) return hci_blacklist_clear(hdev); - entry = hci_blacklist_lookup(hdev, bdaddr); + entry = hci_blacklist_lookup(hdev, bdaddr, type); if (!entry) return -ENOENT; @@ -2111,13 +2859,19 @@ struct hci_dev *hci_alloc_dev(void) hdev->pkt_type = (HCI_DM1 | HCI_DH1 | HCI_HV1); hdev->esco_type = (ESCO_HV1); hdev->link_mode = (HCI_LM_ACCEPT); - hdev->io_capability = 0x03; /* No Input No Output */ + hdev->num_iac = 0x01; /* One IAC support is mandatory */ + hdev->io_capability = 0x03; /* No Input No Output */ hdev->inq_tx_power = HCI_TX_POWER_INVALID; hdev->adv_tx_power = HCI_TX_POWER_INVALID; hdev->sniff_max_interval = 800; hdev->sniff_min_interval = 80; + hdev->le_scan_interval = 0x0060; + hdev->le_scan_window = 0x0030; + hdev->le_conn_min_interval = 0x0028; + hdev->le_conn_max_interval = 0x0038; + mutex_init(&hdev->lock); mutex_init(&hdev->req_lock); @@ -2206,7 +2960,12 @@ int hci_register_dev(struct hci_dev *hdev) goto err; } - error = hci_add_sysfs(hdev); + if (!IS_ERR_OR_NULL(bt_debugfs)) + hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs); + + dev_set_name(&hdev->dev, "%s", hdev->name); + + error = device_add(&hdev->dev); if (error < 0) goto err_wqueue; @@ -2224,9 +2983,14 @@ int hci_register_dev(struct hci_dev *hdev) set_bit(HCI_RFKILLED, &hdev->dev_flags); set_bit(HCI_SETUP, &hdev->dev_flags); + set_bit(HCI_AUTO_OFF, &hdev->dev_flags); - if (hdev->dev_type != HCI_AMP) - set_bit(HCI_AUTO_OFF, &hdev->dev_flags); + if (hdev->dev_type == HCI_BREDR) { + /* Assume BR/EDR support until proven otherwise (such as + * through reading supported features during init. + */ + set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + } write_lock(&hci_dev_list_lock); list_add(&hdev->list, &hci_dev_list); @@ -2289,7 +3053,9 @@ void hci_unregister_dev(struct hci_dev *hdev) rfkill_destroy(hdev->rfkill); } - hci_del_sysfs(hdev); + device_del(&hdev->dev); + + debugfs_remove_recursive(hdev->debugfs); destroy_workqueue(hdev->workqueue); destroy_workqueue(hdev->req_workqueue); @@ -2325,9 +3091,8 @@ int hci_resume_dev(struct hci_dev *hdev) EXPORT_SYMBOL(hci_resume_dev); /* Receive frame from HCI drivers */ -int hci_recv_frame(struct sk_buff *skb) +int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev *hdev = (struct hci_dev *) skb->dev; if (!hdev || (!test_bit(HCI_UP, &hdev->flags) && !test_bit(HCI_INIT, &hdev->flags))) { kfree_skb(skb); @@ -2386,7 +3151,6 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data, scb->expect = hlen; scb->pkt_type = type; - skb->dev = (void *) hdev; hdev->reassembly[index] = skb; } @@ -2446,7 +3210,7 @@ static int hci_reassembly(struct hci_dev *hdev, int type, void *data, /* Complete frame */ bt_cb(skb)->pkt_type = type; - hci_recv_frame(skb); + hci_recv_frame(hdev, skb); hdev->reassembly[index] = NULL; return remain; @@ -2537,15 +3301,8 @@ int hci_unregister_cb(struct hci_cb *cb) } EXPORT_SYMBOL(hci_unregister_cb); -static int hci_send_frame(struct sk_buff *skb) +static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { - struct hci_dev *hdev = (struct hci_dev *) skb->dev; - - if (!hdev) { - kfree_skb(skb); - return -ENODEV; - } - BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); /* Time stamp */ @@ -2562,7 +3319,8 @@ static int hci_send_frame(struct sk_buff *skb) /* Get rid of skb owner, prior to sending to the driver. */ skb_orphan(skb); - return hdev->send(skb); + if (hdev->send(hdev, skb) < 0) + BT_ERR("%s sending frame failed", hdev->name); } void hci_req_init(struct hci_request *req, struct hci_dev *hdev) @@ -2625,7 +3383,6 @@ static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, BT_DBG("skb len %d", skb->len); bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; - skb->dev = (void *) hdev; return skb; } @@ -2769,7 +3526,6 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue, do { skb = list; list = list->next; - skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; hci_add_acl_hdr(skb, conn->handle, flags); @@ -2788,8 +3544,6 @@ void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags) BT_DBG("%s chan %p flags 0x%4.4x", hdev->name, chan, flags); - skb->dev = (void *) hdev; - hci_queue_acl(chan, &chan->data_q, skb, flags); queue_work(hdev->workqueue, &hdev->tx_work); @@ -2810,7 +3564,6 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb) skb_reset_transport_header(skb); memcpy(skb_transport_header(skb), &hdr, HCI_SCO_HDR_SIZE); - skb->dev = (void *) hdev; bt_cb(skb)->pkt_type = HCI_SCODATA_PKT; skb_queue_tail(&conn->data_q, skb); @@ -3075,7 +3828,7 @@ static void hci_sched_acl_pkt(struct hci_dev *hdev) hci_conn_enter_active_mode(chan->conn, bt_cb(skb)->force_active); - hci_send_frame(skb); + hci_send_frame(hdev, skb); hdev->acl_last_tx = jiffies; hdev->acl_cnt--; @@ -3127,7 +3880,7 @@ static void hci_sched_acl_blk(struct hci_dev *hdev) hci_conn_enter_active_mode(chan->conn, bt_cb(skb)->force_active); - hci_send_frame(skb); + hci_send_frame(hdev, skb); hdev->acl_last_tx = jiffies; hdev->block_cnt -= blocks; @@ -3180,7 +3933,7 @@ static void hci_sched_sco(struct hci_dev *hdev) while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); - hci_send_frame(skb); + hci_send_frame(hdev, skb); conn->sent++; if (conn->sent == ~0) @@ -3204,7 +3957,7 @@ static void hci_sched_esco(struct hci_dev *hdev) "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); - hci_send_frame(skb); + hci_send_frame(hdev, skb); conn->sent++; if (conn->sent == ~0) @@ -3246,7 +3999,7 @@ static void hci_sched_le(struct hci_dev *hdev) skb = skb_dequeue(&chan->data_q); - hci_send_frame(skb); + hci_send_frame(hdev, skb); hdev->le_last_tx = jiffies; cnt--; @@ -3272,19 +4025,17 @@ static void hci_tx_work(struct work_struct *work) BT_DBG("%s acl %d sco %d le %d", hdev->name, hdev->acl_cnt, hdev->sco_cnt, hdev->le_cnt); - /* Schedule queues and send stuff to HCI driver */ - - hci_sched_acl(hdev); - - hci_sched_sco(hdev); - - hci_sched_esco(hdev); - - hci_sched_le(hdev); + if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + /* Schedule queues and send stuff to HCI driver */ + hci_sched_acl(hdev); + hci_sched_sco(hdev); + hci_sched_esco(hdev); + hci_sched_le(hdev); + } /* Send next queued raw (unknown type) packet */ while ((skb = skb_dequeue(&hdev->raw_q))) - hci_send_frame(skb); + hci_send_frame(hdev, skb); } /* ----- HCI RX task (incoming data processing) ----- */ @@ -3471,7 +4222,8 @@ static void hci_rx_work(struct work_struct *work) hci_send_to_sock(hdev, skb); } - if (test_bit(HCI_RAW, &hdev->flags)) { + if (test_bit(HCI_RAW, &hdev->flags) || + test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { kfree_skb(skb); continue; } @@ -3526,10 +4278,10 @@ static void hci_cmd_work(struct work_struct *work) kfree_skb(hdev->sent_cmd); - hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC); + hdev->sent_cmd = skb_clone(skb, GFP_KERNEL); if (hdev->sent_cmd) { atomic_dec(&hdev->cmd_cnt); - hci_send_frame(skb); + hci_send_frame(hdev, skb); if (test_bit(HCI_RESET, &hdev->flags)) del_timer(&hdev->cmd_timer); else @@ -3541,15 +4293,3 @@ static void hci_cmd_work(struct work_struct *work) } } } - -u8 bdaddr_to_le(u8 bdaddr_type) -{ - switch (bdaddr_type) { - case BDADDR_LE_PUBLIC: - return ADDR_LE_DEV_PUBLIC; - - default: - /* Fallback to LE Random address type */ - return ADDR_LE_DEV_RANDOM; - } -} diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8db3e89fae354aebb67c6ea7172a3e2e1926b66d..5935f748c0f9a6fe71cb3c0fe8baeaedb7019c37 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -29,8 +29,9 @@ #include #include #include -#include -#include + +#include "a2mp.h" +#include "amp.h" /* Handle HCI Event packets */ @@ -194,6 +195,11 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) memset(hdev->adv_data, 0, sizeof(hdev->adv_data)); hdev->adv_data_len = 0; + + memset(hdev->scan_rsp_data, 0, sizeof(hdev->scan_rsp_data)); + hdev->scan_rsp_data_len = 0; + + hdev->ssp_debug_mode = 0; } static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -297,6 +303,11 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) goto done; } + /* We need to ensure that we set this back on if someone changed + * the scan mode through a raw HCI socket. + */ + set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); @@ -304,11 +315,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) set_bit(HCI_ISCAN, &hdev->flags); if (!old_iscan) mgmt_discoverable(hdev, 1); - if (hdev->discov_timeout > 0) { - int to = msecs_to_jiffies(hdev->discov_timeout * 1000); - queue_delayed_work(hdev->workqueue, &hdev->discov_off, - to); - } } else if (old_iscan) mgmt_discoverable(hdev, 0); @@ -412,6 +418,21 @@ static void hci_cc_write_voice_setting(struct hci_dev *hdev, hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); } +static void hci_cc_read_num_supported_iac(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_num_supported_iac *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->num_iac = rp->num_iac; + + BT_DBG("%s num iac %d", hdev->name, hdev->num_iac); +} + static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -449,14 +470,13 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - hdev->hci_ver = rp->hci_ver; - hdev->hci_rev = __le16_to_cpu(rp->hci_rev); - hdev->lmp_ver = rp->lmp_ver; - hdev->manufacturer = __le16_to_cpu(rp->manufacturer); - hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver); - - BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name, - hdev->manufacturer, hdev->hci_ver, hdev->hci_rev); + if (test_bit(HCI_SETUP, &hdev->dev_flags)) { + hdev->hci_ver = rp->hci_ver; + hdev->hci_rev = __le16_to_cpu(rp->hci_rev); + hdev->lmp_ver = rp->lmp_ver; + hdev->manufacturer = __le16_to_cpu(rp->manufacturer); + hdev->lmp_subver = __le16_to_cpu(rp->lmp_subver); + } } static void hci_cc_read_local_commands(struct hci_dev *hdev, @@ -536,7 +556,8 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev, if (rp->status) return; - hdev->max_page = rp->max_page; + if (hdev->max_page < rp->max_page) + hdev->max_page = rp->max_page; if (rp->page < HCI_MAX_PAGES) memcpy(hdev->features[rp->page], rp->features, 8); @@ -913,17 +934,9 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!status) { if (*sent) - set_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags); + set_bit(HCI_ADVERTISING, &hdev->dev_flags); else - clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags); - } - - if (!test_bit(HCI_INIT, &hdev->flags)) { - struct hci_request req; - - hci_req_init(&req, hdev); - hci_update_ad(&req); - hci_req_run(&req, NULL); + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); } hci_dev_unlock(hdev); @@ -994,20 +1007,20 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, return; if (!status) { - if (sent->le) + if (sent->le) { hdev->features[1][0] |= LMP_HOST_LE; - else + set_bit(HCI_LE_ENABLED, &hdev->dev_flags); + } else { hdev->features[1][0] &= ~LMP_HOST_LE; + clear_bit(HCI_LE_ENABLED, &hdev->dev_flags); + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + } if (sent->simul) hdev->features[1][0] |= LMP_HOST_LE_BREDR; else hdev->features[1][0] &= ~LMP_HOST_LE_BREDR; } - - if (test_bit(HCI_MGMT, &hdev->dev_flags) && - !test_bit(HCI_INIT, &hdev->flags)) - mgmt_le_enable_complete(hdev, sent->le, status); } static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, @@ -1291,9 +1304,11 @@ static void hci_cs_remote_name_req(struct hci_dev *hdev, __u8 status) goto unlock; if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { - struct hci_cp_auth_requested cp; - cp.handle = __cpu_to_le16(conn->handle); - hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, sizeof(cp), &cp); + struct hci_cp_auth_requested auth_cp; + + auth_cp.handle = __cpu_to_le16(conn->handle); + hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED, + sizeof(auth_cp), &auth_cp); } unlock: @@ -1465,33 +1480,6 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status) hci_dev_unlock(hdev); } -static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) -{ - struct hci_conn *conn; - - BT_DBG("%s status 0x%2.2x", hdev->name, status); - - if (status) { - hci_dev_lock(hdev); - - conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); - if (!conn) { - hci_dev_unlock(hdev); - return; - } - - BT_DBG("%s bdaddr %pMR conn %p", hdev->name, &conn->dst, conn); - - conn->state = BT_CLOSED; - mgmt_connect_failed(hdev, &conn->dst, conn->type, - conn->dst_type, status); - hci_proto_connect_cfm(conn, status); - hci_conn_del(conn); - - hci_dev_unlock(hdev); - } -} - static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) { struct hci_cp_create_phy_link *cp; @@ -1706,7 +1694,7 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) &flags); if ((mask & HCI_LM_ACCEPT) && - !hci_blacklist_lookup(hdev, &ev->bdaddr)) { + !hci_blacklist_lookup(hdev, &ev->bdaddr, BDADDR_BREDR)) { /* Connection accepted */ struct inquiry_entry *ie; struct hci_conn *conn; @@ -1821,10 +1809,25 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) } if (ev->status == 0) { - if (conn->type == ACL_LINK && conn->flush_key) + u8 type = conn->type; + + if (type == ACL_LINK && conn->flush_key) hci_remove_link_key(hdev, &conn->dst); hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); + + /* Re-enable advertising if necessary, since it might + * have been disabled by the connection. From the + * HCI_LE_Set_Advertise_Enable command description in + * the core specification (v4.0): + * "The Controller shall continue advertising until the Host + * issues an LE_Set_Advertise_Enable command with + * Advertising_Enable set to 0x00 (Advertising is disabled) + * or until a connection is created or until the Advertising + * is timed out due to Directed Advertising." + */ + if (type == LE_LINK) + mgmt_reenable_advertising(hdev); } unlock: @@ -2139,6 +2142,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_write_voice_setting(hdev, skb); break; + case HCI_OP_READ_NUM_SUPPORTED_IAC: + hci_cc_read_num_supported_iac(hdev, skb); + break; + case HCI_OP_WRITE_SSP_MODE: hci_cc_write_ssp_mode(hdev, skb); break; @@ -2342,10 +2349,6 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_disconnect(hdev, ev->status); break; - case HCI_OP_LE_CREATE_CONN: - hci_cs_le_create_conn(hdev, ev->status); - break; - case HCI_OP_CREATE_PHY_LINK: hci_cs_create_phylink(hdev, ev->status); break; @@ -2548,7 +2551,6 @@ static void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { conn->mode = ev->mode; - conn->interval = __le16_to_cpu(ev->interval); if (!test_and_clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->flags)) { @@ -2930,6 +2932,23 @@ unlock: hci_dev_unlock(hdev); } +static inline size_t eir_get_length(u8 *eir, size_t eir_len) +{ + size_t parsed = 0; + + while (parsed < eir_len) { + u8 field_len = eir[0]; + + if (field_len == 0) + return parsed; + + parsed += field_len + 1; + eir += field_len + 1; + } + + return eir_len; +} + static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) { @@ -3170,7 +3189,8 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev, if (hdev->auto_accept_delay > 0) { int delay = msecs_to_jiffies(hdev->auto_accept_delay); - mod_timer(&conn->auto_accept_timer, jiffies + delay); + queue_delayed_work(conn->hdev->workqueue, + &conn->auto_accept_work, delay); goto unlock; } @@ -3485,6 +3505,17 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->dst_type = ev->bdaddr_type; + /* The advertising parameters for own address type + * define which source address and source address + * type this connections has. + */ + if (bacmp(&conn->src, BDADDR_ANY)) { + conn->src_type = ADDR_LE_DEV_PUBLIC; + } else { + bacpy(&conn->src, &hdev->static_addr); + conn->src_type = ADDR_LE_DEV_RANDOM; + } + if (ev->role == LE_CONN_ROLE_MASTER) { conn->out = true; conn->link_mode |= HCI_LM_MASTER; @@ -3640,8 +3671,8 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) skb_pull(skb, HCI_EVENT_HDR_SIZE); if (hdev->sent_cmd && bt_cb(hdev->sent_cmd)->req.event == event) { - struct hci_command_hdr *hdr = (void *) hdev->sent_cmd->data; - u16 opcode = __le16_to_cpu(hdr->opcode); + struct hci_command_hdr *cmd_hdr = (void *) hdev->sent_cmd->data; + u16 opcode = __le16_to_cpu(cmd_hdr->opcode); hci_req_cmd_complete(hdev, opcode, 0); } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 9bd7d959e384c74504a06ed27e0eef0d61dbf70e..71f0be1730801a615191a9badc2bd2f588bd4fbb 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -66,6 +66,46 @@ static struct bt_sock_list hci_sk_list = { .lock = __RW_LOCK_UNLOCKED(hci_sk_list.lock) }; +static bool is_filtered_packet(struct sock *sk, struct sk_buff *skb) +{ + struct hci_filter *flt; + int flt_type, flt_event; + + /* Apply filter */ + flt = &hci_pi(sk)->filter; + + if (bt_cb(skb)->pkt_type == HCI_VENDOR_PKT) + flt_type = 0; + else + flt_type = bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS; + + if (!test_bit(flt_type, &flt->type_mask)) + return true; + + /* Extra filter for event packets only */ + if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT) + return false; + + flt_event = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS); + + if (!hci_test_bit(flt_event, &flt->event_mask)) + return true; + + /* Check filter only when opcode is set */ + if (!flt->opcode) + return false; + + if (flt_event == HCI_EV_CMD_COMPLETE && + flt->opcode != get_unaligned((__le16 *)(skb->data + 3))) + return true; + + if (flt_event == HCI_EV_CMD_STATUS && + flt->opcode != get_unaligned((__le16 *)(skb->data + 4))) + return true; + + return false; +} + /* Send frame to RAW socket */ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) { @@ -77,7 +117,6 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) read_lock(&hci_sk_list.lock); sk_for_each(sk, &hci_sk_list.head) { - struct hci_filter *flt; struct sk_buff *nskb; if (sk->sk_state != BT_BOUND || hci_pi(sk)->hdev != hdev) @@ -87,31 +126,19 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb) if (skb->sk == sk) continue; - if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) - continue; - - /* Apply filter */ - flt = &hci_pi(sk)->filter; - - if (!test_bit((bt_cb(skb)->pkt_type == HCI_VENDOR_PKT) ? - 0 : (bt_cb(skb)->pkt_type & HCI_FLT_TYPE_BITS), - &flt->type_mask)) - continue; - - if (bt_cb(skb)->pkt_type == HCI_EVENT_PKT) { - int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS); - - if (!hci_test_bit(evt, &flt->event_mask)) + if (hci_pi(sk)->channel == HCI_CHANNEL_RAW) { + if (is_filtered_packet(sk, skb)) continue; - - if (flt->opcode && - ((evt == HCI_EV_CMD_COMPLETE && - flt->opcode != - get_unaligned((__le16 *)(skb->data + 3))) || - (evt == HCI_EV_CMD_STATUS && - flt->opcode != - get_unaligned((__le16 *)(skb->data + 4))))) + } else if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { + if (!bt_cb(skb)->incoming) + continue; + if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT && + bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT && + bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) continue; + } else { + /* Don't send frame to other channel types */ + continue; } if (!skb_copy) { @@ -360,7 +387,6 @@ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) __net_timestamp(skb); bt_cb(skb)->pkt_type = HCI_EVENT_PKT; - skb->dev = (void *) hdev; hci_send_to_sock(hdev, skb); kfree_skb(skb); } @@ -426,6 +452,12 @@ static int hci_sock_release(struct socket *sock) bt_sock_unlink(&hci_sk_list, sk); if (hdev) { + if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { + mgmt_index_added(hdev); + clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); + hci_dev_close(hdev->id); + } + atomic_dec(&hdev->promisc); hci_dev_put(hdev); } @@ -449,7 +481,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) hci_dev_lock(hdev); - err = hci_blacklist_add(hdev, &bdaddr, 0); + err = hci_blacklist_add(hdev, &bdaddr, BDADDR_BREDR); hci_dev_unlock(hdev); @@ -466,7 +498,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) hci_dev_lock(hdev); - err = hci_blacklist_del(hdev, &bdaddr, 0); + err = hci_blacklist_del(hdev, &bdaddr, BDADDR_BREDR); hci_dev_unlock(hdev); @@ -482,6 +514,12 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, if (!hdev) return -EBADFD; + if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) + return -EBUSY; + + if (hdev->dev_type != HCI_BREDR) + return -EOPNOTSUPP; + switch (cmd) { case HCISETRAW: if (!capable(CAP_NET_ADMIN)) @@ -512,23 +550,29 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, if (!capable(CAP_NET_ADMIN)) return -EPERM; return hci_sock_blacklist_del(hdev, (void __user *) arg); - - default: - if (hdev->ioctl) - return hdev->ioctl(hdev, cmd, arg); - return -EINVAL; } + + return -ENOIOCTLCMD; } static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { - struct sock *sk = sock->sk; void __user *argp = (void __user *) arg; + struct sock *sk = sock->sk; int err; BT_DBG("cmd %x arg %lx", cmd, arg); + lock_sock(sk); + + if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { + err = -EBADFD; + goto done; + } + + release_sock(sk); + switch (cmd) { case HCIGETDEVLIST: return hci_get_dev_list(argp); @@ -573,13 +617,15 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd, case HCIINQUIRY: return hci_inquiry(argp); - - default: - lock_sock(sk); - err = hci_sock_bound_ioctl(sk, cmd, arg); - release_sock(sk); - return err; } + + lock_sock(sk); + + err = hci_sock_bound_ioctl(sk, cmd, arg); + +done: + release_sock(sk); + return err; } static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, @@ -629,6 +675,56 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, hci_pi(sk)->hdev = hdev; break; + case HCI_CHANNEL_USER: + if (hci_pi(sk)->hdev) { + err = -EALREADY; + goto done; + } + + if (haddr.hci_dev == HCI_DEV_NONE) { + err = -EINVAL; + goto done; + } + + if (!capable(CAP_NET_ADMIN)) { + err = -EPERM; + goto done; + } + + hdev = hci_dev_get(haddr.hci_dev); + if (!hdev) { + err = -ENODEV; + goto done; + } + + if (test_bit(HCI_UP, &hdev->flags) || + test_bit(HCI_INIT, &hdev->flags) || + test_bit(HCI_SETUP, &hdev->dev_flags)) { + err = -EBUSY; + hci_dev_put(hdev); + goto done; + } + + if (test_and_set_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + err = -EUSERS; + hci_dev_put(hdev); + goto done; + } + + mgmt_index_removed(hdev); + + err = hci_dev_open(hdev->id); + if (err) { + clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); + hci_dev_put(hdev); + goto done; + } + + atomic_inc(&hdev->promisc); + + hci_pi(sk)->hdev = hdev; + break; + case HCI_CHANNEL_CONTROL: if (haddr.hci_dev != HCI_DEV_NONE) { err = -EINVAL; @@ -677,22 +773,30 @@ static int hci_sock_getname(struct socket *sock, struct sockaddr *addr, { struct sockaddr_hci *haddr = (struct sockaddr_hci *) addr; struct sock *sk = sock->sk; - struct hci_dev *hdev = hci_pi(sk)->hdev; + struct hci_dev *hdev; + int err = 0; BT_DBG("sock %p sk %p", sock, sk); - if (!hdev) - return -EBADFD; + if (peer) + return -EOPNOTSUPP; lock_sock(sk); + hdev = hci_pi(sk)->hdev; + if (!hdev) { + err = -EBADFD; + goto done; + } + *addr_len = sizeof(*haddr); haddr->hci_family = AF_BLUETOOTH; haddr->hci_dev = hdev->id; - haddr->hci_channel= 0; + haddr->hci_channel= hci_pi(sk)->channel; +done: release_sock(sk); - return 0; + return err; } static void hci_sock_cmsg(struct sock *sk, struct msghdr *msg, @@ -767,6 +871,7 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock, case HCI_CHANNEL_RAW: hci_sock_cmsg(sk, msg, skb); break; + case HCI_CHANNEL_USER: case HCI_CHANNEL_CONTROL: case HCI_CHANNEL_MONITOR: sock_recv_timestamp(msg, sk, skb); @@ -801,6 +906,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, switch (hci_pi(sk)->channel) { case HCI_CHANNEL_RAW: + case HCI_CHANNEL_USER: break; case HCI_CHANNEL_CONTROL: err = mgmt_control(sk, msg, len); @@ -835,9 +941,9 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, bt_cb(skb)->pkt_type = *((unsigned char *) skb->data); skb_pull(skb, 1); - skb->dev = (void *) hdev; - if (bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { + if (hci_pi(sk)->channel == HCI_CHANNEL_RAW && + bt_cb(skb)->pkt_type == HCI_COMMAND_PKT) { u16 opcode = get_unaligned_le16(skb->data); u16 ogf = hci_opcode_ogf(opcode); u16 ocf = hci_opcode_ocf(opcode); @@ -868,6 +974,14 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, goto drop; } + if (hci_pi(sk)->channel == HCI_CHANNEL_USER && + bt_cb(skb)->pkt_type != HCI_COMMAND_PKT && + bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT && + bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) { + err = -EINVAL; + goto drop; + } + skb_queue_tail(&hdev->raw_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } @@ -895,7 +1009,7 @@ static int hci_sock_setsockopt(struct socket *sock, int level, int optname, lock_sock(sk); if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { - err = -EINVAL; + err = -EBADFD; goto done; } @@ -981,7 +1095,7 @@ static int hci_sock_getsockopt(struct socket *sock, int level, int optname, lock_sock(sk); if (hci_pi(sk)->channel != HCI_CHANNEL_RAW) { - err = -EINVAL; + err = -EBADFD; goto done; } diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index edf623a29043117c4be9cf255b37af3ddf22d988..0b61250cfdf90c9e3a488c9ca3cce41ac79d6a84 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -1,17 +1,12 @@ /* Bluetooth HCI driver model support. */ -#include #include -#include #include #include static struct class *bt_class; -struct dentry *bt_debugfs; -EXPORT_SYMBOL_GPL(bt_debugfs); - static inline char *link_typetostr(int type) { switch (type) { @@ -42,29 +37,15 @@ static ssize_t show_link_address(struct device *dev, return sprintf(buf, "%pMR\n", &conn->dst); } -static ssize_t show_link_features(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_conn *conn = to_hci_conn(dev); - - return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", - conn->features[0][0], conn->features[0][1], - conn->features[0][2], conn->features[0][3], - conn->features[0][4], conn->features[0][5], - conn->features[0][6], conn->features[0][7]); -} - #define LINK_ATTR(_name, _mode, _show, _store) \ struct device_attribute link_attr_##_name = __ATTR(_name, _mode, _show, _store) static LINK_ATTR(type, S_IRUGO, show_link_type, NULL); static LINK_ATTR(address, S_IRUGO, show_link_address, NULL); -static LINK_ATTR(features, S_IRUGO, show_link_features, NULL); static struct attribute *bt_link_attrs[] = { &link_attr_type.attr, &link_attr_address.attr, - &link_attr_features.attr, NULL }; @@ -150,28 +131,6 @@ void hci_conn_del_sysfs(struct hci_conn *conn) hci_dev_put(hdev); } -static inline char *host_bustostr(int bus) -{ - switch (bus) { - case HCI_VIRTUAL: - return "VIRTUAL"; - case HCI_USB: - return "USB"; - case HCI_PCCARD: - return "PCCARD"; - case HCI_UART: - return "UART"; - case HCI_RS232: - return "RS232"; - case HCI_PCI: - return "PCI"; - case HCI_SDIO: - return "SDIO"; - default: - return "UNKNOWN"; - } -} - static inline char *host_typetostr(int type) { switch (type) { @@ -184,13 +143,6 @@ static inline char *host_typetostr(int type) } } -static ssize_t show_bus(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%s\n", host_bustostr(hdev->bus)); -} - static ssize_t show_type(struct device *dev, struct device_attribute *attr, char *buf) { @@ -212,14 +164,6 @@ static ssize_t show_name(struct device *dev, return sprintf(buf, "%s\n", name); } -static ssize_t show_class(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "0x%.2x%.2x%.2x\n", hdev->dev_class[2], - hdev->dev_class[1], hdev->dev_class[0]); -} - static ssize_t show_address(struct device *dev, struct device_attribute *attr, char *buf) { @@ -227,150 +171,14 @@ static ssize_t show_address(struct device *dev, return sprintf(buf, "%pMR\n", &hdev->bdaddr); } -static ssize_t show_features(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - - return sprintf(buf, "0x%02x%02x%02x%02x%02x%02x%02x%02x\n", - hdev->features[0][0], hdev->features[0][1], - hdev->features[0][2], hdev->features[0][3], - hdev->features[0][4], hdev->features[0][5], - hdev->features[0][6], hdev->features[0][7]); -} - -static ssize_t show_manufacturer(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->manufacturer); -} - -static ssize_t show_hci_version(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->hci_ver); -} - -static ssize_t show_hci_revision(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->hci_rev); -} - -static ssize_t show_idle_timeout(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->idle_timeout); -} - -static ssize_t store_idle_timeout(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hci_dev *hdev = to_hci_dev(dev); - unsigned int val; - int rv; - - rv = kstrtouint(buf, 0, &val); - if (rv < 0) - return rv; - - if (val != 0 && (val < 500 || val > 3600000)) - return -EINVAL; - - hdev->idle_timeout = val; - - return count; -} - -static ssize_t show_sniff_max_interval(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->sniff_max_interval); -} - -static ssize_t store_sniff_max_interval(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hci_dev *hdev = to_hci_dev(dev); - u16 val; - int rv; - - rv = kstrtou16(buf, 0, &val); - if (rv < 0) - return rv; - - if (val == 0 || val % 2 || val < hdev->sniff_min_interval) - return -EINVAL; - - hdev->sniff_max_interval = val; - - return count; -} - -static ssize_t show_sniff_min_interval(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct hci_dev *hdev = to_hci_dev(dev); - return sprintf(buf, "%d\n", hdev->sniff_min_interval); -} - -static ssize_t store_sniff_min_interval(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct hci_dev *hdev = to_hci_dev(dev); - u16 val; - int rv; - - rv = kstrtou16(buf, 0, &val); - if (rv < 0) - return rv; - - if (val == 0 || val % 2 || val > hdev->sniff_max_interval) - return -EINVAL; - - hdev->sniff_min_interval = val; - - return count; -} - -static DEVICE_ATTR(bus, S_IRUGO, show_bus, NULL); static DEVICE_ATTR(type, S_IRUGO, show_type, NULL); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static DEVICE_ATTR(class, S_IRUGO, show_class, NULL); static DEVICE_ATTR(address, S_IRUGO, show_address, NULL); -static DEVICE_ATTR(features, S_IRUGO, show_features, NULL); -static DEVICE_ATTR(manufacturer, S_IRUGO, show_manufacturer, NULL); -static DEVICE_ATTR(hci_version, S_IRUGO, show_hci_version, NULL); -static DEVICE_ATTR(hci_revision, S_IRUGO, show_hci_revision, NULL); - -static DEVICE_ATTR(idle_timeout, S_IRUGO | S_IWUSR, - show_idle_timeout, store_idle_timeout); -static DEVICE_ATTR(sniff_max_interval, S_IRUGO | S_IWUSR, - show_sniff_max_interval, store_sniff_max_interval); -static DEVICE_ATTR(sniff_min_interval, S_IRUGO | S_IWUSR, - show_sniff_min_interval, store_sniff_min_interval); static struct attribute *bt_host_attrs[] = { - &dev_attr_bus.attr, &dev_attr_type.attr, &dev_attr_name.attr, - &dev_attr_class.attr, &dev_attr_address.attr, - &dev_attr_features.attr, - &dev_attr_manufacturer.attr, - &dev_attr_hci_version.attr, - &dev_attr_hci_revision.attr, - &dev_attr_idle_timeout.attr, - &dev_attr_sniff_max_interval.attr, - &dev_attr_sniff_min_interval.attr, NULL }; @@ -396,141 +204,6 @@ static struct device_type bt_host = { .release = bt_host_release, }; -static int inquiry_cache_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct discovery_state *cache = &hdev->discovery; - struct inquiry_entry *e; - - hci_dev_lock(hdev); - - list_for_each_entry(e, &cache->all, all) { - struct inquiry_data *data = &e->data; - seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n", - &data->bdaddr, - data->pscan_rep_mode, data->pscan_period_mode, - data->pscan_mode, data->dev_class[2], - data->dev_class[1], data->dev_class[0], - __le16_to_cpu(data->clock_offset), - data->rssi, data->ssp_mode, e->timestamp); - } - - hci_dev_unlock(hdev); - - return 0; -} - -static int inquiry_cache_open(struct inode *inode, struct file *file) -{ - return single_open(file, inquiry_cache_show, inode->i_private); -} - -static const struct file_operations inquiry_cache_fops = { - .open = inquiry_cache_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int blacklist_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct bdaddr_list *b; - - hci_dev_lock(hdev); - - list_for_each_entry(b, &hdev->blacklist, list) - seq_printf(f, "%pMR\n", &b->bdaddr); - - hci_dev_unlock(hdev); - - return 0; -} - -static int blacklist_open(struct inode *inode, struct file *file) -{ - return single_open(file, blacklist_show, inode->i_private); -} - -static const struct file_operations blacklist_fops = { - .open = blacklist_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static void print_bt_uuid(struct seq_file *f, u8 *uuid) -{ - u32 data0, data5; - u16 data1, data2, data3, data4; - - data5 = get_unaligned_le32(uuid); - data4 = get_unaligned_le16(uuid + 4); - data3 = get_unaligned_le16(uuid + 6); - data2 = get_unaligned_le16(uuid + 8); - data1 = get_unaligned_le16(uuid + 10); - data0 = get_unaligned_le32(uuid + 12); - - seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n", - data0, data1, data2, data3, data4, data5); -} - -static int uuids_show(struct seq_file *f, void *p) -{ - struct hci_dev *hdev = f->private; - struct bt_uuid *uuid; - - hci_dev_lock(hdev); - - list_for_each_entry(uuid, &hdev->uuids, list) - print_bt_uuid(f, uuid->uuid); - - hci_dev_unlock(hdev); - - return 0; -} - -static int uuids_open(struct inode *inode, struct file *file) -{ - return single_open(file, uuids_show, inode->i_private); -} - -static const struct file_operations uuids_fops = { - .open = uuids_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int auto_accept_delay_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - - hdev->auto_accept_delay = val; - - hci_dev_unlock(hdev); - - return 0; -} - -static int auto_accept_delay_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - - *val = hdev->auto_accept_delay; - - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, - auto_accept_delay_set, "%llu\n"); - void hci_init_sysfs(struct hci_dev *hdev) { struct device *dev = &hdev->dev; @@ -542,52 +215,8 @@ void hci_init_sysfs(struct hci_dev *hdev) device_initialize(dev); } -int hci_add_sysfs(struct hci_dev *hdev) -{ - struct device *dev = &hdev->dev; - int err; - - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - dev_set_name(dev, "%s", hdev->name); - - err = device_add(dev); - if (err < 0) - return err; - - if (!bt_debugfs) - return 0; - - hdev->debugfs = debugfs_create_dir(hdev->name, bt_debugfs); - if (!hdev->debugfs) - return 0; - - debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, - hdev, &inquiry_cache_fops); - - debugfs_create_file("blacklist", 0444, hdev->debugfs, - hdev, &blacklist_fops); - - debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); - - debugfs_create_file("auto_accept_delay", 0444, hdev->debugfs, hdev, - &auto_accept_delay_fops); - return 0; -} - -void hci_del_sysfs(struct hci_dev *hdev) -{ - BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - - debugfs_remove_recursive(hdev->debugfs); - - device_del(&hdev->dev); -} - int __init bt_sysfs_init(void) { - bt_debugfs = debugfs_create_dir("bluetooth", NULL); - bt_class = class_create(THIS_MODULE, "bluetooth"); return PTR_ERR_OR_ZERO(bt_class); @@ -596,6 +225,4 @@ int __init bt_sysfs_init(void) void bt_sysfs_cleanup(void) { class_destroy(bt_class); - - debugfs_remove_recursive(bt_debugfs); } diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index bdc35a7a7feeaf4ac7a918547e82d5d19a7ef537..292e619db8961c82e7c3aa7f3280cb4236176ab8 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -767,10 +767,10 @@ static int hidp_setup_hid(struct hidp_session *session, strncpy(hid->name, req->name, sizeof(req->name) - 1); snprintf(hid->phys, sizeof(hid->phys), "%pMR", - &bt_sk(session->ctrl_sock->sk)->src); + &l2cap_pi(session->ctrl_sock->sk)->chan->src); snprintf(hid->uniq, sizeof(hid->uniq), "%pMR", - &bt_sk(session->ctrl_sock->sk)->dst); + &l2cap_pi(session->ctrl_sock->sk)->chan->dst); hid->dev.parent = &session->conn->hcon->dev; hid->ll_driver = &hidp_hid_driver; @@ -1283,23 +1283,29 @@ static int hidp_session_thread(void *arg) static int hidp_verify_sockets(struct socket *ctrl_sock, struct socket *intr_sock) { + struct l2cap_chan *ctrl_chan, *intr_chan; struct bt_sock *ctrl, *intr; struct hidp_session *session; if (!l2cap_is_socket(ctrl_sock) || !l2cap_is_socket(intr_sock)) return -EINVAL; + ctrl_chan = l2cap_pi(ctrl_sock->sk)->chan; + intr_chan = l2cap_pi(intr_sock->sk)->chan; + + if (bacmp(&ctrl_chan->src, &intr_chan->src) || + bacmp(&ctrl_chan->dst, &intr_chan->dst)) + return -ENOTUNIQ; + ctrl = bt_sk(ctrl_sock->sk); intr = bt_sk(intr_sock->sk); - if (bacmp(&ctrl->src, &intr->src) || bacmp(&ctrl->dst, &intr->dst)) - return -ENOTUNIQ; if (ctrl->sk.sk_state != BT_CONNECTED || intr->sk.sk_state != BT_CONNECTED) return -EBADFD; /* early session check, we check again during session registration */ - session = hidp_session_find(&ctrl->dst); + session = hidp_session_find(&ctrl_chan->dst); if (session) { hidp_session_put(session); return -EEXIST; @@ -1332,7 +1338,7 @@ int hidp_connection_add(struct hidp_connadd_req *req, if (!conn) return -EBADFD; - ret = hidp_session_new(&session, &bt_sk(ctrl_sock->sk)->dst, ctrl_sock, + ret = hidp_session_new(&session, &chan->dst, ctrl_sock, intr_sock, req, conn); if (ret) goto out_conn; diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h index 9e6cc355310504ae965035e033bdac7d5008527b..ab5241400cf78a9d7371d7a866d58aeba0d8043a 100644 --- a/net/bluetooth/hidp/hidp.h +++ b/net/bluetooth/hidp/hidp.h @@ -182,7 +182,7 @@ struct hidp_session { }; /* HIDP init defines */ -extern int __init hidp_init_sockets(void); -extern void __exit hidp_cleanup_sockets(void); +int __init hidp_init_sockets(void); +void __exit hidp_cleanup_sockets(void); #endif /* __HIDP_H */ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 63fa11109a1c391725d5efec2075c1524d9f1a1a..0cef677078381315c7ce3e58abb6573136bc227b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -36,14 +36,15 @@ #include #include #include -#include -#include -#include + +#include "smp.h" +#include "a2mp.h" +#include "amp.h" bool disable_ertm; -static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; -static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, }; +static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD; +static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, }; static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); @@ -58,6 +59,18 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err); static void l2cap_tx(struct l2cap_chan *chan, struct l2cap_ctrl *control, struct sk_buff_head *skbs, u8 event); +static inline __u8 bdaddr_type(struct hci_conn *hcon, __u8 type) +{ + if (hcon->type == LE_LINK) { + if (type == ADDR_LE_DEV_PUBLIC) + return BDADDR_LE_PUBLIC; + else + return BDADDR_LE_RANDOM; + } + + return BDADDR_BREDR; +} + /* ---- L2CAP channels ---- */ static struct l2cap_chan *__l2cap_get_chan_by_dcid(struct l2cap_conn *conn, @@ -148,7 +161,7 @@ static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) struct l2cap_chan *c; list_for_each_entry(c, &chan_list, global_l) { - if (c->sport == psm && !bacmp(&bt_sk(c->sk)->src, src)) + if (c->sport == psm && !bacmp(&c->src, src)) return c; } return NULL; @@ -210,38 +223,25 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) return 0; } -static void __l2cap_state_change(struct l2cap_chan *chan, int state) +static void l2cap_state_change(struct l2cap_chan *chan, int state) { BT_DBG("chan %p %s -> %s", chan, state_to_string(chan->state), state_to_string(state)); chan->state = state; - chan->ops->state_change(chan, state); -} - -static void l2cap_state_change(struct l2cap_chan *chan, int state) -{ - struct sock *sk = chan->sk; - - lock_sock(sk); - __l2cap_state_change(chan, state); - release_sock(sk); + chan->ops->state_change(chan, state, 0); } -static inline void __l2cap_chan_set_err(struct l2cap_chan *chan, int err) +static inline void l2cap_state_change_and_error(struct l2cap_chan *chan, + int state, int err) { - struct sock *sk = chan->sk; - - sk->sk_err = err; + chan->state = state; + chan->ops->state_change(chan, chan->state, err); } static inline void l2cap_chan_set_err(struct l2cap_chan *chan, int err) { - struct sock *sk = chan->sk; - - lock_sock(sk); - __l2cap_chan_set_err(chan, err); - release_sock(sk); + chan->ops->state_change(chan, chan->state, err); } static void __set_retrans_timer(struct l2cap_chan *chan) @@ -620,10 +620,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) void l2cap_chan_close(struct l2cap_chan *chan, int reason) { struct l2cap_conn *conn = chan->conn; - struct sock *sk = chan->sk; - BT_DBG("chan %p state %s sk %p", chan, state_to_string(chan->state), - sk); + BT_DBG("chan %p state %s", chan, state_to_string(chan->state)); switch (chan->state) { case BT_LISTEN: @@ -634,7 +632,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) case BT_CONFIG: if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && conn->hcon->type == ACL_LINK) { - __set_chan_timer(chan, sk->sk_sndtimeo); + __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); l2cap_send_disconn_req(chan, reason); } else l2cap_chan_del(chan, reason); @@ -646,10 +644,11 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) struct l2cap_conn_rsp rsp; __u16 result; - if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) result = L2CAP_CR_SEC_BLOCK; else result = L2CAP_CR_BAD_PSM; + l2cap_state_change(chan, BT_DISCONN); rsp.scid = cpu_to_le16(chan->dcid); @@ -676,7 +675,8 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) { - if (chan->chan_type == L2CAP_CHAN_RAW) { + switch (chan->chan_type) { + case L2CAP_CHAN_RAW: switch (chan->sec_level) { case BT_SECURITY_HIGH: return HCI_AT_DEDICATED_BONDING_MITM; @@ -685,15 +685,29 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) default: return HCI_AT_NO_BONDING; } - } else if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) { - if (chan->sec_level == BT_SECURITY_LOW) - chan->sec_level = BT_SECURITY_SDP; - + break; + case L2CAP_CHAN_CONN_LESS: + if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_3DSP)) { + if (chan->sec_level == BT_SECURITY_LOW) + chan->sec_level = BT_SECURITY_SDP; + } if (chan->sec_level == BT_SECURITY_HIGH) return HCI_AT_NO_BONDING_MITM; else return HCI_AT_NO_BONDING; - } else { + break; + case L2CAP_CHAN_CONN_ORIENTED: + if (chan->psm == __constant_cpu_to_le16(L2CAP_PSM_SDP)) { + if (chan->sec_level == BT_SECURITY_LOW) + chan->sec_level = BT_SECURITY_SDP; + + if (chan->sec_level == BT_SECURITY_HIGH) + return HCI_AT_NO_BONDING_MITM; + else + return HCI_AT_NO_BONDING; + } + /* fall through */ + default: switch (chan->sec_level) { case BT_SECURITY_HIGH: return HCI_AT_GENERAL_BONDING_MITM; @@ -702,6 +716,7 @@ static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) default: return HCI_AT_NO_BONDING; } + break; } } @@ -1015,14 +1030,29 @@ static inline int __l2cap_no_conn_pending(struct l2cap_chan *chan) static bool __amp_capable(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; + struct hci_dev *hdev; + bool amp_available = false; - if (enable_hs && - hci_amp_capable() && - chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED && - conn->fixed_chan_mask & L2CAP_FC_A2MP) - return true; - else + if (!conn->hs_enabled) return false; + + if (!(conn->fixed_chan_mask & L2CAP_FC_A2MP)) + return false; + + read_lock(&hci_dev_list_lock); + list_for_each_entry(hdev, &hci_dev_list, list) { + if (hdev->amp_type != AMP_TYPE_BREDR && + test_bit(HCI_UP, &hdev->flags)) { + amp_available = true; + break; + } + } + read_unlock(&hci_dev_list_lock); + + if (chan->chan_policy == BT_CHANNEL_POLICY_AMP_PREFERRED) + return amp_available; + + return false; } static bool l2cap_check_efs(struct l2cap_chan *chan) @@ -1186,7 +1216,6 @@ static inline int l2cap_mode_supported(__u8 mode, __u32 feat_mask) static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err) { - struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; struct l2cap_disconn_req req; @@ -1209,10 +1238,7 @@ static void l2cap_send_disconn_req(struct l2cap_chan *chan, int err) l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_DISCONN_REQ, sizeof(req), &req); - lock_sock(sk); - __l2cap_state_change(chan, BT_DISCONN); - __l2cap_chan_set_err(chan, err); - release_sock(sk); + l2cap_state_change_and_error(chan, BT_DISCONN, err); } /* ---- L2CAP connections ---- */ @@ -1225,8 +1251,6 @@ static void l2cap_conn_start(struct l2cap_conn *conn) mutex_lock(&conn->chan_lock); list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) { - struct sock *sk = chan->sk; - l2cap_chan_lock(chan); if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { @@ -1258,19 +1282,16 @@ static void l2cap_conn_start(struct l2cap_conn *conn) rsp.dcid = cpu_to_le16(chan->scid); if (l2cap_chan_check_security(chan)) { - lock_sock(sk); - if (test_bit(BT_SK_DEFER_SETUP, - &bt_sk(sk)->flags)) { + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND); rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHOR_PEND); chan->ops->defer(chan); } else { - __l2cap_state_change(chan, BT_CONFIG); + l2cap_state_change(chan, BT_CONFIG); rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); } - release_sock(sk); } else { rsp.result = __constant_cpu_to_le16(L2CAP_CR_PEND); rsp.status = __constant_cpu_to_le16(L2CAP_CS_AUTHEN_PEND); @@ -1309,8 +1330,6 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid, read_lock(&chan_list_lock); list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - if (state && c->state != state) continue; @@ -1319,16 +1338,16 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid, int src_any, dst_any; /* Exact match. */ - src_match = !bacmp(&bt_sk(sk)->src, src); - dst_match = !bacmp(&bt_sk(sk)->dst, dst); + src_match = !bacmp(&c->src, src); + dst_match = !bacmp(&c->dst, dst); if (src_match && dst_match) { read_unlock(&chan_list_lock); return c; } /* Closest match */ - src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY); - dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY); + src_any = !bacmp(&c->src, BDADDR_ANY); + dst_any = !bacmp(&c->dst, BDADDR_ANY); if ((src_match && dst_any) || (src_any && dst_match) || (src_any && dst_any)) c1 = c; @@ -1342,14 +1361,15 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid, static void l2cap_le_conn_ready(struct l2cap_conn *conn) { - struct sock *parent; + struct hci_conn *hcon = conn->hcon; struct l2cap_chan *chan, *pchan; + u8 dst_type; BT_DBG(""); /* Check if we have socket listening on cid */ pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT, - conn->src, conn->dst); + &hcon->src, &hcon->dst); if (!pchan) return; @@ -1357,9 +1377,13 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) if (__l2cap_get_chan_by_dcid(conn, L2CAP_CID_ATT)) return; - parent = pchan->sk; + dst_type = bdaddr_type(hcon, hcon->dst_type); + + /* If device is blocked, do not create a channel for it */ + if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type)) + return; - lock_sock(parent); + l2cap_chan_lock(pchan); chan = pchan->ops->new_connection(pchan); if (!chan) @@ -1367,13 +1391,15 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) chan->dcid = L2CAP_CID_ATT; - bacpy(&bt_sk(chan->sk)->src, conn->src); - bacpy(&bt_sk(chan->sk)->dst, conn->dst); + bacpy(&chan->src, &hcon->src); + bacpy(&chan->dst, &hcon->dst); + chan->src_type = bdaddr_type(hcon, hcon->src_type); + chan->dst_type = dst_type; __l2cap_chan_add(conn, chan); clean: - release_sock(parent); + l2cap_chan_unlock(pchan); } static void l2cap_conn_ready(struct l2cap_conn *conn) @@ -1408,12 +1434,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) l2cap_chan_ready(chan); } else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { - struct sock *sk = chan->sk; - __clear_chan_timer(chan); - lock_sock(sk); - __l2cap_state_change(chan, BT_CONNECTED); - sk->sk_state_change(sk); - release_sock(sk); + l2cap_chan_ready(chan); } else if (chan->state == BT_CONNECT) { l2cap_do_start(chan); @@ -1633,11 +1654,12 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) break; } - conn->src = &hcon->hdev->bdaddr; - conn->dst = &hcon->dst; - conn->feat_mask = 0; + if (hcon->type == ACL_LINK) + conn->hs_enabled = test_bit(HCI_HS_ENABLED, + &hcon->hdev->dev_flags); + spin_lock_init(&conn->lock); mutex_init(&conn->chan_lock); @@ -1688,8 +1710,6 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, read_lock(&chan_list_lock); list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - if (state && c->state != state) continue; @@ -1698,16 +1718,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, int src_any, dst_any; /* Exact match. */ - src_match = !bacmp(&bt_sk(sk)->src, src); - dst_match = !bacmp(&bt_sk(sk)->dst, dst); + src_match = !bacmp(&c->src, src); + dst_match = !bacmp(&c->dst, dst); if (src_match && dst_match) { read_unlock(&chan_list_lock); return c; } /* Closest match */ - src_any = !bacmp(&bt_sk(sk)->src, BDADDR_ANY); - dst_any = !bacmp(&bt_sk(sk)->dst, BDADDR_ANY); + src_any = !bacmp(&c->src, BDADDR_ANY); + dst_any = !bacmp(&c->dst, BDADDR_ANY); if ((src_match && dst_any) || (src_any && dst_match) || (src_any && dst_any)) c1 = c; @@ -1722,18 +1742,16 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm, int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type) { - struct sock *sk = chan->sk; - bdaddr_t *src = &bt_sk(sk)->src; struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; __u8 auth_type; int err; - BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", src, dst, + BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst, dst_type, __le16_to_cpu(psm)); - hdev = hci_get_route(dst, src); + hdev = hci_get_route(dst, &chan->src); if (!hdev) return -EHOSTUNREACH; @@ -1790,9 +1808,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, } /* Set destination address and psm */ - lock_sock(sk); - bacpy(&bt_sk(sk)->dst, dst); - release_sock(sk); + bacpy(&chan->dst, dst); + chan->dst_type = dst_type; chan->psm = psm; chan->dcid = cid; @@ -1825,7 +1842,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, } /* Update source addr of the socket */ - bacpy(src, conn->src); + bacpy(&chan->src, &hcon->src); + chan->src_type = bdaddr_type(hcon, hcon->src_type); l2cap_chan_unlock(chan); l2cap_chan_add(conn, chan); @@ -1835,7 +1853,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, hci_conn_drop(hcon); l2cap_state_change(chan, BT_CONNECT); - __set_chan_timer(chan, sk->sk_sndtimeo); + __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); if (hcon->state == BT_CONNECTED) { if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) { @@ -1855,38 +1873,6 @@ done: return err; } -int __l2cap_wait_ack(struct sock *sk) -{ - struct l2cap_chan *chan = l2cap_pi(sk)->chan; - DECLARE_WAITQUEUE(wait, current); - int err = 0; - int timeo = HZ/5; - - add_wait_queue(sk_sleep(sk), &wait); - set_current_state(TASK_INTERRUPTIBLE); - while (chan->unacked_frames > 0 && chan->conn) { - if (!timeo) - timeo = HZ/5; - - if (signal_pending(current)) { - err = sock_intr_errno(timeo); - break; - } - - release_sock(sk); - timeo = schedule_timeout(timeo); - lock_sock(sk); - set_current_state(TASK_INTERRUPTIBLE); - - err = sock_error(sk); - if (err) - break; - } - set_current_state(TASK_RUNNING); - remove_wait_queue(sk_sleep(sk), &wait); - return err; -} - static void l2cap_monitor_timeout(struct work_struct *work) { struct l2cap_chan *chan = container_of(work, struct l2cap_chan, @@ -2263,7 +2249,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE; struct l2cap_hdr *lh; - BT_DBG("chan %p len %zu priority %u", chan, len, priority); + BT_DBG("chan %p psm 0x%2.2x len %zu priority %u", chan, + __le16_to_cpu(chan->psm), len, priority); count = min_t(unsigned int, (conn->mtu - hlen), len); @@ -2278,7 +2265,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); lh->len = cpu_to_le16(len + L2CAP_PSMLEN_SIZE); - put_unaligned(chan->psm, skb_put(skb, L2CAP_PSMLEN_SIZE)); + put_unaligned(chan->psm, (__le16 *) skb_put(skb, L2CAP_PSMLEN_SIZE)); err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb); if (unlikely(err < 0)) { @@ -2826,17 +2813,16 @@ static void l2cap_raw_recv(struct l2cap_conn *conn, struct sk_buff *skb) mutex_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { - struct sock *sk = chan->sk; if (chan->chan_type != L2CAP_CHAN_RAW) continue; - /* Don't send frame to the socket it came from */ - if (skb->sk == sk) + /* Don't send frame to the channel it came from */ + if (bt_cb(skb)->chan == chan) continue; + nskb = skb_clone(skb, GFP_KERNEL); if (!nskb) continue; - if (chan->ops->recv(chan, nskb)) kfree_skb(nskb); } @@ -3043,8 +3029,8 @@ int l2cap_ertm_init(struct l2cap_chan *chan) skb_queue_head_init(&chan->tx_q); - chan->local_amp_id = 0; - chan->move_id = 0; + chan->local_amp_id = AMP_ID_BREDR; + chan->move_id = AMP_ID_BREDR; chan->move_state = L2CAP_MOVE_STABLE; chan->move_role = L2CAP_MOVE_ROLE_NONE; @@ -3084,20 +3070,20 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) } } -static inline bool __l2cap_ews_supported(struct l2cap_chan *chan) +static inline bool __l2cap_ews_supported(struct l2cap_conn *conn) { - return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW; + return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_WINDOW; } -static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) +static inline bool __l2cap_efs_supported(struct l2cap_conn *conn) { - return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW; + return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_FLOW; } static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan, struct l2cap_conf_rfc *rfc) { - if (chan->local_amp_id && chan->hs_hcon) { + if (chan->local_amp_id != AMP_ID_BREDR && chan->hs_hcon) { u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to; /* Class 1 devices have must have ERTM timeouts @@ -3135,7 +3121,7 @@ static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan, static inline void l2cap_txwin_setup(struct l2cap_chan *chan) { if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && - __l2cap_ews_supported(chan)) { + __l2cap_ews_supported(chan->conn)) { /* use extended control field */ set_bit(FLAG_EXT_CTRL, &chan->flags); chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; @@ -3165,7 +3151,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) break; - if (__l2cap_efs_supported(chan)) + if (__l2cap_efs_supported(chan->conn)) set_bit(FLAG_EFS_ENABLE, &chan->flags); /* fall through */ @@ -3317,7 +3303,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) break; case L2CAP_CONF_EWS: - if (!enable_hs) + if (!chan->conn->hs_enabled) return -ECONNREFUSED; set_bit(FLAG_EXT_CTRL, &chan->flags); @@ -3349,7 +3335,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) } if (remote_efs) { - if (__l2cap_efs_supported(chan)) + if (__l2cap_efs_supported(chan->conn)) set_bit(FLAG_EFS_ENABLE, &chan->flags); else return -ECONNREFUSED; @@ -3715,7 +3701,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_rsp rsp; struct l2cap_chan *chan = NULL, *pchan; - struct sock *parent, *sk = NULL; int result, status = L2CAP_CS_NO_INFO; u16 dcid = 0, scid = __le16_to_cpu(req->scid); @@ -3724,16 +3709,15 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, BT_DBG("psm 0x%2.2x scid 0x%4.4x", __le16_to_cpu(psm), scid); /* Check if we have socket listening on psm */ - pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, conn->src, conn->dst); + pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src, + &conn->hcon->dst); if (!pchan) { result = L2CAP_CR_BAD_PSM; goto sendresp; } - parent = pchan->sk; - mutex_lock(&conn->chan_lock); - lock_sock(parent); + l2cap_chan_lock(pchan); /* Check if the ACL is secure enough (if not SDP) */ if (psm != __constant_cpu_to_le16(L2CAP_PSM_SDP) && @@ -3753,8 +3737,6 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, if (!chan) goto response; - sk = chan->sk; - /* For certain devices (ex: HID mouse), support for authentication, * pairing and bonding is optional. For such devices, inorder to avoid * the ACL alive for too long after L2CAP disconnection, reset the ACL @@ -3762,8 +3744,10 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, */ conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT; - bacpy(&bt_sk(sk)->src, conn->src); - bacpy(&bt_sk(sk)->dst, conn->dst); + bacpy(&chan->src, &conn->hcon->src); + bacpy(&chan->dst, &conn->hcon->dst); + chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type); + chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type); chan->psm = psm; chan->dcid = scid; chan->local_amp_id = amp_id; @@ -3772,14 +3756,14 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, dcid = chan->scid; - __set_chan_timer(chan, sk->sk_sndtimeo); + __set_chan_timer(chan, chan->ops->get_sndtimeo(chan)); chan->ident = cmd->ident; if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) { if (l2cap_chan_check_security(chan)) { - if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) { - __l2cap_state_change(chan, BT_CONNECT2); + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { + l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHOR_PEND; chan->ops->defer(chan); @@ -3788,28 +3772,28 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, * The connection will succeed after the * physical link is up. */ - if (amp_id) { - __l2cap_state_change(chan, BT_CONNECT2); - result = L2CAP_CR_PEND; - } else { - __l2cap_state_change(chan, BT_CONFIG); + if (amp_id == AMP_ID_BREDR) { + l2cap_state_change(chan, BT_CONFIG); result = L2CAP_CR_SUCCESS; + } else { + l2cap_state_change(chan, BT_CONNECT2); + result = L2CAP_CR_PEND; } status = L2CAP_CS_NO_INFO; } } else { - __l2cap_state_change(chan, BT_CONNECT2); + l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_AUTHEN_PEND; } } else { - __l2cap_state_change(chan, BT_CONNECT2); + l2cap_state_change(chan, BT_CONNECT2); result = L2CAP_CR_PEND; status = L2CAP_CS_NO_INFO; } response: - release_sock(parent); + l2cap_chan_unlock(pchan); mutex_unlock(&conn->chan_lock); sendresp: @@ -3891,13 +3875,13 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn, if (scid) { chan = __l2cap_get_chan_by_scid(conn, scid); if (!chan) { - err = -EFAULT; + err = -EBADSLT; goto unlock; } } else { chan = __l2cap_get_chan_by_ident(conn, cmd->ident); if (!chan) { - err = -EFAULT; + err = -EBADSLT; goto unlock; } } @@ -3965,6 +3949,18 @@ static void l2cap_send_efs_conf_rsp(struct l2cap_chan *chan, void *data, L2CAP_CONF_SUCCESS, flags), data); } +static void cmd_reject_invalid_cid(struct l2cap_conn *conn, u8 ident, + u16 scid, u16 dcid) +{ + struct l2cap_cmd_rej_cid rej; + + rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID); + rej.scid = __cpu_to_le16(scid); + rej.dcid = __cpu_to_le16(dcid); + + l2cap_send_cmd(conn, ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); +} + static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) @@ -3984,18 +3980,14 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, BT_DBG("dcid 0x%4.4x flags 0x%2.2x", dcid, flags); chan = l2cap_get_chan_by_scid(conn, dcid); - if (!chan) - return -ENOENT; + if (!chan) { + cmd_reject_invalid_cid(conn, cmd->ident, dcid, 0); + return 0; + } if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2) { - struct l2cap_cmd_rej_cid rej; - - rej.reason = __constant_cpu_to_le16(L2CAP_REJ_INVALID_CID); - rej.scid = cpu_to_le16(chan->scid); - rej.dcid = cpu_to_le16(chan->dcid); - - l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, - sizeof(rej), &rej); + cmd_reject_invalid_cid(conn, cmd->ident, chan->scid, + chan->dcid); goto unlock; } @@ -4198,7 +4190,6 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_disconn_rsp rsp; u16 dcid, scid; struct l2cap_chan *chan; - struct sock *sk; if (cmd_len != sizeof(*req)) return -EPROTO; @@ -4213,20 +4204,17 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, chan = __l2cap_get_chan_by_scid(conn, dcid); if (!chan) { mutex_unlock(&conn->chan_lock); + cmd_reject_invalid_cid(conn, cmd->ident, dcid, scid); return 0; } l2cap_chan_lock(chan); - sk = chan->sk; - rsp.dcid = cpu_to_le16(chan->scid); rsp.scid = cpu_to_le16(chan->dcid); l2cap_send_cmd(conn, cmd->ident, L2CAP_DISCONN_RSP, sizeof(rsp), &rsp); - lock_sock(sk); - sk->sk_shutdown = SHUTDOWN_MASK; - release_sock(sk); + chan->ops->set_shutdown(chan); l2cap_chan_hold(chan); l2cap_chan_del(chan, ECONNRESET); @@ -4303,7 +4291,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, if (!disable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING | L2CAP_FEAT_FCS; - if (enable_hs) + if (conn->hs_enabled) feat_mask |= L2CAP_FEAT_EXT_FLOW | L2CAP_FEAT_EXT_WINDOW; @@ -4314,7 +4302,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, u8 buf[12]; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; - if (enable_hs) + if (conn->hs_enabled) l2cap_fixed_chan[0] |= L2CAP_FC_A2MP; else l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP; @@ -4411,7 +4399,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, if (cmd_len != sizeof(*req)) return -EPROTO; - if (!enable_hs) + if (!conn->hs_enabled) return -EINVAL; psm = le16_to_cpu(req->psm); @@ -4420,7 +4408,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id); /* For controller id 0 make BR/EDR connection */ - if (req->amp_id == HCI_BREDR_ID) { + if (req->amp_id == AMP_ID_BREDR) { l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP, req->amp_id); return 0; @@ -4442,10 +4430,13 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn, struct amp_mgr *mgr = conn->hcon->amp_mgr; struct hci_conn *hs_hcon; - hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst); + hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, + &conn->hcon->dst); if (!hs_hcon) { hci_dev_put(hdev); - return -EFAULT; + cmd_reject_invalid_cid(conn, cmd->ident, chan->scid, + chan->dcid); + return 0; } BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon); @@ -4469,7 +4460,7 @@ error: l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, sizeof(rsp), &rsp); - return -EFAULT; + return 0; } static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id) @@ -4655,7 +4646,7 @@ void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, if (chan->state != BT_CONNECTED) { /* Ignore logical link if channel is on BR/EDR */ - if (chan->local_amp_id) + if (chan->local_amp_id != AMP_ID_BREDR) l2cap_logical_finish_create(chan, hchan); } else { l2cap_logical_finish_move(chan, hchan); @@ -4666,7 +4657,7 @@ void l2cap_move_start(struct l2cap_chan *chan) { BT_DBG("chan %p", chan); - if (chan->local_amp_id == HCI_BREDR_ID) { + if (chan->local_amp_id == AMP_ID_BREDR) { if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED) return; chan->move_role = L2CAP_MOVE_ROLE_INITIATOR; @@ -4723,7 +4714,7 @@ static void l2cap_do_create(struct l2cap_chan *chan, int result, sizeof(rsp), &rsp); if (result == L2CAP_CR_SUCCESS) { - __l2cap_state_change(chan, BT_CONFIG); + l2cap_state_change(chan, BT_CONFIG); set_bit(CONF_REQ_SENT, &chan->conf_state); l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn), L2CAP_CONF_REQ, @@ -4838,7 +4829,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id); - if (!enable_hs) + if (!conn->hs_enabled) return -EINVAL; chan = l2cap_get_chan_by_dcid(conn, icid); @@ -4865,7 +4856,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, goto send_move_response; } - if (req->dest_amp_id) { + if (req->dest_amp_id != AMP_ID_BREDR) { struct hci_dev *hdev; hdev = hci_dev_get(req->dest_amp_id); if (!hdev || hdev->dev_type != HCI_AMP || @@ -4885,7 +4876,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, */ if ((__chan_is_moving(chan) || chan->move_role != L2CAP_MOVE_ROLE_NONE) && - bacmp(conn->src, conn->dst) > 0) { + bacmp(&conn->hcon->src, &conn->hcon->dst) > 0) { result = L2CAP_MR_COLLISION; goto send_move_response; } @@ -4895,7 +4886,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, chan->move_id = req->dest_amp_id; icid = chan->dcid; - if (!req->dest_amp_id) { + if (req->dest_amp_id == AMP_ID_BREDR) { /* Moving to BR/EDR */ if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY; @@ -5087,7 +5078,7 @@ static int l2cap_move_channel_confirm(struct l2cap_conn *conn, if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) { if (result == L2CAP_MC_CONFIRMED) { chan->local_amp_id = chan->move_id; - if (!chan->local_amp_id) + if (chan->local_amp_id == AMP_ID_BREDR) __release_logical_link(chan); } else { chan->move_id = chan->local_amp_id; @@ -5127,7 +5118,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) { chan->local_amp_id = chan->move_id; - if (!chan->local_amp_id && chan->hs_hchan) + if (chan->local_amp_id == AMP_ID_BREDR && chan->hs_hchan) __release_logical_link(chan); l2cap_move_done(chan); @@ -5219,7 +5210,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, case L2CAP_CONN_RSP: case L2CAP_CREATE_CHAN_RSP: - err = l2cap_connect_create_rsp(conn, cmd, cmd_len, data); + l2cap_connect_create_rsp(conn, cmd, cmd_len, data); break; case L2CAP_CONF_REQ: @@ -5227,7 +5218,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, break; case L2CAP_CONF_RSP: - err = l2cap_config_rsp(conn, cmd, cmd_len, data); + l2cap_config_rsp(conn, cmd, cmd_len, data); break; case L2CAP_DISCONN_REQ: @@ -5235,7 +5226,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, break; case L2CAP_DISCONN_RSP: - err = l2cap_disconnect_rsp(conn, cmd, cmd_len, data); + l2cap_disconnect_rsp(conn, cmd, cmd_len, data); break; case L2CAP_ECHO_REQ: @@ -5250,7 +5241,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, break; case L2CAP_INFO_RSP: - err = l2cap_information_rsp(conn, cmd, cmd_len, data); + l2cap_information_rsp(conn, cmd, cmd_len, data); break; case L2CAP_CREATE_CHAN_REQ: @@ -5262,7 +5253,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, break; case L2CAP_MOVE_CHAN_RSP: - err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data); + l2cap_move_channel_rsp(conn, cmd, cmd_len, data); break; case L2CAP_MOVE_CHAN_CFM: @@ -5270,7 +5261,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, break; case L2CAP_MOVE_CHAN_CFM_RSP: - err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data); + l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data); break; default: @@ -5304,51 +5295,48 @@ static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn, static inline void l2cap_le_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) { - u8 *data = skb->data; - int len = skb->len; - struct l2cap_cmd_hdr cmd; + struct hci_conn *hcon = conn->hcon; + struct l2cap_cmd_hdr *cmd; + u16 len; int err; - l2cap_raw_recv(conn, skb); + if (hcon->type != LE_LINK) + goto drop; - while (len >= L2CAP_CMD_HDR_SIZE) { - u16 cmd_len; - memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE); - data += L2CAP_CMD_HDR_SIZE; - len -= L2CAP_CMD_HDR_SIZE; + if (skb->len < L2CAP_CMD_HDR_SIZE) + goto drop; - cmd_len = le16_to_cpu(cmd.len); + cmd = (void *) skb->data; + skb_pull(skb, L2CAP_CMD_HDR_SIZE); - BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd.code, cmd_len, - cmd.ident); + len = le16_to_cpu(cmd->len); - if (cmd_len > len || !cmd.ident) { - BT_DBG("corrupted command"); - break; - } + BT_DBG("code 0x%2.2x len %d id 0x%2.2x", cmd->code, len, cmd->ident); - err = l2cap_le_sig_cmd(conn, &cmd, data); - if (err) { - struct l2cap_cmd_rej_unk rej; + if (len != skb->len || !cmd->ident) { + BT_DBG("corrupted command"); + goto drop; + } - BT_ERR("Wrong link type (%d)", err); + err = l2cap_le_sig_cmd(conn, cmd, skb->data); + if (err) { + struct l2cap_cmd_rej_unk rej; - /* FIXME: Map err to a valid reason */ - rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); - l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, - sizeof(rej), &rej); - } + BT_ERR("Wrong link type (%d)", err); - data += cmd_len; - len -= cmd_len; + rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); + l2cap_send_cmd(conn, cmd->ident, L2CAP_COMMAND_REJ, + sizeof(rej), &rej); } +drop: kfree_skb(skb); } static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) { + struct hci_conn *hcon = conn->hcon; u8 *data = skb->data; int len = skb->len; struct l2cap_cmd_hdr cmd; @@ -5356,6 +5344,9 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, l2cap_raw_recv(conn, skb); + if (hcon->type != ACL_LINK) + goto drop; + while (len >= L2CAP_CMD_HDR_SIZE) { u16 cmd_len; memcpy(&cmd, data, L2CAP_CMD_HDR_SIZE); @@ -5378,7 +5369,6 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, BT_ERR("Wrong link type (%d)", err); - /* FIXME: Map err to a valid reason */ rej.reason = __constant_cpu_to_le16(L2CAP_REJ_NOT_UNDERSTOOD); l2cap_send_cmd(conn, cmd.ident, L2CAP_COMMAND_REJ, sizeof(rej), &rej); @@ -5388,6 +5378,7 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, len -= cmd_len; } +drop: kfree_skb(skb); } @@ -5784,7 +5775,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, struct sk_buff *skb, u8 event) { int err = 0; - bool skb_in_use = 0; + bool skb_in_use = false; BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb, event); @@ -5805,7 +5796,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, control->txseq); chan->buffer_seq = chan->expected_tx_seq; - skb_in_use = 1; + skb_in_use = true; err = l2cap_reassemble_sdu(chan, skb, control); if (err) @@ -5841,7 +5832,7 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, * current frame is stored for later use. */ skb_queue_tail(&chan->srej_q, skb); - skb_in_use = 1; + skb_in_use = true; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); @@ -5919,7 +5910,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, { int err = 0; u16 txseq = control->txseq; - bool skb_in_use = 0; + bool skb_in_use = false; BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb, event); @@ -5931,7 +5922,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, /* Keep frame for reassembly later */ l2cap_pass_to_tx(chan, control); skb_queue_tail(&chan->srej_q, skb); - skb_in_use = 1; + skb_in_use = true; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); @@ -5942,7 +5933,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, l2cap_pass_to_tx(chan, control); skb_queue_tail(&chan->srej_q, skb); - skb_in_use = 1; + skb_in_use = true; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); @@ -5957,7 +5948,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, * the missing frames. */ skb_queue_tail(&chan->srej_q, skb); - skb_in_use = 1; + skb_in_use = true; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); @@ -5971,7 +5962,7 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, * SREJ'd frames. */ skb_queue_tail(&chan->srej_q, skb); - skb_in_use = 1; + skb_in_use = true; BT_DBG("Queued %p (queue len %d)", skb, skb_queue_len(&chan->srej_q)); @@ -6380,9 +6371,13 @@ done: static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, struct sk_buff *skb) { + struct hci_conn *hcon = conn->hcon; struct l2cap_chan *chan; - chan = l2cap_global_chan_by_psm(0, psm, conn->src, conn->dst); + if (hcon->type != ACL_LINK) + goto drop; + + chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst); if (!chan) goto drop; @@ -6394,6 +6389,10 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm, if (chan->imtu < skb->len) goto drop; + /* Store remote BD_ADDR and PSM for msg_name */ + bacpy(&bt_cb(skb)->bdaddr, &hcon->dst); + bt_cb(skb)->psm = psm; + if (!chan->ops->recv(chan, skb)) return; @@ -6404,15 +6403,22 @@ drop: static void l2cap_att_channel(struct l2cap_conn *conn, struct sk_buff *skb) { + struct hci_conn *hcon = conn->hcon; struct l2cap_chan *chan; + if (hcon->type != LE_LINK) + goto drop; + chan = l2cap_global_chan_by_scid(BT_CONNECTED, L2CAP_CID_ATT, - conn->src, conn->dst); + &hcon->src, &hcon->dst); if (!chan) goto drop; BT_DBG("chan %p, len %d", chan, skb->len); + if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type)) + goto drop; + if (chan->imtu < skb->len) goto drop; @@ -6441,9 +6447,6 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) BT_DBG("len %d, cid 0x%4.4x", len, cid); switch (cid) { - case L2CAP_CID_LE_SIGNALING: - l2cap_le_sig_channel(conn, skb); - break; case L2CAP_CID_SIGNALING: l2cap_sig_channel(conn, skb); break; @@ -6458,6 +6461,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) l2cap_att_channel(conn, skb); break; + case L2CAP_CID_LE_SIGNALING: + l2cap_le_sig_channel(conn, skb); + break; + case L2CAP_CID_SMP: if (smp_sig_channel(conn, skb)) l2cap_conn_del(conn->hcon, EACCES); @@ -6481,17 +6488,15 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr) /* Find listening sockets and check their link_mode */ read_lock(&chan_list_lock); list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - if (c->state != BT_LISTEN) continue; - if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { + if (!bacmp(&c->src, &hdev->bdaddr)) { lm1 |= HCI_LM_ACCEPT; if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) lm1 |= HCI_LM_MASTER; exact++; - } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { + } else if (!bacmp(&c->src, BDADDR_ANY)) { lm2 |= HCI_LM_ACCEPT; if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) lm2 |= HCI_LM_MASTER; @@ -6597,11 +6602,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (!status && (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)) { - struct sock *sk = chan->sk; - - clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); - sk->sk_state_change(sk); - + chan->ops->resume(chan); l2cap_check_encryption(chan, encrypt); l2cap_chan_unlock(chan); continue; @@ -6614,32 +6615,26 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); } } else if (chan->state == BT_CONNECT2) { - struct sock *sk = chan->sk; struct l2cap_conn_rsp rsp; __u16 res, stat; - lock_sock(sk); - if (!status) { - if (test_bit(BT_SK_DEFER_SETUP, - &bt_sk(sk)->flags)) { + if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) { res = L2CAP_CR_PEND; stat = L2CAP_CS_AUTHOR_PEND; chan->ops->defer(chan); } else { - __l2cap_state_change(chan, BT_CONFIG); + l2cap_state_change(chan, BT_CONFIG); res = L2CAP_CR_SUCCESS; stat = L2CAP_CS_NO_INFO; } } else { - __l2cap_state_change(chan, BT_DISCONN); + l2cap_state_change(chan, BT_DISCONN); __set_chan_timer(chan, L2CAP_DISC_TIMEOUT); res = L2CAP_CR_SEC_BLOCK; stat = L2CAP_CS_NO_INFO; } - release_sock(sk); - rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); rsp.result = cpu_to_le16(res); @@ -6756,9 +6751,13 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags) conn->rx_len -= skb->len; if (!conn->rx_len) { - /* Complete frame received */ - l2cap_recv_frame(conn, conn->rx_skb); + /* Complete frame received. l2cap_recv_frame + * takes ownership of the skb so set the global + * rx_skb pointer to NULL first. + */ + struct sk_buff *rx_skb = conn->rx_skb; conn->rx_skb = NULL; + l2cap_recv_frame(conn, rx_skb); } break; } @@ -6775,10 +6774,8 @@ static int l2cap_debugfs_show(struct seq_file *f, void *p) read_lock(&chan_list_lock); list_for_each_entry(c, &chan_list, global_l) { - struct sock *sk = c->sk; - seq_printf(f, "%pMR %pMR %d %d 0x%4.4x 0x%4.4x %d %d %d %d\n", - &bt_sk(sk)->src, &bt_sk(sk)->dst, + &c->src, &c->dst, c->state, __le16_to_cpu(c->psm), c->scid, c->dcid, c->imtu, c->omtu, c->sec_level, c->mode); @@ -6811,12 +6808,11 @@ int __init l2cap_init(void) if (err < 0) return err; - if (bt_debugfs) { - l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs, - NULL, &l2cap_debugfs_fops); - if (!l2cap_debugfs) - BT_ERR("Failed to create L2CAP debug file"); - } + if (IS_ERR_OR_NULL(bt_debugfs)) + return 0; + + l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs, + NULL, &l2cap_debugfs_fops); return 0; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 0098af80b21327045c99a38771ed8c95da2544c2..7cc24d263caaab45af9d8bbe5be09ebda20e87a4 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -32,7 +32,8 @@ #include #include #include -#include + +#include "smp.h" static struct bt_sock_list l2cap_sk_list = { .lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock) @@ -68,6 +69,18 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) if (la.l2_cid && la.l2_psm) return -EINVAL; + if (!bdaddr_type_is_valid(la.l2_bdaddr_type)) + return -EINVAL; + + if (bdaddr_type_is_le(la.l2_bdaddr_type)) { + /* Connection oriented channels are not supported on LE */ + if (la.l2_psm) + return -EINVAL; + /* We only allow ATT user space socket */ + if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT)) + return -EINVAL; + } + lock_sock(sk); if (sk->sk_state != BT_OPEN) { @@ -99,11 +112,20 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen) if (err < 0) goto done; - if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP || - __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM) - chan->sec_level = BT_SECURITY_SDP; + switch (chan->chan_type) { + case L2CAP_CHAN_CONN_LESS: + if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_3DSP) + chan->sec_level = BT_SECURITY_SDP; + break; + case L2CAP_CHAN_CONN_ORIENTED: + if (__le16_to_cpu(la.l2_psm) == L2CAP_PSM_SDP || + __le16_to_cpu(la.l2_psm) == L2CAP_PSM_RFCOMM) + chan->sec_level = BT_SECURITY_SDP; + break; + } - bacpy(&bt_sk(sk)->src, &la.l2_bdaddr); + bacpy(&chan->src, &la.l2_bdaddr); + chan->src_type = la.l2_bdaddr_type; chan->state = BT_BOUND; sk->sk_state = BT_BOUND; @@ -134,6 +156,47 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, if (la.l2_cid && la.l2_psm) return -EINVAL; + if (!bdaddr_type_is_valid(la.l2_bdaddr_type)) + return -EINVAL; + + /* Check that the socket wasn't bound to something that + * conflicts with the address given to connect(). If chan->src + * is BDADDR_ANY it means bind() was never used, in which case + * chan->src_type and la.l2_bdaddr_type do not need to match. + */ + if (chan->src_type == BDADDR_BREDR && bacmp(&chan->src, BDADDR_ANY) && + bdaddr_type_is_le(la.l2_bdaddr_type)) { + /* Old user space versions will try to incorrectly bind + * the ATT socket using BDADDR_BREDR. We need to accept + * this and fix up the source address type only when + * both the source CID and destination CID indicate + * ATT. Anything else is an invalid combination. + */ + if (chan->scid != L2CAP_CID_ATT || + la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT)) + return -EINVAL; + + /* We don't have the hdev available here to make a + * better decision on random vs public, but since all + * user space versions that exhibit this issue anyway do + * not support random local addresses assuming public + * here is good enough. + */ + chan->src_type = BDADDR_LE_PUBLIC; + } + + if (chan->src_type != BDADDR_BREDR && la.l2_bdaddr_type == BDADDR_BREDR) + return -EINVAL; + + if (bdaddr_type_is_le(la.l2_bdaddr_type)) { + /* Connection oriented channels are not supported on LE */ + if (la.l2_psm) + return -EINVAL; + /* We only allow ATT user space socket */ + if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT)) + return -EINVAL; + } + err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid), &la.l2_bdaddr, la.l2_bdaddr_type); if (err) @@ -265,12 +328,14 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, if (peer) { la->l2_psm = chan->psm; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst); + bacpy(&la->l2_bdaddr, &chan->dst); la->l2_cid = cpu_to_le16(chan->dcid); + la->l2_bdaddr_type = chan->dst_type; } else { la->l2_psm = chan->sport; - bacpy(&la->l2_bdaddr, &bt_sk(sk)->src); + bacpy(&la->l2_bdaddr, &chan->src); la->l2_cid = cpu_to_le16(chan->scid); + la->l2_bdaddr_type = chan->src_type; } return 0; @@ -445,11 +510,6 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, break; case BT_CHANNEL_POLICY: - if (!enable_hs) { - err = -ENOPROTOOPT; - break; - } - if (put_user(chan->chan_policy, (u32 __user *) optval)) err = -EFAULT; break; @@ -665,10 +725,13 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; } - if (opt) + if (opt) { set_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); - else + set_bit(FLAG_DEFER_SETUP, &chan->flags); + } else { clear_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags); + clear_bit(FLAG_DEFER_SETUP, &chan->flags); + } break; case BT_FLUSHABLE: @@ -683,7 +746,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, } if (opt == BT_FLUSHABLE_OFF) { - struct l2cap_conn *conn = chan->conn; + conn = chan->conn; /* proceed further only when we have l2cap_conn and No Flush support in the LM */ if (!conn || !lmp_no_flush_capable(conn->hcon->hdev)) { @@ -720,11 +783,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, break; case BT_CHANNEL_POLICY: - if (!enable_hs) { - err = -ENOPROTOOPT; - break; - } - if (get_user(opt, (u32 __user *) optval)) { err = -EFAULT; break; @@ -777,6 +835,12 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, if (sk->sk_state != BT_CONNECTED) return -ENOTCONN; + lock_sock(sk); + err = bt_sock_wait_ready(sk, msg->msg_flags); + release_sock(sk); + if (err) + return err; + l2cap_chan_lock(chan); err = l2cap_chan_send(chan, msg, len, sk->sk_priority); l2cap_chan_unlock(chan); @@ -799,8 +863,8 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, pi->chan->state = BT_CONFIG; __l2cap_connect_rsp_defer(pi->chan); - release_sock(sk); - return 0; + err = 0; + goto done; } release_sock(sk); @@ -856,6 +920,38 @@ static void l2cap_sock_kill(struct sock *sk) sock_put(sk); } +static int __l2cap_wait_ack(struct sock *sk) +{ + struct l2cap_chan *chan = l2cap_pi(sk)->chan; + DECLARE_WAITQUEUE(wait, current); + int err = 0; + int timeo = HZ/5; + + add_wait_queue(sk_sleep(sk), &wait); + set_current_state(TASK_INTERRUPTIBLE); + while (chan->unacked_frames > 0 && chan->conn) { + if (!timeo) + timeo = HZ/5; + + if (signal_pending(current)) { + err = sock_intr_errno(timeo); + break; + } + + release_sock(sk); + timeo = schedule_timeout(timeo); + lock_sock(sk); + set_current_state(TASK_INTERRUPTIBLE); + + err = sock_error(sk); + if (err) + break; + } + set_current_state(TASK_RUNNING); + remove_wait_queue(sk_sleep(sk), &wait); + return err; +} + static int l2cap_sock_shutdown(struct socket *sock, int how) { struct sock *sk = sock->sk; @@ -946,6 +1042,8 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) { struct sock *sk, *parent = chan->data; + lock_sock(parent); + /* Check for backlog size */ if (sk_acceptq_is_full(parent)) { BT_DBG("backlog full %d", parent->sk_ack_backlog); @@ -963,18 +1061,19 @@ static struct l2cap_chan *l2cap_sock_new_connection_cb(struct l2cap_chan *chan) bt_accept_enqueue(parent, sk); + release_sock(parent); + return l2cap_pi(sk)->chan; } static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { - int err; struct sock *sk = chan->data; - struct l2cap_pinfo *pi = l2cap_pi(sk); + int err; lock_sock(sk); - if (pi->rx_busy_skb) { + if (l2cap_pi(sk)->rx_busy_skb) { err = -ENOMEM; goto done; } @@ -990,9 +1089,9 @@ static int l2cap_sock_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) * acked and reassembled until there is buffer space * available. */ - if (err < 0 && pi->chan->mode == L2CAP_MODE_ERTM) { - pi->rx_busy_skb = skb; - l2cap_chan_busy(pi->chan, 1); + if (err < 0 && chan->mode == L2CAP_MODE_ERTM) { + l2cap_pi(sk)->rx_busy_skb = skb; + l2cap_chan_busy(chan, 1); err = 0; } @@ -1050,26 +1149,33 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err) release_sock(sk); } -static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state) +static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state, + int err) { struct sock *sk = chan->data; sk->sk_state = state; + + if (err) + sk->sk_err = err; } static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, unsigned long len, int nb) { + struct sock *sk = chan->data; struct sk_buff *skb; int err; l2cap_chan_unlock(chan); - skb = bt_skb_send_alloc(chan->sk, len, nb, &err); + skb = bt_skb_send_alloc(sk, len, nb, &err); l2cap_chan_lock(chan); if (!skb) return ERR_PTR(err); + bt_cb(skb)->chan = chan; + return skb; } @@ -1095,11 +1201,39 @@ static void l2cap_sock_ready_cb(struct l2cap_chan *chan) static void l2cap_sock_defer_cb(struct l2cap_chan *chan) { - struct sock *sk = chan->data; - struct sock *parent = bt_sk(sk)->parent; + struct sock *parent, *sk = chan->data; + + lock_sock(sk); + parent = bt_sk(sk)->parent; if (parent) parent->sk_data_ready(parent, 0); + + release_sock(sk); +} + +static void l2cap_sock_resume_cb(struct l2cap_chan *chan) +{ + struct sock *sk = chan->data; + + clear_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags); + sk->sk_state_change(sk); +} + +static void l2cap_sock_set_shutdown_cb(struct l2cap_chan *chan) +{ + struct sock *sk = chan->data; + + lock_sock(sk); + sk->sk_shutdown = SHUTDOWN_MASK; + release_sock(sk); +} + +static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan) +{ + struct sock *sk = chan->data; + + return sk->sk_sndtimeo; } static struct l2cap_ops l2cap_chan_ops = { @@ -1111,6 +1245,9 @@ static struct l2cap_ops l2cap_chan_ops = { .state_change = l2cap_sock_state_change_cb, .ready = l2cap_sock_ready_cb, .defer = l2cap_sock_defer_cb, + .resume = l2cap_sock_resume_cb, + .set_shutdown = l2cap_sock_set_shutdown_cb, + .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, .alloc_skb = l2cap_sock_alloc_skb_cb, }; @@ -1120,6 +1257,7 @@ static void l2cap_sock_destruct(struct sock *sk) if (l2cap_pi(sk)->chan) l2cap_chan_put(l2cap_pi(sk)->chan); + if (l2cap_pi(sk)->rx_busy_skb) { kfree_skb(l2cap_pi(sk)->rx_busy_skb); l2cap_pi(sk)->rx_busy_skb = NULL; @@ -1129,10 +1267,22 @@ static void l2cap_sock_destruct(struct sock *sk) skb_queue_purge(&sk->sk_write_queue); } +static void l2cap_skb_msg_name(struct sk_buff *skb, void *msg_name, + int *msg_namelen) +{ + struct sockaddr_l2 *la = (struct sockaddr_l2 *) msg_name; + + memset(la, 0, sizeof(struct sockaddr_l2)); + la->l2_family = AF_BLUETOOTH; + la->l2_psm = bt_cb(skb)->psm; + bacpy(&la->l2_bdaddr, &bt_cb(skb)->bdaddr); + + *msg_namelen = sizeof(struct sockaddr_l2); +} + static void l2cap_sock_init(struct sock *sk, struct sock *parent) { - struct l2cap_pinfo *pi = l2cap_pi(sk); - struct l2cap_chan *chan = pi->chan; + struct l2cap_chan *chan = l2cap_pi(sk)->chan; BT_DBG("sk %p", sk); @@ -1156,13 +1306,13 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) security_sk_clone(parent, sk); } else { - switch (sk->sk_type) { case SOCK_RAW: chan->chan_type = L2CAP_CHAN_RAW; break; case SOCK_DGRAM: chan->chan_type = L2CAP_CHAN_CONN_LESS; + bt_sk(sk)->skb_msg_name = l2cap_skb_msg_name; break; case SOCK_SEQPACKET: case SOCK_STREAM: @@ -1224,8 +1374,6 @@ static struct sock *l2cap_sock_alloc(struct net *net, struct socket *sock, l2cap_chan_hold(chan); - chan->sk = sk; - l2cap_pi(sk)->chan = chan; return sk; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fedc5399d4658687e7646f3768d8dd47f2017e33..074d83690a414c8e668499d7f7e7c86215c563ea 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -30,12 +30,11 @@ #include #include #include -#include -bool enable_hs; +#include "smp.h" #define MGMT_VERSION 1 -#define MGMT_REVISION 3 +#define MGMT_REVISION 4 static const u16 mgmt_commands[] = { MGMT_OP_READ_INDEX_LIST, @@ -76,6 +75,10 @@ static const u16 mgmt_commands[] = { MGMT_OP_BLOCK_DEVICE, MGMT_OP_UNBLOCK_DEVICE, MGMT_OP_SET_DEVICE_ID, + MGMT_OP_SET_ADVERTISING, + MGMT_OP_SET_BREDR, + MGMT_OP_SET_STATIC_ADDRESS, + MGMT_OP_SET_SCAN_PARAMS, }; static const u16 mgmt_events[] = { @@ -181,11 +184,6 @@ static u8 mgmt_status_table[] = { MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */ }; -bool mgmt_valid_hdev(struct hci_dev *hdev) -{ - return hdev->dev_type == HCI_BREDR; -} - static u8 mgmt_status(u8 hci_status) { if (hci_status < ARRAY_SIZE(mgmt_status_table)) @@ -321,10 +319,8 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, count = 0; list_for_each_entry(d, &hci_dev_list, list) { - if (!mgmt_valid_hdev(d)) - continue; - - count++; + if (d->dev_type == HCI_BREDR) + count++; } rp_len = sizeof(*rp) + (2 * count); @@ -339,11 +335,13 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, if (test_bit(HCI_SETUP, &d->dev_flags)) continue; - if (!mgmt_valid_hdev(d)) + if (test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; - rp->index[count++] = cpu_to_le16(d->id); - BT_DBG("Added hci%u", d->id); + if (d->dev_type == HCI_BREDR) { + rp->index[count++] = cpu_to_le16(d->id); + BT_DBG("Added hci%u", d->id); + } } rp->num_controllers = cpu_to_le16(count); @@ -366,9 +364,6 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_POWERED; settings |= MGMT_SETTING_PAIRABLE; - if (lmp_ssp_capable(hdev)) - settings |= MGMT_SETTING_SSP; - if (lmp_bredr_capable(hdev)) { settings |= MGMT_SETTING_CONNECTABLE; if (hdev->hci_ver >= BLUETOOTH_VER_1_2) @@ -376,13 +371,17 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_DISCOVERABLE; settings |= MGMT_SETTING_BREDR; settings |= MGMT_SETTING_LINK_SECURITY; - } - if (enable_hs) - settings |= MGMT_SETTING_HS; + if (lmp_ssp_capable(hdev)) { + settings |= MGMT_SETTING_SSP; + settings |= MGMT_SETTING_HS; + } + } - if (lmp_le_capable(hdev)) + if (lmp_le_capable(hdev)) { settings |= MGMT_SETTING_LE; + settings |= MGMT_SETTING_ADVERTISING; + } return settings; } @@ -406,7 +405,7 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_PAIRABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_PAIRABLE; - if (lmp_bredr_capable(hdev)) + if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_BREDR; if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) @@ -421,6 +420,9 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_HS_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_HS; + if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + settings |= MGMT_SETTING_ADVERTISING; + return settings; } @@ -534,6 +536,156 @@ static u8 *create_uuid128_list(struct hci_dev *hdev, u8 *data, ptrdiff_t len) return ptr; } +static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) +{ + struct pending_cmd *cmd; + + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + if (cmd->opcode == opcode) + return cmd; + } + + return NULL; +} + +static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) +{ + u8 ad_len = 0; + size_t name_len; + + name_len = strlen(hdev->dev_name); + if (name_len > 0) { + size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2; + + if (name_len > max_len) { + name_len = max_len; + ptr[1] = EIR_NAME_SHORT; + } else + ptr[1] = EIR_NAME_COMPLETE; + + ptr[0] = name_len + 1; + + memcpy(ptr + 2, hdev->dev_name, name_len); + + ad_len += (name_len + 2); + ptr += (name_len + 2); + } + + return ad_len; +} + +static void update_scan_rsp_data(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + struct hci_cp_le_set_scan_rsp_data cp; + u8 len; + + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + return; + + memset(&cp, 0, sizeof(cp)); + + len = create_scan_rsp_data(hdev, cp.data); + + if (hdev->scan_rsp_data_len == len && + memcmp(cp.data, hdev->scan_rsp_data, len) == 0) + return; + + memcpy(hdev->scan_rsp_data, cp.data, sizeof(cp.data)); + hdev->scan_rsp_data_len = len; + + cp.length = len; + + hci_req_add(req, HCI_OP_LE_SET_SCAN_RSP_DATA, sizeof(cp), &cp); +} + +static u8 get_adv_discov_flags(struct hci_dev *hdev) +{ + struct pending_cmd *cmd; + + /* If there's a pending mgmt command the flags will not yet have + * their final values, so check for this first. + */ + cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev); + if (cmd) { + struct mgmt_mode *cp = cmd->param; + if (cp->val == 0x01) + return LE_AD_GENERAL; + else if (cp->val == 0x02) + return LE_AD_LIMITED; + } else { + if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags)) + return LE_AD_LIMITED; + else if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + return LE_AD_GENERAL; + } + + return 0; +} + +static u8 create_adv_data(struct hci_dev *hdev, u8 *ptr) +{ + u8 ad_len = 0, flags = 0; + + flags |= get_adv_discov_flags(hdev); + + if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + if (lmp_le_br_capable(hdev)) + flags |= LE_AD_SIM_LE_BREDR_CTRL; + if (lmp_host_le_br_capable(hdev)) + flags |= LE_AD_SIM_LE_BREDR_HOST; + } else { + flags |= LE_AD_NO_BREDR; + } + + if (flags) { + BT_DBG("adv flags 0x%02x", flags); + + ptr[0] = 2; + ptr[1] = EIR_FLAGS; + ptr[2] = flags; + + ad_len += 3; + ptr += 3; + } + + if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) { + ptr[0] = 2; + ptr[1] = EIR_TX_POWER; + ptr[2] = (u8) hdev->adv_tx_power; + + ad_len += 3; + ptr += 3; + } + + return ad_len; +} + +static void update_adv_data(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + struct hci_cp_le_set_adv_data cp; + u8 len; + + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + return; + + memset(&cp, 0, sizeof(cp)); + + len = create_adv_data(hdev, cp.data); + + if (hdev->adv_data_len == len && + memcmp(cp.data, hdev->adv_data, len) == 0) + return; + + memcpy(hdev->adv_data, cp.data, sizeof(cp.data)); + hdev->adv_data_len = len; + + cp.length = len; + + hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); +} + static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; @@ -632,6 +784,9 @@ static void update_class(struct hci_request *req) if (!hdev_is_powered(hdev)) return; + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + return; + if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) return; @@ -639,6 +794,9 @@ static void update_class(struct hci_request *req) cod[1] = hdev->major_class; cod[2] = get_service_classes(hdev); + if (test_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags)) + cod[1] |= 0x20; + if (memcmp(cod, hdev->dev_class, 3) == 0) return; @@ -763,18 +921,6 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, } } -static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) -{ - struct pending_cmd *cmd; - - list_for_each_entry(cmd, &hdev->mgmt_pending, list) { - if (cmd->opcode == opcode) - return cmd; - } - - return NULL; -} - static void mgmt_pending_remove(struct pending_cmd *cmd) { list_del(&cmd->list); @@ -804,6 +950,12 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); + if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, + MGMT_STATUS_BUSY); + goto failed; + } + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { cancel_delayed_work(&hdev->power_off); @@ -820,12 +972,6 @@ static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, - MGMT_STATUS_BUSY); - goto failed; - } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len); if (!cmd) { err = -ENOMEM; @@ -883,27 +1029,141 @@ static int new_settings(struct hci_dev *hdev, struct sock *skip) return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip); } +struct cmd_lookup { + struct sock *sk; + struct hci_dev *hdev; + u8 mgmt_status; +}; + +static void settings_rsp(struct pending_cmd *cmd, void *data) +{ + struct cmd_lookup *match = data; + + send_settings_rsp(cmd->sk, cmd->opcode, match->hdev); + + list_del(&cmd->list); + + if (match->sk == NULL) { + match->sk = cmd->sk; + sock_hold(match->sk); + } + + mgmt_pending_free(cmd); +} + +static void cmd_status_rsp(struct pending_cmd *cmd, void *data) +{ + u8 *status = data; + + cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); + mgmt_pending_remove(cmd); +} + +static u8 mgmt_bredr_support(struct hci_dev *hdev) +{ + if (!lmp_bredr_capable(hdev)) + return MGMT_STATUS_NOT_SUPPORTED; + else if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + return MGMT_STATUS_REJECTED; + else + return MGMT_STATUS_SUCCESS; +} + +static u8 mgmt_le_support(struct hci_dev *hdev) +{ + if (!lmp_le_capable(hdev)) + return MGMT_STATUS_NOT_SUPPORTED; + else if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + return MGMT_STATUS_REJECTED; + else + return MGMT_STATUS_SUCCESS; +} + +static void set_discoverable_complete(struct hci_dev *hdev, u8 status) +{ + struct pending_cmd *cmd; + struct mgmt_mode *cp; + struct hci_request req; + bool changed; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev); + if (!cmd) + goto unlock; + + if (status) { + u8 mgmt_err = mgmt_status(status); + cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err); + clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + goto remove_cmd; + } + + cp = cmd->param; + if (cp->val) { + changed = !test_and_set_bit(HCI_DISCOVERABLE, + &hdev->dev_flags); + + if (hdev->discov_timeout > 0) { + int to = msecs_to_jiffies(hdev->discov_timeout * 1000); + queue_delayed_work(hdev->workqueue, &hdev->discov_off, + to); + } + } else { + changed = test_and_clear_bit(HCI_DISCOVERABLE, + &hdev->dev_flags); + } + + send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev); + + if (changed) + new_settings(hdev, cmd->sk); + + /* When the discoverable mode gets changed, make sure + * that class of device has the limited discoverable + * bit correctly set. + */ + hci_req_init(&req, hdev); + update_class(&req); + hci_req_run(&req, NULL); + +remove_cmd: + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_set_discoverable *cp = data; struct pending_cmd *cmd; + struct hci_request req; u16 timeout; u8 scan; int err; BT_DBG("request for %s", hdev->name); - if (!lmp_bredr_capable(hdev)) + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) && + !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, - MGMT_STATUS_NOT_SUPPORTED); + MGMT_STATUS_REJECTED); - if (cp->val != 0x00 && cp->val != 0x01) + if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_INVALID_PARAMS); timeout = __le16_to_cpu(cp->timeout); - if (!cp->val && timeout > 0) + + /* Disabling discoverable requires that no timeout is set, + * and enabling limited discoverable requires a timeout. + */ + if ((cp->val == 0x00 && timeout > 0) || + (cp->val == 0x02 && timeout == 0)) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, MGMT_STATUS_INVALID_PARAMS); @@ -931,6 +1191,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, if (!hdev_is_powered(hdev)) { bool changed = false; + /* Setting limited discoverable when powered off is + * not a valid operation since it requires a timeout + * and so no need to check HCI_LIMITED_DISCOVERABLE. + */ if (!!cp->val != test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { change_bit(HCI_DISCOVERABLE, &hdev->dev_flags); changed = true; @@ -946,16 +1210,20 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) { - if (hdev->discov_timeout > 0) { - cancel_delayed_work(&hdev->discov_off); - hdev->discov_timeout = 0; - } + /* If the current mode is the same, then just update the timeout + * value with the new value. And if only the timeout gets updated, + * then no need for any HCI transactions. + */ + if (!!cp->val == test_bit(HCI_DISCOVERABLE, &hdev->dev_flags) && + (cp->val == 0x02) == test_bit(HCI_LIMITED_DISCOVERABLE, + &hdev->dev_flags)) { + cancel_delayed_work(&hdev->discov_off); + hdev->discov_timeout = timeout; - if (cp->val && timeout > 0) { - hdev->discov_timeout = timeout; + if (cp->val && hdev->discov_timeout > 0) { + int to = msecs_to_jiffies(hdev->discov_timeout * 1000); queue_delayed_work(hdev->workqueue, &hdev->discov_off, - msecs_to_jiffies(hdev->discov_timeout * 1000)); + to); } err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); @@ -968,20 +1236,66 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } + /* Cancel any potential discoverable timeout that might be + * still active and store new timeout value. The arming of + * the timeout happens in the complete handler. + */ + cancel_delayed_work(&hdev->discov_off); + hdev->discov_timeout = timeout; + + /* Limited discoverable mode */ + if (cp->val == 0x02) + set_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + else + clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + + hci_req_init(&req, hdev); + + /* The procedure for LE-only controllers is much simpler - just + * update the advertising data. + */ + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + goto update_ad; + scan = SCAN_PAGE; - if (cp->val) + if (cp->val) { + struct hci_cp_write_current_iac_lap hci_cp; + + if (cp->val == 0x02) { + /* Limited discoverable mode */ + hci_cp.num_iac = 2; + hci_cp.iac_lap[0] = 0x00; /* LIAC */ + hci_cp.iac_lap[1] = 0x8b; + hci_cp.iac_lap[2] = 0x9e; + hci_cp.iac_lap[3] = 0x33; /* GIAC */ + hci_cp.iac_lap[4] = 0x8b; + hci_cp.iac_lap[5] = 0x9e; + } else { + /* General discoverable mode */ + hci_cp.num_iac = 1; + hci_cp.iac_lap[0] = 0x33; /* GIAC */ + hci_cp.iac_lap[1] = 0x8b; + hci_cp.iac_lap[2] = 0x9e; + } + + hci_req_add(&req, HCI_OP_WRITE_CURRENT_IAC_LAP, + (hci_cp.num_iac * 3) + 1, &hci_cp); + scan |= SCAN_INQUIRY; - else - cancel_delayed_work(&hdev->discov_off); + } else { + clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + } + + hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); + +update_ad: + update_adv_data(&req); - err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + err = hci_req_run(&req, set_discoverable_complete); if (err < 0) mgmt_pending_remove(cmd); - if (cp->val) - hdev->discov_timeout = timeout; - failed: hci_dev_unlock(hdev); return err; @@ -993,6 +1307,9 @@ static void write_fast_connectable(struct hci_request *req, bool enable) struct hci_cp_write_page_scan_activity acp; u8 type; + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + return; + if (hdev->hci_ver < BLUETOOTH_VER_1_2) return; @@ -1019,9 +1336,55 @@ static void write_fast_connectable(struct hci_request *req, bool enable) hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); } +static u8 get_adv_type(struct hci_dev *hdev) +{ + struct pending_cmd *cmd; + bool connectable; + + /* If there's a pending mgmt command the flag will not yet have + * it's final value, so check for this first. + */ + cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev); + if (cmd) { + struct mgmt_mode *cp = cmd->param; + connectable = !!cp->val; + } else { + connectable = test_bit(HCI_CONNECTABLE, &hdev->dev_flags); + } + + return connectable ? LE_ADV_IND : LE_ADV_NONCONN_IND; +} + +static void enable_advertising(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + struct hci_cp_le_set_adv_param cp; + u8 enable = 0x01; + + memset(&cp, 0, sizeof(cp)); + cp.min_interval = __constant_cpu_to_le16(0x0800); + cp.max_interval = __constant_cpu_to_le16(0x0800); + cp.type = get_adv_type(hdev); + cp.own_address_type = hdev->own_addr_type; + cp.channel_map = 0x07; + + hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp); + + hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); +} + +static void disable_advertising(struct hci_request *req) +{ + u8 enable = 0x00; + + hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); +} + static void set_connectable_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; + struct mgmt_mode *cp; + bool changed; BT_DBG("status 0x%02x", status); @@ -1031,14 +1394,56 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status) if (!cmd) goto unlock; + if (status) { + u8 mgmt_err = mgmt_status(status); + cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err); + goto remove_cmd; + } + + cp = cmd->param; + if (cp->val) + changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags); + else + changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); + send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev); + if (changed) + new_settings(hdev, cmd->sk); + +remove_cmd: mgmt_pending_remove(cmd); unlock: hci_dev_unlock(hdev); } +static int set_connectable_update_settings(struct hci_dev *hdev, + struct sock *sk, u8 val) +{ + bool changed = false; + int err; + + if (!!val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + changed = true; + + if (val) { + set_bit(HCI_CONNECTABLE, &hdev->dev_flags); + } else { + clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); + clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + } + + err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); + if (err < 0) + return err; + + if (changed) + return new_settings(hdev, sk); + + return 0; +} + static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -1050,9 +1455,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); - if (!lmp_bredr_capable(hdev)) + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) && + !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, - MGMT_STATUS_NOT_SUPPORTED); + MGMT_STATUS_REJECTED); if (cp->val != 0x00 && cp->val != 0x01) return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, @@ -1061,25 +1467,7 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { - bool changed = false; - - if (!!cp->val != test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - changed = true; - - if (cp->val) { - set_bit(HCI_CONNECTABLE, &hdev->dev_flags); - } else { - clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); - clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - } - - err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); - if (err < 0) - goto failed; - - if (changed) - err = new_settings(hdev, sk); - + err = set_connectable_update_settings(hdev, sk, cp->val); goto failed; } @@ -1090,30 +1478,37 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - if (!!cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { - err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); - goto failed; - } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; } - if (cp->val) { - scan = SCAN_PAGE; - } else { - scan = 0; + hci_req_init(&req, hdev); - if (test_bit(HCI_ISCAN, &hdev->flags) && - hdev->discov_timeout > 0) - cancel_delayed_work(&hdev->discov_off); - } + /* If BR/EDR is not enabled and we disable advertising as a + * by-product of disabling connectable, we need to update the + * advertising flags. + */ + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + if (!cp->val) { + clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + } + update_adv_data(&req); + } else if (cp->val != test_bit(HCI_PSCAN, &hdev->flags)) { + if (cp->val) { + scan = SCAN_PAGE; + } else { + scan = 0; - hci_req_init(&req, hdev); + if (test_bit(HCI_ISCAN, &hdev->flags) && + hdev->discov_timeout > 0) + cancel_delayed_work(&hdev->discov_off); + } - hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + } /* If we're going from non-connectable to connectable or * vice-versa when fast connectable is enabled ensure that fast @@ -1124,9 +1519,20 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) write_fast_connectable(&req, false); + if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) && + hci_conn_num(hdev, LE_LINK) == 0) { + disable_advertising(&req); + enable_advertising(&req); + } + err = hci_req_run(&req, set_connectable_complete); - if (err < 0) + if (err < 0) { mgmt_pending_remove(cmd); + if (err == -ENODATA) + err = set_connectable_update_settings(hdev, sk, + cp->val); + goto failed; + } failed: hci_dev_unlock(hdev); @@ -1137,6 +1543,7 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; + bool changed; int err; BT_DBG("request for %s", hdev->name); @@ -1148,17 +1555,18 @@ static int set_pairable(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); if (cp->val) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); + changed = !test_and_set_bit(HCI_PAIRABLE, &hdev->dev_flags); else - clear_bit(HCI_PAIRABLE, &hdev->dev_flags); + changed = test_and_clear_bit(HCI_PAIRABLE, &hdev->dev_flags); err = send_settings_rsp(sk, MGMT_OP_SET_PAIRABLE, hdev); if (err < 0) - goto failed; + goto unlock; - err = new_settings(hdev, sk); + if (changed) + err = new_settings(hdev, sk); -failed: +unlock: hci_dev_unlock(hdev); return err; } @@ -1168,14 +1576,15 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, { struct mgmt_mode *cp = data; struct pending_cmd *cmd; - u8 val; + u8 val, status; int err; BT_DBG("request for %s", hdev->name); - if (!lmp_bredr_capable(hdev)) + status = mgmt_bredr_support(hdev); + if (status) return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, - MGMT_STATUS_NOT_SUPPORTED); + status); if (cp->val != 0x00 && cp->val != 0x01) return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, @@ -1236,11 +1645,15 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; struct pending_cmd *cmd; - u8 val; + u8 status; int err; BT_DBG("request for %s", hdev->name); + status = mgmt_bredr_support(hdev); + if (status) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, status); + if (!lmp_ssp_capable(hdev)) return cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, MGMT_STATUS_NOT_SUPPORTED); @@ -1251,14 +1664,20 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_dev_lock(hdev); - val = !!cp->val; - if (!hdev_is_powered(hdev)) { - bool changed = false; + bool changed; - if (val != test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { - change_bit(HCI_SSP_ENABLED, &hdev->dev_flags); - changed = true; + if (cp->val) { + changed = !test_and_set_bit(HCI_SSP_ENABLED, + &hdev->dev_flags); + } else { + changed = test_and_clear_bit(HCI_SSP_ENABLED, + &hdev->dev_flags); + if (!changed) + changed = test_and_clear_bit(HCI_HS_ENABLED, + &hdev->dev_flags); + else + clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); } err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); @@ -1271,13 +1690,14 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev)) { + if (mgmt_pending_find(MGMT_OP_SET_SSP, hdev) || + mgmt_pending_find(MGMT_OP_SET_HS, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, MGMT_STATUS_BUSY); goto failed; } - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) == val) { + if (!!cp->val == test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); goto failed; } @@ -1288,7 +1708,7 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto failed; } - err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(val), &val); + err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &cp->val); if (err < 0) { mgmt_pending_remove(cmd); goto failed; @@ -1302,23 +1722,90 @@ failed: static int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; + bool changed; + u8 status; + int err; BT_DBG("request for %s", hdev->name); - if (!enable_hs) + status = mgmt_bredr_support(hdev); + if (status) + return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status); + + if (!lmp_ssp_capable(hdev)) return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_NOT_SUPPORTED); + if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, + MGMT_STATUS_REJECTED); + if (cp->val != 0x00 && cp->val != 0x01) return cmd_status(sk, hdev->id, MGMT_OP_SET_HS, MGMT_STATUS_INVALID_PARAMS); - if (cp->val) - set_bit(HCI_HS_ENABLED, &hdev->dev_flags); - else - clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + hci_dev_lock(hdev); + + if (cp->val) { + changed = !test_and_set_bit(HCI_HS_ENABLED, &hdev->dev_flags); + } else { + if (hdev_is_powered(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_HS, + MGMT_STATUS_REJECTED); + goto unlock; + } + + changed = test_and_clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + } + + err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev); + if (err < 0) + goto unlock; + + if (changed) + err = new_settings(hdev, sk); + +unlock: + hci_dev_unlock(hdev); + return err; +} - return send_settings_rsp(sk, MGMT_OP_SET_HS, hdev); +static void le_enable_complete(struct hci_dev *hdev, u8 status) +{ + struct cmd_lookup match = { NULL, hdev }; + + if (status) { + u8 mgmt_err = mgmt_status(status); + + mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp, + &mgmt_err); + return; + } + + mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match); + + new_settings(hdev, match.sk); + + if (match.sk) + sock_put(match.sk); + + /* Make sure the controller has a good default for + * advertising data. Restrict the update to when LE + * has actually been enabled. During power on, the + * update in powered_update_hci will take care of it. + */ + if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + struct hci_request req; + + hci_dev_lock(hdev); + + hci_req_init(&req, hdev); + update_adv_data(&req); + update_scan_rsp_data(&req); + hci_req_run(&req, NULL); + + hci_dev_unlock(hdev); + } } static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) @@ -1326,6 +1813,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) struct mgmt_mode *cp = data; struct hci_cp_write_le_host_supported hci_cp; struct pending_cmd *cmd; + struct hci_request req; int err; u8 val, enabled; @@ -1340,7 +1828,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) MGMT_STATUS_INVALID_PARAMS); /* LE-only devices do not allow toggling LE on/off */ - if (!lmp_bredr_capable(hdev)) + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_REJECTED); @@ -1357,6 +1845,11 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) changed = true; } + if (!val && test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + changed = true; + } + err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev); if (err < 0) goto unlock; @@ -1367,7 +1860,8 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto unlock; } - if (mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { + if (mgmt_pending_find(MGMT_OP_SET_LE, hdev) || + mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_LE, MGMT_STATUS_BUSY); goto unlock; @@ -1379,15 +1873,22 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto unlock; } + hci_req_init(&req, hdev); + memset(&hci_cp, 0, sizeof(hci_cp)); if (val) { hci_cp.le = val; hci_cp.simul = lmp_le_br_capable(hdev); + } else { + if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + disable_advertising(&req); } - err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), - &hci_cp); + hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), + &hci_cp); + + err = hci_req_run(&req, le_enable_complete); if (err < 0) mgmt_pending_remove(cmd); @@ -1706,6 +2207,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, u16 key_count, expected_len; int i; + BT_DBG("request for %s", hdev->name); + + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_NOT_SUPPORTED); + key_count = __le16_to_cpu(cp->key_count); expected_len = sizeof(*cp) + key_count * @@ -2515,8 +3022,11 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, update_eir(&req); } + /* The name is stored in the scan response data and so + * no need to udpate the advertising data here. + */ if (lmp_le_capable(hdev)) - hci_update_ad(&req); + update_scan_rsp_data(&req); err = hci_req_run(&req, set_name_complete); if (err < 0) @@ -2685,6 +3195,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, struct hci_request req; /* General inquiry access code (GIAC) */ u8 lap[3] = { 0x33, 0x8b, 0x9e }; + u8 status; int err; BT_DBG("%s", hdev->name); @@ -2721,9 +3232,10 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, switch (hdev->discovery.type) { case DISCOV_TYPE_BREDR: - if (!lmp_bredr_capable(hdev)) { + status = mgmt_bredr_support(hdev); + if (status) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_NOT_SUPPORTED); + status); mgmt_pending_remove(cmd); goto failed; } @@ -2745,22 +3257,23 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, case DISCOV_TYPE_LE: case DISCOV_TYPE_INTERLEAVED: - if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + status = mgmt_le_support(hdev); + if (status) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_NOT_SUPPORTED); + status); mgmt_pending_remove(cmd); goto failed; } if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED && - !lmp_bredr_capable(hdev)) { + !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, MGMT_STATUS_NOT_SUPPORTED); mgmt_pending_remove(cmd); goto failed; } - if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) { + if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, MGMT_STATUS_REJECTED); mgmt_pending_remove(cmd); @@ -2778,6 +3291,7 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, param_cp.type = LE_SCAN_ACTIVE; param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT); param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN); + param_cp.own_address_type = hdev->own_addr_type; hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp), ¶m_cp); @@ -3065,6 +3579,186 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, return err; } +static void set_advertising_complete(struct hci_dev *hdev, u8 status) +{ + struct cmd_lookup match = { NULL, hdev }; + + if (status) { + u8 mgmt_err = mgmt_status(status); + + mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, + cmd_status_rsp, &mgmt_err); + return; + } + + mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp, + &match); + + new_settings(hdev, match.sk); + + if (match.sk) + sock_put(match.sk); +} + +static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_mode *cp = data; + struct pending_cmd *cmd; + struct hci_request req; + u8 val, enabled, status; + int err; + + BT_DBG("request for %s", hdev->name); + + status = mgmt_le_support(hdev); + if (status) + return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, + status); + + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + val = !!cp->val; + enabled = test_bit(HCI_ADVERTISING, &hdev->dev_flags); + + /* The following conditions are ones which mean that we should + * not do any HCI communication but directly send a mgmt + * response to user space (after toggling the flag if + * necessary). + */ + if (!hdev_is_powered(hdev) || val == enabled || + hci_conn_num(hdev, LE_LINK) > 0) { + bool changed = false; + + if (val != test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { + change_bit(HCI_ADVERTISING, &hdev->dev_flags); + changed = true; + } + + err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING, hdev); + if (err < 0) + goto unlock; + + if (changed) + err = new_settings(hdev, sk); + + goto unlock; + } + + if (mgmt_pending_find(MGMT_OP_SET_ADVERTISING, hdev) || + mgmt_pending_find(MGMT_OP_SET_LE, hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, + MGMT_STATUS_BUSY); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_req_init(&req, hdev); + + if (val) + enable_advertising(&req); + else + disable_advertising(&req); + + err = hci_req_run(&req, set_advertising_complete); + if (err < 0) + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); + return err; +} + +static int set_static_address(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_static_address *cp = data; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, + MGMT_STATUS_NOT_SUPPORTED); + + if (hdev_is_powered(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, + MGMT_STATUS_REJECTED); + + if (bacmp(&cp->bdaddr, BDADDR_ANY)) { + if (!bacmp(&cp->bdaddr, BDADDR_NONE)) + return cmd_status(sk, hdev->id, + MGMT_OP_SET_STATIC_ADDRESS, + MGMT_STATUS_INVALID_PARAMS); + + /* Two most significant bits shall be set */ + if ((cp->bdaddr.b[5] & 0xc0) != 0xc0) + return cmd_status(sk, hdev->id, + MGMT_OP_SET_STATIC_ADDRESS, + MGMT_STATUS_INVALID_PARAMS); + } + + hci_dev_lock(hdev); + + bacpy(&hdev->static_addr, &cp->bdaddr); + + err = cmd_complete(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, 0, NULL, 0); + + hci_dev_unlock(hdev); + + return err; +} + +static int set_scan_params(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_scan_params *cp = data; + __u16 interval, window; + int err; + + BT_DBG("%s", hdev->name); + + if (!lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, + MGMT_STATUS_NOT_SUPPORTED); + + interval = __le16_to_cpu(cp->interval); + + if (interval < 0x0004 || interval > 0x4000) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, + MGMT_STATUS_INVALID_PARAMS); + + window = __le16_to_cpu(cp->window); + + if (window < 0x0004 || window > 0x4000) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, + MGMT_STATUS_INVALID_PARAMS); + + if (window > interval) + return cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + hdev->le_scan_interval = interval; + hdev->le_scan_window = window; + + err = cmd_complete(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 0, NULL, 0); + + hci_dev_unlock(hdev); + + return err; +} + static void fast_connectable_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; @@ -3108,7 +3802,8 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s", hdev->name); - if (!lmp_bredr_capable(hdev) || hdev->hci_ver < BLUETOOTH_VER_1_2) + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) || + hdev->hci_ver < BLUETOOTH_VER_1_2) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_SUPPORTED); @@ -3124,41 +3819,183 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_REJECTED); - hci_dev_lock(hdev); + hci_dev_lock(hdev); + + if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, + MGMT_STATUS_BUSY); + goto unlock; + } + + if (!!cp->val == test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) { + err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE, + hdev); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev, + data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_req_init(&req, hdev); + + write_fast_connectable(&req, cp->val); + + err = hci_req_run(&req, fast_connectable_complete); + if (err < 0) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, + MGMT_STATUS_FAILED); + mgmt_pending_remove(cmd); + } + +unlock: + hci_dev_unlock(hdev); + + return err; +} + +static void set_bredr_scan(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + u8 scan = 0; + + /* Ensure that fast connectable is disabled. This function will + * not do anything if the page scan parameters are already what + * they should be. + */ + write_fast_connectable(req, false); + + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + scan |= SCAN_PAGE; + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + scan |= SCAN_INQUIRY; + + if (scan) + hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); +} + +static void set_bredr_complete(struct hci_dev *hdev, u8 status) +{ + struct pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = mgmt_pending_find(MGMT_OP_SET_BREDR, hdev); + if (!cmd) + goto unlock; + + if (status) { + u8 mgmt_err = mgmt_status(status); + + /* We need to restore the flag if related HCI commands + * failed. + */ + clear_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + + cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err); + } else { + send_settings_rsp(cmd->sk, MGMT_OP_SET_BREDR, hdev); + new_settings(hdev, cmd->sk); + } + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) +{ + struct mgmt_mode *cp = data; + struct pending_cmd *cmd; + struct hci_request req; + int err; + + BT_DBG("request for %s", hdev->name); + + if (!lmp_bredr_capable(hdev) || !lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, + MGMT_STATUS_NOT_SUPPORTED); + + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, + MGMT_STATUS_REJECTED); + + if (cp->val != 0x00 && cp->val != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, + MGMT_STATUS_INVALID_PARAMS); + + hci_dev_lock(hdev); + + if (cp->val == test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev); + goto unlock; + } + + if (!hdev_is_powered(hdev)) { + if (!cp->val) { + clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags); + clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); + clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + } + + change_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + + err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev); + if (err < 0) + goto unlock; + + err = new_settings(hdev, sk); + goto unlock; + } - if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_BUSY); + /* Reject disabling when powered on */ + if (!cp->val) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, + MGMT_STATUS_REJECTED); goto unlock; } - if (!!cp->val == test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) { - err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE, - hdev); + if (mgmt_pending_find(MGMT_OP_SET_BREDR, hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, + MGMT_STATUS_BUSY); goto unlock; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev, - data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_BREDR, hdev, data, len); if (!cmd) { err = -ENOMEM; goto unlock; } + /* We need to flip the bit already here so that update_adv_data + * generates the correct flags. + */ + set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + hci_req_init(&req, hdev); - write_fast_connectable(&req, cp->val); + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + set_bredr_scan(&req); - err = hci_req_run(&req, fast_connectable_complete); - if (err < 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_FAILED); + /* Since only the advertising data flags will change, there + * is no need to update the scan response data. + */ + update_adv_data(&req); + + err = hci_req_run(&req, set_bredr_complete); + if (err < 0) mgmt_pending_remove(cmd); - } unlock: hci_dev_unlock(hdev); - return err; } @@ -3180,6 +4017,12 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, u16 key_count, expected_len; int i, err; + BT_DBG("request for %s", hdev->name); + + if (!lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, + MGMT_STATUS_NOT_SUPPORTED); + key_count = __le16_to_cpu(cp->key_count); expected_len = sizeof(*cp) + key_count * @@ -3208,15 +4051,19 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, for (i = 0; i < key_count; i++) { struct mgmt_ltk_info *key = &cp->keys[i]; - u8 type; + u8 type, addr_type; + + if (key->addr.type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; if (key->master) type = HCI_SMP_LTK; else type = HCI_SMP_LTK_SLAVE; - hci_add_ltk(hdev, &key->addr.bdaddr, - bdaddr_to_le(key->addr.type), + hci_add_ltk(hdev, &key->addr.bdaddr, addr_type, type, 0, key->authenticated, key->val, key->enc_size, key->ediv, key->rand); } @@ -3276,6 +4123,10 @@ static const struct mgmt_handler { { block_device, false, MGMT_BLOCK_DEVICE_SIZE }, { unblock_device, false, MGMT_UNBLOCK_DEVICE_SIZE }, { set_device_id, false, MGMT_SET_DEVICE_ID_SIZE }, + { set_advertising, false, MGMT_SETTING_SIZE }, + { set_bredr, false, MGMT_SETTING_SIZE }, + { set_static_address, false, MGMT_SET_STATIC_ADDRESS_SIZE }, + { set_scan_params, false, MGMT_SET_SCAN_PARAMS_SIZE }, }; @@ -3320,6 +4171,13 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) MGMT_STATUS_INVALID_INDEX); goto done; } + + if (test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + err = cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } } if (opcode >= ARRAY_SIZE(mgmt_handlers) || @@ -3365,74 +4223,24 @@ done: return err; } -static void cmd_status_rsp(struct pending_cmd *cmd, void *data) -{ - u8 *status = data; - - cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); - mgmt_pending_remove(cmd); -} - -int mgmt_index_added(struct hci_dev *hdev) +void mgmt_index_added(struct hci_dev *hdev) { - if (!mgmt_valid_hdev(hdev)) - return -ENOTSUPP; + if (hdev->dev_type != HCI_BREDR) + return; - return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); + mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); } -int mgmt_index_removed(struct hci_dev *hdev) +void mgmt_index_removed(struct hci_dev *hdev) { u8 status = MGMT_STATUS_INVALID_INDEX; - if (!mgmt_valid_hdev(hdev)) - return -ENOTSUPP; + if (hdev->dev_type != HCI_BREDR) + return; mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); - return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); -} - -struct cmd_lookup { - struct sock *sk; - struct hci_dev *hdev; - u8 mgmt_status; -}; - -static void settings_rsp(struct pending_cmd *cmd, void *data) -{ - struct cmd_lookup *match = data; - - send_settings_rsp(cmd->sk, cmd->opcode, match->hdev); - - list_del(&cmd->list); - - if (match->sk == NULL) { - match->sk = cmd->sk; - sock_hold(match->sk); - } - - mgmt_pending_free(cmd); -} - -static void set_bredr_scan(struct hci_request *req) -{ - struct hci_dev *hdev = req->hdev; - u8 scan = 0; - - /* Ensure that fast connectable is disabled. This function will - * not do anything if the page scan parameters are already what - * they should be. - */ - write_fast_connectable(req, false); - - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - scan |= SCAN_PAGE; - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - scan |= SCAN_INQUIRY; - - if (scan) - hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); } static void powered_complete(struct hci_dev *hdev, u8 status) @@ -3483,13 +4291,33 @@ static int powered_update_hci(struct hci_dev *hdev) sizeof(cp), &cp); } + if (lmp_le_capable(hdev)) { + /* Set random address to static address if configured */ + if (bacmp(&hdev->static_addr, BDADDR_ANY)) + hci_req_add(&req, HCI_OP_LE_SET_RANDOM_ADDR, 6, + &hdev->static_addr); + + /* Make sure the controller has a good default for + * advertising data. This also applies to the case + * where BR/EDR was toggled during the AUTO_OFF phase. + */ + if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + update_adv_data(&req); + update_scan_rsp_data(&req); + } + + if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + enable_advertising(&req); + } + link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags); if (link_sec != test_bit(HCI_AUTH, &hdev->flags)) hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(link_sec), &link_sec); if (lmp_bredr_capable(hdev)) { - set_bredr_scan(&req); + if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + set_bredr_scan(&req); update_class(&req); update_name(&req); update_eir(&req); @@ -3533,76 +4361,110 @@ new_settings: return err; } -int mgmt_set_powered_failed(struct hci_dev *hdev, int err) +void mgmt_set_powered_failed(struct hci_dev *hdev, int err) { struct pending_cmd *cmd; u8 status; cmd = mgmt_pending_find(MGMT_OP_SET_POWERED, hdev); if (!cmd) - return -ENOENT; + return; if (err == -ERFKILL) status = MGMT_STATUS_RFKILLED; else status = MGMT_STATUS_FAILED; - err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status); + cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status); mgmt_pending_remove(cmd); +} - return err; +void mgmt_discoverable_timeout(struct hci_dev *hdev) +{ + struct hci_request req; + + hci_dev_lock(hdev); + + /* When discoverable timeout triggers, then just make sure + * the limited discoverable flag is cleared. Even in the case + * of a timeout triggered from general discoverable, it is + * safe to unconditionally clear the flag. + */ + clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); + + hci_req_init(&req, hdev); + if (test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) { + u8 scan = SCAN_PAGE; + hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, + sizeof(scan), &scan); + } + update_class(&req); + update_adv_data(&req); + hci_req_run(&req, NULL); + + hdev->discov_timeout = 0; + + new_settings(hdev, NULL); + + hci_dev_unlock(hdev); } -int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) +void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) { - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; + bool changed; + + /* Nothing needed here if there's a pending command since that + * commands request completion callback takes care of everything + * necessary. + */ + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev)) + return; if (discoverable) { - if (!test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - changed = true; + changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags); } else { - if (test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - changed = true; + clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); } - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, settings_rsp, - &match); - - if (changed) - err = new_settings(hdev, match.sk); + if (changed) { + struct hci_request req; - if (match.sk) - sock_put(match.sk); + /* In case this change in discoverable was triggered by + * a disabling of connectable there could be a need to + * update the advertising flags. + */ + hci_req_init(&req, hdev); + update_adv_data(&req); + hci_req_run(&req, NULL); - return err; + new_settings(hdev, NULL); + } } -int mgmt_connectable(struct hci_dev *hdev, u8 connectable) +void mgmt_connectable(struct hci_dev *hdev, u8 connectable) { - struct pending_cmd *cmd; - bool changed = false; - int err = 0; + bool changed; - if (connectable) { - if (!test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - changed = true; - } + /* Nothing needed here if there's a pending command since that + * commands request completion callback takes care of everything + * necessary. + */ + if (mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) + return; - cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev); + if (connectable) + changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags); + else + changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); if (changed) - err = new_settings(hdev, cmd ? cmd->sk : NULL); - - return err; + new_settings(hdev, NULL); } -int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) +void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { u8 mgmt_err = mgmt_status(status); @@ -3613,12 +4475,10 @@ int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) if (scan & SCAN_INQUIRY) mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, cmd_status_rsp, &mgmt_err); - - return 0; } -int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, - bool persistent) +void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, + bool persistent) { struct mgmt_ev_new_link_key ev; @@ -3631,10 +4491,10 @@ int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE); ev.key.pin_len = key->pin_len; - return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); + mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } -int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) +void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) { struct mgmt_ev_new_long_term_key ev; @@ -3653,13 +4513,23 @@ int mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, u8 persistent) memcpy(ev.key.rand, key->rand, sizeof(key->rand)); memcpy(ev.key.val, key->val, sizeof(key->val)); - return mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), - NULL); + mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL); +} + +static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, + u8 data_len) +{ + eir[eir_len++] = sizeof(type) + data_len; + eir[eir_len++] = type; + memcpy(&eir[eir_len], data, data_len); + eir_len += data_len; + + return eir_len; } -int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u32 flags, u8 *name, u8 name_len, - u8 *dev_class) +void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u32 flags, u8 *name, u8 name_len, + u8 *dev_class) { char buf[512]; struct mgmt_ev_device_connected *ev = (void *) buf; @@ -3680,8 +4550,8 @@ int mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->eir_len = cpu_to_le16(eir_len); - return mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf, - sizeof(*ev) + eir_len, NULL); + mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf, + sizeof(*ev) + eir_len, NULL); } static void disconnect_rsp(struct pending_cmd *cmd, void *data) @@ -3719,12 +4589,11 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_remove(cmd); } -int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 reason) +void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 reason) { struct mgmt_ev_device_disconnected ev; struct sock *sk = NULL; - int err; mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk); @@ -3732,45 +4601,39 @@ int mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, ev.addr.type = link_to_bdaddr(link_type, addr_type); ev.reason = reason; - err = mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), - sk); + mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), sk); if (sk) sock_put(sk); mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, hdev); - - return err; } -int mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 link_type, u8 addr_type, u8 status) +void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 link_type, u8 addr_type, u8 status) { struct mgmt_rp_disconnect rp; struct pending_cmd *cmd; - int err; mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, hdev); cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); if (!cmd) - return -ENOENT; + return; bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = link_to_bdaddr(link_type, addr_type); - err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, - mgmt_status(status), &rp, sizeof(rp)); + cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, + mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); - - return err; } -int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status) +void mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 status) { struct mgmt_ev_connect_failed ev; @@ -3778,10 +4641,10 @@ int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev.addr.type = link_to_bdaddr(link_type, addr_type); ev.status = mgmt_status(status); - return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL); + mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL); } -int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) +void mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) { struct mgmt_ev_pin_code_request ev; @@ -3789,52 +4652,45 @@ int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) ev.addr.type = BDADDR_BREDR; ev.secure = secure; - return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), - NULL); + mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL); } -int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) +void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; - int err; cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); if (!cmd) - return -ENOENT; + return; bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = BDADDR_BREDR; - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, - mgmt_status(status), &rp, sizeof(rp)); + cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, + mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); - - return err; } -int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 status) +void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; - int err; cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); if (!cmd) - return -ENOENT; + return; bacpy(&rp.addr.bdaddr, bdaddr); rp.addr.type = BDADDR_BREDR; - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, - mgmt_status(status), &rp, sizeof(rp)); + cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, + mgmt_status(status), &rp, sizeof(rp)); mgmt_pending_remove(cmd); - - return err; } int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, @@ -3936,8 +4792,8 @@ int mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr, return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL); } -int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 status) +void mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 status) { struct mgmt_ev_auth_failed ev; @@ -3945,40 +4801,36 @@ int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev.addr.type = link_to_bdaddr(link_type, addr_type); ev.status = mgmt_status(status); - return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); + mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); } -int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) +void mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) { struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; + bool changed; if (status) { u8 mgmt_err = mgmt_status(status); mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, cmd_status_rsp, &mgmt_err); - return 0; + return; } - if (test_bit(HCI_AUTH, &hdev->flags)) { - if (!test_and_set_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) - changed = true; - } + if (test_bit(HCI_AUTH, &hdev->flags)) + changed = !test_and_set_bit(HCI_LINK_SECURITY, + &hdev->dev_flags); + else + changed = test_and_clear_bit(HCI_LINK_SECURITY, + &hdev->dev_flags); mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp, &match); if (changed) - err = new_settings(hdev, match.sk); + new_settings(hdev, match.sk); if (match.sk) sock_put(match.sk); - - return err; } static void clear_eir(struct hci_request *req) @@ -3996,38 +4848,41 @@ static void clear_eir(struct hci_request *req) hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp); } -int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) +void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) { struct cmd_lookup match = { NULL, hdev }; struct hci_request req; bool changed = false; - int err = 0; if (status) { u8 mgmt_err = mgmt_status(status); if (enable && test_and_clear_bit(HCI_SSP_ENABLED, - &hdev->dev_flags)) - err = new_settings(hdev, NULL); + &hdev->dev_flags)) { + clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); + new_settings(hdev, NULL); + } mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp, &mgmt_err); - - return err; + return; } if (enable) { - if (!test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - changed = true; + changed = !test_and_set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); } else { - if (test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - changed = true; + changed = test_and_clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); + if (!changed) + changed = test_and_clear_bit(HCI_HS_ENABLED, + &hdev->dev_flags); + else + clear_bit(HCI_HS_ENABLED, &hdev->dev_flags); } mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match); if (changed) - err = new_settings(hdev, match.sk); + new_settings(hdev, match.sk); if (match.sk) sock_put(match.sk); @@ -4040,8 +4895,6 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) clear_eir(&req); hci_req_run(&req, NULL); - - return err; } static void sk_lookup(struct pending_cmd *cmd, void *data) @@ -4054,33 +4907,30 @@ static void sk_lookup(struct pending_cmd *cmd, void *data) } } -int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, - u8 status) +void mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, + u8 status) { struct cmd_lookup match = { NULL, hdev, mgmt_status(status) }; - int err = 0; mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match); mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match); mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match); if (!status) - err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, - 3, NULL); + mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 3, + NULL); if (match.sk) sock_put(match.sk); - - return err; } -int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) +void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) { struct mgmt_cp_set_local_name ev; struct pending_cmd *cmd; if (status) - return 0; + return; memset(&ev, 0, sizeof(ev)); memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); @@ -4094,96 +4944,54 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) * HCI dev don't send any mgmt signals. */ if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) - return 0; + return; } - return mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); + mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); } -int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, - u8 *randomizer, u8 status) +void mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, + u8 *randomizer, u8 status) { struct pending_cmd *cmd; - int err; BT_DBG("%s status %u", hdev->name, status); cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); if (!cmd) - return -ENOENT; + return; if (status) { - err = cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, - mgmt_status(status)); + cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, + mgmt_status(status)); } else { struct mgmt_rp_read_local_oob_data rp; memcpy(rp.hash, hash, sizeof(rp.hash)); memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer)); - err = cmd_complete(cmd->sk, hdev->id, - MGMT_OP_READ_LOCAL_OOB_DATA, 0, &rp, - sizeof(rp)); + cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, + 0, &rp, sizeof(rp)); } mgmt_pending_remove(cmd); - - return err; -} - -int mgmt_le_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) -{ - struct cmd_lookup match = { NULL, hdev }; - bool changed = false; - int err = 0; - - if (status) { - u8 mgmt_err = mgmt_status(status); - - if (enable && test_and_clear_bit(HCI_LE_ENABLED, - &hdev->dev_flags)) - err = new_settings(hdev, NULL); - - mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp, - &mgmt_err); - - return err; - } - - if (enable) { - if (!test_and_set_bit(HCI_LE_ENABLED, &hdev->dev_flags)) - changed = true; - } else { - if (test_and_clear_bit(HCI_LE_ENABLED, &hdev->dev_flags)) - changed = true; - } - - mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match); - - if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); - - return err; } -int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 - ssp, u8 *eir, u16 eir_len) +void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, u8 *dev_class, s8 rssi, u8 cfm_name, u8 + ssp, u8 *eir, u16 eir_len) { char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; size_t ev_size; if (!hci_discovery_active(hdev)) - return -EPERM; + return; /* Leave 5 bytes for a potential CoD field */ if (sizeof(*ev) + eir_len + 5 > sizeof(buf)) - return -EINVAL; + return; memset(buf, 0, sizeof(buf)); @@ -4205,11 +5013,11 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->eir_len = cpu_to_le16(eir_len); ev_size = sizeof(*ev) + eir_len; - return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); + mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); } -int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, - u8 addr_type, s8 rssi, u8 *name, u8 name_len) +void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, + u8 addr_type, s8 rssi, u8 *name, u8 name_len) { struct mgmt_ev_device_found *ev; char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2]; @@ -4228,11 +5036,10 @@ int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->eir_len = cpu_to_le16(eir_len); - return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, - sizeof(*ev) + eir_len, NULL); + mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, sizeof(*ev) + eir_len, NULL); } -int mgmt_discovering(struct hci_dev *hdev, u8 discovering) +void mgmt_discovering(struct hci_dev *hdev, u8 discovering) { struct mgmt_ev_discovering ev; struct pending_cmd *cmd; @@ -4256,7 +5063,7 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering) ev.type = hdev->discovery.type; ev.discovering = discovering; - return mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); + mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); } int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) @@ -4287,5 +5094,35 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) cmd ? cmd->sk : NULL); } -module_param(enable_hs, bool, 0644); -MODULE_PARM_DESC(enable_hs, "Enable High Speed support"); +static void adv_enable_complete(struct hci_dev *hdev, u8 status) +{ + BT_DBG("%s status %u", hdev->name, status); + + /* Clear the advertising mgmt setting if we failed to re-enable it */ + if (status) { + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + new_settings(hdev, NULL); + } +} + +void mgmt_reenable_advertising(struct hci_dev *hdev) +{ + struct hci_request req; + + if (hci_conn_num(hdev, LE_LINK) > 0) + return; + + if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + return; + + hci_req_init(&req, hdev); + enable_advertising(&req); + + /* If this fails we have no option but to let user space know + * that we've disabled advertising. + */ + if (hci_req_run(&req, adv_enable_complete) < 0) { + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + new_settings(hdev, NULL); + } +} diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index ca957d34b0c89fa29341a179ee16e325ac226e55..94d06cbfbc184a6e827aa5c3cbd15d4693eca49a 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -641,13 +641,13 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) { struct rfcomm_session *s; struct list_head *p, *n; - struct bt_sock *sk; + struct l2cap_chan *chan; list_for_each_safe(p, n, &session_list) { s = list_entry(p, struct rfcomm_session, list); - sk = bt_sk(s->sock->sk); + chan = l2cap_pi(s->sock->sk)->chan; - if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) && - !bacmp(&sk->dst, dst)) + if ((!bacmp(src, BDADDR_ANY) || !bacmp(&chan->src, src)) && + !bacmp(&chan->dst, dst)) return s; } return NULL; @@ -732,11 +732,11 @@ failed: void rfcomm_session_getaddr(struct rfcomm_session *s, bdaddr_t *src, bdaddr_t *dst) { - struct sock *sk = s->sock->sk; + struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan; if (src) - bacpy(src, &bt_sk(sk)->src); + bacpy(src, &chan->src); if (dst) - bacpy(dst, &bt_sk(sk)->dst); + bacpy(dst, &chan->dst); } /* ---- RFCOMM frame sending ---- */ @@ -2112,12 +2112,11 @@ static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) rfcomm_lock(); list_for_each_entry(s, &session_list, list) { + struct l2cap_chan *chan = l2cap_pi(s->sock->sk)->chan; struct rfcomm_dlc *d; list_for_each_entry(d, &s->dlcs, list) { - struct sock *sk = s->sock->sk; - seq_printf(f, "%pMR %pMR %ld %d %d %d %d\n", - &bt_sk(sk)->src, &bt_sk(sk)->dst, + &chan->src, &chan->dst, d->state, d->dlci, d->mtu, d->rx_credits, d->tx_credits); } @@ -2155,13 +2154,6 @@ static int __init rfcomm_init(void) goto unregister; } - if (bt_debugfs) { - rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444, - bt_debugfs, NULL, &rfcomm_dlc_debugfs_fops); - if (!rfcomm_dlc_debugfs) - BT_ERR("Failed to create RFCOMM debug file"); - } - err = rfcomm_init_ttys(); if (err < 0) goto stop; @@ -2172,6 +2164,13 @@ static int __init rfcomm_init(void) BT_INFO("RFCOMM ver %s", VERSION); + if (IS_ERR_OR_NULL(bt_debugfs)) + return 0; + + rfcomm_dlc_debugfs = debugfs_create_file("rfcomm_dlc", 0444, + bt_debugfs, NULL, + &rfcomm_dlc_debugfs_fops); + return 0; cleanup: diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 30b3721dc6d77d96be4bdb412ee90c49b8976dc9..c4d3d423f89b84d41b9ff0e6919e9f37786f4960 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -87,7 +87,8 @@ static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err) parent->sk_data_ready(parent, 0); } else { if (d->state == BT_CONNECTED) - rfcomm_session_getaddr(d->session, &bt_sk(sk)->src, NULL); + rfcomm_session_getaddr(d->session, + &rfcomm_pi(sk)->src, NULL); sk->sk_state_change(sk); } @@ -110,7 +111,7 @@ static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src) sk_for_each(sk, &rfcomm_sk_list.head) { if (rfcomm_pi(sk)->channel == channel && - !bacmp(&bt_sk(sk)->src, src)) + !bacmp(&rfcomm_pi(sk)->src, src)) break; } @@ -132,11 +133,11 @@ static struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t * if (rfcomm_pi(sk)->channel == channel) { /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) + if (!bacmp(&rfcomm_pi(sk)->src, src)) break; /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) + if (!bacmp(&rfcomm_pi(sk)->src, BDADDR_ANY)) sk1 = sk; } } @@ -355,7 +356,7 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr err = -EADDRINUSE; } else { /* Save source address */ - bacpy(&bt_sk(sk)->src, &sa->rc_bdaddr); + bacpy(&rfcomm_pi(sk)->src, &sa->rc_bdaddr); rfcomm_pi(sk)->channel = sa->rc_channel; sk->sk_state = BT_BOUND; } @@ -393,13 +394,14 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a } sk->sk_state = BT_CONNECT; - bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr); + bacpy(&rfcomm_pi(sk)->dst, &sa->rc_bdaddr); rfcomm_pi(sk)->channel = sa->rc_channel; d->sec_level = rfcomm_pi(sk)->sec_level; d->role_switch = rfcomm_pi(sk)->role_switch; - err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel); + err = rfcomm_dlc_open(d, &rfcomm_pi(sk)->src, &sa->rc_bdaddr, + sa->rc_channel); if (!err) err = bt_sock_wait_state(sk, BT_CONNECTED, sock_sndtimeo(sk, flags & O_NONBLOCK)); @@ -429,7 +431,7 @@ static int rfcomm_sock_listen(struct socket *sock, int backlog) } if (!rfcomm_pi(sk)->channel) { - bdaddr_t *src = &bt_sk(sk)->src; + bdaddr_t *src = &rfcomm_pi(sk)->src; u8 channel; err = -EINVAL; @@ -530,9 +532,9 @@ static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int * sa->rc_family = AF_BLUETOOTH; sa->rc_channel = rfcomm_pi(sk)->channel; if (peer) - bacpy(&sa->rc_bdaddr, &bt_sk(sk)->dst); + bacpy(&sa->rc_bdaddr, &rfcomm_pi(sk)->dst); else - bacpy(&sa->rc_bdaddr, &bt_sk(sk)->src); + bacpy(&sa->rc_bdaddr, &rfcomm_pi(sk)->src); *len = sizeof(struct sockaddr_rc); return 0; @@ -544,7 +546,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct sock *sk = sock->sk; struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc; struct sk_buff *skb; - int sent = 0; + int sent; if (test_bit(RFCOMM_DEFER_SETUP, &d->flags)) return -ENOTCONN; @@ -559,6 +561,10 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, lock_sock(sk); + sent = bt_sock_wait_ready(sk, msg->msg_flags); + if (sent) + goto done; + while (len) { size_t size = min_t(size_t, len, d->mtu); int err; @@ -594,6 +600,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, len -= size; } +done: release_sock(sk); return sent; @@ -946,8 +953,8 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc * bt_sock_reclassify_lock(sk, BTPROTO_RFCOMM); rfcomm_sock_init(sk, parent); - bacpy(&bt_sk(sk)->src, &src); - bacpy(&bt_sk(sk)->dst, &dst); + bacpy(&rfcomm_pi(sk)->src, &src); + bacpy(&rfcomm_pi(sk)->dst, &dst); rfcomm_pi(sk)->channel = channel; sk->sk_state = BT_CONFIG; @@ -974,7 +981,7 @@ static int rfcomm_sock_debugfs_show(struct seq_file *f, void *p) sk_for_each(sk, &rfcomm_sk_list.head) { seq_printf(f, "%pMR %pMR %d %d\n", - &bt_sk(sk)->src, &bt_sk(sk)->dst, + &rfcomm_pi(sk)->src, &rfcomm_pi(sk)->dst, sk->sk_state, rfcomm_pi(sk)->channel); } @@ -1044,15 +1051,15 @@ int __init rfcomm_init_sockets(void) goto error; } - if (bt_debugfs) { - rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444, - bt_debugfs, NULL, &rfcomm_sock_debugfs_fops); - if (!rfcomm_sock_debugfs) - BT_ERR("Failed to create RFCOMM debug file"); - } - BT_INFO("RFCOMM socket layer initialized"); + if (IS_ERR_OR_NULL(bt_debugfs)) + return 0; + + rfcomm_sock_debugfs = debugfs_create_file("rfcomm", 0444, + bt_debugfs, NULL, + &rfcomm_sock_debugfs_fops); + return 0; error: diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 96bd388d93a4aae145bfb207d26fbc3a90b9e0a8..12a0e51e21e13631beeec14d97e64aa137fd0e99 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -92,9 +92,6 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) hcon->sco_data = conn; conn->hcon = hcon; - conn->src = &hdev->bdaddr; - conn->dst = &hcon->dst; - if (hdev->sco_mtu > 0) conn->mtu = hdev->sco_mtu; else @@ -156,16 +153,14 @@ static int sco_chan_add(struct sco_conn *conn, struct sock *sk, static int sco_connect(struct sock *sk) { - bdaddr_t *src = &bt_sk(sk)->src; - bdaddr_t *dst = &bt_sk(sk)->dst; struct sco_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; int err, type; - BT_DBG("%pMR -> %pMR", src, dst); + BT_DBG("%pMR -> %pMR", &sco_pi(sk)->src, &sco_pi(sk)->dst); - hdev = hci_get_route(dst, src); + hdev = hci_get_route(&sco_pi(sk)->dst, &sco_pi(sk)->src); if (!hdev) return -EHOSTUNREACH; @@ -182,7 +177,8 @@ static int sco_connect(struct sock *sk) goto done; } - hcon = hci_connect_sco(hdev, type, dst, sco_pi(sk)->setting); + hcon = hci_connect_sco(hdev, type, &sco_pi(sk)->dst, + sco_pi(sk)->setting); if (IS_ERR(hcon)) { err = PTR_ERR(hcon); goto done; @@ -196,7 +192,7 @@ static int sco_connect(struct sock *sk) } /* Update source addr of the socket */ - bacpy(src, conn->src); + bacpy(&sco_pi(sk)->src, &hcon->src); err = sco_chan_add(conn, sk, NULL); if (err) @@ -270,7 +266,7 @@ static struct sock *__sco_get_sock_listen_by_addr(bdaddr_t *ba) if (sk->sk_state != BT_LISTEN) continue; - if (!bacmp(&bt_sk(sk)->src, ba)) + if (!bacmp(&sco_pi(sk)->src, ba)) return sk; } @@ -291,11 +287,11 @@ static struct sock *sco_get_sock_listen(bdaddr_t *src) continue; /* Exact match. */ - if (!bacmp(&bt_sk(sk)->src, src)) + if (!bacmp(&sco_pi(sk)->src, src)) break; /* Closest match */ - if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) + if (!bacmp(&sco_pi(sk)->src, BDADDR_ANY)) sk1 = sk; } @@ -475,7 +471,7 @@ static int sco_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_le goto done; } - bacpy(&bt_sk(sk)->src, &sa->sco_bdaddr); + bacpy(&sco_pi(sk)->src, &sa->sco_bdaddr); sk->sk_state = BT_BOUND; @@ -505,7 +501,7 @@ static int sco_sock_connect(struct socket *sock, struct sockaddr *addr, int alen lock_sock(sk); /* Set destination address and psm */ - bacpy(&bt_sk(sk)->dst, &sa->sco_bdaddr); + bacpy(&sco_pi(sk)->dst, &sa->sco_bdaddr); err = sco_connect(sk); if (err) @@ -522,7 +518,7 @@ done: static int sco_sock_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; - bdaddr_t *src = &bt_sk(sk)->src; + bdaddr_t *src = &sco_pi(sk)->src; int err = 0; BT_DBG("sk %p backlog %d", sk, backlog); @@ -626,9 +622,9 @@ static int sco_sock_getname(struct socket *sock, struct sockaddr *addr, int *len *len = sizeof(struct sockaddr_sco); if (peer) - bacpy(&sa->sco_bdaddr, &bt_sk(sk)->dst); + bacpy(&sa->sco_bdaddr, &sco_pi(sk)->dst); else - bacpy(&sa->sco_bdaddr, &bt_sk(sk)->src); + bacpy(&sa->sco_bdaddr, &sco_pi(sk)->src); return 0; } @@ -999,7 +995,7 @@ static void sco_conn_ready(struct sco_conn *conn) } else { sco_conn_lock(conn); - parent = sco_get_sock_listen(conn->src); + parent = sco_get_sock_listen(&conn->hcon->src); if (!parent) { sco_conn_unlock(conn); return; @@ -1017,8 +1013,8 @@ static void sco_conn_ready(struct sco_conn *conn) sco_sock_init(sk, parent); - bacpy(&bt_sk(sk)->src, conn->src); - bacpy(&bt_sk(sk)->dst, conn->dst); + bacpy(&sco_pi(sk)->src, &conn->hcon->src); + bacpy(&sco_pi(sk)->dst, &conn->hcon->dst); hci_conn_hold(conn->hcon); __sco_chan_add(conn, sk, parent); @@ -1051,8 +1047,8 @@ int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags) if (sk->sk_state != BT_LISTEN) continue; - if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) || - !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { + if (!bacmp(&sco_pi(sk)->src, &hdev->bdaddr) || + !bacmp(&sco_pi(sk)->src, BDADDR_ANY)) { lm |= HCI_LM_ACCEPT; if (test_bit(BT_SK_DEFER_SETUP, &bt_sk(sk)->flags)) @@ -1111,8 +1107,8 @@ static int sco_debugfs_show(struct seq_file *f, void *p) read_lock(&sco_sk_list.lock); sk_for_each(sk, &sco_sk_list.head) { - seq_printf(f, "%pMR %pMR %d\n", &bt_sk(sk)->src, - &bt_sk(sk)->dst, sk->sk_state); + seq_printf(f, "%pMR %pMR %d\n", &sco_pi(sk)->src, + &sco_pi(sk)->dst, sk->sk_state); } read_unlock(&sco_sk_list.lock); @@ -1181,15 +1177,14 @@ int __init sco_init(void) goto error; } - if (bt_debugfs) { - sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs, - NULL, &sco_debugfs_fops); - if (!sco_debugfs) - BT_ERR("Failed to create SCO debug file"); - } - BT_INFO("SCO socket layer initialized"); + if (IS_ERR_OR_NULL(bt_debugfs)) + return 0; + + sco_debugfs = debugfs_create_file("sco", 0444, bt_debugfs, + NULL, &sco_debugfs_fops); + return 0; error: diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b5562abdd6e0c84e4a2f52f3175c8046ce70751c..85a2796cac61bcc423ef724f1911f2a794667aff 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -28,7 +28,8 @@ #include #include #include -#include + +#include "smp.h" #define SMP_TIMEOUT msecs_to_jiffies(30000) @@ -85,8 +86,8 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) } static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16], - u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, - u8 _rat, bdaddr_t *ra, u8 res[16]) + u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, + u8 _rat, bdaddr_t *ra, u8 res[16]) { u8 p1[16], p2[16]; int err; @@ -126,8 +127,8 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16], return err; } -static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], - u8 r1[16], u8 r2[16], u8 _r[16]) +static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16], + u8 r2[16], u8 _r[16]) { int err; @@ -150,7 +151,7 @@ static int smp_rand(u8 *buf) } static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code, - u16 dlen, void *data) + u16 dlen, void *data) { struct sk_buff *skb; struct l2cap_hdr *lh; @@ -213,9 +214,8 @@ static __u8 seclevel_to_authreq(__u8 sec_level) } static void build_pairing_cmd(struct l2cap_conn *conn, - struct smp_cmd_pairing *req, - struct smp_cmd_pairing *rsp, - __u8 authreq) + struct smp_cmd_pairing *req, + struct smp_cmd_pairing *rsp, __u8 authreq) { u8 dist_keys = 0; @@ -249,7 +249,7 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) struct smp_chan *smp = conn->smp_chan; if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || - (max_key_size < SMP_MIN_ENC_KEY_SIZE)) + (max_key_size < SMP_MIN_ENC_KEY_SIZE)) return SMP_ENC_KEY_SIZE; smp->enc_key_size = max_key_size; @@ -263,15 +263,15 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send) if (send) smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), - &reason); + &reason); - clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->hcon->flags); - mgmt_auth_failed(conn->hcon->hdev, conn->dst, hcon->type, - hcon->dst_type, HCI_ERROR_AUTH_FAILURE); + clear_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags); + mgmt_auth_failed(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type, + HCI_ERROR_AUTH_FAILURE); cancel_delayed_work_sync(&conn->security_timer); - if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) + if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) smp_chan_destroy(conn); } @@ -309,8 +309,8 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, /* If either side has unknown io_caps, use JUST WORKS */ /* Otherwise, look up method from the table */ if (!(auth & SMP_AUTH_MITM) || - local_io > SMP_IO_KEYBOARD_DISPLAY || - remote_io > SMP_IO_KEYBOARD_DISPLAY) + local_io > SMP_IO_KEYBOARD_DISPLAY || + remote_io > SMP_IO_KEYBOARD_DISPLAY) method = JUST_WORKS; else method = gen_method[remote_io][local_io]; @@ -354,10 +354,10 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, hci_dev_lock(hcon->hdev); if (method == REQ_PASSKEY) - ret = mgmt_user_passkey_request(hcon->hdev, conn->dst, + ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type); else - ret = mgmt_user_confirm_request(hcon->hdev, conn->dst, + ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type, hcon->dst_type, cpu_to_le32(passkey), 0); @@ -386,12 +386,13 @@ static void confirm_work(struct work_struct *work) smp->tfm = tfm; if (conn->hcon->out) - ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, res); + ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, + conn->hcon->src_type, &conn->hcon->src, + conn->hcon->dst_type, &conn->hcon->dst, res); else ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, - conn->hcon->dst_type, conn->dst, 0, conn->src, - res); + conn->hcon->dst_type, &conn->hcon->dst, + conn->hcon->src_type, &conn->hcon->src, res); if (ret) { reason = SMP_UNSPECIFIED; goto error; @@ -425,11 +426,13 @@ static void random_work(struct work_struct *work) BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); if (hcon->out) - ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0, - conn->src, hcon->dst_type, conn->dst, res); + ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, + hcon->src_type, &hcon->src, + hcon->dst_type, &hcon->dst, res); else ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, - hcon->dst_type, conn->dst, 0, conn->src, res); + hcon->dst_type, &hcon->dst, + hcon->src_type, &hcon->src, res); if (ret) { reason = SMP_UNSPECIFIED; goto error; @@ -477,9 +480,9 @@ static void random_work(struct work_struct *work) swap128(key, stk); memset(stk + smp->enc_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); + SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); - hci_add_ltk(hcon->hdev, conn->dst, hcon->dst_type, + hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, HCI_SMP_STK_SLAVE, 0, 0, stk, smp->enc_key_size, ediv, rand); } @@ -494,7 +497,7 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) { struct smp_chan *smp; - smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC); + smp = kzalloc(sizeof(*smp), GFP_ATOMIC); if (!smp) return NULL; @@ -649,7 +652,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); if ((req->auth_req & SMP_AUTH_BONDING) && - (rsp->auth_req & SMP_AUTH_BONDING)) + (rsp->auth_req & SMP_AUTH_BONDING)) auth = SMP_AUTH_BONDING; auth |= (req->auth_req | rsp->auth_req) & SMP_AUTH_MITM; @@ -684,7 +687,7 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) swap128(smp->prnd, random); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), - random); + random); } else if (test_bit(SMP_FLAG_TK_VALID, &smp->smp_flags)) { queue_work(hdev->workqueue, &smp->confirm); } else { @@ -714,7 +717,7 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) struct smp_ltk *key; struct hci_conn *hcon = conn->hcon; - key = hci_find_ltk_by_addr(hcon->hdev, conn->dst, hcon->dst_type); + key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type); if (!key) return 0; @@ -728,8 +731,8 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) hcon->enc_key_size = key->enc_size; return 1; - } + static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_security_req *rp = (void *) skb->data; @@ -835,9 +838,9 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(*rp)); hci_dev_lock(hdev); - authenticated = (conn->hcon->sec_level == BT_SECURITY_HIGH); - hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, - HCI_SMP_LTK, 1, authenticated, smp->tk, smp->enc_key_size, + authenticated = (hcon->sec_level == BT_SECURITY_HIGH); + hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, 1, + authenticated, smp->tk, smp->enc_key_size, rp->ediv, rp->rand); smp_distribute_keys(conn, 1); hci_dev_unlock(hdev); @@ -847,16 +850,27 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) { - __u8 code = skb->data[0]; - __u8 reason; + struct hci_conn *hcon = conn->hcon; + __u8 code, reason; int err = 0; - if (!test_bit(HCI_LE_ENABLED, &conn->hcon->hdev->dev_flags)) { + if (hcon->type != LE_LINK) { + kfree_skb(skb); + return 0; + } + + if (skb->len < 1) { + kfree_skb(skb); + return -EILSEQ; + } + + if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags)) { err = -ENOTSUPP; reason = SMP_PAIRING_NOTSUPP; goto done; } + code = skb->data[0]; skb_pull(skb, sizeof(code)); /* @@ -974,7 +988,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); authenticated = hcon->sec_level == BT_SECURITY_HIGH; - hci_add_ltk(conn->hcon->hdev, conn->dst, hcon->dst_type, + hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK_SLAVE, 1, authenticated, enc.ltk, smp->enc_key_size, ediv, ident.rand); @@ -996,10 +1010,10 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) /* Just public address */ memset(&addrinfo, 0, sizeof(addrinfo)); - bacpy(&addrinfo.bdaddr, conn->src); + bacpy(&addrinfo.bdaddr, &conn->hcon->src); smp_send_cmd(conn, SMP_CMD_IDENT_ADDR_INFO, sizeof(addrinfo), - &addrinfo); + &addrinfo); *keydist &= ~SMP_DIST_ID_KEY; } diff --git a/include/net/bluetooth/smp.h b/net/bluetooth/smp.h similarity index 100% rename from include/net/bluetooth/smp.h rename to net/bluetooth/smp.h diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 686284ff3d6a1cabd0f16251d19346e11a810256..4c214b2b88efa01d81be93a8b8d77e57229c1fa4 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -363,7 +363,7 @@ static struct sk_buff *br_ip4_multicast_alloc_query(struct net_bridge *br, skb_reset_mac_header(skb); eth = eth_hdr(skb); - memcpy(eth->h_source, br->dev->dev_addr, 6); + memcpy(eth->h_source, br->dev->dev_addr, ETH_ALEN); eth->h_dest[0] = 1; eth->h_dest[1] = 0; eth->h_dest[2] = 0x5e; @@ -433,7 +433,7 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, skb_reset_mac_header(skb); eth = eth_hdr(skb); - memcpy(eth->h_source, br->dev->dev_addr, 6); + memcpy(eth->h_source, br->dev->dev_addr, ETH_ALEN); eth->h_proto = htons(ETH_P_IPV6); skb_put(skb, sizeof(*eth)); diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index f87736270eaa875ba0060048a38cfabacfd29444..80cad2cf02a76d03e4f33a955f2e56dacd25d3b7 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -559,6 +559,8 @@ static struct net_device *setup_pre_routing(struct sk_buff *skb) else if (skb->protocol == htons(ETH_P_PPP_SES)) nf_bridge->mask |= BRNF_PPPoE; + /* Must drop socket now because of tproxy. */ + skb_orphan(skb); return skb->dev; } @@ -619,7 +621,7 @@ bad: /* Replicate the checks that IPv6 does on packet reception and pass the packet * to ip6tables, which doesn't support NAT, so things are fairly simple. */ -static unsigned int br_nf_pre_routing_ipv6(unsigned int hook, +static unsigned int br_nf_pre_routing_ipv6(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -669,7 +671,8 @@ static unsigned int br_nf_pre_routing_ipv6(unsigned int hook, * receiving device) to make netfilter happy, the REDIRECT * target in particular. Save the original destination IP * address to be able to detect DNAT afterwards. */ -static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_pre_routing(const struct nf_hook_ops *ops, + struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) @@ -691,7 +694,7 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb, return NF_ACCEPT; nf_bridge_pull_encap_header_rcsum(skb); - return br_nf_pre_routing_ipv6(hook, skb, in, out, okfn); + return br_nf_pre_routing_ipv6(ops, skb, in, out, okfn); } if (!brnf_call_iptables && !br->nf_call_iptables) @@ -727,7 +730,8 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff *skb, * took place when the packet entered the bridge), but we * register an IPv4 PRE_ROUTING 'sabotage' hook that will * prevent this from happening. */ -static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_local_in(const struct nf_hook_ops *ops, + struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) @@ -765,7 +769,8 @@ static int br_nf_forward_finish(struct sk_buff *skb) * but we are still able to filter on the 'real' indev/outdev * because of the physdev module. For ARP, indev and outdev are the * bridge ports. */ -static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_forward_ip(const struct nf_hook_ops *ops, + struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) @@ -818,7 +823,8 @@ static unsigned int br_nf_forward_ip(unsigned int hook, struct sk_buff *skb, return NF_STOLEN; } -static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_forward_arp(const struct nf_hook_ops *ops, + struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) @@ -878,7 +884,8 @@ static int br_nf_dev_queue_xmit(struct sk_buff *skb) #endif /* PF_BRIDGE/POST_ROUTING ********************************************/ -static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb, +static unsigned int br_nf_post_routing(const struct nf_hook_ops *ops, + struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) @@ -923,7 +930,8 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff *skb, /* IP/SABOTAGE *****************************************************/ /* Don't hand locally destined packets to PF_INET(6)/PRE_ROUTING * for the second time. */ -static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff *skb, +static unsigned int ip_sabotage_in(const struct nf_hook_ops *ops, + struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index 2e8244efb262d54362bd36a12b9c3000c4388260..229d820bdf0b06453cb43058020d54e33f210297 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -343,10 +343,9 @@ static inline int br_is_root_bridge(const struct net_bridge *br) } /* br_device.c */ -extern void br_dev_setup(struct net_device *dev); -extern void br_dev_delete(struct net_device *dev, struct list_head *list); -extern netdev_tx_t br_dev_xmit(struct sk_buff *skb, - struct net_device *dev); +void br_dev_setup(struct net_device *dev); +void br_dev_delete(struct net_device *dev, struct list_head *list); +netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev); #ifdef CONFIG_NET_POLL_CONTROLLER static inline void br_netpoll_send_skb(const struct net_bridge_port *p, struct sk_buff *skb) @@ -357,8 +356,8 @@ static inline void br_netpoll_send_skb(const struct net_bridge_port *p, netpoll_send_skb(np, skb); } -extern int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp); -extern void br_netpoll_disable(struct net_bridge_port *p); +int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp); +void br_netpoll_disable(struct net_bridge_port *p); #else static inline void br_netpoll_send_skb(const struct net_bridge_port *p, struct sk_buff *skb) @@ -376,117 +375,99 @@ static inline void br_netpoll_disable(struct net_bridge_port *p) #endif /* br_fdb.c */ -extern int br_fdb_init(void); -extern void br_fdb_fini(void); -extern void br_fdb_flush(struct net_bridge *br); -extern void br_fdb_changeaddr(struct net_bridge_port *p, - const unsigned char *newaddr); -extern void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr); -extern void br_fdb_cleanup(unsigned long arg); -extern void br_fdb_delete_by_port(struct net_bridge *br, - const struct net_bridge_port *p, int do_all); -extern struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, - const unsigned char *addr, - __u16 vid); -extern int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); -extern int br_fdb_fillbuf(struct net_bridge *br, void *buf, - unsigned long count, unsigned long off); -extern int br_fdb_insert(struct net_bridge *br, - struct net_bridge_port *source, - const unsigned char *addr, - u16 vid); -extern void br_fdb_update(struct net_bridge *br, - struct net_bridge_port *source, - const unsigned char *addr, - u16 vid); -extern int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid); - -extern int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, - const unsigned char *addr); -extern int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], - struct net_device *dev, - const unsigned char *addr, - u16 nlh_flags); -extern int br_fdb_dump(struct sk_buff *skb, - struct netlink_callback *cb, - struct net_device *dev, - int idx); +int br_fdb_init(void); +void br_fdb_fini(void); +void br_fdb_flush(struct net_bridge *br); +void br_fdb_changeaddr(struct net_bridge_port *p, const unsigned char *newaddr); +void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr); +void br_fdb_cleanup(unsigned long arg); +void br_fdb_delete_by_port(struct net_bridge *br, + const struct net_bridge_port *p, int do_all); +struct net_bridge_fdb_entry *__br_fdb_get(struct net_bridge *br, + const unsigned char *addr, __u16 vid); +int br_fdb_test_addr(struct net_device *dev, unsigned char *addr); +int br_fdb_fillbuf(struct net_bridge *br, void *buf, unsigned long count, + unsigned long off); +int br_fdb_insert(struct net_bridge *br, struct net_bridge_port *source, + const unsigned char *addr, u16 vid); +void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, + const unsigned char *addr, u16 vid); +int fdb_delete_by_addr(struct net_bridge *br, const u8 *addr, u16 vid); + +int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, const unsigned char *addr); +int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev, + const unsigned char *addr, u16 nlh_flags); +int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, + struct net_device *dev, int idx); /* br_forward.c */ -extern void br_deliver(const struct net_bridge_port *to, - struct sk_buff *skb); -extern int br_dev_queue_push_xmit(struct sk_buff *skb); -extern void br_forward(const struct net_bridge_port *to, +void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); +int br_dev_queue_push_xmit(struct sk_buff *skb); +void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0); -extern int br_forward_finish(struct sk_buff *skb); -extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, - bool unicast); -extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, - struct sk_buff *skb2, bool unicast); +int br_forward_finish(struct sk_buff *skb); +void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb, bool unicast); +void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, + struct sk_buff *skb2, bool unicast); /* br_if.c */ -extern void br_port_carrier_check(struct net_bridge_port *p); -extern int br_add_bridge(struct net *net, const char *name); -extern int br_del_bridge(struct net *net, const char *name); -extern void br_net_exit(struct net *net); -extern int br_add_if(struct net_bridge *br, - struct net_device *dev); -extern int br_del_if(struct net_bridge *br, - struct net_device *dev); -extern int br_min_mtu(const struct net_bridge *br); -extern netdev_features_t br_features_recompute(struct net_bridge *br, - netdev_features_t features); +void br_port_carrier_check(struct net_bridge_port *p); +int br_add_bridge(struct net *net, const char *name); +int br_del_bridge(struct net *net, const char *name); +void br_net_exit(struct net *net); +int br_add_if(struct net_bridge *br, struct net_device *dev); +int br_del_if(struct net_bridge *br, struct net_device *dev); +int br_min_mtu(const struct net_bridge *br); +netdev_features_t br_features_recompute(struct net_bridge *br, + netdev_features_t features); /* br_input.c */ -extern int br_handle_frame_finish(struct sk_buff *skb); -extern rx_handler_result_t br_handle_frame(struct sk_buff **pskb); +int br_handle_frame_finish(struct sk_buff *skb); +rx_handler_result_t br_handle_frame(struct sk_buff **pskb); /* br_ioctl.c */ -extern int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); -extern int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *arg); +int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, + void __user *arg); /* br_multicast.c */ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING extern unsigned int br_mdb_rehash_seq; -extern int br_multicast_rcv(struct net_bridge *br, - struct net_bridge_port *port, - struct sk_buff *skb, - u16 vid); -extern struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, - struct sk_buff *skb, u16 vid); -extern void br_multicast_add_port(struct net_bridge_port *port); -extern void br_multicast_del_port(struct net_bridge_port *port); -extern void br_multicast_enable_port(struct net_bridge_port *port); -extern void br_multicast_disable_port(struct net_bridge_port *port); -extern void br_multicast_init(struct net_bridge *br); -extern void br_multicast_open(struct net_bridge *br); -extern void br_multicast_stop(struct net_bridge *br); -extern void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, - struct sk_buff *skb); -extern void br_multicast_forward(struct net_bridge_mdb_entry *mdst, - struct sk_buff *skb, struct sk_buff *skb2); -extern int br_multicast_set_router(struct net_bridge *br, unsigned long val); -extern int br_multicast_set_port_router(struct net_bridge_port *p, - unsigned long val); -extern int br_multicast_toggle(struct net_bridge *br, unsigned long val); -extern int br_multicast_set_querier(struct net_bridge *br, unsigned long val); -extern int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val); -extern struct net_bridge_mdb_entry *br_mdb_ip_get( - struct net_bridge_mdb_htable *mdb, - struct br_ip *dst); -extern struct net_bridge_mdb_entry *br_multicast_new_group(struct net_bridge *br, - struct net_bridge_port *port, struct br_ip *group); -extern void br_multicast_free_pg(struct rcu_head *head); -extern struct net_bridge_port_group *br_multicast_new_port_group( - struct net_bridge_port *port, - struct br_ip *group, - struct net_bridge_port_group __rcu *next, - unsigned char state); -extern void br_mdb_init(void); -extern void br_mdb_uninit(void); -extern void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, - struct br_ip *group, int type); +int br_multicast_rcv(struct net_bridge *br, struct net_bridge_port *port, + struct sk_buff *skb, u16 vid); +struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, + struct sk_buff *skb, u16 vid); +void br_multicast_add_port(struct net_bridge_port *port); +void br_multicast_del_port(struct net_bridge_port *port); +void br_multicast_enable_port(struct net_bridge_port *port); +void br_multicast_disable_port(struct net_bridge_port *port); +void br_multicast_init(struct net_bridge *br); +void br_multicast_open(struct net_bridge *br); +void br_multicast_stop(struct net_bridge *br); +void br_multicast_deliver(struct net_bridge_mdb_entry *mdst, + struct sk_buff *skb); +void br_multicast_forward(struct net_bridge_mdb_entry *mdst, + struct sk_buff *skb, struct sk_buff *skb2); +int br_multicast_set_router(struct net_bridge *br, unsigned long val); +int br_multicast_set_port_router(struct net_bridge_port *p, unsigned long val); +int br_multicast_toggle(struct net_bridge *br, unsigned long val); +int br_multicast_set_querier(struct net_bridge *br, unsigned long val); +int br_multicast_set_hash_max(struct net_bridge *br, unsigned long val); +struct net_bridge_mdb_entry * +br_mdb_ip_get(struct net_bridge_mdb_htable *mdb, struct br_ip *dst); +struct net_bridge_mdb_entry * +br_multicast_new_group(struct net_bridge *br, struct net_bridge_port *port, + struct br_ip *group); +void br_multicast_free_pg(struct rcu_head *head); +struct net_bridge_port_group * +br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group, + struct net_bridge_port_group __rcu *next, + unsigned char state); +void br_mdb_init(void); +void br_mdb_uninit(void); +void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port, + struct br_ip *group, int type); #define mlock_dereference(X, br) \ rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) @@ -592,22 +573,21 @@ static inline void br_mdb_uninit(void) /* br_vlan.c */ #ifdef CONFIG_BRIDGE_VLAN_FILTERING -extern bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, - struct sk_buff *skb, u16 *vid); -extern bool br_allowed_egress(struct net_bridge *br, - const struct net_port_vlans *v, - const struct sk_buff *skb); -extern struct sk_buff *br_handle_vlan(struct net_bridge *br, - const struct net_port_vlans *v, - struct sk_buff *skb); -extern int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); -extern int br_vlan_delete(struct net_bridge *br, u16 vid); -extern void br_vlan_flush(struct net_bridge *br); -extern int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); -extern int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); -extern int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); -extern void nbp_vlan_flush(struct net_bridge_port *port); -extern bool nbp_vlan_find(struct net_bridge_port *port, u16 vid); +bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v, + struct sk_buff *skb, u16 *vid); +bool br_allowed_egress(struct net_bridge *br, const struct net_port_vlans *v, + const struct sk_buff *skb); +struct sk_buff *br_handle_vlan(struct net_bridge *br, + const struct net_port_vlans *v, + struct sk_buff *skb); +int br_vlan_add(struct net_bridge *br, u16 vid, u16 flags); +int br_vlan_delete(struct net_bridge *br, u16 vid); +void br_vlan_flush(struct net_bridge *br); +int br_vlan_filter_toggle(struct net_bridge *br, unsigned long val); +int nbp_vlan_add(struct net_bridge_port *port, u16 vid, u16 flags); +int nbp_vlan_delete(struct net_bridge_port *port, u16 vid); +void nbp_vlan_flush(struct net_bridge_port *port); +bool nbp_vlan_find(struct net_bridge_port *port, u16 vid); static inline struct net_port_vlans *br_get_vlan_info( const struct net_bridge *br) @@ -726,9 +706,9 @@ static inline u16 br_get_pvid(const struct net_port_vlans *v) /* br_netfilter.c */ #ifdef CONFIG_BRIDGE_NETFILTER -extern int br_netfilter_init(void); -extern void br_netfilter_fini(void); -extern void br_netfilter_rtable_init(struct net_bridge *); +int br_netfilter_init(void); +void br_netfilter_fini(void); +void br_netfilter_rtable_init(struct net_bridge *); #else #define br_netfilter_init() (0) #define br_netfilter_fini() do { } while(0) @@ -736,43 +716,39 @@ extern void br_netfilter_rtable_init(struct net_bridge *); #endif /* br_stp.c */ -extern void br_log_state(const struct net_bridge_port *p); -extern struct net_bridge_port *br_get_port(struct net_bridge *br, - u16 port_no); -extern void br_init_port(struct net_bridge_port *p); -extern void br_become_designated_port(struct net_bridge_port *p); +void br_log_state(const struct net_bridge_port *p); +struct net_bridge_port *br_get_port(struct net_bridge *br, u16 port_no); +void br_init_port(struct net_bridge_port *p); +void br_become_designated_port(struct net_bridge_port *p); -extern void __br_set_forward_delay(struct net_bridge *br, unsigned long t); -extern int br_set_forward_delay(struct net_bridge *br, unsigned long x); -extern int br_set_hello_time(struct net_bridge *br, unsigned long x); -extern int br_set_max_age(struct net_bridge *br, unsigned long x); +void __br_set_forward_delay(struct net_bridge *br, unsigned long t); +int br_set_forward_delay(struct net_bridge *br, unsigned long x); +int br_set_hello_time(struct net_bridge *br, unsigned long x); +int br_set_max_age(struct net_bridge *br, unsigned long x); /* br_stp_if.c */ -extern void br_stp_enable_bridge(struct net_bridge *br); -extern void br_stp_disable_bridge(struct net_bridge *br); -extern void br_stp_set_enabled(struct net_bridge *br, unsigned long val); -extern void br_stp_enable_port(struct net_bridge_port *p); -extern void br_stp_disable_port(struct net_bridge_port *p); -extern bool br_stp_recalculate_bridge_id(struct net_bridge *br); -extern void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *a); -extern void br_stp_set_bridge_priority(struct net_bridge *br, - u16 newprio); -extern int br_stp_set_port_priority(struct net_bridge_port *p, - unsigned long newprio); -extern int br_stp_set_path_cost(struct net_bridge_port *p, - unsigned long path_cost); -extern ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id); +void br_stp_enable_bridge(struct net_bridge *br); +void br_stp_disable_bridge(struct net_bridge *br); +void br_stp_set_enabled(struct net_bridge *br, unsigned long val); +void br_stp_enable_port(struct net_bridge_port *p); +void br_stp_disable_port(struct net_bridge_port *p); +bool br_stp_recalculate_bridge_id(struct net_bridge *br); +void br_stp_change_bridge_id(struct net_bridge *br, const unsigned char *a); +void br_stp_set_bridge_priority(struct net_bridge *br, u16 newprio); +int br_stp_set_port_priority(struct net_bridge_port *p, unsigned long newprio); +int br_stp_set_path_cost(struct net_bridge_port *p, unsigned long path_cost); +ssize_t br_show_bridge_id(char *buf, const struct bridge_id *id); /* br_stp_bpdu.c */ struct stp_proto; -extern void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, - struct net_device *dev); +void br_stp_rcv(const struct stp_proto *proto, struct sk_buff *skb, + struct net_device *dev); /* br_stp_timer.c */ -extern void br_stp_timer_init(struct net_bridge *br); -extern void br_stp_port_timer_init(struct net_bridge_port *p); -extern unsigned long br_timer_value(const struct timer_list *timer); +void br_stp_timer_init(struct net_bridge *br); +void br_stp_port_timer_init(struct net_bridge_port *p); +unsigned long br_timer_value(const struct timer_list *timer); /* br.c */ #if IS_ENABLED(CONFIG_ATM_LANE) @@ -781,23 +757,23 @@ extern int (*br_fdb_test_addr_hook)(struct net_device *dev, unsigned char *addr) /* br_netlink.c */ extern struct rtnl_link_ops br_link_ops; -extern int br_netlink_init(void); -extern void br_netlink_fini(void); -extern void br_ifinfo_notify(int event, struct net_bridge_port *port); -extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); -extern int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg); -extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, - struct net_device *dev, u32 filter_mask); +int br_netlink_init(void); +void br_netlink_fini(void); +void br_ifinfo_notify(int event, struct net_bridge_port *port); +int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); +int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg); +int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct net_device *dev, + u32 filter_mask); #ifdef CONFIG_SYSFS /* br_sysfs_if.c */ extern const struct sysfs_ops brport_sysfs_ops; -extern int br_sysfs_addif(struct net_bridge_port *p); -extern int br_sysfs_renameif(struct net_bridge_port *p); +int br_sysfs_addif(struct net_bridge_port *p); +int br_sysfs_renameif(struct net_bridge_port *p); /* br_sysfs_br.c */ -extern int br_sysfs_addbr(struct net_device *dev); -extern void br_sysfs_delbr(struct net_device *dev); +int br_sysfs_addbr(struct net_device *dev); +void br_sysfs_delbr(struct net_device *dev); #else diff --git a/net/bridge/br_private_stp.h b/net/bridge/br_private_stp.h index 0c0fe36e7aa92eebd67e3e91d07c440a5310090b..2fe910c4e1700776c33275f0ae277d354e348f6c 100644 --- a/net/bridge/br_private_stp.h +++ b/net/bridge/br_private_stp.h @@ -51,19 +51,19 @@ static inline int br_is_designated_port(const struct net_bridge_port *p) /* br_stp.c */ -extern void br_become_root_bridge(struct net_bridge *br); -extern void br_config_bpdu_generation(struct net_bridge *); -extern void br_configuration_update(struct net_bridge *); -extern void br_port_state_selection(struct net_bridge *); -extern void br_received_config_bpdu(struct net_bridge_port *p, - const struct br_config_bpdu *bpdu); -extern void br_received_tcn_bpdu(struct net_bridge_port *p); -extern void br_transmit_config(struct net_bridge_port *p); -extern void br_transmit_tcn(struct net_bridge *br); -extern void br_topology_change_detection(struct net_bridge *br); +void br_become_root_bridge(struct net_bridge *br); +void br_config_bpdu_generation(struct net_bridge *); +void br_configuration_update(struct net_bridge *); +void br_port_state_selection(struct net_bridge *); +void br_received_config_bpdu(struct net_bridge_port *p, + const struct br_config_bpdu *bpdu); +void br_received_tcn_bpdu(struct net_bridge_port *p); +void br_transmit_config(struct net_bridge_port *p); +void br_transmit_tcn(struct net_bridge *br); +void br_topology_change_detection(struct net_bridge *br); /* br_stp_bpdu.c */ -extern void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *); -extern void br_send_tcn_bpdu(struct net_bridge_port *); +void br_send_config_bpdu(struct net_bridge_port *, struct br_config_bpdu *); +void br_send_tcn_bpdu(struct net_bridge_port *); #endif diff --git a/net/bridge/netfilter/Kconfig b/net/bridge/netfilter/Kconfig index a9aff9c7d0273b2a41ef9d122ac38e769956a640..5ca74a0e595fe5ce782985f6b2e17f185b5a52ec 100644 --- a/net/bridge/netfilter/Kconfig +++ b/net/bridge/netfilter/Kconfig @@ -1,6 +1,10 @@ # # Bridge netfilter configuration # +# +config NF_TABLES_BRIDGE + depends on NF_TABLES + tristate "Ethernet Bridge nf_tables support" menuconfig BRIDGE_NF_EBTABLES tristate "Ethernet Bridge tables (ebtables) support" diff --git a/net/bridge/netfilter/Makefile b/net/bridge/netfilter/Makefile index 0718699540b023fd6d95be217b79e4bfe5029821..ea7629f58b3d1c44e28524df8a0937de3a18546b 100644 --- a/net/bridge/netfilter/Makefile +++ b/net/bridge/netfilter/Makefile @@ -2,6 +2,8 @@ # Makefile for the netfilter modules for Link Layer filtering on a bridge. # +obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o + obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o # tables diff --git a/net/bridge/netfilter/ebt_among.c b/net/bridge/netfilter/ebt_among.c index 8b84c581be3082ea4c8a6a21a362a3077ab17751..3fb3c848affef74249a1fd9ed610aea7d1db5764 100644 --- a/net/bridge/netfilter/ebt_among.c +++ b/net/bridge/netfilter/ebt_among.c @@ -28,7 +28,7 @@ static bool ebt_mac_wormhash_contains(const struct ebt_mac_wormhash *wh, uint32_t cmp[2] = { 0, 0 }; int key = ((const unsigned char *)mac)[5]; - memcpy(((char *) cmp) + 2, mac, 6); + memcpy(((char *) cmp) + 2, mac, ETH_ALEN); start = wh->table[key]; limit = wh->table[key + 1]; if (ip) { diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index 94b2b700cff8d0de28058497f7b863ce685633a2..bb2da7b706e7214f05fb7d56f87142e5ed5a5667 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -60,17 +60,21 @@ static const struct ebt_table frame_filter = }; static unsigned int -ebt_in_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_in_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + int (*okfn)(struct sk_buff *)) { - return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_filter); + return ebt_do_table(ops->hooknum, skb, in, out, + dev_net(in)->xt.frame_filter); } static unsigned int -ebt_out_hook(unsigned int hook, struct sk_buff *skb, const struct net_device *in, - const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_out_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + int (*okfn)(struct sk_buff *)) { - return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_filter); + return ebt_do_table(ops->hooknum, skb, in, out, + dev_net(out)->xt.frame_filter); } static struct nf_hook_ops ebt_ops_filter[] __read_mostly = { diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 322555acdd4009aaa677147f7a33e558d82a67cc..bd238f1f105b94ac5f8db9982b7738e255f42630 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -60,17 +60,21 @@ static struct ebt_table frame_nat = }; static unsigned int -ebt_nat_in(unsigned int hook, struct sk_buff *skb, const struct net_device *in - , const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_nat_in(const struct nf_hook_ops *ops, struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + int (*okfn)(struct sk_buff *)) { - return ebt_do_table(hook, skb, in, out, dev_net(in)->xt.frame_nat); + return ebt_do_table(ops->hooknum, skb, in, out, + dev_net(in)->xt.frame_nat); } static unsigned int -ebt_nat_out(unsigned int hook, struct sk_buff *skb, const struct net_device *in - , const struct net_device *out, int (*okfn)(struct sk_buff *)) +ebt_nat_out(const struct nf_hook_ops *ops, struct sk_buff *skb, + const struct net_device *in, const struct net_device *out, + int (*okfn)(struct sk_buff *)) { - return ebt_do_table(hook, skb, in, out, dev_net(out)->xt.frame_nat); + return ebt_do_table(ops->hooknum, skb, in, out, + dev_net(out)->xt.frame_nat); } static struct nf_hook_ops ebt_ops_nat[] __read_mostly = { diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c new file mode 100644 index 0000000000000000000000000000000000000000..cf54b22818c889172b9207fcc2f3898c454c37e3 --- /dev/null +++ b/net/bridge/netfilter/nf_tables_bridge.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2008 Patrick McHardy + * Copyright (c) 2013 Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include + +static struct nft_af_info nft_af_bridge __read_mostly = { + .family = NFPROTO_BRIDGE, + .nhooks = NF_BR_NUMHOOKS, + .owner = THIS_MODULE, +}; + +static int nf_tables_bridge_init_net(struct net *net) +{ + net->nft.bridge = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); + if (net->nft.bridge == NULL) + return -ENOMEM; + + memcpy(net->nft.bridge, &nft_af_bridge, sizeof(nft_af_bridge)); + + if (nft_register_afinfo(net, net->nft.bridge) < 0) + goto err; + + return 0; +err: + kfree(net->nft.bridge); + return -ENOMEM; +} + +static void nf_tables_bridge_exit_net(struct net *net) +{ + nft_unregister_afinfo(net->nft.bridge); + kfree(net->nft.bridge); +} + +static struct pernet_operations nf_tables_bridge_net_ops = { + .init = nf_tables_bridge_init_net, + .exit = nf_tables_bridge_exit_net, +}; + +static unsigned int +nft_do_chain_bridge(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct nft_pktinfo pkt; + + nft_set_pktinfo(&pkt, ops, skb, in, out); + + return nft_do_chain_pktinfo(&pkt, ops); +} + +static struct nf_chain_type filter_bridge = { + .family = NFPROTO_BRIDGE, + .name = "filter", + .type = NFT_CHAIN_T_DEFAULT, + .hook_mask = (1 << NF_BR_LOCAL_IN) | + (1 << NF_BR_FORWARD) | + (1 << NF_BR_LOCAL_OUT), + .fn = { + [NF_BR_LOCAL_IN] = nft_do_chain_bridge, + [NF_BR_FORWARD] = nft_do_chain_bridge, + [NF_BR_LOCAL_OUT] = nft_do_chain_bridge, + }, +}; + +static int __init nf_tables_bridge_init(void) +{ + int ret; + + nft_register_chain_type(&filter_bridge); + ret = register_pernet_subsys(&nf_tables_bridge_net_ops); + if (ret < 0) + nft_unregister_chain_type(&filter_bridge); + + return ret; +} + +static void __exit nf_tables_bridge_exit(void) +{ + unregister_pernet_subsys(&nf_tables_bridge_net_ops); + nft_unregister_chain_type(&filter_bridge); +} + +module_init(nf_tables_bridge_init); +module_exit(nf_tables_bridge_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_FAMILY(AF_BRIDGE); diff --git a/net/caif/cfpkt_skbuff.c b/net/caif/cfpkt_skbuff.c index 6493351f39c6192b20a7a31e94c9b0cbb5268747..1be0b521ac490143e60e3b2d5b02d7fd68c24087 100644 --- a/net/caif/cfpkt_skbuff.c +++ b/net/caif/cfpkt_skbuff.c @@ -203,20 +203,10 @@ int cfpkt_add_body(struct cfpkt *pkt, const void *data, u16 len) PKT_ERROR(pkt, "cow failed\n"); return -EPROTO; } - /* - * Is the SKB non-linear after skb_cow_data()? If so, we are - * going to add data to the last SKB, so we need to adjust - * lengths of the top SKB. - */ - if (lastskb != skb) { - pr_warn("Packet is non-linear\n"); - skb->len += len; - skb->data_len += len; - } } /* All set to put the last SKB and optionally write data there. */ - to = skb_put(lastskb, len); + to = pskb_put(skb, lastskb, len); if (likely(data)) memcpy(to, data, len); return 0; diff --git a/net/can/af_can.h b/net/can/af_can.h index 1dccb4c33894af7074ee85e320e32c035dbfa368..6de58b40535cc309e59f1a61ed624c930b7c7ffe 100644 --- a/net/can/af_can.h +++ b/net/can/af_can.h @@ -108,9 +108,9 @@ struct s_pstats { extern struct dev_rcv_lists can_rx_alldev_list; /* function prototypes for the CAN networklayer procfs (proc.c) */ -extern void can_init_proc(void); -extern void can_remove_proc(void); -extern void can_stat_update(unsigned long data); +void can_init_proc(void); +void can_remove_proc(void); +void can_stat_update(unsigned long data); /* structures and variables from af_can.c needed in proc.c for reading */ extern struct timer_list can_stattimer; /* timer for statistics update */ diff --git a/net/ceph/auth_none.h b/net/ceph/auth_none.h index ed7d088b1bc92e14372e2f24c8f5016750e07014..059a3ce4b53f48425336bf4b792d2ac6a5c6e849 100644 --- a/net/ceph/auth_none.h +++ b/net/ceph/auth_none.h @@ -23,7 +23,7 @@ struct ceph_auth_none_info { struct ceph_none_authorizer au; /* we only need one; it's static */ }; -extern int ceph_auth_none_init(struct ceph_auth_client *ac); +int ceph_auth_none_init(struct ceph_auth_client *ac); #endif diff --git a/net/ceph/auth_x.h b/net/ceph/auth_x.h index c5a058da7ac8e7ae6a51f695b2a9d1b9362cdab3..65ee72082d99866d0f98057f15c4f181b21d971c 100644 --- a/net/ceph/auth_x.h +++ b/net/ceph/auth_x.h @@ -45,7 +45,7 @@ struct ceph_x_info { struct ceph_x_authorizer auth_authorizer; }; -extern int ceph_x_init(struct ceph_auth_client *ac); +int ceph_x_init(struct ceph_auth_client *ac); #endif diff --git a/net/ceph/crypto.h b/net/ceph/crypto.h index 3572dc518bc984aece38fb8bd9f8cbe7b1b5c239..d1498224c49d4c6c18e082ea908cb3e5a8fed30c 100644 --- a/net/ceph/crypto.h +++ b/net/ceph/crypto.h @@ -20,34 +20,32 @@ static inline void ceph_crypto_key_destroy(struct ceph_crypto_key *key) kfree(key->key); } -extern int ceph_crypto_key_clone(struct ceph_crypto_key *dst, - const struct ceph_crypto_key *src); -extern int ceph_crypto_key_encode(struct ceph_crypto_key *key, - void **p, void *end); -extern int ceph_crypto_key_decode(struct ceph_crypto_key *key, - void **p, void *end); -extern int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in); +int ceph_crypto_key_clone(struct ceph_crypto_key *dst, + const struct ceph_crypto_key *src); +int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end); +int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end); +int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in); /* crypto.c */ -extern int ceph_decrypt(struct ceph_crypto_key *secret, - void *dst, size_t *dst_len, - const void *src, size_t src_len); -extern int ceph_encrypt(struct ceph_crypto_key *secret, - void *dst, size_t *dst_len, - const void *src, size_t src_len); -extern int ceph_decrypt2(struct ceph_crypto_key *secret, - void *dst1, size_t *dst1_len, - void *dst2, size_t *dst2_len, - const void *src, size_t src_len); -extern int ceph_encrypt2(struct ceph_crypto_key *secret, - void *dst, size_t *dst_len, - const void *src1, size_t src1_len, - const void *src2, size_t src2_len); -extern int ceph_crypto_init(void); -extern void ceph_crypto_shutdown(void); +int ceph_decrypt(struct ceph_crypto_key *secret, + void *dst, size_t *dst_len, + const void *src, size_t src_len); +int ceph_encrypt(struct ceph_crypto_key *secret, + void *dst, size_t *dst_len, + const void *src, size_t src_len); +int ceph_decrypt2(struct ceph_crypto_key *secret, + void *dst1, size_t *dst1_len, + void *dst2, size_t *dst2_len, + const void *src, size_t src_len); +int ceph_encrypt2(struct ceph_crypto_key *secret, + void *dst, size_t *dst_len, + const void *src1, size_t src1_len, + const void *src2, size_t src2_len); +int ceph_crypto_init(void); +void ceph_crypto_shutdown(void); /* armor.c */ -extern int ceph_armor(char *dst, const char *src, const char *end); -extern int ceph_unarmor(char *dst, const char *src, const char *end); +int ceph_armor(char *dst, const char *src, const char *end); +int ceph_unarmor(char *dst, const char *src, const char *end); #endif diff --git a/net/core/datagram.c b/net/core/datagram.c index af814e764206f6d4221b91f03dc2e3bfa1da05f2..a16ed7bbe37689007e1fabed938076619c65a2e2 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -577,7 +577,7 @@ EXPORT_SYMBOL(skb_copy_datagram_from_iovec); /** * zerocopy_sg_from_iovec - Build a zerocopy datagram from an iovec * @skb: buffer to copy - * @from: io vector to copy to + * @from: io vector to copy from * @offset: offset in the io vector to start copying from * @count: amount of vectors to copy to buffer from * diff --git a/net/core/dev.c b/net/core/dev.c index 3430b1ed12e5f93b14ca1f377f848fa29a81be00..8ffc52e01ece35db173fee05a3fdc160d733b684 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1203,7 +1203,7 @@ void netdev_state_change(struct net_device *dev) { if (dev->flags & IFF_UP) { call_netdevice_notifiers(NETDEV_CHANGE, dev); - rtmsg_ifinfo(RTM_NEWLINK, dev, 0); + rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); } } EXPORT_SYMBOL(netdev_state_change); @@ -1293,7 +1293,7 @@ int dev_open(struct net_device *dev) if (ret < 0) return ret; - rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING); + rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL); call_netdevice_notifiers(NETDEV_UP, dev); return ret; @@ -1307,7 +1307,7 @@ static int __dev_close_many(struct list_head *head) ASSERT_RTNL(); might_sleep(); - list_for_each_entry(dev, head, unreg_list) { + list_for_each_entry(dev, head, close_list) { call_netdevice_notifiers(NETDEV_GOING_DOWN, dev); clear_bit(__LINK_STATE_START, &dev->state); @@ -1323,7 +1323,7 @@ static int __dev_close_many(struct list_head *head) dev_deactivate_many(head); - list_for_each_entry(dev, head, unreg_list) { + list_for_each_entry(dev, head, close_list) { const struct net_device_ops *ops = dev->netdev_ops; /* @@ -1351,7 +1351,7 @@ static int __dev_close(struct net_device *dev) /* Temporarily disable netpoll until the interface is down */ netpoll_rx_disable(dev); - list_add(&dev->unreg_list, &single); + list_add(&dev->close_list, &single); retval = __dev_close_many(&single); list_del(&single); @@ -1362,21 +1362,20 @@ static int __dev_close(struct net_device *dev) static int dev_close_many(struct list_head *head) { struct net_device *dev, *tmp; - LIST_HEAD(tmp_list); - list_for_each_entry_safe(dev, tmp, head, unreg_list) + /* Remove the devices that don't need to be closed */ + list_for_each_entry_safe(dev, tmp, head, close_list) if (!(dev->flags & IFF_UP)) - list_move(&dev->unreg_list, &tmp_list); + list_del_init(&dev->close_list); __dev_close_many(head); - list_for_each_entry(dev, head, unreg_list) { - rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING); + list_for_each_entry_safe(dev, tmp, head, close_list) { + rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING, GFP_KERNEL); call_netdevice_notifiers(NETDEV_DOWN, dev); + list_del_init(&dev->close_list); } - /* rollback_registered_many needs the complete original list */ - list_splice(&tmp_list, head); return 0; } @@ -1397,7 +1396,7 @@ int dev_close(struct net_device *dev) /* Block netpoll rx while the interface is going down */ netpoll_rx_disable(dev); - list_add(&dev->unreg_list, &single); + list_add(&dev->close_list, &single); dev_close_many(&single); list_del(&single); @@ -2378,6 +2377,8 @@ struct sk_buff *__skb_gso_segment(struct sk_buff *skb, } SKB_GSO_CB(skb)->mac_offset = skb_headroom(skb); + SKB_GSO_CB(skb)->encap_level = 0; + skb_reset_mac_header(skb); skb_reset_mac_len(skb); @@ -2537,7 +2538,7 @@ static inline int skb_needs_linearize(struct sk_buff *skb, } int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, - struct netdev_queue *txq) + struct netdev_queue *txq, void *accel_priv) { const struct net_device_ops *ops = dev->netdev_ops; int rc = NETDEV_TX_OK; @@ -2603,9 +2604,13 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev, dev_queue_xmit_nit(skb, dev); skb_len = skb->len; - rc = ops->ndo_start_xmit(skb, dev); + if (accel_priv) + rc = ops->ndo_dfwd_start_xmit(skb, dev, accel_priv); + else + rc = ops->ndo_start_xmit(skb, dev); + trace_net_dev_xmit(skb, rc, dev, skb_len); - if (rc == NETDEV_TX_OK) + if (rc == NETDEV_TX_OK && txq) txq_trans_update(txq); return rc; } @@ -2621,7 +2626,10 @@ gso: dev_queue_xmit_nit(nskb, dev); skb_len = nskb->len; - rc = ops->ndo_start_xmit(nskb, dev); + if (accel_priv) + rc = ops->ndo_dfwd_start_xmit(nskb, dev, accel_priv); + else + rc = ops->ndo_start_xmit(nskb, dev); trace_net_dev_xmit(nskb, rc, dev, skb_len); if (unlikely(rc != NETDEV_TX_OK)) { if (rc & ~NETDEV_TX_MASK) @@ -2646,6 +2654,7 @@ out_kfree_skb: out: return rc; } +EXPORT_SYMBOL_GPL(dev_hard_start_xmit); static void qdisc_pkt_len_init(struct sk_buff *skb) { @@ -2853,7 +2862,7 @@ int dev_queue_xmit(struct sk_buff *skb) if (!netif_xmit_stopped(txq)) { __this_cpu_inc(xmit_recursion); - rc = dev_hard_start_xmit(skb, dev, txq); + rc = dev_hard_start_xmit(skb, dev, txq, NULL); __this_cpu_dec(xmit_recursion); if (dev_xmit_complete(rc)) { HARD_TX_UNLOCK(dev, txq); @@ -4374,42 +4383,40 @@ struct netdev_adjacent { /* upper master flag, there can only be one master device per list */ bool master; - /* indicates that this dev is our first-level lower/upper device */ - bool neighbour; - /* counter for the number of times this device was added to us */ u16 ref_nr; + /* private field for the users */ + void *private; + struct list_head list; struct rcu_head rcu; }; -static struct netdev_adjacent *__netdev_find_adj(struct net_device *dev, - struct net_device *adj_dev, - bool upper) +static struct netdev_adjacent *__netdev_find_adj_rcu(struct net_device *dev, + struct net_device *adj_dev, + struct list_head *adj_list) { struct netdev_adjacent *adj; - struct list_head *dev_list; - - dev_list = upper ? &dev->upper_dev_list : &dev->lower_dev_list; - list_for_each_entry(adj, dev_list, list) { + list_for_each_entry_rcu(adj, adj_list, list) { if (adj->dev == adj_dev) return adj; } return NULL; } -static inline struct netdev_adjacent *__netdev_find_upper(struct net_device *dev, - struct net_device *udev) +static struct netdev_adjacent *__netdev_find_adj(struct net_device *dev, + struct net_device *adj_dev, + struct list_head *adj_list) { - return __netdev_find_adj(dev, udev, true); -} + struct netdev_adjacent *adj; -static inline struct netdev_adjacent *__netdev_find_lower(struct net_device *dev, - struct net_device *ldev) -{ - return __netdev_find_adj(dev, ldev, false); + list_for_each_entry(adj, adj_list, list) { + if (adj->dev == adj_dev) + return adj; + } + return NULL; } /** @@ -4426,7 +4433,7 @@ bool netdev_has_upper_dev(struct net_device *dev, { ASSERT_RTNL(); - return __netdev_find_upper(dev, upper_dev); + return __netdev_find_adj(dev, upper_dev, &dev->all_adj_list.upper); } EXPORT_SYMBOL(netdev_has_upper_dev); @@ -4441,7 +4448,7 @@ bool netdev_has_any_upper_dev(struct net_device *dev) { ASSERT_RTNL(); - return !list_empty(&dev->upper_dev_list); + return !list_empty(&dev->all_adj_list.upper); } EXPORT_SYMBOL(netdev_has_any_upper_dev); @@ -4458,10 +4465,10 @@ struct net_device *netdev_master_upper_dev_get(struct net_device *dev) ASSERT_RTNL(); - if (list_empty(&dev->upper_dev_list)) + if (list_empty(&dev->adj_list.upper)) return NULL; - upper = list_first_entry(&dev->upper_dev_list, + upper = list_first_entry(&dev->adj_list.upper, struct netdev_adjacent, list); if (likely(upper->master)) return upper->dev; @@ -4469,15 +4476,26 @@ struct net_device *netdev_master_upper_dev_get(struct net_device *dev) } EXPORT_SYMBOL(netdev_master_upper_dev_get); -/* netdev_upper_get_next_dev_rcu - Get the next dev from upper list +void *netdev_adjacent_get_private(struct list_head *adj_list) +{ + struct netdev_adjacent *adj; + + adj = list_entry(adj_list, struct netdev_adjacent, list); + + return adj->private; +} +EXPORT_SYMBOL(netdev_adjacent_get_private); + +/** + * netdev_all_upper_get_next_dev_rcu - Get the next dev from upper list * @dev: device * @iter: list_head ** of the current position * * Gets the next device from the dev's upper list, starting from iter * position. The caller must hold RCU read lock. */ -struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev, - struct list_head **iter) +struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev, + struct list_head **iter) { struct netdev_adjacent *upper; @@ -4485,14 +4503,71 @@ struct net_device *netdev_upper_get_next_dev_rcu(struct net_device *dev, upper = list_entry_rcu((*iter)->next, struct netdev_adjacent, list); - if (&upper->list == &dev->upper_dev_list) + if (&upper->list == &dev->all_adj_list.upper) return NULL; *iter = &upper->list; return upper->dev; } -EXPORT_SYMBOL(netdev_upper_get_next_dev_rcu); +EXPORT_SYMBOL(netdev_all_upper_get_next_dev_rcu); + +/** + * netdev_lower_get_next_private - Get the next ->private from the + * lower neighbour list + * @dev: device + * @iter: list_head ** of the current position + * + * Gets the next netdev_adjacent->private from the dev's lower neighbour + * list, starting from iter position. The caller must hold either hold the + * RTNL lock or its own locking that guarantees that the neighbour lower + * list will remain unchainged. + */ +void *netdev_lower_get_next_private(struct net_device *dev, + struct list_head **iter) +{ + struct netdev_adjacent *lower; + + lower = list_entry(*iter, struct netdev_adjacent, list); + + if (&lower->list == &dev->adj_list.lower) + return NULL; + + if (iter) + *iter = lower->list.next; + + return lower->private; +} +EXPORT_SYMBOL(netdev_lower_get_next_private); + +/** + * netdev_lower_get_next_private_rcu - Get the next ->private from the + * lower neighbour list, RCU + * variant + * @dev: device + * @iter: list_head ** of the current position + * + * Gets the next netdev_adjacent->private from the dev's lower neighbour + * list, starting from iter position. The caller must hold RCU read lock. + */ +void *netdev_lower_get_next_private_rcu(struct net_device *dev, + struct list_head **iter) +{ + struct netdev_adjacent *lower; + + WARN_ON_ONCE(!rcu_read_lock_held()); + + lower = list_entry_rcu((*iter)->next, struct netdev_adjacent, list); + + if (&lower->list == &dev->adj_list.lower) + return NULL; + + if (iter) + *iter = &lower->list; + + return lower->private; +} +EXPORT_SYMBOL(netdev_lower_get_next_private_rcu); /** * netdev_master_upper_dev_get_rcu - Get master upper device @@ -4505,7 +4580,7 @@ struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev) { struct netdev_adjacent *upper; - upper = list_first_or_null_rcu(&dev->upper_dev_list, + upper = list_first_or_null_rcu(&dev->adj_list.upper, struct netdev_adjacent, list); if (upper && likely(upper->master)) return upper->dev; @@ -4515,15 +4590,16 @@ EXPORT_SYMBOL(netdev_master_upper_dev_get_rcu); static int __netdev_adjacent_dev_insert(struct net_device *dev, struct net_device *adj_dev, - bool neighbour, bool master, - bool upper) + struct list_head *dev_list, + void *private, bool master) { struct netdev_adjacent *adj; + char linkname[IFNAMSIZ+7]; + int ret; - adj = __netdev_find_adj(dev, adj_dev, upper); + adj = __netdev_find_adj(dev, adj_dev, dev_list); if (adj) { - BUG_ON(neighbour); adj->ref_nr++; return 0; } @@ -4534,124 +4610,179 @@ static int __netdev_adjacent_dev_insert(struct net_device *dev, adj->dev = adj_dev; adj->master = master; - adj->neighbour = neighbour; adj->ref_nr = 1; - + adj->private = private; dev_hold(adj_dev); - pr_debug("dev_hold for %s, because of %s link added from %s to %s\n", - adj_dev->name, upper ? "upper" : "lower", dev->name, - adj_dev->name); - if (!upper) { - list_add_tail_rcu(&adj->list, &dev->lower_dev_list); - return 0; + pr_debug("dev_hold for %s, because of link added from %s to %s\n", + adj_dev->name, dev->name, adj_dev->name); + + if (dev_list == &dev->adj_list.lower) { + sprintf(linkname, "lower_%s", adj_dev->name); + ret = sysfs_create_link(&(dev->dev.kobj), + &(adj_dev->dev.kobj), linkname); + if (ret) + goto free_adj; + } else if (dev_list == &dev->adj_list.upper) { + sprintf(linkname, "upper_%s", adj_dev->name); + ret = sysfs_create_link(&(dev->dev.kobj), + &(adj_dev->dev.kobj), linkname); + if (ret) + goto free_adj; } - /* Ensure that master upper link is always the first item in list. */ - if (master) - list_add_rcu(&adj->list, &dev->upper_dev_list); - else - list_add_tail_rcu(&adj->list, &dev->upper_dev_list); + /* Ensure that master link is always the first item in list. */ + if (master) { + ret = sysfs_create_link(&(dev->dev.kobj), + &(adj_dev->dev.kobj), "master"); + if (ret) + goto remove_symlinks; + + list_add_rcu(&adj->list, dev_list); + } else { + list_add_tail_rcu(&adj->list, dev_list); + } return 0; -} -static inline int __netdev_upper_dev_insert(struct net_device *dev, - struct net_device *udev, - bool master, bool neighbour) -{ - return __netdev_adjacent_dev_insert(dev, udev, neighbour, master, - true); -} +remove_symlinks: + if (dev_list == &dev->adj_list.lower) { + sprintf(linkname, "lower_%s", adj_dev->name); + sysfs_remove_link(&(dev->dev.kobj), linkname); + } else if (dev_list == &dev->adj_list.upper) { + sprintf(linkname, "upper_%s", adj_dev->name); + sysfs_remove_link(&(dev->dev.kobj), linkname); + } -static inline int __netdev_lower_dev_insert(struct net_device *dev, - struct net_device *ldev, - bool neighbour) -{ - return __netdev_adjacent_dev_insert(dev, ldev, neighbour, false, - false); +free_adj: + kfree(adj); + dev_put(adj_dev); + + return ret; } void __netdev_adjacent_dev_remove(struct net_device *dev, - struct net_device *adj_dev, bool upper) + struct net_device *adj_dev, + struct list_head *dev_list) { struct netdev_adjacent *adj; + char linkname[IFNAMSIZ+7]; - if (upper) - adj = __netdev_find_upper(dev, adj_dev); - else - adj = __netdev_find_lower(dev, adj_dev); + adj = __netdev_find_adj(dev, adj_dev, dev_list); - if (!adj) + if (!adj) { + pr_err("tried to remove device %s from %s\n", + dev->name, adj_dev->name); BUG(); + } if (adj->ref_nr > 1) { + pr_debug("%s to %s ref_nr-- = %d\n", dev->name, adj_dev->name, + adj->ref_nr-1); adj->ref_nr--; return; } + if (adj->master) + sysfs_remove_link(&(dev->dev.kobj), "master"); + + if (dev_list == &dev->adj_list.lower) { + sprintf(linkname, "lower_%s", adj_dev->name); + sysfs_remove_link(&(dev->dev.kobj), linkname); + } else if (dev_list == &dev->adj_list.upper) { + sprintf(linkname, "upper_%s", adj_dev->name); + sysfs_remove_link(&(dev->dev.kobj), linkname); + } + list_del_rcu(&adj->list); - pr_debug("dev_put for %s, because of %s link removed from %s to %s\n", - adj_dev->name, upper ? "upper" : "lower", dev->name, - adj_dev->name); + pr_debug("dev_put for %s, because link removed from %s to %s\n", + adj_dev->name, dev->name, adj_dev->name); dev_put(adj_dev); kfree_rcu(adj, rcu); } -static inline void __netdev_upper_dev_remove(struct net_device *dev, - struct net_device *udev) -{ - return __netdev_adjacent_dev_remove(dev, udev, true); -} - -static inline void __netdev_lower_dev_remove(struct net_device *dev, - struct net_device *ldev) -{ - return __netdev_adjacent_dev_remove(dev, ldev, false); -} - -int __netdev_adjacent_dev_insert_link(struct net_device *dev, - struct net_device *upper_dev, - bool master, bool neighbour) +int __netdev_adjacent_dev_link_lists(struct net_device *dev, + struct net_device *upper_dev, + struct list_head *up_list, + struct list_head *down_list, + void *private, bool master) { int ret; - ret = __netdev_upper_dev_insert(dev, upper_dev, master, neighbour); + ret = __netdev_adjacent_dev_insert(dev, upper_dev, up_list, private, + master); if (ret) return ret; - ret = __netdev_lower_dev_insert(upper_dev, dev, neighbour); + ret = __netdev_adjacent_dev_insert(upper_dev, dev, down_list, private, + false); if (ret) { - __netdev_upper_dev_remove(dev, upper_dev); + __netdev_adjacent_dev_remove(dev, upper_dev, up_list); return ret; } return 0; } -static inline int __netdev_adjacent_dev_link(struct net_device *dev, - struct net_device *udev) +int __netdev_adjacent_dev_link(struct net_device *dev, + struct net_device *upper_dev) { - return __netdev_adjacent_dev_insert_link(dev, udev, false, false); + return __netdev_adjacent_dev_link_lists(dev, upper_dev, + &dev->all_adj_list.upper, + &upper_dev->all_adj_list.lower, + NULL, false); } -static inline int __netdev_adjacent_dev_link_neighbour(struct net_device *dev, - struct net_device *udev, - bool master) +void __netdev_adjacent_dev_unlink_lists(struct net_device *dev, + struct net_device *upper_dev, + struct list_head *up_list, + struct list_head *down_list) { - return __netdev_adjacent_dev_insert_link(dev, udev, master, true); + __netdev_adjacent_dev_remove(dev, upper_dev, up_list); + __netdev_adjacent_dev_remove(upper_dev, dev, down_list); } void __netdev_adjacent_dev_unlink(struct net_device *dev, struct net_device *upper_dev) { - __netdev_upper_dev_remove(dev, upper_dev); - __netdev_lower_dev_remove(upper_dev, dev); + __netdev_adjacent_dev_unlink_lists(dev, upper_dev, + &dev->all_adj_list.upper, + &upper_dev->all_adj_list.lower); } +int __netdev_adjacent_dev_link_neighbour(struct net_device *dev, + struct net_device *upper_dev, + void *private, bool master) +{ + int ret = __netdev_adjacent_dev_link(dev, upper_dev); + + if (ret) + return ret; + + ret = __netdev_adjacent_dev_link_lists(dev, upper_dev, + &dev->adj_list.upper, + &upper_dev->adj_list.lower, + private, master); + if (ret) { + __netdev_adjacent_dev_unlink(dev, upper_dev); + return ret; + } + + return 0; +} + +void __netdev_adjacent_dev_unlink_neighbour(struct net_device *dev, + struct net_device *upper_dev) +{ + __netdev_adjacent_dev_unlink(dev, upper_dev); + __netdev_adjacent_dev_unlink_lists(dev, upper_dev, + &dev->adj_list.upper, + &upper_dev->adj_list.lower); +} static int __netdev_upper_dev_link(struct net_device *dev, - struct net_device *upper_dev, bool master) + struct net_device *upper_dev, bool master, + void *private) { struct netdev_adjacent *i, *j, *to_i, *to_j; int ret = 0; @@ -4662,26 +4793,29 @@ static int __netdev_upper_dev_link(struct net_device *dev, return -EBUSY; /* To prevent loops, check if dev is not upper device to upper_dev. */ - if (__netdev_find_upper(upper_dev, dev)) + if (__netdev_find_adj(upper_dev, dev, &upper_dev->all_adj_list.upper)) return -EBUSY; - if (__netdev_find_upper(dev, upper_dev)) + if (__netdev_find_adj(dev, upper_dev, &dev->all_adj_list.upper)) return -EEXIST; if (master && netdev_master_upper_dev_get(dev)) return -EBUSY; - ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, master); + ret = __netdev_adjacent_dev_link_neighbour(dev, upper_dev, private, + master); if (ret) return ret; /* Now that we linked these devs, make all the upper_dev's - * upper_dev_list visible to every dev's lower_dev_list and vice + * all_adj_list.upper visible to every dev's all_adj_list.lower an * versa, and don't forget the devices itself. All of these * links are non-neighbours. */ - list_for_each_entry(i, &dev->lower_dev_list, list) { - list_for_each_entry(j, &upper_dev->upper_dev_list, list) { + list_for_each_entry(i, &dev->all_adj_list.lower, list) { + list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) { + pr_debug("Interlinking %s with %s, non-neighbour\n", + i->dev->name, j->dev->name); ret = __netdev_adjacent_dev_link(i->dev, j->dev); if (ret) goto rollback_mesh; @@ -4689,14 +4823,18 @@ static int __netdev_upper_dev_link(struct net_device *dev, } /* add dev to every upper_dev's upper device */ - list_for_each_entry(i, &upper_dev->upper_dev_list, list) { + list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) { + pr_debug("linking %s's upper device %s with %s\n", + upper_dev->name, i->dev->name, dev->name); ret = __netdev_adjacent_dev_link(dev, i->dev); if (ret) goto rollback_upper_mesh; } /* add upper_dev to every dev's lower device */ - list_for_each_entry(i, &dev->lower_dev_list, list) { + list_for_each_entry(i, &dev->all_adj_list.lower, list) { + pr_debug("linking %s's lower device %s with %s\n", dev->name, + i->dev->name, upper_dev->name); ret = __netdev_adjacent_dev_link(i->dev, upper_dev); if (ret) goto rollback_lower_mesh; @@ -4707,7 +4845,7 @@ static int __netdev_upper_dev_link(struct net_device *dev, rollback_lower_mesh: to_i = i; - list_for_each_entry(i, &dev->lower_dev_list, list) { + list_for_each_entry(i, &dev->all_adj_list.lower, list) { if (i == to_i) break; __netdev_adjacent_dev_unlink(i->dev, upper_dev); @@ -4717,7 +4855,7 @@ rollback_lower_mesh: rollback_upper_mesh: to_i = i; - list_for_each_entry(i, &upper_dev->upper_dev_list, list) { + list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) { if (i == to_i) break; __netdev_adjacent_dev_unlink(dev, i->dev); @@ -4728,8 +4866,8 @@ rollback_upper_mesh: rollback_mesh: to_i = i; to_j = j; - list_for_each_entry(i, &dev->lower_dev_list, list) { - list_for_each_entry(j, &upper_dev->upper_dev_list, list) { + list_for_each_entry(i, &dev->all_adj_list.lower, list) { + list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) { if (i == to_i && j == to_j) break; __netdev_adjacent_dev_unlink(i->dev, j->dev); @@ -4738,7 +4876,7 @@ rollback_mesh: break; } - __netdev_adjacent_dev_unlink(dev, upper_dev); + __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); return ret; } @@ -4756,7 +4894,7 @@ rollback_mesh: int netdev_upper_dev_link(struct net_device *dev, struct net_device *upper_dev) { - return __netdev_upper_dev_link(dev, upper_dev, false); + return __netdev_upper_dev_link(dev, upper_dev, false, NULL); } EXPORT_SYMBOL(netdev_upper_dev_link); @@ -4774,10 +4912,18 @@ EXPORT_SYMBOL(netdev_upper_dev_link); int netdev_master_upper_dev_link(struct net_device *dev, struct net_device *upper_dev) { - return __netdev_upper_dev_link(dev, upper_dev, true); + return __netdev_upper_dev_link(dev, upper_dev, true, NULL); } EXPORT_SYMBOL(netdev_master_upper_dev_link); +int netdev_master_upper_dev_link_private(struct net_device *dev, + struct net_device *upper_dev, + void *private) +{ + return __netdev_upper_dev_link(dev, upper_dev, true, private); +} +EXPORT_SYMBOL(netdev_master_upper_dev_link_private); + /** * netdev_upper_dev_unlink - Removes a link to upper device * @dev: device @@ -4792,29 +4938,59 @@ void netdev_upper_dev_unlink(struct net_device *dev, struct netdev_adjacent *i, *j; ASSERT_RTNL(); - __netdev_adjacent_dev_unlink(dev, upper_dev); + __netdev_adjacent_dev_unlink_neighbour(dev, upper_dev); /* Here is the tricky part. We must remove all dev's lower * devices from all upper_dev's upper devices and vice * versa, to maintain the graph relationship. */ - list_for_each_entry(i, &dev->lower_dev_list, list) - list_for_each_entry(j, &upper_dev->upper_dev_list, list) + list_for_each_entry(i, &dev->all_adj_list.lower, list) + list_for_each_entry(j, &upper_dev->all_adj_list.upper, list) __netdev_adjacent_dev_unlink(i->dev, j->dev); /* remove also the devices itself from lower/upper device * list */ - list_for_each_entry(i, &dev->lower_dev_list, list) + list_for_each_entry(i, &dev->all_adj_list.lower, list) __netdev_adjacent_dev_unlink(i->dev, upper_dev); - list_for_each_entry(i, &upper_dev->upper_dev_list, list) + list_for_each_entry(i, &upper_dev->all_adj_list.upper, list) __netdev_adjacent_dev_unlink(dev, i->dev); call_netdevice_notifiers(NETDEV_CHANGEUPPER, dev); } EXPORT_SYMBOL(netdev_upper_dev_unlink); +void *netdev_lower_dev_get_private_rcu(struct net_device *dev, + struct net_device *lower_dev) +{ + struct netdev_adjacent *lower; + + if (!lower_dev) + return NULL; + lower = __netdev_find_adj_rcu(dev, lower_dev, &dev->adj_list.lower); + if (!lower) + return NULL; + + return lower->private; +} +EXPORT_SYMBOL(netdev_lower_dev_get_private_rcu); + +void *netdev_lower_dev_get_private(struct net_device *dev, + struct net_device *lower_dev) +{ + struct netdev_adjacent *lower; + + if (!lower_dev) + return NULL; + lower = __netdev_find_adj(dev, lower_dev, &dev->adj_list.lower); + if (!lower) + return NULL; + + return lower->private; +} +EXPORT_SYMBOL(netdev_lower_dev_get_private); + static void dev_change_rx_flags(struct net_device *dev, int flags) { const struct net_device_ops *ops = dev->netdev_ops; @@ -4823,7 +4999,7 @@ static void dev_change_rx_flags(struct net_device *dev, int flags) ops->ndo_change_rx_flags(dev, flags); } -static int __dev_set_promiscuity(struct net_device *dev, int inc) +static int __dev_set_promiscuity(struct net_device *dev, int inc, bool notify) { unsigned int old_flags = dev->flags; kuid_t uid; @@ -4866,6 +5042,8 @@ static int __dev_set_promiscuity(struct net_device *dev, int inc) dev_change_rx_flags(dev, IFF_PROMISC); } + if (notify) + __dev_notify_flags(dev, old_flags, IFF_PROMISC); return 0; } @@ -4885,7 +5063,7 @@ int dev_set_promiscuity(struct net_device *dev, int inc) unsigned int old_flags = dev->flags; int err; - err = __dev_set_promiscuity(dev, inc); + err = __dev_set_promiscuity(dev, inc, true); if (err < 0) return err; if (dev->flags != old_flags) @@ -4894,22 +5072,9 @@ int dev_set_promiscuity(struct net_device *dev, int inc) } EXPORT_SYMBOL(dev_set_promiscuity); -/** - * dev_set_allmulti - update allmulti count on a device - * @dev: device - * @inc: modifier - * - * Add or remove reception of all multicast frames to a device. While the - * count in the device remains above zero the interface remains listening - * to all interfaces. Once it hits zero the device reverts back to normal - * filtering operation. A negative @inc value is used to drop the counter - * when releasing a resource needing all multicasts. - * Return 0 if successful or a negative errno code on error. - */ - -int dev_set_allmulti(struct net_device *dev, int inc) +static int __dev_set_allmulti(struct net_device *dev, int inc, bool notify) { - unsigned int old_flags = dev->flags; + unsigned int old_flags = dev->flags, old_gflags = dev->gflags; ASSERT_RTNL(); @@ -4932,9 +5097,30 @@ int dev_set_allmulti(struct net_device *dev, int inc) if (dev->flags ^ old_flags) { dev_change_rx_flags(dev, IFF_ALLMULTI); dev_set_rx_mode(dev); + if (notify) + __dev_notify_flags(dev, old_flags, + dev->gflags ^ old_gflags); } return 0; } + +/** + * dev_set_allmulti - update allmulti count on a device + * @dev: device + * @inc: modifier + * + * Add or remove reception of all multicast frames to a device. While the + * count in the device remains above zero the interface remains listening + * to all interfaces. Once it hits zero the device reverts back to normal + * filtering operation. A negative @inc value is used to drop the counter + * when releasing a resource needing all multicasts. + * Return 0 if successful or a negative errno code on error. + */ + +int dev_set_allmulti(struct net_device *dev, int inc) +{ + return __dev_set_allmulti(dev, inc, true); +} EXPORT_SYMBOL(dev_set_allmulti); /* @@ -4959,10 +5145,10 @@ void __dev_set_rx_mode(struct net_device *dev) * therefore calling __dev_set_promiscuity here is safe. */ if (!netdev_uc_empty(dev) && !dev->uc_promisc) { - __dev_set_promiscuity(dev, 1); + __dev_set_promiscuity(dev, 1, false); dev->uc_promisc = true; } else if (netdev_uc_empty(dev) && dev->uc_promisc) { - __dev_set_promiscuity(dev, -1); + __dev_set_promiscuity(dev, -1, false); dev->uc_promisc = false; } } @@ -5051,9 +5237,13 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags) if ((flags ^ dev->gflags) & IFF_PROMISC) { int inc = (flags & IFF_PROMISC) ? 1 : -1; + unsigned int old_flags = dev->flags; dev->gflags ^= IFF_PROMISC; - dev_set_promiscuity(dev, inc); + + if (__dev_set_promiscuity(dev, inc, false) >= 0) + if (dev->flags != old_flags) + dev_set_rx_mode(dev); } /* NOTE: order of synchronization of IFF_PROMISC and IFF_ALLMULTI @@ -5064,16 +5254,20 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags) int inc = (flags & IFF_ALLMULTI) ? 1 : -1; dev->gflags ^= IFF_ALLMULTI; - dev_set_allmulti(dev, inc); + __dev_set_allmulti(dev, inc, false); } return ret; } -void __dev_notify_flags(struct net_device *dev, unsigned int old_flags) +void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, + unsigned int gchanges) { unsigned int changes = dev->flags ^ old_flags; + if (gchanges) + rtmsg_ifinfo(RTM_NEWLINK, dev, gchanges, GFP_ATOMIC); + if (changes & IFF_UP) { if (dev->flags & IFF_UP) call_netdevice_notifiers(NETDEV_UP, dev); @@ -5102,17 +5296,14 @@ void __dev_notify_flags(struct net_device *dev, unsigned int old_flags) int dev_change_flags(struct net_device *dev, unsigned int flags) { int ret; - unsigned int changes, old_flags = dev->flags; + unsigned int changes, old_flags = dev->flags, old_gflags = dev->gflags; ret = __dev_change_flags(dev, flags); if (ret < 0) return ret; - changes = old_flags ^ dev->flags; - if (changes) - rtmsg_ifinfo(RTM_NEWLINK, dev, changes); - - __dev_notify_flags(dev, old_flags); + changes = (old_flags ^ dev->flags) | (old_gflags ^ dev->gflags); + __dev_notify_flags(dev, old_flags, changes); return ret; } EXPORT_SYMBOL(dev_change_flags); @@ -5259,6 +5450,7 @@ static void net_set_todo(struct net_device *dev) static void rollback_registered_many(struct list_head *head) { struct net_device *dev, *tmp; + LIST_HEAD(close_head); BUG_ON(dev_boot_phase); ASSERT_RTNL(); @@ -5281,7 +5473,9 @@ static void rollback_registered_many(struct list_head *head) } /* If device is running, close it first. */ - dev_close_many(head); + list_for_each_entry(dev, head, unreg_list) + list_add_tail(&dev->close_list, &close_head); + dev_close_many(&close_head); list_for_each_entry(dev, head, unreg_list) { /* And unlink it from device chain. */ @@ -5304,7 +5498,7 @@ static void rollback_registered_many(struct list_head *head) if (!dev->rtnl_link_ops || dev->rtnl_link_state == RTNL_LINK_INITIALIZED) - rtmsg_ifinfo(RTM_DELLINK, dev, ~0U); + rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL); /* * Flush the unicast and multicast chains @@ -5703,7 +5897,7 @@ int register_netdevice(struct net_device *dev) */ if (!dev->rtnl_link_ops || dev->rtnl_link_state == RTNL_LINK_INITIALIZED) - rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U); + rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL); out: return ret; @@ -6010,6 +6204,16 @@ void netdev_set_default_ethtool_ops(struct net_device *dev, } EXPORT_SYMBOL_GPL(netdev_set_default_ethtool_ops); +void netdev_freemem(struct net_device *dev) +{ + char *addr = (char *)dev - dev->padded; + + if (is_vmalloc_addr(addr)) + vfree(addr); + else + kfree(addr); +} + /** * alloc_netdev_mqs - allocate network device * @sizeof_priv: size of private data to allocate space for @@ -6053,7 +6257,9 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, /* ensure 32-byte alignment of whole construct */ alloc_size += NETDEV_ALIGN - 1; - p = kzalloc(alloc_size, GFP_KERNEL); + p = kzalloc(alloc_size, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT); + if (!p) + p = vzalloc(alloc_size); if (!p) return NULL; @@ -6062,7 +6268,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, dev->pcpu_refcnt = alloc_percpu(int); if (!dev->pcpu_refcnt) - goto free_p; + goto free_dev; if (dev_addr_init(dev)) goto free_pcpu; @@ -6077,9 +6283,12 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, INIT_LIST_HEAD(&dev->napi_list); INIT_LIST_HEAD(&dev->unreg_list); + INIT_LIST_HEAD(&dev->close_list); INIT_LIST_HEAD(&dev->link_watch_list); - INIT_LIST_HEAD(&dev->upper_dev_list); - INIT_LIST_HEAD(&dev->lower_dev_list); + INIT_LIST_HEAD(&dev->adj_list.upper); + INIT_LIST_HEAD(&dev->adj_list.lower); + INIT_LIST_HEAD(&dev->all_adj_list.upper); + INIT_LIST_HEAD(&dev->all_adj_list.lower); dev->priv_flags = IFF_XMIT_DST_RELEASE; setup(dev); @@ -6112,8 +6321,8 @@ free_pcpu: kfree(dev->_rx); #endif -free_p: - kfree(p); +free_dev: + netdev_freemem(dev); return NULL; } EXPORT_SYMBOL(alloc_netdev_mqs); @@ -6150,7 +6359,7 @@ void free_netdev(struct net_device *dev) /* Compatibility with error handling in drivers */ if (dev->reg_state == NETREG_UNINITIALIZED) { - kfree((char *)dev - dev->padded); + netdev_freemem(dev); return; } @@ -6312,7 +6521,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char call_netdevice_notifiers(NETDEV_UNREGISTER, dev); rcu_barrier(); call_netdevice_notifiers(NETDEV_UNREGISTER_FINAL, dev); - rtmsg_ifinfo(RTM_DELLINK, dev, ~0U); + rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL); /* * Flush the unicast and multicast chains @@ -6351,7 +6560,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char * Prevent userspace races by waiting until the network * device is fully setup before sending notifications. */ - rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U); + rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL); synchronize_net(); err = 0; diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 6cda4e2c2132e345f82e3cd0feaeaaa16fbd75e6..ec40a849fc42ac44faeb9eae6da028caa73157fc 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -752,7 +752,7 @@ int dev_mc_del_global(struct net_device *dev, const unsigned char *addr) EXPORT_SYMBOL(dev_mc_del_global); /** - * dev_mc_sync - Synchronize device's unicast list to another device + * dev_mc_sync - Synchronize device's multicast list to another device * @to: destination device * @from: source device * @@ -780,7 +780,7 @@ int dev_mc_sync(struct net_device *to, struct net_device *from) EXPORT_SYMBOL(dev_mc_sync); /** - * dev_mc_sync_multiple - Synchronize device's unicast list to another + * dev_mc_sync_multiple - Synchronize device's multicast list to another * device, but allow for multiple calls to sync to multiple devices. * @to: destination device * @from: source device diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 78e9d9223e40dfe5bc9956170619e0a8e3335e9d..30071dec287a4674b64ec4f29c158d8a0a57bf56 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -81,6 +81,8 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_TSO6_BIT] = "tx-tcp6-segmentation", [NETIF_F_FSO_BIT] = "tx-fcoe-segmentation", [NETIF_F_GSO_GRE_BIT] = "tx-gre-segmentation", + [NETIF_F_GSO_IPIP_BIT] = "tx-ipip-segmentation", + [NETIF_F_GSO_SIT_BIT] = "tx-sit-segmentation", [NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation", [NETIF_F_GSO_MPLS_BIT] = "tx-mpls-segmentation", @@ -94,6 +96,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_LOOPBACK_BIT] = "loopback", [NETIF_F_RXFCS_BIT] = "rx-fcs", [NETIF_F_RXALL_BIT] = "rx-all", + [NETIF_F_HW_L2FW_DOFFLOAD_BIT] = "l2-fwd-offload", }; static int ethtool_get_features(struct net_device *dev, void __user *useraddr) diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index 2e654138433c0521cee0b07992e6aa7535e970b0..f409e0bd35c06456f4877d10d98c342f52362d84 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -460,7 +460,8 @@ static int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr* nlh) if (frh->action && (frh->action != rule->action)) continue; - if (frh->table && (frh_get_table(frh, tb) != rule->table)) + if (frh_get_table(frh, tb) && + (frh_get_table(frh, tb) != rule->table)) continue; if (tb[FRA_PRIORITY] && diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 143b6fdb9647dae2d9c9f65d1a72e56bce1a1cc5..d6ef173225008ec1946ed232f781cc63754b9575 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -25,9 +25,35 @@ static void iph_to_flow_copy_addrs(struct flow_keys *flow, const struct iphdr *i memcpy(&flow->src, &iph->saddr, sizeof(flow->src) + sizeof(flow->dst)); } +/** + * skb_flow_get_ports - extract the upper layer ports and return them + * @skb: buffer to extract the ports from + * @thoff: transport header offset + * @ip_proto: protocol for which to get port offset + * + * The function will try to retrieve the ports at offset thoff + poff where poff + * is the protocol port offset returned from proto_ports_offset + */ +__be32 skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto) +{ + int poff = proto_ports_offset(ip_proto); + + if (poff >= 0) { + __be32 *ports, _ports; + + ports = skb_header_pointer(skb, thoff + poff, + sizeof(_ports), &_ports); + if (ports) + return *ports; + } + + return 0; +} +EXPORT_SYMBOL(skb_flow_get_ports); + bool skb_flow_dissect(const struct sk_buff *skb, struct flow_keys *flow) { - int poff, nhoff = skb_network_offset(skb); + int nhoff = skb_network_offset(skb); u8 ip_proto; __be16 proto = skb->protocol; @@ -42,13 +68,13 @@ ip: iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph); if (!iph || iph->ihl < 5) return false; + nhoff += iph->ihl * 4; + ip_proto = iph->protocol; if (ip_is_fragment(iph)) ip_proto = 0; - else - ip_proto = iph->protocol; + iph_to_flow_copy_addrs(flow, iph); - nhoff += iph->ihl * 4; break; } case __constant_htons(ETH_P_IPV6): { @@ -150,16 +176,7 @@ ipv6: } flow->ip_proto = ip_proto; - poff = proto_ports_offset(ip_proto); - if (poff >= 0) { - __be32 *ports, _ports; - - ports = skb_header_pointer(skb, nhoff + poff, - sizeof(_ports), &_ports); - if (ports) - flow->ports = *ports; - } - + flow->ports = skb_flow_get_ports(skb, nhoff, ip_proto); flow->thoff = (u16) nhoff; return true; @@ -167,6 +184,22 @@ ipv6: EXPORT_SYMBOL(skb_flow_dissect); static u32 hashrnd __read_mostly; +static __always_inline void __flow_hash_secret_init(void) +{ + net_get_random_once(&hashrnd, sizeof(hashrnd)); +} + +static __always_inline u32 __flow_hash_3words(u32 a, u32 b, u32 c) +{ + __flow_hash_secret_init(); + return jhash_3words(a, b, c, hashrnd); +} + +static __always_inline u32 __flow_hash_1word(u32 a) +{ + __flow_hash_secret_init(); + return jhash_1word(a, hashrnd); +} /* * __skb_get_rxhash: calculate a flow hash based on src/dst addresses @@ -193,9 +226,9 @@ void __skb_get_rxhash(struct sk_buff *skb) swap(keys.port16[0], keys.port16[1]); } - hash = jhash_3words((__force u32)keys.dst, - (__force u32)keys.src, - (__force u32)keys.ports, hashrnd); + hash = __flow_hash_3words((__force u32)keys.dst, + (__force u32)keys.src, + (__force u32)keys.ports); if (!hash) hash = 1; @@ -231,7 +264,7 @@ u16 __skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb, hash = skb->sk->sk_hash; else hash = (__force u16) skb->protocol; - hash = jhash_1word(hash, hashrnd); + hash = __flow_hash_1word(hash); return (u16) (((u64) hash * qcount) >> 32) + qoffset; } @@ -323,7 +356,7 @@ static inline int get_xps_queue(struct net_device *dev, struct sk_buff *skb) else hash = (__force u16) skb->protocol ^ skb->rxhash; - hash = jhash_1word(hash, hashrnd); + hash = __flow_hash_1word(hash); queue_index = map->queues[ ((u64)hash * map->len) >> 32]; } @@ -378,11 +411,3 @@ struct netdev_queue *netdev_pick_tx(struct net_device *dev, skb_set_queue_mapping(skb, queue_index); return netdev_get_tx_queue(dev, queue_index); } - -static int __init initialize_hashrnd(void) -{ - get_random_bytes(&hashrnd, sizeof(hashrnd)); - return 0; -} - -late_initcall_sync(initialize_hashrnd); diff --git a/net/core/iovec.c b/net/core/iovec.c index b77eeecc00115eefd2395648e71cb337ee53757a..4cdb7c48dad6cb7ade5b1b9c76b32bff08c51d25 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c @@ -100,7 +100,7 @@ int memcpy_toiovecend(const struct iovec *iov, unsigned char *kdata, EXPORT_SYMBOL(memcpy_toiovecend); /* - * Copy iovec from kernel. Returns -EFAULT on error. + * Copy iovec to kernel. Returns -EFAULT on error. */ int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 6072610a8672d1a54a0e7618214f70610fa2a6fa..ca15f32821fb8d536586354108488050b7cc6a0e 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -867,7 +867,7 @@ static void neigh_invalidate(struct neighbour *neigh) static void neigh_probe(struct neighbour *neigh) __releases(neigh->lock) { - struct sk_buff *skb = skb_peek(&neigh->arp_queue); + struct sk_buff *skb = skb_peek_tail(&neigh->arp_queue); /* keep skb alive even if arp_queue overflows */ if (skb) skb = skb_copy(skb, GFP_ATOMIC); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 325dee863e466dc74cc7fa4fd26abb56a2fae85c..f3edf9635e029bb47c028cd2336dbeccb803c846 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1263,7 +1263,7 @@ static void netdev_release(struct device *d) BUG_ON(dev->reg_state != NETREG_RELEASED); kfree(dev->ifalias); - kfree((char *)dev - dev->padded); + netdev_freemem(dev); } static const void *net_namespace(struct device *d) diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c index d9cd627e6a16a55bb9b6e8ec1619a0291e1621ab..9b7cf6c85f82447d3e03f33cd2c599de13ed3644 100644 --- a/net/core/netprio_cgroup.c +++ b/net/core/netprio_cgroup.c @@ -222,11 +222,10 @@ static void net_prio_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { struct task_struct *p; - void *v; + void *v = (void *)(unsigned long)css->cgroup->id; cgroup_taskset_for_each(p, css, tset) { task_lock(p); - v = (void *)(unsigned long)task_netprioidx(p); iterate_fd(p->files, 0, update_netprio, v); task_unlock(p); } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 2a0e21de3060cddbc9cd657acb24ee84b748c4d1..cf67144d3e3c28f5fa2ab9965fba2884b03130d1 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1647,9 +1647,8 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm) } dev->rtnl_link_state = RTNL_LINK_INITIALIZED; - rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U); - __dev_notify_flags(dev, old_flags); + __dev_notify_flags(dev, old_flags, ~0U); return 0; } EXPORT_SYMBOL(rtnl_configure_link); @@ -1985,14 +1984,15 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) return skb->len; } -void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change) +void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change, + gfp_t flags) { struct net *net = dev_net(dev); struct sk_buff *skb; int err = -ENOBUFS; size_t if_info_size; - skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), GFP_KERNEL); + skb = nlmsg_new((if_info_size = if_nlmsg_size(dev, 0)), flags); if (skb == NULL) goto errout; @@ -2003,7 +2003,7 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change) kfree_skb(skb); goto errout; } - rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL); + rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags); return; errout: if (err < 0) @@ -2717,7 +2717,7 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi case NETDEV_JOIN: break; default: - rtmsg_ifinfo(RTM_NEWLINK, dev, 0); + rtmsg_ifinfo(RTM_NEWLINK, dev, 0, GFP_KERNEL); break; } return NOTIFY_DONE; diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index 8d9d05edd2eb1e66024311c62c6b7679c52bdc7d..897da56f3affae13161d4610989667a82244c308 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -15,20 +16,9 @@ static u32 net_secret[NET_SECRET_SIZE] ____cacheline_aligned; -static void net_secret_init(void) +static __always_inline void net_secret_init(void) { - u32 tmp; - int i; - - if (likely(net_secret[0])) - return; - - for (i = NET_SECRET_SIZE; i > 0;) { - do { - get_random_bytes(&tmp, sizeof(tmp)); - } while (!tmp); - cmpxchg(&net_secret[--i], 0, tmp); - } + net_get_random_once(net_secret, sizeof(net_secret)); } #endif diff --git a/net/core/skbuff.c b/net/core/skbuff.c index d81cff119f734a5c8f405fed01f4047b9169f5d1..8cec1e6b844df666b563429f538adf7695a527b6 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -476,6 +476,18 @@ void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off, } EXPORT_SYMBOL(skb_add_rx_frag); +void skb_coalesce_rx_frag(struct sk_buff *skb, int i, int size, + unsigned int truesize) +{ + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + skb_frag_size_add(frag, size); + skb->len += size; + skb->data_len += size; + skb->truesize += truesize; +} +EXPORT_SYMBOL(skb_coalesce_rx_frag); + static void skb_drop_list(struct sk_buff **listp) { kfree_skb_list(*listp); @@ -580,9 +592,6 @@ static void skb_release_head_state(struct sk_buff *skb) #if IS_ENABLED(CONFIG_NF_CONNTRACK) nf_conntrack_put(skb->nfct); #endif -#ifdef NET_SKBUFF_NF_DEFRAG_NEEDED - nf_conntrack_put_reasm(skb->nfct_reasm); -#endif #ifdef CONFIG_BRIDGE_NETFILTER nf_bridge_put(skb->nf_bridge); #endif @@ -903,6 +912,9 @@ EXPORT_SYMBOL(skb_clone); static void skb_headers_offset_update(struct sk_buff *skb, int off) { + /* Only adjust this if it actually is csum_start rather than csum */ + if (skb->ip_summed == CHECKSUM_PARTIAL) + skb->csum_start += off; /* {transport,network,mac}_header and tail are relative to skb->head */ skb->transport_header += off; skb->network_header += off; @@ -1036,8 +1048,8 @@ EXPORT_SYMBOL(__pskb_copy); * @ntail: room to add at tail * @gfp_mask: allocation priority * - * Expands (or creates identical copy, if &nhead and &ntail are zero) - * header of skb. &sk_buff itself is not changed. &sk_buff MUST have + * Expands (or creates identical copy, if @nhead and @ntail are zero) + * header of @skb. &sk_buff itself is not changed. &sk_buff MUST have * reference count of 1. Returns zero in the case of success or error, * if expansion failed. In the last case, &sk_buff is not changed. * @@ -1109,9 +1121,6 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, #endif skb->tail += off; skb_headers_offset_update(skb, nhead); - /* Only adjust this if it actually is csum_start rather than csum */ - if (skb->ip_summed == CHECKSUM_PARTIAL) - skb->csum_start += nhead; skb->cloned = 0; skb->hdr_len = 0; skb->nohdr = 0; @@ -1176,7 +1185,6 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb, NUMA_NO_NODE); int oldheadroom = skb_headroom(skb); int head_copy_len, head_copy_off; - int off; if (!n) return NULL; @@ -1200,11 +1208,7 @@ struct sk_buff *skb_copy_expand(const struct sk_buff *skb, copy_skb_header(n, skb); - off = newheadroom - oldheadroom; - if (n->ip_summed == CHECKSUM_PARTIAL) - n->csum_start += off; - - skb_headers_offset_update(n, off); + skb_headers_offset_update(n, newheadroom - oldheadroom); return n; } @@ -1256,6 +1260,29 @@ free_skb: } EXPORT_SYMBOL(skb_pad); +/** + * pskb_put - add data to the tail of a potentially fragmented buffer + * @skb: start of the buffer to use + * @tail: tail fragment of the buffer to use + * @len: amount of data to add + * + * This function extends the used data area of the potentially + * fragmented buffer. @tail must be the last fragment of @skb -- or + * @skb itself. If this would exceed the total buffer size the kernel + * will panic. A pointer to the first byte of the extra data is + * returned. + */ + +unsigned char *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len) +{ + if (tail != skb) { + skb->data_len += len; + skb->len += len; + } + return skb_put(tail, len); +} +EXPORT_SYMBOL_GPL(pskb_put); + /** * skb_put - add data to a buffer * @skb: buffer to use @@ -1933,9 +1960,8 @@ fault: EXPORT_SYMBOL(skb_store_bits); /* Checksum skb data. */ - -__wsum skb_checksum(const struct sk_buff *skb, int offset, - int len, __wsum csum) +__wsum __skb_checksum(const struct sk_buff *skb, int offset, int len, + __wsum csum, const struct skb_checksum_ops *ops) { int start = skb_headlen(skb); int i, copy = start - offset; @@ -1946,7 +1972,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset, if (copy > 0) { if (copy > len) copy = len; - csum = csum_partial(skb->data + offset, copy, csum); + csum = ops->update(skb->data + offset, copy, csum); if ((len -= copy) == 0) return csum; offset += copy; @@ -1967,10 +1993,10 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset, if (copy > len) copy = len; vaddr = kmap_atomic(skb_frag_page(frag)); - csum2 = csum_partial(vaddr + frag->page_offset + - offset - start, copy, 0); + csum2 = ops->update(vaddr + frag->page_offset + + offset - start, copy, 0); kunmap_atomic(vaddr); - csum = csum_block_add(csum, csum2, pos); + csum = ops->combine(csum, csum2, pos, copy); if (!(len -= copy)) return csum; offset += copy; @@ -1989,9 +2015,9 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset, __wsum csum2; if (copy > len) copy = len; - csum2 = skb_checksum(frag_iter, offset - start, - copy, 0); - csum = csum_block_add(csum, csum2, pos); + csum2 = __skb_checksum(frag_iter, offset - start, + copy, 0, ops); + csum = ops->combine(csum, csum2, pos, copy); if ((len -= copy) == 0) return csum; offset += copy; @@ -2003,6 +2029,18 @@ __wsum skb_checksum(const struct sk_buff *skb, int offset, return csum; } +EXPORT_SYMBOL(__skb_checksum); + +__wsum skb_checksum(const struct sk_buff *skb, int offset, + int len, __wsum csum) +{ + const struct skb_checksum_ops ops = { + .update = csum_partial_ext, + .combine = csum_block_add_ext, + }; + + return __skb_checksum(skb, offset, len, csum, &ops); +} EXPORT_SYMBOL(skb_checksum); /* Both of above in one bottle. */ @@ -2522,14 +2560,14 @@ EXPORT_SYMBOL(skb_prepare_seq_read); * @data: destination pointer for data to be returned * @st: state variable * - * Reads a block of skb data at &consumed relative to the + * Reads a block of skb data at @consumed relative to the * lower offset specified to skb_prepare_seq_read(). Assigns - * the head of the data block to &data and returns the length + * the head of the data block to @data and returns the length * of the block or 0 if the end of the skb data or the upper * offset has been reached. * * The caller is not required to consume all of the data - * returned, i.e. &consumed is typically set to the number + * returned, i.e. @consumed is typically set to the number * of bytes already consumed and the next call to * skb_seq_read() will return the remaining part of the block. * @@ -2837,14 +2875,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features) __copy_skb_header(nskb, skb); nskb->mac_len = skb->mac_len; - /* nskb and skb might have different headroom */ - if (nskb->ip_summed == CHECKSUM_PARTIAL) - nskb->csum_start += skb_headroom(nskb) - headroom; - - skb_reset_mac_header(nskb); - skb_set_network_header(nskb, skb->mac_len); - nskb->transport_header = (nskb->network_header + - skb_network_header_len(skb)); + skb_headers_offset_update(nskb, skb_headroom(nskb) - headroom); skb_copy_from_linear_data_offset(skb, -tnl_hlen, nskb->data - tnl_hlen, @@ -2936,32 +2967,30 @@ EXPORT_SYMBOL_GPL(skb_segment); int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) { - struct sk_buff *p = *head; - struct sk_buff *nskb; - struct skb_shared_info *skbinfo = skb_shinfo(skb); - struct skb_shared_info *pinfo = skb_shinfo(p); - unsigned int headroom; - unsigned int len = skb_gro_len(skb); + struct skb_shared_info *pinfo, *skbinfo = skb_shinfo(skb); unsigned int offset = skb_gro_offset(skb); unsigned int headlen = skb_headlen(skb); + struct sk_buff *nskb, *lp, *p = *head; + unsigned int len = skb_gro_len(skb); unsigned int delta_truesize; + unsigned int headroom; - if (p->len + len >= 65536) + if (unlikely(p->len + len >= 65536)) return -E2BIG; - if (pinfo->frag_list) - goto merge; - else if (headlen <= offset) { + lp = NAPI_GRO_CB(p)->last ?: p; + pinfo = skb_shinfo(lp); + + if (headlen <= offset) { skb_frag_t *frag; skb_frag_t *frag2; int i = skbinfo->nr_frags; int nr_frags = pinfo->nr_frags + i; - offset -= headlen; - if (nr_frags > MAX_SKB_FRAGS) - return -E2BIG; + goto merge; + offset -= headlen; pinfo->nr_frags = nr_frags; skbinfo->nr_frags = 0; @@ -2992,7 +3021,7 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) unsigned int first_offset; if (nr_frags + 1 + skbinfo->nr_frags > MAX_SKB_FRAGS) - return -E2BIG; + goto merge; first_offset = skb->data - (unsigned char *)page_address(page) + @@ -3010,7 +3039,10 @@ int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb) delta_truesize = skb->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff)); NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE_STOLEN_HEAD; goto done; - } else if (skb_gro_len(p) != pinfo->gso_size) + } + if (pinfo->frag_list) + goto merge; + if (skb_gro_len(p) != pinfo->gso_size) return -E2BIG; headroom = skb_headroom(p); @@ -3062,16 +3094,24 @@ merge: __skb_pull(skb, offset); - NAPI_GRO_CB(p)->last->next = skb; + if (!NAPI_GRO_CB(p)->last) + skb_shinfo(p)->frag_list = skb; + else + NAPI_GRO_CB(p)->last->next = skb; NAPI_GRO_CB(p)->last = skb; skb_header_release(skb); + lp = p; done: NAPI_GRO_CB(p)->count++; p->data_len += len; p->truesize += delta_truesize; p->len += len; - + if (lp != p) { + lp->data_len += len; + lp->truesize += delta_truesize; + lp->len += len; + } NAPI_GRO_CB(skb)->same_flow = 1; return 0; } diff --git a/net/core/sock.c b/net/core/sock.c index 0b39e7ae43837e602ff473b641675fbf22b83b5c..ab20ed9b0f31da64cb118cf645fa88f5787b5571 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -475,12 +475,6 @@ discard_and_relse: } EXPORT_SYMBOL(sk_receive_skb); -void sk_reset_txq(struct sock *sk) -{ - sk_tx_queue_clear(sk); -} -EXPORT_SYMBOL(sk_reset_txq); - struct dst_entry *__sk_dst_check(struct sock *sk, u32 cookie) { struct dst_entry *dst = __sk_dst_get(sk); @@ -914,6 +908,13 @@ set_rcvbuf: } break; #endif + + case SO_MAX_PACING_RATE: + sk->sk_max_pacing_rate = val; + sk->sk_pacing_rate = min(sk->sk_pacing_rate, + sk->sk_max_pacing_rate); + break; + default: ret = -ENOPROTOOPT; break; @@ -1177,6 +1178,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, break; #endif + case SO_MAX_PACING_RATE: + v.val = sk->sk_max_pacing_rate; + break; + default: return -ENOPROTOOPT; } @@ -1836,7 +1841,17 @@ EXPORT_SYMBOL(sock_alloc_send_skb); /* On 32bit arches, an skb frag is limited to 2^15 */ #define SKB_FRAG_PAGE_ORDER get_order(32768) -bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag) +/** + * skb_page_frag_refill - check that a page_frag contains enough room + * @sz: minimum size of the fragment we want to get + * @pfrag: pointer to page_frag + * @prio: priority for memory allocation + * + * Note: While this allocator tries to use high order pages, there is + * no guarantee that allocations succeed. Therefore, @sz MUST be + * less or equal than PAGE_SIZE. + */ +bool skb_page_frag_refill(unsigned int sz, struct page_frag *pfrag, gfp_t prio) { int order; @@ -1845,16 +1860,16 @@ bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag) pfrag->offset = 0; return true; } - if (pfrag->offset < pfrag->size) + if (pfrag->offset + sz <= pfrag->size) return true; put_page(pfrag->page); } /* We restrict high order allocations to users that can afford to wait */ - order = (sk->sk_allocation & __GFP_WAIT) ? SKB_FRAG_PAGE_ORDER : 0; + order = (prio & __GFP_WAIT) ? SKB_FRAG_PAGE_ORDER : 0; do { - gfp_t gfp = sk->sk_allocation; + gfp_t gfp = prio; if (order) gfp |= __GFP_COMP | __GFP_NOWARN; @@ -1866,6 +1881,15 @@ bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag) } } while (--order >= 0); + return false; +} +EXPORT_SYMBOL(skb_page_frag_refill); + +bool sk_page_frag_refill(struct sock *sk, struct page_frag *pfrag) +{ + if (likely(skb_page_frag_refill(32U, pfrag, sk->sk_allocation))) + return true; + sk_enter_memory_pressure(sk); sk_stream_moderate_sndbuf(sk); return false; @@ -2319,6 +2343,7 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_ll_usec = sysctl_net_busy_read; #endif + sk->sk_max_pacing_rate = ~0U; sk->sk_pacing_rate = ~0U; /* * Before updating sk_refcnt, we must commit prior changes to memory diff --git a/net/core/utils.c b/net/core/utils.c index aa88e23fc87aa44f95f8794cf234846d8d06d21d..2f737bf90b3fe4235c75ccca6640c735ecaa076b 100644 --- a/net/core/utils.c +++ b/net/core/utils.c @@ -338,3 +338,52 @@ void inet_proto_csum_replace16(__sum16 *sum, struct sk_buff *skb, csum_unfold(*sum))); } EXPORT_SYMBOL(inet_proto_csum_replace16); + +struct __net_random_once_work { + struct work_struct work; + struct static_key *key; +}; + +static void __net_random_once_deferred(struct work_struct *w) +{ + struct __net_random_once_work *work = + container_of(w, struct __net_random_once_work, work); + if (!static_key_enabled(work->key)) + static_key_slow_inc(work->key); + kfree(work); +} + +static void __net_random_once_disable_jump(struct static_key *key) +{ + struct __net_random_once_work *w; + + w = kmalloc(sizeof(*w), GFP_ATOMIC); + if (!w) + return; + + INIT_WORK(&w->work, __net_random_once_deferred); + w->key = key; + schedule_work(&w->work); +} + +bool __net_get_random_once(void *buf, int nbytes, bool *done, + struct static_key *done_key) +{ + static DEFINE_SPINLOCK(lock); + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + if (*done) { + spin_unlock_irqrestore(&lock, flags); + return false; + } + + get_random_bytes(buf, nbytes); + *done = true; + spin_unlock_irqrestore(&lock, flags); + + __net_random_once_disable_jump(done_key); + + return true; +} +EXPORT_SYMBOL(__net_get_random_once); diff --git a/net/dccp/ackvec.h b/net/dccp/ackvec.h index a269aa7f7923bf2b7f7b9d1dd3b26030456fc47c..3284bfa988c0ea45c0c46c4b5c7e9a80528bd0b0 100644 --- a/net/dccp/ackvec.h +++ b/net/dccp/ackvec.h @@ -101,16 +101,16 @@ struct dccp_ackvec_record { u8 avr_ack_nonce:1; }; -extern int dccp_ackvec_init(void); -extern void dccp_ackvec_exit(void); +int dccp_ackvec_init(void); +void dccp_ackvec_exit(void); -extern struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority); -extern void dccp_ackvec_free(struct dccp_ackvec *av); +struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority); +void dccp_ackvec_free(struct dccp_ackvec *av); -extern void dccp_ackvec_input(struct dccp_ackvec *av, struct sk_buff *skb); -extern int dccp_ackvec_update_records(struct dccp_ackvec *av, u64 seq, u8 sum); -extern void dccp_ackvec_clear_state(struct dccp_ackvec *av, const u64 ackno); -extern u16 dccp_ackvec_buflen(const struct dccp_ackvec *av); +void dccp_ackvec_input(struct dccp_ackvec *av, struct sk_buff *skb); +int dccp_ackvec_update_records(struct dccp_ackvec *av, u64 seq, u8 sum); +void dccp_ackvec_clear_state(struct dccp_ackvec *av, const u64 ackno); +u16 dccp_ackvec_buflen(const struct dccp_ackvec *av); static inline bool dccp_ackvec_is_empty(const struct dccp_ackvec *av) { @@ -133,7 +133,6 @@ struct dccp_ackvec_parsed { struct list_head node; }; -extern int dccp_ackvec_parsed_add(struct list_head *head, - u8 *vec, u8 len, u8 nonce); -extern void dccp_ackvec_parsed_cleanup(struct list_head *parsed_chunks); +int dccp_ackvec_parsed_add(struct list_head *head, u8 *vec, u8 len, u8 nonce); +void dccp_ackvec_parsed_cleanup(struct list_head *parsed_chunks); #endif /* _ACKVEC_H */ diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h index fb85d371a8dec875a01205b22ef6ddef5e99d511..6eb837a47b5c42f8c73c498525fe4d269fc5b97d 100644 --- a/net/dccp/ccid.h +++ b/net/dccp/ccid.h @@ -93,8 +93,8 @@ extern struct ccid_operations ccid2_ops; extern struct ccid_operations ccid3_ops; #endif -extern int ccid_initialize_builtins(void); -extern void ccid_cleanup_builtins(void); +int ccid_initialize_builtins(void); +void ccid_cleanup_builtins(void); struct ccid { struct ccid_operations *ccid_ops; @@ -106,12 +106,12 @@ static inline void *ccid_priv(const struct ccid *ccid) return (void *)ccid->ccid_priv; } -extern bool ccid_support_check(u8 const *ccid_array, u8 array_len); -extern int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len); -extern int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, - char __user *, int __user *); +bool ccid_support_check(u8 const *ccid_array, u8 array_len); +int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len); +int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, + char __user *, int __user *); -extern struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx); +struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx); static inline int ccid_get_current_rx_ccid(struct dccp_sock *dp) { @@ -131,8 +131,8 @@ static inline int ccid_get_current_tx_ccid(struct dccp_sock *dp) return ccid->ccid_ops->ccid_id; } -extern void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk); -extern void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk); +void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk); +void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk); /* * Congestion control of queued data packets via CCID decision. diff --git a/net/dccp/ccids/lib/loss_interval.h b/net/dccp/ccids/lib/loss_interval.h index d1d2f5383b7d3d7cec533be0d7b8d939ebe492dd..57f631a86ccd35c9a7a949003091909330971cb3 100644 --- a/net/dccp/ccids/lib/loss_interval.h +++ b/net/dccp/ccids/lib/loss_interval.h @@ -65,9 +65,9 @@ static inline u8 tfrc_lh_length(struct tfrc_loss_hist *lh) struct tfrc_rx_hist; -extern int tfrc_lh_interval_add(struct tfrc_loss_hist *, struct tfrc_rx_hist *, - u32 (*first_li)(struct sock *), struct sock *); -extern u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *); -extern void tfrc_lh_cleanup(struct tfrc_loss_hist *lh); +int tfrc_lh_interval_add(struct tfrc_loss_hist *, struct tfrc_rx_hist *, + u32 (*first_li)(struct sock *), struct sock *); +u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *); +void tfrc_lh_cleanup(struct tfrc_loss_hist *lh); #endif /* _DCCP_LI_HIST_ */ diff --git a/net/dccp/ccids/lib/packet_history.h b/net/dccp/ccids/lib/packet_history.h index 7ee4a9d9d3352337bb5e899d214a754b740ac8c0..ee362b0b630ddde5a46242dc4b86c2213efe581c 100644 --- a/net/dccp/ccids/lib/packet_history.h +++ b/net/dccp/ccids/lib/packet_history.h @@ -60,8 +60,8 @@ static inline struct tfrc_tx_hist_entry * return head; } -extern int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno); -extern void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp); +int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno); +void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp); /* Subtraction a-b modulo-16, respects circular wrap-around */ #define SUB16(a, b) (((a) + 16 - (b)) & 0xF) @@ -139,20 +139,17 @@ static inline bool tfrc_rx_hist_loss_pending(const struct tfrc_rx_hist *h) return h->loss_count > 0; } -extern void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, - const struct sk_buff *skb, const u64 ndp); +void tfrc_rx_hist_add_packet(struct tfrc_rx_hist *h, const struct sk_buff *skb, + const u64 ndp); -extern int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb); +int tfrc_rx_hist_duplicate(struct tfrc_rx_hist *h, struct sk_buff *skb); struct tfrc_loss_hist; -extern int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, - struct tfrc_loss_hist *lh, - struct sk_buff *skb, const u64 ndp, - u32 (*first_li)(struct sock *sk), - struct sock *sk); -extern u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, - const struct sk_buff *skb); -extern int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h); -extern void tfrc_rx_hist_purge(struct tfrc_rx_hist *h); +int tfrc_rx_handle_loss(struct tfrc_rx_hist *h, struct tfrc_loss_hist *lh, + struct sk_buff *skb, const u64 ndp, + u32 (*first_li)(struct sock *sk), struct sock *sk); +u32 tfrc_rx_hist_sample_rtt(struct tfrc_rx_hist *h, const struct sk_buff *skb); +int tfrc_rx_hist_alloc(struct tfrc_rx_hist *h); +void tfrc_rx_hist_purge(struct tfrc_rx_hist *h); #endif /* _DCCP_PKT_HIST_ */ diff --git a/net/dccp/ccids/lib/tfrc.h b/net/dccp/ccids/lib/tfrc.h index ed698c42a5fbef17b34a9a4daf6fc34464d914f5..40ee7d62b6520d7daf2a1a8f3b54bda9cfbedfc2 100644 --- a/net/dccp/ccids/lib/tfrc.h +++ b/net/dccp/ccids/lib/tfrc.h @@ -55,21 +55,21 @@ static inline u32 tfrc_ewma(const u32 avg, const u32 newval, const u8 weight) return avg ? (weight * avg + (10 - weight) * newval) / 10 : newval; } -extern u32 tfrc_calc_x(u16 s, u32 R, u32 p); -extern u32 tfrc_calc_x_reverse_lookup(u32 fvalue); -extern u32 tfrc_invert_loss_event_rate(u32 loss_event_rate); +u32 tfrc_calc_x(u16 s, u32 R, u32 p); +u32 tfrc_calc_x_reverse_lookup(u32 fvalue); +u32 tfrc_invert_loss_event_rate(u32 loss_event_rate); -extern int tfrc_tx_packet_history_init(void); -extern void tfrc_tx_packet_history_exit(void); -extern int tfrc_rx_packet_history_init(void); -extern void tfrc_rx_packet_history_exit(void); +int tfrc_tx_packet_history_init(void); +void tfrc_tx_packet_history_exit(void); +int tfrc_rx_packet_history_init(void); +void tfrc_rx_packet_history_exit(void); -extern int tfrc_li_init(void); -extern void tfrc_li_exit(void); +int tfrc_li_init(void); +void tfrc_li_exit(void); #ifdef CONFIG_IP_DCCP_TFRC_LIB -extern int tfrc_lib_init(void); -extern void tfrc_lib_exit(void); +int tfrc_lib_init(void); +void tfrc_lib_exit(void); #else #define tfrc_lib_init() (0) #define tfrc_lib_exit() diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h index 708e75bf623dea1f78dcd844a1b1af5cd6928d09..30948784dd58ff0a35df38b075c3bebd53f90cbe 100644 --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -53,7 +53,7 @@ extern struct inet_hashinfo dccp_hashinfo; extern struct percpu_counter dccp_orphan_count; -extern void dccp_time_wait(struct sock *sk, int state, int timeo); +void dccp_time_wait(struct sock *sk, int state, int timeo); /* * Set safe upper bounds for header and option length. Since Data Offset is 8 @@ -224,114 +224,108 @@ static inline void dccp_csum_outgoing(struct sk_buff *skb) skb->csum = skb_checksum(skb, 0, (cov > skb->len)? skb->len : cov, 0); } -extern void dccp_v4_send_check(struct sock *sk, struct sk_buff *skb); +void dccp_v4_send_check(struct sock *sk, struct sk_buff *skb); -extern int dccp_retransmit_skb(struct sock *sk); +int dccp_retransmit_skb(struct sock *sk); -extern void dccp_send_ack(struct sock *sk); -extern void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, - struct request_sock *rsk); +void dccp_send_ack(struct sock *sk); +void dccp_reqsk_send_ack(struct sock *sk, struct sk_buff *skb, + struct request_sock *rsk); -extern void dccp_send_sync(struct sock *sk, const u64 seq, - const enum dccp_pkt_type pkt_type); +void dccp_send_sync(struct sock *sk, const u64 seq, + const enum dccp_pkt_type pkt_type); /* * TX Packet Dequeueing Interface */ -extern void dccp_qpolicy_push(struct sock *sk, struct sk_buff *skb); -extern bool dccp_qpolicy_full(struct sock *sk); -extern void dccp_qpolicy_drop(struct sock *sk, struct sk_buff *skb); -extern struct sk_buff *dccp_qpolicy_top(struct sock *sk); -extern struct sk_buff *dccp_qpolicy_pop(struct sock *sk); -extern bool dccp_qpolicy_param_ok(struct sock *sk, __be32 param); +void dccp_qpolicy_push(struct sock *sk, struct sk_buff *skb); +bool dccp_qpolicy_full(struct sock *sk); +void dccp_qpolicy_drop(struct sock *sk, struct sk_buff *skb); +struct sk_buff *dccp_qpolicy_top(struct sock *sk); +struct sk_buff *dccp_qpolicy_pop(struct sock *sk); +bool dccp_qpolicy_param_ok(struct sock *sk, __be32 param); /* * TX Packet Output and TX Timers */ -extern void dccp_write_xmit(struct sock *sk); -extern void dccp_write_space(struct sock *sk); -extern void dccp_flush_write_queue(struct sock *sk, long *time_budget); +void dccp_write_xmit(struct sock *sk); +void dccp_write_space(struct sock *sk); +void dccp_flush_write_queue(struct sock *sk, long *time_budget); -extern void dccp_init_xmit_timers(struct sock *sk); +void dccp_init_xmit_timers(struct sock *sk); static inline void dccp_clear_xmit_timers(struct sock *sk) { inet_csk_clear_xmit_timers(sk); } -extern unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu); +unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu); -extern const char *dccp_packet_name(const int type); +const char *dccp_packet_name(const int type); -extern void dccp_set_state(struct sock *sk, const int state); -extern void dccp_done(struct sock *sk); +void dccp_set_state(struct sock *sk, const int state); +void dccp_done(struct sock *sk); -extern int dccp_reqsk_init(struct request_sock *rq, struct dccp_sock const *dp, - struct sk_buff const *skb); +int dccp_reqsk_init(struct request_sock *rq, struct dccp_sock const *dp, + struct sk_buff const *skb); -extern int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb); +int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb); -extern struct sock *dccp_create_openreq_child(struct sock *sk, - const struct request_sock *req, - const struct sk_buff *skb); +struct sock *dccp_create_openreq_child(struct sock *sk, + const struct request_sock *req, + const struct sk_buff *skb); -extern int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb); +int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb); -extern struct sock *dccp_v4_request_recv_sock(struct sock *sk, - struct sk_buff *skb, - struct request_sock *req, - struct dst_entry *dst); -extern struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, - struct request_sock *req, - struct request_sock **prev); +struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct dst_entry *dst); +struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb, + struct request_sock *req, + struct request_sock **prev); -extern int dccp_child_process(struct sock *parent, struct sock *child, - struct sk_buff *skb); -extern int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, - struct dccp_hdr *dh, unsigned int len); -extern int dccp_rcv_established(struct sock *sk, struct sk_buff *skb, - const struct dccp_hdr *dh, const unsigned int len); +int dccp_child_process(struct sock *parent, struct sock *child, + struct sk_buff *skb); +int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, + struct dccp_hdr *dh, unsigned int len); +int dccp_rcv_established(struct sock *sk, struct sk_buff *skb, + const struct dccp_hdr *dh, const unsigned int len); -extern int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized); -extern void dccp_destroy_sock(struct sock *sk); +int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized); +void dccp_destroy_sock(struct sock *sk); -extern void dccp_close(struct sock *sk, long timeout); -extern struct sk_buff *dccp_make_response(struct sock *sk, - struct dst_entry *dst, - struct request_sock *req); +void dccp_close(struct sock *sk, long timeout); +struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, + struct request_sock *req); -extern int dccp_connect(struct sock *sk); -extern int dccp_disconnect(struct sock *sk, int flags); -extern int dccp_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen); -extern int dccp_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen); +int dccp_connect(struct sock *sk); +int dccp_disconnect(struct sock *sk, int flags); +int dccp_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); +int dccp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); #ifdef CONFIG_COMPAT -extern int compat_dccp_getsockopt(struct sock *sk, - int level, int optname, - char __user *optval, int __user *optlen); -extern int compat_dccp_setsockopt(struct sock *sk, - int level, int optname, - char __user *optval, unsigned int optlen); +int compat_dccp_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); +int compat_dccp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); #endif -extern int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg); -extern int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t size); -extern int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, int nonblock, - int flags, int *addr_len); -extern void dccp_shutdown(struct sock *sk, int how); -extern int inet_dccp_listen(struct socket *sock, int backlog); -extern unsigned int dccp_poll(struct file *file, struct socket *sock, - poll_table *wait); -extern int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, - int addr_len); - -extern struct sk_buff *dccp_ctl_make_reset(struct sock *sk, - struct sk_buff *skb); -extern int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code); -extern void dccp_send_close(struct sock *sk, const int active); -extern int dccp_invalid_packet(struct sk_buff *skb); -extern u32 dccp_sample_rtt(struct sock *sk, long delta); +int dccp_ioctl(struct sock *sk, int cmd, unsigned long arg); +int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t size); +int dccp_recvmsg(struct kiocb *iocb, struct sock *sk, + struct msghdr *msg, size_t len, int nonblock, int flags, + int *addr_len); +void dccp_shutdown(struct sock *sk, int how); +int inet_dccp_listen(struct socket *sock, int backlog); +unsigned int dccp_poll(struct file *file, struct socket *sock, + poll_table *wait); +int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); + +struct sk_buff *dccp_ctl_make_reset(struct sock *sk, struct sk_buff *skb); +int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code); +void dccp_send_close(struct sock *sk, const int active); +int dccp_invalid_packet(struct sk_buff *skb); +u32 dccp_sample_rtt(struct sock *sk, long delta); static inline int dccp_bad_service_code(const struct sock *sk, const __be32 service) @@ -475,25 +469,25 @@ static inline int dccp_ack_pending(const struct sock *sk) return dccp_ackvec_pending(sk) || inet_csk_ack_scheduled(sk); } -extern int dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val); -extern int dccp_feat_finalise_settings(struct dccp_sock *dp); -extern int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq); -extern int dccp_feat_insert_opts(struct dccp_sock*, struct dccp_request_sock*, - struct sk_buff *skb); -extern int dccp_feat_activate_values(struct sock *sk, struct list_head *fn); -extern void dccp_feat_list_purge(struct list_head *fn_list); - -extern int dccp_insert_options(struct sock *sk, struct sk_buff *skb); -extern int dccp_insert_options_rsk(struct dccp_request_sock*, struct sk_buff*); -extern int dccp_insert_option_elapsed_time(struct sk_buff *skb, u32 elapsed); -extern u32 dccp_timestamp(void); -extern void dccp_timestamping_init(void); -extern int dccp_insert_option(struct sk_buff *skb, unsigned char option, - const void *value, unsigned char len); +int dccp_feat_signal_nn_change(struct sock *sk, u8 feat, u64 nn_val); +int dccp_feat_finalise_settings(struct dccp_sock *dp); +int dccp_feat_server_ccid_dependencies(struct dccp_request_sock *dreq); +int dccp_feat_insert_opts(struct dccp_sock*, struct dccp_request_sock*, + struct sk_buff *skb); +int dccp_feat_activate_values(struct sock *sk, struct list_head *fn); +void dccp_feat_list_purge(struct list_head *fn_list); + +int dccp_insert_options(struct sock *sk, struct sk_buff *skb); +int dccp_insert_options_rsk(struct dccp_request_sock *, struct sk_buff *); +int dccp_insert_option_elapsed_time(struct sk_buff *skb, u32 elapsed); +u32 dccp_timestamp(void); +void dccp_timestamping_init(void); +int dccp_insert_option(struct sk_buff *skb, unsigned char option, + const void *value, unsigned char len); #ifdef CONFIG_SYSCTL -extern int dccp_sysctl_init(void); -extern void dccp_sysctl_exit(void); +int dccp_sysctl_init(void); +void dccp_sysctl_exit(void); #else static inline int dccp_sysctl_init(void) { diff --git a/net/dccp/feat.h b/net/dccp/feat.h index 90b957d34d2602987d49175ef483af3463f9c523..0e75cebb2187ef41c5e06c0e18e9c89b73a27cd2 100644 --- a/net/dccp/feat.h +++ b/net/dccp/feat.h @@ -107,13 +107,13 @@ extern unsigned long sysctl_dccp_sequence_window; extern int sysctl_dccp_rx_ccid; extern int sysctl_dccp_tx_ccid; -extern int dccp_feat_init(struct sock *sk); -extern void dccp_feat_initialise_sysctls(void); -extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, - u8 const *list, u8 len); -extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, - u8 mand, u8 opt, u8 feat, u8 *val, u8 len); -extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); +int dccp_feat_init(struct sock *sk); +void dccp_feat_initialise_sysctls(void); +int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local, + u8 const *list, u8 len); +int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *, + u8 mand, u8 opt, u8 feat, u8 *val, u8 len); +int dccp_feat_clone_list(struct list_head const *, struct list_head *); /* * Encoding variable-length options and their maximum length. @@ -127,11 +127,11 @@ extern int dccp_feat_clone_list(struct list_head const *, struct list_head *); */ #define DCCP_OPTVAL_MAXLEN 6 -extern void dccp_encode_value_var(const u64 value, u8 *to, const u8 len); -extern u64 dccp_decode_value_var(const u8 *bf, const u8 len); -extern u64 dccp_feat_nn_get(struct sock *sk, u8 feat); +void dccp_encode_value_var(const u64 value, u8 *to, const u8 len); +u64 dccp_decode_value_var(const u8 *bf, const u8 len); +u64 dccp_feat_nn_get(struct sock *sk, u8 feat); -extern int dccp_insert_option_mandatory(struct sk_buff *skb); -extern int dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat, - u8 *val, u8 len, bool repeat_first); +int dccp_insert_option_mandatory(struct sk_buff *skb); +int dccp_insert_fn_opt(struct sk_buff *skb, u8 type, u8 feat, u8 *val, u8 len, + bool repeat_first); #endif /* _DCCP_FEAT_H */ diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index ebc54fef85a5ed15110fe39002defd42d6a0b8f7..d9f65fc66db5ea28773962387c008597d0f99269 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -174,6 +174,7 @@ static inline void dccp_do_pmtu_discovery(struct sock *sk, mtu = dst_mtu(dst); if (inet->pmtudisc != IP_PMTUDISC_DONT && + ip_sk_accept_pmtu(sk) && inet_csk(sk)->icsk_pmtu_cookie > mtu) { dccp_sync_mss(sk, mtu); @@ -409,9 +410,9 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb, newinet = inet_sk(newsk); ireq = inet_rsk(req); - newinet->inet_daddr = ireq->rmt_addr; - newinet->inet_rcv_saddr = ireq->loc_addr; - newinet->inet_saddr = ireq->loc_addr; + newinet->inet_daddr = ireq->ir_rmt_addr; + newinet->inet_rcv_saddr = ireq->ir_loc_addr; + newinet->inet_saddr = ireq->ir_loc_addr; newinet->inet_opt = ireq->opt; ireq->opt = NULL; newinet->mc_index = inet_iif(skb); @@ -516,10 +517,10 @@ static int dccp_v4_send_response(struct sock *sk, struct request_sock *req) const struct inet_request_sock *ireq = inet_rsk(req); struct dccp_hdr *dh = dccp_hdr(skb); - dh->dccph_checksum = dccp_v4_csum_finish(skb, ireq->loc_addr, - ireq->rmt_addr); - err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr, - ireq->rmt_addr, + dh->dccph_checksum = dccp_v4_csum_finish(skb, ireq->ir_loc_addr, + ireq->ir_rmt_addr); + err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr, + ireq->ir_rmt_addr, ireq->opt); err = net_xmit_eval(err); } @@ -641,8 +642,8 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) goto drop_and_free; ireq = inet_rsk(req); - ireq->loc_addr = ip_hdr(skb)->daddr; - ireq->rmt_addr = ip_hdr(skb)->saddr; + ireq->ir_loc_addr = ip_hdr(skb)->daddr; + ireq->ir_rmt_addr = ip_hdr(skb)->saddr; /* * Step 3: Process LISTEN state diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 6cf9f7782ad4238208173f390369b5fc4cc75e2b..4ac71ff7c2e47c4a3bcec19206845c8afd7ce9e8 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -67,7 +67,7 @@ static inline void dccp_v6_send_check(struct sock *sk, struct sk_buff *skb) struct dccp_hdr *dh = dccp_hdr(skb); dccp_csum_outgoing(skb); - dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &np->daddr); + dh->dccph_checksum = dccp_v6_csum_finish(skb, &np->saddr, &sk->sk_v6_daddr); } static inline __u64 dccp_v6_init_sequence(struct sk_buff *skb) @@ -216,7 +216,7 @@ out: static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) { - struct inet6_request_sock *ireq6 = inet6_rsk(req); + struct inet_request_sock *ireq = inet_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); struct sk_buff *skb; struct in6_addr *final_p, final; @@ -226,12 +226,12 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_DCCP; - fl6.daddr = ireq6->rmt_addr; - fl6.saddr = ireq6->loc_addr; + fl6.daddr = ireq->ir_v6_rmt_addr; + fl6.saddr = ireq->ir_v6_loc_addr; fl6.flowlabel = 0; - fl6.flowi6_oif = ireq6->iif; - fl6.fl6_dport = inet_rsk(req)->rmt_port; - fl6.fl6_sport = inet_rsk(req)->loc_port; + fl6.flowi6_oif = ireq->ir_iif; + fl6.fl6_dport = ireq->ir_rmt_port; + fl6.fl6_sport = htons(ireq->ir_num); security_req_classify_flow(req, flowi6_to_flowi(&fl6)); @@ -249,9 +249,9 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req) struct dccp_hdr *dh = dccp_hdr(skb); dh->dccph_checksum = dccp_v6_csum_finish(skb, - &ireq6->loc_addr, - &ireq6->rmt_addr); - fl6.daddr = ireq6->rmt_addr; + &ireq->ir_v6_loc_addr, + &ireq->ir_v6_rmt_addr); + fl6.daddr = ireq->ir_v6_rmt_addr; err = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); err = net_xmit_eval(err); } @@ -264,8 +264,7 @@ done: static void dccp_v6_reqsk_destructor(struct request_sock *req) { dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg); - if (inet6_rsk(req)->pktopts != NULL) - kfree_skb(inet6_rsk(req)->pktopts); + kfree_skb(inet_rsk(req)->pktopts); } static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb) @@ -359,7 +358,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) { struct request_sock *req; struct dccp_request_sock *dreq; - struct inet6_request_sock *ireq6; + struct inet_request_sock *ireq; struct ipv6_pinfo *np = inet6_sk(sk); const __be32 service = dccp_hdr_request(skb)->dccph_req_service; struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); @@ -398,22 +397,22 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) if (security_inet_conn_request(sk, skb, req)) goto drop_and_free; - ireq6 = inet6_rsk(req); - ireq6->rmt_addr = ipv6_hdr(skb)->saddr; - ireq6->loc_addr = ipv6_hdr(skb)->daddr; + ireq = inet_rsk(req); + ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; + ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; if (ipv6_opt_accepted(sk, skb) || np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) { atomic_inc(&skb->users); - ireq6->pktopts = skb; + ireq->pktopts = skb; } - ireq6->iif = sk->sk_bound_dev_if; + ireq->ir_iif = sk->sk_bound_dev_if; /* So that link locals have meaning */ if (!sk->sk_bound_dev_if && - ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL) - ireq6->iif = inet6_iif(skb); + ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) + ireq->ir_iif = inet6_iif(skb); /* * Step 3: Process LISTEN state @@ -446,7 +445,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, struct request_sock *req, struct dst_entry *dst) { - struct inet6_request_sock *ireq6 = inet6_rsk(req); + struct inet_request_sock *ireq = inet_rsk(req); struct ipv6_pinfo *newnp, *np = inet6_sk(sk); struct inet_sock *newinet; struct dccp6_sock *newdp6; @@ -467,11 +466,11 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, memcpy(newnp, np, sizeof(struct ipv6_pinfo)); - ipv6_addr_set_v4mapped(newinet->inet_daddr, &newnp->daddr); + ipv6_addr_set_v4mapped(newinet->inet_daddr, &newsk->sk_v6_daddr); ipv6_addr_set_v4mapped(newinet->inet_saddr, &newnp->saddr); - newnp->rcv_saddr = newnp->saddr; + newsk->sk_v6_rcv_saddr = newnp->saddr; inet_csk(newsk)->icsk_af_ops = &dccp_ipv6_mapped; newsk->sk_backlog_rcv = dccp_v4_do_rcv; @@ -505,12 +504,12 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_DCCP; - fl6.daddr = ireq6->rmt_addr; + fl6.daddr = ireq->ir_v6_rmt_addr; final_p = fl6_update_dst(&fl6, np->opt, &final); - fl6.saddr = ireq6->loc_addr; + fl6.saddr = ireq->ir_v6_loc_addr; fl6.flowi6_oif = sk->sk_bound_dev_if; - fl6.fl6_dport = inet_rsk(req)->rmt_port; - fl6.fl6_sport = inet_rsk(req)->loc_port; + fl6.fl6_dport = ireq->ir_rmt_port; + fl6.fl6_sport = htons(ireq->ir_num); security_sk_classify_flow(sk, flowi6_to_flowi(&fl6)); dst = ip6_dst_lookup_flow(sk, &fl6, final_p, false); @@ -538,10 +537,10 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, memcpy(newnp, np, sizeof(struct ipv6_pinfo)); - newnp->daddr = ireq6->rmt_addr; - newnp->saddr = ireq6->loc_addr; - newnp->rcv_saddr = ireq6->loc_addr; - newsk->sk_bound_dev_if = ireq6->iif; + newsk->sk_v6_daddr = ireq->ir_v6_rmt_addr; + newnp->saddr = ireq->ir_v6_loc_addr; + newsk->sk_v6_rcv_saddr = ireq->ir_v6_loc_addr; + newsk->sk_bound_dev_if = ireq->ir_iif; /* Now IPv6 options... @@ -554,10 +553,10 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, /* Clone pktoptions received with SYN */ newnp->pktoptions = NULL; - if (ireq6->pktopts != NULL) { - newnp->pktoptions = skb_clone(ireq6->pktopts, GFP_ATOMIC); - consume_skb(ireq6->pktopts); - ireq6->pktopts = NULL; + if (ireq->pktopts != NULL) { + newnp->pktoptions = skb_clone(ireq->pktopts, GFP_ATOMIC); + consume_skb(ireq->pktopts); + ireq->pktopts = NULL; if (newnp->pktoptions) skb_set_owner_r(newnp->pktoptions, newsk); } @@ -885,7 +884,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, return -EINVAL; } - np->daddr = usin->sin6_addr; + sk->sk_v6_daddr = usin->sin6_addr; np->flow_label = fl6.flowlabel; /* @@ -915,16 +914,16 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, goto failure; } ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr); - ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, &np->rcv_saddr); + ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, &sk->sk_v6_rcv_saddr); return err; } - if (!ipv6_addr_any(&np->rcv_saddr)) - saddr = &np->rcv_saddr; + if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) + saddr = &sk->sk_v6_rcv_saddr; fl6.flowi6_proto = IPPROTO_DCCP; - fl6.daddr = np->daddr; + fl6.daddr = sk->sk_v6_daddr; fl6.saddr = saddr ? *saddr : np->saddr; fl6.flowi6_oif = sk->sk_bound_dev_if; fl6.fl6_dport = usin->sin6_port; @@ -941,7 +940,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if (saddr == NULL) { saddr = &fl6.saddr; - np->rcv_saddr = *saddr; + sk->sk_v6_rcv_saddr = *saddr; } /* set the source address */ @@ -963,7 +962,7 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, goto late_failure; dp->dccps_iss = secure_dccpv6_sequence_number(np->saddr.s6_addr32, - np->daddr.s6_addr32, + sk->sk_v6_daddr.s6_addr32, inet->inet_sport, inet->inet_dport); err = dccp_connect(sk); diff --git a/net/dccp/ipv6.h b/net/dccp/ipv6.h index 6eef81fdbe566f5623fc0816370ac15b4c01dc5a..af259e15e7f0b8c4cb09381bba5423a59c7e5540 100644 --- a/net/dccp/ipv6.h +++ b/net/dccp/ipv6.h @@ -25,12 +25,10 @@ struct dccp6_sock { struct dccp6_request_sock { struct dccp_request_sock dccp; - struct inet6_request_sock inet6; }; struct dccp6_timewait_sock { struct inet_timewait_sock inet; - struct inet6_timewait_sock tw6; }; #endif /* _DCCP_IPV6_H */ diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c index 662071b249cc627130cf90eeaa8e5b7bcd8b829d..9e2f78bc1553e38ed45c66e3c3aba4db302fa0b0 100644 --- a/net/dccp/minisocks.c +++ b/net/dccp/minisocks.c @@ -56,12 +56,9 @@ void dccp_time_wait(struct sock *sk, int state, int timeo) #if IS_ENABLED(CONFIG_IPV6) if (tw->tw_family == PF_INET6) { const struct ipv6_pinfo *np = inet6_sk(sk); - struct inet6_timewait_sock *tw6; - tw->tw_ipv6_offset = inet6_tw_offset(sk->sk_prot); - tw6 = inet6_twsk((struct sock *)tw); - tw6->tw_v6_daddr = np->daddr; - tw6->tw_v6_rcv_saddr = np->rcv_saddr; + tw->tw_v6_daddr = sk->sk_v6_daddr; + tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr; tw->tw_ipv6only = np->ipv6only; } #endif @@ -269,10 +266,10 @@ int dccp_reqsk_init(struct request_sock *req, { struct dccp_request_sock *dreq = dccp_rsk(req); - inet_rsk(req)->rmt_port = dccp_hdr(skb)->dccph_sport; - inet_rsk(req)->loc_port = dccp_hdr(skb)->dccph_dport; - inet_rsk(req)->acked = 0; - dreq->dreq_timestamp_echo = 0; + inet_rsk(req)->ir_rmt_port = dccp_hdr(skb)->dccph_sport; + inet_rsk(req)->ir_num = ntohs(dccp_hdr(skb)->dccph_dport); + inet_rsk(req)->acked = 0; + dreq->dreq_timestamp_echo = 0; /* inherit feature negotiation options from listening socket */ return dccp_feat_clone_list(&dp->dccps_featneg, &dreq->dreq_featneg); diff --git a/net/dccp/output.c b/net/dccp/output.c index d17fc90a74b63822bb6de1f647c34903a855b197..8876078859dac20ef543043ae5b98ec42f8731cd 100644 --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -424,8 +424,8 @@ struct sk_buff *dccp_make_response(struct sock *sk, struct dst_entry *dst, /* Build and checksum header */ dh = dccp_zeroed_hdr(skb, dccp_header_size); - dh->dccph_sport = inet_rsk(req)->loc_port; - dh->dccph_dport = inet_rsk(req)->rmt_port; + dh->dccph_sport = htons(inet_rsk(req)->ir_num); + dh->dccph_dport = inet_rsk(req)->ir_rmt_port; dh->dccph_doff = (dccp_header_size + DCCP_SKB_CB(skb)->dccpd_opt_len) / 4; dh->dccph_type = DCCP_PKT_RESPONSE; diff --git a/net/dccp/proto.c b/net/dccp/proto.c index ba64750f038726a990caa71d90e45f77bad93230..eb892b4f48144966e47f386108942f51b8b85e50 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -1158,10 +1158,8 @@ static int __init dccp_init(void) goto out_free_bind_bucket_cachep; } - for (i = 0; i <= dccp_hashinfo.ehash_mask; i++) { + for (i = 0; i <= dccp_hashinfo.ehash_mask; i++) INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].chain, i); - INIT_HLIST_NULLS_HEAD(&dccp_hashinfo.ehash[i].twchain, i); - } if (inet_ehash_locks_alloc(&dccp_hashinfo)) goto out_free_dccp_ehash; diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index 2a7efe388344fdd37b10487c50ba3d712eedbf8a..e83015cecfa7507d551bd19e4b4121ad0f25eeaf 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -87,7 +87,7 @@ static void dnrmg_send_peer(struct sk_buff *skb) } -static unsigned int dnrmg_hook(unsigned int hook, +static unsigned int dnrmg_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c index be1f64d35358fbfbf61d84683c52e4d6fb423f34..8f032bae60ad8fdc8e284c5cd5ae7501660fcf50 100644 --- a/net/ethernet/eth.c +++ b/net/ethernet/eth.c @@ -58,7 +58,7 @@ #include #include #include -#include +#include __setup("ether=", netdev_boot_setup); @@ -133,7 +133,7 @@ int eth_rebuild_header(struct sk_buff *skb) return arp_find(eth->h_dest, skb); #endif default: - printk(KERN_DEBUG + netdev_dbg(dev, "%s: unable to resolve type %X addresses.\n", dev->name, ntohs(eth->h_proto)); @@ -169,20 +169,9 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) else skb->pkt_type = PACKET_MULTICAST; } - - /* - * This ALLMULTI check should be redundant by 1.4 - * so don't forget to remove it. - * - * Seems, you forgot to remove it. All silly devices - * seems to set IFF_PROMISC. - */ - - else if (1 /*dev->flags&IFF_PROMISC */ ) { - if (unlikely(!ether_addr_equal_64bits(eth->h_dest, - dev->dev_addr))) - skb->pkt_type = PACKET_OTHERHOST; - } + else if (unlikely(!ether_addr_equal_64bits(eth->h_dest, + dev->dev_addr))) + skb->pkt_type = PACKET_OTHERHOST; /* * Some variants of DSA tagging don't have an ethertype field @@ -190,12 +179,13 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) * variants has been configured on the receiving interface, * and if so, set skb->protocol without looking at the packet. */ - if (netdev_uses_dsa_tags(dev)) + if (unlikely(netdev_uses_dsa_tags(dev))) return htons(ETH_P_DSA); - if (netdev_uses_trailer_tags(dev)) + + if (unlikely(netdev_uses_trailer_tags(dev))) return htons(ETH_P_TRAILER); - if (ntohs(eth->h_proto) >= ETH_P_802_3_MIN) + if (likely(ntohs(eth->h_proto) >= ETH_P_802_3_MIN)) return eth->h_proto; /* @@ -204,7 +194,7 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev) * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This * won't work for fault tolerant netware but does for the rest. */ - if (skb->len >= 2 && *(unsigned short *)(skb->data) == 0xFFFF) + if (unlikely(skb->len >= 2 && *(unsigned short *)(skb->data) == 0xFFFF)) return htons(ETH_P_802_3); /* diff --git a/net/hsr/Kconfig b/net/hsr/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..0d3d709052cac91a807d38bc3e971bc2ae8c6a36 --- /dev/null +++ b/net/hsr/Kconfig @@ -0,0 +1,27 @@ +# +# IEC 62439-3 High-availability Seamless Redundancy +# + +config HSR + tristate "High-availability Seamless Redundancy (HSR)" + ---help--- + If you say Y here, then your Linux box will be able to act as a + DANH ("Doubly attached node implementing HSR"). For this to work, + your Linux box needs (at least) two physical Ethernet interfaces, + and it must be connected as a node in a ring network together with + other HSR capable nodes. + + All Ethernet frames sent over the hsr device will be sent in both + directions on the ring (over both slave ports), giving a redundant, + instant fail-over network. Each HSR node in the ring acts like a + bridge for HSR frames, but filters frames that have been forwarded + earlier. + + This code is a "best effort" to comply with the HSR standard as + described in IEC 62439-3:2010 (HSRv0), but no compliancy tests have + been made. + + You need to perform any and all necessary tests yourself before + relying on this code in a safety critical system! + + If unsure, say N. diff --git a/net/hsr/Makefile b/net/hsr/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b68359f181cca9747c73d123d9dfd8b72f8e8c4f --- /dev/null +++ b/net/hsr/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for HSR +# + +obj-$(CONFIG_HSR) += hsr.o + +hsr-y := hsr_main.o hsr_framereg.o hsr_device.o hsr_netlink.o diff --git a/net/hsr/hsr_device.c b/net/hsr/hsr_device.c new file mode 100644 index 0000000000000000000000000000000000000000..cac505f166d51133a32dac0e08c12358871fe6f6 --- /dev/null +++ b/net/hsr/hsr_device.c @@ -0,0 +1,596 @@ +/* Copyright 2011-2013 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + * + * This file contains device methods for creating, using and destroying + * virtual HSR devices. + */ + +#include +#include +#include +#include +#include +#include +#include "hsr_device.h" +#include "hsr_framereg.h" +#include "hsr_main.h" + + +static bool is_admin_up(struct net_device *dev) +{ + return dev && (dev->flags & IFF_UP); +} + +static bool is_slave_up(struct net_device *dev) +{ + return dev && is_admin_up(dev) && netif_oper_up(dev); +} + +static void __hsr_set_operstate(struct net_device *dev, int transition) +{ + write_lock_bh(&dev_base_lock); + if (dev->operstate != transition) { + dev->operstate = transition; + write_unlock_bh(&dev_base_lock); + netdev_state_change(dev); + } else { + write_unlock_bh(&dev_base_lock); + } +} + +void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1, + struct net_device *slave2) +{ + if (!is_admin_up(hsr_dev)) { + __hsr_set_operstate(hsr_dev, IF_OPER_DOWN); + return; + } + + if (is_slave_up(slave1) || is_slave_up(slave2)) + __hsr_set_operstate(hsr_dev, IF_OPER_UP); + else + __hsr_set_operstate(hsr_dev, IF_OPER_LOWERLAYERDOWN); +} + +void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1, + struct net_device *slave2) +{ + if (is_slave_up(slave1) || is_slave_up(slave2)) + netif_carrier_on(hsr_dev); + else + netif_carrier_off(hsr_dev); +} + + +void hsr_check_announce(struct net_device *hsr_dev, int old_operstate) +{ + struct hsr_priv *hsr_priv; + + hsr_priv = netdev_priv(hsr_dev); + + if ((hsr_dev->operstate == IF_OPER_UP) && (old_operstate != IF_OPER_UP)) { + /* Went up */ + hsr_priv->announce_count = 0; + hsr_priv->announce_timer.expires = jiffies + + msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL); + add_timer(&hsr_priv->announce_timer); + } + + if ((hsr_dev->operstate != IF_OPER_UP) && (old_operstate == IF_OPER_UP)) + /* Went down */ + del_timer(&hsr_priv->announce_timer); +} + + +int hsr_get_max_mtu(struct hsr_priv *hsr_priv) +{ + int mtu_max; + + if (hsr_priv->slave[0] && hsr_priv->slave[1]) + mtu_max = min(hsr_priv->slave[0]->mtu, hsr_priv->slave[1]->mtu); + else if (hsr_priv->slave[0]) + mtu_max = hsr_priv->slave[0]->mtu; + else if (hsr_priv->slave[1]) + mtu_max = hsr_priv->slave[1]->mtu; + else + mtu_max = HSR_TAGLEN; + + return mtu_max - HSR_TAGLEN; +} + +static int hsr_dev_change_mtu(struct net_device *dev, int new_mtu) +{ + struct hsr_priv *hsr_priv; + + hsr_priv = netdev_priv(dev); + + if (new_mtu > hsr_get_max_mtu(hsr_priv)) { + netdev_info(hsr_priv->dev, "A HSR master's MTU cannot be greater than the smallest MTU of its slaves minus the HSR Tag length (%d octets).\n", + HSR_TAGLEN); + return -EINVAL; + } + + dev->mtu = new_mtu; + + return 0; +} + +static int hsr_dev_open(struct net_device *dev) +{ + struct hsr_priv *hsr_priv; + int i; + char *slave_name; + + hsr_priv = netdev_priv(dev); + + for (i = 0; i < HSR_MAX_SLAVE; i++) { + if (hsr_priv->slave[i]) + slave_name = hsr_priv->slave[i]->name; + else + slave_name = "null"; + + if (!is_slave_up(hsr_priv->slave[i])) + netdev_warn(dev, "Slave %c (%s) is not up; please bring it up to get a working HSR network\n", + 'A' + i, slave_name); + } + + return 0; +} + +static int hsr_dev_close(struct net_device *dev) +{ + /* Nothing to do here. We could try to restore the state of the slaves + * to what they were before being changed by the hsr master dev's state, + * but they might have been changed manually in the mean time too, so + * taking them up or down here might be confusing and is probably not a + * good idea. + */ + return 0; +} + + +static void hsr_fill_tag(struct hsr_ethhdr *hsr_ethhdr, struct hsr_priv *hsr_priv) +{ + unsigned long irqflags; + + /* IEC 62439-1:2010, p 48, says the 4-bit "path" field can take values + * between 0001-1001 ("ring identifier", for regular HSR frames), + * or 1111 ("HSR management", supervision frames). Unfortunately, the + * spec writers forgot to explain what a "ring identifier" is, or + * how it is used. So we just set this to 0001 for regular frames, + * and 1111 for supervision frames. + */ + set_hsr_tag_path(&hsr_ethhdr->hsr_tag, 0x1); + + /* IEC 62439-1:2010, p 12: "The link service data unit in an Ethernet + * frame is the content of the frame located between the Length/Type + * field and the Frame Check Sequence." + * + * IEC 62439-3, p 48, specifies the "original LPDU" to include the + * original "LT" field (what "LT" means is not explained anywhere as + * far as I can see - perhaps "Length/Type"?). So LSDU_size might + * equal original length + 2. + * Also, the fact that this field is not used anywhere (might be used + * by a RedBox connecting HSR and PRP nets?) means I cannot test its + * correctness. Instead of guessing, I set this to 0 here, to make any + * problems immediately apparent. Anyone using this driver with PRP/HSR + * RedBoxes might need to fix this... + */ + set_hsr_tag_LSDU_size(&hsr_ethhdr->hsr_tag, 0); + + spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags); + hsr_ethhdr->hsr_tag.sequence_nr = htons(hsr_priv->sequence_nr); + hsr_priv->sequence_nr++; + spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags); + + hsr_ethhdr->hsr_tag.encap_proto = hsr_ethhdr->ethhdr.h_proto; + + hsr_ethhdr->ethhdr.h_proto = htons(ETH_P_PRP); +} + +static int slave_xmit(struct sk_buff *skb, struct hsr_priv *hsr_priv, + enum hsr_dev_idx dev_idx) +{ + struct hsr_ethhdr *hsr_ethhdr; + + hsr_ethhdr = (struct hsr_ethhdr *) skb->data; + + skb->dev = hsr_priv->slave[dev_idx]; + + hsr_addr_subst_dest(hsr_priv, &hsr_ethhdr->ethhdr, dev_idx); + + /* Address substitution (IEC62439-3 pp 26, 50): replace mac + * address of outgoing frame with that of the outgoing slave's. + */ + memcpy(hsr_ethhdr->ethhdr.h_source, skb->dev->dev_addr, ETH_ALEN); + + return dev_queue_xmit(skb); +} + + +static int hsr_dev_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct hsr_priv *hsr_priv; + struct hsr_ethhdr *hsr_ethhdr; + struct sk_buff *skb2; + int res1, res2; + + hsr_priv = netdev_priv(dev); + hsr_ethhdr = (struct hsr_ethhdr *) skb->data; + + if ((skb->protocol != htons(ETH_P_PRP)) || + (hsr_ethhdr->ethhdr.h_proto != htons(ETH_P_PRP))) { + hsr_fill_tag(hsr_ethhdr, hsr_priv); + skb->protocol = htons(ETH_P_PRP); + } + + skb2 = pskb_copy(skb, GFP_ATOMIC); + + res1 = NET_XMIT_DROP; + if (likely(hsr_priv->slave[HSR_DEV_SLAVE_A])) + res1 = slave_xmit(skb, hsr_priv, HSR_DEV_SLAVE_A); + + res2 = NET_XMIT_DROP; + if (likely(skb2 && hsr_priv->slave[HSR_DEV_SLAVE_B])) + res2 = slave_xmit(skb2, hsr_priv, HSR_DEV_SLAVE_B); + + if (likely(res1 == NET_XMIT_SUCCESS || res1 == NET_XMIT_CN || + res2 == NET_XMIT_SUCCESS || res2 == NET_XMIT_CN)) { + hsr_priv->dev->stats.tx_packets++; + hsr_priv->dev->stats.tx_bytes += skb->len; + } else { + hsr_priv->dev->stats.tx_dropped++; + } + + return NETDEV_TX_OK; +} + + +static int hsr_header_create(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned int len) +{ + int res; + + /* Make room for the HSR tag now. We will fill it in later (in + * hsr_dev_xmit) + */ + if (skb_headroom(skb) < HSR_TAGLEN + ETH_HLEN) + return -ENOBUFS; + skb_push(skb, HSR_TAGLEN); + + /* To allow VLAN/HSR combos we should probably use + * res = dev_hard_header(skb, dev, type, daddr, saddr, len + HSR_TAGLEN); + * here instead. It would require other changes too, though - e.g. + * separate headers for each slave etc... + */ + res = eth_header(skb, dev, type, daddr, saddr, len + HSR_TAGLEN); + if (res <= 0) + return res; + skb_reset_mac_header(skb); + + return res + HSR_TAGLEN; +} + + +static const struct header_ops hsr_header_ops = { + .create = hsr_header_create, + .parse = eth_header_parse, +}; + + +/* HSR:2010 supervision frames should be padded so that the whole frame, + * including headers and FCS, is 64 bytes (without VLAN). + */ +static int hsr_pad(int size) +{ + const int min_size = ETH_ZLEN - HSR_TAGLEN - ETH_HLEN; + + if (size >= min_size) + return size; + return min_size; +} + +static void send_hsr_supervision_frame(struct net_device *hsr_dev, u8 type) +{ + struct hsr_priv *hsr_priv; + struct sk_buff *skb; + int hlen, tlen; + struct hsr_sup_tag *hsr_stag; + struct hsr_sup_payload *hsr_sp; + unsigned long irqflags; + + hlen = LL_RESERVED_SPACE(hsr_dev); + tlen = hsr_dev->needed_tailroom; + skb = alloc_skb(hsr_pad(sizeof(struct hsr_sup_payload)) + hlen + tlen, + GFP_ATOMIC); + + if (skb == NULL) + return; + + hsr_priv = netdev_priv(hsr_dev); + + skb_reserve(skb, hlen); + + skb->dev = hsr_dev; + skb->protocol = htons(ETH_P_PRP); + skb->priority = TC_PRIO_CONTROL; + + if (dev_hard_header(skb, skb->dev, ETH_P_PRP, + hsr_priv->sup_multicast_addr, + skb->dev->dev_addr, skb->len) < 0) + goto out; + + skb_pull(skb, sizeof(struct ethhdr)); + hsr_stag = (typeof(hsr_stag)) skb->data; + + set_hsr_stag_path(hsr_stag, 0xf); + set_hsr_stag_HSR_Ver(hsr_stag, 0); + + spin_lock_irqsave(&hsr_priv->seqnr_lock, irqflags); + hsr_stag->sequence_nr = htons(hsr_priv->sequence_nr); + hsr_priv->sequence_nr++; + spin_unlock_irqrestore(&hsr_priv->seqnr_lock, irqflags); + + hsr_stag->HSR_TLV_Type = type; + hsr_stag->HSR_TLV_Length = 12; + + skb_push(skb, sizeof(struct ethhdr)); + + /* Payload: MacAddressA */ + hsr_sp = (typeof(hsr_sp)) skb_put(skb, sizeof(*hsr_sp)); + memcpy(hsr_sp->MacAddressA, hsr_dev->dev_addr, ETH_ALEN); + + dev_queue_xmit(skb); + return; + +out: + kfree_skb(skb); +} + + +/* Announce (supervision frame) timer function + */ +static void hsr_announce(unsigned long data) +{ + struct hsr_priv *hsr_priv; + + hsr_priv = (struct hsr_priv *) data; + + if (hsr_priv->announce_count < 3) { + send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_ANNOUNCE); + hsr_priv->announce_count++; + } else { + send_hsr_supervision_frame(hsr_priv->dev, HSR_TLV_LIFE_CHECK); + } + + if (hsr_priv->announce_count < 3) + hsr_priv->announce_timer.expires = jiffies + + msecs_to_jiffies(HSR_ANNOUNCE_INTERVAL); + else + hsr_priv->announce_timer.expires = jiffies + + msecs_to_jiffies(HSR_LIFE_CHECK_INTERVAL); + + if (is_admin_up(hsr_priv->dev)) + add_timer(&hsr_priv->announce_timer); +} + + +static void restore_slaves(struct net_device *hsr_dev) +{ + struct hsr_priv *hsr_priv; + int i; + int res; + + hsr_priv = netdev_priv(hsr_dev); + + rtnl_lock(); + + /* Restore promiscuity */ + for (i = 0; i < HSR_MAX_SLAVE; i++) { + if (!hsr_priv->slave[i]) + continue; + res = dev_set_promiscuity(hsr_priv->slave[i], -1); + if (res) + netdev_info(hsr_dev, + "Cannot restore slave promiscuity (%s, %d)\n", + hsr_priv->slave[i]->name, res); + } + + rtnl_unlock(); +} + +static void reclaim_hsr_dev(struct rcu_head *rh) +{ + struct hsr_priv *hsr_priv; + + hsr_priv = container_of(rh, struct hsr_priv, rcu_head); + free_netdev(hsr_priv->dev); +} + + +/* According to comments in the declaration of struct net_device, this function + * is "Called from unregister, can be used to call free_netdev". Ok then... + */ +static void hsr_dev_destroy(struct net_device *hsr_dev) +{ + struct hsr_priv *hsr_priv; + + hsr_priv = netdev_priv(hsr_dev); + + del_timer(&hsr_priv->announce_timer); + unregister_hsr_master(hsr_priv); /* calls list_del_rcu on hsr_priv */ + restore_slaves(hsr_dev); + call_rcu(&hsr_priv->rcu_head, reclaim_hsr_dev); /* reclaim hsr_priv */ +} + +static const struct net_device_ops hsr_device_ops = { + .ndo_change_mtu = hsr_dev_change_mtu, + .ndo_open = hsr_dev_open, + .ndo_stop = hsr_dev_close, + .ndo_start_xmit = hsr_dev_xmit, +}; + + +void hsr_dev_setup(struct net_device *dev) +{ + random_ether_addr(dev->dev_addr); + + ether_setup(dev); + dev->header_ops = &hsr_header_ops; + dev->netdev_ops = &hsr_device_ops; + dev->tx_queue_len = 0; + + dev->destructor = hsr_dev_destroy; +} + + +/* Return true if dev is a HSR master; return false otherwise. + */ +bool is_hsr_master(struct net_device *dev) +{ + return (dev->netdev_ops->ndo_start_xmit == hsr_dev_xmit); +} + +static int check_slave_ok(struct net_device *dev) +{ + /* Don't allow HSR on non-ethernet like devices */ + if ((dev->flags & IFF_LOOPBACK) || (dev->type != ARPHRD_ETHER) || + (dev->addr_len != ETH_ALEN)) { + netdev_info(dev, "Cannot use loopback or non-ethernet device as HSR slave.\n"); + return -EINVAL; + } + + /* Don't allow enslaving hsr devices */ + if (is_hsr_master(dev)) { + netdev_info(dev, "Cannot create trees of HSR devices.\n"); + return -EINVAL; + } + + if (is_hsr_slave(dev)) { + netdev_info(dev, "This device is already a HSR slave.\n"); + return -EINVAL; + } + + if (dev->priv_flags & IFF_802_1Q_VLAN) { + netdev_info(dev, "HSR on top of VLAN is not yet supported in this driver.\n"); + return -EINVAL; + } + + /* HSR over bonded devices has not been tested, but I'm not sure it + * won't work... + */ + + return 0; +} + + +/* Default multicast address for HSR Supervision frames */ +static const unsigned char def_multicast_addr[ETH_ALEN] = { + 0x01, 0x15, 0x4e, 0x00, 0x01, 0x00 +}; + +int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], + unsigned char multicast_spec) +{ + struct hsr_priv *hsr_priv; + int i; + int res; + + hsr_priv = netdev_priv(hsr_dev); + hsr_priv->dev = hsr_dev; + INIT_LIST_HEAD(&hsr_priv->node_db); + INIT_LIST_HEAD(&hsr_priv->self_node_db); + for (i = 0; i < HSR_MAX_SLAVE; i++) + hsr_priv->slave[i] = slave[i]; + + spin_lock_init(&hsr_priv->seqnr_lock); + /* Overflow soon to find bugs easier: */ + hsr_priv->sequence_nr = USHRT_MAX - 1024; + + init_timer(&hsr_priv->announce_timer); + hsr_priv->announce_timer.function = hsr_announce; + hsr_priv->announce_timer.data = (unsigned long) hsr_priv; + + memcpy(hsr_priv->sup_multicast_addr, def_multicast_addr, ETH_ALEN); + hsr_priv->sup_multicast_addr[ETH_ALEN - 1] = multicast_spec; + +/* FIXME: should I modify the value of these? + * + * - hsr_dev->flags - i.e. + * IFF_MASTER/SLAVE? + * - hsr_dev->priv_flags - i.e. + * IFF_EBRIDGE? + * IFF_TX_SKB_SHARING? + * IFF_HSR_MASTER/SLAVE? + */ + + for (i = 0; i < HSR_MAX_SLAVE; i++) { + res = check_slave_ok(slave[i]); + if (res) + return res; + } + + hsr_dev->features = slave[0]->features & slave[1]->features; + /* Prevent recursive tx locking */ + hsr_dev->features |= NETIF_F_LLTX; + /* VLAN on top of HSR needs testing and probably some work on + * hsr_header_create() etc. + */ + hsr_dev->features |= NETIF_F_VLAN_CHALLENGED; + + /* Set hsr_dev's MAC address to that of mac_slave1 */ + memcpy(hsr_dev->dev_addr, hsr_priv->slave[0]->dev_addr, ETH_ALEN); + + /* Set required header length */ + for (i = 0; i < HSR_MAX_SLAVE; i++) { + if (slave[i]->hard_header_len + HSR_TAGLEN > + hsr_dev->hard_header_len) + hsr_dev->hard_header_len = + slave[i]->hard_header_len + HSR_TAGLEN; + } + + /* MTU */ + for (i = 0; i < HSR_MAX_SLAVE; i++) + if (slave[i]->mtu - HSR_TAGLEN < hsr_dev->mtu) + hsr_dev->mtu = slave[i]->mtu - HSR_TAGLEN; + + /* Make sure the 1st call to netif_carrier_on() gets through */ + netif_carrier_off(hsr_dev); + + /* Promiscuity */ + for (i = 0; i < HSR_MAX_SLAVE; i++) { + res = dev_set_promiscuity(slave[i], 1); + if (res) { + netdev_info(hsr_dev, "Cannot set slave promiscuity (%s, %d)\n", + slave[i]->name, res); + goto fail; + } + } + + /* Make sure we recognize frames from ourselves in hsr_rcv() */ + res = hsr_create_self_node(&hsr_priv->self_node_db, + hsr_dev->dev_addr, + hsr_priv->slave[1]->dev_addr); + if (res < 0) + goto fail; + + res = register_netdevice(hsr_dev); + if (res) + goto fail; + + register_hsr_master(hsr_priv); + + return 0; + +fail: + restore_slaves(hsr_dev); + return res; +} diff --git a/net/hsr/hsr_device.h b/net/hsr/hsr_device.h new file mode 100644 index 0000000000000000000000000000000000000000..2c7148e73914e9b6c5dd96a1ea488edd7402d451 --- /dev/null +++ b/net/hsr/hsr_device.h @@ -0,0 +1,29 @@ +/* Copyright 2011-2013 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + */ + +#ifndef __HSR_DEVICE_H +#define __HSR_DEVICE_H + +#include +#include "hsr_main.h" + +void hsr_dev_setup(struct net_device *dev); +int hsr_dev_finalize(struct net_device *hsr_dev, struct net_device *slave[2], + unsigned char multicast_spec); +void hsr_set_operstate(struct net_device *hsr_dev, struct net_device *slave1, + struct net_device *slave2); +void hsr_set_carrier(struct net_device *hsr_dev, struct net_device *slave1, + struct net_device *slave2); +void hsr_check_announce(struct net_device *hsr_dev, int old_operstate); +bool is_hsr_master(struct net_device *dev); +int hsr_get_max_mtu(struct hsr_priv *hsr_priv); + +#endif /* __HSR_DEVICE_H */ diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c new file mode 100644 index 0000000000000000000000000000000000000000..003f5bb3acd25f4dc696255c56ac1985eae58a62 --- /dev/null +++ b/net/hsr/hsr_framereg.c @@ -0,0 +1,503 @@ +/* Copyright 2011-2013 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + * + * The HSR spec says never to forward the same frame twice on the same + * interface. A frame is identified by its source MAC address and its HSR + * sequence number. This code keeps track of senders and their sequence numbers + * to allow filtering of duplicate frames, and to detect HSR ring errors. + */ + +#include +#include +#include +#include +#include "hsr_main.h" +#include "hsr_framereg.h" +#include "hsr_netlink.h" + + +struct node_entry { + struct list_head mac_list; + unsigned char MacAddressA[ETH_ALEN]; + unsigned char MacAddressB[ETH_ALEN]; + enum hsr_dev_idx AddrB_if; /* The local slave through which AddrB + * frames are received from this node + */ + unsigned long time_in[HSR_MAX_SLAVE]; + bool time_in_stale[HSR_MAX_SLAVE]; + u16 seq_out[HSR_MAX_DEV]; + struct rcu_head rcu_head; +}; + +/* TODO: use hash lists for mac addresses (linux/jhash.h)? */ + + + +/* Search for mac entry. Caller must hold rcu read lock. + */ +static struct node_entry *find_node_by_AddrA(struct list_head *node_db, + const unsigned char addr[ETH_ALEN]) +{ + struct node_entry *node; + + list_for_each_entry_rcu(node, node_db, mac_list) { + if (ether_addr_equal(node->MacAddressA, addr)) + return node; + } + + return NULL; +} + + +/* Search for mac entry. Caller must hold rcu read lock. + */ +static struct node_entry *find_node_by_AddrB(struct list_head *node_db, + const unsigned char addr[ETH_ALEN]) +{ + struct node_entry *node; + + list_for_each_entry_rcu(node, node_db, mac_list) { + if (ether_addr_equal(node->MacAddressB, addr)) + return node; + } + + return NULL; +} + + +/* Search for mac entry. Caller must hold rcu read lock. + */ +struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb) +{ + struct node_entry *node; + struct ethhdr *ethhdr; + + if (!skb_mac_header_was_set(skb)) + return NULL; + + ethhdr = (struct ethhdr *) skb_mac_header(skb); + + list_for_each_entry_rcu(node, node_db, mac_list) { + if (ether_addr_equal(node->MacAddressA, ethhdr->h_source)) + return node; + if (ether_addr_equal(node->MacAddressB, ethhdr->h_source)) + return node; + } + + return NULL; +} + + +/* Helper for device init; the self_node_db is used in hsr_rcv() to recognize + * frames from self that's been looped over the HSR ring. + */ +int hsr_create_self_node(struct list_head *self_node_db, + unsigned char addr_a[ETH_ALEN], + unsigned char addr_b[ETH_ALEN]) +{ + struct node_entry *node, *oldnode; + + node = kmalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + memcpy(node->MacAddressA, addr_a, ETH_ALEN); + memcpy(node->MacAddressB, addr_b, ETH_ALEN); + + rcu_read_lock(); + oldnode = list_first_or_null_rcu(self_node_db, + struct node_entry, mac_list); + if (oldnode) { + list_replace_rcu(&oldnode->mac_list, &node->mac_list); + rcu_read_unlock(); + synchronize_rcu(); + kfree(oldnode); + } else { + rcu_read_unlock(); + list_add_tail_rcu(&node->mac_list, self_node_db); + } + + return 0; +} + +static void node_entry_reclaim(struct rcu_head *rh) +{ + kfree(container_of(rh, struct node_entry, rcu_head)); +} + + +/* Add/merge node to the database of nodes. 'skb' must contain an HSR + * supervision frame. + * - If the supervision header's MacAddressA field is not yet in the database, + * this frame is from an hitherto unknown node - add it to the database. + * - If the sender's MAC address is not the same as its MacAddressA address, + * the node is using PICS_SUBS (address substitution). Record the sender's + * address as the node's MacAddressB. + * + * This function needs to work even if the sender node has changed one of its + * slaves' MAC addresses. In this case, there are four different cases described + * by (Addr-changed, received-from) pairs as follows. Note that changing the + * SlaveA address is equal to changing the node's own address: + * + * - (AddrB, SlaveB): The new AddrB will be recorded by PICS_SUBS code since + * node == NULL. + * - (AddrB, SlaveA): Will work as usual (the AddrB change won't be detected + * from this frame). + * + * - (AddrA, SlaveB): The old node will be found. We need to detect this and + * remove the node. + * - (AddrA, SlaveA): A new node will be registered (non-PICS_SUBS at first). + * The old one will be pruned after HSR_NODE_FORGET_TIME. + * + * We also need to detect if the sender's SlaveA and SlaveB cables have been + * swapped. + */ +struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv, + struct node_entry *node, + struct sk_buff *skb, + enum hsr_dev_idx dev_idx) +{ + struct hsr_sup_payload *hsr_sp; + struct hsr_ethhdr_sp *hsr_ethsup; + int i; + unsigned long now; + + hsr_ethsup = (struct hsr_ethhdr_sp *) skb_mac_header(skb); + hsr_sp = (struct hsr_sup_payload *) skb->data; + + if (node && !ether_addr_equal(node->MacAddressA, hsr_sp->MacAddressA)) { + /* Node has changed its AddrA, frame was received from SlaveB */ + list_del_rcu(&node->mac_list); + call_rcu(&node->rcu_head, node_entry_reclaim); + node = NULL; + } + + if (node && (dev_idx == node->AddrB_if) && + !ether_addr_equal(node->MacAddressB, hsr_ethsup->ethhdr.h_source)) { + /* Cables have been swapped */ + list_del_rcu(&node->mac_list); + call_rcu(&node->rcu_head, node_entry_reclaim); + node = NULL; + } + + if (node && (dev_idx != node->AddrB_if) && + (node->AddrB_if != HSR_DEV_NONE) && + !ether_addr_equal(node->MacAddressA, hsr_ethsup->ethhdr.h_source)) { + /* Cables have been swapped */ + list_del_rcu(&node->mac_list); + call_rcu(&node->rcu_head, node_entry_reclaim); + node = NULL; + } + + if (node) + return node; + + node = find_node_by_AddrA(&hsr_priv->node_db, hsr_sp->MacAddressA); + if (node) { + /* Node is known, but frame was received from an unknown + * address. Node is PICS_SUBS capable; merge its AddrB. + */ + memcpy(node->MacAddressB, hsr_ethsup->ethhdr.h_source, ETH_ALEN); + node->AddrB_if = dev_idx; + return node; + } + + node = kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + return NULL; + + memcpy(node->MacAddressA, hsr_sp->MacAddressA, ETH_ALEN); + memcpy(node->MacAddressB, hsr_ethsup->ethhdr.h_source, ETH_ALEN); + if (!ether_addr_equal(hsr_sp->MacAddressA, hsr_ethsup->ethhdr.h_source)) + node->AddrB_if = dev_idx; + else + node->AddrB_if = HSR_DEV_NONE; + + /* We are only interested in time diffs here, so use current jiffies + * as initialization. (0 could trigger an spurious ring error warning). + */ + now = jiffies; + for (i = 0; i < HSR_MAX_SLAVE; i++) + node->time_in[i] = now; + for (i = 0; i < HSR_MAX_DEV; i++) + node->seq_out[i] = ntohs(hsr_ethsup->hsr_sup.sequence_nr) - 1; + + list_add_tail_rcu(&node->mac_list, &hsr_priv->node_db); + + return node; +} + + +/* 'skb' is a frame meant for this host, that is to be passed to upper layers. + * + * If the frame was sent by a node's B interface, replace the sender + * address with that node's "official" address (MacAddressA) so that upper + * layers recognize where it came from. + */ +void hsr_addr_subst_source(struct hsr_priv *hsr_priv, struct sk_buff *skb) +{ + struct ethhdr *ethhdr; + struct node_entry *node; + + if (!skb_mac_header_was_set(skb)) { + WARN_ONCE(1, "%s: Mac header not set\n", __func__); + return; + } + ethhdr = (struct ethhdr *) skb_mac_header(skb); + + rcu_read_lock(); + node = find_node_by_AddrB(&hsr_priv->node_db, ethhdr->h_source); + if (node) + memcpy(ethhdr->h_source, node->MacAddressA, ETH_ALEN); + rcu_read_unlock(); +} + + +/* 'skb' is a frame meant for another host. + * 'hsr_dev_idx' is the HSR index of the outgoing device + * + * Substitute the target (dest) MAC address if necessary, so the it matches the + * recipient interface MAC address, regardless of whether that is the + * recipient's A or B interface. + * This is needed to keep the packets flowing through switches that learn on + * which "side" the different interfaces are. + */ +void hsr_addr_subst_dest(struct hsr_priv *hsr_priv, struct ethhdr *ethhdr, + enum hsr_dev_idx dev_idx) +{ + struct node_entry *node; + + rcu_read_lock(); + node = find_node_by_AddrA(&hsr_priv->node_db, ethhdr->h_dest); + if (node && (node->AddrB_if == dev_idx)) + memcpy(ethhdr->h_dest, node->MacAddressB, ETH_ALEN); + rcu_read_unlock(); +} + + +/* seq_nr_after(a, b) - return true if a is after (higher in sequence than) b, + * false otherwise. + */ +static bool seq_nr_after(u16 a, u16 b) +{ + /* Remove inconsistency where + * seq_nr_after(a, b) == seq_nr_before(a, b) */ + if ((int) b - a == 32768) + return false; + + return (((s16) (b - a)) < 0); +} +#define seq_nr_before(a, b) seq_nr_after((b), (a)) +#define seq_nr_after_or_eq(a, b) (!seq_nr_before((a), (b))) +#define seq_nr_before_or_eq(a, b) (!seq_nr_after((a), (b))) + + +void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx) +{ + if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) { + WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx); + return; + } + node->time_in[dev_idx] = jiffies; + node->time_in_stale[dev_idx] = false; +} + + +/* 'skb' is a HSR Ethernet frame (with a HSR tag inserted), with a valid + * ethhdr->h_source address and skb->mac_header set. + * + * Return: + * 1 if frame can be shown to have been sent recently on this interface, + * 0 otherwise, or + * negative error code on error + */ +int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx dev_idx, + struct sk_buff *skb) +{ + struct hsr_ethhdr *hsr_ethhdr; + u16 sequence_nr; + + if ((dev_idx < 0) || (dev_idx >= HSR_MAX_DEV)) { + WARN_ONCE(1, "%s: Invalid dev_idx (%d)\n", __func__, dev_idx); + return -EINVAL; + } + if (!skb_mac_header_was_set(skb)) { + WARN_ONCE(1, "%s: Mac header not set\n", __func__); + return -EINVAL; + } + hsr_ethhdr = (struct hsr_ethhdr *) skb_mac_header(skb); + + sequence_nr = ntohs(hsr_ethhdr->hsr_tag.sequence_nr); + if (seq_nr_before_or_eq(sequence_nr, node->seq_out[dev_idx])) + return 1; + + node->seq_out[dev_idx] = sequence_nr; + return 0; +} + + + +static bool is_late(struct node_entry *node, enum hsr_dev_idx dev_idx) +{ + enum hsr_dev_idx other; + + if (node->time_in_stale[dev_idx]) + return true; + + if (dev_idx == HSR_DEV_SLAVE_A) + other = HSR_DEV_SLAVE_B; + else + other = HSR_DEV_SLAVE_A; + + if (node->time_in_stale[other]) + return false; + + if (time_after(node->time_in[other], node->time_in[dev_idx] + + msecs_to_jiffies(MAX_SLAVE_DIFF))) + return true; + + return false; +} + + +/* Remove stale sequence_nr records. Called by timer every + * HSR_LIFE_CHECK_INTERVAL (two seconds or so). + */ +void hsr_prune_nodes(struct hsr_priv *hsr_priv) +{ + struct node_entry *node; + unsigned long timestamp; + unsigned long time_a, time_b; + + rcu_read_lock(); + list_for_each_entry_rcu(node, &hsr_priv->node_db, mac_list) { + /* Shorthand */ + time_a = node->time_in[HSR_DEV_SLAVE_A]; + time_b = node->time_in[HSR_DEV_SLAVE_B]; + + /* Check for timestamps old enough to risk wrap-around */ + if (time_after(jiffies, time_a + MAX_JIFFY_OFFSET/2)) + node->time_in_stale[HSR_DEV_SLAVE_A] = true; + if (time_after(jiffies, time_b + MAX_JIFFY_OFFSET/2)) + node->time_in_stale[HSR_DEV_SLAVE_B] = true; + + /* Get age of newest frame from node. + * At least one time_in is OK here; nodes get pruned long + * before both time_ins can get stale + */ + timestamp = time_a; + if (node->time_in_stale[HSR_DEV_SLAVE_A] || + (!node->time_in_stale[HSR_DEV_SLAVE_B] && + time_after(time_b, time_a))) + timestamp = time_b; + + /* Warn of ring error only as long as we get frames at all */ + if (time_is_after_jiffies(timestamp + + msecs_to_jiffies(1.5*MAX_SLAVE_DIFF))) { + + if (is_late(node, HSR_DEV_SLAVE_A)) + hsr_nl_ringerror(hsr_priv, node->MacAddressA, + HSR_DEV_SLAVE_A); + else if (is_late(node, HSR_DEV_SLAVE_B)) + hsr_nl_ringerror(hsr_priv, node->MacAddressA, + HSR_DEV_SLAVE_B); + } + + /* Prune old entries */ + if (time_is_before_jiffies(timestamp + + msecs_to_jiffies(HSR_NODE_FORGET_TIME))) { + hsr_nl_nodedown(hsr_priv, node->MacAddressA); + list_del_rcu(&node->mac_list); + /* Note that we need to free this entry later: */ + call_rcu(&node->rcu_head, node_entry_reclaim); + } + } + rcu_read_unlock(); +} + + +void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos, + unsigned char addr[ETH_ALEN]) +{ + struct node_entry *node; + + if (!_pos) { + node = list_first_or_null_rcu(&hsr_priv->node_db, + struct node_entry, mac_list); + if (node) + memcpy(addr, node->MacAddressA, ETH_ALEN); + return node; + } + + node = _pos; + list_for_each_entry_continue_rcu(node, &hsr_priv->node_db, mac_list) { + memcpy(addr, node->MacAddressA, ETH_ALEN); + return node; + } + + return NULL; +} + + +int hsr_get_node_data(struct hsr_priv *hsr_priv, + const unsigned char *addr, + unsigned char addr_b[ETH_ALEN], + unsigned int *addr_b_ifindex, + int *if1_age, + u16 *if1_seq, + int *if2_age, + u16 *if2_seq) +{ + struct node_entry *node; + unsigned long tdiff; + + + rcu_read_lock(); + node = find_node_by_AddrA(&hsr_priv->node_db, addr); + if (!node) { + rcu_read_unlock(); + return -ENOENT; /* No such entry */ + } + + memcpy(addr_b, node->MacAddressB, ETH_ALEN); + + tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_A]; + if (node->time_in_stale[HSR_DEV_SLAVE_A]) + *if1_age = INT_MAX; +#if HZ <= MSEC_PER_SEC + else if (tdiff > msecs_to_jiffies(INT_MAX)) + *if1_age = INT_MAX; +#endif + else + *if1_age = jiffies_to_msecs(tdiff); + + tdiff = jiffies - node->time_in[HSR_DEV_SLAVE_B]; + if (node->time_in_stale[HSR_DEV_SLAVE_B]) + *if2_age = INT_MAX; +#if HZ <= MSEC_PER_SEC + else if (tdiff > msecs_to_jiffies(INT_MAX)) + *if2_age = INT_MAX; +#endif + else + *if2_age = jiffies_to_msecs(tdiff); + + /* Present sequence numbers as if they were incoming on interface */ + *if1_seq = node->seq_out[HSR_DEV_SLAVE_B]; + *if2_seq = node->seq_out[HSR_DEV_SLAVE_A]; + + if ((node->AddrB_if != HSR_DEV_NONE) && hsr_priv->slave[node->AddrB_if]) + *addr_b_ifindex = hsr_priv->slave[node->AddrB_if]->ifindex; + else + *addr_b_ifindex = -1; + + rcu_read_unlock(); + + return 0; +} diff --git a/net/hsr/hsr_framereg.h b/net/hsr/hsr_framereg.h new file mode 100644 index 0000000000000000000000000000000000000000..e6c4022030ad17f74d6007d44d9fc9231ea01fac --- /dev/null +++ b/net/hsr/hsr_framereg.h @@ -0,0 +1,53 @@ +/* Copyright 2011-2013 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + */ + +#ifndef _HSR_FRAMEREG_H +#define _HSR_FRAMEREG_H + +#include "hsr_main.h" + +struct node_entry; + +struct node_entry *hsr_find_node(struct list_head *node_db, struct sk_buff *skb); + +struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv, + struct node_entry *node, + struct sk_buff *skb, + enum hsr_dev_idx dev_idx); + +void hsr_addr_subst_source(struct hsr_priv *hsr_priv, struct sk_buff *skb); +void hsr_addr_subst_dest(struct hsr_priv *hsr_priv, struct ethhdr *ethhdr, + enum hsr_dev_idx dev_idx); + +void hsr_register_frame_in(struct node_entry *node, enum hsr_dev_idx dev_idx); + +int hsr_register_frame_out(struct node_entry *node, enum hsr_dev_idx dev_idx, + struct sk_buff *skb); + +void hsr_prune_nodes(struct hsr_priv *hsr_priv); + +int hsr_create_self_node(struct list_head *self_node_db, + unsigned char addr_a[ETH_ALEN], + unsigned char addr_b[ETH_ALEN]); + +void *hsr_get_next_node(struct hsr_priv *hsr_priv, void *_pos, + unsigned char addr[ETH_ALEN]); + +int hsr_get_node_data(struct hsr_priv *hsr_priv, + const unsigned char *addr, + unsigned char addr_b[ETH_ALEN], + unsigned int *addr_b_ifindex, + int *if1_age, + u16 *if1_seq, + int *if2_age, + u16 *if2_seq); + +#endif /* _HSR_FRAMEREG_H */ diff --git a/net/hsr/hsr_main.c b/net/hsr/hsr_main.c new file mode 100644 index 0000000000000000000000000000000000000000..af68dd83a4e3df0ed12b18d5361d436fa3edd9f6 --- /dev/null +++ b/net/hsr/hsr_main.c @@ -0,0 +1,469 @@ +/* Copyright 2011-2013 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + * + * In addition to routines for registering and unregistering HSR support, this + * file also contains the receive routine that handles all incoming frames with + * Ethertype (protocol) ETH_P_PRP (HSRv0), and network device event handling. + */ + +#include +#include +#include +#include +#include "hsr_main.h" +#include "hsr_device.h" +#include "hsr_netlink.h" +#include "hsr_framereg.h" + + +/* List of all registered virtual HSR devices */ +static LIST_HEAD(hsr_list); + +void register_hsr_master(struct hsr_priv *hsr_priv) +{ + list_add_tail_rcu(&hsr_priv->hsr_list, &hsr_list); +} + +void unregister_hsr_master(struct hsr_priv *hsr_priv) +{ + struct hsr_priv *hsr_priv_it; + + list_for_each_entry(hsr_priv_it, &hsr_list, hsr_list) + if (hsr_priv_it == hsr_priv) { + list_del_rcu(&hsr_priv_it->hsr_list); + return; + } +} + +bool is_hsr_slave(struct net_device *dev) +{ + struct hsr_priv *hsr_priv_it; + + list_for_each_entry_rcu(hsr_priv_it, &hsr_list, hsr_list) { + if (dev == hsr_priv_it->slave[0]) + return true; + if (dev == hsr_priv_it->slave[1]) + return true; + } + + return false; +} + + +/* If dev is a HSR slave device, return the virtual master device. Return NULL + * otherwise. + */ +static struct hsr_priv *get_hsr_master(struct net_device *dev) +{ + struct hsr_priv *hsr_priv; + + rcu_read_lock(); + list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list) + if ((dev == hsr_priv->slave[0]) || + (dev == hsr_priv->slave[1])) { + rcu_read_unlock(); + return hsr_priv; + } + + rcu_read_unlock(); + return NULL; +} + + +/* If dev is a HSR slave device, return the other slave device. Return NULL + * otherwise. + */ +static struct net_device *get_other_slave(struct hsr_priv *hsr_priv, + struct net_device *dev) +{ + if (dev == hsr_priv->slave[0]) + return hsr_priv->slave[1]; + if (dev == hsr_priv->slave[1]) + return hsr_priv->slave[0]; + + return NULL; +} + + +static int hsr_netdev_notify(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct net_device *slave, *other_slave; + struct hsr_priv *hsr_priv; + int old_operstate; + int mtu_max; + int res; + struct net_device *dev; + + dev = netdev_notifier_info_to_dev(ptr); + + hsr_priv = get_hsr_master(dev); + if (hsr_priv) { + /* dev is a slave device */ + slave = dev; + other_slave = get_other_slave(hsr_priv, slave); + } else { + if (!is_hsr_master(dev)) + return NOTIFY_DONE; + hsr_priv = netdev_priv(dev); + slave = hsr_priv->slave[0]; + other_slave = hsr_priv->slave[1]; + } + + switch (event) { + case NETDEV_UP: /* Administrative state DOWN */ + case NETDEV_DOWN: /* Administrative state UP */ + case NETDEV_CHANGE: /* Link (carrier) state changes */ + old_operstate = hsr_priv->dev->operstate; + hsr_set_carrier(hsr_priv->dev, slave, other_slave); + /* netif_stacked_transfer_operstate() cannot be used here since + * it doesn't set IF_OPER_LOWERLAYERDOWN (?) + */ + hsr_set_operstate(hsr_priv->dev, slave, other_slave); + hsr_check_announce(hsr_priv->dev, old_operstate); + break; + case NETDEV_CHANGEADDR: + + /* This should not happen since there's no ndo_set_mac_address() + * for HSR devices - i.e. not supported. + */ + if (dev == hsr_priv->dev) + break; + + if (dev == hsr_priv->slave[0]) + memcpy(hsr_priv->dev->dev_addr, + hsr_priv->slave[0]->dev_addr, ETH_ALEN); + + /* Make sure we recognize frames from ourselves in hsr_rcv() */ + res = hsr_create_self_node(&hsr_priv->self_node_db, + hsr_priv->dev->dev_addr, + hsr_priv->slave[1] ? + hsr_priv->slave[1]->dev_addr : + hsr_priv->dev->dev_addr); + if (res) + netdev_warn(hsr_priv->dev, + "Could not update HSR node address.\n"); + + if (dev == hsr_priv->slave[0]) + call_netdevice_notifiers(NETDEV_CHANGEADDR, hsr_priv->dev); + break; + case NETDEV_CHANGEMTU: + if (dev == hsr_priv->dev) + break; /* Handled in ndo_change_mtu() */ + mtu_max = hsr_get_max_mtu(hsr_priv); + if (hsr_priv->dev->mtu > mtu_max) + dev_set_mtu(hsr_priv->dev, mtu_max); + break; + case NETDEV_UNREGISTER: + if (dev == hsr_priv->slave[0]) + hsr_priv->slave[0] = NULL; + if (dev == hsr_priv->slave[1]) + hsr_priv->slave[1] = NULL; + + /* There should really be a way to set a new slave device... */ + + break; + case NETDEV_PRE_TYPE_CHANGE: + /* HSR works only on Ethernet devices. Refuse slave to change + * its type. + */ + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} + + +static struct timer_list prune_timer; + +static void prune_nodes_all(unsigned long data) +{ + struct hsr_priv *hsr_priv; + + rcu_read_lock(); + list_for_each_entry_rcu(hsr_priv, &hsr_list, hsr_list) + hsr_prune_nodes(hsr_priv); + rcu_read_unlock(); + + prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); + add_timer(&prune_timer); +} + + +static struct sk_buff *hsr_pull_tag(struct sk_buff *skb) +{ + struct hsr_tag *hsr_tag; + struct sk_buff *skb2; + + skb2 = skb_share_check(skb, GFP_ATOMIC); + if (unlikely(!skb2)) + goto err_free; + skb = skb2; + + if (unlikely(!pskb_may_pull(skb, HSR_TAGLEN))) + goto err_free; + + hsr_tag = (struct hsr_tag *) skb->data; + skb->protocol = hsr_tag->encap_proto; + skb_pull(skb, HSR_TAGLEN); + + return skb; + +err_free: + kfree_skb(skb); + return NULL; +} + + +/* The uses I can see for these HSR supervision frames are: + * 1) Use the frames that are sent after node initialization ("HSR_TLV.Type = + * 22") to reset any sequence_nr counters belonging to that node. Useful if + * the other node's counter has been reset for some reason. + * -- + * Or not - resetting the counter and bridging the frame would create a + * loop, unfortunately. + * + * 2) Use the LifeCheck frames to detect ring breaks. I.e. if no LifeCheck + * frame is received from a particular node, we know something is wrong. + * We just register these (as with normal frames) and throw them away. + * + * 3) Allow different MAC addresses for the two slave interfaces, using the + * MacAddressA field. + */ +static bool is_supervision_frame(struct hsr_priv *hsr_priv, struct sk_buff *skb) +{ + struct hsr_sup_tag *hsr_stag; + + if (!ether_addr_equal(eth_hdr(skb)->h_dest, + hsr_priv->sup_multicast_addr)) + return false; + + hsr_stag = (struct hsr_sup_tag *) skb->data; + if (get_hsr_stag_path(hsr_stag) != 0x0f) + return false; + if ((hsr_stag->HSR_TLV_Type != HSR_TLV_ANNOUNCE) && + (hsr_stag->HSR_TLV_Type != HSR_TLV_LIFE_CHECK)) + return false; + if (hsr_stag->HSR_TLV_Length != 12) + return false; + + return true; +} + + +/* Implementation somewhat according to IEC-62439-3, p. 43 + */ +static int hsr_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, struct net_device *orig_dev) +{ + struct hsr_priv *hsr_priv; + struct net_device *other_slave; + struct node_entry *node; + bool deliver_to_self; + struct sk_buff *skb_deliver; + enum hsr_dev_idx dev_in_idx, dev_other_idx; + bool dup_out; + int ret; + + hsr_priv = get_hsr_master(dev); + + if (!hsr_priv) { + /* Non-HSR-slave device 'dev' is connected to a HSR network */ + kfree_skb(skb); + dev->stats.rx_errors++; + return NET_RX_SUCCESS; + } + + if (dev == hsr_priv->slave[0]) { + dev_in_idx = HSR_DEV_SLAVE_A; + dev_other_idx = HSR_DEV_SLAVE_B; + } else { + dev_in_idx = HSR_DEV_SLAVE_B; + dev_other_idx = HSR_DEV_SLAVE_A; + } + + node = hsr_find_node(&hsr_priv->self_node_db, skb); + if (node) { + /* Always kill frames sent by ourselves */ + kfree_skb(skb); + return NET_RX_SUCCESS; + } + + /* Is this frame a candidate for local reception? */ + deliver_to_self = false; + if ((skb->pkt_type == PACKET_HOST) || + (skb->pkt_type == PACKET_MULTICAST) || + (skb->pkt_type == PACKET_BROADCAST)) + deliver_to_self = true; + else if (ether_addr_equal(eth_hdr(skb)->h_dest, + hsr_priv->dev->dev_addr)) { + skb->pkt_type = PACKET_HOST; + deliver_to_self = true; + } + + + rcu_read_lock(); /* node_db */ + node = hsr_find_node(&hsr_priv->node_db, skb); + + if (is_supervision_frame(hsr_priv, skb)) { + skb_pull(skb, sizeof(struct hsr_sup_tag)); + node = hsr_merge_node(hsr_priv, node, skb, dev_in_idx); + if (!node) { + rcu_read_unlock(); /* node_db */ + kfree_skb(skb); + hsr_priv->dev->stats.rx_dropped++; + return NET_RX_DROP; + } + skb_push(skb, sizeof(struct hsr_sup_tag)); + deliver_to_self = false; + } + + if (!node) { + /* Source node unknown; this might be a HSR frame from + * another net (different multicast address). Ignore it. + */ + rcu_read_unlock(); /* node_db */ + kfree_skb(skb); + return NET_RX_SUCCESS; + } + + /* Register ALL incoming frames as outgoing through the other interface. + * This allows us to register frames as incoming only if they are valid + * for the receiving interface, without using a specific counter for + * incoming frames. + */ + dup_out = hsr_register_frame_out(node, dev_other_idx, skb); + if (!dup_out) + hsr_register_frame_in(node, dev_in_idx); + + /* Forward this frame? */ + if (!dup_out && (skb->pkt_type != PACKET_HOST)) + other_slave = get_other_slave(hsr_priv, dev); + else + other_slave = NULL; + + if (hsr_register_frame_out(node, HSR_DEV_MASTER, skb)) + deliver_to_self = false; + + rcu_read_unlock(); /* node_db */ + + if (!deliver_to_self && !other_slave) { + kfree_skb(skb); + /* Circulated frame; silently remove it. */ + return NET_RX_SUCCESS; + } + + skb_deliver = skb; + if (deliver_to_self && other_slave) { + /* skb_clone() is not enough since we will strip the hsr tag + * and do address substitution below + */ + skb_deliver = pskb_copy(skb, GFP_ATOMIC); + if (!skb_deliver) { + deliver_to_self = false; + hsr_priv->dev->stats.rx_dropped++; + } + } + + if (deliver_to_self) { + bool multicast_frame; + + skb_deliver = hsr_pull_tag(skb_deliver); + if (!skb_deliver) { + hsr_priv->dev->stats.rx_dropped++; + goto forward; + } +#if !defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) + /* Move everything in the header that is after the HSR tag, + * to work around alignment problems caused by the 6-byte HSR + * tag. In practice, this removes/overwrites the HSR tag in + * the header and restores a "standard" packet. + */ + memmove(skb_deliver->data - HSR_TAGLEN, skb_deliver->data, + skb_headlen(skb_deliver)); + + /* Adjust skb members so they correspond with the move above. + * This cannot possibly underflow skb->data since hsr_pull_tag() + * above succeeded. + * At this point in the protocol stack, the transport and + * network headers have not been set yet, and we haven't touched + * the mac header nor the head. So we only need to adjust data + * and tail: + */ + skb_deliver->data -= HSR_TAGLEN; + skb_deliver->tail -= HSR_TAGLEN; +#endif + skb_deliver->dev = hsr_priv->dev; + hsr_addr_subst_source(hsr_priv, skb_deliver); + multicast_frame = (skb_deliver->pkt_type == PACKET_MULTICAST); + ret = netif_rx(skb_deliver); + if (ret == NET_RX_DROP) { + hsr_priv->dev->stats.rx_dropped++; + } else { + hsr_priv->dev->stats.rx_packets++; + hsr_priv->dev->stats.rx_bytes += skb->len; + if (multicast_frame) + hsr_priv->dev->stats.multicast++; + } + } + +forward: + if (other_slave) { + skb_push(skb, ETH_HLEN); + skb->dev = other_slave; + dev_queue_xmit(skb); + } + + return NET_RX_SUCCESS; +} + + +static struct packet_type hsr_pt __read_mostly = { + .type = htons(ETH_P_PRP), + .func = hsr_rcv, +}; + +static struct notifier_block hsr_nb = { + .notifier_call = hsr_netdev_notify, /* Slave event notifications */ +}; + + +static int __init hsr_init(void) +{ + int res; + + BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_TAGLEN); + + dev_add_pack(&hsr_pt); + + init_timer(&prune_timer); + prune_timer.function = prune_nodes_all; + prune_timer.data = 0; + prune_timer.expires = jiffies + msecs_to_jiffies(PRUNE_PERIOD); + add_timer(&prune_timer); + + register_netdevice_notifier(&hsr_nb); + + res = hsr_netlink_init(); + + return res; +} + +static void __exit hsr_exit(void) +{ + unregister_netdevice_notifier(&hsr_nb); + del_timer(&prune_timer); + hsr_netlink_exit(); + dev_remove_pack(&hsr_pt); +} + +module_init(hsr_init); +module_exit(hsr_exit); +MODULE_LICENSE("GPL"); diff --git a/net/hsr/hsr_main.h b/net/hsr/hsr_main.h new file mode 100644 index 0000000000000000000000000000000000000000..56fe060c0ab1872330adda30daace83c429313ff --- /dev/null +++ b/net/hsr/hsr_main.h @@ -0,0 +1,166 @@ +/* Copyright 2011-2013 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + */ + +#ifndef _HSR_PRIVATE_H +#define _HSR_PRIVATE_H + +#include +#include + + +/* Time constants as specified in the HSR specification (IEC-62439-3 2010) + * Table 8. + * All values in milliseconds. + */ +#define HSR_LIFE_CHECK_INTERVAL 2000 /* ms */ +#define HSR_NODE_FORGET_TIME 60000 /* ms */ +#define HSR_ANNOUNCE_INTERVAL 100 /* ms */ + + +/* By how much may slave1 and slave2 timestamps of latest received frame from + * each node differ before we notify of communication problem? + */ +#define MAX_SLAVE_DIFF 3000 /* ms */ + + +/* How often shall we check for broken ring and remove node entries older than + * HSR_NODE_FORGET_TIME? + */ +#define PRUNE_PERIOD 3000 /* ms */ + + +#define HSR_TLV_ANNOUNCE 22 +#define HSR_TLV_LIFE_CHECK 23 + + +/* HSR Tag. + * As defined in IEC-62439-3:2010, the HSR tag is really { ethertype = 0x88FB, + * path, LSDU_size, sequence Nr }. But we let eth_header() create { h_dest, + * h_source, h_proto = 0x88FB }, and add { path, LSDU_size, sequence Nr, + * encapsulated protocol } instead. + */ +#define HSR_TAGLEN 6 + +/* Field names below as defined in the IEC:2010 standard for HSR. */ +struct hsr_tag { + __be16 path_and_LSDU_size; + __be16 sequence_nr; + __be16 encap_proto; +} __packed; + + +/* The helper functions below assumes that 'path' occupies the 4 most + * significant bits of the 16-bit field shared by 'path' and 'LSDU_size' (or + * equivalently, the 4 most significant bits of HSR tag byte 14). + * + * This is unclear in the IEC specification; its definition of MAC addresses + * indicates the spec is written with the least significant bit first (to the + * left). This, however, would mean that the LSDU field would be split in two + * with the path field in-between, which seems strange. I'm guessing the MAC + * address definition is in error. + */ +static inline u16 get_hsr_tag_path(struct hsr_tag *ht) +{ + return ntohs(ht->path_and_LSDU_size) >> 12; +} + +static inline u16 get_hsr_tag_LSDU_size(struct hsr_tag *ht) +{ + return ntohs(ht->path_and_LSDU_size) & 0x0FFF; +} + +static inline void set_hsr_tag_path(struct hsr_tag *ht, u16 path) +{ + ht->path_and_LSDU_size = htons( + (ntohs(ht->path_and_LSDU_size) & 0x0FFF) | (path << 12)); +} + +static inline void set_hsr_tag_LSDU_size(struct hsr_tag *ht, u16 LSDU_size) +{ + ht->path_and_LSDU_size = htons( + (ntohs(ht->path_and_LSDU_size) & 0xF000) | + (LSDU_size & 0x0FFF)); +} + +struct hsr_ethhdr { + struct ethhdr ethhdr; + struct hsr_tag hsr_tag; +} __packed; + + +/* HSR Supervision Frame data types. + * Field names as defined in the IEC:2010 standard for HSR. + */ +struct hsr_sup_tag { + __be16 path_and_HSR_Ver; + __be16 sequence_nr; + __u8 HSR_TLV_Type; + __u8 HSR_TLV_Length; +} __packed; + +struct hsr_sup_payload { + unsigned char MacAddressA[ETH_ALEN]; +} __packed; + +static inline u16 get_hsr_stag_path(struct hsr_sup_tag *hst) +{ + return get_hsr_tag_path((struct hsr_tag *) hst); +} + +static inline u16 get_hsr_stag_HSR_ver(struct hsr_sup_tag *hst) +{ + return get_hsr_tag_LSDU_size((struct hsr_tag *) hst); +} + +static inline void set_hsr_stag_path(struct hsr_sup_tag *hst, u16 path) +{ + set_hsr_tag_path((struct hsr_tag *) hst, path); +} + +static inline void set_hsr_stag_HSR_Ver(struct hsr_sup_tag *hst, u16 HSR_Ver) +{ + set_hsr_tag_LSDU_size((struct hsr_tag *) hst, HSR_Ver); +} + +struct hsr_ethhdr_sp { + struct ethhdr ethhdr; + struct hsr_sup_tag hsr_sup; +} __packed; + + +enum hsr_dev_idx { + HSR_DEV_NONE = -1, + HSR_DEV_SLAVE_A = 0, + HSR_DEV_SLAVE_B, + HSR_DEV_MASTER, +}; +#define HSR_MAX_SLAVE (HSR_DEV_SLAVE_B + 1) +#define HSR_MAX_DEV (HSR_DEV_MASTER + 1) + +struct hsr_priv { + struct list_head hsr_list; /* List of hsr devices */ + struct rcu_head rcu_head; + struct net_device *dev; + struct net_device *slave[HSR_MAX_SLAVE]; + struct list_head node_db; /* Other HSR nodes */ + struct list_head self_node_db; /* MACs of slaves */ + struct timer_list announce_timer; /* Supervision frame dispatch */ + int announce_count; + u16 sequence_nr; + spinlock_t seqnr_lock; /* locking for sequence_nr */ + unsigned char sup_multicast_addr[ETH_ALEN]; +}; + +void register_hsr_master(struct hsr_priv *hsr_priv); +void unregister_hsr_master(struct hsr_priv *hsr_priv); +bool is_hsr_slave(struct net_device *dev); + +#endif /* _HSR_PRIVATE_H */ diff --git a/net/hsr/hsr_netlink.c b/net/hsr/hsr_netlink.c new file mode 100644 index 0000000000000000000000000000000000000000..4e66bf61f585e58c2f975799833d540b5e5c9813 --- /dev/null +++ b/net/hsr/hsr_netlink.c @@ -0,0 +1,457 @@ +/* Copyright 2011-2013 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + * + * Routines for handling Netlink messages for HSR. + */ + +#include "hsr_netlink.h" +#include +#include +#include +#include "hsr_main.h" +#include "hsr_device.h" +#include "hsr_framereg.h" + +static const struct nla_policy hsr_policy[IFLA_HSR_MAX + 1] = { + [IFLA_HSR_SLAVE1] = { .type = NLA_U32 }, + [IFLA_HSR_SLAVE2] = { .type = NLA_U32 }, + [IFLA_HSR_MULTICAST_SPEC] = { .type = NLA_U8 }, +}; + + +/* Here, it seems a netdevice has already been allocated for us, and the + * hsr_dev_setup routine has been executed. Nice! + */ +static int hsr_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct net_device *link[2]; + unsigned char multicast_spec; + + if (!data[IFLA_HSR_SLAVE1]) { + netdev_info(dev, "IFLA_HSR_SLAVE1 missing!\n"); + return -EINVAL; + } + link[0] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE1])); + if (!data[IFLA_HSR_SLAVE2]) { + netdev_info(dev, "IFLA_HSR_SLAVE2 missing!\n"); + return -EINVAL; + } + link[1] = __dev_get_by_index(src_net, nla_get_u32(data[IFLA_HSR_SLAVE2])); + + if (!link[0] || !link[1]) + return -ENODEV; + if (link[0] == link[1]) + return -EINVAL; + + if (!data[IFLA_HSR_MULTICAST_SPEC]) + multicast_spec = 0; + else + multicast_spec = nla_get_u8(data[IFLA_HSR_MULTICAST_SPEC]); + + return hsr_dev_finalize(dev, link, multicast_spec); +} + +static struct rtnl_link_ops hsr_link_ops __read_mostly = { + .kind = "hsr", + .maxtype = IFLA_HSR_MAX, + .policy = hsr_policy, + .priv_size = sizeof(struct hsr_priv), + .setup = hsr_dev_setup, + .newlink = hsr_newlink, +}; + + + +/* attribute policy */ +/* NLA_BINARY missing in libnl; use NLA_UNSPEC in userspace instead. */ +static const struct nla_policy hsr_genl_policy[HSR_A_MAX + 1] = { + [HSR_A_NODE_ADDR] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [HSR_A_NODE_ADDR_B] = { .type = NLA_BINARY, .len = ETH_ALEN }, + [HSR_A_IFINDEX] = { .type = NLA_U32 }, + [HSR_A_IF1_AGE] = { .type = NLA_U32 }, + [HSR_A_IF2_AGE] = { .type = NLA_U32 }, + [HSR_A_IF1_SEQ] = { .type = NLA_U16 }, + [HSR_A_IF2_SEQ] = { .type = NLA_U16 }, +}; + +static struct genl_family hsr_genl_family = { + .id = GENL_ID_GENERATE, + .hdrsize = 0, + .name = "HSR", + .version = 1, + .maxattr = HSR_A_MAX, +}; + +static struct genl_multicast_group hsr_network_genl_mcgrp = { + .name = "hsr-network", +}; + + + +/* This is called if for some node with MAC address addr, we only get frames + * over one of the slave interfaces. This would indicate an open network ring + * (i.e. a link has failed somewhere). + */ +void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN], + enum hsr_dev_idx dev_idx) +{ + struct sk_buff *skb; + void *msg_head; + int res; + int ifindex; + + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + if (!skb) + goto fail; + + msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_RING_ERROR); + if (!msg_head) + goto nla_put_failure; + + res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr); + if (res < 0) + goto nla_put_failure; + + if (hsr_priv->slave[dev_idx]) + ifindex = hsr_priv->slave[dev_idx]->ifindex; + else + ifindex = -1; + res = nla_put_u32(skb, HSR_A_IFINDEX, ifindex); + if (res < 0) + goto nla_put_failure; + + genlmsg_end(skb, msg_head); + genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC); + + return; + +nla_put_failure: + kfree_skb(skb); + +fail: + netdev_warn(hsr_priv->dev, "Could not send HSR ring error message\n"); +} + +/* This is called when we haven't heard from the node with MAC address addr for + * some time (just before the node is removed from the node table/list). + */ +void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN]) +{ + struct sk_buff *skb; + void *msg_head; + int res; + + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC); + if (!skb) + goto fail; + + msg_head = genlmsg_put(skb, 0, 0, &hsr_genl_family, 0, HSR_C_NODE_DOWN); + if (!msg_head) + goto nla_put_failure; + + + res = nla_put(skb, HSR_A_NODE_ADDR, ETH_ALEN, addr); + if (res < 0) + goto nla_put_failure; + + genlmsg_end(skb, msg_head); + genlmsg_multicast(skb, 0, hsr_network_genl_mcgrp.id, GFP_ATOMIC); + + return; + +nla_put_failure: + kfree_skb(skb); + +fail: + netdev_warn(hsr_priv->dev, "Could not send HSR node down\n"); +} + + +/* HSR_C_GET_NODE_STATUS lets userspace query the internal HSR node table + * about the status of a specific node in the network, defined by its MAC + * address. + * + * Input: hsr ifindex, node mac address + * Output: hsr ifindex, node mac address (copied from request), + * age of latest frame from node over slave 1, slave 2 [ms] + */ +static int hsr_get_node_status(struct sk_buff *skb_in, struct genl_info *info) +{ + /* For receiving */ + struct nlattr *na; + struct net_device *hsr_dev; + + /* For sending */ + struct sk_buff *skb_out; + void *msg_head; + struct hsr_priv *hsr_priv; + unsigned char hsr_node_addr_b[ETH_ALEN]; + int hsr_node_if1_age; + u16 hsr_node_if1_seq; + int hsr_node_if2_age; + u16 hsr_node_if2_seq; + int addr_b_ifindex; + int res; + + if (!info) + goto invalid; + + na = info->attrs[HSR_A_IFINDEX]; + if (!na) + goto invalid; + na = info->attrs[HSR_A_NODE_ADDR]; + if (!na) + goto invalid; + + hsr_dev = __dev_get_by_index(genl_info_net(info), + nla_get_u32(info->attrs[HSR_A_IFINDEX])); + if (!hsr_dev) + goto invalid; + if (!is_hsr_master(hsr_dev)) + goto invalid; + + + /* Send reply */ + + skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb_out) { + res = -ENOMEM; + goto fail; + } + + msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid, + info->snd_seq, &hsr_genl_family, 0, + HSR_C_SET_NODE_STATUS); + if (!msg_head) { + res = -ENOMEM; + goto nla_put_failure; + } + + res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex); + if (res < 0) + goto nla_put_failure; + + hsr_priv = netdev_priv(hsr_dev); + res = hsr_get_node_data(hsr_priv, + (unsigned char *) nla_data(info->attrs[HSR_A_NODE_ADDR]), + hsr_node_addr_b, + &addr_b_ifindex, + &hsr_node_if1_age, + &hsr_node_if1_seq, + &hsr_node_if2_age, + &hsr_node_if2_seq); + if (res < 0) + goto fail; + + res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, + nla_data(info->attrs[HSR_A_NODE_ADDR])); + if (res < 0) + goto nla_put_failure; + + if (addr_b_ifindex > -1) { + res = nla_put(skb_out, HSR_A_NODE_ADDR_B, ETH_ALEN, + hsr_node_addr_b); + if (res < 0) + goto nla_put_failure; + + res = nla_put_u32(skb_out, HSR_A_ADDR_B_IFINDEX, addr_b_ifindex); + if (res < 0) + goto nla_put_failure; + } + + res = nla_put_u32(skb_out, HSR_A_IF1_AGE, hsr_node_if1_age); + if (res < 0) + goto nla_put_failure; + res = nla_put_u16(skb_out, HSR_A_IF1_SEQ, hsr_node_if1_seq); + if (res < 0) + goto nla_put_failure; + if (hsr_priv->slave[0]) + res = nla_put_u32(skb_out, HSR_A_IF1_IFINDEX, + hsr_priv->slave[0]->ifindex); + if (res < 0) + goto nla_put_failure; + + res = nla_put_u32(skb_out, HSR_A_IF2_AGE, hsr_node_if2_age); + if (res < 0) + goto nla_put_failure; + res = nla_put_u16(skb_out, HSR_A_IF2_SEQ, hsr_node_if2_seq); + if (res < 0) + goto nla_put_failure; + if (hsr_priv->slave[1]) + res = nla_put_u32(skb_out, HSR_A_IF2_IFINDEX, + hsr_priv->slave[1]->ifindex); + + genlmsg_end(skb_out, msg_head); + genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); + + return 0; + +invalid: + netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL); + return 0; + +nla_put_failure: + kfree_skb(skb_out); + /* Fall through */ + +fail: + return res; +} + +static struct genl_ops hsr_ops_get_node_status = { + .cmd = HSR_C_GET_NODE_STATUS, + .flags = 0, + .policy = hsr_genl_policy, + .doit = hsr_get_node_status, + .dumpit = NULL, +}; + + +/* Get a list of MacAddressA of all nodes known to this node (other than self). + */ +static int hsr_get_node_list(struct sk_buff *skb_in, struct genl_info *info) +{ + /* For receiving */ + struct nlattr *na; + struct net_device *hsr_dev; + + /* For sending */ + struct sk_buff *skb_out; + void *msg_head; + struct hsr_priv *hsr_priv; + void *pos; + unsigned char addr[ETH_ALEN]; + int res; + + if (!info) + goto invalid; + + na = info->attrs[HSR_A_IFINDEX]; + if (!na) + goto invalid; + + hsr_dev = __dev_get_by_index(genl_info_net(info), + nla_get_u32(info->attrs[HSR_A_IFINDEX])); + if (!hsr_dev) + goto invalid; + if (!is_hsr_master(hsr_dev)) + goto invalid; + + + /* Send reply */ + + skb_out = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb_out) { + res = -ENOMEM; + goto fail; + } + + msg_head = genlmsg_put(skb_out, NETLINK_CB(skb_in).portid, + info->snd_seq, &hsr_genl_family, 0, + HSR_C_SET_NODE_LIST); + if (!msg_head) { + res = -ENOMEM; + goto nla_put_failure; + } + + res = nla_put_u32(skb_out, HSR_A_IFINDEX, hsr_dev->ifindex); + if (res < 0) + goto nla_put_failure; + + hsr_priv = netdev_priv(hsr_dev); + + rcu_read_lock(); + pos = hsr_get_next_node(hsr_priv, NULL, addr); + while (pos) { + res = nla_put(skb_out, HSR_A_NODE_ADDR, ETH_ALEN, addr); + if (res < 0) { + rcu_read_unlock(); + goto nla_put_failure; + } + pos = hsr_get_next_node(hsr_priv, pos, addr); + } + rcu_read_unlock(); + + genlmsg_end(skb_out, msg_head); + genlmsg_unicast(genl_info_net(info), skb_out, info->snd_portid); + + return 0; + +invalid: + netlink_ack(skb_in, nlmsg_hdr(skb_in), -EINVAL); + return 0; + +nla_put_failure: + kfree_skb(skb_out); + /* Fall through */ + +fail: + return res; +} + + +static struct genl_ops hsr_ops_get_node_list = { + .cmd = HSR_C_GET_NODE_LIST, + .flags = 0, + .policy = hsr_genl_policy, + .doit = hsr_get_node_list, + .dumpit = NULL, +}; + +int __init hsr_netlink_init(void) +{ + int rc; + + rc = rtnl_link_register(&hsr_link_ops); + if (rc) + goto fail_rtnl_link_register; + + rc = genl_register_family(&hsr_genl_family); + if (rc) + goto fail_genl_register_family; + + rc = genl_register_ops(&hsr_genl_family, &hsr_ops_get_node_status); + if (rc) + goto fail_genl_register_ops; + + rc = genl_register_ops(&hsr_genl_family, &hsr_ops_get_node_list); + if (rc) + goto fail_genl_register_ops_node_list; + + rc = genl_register_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp); + if (rc) + goto fail_genl_register_mc_group; + + return 0; + +fail_genl_register_mc_group: + genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_list); +fail_genl_register_ops_node_list: + genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_status); +fail_genl_register_ops: + genl_unregister_family(&hsr_genl_family); +fail_genl_register_family: + rtnl_link_unregister(&hsr_link_ops); +fail_rtnl_link_register: + + return rc; +} + +void __exit hsr_netlink_exit(void) +{ + genl_unregister_mc_group(&hsr_genl_family, &hsr_network_genl_mcgrp); + genl_unregister_ops(&hsr_genl_family, &hsr_ops_get_node_status); + genl_unregister_family(&hsr_genl_family); + + rtnl_link_unregister(&hsr_link_ops); +} + +MODULE_ALIAS_RTNL_LINK("hsr"); diff --git a/net/hsr/hsr_netlink.h b/net/hsr/hsr_netlink.h new file mode 100644 index 0000000000000000000000000000000000000000..d4579dcc3c7d50adde22a34c01008c46028283d8 --- /dev/null +++ b/net/hsr/hsr_netlink.h @@ -0,0 +1,30 @@ +/* Copyright 2011-2013 Autronica Fire and Security AS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * Author(s): + * 2011-2013 Arvid Brodin, arvid.brodin@xdin.com + */ + +#ifndef __HSR_NETLINK_H +#define __HSR_NETLINK_H + +#include +#include +#include + +struct hsr_priv; + +int __init hsr_netlink_init(void); +void __exit hsr_netlink_exit(void); + +void hsr_nl_ringerror(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN], + int dev_idx); +void hsr_nl_nodedown(struct hsr_priv *hsr_priv, unsigned char addr[ETH_ALEN]); +void hsr_nl_framedrop(int dropcount, int dev_idx); +void hsr_nl_linkdown(int dev_idx); + +#endif /* __HSR_NETLINK_H */ diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index ff41b4d60d302e18a1ebcf1bdfc950970fdd3aae..426b5df1c98f1cc195158c9829a536cfbd0fefd6 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -440,7 +440,6 @@ lowpan_uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh) default: pr_debug("ERROR: unknown UDP format\n"); goto err; - break; } pr_debug("uncompressed UDP ports: src = %d, dst = %d\n", @@ -655,7 +654,9 @@ static int lowpan_header_create(struct sk_buff *skb, head[1] = iphc1; skb_pull(skb, sizeof(struct ipv6hdr)); + skb_reset_transport_header(skb); memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head); + skb_reset_network_header(skb); lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len); @@ -738,7 +739,6 @@ static int lowpan_skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr) return -ENOMEM; skb_push(new, sizeof(struct ipv6hdr)); - skb_reset_network_header(new); skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr)); new->protocol = htons(ETH_P_IPV6); @@ -785,7 +785,6 @@ lowpan_alloc_new_frame(struct sk_buff *skb, u16 len, u16 tag) goto skb_err; frame->skb->priority = skb->priority; - frame->skb->dev = skb->dev; /* reserve headroom for uncompressed ipv6 header */ skb_reserve(frame->skb, sizeof(struct ipv6hdr)); @@ -1061,7 +1060,6 @@ lowpan_process_data(struct sk_buff *skb) skb = new; skb_push(skb, sizeof(struct udphdr)); - skb_reset_transport_header(skb); skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr)); lowpan_raw_dump_table(__func__, "raw UDP header dump", @@ -1104,50 +1102,40 @@ static int lowpan_set_address(struct net_device *dev, void *p) return 0; } -static int lowpan_get_mac_header_length(struct sk_buff *skb) -{ - /* - * Currently long addressing mode is supported only, so the overall - * header size is 21: - * FC SeqNum DPAN DA SA Sec - * 2 + 1 + 2 + 8 + 8 + 0 = 21 - */ - return 21; -} - static int lowpan_fragment_xmit(struct sk_buff *skb, u8 *head, int mlen, int plen, int offset, int type) { struct sk_buff *frag; - int hlen, ret; + int hlen; hlen = (type == LOWPAN_DISPATCH_FRAG1) ? LOWPAN_FRAG1_HEAD_SIZE : LOWPAN_FRAGN_HEAD_SIZE; lowpan_raw_dump_inline(__func__, "6lowpan fragment header", head, hlen); - frag = dev_alloc_skb(hlen + mlen + plen + IEEE802154_MFR_SIZE); + frag = netdev_alloc_skb(skb->dev, + hlen + mlen + plen + IEEE802154_MFR_SIZE); if (!frag) return -ENOMEM; frag->priority = skb->priority; - frag->dev = skb->dev; /* copy header, MFR and payload */ - memcpy(skb_put(frag, mlen), skb->data, mlen); - memcpy(skb_put(frag, hlen), head, hlen); + skb_put(frag, mlen); + skb_copy_to_linear_data(frag, skb_mac_header(skb), mlen); + + skb_put(frag, hlen); + skb_copy_to_linear_data_offset(frag, mlen, head, hlen); - if (plen) - skb_copy_from_linear_data_offset(skb, offset + mlen, - skb_put(frag, plen), plen); + skb_put(frag, plen); + skb_copy_to_linear_data_offset(frag, mlen + hlen, + skb_network_header(skb) + offset, plen); lowpan_raw_dump_table(__func__, " raw fragment dump", frag->data, frag->len); - ret = dev_queue_xmit(frag); - - return ret; + return dev_queue_xmit(frag); } static int @@ -1156,7 +1144,7 @@ lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev) int err, header_length, payload_length, tag, offset = 0; u8 head[5]; - header_length = lowpan_get_mac_header_length(skb); + header_length = skb->mac_len; payload_length = skb->len - header_length; tag = lowpan_dev_info(dev)->fragment_tag++; @@ -1181,7 +1169,7 @@ lowpan_skb_fragmentation(struct sk_buff *skb, struct net_device *dev) head[0] &= ~LOWPAN_DISPATCH_FRAG1; head[0] |= LOWPAN_DISPATCH_FRAGN; - while ((payload_length - offset > 0) && (err >= 0)) { + while (payload_length - offset > 0) { int len = LOWPAN_FRAG_SIZE; head[4] = offset / 8; @@ -1327,8 +1315,6 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev, /* Pull off the 1-byte of 6lowpan header. */ skb_pull(local_skb, 1); - skb_reset_network_header(local_skb); - skb_set_transport_header(local_skb, sizeof(struct ipv6hdr)); lowpan_give_skb_to_devices(local_skb); @@ -1372,8 +1358,10 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev, real_dev = dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); if (!real_dev) return -ENODEV; - if (real_dev->type != ARPHRD_IEEE802154) + if (real_dev->type != ARPHRD_IEEE802154) { + dev_put(real_dev); return -EINVAL; + } lowpan_dev_info(dev)->real_dev = real_dev; lowpan_dev_info(dev)->fragment_tag = 0; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index cfeb85cff4f02abc28570b267ba6e64784595fab..68af9aac91d04543bdbbbd55997ae901491543fd 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -245,29 +245,6 @@ out: } EXPORT_SYMBOL(inet_listen); -u32 inet_ehash_secret __read_mostly; -EXPORT_SYMBOL(inet_ehash_secret); - -u32 ipv6_hash_secret __read_mostly; -EXPORT_SYMBOL(ipv6_hash_secret); - -/* - * inet_ehash_secret must be set exactly once, and to a non nul value - * ipv6_hash_secret must be set exactly once. - */ -void build_ehash_secret(void) -{ - u32 rnd; - - do { - get_random_bytes(&rnd, sizeof(rnd)); - } while (rnd == 0); - - if (cmpxchg(&inet_ehash_secret, 0, rnd) == 0) - get_random_bytes(&ipv6_hash_secret, sizeof(ipv6_hash_secret)); -} -EXPORT_SYMBOL(build_ehash_secret); - /* * Create an inet socket. */ @@ -284,10 +261,6 @@ static int inet_create(struct net *net, struct socket *sock, int protocol, int try_loading_module = 0; int err; - if (unlikely(!inet_ehash_secret)) - if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) - build_ehash_secret(); - sock->state = SS_UNCONNECTED; /* Look for the requested type/protocol pair. */ @@ -1254,36 +1227,36 @@ static int inet_gso_send_check(struct sk_buff *skb) if (ihl < sizeof(*iph)) goto out; + proto = iph->protocol; + + /* Warning: after this point, iph might be no longer valid */ if (unlikely(!pskb_may_pull(skb, ihl))) goto out; - __skb_pull(skb, ihl); + skb_reset_transport_header(skb); - iph = ip_hdr(skb); - proto = iph->protocol; err = -EPROTONOSUPPORT; - rcu_read_lock(); ops = rcu_dereference(inet_offloads[proto]); if (likely(ops && ops->callbacks.gso_send_check)) err = ops->callbacks.gso_send_check(skb); - rcu_read_unlock(); out: return err; } static struct sk_buff *inet_gso_segment(struct sk_buff *skb, - netdev_features_t features) + netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); const struct net_offload *ops; + unsigned int offset = 0; + bool udpfrag, encap; struct iphdr *iph; int proto; + int nhoff; int ihl; int id; - unsigned int offset = 0; - bool tunnel; if (unlikely(skb_shinfo(skb)->gso_type & ~(SKB_GSO_TCPV4 | @@ -1291,12 +1264,16 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_GRE | + SKB_GSO_IPIP | + SKB_GSO_SIT | SKB_GSO_TCPV6 | SKB_GSO_UDP_TUNNEL | SKB_GSO_MPLS | 0))) goto out; + skb_reset_network_header(skb); + nhoff = skb_network_header(skb) - skb_mac_header(skb); if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) goto out; @@ -1305,42 +1282,50 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, if (ihl < sizeof(*iph)) goto out; + id = ntohs(iph->id); + proto = iph->protocol; + + /* Warning: after this point, iph might be no longer valid */ if (unlikely(!pskb_may_pull(skb, ihl))) goto out; + __skb_pull(skb, ihl); - tunnel = !!skb->encapsulation; + encap = SKB_GSO_CB(skb)->encap_level > 0; + if (encap) + features = skb->dev->hw_enc_features & netif_skb_features(skb); + SKB_GSO_CB(skb)->encap_level += ihl; - __skb_pull(skb, ihl); skb_reset_transport_header(skb); - iph = ip_hdr(skb); - id = ntohs(iph->id); - proto = iph->protocol; + segs = ERR_PTR(-EPROTONOSUPPORT); - rcu_read_lock(); + /* Note : following gso_segment() might change skb->encapsulation */ + udpfrag = !skb->encapsulation && proto == IPPROTO_UDP; + ops = rcu_dereference(inet_offloads[proto]); if (likely(ops && ops->callbacks.gso_segment)) segs = ops->callbacks.gso_segment(skb, features); - rcu_read_unlock(); if (IS_ERR_OR_NULL(segs)) goto out; skb = segs; do { - iph = ip_hdr(skb); - if (!tunnel && proto == IPPROTO_UDP) { + iph = (struct iphdr *)(skb_mac_header(skb) + nhoff); + if (udpfrag) { iph->id = htons(id); iph->frag_off = htons(offset >> 3); if (skb->next != NULL) iph->frag_off |= htons(IP_MF); - offset += (skb->len - skb->mac_len - iph->ihl * 4); - } else { + offset += skb->len - nhoff - ihl; + } else { iph->id = htons(id++); } - iph->tot_len = htons(skb->len - skb->mac_len); - iph->check = 0; - iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl); + iph->tot_len = htons(skb->len - nhoff); + ip_send_check(iph); + if (encap) + skb_reset_inner_headers(skb); + skb->network_header = (u8 *)iph - skb->head; } while ((skb = skb->next)); out: @@ -1546,6 +1531,7 @@ static const struct net_protocol tcp_protocol = { }; static const struct net_protocol udp_protocol = { + .early_demux = udp_v4_early_demux, .handler = udp_rcv, .err_handler = udp_err, .no_policy = 1, @@ -1646,6 +1632,13 @@ static struct packet_offload ip_packet_offload __read_mostly = { }, }; +static const struct net_offload ipip_offload = { + .callbacks = { + .gso_send_check = inet_gso_send_check, + .gso_segment = inet_gso_segment, + }, +}; + static int __init ipv4_offload_init(void) { /* @@ -1657,6 +1650,7 @@ static int __init ipv4_offload_init(void) pr_crit("%s: Cannot add TCP protocol offload\n", __func__); dev_add_offload(&ip_packet_offload); + inet_add_offload(&ipip_offload, IPPROTO_IPIP); return 0; } @@ -1705,8 +1699,6 @@ static int __init inet_init(void) ip_static_sysctl_init(); #endif - tcp_prot.sysctl_mem = init_net.ipv4.sysctl_tcp_mem; - /* * Add all the base protocols. */ diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 109ee89f123e5e51714cae1fa9097e6ec65f0627..7785b28061acfecd59e4ece70815edb3dcc58977 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -121,7 +121,6 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) struct aead_givcrypt_request *req; struct scatterlist *sg; struct scatterlist *asg; - struct esp_data *esp; struct sk_buff *trailer; void *tmp; u8 *iv; @@ -139,8 +138,7 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) /* skb is pure payload to encrypt */ - esp = x->data; - aead = esp->aead; + aead = x->data; alen = crypto_aead_authsize(aead); tfclen = 0; @@ -154,8 +152,6 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) } blksize = ALIGN(crypto_aead_blocksize(aead), 4); clen = ALIGN(skb->len + 2 + tfclen, blksize); - if (esp->padlen) - clen = ALIGN(clen, esp->padlen); plen = clen - skb->len - tfclen; err = skb_cow_data(skb, tfclen + plen + alen, &trailer); @@ -280,8 +276,7 @@ static int esp_input_done2(struct sk_buff *skb, int err) { const struct iphdr *iph; struct xfrm_state *x = xfrm_input_state(skb); - struct esp_data *esp = x->data; - struct crypto_aead *aead = esp->aead; + struct crypto_aead *aead = x->data; int alen = crypto_aead_authsize(aead); int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); int elen = skb->len - hlen; @@ -376,8 +371,7 @@ static void esp_input_done(struct crypto_async_request *base, int err) static int esp_input(struct xfrm_state *x, struct sk_buff *skb) { struct ip_esp_hdr *esph; - struct esp_data *esp = x->data; - struct crypto_aead *aead = esp->aead; + struct crypto_aead *aead = x->data; struct aead_request *req; struct sk_buff *trailer; int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead); @@ -459,9 +453,8 @@ out: static u32 esp4_get_mtu(struct xfrm_state *x, int mtu) { - struct esp_data *esp = x->data; - u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4); - u32 align = max_t(u32, blksize, esp->padlen); + struct crypto_aead *aead = x->data; + u32 blksize = ALIGN(crypto_aead_blocksize(aead), 4); unsigned int net_adj; switch (x->props.mode) { @@ -476,8 +469,8 @@ static u32 esp4_get_mtu(struct xfrm_state *x, int mtu) BUG(); } - return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) - - net_adj) & ~(align - 1)) + net_adj - 2; + return ((mtu - x->props.header_len - crypto_aead_authsize(aead) - + net_adj) & ~(blksize - 1)) + net_adj - 2; } static void esp4_err(struct sk_buff *skb, u32 info) @@ -511,18 +504,16 @@ static void esp4_err(struct sk_buff *skb, u32 info) static void esp_destroy(struct xfrm_state *x) { - struct esp_data *esp = x->data; + struct crypto_aead *aead = x->data; - if (!esp) + if (!aead) return; - crypto_free_aead(esp->aead); - kfree(esp); + crypto_free_aead(aead); } static int esp_init_aead(struct xfrm_state *x) { - struct esp_data *esp = x->data; struct crypto_aead *aead; int err; @@ -531,7 +522,7 @@ static int esp_init_aead(struct xfrm_state *x) if (IS_ERR(aead)) goto error; - esp->aead = aead; + x->data = aead; err = crypto_aead_setkey(aead, x->aead->alg_key, (x->aead->alg_key_len + 7) / 8); @@ -548,7 +539,6 @@ error: static int esp_init_authenc(struct xfrm_state *x) { - struct esp_data *esp = x->data; struct crypto_aead *aead; struct crypto_authenc_key_param *param; struct rtattr *rta; @@ -583,7 +573,7 @@ static int esp_init_authenc(struct xfrm_state *x) if (IS_ERR(aead)) goto error; - esp->aead = aead; + x->data = aead; keylen = (x->aalg ? (x->aalg->alg_key_len + 7) / 8 : 0) + (x->ealg->alg_key_len + 7) / 8 + RTA_SPACE(sizeof(*param)); @@ -638,16 +628,11 @@ error: static int esp_init_state(struct xfrm_state *x) { - struct esp_data *esp; struct crypto_aead *aead; u32 align; int err; - esp = kzalloc(sizeof(*esp), GFP_KERNEL); - if (esp == NULL) - return -ENOMEM; - - x->data = esp; + x->data = NULL; if (x->aead) err = esp_init_aead(x); @@ -657,9 +642,7 @@ static int esp_init_state(struct xfrm_state *x) if (err) goto error; - aead = esp->aead; - - esp->padlen = 0; + aead = x->data; x->props.header_len = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); @@ -683,9 +666,7 @@ static int esp_init_state(struct xfrm_state *x) } align = ALIGN(crypto_aead_blocksize(aead), 4); - if (esp->padlen) - align = max_t(u32, align, esp->padlen); - x->props.trailer_len = align + 1 + crypto_aead_authsize(esp->aead); + x->props.trailer_len = align + 1 + crypto_aead_authsize(aead); error: return err; diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index b3f627ac4ed8ae5031d559a5648612b46f015e25..d846304b7b89d619d53609c5e435b3ddcc3e2d84 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -933,7 +933,6 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb) local_bh_disable(); frn->tb_id = tb->tb_id; - rcu_read_lock(); frn->err = fib_table_lookup(tb, &fl4, &res, FIB_LOOKUP_NOREF); if (!frn->err) { @@ -942,7 +941,6 @@ static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb) frn->type = res.type; frn->scope = res.scope; } - rcu_read_unlock(); local_bh_enable(); } } diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h index af0f14aba169a2fa11e03eb0fb1bde4b50261568..388d113fd289b9134cf34e930a26c6c99b7ea721 100644 --- a/net/ipv4/fib_lookup.h +++ b/net/ipv4/fib_lookup.h @@ -24,21 +24,17 @@ static inline void fib_alias_accessed(struct fib_alias *fa) } /* Exported by fib_semantics.c */ -extern void fib_release_info(struct fib_info *); -extern struct fib_info *fib_create_info(struct fib_config *cfg); -extern int fib_nh_match(struct fib_config *cfg, struct fib_info *fi); -extern int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, - u32 tb_id, u8 type, __be32 dst, - int dst_len, u8 tos, struct fib_info *fi, - unsigned int); -extern void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, - int dst_len, u32 tb_id, struct nl_info *info, - unsigned int nlm_flags); -extern struct fib_alias *fib_find_alias(struct list_head *fah, - u8 tos, u32 prio); -extern int fib_detect_death(struct fib_info *fi, int order, - struct fib_info **last_resort, - int *last_idx, int dflt); +void fib_release_info(struct fib_info *); +struct fib_info *fib_create_info(struct fib_config *cfg); +int fib_nh_match(struct fib_config *cfg, struct fib_info *fi); +int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, u32 tb_id, + u8 type, __be32 dst, int dst_len, u8 tos, struct fib_info *fi, + unsigned int); +void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, int dst_len, + u32 tb_id, const struct nl_info *info, unsigned int nlm_flags); +struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio); +int fib_detect_death(struct fib_info *fi, int order, + struct fib_info **last_resort, int *last_idx, int dflt); static inline void fib_result_assign(struct fib_result *res, struct fib_info *fi) diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index d5dbca5ecf628c4dd5a52daf306047fb971de33e..e63f47a4e651f2c5587723f7796e2fd9ec43b4f8 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -380,7 +380,7 @@ static inline size_t fib_nlmsg_size(struct fib_info *fi) } void rtmsg_fib(int event, __be32 key, struct fib_alias *fa, - int dst_len, u32 tb_id, struct nl_info *info, + int dst_len, u32 tb_id, const struct nl_info *info, unsigned int nlm_flags) { struct sk_buff *skb; diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 3df6d3edb2a15a98cb0e90a4e5ed935f42f15f1c..ec9a9ef4ce50851639cf4cbe8e497390bea371f6 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -762,12 +762,9 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn) if (IS_LEAF(node) || ((struct tnode *) node)->pos > tn->pos + tn->bits - 1) { - if (tkey_extract_bits(node->key, - oldtnode->pos + oldtnode->bits, - 1) == 0) - put_child(tn, 2*i, node); - else - put_child(tn, 2*i+1, node); + put_child(tn, + tkey_extract_bits(node->key, oldtnode->pos, oldtnode->bits + 1), + node); continue; } @@ -1120,12 +1117,8 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen) * first tnode need some special handling */ - if (tp) - pos = tp->pos+tp->bits; - else - pos = 0; - if (n) { + pos = tp ? tp->pos+tp->bits : 0; newpos = tkey_mismatch(key, pos, n->key); tn = tnode_new(n->key, newpos, 1); } else { diff --git a/net/ipv4/gre_demux.c b/net/ipv4/gre_demux.c index 736c9fc3ef93c0f2de02658997782f7af011c122..5893e99e82990ae75f4221249e3e5fcbceb819c4 100644 --- a/net/ipv4/gre_demux.c +++ b/net/ipv4/gre_demux.c @@ -93,35 +93,6 @@ void gre_build_header(struct sk_buff *skb, const struct tnl_ptk_info *tpi, } EXPORT_SYMBOL_GPL(gre_build_header); -struct sk_buff *gre_handle_offloads(struct sk_buff *skb, bool gre_csum) -{ - int err; - - if (likely(!skb->encapsulation)) { - skb_reset_inner_headers(skb); - skb->encapsulation = 1; - } - - if (skb_is_gso(skb)) { - err = skb_unclone(skb, GFP_ATOMIC); - if (unlikely(err)) - goto error; - skb_shinfo(skb)->gso_type |= SKB_GSO_GRE; - return skb; - } else if (skb->ip_summed == CHECKSUM_PARTIAL && gre_csum) { - err = skb_checksum_help(skb); - if (unlikely(err)) - goto error; - } else if (skb->ip_summed != CHECKSUM_PARTIAL) - skb->ip_summed = CHECKSUM_NONE; - - return skb; -error: - kfree_skb(skb); - return ERR_PTR(err); -} -EXPORT_SYMBOL_GPL(gre_handle_offloads); - static __sum16 check_checksum(struct sk_buff *skb) { __sum16 csum = 0; diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c index 55e6bfb3a28921c26b0be3170f10c1245a9a74ae..e5d436188464eba55eca773f4fb88fd0f0634e97 100644 --- a/net/ipv4/gre_offload.c +++ b/net/ipv4/gre_offload.c @@ -39,7 +39,8 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb, SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCP_ECN | - SKB_GSO_GRE))) + SKB_GSO_GRE | + SKB_GSO_IPIP))) goto out; if (unlikely(!pskb_may_pull(skb, sizeof(*greh)))) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 5f7d11a458713f9c755dd1a1a40289b180a3e041..5c0e8bc6e5ba275d2469336533b5848de94ae6c1 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -353,6 +353,9 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) saddr = fib_compute_spec_dst(skb); ipc.opt = NULL; ipc.tx_flags = 0; + ipc.ttl = 0; + ipc.tos = -1; + if (icmp_param->replyopts.opt.opt.optlen) { ipc.opt = &icmp_param->replyopts.opt; if (ipc.opt->opt.srr) @@ -608,6 +611,8 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) ipc.addr = iph->saddr; ipc.opt = &icmp_param->replyopts.opt; ipc.tx_flags = 0; + ipc.ttl = 0; + ipc.tos = -1; rt = icmp_route_lookup(net, &fl4, skb_in, iph, saddr, tos, type, code, icmp_param); diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 6acb541c90910204f02449e7500138362da6998a..fc0e649cc002beb0631c390e6aed5f1ea3fe7390 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -29,27 +29,19 @@ const char inet_csk_timer_bug_msg[] = "inet_csk BUG: unknown timer value\n"; EXPORT_SYMBOL(inet_csk_timer_bug_msg); #endif -/* - * This struct holds the first and last local port number. - */ -struct local_ports sysctl_local_ports __read_mostly = { - .lock = __SEQLOCK_UNLOCKED(sysctl_local_ports.lock), - .range = { 32768, 61000 }, -}; - unsigned long *sysctl_local_reserved_ports; EXPORT_SYMBOL(sysctl_local_reserved_ports); -void inet_get_local_port_range(int *low, int *high) +void inet_get_local_port_range(struct net *net, int *low, int *high) { unsigned int seq; do { - seq = read_seqbegin(&sysctl_local_ports.lock); + seq = read_seqbegin(&net->ipv4.sysctl_local_ports.lock); - *low = sysctl_local_ports.range[0]; - *high = sysctl_local_ports.range[1]; - } while (read_seqretry(&sysctl_local_ports.lock, seq)); + *low = net->ipv4.sysctl_local_ports.range[0]; + *high = net->ipv4.sysctl_local_ports.range[1]; + } while (read_seqretry(&net->ipv4.sysctl_local_ports.lock, seq)); } EXPORT_SYMBOL(inet_get_local_port_range); @@ -79,17 +71,16 @@ int inet_csk_bind_conflict(const struct sock *sk, (!reuseport || !sk2->sk_reuseport || (sk2->sk_state != TCP_TIME_WAIT && !uid_eq(uid, sock_i_uid(sk2))))) { - const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2); - if (!sk2_rcv_saddr || !sk_rcv_saddr(sk) || - sk2_rcv_saddr == sk_rcv_saddr(sk)) + + if (!sk2->sk_rcv_saddr || !sk->sk_rcv_saddr || + sk2->sk_rcv_saddr == sk->sk_rcv_saddr) break; } if (!relax && reuse && sk2->sk_reuse && sk2->sk_state != TCP_LISTEN) { - const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2); - if (!sk2_rcv_saddr || !sk_rcv_saddr(sk) || - sk2_rcv_saddr == sk_rcv_saddr(sk)) + if (!sk2->sk_rcv_saddr || !sk->sk_rcv_saddr || + sk2->sk_rcv_saddr == sk->sk_rcv_saddr) break; } } @@ -116,7 +107,7 @@ int inet_csk_get_port(struct sock *sk, unsigned short snum) int remaining, rover, low, high; again: - inet_get_local_port_range(&low, &high); + inet_get_local_port_range(net, &low, &high); remaining = (high - low) + 1; smallest_rover = rover = net_random() % remaining + low; @@ -421,8 +412,8 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, flags, - (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, + ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -457,8 +448,8 @@ struct dst_entry *inet_csk_route_child_sock(struct sock *sk, flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, sk->sk_protocol, inet_sk_flowi_flags(sk), - (opt && opt->opt.srr) ? opt->opt.faddr : ireq->rmt_addr, - ireq->loc_addr, ireq->rmt_port, inet_sk(sk)->inet_sport); + (opt && opt->opt.srr) ? opt->opt.faddr : ireq->ir_rmt_addr, + ireq->ir_loc_addr, ireq->ir_rmt_port, inet_sk(sk)->inet_sport); security_req_classify_flow(req, flowi4_to_flowi(fl4)); rt = ip_route_output_flow(net, fl4, sk); if (IS_ERR(rt)) @@ -504,9 +495,9 @@ struct request_sock *inet_csk_search_req(const struct sock *sk, prev = &req->dl_next) { const struct inet_request_sock *ireq = inet_rsk(req); - if (ireq->rmt_port == rport && - ireq->rmt_addr == raddr && - ireq->loc_addr == laddr && + if (ireq->ir_rmt_port == rport && + ireq->ir_rmt_addr == raddr && + ireq->ir_loc_addr == laddr && AF_INET_FAMILY(req->rsk_ops->family)) { WARN_ON(req->sk); *prevp = prev; @@ -523,7 +514,8 @@ void inet_csk_reqsk_queue_hash_add(struct sock *sk, struct request_sock *req, { struct inet_connection_sock *icsk = inet_csk(sk); struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; - const u32 h = inet_synq_hash(inet_rsk(req)->rmt_addr, inet_rsk(req)->rmt_port, + const u32 h = inet_synq_hash(inet_rsk(req)->ir_rmt_addr, + inet_rsk(req)->ir_rmt_port, lopt->hash_rnd, lopt->nr_table_entries); reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout); @@ -683,9 +675,9 @@ struct sock *inet_csk_clone_lock(const struct sock *sk, newsk->sk_state = TCP_SYN_RECV; newicsk->icsk_bind_hash = NULL; - inet_sk(newsk)->inet_dport = inet_rsk(req)->rmt_port; - inet_sk(newsk)->inet_num = ntohs(inet_rsk(req)->loc_port); - inet_sk(newsk)->inet_sport = inet_rsk(req)->loc_port; + inet_sk(newsk)->inet_dport = inet_rsk(req)->ir_rmt_port; + inet_sk(newsk)->inet_num = inet_rsk(req)->ir_num; + inet_sk(newsk)->inet_sport = htons(inet_rsk(req)->ir_num); newsk->sk_write_space = sk_stream_write_space; newicsk->icsk_retransmits = 0; diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 5f648751fce2d03418f4a6425c7ba0dbfb3ddfc0..56a964a553d2c739a03b880acd7158a4c9714b66 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -121,13 +121,13 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, #if IS_ENABLED(CONFIG_IPV6) if (r->idiag_family == AF_INET6) { - const struct ipv6_pinfo *np = inet6_sk(sk); - *(struct in6_addr *)r->id.idiag_src = np->rcv_saddr; - *(struct in6_addr *)r->id.idiag_dst = np->daddr; + *(struct in6_addr *)r->id.idiag_src = sk->sk_v6_rcv_saddr; + *(struct in6_addr *)r->id.idiag_dst = sk->sk_v6_daddr; if (ext & (1 << (INET_DIAG_TCLASS - 1))) - if (nla_put_u8(skb, INET_DIAG_TCLASS, np->tclass) < 0) + if (nla_put_u8(skb, INET_DIAG_TCLASS, + inet6_sk(sk)->tclass) < 0) goto errout; } #endif @@ -222,7 +222,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, u32 portid, u32 seq, u16 nlmsg_flags, const struct nlmsghdr *unlh) { - long tmo; + s32 tmo; struct inet_diag_msg *r; struct nlmsghdr *nlh; @@ -234,7 +234,7 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, r = nlmsg_data(nlh); BUG_ON(tw->tw_state != TCP_TIME_WAIT); - tmo = tw->tw_ttd - jiffies; + tmo = tw->tw_ttd - inet_tw_time_stamp(); if (tmo < 0) tmo = 0; @@ -248,18 +248,15 @@ static int inet_twsk_diag_fill(struct inet_timewait_sock *tw, r->id.idiag_dst[0] = tw->tw_daddr; r->idiag_state = tw->tw_substate; r->idiag_timer = 3; - r->idiag_expires = DIV_ROUND_UP(tmo * 1000, HZ); + r->idiag_expires = jiffies_to_msecs(tmo); r->idiag_rqueue = 0; r->idiag_wqueue = 0; r->idiag_uid = 0; r->idiag_inode = 0; #if IS_ENABLED(CONFIG_IPV6) if (tw->tw_family == AF_INET6) { - const struct inet6_timewait_sock *tw6 = - inet6_twsk((struct sock *)tw); - - *(struct in6_addr *)r->id.idiag_src = tw6->tw_v6_rcv_saddr; - *(struct in6_addr *)r->id.idiag_dst = tw6->tw_v6_daddr; + *(struct in6_addr *)r->id.idiag_src = tw->tw_v6_rcv_saddr; + *(struct in6_addr *)r->id.idiag_dst = tw->tw_v6_daddr; } #endif @@ -273,10 +270,11 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, const struct nlmsghdr *unlh) { if (sk->sk_state == TCP_TIME_WAIT) - return inet_twsk_diag_fill((struct inet_timewait_sock *)sk, - skb, r, portid, seq, nlmsg_flags, - unlh); - return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq, nlmsg_flags, unlh); + return inet_twsk_diag_fill(inet_twsk(sk), skb, r, portid, seq, + nlmsg_flags, unlh); + + return inet_csk_diag_fill(sk, skb, r, user_ns, portid, seq, + nlmsg_flags, unlh); } int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_skb, @@ -338,12 +336,9 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, struct sk_buff *in_s err = 0; out: - if (sk) { - if (sk->sk_state == TCP_TIME_WAIT) - inet_twsk_put((struct inet_timewait_sock *)sk); - else - sock_put(sk); - } + if (sk) + sock_gen_put(sk); + out_nosk: return err; } @@ -489,10 +484,9 @@ int inet_diag_bc_sk(const struct nlattr *bc, struct sock *sk) entry.family = sk->sk_family; #if IS_ENABLED(CONFIG_IPV6) if (entry.family == AF_INET6) { - struct ipv6_pinfo *np = inet6_sk(sk); - entry.saddr = np->rcv_saddr.s6_addr32; - entry.daddr = np->daddr.s6_addr32; + entry.saddr = sk->sk_v6_rcv_saddr.s6_addr32; + entry.daddr = sk->sk_v6_daddr.s6_addr32; } else #endif { @@ -635,22 +629,22 @@ static int inet_csk_diag_dump(struct sock *sk, cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh); } -static int inet_twsk_diag_dump(struct inet_timewait_sock *tw, +static int inet_twsk_diag_dump(struct sock *sk, struct sk_buff *skb, struct netlink_callback *cb, struct inet_diag_req_v2 *r, const struct nlattr *bc) { + struct inet_timewait_sock *tw = inet_twsk(sk); + if (bc != NULL) { struct inet_diag_entry entry; entry.family = tw->tw_family; #if IS_ENABLED(CONFIG_IPV6) if (tw->tw_family == AF_INET6) { - struct inet6_timewait_sock *tw6 = - inet6_twsk((struct sock *)tw); - entry.saddr = tw6->tw_v6_rcv_saddr.s6_addr32; - entry.daddr = tw6->tw_v6_daddr.s6_addr32; + entry.saddr = tw->tw_v6_rcv_saddr.s6_addr32; + entry.daddr = tw->tw_v6_daddr.s6_addr32; } else #endif { @@ -682,12 +676,12 @@ static inline void inet_diag_req_addrs(const struct sock *sk, #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family == AF_INET6) { if (req->rsk_ops->family == AF_INET6) { - entry->saddr = inet6_rsk(req)->loc_addr.s6_addr32; - entry->daddr = inet6_rsk(req)->rmt_addr.s6_addr32; + entry->saddr = ireq->ir_v6_loc_addr.s6_addr32; + entry->daddr = ireq->ir_v6_rmt_addr.s6_addr32; } else if (req->rsk_ops->family == AF_INET) { - ipv6_addr_set_v4mapped(ireq->loc_addr, + ipv6_addr_set_v4mapped(ireq->ir_loc_addr, &entry->saddr_storage); - ipv6_addr_set_v4mapped(ireq->rmt_addr, + ipv6_addr_set_v4mapped(ireq->ir_rmt_addr, &entry->daddr_storage); entry->saddr = entry->saddr_storage.s6_addr32; entry->daddr = entry->daddr_storage.s6_addr32; @@ -695,8 +689,8 @@ static inline void inet_diag_req_addrs(const struct sock *sk, } else #endif { - entry->saddr = &ireq->loc_addr; - entry->daddr = &ireq->rmt_addr; + entry->saddr = &ireq->ir_loc_addr; + entry->daddr = &ireq->ir_rmt_addr; } } @@ -731,9 +725,9 @@ static int inet_diag_fill_req(struct sk_buff *skb, struct sock *sk, tmo = 0; r->id.idiag_sport = inet->inet_sport; - r->id.idiag_dport = ireq->rmt_port; - r->id.idiag_src[0] = ireq->loc_addr; - r->id.idiag_dst[0] = ireq->rmt_addr; + r->id.idiag_dport = ireq->ir_rmt_port; + r->id.idiag_src[0] = ireq->ir_loc_addr; + r->id.idiag_dst[0] = ireq->ir_rmt_addr; r->idiag_expires = jiffies_to_msecs(tmo); r->idiag_rqueue = 0; r->idiag_wqueue = 0; @@ -792,13 +786,13 @@ static int inet_diag_dump_reqs(struct sk_buff *skb, struct sock *sk, if (reqnum < s_reqnum) continue; - if (r->id.idiag_dport != ireq->rmt_port && + if (r->id.idiag_dport != ireq->ir_rmt_port && r->id.idiag_dport) continue; if (bc) { inet_diag_req_addrs(sk, req, &entry); - entry.dport = ntohs(ireq->rmt_port); + entry.dport = ntohs(ireq->ir_rmt_port); if (!inet_diag_bc_run(bc, &entry)) continue; @@ -911,8 +905,7 @@ skip_listen_ht: num = 0; - if (hlist_nulls_empty(&head->chain) && - hlist_nulls_empty(&head->twchain)) + if (hlist_nulls_empty(&head->chain)) continue; if (i > s_i) @@ -920,7 +913,7 @@ skip_listen_ht: spin_lock_bh(lock); sk_nulls_for_each(sk, node, &head->chain) { - struct inet_sock *inet = inet_sk(sk); + int res; if (!net_eq(sock_net(sk), net)) continue; @@ -929,15 +922,19 @@ skip_listen_ht: if (!(r->idiag_states & (1 << sk->sk_state))) goto next_normal; if (r->sdiag_family != AF_UNSPEC && - sk->sk_family != r->sdiag_family) + sk->sk_family != r->sdiag_family) goto next_normal; - if (r->id.idiag_sport != inet->inet_sport && + if (r->id.idiag_sport != htons(sk->sk_num) && r->id.idiag_sport) goto next_normal; - if (r->id.idiag_dport != inet->inet_dport && + if (r->id.idiag_dport != sk->sk_dport && r->id.idiag_dport) goto next_normal; - if (inet_csk_diag_dump(sk, skb, cb, r, bc) < 0) { + if (sk->sk_state == TCP_TIME_WAIT) + res = inet_twsk_diag_dump(sk, skb, cb, r, bc); + else + res = inet_csk_diag_dump(sk, skb, cb, r, bc); + if (res < 0) { spin_unlock_bh(lock); goto done; } @@ -945,33 +942,6 @@ next_normal: ++num; } - if (r->idiag_states & TCPF_TIME_WAIT) { - struct inet_timewait_sock *tw; - - inet_twsk_for_each(tw, node, - &head->twchain) { - if (!net_eq(twsk_net(tw), net)) - continue; - - if (num < s_num) - goto next_dying; - if (r->sdiag_family != AF_UNSPEC && - tw->tw_family != r->sdiag_family) - goto next_dying; - if (r->id.idiag_sport != tw->tw_sport && - r->id.idiag_sport) - goto next_dying; - if (r->id.idiag_dport != tw->tw_dport && - r->id.idiag_dport) - goto next_dying; - if (inet_twsk_diag_dump(tw, skb, cb, r, bc) < 0) { - spin_unlock_bh(lock); - goto done; - } -next_dying: - ++num; - } - } spin_unlock_bh(lock); } diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index c5313a9c019b9b94aca569aba011b63fc452e1c5..bb075fc9a14f25169c175c7fcdcb86d56c709627 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -93,9 +93,6 @@ void inet_frags_init(struct inet_frags *f) } rwlock_init(&f->lock); - f->rnd = (u32) ((totalram_pages ^ (totalram_pages >> 7)) ^ - (jiffies ^ (jiffies >> 6))); - setup_timer(&f->secret_timer, inet_frag_secret_rebuild, (unsigned long)f); f->secret_timer.expires = jiffies + f->secret_interval; diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 96da9c77decad927aa7a29ea816efa2a3013db37..8b9cf279450d6cf0c24e64a20fb0d05b9fb89a82 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -24,6 +24,31 @@ #include #include +static unsigned int inet_ehashfn(struct net *net, const __be32 laddr, + const __u16 lport, const __be32 faddr, + const __be16 fport) +{ + static u32 inet_ehash_secret __read_mostly; + + net_get_random_once(&inet_ehash_secret, sizeof(inet_ehash_secret)); + + return __inet_ehashfn(laddr, lport, faddr, fport, + inet_ehash_secret + net_hash_mix(net)); +} + + +static unsigned int inet_sk_ehashfn(const struct sock *sk) +{ + const struct inet_sock *inet = inet_sk(sk); + const __be32 laddr = inet->inet_rcv_saddr; + const __u16 lport = inet->inet_num; + const __be32 faddr = inet->inet_daddr; + const __be16 fport = inet->inet_dport; + struct net *net = sock_net(sk); + + return inet_ehashfn(net, laddr, lport, faddr, fport); +} + /* * Allocate and initialize a new local port bind bucket. * The bindhash mutex for snum's hash chain must be held here. @@ -230,6 +255,19 @@ begin: } EXPORT_SYMBOL_GPL(__inet_lookup_listener); +/* All sockets share common refcount, but have different destructors */ +void sock_gen_put(struct sock *sk) +{ + if (!atomic_dec_and_test(&sk->sk_refcnt)) + return; + + if (sk->sk_state == TCP_TIME_WAIT) + inet_twsk_free(inet_twsk(sk)); + else + sk_free(sk); +} +EXPORT_SYMBOL_GPL(sock_gen_put); + struct sock *__inet_lookup_established(struct net *net, struct inet_hashinfo *hashinfo, const __be32 saddr, const __be16 sport, @@ -255,13 +293,13 @@ begin: if (likely(INET_MATCH(sk, net, acookie, saddr, daddr, ports, dif))) { if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) - goto begintw; + goto out; if (unlikely(!INET_MATCH(sk, net, acookie, saddr, daddr, ports, dif))) { - sock_put(sk); + sock_gen_put(sk); goto begin; } - goto out; + goto found; } } /* @@ -271,37 +309,9 @@ begin: */ if (get_nulls_value(node) != slot) goto begin; - -begintw: - /* Must check for a TIME_WAIT'er before going to listener hash. */ - sk_nulls_for_each_rcu(sk, node, &head->twchain) { - if (sk->sk_hash != hash) - continue; - if (likely(INET_TW_MATCH(sk, net, acookie, - saddr, daddr, ports, - dif))) { - if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) { - sk = NULL; - goto out; - } - if (unlikely(!INET_TW_MATCH(sk, net, acookie, - saddr, daddr, ports, - dif))) { - inet_twsk_put(inet_twsk(sk)); - goto begintw; - } - goto out; - } - } - /* - * if the nulls value we got at the end of this lookup is - * not the expected one, we must restart lookup. - * We probably met an item that was moved to another chain. - */ - if (get_nulls_value(node) != slot) - goto begintw; - sk = NULL; out: + sk = NULL; +found: rcu_read_unlock(); return sk; } @@ -326,39 +336,29 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row, spinlock_t *lock = inet_ehash_lockp(hinfo, hash); struct sock *sk2; const struct hlist_nulls_node *node; - struct inet_timewait_sock *tw; + struct inet_timewait_sock *tw = NULL; int twrefcnt = 0; spin_lock(lock); - /* Check TIME-WAIT sockets first. */ - sk_nulls_for_each(sk2, node, &head->twchain) { - if (sk2->sk_hash != hash) - continue; - - if (likely(INET_TW_MATCH(sk2, net, acookie, - saddr, daddr, ports, dif))) { - tw = inet_twsk(sk2); - if (twsk_unique(sk, sk2, twp)) - goto unique; - else - goto not_unique; - } - } - tw = NULL; - - /* And established part... */ sk_nulls_for_each(sk2, node, &head->chain) { if (sk2->sk_hash != hash) continue; + if (likely(INET_MATCH(sk2, net, acookie, - saddr, daddr, ports, dif))) + saddr, daddr, ports, dif))) { + if (sk2->sk_state == TCP_TIME_WAIT) { + tw = inet_twsk(sk2); + if (twsk_unique(sk, sk2, twp)) + break; + } goto not_unique; + } } -unique: /* Must record num and sport now. Otherwise we will see - * in hash table socket with a funny identity. */ + * in hash table socket with a funny identity. + */ inet->inet_num = lport; inet->inet_sport = htons(lport); sk->sk_hash = hash; @@ -494,7 +494,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, u32 offset = hint + port_offset; struct inet_timewait_sock *tw = NULL; - inet_get_local_port_range(&low, &high); + inet_get_local_port_range(net, &low, &high); remaining = (high - low) + 1; local_bh_disable(); diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c index 1f27c9f4afd07fbf55589ed4e6e730375dcdc0a3..6d592f8555fb8bf15506d828b53c6582386f7bec 100644 --- a/net/ipv4/inet_timewait_sock.c +++ b/net/ipv4/inet_timewait_sock.c @@ -87,19 +87,11 @@ static void __inet_twsk_kill(struct inet_timewait_sock *tw, refcnt += inet_twsk_bind_unhash(tw, hashinfo); spin_unlock(&bhead->lock); -#ifdef SOCK_REFCNT_DEBUG - if (atomic_read(&tw->tw_refcnt) != 1) { - pr_debug("%s timewait_sock %p refcnt=%d\n", - tw->tw_prot->name, tw, atomic_read(&tw->tw_refcnt)); - } -#endif - while (refcnt) { - inet_twsk_put(tw); - refcnt--; - } + BUG_ON(refcnt >= atomic_read(&tw->tw_refcnt)); + atomic_sub(refcnt, &tw->tw_refcnt); } -static noinline void inet_twsk_free(struct inet_timewait_sock *tw) +void inet_twsk_free(struct inet_timewait_sock *tw) { struct module *owner = tw->tw_prot->owner; twsk_destructor((struct sock *)tw); @@ -118,6 +110,18 @@ void inet_twsk_put(struct inet_timewait_sock *tw) } EXPORT_SYMBOL_GPL(inet_twsk_put); +static void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw, + struct hlist_nulls_head *list) +{ + hlist_nulls_add_head_rcu(&tw->tw_node, list); +} + +static void inet_twsk_add_bind_node(struct inet_timewait_sock *tw, + struct hlist_head *list) +{ + hlist_add_head(&tw->tw_bind_node, list); +} + /* * Enter the time wait state. This is called with locally disabled BH. * Essentially we whip up a timewait bucket, copy the relevant info into it @@ -146,26 +150,21 @@ void __inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk, spin_lock(lock); /* - * Step 2: Hash TW into TIMEWAIT chain. - * Should be done before removing sk from established chain - * because readers are lockless and search established first. + * Step 2: Hash TW into tcp ehash chain. + * Notes : + * - tw_refcnt is set to 3 because : + * - We have one reference from bhash chain. + * - We have one reference from ehash chain. + * We can use atomic_set() because prior spin_lock()/spin_unlock() + * committed into memory all tw fields. */ - inet_twsk_add_node_rcu(tw, &ehead->twchain); + atomic_set(&tw->tw_refcnt, 1 + 1 + 1); + inet_twsk_add_node_rcu(tw, &ehead->chain); - /* Step 3: Remove SK from established hash. */ + /* Step 3: Remove SK from hash chain */ if (__sk_nulls_del_node_init_rcu(sk)) sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); - /* - * Notes : - * - We initially set tw_refcnt to 0 in inet_twsk_alloc() - * - We add one reference for the bhash link - * - We add one reference for the ehash link - * - We want this refcnt update done before allowing other - * threads to find this tw in ehash chain. - */ - atomic_add(1 + 1 + 1, &tw->tw_refcnt); - spin_unlock(lock); } EXPORT_SYMBOL_GPL(__inet_twsk_hashdance); @@ -387,11 +386,11 @@ void inet_twsk_schedule(struct inet_timewait_sock *tw, if (slot >= INET_TWDR_TWKILL_SLOTS) slot = INET_TWDR_TWKILL_SLOTS - 1; } - tw->tw_ttd = jiffies + timeo; + tw->tw_ttd = inet_tw_time_stamp() + timeo; slot = (twdr->slot + slot) & (INET_TWDR_TWKILL_SLOTS - 1); list = &twdr->cells[slot]; } else { - tw->tw_ttd = jiffies + (slot << INET_TWDR_RECYCLE_TICK); + tw->tw_ttd = inet_tw_time_stamp() + (slot << INET_TWDR_RECYCLE_TICK); if (twdr->twcal_hand < 0) { twdr->twcal_hand = 0; @@ -490,7 +489,9 @@ void inet_twsk_purge(struct inet_hashinfo *hashinfo, restart_rcu: rcu_read_lock(); restart: - sk_nulls_for_each_rcu(sk, node, &head->twchain) { + sk_nulls_for_each_rcu(sk, node, &head->chain) { + if (sk->sk_state != TCP_TIME_WAIT) + continue; tw = inet_twsk(sk); if ((tw->tw_family != family) || atomic_read(&twsk_net(tw)->count)) diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index b66910aaef4d633977d2e983f2c63b419da38f77..2481993a49708337af890fad97c70f8d6c85f43d 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -106,6 +106,7 @@ struct ip4_create_arg { static unsigned int ipqhashfn(__be16 id, __be32 saddr, __be32 daddr, u8 prot) { + net_get_random_once(&ip4_frags.rnd, sizeof(ip4_frags.rnd)); return jhash_3words((__force u32)id << 16 | prot, (__force u32)saddr, (__force u32)daddr, ip4_frags.rnd) & (INETFRAGS_HASHSZ - 1); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 3982eabf61e126060fc7c5b48042bea2f0417135..912402752f2ffce701d697de1bb7a32b05c3b482 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -810,7 +810,7 @@ static int __ip_append_data(struct sock *sk, int copy; int err; int offset = 0; - unsigned int maxfraglen, fragheaderlen; + unsigned int maxfraglen, fragheaderlen, maxnonfragsize; int csummode = CHECKSUM_NONE; struct rtable *rt = (struct rtable *)cork->dst; @@ -823,8 +823,10 @@ static int __ip_append_data(struct sock *sk, fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; + maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ? + mtu : 0xFFFF; - if (cork->length + length > 0xFFFF - fragheaderlen) { + if (cork->length + length > maxnonfragsize - fragheaderlen) { ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, mtu-exthdrlen); return -EMSGSIZE; @@ -1035,7 +1037,6 @@ error: static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, struct ipcm_cookie *ipc, struct rtable **rtp) { - struct inet_sock *inet = inet_sk(sk); struct ip_options_rcu *opt; struct rtable *rt; @@ -1061,10 +1062,13 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, * We steal reference to this route, caller should not release it */ *rtp = NULL; - cork->fragsize = inet->pmtudisc == IP_PMTUDISC_PROBE ? - rt->dst.dev->mtu : dst_mtu(&rt->dst); + cork->fragsize = ip_sk_use_pmtu(sk) ? + dst_mtu(&rt->dst) : rt->dst.dev->mtu; cork->dst = &rt->dst; cork->length = 0; + cork->ttl = ipc->ttl; + cork->tos = ipc->tos; + cork->priority = ipc->priority; cork->tx_flags = ipc->tx_flags; return 0; @@ -1119,7 +1123,7 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, int mtu; int len; int err; - unsigned int maxfraglen, fragheaderlen, fraggap; + unsigned int maxfraglen, fragheaderlen, fraggap, maxnonfragsize; if (inet->hdrincl) return -EPERM; @@ -1143,8 +1147,10 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; + maxnonfragsize = (inet->pmtudisc >= IP_PMTUDISC_DO) ? + mtu : 0xFFFF; - if (cork->length + size > 0xFFFF - fragheaderlen) { + if (cork->length + size > maxnonfragsize - fragheaderlen) { ip_local_error(sk, EMSGSIZE, fl4->daddr, inet->inet_dport, mtu); return -EMSGSIZE; } @@ -1308,7 +1314,8 @@ struct sk_buff *__ip_make_skb(struct sock *sk, /* DF bit is set when we want to see DF on outgoing frames. * If local_df is set too, we still allow to fragment this frame * locally. */ - if (inet->pmtudisc >= IP_PMTUDISC_DO || + if (inet->pmtudisc == IP_PMTUDISC_DO || + inet->pmtudisc == IP_PMTUDISC_PROBE || (skb->len <= dst_mtu(&rt->dst) && ip_dont_fragment(sk, &rt->dst))) df = htons(IP_DF); @@ -1316,7 +1323,9 @@ struct sk_buff *__ip_make_skb(struct sock *sk, if (cork->flags & IPCORK_OPT) opt = cork->opt; - if (rt->rt_type == RTN_MULTICAST) + if (cork->ttl != 0) + ttl = cork->ttl; + else if (rt->rt_type == RTN_MULTICAST) ttl = inet->mc_ttl; else ttl = ip_select_ttl(inet, &rt->dst); @@ -1324,7 +1333,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, iph = ip_hdr(skb); iph->version = 4; iph->ihl = 5; - iph->tos = inet->tos; + iph->tos = (cork->tos != -1) ? cork->tos : inet->tos; iph->frag_off = df; iph->ttl = ttl; iph->protocol = sk->sk_protocol; @@ -1336,7 +1345,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk, ip_options_build(skb, opt, cork->addr, rt, 0); } - skb->priority = sk->sk_priority; + skb->priority = (cork->tos != -1) ? cork->priority: sk->sk_priority; skb->mark = sk->sk_mark; /* * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec @@ -1486,6 +1495,8 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb, __be32 daddr, ipc.addr = daddr; ipc.opt = NULL; ipc.tx_flags = 0; + ipc.ttl = 0; + ipc.tos = -1; if (replyopts.opt.opt.optlen) { ipc.opt = &replyopts.opt; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index d9c4f113d7093bba7eba2beefc31cd0af4b9bb95..3f858266fa7e33a0941a135ec8a36b36278fdfce 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -189,7 +189,7 @@ EXPORT_SYMBOL(ip_cmsg_recv); int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) { - int err; + int err, val; struct cmsghdr *cmsg; for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { @@ -215,6 +215,24 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc) ipc->addr = info->ipi_spec_dst.s_addr; break; } + case IP_TTL: + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) + return -EINVAL; + val = *(int *)CMSG_DATA(cmsg); + if (val < 1 || val > 255) + return -EINVAL; + ipc->ttl = val; + break; + case IP_TOS: + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) + return -EINVAL; + val = *(int *)CMSG_DATA(cmsg); + if (val < 0 || val > 255) + return -EINVAL; + ipc->tos = val; + ipc->priority = rt_tos2priority(ipc->tos); + break; + default: return -EINVAL; } @@ -609,7 +627,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, inet->nodefrag = val ? 1 : 0; break; case IP_MTU_DISCOVER: - if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_PROBE) + if (val < IP_PMTUDISC_DONT || val > IP_PMTUDISC_INTERFACE) goto e_inval; inet->pmtudisc = val; break; @@ -1034,11 +1052,12 @@ e_inval: * destination in skb->cb[] before dst drop. * This way, receiver doesnt make cache line misses to read rtable. */ -void ipv4_pktinfo_prepare(struct sk_buff *skb) +void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) { struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb); - if (skb_rtable(skb)) { + if ((inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) && + skb_rtable(skb)) { pktinfo->ipi_ifindex = inet_iif(skb); pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb); } else { diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c index c31e3ad98ef28e91eff2679d6976d4c20c055410..42ffbc8d65c65fda2b288008b4aed7670b43ee3f 100644 --- a/net/ipv4/ip_tunnel_core.c +++ b/net/ipv4/ip_tunnel_core.c @@ -116,3 +116,36 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto) return 0; } EXPORT_SYMBOL_GPL(iptunnel_pull_header); + +struct sk_buff *iptunnel_handle_offloads(struct sk_buff *skb, + bool csum_help, + int gso_type_mask) +{ + int err; + + if (likely(!skb->encapsulation)) { + skb_reset_inner_headers(skb); + skb->encapsulation = 1; + } + + if (skb_is_gso(skb)) { + err = skb_unclone(skb, GFP_ATOMIC); + if (unlikely(err)) + goto error; + skb_shinfo(skb)->gso_type |= gso_type_mask; + return skb; + } + + if (skb->ip_summed == CHECKSUM_PARTIAL && csum_help) { + err = skb_checksum_help(skb); + if (unlikely(err)) + goto error; + } else if (skb->ip_summed != CHECKSUM_PARTIAL) + skb->ip_summed = CHECKSUM_NONE; + + return skb; +error: + kfree_skb(skb); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(iptunnel_handle_offloads); diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 6e87f853d03334567906c8782250049c568e4280..5d9c845d288a3d8cce3eb96aeb65384e4a56edbc 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -49,70 +49,6 @@ static struct rtnl_link_ops vti_link_ops __read_mostly; static int vti_net_id __read_mostly; static int vti_tunnel_init(struct net_device *dev); -static int vti_err(struct sk_buff *skb, u32 info) -{ - - /* All the routers (except for Linux) return only - * 8 bytes of packet payload. It means, that precise relaying of - * ICMP in the real Internet is absolutely infeasible. - */ - struct net *net = dev_net(skb->dev); - struct ip_tunnel_net *itn = net_generic(net, vti_net_id); - struct iphdr *iph = (struct iphdr *)skb->data; - const int type = icmp_hdr(skb)->type; - const int code = icmp_hdr(skb)->code; - struct ip_tunnel *t; - int err; - - switch (type) { - default: - case ICMP_PARAMETERPROB: - return 0; - - case ICMP_DEST_UNREACH: - switch (code) { - case ICMP_SR_FAILED: - case ICMP_PORT_UNREACH: - /* Impossible event. */ - return 0; - default: - /* All others are translated to HOST_UNREACH. */ - break; - } - break; - case ICMP_TIME_EXCEEDED: - if (code != ICMP_EXC_TTL) - return 0; - break; - } - - err = -ENOENT; - - t = ip_tunnel_lookup(itn, skb->dev->ifindex, TUNNEL_NO_KEY, - iph->daddr, iph->saddr, 0); - if (t == NULL) - goto out; - - if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) { - ipv4_update_pmtu(skb, dev_net(skb->dev), info, - t->parms.link, 0, IPPROTO_IPIP, 0); - err = 0; - goto out; - } - - err = 0; - if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED) - goto out; - - if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO)) - t->err_count++; - else - t->err_count = 1; - t->err_time = jiffies; -out: - return err; -} - /* We dont digest the packet therefore let the packet pass */ static int vti_rcv(struct sk_buff *skb) { @@ -304,9 +240,8 @@ static void __net_init vti_fb_tunnel_init(struct net_device *dev) iph->ihl = 5; } -static struct xfrm_tunnel vti_handler __read_mostly = { +static struct xfrm_tunnel_notifier vti_handler __read_mostly = { .handler = vti_rcv, - .err_handler = vti_err, .priority = 1, }; diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 7f80fb4b82d3716ad79044a43ed43545f6183957..fe3e9f7f1f0beaf0ba6a8b7b4c788c8a021ba54d 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -220,17 +220,17 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(skb->protocol != htons(ETH_P_IP))) goto tx_error; - if (likely(!skb->encapsulation)) { - skb_reset_inner_headers(skb); - skb->encapsulation = 1; - } + skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP); + if (IS_ERR(skb)) + goto out; ip_tunnel_xmit(skb, dev, tiph, tiph->protocol); return NETDEV_TX_OK; tx_error: - dev->stats.tx_errors++; dev_kfree_skb(skb); +out: + dev->stats.tx_errors++; return NETDEV_TX_OK; } @@ -275,6 +275,7 @@ static const struct net_device_ops ipip_netdev_ops = { #define IPIP_FEATURES (NETIF_F_SG | \ NETIF_F_FRAGLIST | \ NETIF_F_HIGHDMA | \ + NETIF_F_GSO_SOFTWARE | \ NETIF_F_HW_CSUM) static void ipip_tunnel_setup(struct net_device *dev) diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index 1657e39b291f2ae8747e21e944627def23ebfcbb..40d56073cd19d3b3c27372b7dca255469d7e0068 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -36,6 +36,27 @@ config NF_CONNTRACK_PROC_COMPAT If unsure, say Y. +config NF_TABLES_IPV4 + depends on NF_TABLES + tristate "IPv4 nf_tables support" + +config NFT_REJECT_IPV4 + depends on NF_TABLES_IPV4 + tristate "nf_tables IPv4 reject support" + +config NFT_CHAIN_ROUTE_IPV4 + depends on NF_TABLES_IPV4 + tristate "IPv4 nf_tables route chain support" + +config NFT_CHAIN_NAT_IPV4 + depends on NF_TABLES_IPV4 + depends on NF_NAT_IPV4 && NFT_NAT + tristate "IPv4 nf_tables nat chain support" + +config NF_TABLES_ARP + depends on NF_TABLES + tristate "ARP nf_tables support" + config IP_NF_IPTABLES tristate "IP tables support (required for filtering/masq/NAT)" default m if NETFILTER_ADVANCED=n diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index 3622b248b6dd7ad78aa4411243b4b507731d7e2b..19df72b7ba8810e698bf6d50e8eee5c23033b34a 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -27,6 +27,12 @@ obj-$(CONFIG_NF_NAT_SNMP_BASIC) += nf_nat_snmp_basic.o # NAT protocols (nf_nat) obj-$(CONFIG_NF_NAT_PROTO_GRE) += nf_nat_proto_gre.o +obj-$(CONFIG_NF_TABLES_IPV4) += nf_tables_ipv4.o +obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o +obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o +obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o +obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o + # generic IP tables obj-$(CONFIG_IP_NF_IPTABLES) += ip_tables.o diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c index a865f6f9401318663a2e973ac34cf707be4e1452..802ddecb30b8110474da0e0a34c134aceaece43b 100644 --- a/net/ipv4/netfilter/arptable_filter.c +++ b/net/ipv4/netfilter/arptable_filter.c @@ -27,13 +27,14 @@ static const struct xt_table packet_filter = { /* The work comes in here from netfilter.c */ static unsigned int -arptable_filter_hook(unsigned int hook, struct sk_buff *skb, +arptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { const struct net *net = dev_net((in != NULL) ? in : out); - return arpt_do_table(skb, hook, in, out, net->ipv4.arptable_filter); + return arpt_do_table(skb, ops->hooknum, in, out, + net->ipv4.arptable_filter); } static struct nf_hook_ops *arpfilter_ops __read_mostly; diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 0b732efd32e20aa8e22e6c9bd5aa4edfb7e5516d..2510c02c2d2168ffcd27f286c7eab3ca75165f26 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -57,15 +58,21 @@ struct clusterip_config { struct rcu_head rcu; }; -static LIST_HEAD(clusterip_configs); +#ifdef CONFIG_PROC_FS +static const struct file_operations clusterip_proc_fops; +#endif -/* clusterip_lock protects the clusterip_configs list */ -static DEFINE_SPINLOCK(clusterip_lock); +static int clusterip_net_id __read_mostly; + +struct clusterip_net { + struct list_head configs; + /* lock protects the configs list */ + spinlock_t lock; #ifdef CONFIG_PROC_FS -static const struct file_operations clusterip_proc_fops; -static struct proc_dir_entry *clusterip_procdir; + struct proc_dir_entry *procdir; #endif +}; static inline void clusterip_config_get(struct clusterip_config *c) @@ -92,10 +99,13 @@ clusterip_config_put(struct clusterip_config *c) static inline void clusterip_config_entry_put(struct clusterip_config *c) { + struct net *net = dev_net(c->dev); + struct clusterip_net *cn = net_generic(net, clusterip_net_id); + local_bh_disable(); - if (atomic_dec_and_lock(&c->entries, &clusterip_lock)) { + if (atomic_dec_and_lock(&c->entries, &cn->lock)) { list_del_rcu(&c->list); - spin_unlock(&clusterip_lock); + spin_unlock(&cn->lock); local_bh_enable(); dev_mc_del(c->dev, c->clustermac); @@ -113,11 +123,12 @@ clusterip_config_entry_put(struct clusterip_config *c) } static struct clusterip_config * -__clusterip_config_find(__be32 clusterip) +__clusterip_config_find(struct net *net, __be32 clusterip) { struct clusterip_config *c; + struct clusterip_net *cn = net_generic(net, clusterip_net_id); - list_for_each_entry_rcu(c, &clusterip_configs, list) { + list_for_each_entry_rcu(c, &cn->configs, list) { if (c->clusterip == clusterip) return c; } @@ -126,12 +137,12 @@ __clusterip_config_find(__be32 clusterip) } static inline struct clusterip_config * -clusterip_config_find_get(__be32 clusterip, int entry) +clusterip_config_find_get(struct net *net, __be32 clusterip, int entry) { struct clusterip_config *c; rcu_read_lock_bh(); - c = __clusterip_config_find(clusterip); + c = __clusterip_config_find(net, clusterip); if (c) { if (unlikely(!atomic_inc_not_zero(&c->refcount))) c = NULL; @@ -158,6 +169,7 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip, struct net_device *dev) { struct clusterip_config *c; + struct clusterip_net *cn = net_generic(dev_net(dev), clusterip_net_id); c = kzalloc(sizeof(*c), GFP_ATOMIC); if (!c) @@ -180,7 +192,7 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip, /* create proc dir entry */ sprintf(buffer, "%pI4", &ip); c->pde = proc_create_data(buffer, S_IWUSR|S_IRUSR, - clusterip_procdir, + cn->procdir, &clusterip_proc_fops, c); if (!c->pde) { kfree(c); @@ -189,9 +201,9 @@ clusterip_config_init(const struct ipt_clusterip_tgt_info *i, __be32 ip, } #endif - spin_lock_bh(&clusterip_lock); - list_add_rcu(&c->list, &clusterip_configs); - spin_unlock_bh(&clusterip_lock); + spin_lock_bh(&cn->lock); + list_add_rcu(&c->list, &cn->configs); + spin_unlock_bh(&cn->lock); return c; } @@ -370,7 +382,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par) /* FIXME: further sanity checks */ - config = clusterip_config_find_get(e->ip.dst.s_addr, 1); + config = clusterip_config_find_get(par->net, e->ip.dst.s_addr, 1); if (!config) { if (!(cipinfo->flags & CLUSTERIP_FLAG_NEW)) { pr_info("no config found for %pI4, need 'new'\n", @@ -384,7 +396,7 @@ static int clusterip_tg_check(const struct xt_tgchk_param *par) return -EINVAL; } - dev = dev_get_by_name(&init_net, e->ip.iniface); + dev = dev_get_by_name(par->net, e->ip.iniface); if (!dev) { pr_info("no such interface %s\n", e->ip.iniface); @@ -483,7 +495,7 @@ static void arp_print(struct arp_payload *payload) #endif static unsigned int -arp_mangle(unsigned int hook, +arp_mangle(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -492,6 +504,7 @@ arp_mangle(unsigned int hook, struct arphdr *arp = arp_hdr(skb); struct arp_payload *payload; struct clusterip_config *c; + struct net *net = dev_net(in ? in : out); /* we don't care about non-ethernet and non-ipv4 ARP */ if (arp->ar_hrd != htons(ARPHRD_ETHER) || @@ -508,7 +521,7 @@ arp_mangle(unsigned int hook, /* if there is no clusterip configuration for the arp reply's * source ip, we don't want to mangle it */ - c = clusterip_config_find_get(payload->src_ip, 0); + c = clusterip_config_find_get(net, payload->src_ip, 0); if (!c) return NF_ACCEPT; @@ -698,48 +711,75 @@ static const struct file_operations clusterip_proc_fops = { #endif /* CONFIG_PROC_FS */ +static int clusterip_net_init(struct net *net) +{ + struct clusterip_net *cn = net_generic(net, clusterip_net_id); + + INIT_LIST_HEAD(&cn->configs); + + spin_lock_init(&cn->lock); + +#ifdef CONFIG_PROC_FS + cn->procdir = proc_mkdir("ipt_CLUSTERIP", net->proc_net); + if (!cn->procdir) { + pr_err("Unable to proc dir entry\n"); + return -ENOMEM; + } +#endif /* CONFIG_PROC_FS */ + + return 0; +} + +static void clusterip_net_exit(struct net *net) +{ +#ifdef CONFIG_PROC_FS + struct clusterip_net *cn = net_generic(net, clusterip_net_id); + proc_remove(cn->procdir); +#endif +} + +static struct pernet_operations clusterip_net_ops = { + .init = clusterip_net_init, + .exit = clusterip_net_exit, + .id = &clusterip_net_id, + .size = sizeof(struct clusterip_net), +}; + static int __init clusterip_tg_init(void) { int ret; - ret = xt_register_target(&clusterip_tg_reg); + ret = register_pernet_subsys(&clusterip_net_ops); if (ret < 0) return ret; + ret = xt_register_target(&clusterip_tg_reg); + if (ret < 0) + goto cleanup_subsys; + ret = nf_register_hook(&cip_arp_ops); if (ret < 0) goto cleanup_target; -#ifdef CONFIG_PROC_FS - clusterip_procdir = proc_mkdir("ipt_CLUSTERIP", init_net.proc_net); - if (!clusterip_procdir) { - pr_err("Unable to proc dir entry\n"); - ret = -ENOMEM; - goto cleanup_hook; - } -#endif /* CONFIG_PROC_FS */ - pr_info("ClusterIP Version %s loaded successfully\n", CLUSTERIP_VERSION); + return 0; -#ifdef CONFIG_PROC_FS -cleanup_hook: - nf_unregister_hook(&cip_arp_ops); -#endif /* CONFIG_PROC_FS */ cleanup_target: xt_unregister_target(&clusterip_tg_reg); +cleanup_subsys: + unregister_pernet_subsys(&clusterip_net_ops); return ret; } static void __exit clusterip_tg_exit(void) { pr_info("ClusterIP Version %s unloading\n", CLUSTERIP_VERSION); -#ifdef CONFIG_PROC_FS - proc_remove(clusterip_procdir); -#endif + nf_unregister_hook(&cip_arp_ops); xt_unregister_target(&clusterip_tg_reg); + unregister_pernet_subsys(&clusterip_net_ops); /* Wait for completion of call_rcu_bh()'s (clusterip_config_rcu_free) */ rcu_barrier_bh(); diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index b6346bf2fde3bc16f0655e9c9f7c6ade5ffeae8a..01cffeaa0085ede5bb802999fc1f29c9e6ec4b7f 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -297,7 +297,7 @@ synproxy_tg4(struct sk_buff *skb, const struct xt_action_param *par) return XT_CONTINUE; } -static unsigned int ipv4_synproxy_hook(unsigned int hooknum, +static unsigned int ipv4_synproxy_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c index 50af5b45c050c22773db181353e2aee9e02137e3..e08a74a243a85d125ccbd043314f06c6b528b368 100644 --- a/net/ipv4/netfilter/iptable_filter.c +++ b/net/ipv4/netfilter/iptable_filter.c @@ -33,20 +33,21 @@ static const struct xt_table packet_filter = { }; static unsigned int -iptable_filter_hook(unsigned int hook, struct sk_buff *skb, +iptable_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { const struct net *net; - if (hook == NF_INET_LOCAL_OUT && + if (ops->hooknum == NF_INET_LOCAL_OUT && (skb->len < sizeof(struct iphdr) || ip_hdrlen(skb) < sizeof(struct iphdr))) /* root is playing with raw sockets. */ return NF_ACCEPT; net = dev_net((in != NULL) ? in : out); - return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_filter); + return ipt_do_table(skb, ops->hooknum, in, out, + net->ipv4.iptable_filter); } static struct nf_hook_ops *filter_ops __read_mostly; diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c index 0d8cd82e0fadf676bebd1a3cb3b5b559159b3491..6a5079c34bb363c34135e9bed5700a645f15b249 100644 --- a/net/ipv4/netfilter/iptable_mangle.c +++ b/net/ipv4/netfilter/iptable_mangle.c @@ -79,19 +79,19 @@ ipt_mangle_out(struct sk_buff *skb, const struct net_device *out) /* The work comes in here from netfilter.c. */ static unsigned int -iptable_mangle_hook(unsigned int hook, +iptable_mangle_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - if (hook == NF_INET_LOCAL_OUT) + if (ops->hooknum == NF_INET_LOCAL_OUT) return ipt_mangle_out(skb, out); - if (hook == NF_INET_POST_ROUTING) - return ipt_do_table(skb, hook, in, out, + if (ops->hooknum == NF_INET_POST_ROUTING) + return ipt_do_table(skb, ops->hooknum, in, out, dev_net(out)->ipv4.iptable_mangle); /* PREROUTING/INPUT/FORWARD: */ - return ipt_do_table(skb, hook, in, out, + return ipt_do_table(skb, ops->hooknum, in, out, dev_net(in)->ipv4.iptable_mangle); } diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index 683bfaffed65da561cb3a1c2eed598fb8cbe32a1..ee2886126e3dfad44e7c801d82ad2ec4bad621d3 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -61,7 +61,7 @@ static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum, } static unsigned int -nf_nat_ipv4_fn(unsigned int hooknum, +nf_nat_ipv4_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -71,7 +71,7 @@ nf_nat_ipv4_fn(unsigned int hooknum, enum ip_conntrack_info ctinfo; struct nf_conn_nat *nat; /* maniptype == SRC for postrouting. */ - enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); + enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); /* We never see fragments: conntrack defrags on pre-routing * and local-out, and nf_nat_out protects post-routing. @@ -108,7 +108,7 @@ nf_nat_ipv4_fn(unsigned int hooknum, case IP_CT_RELATED_REPLY: if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, - hooknum)) + ops->hooknum)) return NF_DROP; else return NF_ACCEPT; @@ -121,14 +121,14 @@ nf_nat_ipv4_fn(unsigned int hooknum, if (!nf_nat_initialized(ct, maniptype)) { unsigned int ret; - ret = nf_nat_rule_find(skb, hooknum, in, out, ct); + ret = nf_nat_rule_find(skb, ops->hooknum, in, out, ct); if (ret != NF_ACCEPT) return ret; } else { pr_debug("Already setup manip %s for ct %p\n", maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", ct); - if (nf_nat_oif_changed(hooknum, ctinfo, nat, out)) + if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) goto oif_changed; } break; @@ -137,11 +137,11 @@ nf_nat_ipv4_fn(unsigned int hooknum, /* ESTABLISHED */ NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || ctinfo == IP_CT_ESTABLISHED_REPLY); - if (nf_nat_oif_changed(hooknum, ctinfo, nat, out)) + if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) goto oif_changed; } - return nf_nat_packet(ct, ctinfo, hooknum, skb); + return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); oif_changed: nf_ct_kill_acct(ct, ctinfo, skb); @@ -149,7 +149,7 @@ oif_changed: } static unsigned int -nf_nat_ipv4_in(unsigned int hooknum, +nf_nat_ipv4_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -158,7 +158,7 @@ nf_nat_ipv4_in(unsigned int hooknum, unsigned int ret; __be32 daddr = ip_hdr(skb)->daddr; - ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn); + ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn); if (ret != NF_DROP && ret != NF_STOLEN && daddr != ip_hdr(skb)->daddr) skb_dst_drop(skb); @@ -167,7 +167,7 @@ nf_nat_ipv4_in(unsigned int hooknum, } static unsigned int -nf_nat_ipv4_out(unsigned int hooknum, +nf_nat_ipv4_out(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -185,7 +185,7 @@ nf_nat_ipv4_out(unsigned int hooknum, ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; - ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn); + ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn); #ifdef CONFIG_XFRM if (ret != NF_DROP && ret != NF_STOLEN && !(IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) && @@ -207,7 +207,7 @@ nf_nat_ipv4_out(unsigned int hooknum, } static unsigned int -nf_nat_ipv4_local_fn(unsigned int hooknum, +nf_nat_ipv4_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -223,7 +223,7 @@ nf_nat_ipv4_local_fn(unsigned int hooknum, ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; - ret = nf_nat_ipv4_fn(hooknum, skb, in, out, okfn); + ret = nf_nat_ipv4_fn(ops, skb, in, out, okfn); if (ret != NF_DROP && ret != NF_STOLEN && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c index 1f82aea11df63e0683ba6e2c0e85d270534a6d74..b2f7e8f98316d2733e3936ead8dec20da2978f73 100644 --- a/net/ipv4/netfilter/iptable_raw.c +++ b/net/ipv4/netfilter/iptable_raw.c @@ -20,20 +20,20 @@ static const struct xt_table packet_raw = { /* The work comes in here from netfilter.c. */ static unsigned int -iptable_raw_hook(unsigned int hook, struct sk_buff *skb, +iptable_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { const struct net *net; - if (hook == NF_INET_LOCAL_OUT && + if (ops->hooknum == NF_INET_LOCAL_OUT && (skb->len < sizeof(struct iphdr) || ip_hdrlen(skb) < sizeof(struct iphdr))) /* root is playing with raw sockets. */ return NF_ACCEPT; net = dev_net((in != NULL) ? in : out); - return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_raw); + return ipt_do_table(skb, ops->hooknum, in, out, net->ipv4.iptable_raw); } static struct nf_hook_ops *rawtable_ops __read_mostly; diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c index f867a8d38bf7513d38d3da645fb21c2096f5e2a5..c86647ed2078f660cf0e9f8b69957a6f4c79b1f6 100644 --- a/net/ipv4/netfilter/iptable_security.c +++ b/net/ipv4/netfilter/iptable_security.c @@ -37,21 +37,22 @@ static const struct xt_table security_table = { }; static unsigned int -iptable_security_hook(unsigned int hook, struct sk_buff *skb, +iptable_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { const struct net *net; - if (hook == NF_INET_LOCAL_OUT && + if (ops->hooknum == NF_INET_LOCAL_OUT && (skb->len < sizeof(struct iphdr) || ip_hdrlen(skb) < sizeof(struct iphdr))) /* Somebody is playing with raw sockets. */ return NF_ACCEPT; net = dev_net((in != NULL) ? in : out); - return ipt_do_table(skb, hook, in, out, net->ipv4.iptable_security); + return ipt_do_table(skb, ops->hooknum, in, out, + net->ipv4.iptable_security); } static struct nf_hook_ops *sectbl_ops __read_mostly; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 86f5b34a4ed1865cd1438817bfb46f6eb589f61e..ecd8bec411c9719c5ef083d9781e90d0377b88e9 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -92,7 +92,7 @@ static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, return NF_ACCEPT; } -static unsigned int ipv4_helper(unsigned int hooknum, +static unsigned int ipv4_helper(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -121,7 +121,7 @@ static unsigned int ipv4_helper(unsigned int hooknum, ct, ctinfo); } -static unsigned int ipv4_confirm(unsigned int hooknum, +static unsigned int ipv4_confirm(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -147,16 +147,16 @@ out: return nf_conntrack_confirm(skb); } -static unsigned int ipv4_conntrack_in(unsigned int hooknum, +static unsigned int ipv4_conntrack_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return nf_conntrack_in(dev_net(in), PF_INET, hooknum, skb); + return nf_conntrack_in(dev_net(in), PF_INET, ops->hooknum, skb); } -static unsigned int ipv4_conntrack_local(unsigned int hooknum, +static unsigned int ipv4_conntrack_local(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -166,7 +166,7 @@ static unsigned int ipv4_conntrack_local(unsigned int hooknum, if (skb->len < sizeof(struct iphdr) || ip_hdrlen(skb) < sizeof(struct iphdr)) return NF_ACCEPT; - return nf_conntrack_in(dev_net(out), PF_INET, hooknum, skb); + return nf_conntrack_in(dev_net(out), PF_INET, ops->hooknum, skb); } /* Connection tracking may drop packets, but never alters them, so diff --git a/net/ipv4/netfilter/nf_defrag_ipv4.c b/net/ipv4/netfilter/nf_defrag_ipv4.c index 742815518b0fd5ea78ec4205bfee76a8a804c88d..12e13bd82b5bba4fdd183d5ba2cda098a1c0c683 100644 --- a/net/ipv4/netfilter/nf_defrag_ipv4.c +++ b/net/ipv4/netfilter/nf_defrag_ipv4.c @@ -60,7 +60,7 @@ static enum ip_defrag_users nf_ct_defrag_user(unsigned int hooknum, return IP_DEFRAG_CONNTRACK_OUT + zone; } -static unsigned int ipv4_conntrack_defrag(unsigned int hooknum, +static unsigned int ipv4_conntrack_defrag(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -83,7 +83,9 @@ static unsigned int ipv4_conntrack_defrag(unsigned int hooknum, #endif /* Gather fragments. */ if (ip_is_fragment(ip_hdr(skb))) { - enum ip_defrag_users user = nf_ct_defrag_user(hooknum, skb); + enum ip_defrag_users user = + nf_ct_defrag_user(ops->hooknum, skb); + if (nf_ct_ipv4_gather_frags(skb, user)) return NF_STOLEN; } diff --git a/net/ipv4/netfilter/nf_tables_arp.c b/net/ipv4/netfilter/nf_tables_arp.c new file mode 100644 index 0000000000000000000000000000000000000000..3e67ef1c676f6a311242b834f8092cbd152040e8 --- /dev/null +++ b/net/ipv4/netfilter/nf_tables_arp.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2008-2010 Patrick McHardy + * Copyright (c) 2013 Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include + +static struct nft_af_info nft_af_arp __read_mostly = { + .family = NFPROTO_ARP, + .nhooks = NF_ARP_NUMHOOKS, + .owner = THIS_MODULE, +}; + +static int nf_tables_arp_init_net(struct net *net) +{ + net->nft.arp = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); + if (net->nft.arp== NULL) + return -ENOMEM; + + memcpy(net->nft.arp, &nft_af_arp, sizeof(nft_af_arp)); + + if (nft_register_afinfo(net, net->nft.arp) < 0) + goto err; + + return 0; +err: + kfree(net->nft.arp); + return -ENOMEM; +} + +static void nf_tables_arp_exit_net(struct net *net) +{ + nft_unregister_afinfo(net->nft.arp); + kfree(net->nft.arp); +} + +static struct pernet_operations nf_tables_arp_net_ops = { + .init = nf_tables_arp_init_net, + .exit = nf_tables_arp_exit_net, +}; + +static unsigned int +nft_do_chain_arp(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct nft_pktinfo pkt; + + nft_set_pktinfo(&pkt, ops, skb, in, out); + + return nft_do_chain_pktinfo(&pkt, ops); +} + +static struct nf_chain_type filter_arp = { + .family = NFPROTO_ARP, + .name = "filter", + .type = NFT_CHAIN_T_DEFAULT, + .hook_mask = (1 << NF_ARP_IN) | + (1 << NF_ARP_OUT) | + (1 << NF_ARP_FORWARD), + .fn = { + [NF_ARP_IN] = nft_do_chain_arp, + [NF_ARP_OUT] = nft_do_chain_arp, + [NF_ARP_FORWARD] = nft_do_chain_arp, + }, +}; + +static int __init nf_tables_arp_init(void) +{ + int ret; + + nft_register_chain_type(&filter_arp); + ret = register_pernet_subsys(&nf_tables_arp_net_ops); + if (ret < 0) + nft_unregister_chain_type(&filter_arp); + + return ret; +} + +static void __exit nf_tables_arp_exit(void) +{ + unregister_pernet_subsys(&nf_tables_arp_net_ops); + nft_unregister_chain_type(&filter_arp); +} + +module_init(nf_tables_arp_init); +module_exit(nf_tables_arp_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_FAMILY(3); /* NFPROTO_ARP */ diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c new file mode 100644 index 0000000000000000000000000000000000000000..0f4cbfeb19bd07042aa8bc7f50ec9af15a2c1b2a --- /dev/null +++ b/net/ipv4/netfilter/nf_tables_ipv4.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2008 Patrick McHardy + * Copyright (c) 2012-2013 Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct nft_pktinfo pkt; + + if (unlikely(skb->len < sizeof(struct iphdr) || + ip_hdr(skb)->ihl < sizeof(struct iphdr) / 4)) { + if (net_ratelimit()) + pr_info("nf_tables_ipv4: ignoring short SOCK_RAW " + "packet\n"); + return NF_ACCEPT; + } + nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); + + return nft_do_chain_pktinfo(&pkt, ops); +} + +static struct nft_af_info nft_af_ipv4 __read_mostly = { + .family = NFPROTO_IPV4, + .nhooks = NF_INET_NUMHOOKS, + .owner = THIS_MODULE, + .hooks = { + [NF_INET_LOCAL_OUT] = nft_ipv4_output, + }, +}; + +static int nf_tables_ipv4_init_net(struct net *net) +{ + net->nft.ipv4 = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); + if (net->nft.ipv4 == NULL) + return -ENOMEM; + + memcpy(net->nft.ipv4, &nft_af_ipv4, sizeof(nft_af_ipv4)); + + if (nft_register_afinfo(net, net->nft.ipv4) < 0) + goto err; + + return 0; +err: + kfree(net->nft.ipv4); + return -ENOMEM; +} + +static void nf_tables_ipv4_exit_net(struct net *net) +{ + nft_unregister_afinfo(net->nft.ipv4); + kfree(net->nft.ipv4); +} + +static struct pernet_operations nf_tables_ipv4_net_ops = { + .init = nf_tables_ipv4_init_net, + .exit = nf_tables_ipv4_exit_net, +}; + +static unsigned int +nft_do_chain_ipv4(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct nft_pktinfo pkt; + + nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); + + return nft_do_chain_pktinfo(&pkt, ops); +} + +static struct nf_chain_type filter_ipv4 = { + .family = NFPROTO_IPV4, + .name = "filter", + .type = NFT_CHAIN_T_DEFAULT, + .hook_mask = (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_FORWARD) | + (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_POST_ROUTING), + .fn = { + [NF_INET_LOCAL_IN] = nft_do_chain_ipv4, + [NF_INET_LOCAL_OUT] = nft_ipv4_output, + [NF_INET_FORWARD] = nft_do_chain_ipv4, + [NF_INET_PRE_ROUTING] = nft_do_chain_ipv4, + [NF_INET_POST_ROUTING] = nft_do_chain_ipv4, + }, +}; + +static int __init nf_tables_ipv4_init(void) +{ + nft_register_chain_type(&filter_ipv4); + return register_pernet_subsys(&nf_tables_ipv4_net_ops); +} + +static void __exit nf_tables_ipv4_exit(void) +{ + unregister_pernet_subsys(&nf_tables_ipv4_net_ops); + nft_unregister_chain_type(&filter_ipv4); +} + +module_init(nf_tables_ipv4_init); +module_exit(nf_tables_ipv4_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_FAMILY(AF_INET); diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c new file mode 100644 index 0000000000000000000000000000000000000000..cf2c792cd971cc757fb3986c0956c0841922d385 --- /dev/null +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * Copyright (c) 2012 Pablo Neira Ayuso + * Copyright (c) 2012 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * NAT chains + */ + +static unsigned int nf_nat_fn(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + struct nf_conn_nat *nat; + enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); + struct nft_pktinfo pkt; + unsigned int ret; + + if (ct == NULL || nf_ct_is_untracked(ct)) + return NF_ACCEPT; + + NF_CT_ASSERT(!(ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET))); + + nat = nfct_nat(ct); + if (nat == NULL) { + /* Conntrack module was loaded late, can't add extension. */ + if (nf_ct_is_confirmed(ct)) + return NF_ACCEPT; + nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); + if (nat == NULL) + return NF_ACCEPT; + } + + switch (ctinfo) { + case IP_CT_RELATED: + case IP_CT_RELATED + IP_CT_IS_REPLY: + if (ip_hdr(skb)->protocol == IPPROTO_ICMP) { + if (!nf_nat_icmp_reply_translation(skb, ct, ctinfo, + ops->hooknum)) + return NF_DROP; + else + return NF_ACCEPT; + } + /* Fall through */ + case IP_CT_NEW: + if (nf_nat_initialized(ct, maniptype)) + break; + + nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); + + ret = nft_do_chain_pktinfo(&pkt, ops); + if (ret != NF_ACCEPT) + return ret; + if (!nf_nat_initialized(ct, maniptype)) { + ret = nf_nat_alloc_null_binding(ct, ops->hooknum); + if (ret != NF_ACCEPT) + return ret; + } + default: + break; + } + + return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); +} + +static unsigned int nf_nat_prerouting(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + __be32 daddr = ip_hdr(skb)->daddr; + unsigned int ret; + + ret = nf_nat_fn(ops, skb, in, out, okfn); + if (ret != NF_DROP && ret != NF_STOLEN && + ip_hdr(skb)->daddr != daddr) { + skb_dst_drop(skb); + } + return ret; +} + +static unsigned int nf_nat_postrouting(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + enum ip_conntrack_info ctinfo __maybe_unused; + const struct nf_conn *ct __maybe_unused; + unsigned int ret; + + ret = nf_nat_fn(ops, skb, in, out, okfn); +#ifdef CONFIG_XFRM + if (ret != NF_DROP && ret != NF_STOLEN && + (ct = nf_ct_get(skb, &ctinfo)) != NULL) { + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + + if (ct->tuplehash[dir].tuple.src.u3.ip != + ct->tuplehash[!dir].tuple.dst.u3.ip || + ct->tuplehash[dir].tuple.src.u.all != + ct->tuplehash[!dir].tuple.dst.u.all) + return nf_xfrm_me_harder(skb, AF_INET) == 0 ? + ret : NF_DROP; + } +#endif + return ret; +} + +static unsigned int nf_nat_output(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + enum ip_conntrack_info ctinfo; + const struct nf_conn *ct; + unsigned int ret; + + ret = nf_nat_fn(ops, skb, in, out, okfn); + if (ret != NF_DROP && ret != NF_STOLEN && + (ct = nf_ct_get(skb, &ctinfo)) != NULL) { + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + + if (ct->tuplehash[dir].tuple.dst.u3.ip != + ct->tuplehash[!dir].tuple.src.u3.ip) { + if (ip_route_me_harder(skb, RTN_UNSPEC)) + ret = NF_DROP; + } +#ifdef CONFIG_XFRM + else if (ct->tuplehash[dir].tuple.dst.u.all != + ct->tuplehash[!dir].tuple.src.u.all) + if (nf_xfrm_me_harder(skb, AF_INET)) + ret = NF_DROP; +#endif + } + return ret; +} + +static struct nf_chain_type nft_chain_nat_ipv4 = { + .family = NFPROTO_IPV4, + .name = "nat", + .type = NFT_CHAIN_T_NAT, + .hook_mask = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_LOCAL_IN), + .fn = { + [NF_INET_PRE_ROUTING] = nf_nat_prerouting, + [NF_INET_POST_ROUTING] = nf_nat_postrouting, + [NF_INET_LOCAL_OUT] = nf_nat_output, + [NF_INET_LOCAL_IN] = nf_nat_fn, + }, + .me = THIS_MODULE, +}; + +static int __init nft_chain_nat_init(void) +{ + int err; + + err = nft_register_chain_type(&nft_chain_nat_ipv4); + if (err < 0) + return err; + + return 0; +} + +static void __exit nft_chain_nat_exit(void) +{ + nft_unregister_chain_type(&nft_chain_nat_ipv4); +} + +module_init(nft_chain_nat_init); +module_exit(nft_chain_nat_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_CHAIN(AF_INET, "nat"); diff --git a/net/ipv4/netfilter/nft_chain_route_ipv4.c b/net/ipv4/netfilter/nft_chain_route_ipv4.c new file mode 100644 index 0000000000000000000000000000000000000000..4e6bf9a3d7aa21a603f6c93f5a3136a623f5ec7f --- /dev/null +++ b/net/ipv4/netfilter/nft_chain_route_ipv4.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2008 Patrick McHardy + * Copyright (c) 2012 Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + unsigned int ret; + struct nft_pktinfo pkt; + u32 mark; + __be32 saddr, daddr; + u_int8_t tos; + const struct iphdr *iph; + + /* root is playing with raw sockets. */ + if (skb->len < sizeof(struct iphdr) || + ip_hdrlen(skb) < sizeof(struct iphdr)) + return NF_ACCEPT; + + nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); + + mark = skb->mark; + iph = ip_hdr(skb); + saddr = iph->saddr; + daddr = iph->daddr; + tos = iph->tos; + + ret = nft_do_chain_pktinfo(&pkt, ops); + if (ret != NF_DROP && ret != NF_QUEUE) { + iph = ip_hdr(skb); + + if (iph->saddr != saddr || + iph->daddr != daddr || + skb->mark != mark || + iph->tos != tos) + if (ip_route_me_harder(skb, RTN_UNSPEC)) + ret = NF_DROP; + } + return ret; +} + +static struct nf_chain_type nft_chain_route_ipv4 = { + .family = NFPROTO_IPV4, + .name = "route", + .type = NFT_CHAIN_T_ROUTE, + .hook_mask = (1 << NF_INET_LOCAL_OUT), + .fn = { + [NF_INET_LOCAL_OUT] = nf_route_table_hook, + }, + .me = THIS_MODULE, +}; + +static int __init nft_chain_route_init(void) +{ + return nft_register_chain_type(&nft_chain_route_ipv4); +} + +static void __exit nft_chain_route_exit(void) +{ + nft_unregister_chain_type(&nft_chain_route_ipv4); +} + +module_init(nft_chain_route_init); +module_exit(nft_chain_route_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_CHAIN(AF_INET, "route"); diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c new file mode 100644 index 0000000000000000000000000000000000000000..fff5ba1a33b76321f70d1365d072f7330d524ea9 --- /dev/null +++ b/net/ipv4/netfilter/nft_reject_ipv4.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_reject { + enum nft_reject_types type:8; + u8 icmp_code; +}; + +static void nft_reject_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + struct nft_reject *priv = nft_expr_priv(expr); + + switch (priv->type) { + case NFT_REJECT_ICMP_UNREACH: + icmp_send(pkt->skb, ICMP_DEST_UNREACH, priv->icmp_code, 0); + break; + case NFT_REJECT_TCP_RST: + break; + } + + data[NFT_REG_VERDICT].verdict = NF_DROP; +} + +static const struct nla_policy nft_reject_policy[NFTA_REJECT_MAX + 1] = { + [NFTA_REJECT_TYPE] = { .type = NLA_U32 }, + [NFTA_REJECT_ICMP_CODE] = { .type = NLA_U8 }, +}; + +static int nft_reject_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_reject *priv = nft_expr_priv(expr); + + if (tb[NFTA_REJECT_TYPE] == NULL) + return -EINVAL; + + priv->type = ntohl(nla_get_be32(tb[NFTA_REJECT_TYPE])); + switch (priv->type) { + case NFT_REJECT_ICMP_UNREACH: + if (tb[NFTA_REJECT_ICMP_CODE] == NULL) + return -EINVAL; + priv->icmp_code = nla_get_u8(tb[NFTA_REJECT_ICMP_CODE]); + case NFT_REJECT_TCP_RST: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nft_reject_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_reject *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_REJECT_TYPE, priv->type)) + goto nla_put_failure; + + switch (priv->type) { + case NFT_REJECT_ICMP_UNREACH: + if (nla_put_u8(skb, NFTA_REJECT_ICMP_CODE, priv->icmp_code)) + goto nla_put_failure; + break; + } + + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_reject_type; +static const struct nft_expr_ops nft_reject_ops = { + .type = &nft_reject_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_reject)), + .eval = nft_reject_eval, + .init = nft_reject_init, + .dump = nft_reject_dump, +}; + +static struct nft_expr_type nft_reject_type __read_mostly = { + .name = "reject", + .ops = &nft_reject_ops, + .policy = nft_reject_policy, + .maxattr = NFTA_REJECT_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_reject_module_init(void) +{ + return nft_register_expr(&nft_reject_type); +} + +static void __exit nft_reject_module_exit(void) +{ + nft_unregister_expr(&nft_reject_type); +} + +module_init(nft_reject_module_init); +module_exit(nft_reject_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_EXPR("reject"); diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index d7d9882d4caea169964a58e294ffe6c73a99d36c..9afbdb19f4a2f5dc1bccb6cf5d73db795fbf5f03 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -202,15 +202,14 @@ static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident) #if IS_ENABLED(CONFIG_IPV6) } else if (skb->protocol == htons(ETH_P_IPV6) && sk->sk_family == AF_INET6) { - struct ipv6_pinfo *np = inet6_sk(sk); pr_debug("found: %p: num=%d, daddr=%pI6c, dif=%d\n", sk, (int) isk->inet_num, - &inet6_sk(sk)->rcv_saddr, + &sk->sk_v6_rcv_saddr, sk->sk_bound_dev_if); - if (!ipv6_addr_any(&np->rcv_saddr) && - !ipv6_addr_equal(&np->rcv_saddr, + if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr) && + !ipv6_addr_equal(&sk->sk_v6_rcv_saddr, &ipv6_hdr(skb)->daddr)) continue; #endif @@ -237,11 +236,11 @@ static void inet_get_ping_group_range_net(struct net *net, kgid_t *low, unsigned int seq; do { - seq = read_seqbegin(&sysctl_local_ports.lock); + seq = read_seqbegin(&net->ipv4.sysctl_local_ports.lock); *low = data[0]; *high = data[1]; - } while (read_seqretry(&sysctl_local_ports.lock, seq)); + } while (read_seqretry(&net->ipv4.sysctl_local_ports.lock, seq)); } @@ -362,7 +361,7 @@ static void ping_set_saddr(struct sock *sk, struct sockaddr *saddr) } else if (saddr->sa_family == AF_INET6) { struct sockaddr_in6 *addr = (struct sockaddr_in6 *) saddr; struct ipv6_pinfo *np = inet6_sk(sk); - np->rcv_saddr = np->saddr = addr->sin6_addr; + sk->sk_v6_rcv_saddr = np->saddr = addr->sin6_addr; #endif } } @@ -376,7 +375,7 @@ static void ping_clear_saddr(struct sock *sk, int dif) #if IS_ENABLED(CONFIG_IPV6) } else if (sk->sk_family == AF_INET6) { struct ipv6_pinfo *np = inet6_sk(sk); - memset(&np->rcv_saddr, 0, sizeof(np->rcv_saddr)); + memset(&sk->sk_v6_rcv_saddr, 0, sizeof(sk->sk_v6_rcv_saddr)); memset(&np->saddr, 0, sizeof(np->saddr)); #endif } @@ -416,10 +415,12 @@ int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) (int)sk->sk_bound_dev_if); err = 0; - if ((sk->sk_family == AF_INET && isk->inet_rcv_saddr) || - (sk->sk_family == AF_INET6 && - !ipv6_addr_any(&inet6_sk(sk)->rcv_saddr))) + if (sk->sk_family == AF_INET && isk->inet_rcv_saddr) sk->sk_userlocks |= SOCK_BINDADDR_LOCK; +#if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family == AF_INET6 && !ipv6_addr_any(&sk->sk_v6_rcv_saddr)) + sk->sk_userlocks |= SOCK_BINDADDR_LOCK; +#endif if (snum) sk->sk_userlocks |= SOCK_BINDPORT_LOCK; @@ -429,7 +430,7 @@ int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family == AF_INET6) - memset(&inet6_sk(sk)->daddr, 0, sizeof(inet6_sk(sk)->daddr)); + memset(&sk->sk_v6_daddr, 0, sizeof(sk->sk_v6_daddr)); #endif sk_dst_reset(sk); @@ -713,6 +714,8 @@ int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.opt = NULL; ipc.oif = sk->sk_bound_dev_if; ipc.tx_flags = 0; + ipc.ttl = 0; + ipc.tos = -1; sock_tx_timestamp(sk, &ipc.tx_flags); @@ -744,7 +747,7 @@ int ping_v4_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, return -EINVAL; faddr = ipc.opt->opt.faddr; } - tos = RT_TOS(inet->tos); + tos = get_rttos(&ipc, inet); if (sock_flag(sk, SOCK_LOCALROUTE) || (msg->msg_flags & MSG_DONTROUTE) || (ipc.opt && ipc.opt->opt.is_strictroute)) { diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 193db03540ad7c8dabd2f88be81fb5fe78eb8eab..41e1d2845c8f6690b5588888134bd80e49fc5dcf 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -299,7 +299,7 @@ static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb) { /* Charge it to the socket. */ - ipv4_pktinfo_prepare(skb); + ipv4_pktinfo_prepare(sk, skb); if (sock_queue_rcv_skb(sk, skb) < 0) { kfree_skb(skb); return NET_RX_DROP; @@ -519,6 +519,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.addr = inet->inet_saddr; ipc.opt = NULL; ipc.tx_flags = 0; + ipc.ttl = 0; + ipc.tos = -1; ipc.oif = sk->sk_bound_dev_if; if (msg->msg_controllen) { @@ -558,7 +560,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, daddr = ipc.opt->opt.faddr; } } - tos = RT_CONN_FLAGS(sk); + tos = get_rtconn_flags(&ipc, sk); if (msg->msg_flags & MSG_DONTROUTE) tos |= RTO_ONLINK; diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6011615e810d397ce8ffa5daeca58ce6921e85f0..f428935c50dba64b7cf7babd955978ed238fd928 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -295,7 +295,7 @@ static int rt_cpu_seq_show(struct seq_file *seq, void *v) seq_printf(seq,"%08x %08x %08x %08x %08x %08x %08x %08x " " %08x %08x %08x %08x %08x %08x %08x %08x %08x \n", dst_entries_get_slow(&ipv4_dst_ops), - st->in_hit, + 0, /* st->in_hit */ st->in_slow_tot, st->in_slow_mc, st->in_no_route, @@ -303,16 +303,16 @@ static int rt_cpu_seq_show(struct seq_file *seq, void *v) st->in_martian_dst, st->in_martian_src, - st->out_hit, + 0, /* st->out_hit */ st->out_slow_tot, st->out_slow_mc, - st->gc_total, - st->gc_ignored, - st->gc_goal_miss, - st->gc_dst_overflow, - st->in_hlist_search, - st->out_hlist_search + 0, /* st->gc_total */ + 0, /* st->gc_ignored */ + 0, /* st->gc_goal_miss */ + 0, /* st->gc_dst_overflow */ + 0, /* st->in_hlist_search */ + 0 /* st->out_hlist_search */ ); return 0; } @@ -1036,6 +1036,10 @@ void ipv4_sk_update_pmtu(struct sk_buff *skb, struct sock *sk, u32 mtu) bool new = false; bh_lock_sock(sk); + + if (!ip_sk_accept_pmtu(sk)) + goto out; + rt = (struct rtable *) __sk_dst_get(sk); if (sock_owned_by_user(sk) || !rt) { diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 14a15c49129df8984076c05c4dd757012fed03b3..b95331e6c077cea0ff215702087fa14acd00abaf 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -25,15 +25,7 @@ extern int sysctl_tcp_syncookies; -__u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS]; -EXPORT_SYMBOL(syncookie_secret); - -static __init int init_syncookies(void) -{ - get_random_bytes(syncookie_secret, sizeof(syncookie_secret)); - return 0; -} -__initcall(init_syncookies); +static u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS]; #define COOKIEBITS 24 /* Upper bits store count */ #define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1) @@ -44,8 +36,11 @@ static DEFINE_PER_CPU(__u32 [16 + 5 + SHA_WORKSPACE_WORDS], static u32 cookie_hash(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport, u32 count, int c) { - __u32 *tmp = __get_cpu_var(ipv4_cookie_scratch); + __u32 *tmp; + + net_get_random_once(syncookie_secret, sizeof(syncookie_secret)); + tmp = __get_cpu_var(ipv4_cookie_scratch); memcpy(tmp + 4, syncookie_secret[c], sizeof(syncookie_secret[c])); tmp[0] = (__force u32)saddr; tmp[1] = (__force u32)daddr; @@ -89,8 +84,7 @@ __u32 cookie_init_timestamp(struct request_sock *req) static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport, - __be16 dport, __u32 sseq, __u32 count, - __u32 data) + __be16 dport, __u32 sseq, __u32 data) { /* * Compute the secure sequence number. @@ -102,7 +96,7 @@ static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport, * As an extra hack, we add a small "data" value that encodes the * MSS into the second hash value. */ - + u32 count = tcp_cookie_time(); return (cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq + (count << COOKIEBITS) + ((cookie_hash(saddr, daddr, sport, dport, count, 1) + data) @@ -114,22 +108,21 @@ static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport, * If the syncookie is bad, the data returned will be out of * range. This must be checked by the caller. * - * The count value used to generate the cookie must be within - * "maxdiff" if the current (passed-in) "count". The return value - * is (__u32)-1 if this test fails. + * The count value used to generate the cookie must be less than + * MAX_SYNCOOKIE_AGE minutes in the past. + * The return value (__u32)-1 if this test fails. */ static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr, - __be16 sport, __be16 dport, __u32 sseq, - __u32 count, __u32 maxdiff) + __be16 sport, __be16 dport, __u32 sseq) { - __u32 diff; + u32 diff, count = tcp_cookie_time(); /* Strip away the layers from the cookie */ cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq; /* Cookie is now reduced to (count * 2^24) ^ (hash % 2^24) */ diff = (count - (cookie >> COOKIEBITS)) & ((__u32) - 1 >> COOKIEBITS); - if (diff >= maxdiff) + if (diff >= MAX_SYNCOOKIE_AGE) return (__u32)-1; return (cookie - @@ -138,22 +131,22 @@ static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr, } /* - * MSS Values are taken from the 2009 paper - * 'Measuring TCP Maximum Segment Size' by S. Alcock and R. Nelson: - * - values 1440 to 1460 accounted for 80% of observed mss values - * - values outside the 536-1460 range are rare (<0.2%). + * MSS Values are chosen based on the 2011 paper + * 'An Analysis of TCP Maximum Segement Sizes' by S. Alcock and R. Nelson. + * Values .. + * .. lower than 536 are rare (< 0.2%) + * .. between 537 and 1299 account for less than < 1.5% of observed values + * .. in the 1300-1349 range account for about 15 to 20% of observed mss values + * .. exceeding 1460 are very rare (< 0.04%) * - * Table must be sorted. + * 1460 is the single most frequently announced mss value (30 to 46% depending + * on monitor location). Table must be sorted. */ static __u16 const msstab[] = { - 64, - 512, 536, - 1024, - 1440, + 1300, + 1440, /* 1440, 1452: PPPoE */ 1460, - 4312, - 8960, }; /* @@ -173,7 +166,7 @@ u32 __cookie_v4_init_sequence(const struct iphdr *iph, const struct tcphdr *th, return secure_tcp_syn_cookie(iph->saddr, iph->daddr, th->source, th->dest, ntohl(th->seq), - jiffies / (HZ * 60), mssind); + mssind); } EXPORT_SYMBOL_GPL(__cookie_v4_init_sequence); @@ -188,13 +181,6 @@ __u32 cookie_v4_init_sequence(struct sock *sk, struct sk_buff *skb, __u16 *mssp) return __cookie_v4_init_sequence(iph, th, mssp); } -/* - * This (misnamed) value is the age of syncookie which is permitted. - * Its ideal value should be dependent on TCP_TIMEOUT_INIT and - * sysctl_tcp_retries1. It's a rather complicated formula (exponential - * backoff) to compute at runtime so it's currently hardcoded here. - */ -#define COUNTER_TRIES 4 /* * Check if a ack sequence number is a valid syncookie. * Return the decoded mss if it is, or 0 if not. @@ -204,9 +190,7 @@ int __cookie_v4_check(const struct iphdr *iph, const struct tcphdr *th, { __u32 seq = ntohl(th->seq) - 1; __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr, - th->source, th->dest, seq, - jiffies / (HZ * 60), - COUNTER_TRIES); + th->source, th->dest, seq); return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0; } @@ -315,10 +299,10 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, treq->rcv_isn = ntohl(th->seq) - 1; treq->snt_isn = cookie; req->mss = mss; - ireq->loc_port = th->dest; - ireq->rmt_port = th->source; - ireq->loc_addr = ip_hdr(skb)->daddr; - ireq->rmt_addr = ip_hdr(skb)->saddr; + ireq->ir_num = ntohs(th->dest); + ireq->ir_rmt_port = th->source; + ireq->ir_loc_addr = ip_hdr(skb)->daddr; + ireq->ir_rmt_addr = ip_hdr(skb)->saddr; ireq->ecn_ok = ecn_ok; ireq->snd_wscale = tcp_opt.snd_wscale; ireq->sack_ok = tcp_opt.sack_ok; @@ -358,8 +342,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, flowi4_init_output(&fl4, sk->sk_bound_dev_if, sk->sk_mark, RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP, inet_sk_flowi_flags(sk), - (opt && opt->srr) ? opt->faddr : ireq->rmt_addr, - ireq->loc_addr, th->source, th->dest); + (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr, + ireq->ir_loc_addr, th->source, th->dest); security_req_classify_flow(req, flowi4_to_flowi(&fl4)); rt = ip_route_output_key(sock_net(sk), &fl4); if (IS_ERR(rt)) { diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 540279f4c531be079e45c027aa11d37920c14a88..3d69ec8dac578ee101e2c4b9467b3be83a430ac6 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -43,12 +43,12 @@ static int ip_ping_group_range_min[] = { 0, 0 }; static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX }; /* Update system visible IP port range */ -static void set_local_port_range(int range[2]) +static void set_local_port_range(struct net *net, int range[2]) { - write_seqlock(&sysctl_local_ports.lock); - sysctl_local_ports.range[0] = range[0]; - sysctl_local_ports.range[1] = range[1]; - write_sequnlock(&sysctl_local_ports.lock); + write_seqlock(&net->ipv4.sysctl_local_ports.lock); + net->ipv4.sysctl_local_ports.range[0] = range[0]; + net->ipv4.sysctl_local_ports.range[1] = range[1]; + write_sequnlock(&net->ipv4.sysctl_local_ports.lock); } /* Validate changes from /proc interface. */ @@ -56,6 +56,8 @@ static int ipv4_local_port_range(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { + struct net *net = + container_of(table->data, struct net, ipv4.sysctl_local_ports.range); int ret; int range[2]; struct ctl_table tmp = { @@ -66,14 +68,15 @@ static int ipv4_local_port_range(struct ctl_table *table, int write, .extra2 = &ip_local_port_range_max, }; - inet_get_local_port_range(range, range + 1); + inet_get_local_port_range(net, &range[0], &range[1]); + ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos); if (write && ret == 0) { if (range[1] < range[0]) ret = -EINVAL; else - set_local_port_range(range); + set_local_port_range(net, range); } return ret; @@ -83,23 +86,27 @@ static int ipv4_local_port_range(struct ctl_table *table, int write, static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low, kgid_t *high) { kgid_t *data = table->data; + struct net *net = + container_of(table->data, struct net, ipv4.sysctl_ping_group_range); unsigned int seq; do { - seq = read_seqbegin(&sysctl_local_ports.lock); + seq = read_seqbegin(&net->ipv4.sysctl_local_ports.lock); *low = data[0]; *high = data[1]; - } while (read_seqretry(&sysctl_local_ports.lock, seq)); + } while (read_seqretry(&net->ipv4.sysctl_local_ports.lock, seq)); } /* Update system visible IP port range */ static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t high) { kgid_t *data = table->data; - write_seqlock(&sysctl_local_ports.lock); + struct net *net = + container_of(table->data, struct net, ipv4.sysctl_ping_group_range); + write_seqlock(&net->ipv4.sysctl_local_ports.lock); data[0] = low; data[1] = high; - write_sequnlock(&sysctl_local_ports.lock); + write_sequnlock(&net->ipv4.sysctl_local_ports.lock); } /* Validate changes from /proc interface. */ @@ -193,49 +200,6 @@ static int proc_allowed_congestion_control(struct ctl_table *ctl, return ret; } -static int ipv4_tcp_mem(struct ctl_table *ctl, int write, - void __user *buffer, size_t *lenp, - loff_t *ppos) -{ - int ret; - unsigned long vec[3]; - struct net *net = current->nsproxy->net_ns; -#ifdef CONFIG_MEMCG_KMEM - struct mem_cgroup *memcg; -#endif - - struct ctl_table tmp = { - .data = &vec, - .maxlen = sizeof(vec), - .mode = ctl->mode, - }; - - if (!write) { - ctl->data = &net->ipv4.sysctl_tcp_mem; - return proc_doulongvec_minmax(ctl, write, buffer, lenp, ppos); - } - - ret = proc_doulongvec_minmax(&tmp, write, buffer, lenp, ppos); - if (ret) - return ret; - -#ifdef CONFIG_MEMCG_KMEM - rcu_read_lock(); - memcg = mem_cgroup_from_task(current); - - tcp_prot_mem(memcg, vec[0], 0); - tcp_prot_mem(memcg, vec[1], 1); - tcp_prot_mem(memcg, vec[2], 2); - rcu_read_unlock(); -#endif - - net->ipv4.sysctl_tcp_mem[0] = vec[0]; - net->ipv4.sysctl_tcp_mem[1] = vec[1]; - net->ipv4.sysctl_tcp_mem[2] = vec[2]; - - return 0; -} - static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -267,6 +231,11 @@ static int proc_tcp_fastopen_key(struct ctl_table *ctl, int write, ret = -EINVAL; goto bad_key; } + /* Generate a dummy secret but don't publish it. This + * is needed so we don't regenerate a new key on the + * first invocation of tcp_fastopen_cookie_gen + */ + tcp_fastopen_init_key_once(false); tcp_fastopen_reset_cipher(user_key, TCP_FASTOPEN_KEY_LENGTH); } @@ -474,13 +443,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, - { - .procname = "ip_local_port_range", - .data = &sysctl_local_ports.range, - .maxlen = sizeof(sysctl_local_ports.range), - .mode = 0644, - .proc_handler = ipv4_local_port_range, - }, { .procname = "ip_local_reserved_ports", .data = NULL, /* initialized in sysctl_ipv4_init */ @@ -551,6 +513,13 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "tcp_mem", + .maxlen = sizeof(sysctl_tcp_mem), + .data = &sysctl_tcp_mem, + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + }, { .procname = "tcp_wmem", .data = &sysctl_tcp_wmem, @@ -731,13 +700,6 @@ static struct ctl_table ipv4_table[] = { .mode = 0644, .proc_handler = proc_allowed_congestion_control, }, - { - .procname = "tcp_max_ssthresh", - .data = &sysctl_tcp_max_ssthresh, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { .procname = "tcp_thin_linear_timeouts", .data = &sysctl_tcp_thin_linear_timeouts, @@ -854,10 +816,11 @@ static struct ctl_table ipv4_net_table[] = { .proc_handler = proc_dointvec }, { - .procname = "tcp_mem", - .maxlen = sizeof(init_net.ipv4.sysctl_tcp_mem), + .procname = "ip_local_port_range", + .maxlen = sizeof(init_net.ipv4.sysctl_local_ports.range), + .data = &init_net.ipv4.sysctl_local_ports.range, .mode = 0644, - .proc_handler = ipv4_tcp_mem, + .proc_handler = ipv4_local_port_range, }, { } }; @@ -868,30 +831,15 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) table = ipv4_net_table; if (!net_eq(net, &init_net)) { + int i; + table = kmemdup(table, sizeof(ipv4_net_table), GFP_KERNEL); if (table == NULL) goto err_alloc; - table[0].data = - &net->ipv4.sysctl_icmp_echo_ignore_all; - table[1].data = - &net->ipv4.sysctl_icmp_echo_ignore_broadcasts; - table[2].data = - &net->ipv4.sysctl_icmp_ignore_bogus_error_responses; - table[3].data = - &net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr; - table[4].data = - &net->ipv4.sysctl_icmp_ratelimit; - table[5].data = - &net->ipv4.sysctl_icmp_ratemask; - table[6].data = - &net->ipv4.sysctl_ping_group_range; - table[7].data = - &net->ipv4.sysctl_tcp_ecn; - - /* Don't export sysctls to unprivileged users */ - if (net->user_ns != &init_user_ns) - table[0].procname = NULL; + /* Update the variables to point into the current struct net */ + for (i = 0; i < ARRAY_SIZE(ipv4_net_table) - 1; i++) + table[i].data += (void *)net - (void *)&init_net; } /* @@ -901,7 +849,12 @@ static __net_init int ipv4_sysctl_init_net(struct net *net) net->ipv4.sysctl_ping_group_range[0] = make_kgid(&init_user_ns, 1); net->ipv4.sysctl_ping_group_range[1] = make_kgid(&init_user_ns, 0); - tcp_init_mem(net); + /* + * Set defaults for local port range + */ + seqlock_init(&net->ipv4.sysctl_local_ports.lock); + net->ipv4.sysctl_local_ports.range[0] = 32768; + net->ipv4.sysctl_local_ports.range[1] = 61000; net->ipv4.ipv4_hdr = register_net_sysctl(net, "net/ipv4", table); if (net->ipv4.ipv4_hdr == NULL) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 6e5617b9f9db6f3c2aeacc7923ea763660761d99..8e8529d3c8c92a4473a3ed9fd8db876ca83533bd 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -288,9 +288,11 @@ int sysctl_tcp_min_tso_segs __read_mostly = 2; struct percpu_counter tcp_orphan_count; EXPORT_SYMBOL_GPL(tcp_orphan_count); +long sysctl_tcp_mem[3] __read_mostly; int sysctl_tcp_wmem[3] __read_mostly; int sysctl_tcp_rmem[3] __read_mostly; +EXPORT_SYMBOL(sysctl_tcp_mem); EXPORT_SYMBOL(sysctl_tcp_rmem); EXPORT_SYMBOL(sysctl_tcp_wmem); @@ -3097,13 +3099,13 @@ static int __init set_thash_entries(char *str) } __setup("thash_entries=", set_thash_entries); -void tcp_init_mem(struct net *net) +static void tcp_init_mem(void) { unsigned long limit = nr_free_buffer_pages() / 8; limit = max(limit, 128UL); - net->ipv4.sysctl_tcp_mem[0] = limit / 4 * 3; - net->ipv4.sysctl_tcp_mem[1] = limit; - net->ipv4.sysctl_tcp_mem[2] = net->ipv4.sysctl_tcp_mem[0] * 2; + sysctl_tcp_mem[0] = limit / 4 * 3; + sysctl_tcp_mem[1] = limit; + sysctl_tcp_mem[2] = sysctl_tcp_mem[0] * 2; } void __init tcp_init(void) @@ -3137,10 +3139,9 @@ void __init tcp_init(void) &tcp_hashinfo.ehash_mask, 0, thash_entries ? 0 : 512 * 1024); - for (i = 0; i <= tcp_hashinfo.ehash_mask; i++) { + for (i = 0; i <= tcp_hashinfo.ehash_mask; i++) INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].chain, i); - INIT_HLIST_NULLS_HEAD(&tcp_hashinfo.ehash[i].twchain, i); - } + if (inet_ehash_locks_alloc(&tcp_hashinfo)) panic("TCP: failed to alloc ehash_locks"); tcp_hashinfo.bhash = @@ -3166,7 +3167,7 @@ void __init tcp_init(void) sysctl_tcp_max_orphans = cnt / 2; sysctl_max_syn_backlog = max(128, cnt / 256); - tcp_init_mem(&init_net); + tcp_init_mem(); /* Set per-socket limits to no more than 1/128 the pressure threshold */ limit = nr_free_buffer_pages() << (PAGE_SHIFT - 7); max_wshare = min(4UL*1024*1024, limit); diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c index f45e1c24244091a750f5f34ca0fe9e9038731ca5..821846fb0a7e211fc870b1afce5991bc3de28494 100644 --- a/net/ipv4/tcp_bic.c +++ b/net/ipv4/tcp_bic.c @@ -140,7 +140,8 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) ca->cnt = 1; } -static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, + u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); @@ -149,7 +150,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) return; if (tp->snd_cwnd <= tp->snd_ssthresh) - tcp_slow_start(tp); + tcp_slow_start(tp, acked); else { bictcp_update(ca, tp->snd_cwnd); tcp_cong_avoid_ai(tp, ca->cnt); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 019c2389a341f6254185c8b9169d9b09c8c16c78..ad37bf18ae4b95a6870a8019ecc3ca9ff55d7679 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -15,8 +15,6 @@ #include #include -int sysctl_tcp_max_ssthresh = 0; - static DEFINE_SPINLOCK(tcp_cong_list_lock); static LIST_HEAD(tcp_cong_list); @@ -299,35 +297,24 @@ bool tcp_is_cwnd_limited(const struct sock *sk, u32 in_flight) } EXPORT_SYMBOL_GPL(tcp_is_cwnd_limited); -/* - * Slow start is used when congestion window is less than slow start - * threshold. This version implements the basic RFC2581 version - * and optionally supports: - * RFC3742 Limited Slow Start - growth limited to max_ssthresh - * RFC3465 Appropriate Byte Counting - growth limited by bytes acknowledged +/* Slow start is used when congestion window is no greater than the slow start + * threshold. We base on RFC2581 and also handle stretch ACKs properly. + * We do not implement RFC3465 Appropriate Byte Counting (ABC) per se but + * something better;) a packet is only considered (s)acked in its entirety to + * defend the ACK attacks described in the RFC. Slow start processes a stretch + * ACK of degree N as if N acks of degree 1 are received back to back except + * ABC caps N to 2. Slow start exits when cwnd grows over ssthresh and + * returns the leftover acks to adjust cwnd in congestion avoidance mode. */ -void tcp_slow_start(struct tcp_sock *tp) +int tcp_slow_start(struct tcp_sock *tp, u32 acked) { - int cnt; /* increase in packets */ - unsigned int delta = 0; - u32 snd_cwnd = tp->snd_cwnd; - - if (unlikely(!snd_cwnd)) { - pr_err_once("snd_cwnd is nul, please report this bug.\n"); - snd_cwnd = 1U; - } + u32 cwnd = tp->snd_cwnd + acked; - if (sysctl_tcp_max_ssthresh > 0 && tp->snd_cwnd > sysctl_tcp_max_ssthresh) - cnt = sysctl_tcp_max_ssthresh >> 1; /* limited slow start */ - else - cnt = snd_cwnd; /* exponential increase */ - - tp->snd_cwnd_cnt += cnt; - while (tp->snd_cwnd_cnt >= snd_cwnd) { - tp->snd_cwnd_cnt -= snd_cwnd; - delta++; - } - tp->snd_cwnd = min(snd_cwnd + delta, tp->snd_cwnd_clamp); + if (cwnd > tp->snd_ssthresh) + cwnd = tp->snd_ssthresh + 1; + acked -= cwnd - tp->snd_cwnd; + tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp); + return acked; } EXPORT_SYMBOL_GPL(tcp_slow_start); @@ -351,7 +338,7 @@ EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai); /* This is Jacobson's slow start and congestion avoidance. * SIGCOMM '88, p. 328. */ -void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); @@ -360,7 +347,7 @@ void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) /* In "safe" area, increase. */ if (tp->snd_cwnd <= tp->snd_ssthresh) - tcp_slow_start(tp); + tcp_slow_start(tp, acked); /* In dangerous area, increase slowly. */ else tcp_cong_avoid_ai(tp, tp->snd_cwnd); diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index b6ae92a51f58607f7c32be2d0762a49380205012..828e4c3ffbaf2d724086a0d408781dd99056a5bf 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -304,7 +304,8 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) ca->cnt = 1; } -static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, + u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); @@ -315,7 +316,7 @@ static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) if (tp->snd_cwnd <= tp->snd_ssthresh) { if (hystart && after(ack, ca->end_seq)) bictcp_hystart_reset(sk); - tcp_slow_start(tp); + tcp_slow_start(tp, acked); } else { bictcp_update(ca, tp->snd_cwnd); tcp_cong_avoid_ai(tp, ca->cnt); diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index ab7bd35bb312c6e9e07aa2950d75ec4bbc982eac..f195d9316e55d3d7ef6dc5285606ea9de0f52a51 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -8,12 +8,26 @@ #include #include -int sysctl_tcp_fastopen __read_mostly; +int sysctl_tcp_fastopen __read_mostly = TFO_CLIENT_ENABLE; struct tcp_fastopen_context __rcu *tcp_fastopen_ctx; static DEFINE_SPINLOCK(tcp_fastopen_ctx_lock); +void tcp_fastopen_init_key_once(bool publish) +{ + static u8 key[TCP_FASTOPEN_KEY_LENGTH]; + + /* tcp_fastopen_reset_cipher publishes the new context + * atomically, so we allow this race happening here. + * + * All call sites of tcp_fastopen_cookie_gen also check + * for a valid cookie, so this is an acceptable risk. + */ + if (net_get_random_once(key, sizeof(key)) && publish) + tcp_fastopen_reset_cipher(key, sizeof(key)); +} + static void tcp_fastopen_ctx_free(struct rcu_head *head) { struct tcp_fastopen_context *ctx = @@ -70,6 +84,8 @@ void tcp_fastopen_cookie_gen(__be32 src, __be32 dst, __be32 path[4] = { src, dst, 0, 0 }; struct tcp_fastopen_context *ctx; + tcp_fastopen_init_key_once(true); + rcu_read_lock(); ctx = rcu_dereference(tcp_fastopen_ctx); if (ctx) { @@ -78,14 +94,3 @@ void tcp_fastopen_cookie_gen(__be32 src, __be32 dst, } rcu_read_unlock(); } - -static int __init tcp_fastopen_init(void) -{ - __u8 key[TCP_FASTOPEN_KEY_LENGTH]; - - get_random_bytes(key, sizeof(key)); - tcp_fastopen_reset_cipher(key, sizeof(key)); - return 0; -} - -late_initcall(tcp_fastopen_init); diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c index 30f27f6b3655fb030b8f501c700c90ad7fb91b38..8ed9305dfdf4f63dc06e951be32a9c5e0b023d1e 100644 --- a/net/ipv4/tcp_highspeed.c +++ b/net/ipv4/tcp_highspeed.c @@ -109,7 +109,7 @@ static void hstcp_init(struct sock *sk) tp->snd_cwnd_clamp = min_t(u32, tp->snd_cwnd_clamp, 0xffffffff/128); } -static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 in_flight) +static void hstcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); struct hstcp *ca = inet_csk_ca(sk); @@ -118,7 +118,7 @@ static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 in_flight) return; if (tp->snd_cwnd <= tp->snd_ssthresh) - tcp_slow_start(tp); + tcp_slow_start(tp, acked); else { /* Update AIMD parameters. * diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c index c1a8175361e896a37c515b93ed64f23f0cd311ee..4a194acfd9237f1aaadfe9f9831358f5c9037cf7 100644 --- a/net/ipv4/tcp_htcp.c +++ b/net/ipv4/tcp_htcp.c @@ -227,7 +227,7 @@ static u32 htcp_recalc_ssthresh(struct sock *sk) return max((tp->snd_cwnd * ca->beta) >> 7, 2U); } -static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); struct htcp *ca = inet_csk_ca(sk); @@ -236,7 +236,7 @@ static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) return; if (tp->snd_cwnd <= tp->snd_ssthresh) - tcp_slow_start(tp); + tcp_slow_start(tp, acked); else { /* In dangerous area, increase slowly. * In theory this is tp->snd_cwnd += alpha / tp->snd_cwnd diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c index 57bdd17dff4d0abfcce0b4ee1078b8e442101371..478fe82611bff24459349cb14fd1ce6d8a9f26ac 100644 --- a/net/ipv4/tcp_hybla.c +++ b/net/ipv4/tcp_hybla.c @@ -85,7 +85,8 @@ static inline u32 hybla_fraction(u32 odds) * o Give cwnd a new value based on the model proposed * o remember increments <1 */ -static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 acked, + u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); struct hybla *ca = inet_csk_ca(sk); @@ -102,7 +103,7 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) return; if (!ca->hybla_en) { - tcp_reno_cong_avoid(sk, ack, in_flight); + tcp_reno_cong_avoid(sk, ack, acked, in_flight); return; } diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c index 834857f3c8713e8d8b80ab7b4f413725ec0ca9b4..8a520996f3d268b6a3a1407fce986c3cf781a871 100644 --- a/net/ipv4/tcp_illinois.c +++ b/net/ipv4/tcp_illinois.c @@ -256,7 +256,8 @@ static void tcp_illinois_state(struct sock *sk, u8 new_state) /* * Increase window in response to successful acknowledgment. */ -static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 acked, + u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); struct illinois *ca = inet_csk_ca(sk); @@ -270,7 +271,7 @@ static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) /* In slow start */ if (tp->snd_cwnd <= tp->snd_ssthresh) - tcp_slow_start(tp); + tcp_slow_start(tp, acked); else { u32 delta; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 068c8fb0d1585ec8823c0310b0a369fb76a559c0..c53b7f35c51dbd6f778455760e8d77733403ddd1 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -267,11 +267,31 @@ static bool TCP_ECN_rcv_ecn_echo(const struct tcp_sock *tp, const struct tcphdr * 1. Tuning sk->sk_sndbuf, when connection enters established state. */ -static void tcp_fixup_sndbuf(struct sock *sk) +static void tcp_sndbuf_expand(struct sock *sk) { - int sndmem = SKB_TRUESIZE(tcp_sk(sk)->rx_opt.mss_clamp + MAX_TCP_HEADER); + const struct tcp_sock *tp = tcp_sk(sk); + int sndmem, per_mss; + u32 nr_segs; + + /* Worst case is non GSO/TSO : each frame consumes one skb + * and skb->head is kmalloced using power of two area of memory + */ + per_mss = max_t(u32, tp->rx_opt.mss_clamp, tp->mss_cache) + + MAX_TCP_HEADER + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + per_mss = roundup_pow_of_two(per_mss) + + SKB_DATA_ALIGN(sizeof(struct sk_buff)); + + nr_segs = max_t(u32, TCP_INIT_CWND, tp->snd_cwnd); + nr_segs = max_t(u32, nr_segs, tp->reordering + 1); + + /* Fast Recovery (RFC 5681 3.2) : + * Cubic needs 1.7 factor, rounded to 2 to include + * extra cushion (application might react slowly to POLLOUT) + */ + sndmem = 2 * nr_segs * per_mss; - sndmem *= TCP_INIT_CWND; if (sk->sk_sndbuf < sndmem) sk->sk_sndbuf = min(sndmem, sysctl_tcp_wmem[2]); } @@ -355,6 +375,12 @@ static void tcp_fixup_rcvbuf(struct sock *sk) rcvmem = 2 * SKB_TRUESIZE(mss + MAX_TCP_HEADER) * tcp_default_init_rwnd(mss); + /* Dynamic Right Sizing (DRS) has 2 to 3 RTT latency + * Allow enough cushion so that sender is not limited by our window + */ + if (sysctl_tcp_moderate_rcvbuf) + rcvmem <<= 2; + if (sk->sk_rcvbuf < rcvmem) sk->sk_rcvbuf = min(rcvmem, sysctl_tcp_rmem[2]); } @@ -370,9 +396,11 @@ void tcp_init_buffer_space(struct sock *sk) if (!(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) tcp_fixup_rcvbuf(sk); if (!(sk->sk_userlocks & SOCK_SNDBUF_LOCK)) - tcp_fixup_sndbuf(sk); + tcp_sndbuf_expand(sk); tp->rcvq_space.space = tp->rcv_wnd; + tp->rcvq_space.time = tcp_time_stamp; + tp->rcvq_space.seq = tp->copied_seq; maxwin = tcp_full_space(sk); @@ -512,48 +540,62 @@ void tcp_rcv_space_adjust(struct sock *sk) { struct tcp_sock *tp = tcp_sk(sk); int time; - int space; - - if (tp->rcvq_space.time == 0) - goto new_measure; + int copied; time = tcp_time_stamp - tp->rcvq_space.time; if (time < (tp->rcv_rtt_est.rtt >> 3) || tp->rcv_rtt_est.rtt == 0) return; - space = 2 * (tp->copied_seq - tp->rcvq_space.seq); + /* Number of bytes copied to user in last RTT */ + copied = tp->copied_seq - tp->rcvq_space.seq; + if (copied <= tp->rcvq_space.space) + goto new_measure; - space = max(tp->rcvq_space.space, space); + /* A bit of theory : + * copied = bytes received in previous RTT, our base window + * To cope with packet losses, we need a 2x factor + * To cope with slow start, and sender growing its cwin by 100 % + * every RTT, we need a 4x factor, because the ACK we are sending + * now is for the next RTT, not the current one : + * + */ - if (tp->rcvq_space.space != space) { - int rcvmem; + if (sysctl_tcp_moderate_rcvbuf && + !(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) { + int rcvwin, rcvmem, rcvbuf; - tp->rcvq_space.space = space; + /* minimal window to cope with packet losses, assuming + * steady state. Add some cushion because of small variations. + */ + rcvwin = (copied << 1) + 16 * tp->advmss; - if (sysctl_tcp_moderate_rcvbuf && - !(sk->sk_userlocks & SOCK_RCVBUF_LOCK)) { - int new_clamp = space; + /* If rate increased by 25%, + * assume slow start, rcvwin = 3 * copied + * If rate increased by 50%, + * assume sender can use 2x growth, rcvwin = 4 * copied + */ + if (copied >= + tp->rcvq_space.space + (tp->rcvq_space.space >> 2)) { + if (copied >= + tp->rcvq_space.space + (tp->rcvq_space.space >> 1)) + rcvwin <<= 1; + else + rcvwin += (rcvwin >> 1); + } - /* Receive space grows, normalize in order to - * take into account packet headers and sk_buff - * structure overhead. - */ - space /= tp->advmss; - if (!space) - space = 1; - rcvmem = SKB_TRUESIZE(tp->advmss + MAX_TCP_HEADER); - while (tcp_win_from_space(rcvmem) < tp->advmss) - rcvmem += 128; - space *= rcvmem; - space = min(space, sysctl_tcp_rmem[2]); - if (space > sk->sk_rcvbuf) { - sk->sk_rcvbuf = space; - - /* Make the window clamp follow along. */ - tp->window_clamp = new_clamp; - } + rcvmem = SKB_TRUESIZE(tp->advmss + MAX_TCP_HEADER); + while (tcp_win_from_space(rcvmem) < tp->advmss) + rcvmem += 128; + + rcvbuf = min(rcvwin / tp->advmss * rcvmem, sysctl_tcp_rmem[2]); + if (rcvbuf > sk->sk_rcvbuf) { + sk->sk_rcvbuf = rcvbuf; + + /* Make the window clamp follow along. */ + tp->window_clamp = rcvwin; } } + tp->rcvq_space.space = copied; new_measure: tp->rcvq_space.seq = tp->copied_seq; @@ -713,7 +755,12 @@ static void tcp_update_pacing_rate(struct sock *sk) if (tp->srtt > 8 + 2) do_div(rate, tp->srtt); - sk->sk_pacing_rate = min_t(u64, rate, ~0U); + /* ACCESS_ONCE() is needed because sch_fq fetches sk_pacing_rate + * without any lock. We want to make sure compiler wont store + * intermediate values in this location. + */ + ACCESS_ONCE(sk->sk_pacing_rate) = min_t(u64, rate, + sk->sk_max_pacing_rate); } /* Calculate rto without backoff. This is the second half of Van Jacobson's @@ -2887,10 +2934,10 @@ static void tcp_synack_rtt_meas(struct sock *sk, const u32 synack_stamp) tcp_ack_update_rtt(sk, FLAG_SYN_ACKED, seq_rtt, -1); } -static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 acked, u32 in_flight) { const struct inet_connection_sock *icsk = inet_csk(sk); - icsk->icsk_ca_ops->cong_avoid(sk, ack, in_flight); + icsk->icsk_ca_ops->cong_avoid(sk, ack, acked, in_flight); tcp_sk(sk)->snd_cwnd_stamp = tcp_time_stamp; } @@ -2979,7 +3026,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets, const struct inet_connection_sock *icsk = inet_csk(sk); struct sk_buff *skb; u32 now = tcp_time_stamp; - int fully_acked = true; + bool fully_acked = true; int flag = 0; u32 pkts_acked = 0; u32 reord = tp->packets_out; @@ -3407,7 +3454,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) /* Advance cwnd if state allows */ if (tcp_may_raise_cwnd(sk, flag)) - tcp_cong_avoid(sk, ack, prior_in_flight); + tcp_cong_avoid(sk, ack, acked, prior_in_flight); if (tcp_ack_is_dubious(sk, flag)) { is_dupack = !(flag & (FLAG_SND_UNA_ADVANCED | FLAG_NOT_DUP)); @@ -4717,15 +4764,7 @@ static void tcp_new_space(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); if (tcp_should_expand_sndbuf(sk)) { - int sndmem = SKB_TRUESIZE(max_t(u32, - tp->rx_opt.mss_clamp, - tp->mss_cache) + - MAX_TCP_HEADER); - int demanded = max_t(unsigned int, tp->snd_cwnd, - tp->reordering + 1); - sndmem *= 2 * demanded; - if (sndmem > sk->sk_sndbuf) - sk->sk_sndbuf = min(sndmem, sysctl_tcp_wmem[2]); + tcp_sndbuf_expand(sk); tp->snd_cwnd_stamp = tcp_time_stamp; } @@ -5693,8 +5732,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, tcp_init_congestion_control(sk); tcp_mtup_init(sk); - tcp_init_buffer_space(sk); tp->copied_seq = tp->rcv_nxt; + tcp_init_buffer_space(sk); } smp_mb(); tcp_set_state(sk, TCP_ESTABLISHED); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index b14266bb91eb5e3b1f43f3329dc2510c8be26a19..14bba8a1c5a74ed9d3e95e6de3bc2494b926a817 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -288,6 +288,7 @@ static void tcp_v4_mtu_reduced(struct sock *sk) mtu = dst_mtu(dst); if (inet->pmtudisc != IP_PMTUDISC_DONT && + ip_sk_accept_pmtu(sk) && inet_csk(sk)->icsk_pmtu_cookie > mtu) { tcp_sync_mss(sk, mtu); @@ -835,11 +836,11 @@ static int tcp_v4_send_synack(struct sock *sk, struct dst_entry *dst, skb = tcp_make_synack(sk, dst, req, NULL); if (skb) { - __tcp_v4_send_check(skb, ireq->loc_addr, ireq->rmt_addr); + __tcp_v4_send_check(skb, ireq->ir_loc_addr, ireq->ir_rmt_addr); skb_set_queue_mapping(skb, queue_mapping); - err = ip_build_and_send_pkt(skb, sk, ireq->loc_addr, - ireq->rmt_addr, + err = ip_build_and_send_pkt(skb, sk, ireq->ir_loc_addr, + ireq->ir_rmt_addr, ireq->opt); err = net_xmit_eval(err); if (!tcp_rsk(req)->snt_synack && !err) @@ -972,7 +973,7 @@ static struct tcp_md5sig_key *tcp_v4_reqsk_md5_lookup(struct sock *sk, { union tcp_md5_addr *addr; - addr = (union tcp_md5_addr *)&inet_rsk(req)->rmt_addr; + addr = (union tcp_md5_addr *)&inet_rsk(req)->ir_rmt_addr; return tcp_md5_do_lookup(sk, addr, AF_INET); } @@ -1149,8 +1150,8 @@ int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, saddr = inet_sk(sk)->inet_saddr; daddr = inet_sk(sk)->inet_daddr; } else if (req) { - saddr = inet_rsk(req)->loc_addr; - daddr = inet_rsk(req)->rmt_addr; + saddr = inet_rsk(req)->ir_loc_addr; + daddr = inet_rsk(req)->ir_rmt_addr; } else { const struct iphdr *iph = ip_hdr(skb); saddr = iph->saddr; @@ -1366,8 +1367,8 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk, kfree_skb(skb_synack); return -1; } - err = ip_build_and_send_pkt(skb_synack, sk, ireq->loc_addr, - ireq->rmt_addr, ireq->opt); + err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr, + ireq->ir_rmt_addr, ireq->opt); err = net_xmit_eval(err); if (!err) tcp_rsk(req)->snt_synack = tcp_time_stamp; @@ -1410,8 +1411,8 @@ static int tcp_v4_conn_req_fastopen(struct sock *sk, inet_csk(child)->icsk_af_ops->rebuild_header(child); tcp_init_congestion_control(child); tcp_mtup_init(child); - tcp_init_buffer_space(child); tcp_init_metrics(child); + tcp_init_buffer_space(child); /* Queue the data carried in the SYN packet. We need to first * bump skb's refcnt because the caller will attempt to free it. @@ -1502,8 +1503,8 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) tcp_openreq_init(req, &tmp_opt, skb); ireq = inet_rsk(req); - ireq->loc_addr = daddr; - ireq->rmt_addr = saddr; + ireq->ir_loc_addr = daddr; + ireq->ir_rmt_addr = saddr; ireq->no_srccheck = inet_sk(sk)->transparent; ireq->opt = tcp_v4_save_options(skb); @@ -1578,15 +1579,15 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) fastopen_cookie_present(&valid_foc) ? &valid_foc : NULL); if (skb_synack) { - __tcp_v4_send_check(skb_synack, ireq->loc_addr, ireq->rmt_addr); + __tcp_v4_send_check(skb_synack, ireq->ir_loc_addr, ireq->ir_rmt_addr); skb_set_queue_mapping(skb_synack, skb_get_queue_mapping(skb)); } else goto drop_and_free; if (likely(!do_fastopen)) { int err; - err = ip_build_and_send_pkt(skb_synack, sk, ireq->loc_addr, - ireq->rmt_addr, ireq->opt); + err = ip_build_and_send_pkt(skb_synack, sk, ireq->ir_loc_addr, + ireq->ir_rmt_addr, ireq->opt); err = net_xmit_eval(err); if (err || want_cookie) goto drop_and_free; @@ -1644,9 +1645,9 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb, newtp = tcp_sk(newsk); newinet = inet_sk(newsk); ireq = inet_rsk(req); - newinet->inet_daddr = ireq->rmt_addr; - newinet->inet_rcv_saddr = ireq->loc_addr; - newinet->inet_saddr = ireq->loc_addr; + newinet->inet_daddr = ireq->ir_rmt_addr; + newinet->inet_rcv_saddr = ireq->ir_loc_addr; + newinet->inet_saddr = ireq->ir_loc_addr; inet_opt = ireq->opt; rcu_assign_pointer(newinet->inet_opt, inet_opt); ireq->opt = NULL; @@ -2194,18 +2195,6 @@ EXPORT_SYMBOL(tcp_v4_destroy_sock); #ifdef CONFIG_PROC_FS /* Proc filesystem TCP sock list dumping. */ -static inline struct inet_timewait_sock *tw_head(struct hlist_nulls_head *head) -{ - return hlist_nulls_empty(head) ? NULL : - list_entry(head->first, struct inet_timewait_sock, tw_node); -} - -static inline struct inet_timewait_sock *tw_next(struct inet_timewait_sock *tw) -{ - return !is_a_nulls(tw->tw_node.next) ? - hlist_nulls_entry(tw->tw_node.next, typeof(*tw), tw_node) : NULL; -} - /* * Get next listener socket follow cur. If cur is NULL, get first socket * starting from bucket given in st->bucket; when st->bucket is zero the @@ -2309,10 +2298,9 @@ static void *listening_get_idx(struct seq_file *seq, loff_t *pos) return rc; } -static inline bool empty_bucket(struct tcp_iter_state *st) +static inline bool empty_bucket(const struct tcp_iter_state *st) { - return hlist_nulls_empty(&tcp_hashinfo.ehash[st->bucket].chain) && - hlist_nulls_empty(&tcp_hashinfo.ehash[st->bucket].twchain); + return hlist_nulls_empty(&tcp_hashinfo.ehash[st->bucket].chain); } /* @@ -2329,7 +2317,6 @@ static void *established_get_first(struct seq_file *seq) for (; st->bucket <= tcp_hashinfo.ehash_mask; ++st->bucket) { struct sock *sk; struct hlist_nulls_node *node; - struct inet_timewait_sock *tw; spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, st->bucket); /* Lockless fast path for the common case of empty buckets */ @@ -2345,18 +2332,7 @@ static void *established_get_first(struct seq_file *seq) rc = sk; goto out; } - st->state = TCP_SEQ_STATE_TIME_WAIT; - inet_twsk_for_each(tw, node, - &tcp_hashinfo.ehash[st->bucket].twchain) { - if (tw->tw_family != st->family || - !net_eq(twsk_net(tw), net)) { - continue; - } - rc = tw; - goto out; - } spin_unlock_bh(lock); - st->state = TCP_SEQ_STATE_ESTABLISHED; } out: return rc; @@ -2365,7 +2341,6 @@ out: static void *established_get_next(struct seq_file *seq, void *cur) { struct sock *sk = cur; - struct inet_timewait_sock *tw; struct hlist_nulls_node *node; struct tcp_iter_state *st = seq->private; struct net *net = seq_file_net(seq); @@ -2373,45 +2348,16 @@ static void *established_get_next(struct seq_file *seq, void *cur) ++st->num; ++st->offset; - if (st->state == TCP_SEQ_STATE_TIME_WAIT) { - tw = cur; - tw = tw_next(tw); -get_tw: - while (tw && (tw->tw_family != st->family || !net_eq(twsk_net(tw), net))) { - tw = tw_next(tw); - } - if (tw) { - cur = tw; - goto out; - } - spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket)); - st->state = TCP_SEQ_STATE_ESTABLISHED; - - /* Look for next non empty bucket */ - st->offset = 0; - while (++st->bucket <= tcp_hashinfo.ehash_mask && - empty_bucket(st)) - ; - if (st->bucket > tcp_hashinfo.ehash_mask) - return NULL; - - spin_lock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket)); - sk = sk_nulls_head(&tcp_hashinfo.ehash[st->bucket].chain); - } else - sk = sk_nulls_next(sk); + sk = sk_nulls_next(sk); sk_nulls_for_each_from(sk, node) { if (sk->sk_family == st->family && net_eq(sock_net(sk), net)) - goto found; + return sk; } - st->state = TCP_SEQ_STATE_TIME_WAIT; - tw = tw_head(&tcp_hashinfo.ehash[st->bucket].twchain); - goto get_tw; -found: - cur = sk; -out: - return cur; + spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket)); + ++st->bucket; + return established_get_first(seq); } static void *established_get_idx(struct seq_file *seq, loff_t pos) @@ -2464,10 +2410,9 @@ static void *tcp_seek_last_pos(struct seq_file *seq) if (rc) break; st->bucket = 0; + st->state = TCP_SEQ_STATE_ESTABLISHED; /* Fallthrough */ case TCP_SEQ_STATE_ESTABLISHED: - case TCP_SEQ_STATE_TIME_WAIT: - st->state = TCP_SEQ_STATE_ESTABLISHED; if (st->bucket > tcp_hashinfo.ehash_mask) break; rc = established_get_first(seq); @@ -2524,7 +2469,6 @@ static void *tcp_seq_next(struct seq_file *seq, void *v, loff_t *pos) } break; case TCP_SEQ_STATE_ESTABLISHED: - case TCP_SEQ_STATE_TIME_WAIT: rc = established_get_next(seq, v); break; } @@ -2548,7 +2492,6 @@ static void tcp_seq_stop(struct seq_file *seq, void *v) if (v != SEQ_START_TOKEN) spin_unlock_bh(&tcp_hashinfo.listening_hash[st->bucket].lock); break; - case TCP_SEQ_STATE_TIME_WAIT: case TCP_SEQ_STATE_ESTABLISHED: if (v) spin_unlock_bh(inet_ehash_lockp(&tcp_hashinfo, st->bucket)); @@ -2606,10 +2549,10 @@ static void get_openreq4(const struct sock *sk, const struct request_sock *req, seq_printf(f, "%4d: %08X:%04X %08X:%04X" " %02X %08X:%08X %02X:%08lX %08X %5u %8d %u %d %pK%n", i, - ireq->loc_addr, + ireq->ir_loc_addr, ntohs(inet_sk(sk)->inet_sport), - ireq->rmt_addr, - ntohs(ireq->rmt_port), + ireq->ir_rmt_addr, + ntohs(ireq->ir_rmt_port), TCP_SYN_RECV, 0, 0, /* could print option size, but that is af dependent. */ 1, /* timers active (only the expire timer) */ @@ -2707,6 +2650,7 @@ static void get_timewait4_sock(const struct inet_timewait_sock *tw, static int tcp4_seq_show(struct seq_file *seq, void *v) { struct tcp_iter_state *st; + struct sock *sk = v; int len; if (v == SEQ_START_TOKEN) { @@ -2721,14 +2665,14 @@ static int tcp4_seq_show(struct seq_file *seq, void *v) switch (st->state) { case TCP_SEQ_STATE_LISTENING: case TCP_SEQ_STATE_ESTABLISHED: - get_tcp4_sock(v, seq, st->num, &len); + if (sk->sk_state == TCP_TIME_WAIT) + get_timewait4_sock(v, seq, st->num, &len); + else + get_tcp4_sock(v, seq, st->num, &len); break; case TCP_SEQ_STATE_OPENREQ: get_openreq4(st->syn_wait_sk, v, seq, st->num, st->uid, &len); break; - case TCP_SEQ_STATE_TIME_WAIT: - get_timewait4_sock(v, seq, st->num, &len); - break; } seq_printf(seq, "%*s\n", TMPSZ - 1 - len, ""); out: @@ -2806,6 +2750,7 @@ struct proto tcp_prot = { .orphan_count = &tcp_orphan_count, .memory_allocated = &tcp_memory_allocated, .memory_pressure = &tcp_memory_pressure, + .sysctl_mem = sysctl_tcp_mem, .sysctl_wmem = sysctl_tcp_wmem, .sysctl_rmem = sysctl_tcp_rmem, .max_header = MAX_TCP_HEADER, diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c index 72f7218b03f57856194d39872737d12f2113883c..991d62a2f9bb3abb1d155d075c202c1ea558ca7f 100644 --- a/net/ipv4/tcp_lp.c +++ b/net/ipv4/tcp_lp.c @@ -115,12 +115,13 @@ static void tcp_lp_init(struct sock *sk) * Will only call newReno CA when away from inference. * From TCP-LP's paper, this will be handled in additive increasement. */ -static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 acked, + u32 in_flight) { struct lp *lp = inet_csk_ca(sk); if (!(lp->flag & LP_WITHIN_INF)) - tcp_reno_cong_avoid(sk, ack, in_flight); + tcp_reno_cong_avoid(sk, ack, acked, in_flight); } /** diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c index 559d4ae6ebf4ed32ff80605e3d1b5c3792b1752c..03e9154f7e687efef63c91878e33427672bc4036 100644 --- a/net/ipv4/tcp_memcontrol.c +++ b/net/ipv4/tcp_memcontrol.c @@ -6,15 +6,10 @@ #include #include -static inline struct tcp_memcontrol *tcp_from_cgproto(struct cg_proto *cg_proto) -{ - return container_of(cg_proto, struct tcp_memcontrol, cg_proto); -} - static void memcg_tcp_enter_memory_pressure(struct sock *sk) { if (sk->sk_cgrp->memory_pressure) - *sk->sk_cgrp->memory_pressure = 1; + sk->sk_cgrp->memory_pressure = 1; } EXPORT_SYMBOL(memcg_tcp_enter_memory_pressure); @@ -27,34 +22,24 @@ int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss) */ struct res_counter *res_parent = NULL; struct cg_proto *cg_proto, *parent_cg; - struct tcp_memcontrol *tcp; struct mem_cgroup *parent = parent_mem_cgroup(memcg); - struct net *net = current->nsproxy->net_ns; cg_proto = tcp_prot.proto_cgroup(memcg); if (!cg_proto) return 0; - tcp = tcp_from_cgproto(cg_proto); - - tcp->tcp_prot_mem[0] = net->ipv4.sysctl_tcp_mem[0]; - tcp->tcp_prot_mem[1] = net->ipv4.sysctl_tcp_mem[1]; - tcp->tcp_prot_mem[2] = net->ipv4.sysctl_tcp_mem[2]; - tcp->tcp_memory_pressure = 0; + cg_proto->sysctl_mem[0] = sysctl_tcp_mem[0]; + cg_proto->sysctl_mem[1] = sysctl_tcp_mem[1]; + cg_proto->sysctl_mem[2] = sysctl_tcp_mem[2]; + cg_proto->memory_pressure = 0; + cg_proto->memcg = memcg; parent_cg = tcp_prot.proto_cgroup(parent); if (parent_cg) - res_parent = parent_cg->memory_allocated; - - res_counter_init(&tcp->tcp_memory_allocated, res_parent); - percpu_counter_init(&tcp->tcp_sockets_allocated, 0); + res_parent = &parent_cg->memory_allocated; - cg_proto->enter_memory_pressure = memcg_tcp_enter_memory_pressure; - cg_proto->memory_pressure = &tcp->tcp_memory_pressure; - cg_proto->sysctl_mem = tcp->tcp_prot_mem; - cg_proto->memory_allocated = &tcp->tcp_memory_allocated; - cg_proto->sockets_allocated = &tcp->tcp_sockets_allocated; - cg_proto->memcg = memcg; + res_counter_init(&cg_proto->memory_allocated, res_parent); + percpu_counter_init(&cg_proto->sockets_allocated, 0); return 0; } @@ -63,21 +48,17 @@ EXPORT_SYMBOL(tcp_init_cgroup); void tcp_destroy_cgroup(struct mem_cgroup *memcg) { struct cg_proto *cg_proto; - struct tcp_memcontrol *tcp; cg_proto = tcp_prot.proto_cgroup(memcg); if (!cg_proto) return; - tcp = tcp_from_cgproto(cg_proto); - percpu_counter_destroy(&tcp->tcp_sockets_allocated); + percpu_counter_destroy(&cg_proto->sockets_allocated); } EXPORT_SYMBOL(tcp_destroy_cgroup); static int tcp_update_limit(struct mem_cgroup *memcg, u64 val) { - struct net *net = current->nsproxy->net_ns; - struct tcp_memcontrol *tcp; struct cg_proto *cg_proto; u64 old_lim; int i; @@ -90,16 +71,14 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val) if (val > RES_COUNTER_MAX) val = RES_COUNTER_MAX; - tcp = tcp_from_cgproto(cg_proto); - - old_lim = res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT); - ret = res_counter_set_limit(&tcp->tcp_memory_allocated, val); + old_lim = res_counter_read_u64(&cg_proto->memory_allocated, RES_LIMIT); + ret = res_counter_set_limit(&cg_proto->memory_allocated, val); if (ret) return ret; for (i = 0; i < 3; i++) - tcp->tcp_prot_mem[i] = min_t(long, val >> PAGE_SHIFT, - net->ipv4.sysctl_tcp_mem[i]); + cg_proto->sysctl_mem[i] = min_t(long, val >> PAGE_SHIFT, + sysctl_tcp_mem[i]); if (val == RES_COUNTER_MAX) clear_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags); @@ -156,28 +135,24 @@ static int tcp_cgroup_write(struct cgroup_subsys_state *css, struct cftype *cft, static u64 tcp_read_stat(struct mem_cgroup *memcg, int type, u64 default_val) { - struct tcp_memcontrol *tcp; struct cg_proto *cg_proto; cg_proto = tcp_prot.proto_cgroup(memcg); if (!cg_proto) return default_val; - tcp = tcp_from_cgproto(cg_proto); - return res_counter_read_u64(&tcp->tcp_memory_allocated, type); + return res_counter_read_u64(&cg_proto->memory_allocated, type); } static u64 tcp_read_usage(struct mem_cgroup *memcg) { - struct tcp_memcontrol *tcp; struct cg_proto *cg_proto; cg_proto = tcp_prot.proto_cgroup(memcg); if (!cg_proto) return atomic_long_read(&tcp_memory_allocated) << PAGE_SHIFT; - tcp = tcp_from_cgproto(cg_proto); - return res_counter_read_u64(&tcp->tcp_memory_allocated, RES_USAGE); + return res_counter_read_u64(&cg_proto->memory_allocated, RES_USAGE); } static u64 tcp_cgroup_read(struct cgroup_subsys_state *css, struct cftype *cft) @@ -205,54 +180,25 @@ static u64 tcp_cgroup_read(struct cgroup_subsys_state *css, struct cftype *cft) static int tcp_cgroup_reset(struct cgroup_subsys_state *css, unsigned int event) { struct mem_cgroup *memcg; - struct tcp_memcontrol *tcp; struct cg_proto *cg_proto; memcg = mem_cgroup_from_css(css); cg_proto = tcp_prot.proto_cgroup(memcg); if (!cg_proto) return 0; - tcp = tcp_from_cgproto(cg_proto); switch (event) { case RES_MAX_USAGE: - res_counter_reset_max(&tcp->tcp_memory_allocated); + res_counter_reset_max(&cg_proto->memory_allocated); break; case RES_FAILCNT: - res_counter_reset_failcnt(&tcp->tcp_memory_allocated); + res_counter_reset_failcnt(&cg_proto->memory_allocated); break; } return 0; } -unsigned long long tcp_max_memory(const struct mem_cgroup *memcg) -{ - struct tcp_memcontrol *tcp; - struct cg_proto *cg_proto; - - cg_proto = tcp_prot.proto_cgroup((struct mem_cgroup *)memcg); - if (!cg_proto) - return 0; - - tcp = tcp_from_cgproto(cg_proto); - return res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT); -} - -void tcp_prot_mem(struct mem_cgroup *memcg, long val, int idx) -{ - struct tcp_memcontrol *tcp; - struct cg_proto *cg_proto; - - cg_proto = tcp_prot.proto_cgroup(memcg); - if (!cg_proto) - return; - - tcp = tcp_from_cgproto(cg_proto); - - tcp->tcp_prot_mem[idx] = val; -} - static struct cftype tcp_files[] = { { .name = "kmem.tcp.limit_in_bytes", diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 52f3c6b971d2def6854cf7d489022ad499ee0d24..2ab09cbae74d29155d08f7dd701851136d205e0f 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -215,13 +215,15 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, addr.family = req->rsk_ops->family; switch (addr.family) { case AF_INET: - addr.addr.a4 = inet_rsk(req)->rmt_addr; + addr.addr.a4 = inet_rsk(req)->ir_rmt_addr; hash = (__force unsigned int) addr.addr.a4; break; +#if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - *(struct in6_addr *)addr.addr.a6 = inet6_rsk(req)->rmt_addr; - hash = ipv6_addr_hash(&inet6_rsk(req)->rmt_addr); + *(struct in6_addr *)addr.addr.a6 = inet_rsk(req)->ir_v6_rmt_addr; + hash = ipv6_addr_hash(&inet_rsk(req)->ir_v6_rmt_addr); break; +#endif default: return NULL; } @@ -240,7 +242,6 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req, static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock *tw) { - struct inet6_timewait_sock *tw6; struct tcp_metrics_block *tm; struct inetpeer_addr addr; unsigned int hash; @@ -252,11 +253,12 @@ static struct tcp_metrics_block *__tcp_get_metrics_tw(struct inet_timewait_sock addr.addr.a4 = tw->tw_daddr; hash = (__force unsigned int) addr.addr.a4; break; +#if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - tw6 = inet6_twsk((struct sock *)tw); - *(struct in6_addr *)addr.addr.a6 = tw6->tw_v6_daddr; - hash = ipv6_addr_hash(&tw6->tw_v6_daddr); + *(struct in6_addr *)addr.addr.a6 = tw->tw_v6_daddr; + hash = ipv6_addr_hash(&tw->tw_v6_daddr); break; +#endif default: return NULL; } @@ -288,10 +290,12 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk, addr.addr.a4 = inet_sk(sk)->inet_daddr; hash = (__force unsigned int) addr.addr.a4; break; +#if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - *(struct in6_addr *)addr.addr.a6 = inet6_sk(sk)->daddr; - hash = ipv6_addr_hash(&inet6_sk(sk)->daddr); + *(struct in6_addr *)addr.addr.a6 = sk->sk_v6_daddr; + hash = ipv6_addr_hash(&sk->sk_v6_daddr); break; +#endif default: return NULL; } @@ -667,8 +671,9 @@ void tcp_fastopen_cache_set(struct sock *sk, u16 mss, struct tcp_fastopen_metrics *tfom = &tm->tcpm_fastopen; write_seqlock_bh(&fastopen_seqlock); - tfom->mss = mss; - if (cookie->len > 0) + if (mss) + tfom->mss = mss; + if (cookie && cookie->len > 0) tfom->cookie = *cookie; if (syn_lost) { ++tfom->syn_loss; diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 58a3e69aef6440d36061ea9e6291152442954239..97b684159861bc451219bf66359afa288049a63e 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -293,12 +293,9 @@ void tcp_time_wait(struct sock *sk, int state, int timeo) #if IS_ENABLED(CONFIG_IPV6) if (tw->tw_family == PF_INET6) { struct ipv6_pinfo *np = inet6_sk(sk); - struct inet6_timewait_sock *tw6; - tw->tw_ipv6_offset = inet6_tw_offset(sk->sk_prot); - tw6 = inet6_twsk((struct sock *)tw); - tw6->tw_v6_daddr = np->daddr; - tw6->tw_v6_rcv_saddr = np->rcv_saddr; + tw->tw_v6_daddr = sk->sk_v6_daddr; + tw->tw_v6_rcv_saddr = sk->sk_v6_rcv_saddr; tw->tw_tclass = np->tclass; tw->tw_ipv6only = np->ipv6only; } diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c index 533c58a5cfb7a9cf9f4f2b1842533916b20b39da..a2b68a108eae69170c8d915be778c9aeebf0f371 100644 --- a/net/ipv4/tcp_offload.c +++ b/net/ipv4/tcp_offload.c @@ -14,7 +14,7 @@ #include #include -struct sk_buff *tcp_tso_segment(struct sk_buff *skb, +struct sk_buff *tcp_gso_segment(struct sk_buff *skb, netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); @@ -57,6 +57,8 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, SKB_GSO_TCP_ECN | SKB_GSO_TCPV6 | SKB_GSO_GRE | + SKB_GSO_IPIP | + SKB_GSO_SIT | SKB_GSO_MPLS | SKB_GSO_UDP_TUNNEL | 0) || @@ -136,7 +138,7 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, out: return segs; } -EXPORT_SYMBOL(tcp_tso_segment); +EXPORT_SYMBOL(tcp_gso_segment); struct sk_buff **tcp_gro_receive(struct sk_buff **head, struct sk_buff *skb) { @@ -317,7 +319,7 @@ static int tcp4_gro_complete(struct sk_buff *skb) static const struct net_offload tcpv4_offload = { .callbacks = { .gso_send_check = tcp_v4_gso_send_check, - .gso_segment = tcp_tso_segment, + .gso_segment = tcp_gso_segment, .gro_receive = tcp4_gro_receive, .gro_complete = tcp4_gro_complete, }, diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index d46f2143305c2632e7703f2677310dd177d67208..672854664ff5c1d36783ac1bfb72fea481648ca1 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -850,15 +850,15 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, BUG_ON(!skb || !tcp_skb_pcount(skb)); - /* If congestion control is doing timestamping, we must - * take such a timestamp before we potentially clone/copy. - */ - if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP) - __net_timestamp(skb); - - if (likely(clone_it)) { + if (clone_it) { const struct sk_buff *fclone = skb + 1; + /* If congestion control is doing timestamping, we must + * take such a timestamp before we potentially clone/copy. + */ + if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP) + __net_timestamp(skb); + if (unlikely(skb->fclone == SKB_FCLONE_ORIG && fclone->fclone == SKB_FCLONE_CLONE)) NET_INC_STATS_BH(sock_net(sk), @@ -2353,21 +2353,6 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) tcp_retrans_try_collapse(sk, skb, cur_mss); - /* Some Solaris stacks overoptimize and ignore the FIN on a - * retransmit when old data is attached. So strip it off - * since it is cheap to do so and saves bytes on the network. - */ - if (skb->len > 0 && - (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) && - tp->snd_una == (TCP_SKB_CB(skb)->end_seq - 1)) { - if (!pskb_trim(skb, 0)) { - /* Reuse, even though it does some unnecessary work */ - tcp_init_nondata_skb(skb, TCP_SKB_CB(skb)->end_seq - 1, - TCP_SKB_CB(skb)->tcp_flags); - skb->ip_summed = CHECKSUM_NONE; - } - } - /* Make a copy, if the first transmission SKB clone we made * is still in somebody's hands, else make a clone. */ @@ -2736,8 +2721,8 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst, th->syn = 1; th->ack = 1; TCP_ECN_make_synack(req, th); - th->source = ireq->loc_port; - th->dest = ireq->rmt_port; + th->source = htons(ireq->ir_num); + th->dest = ireq->ir_rmt_port; /* Setting of flags are superfluous here for callers (and ECE is * not even correctly set) */ diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index 611beab38a004aae36730d18bffb62928a2321b1..8b97d71e193b97cf18e424f01be911e34a614b32 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c @@ -101,22 +101,6 @@ static inline int tcp_probe_avail(void) si4.sin_addr.s_addr = inet->inet_##mem##addr; \ } while (0) \ -#if IS_ENABLED(CONFIG_IPV6) -#define tcp_probe_copy_fl_to_si6(inet, si6, mem) \ - do { \ - struct ipv6_pinfo *pi6 = inet->pinet6; \ - si6.sin6_family = AF_INET6; \ - si6.sin6_port = inet->inet_##mem##port; \ - si6.sin6_addr = pi6->mem##addr; \ - si6.sin6_flowinfo = 0; /* No need here. */ \ - si6.sin6_scope_id = 0; /* No need here. */ \ - } while (0) -#else -#define tcp_probe_copy_fl_to_si6(fl, si6, mem) \ - do { \ - memset(&si6, 0, sizeof(si6)); \ - } while (0) -#endif /* * Hook inserted to be called before each receive packet. @@ -147,8 +131,17 @@ static void jtcp_rcv_established(struct sock *sk, struct sk_buff *skb, tcp_probe_copy_fl_to_si4(inet, p->dst.v4, d); break; case AF_INET6: - tcp_probe_copy_fl_to_si6(inet, p->src.v6, s); - tcp_probe_copy_fl_to_si6(inet, p->dst.v6, d); + memset(&p->src.v6, 0, sizeof(p->src.v6)); + memset(&p->dst.v6, 0, sizeof(p->dst.v6)); +#if IS_ENABLED(CONFIG_IPV6) + p->src.v6.sin6_family = AF_INET6; + p->src.v6.sin6_port = inet->inet_sport; + p->src.v6.sin6_addr = inet6_sk(sk)->saddr; + + p->dst.v6.sin6_family = AF_INET6; + p->dst.v6.sin6_port = inet->inet_dport; + p->dst.v6.sin6_addr = sk->sk_v6_daddr; +#endif break; default: BUG(); diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c index 8ce55b8aaec8476e3b8292ddd7bf10c98ac402b2..19ea6c2951f3b35a29a9ddc04f8578f7abe3f87c 100644 --- a/net/ipv4/tcp_scalable.c +++ b/net/ipv4/tcp_scalable.c @@ -15,7 +15,8 @@ #define TCP_SCALABLE_AI_CNT 50U #define TCP_SCALABLE_MD_SCALE 3 -static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked, + u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); @@ -23,7 +24,7 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) return; if (tp->snd_cwnd <= tp->snd_ssthresh) - tcp_slow_start(tp); + tcp_slow_start(tp, acked); else tcp_cong_avoid_ai(tp, min(tp->snd_cwnd, TCP_SCALABLE_AI_CNT)); } diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index 4b85e6f636c9e66dbb1cc0f4acd197c33cb6cfb6..64f0354c84c7a8956230f2794b4dcb56331d98fc 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -156,12 +156,16 @@ static bool retransmits_timed_out(struct sock *sk, static int tcp_write_timeout(struct sock *sk) { struct inet_connection_sock *icsk = inet_csk(sk); + struct tcp_sock *tp = tcp_sk(sk); int retry_until; bool do_reset, syn_set = false; if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) { - if (icsk->icsk_retransmits) + if (icsk->icsk_retransmits) { dst_negative_advice(sk); + if (tp->syn_fastopen || tp->syn_data) + tcp_fastopen_cache_set(sk, 0, NULL, true); + } retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries; syn_set = true; } else { @@ -374,9 +378,8 @@ void tcp_retransmit_timer(struct sock *sk) } #if IS_ENABLED(CONFIG_IPV6) else if (sk->sk_family == AF_INET6) { - struct ipv6_pinfo *np = inet6_sk(sk); LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"), - &np->daddr, + &sk->sk_v6_daddr, ntohs(inet->inet_dport), inet->inet_num, tp->snd_una, tp->snd_nxt); } diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c index 80fa2bfd7edef91e309119d2b5bda953342e1e1b..06cae62bf20845fe67b6eff1c7919ec4f342569f 100644 --- a/net/ipv4/tcp_vegas.c +++ b/net/ipv4/tcp_vegas.c @@ -163,13 +163,14 @@ static inline u32 tcp_vegas_ssthresh(struct tcp_sock *tp) return min(tp->snd_ssthresh, tp->snd_cwnd-1); } -static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 acked, + u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); struct vegas *vegas = inet_csk_ca(sk); if (!vegas->doing_vegas_now) { - tcp_reno_cong_avoid(sk, ack, in_flight); + tcp_reno_cong_avoid(sk, ack, acked, in_flight); return; } @@ -194,7 +195,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) /* We don't have enough RTT samples to do the Vegas * calculation, so we'll behave like Reno. */ - tcp_reno_cong_avoid(sk, ack, in_flight); + tcp_reno_cong_avoid(sk, ack, acked, in_flight); } else { u32 rtt, diff; u64 target_cwnd; @@ -243,7 +244,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) } else if (tp->snd_cwnd <= tp->snd_ssthresh) { /* Slow start. */ - tcp_slow_start(tp); + tcp_slow_start(tp, acked); } else { /* Congestion avoidance. */ @@ -283,7 +284,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) } /* Use normal slow start */ else if (tp->snd_cwnd <= tp->snd_ssthresh) - tcp_slow_start(tp); + tcp_slow_start(tp, acked); } diff --git a/net/ipv4/tcp_vegas.h b/net/ipv4/tcp_vegas.h index 6c0eea2f8249bf6b08c85395c0ad4b4d8ef52b39..0531b99d8637bac1c4f19537a351f060fffa56ed 100644 --- a/net/ipv4/tcp_vegas.h +++ b/net/ipv4/tcp_vegas.h @@ -15,10 +15,10 @@ struct vegas { u32 baseRTT; /* the min of all Vegas RTT measurements seen (in usec) */ }; -extern void tcp_vegas_init(struct sock *sk); -extern void tcp_vegas_state(struct sock *sk, u8 ca_state); -extern void tcp_vegas_pkts_acked(struct sock *sk, u32 cnt, s32 rtt_us); -extern void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event); -extern void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb); +void tcp_vegas_init(struct sock *sk); +void tcp_vegas_state(struct sock *sk, u8 ca_state); +void tcp_vegas_pkts_acked(struct sock *sk, u32 cnt, s32 rtt_us); +void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event); +void tcp_vegas_get_info(struct sock *sk, u32 ext, struct sk_buff *skb); #endif /* __TCP_VEGAS_H */ diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c index ac43cd747bcebdd83ce98fb26c3f7b1496ea3c69..326475a94865c2fd0cbcf7bffe97c21d37159683 100644 --- a/net/ipv4/tcp_veno.c +++ b/net/ipv4/tcp_veno.c @@ -114,13 +114,14 @@ static void tcp_veno_cwnd_event(struct sock *sk, enum tcp_ca_event event) tcp_veno_init(sk); } -static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 acked, + u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); struct veno *veno = inet_csk_ca(sk); if (!veno->doing_veno_now) { - tcp_reno_cong_avoid(sk, ack, in_flight); + tcp_reno_cong_avoid(sk, ack, acked, in_flight); return; } @@ -133,7 +134,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) /* We don't have enough rtt samples to do the Veno * calculation, so we'll behave like Reno. */ - tcp_reno_cong_avoid(sk, ack, in_flight); + tcp_reno_cong_avoid(sk, ack, acked, in_flight); } else { u64 target_cwnd; u32 rtt; @@ -152,7 +153,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) if (tp->snd_cwnd <= tp->snd_ssthresh) { /* Slow start. */ - tcp_slow_start(tp); + tcp_slow_start(tp, acked); } else { /* Congestion avoidance. */ if (veno->diff < beta) { diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c index 05c3b6f0e8e1cfa4b6f0530c826df9f5402dfe7d..a347a078ee073fc31991ecd581b8f5bca7a5305b 100644 --- a/net/ipv4/tcp_yeah.c +++ b/net/ipv4/tcp_yeah.c @@ -69,7 +69,8 @@ static void tcp_yeah_pkts_acked(struct sock *sk, u32 pkts_acked, s32 rtt_us) tcp_vegas_pkts_acked(sk, pkts_acked, rtt_us); } -static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) +static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 acked, + u32 in_flight) { struct tcp_sock *tp = tcp_sk(sk); struct yeah *yeah = inet_csk_ca(sk); @@ -78,7 +79,7 @@ static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, u32 in_flight) return; if (tp->snd_cwnd <= tp->snd_ssthresh) - tcp_slow_start(tp); + tcp_slow_start(tp, acked); else if (!yeah->doing_reno_now) { /* Scalable */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 0ca44df51ee94a2875427e5c9d3b1c77434f5f40..89909dd730ddd65a4eac4f18e8a38925962b8534 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -103,6 +103,7 @@ #include #include #include +#include #include #include #include @@ -219,7 +220,7 @@ int udp_lib_get_port(struct sock *sk, unsigned short snum, unsigned short first, last; DECLARE_BITMAP(bitmap, PORTS_PER_CHAIN); - inet_get_local_port_range(&low, &high); + inet_get_local_port_range(net, &low, &high); remaining = (high - low) + 1; rand = net_random(); @@ -406,6 +407,18 @@ static inline int compute_score2(struct sock *sk, struct net *net, return score; } +static unsigned int udp_ehashfn(struct net *net, const __be32 laddr, + const __u16 lport, const __be32 faddr, + const __be16 fport) +{ + static u32 udp_ehash_secret __read_mostly; + + net_get_random_once(&udp_ehash_secret, sizeof(udp_ehash_secret)); + + return __inet_ehashfn(laddr, lport, faddr, fport, + udp_ehash_secret + net_hash_mix(net)); +} + /* called with read_rcu_lock() */ static struct sock *udp4_lib_lookup2(struct net *net, @@ -429,8 +442,8 @@ begin: badness = score; reuseport = sk->sk_reuseport; if (reuseport) { - hash = inet_ehashfn(net, daddr, hnum, - saddr, sport); + hash = udp_ehashfn(net, daddr, hnum, + saddr, sport); matches = 1; } } else if (score == badness && reuseport) { @@ -510,8 +523,8 @@ begin: badness = score; reuseport = sk->sk_reuseport; if (reuseport) { - hash = inet_ehashfn(net, daddr, hnum, - saddr, sport); + hash = udp_ehashfn(net, daddr, hnum, + saddr, sport); matches = 1; } } else if (score == badness && reuseport) { @@ -565,6 +578,26 @@ struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, } EXPORT_SYMBOL_GPL(udp4_lib_lookup); +static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk, + __be16 loc_port, __be32 loc_addr, + __be16 rmt_port, __be32 rmt_addr, + int dif, unsigned short hnum) +{ + struct inet_sock *inet = inet_sk(sk); + + if (!net_eq(sock_net(sk), net) || + udp_sk(sk)->udp_port_hash != hnum || + (inet->inet_daddr && inet->inet_daddr != rmt_addr) || + (inet->inet_dport != rmt_port && inet->inet_dport) || + (inet->inet_rcv_saddr && inet->inet_rcv_saddr != loc_addr) || + ipv6_only_sock(sk) || + (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)) + return false; + if (!ip_mc_sf_allow(sk, loc_addr, rmt_addr, dif)) + return false; + return true; +} + static inline struct sock *udp_v4_mcast_next(struct net *net, struct sock *sk, __be16 loc_port, __be32 loc_addr, __be16 rmt_port, __be32 rmt_addr, @@ -575,20 +608,11 @@ static inline struct sock *udp_v4_mcast_next(struct net *net, struct sock *sk, unsigned short hnum = ntohs(loc_port); sk_nulls_for_each_from(s, node) { - struct inet_sock *inet = inet_sk(s); - - if (!net_eq(sock_net(s), net) || - udp_sk(s)->udp_port_hash != hnum || - (inet->inet_daddr && inet->inet_daddr != rmt_addr) || - (inet->inet_dport != rmt_port && inet->inet_dport) || - (inet->inet_rcv_saddr && - inet->inet_rcv_saddr != loc_addr) || - ipv6_only_sock(s) || - (s->sk_bound_dev_if && s->sk_bound_dev_if != dif)) - continue; - if (!ip_mc_sf_allow(s, loc_addr, rmt_addr, dif)) - continue; - goto found; + if (__udp_is_mcast_sock(net, s, + loc_port, loc_addr, + rmt_port, rmt_addr, + dif, hnum)) + goto found; } s = NULL; found: @@ -855,6 +879,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.opt = NULL; ipc.tx_flags = 0; + ipc.ttl = 0; + ipc.tos = -1; getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; @@ -938,7 +964,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, faddr = ipc.opt->opt.faddr; connected = 0; } - tos = RT_TOS(inet->tos); + tos = get_rttos(&ipc, inet); if (sock_flag(sk, SOCK_LOCALROUTE) || (msg->msg_flags & MSG_DONTROUTE) || (ipc.opt && ipc.opt->opt.is_strictroute)) { @@ -1403,8 +1429,10 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { int rc; - if (inet_sk(sk)->inet_daddr) + if (inet_sk(sk)->inet_daddr) { sock_rps_save_rxhash(sk, skb); + sk_mark_napi_id(sk, skb); + } rc = sock_queue_rcv_skb(sk, skb); if (rc < 0) { @@ -1528,7 +1556,7 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) rc = 0; - ipv4_pktinfo_prepare(skb); + ipv4_pktinfo_prepare(sk, skb); bh_lock_sock(sk); if (!sock_owned_by_user(sk)) rc = __udp_queue_rcv_skb(sk, skb); @@ -1577,6 +1605,14 @@ static void flush_stack(struct sock **stack, unsigned int count, kfree_skb(skb1); } +static void udp_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + + dst_hold(dst); + sk->sk_rx_dst = dst; +} + /* * Multicasts and broadcasts go to each listener. * @@ -1705,16 +1741,32 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, if (udp4_csum_init(skb, uh, proto)) goto csum_error; - if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) - return __udp4_lib_mcast_deliver(net, skb, uh, - saddr, daddr, udptable); + if (skb->sk) { + int ret; + sk = skb->sk; + + if (unlikely(sk->sk_rx_dst == NULL)) + udp_sk_rx_dst_set(sk, skb); - sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable); + ret = udp_queue_rcv_skb(sk, skb); + + /* a return value > 0 means to resubmit the input, but + * it wants the return to be -protocol, or 0 + */ + if (ret > 0) + return -ret; + return 0; + } else { + if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST)) + return __udp4_lib_mcast_deliver(net, skb, uh, + saddr, daddr, udptable); + + sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable); + } if (sk != NULL) { int ret; - sk_mark_napi_id(sk, skb); ret = udp_queue_rcv_skb(sk, skb); sock_put(sk); @@ -1768,6 +1820,135 @@ drop: return 0; } +/* We can only early demux multicast if there is a single matching socket. + * If more than one socket found returns NULL + */ +static struct sock *__udp4_lib_mcast_demux_lookup(struct net *net, + __be16 loc_port, __be32 loc_addr, + __be16 rmt_port, __be32 rmt_addr, + int dif) +{ + struct sock *sk, *result; + struct hlist_nulls_node *node; + unsigned short hnum = ntohs(loc_port); + unsigned int count, slot = udp_hashfn(net, hnum, udp_table.mask); + struct udp_hslot *hslot = &udp_table.hash[slot]; + + rcu_read_lock(); +begin: + count = 0; + result = NULL; + sk_nulls_for_each_rcu(sk, node, &hslot->head) { + if (__udp_is_mcast_sock(net, sk, + loc_port, loc_addr, + rmt_port, rmt_addr, + dif, hnum)) { + result = sk; + ++count; + } + } + /* + * if the nulls value we got at the end of this lookup is + * not the expected one, we must restart lookup. + * We probably met an item that was moved to another chain. + */ + if (get_nulls_value(node) != slot) + goto begin; + + if (result) { + if (count != 1 || + unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2))) + result = NULL; + else if (unlikely(!__udp_is_mcast_sock(net, result, + loc_port, loc_addr, + rmt_port, rmt_addr, + dif, hnum))) { + sock_put(result); + result = NULL; + } + } + rcu_read_unlock(); + return result; +} + +/* For unicast we should only early demux connected sockets or we can + * break forwarding setups. The chains here can be long so only check + * if the first socket is an exact match and if not move on. + */ +static struct sock *__udp4_lib_demux_lookup(struct net *net, + __be16 loc_port, __be32 loc_addr, + __be16 rmt_port, __be32 rmt_addr, + int dif) +{ + struct sock *sk, *result; + struct hlist_nulls_node *node; + unsigned short hnum = ntohs(loc_port); + unsigned int hash2 = udp4_portaddr_hash(net, loc_addr, hnum); + unsigned int slot2 = hash2 & udp_table.mask; + struct udp_hslot *hslot2 = &udp_table.hash2[slot2]; + INET_ADDR_COOKIE(acookie, rmt_addr, loc_addr) + const __portpair ports = INET_COMBINED_PORTS(rmt_port, hnum); + + rcu_read_lock(); + result = NULL; + udp_portaddr_for_each_entry_rcu(sk, node, &hslot2->head) { + if (INET_MATCH(sk, net, acookie, + rmt_addr, loc_addr, ports, dif)) + result = sk; + /* Only check first socket in chain */ + break; + } + + if (result) { + if (unlikely(!atomic_inc_not_zero_hint(&result->sk_refcnt, 2))) + result = NULL; + else if (unlikely(!INET_MATCH(sk, net, acookie, + rmt_addr, loc_addr, + ports, dif))) { + sock_put(result); + result = NULL; + } + } + rcu_read_unlock(); + return result; +} + +void udp_v4_early_demux(struct sk_buff *skb) +{ + const struct iphdr *iph = ip_hdr(skb); + const struct udphdr *uh = udp_hdr(skb); + struct sock *sk; + struct dst_entry *dst; + struct net *net = dev_net(skb->dev); + int dif = skb->dev->ifindex; + + /* validate the packet */ + if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct udphdr))) + return; + + if (skb->pkt_type == PACKET_BROADCAST || + skb->pkt_type == PACKET_MULTICAST) + sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr, + uh->source, iph->saddr, dif); + else if (skb->pkt_type == PACKET_HOST) + sk = __udp4_lib_demux_lookup(net, uh->dest, iph->daddr, + uh->source, iph->saddr, dif); + else + return; + + if (!sk) + return; + + skb->sk = sk; + skb->destructor = sock_edemux; + dst = sk->sk_rx_dst; + + if (dst) + dst = dst_check(dst, 0); + if (dst) + skb_dst_set_noref(skb, dst); +} + int udp_rcv(struct sk_buff *skb) { return __udp4_lib_rcv(skb, &udp_table, IPPROTO_UDP); diff --git a/net/ipv4/udp_impl.h b/net/ipv4/udp_impl.h index 5a681e298b905127cdd5f0655d136754ff6eaabb..f3c27899f62b914f7b406094472a47e2fe2df613 100644 --- a/net/ipv4/udp_impl.h +++ b/net/ipv4/udp_impl.h @@ -5,30 +5,30 @@ #include #include -extern int __udp4_lib_rcv(struct sk_buff *, struct udp_table *, int ); -extern void __udp4_lib_err(struct sk_buff *, u32, struct udp_table *); +int __udp4_lib_rcv(struct sk_buff *, struct udp_table *, int); +void __udp4_lib_err(struct sk_buff *, u32, struct udp_table *); -extern int udp_v4_get_port(struct sock *sk, unsigned short snum); +int udp_v4_get_port(struct sock *sk, unsigned short snum); -extern int udp_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen); -extern int udp_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen); +int udp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); +int udp_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); #ifdef CONFIG_COMPAT -extern int compat_udp_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen); -extern int compat_udp_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen); +int compat_udp_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); +int compat_udp_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); #endif -extern int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, - size_t len, int noblock, int flags, int *addr_len); -extern int udp_sendpage(struct sock *sk, struct page *page, int offset, - size_t size, int flags); -extern int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); -extern void udp_destroy_sock(struct sock *sk); +int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len); +int udp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, + int flags); +int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); +void udp_destroy_sock(struct sock *sk); #ifdef CONFIG_PROC_FS -extern int udp4_seq_show(struct seq_file *seq, void *v); +int udp4_seq_show(struct seq_file *seq, void *v); #endif #endif /* _UDP4_IMPL_H */ diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index f35eccaa855ebfbac508503c9111311d6728f8ff..83206de2bc7679dc20e4fdcf34f8b1f5cd7831ef 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -52,6 +52,7 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_UDP_TUNNEL | + SKB_GSO_IPIP | SKB_GSO_GRE | SKB_GSO_MPLS) || !(type & (SKB_GSO_UDP)))) goto out; diff --git a/net/ipv4/xfrm4_mode_tunnel.c b/net/ipv4/xfrm4_mode_tunnel.c index b5663c37f089ed0afe33115bcbfad2555b8d0f48..31b18152528fe4dbf9e500ae0c9a2a1a5a3a2adf 100644 --- a/net/ipv4/xfrm4_mode_tunnel.c +++ b/net/ipv4/xfrm4_mode_tunnel.c @@ -16,13 +16,13 @@ #include /* Informational hook. The decap is still done here. */ -static struct xfrm_tunnel __rcu *rcv_notify_handlers __read_mostly; +static struct xfrm_tunnel_notifier __rcu *rcv_notify_handlers __read_mostly; static DEFINE_MUTEX(xfrm4_mode_tunnel_input_mutex); -int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel *handler) +int xfrm4_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler) { - struct xfrm_tunnel __rcu **pprev; - struct xfrm_tunnel *t; + struct xfrm_tunnel_notifier __rcu **pprev; + struct xfrm_tunnel_notifier *t; int ret = -EEXIST; int priority = handler->priority; @@ -50,10 +50,10 @@ err: } EXPORT_SYMBOL_GPL(xfrm4_mode_tunnel_input_register); -int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel *handler) +int xfrm4_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler) { - struct xfrm_tunnel __rcu **pprev; - struct xfrm_tunnel *t; + struct xfrm_tunnel_notifier __rcu **pprev; + struct xfrm_tunnel_notifier *t; int ret = -ENOENT; mutex_lock(&xfrm4_mode_tunnel_input_mutex); @@ -134,7 +134,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) static int xfrm4_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) { - struct xfrm_tunnel *handler; + struct xfrm_tunnel_notifier *handler; int err = -EINVAL; if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPIP) diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 11b13ea69db4e6c35c8b992de9055bf6a312d802..d92e5586783e518c1f5db2b1bd3261766ff7a913 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -21,24 +21,6 @@ menuconfig IPV6 if IPV6 -config IPV6_PRIVACY - bool "IPv6: Privacy Extensions (RFC 3041) support" - ---help--- - Privacy Extensions for Stateless Address Autoconfiguration in IPv6 - support. With this option, additional periodically-altered - pseudo-random global-scope unicast address(es) will be assigned to - your interface(s). - - We use our standard pseudo-random algorithm to generate the - randomized interface identifier, instead of one described in RFC 3041. - - By default the kernel does not generate temporary addresses. - To use temporary addresses, do - - echo 2 >/proc/sys/net/ipv6/conf/all/use_tempaddr - - See for details. - config IPV6_ROUTER_PREF bool "IPv6: Router Preference (RFC 4191) support" ---help--- @@ -153,6 +135,17 @@ config INET6_XFRM_MODE_ROUTEOPTIMIZATION ---help--- Support for MIPv6 route optimization mode. +config IPV6_VTI +tristate "Virtual (secure) IPv6: tunneling" + select IPV6_TUNNEL + depends on INET6_XFRM_MODE_TUNNEL + ---help--- + Tunneling means encapsulating data of one protocol type within + another protocol and sending it over a channel that understands the + encapsulating protocol. This can be used with xfrm mode tunnel to give + the notion of a secure tunnel for IPSEC and then use routing protocol + on top. + config IPV6_SIT tristate "IPv6: IPv6-in-IPv4 tunnel (SIT driver)" select INET_TUNNEL diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 470a9c008e9b9e921045bb5b11b77569e53ac5c9..17bb830872db21e07080ed9f6a0ef93cbccee9b7 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o obj-$(CONFIG_IPV6_MIP6) += mip6.o obj-$(CONFIG_NETFILTER) += netfilter/ +obj-$(CONFIG_IPV6_VTI) += ip6_vti.o obj-$(CONFIG_IPV6_SIT) += sit.o obj-$(CONFIG_IPV6_TUNNEL) += ip6_tunnel.o obj-$(CONFIG_IPV6_GRE) += ip6_gre.o diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index cd3fb301da38a970cd48302386428a8923f21c91..542d09561ed6df588c87946e50ba458ea20c9cf1 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -83,11 +83,7 @@ #include #include #include - -#ifdef CONFIG_IPV6_PRIVACY #include -#endif - #include #include @@ -124,11 +120,9 @@ static inline void addrconf_sysctl_unregister(struct inet6_dev *idev) } #endif -#ifdef CONFIG_IPV6_PRIVACY static void __ipv6_regen_rndid(struct inet6_dev *idev); static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmpaddr); static void ipv6_regen_rndid(unsigned long data); -#endif static int ipv6_generate_eui64(u8 *eui, struct net_device *dev); static int ipv6_count_addresses(struct inet6_dev *idev); @@ -183,13 +177,11 @@ static struct ipv6_devconf ipv6_devconf __read_mostly = { .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, -#ifdef CONFIG_IPV6_PRIVACY .use_tempaddr = 0, .temp_valid_lft = TEMP_VALID_LIFETIME, .temp_prefered_lft = TEMP_PREFERRED_LIFETIME, .regen_max_retry = REGEN_MAX_RETRY, .max_desync_factor = MAX_DESYNC_FACTOR, -#endif .max_addresses = IPV6_MAX_ADDRESSES, .accept_ra_defrtr = 1, .accept_ra_pinfo = 1, @@ -221,13 +213,11 @@ static struct ipv6_devconf ipv6_devconf_dflt __read_mostly = { .rtr_solicits = MAX_RTR_SOLICITATIONS, .rtr_solicit_interval = RTR_SOLICITATION_INTERVAL, .rtr_solicit_delay = MAX_RTR_SOLICITATION_DELAY, -#ifdef CONFIG_IPV6_PRIVACY .use_tempaddr = 0, .temp_valid_lft = TEMP_VALID_LIFETIME, .temp_prefered_lft = TEMP_PREFERRED_LIFETIME, .regen_max_retry = REGEN_MAX_RETRY, .max_desync_factor = MAX_DESYNC_FACTOR, -#endif .max_addresses = IPV6_MAX_ADDRESSES, .accept_ra_defrtr = 1, .accept_ra_pinfo = 1, @@ -371,7 +361,6 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) } #endif -#ifdef CONFIG_IPV6_PRIVACY INIT_LIST_HEAD(&ndev->tempaddr_list); setup_timer(&ndev->regen_timer, ipv6_regen_rndid, (unsigned long)ndev); if ((dev->flags&IFF_LOOPBACK) || @@ -384,7 +373,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev) in6_dev_hold(ndev); ipv6_regen_rndid((unsigned long) ndev); } -#endif + ndev->token = in6addr_any; if (netif_running(dev) && addrconf_qdisc_ok(dev)) @@ -865,12 +854,10 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, /* Add to inet6_dev unicast addr list. */ ipv6_link_dev_addr(idev, ifa); -#ifdef CONFIG_IPV6_PRIVACY if (ifa->flags&IFA_F_TEMPORARY) { list_add(&ifa->tmp_list, &idev->tempaddr_list); in6_ifa_hold(ifa); } -#endif in6_ifa_hold(ifa); write_unlock(&idev->lock); @@ -913,7 +900,7 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) spin_unlock_bh(&addrconf_hash_lock); write_lock_bh(&idev->lock); -#ifdef CONFIG_IPV6_PRIVACY + if (ifp->flags&IFA_F_TEMPORARY) { list_del(&ifp->tmp_list); if (ifp->ifpub) { @@ -922,7 +909,6 @@ static void ipv6_del_addr(struct inet6_ifaddr *ifp) } __in6_ifa_put(ifp); } -#endif list_for_each_entry_safe(ifa, ifn, &idev->addr_list, if_list) { if (ifa == ifp) { @@ -1013,7 +999,6 @@ out: in6_ifa_put(ifp); } -#ifdef CONFIG_IPV6_PRIVACY static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *ift) { struct inet6_dev *idev = ifp->idev; @@ -1116,7 +1101,6 @@ retry: out: return ret; } -#endif /* * Choose an appropriate source address (RFC3484) @@ -1131,9 +1115,7 @@ enum { #endif IPV6_SADDR_RULE_OIF, IPV6_SADDR_RULE_LABEL, -#ifdef CONFIG_IPV6_PRIVACY IPV6_SADDR_RULE_PRIVACY, -#endif IPV6_SADDR_RULE_ORCHID, IPV6_SADDR_RULE_PREFIX, IPV6_SADDR_RULE_MAX @@ -1247,7 +1229,6 @@ static int ipv6_get_saddr_eval(struct net *net, &score->ifa->addr, score->addr_type, score->ifa->idev->dev->ifindex) == dst->label; break; -#ifdef CONFIG_IPV6_PRIVACY case IPV6_SADDR_RULE_PRIVACY: { /* Rule 7: Prefer public address @@ -1259,7 +1240,6 @@ static int ipv6_get_saddr_eval(struct net *net, ret = (!(score->ifa->flags & IFA_F_TEMPORARY)) ^ preftmp; break; } -#endif case IPV6_SADDR_RULE_ORCHID: /* Rule 8-: Prefer ORCHID vs ORCHID or * non-ORCHID vs non-ORCHID @@ -1588,7 +1568,6 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed) if (dad_failed) ipv6_ifa_notify(0, ifp); in6_ifa_put(ifp); -#ifdef CONFIG_IPV6_PRIVACY } else if (ifp->flags&IFA_F_TEMPORARY) { struct inet6_ifaddr *ifpub; spin_lock_bh(&ifp->lock); @@ -1602,7 +1581,6 @@ static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed) spin_unlock_bh(&ifp->lock); } ipv6_del_addr(ifp); -#endif } else ipv6_del_addr(ifp); } @@ -1851,7 +1829,6 @@ static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) return err; } -#ifdef CONFIG_IPV6_PRIVACY /* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */ static void __ipv6_regen_rndid(struct inet6_dev *idev) { @@ -1919,7 +1896,6 @@ static void __ipv6_try_regen_rndid(struct inet6_dev *idev, struct in6_addr *tmp if (tmpaddr && memcmp(idev->rndid, &tmpaddr->s6_addr[8], 8) == 0) __ipv6_regen_rndid(idev); } -#endif /* * Add prefix route. @@ -2207,9 +2183,7 @@ ok: if (ifp) { int flags; unsigned long now; -#ifdef CONFIG_IPV6_PRIVACY struct inet6_ifaddr *ift; -#endif u32 stored_lft; /* update lifetime (RFC2462 5.5.3 e) */ @@ -2250,7 +2224,6 @@ ok: } else spin_unlock(&ifp->lock); -#ifdef CONFIG_IPV6_PRIVACY read_lock_bh(&in6_dev->lock); /* update all temporary addresses in the list */ list_for_each_entry(ift, &in6_dev->tempaddr_list, @@ -2315,7 +2288,7 @@ ok: } else { read_unlock_bh(&in6_dev->lock); } -#endif + in6_ifa_put(ifp); addrconf_verify(0); } @@ -2995,7 +2968,6 @@ static int addrconf_ifdown(struct net_device *dev, int how) if (!how) idev->if_flags &= ~(IF_RS_SENT|IF_RA_RCVD|IF_READY); -#ifdef CONFIG_IPV6_PRIVACY if (how && del_timer(&idev->regen_timer)) in6_dev_put(idev); @@ -3015,7 +2987,6 @@ static int addrconf_ifdown(struct net_device *dev, int how) in6_ifa_put(ifa); write_lock_bh(&idev->lock); } -#endif while (!list_empty(&idev->addr_list)) { ifa = list_first_entry(&idev->addr_list, @@ -3528,7 +3499,6 @@ restart: in6_ifa_put(ifp); goto restart; } -#ifdef CONFIG_IPV6_PRIVACY } else if ((ifp->flags&IFA_F_TEMPORARY) && !(ifp->flags&IFA_F_TENTATIVE)) { unsigned long regen_advance = ifp->idev->cnf.regen_max_retry * @@ -3556,7 +3526,6 @@ restart: } else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next)) next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ; spin_unlock(&ifp->lock); -#endif } else { /* ifp->prefered_lft <= ifp->valid_lft */ if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next)) @@ -4128,13 +4097,11 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf, jiffies_to_msecs(cnf->mldv1_unsolicited_report_interval); array[DEVCONF_MLDV2_UNSOLICITED_REPORT_INTERVAL] = jiffies_to_msecs(cnf->mldv2_unsolicited_report_interval); -#ifdef CONFIG_IPV6_PRIVACY array[DEVCONF_USE_TEMPADDR] = cnf->use_tempaddr; array[DEVCONF_TEMP_VALID_LFT] = cnf->temp_valid_lft; array[DEVCONF_TEMP_PREFERED_LFT] = cnf->temp_prefered_lft; array[DEVCONF_REGEN_MAX_RETRY] = cnf->regen_max_retry; array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor; -#endif array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses; array[DEVCONF_ACCEPT_RA_DEFRTR] = cnf->accept_ra_defrtr; array[DEVCONF_ACCEPT_RA_PINFO] = cnf->accept_ra_pinfo; @@ -4828,7 +4795,6 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = proc_dointvec_ms_jiffies, }, -#ifdef CONFIG_IPV6_PRIVACY { .procname = "use_tempaddr", .data = &ipv6_devconf.use_tempaddr, @@ -4864,7 +4830,6 @@ static struct addrconf_sysctl_table .mode = 0644, .proc_handler = proc_dointvec, }, -#endif { .procname = "max_addresses", .data = &ipv6_devconf.max_addresses, diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 7c96100b021ef0558a739b6da719246548153b11..6468bda1f2b94c382fe7212135ed1245f71e221f 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -110,11 +110,6 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, int try_loading_module = 0; int err; - if (sock->type != SOCK_RAW && - sock->type != SOCK_DGRAM && - !inet_ehash_secret) - build_ehash_secret(); - /* Look for the requested type/protocol pair. */ lookup_protocol: err = -ESOCKTNOSUPPORT; @@ -364,7 +359,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) inet->inet_rcv_saddr = v4addr; inet->inet_saddr = v4addr; - np->rcv_saddr = addr->sin6_addr; + sk->sk_v6_rcv_saddr = addr->sin6_addr; if (!(addr_type & IPV6_ADDR_MULTICAST)) np->saddr = addr->sin6_addr; @@ -461,14 +456,14 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, peer == 1) return -ENOTCONN; sin->sin6_port = inet->inet_dport; - sin->sin6_addr = np->daddr; + sin->sin6_addr = sk->sk_v6_daddr; if (np->sndflow) sin->sin6_flowinfo = np->flow_label; } else { - if (ipv6_addr_any(&np->rcv_saddr)) + if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) sin->sin6_addr = np->saddr; else - sin->sin6_addr = np->rcv_saddr; + sin->sin6_addr = sk->sk_v6_rcv_saddr; sin->sin6_port = inet->inet_sport; } @@ -655,7 +650,7 @@ int inet6_sk_rebuild_header(struct sock *sk) memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = sk->sk_protocol; - fl6.daddr = np->daddr; + fl6.daddr = sk->sk_v6_daddr; fl6.saddr = np->saddr; fl6.flowlabel = np->flow_label; fl6.flowi6_oif = sk->sk_bound_dev_if; @@ -870,8 +865,6 @@ static int __init inet6_init(void) if (err) goto out_sock_register_fail; - tcpv6_prot.sysctl_mem = init_net.ipv4.sysctl_tcp_mem; - /* * ipngwg API draft makes clear that the correct semantics * for TCP and UDP is to consider one TCP and UDP instance @@ -1028,52 +1021,4 @@ out_unregister_tcp_proto: } module_init(inet6_init); -static void __exit inet6_exit(void) -{ - if (disable_ipv6_mod) - return; - - /* First of all disallow new sockets creation. */ - sock_unregister(PF_INET6); - /* Disallow any further netlink messages */ - rtnl_unregister_all(PF_INET6); - - udpv6_exit(); - udplitev6_exit(); - tcpv6_exit(); - - /* Cleanup code parts. */ - ipv6_packet_cleanup(); - ipv6_frag_exit(); - ipv6_exthdrs_exit(); - addrconf_cleanup(); - ip6_flowlabel_cleanup(); - ndisc_late_cleanup(); - ip6_route_cleanup(); -#ifdef CONFIG_PROC_FS - - /* Cleanup code parts. */ - if6_proc_exit(); - ipv6_misc_proc_exit(); - udplite6_proc_exit(); - raw6_proc_exit(); -#endif - ipv6_netfilter_fini(); - ipv6_stub = NULL; - igmp6_cleanup(); - ndisc_cleanup(); - ip6_mr_cleanup(); - icmpv6_cleanup(); - rawv6_exit(); - - unregister_pernet_subsys(&inet6_net_ops); - proto_unregister(&rawv6_prot); - proto_unregister(&udplitev6_prot); - proto_unregister(&udpv6_prot); - proto_unregister(&tcpv6_prot); - - rcu_barrier(); /* Wait for completion of call_rcu()'s */ -} -module_exit(inet6_exit); - MODULE_ALIAS_NETPROTO(PF_INET6); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 48b6bd2a9a1451b7adf9a678ccce859003d57a79..a454b0ff57c7c67a91e2e5c887609e7a65e5a910 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -107,16 +107,16 @@ ipv4_connected: if (err) goto out; - ipv6_addr_set_v4mapped(inet->inet_daddr, &np->daddr); + ipv6_addr_set_v4mapped(inet->inet_daddr, &sk->sk_v6_daddr); if (ipv6_addr_any(&np->saddr) || ipv6_mapped_addr_any(&np->saddr)) ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr); - if (ipv6_addr_any(&np->rcv_saddr) || - ipv6_mapped_addr_any(&np->rcv_saddr)) { + if (ipv6_addr_any(&sk->sk_v6_rcv_saddr) || + ipv6_mapped_addr_any(&sk->sk_v6_rcv_saddr)) { ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, - &np->rcv_saddr); + &sk->sk_v6_rcv_saddr); if (sk->sk_prot->rehash) sk->sk_prot->rehash(sk); } @@ -145,7 +145,7 @@ ipv4_connected: } } - np->daddr = *daddr; + sk->sk_v6_daddr = *daddr; np->flow_label = fl6.flowlabel; inet->inet_dport = usin->sin6_port; @@ -156,7 +156,7 @@ ipv4_connected: */ fl6.flowi6_proto = sk->sk_protocol; - fl6.daddr = np->daddr; + fl6.daddr = sk->sk_v6_daddr; fl6.saddr = np->saddr; fl6.flowi6_oif = sk->sk_bound_dev_if; fl6.flowi6_mark = sk->sk_mark; @@ -183,16 +183,16 @@ ipv4_connected: if (ipv6_addr_any(&np->saddr)) np->saddr = fl6.saddr; - if (ipv6_addr_any(&np->rcv_saddr)) { - np->rcv_saddr = fl6.saddr; + if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { + sk->sk_v6_rcv_saddr = fl6.saddr; inet->inet_rcv_saddr = LOOPBACK4_IPV6; if (sk->sk_prot->rehash) sk->sk_prot->rehash(sk); } ip6_dst_store(sk, dst, - ipv6_addr_equal(&fl6.daddr, &np->daddr) ? - &np->daddr : NULL, + ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ? + &sk->sk_v6_daddr : NULL, #ifdef CONFIG_IPV6_SUBTREES ipv6_addr_equal(&fl6.saddr, &np->saddr) ? &np->saddr : @@ -883,11 +883,10 @@ EXPORT_SYMBOL_GPL(ip6_datagram_send_ctl); void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp, __u16 srcp, __u16 destp, int bucket) { - struct ipv6_pinfo *np = inet6_sk(sp); const struct in6_addr *dest, *src; - dest = &np->daddr; - src = &np->rcv_saddr; + dest = &sp->sk_v6_daddr; + src = &sp->sk_v6_rcv_saddr; seq_printf(seq, "%5d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X " "%02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %d\n", diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index e67e63f9858d7feae9e6fba4de667edb8f81875d..b8719df0366eb30759b39c72fd8ba28a0c0f67ed 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -164,10 +164,9 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) u8 *iv; u8 *tail; __be32 *seqhi; - struct esp_data *esp = x->data; /* skb is pure payload to encrypt */ - aead = esp->aead; + aead = x->data; alen = crypto_aead_authsize(aead); tfclen = 0; @@ -181,8 +180,6 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) } blksize = ALIGN(crypto_aead_blocksize(aead), 4); clen = ALIGN(skb->len + 2 + tfclen, blksize); - if (esp->padlen) - clen = ALIGN(clen, esp->padlen); plen = clen - skb->len - tfclen; err = skb_cow_data(skb, tfclen + plen + alen, &trailer); @@ -271,8 +268,7 @@ error: static int esp_input_done2(struct sk_buff *skb, int err) { struct xfrm_state *x = xfrm_input_state(skb); - struct esp_data *esp = x->data; - struct crypto_aead *aead = esp->aead; + struct crypto_aead *aead = x->data; int alen = crypto_aead_authsize(aead); int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); int elen = skb->len - hlen; @@ -325,8 +321,7 @@ static void esp_input_done(struct crypto_async_request *base, int err) static int esp6_input(struct xfrm_state *x, struct sk_buff *skb) { struct ip_esp_hdr *esph; - struct esp_data *esp = x->data; - struct crypto_aead *aead = esp->aead; + struct crypto_aead *aead = x->data; struct aead_request *req; struct sk_buff *trailer; int elen = skb->len - sizeof(*esph) - crypto_aead_ivsize(aead); @@ -414,9 +409,8 @@ out: static u32 esp6_get_mtu(struct xfrm_state *x, int mtu) { - struct esp_data *esp = x->data; - u32 blksize = ALIGN(crypto_aead_blocksize(esp->aead), 4); - u32 align = max_t(u32, blksize, esp->padlen); + struct crypto_aead *aead = x->data; + u32 blksize = ALIGN(crypto_aead_blocksize(aead), 4); unsigned int net_adj; if (x->props.mode != XFRM_MODE_TUNNEL) @@ -424,8 +418,8 @@ static u32 esp6_get_mtu(struct xfrm_state *x, int mtu) else net_adj = 0; - return ((mtu - x->props.header_len - crypto_aead_authsize(esp->aead) - - net_adj) & ~(align - 1)) + net_adj - 2; + return ((mtu - x->props.header_len - crypto_aead_authsize(aead) - + net_adj) & ~(blksize - 1)) + net_adj - 2; } static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, @@ -454,18 +448,16 @@ static void esp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, static void esp6_destroy(struct xfrm_state *x) { - struct esp_data *esp = x->data; + struct crypto_aead *aead = x->data; - if (!esp) + if (!aead) return; - crypto_free_aead(esp->aead); - kfree(esp); + crypto_free_aead(aead); } static int esp_init_aead(struct xfrm_state *x) { - struct esp_data *esp = x->data; struct crypto_aead *aead; int err; @@ -474,7 +466,7 @@ static int esp_init_aead(struct xfrm_state *x) if (IS_ERR(aead)) goto error; - esp->aead = aead; + x->data = aead; err = crypto_aead_setkey(aead, x->aead->alg_key, (x->aead->alg_key_len + 7) / 8); @@ -491,7 +483,6 @@ error: static int esp_init_authenc(struct xfrm_state *x) { - struct esp_data *esp = x->data; struct crypto_aead *aead; struct crypto_authenc_key_param *param; struct rtattr *rta; @@ -526,7 +517,7 @@ static int esp_init_authenc(struct xfrm_state *x) if (IS_ERR(aead)) goto error; - esp->aead = aead; + x->data = aead; keylen = (x->aalg ? (x->aalg->alg_key_len + 7) / 8 : 0) + (x->ealg->alg_key_len + 7) / 8 + RTA_SPACE(sizeof(*param)); @@ -581,7 +572,6 @@ error: static int esp6_init_state(struct xfrm_state *x) { - struct esp_data *esp; struct crypto_aead *aead; u32 align; int err; @@ -589,11 +579,7 @@ static int esp6_init_state(struct xfrm_state *x) if (x->encap) return -EINVAL; - esp = kzalloc(sizeof(*esp), GFP_KERNEL); - if (esp == NULL) - return -ENOMEM; - - x->data = esp; + x->data = NULL; if (x->aead) err = esp_init_aead(x); @@ -603,9 +589,7 @@ static int esp6_init_state(struct xfrm_state *x) if (err) goto error; - aead = esp->aead; - - esp->padlen = 0; + aead = x->data; x->props.header_len = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); @@ -625,9 +609,7 @@ static int esp6_init_state(struct xfrm_state *x) } align = ALIGN(crypto_aead_blocksize(aead), 4); - if (esp->padlen) - align = max_t(u32, align, esp->padlen); - x->props.trailer_len = align + 1 + crypto_aead_authsize(esp->aead); + x->props.trailer_len = align + 1 + crypto_aead_authsize(aead); error: return err; diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index e4311cbc8b4ecbf70ea1fb2e2f2415342be382cc..77bb8afb141d6349e3de622e0f20452552d70344 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -70,20 +70,20 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, struct flowi6 *fl6, const struct request_sock *req) { - struct inet6_request_sock *treq = inet6_rsk(req); + struct inet_request_sock *ireq = inet_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); struct in6_addr *final_p, final; struct dst_entry *dst; memset(fl6, 0, sizeof(*fl6)); fl6->flowi6_proto = IPPROTO_TCP; - fl6->daddr = treq->rmt_addr; + fl6->daddr = ireq->ir_v6_rmt_addr; final_p = fl6_update_dst(fl6, np->opt, &final); - fl6->saddr = treq->loc_addr; - fl6->flowi6_oif = treq->iif; + fl6->saddr = ireq->ir_v6_loc_addr; + fl6->flowi6_oif = ireq->ir_iif; fl6->flowi6_mark = sk->sk_mark; - fl6->fl6_dport = inet_rsk(req)->rmt_port; - fl6->fl6_sport = inet_rsk(req)->loc_port; + fl6->fl6_dport = ireq->ir_rmt_port; + fl6->fl6_sport = htons(ireq->ir_num); security_req_classify_flow(req, flowi6_to_flowi(fl6)); dst = ip6_dst_lookup_flow(sk, fl6, final_p, false); @@ -129,13 +129,13 @@ struct request_sock *inet6_csk_search_req(const struct sock *sk, lopt->nr_table_entries)]; (req = *prev) != NULL; prev = &req->dl_next) { - const struct inet6_request_sock *treq = inet6_rsk(req); + const struct inet_request_sock *ireq = inet_rsk(req); - if (inet_rsk(req)->rmt_port == rport && + if (ireq->ir_rmt_port == rport && req->rsk_ops->family == AF_INET6 && - ipv6_addr_equal(&treq->rmt_addr, raddr) && - ipv6_addr_equal(&treq->loc_addr, laddr) && - (!treq->iif || treq->iif == iif)) { + ipv6_addr_equal(&ireq->ir_v6_rmt_addr, raddr) && + ipv6_addr_equal(&ireq->ir_v6_loc_addr, laddr) && + (!ireq->ir_iif || ireq->ir_iif == iif)) { WARN_ON(req->sk != NULL); *prevp = prev; return req; @@ -153,8 +153,8 @@ void inet6_csk_reqsk_queue_hash_add(struct sock *sk, { struct inet_connection_sock *icsk = inet_csk(sk); struct listen_sock *lopt = icsk->icsk_accept_queue.listen_opt; - const u32 h = inet6_synq_hash(&inet6_rsk(req)->rmt_addr, - inet_rsk(req)->rmt_port, + const u32 h = inet6_synq_hash(&inet_rsk(req)->ir_v6_rmt_addr, + inet_rsk(req)->ir_rmt_port, lopt->hash_rnd, lopt->nr_table_entries); reqsk_queue_hash_req(&icsk->icsk_accept_queue, h, req, timeout); @@ -165,11 +165,10 @@ EXPORT_SYMBOL_GPL(inet6_csk_reqsk_queue_hash_add); void inet6_csk_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr) { - struct ipv6_pinfo *np = inet6_sk(sk); struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) uaddr; sin6->sin6_family = AF_INET6; - sin6->sin6_addr = np->daddr; + sin6->sin6_addr = sk->sk_v6_daddr; sin6->sin6_port = inet_sk(sk)->inet_dport; /* We do not store received flowlabel for TCP */ sin6->sin6_flowinfo = 0; @@ -203,7 +202,7 @@ static struct dst_entry *inet6_csk_route_socket(struct sock *sk, memset(fl6, 0, sizeof(*fl6)); fl6->flowi6_proto = sk->sk_protocol; - fl6->daddr = np->daddr; + fl6->daddr = sk->sk_v6_daddr; fl6->saddr = np->saddr; fl6->flowlabel = np->flow_label; IP6_ECN_flow_xmit(sk, fl6->flowlabel); @@ -245,7 +244,7 @@ int inet6_csk_xmit(struct sk_buff *skb, struct flowi *fl_unused) skb_dst_set_noref(skb, dst); /* Restore final destination back after routing done */ - fl6.daddr = np->daddr; + fl6.daddr = sk->sk_v6_daddr; res = ip6_xmit(sk, skb, &fl6, np->opt, np->tclass); rcu_read_unlock(); diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index 066640e0ba8e3b5f4759cabcfc5bbe125ef2df20..262e13c02ec27dea15154d9b4af5fd413dd1c504 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -23,6 +23,39 @@ #include #include +static unsigned int inet6_ehashfn(struct net *net, + const struct in6_addr *laddr, + const u16 lport, + const struct in6_addr *faddr, + const __be16 fport) +{ + static u32 inet6_ehash_secret __read_mostly; + static u32 ipv6_hash_secret __read_mostly; + + u32 lhash, fhash; + + net_get_random_once(&inet6_ehash_secret, sizeof(inet6_ehash_secret)); + net_get_random_once(&ipv6_hash_secret, sizeof(ipv6_hash_secret)); + + lhash = (__force u32)laddr->s6_addr32[3]; + fhash = __ipv6_addr_jhash(faddr, ipv6_hash_secret); + + return __inet6_ehashfn(lhash, lport, fhash, fport, + inet6_ehash_secret + net_hash_mix(net)); +} + +static int inet6_sk_ehashfn(const struct sock *sk) +{ + const struct inet_sock *inet = inet_sk(sk); + const struct in6_addr *laddr = &sk->sk_v6_rcv_saddr; + const struct in6_addr *faddr = &sk->sk_v6_daddr; + const __u16 lport = inet->inet_num; + const __be16 fport = inet->inet_dport; + struct net *net = sock_net(sk); + + return inet6_ehashfn(net, laddr, lport, faddr, fport); +} + int __inet6_hash(struct sock *sk, struct inet_timewait_sock *tw) { struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; @@ -89,43 +122,22 @@ begin: sk_nulls_for_each_rcu(sk, node, &head->chain) { if (sk->sk_hash != hash) continue; - if (likely(INET6_MATCH(sk, net, saddr, daddr, ports, dif))) { - if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) - goto begintw; - if (unlikely(!INET6_MATCH(sk, net, saddr, daddr, - ports, dif))) { - sock_put(sk); - goto begin; - } - goto out; - } - } - if (get_nulls_value(node) != slot) - goto begin; - -begintw: - /* Must check for a TIME_WAIT'er before going to listener hash. */ - sk_nulls_for_each_rcu(sk, node, &head->twchain) { - if (sk->sk_hash != hash) + if (!INET6_MATCH(sk, net, saddr, daddr, ports, dif)) continue; - if (likely(INET6_TW_MATCH(sk, net, saddr, daddr, - ports, dif))) { - if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) { - sk = NULL; - goto out; - } - if (unlikely(!INET6_TW_MATCH(sk, net, saddr, daddr, - ports, dif))) { - inet_twsk_put(inet_twsk(sk)); - goto begintw; - } + if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) goto out; + + if (unlikely(!INET6_MATCH(sk, net, saddr, daddr, ports, dif))) { + sock_gen_put(sk); + goto begin; } + goto found; } if (get_nulls_value(node) != slot) - goto begintw; - sk = NULL; + goto begin; out: + sk = NULL; +found: rcu_read_unlock(); return sk; } @@ -140,11 +152,10 @@ static inline int compute_score(struct sock *sk, struct net *net, if (net_eq(sock_net(sk), net) && inet_sk(sk)->inet_num == hnum && sk->sk_family == PF_INET6) { - const struct ipv6_pinfo *np = inet6_sk(sk); score = 1; - if (!ipv6_addr_any(&np->rcv_saddr)) { - if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) + if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { + if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) return -1; score++; } @@ -236,9 +247,8 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row, { struct inet_hashinfo *hinfo = death_row->hashinfo; struct inet_sock *inet = inet_sk(sk); - const struct ipv6_pinfo *np = inet6_sk(sk); - const struct in6_addr *daddr = &np->rcv_saddr; - const struct in6_addr *saddr = &np->daddr; + const struct in6_addr *daddr = &sk->sk_v6_rcv_saddr; + const struct in6_addr *saddr = &sk->sk_v6_daddr; const int dif = sk->sk_bound_dev_if; const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport); struct net *net = sock_net(sk); @@ -248,38 +258,28 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row, spinlock_t *lock = inet_ehash_lockp(hinfo, hash); struct sock *sk2; const struct hlist_nulls_node *node; - struct inet_timewait_sock *tw; + struct inet_timewait_sock *tw = NULL; int twrefcnt = 0; spin_lock(lock); - /* Check TIME-WAIT sockets first. */ - sk_nulls_for_each(sk2, node, &head->twchain) { - if (sk2->sk_hash != hash) - continue; - - if (likely(INET6_TW_MATCH(sk2, net, saddr, daddr, - ports, dif))) { - tw = inet_twsk(sk2); - if (twsk_unique(sk, sk2, twp)) - goto unique; - else - goto not_unique; - } - } - tw = NULL; - - /* And established part... */ sk_nulls_for_each(sk2, node, &head->chain) { if (sk2->sk_hash != hash) continue; - if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports, dif))) + + if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports, dif))) { + if (sk2->sk_state == TCP_TIME_WAIT) { + tw = inet_twsk(sk2); + if (twsk_unique(sk, sk2, twp)) + break; + } goto not_unique; + } } -unique: /* Must record num and sport now. Otherwise we will see - * in hash table socket with a funny identity. */ + * in hash table socket with a funny identity. + */ inet->inet_num = lport; inet->inet_sport = htons(lport); sk->sk_hash = hash; @@ -312,9 +312,9 @@ not_unique: static inline u32 inet6_sk_port_offset(const struct sock *sk) { const struct inet_sock *inet = inet_sk(sk); - const struct ipv6_pinfo *np = inet6_sk(sk); - return secure_ipv6_port_ephemeral(np->rcv_saddr.s6_addr32, - np->daddr.s6_addr32, + + return secure_ipv6_port_ephemeral(sk->sk_v6_rcv_saddr.s6_addr32, + sk->sk_v6_daddr.s6_addr32, inet->inet_dport); } diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 5bec666aba61d464fab4e77684eedd4265143cf9..5550a8113a6dc5f202c0ccda2c7166e98e81a7e9 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1529,25 +1529,6 @@ static void fib6_clean_tree(struct net *net, struct fib6_node *root, fib6_walk(&c.w); } -void fib6_clean_all_ro(struct net *net, int (*func)(struct rt6_info *, void *arg), - int prune, void *arg) -{ - struct fib6_table *table; - struct hlist_head *head; - unsigned int h; - - rcu_read_lock(); - for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { - head = &net->ipv6.fib_table_hash[h]; - hlist_for_each_entry_rcu(table, head, tb6_hlist) { - read_lock_bh(&table->tb6_lock); - fib6_clean_tree(net, &table->tb6_root, - func, prune, arg); - read_unlock_bh(&table->tb6_lock); - } - } - rcu_read_unlock(); -} void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg), int prune, void *arg) { @@ -1782,3 +1763,189 @@ void fib6_gc_cleanup(void) unregister_pernet_subsys(&fib6_net_ops); kmem_cache_destroy(fib6_node_kmem); } + +#ifdef CONFIG_PROC_FS + +struct ipv6_route_iter { + struct seq_net_private p; + struct fib6_walker_t w; + loff_t skip; + struct fib6_table *tbl; + __u32 sernum; +}; + +static int ipv6_route_seq_show(struct seq_file *seq, void *v) +{ + struct rt6_info *rt = v; + struct ipv6_route_iter *iter = seq->private; + + seq_printf(seq, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen); + +#ifdef CONFIG_IPV6_SUBTREES + seq_printf(seq, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen); +#else + seq_puts(seq, "00000000000000000000000000000000 00 "); +#endif + if (rt->rt6i_flags & RTF_GATEWAY) + seq_printf(seq, "%pi6", &rt->rt6i_gateway); + else + seq_puts(seq, "00000000000000000000000000000000"); + + seq_printf(seq, " %08x %08x %08x %08x %8s\n", + rt->rt6i_metric, atomic_read(&rt->dst.__refcnt), + rt->dst.__use, rt->rt6i_flags, + rt->dst.dev ? rt->dst.dev->name : ""); + iter->w.leaf = NULL; + return 0; +} + +static int ipv6_route_yield(struct fib6_walker_t *w) +{ + struct ipv6_route_iter *iter = w->args; + + if (!iter->skip) + return 1; + + do { + iter->w.leaf = iter->w.leaf->dst.rt6_next; + iter->skip--; + if (!iter->skip && iter->w.leaf) + return 1; + } while (iter->w.leaf); + + return 0; +} + +static void ipv6_route_seq_setup_walk(struct ipv6_route_iter *iter) +{ + memset(&iter->w, 0, sizeof(iter->w)); + iter->w.func = ipv6_route_yield; + iter->w.root = &iter->tbl->tb6_root; + iter->w.state = FWS_INIT; + iter->w.node = iter->w.root; + iter->w.args = iter; + iter->sernum = iter->w.root->fn_sernum; + INIT_LIST_HEAD(&iter->w.lh); + fib6_walker_link(&iter->w); +} + +static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl, + struct net *net) +{ + unsigned int h; + struct hlist_node *node; + + if (tbl) { + h = (tbl->tb6_id & (FIB6_TABLE_HASHSZ - 1)) + 1; + node = rcu_dereference_bh(hlist_next_rcu(&tbl->tb6_hlist)); + } else { + h = 0; + node = NULL; + } + + while (!node && h < FIB6_TABLE_HASHSZ) { + node = rcu_dereference_bh( + hlist_first_rcu(&net->ipv6.fib_table_hash[h++])); + } + return hlist_entry_safe(node, struct fib6_table, tb6_hlist); +} + +static void ipv6_route_check_sernum(struct ipv6_route_iter *iter) +{ + if (iter->sernum != iter->w.root->fn_sernum) { + iter->sernum = iter->w.root->fn_sernum; + iter->w.state = FWS_INIT; + iter->w.node = iter->w.root; + WARN_ON(iter->w.skip); + iter->w.skip = iter->w.count; + } +} + +static void *ipv6_route_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + int r; + struct rt6_info *n; + struct net *net = seq_file_net(seq); + struct ipv6_route_iter *iter = seq->private; + + if (!v) + goto iter_table; + + n = ((struct rt6_info *)v)->dst.rt6_next; + if (n) { + ++*pos; + return n; + } + +iter_table: + ipv6_route_check_sernum(iter); + read_lock(&iter->tbl->tb6_lock); + r = fib6_walk_continue(&iter->w); + read_unlock(&iter->tbl->tb6_lock); + if (r > 0) { + if (v) + ++*pos; + return iter->w.leaf; + } else if (r < 0) { + fib6_walker_unlink(&iter->w); + return NULL; + } + fib6_walker_unlink(&iter->w); + + iter->tbl = ipv6_route_seq_next_table(iter->tbl, net); + if (!iter->tbl) + return NULL; + + ipv6_route_seq_setup_walk(iter); + goto iter_table; +} + +static void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos) + __acquires(RCU_BH) +{ + struct net *net = seq_file_net(seq); + struct ipv6_route_iter *iter = seq->private; + + rcu_read_lock_bh(); + iter->tbl = ipv6_route_seq_next_table(NULL, net); + iter->skip = *pos; + + if (iter->tbl) { + ipv6_route_seq_setup_walk(iter); + return ipv6_route_seq_next(seq, NULL, pos); + } else { + return NULL; + } +} + +static bool ipv6_route_iter_active(struct ipv6_route_iter *iter) +{ + struct fib6_walker_t *w = &iter->w; + return w->node && !(w->state == FWS_U && w->node == w->root); +} + +static void ipv6_route_seq_stop(struct seq_file *seq, void *v) + __releases(RCU_BH) +{ + struct ipv6_route_iter *iter = seq->private; + + if (ipv6_route_iter_active(iter)) + fib6_walker_unlink(&iter->w); + + rcu_read_unlock_bh(); +} + +static const struct seq_operations ipv6_route_seq_ops = { + .start = ipv6_route_seq_start, + .next = ipv6_route_seq_next, + .stop = ipv6_route_seq_stop, + .show = ipv6_route_seq_show +}; + +int ipv6_route_open(struct inode *inode, struct file *file) +{ + return seq_open_net(inode, file, &ipv6_route_seq_ops, + sizeof(struct ipv6_route_iter)); +} + +#endif /* CONFIG_PROC_FS */ diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 46e88433ec7d229550eecadc29f8eacc139fe5b2..e7fb7106550f1d0c45bfa59b341681175be588b2 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -41,7 +41,7 @@ #define FL_MIN_LINGER 6 /* Minimal linger. It is set to 6sec specified in old IPv6 RFC. Well, it was reasonable value. */ -#define FL_MAX_LINGER 60 /* Maximal linger timeout */ +#define FL_MAX_LINGER 150 /* Maximal linger timeout */ /* FL hash table */ @@ -345,6 +345,8 @@ static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned lo expires = check_linger(expires); if (!expires) return -EPERM; + + spin_lock_bh(&ip6_fl_lock); fl->lastuse = jiffies; if (time_before(fl->linger, linger)) fl->linger = linger; @@ -352,6 +354,8 @@ static int fl6_renew(struct ip6_flowlabel *fl, unsigned long linger, unsigned lo expires = fl->linger; if (time_before(fl->expires, fl->lastuse + expires)) fl->expires = fl->lastuse + expires; + spin_unlock_bh(&ip6_fl_lock); + return 0; } @@ -453,8 +457,10 @@ static int mem_check(struct sock *sk) if (room > FL_MAX_SIZE - FL_MAX_PER_SOCK) return 0; + rcu_read_lock_bh(); for_each_sk_fl_rcu(np, sfl) count++; + rcu_read_unlock_bh(); if (room <= 0 || ((count >= FL_MAX_PER_SOCK || @@ -465,34 +471,6 @@ static int mem_check(struct sock *sk) return 0; } -static bool ipv6_hdr_cmp(struct ipv6_opt_hdr *h1, struct ipv6_opt_hdr *h2) -{ - if (h1 == h2) - return false; - if (h1 == NULL || h2 == NULL) - return true; - if (h1->hdrlen != h2->hdrlen) - return true; - return memcmp(h1+1, h2+1, ((h1->hdrlen+1)<<3) - sizeof(*h1)); -} - -static bool ipv6_opt_cmp(struct ipv6_txoptions *o1, struct ipv6_txoptions *o2) -{ - if (o1 == o2) - return false; - if (o1 == NULL || o2 == NULL) - return true; - if (o1->opt_nflen != o2->opt_nflen) - return true; - if (ipv6_hdr_cmp(o1->hopopt, o2->hopopt)) - return true; - if (ipv6_hdr_cmp(o1->dst0opt, o2->dst0opt)) - return true; - if (ipv6_hdr_cmp((struct ipv6_opt_hdr *)o1->srcrt, (struct ipv6_opt_hdr *)o2->srcrt)) - return true; - return false; -} - static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, struct ip6_flowlabel *fl) { @@ -503,6 +481,32 @@ static inline void fl_link(struct ipv6_pinfo *np, struct ipv6_fl_socklist *sfl, spin_unlock_bh(&ip6_sk_fl_lock); } +int ipv6_flowlabel_opt_get(struct sock *sk, struct in6_flowlabel_req *freq) +{ + struct ipv6_pinfo *np = inet6_sk(sk); + struct ipv6_fl_socklist *sfl; + + rcu_read_lock_bh(); + + for_each_sk_fl_rcu(np, sfl) { + if (sfl->fl->label == (np->flow_label & IPV6_FLOWLABEL_MASK)) { + spin_lock_bh(&ip6_fl_lock); + freq->flr_label = sfl->fl->label; + freq->flr_dst = sfl->fl->dst; + freq->flr_share = sfl->fl->share; + freq->flr_expires = (sfl->fl->expires - jiffies) / HZ; + freq->flr_linger = sfl->fl->linger / HZ; + + spin_unlock_bh(&ip6_fl_lock); + rcu_read_unlock_bh(); + return 0; + } + } + rcu_read_unlock_bh(); + + return -ENOENT; +} + int ipv6_flowlabel_opt(struct sock *sk, char __user *optval, int optlen) { int uninitialized_var(err); @@ -603,11 +607,6 @@ recheck: uid_eq(fl1->owner.uid, fl->owner.uid))) goto release; - err = -EINVAL; - if (!ipv6_addr_equal(&fl1->dst, &fl->dst) || - ipv6_opt_cmp(fl1->opt, fl->opt)) - goto release; - err = -ENOMEM; if (sfl1 == NULL) goto release; diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c index d82de72281009c1b93d1cbde1bac91c023ecd930..4b851692b1f6bed3fbe476c65672c45e28570b3e 100644 --- a/net/ipv6/ip6_offload.c +++ b/net/ipv6/ip6_offload.c @@ -66,7 +66,6 @@ static int ipv6_gso_send_check(struct sk_buff *skb) __skb_pull(skb, sizeof(*ipv6h)); err = -EPROTONOSUPPORT; - rcu_read_lock(); ops = rcu_dereference(inet6_offloads[ ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]); @@ -74,7 +73,6 @@ static int ipv6_gso_send_check(struct sk_buff *skb) skb_reset_transport_header(skb); err = ops->callbacks.gso_send_check(skb); } - rcu_read_unlock(); out: return err; @@ -92,46 +90,58 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, u8 *prevhdr; int offset = 0; bool tunnel; + int nhoff; if (unlikely(skb_shinfo(skb)->gso_type & ~(SKB_GSO_UDP | SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_GRE | + SKB_GSO_IPIP | + SKB_GSO_SIT | SKB_GSO_UDP_TUNNEL | SKB_GSO_MPLS | SKB_GSO_TCPV6 | 0))) goto out; + skb_reset_network_header(skb); + nhoff = skb_network_header(skb) - skb_mac_header(skb); if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) goto out; - tunnel = skb->encapsulation; + tunnel = SKB_GSO_CB(skb)->encap_level > 0; + if (tunnel) + features = skb->dev->hw_enc_features & netif_skb_features(skb); + SKB_GSO_CB(skb)->encap_level += sizeof(*ipv6h); + ipv6h = ipv6_hdr(skb); __skb_pull(skb, sizeof(*ipv6h)); segs = ERR_PTR(-EPROTONOSUPPORT); proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); - rcu_read_lock(); + ops = rcu_dereference(inet6_offloads[proto]); if (likely(ops && ops->callbacks.gso_segment)) { skb_reset_transport_header(skb); segs = ops->callbacks.gso_segment(skb, features); } - rcu_read_unlock(); if (IS_ERR(segs)) goto out; for (skb = segs; skb; skb = skb->next) { - ipv6h = ipv6_hdr(skb); - ipv6h->payload_len = htons(skb->len - skb->mac_len - - sizeof(*ipv6h)); + ipv6h = (struct ipv6hdr *)(skb_mac_header(skb) + nhoff); + ipv6h->payload_len = htons(skb->len - nhoff - sizeof(*ipv6h)); + if (tunnel) { + skb_reset_inner_headers(skb); + skb->encapsulation = 1; + } + skb->network_header = (u8 *)ipv6h - skb->head; + if (!tunnel && proto == IPPROTO_UDP) { unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr); - fptr = (struct frag_hdr *)(skb_network_header(skb) + - unfrag_ip6hlen); + fptr = (struct frag_hdr *)((u8 *)ipv6h + unfrag_ip6hlen); fptr->frag_off = htons(offset); if (skb->next != NULL) fptr->frag_off |= htons(IP6_MF); @@ -267,6 +277,13 @@ static struct packet_offload ipv6_packet_offload __read_mostly = { }, }; +static const struct net_offload sit_offload = { + .callbacks = { + .gso_send_check = ipv6_gso_send_check, + .gso_segment = ipv6_gso_segment, + }, +}; + static int __init ipv6_offload_init(void) { @@ -278,6 +295,9 @@ static int __init ipv6_offload_init(void) pr_crit("%s: Cannot add EXTHDRS protocol offload\n", __func__); dev_add_offload(&ipv6_packet_offload); + + inet_add_offload(&sit_offload, IPPROTO_IPV6); + return 0; } diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 91fb4e8212f52d434e6d072103f70dbb8d219326..5e31a909a2b0c215df8266a76f28d2d0218e9790 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -125,7 +125,8 @@ static int ip6_finish_output2(struct sk_buff *skb) static int ip6_finish_output(struct sk_buff *skb) { if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) || - dst_allfrag(skb_dst(skb))) + dst_allfrag(skb_dst(skb)) || + (IP6CB(skb)->frag_max_size && skb->len > IP6CB(skb)->frag_max_size)) return ip6_fragment(skb, ip6_finish_output2); else return ip6_finish_output2(skb); diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c new file mode 100644 index 0000000000000000000000000000000000000000..ed94ba61dda0ec1c3e2b170fe74eb2334b752700 --- /dev/null +++ b/net/ipv6/ip6_vti.c @@ -0,0 +1,1056 @@ +/* + * IPv6 virtual tunneling interface + * + * Copyright (C) 2013 secunet Security Networks AG + * + * Author: + * Steffen Klassert + * + * Based on: + * net/ipv6/ip6_tunnel.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HASH_SIZE_SHIFT 5 +#define HASH_SIZE (1 << HASH_SIZE_SHIFT) + +static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) +{ + u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2); + + return hash_32(hash, HASH_SIZE_SHIFT); +} + +static int vti6_dev_init(struct net_device *dev); +static void vti6_dev_setup(struct net_device *dev); +static struct rtnl_link_ops vti6_link_ops __read_mostly; + +static int vti6_net_id __read_mostly; +struct vti6_net { + /* the vti6 tunnel fallback device */ + struct net_device *fb_tnl_dev; + /* lists for storing tunnels in use */ + struct ip6_tnl __rcu *tnls_r_l[HASH_SIZE]; + struct ip6_tnl __rcu *tnls_wc[1]; + struct ip6_tnl __rcu **tnls[2]; +}; + +static struct net_device_stats *vti6_get_stats(struct net_device *dev) +{ + struct pcpu_tstats sum = { 0 }; + int i; + + for_each_possible_cpu(i) { + const struct pcpu_tstats *tstats = per_cpu_ptr(dev->tstats, i); + + sum.rx_packets += tstats->rx_packets; + sum.rx_bytes += tstats->rx_bytes; + sum.tx_packets += tstats->tx_packets; + sum.tx_bytes += tstats->tx_bytes; + } + dev->stats.rx_packets = sum.rx_packets; + dev->stats.rx_bytes = sum.rx_bytes; + dev->stats.tx_packets = sum.tx_packets; + dev->stats.tx_bytes = sum.tx_bytes; + return &dev->stats; +} + +#define for_each_vti6_tunnel_rcu(start) \ + for (t = rcu_dereference(start); t; t = rcu_dereference(t->next)) + +/** + * vti6_tnl_lookup - fetch tunnel matching the end-point addresses + * @net: network namespace + * @remote: the address of the tunnel exit-point + * @local: the address of the tunnel entry-point + * + * Return: + * tunnel matching given end-points if found, + * else fallback tunnel if its device is up, + * else %NULL + **/ +static struct ip6_tnl * +vti6_tnl_lookup(struct net *net, const struct in6_addr *remote, + const struct in6_addr *local) +{ + unsigned int hash = HASH(remote, local); + struct ip6_tnl *t; + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + + for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) { + if (ipv6_addr_equal(local, &t->parms.laddr) && + ipv6_addr_equal(remote, &t->parms.raddr) && + (t->dev->flags & IFF_UP)) + return t; + } + t = rcu_dereference(ip6n->tnls_wc[0]); + if (t && (t->dev->flags & IFF_UP)) + return t; + + return NULL; +} + +/** + * vti6_tnl_bucket - get head of list matching given tunnel parameters + * @p: parameters containing tunnel end-points + * + * Description: + * vti6_tnl_bucket() returns the head of the list matching the + * &struct in6_addr entries laddr and raddr in @p. + * + * Return: head of IPv6 tunnel list + **/ +static struct ip6_tnl __rcu ** +vti6_tnl_bucket(struct vti6_net *ip6n, const struct __ip6_tnl_parm *p) +{ + const struct in6_addr *remote = &p->raddr; + const struct in6_addr *local = &p->laddr; + unsigned int h = 0; + int prio = 0; + + if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { + prio = 1; + h = HASH(remote, local); + } + return &ip6n->tnls[prio][h]; +} + +static void +vti6_tnl_link(struct vti6_net *ip6n, struct ip6_tnl *t) +{ + struct ip6_tnl __rcu **tp = vti6_tnl_bucket(ip6n, &t->parms); + + rcu_assign_pointer(t->next , rtnl_dereference(*tp)); + rcu_assign_pointer(*tp, t); +} + +static void +vti6_tnl_unlink(struct vti6_net *ip6n, struct ip6_tnl *t) +{ + struct ip6_tnl __rcu **tp; + struct ip6_tnl *iter; + + for (tp = vti6_tnl_bucket(ip6n, &t->parms); + (iter = rtnl_dereference(*tp)) != NULL; + tp = &iter->next) { + if (t == iter) { + rcu_assign_pointer(*tp, t->next); + break; + } + } +} + +static void vti6_dev_free(struct net_device *dev) +{ + free_percpu(dev->tstats); + free_netdev(dev); +} + +static int vti6_tnl_create2(struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct net *net = dev_net(dev); + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + int err; + + err = vti6_dev_init(dev); + if (err < 0) + goto out; + + err = register_netdevice(dev); + if (err < 0) + goto out; + + strcpy(t->parms.name, dev->name); + dev->rtnl_link_ops = &vti6_link_ops; + + dev_hold(dev); + vti6_tnl_link(ip6n, t); + + return 0; + +out: + return err; +} + +static struct ip6_tnl *vti6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) +{ + struct net_device *dev; + struct ip6_tnl *t; + char name[IFNAMSIZ]; + int err; + + if (p->name[0]) + strlcpy(name, p->name, IFNAMSIZ); + else + sprintf(name, "ip6_vti%%d"); + + dev = alloc_netdev(sizeof(*t), name, vti6_dev_setup); + if (dev == NULL) + goto failed; + + dev_net_set(dev, net); + + t = netdev_priv(dev); + t->parms = *p; + t->net = dev_net(dev); + + err = vti6_tnl_create2(dev); + if (err < 0) + goto failed_free; + + return t; + +failed_free: + vti6_dev_free(dev); +failed: + return NULL; +} + +/** + * vti6_locate - find or create tunnel matching given parameters + * @net: network namespace + * @p: tunnel parameters + * @create: != 0 if allowed to create new tunnel if no match found + * + * Description: + * vti6_locate() first tries to locate an existing tunnel + * based on @parms. If this is unsuccessful, but @create is set a new + * tunnel device is created and registered for use. + * + * Return: + * matching tunnel or NULL + **/ +static struct ip6_tnl *vti6_locate(struct net *net, struct __ip6_tnl_parm *p, + int create) +{ + const struct in6_addr *remote = &p->raddr; + const struct in6_addr *local = &p->laddr; + struct ip6_tnl __rcu **tp; + struct ip6_tnl *t; + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + + for (tp = vti6_tnl_bucket(ip6n, p); + (t = rtnl_dereference(*tp)) != NULL; + tp = &t->next) { + if (ipv6_addr_equal(local, &t->parms.laddr) && + ipv6_addr_equal(remote, &t->parms.raddr)) + return t; + } + if (!create) + return NULL; + return vti6_tnl_create(net, p); +} + +/** + * vti6_dev_uninit - tunnel device uninitializer + * @dev: the device to be destroyed + * + * Description: + * vti6_dev_uninit() removes tunnel from its list + **/ +static void vti6_dev_uninit(struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct net *net = dev_net(dev); + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + + if (dev == ip6n->fb_tnl_dev) + RCU_INIT_POINTER(ip6n->tnls_wc[0], NULL); + else + vti6_tnl_unlink(ip6n, t); + ip6_tnl_dst_reset(t); + dev_put(dev); +} + +static int vti6_rcv(struct sk_buff *skb) +{ + struct ip6_tnl *t; + const struct ipv6hdr *ipv6h = ipv6_hdr(skb); + + rcu_read_lock(); + + if ((t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, + &ipv6h->daddr)) != NULL) { + struct pcpu_tstats *tstats; + + if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) { + rcu_read_unlock(); + goto discard; + } + + if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { + rcu_read_unlock(); + return 0; + } + + if (!ip6_tnl_rcv_ctl(t, &ipv6h->daddr, &ipv6h->saddr)) { + t->dev->stats.rx_dropped++; + rcu_read_unlock(); + goto discard; + } + + tstats = this_cpu_ptr(t->dev->tstats); + tstats->rx_packets++; + tstats->rx_bytes += skb->len; + + skb->mark = 0; + secpath_reset(skb); + skb->dev = t->dev; + + rcu_read_unlock(); + return 0; + } + rcu_read_unlock(); + return 1; + +discard: + kfree_skb(skb); + return 0; +} + +/** + * vti6_addr_conflict - compare packet addresses to tunnel's own + * @t: the outgoing tunnel device + * @hdr: IPv6 header from the incoming packet + * + * Description: + * Avoid trivial tunneling loop by checking that tunnel exit-point + * doesn't match source of incoming packet. + * + * Return: + * 1 if conflict, + * 0 else + **/ +static inline bool +vti6_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr) +{ + return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); +} + +/** + * vti6_xmit - send a packet + * @skb: the outgoing socket buffer + * @dev: the outgoing tunnel device + **/ +static int vti6_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct net *net = dev_net(dev); + struct ip6_tnl *t = netdev_priv(dev); + struct net_device_stats *stats = &t->dev->stats; + struct dst_entry *dst = NULL, *ndst = NULL; + struct flowi6 fl6; + struct ipv6hdr *ipv6h = ipv6_hdr(skb); + struct net_device *tdev; + int err = -1; + + if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) || + !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h)) + return err; + + dst = ip6_tnl_dst_check(t); + if (!dst) { + memcpy(&fl6, &t->fl.u.ip6, sizeof(fl6)); + + ndst = ip6_route_output(net, NULL, &fl6); + + if (ndst->error) + goto tx_err_link_failure; + ndst = xfrm_lookup(net, ndst, flowi6_to_flowi(&fl6), NULL, 0); + if (IS_ERR(ndst)) { + err = PTR_ERR(ndst); + ndst = NULL; + goto tx_err_link_failure; + } + dst = ndst; + } + + if (!dst->xfrm || dst->xfrm->props.mode != XFRM_MODE_TUNNEL) + goto tx_err_link_failure; + + tdev = dst->dev; + + if (tdev == dev) { + stats->collisions++; + net_warn_ratelimited("%s: Local routing loop detected!\n", + t->parms.name); + goto tx_err_dst_release; + } + + + skb_dst_drop(skb); + skb_dst_set_noref(skb, dst); + + ip6tunnel_xmit(skb, dev); + if (ndst) { + dev->mtu = dst_mtu(ndst); + ip6_tnl_dst_store(t, ndst); + } + + return 0; +tx_err_link_failure: + stats->tx_carrier_errors++; + dst_link_failure(skb); +tx_err_dst_release: + dst_release(ndst); + return err; +} + +static netdev_tx_t +vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct net_device_stats *stats = &t->dev->stats; + int ret; + + switch (skb->protocol) { + case htons(ETH_P_IPV6): + ret = vti6_xmit(skb, dev); + break; + default: + goto tx_err; + } + + if (ret < 0) + goto tx_err; + + return NETDEV_TX_OK; + +tx_err: + stats->tx_errors++; + stats->tx_dropped++; + kfree_skb(skb); + return NETDEV_TX_OK; +} + +static void vti6_link_config(struct ip6_tnl *t) +{ + struct dst_entry *dst; + struct net_device *dev = t->dev; + struct __ip6_tnl_parm *p = &t->parms; + struct flowi6 *fl6 = &t->fl.u.ip6; + + memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr)); + memcpy(dev->broadcast, &p->raddr, sizeof(struct in6_addr)); + + /* Set up flowi template */ + fl6->saddr = p->laddr; + fl6->daddr = p->raddr; + fl6->flowi6_oif = p->link; + fl6->flowi6_mark = be32_to_cpu(p->i_key); + fl6->flowi6_proto = p->proto; + fl6->flowlabel = 0; + + p->flags &= ~(IP6_TNL_F_CAP_XMIT | IP6_TNL_F_CAP_RCV | + IP6_TNL_F_CAP_PER_PACKET); + p->flags |= ip6_tnl_get_cap(t, &p->laddr, &p->raddr); + + if (p->flags & IP6_TNL_F_CAP_XMIT && p->flags & IP6_TNL_F_CAP_RCV) + dev->flags |= IFF_POINTOPOINT; + else + dev->flags &= ~IFF_POINTOPOINT; + + dev->iflink = p->link; + + if (p->flags & IP6_TNL_F_CAP_XMIT) { + + dst = ip6_route_output(dev_net(dev), NULL, fl6); + if (dst->error) + return; + + dst = xfrm_lookup(dev_net(dev), dst, flowi6_to_flowi(fl6), + NULL, 0); + if (IS_ERR(dst)) + return; + + if (dst->dev) { + dev->hard_header_len = dst->dev->hard_header_len; + + dev->mtu = dst_mtu(dst); + + if (dev->mtu < IPV6_MIN_MTU) + dev->mtu = IPV6_MIN_MTU; + } + dst_release(dst); + } +} + +/** + * vti6_tnl_change - update the tunnel parameters + * @t: tunnel to be changed + * @p: tunnel configuration parameters + * + * Description: + * vti6_tnl_change() updates the tunnel parameters + **/ +static int +vti6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) +{ + t->parms.laddr = p->laddr; + t->parms.raddr = p->raddr; + t->parms.link = p->link; + t->parms.i_key = p->i_key; + t->parms.o_key = p->o_key; + t->parms.proto = p->proto; + ip6_tnl_dst_reset(t); + vti6_link_config(t); + return 0; +} + +static int vti6_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p) +{ + struct net *net = dev_net(t->dev); + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + int err; + + vti6_tnl_unlink(ip6n, t); + synchronize_net(); + err = vti6_tnl_change(t, p); + vti6_tnl_link(ip6n, t); + netdev_state_change(t->dev); + return err; +} + +static void +vti6_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm2 *u) +{ + p->laddr = u->laddr; + p->raddr = u->raddr; + p->link = u->link; + p->i_key = u->i_key; + p->o_key = u->o_key; + p->proto = u->proto; + + memcpy(p->name, u->name, sizeof(u->name)); +} + +static void +vti6_parm_to_user(struct ip6_tnl_parm2 *u, const struct __ip6_tnl_parm *p) +{ + u->laddr = p->laddr; + u->raddr = p->raddr; + u->link = p->link; + u->i_key = p->i_key; + u->o_key = p->o_key; + u->proto = p->proto; + + memcpy(u->name, p->name, sizeof(u->name)); +} + +/** + * vti6_tnl_ioctl - configure vti6 tunnels from userspace + * @dev: virtual device associated with tunnel + * @ifr: parameters passed from userspace + * @cmd: command to be performed + * + * Description: + * vti6_ioctl() is used for managing vti6 tunnels + * from userspace. + * + * The possible commands are the following: + * %SIOCGETTUNNEL: get tunnel parameters for device + * %SIOCADDTUNNEL: add tunnel matching given tunnel parameters + * %SIOCCHGTUNNEL: change tunnel parameters to those given + * %SIOCDELTUNNEL: delete tunnel + * + * The fallback device "ip6_vti0", created during module + * initialization, can be used for creating other tunnel devices. + * + * Return: + * 0 on success, + * %-EFAULT if unable to copy data to or from userspace, + * %-EPERM if current process hasn't %CAP_NET_ADMIN set + * %-EINVAL if passed tunnel parameters are invalid, + * %-EEXIST if changing a tunnel's parameters would cause a conflict + * %-ENODEV if attempting to change or delete a nonexisting device + **/ +static int +vti6_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + int err = 0; + struct ip6_tnl_parm2 p; + struct __ip6_tnl_parm p1; + struct ip6_tnl *t = NULL; + struct net *net = dev_net(dev); + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + + switch (cmd) { + case SIOCGETTUNNEL: + if (dev == ip6n->fb_tnl_dev) { + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) { + err = -EFAULT; + break; + } + vti6_parm_from_user(&p1, &p); + t = vti6_locate(net, &p1, 0); + } else { + memset(&p, 0, sizeof(p)); + } + if (t == NULL) + t = netdev_priv(dev); + vti6_parm_to_user(&p, &t->parms); + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) + err = -EFAULT; + break; + case SIOCADDTUNNEL: + case SIOCCHGTUNNEL: + err = -EPERM; + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) + break; + err = -EFAULT; + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) + break; + err = -EINVAL; + if (p.proto != IPPROTO_IPV6 && p.proto != 0) + break; + vti6_parm_from_user(&p1, &p); + t = vti6_locate(net, &p1, cmd == SIOCADDTUNNEL); + if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) { + if (t != NULL) { + if (t->dev != dev) { + err = -EEXIST; + break; + } + } else + t = netdev_priv(dev); + + err = vti6_update(t, &p1); + } + if (t) { + err = 0; + vti6_parm_to_user(&p, &t->parms); + if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) + err = -EFAULT; + + } else + err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT); + break; + case SIOCDELTUNNEL: + err = -EPERM; + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) + break; + + if (dev == ip6n->fb_tnl_dev) { + err = -EFAULT; + if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p))) + break; + err = -ENOENT; + vti6_parm_from_user(&p1, &p); + t = vti6_locate(net, &p1, 0); + if (t == NULL) + break; + err = -EPERM; + if (t->dev == ip6n->fb_tnl_dev) + break; + dev = t->dev; + } + err = 0; + unregister_netdevice(dev); + break; + default: + err = -EINVAL; + } + return err; +} + +/** + * vti6_tnl_change_mtu - change mtu manually for tunnel device + * @dev: virtual device associated with tunnel + * @new_mtu: the new mtu + * + * Return: + * 0 on success, + * %-EINVAL if mtu too small + **/ +static int vti6_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < IPV6_MIN_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + +static const struct net_device_ops vti6_netdev_ops = { + .ndo_uninit = vti6_dev_uninit, + .ndo_start_xmit = vti6_tnl_xmit, + .ndo_do_ioctl = vti6_ioctl, + .ndo_change_mtu = vti6_change_mtu, + .ndo_get_stats = vti6_get_stats, +}; + +/** + * vti6_dev_setup - setup virtual tunnel device + * @dev: virtual device associated with tunnel + * + * Description: + * Initialize function pointers and device parameters + **/ +static void vti6_dev_setup(struct net_device *dev) +{ + struct ip6_tnl *t; + + dev->netdev_ops = &vti6_netdev_ops; + dev->destructor = vti6_dev_free; + + dev->type = ARPHRD_TUNNEL6; + dev->hard_header_len = LL_MAX_HEADER + sizeof(struct ipv6hdr); + dev->mtu = ETH_DATA_LEN; + t = netdev_priv(dev); + dev->flags |= IFF_NOARP; + dev->addr_len = sizeof(struct in6_addr); + dev->features |= NETIF_F_NETNS_LOCAL; + dev->priv_flags &= ~IFF_XMIT_DST_RELEASE; +} + +/** + * vti6_dev_init_gen - general initializer for all tunnel devices + * @dev: virtual device associated with tunnel + **/ +static inline int vti6_dev_init_gen(struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + + t->dev = dev; + t->net = dev_net(dev); + dev->tstats = alloc_percpu(struct pcpu_tstats); + if (!dev->tstats) + return -ENOMEM; + return 0; +} + +/** + * vti6_dev_init - initializer for all non fallback tunnel devices + * @dev: virtual device associated with tunnel + **/ +static int vti6_dev_init(struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + int err = vti6_dev_init_gen(dev); + + if (err) + return err; + vti6_link_config(t); + return 0; +} + +/** + * vti6_fb_tnl_dev_init - initializer for fallback tunnel device + * @dev: fallback device + * + * Return: 0 + **/ +static int __net_init vti6_fb_tnl_dev_init(struct net_device *dev) +{ + struct ip6_tnl *t = netdev_priv(dev); + struct net *net = dev_net(dev); + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + int err = vti6_dev_init_gen(dev); + + if (err) + return err; + + t->parms.proto = IPPROTO_IPV6; + dev_hold(dev); + + vti6_link_config(t); + + rcu_assign_pointer(ip6n->tnls_wc[0], t); + return 0; +} + +static int vti6_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + return 0; +} + +static void vti6_netlink_parms(struct nlattr *data[], + struct __ip6_tnl_parm *parms) +{ + memset(parms, 0, sizeof(*parms)); + + if (!data) + return; + + if (data[IFLA_VTI_LINK]) + parms->link = nla_get_u32(data[IFLA_VTI_LINK]); + + if (data[IFLA_VTI_LOCAL]) + nla_memcpy(&parms->laddr, data[IFLA_VTI_LOCAL], + sizeof(struct in6_addr)); + + if (data[IFLA_VTI_REMOTE]) + nla_memcpy(&parms->raddr, data[IFLA_VTI_REMOTE], + sizeof(struct in6_addr)); + + if (data[IFLA_VTI_IKEY]) + parms->i_key = nla_get_be32(data[IFLA_VTI_IKEY]); + + if (data[IFLA_VTI_OKEY]) + parms->o_key = nla_get_be32(data[IFLA_VTI_OKEY]); +} + +static int vti6_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + struct net *net = dev_net(dev); + struct ip6_tnl *nt; + + nt = netdev_priv(dev); + vti6_netlink_parms(data, &nt->parms); + + nt->parms.proto = IPPROTO_IPV6; + + if (vti6_locate(net, &nt->parms, 0)) + return -EEXIST; + + return vti6_tnl_create2(dev); +} + +static int vti6_changelink(struct net_device *dev, struct nlattr *tb[], + struct nlattr *data[]) +{ + struct ip6_tnl *t; + struct __ip6_tnl_parm p; + struct net *net = dev_net(dev); + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + + if (dev == ip6n->fb_tnl_dev) + return -EINVAL; + + vti6_netlink_parms(data, &p); + + t = vti6_locate(net, &p, 0); + + if (t) { + if (t->dev != dev) + return -EEXIST; + } else + t = netdev_priv(dev); + + return vti6_update(t, &p); +} + +static size_t vti6_get_size(const struct net_device *dev) +{ + return + /* IFLA_VTI_LINK */ + nla_total_size(4) + + /* IFLA_VTI_LOCAL */ + nla_total_size(sizeof(struct in6_addr)) + + /* IFLA_VTI_REMOTE */ + nla_total_size(sizeof(struct in6_addr)) + + /* IFLA_VTI_IKEY */ + nla_total_size(4) + + /* IFLA_VTI_OKEY */ + nla_total_size(4) + + 0; +} + +static int vti6_fill_info(struct sk_buff *skb, const struct net_device *dev) +{ + struct ip6_tnl *tunnel = netdev_priv(dev); + struct __ip6_tnl_parm *parm = &tunnel->parms; + + if (nla_put_u32(skb, IFLA_VTI_LINK, parm->link) || + nla_put(skb, IFLA_VTI_LOCAL, sizeof(struct in6_addr), + &parm->laddr) || + nla_put(skb, IFLA_VTI_REMOTE, sizeof(struct in6_addr), + &parm->raddr) || + nla_put_be32(skb, IFLA_VTI_IKEY, parm->i_key) || + nla_put_be32(skb, IFLA_VTI_OKEY, parm->o_key)) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +static const struct nla_policy vti6_policy[IFLA_VTI_MAX + 1] = { + [IFLA_VTI_LINK] = { .type = NLA_U32 }, + [IFLA_VTI_LOCAL] = { .len = sizeof(struct in6_addr) }, + [IFLA_VTI_REMOTE] = { .len = sizeof(struct in6_addr) }, + [IFLA_VTI_IKEY] = { .type = NLA_U32 }, + [IFLA_VTI_OKEY] = { .type = NLA_U32 }, +}; + +static struct rtnl_link_ops vti6_link_ops __read_mostly = { + .kind = "vti6", + .maxtype = IFLA_VTI_MAX, + .policy = vti6_policy, + .priv_size = sizeof(struct ip6_tnl), + .setup = vti6_dev_setup, + .validate = vti6_validate, + .newlink = vti6_newlink, + .changelink = vti6_changelink, + .get_size = vti6_get_size, + .fill_info = vti6_fill_info, +}; + +static struct xfrm_tunnel_notifier vti6_handler __read_mostly = { + .handler = vti6_rcv, + .priority = 1, +}; + +static void __net_exit vti6_destroy_tunnels(struct vti6_net *ip6n) +{ + int h; + struct ip6_tnl *t; + LIST_HEAD(list); + + for (h = 0; h < HASH_SIZE; h++) { + t = rtnl_dereference(ip6n->tnls_r_l[h]); + while (t != NULL) { + unregister_netdevice_queue(t->dev, &list); + t = rtnl_dereference(t->next); + } + } + + t = rtnl_dereference(ip6n->tnls_wc[0]); + unregister_netdevice_queue(t->dev, &list); + unregister_netdevice_many(&list); +} + +static int __net_init vti6_init_net(struct net *net) +{ + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + struct ip6_tnl *t = NULL; + int err; + + ip6n->tnls[0] = ip6n->tnls_wc; + ip6n->tnls[1] = ip6n->tnls_r_l; + + err = -ENOMEM; + ip6n->fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6_vti0", + vti6_dev_setup); + + if (!ip6n->fb_tnl_dev) + goto err_alloc_dev; + dev_net_set(ip6n->fb_tnl_dev, net); + + err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev); + if (err < 0) + goto err_register; + + err = register_netdev(ip6n->fb_tnl_dev); + if (err < 0) + goto err_register; + + t = netdev_priv(ip6n->fb_tnl_dev); + + strcpy(t->parms.name, ip6n->fb_tnl_dev->name); + return 0; + +err_register: + vti6_dev_free(ip6n->fb_tnl_dev); +err_alloc_dev: + return err; +} + +static void __net_exit vti6_exit_net(struct net *net) +{ + struct vti6_net *ip6n = net_generic(net, vti6_net_id); + + rtnl_lock(); + vti6_destroy_tunnels(ip6n); + rtnl_unlock(); +} + +static struct pernet_operations vti6_net_ops = { + .init = vti6_init_net, + .exit = vti6_exit_net, + .id = &vti6_net_id, + .size = sizeof(struct vti6_net), +}; + +/** + * vti6_tunnel_init - register protocol and reserve needed resources + * + * Return: 0 on success + **/ +static int __init vti6_tunnel_init(void) +{ + int err; + + err = register_pernet_device(&vti6_net_ops); + if (err < 0) + goto out_pernet; + + err = xfrm6_mode_tunnel_input_register(&vti6_handler); + if (err < 0) { + pr_err("%s: can't register vti6\n", __func__); + goto out; + } + err = rtnl_link_register(&vti6_link_ops); + if (err < 0) + goto rtnl_link_failed; + + return 0; + +rtnl_link_failed: + xfrm6_mode_tunnel_input_deregister(&vti6_handler); +out: + unregister_pernet_device(&vti6_net_ops); +out_pernet: + return err; +} + +/** + * vti6_tunnel_cleanup - free resources and unregister protocol + **/ +static void __exit vti6_tunnel_cleanup(void) +{ + rtnl_link_unregister(&vti6_link_ops); + if (xfrm6_mode_tunnel_input_deregister(&vti6_handler)) + pr_info("%s: can't deregister vti6\n", __func__); + + unregister_pernet_device(&vti6_net_ops); +} + +module_init(vti6_tunnel_init); +module_exit(vti6_tunnel_cleanup); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_RTNL_LINK("vti6"); +MODULE_ALIAS_NETDEV("ip6_vti0"); +MODULE_AUTHOR("Steffen Klassert"); +MODULE_DESCRIPTION("IPv6 virtual tunnel interface"); diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index d1e2e8ef29c54abd86064728ec145f0478f360af..1c6ce3119ff8ce45f1b406c9b1356a2400f6fc1d 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -174,7 +174,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, } if (ipv6_only_sock(sk) || - !ipv6_addr_v4mapped(&np->daddr)) { + !ipv6_addr_v4mapped(&sk->sk_v6_daddr)) { retv = -EADDRNOTAVAIL; break; } @@ -1011,7 +1011,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, struct in6_pktinfo src_info; src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif : np->sticky_pktinfo.ipi6_ifindex; - src_info.ipi6_addr = np->mcast_oif ? np->daddr : np->sticky_pktinfo.ipi6_addr; + src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : np->sticky_pktinfo.ipi6_addr; put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxhlim) { @@ -1026,7 +1026,8 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, struct in6_pktinfo src_info; src_info.ipi6_ifindex = np->mcast_oif ? np->mcast_oif : np->sticky_pktinfo.ipi6_ifindex; - src_info.ipi6_addr = np->mcast_oif ? np->daddr : np->sticky_pktinfo.ipi6_addr; + src_info.ipi6_addr = np->mcast_oif ? sk->sk_v6_daddr : + np->sticky_pktinfo.ipi6_addr; put_cmsg(&msg, SOL_IPV6, IPV6_2292PKTINFO, sizeof(src_info), &src_info); } if (np->rxopt.bits.rxohlim) { @@ -1211,6 +1212,34 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, val = np->sndflow; break; + case IPV6_FLOWLABEL_MGR: + { + struct in6_flowlabel_req freq; + + if (len < sizeof(freq)) + return -EINVAL; + + if (copy_from_user(&freq, optval, sizeof(freq))) + return -EFAULT; + + if (freq.flr_action != IPV6_FL_A_GET) + return -EINVAL; + + len = sizeof(freq); + memset(&freq, 0, sizeof(freq)); + + val = ipv6_flowlabel_opt_get(sk, &freq); + if (val < 0) + return val; + + if (put_user(len, optlen)) + return -EFAULT; + if (copy_to_user(optval, &freq, len)) + return -EFAULT; + + return 0; + } + case IPV6_ADDR_PREFERENCES: val = 0; diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index a7f842b29b67b2ca5ff2c2cbe13588e3098ce44b..7702f9e90a043ac458b63a2e0a07ed3975d37aa6 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -25,6 +25,19 @@ config NF_CONNTRACK_IPV6 To compile it as a module, choose M here. If unsure, say N. +config NF_TABLES_IPV6 + depends on NF_TABLES + tristate "IPv6 nf_tables support" + +config NFT_CHAIN_ROUTE_IPV6 + depends on NF_TABLES_IPV6 + tristate "IPv6 nf_tables route chain support" + +config NFT_CHAIN_NAT_IPV6 + depends on NF_TABLES_IPV6 + depends on NF_NAT_IPV6 && NFT_NAT + tristate "IPv6 nf_tables nat chain support" + config IP6_NF_IPTABLES tristate "IP6 tables support (required for filtering)" depends on INET && IPV6 diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile index 2b53738f798cd3a9898d89a2a1a76b48ffe983b8..d1b4928f34f7ba3f80200ec0b9db67c12d47715b 100644 --- a/net/ipv6/netfilter/Makefile +++ b/net/ipv6/netfilter/Makefile @@ -23,6 +23,11 @@ obj-$(CONFIG_NF_NAT_IPV6) += nf_nat_ipv6.o nf_defrag_ipv6-y := nf_defrag_ipv6_hooks.o nf_conntrack_reasm.o obj-$(CONFIG_NF_DEFRAG_IPV6) += nf_defrag_ipv6.o +# nf_tables +obj-$(CONFIG_NF_TABLES_IPV6) += nf_tables_ipv6.o +obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o +obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o + # matches obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o obj-$(CONFIG_IP6_NF_MATCH_EUI64) += ip6t_eui64.o diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index 56eef30ee5f6afb2ad1338649b30ffe8a5c5b948..da00a2ecde559431993f2e08ca29bc06a226155c 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -39,7 +39,7 @@ MODULE_DESCRIPTION("Xtables: packet \"rejection\" target for IPv6"); MODULE_LICENSE("GPL"); /* Send RST reply */ -static void send_reset(struct net *net, struct sk_buff *oldskb) +static void send_reset(struct net *net, struct sk_buff *oldskb, int hook) { struct sk_buff *nskb; struct tcphdr otcph, *tcph; @@ -88,8 +88,7 @@ static void send_reset(struct net *net, struct sk_buff *oldskb) } /* Check checksum. */ - if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP, - skb_checksum(oldskb, tcphoff, otcplen, 0))) { + if (nf_ip6_checksum(oldskb, hook, tcphoff, IPPROTO_TCP)) { pr_debug("TCP checksum is invalid\n"); return; } @@ -227,7 +226,7 @@ reject_tg6(struct sk_buff *skb, const struct xt_action_param *par) /* Do nothing */ break; case IP6T_TCP_RESET: - send_reset(net, skb); + send_reset(net, skb, par->hooknum); break; default: net_info_ratelimited("case %u not handled yet\n", reject->with); diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index 2748b042da72eceb4002cf4183a781282c5e84d4..bf9f612c1bc24eb9eb0b26fbf7d0a62f74932735 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -312,7 +312,7 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) return XT_CONTINUE; } -static unsigned int ipv6_synproxy_hook(unsigned int hooknum, +static unsigned int ipv6_synproxy_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c index 29b44b14c5ea84bfa50f96d9e6f4f9914dc1e828..ca7f6c1280861b2977dce643fdea349eb3ec5078 100644 --- a/net/ipv6/netfilter/ip6table_filter.c +++ b/net/ipv6/netfilter/ip6table_filter.c @@ -32,13 +32,14 @@ static const struct xt_table packet_filter = { /* The work comes in here from netfilter.c. */ static unsigned int -ip6table_filter_hook(unsigned int hook, struct sk_buff *skb, +ip6table_filter_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { const struct net *net = dev_net((in != NULL) ? in : out); - return ip6t_do_table(skb, hook, in, out, net->ipv6.ip6table_filter); + return ip6t_do_table(skb, ops->hooknum, in, out, + net->ipv6.ip6table_filter); } static struct nf_hook_ops *filter_ops __read_mostly; diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c index c705907ae6ab4178f683c0432b45aff0630ad2f5..307bbb782d147011d689f04c92e0ba5ac7c13074 100644 --- a/net/ipv6/netfilter/ip6table_mangle.c +++ b/net/ipv6/netfilter/ip6table_mangle.c @@ -76,17 +76,17 @@ ip6t_mangle_out(struct sk_buff *skb, const struct net_device *out) /* The work comes in here from netfilter.c. */ static unsigned int -ip6table_mangle_hook(unsigned int hook, struct sk_buff *skb, +ip6table_mangle_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - if (hook == NF_INET_LOCAL_OUT) + if (ops->hooknum == NF_INET_LOCAL_OUT) return ip6t_mangle_out(skb, out); - if (hook == NF_INET_POST_ROUTING) - return ip6t_do_table(skb, hook, in, out, + if (ops->hooknum == NF_INET_POST_ROUTING) + return ip6t_do_table(skb, ops->hooknum, in, out, dev_net(out)->ipv6.ip6table_mangle); /* INPUT/FORWARD */ - return ip6t_do_table(skb, hook, in, out, + return ip6t_do_table(skb, ops->hooknum, in, out, dev_net(in)->ipv6.ip6table_mangle); } diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index 9b076d2d3a7b6ec6a495302efd97324e9bc95b14..84c7f33d0cf858115abdb5f359a658b574637d57 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -63,7 +63,7 @@ static unsigned int nf_nat_rule_find(struct sk_buff *skb, unsigned int hooknum, } static unsigned int -nf_nat_ipv6_fn(unsigned int hooknum, +nf_nat_ipv6_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -72,7 +72,7 @@ nf_nat_ipv6_fn(unsigned int hooknum, struct nf_conn *ct; enum ip_conntrack_info ctinfo; struct nf_conn_nat *nat; - enum nf_nat_manip_type maniptype = HOOK2MANIP(hooknum); + enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); __be16 frag_off; int hdrlen; u8 nexthdr; @@ -111,7 +111,8 @@ nf_nat_ipv6_fn(unsigned int hooknum, if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo, - hooknum, hdrlen)) + ops->hooknum, + hdrlen)) return NF_DROP; else return NF_ACCEPT; @@ -124,14 +125,14 @@ nf_nat_ipv6_fn(unsigned int hooknum, if (!nf_nat_initialized(ct, maniptype)) { unsigned int ret; - ret = nf_nat_rule_find(skb, hooknum, in, out, ct); + ret = nf_nat_rule_find(skb, ops->hooknum, in, out, ct); if (ret != NF_ACCEPT) return ret; } else { pr_debug("Already setup manip %s for ct %p\n", maniptype == NF_NAT_MANIP_SRC ? "SRC" : "DST", ct); - if (nf_nat_oif_changed(hooknum, ctinfo, nat, out)) + if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) goto oif_changed; } break; @@ -140,11 +141,11 @@ nf_nat_ipv6_fn(unsigned int hooknum, /* ESTABLISHED */ NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || ctinfo == IP_CT_ESTABLISHED_REPLY); - if (nf_nat_oif_changed(hooknum, ctinfo, nat, out)) + if (nf_nat_oif_changed(ops->hooknum, ctinfo, nat, out)) goto oif_changed; } - return nf_nat_packet(ct, ctinfo, hooknum, skb); + return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); oif_changed: nf_ct_kill_acct(ct, ctinfo, skb); @@ -152,7 +153,7 @@ oif_changed: } static unsigned int -nf_nat_ipv6_in(unsigned int hooknum, +nf_nat_ipv6_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -161,7 +162,7 @@ nf_nat_ipv6_in(unsigned int hooknum, unsigned int ret; struct in6_addr daddr = ipv6_hdr(skb)->daddr; - ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn); + ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); if (ret != NF_DROP && ret != NF_STOLEN && ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr)) skb_dst_drop(skb); @@ -170,7 +171,7 @@ nf_nat_ipv6_in(unsigned int hooknum, } static unsigned int -nf_nat_ipv6_out(unsigned int hooknum, +nf_nat_ipv6_out(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -187,7 +188,7 @@ nf_nat_ipv6_out(unsigned int hooknum, if (skb->len < sizeof(struct ipv6hdr)) return NF_ACCEPT; - ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn); + ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); #ifdef CONFIG_XFRM if (ret != NF_DROP && ret != NF_STOLEN && !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && @@ -209,7 +210,7 @@ nf_nat_ipv6_out(unsigned int hooknum, } static unsigned int -nf_nat_ipv6_local_fn(unsigned int hooknum, +nf_nat_ipv6_local_fn(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -224,7 +225,7 @@ nf_nat_ipv6_local_fn(unsigned int hooknum, if (skb->len < sizeof(struct ipv6hdr)) return NF_ACCEPT; - ret = nf_nat_ipv6_fn(hooknum, skb, in, out, okfn); + ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); if (ret != NF_DROP && ret != NF_STOLEN && (ct = nf_ct_get(skb, &ctinfo)) != NULL) { enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c index 9a626d86720fc6a0d1b4eeffa79a85b47a0dec2f..5274740acecc93b4550dabdd7f48fac3c04f67ac 100644 --- a/net/ipv6/netfilter/ip6table_raw.c +++ b/net/ipv6/netfilter/ip6table_raw.c @@ -19,13 +19,14 @@ static const struct xt_table packet_raw = { /* The work comes in here from netfilter.c. */ static unsigned int -ip6table_raw_hook(unsigned int hook, struct sk_buff *skb, +ip6table_raw_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { const struct net *net = dev_net((in != NULL) ? in : out); - return ip6t_do_table(skb, hook, in, out, net->ipv6.ip6table_raw); + return ip6t_do_table(skb, ops->hooknum, in, out, + net->ipv6.ip6table_raw); } static struct nf_hook_ops *rawtable_ops __read_mostly; diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c index ce88d1d7e5255d69ee102229e463f12701bdac99..ab3b0219ecfa436c07eb5cb86af36bd04efbdfb7 100644 --- a/net/ipv6/netfilter/ip6table_security.c +++ b/net/ipv6/netfilter/ip6table_security.c @@ -36,14 +36,15 @@ static const struct xt_table security_table = { }; static unsigned int -ip6table_security_hook(unsigned int hook, struct sk_buff *skb, +ip6table_security_hook(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { const struct net *net = dev_net((in != NULL) ? in : out); - return ip6t_do_table(skb, hook, in, out, net->ipv6.ip6table_security); + return ip6t_do_table(skb, ops->hooknum, in, out, + net->ipv6.ip6table_security); } static struct nf_hook_ops *sectbl_ops __read_mostly; diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index d6e4dd8b58dfaf67a9f64fe8bfafcf2f9f6b24ea..4cbc6b290dd5f324254e99fe633596789160a2af 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -95,7 +95,7 @@ static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, return NF_ACCEPT; } -static unsigned int ipv6_helper(unsigned int hooknum, +static unsigned int ipv6_helper(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -133,7 +133,7 @@ static unsigned int ipv6_helper(unsigned int hooknum, return helper->help(skb, protoff, ct, ctinfo); } -static unsigned int ipv6_confirm(unsigned int hooknum, +static unsigned int ipv6_confirm(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -169,66 +169,16 @@ out: return nf_conntrack_confirm(skb); } -static unsigned int __ipv6_conntrack_in(struct net *net, - unsigned int hooknum, - struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct sk_buff *reasm = skb->nfct_reasm; - const struct nf_conn_help *help; - struct nf_conn *ct; - enum ip_conntrack_info ctinfo; - - /* This packet is fragmented and has reassembled packet. */ - if (reasm) { - /* Reassembled packet isn't parsed yet ? */ - if (!reasm->nfct) { - unsigned int ret; - - ret = nf_conntrack_in(net, PF_INET6, hooknum, reasm); - if (ret != NF_ACCEPT) - return ret; - } - - /* Conntrack helpers need the entire reassembled packet in the - * POST_ROUTING hook. In case of unconfirmed connections NAT - * might reassign a helper, so the entire packet is also - * required. - */ - ct = nf_ct_get(reasm, &ctinfo); - if (ct != NULL && !nf_ct_is_untracked(ct)) { - help = nfct_help(ct); - if ((help && help->helper) || !nf_ct_is_confirmed(ct)) { - nf_conntrack_get_reasm(reasm); - NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, reasm, - (struct net_device *)in, - (struct net_device *)out, - okfn, NF_IP6_PRI_CONNTRACK + 1); - return NF_DROP_ERR(-ECANCELED); - } - } - - nf_conntrack_get(reasm->nfct); - skb->nfct = reasm->nfct; - skb->nfctinfo = reasm->nfctinfo; - return NF_ACCEPT; - } - - return nf_conntrack_in(net, PF_INET6, hooknum, skb); -} - -static unsigned int ipv6_conntrack_in(unsigned int hooknum, +static unsigned int ipv6_conntrack_in(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return __ipv6_conntrack_in(dev_net(in), hooknum, skb, in, out, okfn); + return nf_conntrack_in(dev_net(in), PF_INET6, ops->hooknum, skb); } -static unsigned int ipv6_conntrack_local(unsigned int hooknum, +static unsigned int ipv6_conntrack_local(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -239,7 +189,7 @@ static unsigned int ipv6_conntrack_local(unsigned int hooknum, net_notice_ratelimited("ipv6_conntrack_local: packet too short\n"); return NF_ACCEPT; } - return __ipv6_conntrack_in(dev_net(out), hooknum, skb, in, out, okfn); + return nf_conntrack_in(dev_net(out), PF_INET6, ops->hooknum, skb); } static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { @@ -297,9 +247,9 @@ ipv6_getorigdst(struct sock *sk, int optval, void __user *user, int *len) struct nf_conntrack_tuple tuple = { .src.l3num = NFPROTO_IPV6 }; struct nf_conn *ct; - tuple.src.u3.in6 = inet6->rcv_saddr; + tuple.src.u3.in6 = sk->sk_v6_rcv_saddr; tuple.src.u.tcp.port = inet->inet_sport; - tuple.dst.u3.in6 = inet6->daddr; + tuple.dst.u3.in6 = sk->sk_v6_daddr; tuple.dst.u.tcp.port = inet->inet_dport; tuple.dst.protonum = sk->sk_protocol; diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index dffdc1a389c57da1d16ebeda9b9aff44d6cd961a..767ab8da82189479456c632ecec95eef67da4bae 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -144,12 +144,24 @@ static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h) return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK); } +static unsigned int nf_hash_frag(__be32 id, const struct in6_addr *saddr, + const struct in6_addr *daddr) +{ + u32 c; + + net_get_random_once(&nf_frags.rnd, sizeof(nf_frags.rnd)); + c = jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr), + (__force u32)id, nf_frags.rnd); + return c & (INETFRAGS_HASHSZ - 1); +} + + static unsigned int nf_hashfn(struct inet_frag_queue *q) { const struct frag_queue *nq; nq = container_of(q, struct frag_queue, q); - return inet6_hash_frag(nq->id, &nq->saddr, &nq->daddr, nf_frags.rnd); + return nf_hash_frag(nq->id, &nq->saddr, &nq->daddr); } static void nf_skb_free(struct sk_buff *skb) @@ -185,7 +197,7 @@ static inline struct frag_queue *fq_find(struct net *net, __be32 id, arg.ecn = ecn; read_lock_bh(&nf_frags.lock); - hash = inet6_hash_frag(id, src, dst, nf_frags.rnd); + hash = nf_hash_frag(id, src, dst); q = inet_frag_find(&net->nf_frag.frags, &nf_frags, &arg, hash); local_bh_enable(); @@ -621,31 +633,16 @@ ret_orig: return skb; } -void nf_ct_frag6_output(unsigned int hooknum, struct sk_buff *skb, - struct net_device *in, struct net_device *out, - int (*okfn)(struct sk_buff *)) +void nf_ct_frag6_consume_orig(struct sk_buff *skb) { struct sk_buff *s, *s2; - unsigned int ret = 0; for (s = NFCT_FRAG6_CB(skb)->orig; s;) { - nf_conntrack_put_reasm(s->nfct_reasm); - nf_conntrack_get_reasm(skb); - s->nfct_reasm = skb; - s2 = s->next; s->next = NULL; - - if (ret != -ECANCELED) - ret = NF_HOOK_THRESH(NFPROTO_IPV6, hooknum, s, - in, out, okfn, - NF_IP6_PRI_CONNTRACK_DEFRAG + 1); - else - kfree_skb(s); - + consume_skb(s); s = s2; } - nf_conntrack_put_reasm(skb); } static int nf_ct_net_init(struct net *net) diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c index aacd121fe8c54365607f49e40679d7fd08dcd8e8..7b9a748c6bacbe52cceb0277e4f834b81b012915 100644 --- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c @@ -52,7 +52,7 @@ static enum ip6_defrag_users nf_ct6_defrag_user(unsigned int hooknum, } -static unsigned int ipv6_defrag(unsigned int hooknum, +static unsigned int ipv6_defrag(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -66,7 +66,7 @@ static unsigned int ipv6_defrag(unsigned int hooknum, return NF_ACCEPT; #endif - reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(hooknum, skb)); + reasm = nf_ct_frag6_gather(skb, nf_ct6_defrag_user(ops->hooknum, skb)); /* queued */ if (reasm == NULL) return NF_STOLEN; @@ -75,8 +75,11 @@ static unsigned int ipv6_defrag(unsigned int hooknum, if (reasm == skb) return NF_ACCEPT; - nf_ct_frag6_output(hooknum, reasm, (struct net_device *)in, - (struct net_device *)out, okfn); + nf_ct_frag6_consume_orig(reasm); + + NF_HOOK_THRESH(NFPROTO_IPV6, ops->hooknum, reasm, + (struct net_device *) in, (struct net_device *) out, + okfn, NF_IP6_PRI_CONNTRACK_DEFRAG + 1); return NF_STOLEN; } diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c new file mode 100644 index 0000000000000000000000000000000000000000..d77db8a13505083d18b1993b03da91b4c5e7ef23 --- /dev/null +++ b/net/ipv6/netfilter/nf_tables_ipv6.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2008 Patrick McHardy + * Copyright (c) 2012-2013 Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include + +static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct nft_pktinfo pkt; + + if (unlikely(skb->len < sizeof(struct ipv6hdr))) { + if (net_ratelimit()) + pr_info("nf_tables_ipv6: ignoring short SOCK_RAW " + "packet\n"); + return NF_ACCEPT; + } + if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0) + return NF_DROP; + + return nft_do_chain_pktinfo(&pkt, ops); +} + +static struct nft_af_info nft_af_ipv6 __read_mostly = { + .family = NFPROTO_IPV6, + .nhooks = NF_INET_NUMHOOKS, + .owner = THIS_MODULE, + .hooks = { + [NF_INET_LOCAL_OUT] = nft_ipv6_output, + }, +}; + +static int nf_tables_ipv6_init_net(struct net *net) +{ + net->nft.ipv6 = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL); + if (net->nft.ipv6 == NULL) + return -ENOMEM; + + memcpy(net->nft.ipv6, &nft_af_ipv6, sizeof(nft_af_ipv6)); + + if (nft_register_afinfo(net, net->nft.ipv6) < 0) + goto err; + + return 0; +err: + kfree(net->nft.ipv6); + return -ENOMEM; +} + +static void nf_tables_ipv6_exit_net(struct net *net) +{ + nft_unregister_afinfo(net->nft.ipv6); + kfree(net->nft.ipv6); +} + +static struct pernet_operations nf_tables_ipv6_net_ops = { + .init = nf_tables_ipv6_init_net, + .exit = nf_tables_ipv6_exit_net, +}; + +static unsigned int +nft_do_chain_ipv6(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct nft_pktinfo pkt; + + /* malformed packet, drop it */ + if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0) + return NF_DROP; + + return nft_do_chain_pktinfo(&pkt, ops); +} + +static struct nf_chain_type filter_ipv6 = { + .family = NFPROTO_IPV6, + .name = "filter", + .type = NFT_CHAIN_T_DEFAULT, + .hook_mask = (1 << NF_INET_LOCAL_IN) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_FORWARD) | + (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_POST_ROUTING), + .fn = { + [NF_INET_LOCAL_IN] = nft_do_chain_ipv6, + [NF_INET_LOCAL_OUT] = nft_ipv6_output, + [NF_INET_FORWARD] = nft_do_chain_ipv6, + [NF_INET_PRE_ROUTING] = nft_do_chain_ipv6, + [NF_INET_POST_ROUTING] = nft_do_chain_ipv6, + }, +}; + +static int __init nf_tables_ipv6_init(void) +{ + nft_register_chain_type(&filter_ipv6); + return register_pernet_subsys(&nf_tables_ipv6_net_ops); +} + +static void __exit nf_tables_ipv6_exit(void) +{ + unregister_pernet_subsys(&nf_tables_ipv6_net_ops); + nft_unregister_chain_type(&filter_ipv6); +} + +module_init(nf_tables_ipv6_init); +module_exit(nf_tables_ipv6_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_FAMILY(AF_INET6); diff --git a/net/ipv6/netfilter/nft_chain_nat_ipv6.c b/net/ipv6/netfilter/nft_chain_nat_ipv6.c new file mode 100644 index 0000000000000000000000000000000000000000..e86dcd70dc767fb9a64d5155ff60ecd17b17e2d8 --- /dev/null +++ b/net/ipv6/netfilter/nft_chain_nat_ipv6.c @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2011 Patrick McHardy + * Copyright (c) 2012 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * IPv6 NAT chains + */ + +static unsigned int nf_nat_ipv6_fn(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(skb, &ctinfo); + struct nf_conn_nat *nat; + enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); + __be16 frag_off; + int hdrlen; + u8 nexthdr; + struct nft_pktinfo pkt; + unsigned int ret; + + if (ct == NULL || nf_ct_is_untracked(ct)) + return NF_ACCEPT; + + nat = nfct_nat(ct); + if (nat == NULL) { + /* Conntrack module was loaded late, can't add extension. */ + if (nf_ct_is_confirmed(ct)) + return NF_ACCEPT; + nat = nf_ct_ext_add(ct, NF_CT_EXT_NAT, GFP_ATOMIC); + if (nat == NULL) + return NF_ACCEPT; + } + + switch (ctinfo) { + case IP_CT_RELATED: + case IP_CT_RELATED + IP_CT_IS_REPLY: + nexthdr = ipv6_hdr(skb)->nexthdr; + hdrlen = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), + &nexthdr, &frag_off); + + if (hdrlen >= 0 && nexthdr == IPPROTO_ICMPV6) { + if (!nf_nat_icmpv6_reply_translation(skb, ct, ctinfo, + ops->hooknum, + hdrlen)) + return NF_DROP; + else + return NF_ACCEPT; + } + /* Fall through */ + case IP_CT_NEW: + if (nf_nat_initialized(ct, maniptype)) + break; + + nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out); + + ret = nft_do_chain_pktinfo(&pkt, ops); + if (ret != NF_ACCEPT) + return ret; + if (!nf_nat_initialized(ct, maniptype)) { + ret = nf_nat_alloc_null_binding(ct, ops->hooknum); + if (ret != NF_ACCEPT) + return ret; + } + default: + break; + } + + return nf_nat_packet(ct, ctinfo, ops->hooknum, skb); +} + +static unsigned int nf_nat_ipv6_prerouting(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + struct in6_addr daddr = ipv6_hdr(skb)->daddr; + unsigned int ret; + + ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); + if (ret != NF_DROP && ret != NF_STOLEN && + ipv6_addr_cmp(&daddr, &ipv6_hdr(skb)->daddr)) + skb_dst_drop(skb); + + return ret; +} + +static unsigned int nf_nat_ipv6_postrouting(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + enum ip_conntrack_info ctinfo __maybe_unused; + const struct nf_conn *ct __maybe_unused; + unsigned int ret; + + ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); +#ifdef CONFIG_XFRM + if (ret != NF_DROP && ret != NF_STOLEN && + !(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && + (ct = nf_ct_get(skb, &ctinfo)) != NULL) { + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + + if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.src.u3, + &ct->tuplehash[!dir].tuple.dst.u3) || + (ct->tuplehash[dir].tuple.src.u.all != + ct->tuplehash[!dir].tuple.dst.u.all)) + if (nf_xfrm_me_harder(skb, AF_INET6) < 0) + ret = NF_DROP; + } +#endif + return ret; +} + +static unsigned int nf_nat_ipv6_output(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + enum ip_conntrack_info ctinfo; + const struct nf_conn *ct; + unsigned int ret; + + ret = nf_nat_ipv6_fn(ops, skb, in, out, okfn); + if (ret != NF_DROP && ret != NF_STOLEN && + (ct = nf_ct_get(skb, &ctinfo)) != NULL) { + enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); + + if (!nf_inet_addr_cmp(&ct->tuplehash[dir].tuple.dst.u3, + &ct->tuplehash[!dir].tuple.src.u3)) { + if (ip6_route_me_harder(skb)) + ret = NF_DROP; + } +#ifdef CONFIG_XFRM + else if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && + ct->tuplehash[dir].tuple.dst.u.all != + ct->tuplehash[!dir].tuple.src.u.all) + if (nf_xfrm_me_harder(skb, AF_INET6)) + ret = NF_DROP; +#endif + } + return ret; +} + +static struct nf_chain_type nft_chain_nat_ipv6 = { + .family = NFPROTO_IPV6, + .name = "nat", + .type = NFT_CHAIN_T_NAT, + .hook_mask = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_LOCAL_IN), + .fn = { + [NF_INET_PRE_ROUTING] = nf_nat_ipv6_prerouting, + [NF_INET_POST_ROUTING] = nf_nat_ipv6_postrouting, + [NF_INET_LOCAL_OUT] = nf_nat_ipv6_output, + [NF_INET_LOCAL_IN] = nf_nat_ipv6_fn, + }, + .me = THIS_MODULE, +}; + +static int __init nft_chain_nat_ipv6_init(void) +{ + int err; + + err = nft_register_chain_type(&nft_chain_nat_ipv6); + if (err < 0) + return err; + + return 0; +} + +static void __exit nft_chain_nat_ipv6_exit(void) +{ + nft_unregister_chain_type(&nft_chain_nat_ipv6); +} + +module_init(nft_chain_nat_ipv6_init); +module_exit(nft_chain_nat_ipv6_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Bursztyka "); +MODULE_ALIAS_NFT_CHAIN(AF_INET6, "nat"); diff --git a/net/ipv6/netfilter/nft_chain_route_ipv6.c b/net/ipv6/netfilter/nft_chain_route_ipv6.c new file mode 100644 index 0000000000000000000000000000000000000000..3fe40f0456add7bd0272b25a6df05a4d34752632 --- /dev/null +++ b/net/ipv6/netfilter/nft_chain_route_ipv6.c @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2008 Patrick McHardy + * Copyright (c) 2012 Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, + struct sk_buff *skb, + const struct net_device *in, + const struct net_device *out, + int (*okfn)(struct sk_buff *)) +{ + unsigned int ret; + struct nft_pktinfo pkt; + struct in6_addr saddr, daddr; + u_int8_t hop_limit; + u32 mark, flowlabel; + + /* malformed packet, drop it */ + if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0) + return NF_DROP; + + /* save source/dest address, mark, hoplimit, flowlabel, priority */ + memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(saddr)); + memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(daddr)); + mark = skb->mark; + hop_limit = ipv6_hdr(skb)->hop_limit; + + /* flowlabel and prio (includes version, which shouldn't change either */ + flowlabel = *((u32 *)ipv6_hdr(skb)); + + ret = nft_do_chain_pktinfo(&pkt, ops); + if (ret != NF_DROP && ret != NF_QUEUE && + (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) || + memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) || + skb->mark != mark || + ipv6_hdr(skb)->hop_limit != hop_limit || + flowlabel != *((u_int32_t *)ipv6_hdr(skb)))) + return ip6_route_me_harder(skb) == 0 ? ret : NF_DROP; + + return ret; +} + +static struct nf_chain_type nft_chain_route_ipv6 = { + .family = NFPROTO_IPV6, + .name = "route", + .type = NFT_CHAIN_T_ROUTE, + .hook_mask = (1 << NF_INET_LOCAL_OUT), + .fn = { + [NF_INET_LOCAL_OUT] = nf_route_table_hook, + }, + .me = THIS_MODULE, +}; + +static int __init nft_chain_route_init(void) +{ + return nft_register_chain_type(&nft_chain_route_ipv6); +} + +static void __exit nft_chain_route_exit(void) +{ + nft_unregister_chain_type(&nft_chain_route_ipv6); +} + +module_init(nft_chain_route_init); +module_exit(nft_chain_route_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_CHAIN(AF_INET6, "route"); diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c index 18f19df4189f19734564b5fcfbd46ec22b67a1e8..8815e31a87fed4ba51ebd78a6724df8a3a436b85 100644 --- a/net/ipv6/ping.c +++ b/net/ipv6/ping.c @@ -116,7 +116,7 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } else { if (sk->sk_state != TCP_ESTABLISHED) return -EDESTADDRREQ; - daddr = &np->daddr; + daddr = &sk->sk_v6_daddr; } if (!iif) diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index a4ed2416399ed52622b9c60460d411b9507e6d85..3c00842b0079240f1788c83bfab346d5879336d8 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -77,20 +77,19 @@ static struct sock *__raw_v6_lookup(struct net *net, struct sock *sk, sk_for_each_from(sk) if (inet_sk(sk)->inet_num == num) { - struct ipv6_pinfo *np = inet6_sk(sk); if (!net_eq(sock_net(sk), net)) continue; - if (!ipv6_addr_any(&np->daddr) && - !ipv6_addr_equal(&np->daddr, rmt_addr)) + if (!ipv6_addr_any(&sk->sk_v6_daddr) && + !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) continue; if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) continue; - if (!ipv6_addr_any(&np->rcv_saddr)) { - if (ipv6_addr_equal(&np->rcv_saddr, loc_addr)) + if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { + if (ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr)) goto found; if (is_multicast && inet6_mc_check(sk, loc_addr, rmt_addr)) @@ -302,7 +301,7 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) } inet->inet_rcv_saddr = inet->inet_saddr = v4addr; - np->rcv_saddr = addr->sin6_addr; + sk->sk_v6_rcv_saddr = addr->sin6_addr; if (!(addr_type & IPV6_ADDR_MULTICAST)) np->saddr = addr->sin6_addr; err = 0; @@ -804,8 +803,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, * sk->sk_dst_cache. */ if (sk->sk_state == TCP_ESTABLISHED && - ipv6_addr_equal(daddr, &np->daddr)) - daddr = &np->daddr; + ipv6_addr_equal(daddr, &sk->sk_v6_daddr)) + daddr = &sk->sk_v6_daddr; if (addr_len >= sizeof(struct sockaddr_in6) && sin6->sin6_scope_id && @@ -816,7 +815,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, return -EDESTADDRREQ; proto = inet->inet_num; - daddr = &np->daddr; + daddr = &sk->sk_v6_daddr; fl6.flowlabel = np->flow_label; } diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index 1aeb473b2cc695d8d2b0a3696972ec9228455d14..cc85a9ba50101c5abf86cb3e6d493bfb9b680c74 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -82,24 +82,24 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev, * callers should be careful not to use the hash value outside the ipfrag_lock * as doing so could race with ipfrag_hash_rnd being recalculated. */ -unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr, - const struct in6_addr *daddr, u32 rnd) +static unsigned int inet6_hash_frag(__be32 id, const struct in6_addr *saddr, + const struct in6_addr *daddr) { u32 c; + net_get_random_once(&ip6_frags.rnd, sizeof(ip6_frags.rnd)); c = jhash_3words(ipv6_addr_hash(saddr), ipv6_addr_hash(daddr), - (__force u32)id, rnd); + (__force u32)id, ip6_frags.rnd); return c & (INETFRAGS_HASHSZ - 1); } -EXPORT_SYMBOL_GPL(inet6_hash_frag); static unsigned int ip6_hashfn(struct inet_frag_queue *q) { struct frag_queue *fq; fq = container_of(q, struct frag_queue, q); - return inet6_hash_frag(fq->id, &fq->saddr, &fq->daddr, ip6_frags.rnd); + return inet6_hash_frag(fq->id, &fq->saddr, &fq->daddr); } bool ip6_frag_match(struct inet_frag_queue *q, void *a) @@ -193,7 +193,7 @@ fq_find(struct net *net, __be32 id, const struct in6_addr *src, arg.ecn = ecn; read_lock(&ip6_frags.lock); - hash = inet6_hash_frag(id, src, dst, ip6_frags.rnd); + hash = inet6_hash_frag(id, src, dst); q = inet_frag_find(&net->ipv6.frags, &ip6_frags, &arg, hash); if (IS_ERR_OR_NULL(q)) { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 04e17b3309fbe91af3aaf8caad4a6c32af1eab41..7faa9d5e15033ae45715d63a33c21150e74f8665 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -619,7 +619,7 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict, goto out; m = rt6_score_route(rt, oif, strict); - if (m == RT6_NUD_FAIL_SOFT && !IS_ENABLED(CONFIG_IPV6_ROUTER_PREF)) { + if (m == RT6_NUD_FAIL_SOFT) { match_do_rr = true; m = 0; /* lowest valid score */ } else if (m < 0) { @@ -731,8 +731,11 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, prefix = &prefix_buf; } - rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, gwaddr, - dev->ifindex); + if (rinfo->prefix_len == 0) + rt = rt6_get_dflt_router(gwaddr, dev); + else + rt = rt6_get_route_info(net, prefix, rinfo->prefix_len, + gwaddr, dev->ifindex); if (rt && !lifetime) { ip6_del_rt(rt); @@ -871,11 +874,9 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, rt = ip6_rt_copy(ort, daddr); if (rt) { - if (!(rt->rt6i_flags & RTF_GATEWAY)) { - if (ort->rt6i_dst.plen != 128 && - ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) - rt->rt6i_flags |= RTF_ANYCAST; - } + if (ort->rt6i_dst.plen != 128 && + ipv6_addr_equal(&ort->rt6i_dst.addr, daddr)) + rt->rt6i_flags |= RTF_ANYCAST; rt->rt6i_flags |= RTF_CACHE; @@ -1163,7 +1164,6 @@ void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = oif; fl6.flowi6_mark = mark; - fl6.flowi6_flags = 0; fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); @@ -1262,7 +1262,6 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark) memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = oif; fl6.flowi6_mark = mark; - fl6.flowi6_flags = 0; fl6.daddr = iph->daddr; fl6.saddr = iph->saddr; fl6.flowlabel = ip6_flowinfo(iph); @@ -1284,7 +1283,6 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = oif; fl6.flowi6_mark = mark; - fl6.flowi6_flags = 0; fl6.daddr = msg->dest; fl6.saddr = iph->daddr; @@ -2831,56 +2829,12 @@ static int ip6_route_dev_notify(struct notifier_block *this, #ifdef CONFIG_PROC_FS -struct rt6_proc_arg -{ - char *buffer; - int offset; - int length; - int skip; - int len; -}; - -static int rt6_info_route(struct rt6_info *rt, void *p_arg) -{ - struct seq_file *m = p_arg; - - seq_printf(m, "%pi6 %02x ", &rt->rt6i_dst.addr, rt->rt6i_dst.plen); - -#ifdef CONFIG_IPV6_SUBTREES - seq_printf(m, "%pi6 %02x ", &rt->rt6i_src.addr, rt->rt6i_src.plen); -#else - seq_puts(m, "00000000000000000000000000000000 00 "); -#endif - if (rt->rt6i_flags & RTF_GATEWAY) { - seq_printf(m, "%pi6", &rt->rt6i_gateway); - } else { - seq_puts(m, "00000000000000000000000000000000"); - } - seq_printf(m, " %08x %08x %08x %08x %8s\n", - rt->rt6i_metric, atomic_read(&rt->dst.__refcnt), - rt->dst.__use, rt->rt6i_flags, - rt->dst.dev ? rt->dst.dev->name : ""); - return 0; -} - -static int ipv6_route_show(struct seq_file *m, void *v) -{ - struct net *net = (struct net *)m->private; - fib6_clean_all_ro(net, rt6_info_route, 0, m); - return 0; -} - -static int ipv6_route_open(struct inode *inode, struct file *file) -{ - return single_open_net(inode, file, ipv6_route_show); -} - static const struct file_operations ipv6_route_proc_fops = { .owner = THIS_MODULE, .open = ipv6_route_open, .read = seq_read, .llseek = seq_lseek, - .release = single_release_net, + .release = seq_release_net, }; static int rt6_stats_seq_show(struct seq_file *seq, void *v) diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 19269453a8eaca1d34eefa648e401ccea7d829ac..3a9038dd818d7588c950c4bca2ff56279683314c 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -933,10 +933,9 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, ttl = iph6->hop_limit; tos = INET_ECN_encapsulate(tos, ipv6_get_dsfield(iph6)); - if (likely(!skb->encapsulation)) { - skb_reset_inner_headers(skb); - skb->encapsulation = 1; - } + skb = iptunnel_handle_offloads(skb, false, SKB_GSO_SIT); + if (IS_ERR(skb)) + goto out; err = iptunnel_xmit(rt, skb, fl4.saddr, fl4.daddr, IPPROTO_IPV6, tos, ttl, df, !net_eq(tunnel->net, dev_net(dev))); @@ -946,8 +945,9 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, tx_error_icmp: dst_link_failure(skb); tx_error: - dev->stats.tx_errors++; dev_kfree_skb(skb); +out: + dev->stats.tx_errors++; return NETDEV_TX_OK; } @@ -956,13 +956,15 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) struct ip_tunnel *tunnel = netdev_priv(dev); const struct iphdr *tiph = &tunnel->parms.iph; - if (likely(!skb->encapsulation)) { - skb_reset_inner_headers(skb); - skb->encapsulation = 1; - } + skb = iptunnel_handle_offloads(skb, false, SKB_GSO_IPIP); + if (IS_ERR(skb)) + goto out; ip_tunnel_xmit(skb, dev, tiph, IPPROTO_IPIP); return NETDEV_TX_OK; +out: + dev->stats.tx_errors++; + return NETDEV_TX_OK; } static netdev_tx_t sit_tunnel_xmit(struct sk_buff *skb, @@ -1292,6 +1294,12 @@ static void ipip6_dev_free(struct net_device *dev) free_netdev(dev); } +#define SIT_FEATURES (NETIF_F_SG | \ + NETIF_F_FRAGLIST | \ + NETIF_F_HIGHDMA | \ + NETIF_F_GSO_SOFTWARE | \ + NETIF_F_HW_CSUM) + static void ipip6_tunnel_setup(struct net_device *dev) { dev->netdev_ops = &ipip6_netdev_ops; @@ -1305,6 +1313,8 @@ static void ipip6_tunnel_setup(struct net_device *dev) dev->iflink = 0; dev->addr_len = 4; dev->features |= NETIF_F_LLTX; + dev->features |= SIT_FEATURES; + dev->hw_features |= SIT_FEATURES; } static int ipip6_tunnel_init(struct net_device *dev) diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index bf63ac8a49b9df8601a5e0dabc4074e65007f94b..535a3ad262f18d7dcc04586fc55c3f0e9d4afe8b 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -24,26 +24,23 @@ #define COOKIEBITS 24 /* Upper bits store count */ #define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1) -/* Table must be sorted. */ +static u32 syncookie6_secret[2][16-4+SHA_DIGEST_WORDS]; + +/* RFC 2460, Section 8.3: + * [ipv6 tcp] MSS must be computed as the maximum packet size minus 60 [..] + * + * Due to IPV6_MIN_MTU=1280 the lowest possible MSS is 1220, which allows + * using higher values than ipv4 tcp syncookies. + * The other values are chosen based on ethernet (1500 and 9k MTU), plus + * one that accounts for common encap (PPPoe) overhead. Table must be sorted. + */ static __u16 const msstab[] = { - 64, - 512, - 536, - 1280 - 60, + 1280 - 60, /* IPV6_MIN_MTU - 60 */ 1480 - 60, 1500 - 60, - 4460 - 60, 9000 - 60, }; -/* - * This (misnamed) value is the age of syncookie which is permitted. - * Its ideal value should be dependent on TCP_TIMEOUT_INIT and - * sysctl_tcp_retries1. It's a rather complicated formula (exponential - * backoff) to compute at runtime so it's currently hardcoded here. - */ -#define COUNTER_TRIES 4 - static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct dst_entry *dst) @@ -66,14 +63,18 @@ static DEFINE_PER_CPU(__u32 [16 + 5 + SHA_WORKSPACE_WORDS], static u32 cookie_hash(const struct in6_addr *saddr, const struct in6_addr *daddr, __be16 sport, __be16 dport, u32 count, int c) { - __u32 *tmp = __get_cpu_var(ipv6_cookie_scratch); + __u32 *tmp; + + net_get_random_once(syncookie6_secret, sizeof(syncookie6_secret)); + + tmp = __get_cpu_var(ipv6_cookie_scratch); /* * we have 320 bits of information to hash, copy in the remaining - * 192 bits required for sha_transform, from the syncookie_secret + * 192 bits required for sha_transform, from the syncookie6_secret * and overwrite the digest with the secret */ - memcpy(tmp + 10, syncookie_secret[c], 44); + memcpy(tmp + 10, syncookie6_secret[c], 44); memcpy(tmp, saddr, 16); memcpy(tmp + 4, daddr, 16); tmp[8] = ((__force u32)sport << 16) + (__force u32)dport; @@ -86,8 +87,9 @@ static u32 cookie_hash(const struct in6_addr *saddr, const struct in6_addr *dadd static __u32 secure_tcp_syn_cookie(const struct in6_addr *saddr, const struct in6_addr *daddr, __be16 sport, __be16 dport, __u32 sseq, - __u32 count, __u32 data) + __u32 data) { + u32 count = tcp_cookie_time(); return (cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq + (count << COOKIEBITS) + ((cookie_hash(saddr, daddr, sport, dport, count, 1) + data) @@ -96,15 +98,14 @@ static __u32 secure_tcp_syn_cookie(const struct in6_addr *saddr, static __u32 check_tcp_syn_cookie(__u32 cookie, const struct in6_addr *saddr, const struct in6_addr *daddr, __be16 sport, - __be16 dport, __u32 sseq, __u32 count, - __u32 maxdiff) + __be16 dport, __u32 sseq) { - __u32 diff; + __u32 diff, count = tcp_cookie_time(); cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq; diff = (count - (cookie >> COOKIEBITS)) & ((__u32) -1 >> COOKIEBITS); - if (diff >= maxdiff) + if (diff >= MAX_SYNCOOKIE_AGE) return (__u32)-1; return (cookie - @@ -125,8 +126,7 @@ u32 __cookie_v6_init_sequence(const struct ipv6hdr *iph, *mssp = msstab[mssind]; return secure_tcp_syn_cookie(&iph->saddr, &iph->daddr, th->source, - th->dest, ntohl(th->seq), - jiffies / (HZ * 60), mssind); + th->dest, ntohl(th->seq), mssind); } EXPORT_SYMBOL_GPL(__cookie_v6_init_sequence); @@ -146,8 +146,7 @@ int __cookie_v6_check(const struct ipv6hdr *iph, const struct tcphdr *th, { __u32 seq = ntohl(th->seq) - 1; __u32 mssind = check_tcp_syn_cookie(cookie, &iph->saddr, &iph->daddr, - th->source, th->dest, seq, - jiffies / (HZ * 60), COUNTER_TRIES); + th->source, th->dest, seq); return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0; } @@ -157,7 +156,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) { struct tcp_options_received tcp_opt; struct inet_request_sock *ireq; - struct inet6_request_sock *ireq6; struct tcp_request_sock *treq; struct ipv6_pinfo *np = inet6_sk(sk); struct tcp_sock *tp = tcp_sk(sk); @@ -194,7 +192,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) goto out; ireq = inet_rsk(req); - ireq6 = inet6_rsk(req); treq = tcp_rsk(req); treq->listener = NULL; @@ -202,22 +199,22 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) goto out_free; req->mss = mss; - ireq->rmt_port = th->source; - ireq->loc_port = th->dest; - ireq6->rmt_addr = ipv6_hdr(skb)->saddr; - ireq6->loc_addr = ipv6_hdr(skb)->daddr; + ireq->ir_rmt_port = th->source; + ireq->ir_num = ntohs(th->dest); + ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; + ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; if (ipv6_opt_accepted(sk, skb) || np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) { atomic_inc(&skb->users); - ireq6->pktopts = skb; + ireq->pktopts = skb; } - ireq6->iif = sk->sk_bound_dev_if; + ireq->ir_iif = sk->sk_bound_dev_if; /* So that link locals have meaning */ if (!sk->sk_bound_dev_if && - ipv6_addr_type(&ireq6->rmt_addr) & IPV6_ADDR_LINKLOCAL) - ireq6->iif = inet6_iif(skb); + ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) + ireq->ir_iif = inet6_iif(skb); req->expires = 0UL; req->num_retrans = 0; @@ -241,12 +238,12 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) struct flowi6 fl6; memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_proto = IPPROTO_TCP; - fl6.daddr = ireq6->rmt_addr; + fl6.daddr = ireq->ir_v6_rmt_addr; final_p = fl6_update_dst(&fl6, np->opt, &final); - fl6.saddr = ireq6->loc_addr; + fl6.saddr = ireq->ir_v6_loc_addr; fl6.flowi6_oif = sk->sk_bound_dev_if; fl6.flowi6_mark = sk->sk_mark; - fl6.fl6_dport = inet_rsk(req)->rmt_port; + fl6.fl6_dport = ireq->ir_rmt_port; fl6.fl6_sport = inet_sk(sk)->inet_sport; security_req_classify_flow(req, flowi6_to_flowi(&fl6)); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5c71501fc917d6271f72cea50cb98b9ad783f1c4..0740f93a114a26ac09150638576f523bcd53694c 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -192,13 +192,13 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, } if (tp->rx_opt.ts_recent_stamp && - !ipv6_addr_equal(&np->daddr, &usin->sin6_addr)) { + !ipv6_addr_equal(&sk->sk_v6_daddr, &usin->sin6_addr)) { tp->rx_opt.ts_recent = 0; tp->rx_opt.ts_recent_stamp = 0; tp->write_seq = 0; } - np->daddr = usin->sin6_addr; + sk->sk_v6_daddr = usin->sin6_addr; np->flow_label = fl6.flowlabel; /* @@ -237,17 +237,17 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, } else { ipv6_addr_set_v4mapped(inet->inet_saddr, &np->saddr); ipv6_addr_set_v4mapped(inet->inet_rcv_saddr, - &np->rcv_saddr); + &sk->sk_v6_rcv_saddr); } return err; } - if (!ipv6_addr_any(&np->rcv_saddr)) - saddr = &np->rcv_saddr; + if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) + saddr = &sk->sk_v6_rcv_saddr; fl6.flowi6_proto = IPPROTO_TCP; - fl6.daddr = np->daddr; + fl6.daddr = sk->sk_v6_daddr; fl6.saddr = saddr ? *saddr : np->saddr; fl6.flowi6_oif = sk->sk_bound_dev_if; fl6.flowi6_mark = sk->sk_mark; @@ -266,7 +266,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if (saddr == NULL) { saddr = &fl6.saddr; - np->rcv_saddr = *saddr; + sk->sk_v6_rcv_saddr = *saddr; } /* set the source address */ @@ -279,7 +279,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, rt = (struct rt6_info *) dst; if (tcp_death_row.sysctl_tw_recycle && !tp->rx_opt.ts_recent_stamp && - ipv6_addr_equal(&rt->rt6i_dst.addr, &np->daddr)) + ipv6_addr_equal(&rt->rt6i_dst.addr, &sk->sk_v6_daddr)) tcp_fetch_timewait_stamp(sk, dst); icsk->icsk_ext_hdr_len = 0; @@ -298,7 +298,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, if (!tp->write_seq && likely(!tp->repair)) tp->write_seq = secure_tcpv6_sequence_number(np->saddr.s6_addr32, - np->daddr.s6_addr32, + sk->sk_v6_daddr.s6_addr32, inet->inet_sport, inet->inet_dport); @@ -465,7 +465,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, struct request_sock *req, u16 queue_mapping) { - struct inet6_request_sock *treq = inet6_rsk(req); + struct inet_request_sock *ireq = inet_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); struct sk_buff * skb; int err = -ENOMEM; @@ -477,9 +477,10 @@ static int tcp_v6_send_synack(struct sock *sk, struct dst_entry *dst, skb = tcp_make_synack(sk, dst, req, NULL); if (skb) { - __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr); + __tcp_v6_send_check(skb, &ireq->ir_v6_loc_addr, + &ireq->ir_v6_rmt_addr); - fl6->daddr = treq->rmt_addr; + fl6->daddr = ireq->ir_v6_rmt_addr; skb_set_queue_mapping(skb, queue_mapping); err = ip6_xmit(sk, skb, fl6, np->opt, np->tclass); err = net_xmit_eval(err); @@ -502,7 +503,7 @@ static int tcp_v6_rtx_synack(struct sock *sk, struct request_sock *req) static void tcp_v6_reqsk_destructor(struct request_sock *req) { - kfree_skb(inet6_rsk(req)->pktopts); + kfree_skb(inet_rsk(req)->pktopts); } #ifdef CONFIG_TCP_MD5SIG @@ -515,13 +516,13 @@ static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk, static struct tcp_md5sig_key *tcp_v6_md5_lookup(struct sock *sk, struct sock *addr_sk) { - return tcp_v6_md5_do_lookup(sk, &inet6_sk(addr_sk)->daddr); + return tcp_v6_md5_do_lookup(sk, &addr_sk->sk_v6_daddr); } static struct tcp_md5sig_key *tcp_v6_reqsk_md5_lookup(struct sock *sk, struct request_sock *req) { - return tcp_v6_md5_do_lookup(sk, &inet6_rsk(req)->rmt_addr); + return tcp_v6_md5_do_lookup(sk, &inet_rsk(req)->ir_v6_rmt_addr); } static int tcp_v6_parse_md5_keys (struct sock *sk, char __user *optval, @@ -621,10 +622,10 @@ static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key, if (sk) { saddr = &inet6_sk(sk)->saddr; - daddr = &inet6_sk(sk)->daddr; + daddr = &sk->sk_v6_daddr; } else if (req) { - saddr = &inet6_rsk(req)->loc_addr; - daddr = &inet6_rsk(req)->rmt_addr; + saddr = &inet_rsk(req)->ir_v6_loc_addr; + daddr = &inet_rsk(req)->ir_v6_rmt_addr; } else { const struct ipv6hdr *ip6h = ipv6_hdr(skb); saddr = &ip6h->saddr; @@ -949,7 +950,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) { struct tcp_options_received tmp_opt; struct request_sock *req; - struct inet6_request_sock *treq; + struct inet_request_sock *ireq; struct ipv6_pinfo *np = inet6_sk(sk); struct tcp_sock *tp = tcp_sk(sk); __u32 isn = TCP_SKB_CB(skb)->when; @@ -994,25 +995,25 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) tmp_opt.tstamp_ok = tmp_opt.saw_tstamp; tcp_openreq_init(req, &tmp_opt, skb); - treq = inet6_rsk(req); - treq->rmt_addr = ipv6_hdr(skb)->saddr; - treq->loc_addr = ipv6_hdr(skb)->daddr; + ireq = inet_rsk(req); + ireq->ir_v6_rmt_addr = ipv6_hdr(skb)->saddr; + ireq->ir_v6_loc_addr = ipv6_hdr(skb)->daddr; if (!want_cookie || tmp_opt.tstamp_ok) TCP_ECN_create_request(req, skb, sock_net(sk)); - treq->iif = sk->sk_bound_dev_if; + ireq->ir_iif = sk->sk_bound_dev_if; /* So that link locals have meaning */ if (!sk->sk_bound_dev_if && - ipv6_addr_type(&treq->rmt_addr) & IPV6_ADDR_LINKLOCAL) - treq->iif = inet6_iif(skb); + ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL) + ireq->ir_iif = inet6_iif(skb); if (!isn) { if (ipv6_opt_accepted(sk, skb) || np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo || np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) { atomic_inc(&skb->users); - treq->pktopts = skb; + ireq->pktopts = skb; } if (want_cookie) { @@ -1051,7 +1052,7 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) * to the moment of synflood. */ LIMIT_NETDEBUG(KERN_DEBUG "TCP: drop open request from %pI6/%u\n", - &treq->rmt_addr, ntohs(tcp_hdr(skb)->source)); + &ireq->ir_v6_rmt_addr, ntohs(tcp_hdr(skb)->source)); goto drop_and_release; } @@ -1086,7 +1087,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, struct request_sock *req, struct dst_entry *dst) { - struct inet6_request_sock *treq; + struct inet_request_sock *ireq; struct ipv6_pinfo *newnp, *np = inet6_sk(sk); struct tcp6_sock *newtcp6sk; struct inet_sock *newinet; @@ -1116,11 +1117,11 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, memcpy(newnp, np, sizeof(struct ipv6_pinfo)); - ipv6_addr_set_v4mapped(newinet->inet_daddr, &newnp->daddr); + ipv6_addr_set_v4mapped(newinet->inet_daddr, &newsk->sk_v6_daddr); ipv6_addr_set_v4mapped(newinet->inet_saddr, &newnp->saddr); - newnp->rcv_saddr = newnp->saddr; + newsk->sk_v6_rcv_saddr = newnp->saddr; inet_csk(newsk)->icsk_af_ops = &ipv6_mapped; newsk->sk_backlog_rcv = tcp_v4_do_rcv; @@ -1151,7 +1152,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, return newsk; } - treq = inet6_rsk(req); + ireq = inet_rsk(req); if (sk_acceptq_is_full(sk)) goto out_overflow; @@ -1185,10 +1186,10 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, memcpy(newnp, np, sizeof(struct ipv6_pinfo)); - newnp->daddr = treq->rmt_addr; - newnp->saddr = treq->loc_addr; - newnp->rcv_saddr = treq->loc_addr; - newsk->sk_bound_dev_if = treq->iif; + newsk->sk_v6_daddr = ireq->ir_v6_rmt_addr; + newnp->saddr = ireq->ir_v6_loc_addr; + newsk->sk_v6_rcv_saddr = ireq->ir_v6_loc_addr; + newsk->sk_bound_dev_if = ireq->ir_iif; /* Now IPv6 options... @@ -1203,11 +1204,11 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, /* Clone pktoptions received with SYN */ newnp->pktoptions = NULL; - if (treq->pktopts != NULL) { - newnp->pktoptions = skb_clone(treq->pktopts, + if (ireq->pktopts != NULL) { + newnp->pktoptions = skb_clone(ireq->pktopts, sk_gfp_atomic(sk, GFP_ATOMIC)); - consume_skb(treq->pktopts); - treq->pktopts = NULL; + consume_skb(ireq->pktopts); + ireq->pktopts = NULL; if (newnp->pktoptions) skb_set_owner_r(newnp->pktoptions, newsk); } @@ -1244,13 +1245,13 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb, #ifdef CONFIG_TCP_MD5SIG /* Copy over the MD5 key from the original socket */ - if ((key = tcp_v6_md5_do_lookup(sk, &newnp->daddr)) != NULL) { + if ((key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr)) != NULL) { /* We're using one, so create a matching key * on the newsk structure. If we fail to get * memory, then we end up not copying the key * across. Shucks. */ - tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newnp->daddr, + tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, AF_INET6, key->key, key->keylen, sk_gfp_atomic(sk, GFP_ATOMIC)); } @@ -1722,8 +1723,8 @@ static void get_openreq6(struct seq_file *seq, const struct sock *sk, struct request_sock *req, int i, kuid_t uid) { int ttd = req->expires - jiffies; - const struct in6_addr *src = &inet6_rsk(req)->loc_addr; - const struct in6_addr *dest = &inet6_rsk(req)->rmt_addr; + const struct in6_addr *src = &inet_rsk(req)->ir_v6_loc_addr; + const struct in6_addr *dest = &inet_rsk(req)->ir_v6_rmt_addr; if (ttd < 0) ttd = 0; @@ -1734,10 +1735,10 @@ static void get_openreq6(struct seq_file *seq, i, src->s6_addr32[0], src->s6_addr32[1], src->s6_addr32[2], src->s6_addr32[3], - ntohs(inet_rsk(req)->loc_port), + inet_rsk(req)->ir_num, dest->s6_addr32[0], dest->s6_addr32[1], dest->s6_addr32[2], dest->s6_addr32[3], - ntohs(inet_rsk(req)->rmt_port), + ntohs(inet_rsk(req)->ir_rmt_port), TCP_SYN_RECV, 0,0, /* could print option size, but that is af dependent. */ 1, /* timers active (only the expire timer) */ @@ -1758,10 +1759,9 @@ static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i) const struct inet_sock *inet = inet_sk(sp); const struct tcp_sock *tp = tcp_sk(sp); const struct inet_connection_sock *icsk = inet_csk(sp); - const struct ipv6_pinfo *np = inet6_sk(sp); - dest = &np->daddr; - src = &np->rcv_saddr; + dest = &sp->sk_v6_daddr; + src = &sp->sk_v6_rcv_saddr; destp = ntohs(inet->inet_dport); srcp = ntohs(inet->inet_sport); @@ -1810,11 +1810,10 @@ static void get_timewait6_sock(struct seq_file *seq, { const struct in6_addr *dest, *src; __u16 destp, srcp; - const struct inet6_timewait_sock *tw6 = inet6_twsk((struct sock *)tw); - long delta = tw->tw_ttd - jiffies; + s32 delta = tw->tw_ttd - inet_tw_time_stamp(); - dest = &tw6->tw_v6_daddr; - src = &tw6->tw_v6_rcv_saddr; + dest = &tw->tw_v6_daddr; + src = &tw->tw_v6_rcv_saddr; destp = ntohs(tw->tw_dport); srcp = ntohs(tw->tw_sport); @@ -1834,6 +1833,7 @@ static void get_timewait6_sock(struct seq_file *seq, static int tcp6_seq_show(struct seq_file *seq, void *v) { struct tcp_iter_state *st; + struct sock *sk = v; if (v == SEQ_START_TOKEN) { seq_puts(seq, @@ -1849,14 +1849,14 @@ static int tcp6_seq_show(struct seq_file *seq, void *v) switch (st->state) { case TCP_SEQ_STATE_LISTENING: case TCP_SEQ_STATE_ESTABLISHED: - get_tcp6_sock(seq, v, st->num); + if (sk->sk_state == TCP_TIME_WAIT) + get_timewait6_sock(seq, v, st->num); + else + get_tcp6_sock(seq, v, st->num); break; case TCP_SEQ_STATE_OPENREQ: get_openreq6(seq, st->syn_wait_sk, v, st->num, st->uid); break; - case TCP_SEQ_STATE_TIME_WAIT: - get_timewait6_sock(seq, v, st->num); - break; } out: return 0; @@ -1929,6 +1929,7 @@ struct proto tcpv6_prot = { .memory_allocated = &tcp_memory_allocated, .memory_pressure = &tcp_memory_pressure, .orphan_count = &tcp_orphan_count, + .sysctl_mem = sysctl_tcp_mem, .sysctl_wmem = sysctl_tcp_wmem, .sysctl_rmem = sysctl_tcp_rmem, .max_header = MAX_TCP_HEADER, diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c index 2ec6bf6a0aa002d6c21468d090767736161a5d1b..c1097c79890070e3d04517cecb7f0b263c0a4fc7 100644 --- a/net/ipv6/tcpv6_offload.c +++ b/net/ipv6/tcpv6_offload.c @@ -83,7 +83,7 @@ static int tcp6_gro_complete(struct sk_buff *skb) static const struct net_offload tcpv6_offload = { .callbacks = { .gso_send_check = tcp_v6_gso_send_check, - .gso_segment = tcp_tso_segment, + .gso_segment = tcp_gso_segment, .gro_receive = tcp6_gro_receive, .gro_complete = tcp6_gro_complete, }, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 18786098fd412dd5fa95a9fc096195f387b53fa0..f3893e897f721aa4345c82f0ea2b899c44fa0ae4 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -53,22 +53,42 @@ #include #include "udp_impl.h" +static unsigned int udp6_ehashfn(struct net *net, + const struct in6_addr *laddr, + const u16 lport, + const struct in6_addr *faddr, + const __be16 fport) +{ + static u32 udp6_ehash_secret __read_mostly; + static u32 udp_ipv6_hash_secret __read_mostly; + + u32 lhash, fhash; + + net_get_random_once(&udp6_ehash_secret, + sizeof(udp6_ehash_secret)); + net_get_random_once(&udp_ipv6_hash_secret, + sizeof(udp_ipv6_hash_secret)); + + lhash = (__force u32)laddr->s6_addr32[3]; + fhash = __ipv6_addr_jhash(faddr, udp_ipv6_hash_secret); + + return __inet6_ehashfn(lhash, lport, fhash, fport, + udp_ipv6_hash_secret + net_hash_mix(net)); +} + int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) { - const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr; const struct in6_addr *sk2_rcv_saddr6 = inet6_rcv_saddr(sk2); - __be32 sk1_rcv_saddr = sk_rcv_saddr(sk); - __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2); int sk_ipv6only = ipv6_only_sock(sk); int sk2_ipv6only = inet_v6_ipv6only(sk2); - int addr_type = ipv6_addr_type(sk_rcv_saddr6); + int addr_type = ipv6_addr_type(&sk->sk_v6_rcv_saddr); int addr_type2 = sk2_rcv_saddr6 ? ipv6_addr_type(sk2_rcv_saddr6) : IPV6_ADDR_MAPPED; /* if both are mapped, treat as IPv4 */ if (addr_type == IPV6_ADDR_MAPPED && addr_type2 == IPV6_ADDR_MAPPED) return (!sk2_ipv6only && - (!sk1_rcv_saddr || !sk2_rcv_saddr || - sk1_rcv_saddr == sk2_rcv_saddr)); + (!sk->sk_rcv_saddr || !sk2->sk_rcv_saddr || + sk->sk_rcv_saddr == sk2->sk_rcv_saddr)); if (addr_type2 == IPV6_ADDR_ANY && !(sk2_ipv6only && addr_type == IPV6_ADDR_MAPPED)) @@ -79,7 +99,7 @@ int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2) return 1; if (sk2_rcv_saddr6 && - ipv6_addr_equal(sk_rcv_saddr6, sk2_rcv_saddr6)) + ipv6_addr_equal(&sk->sk_v6_rcv_saddr, sk2_rcv_saddr6)) return 1; return 0; @@ -107,7 +127,7 @@ int udp_v6_get_port(struct sock *sk, unsigned short snum) unsigned int hash2_nulladdr = udp6_portaddr_hash(sock_net(sk), &in6addr_any, snum); unsigned int hash2_partial = - udp6_portaddr_hash(sock_net(sk), &inet6_sk(sk)->rcv_saddr, 0); + udp6_portaddr_hash(sock_net(sk), &sk->sk_v6_rcv_saddr, 0); /* precompute partial secondary hash */ udp_sk(sk)->udp_portaddr_hash = hash2_partial; @@ -117,7 +137,7 @@ int udp_v6_get_port(struct sock *sk, unsigned short snum) static void udp_v6_rehash(struct sock *sk) { u16 new_hash = udp6_portaddr_hash(sock_net(sk), - &inet6_sk(sk)->rcv_saddr, + &sk->sk_v6_rcv_saddr, inet_sk(sk)->inet_num); udp_lib_rehash(sk, new_hash); @@ -133,7 +153,6 @@ static inline int compute_score(struct sock *sk, struct net *net, if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum && sk->sk_family == PF_INET6) { - struct ipv6_pinfo *np = inet6_sk(sk); struct inet_sock *inet = inet_sk(sk); score = 0; @@ -142,13 +161,13 @@ static inline int compute_score(struct sock *sk, struct net *net, return -1; score++; } - if (!ipv6_addr_any(&np->rcv_saddr)) { - if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) + if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { + if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) return -1; score++; } - if (!ipv6_addr_any(&np->daddr)) { - if (!ipv6_addr_equal(&np->daddr, saddr)) + if (!ipv6_addr_any(&sk->sk_v6_daddr)) { + if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr)) return -1; score++; } @@ -171,10 +190,9 @@ static inline int compute_score2(struct sock *sk, struct net *net, if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum && sk->sk_family == PF_INET6) { - struct ipv6_pinfo *np = inet6_sk(sk); struct inet_sock *inet = inet_sk(sk); - if (!ipv6_addr_equal(&np->rcv_saddr, daddr)) + if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr)) return -1; score = 0; if (inet->inet_dport) { @@ -182,8 +200,8 @@ static inline int compute_score2(struct sock *sk, struct net *net, return -1; score++; } - if (!ipv6_addr_any(&np->daddr)) { - if (!ipv6_addr_equal(&np->daddr, saddr)) + if (!ipv6_addr_any(&sk->sk_v6_daddr)) { + if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr)) return -1; score++; } @@ -219,8 +237,8 @@ begin: badness = score; reuseport = sk->sk_reuseport; if (reuseport) { - hash = inet6_ehashfn(net, daddr, hnum, - saddr, sport); + hash = udp6_ehashfn(net, daddr, hnum, + saddr, sport); matches = 1; } else if (score == SCORE2_MAX) goto exact_match; @@ -300,8 +318,8 @@ begin: badness = score; reuseport = sk->sk_reuseport; if (reuseport) { - hash = inet6_ehashfn(net, daddr, hnum, - saddr, sport); + hash = udp6_ehashfn(net, daddr, hnum, + saddr, sport); matches = 1; } } else if (score == badness && reuseport) { @@ -551,8 +569,10 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) { int rc; - if (!ipv6_addr_any(&inet6_sk(sk)->daddr)) + if (!ipv6_addr_any(&sk->sk_v6_daddr)) { sock_rps_save_rxhash(sk, skb); + sk_mark_napi_id(sk, skb); + } rc = sock_queue_rcv_skb(sk, skb); if (rc < 0) { @@ -690,20 +710,19 @@ static struct sock *udp_v6_mcast_next(struct net *net, struct sock *sk, if (udp_sk(s)->udp_port_hash == num && s->sk_family == PF_INET6) { - struct ipv6_pinfo *np = inet6_sk(s); if (inet->inet_dport) { if (inet->inet_dport != rmt_port) continue; } - if (!ipv6_addr_any(&np->daddr) && - !ipv6_addr_equal(&np->daddr, rmt_addr)) + if (!ipv6_addr_any(&sk->sk_v6_daddr) && + !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) continue; if (s->sk_bound_dev_if && s->sk_bound_dev_if != dif) continue; - if (!ipv6_addr_any(&np->rcv_saddr)) { - if (!ipv6_addr_equal(&np->rcv_saddr, loc_addr)) + if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { + if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr)) continue; } if (!inet6_mc_check(s, loc_addr, rmt_addr)) @@ -846,7 +865,6 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, if (sk != NULL) { int ret; - sk_mark_napi_id(sk, skb); ret = udpv6_queue_rcv_skb(sk, skb); sock_put(sk); @@ -1064,7 +1082,7 @@ int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, } else if (!up->pending) { if (sk->sk_state != TCP_ESTABLISHED) return -EDESTADDRREQ; - daddr = &np->daddr; + daddr = &sk->sk_v6_daddr; } else daddr = NULL; @@ -1134,8 +1152,8 @@ do_udp_sendmsg: * sk->sk_dst_cache. */ if (sk->sk_state == TCP_ESTABLISHED && - ipv6_addr_equal(daddr, &np->daddr)) - daddr = &np->daddr; + ipv6_addr_equal(daddr, &sk->sk_v6_daddr)) + daddr = &sk->sk_v6_daddr; if (addr_len >= sizeof(struct sockaddr_in6) && sin6->sin6_scope_id && @@ -1146,7 +1164,7 @@ do_udp_sendmsg: return -EDESTADDRREQ; fl6.fl6_dport = inet->inet_dport; - daddr = &np->daddr; + daddr = &sk->sk_v6_daddr; fl6.flowlabel = np->flow_label; connected = 1; } @@ -1261,8 +1279,8 @@ do_append_data: if (dst) { if (connected) { ip6_dst_store(sk, dst, - ipv6_addr_equal(&fl6.daddr, &np->daddr) ? - &np->daddr : NULL, + ipv6_addr_equal(&fl6.daddr, &sk->sk_v6_daddr) ? + &sk->sk_v6_daddr : NULL, #ifdef CONFIG_IPV6_SUBTREES ipv6_addr_equal(&fl6.saddr, &np->saddr) ? &np->saddr : diff --git a/net/ipv6/udp_impl.h b/net/ipv6/udp_impl.h index 4691ed50a9282a108e43ce8e7de56d79c1635c4a..c779c3c90b9d3b90c3b4821508d3fdf31b6d5da6 100644 --- a/net/ipv6/udp_impl.h +++ b/net/ipv6/udp_impl.h @@ -7,33 +7,32 @@ #include #include -extern int __udp6_lib_rcv(struct sk_buff *, struct udp_table *, int ); -extern void __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *, - u8 , u8 , int , __be32 , struct udp_table *); +int __udp6_lib_rcv(struct sk_buff *, struct udp_table *, int); +void __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int, + __be32, struct udp_table *); -extern int udp_v6_get_port(struct sock *sk, unsigned short snum); +int udp_v6_get_port(struct sock *sk, unsigned short snum); -extern int udpv6_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen); -extern int udpv6_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen); +int udpv6_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); +int udpv6_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); #ifdef CONFIG_COMPAT -extern int compat_udpv6_setsockopt(struct sock *sk, int level, int optname, - char __user *optval, unsigned int optlen); -extern int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, - char __user *optval, int __user *optlen); +int compat_udpv6_setsockopt(struct sock *sk, int level, int optname, + char __user *optval, unsigned int optlen); +int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, + char __user *optval, int __user *optlen); #endif -extern int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len); -extern int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, - struct msghdr *msg, size_t len, - int noblock, int flags, int *addr_len); -extern int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb); -extern void udpv6_destroy_sock(struct sock *sk); +int udpv6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len); +int udpv6_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + size_t len, int noblock, int flags, int *addr_len); +int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); +void udpv6_destroy_sock(struct sock *sk); -extern void udp_v6_clear_sk(struct sock *sk, int size); +void udp_v6_clear_sk(struct sock *sk, int size); #ifdef CONFIG_PROC_FS -extern int udp6_seq_show(struct seq_file *seq, void *v); +int udp6_seq_show(struct seq_file *seq, void *v); #endif #endif /* _UDP6_IMPL_H */ diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index 60559511bd9c479e7996ff9826c4852e8051df72..e7359f9eaa8d4dd14b706afc9c7241c85e52d056 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -64,6 +64,8 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_UDP_TUNNEL | SKB_GSO_GRE | + SKB_GSO_IPIP | + SKB_GSO_SIT | SKB_GSO_MPLS) || !(type & (SKB_GSO_UDP)))) goto out; @@ -88,7 +90,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, /* Check if there is enough headroom to insert fragment header. */ tnl_hlen = skb_tnl_header_len(skb); - if (skb_headroom(skb) < (tnl_hlen + frag_hdr_sz)) { + if (skb->mac_header < (tnl_hlen + frag_hdr_sz)) { if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz)) goto out; } diff --git a/net/ipv6/xfrm6_mode_tunnel.c b/net/ipv6/xfrm6_mode_tunnel.c index 4770d515c2c856684e93416d4696067cae7e3114..cb04f7a16b5e102f2944be051d61511d5645d3f7 100644 --- a/net/ipv6/xfrm6_mode_tunnel.c +++ b/net/ipv6/xfrm6_mode_tunnel.c @@ -18,6 +18,65 @@ #include #include +/* Informational hook. The decap is still done here. */ +static struct xfrm_tunnel_notifier __rcu *rcv_notify_handlers __read_mostly; +static DEFINE_MUTEX(xfrm6_mode_tunnel_input_mutex); + +int xfrm6_mode_tunnel_input_register(struct xfrm_tunnel_notifier *handler) +{ + struct xfrm_tunnel_notifier __rcu **pprev; + struct xfrm_tunnel_notifier *t; + int ret = -EEXIST; + int priority = handler->priority; + + mutex_lock(&xfrm6_mode_tunnel_input_mutex); + + for (pprev = &rcv_notify_handlers; + (t = rcu_dereference_protected(*pprev, + lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL; + pprev = &t->next) { + if (t->priority > priority) + break; + if (t->priority == priority) + goto err; + + } + + handler->next = *pprev; + rcu_assign_pointer(*pprev, handler); + + ret = 0; + +err: + mutex_unlock(&xfrm6_mode_tunnel_input_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_register); + +int xfrm6_mode_tunnel_input_deregister(struct xfrm_tunnel_notifier *handler) +{ + struct xfrm_tunnel_notifier __rcu **pprev; + struct xfrm_tunnel_notifier *t; + int ret = -ENOENT; + + mutex_lock(&xfrm6_mode_tunnel_input_mutex); + for (pprev = &rcv_notify_handlers; + (t = rcu_dereference_protected(*pprev, + lockdep_is_held(&xfrm6_mode_tunnel_input_mutex))) != NULL; + pprev = &t->next) { + if (t == handler) { + *pprev = handler->next; + ret = 0; + break; + } + } + mutex_unlock(&xfrm6_mode_tunnel_input_mutex); + synchronize_net(); + + return ret; +} +EXPORT_SYMBOL_GPL(xfrm6_mode_tunnel_input_deregister); + static inline void ipip6_ecn_decapsulate(struct sk_buff *skb) { const struct ipv6hdr *outer_iph = ipv6_hdr(skb); @@ -63,8 +122,15 @@ static int xfrm6_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb) return 0; } +#define for_each_input_rcu(head, handler) \ + for (handler = rcu_dereference(head); \ + handler != NULL; \ + handler = rcu_dereference(handler->next)) + + static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) { + struct xfrm_tunnel_notifier *handler; int err = -EINVAL; if (XFRM_MODE_SKB_CB(skb)->protocol != IPPROTO_IPV6) @@ -72,6 +138,9 @@ static int xfrm6_mode_tunnel_input(struct xfrm_state *x, struct sk_buff *skb) if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto out; + for_each_input_rcu(rcv_notify_handlers, handler) + handler->handler(skb); + err = skb_unclone(skb, GFP_ATOMIC); if (err) goto out; diff --git a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h index 564eb0b8afa350eb5f933c981ab7bd0d68abd1b0..8d65bb9477fce626c7e781c0d38d78b488b5271c 100644 --- a/net/irda/irnet/irnet.h +++ b/net/irda/irnet/irnet.h @@ -509,16 +509,11 @@ typedef struct irnet_ctrl_channel */ /* -------------------------- IRDA PART -------------------------- */ -extern int - irda_irnet_create(irnet_socket *); /* Initialise a IrNET socket */ -extern int - irda_irnet_connect(irnet_socket *); /* Try to connect over IrDA */ -extern void - irda_irnet_destroy(irnet_socket *); /* Teardown a IrNET socket */ -extern int - irda_irnet_init(void); /* Initialise IrDA part of IrNET */ -extern void - irda_irnet_cleanup(void); /* Teardown IrDA part of IrNET */ +int irda_irnet_create(irnet_socket *); /* Initialise an IrNET socket */ +int irda_irnet_connect(irnet_socket *); /* Try to connect over IrDA */ +void irda_irnet_destroy(irnet_socket *); /* Teardown an IrNET socket */ +int irda_irnet_init(void); /* Initialise IrDA part of IrNET */ +void irda_irnet_cleanup(void); /* Teardown IrDA part of IrNET */ /**************************** VARIABLES ****************************/ diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index b076e8309bc2722885c27108316634f064cb44ff..9af77d9c0ec9b7327c275feeacc6766c7681f54d 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -1181,7 +1181,7 @@ static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb, !(skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) { __wsum csum = skb_checksum(skb, 0, udp_len, 0); skb->ip_summed = CHECKSUM_UNNECESSARY; - uh->check = csum_ipv6_magic(&np->saddr, &np->daddr, udp_len, + uh->check = csum_ipv6_magic(&np->saddr, &sk->sk_v6_daddr, udp_len, IPPROTO_UDP, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; @@ -1189,7 +1189,7 @@ static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb, skb->ip_summed = CHECKSUM_PARTIAL; skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct udphdr, check); - uh->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, + uh->check = ~csum_ipv6_magic(&np->saddr, &sk->sk_v6_daddr, udp_len, IPPROTO_UDP, 0); } } @@ -1713,13 +1713,13 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 struct ipv6_pinfo *np = inet6_sk(sk); if (ipv6_addr_v4mapped(&np->saddr) && - ipv6_addr_v4mapped(&np->daddr)) { + ipv6_addr_v4mapped(&sk->sk_v6_daddr)) { struct inet_sock *inet = inet_sk(sk); tunnel->v4mapped = true; inet->inet_saddr = np->saddr.s6_addr32[3]; - inet->inet_rcv_saddr = np->rcv_saddr.s6_addr32[3]; - inet->inet_daddr = np->daddr.s6_addr32[3]; + inet->inet_rcv_saddr = sk->sk_v6_rcv_saddr.s6_addr32[3]; + inet->inet_daddr = sk->sk_v6_daddr.s6_addr32[3]; } else { tunnel->v4mapped = false; } diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 6f251cbc2ed787409794f3f5063312ef7b9e9c72..1ee9f6965d6850c94f9ea70a6320836e3d0734f0 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -238,29 +238,40 @@ out: return tunnel; } -extern struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel); -extern void l2tp_tunnel_sock_put(struct sock *sk); -extern struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id); -extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth); -extern struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname); -extern struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id); -extern struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth); - -extern int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp); -extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); -extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); -extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg); -extern void __l2tp_session_unhash(struct l2tp_session *session); -extern int l2tp_session_delete(struct l2tp_session *session); -extern void l2tp_session_free(struct l2tp_session *session); -extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, unsigned char *ptr, unsigned char *optr, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb)); -extern int l2tp_session_queue_purge(struct l2tp_session *session); -extern int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb); - -extern int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len); - -extern int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, const struct l2tp_nl_cmd_ops *ops); -extern void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type); +struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel); +void l2tp_tunnel_sock_put(struct sock *sk); +struct l2tp_session *l2tp_session_find(struct net *net, + struct l2tp_tunnel *tunnel, + u32 session_id); +struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth); +struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname); +struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id); +struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth); + +int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, + u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, + struct l2tp_tunnel **tunnelp); +void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); +int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); +struct l2tp_session *l2tp_session_create(int priv_size, + struct l2tp_tunnel *tunnel, + u32 session_id, u32 peer_session_id, + struct l2tp_session_cfg *cfg); +void __l2tp_session_unhash(struct l2tp_session *session); +int l2tp_session_delete(struct l2tp_session *session); +void l2tp_session_free(struct l2tp_session *session); +void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, + unsigned char *ptr, unsigned char *optr, u16 hdrflags, + int length, int (*payload_hook)(struct sk_buff *skb)); +int l2tp_session_queue_purge(struct l2tp_session *session); +int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb); + +int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, + int hdr_len); + +int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, + const struct l2tp_nl_cmd_ops *ops); +void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type); /* Session reference counts. Incremented when code obtains a reference * to a session. diff --git a/net/l2tp/l2tp_debugfs.c b/net/l2tp/l2tp_debugfs.c index 072d7202e182ffa2125fe7de9c54c584626fdeb0..2d6760a2ae347b96d465e30192ab8a7957258d32 100644 --- a/net/l2tp/l2tp_debugfs.c +++ b/net/l2tp/l2tp_debugfs.c @@ -127,9 +127,10 @@ static void l2tp_dfs_seq_tunnel_show(struct seq_file *m, void *v) #if IS_ENABLED(CONFIG_IPV6) if (tunnel->sock->sk_family == AF_INET6) { - struct ipv6_pinfo *np = inet6_sk(tunnel->sock); + const struct ipv6_pinfo *np = inet6_sk(tunnel->sock); + seq_printf(m, " from %pI6c to %pI6c\n", - &np->saddr, &np->daddr); + &np->saddr, &tunnel->sock->sk_v6_daddr); } else #endif seq_printf(m, " from %pI4 to %pI4\n", diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index b8a6039314e868781d3130f06fbb78ced6c4a678..cfd65304be60ae8937449a8c1e4cf77406c11305 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -63,7 +63,7 @@ static struct sock *__l2tp_ip6_bind_lookup(struct net *net, struct sock *sk; sk_for_each_bound(sk, &l2tp_ip6_bind_table) { - struct in6_addr *addr = inet6_rcv_saddr(sk); + const struct in6_addr *addr = inet6_rcv_saddr(sk); struct l2tp_ip6_sock *l2tp = l2tp_ip6_sk(sk); if (l2tp == NULL) @@ -331,7 +331,7 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) rcu_read_unlock(); inet->inet_rcv_saddr = inet->inet_saddr = v4addr; - np->rcv_saddr = addr->l2tp_addr; + sk->sk_v6_rcv_saddr = addr->l2tp_addr; np->saddr = addr->l2tp_addr; l2tp_ip6_sk(sk)->conn_id = addr->l2tp_conn_id; @@ -421,14 +421,14 @@ static int l2tp_ip6_getname(struct socket *sock, struct sockaddr *uaddr, if (!lsk->peer_conn_id) return -ENOTCONN; lsa->l2tp_conn_id = lsk->peer_conn_id; - lsa->l2tp_addr = np->daddr; + lsa->l2tp_addr = sk->sk_v6_daddr; if (np->sndflow) lsa->l2tp_flowinfo = np->flow_label; } else { - if (ipv6_addr_any(&np->rcv_saddr)) + if (ipv6_addr_any(&sk->sk_v6_rcv_saddr)) lsa->l2tp_addr = np->saddr; else - lsa->l2tp_addr = np->rcv_saddr; + lsa->l2tp_addr = sk->sk_v6_rcv_saddr; lsa->l2tp_conn_id = lsk->conn_id; } @@ -537,8 +537,8 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, * sk->sk_dst_cache. */ if (sk->sk_state == TCP_ESTABLISHED && - ipv6_addr_equal(daddr, &np->daddr)) - daddr = &np->daddr; + ipv6_addr_equal(daddr, &sk->sk_v6_daddr)) + daddr = &sk->sk_v6_daddr; if (addr_len >= sizeof(struct sockaddr_in6) && lsa->l2tp_scope_id && @@ -548,7 +548,7 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, if (sk->sk_state != TCP_ESTABLISHED) return -EDESTADDRREQ; - daddr = &np->daddr; + daddr = &sk->sk_v6_daddr; fl6.flowlabel = np->flow_label; } diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 0825ff26e113f3e2acdbbe2b88bd0f4fbfd92410..be446d517bc96641d413dc0bba09d9150398a81e 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -306,8 +306,8 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 portid, u32 seq, int fla if (np) { if (nla_put(skb, L2TP_ATTR_IP6_SADDR, sizeof(np->saddr), &np->saddr) || - nla_put(skb, L2TP_ATTR_IP6_DADDR, sizeof(np->daddr), - &np->daddr)) + nla_put(skb, L2TP_ATTR_IP6_DADDR, sizeof(sk->sk_v6_daddr), + &sk->sk_v6_daddr)) goto nla_put_failure; } else #endif diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 8c46b271064a43607190198e76c1e1ff4c540396..ffda81ef1a709df605ef128ebc866cb00c75fe32 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -910,8 +910,8 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, #if IS_ENABLED(CONFIG_IPV6) } else if ((tunnel->version == 2) && (tunnel->sock->sk_family == AF_INET6)) { - struct ipv6_pinfo *np = inet6_sk(tunnel->sock); struct sockaddr_pppol2tpin6 sp; + len = sizeof(sp); memset(&sp, 0, len); sp.sa_family = AF_PPPOX; @@ -924,13 +924,13 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, sp.pppol2tp.d_session = session->peer_session_id; sp.pppol2tp.addr.sin6_family = AF_INET6; sp.pppol2tp.addr.sin6_port = inet->inet_dport; - memcpy(&sp.pppol2tp.addr.sin6_addr, &np->daddr, - sizeof(np->daddr)); + memcpy(&sp.pppol2tp.addr.sin6_addr, &tunnel->sock->sk_v6_daddr, + sizeof(tunnel->sock->sk_v6_daddr)); memcpy(uaddr, &sp, len); } else if ((tunnel->version == 3) && (tunnel->sock->sk_family == AF_INET6)) { - struct ipv6_pinfo *np = inet6_sk(tunnel->sock); struct sockaddr_pppol2tpv3in6 sp; + len = sizeof(sp); memset(&sp, 0, len); sp.sa_family = AF_PPPOX; @@ -943,8 +943,8 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr, sp.pppol2tp.d_session = session->peer_session_id; sp.pppol2tp.addr.sin6_family = AF_INET6; sp.pppol2tp.addr.sin6_port = inet->inet_dport; - memcpy(&sp.pppol2tp.addr.sin6_addr, &np->daddr, - sizeof(np->daddr)); + memcpy(&sp.pppol2tp.addr.sin6_addr, &tunnel->sock->sk_v6_daddr, + sizeof(tunnel->sock->sk_v6_daddr)); memcpy(uaddr, &sp, len); #endif } else if (tunnel->version == 3) { diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index 62535fe9f570273d83597567e8fb360262f7f208..97b5dcad50250d22bb5c5ad7ac41c18558d967ce 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -4,6 +4,7 @@ config MAC80211 select CRYPTO select CRYPTO_ARC4 select CRYPTO_AES + select CRYPTO_CCM select CRC32 select AVERAGE ---help--- @@ -258,6 +259,17 @@ config MAC80211_MESH_SYNC_DEBUG Do not select this option. +config MAC80211_MESH_CSA_DEBUG + bool "Verbose mesh channel switch debugging" + depends on MAC80211_DEBUG_MENU + depends on MAC80211_MESH + ---help--- + Selecting this option causes mac80211 to print out very verbose mesh + channel switch debugging messages (when mac80211 is taking part in a + mesh network). + + Do not select this option. + config MAC80211_MESH_PS_DEBUG bool "Verbose mesh powersave debugging" depends on MAC80211_DEBUG_MENU diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index be7614b9ed27b7827cbae7a423e5957ec1302c25..7c7df475a401693dd44cb1a6b9d68dd255b2acd5 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -2,6 +2,8 @@ * Copyright 2003-2004, Instant802 Networks, Inc. * Copyright 2005-2006, Devicescape Software, Inc. * + * Rewrite: Copyright (C) 2013 Linaro Ltd + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -17,134 +19,75 @@ #include "key.h" #include "aes_ccm.h" -static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *scratch, u8 *a) +void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, + u8 *data, size_t data_len, u8 *mic) { - int i; - u8 *b_0, *aad, *b, *s_0; - - b_0 = scratch + 3 * AES_BLOCK_SIZE; - aad = scratch + 4 * AES_BLOCK_SIZE; - b = scratch; - s_0 = scratch + AES_BLOCK_SIZE; - - crypto_cipher_encrypt_one(tfm, b, b_0); + struct scatterlist assoc, pt, ct[2]; + struct { + struct aead_request req; + u8 priv[crypto_aead_reqsize(tfm)]; + } aead_req; - /* Extra Authenticate-only data (always two AES blocks) */ - for (i = 0; i < AES_BLOCK_SIZE; i++) - aad[i] ^= b[i]; - crypto_cipher_encrypt_one(tfm, b, aad); + memset(&aead_req, 0, sizeof(aead_req)); - aad += AES_BLOCK_SIZE; + sg_init_one(&pt, data, data_len); + sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); + sg_init_table(ct, 2); + sg_set_buf(&ct[0], data, data_len); + sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); - for (i = 0; i < AES_BLOCK_SIZE; i++) - aad[i] ^= b[i]; - crypto_cipher_encrypt_one(tfm, a, aad); + aead_request_set_tfm(&aead_req.req, tfm); + aead_request_set_assoc(&aead_req.req, &assoc, assoc.length); + aead_request_set_crypt(&aead_req.req, &pt, ct, data_len, b_0); - /* Mask out bits from auth-only-b_0 */ - b_0[0] &= 0x07; - - /* S_0 is used to encrypt T (= MIC) */ - b_0[14] = 0; - b_0[15] = 0; - crypto_cipher_encrypt_one(tfm, s_0, b_0); + crypto_aead_encrypt(&aead_req.req); } - -void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, - u8 *data, size_t data_len, - u8 *cdata, u8 *mic) +int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, + u8 *data, size_t data_len, u8 *mic) { - int i, j, last_len, num_blocks; - u8 *pos, *cpos, *b, *s_0, *e, *b_0; - - b = scratch; - s_0 = scratch + AES_BLOCK_SIZE; - e = scratch + 2 * AES_BLOCK_SIZE; - b_0 = scratch + 3 * AES_BLOCK_SIZE; - - num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); - last_len = data_len % AES_BLOCK_SIZE; - aes_ccm_prepare(tfm, scratch, b); - - /* Process payload blocks */ - pos = data; - cpos = cdata; - for (j = 1; j <= num_blocks; j++) { - int blen = (j == num_blocks && last_len) ? - last_len : AES_BLOCK_SIZE; - - /* Authentication followed by encryption */ - for (i = 0; i < blen; i++) - b[i] ^= pos[i]; - crypto_cipher_encrypt_one(tfm, b, b); - - b_0[14] = (j >> 8) & 0xff; - b_0[15] = j & 0xff; - crypto_cipher_encrypt_one(tfm, e, b_0); - for (i = 0; i < blen; i++) - *cpos++ = *pos++ ^ e[i]; - } - - for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) - mic[i] = b[i] ^ s_0[i]; + struct scatterlist assoc, pt, ct[2]; + struct { + struct aead_request req; + u8 priv[crypto_aead_reqsize(tfm)]; + } aead_req; + + memset(&aead_req, 0, sizeof(aead_req)); + + sg_init_one(&pt, data, data_len); + sg_init_one(&assoc, &aad[2], be16_to_cpup((__be16 *)aad)); + sg_init_table(ct, 2); + sg_set_buf(&ct[0], data, data_len); + sg_set_buf(&ct[1], mic, IEEE80211_CCMP_MIC_LEN); + + aead_request_set_tfm(&aead_req.req, tfm); + aead_request_set_assoc(&aead_req.req, &assoc, assoc.length); + aead_request_set_crypt(&aead_req.req, ct, &pt, + data_len + IEEE80211_CCMP_MIC_LEN, b_0); + + return crypto_aead_decrypt(&aead_req.req); } - -int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, - u8 *cdata, size_t data_len, u8 *mic, u8 *data) +struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]) { - int i, j, last_len, num_blocks; - u8 *pos, *cpos, *b, *s_0, *a, *b_0; - - b = scratch; - s_0 = scratch + AES_BLOCK_SIZE; - a = scratch + 2 * AES_BLOCK_SIZE; - b_0 = scratch + 3 * AES_BLOCK_SIZE; - - num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE); - last_len = data_len % AES_BLOCK_SIZE; - aes_ccm_prepare(tfm, scratch, a); - - /* Process payload blocks */ - cpos = cdata; - pos = data; - for (j = 1; j <= num_blocks; j++) { - int blen = (j == num_blocks && last_len) ? - last_len : AES_BLOCK_SIZE; - - /* Decryption followed by authentication */ - b_0[14] = (j >> 8) & 0xff; - b_0[15] = j & 0xff; - crypto_cipher_encrypt_one(tfm, b, b_0); - for (i = 0; i < blen; i++) { - *pos = *cpos++ ^ b[i]; - a[i] ^= *pos++; - } - crypto_cipher_encrypt_one(tfm, a, a); - } - - for (i = 0; i < IEEE80211_CCMP_MIC_LEN; i++) { - if ((mic[i] ^ s_0[i]) != a[i]) - return -1; - } - - return 0; -} + struct crypto_aead *tfm; + int err; + tfm = crypto_alloc_aead("ccm(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) + return tfm; -struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]) -{ - struct crypto_cipher *tfm; + err = crypto_aead_setkey(tfm, key, WLAN_KEY_LEN_CCMP); + if (!err) + err = crypto_aead_setauthsize(tfm, IEEE80211_CCMP_MIC_LEN); + if (!err) + return tfm; - tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC); - if (!IS_ERR(tfm)) - crypto_cipher_setkey(tfm, key, WLAN_KEY_LEN_CCMP); - - return tfm; + crypto_free_aead(tfm); + return ERR_PTR(err); } - -void ieee80211_aes_key_free(struct crypto_cipher *tfm) +void ieee80211_aes_key_free(struct crypto_aead *tfm) { - crypto_free_cipher(tfm); + crypto_free_aead(tfm); } diff --git a/net/mac80211/aes_ccm.h b/net/mac80211/aes_ccm.h index 5b7d744e237032e7326bcd792bc2a17423e20f1f..2c7ab1948a2edba3964a5c0edfb7e941752719f6 100644 --- a/net/mac80211/aes_ccm.h +++ b/net/mac80211/aes_ccm.h @@ -12,13 +12,11 @@ #include -struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[]); -void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *scratch, - u8 *data, size_t data_len, - u8 *cdata, u8 *mic); -int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *scratch, - u8 *cdata, size_t data_len, - u8 *mic, u8 *data); -void ieee80211_aes_key_free(struct crypto_cipher *tfm); +struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[]); +void ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, + u8 *data, size_t data_len, u8 *mic); +int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, + u8 *data, size_t data_len, u8 *mic); +void ieee80211_aes_key_free(struct crypto_aead *tfm); #endif /* AES_CCM_H */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 629dee7ec9bfbae6dc0f00cdaf91b58968980c99..95667b088c5b73cd0e95e8c1753ed76acec9dca0 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1059,6 +1059,7 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) /* abort any running channel switch */ sdata->vif.csa_active = false; cancel_work_sync(&sdata->csa_finalize_work); + cancel_work_sync(&sdata->u.ap.request_smps_work); /* turn off carrier for this interface and dependent VLANs */ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) @@ -1342,8 +1343,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->plink_state = params->plink_state; ieee80211_mps_sta_status_update(sta); - changed |= - ieee80211_mps_local_status_update(sdata); + changed |= ieee80211_mps_set_sta_local_pm(sta, + NL80211_MESH_POWER_UNKNOWN); break; default: /* nothing */ @@ -1553,6 +1554,20 @@ static int ieee80211_change_station(struct wiphy *wiphy, mutex_unlock(&local->sta_mtx); + if ((sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_AP_VLAN) && + sta->known_smps_mode != sta->sdata->bss->req_smps && + test_sta_flag(sta, WLAN_STA_AUTHORIZED) && + sta_info_tx_streams(sta) != 1) { + ht_dbg(sta->sdata, + "%pM just authorized and MIMO capable - update SMPS\n", + sta->sta.addr); + ieee80211_send_smps_action(sta->sdata, + sta->sdata->bss->req_smps, + sta->sta.addr, + sta->sdata->vif.bss_conf.bssid); + } + if (sdata->vif.type == NL80211_IFTYPE_STATION && params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { ieee80211_recalc_ps(local, -1); @@ -2337,8 +2352,92 @@ static int ieee80211_testmode_dump(struct wiphy *wiphy, } #endif -int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, - enum ieee80211_smps_mode smps_mode) +int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, + enum ieee80211_smps_mode smps_mode) +{ + struct sta_info *sta; + enum ieee80211_smps_mode old_req; + int i; + + if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP)) + return -EINVAL; + + if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) + return 0; + + old_req = sdata->u.ap.req_smps; + sdata->u.ap.req_smps = smps_mode; + + /* AUTOMATIC doesn't mean much for AP - don't allow it */ + if (old_req == smps_mode || + smps_mode == IEEE80211_SMPS_AUTOMATIC) + return 0; + + /* If no associated stations, there's no need to do anything */ + if (!atomic_read(&sdata->u.ap.num_mcast_sta)) { + sdata->smps_mode = smps_mode; + ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); + return 0; + } + + ht_dbg(sdata, + "SMSP %d requested in AP mode, sending Action frame to %d stations\n", + smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); + + mutex_lock(&sdata->local->sta_mtx); + for (i = 0; i < STA_HASH_SIZE; i++) { + for (sta = rcu_dereference_protected(sdata->local->sta_hash[i], + lockdep_is_held(&sdata->local->sta_mtx)); + sta; + sta = rcu_dereference_protected(sta->hnext, + lockdep_is_held(&sdata->local->sta_mtx))) { + /* + * Only stations associated to our AP and + * associated VLANs + */ + if (sta->sdata->bss != &sdata->u.ap) + continue; + + /* This station doesn't support MIMO - skip it */ + if (sta_info_tx_streams(sta) == 1) + continue; + + /* + * Don't wake up a STA just to send the action frame + * unless we are getting more restrictive. + */ + if (test_sta_flag(sta, WLAN_STA_PS_STA) && + !ieee80211_smps_is_restrictive(sta->known_smps_mode, + smps_mode)) { + ht_dbg(sdata, + "Won't send SMPS to sleeping STA %pM\n", + sta->sta.addr); + continue; + } + + /* + * If the STA is not authorized, wait until it gets + * authorized and the action frame will be sent then. + */ + if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + continue; + + ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr); + ieee80211_send_smps_action(sdata, smps_mode, + sta->sta.addr, + sdata->vif.bss_conf.bssid); + } + } + mutex_unlock(&sdata->local->sta_mtx); + + sdata->smps_mode = smps_mode; + ieee80211_queue_work(&sdata->local->hw, &sdata->recalc_smps); + + return 0; +} + +int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, + enum ieee80211_smps_mode smps_mode) { const u8 *ap; enum ieee80211_smps_mode old_req; @@ -2346,6 +2445,9 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, lockdep_assert_held(&sdata->wdev.mtx); + if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_STATION)) + return -EINVAL; + old_req = sdata->u.mgd.req_smps; sdata->u.mgd.req_smps = smps_mode; @@ -2402,7 +2504,7 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, /* no change, but if automatic follow powersave */ sdata_lock(sdata); - __ieee80211_request_smps(sdata, sdata->u.mgd.req_smps); + __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.req_smps); sdata_unlock(sdata); if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) @@ -2860,35 +2962,55 @@ void ieee80211_csa_finalize_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, csa_finalize_work); struct ieee80211_local *local = sdata->local; - int err, changed; + int err, changed = 0; if (!ieee80211_sdata_running(sdata)) return; - if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP)) - return; - sdata->radar_required = sdata->csa_radar_required; err = ieee80211_vif_change_channel(sdata, &local->csa_chandef, &changed); if (WARN_ON(err < 0)) return; - err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); - if (err < 0) - return; + if (!local->use_chanctx) { + local->_oper_chandef = local->csa_chandef; + ieee80211_hw_config(local, 0); + } - changed |= err; - kfree(sdata->u.ap.next_beacon); - sdata->u.ap.next_beacon = NULL; + ieee80211_bss_info_change_notify(sdata, changed); + + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon); + if (err < 0) + return; + changed |= err; + kfree(sdata->u.ap.next_beacon); + sdata->u.ap.next_beacon = NULL; + + ieee80211_bss_info_change_notify(sdata, err); + break; + case NL80211_IFTYPE_ADHOC: + ieee80211_ibss_finish_csa(sdata); + break; +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: + err = ieee80211_mesh_finish_csa(sdata); + if (err < 0) + return; + break; +#endif + default: + WARN_ON(1); + return; + } sdata->vif.csa_active = false; ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); - ieee80211_bss_info_change_notify(sdata, changed); - cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef); } @@ -2899,6 +3021,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_local *local = sdata->local; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx *chanctx; + struct ieee80211_if_mesh __maybe_unused *ifmsh; int err, num_chanctx; if (!list_empty(&local->roc_list) || local->scanning) @@ -2936,20 +3059,76 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, if (sdata->vif.csa_active) return -EBUSY; - /* only handle AP for now. */ switch (sdata->vif.type) { case NL80211_IFTYPE_AP: + sdata->csa_counter_offset_beacon = + params->counter_offset_beacon; + sdata->csa_counter_offset_presp = params->counter_offset_presp; + sdata->u.ap.next_beacon = + cfg80211_beacon_dup(¶ms->beacon_after); + if (!sdata->u.ap.next_beacon) + return -ENOMEM; + + err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); + if (err < 0) { + kfree(sdata->u.ap.next_beacon); + return err; + } break; + case NL80211_IFTYPE_ADHOC: + if (!sdata->vif.bss_conf.ibss_joined) + return -EINVAL; + + if (params->chandef.width != sdata->u.ibss.chandef.width) + return -EINVAL; + + switch (params->chandef.width) { + case NL80211_CHAN_WIDTH_40: + if (cfg80211_get_chandef_type(¶ms->chandef) != + cfg80211_get_chandef_type(&sdata->u.ibss.chandef)) + return -EINVAL; + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + break; + default: + return -EINVAL; + } + + /* changes into another band are not supported */ + if (sdata->u.ibss.chandef.chan->band != + params->chandef.chan->band) + return -EINVAL; + + err = ieee80211_ibss_csa_beacon(sdata, params); + if (err < 0) + return err; + break; +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: + ifmsh = &sdata->u.mesh; + + if (!ifmsh->mesh_id) + return -EINVAL; + + if (params->chandef.width != sdata->vif.bss_conf.chandef.width) + return -EINVAL; + + /* changes into another band are not supported */ + if (sdata->vif.bss_conf.chandef.chan->band != + params->chandef.chan->band) + return -EINVAL; + + err = ieee80211_mesh_csa_beacon(sdata, params, true); + if (err < 0) + return err; + break; +#endif default: return -EOPNOTSUPP; } - sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after); - if (!sdata->u.ap.next_beacon) - return -ENOMEM; - - sdata->csa_counter_offset_beacon = params->counter_offset_beacon; - sdata->csa_counter_offset_presp = params->counter_offset_presp; sdata->csa_radar_required = params->radar_required; if (params->block_tx) @@ -2957,10 +3136,6 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); - err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa); - if (err < 0) - return err; - local->csa_chandef = params->chandef; sdata->vif.csa_active = true; @@ -3014,7 +3189,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, need_offchan = true; if (!ieee80211_is_action(mgmt->frame_control) || mgmt->u.action.category == WLAN_CATEGORY_PUBLIC || - mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED) + mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED || + mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT) break; rcu_read_lock(); sta = sta_info_get(sdata, mgmt->da); diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index 3a4764b2869efffdbcc3f90a363cf3f8b095496c..03ba6b5c5373b373d47956518fafeaf1338b2ad0 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -453,11 +453,6 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata, chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; drv_change_chanctx(local, ctx, chanctx_changed); - if (!local->use_chanctx) { - local->_oper_chandef = *chandef; - ieee80211_hw_config(local, 0); - } - ieee80211_recalc_chanctx_chantype(local, ctx); ieee80211_recalc_smps_chanctx(local, ctx); ieee80211_recalc_radar_chanctx(local, ctx); diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h index 4ccc5ed6237dd00e5701c19908e55d413e83d506..493d68061f0c2717d8828809e8ec27d87d6d08b6 100644 --- a/net/mac80211/debug.h +++ b/net/mac80211/debug.h @@ -44,6 +44,12 @@ #define MAC80211_MESH_SYNC_DEBUG 0 #endif +#ifdef CONFIG_MAC80211_MESH_CSA_DEBUG +#define MAC80211_MESH_CSA_DEBUG 1 +#else +#define MAC80211_MESH_CSA_DEBUG 0 +#endif + #ifdef CONFIG_MAC80211_MESH_PS_DEBUG #define MAC80211_MESH_PS_DEBUG 1 #else @@ -157,6 +163,10 @@ do { \ _sdata_dbg(MAC80211_MESH_SYNC_DEBUG, \ sdata, fmt, ##__VA_ARGS__) +#define mcsa_dbg(sdata, fmt, ...) \ + _sdata_dbg(MAC80211_MESH_CSA_DEBUG, \ + sdata, fmt, ##__VA_ARGS__) + #define mps_dbg(sdata, fmt, ...) \ _sdata_dbg(MAC80211_MESH_PS_DEBUG, \ sdata, fmt, ##__VA_ARGS__) diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index b0e32d6281146231061d34f63088db6b90106fc1..5c090e41d9bbf1ea379307c8ecd6a648cb33f0ac 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -103,54 +103,57 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf, if (!buf) return 0; - sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags); + sf += scnprintf(buf, mxln - sf, "0x%x\n", local->hw.flags); if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) - sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n"); + sf += scnprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n"); if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) - sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n"); + sf += scnprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n"); if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) - sf += snprintf(buf + sf, mxln - sf, - "HOST_BCAST_PS_BUFFERING\n"); + sf += scnprintf(buf + sf, mxln - sf, + "HOST_BCAST_PS_BUFFERING\n"); if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE) - sf += snprintf(buf + sf, mxln - sf, - "2GHZ_SHORT_SLOT_INCAPABLE\n"); + sf += scnprintf(buf + sf, mxln - sf, + "2GHZ_SHORT_SLOT_INCAPABLE\n"); if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE) - sf += snprintf(buf + sf, mxln - sf, - "2GHZ_SHORT_PREAMBLE_INCAPABLE\n"); + sf += scnprintf(buf + sf, mxln - sf, + "2GHZ_SHORT_PREAMBLE_INCAPABLE\n"); if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) - sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n"); + sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n"); if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) - sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n"); + sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n"); if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC) - sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_BEFORE_ASSOC\n"); + sf += scnprintf(buf + sf, mxln - sf, + "NEED_DTIM_BEFORE_ASSOC\n"); if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT) - sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n"); + sf += scnprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n"); if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) - sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n"); + sf += scnprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS) - sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n"); + sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n"); if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) - sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n"); + sf += scnprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) - sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); + sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n"); if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE) - sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); + sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS) - sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n"); + sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS) - sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n"); + sf += scnprintf(buf + sf, mxln - sf, + "SUPPORTS_DYNAMIC_SMPS\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) - sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n"); + sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n"); if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) - sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n"); + sf += scnprintf(buf + sf, mxln - sf, + "REPORTS_TX_ACK_STATUS\n"); if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) - sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n"); + sf += scnprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n"); if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK) - sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n"); + sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n"); if (local->hw.flags & IEEE80211_HW_AP_LINK_PS) - sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n"); + sf += scnprintf(buf + sf, mxln - sf, "AP_LINK_PS\n"); if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW) - sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n"); + sf += scnprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n"); rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); kfree(buf); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index cafe614ef93d672087fb66a1d82aaff4110cc22c..04b5a14c8a054e33227b893de78912e5ee4a6f80 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -224,12 +224,15 @@ static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata, smps_mode == IEEE80211_SMPS_AUTOMATIC)) return -EINVAL; - /* supported only on managed interfaces for now */ - if (sdata->vif.type != NL80211_IFTYPE_STATION) + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_AP) return -EOPNOTSUPP; sdata_lock(sdata); - err = __ieee80211_request_smps(sdata, smps_mode); + if (sdata->vif.type == NL80211_IFTYPE_STATION) + err = __ieee80211_request_smps_mgd(sdata, smps_mode); + else + err = __ieee80211_request_smps_ap(sdata, smps_mode); sdata_unlock(sdata); return err; @@ -245,12 +248,15 @@ static const char *smps_modes[IEEE80211_SMPS_NUM_MODES] = { static ssize_t ieee80211_if_fmt_smps(const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) { - if (sdata->vif.type != NL80211_IFTYPE_STATION) - return -EOPNOTSUPP; - - return snprintf(buf, buflen, "request: %s\nused: %s\n", - smps_modes[sdata->u.mgd.req_smps], - smps_modes[sdata->smps_mode]); + if (sdata->vif.type == NL80211_IFTYPE_STATION) + return snprintf(buf, buflen, "request: %s\nused: %s\n", + smps_modes[sdata->u.mgd.req_smps], + smps_modes[sdata->smps_mode]); + if (sdata->vif.type == NL80211_IFTYPE_AP) + return snprintf(buf, buflen, "request: %s\nused: %s\n", + smps_modes[sdata->u.ap.req_smps], + smps_modes[sdata->smps_mode]); + return -EINVAL; } static ssize_t ieee80211_if_parse_smps(struct ieee80211_sub_if_data *sdata, @@ -563,6 +569,7 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata) static void add_ap_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(num_mcast_sta); + DEBUGFS_ADD_MODE(smps, 0600); DEBUGFS_ADD(num_sta_ps); DEBUGFS_ADD(dtim_count); DEBUGFS_ADD(num_buffered_multicast); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index b3ea11f3d526962ddd8190587f82d8d84134067c..5d03c47c0a4cb4fa60e861750956c64bb8c90337 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1085,4 +1085,31 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata, } } +static inline int drv_join_ibss(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + int ret = 0; + + might_sleep(); + check_sdata_in_driver(sdata); + + trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf); + if (local->ops->join_ibss) + ret = local->ops->join_ibss(&local->hw, &sdata->vif); + trace_drv_return_int(local, ret); + return ret; +} + +static inline void drv_leave_ibss(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + might_sleep(); + check_sdata_in_driver(sdata); + + trace_drv_leave_ibss(local, sdata); + if (local->ops->leave_ibss) + local->ops->leave_ibss(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 529bf58bc14511beae95c4ff250ec54eff5c9710..9a8be8f69224d0be11fe9b10a0176a2f5a0002f1 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -448,14 +448,25 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, return 0; } -void ieee80211_request_smps_work(struct work_struct *work) +void ieee80211_request_smps_mgd_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, u.mgd.request_smps_work); sdata_lock(sdata); - __ieee80211_request_smps(sdata, sdata->u.mgd.driver_smps_mode); + __ieee80211_request_smps_mgd(sdata, sdata->u.mgd.driver_smps_mode); + sdata_unlock(sdata); +} + +void ieee80211_request_smps_ap_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.ap.request_smps_work); + + sdata_lock(sdata); + __ieee80211_request_smps_ap(sdata, sdata->u.ap.driver_smps_mode); sdata_unlock(sdata); } @@ -464,19 +475,29 @@ void ieee80211_request_smps(struct ieee80211_vif *vif, { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); - if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) + if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION && + vif->type != NL80211_IFTYPE_AP)) return; if (WARN_ON(smps_mode == IEEE80211_SMPS_OFF)) smps_mode = IEEE80211_SMPS_AUTOMATIC; - if (sdata->u.mgd.driver_smps_mode == smps_mode) - return; - - sdata->u.mgd.driver_smps_mode = smps_mode; - - ieee80211_queue_work(&sdata->local->hw, - &sdata->u.mgd.request_smps_work); + if (vif->type == NL80211_IFTYPE_STATION) { + if (sdata->u.mgd.driver_smps_mode == smps_mode) + return; + sdata->u.mgd.driver_smps_mode = smps_mode; + ieee80211_queue_work(&sdata->local->hw, + &sdata->u.mgd.request_smps_work); + } else { + /* AUTOMATIC is meaningless in AP mode */ + if (WARN_ON_ONCE(smps_mode == IEEE80211_SMPS_AUTOMATIC)) + return; + if (sdata->u.ap.driver_smps_mode == smps_mode) + return; + sdata->u.ap.driver_smps_mode = smps_mode; + ieee80211_queue_work(&sdata->local->hw, + &sdata->u.ap.request_smps_work); + } } /* this might change ... don't want non-open drivers using it */ EXPORT_SYMBOL_GPL(ieee80211_request_smps); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index a12afe77bb26b5037fe58d6624bbc87939c30cde..531be040b9ae85972d61bf7df248ae28308d811b 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -39,7 +39,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, const int beacon_int, const u32 basic_rates, const u16 capability, u64 tsf, struct cfg80211_chan_def *chandef, - bool *have_higher_than_11mbit) + bool *have_higher_than_11mbit, + struct cfg80211_csa_settings *csa_settings) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; @@ -59,6 +60,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, 2 + 8 /* max Supported Rates */ + 3 /* max DS params */ + 4 /* IBSS params */ + + 5 /* Channel Switch Announcement */ + 2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2 + sizeof(struct ieee80211_ht_cap) + 2 + sizeof(struct ieee80211_ht_operation) + @@ -135,6 +137,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, *pos++ = 0; *pos++ = 0; + if (csa_settings) { + *pos++ = WLAN_EID_CHANNEL_SWITCH; + *pos++ = 3; + *pos++ = csa_settings->block_tx ? 1 : 0; + *pos++ = ieee80211_frequency_to_channel( + csa_settings->chandef.chan->center_freq); + sdata->csa_counter_offset_beacon = (pos - presp->head); + *pos++ = csa_settings->count; + } + /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */ if (rates_n > 8) { *pos++ = WLAN_EID_EXT_SUPP_RATES; @@ -217,6 +229,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct beacon_data *presp; enum nl80211_bss_scan_width scan_width; bool have_higher_than_11mbit; + bool radar_required = false; + int err; sdata_assert_lock(sdata); @@ -235,6 +249,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IBSS | BSS_CHANGED_BEACON_ENABLED); + drv_leave_ibss(local, sdata); } presp = rcu_dereference_protected(ifibss->presp, @@ -259,6 +274,23 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, } chandef.width = NL80211_CHAN_WIDTH_20; chandef.center_freq1 = chan->center_freq; + /* check again for downgraded chandef */ + if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) { + sdata_info(sdata, + "Failed to join IBSS, beacons forbidden\n"); + return; + } + } + + err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, + &chandef); + if (err > 0) { + if (!ifibss->userspace_handles_dfs) { + sdata_info(sdata, + "Failed to join IBSS, DFS channel without control program\n"); + return; + } + radar_required = true; } ieee80211_vif_release_channel(sdata); @@ -276,13 +308,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates, capability, tsf, &chandef, - &have_higher_than_11mbit); + &have_higher_than_11mbit, NULL); if (!presp) return; rcu_assign_pointer(ifibss->presp, presp); mgmt = (void *)presp->head; + sdata->radar_required = radar_required; sdata->vif.bss_conf.enable_beacon = true; sdata->vif.bss_conf.beacon_int = beacon_int; sdata->vif.bss_conf.basic_rates = basic_rates; @@ -317,11 +350,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, else sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; + ieee80211_set_wmm_default(sdata, true); + sdata->vif.bss_conf.ibss_joined = true; sdata->vif.bss_conf.ibss_creator = creator; - ieee80211_bss_info_change_notify(sdata, bss_change); - ieee80211_set_wmm_default(sdata, true); + err = drv_join_ibss(local, sdata); + if (err) { + sdata->vif.bss_conf.ibss_joined = false; + sdata->vif.bss_conf.ibss_creator = false; + sdata->vif.bss_conf.enable_beacon = false; + sdata->vif.bss_conf.ssid_len = 0; + RCU_INIT_POINTER(ifibss->presp, NULL); + kfree_rcu(presp, rcu_head); + ieee80211_vif_release_channel(sdata); + sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n", + err); + return; + } + + ieee80211_bss_info_change_notify(sdata, bss_change); ifibss->state = IEEE80211_IBSS_MLME_JOINED; mod_timer(&ifibss->timer, @@ -416,6 +464,115 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, tsf, false); } +int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings) +{ + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + struct beacon_data *presp, *old_presp; + struct cfg80211_bss *cbss; + const struct cfg80211_bss_ies *ies; + u16 capability; + u64 tsf; + int ret = 0; + + sdata_assert_lock(sdata); + + capability = WLAN_CAPABILITY_IBSS; + + if (ifibss->privacy) + capability |= WLAN_CAPABILITY_PRIVACY; + + cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan, + ifibss->bssid, ifibss->ssid, + ifibss->ssid_len, WLAN_CAPABILITY_IBSS | + WLAN_CAPABILITY_PRIVACY, + capability); + + if (WARN_ON(!cbss)) { + ret = -EINVAL; + goto out; + } + + rcu_read_lock(); + ies = rcu_dereference(cbss->ies); + tsf = ies->tsf; + rcu_read_unlock(); + cfg80211_put_bss(sdata->local->hw.wiphy, cbss); + + old_presp = rcu_dereference_protected(ifibss->presp, + lockdep_is_held(&sdata->wdev.mtx)); + + presp = ieee80211_ibss_build_presp(sdata, + sdata->vif.bss_conf.beacon_int, + sdata->vif.bss_conf.basic_rates, + capability, tsf, &ifibss->chandef, + NULL, csa_settings); + if (!presp) { + ret = -ENOMEM; + goto out; + } + + rcu_assign_pointer(ifibss->presp, presp); + if (old_presp) + kfree_rcu(old_presp, rcu_head); + + /* it might not send the beacon for a while. send an action frame + * immediately to announce the channel switch. + */ + if (csa_settings) + ieee80211_send_action_csa(sdata, csa_settings); + + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + out: + return ret; +} + +int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + struct cfg80211_bss *cbss; + int err; + u16 capability; + + sdata_lock(sdata); + /* update cfg80211 bss information with the new channel */ + if (!is_zero_ether_addr(ifibss->bssid)) { + capability = WLAN_CAPABILITY_IBSS; + + if (ifibss->privacy) + capability |= WLAN_CAPABILITY_PRIVACY; + + cbss = cfg80211_get_bss(sdata->local->hw.wiphy, + ifibss->chandef.chan, + ifibss->bssid, ifibss->ssid, + ifibss->ssid_len, WLAN_CAPABILITY_IBSS | + WLAN_CAPABILITY_PRIVACY, + capability); + /* XXX: should not really modify cfg80211 data */ + if (cbss) { + cbss->channel = sdata->local->csa_chandef.chan; + cfg80211_put_bss(sdata->local->hw.wiphy, cbss); + } + } + + ifibss->chandef = sdata->local->csa_chandef; + + /* generate the beacon */ + err = ieee80211_ibss_csa_beacon(sdata, NULL); + sdata_unlock(sdata); + if (err < 0) + return err; + + return 0; +} + +void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + + cancel_work_sync(&ifibss->csa_connection_drop_work); +} + static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta) __acquires(RCU) { @@ -499,6 +656,315 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid, return ieee80211_ibss_finish_sta(sta); } +static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_local *local = sdata->local; + int active = 0; + struct sta_info *sta; + + sdata_assert_lock(sdata); + + rcu_read_lock(); + + list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (sta->sdata == sdata && + time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, + jiffies)) { + active++; + break; + } + } + + rcu_read_unlock(); + + return active; +} + +static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + struct ieee80211_local *local = sdata->local; + struct cfg80211_bss *cbss; + struct beacon_data *presp; + struct sta_info *sta; + int active_ibss; + u16 capability; + + active_ibss = ieee80211_sta_active_ibss(sdata); + + if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { + capability = WLAN_CAPABILITY_IBSS; + + if (ifibss->privacy) + capability |= WLAN_CAPABILITY_PRIVACY; + + cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, + ifibss->bssid, ifibss->ssid, + ifibss->ssid_len, WLAN_CAPABILITY_IBSS | + WLAN_CAPABILITY_PRIVACY, + capability); + + if (cbss) { + cfg80211_unlink_bss(local->hw.wiphy, cbss); + cfg80211_put_bss(sdata->local->hw.wiphy, cbss); + } + } + + ifibss->state = IEEE80211_IBSS_MLME_SEARCH; + + sta_info_flush(sdata); + + spin_lock_bh(&ifibss->incomplete_lock); + while (!list_empty(&ifibss->incomplete_stations)) { + sta = list_first_entry(&ifibss->incomplete_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&ifibss->incomplete_lock); + + sta_info_free(local, sta); + spin_lock_bh(&ifibss->incomplete_lock); + } + spin_unlock_bh(&ifibss->incomplete_lock); + + netif_carrier_off(sdata->dev); + + sdata->vif.bss_conf.ibss_joined = false; + sdata->vif.bss_conf.ibss_creator = false; + sdata->vif.bss_conf.enable_beacon = false; + sdata->vif.bss_conf.ssid_len = 0; + + /* remove beacon */ + presp = rcu_dereference_protected(ifibss->presp, + lockdep_is_held(&sdata->wdev.mtx)); + RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); + if (presp) + kfree_rcu(presp, rcu_head); + + clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | + BSS_CHANGED_IBSS); + drv_leave_ibss(local, sdata); + ieee80211_vif_release_channel(sdata); +} + +static void ieee80211_csa_connection_drop_work(struct work_struct *work) +{ + struct ieee80211_sub_if_data *sdata = + container_of(work, struct ieee80211_sub_if_data, + u.ibss.csa_connection_drop_work); + + ieee80211_ibss_disconnect(sdata); + synchronize_rcu(); + skb_queue_purge(&sdata->skb_queue); + + /* trigger a scan to find another IBSS network to join */ + ieee80211_queue_work(&sdata->local->hw, &sdata->work); +} + +static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + int err; + + /* if the current channel is a DFS channel, mark the channel as + * unavailable. + */ + err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, + &ifibss->chandef); + if (err > 0) + cfg80211_radar_event(sdata->local->hw.wiphy, &ifibss->chandef, + GFP_ATOMIC); +} + +static bool +ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *elems, + bool beacon) +{ + struct cfg80211_csa_settings params; + struct ieee80211_csa_ie csa_ie; + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; + enum nl80211_channel_type ch_type; + int err, num_chanctx; + u32 sta_flags; + + if (sdata->vif.csa_active) + return true; + + if (!sdata->vif.bss_conf.ibss_joined) + return false; + + sta_flags = IEEE80211_STA_DISABLE_VHT; + switch (ifibss->chandef.width) { + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_20_NOHT: + sta_flags |= IEEE80211_STA_DISABLE_HT; + /* fall through */ + case NL80211_CHAN_WIDTH_20: + sta_flags |= IEEE80211_STA_DISABLE_40MHZ; + break; + default: + break; + } + + memset(¶ms, 0, sizeof(params)); + memset(&csa_ie, 0, sizeof(csa_ie)); + err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, + ifibss->chandef.chan->band, + sta_flags, ifibss->bssid, &csa_ie); + /* can't switch to destination channel, fail */ + if (err < 0) + goto disconnect; + + /* did not contain a CSA */ + if (err) + return false; + + params.count = csa_ie.count; + params.chandef = csa_ie.chandef; + + if (ifibss->chandef.chan->band != params.chandef.chan->band) + goto disconnect; + + switch (ifibss->chandef.width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_40: + /* keep our current HT mode (HT20/HT40+/HT40-), even if + * another mode has been announced. The mode is not adopted + * within the beacon while doing CSA and we should therefore + * keep the mode which we announce. + */ + ch_type = cfg80211_get_chandef_type(&ifibss->chandef); + cfg80211_chandef_create(¶ms.chandef, params.chandef.chan, + ch_type); + break; + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + if (params.chandef.width != ifibss->chandef.width) { + sdata_info(sdata, + "IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", + ifibss->bssid, + params.chandef.chan->center_freq, + params.chandef.width, + params.chandef.center_freq1, + params.chandef.center_freq2); + goto disconnect; + } + break; + default: + /* should not happen, sta_flags should prevent VHT modes. */ + WARN_ON(1); + goto disconnect; + } + + if (!cfg80211_reg_can_beacon(sdata->local->hw.wiphy, ¶ms.chandef)) { + sdata_info(sdata, + "IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", + ifibss->bssid, + params.chandef.chan->center_freq, + params.chandef.width, + params.chandef.center_freq1, + params.chandef.center_freq2); + goto disconnect; + } + + err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, + ¶ms.chandef); + if (err < 0) + goto disconnect; + if (err) { + /* IBSS-DFS only allowed with a control program */ + if (!ifibss->userspace_handles_dfs) + goto disconnect; + + params.radar_required = true; + } + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) { + rcu_read_unlock(); + goto disconnect; + } + + /* don't handle for multi-VIF cases */ + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); + if (chanctx->refcount > 1) { + rcu_read_unlock(); + goto disconnect; + } + num_chanctx = 0; + list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list) + num_chanctx++; + + if (num_chanctx > 1) { + rcu_read_unlock(); + goto disconnect; + } + rcu_read_unlock(); + + /* all checks done, now perform the channel switch. */ + ibss_dbg(sdata, + "received channel switch announcement to go to channel %d MHz\n", + params.chandef.chan->center_freq); + + params.block_tx = !!csa_ie.mode; + + ieee80211_ibss_csa_beacon(sdata, ¶ms); + sdata->csa_radar_required = params.radar_required; + + if (params.block_tx) + ieee80211_stop_queues_by_reason(&sdata->local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + + sdata->local->csa_chandef = params.chandef; + sdata->vif.csa_active = true; + + ieee80211_bss_info_change_notify(sdata, err); + drv_channel_switch_beacon(sdata, ¶ms.chandef); + + ieee80211_ibss_csa_mark_radar(sdata); + + return true; +disconnect: + ibss_dbg(sdata, "Can't handle channel switch, disconnect\n"); + ieee80211_queue_work(&sdata->local->hw, + &ifibss->csa_connection_drop_work); + + ieee80211_ibss_csa_mark_radar(sdata); + + return true; +} + +static void +ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status, + struct ieee802_11_elems *elems) +{ + int required_len; + + if (len < IEEE80211_MIN_ACTION_SIZE + 1) + return; + + /* CSA is the only action we handle for now */ + if (mgmt->u.action.u.measurement.action_code != + WLAN_ACTION_SPCT_CHL_SWITCH) + return; + + required_len = IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.chan_switch); + if (len < required_len) + return; + + ieee80211_ibss_process_chanswitch(sdata, elems, false); +} + static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) @@ -661,10 +1127,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, /* check if we need to merge IBSS */ - /* we use a fixed BSSID */ - if (sdata->u.ibss.fixed_bssid) - goto put_bss; - /* not an IBSS */ if (!(cbss->capability & WLAN_CAPABILITY_IBSS)) goto put_bss; @@ -680,10 +1142,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.ssid_len)) goto put_bss; + /* process channel switch */ + if (ieee80211_ibss_process_chanswitch(sdata, elems, true)) + goto put_bss; + /* same BSSID */ if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid)) goto put_bss; + /* we use a fixed BSSID */ + if (sdata->u.ibss.fixed_bssid) + goto put_bss; + if (ieee80211_have_rx_timestamp(rx_status)) { /* time when timestamp field was received */ rx_timestamp = @@ -775,30 +1245,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, ieee80211_queue_work(&local->hw, &sdata->work); } -static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) -{ - struct ieee80211_local *local = sdata->local; - int active = 0; - struct sta_info *sta; - - sdata_assert_lock(sdata); - - rcu_read_lock(); - - list_for_each_entry_rcu(sta, &local->sta_list, list) { - if (sta->sdata == sdata && - time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL, - jiffies)) { - active++; - break; - } - } - - rcu_read_unlock(); - - return active; -} - static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -1076,6 +1522,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct ieee80211_rx_status *rx_status; struct ieee80211_mgmt *mgmt; u16 fc; + struct ieee802_11_elems elems; + int ies_len; rx_status = IEEE80211_SKB_RXCB(skb); mgmt = (struct ieee80211_mgmt *) skb->data; @@ -1101,6 +1549,27 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, case IEEE80211_STYPE_DEAUTH: ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len); break; + case IEEE80211_STYPE_ACTION: + switch (mgmt->u.action.category) { + case WLAN_CATEGORY_SPECTRUM_MGMT: + ies_len = skb->len - + offsetof(struct ieee80211_mgmt, + u.action.u.chan_switch.variable); + + if (ies_len < 0) + break; + + ieee802_11_parse_elems( + mgmt->u.action.u.chan_switch.variable, + ies_len, true, &elems); + + if (elems.parse_error) + break; + + ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len, + rx_status, &elems); + break; + } } mgmt_out: @@ -1167,6 +1636,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) (unsigned long) sdata); INIT_LIST_HEAD(&ifibss->incomplete_stations); spin_lock_init(&ifibss->incomplete_lock); + INIT_WORK(&ifibss->csa_connection_drop_work, + ieee80211_csa_connection_drop_work); } /* scan finished notification */ @@ -1202,6 +1673,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.privacy = params->privacy; sdata->u.ibss.control_port = params->control_port; + sdata->u.ibss.userspace_handles_dfs = params->userspace_handles_dfs; sdata->u.ibss.basic_rates = params->basic_rates; /* fix basic_rates if channel does not support these rates */ @@ -1265,73 +1737,19 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - struct ieee80211_local *local = sdata->local; - struct cfg80211_bss *cbss; - u16 capability; - int active_ibss; - struct sta_info *sta; - struct beacon_data *presp; - - active_ibss = ieee80211_sta_active_ibss(sdata); - - if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) { - capability = WLAN_CAPABILITY_IBSS; - if (ifibss->privacy) - capability |= WLAN_CAPABILITY_PRIVACY; - - cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, - ifibss->bssid, ifibss->ssid, - ifibss->ssid_len, WLAN_CAPABILITY_IBSS | - WLAN_CAPABILITY_PRIVACY, - capability); - - if (cbss) { - cfg80211_unlink_bss(local->hw.wiphy, cbss); - cfg80211_put_bss(local->hw.wiphy, cbss); - } - } - - ifibss->state = IEEE80211_IBSS_MLME_SEARCH; - memset(ifibss->bssid, 0, ETH_ALEN); + ieee80211_ibss_disconnect(sdata); ifibss->ssid_len = 0; - - sta_info_flush(sdata); - - spin_lock_bh(&ifibss->incomplete_lock); - while (!list_empty(&ifibss->incomplete_stations)) { - sta = list_first_entry(&ifibss->incomplete_stations, - struct sta_info, list); - list_del(&sta->list); - spin_unlock_bh(&ifibss->incomplete_lock); - - sta_info_free(local, sta); - spin_lock_bh(&ifibss->incomplete_lock); - } - spin_unlock_bh(&ifibss->incomplete_lock); - - netif_carrier_off(sdata->dev); + memset(ifibss->bssid, 0, ETH_ALEN); /* remove beacon */ kfree(sdata->u.ibss.ie); - presp = rcu_dereference_protected(ifibss->presp, - lockdep_is_held(&sdata->wdev.mtx)); - RCU_INIT_POINTER(sdata->u.ibss.presp, NULL); /* on the next join, re-program HT parameters */ memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa)); memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask)); - sdata->vif.bss_conf.ibss_joined = false; - sdata->vif.bss_conf.ibss_creator = false; - sdata->vif.bss_conf.enable_beacon = false; - sdata->vif.bss_conf.ssid_len = 0; - clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state); - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED | - BSS_CHANGED_IBSS); - ieee80211_vif_release_channel(sdata); synchronize_rcu(); - kfree(presp); skb_queue_purge(&sdata->skb_queue); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 611abfcfb5ebba3c135edbcf8b2efb4a47f38374..29dc505be125c3c19737f8f6cee911492c52727c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -262,6 +262,10 @@ struct ieee80211_if_ap { struct ps_data ps; atomic_t num_mcast_sta; /* number of stations receiving multicast */ + enum ieee80211_smps_mode req_smps, /* requested smps mode */ + driver_smps_mode; /* smps mode request */ + + struct work_struct request_smps_work; }; struct ieee80211_if_wds { @@ -322,7 +326,6 @@ struct ieee80211_roc_work { /* flags used in struct ieee80211_if_managed.flags */ enum ieee80211_sta_flags { - IEEE80211_STA_BEACON_POLL = BIT(0), IEEE80211_STA_CONNECTION_POLL = BIT(1), IEEE80211_STA_CONTROL_PORT = BIT(2), IEEE80211_STA_DISABLE_HT = BIT(4), @@ -335,6 +338,7 @@ enum ieee80211_sta_flags { IEEE80211_STA_DISABLE_VHT = BIT(11), IEEE80211_STA_DISABLE_80P80MHZ = BIT(12), IEEE80211_STA_DISABLE_160MHZ = BIT(13), + IEEE80211_STA_DISABLE_WMM = BIT(14), }; struct ieee80211_mgd_auth_data { @@ -487,6 +491,7 @@ struct ieee80211_if_managed { struct ieee80211_if_ibss { struct timer_list timer; + struct work_struct csa_connection_drop_work; unsigned long last_scan_completed; @@ -497,6 +502,7 @@ struct ieee80211_if_ibss { bool privacy; bool control_port; + bool userspace_handles_dfs; u8 bssid[ETH_ALEN] __aligned(2); u8 ssid[IEEE80211_MAX_SSID_LEN]; @@ -538,6 +544,11 @@ struct ieee80211_mesh_sync_ops { /* add other framework functions here */ }; +struct mesh_csa_settings { + struct rcu_head rcu_head; + struct cfg80211_csa_settings settings; +}; + struct ieee80211_if_mesh { struct timer_list housekeeping_timer; struct timer_list mesh_path_timer; @@ -598,6 +609,11 @@ struct ieee80211_if_mesh { int ps_peers_light_sleep; int ps_peers_deep_sleep; struct ps_data ps; + /* Channel Switching Support */ + struct mesh_csa_settings __rcu *csa; + bool chsw_init; + u8 chsw_ttl; + u16 pre_value; }; #ifdef CONFIG_MAC80211_MESH @@ -1206,6 +1222,14 @@ struct ieee80211_ra_tid { u16 tid; }; +/* this struct holds the value parsing from channel switch IE */ +struct ieee80211_csa_ie { + struct cfg80211_chan_def chandef; + u8 mode; + u8 count; + u8 ttl; +}; + /* Parsed Information Elements */ struct ieee802_11_elems { const u8 *ie_start; @@ -1242,6 +1266,7 @@ struct ieee802_11_elems { const struct ieee80211_timeout_interval_ie *timeout_int; const u8 *opmode_notif; const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; + const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie; /* length of them, respectively */ u8 ssid_len; @@ -1333,11 +1358,19 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata); void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings); +int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata); +void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata); /* mesh code */ void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings, + bool csa_action); +int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata); /* scan/BSS handling */ void ieee80211_scan_work(struct work_struct *work); @@ -1434,7 +1467,10 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata, enum ieee80211_smps_mode smps, const u8 *da, const u8 *bssid); -void ieee80211_request_smps_work(struct work_struct *work); +void ieee80211_request_smps_ap_work(struct work_struct *work); +void ieee80211_request_smps_mgd_work(struct work_struct *work); +bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, + enum ieee80211_smps_mode smps_mode_new); void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); @@ -1484,6 +1520,28 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata, void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len); +/** + * ieee80211_parse_ch_switch_ie - parses channel switch IEs + * @sdata: the sdata of the interface which has received the frame + * @elems: parsed 802.11 elements received with the frame + * @beacon: indicates if the frame was a beacon or probe response + * @current_band: indicates the current band + * @sta_flags: contains information about own capabilities and restrictions + * to decide which channel switch announcements can be accepted. Only the + * following subset of &enum ieee80211_sta_flags are evaluated: + * %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT, + * %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ, + * %IEEE80211_STA_DISABLE_160MHZ. + * @bssid: the currently connected bssid (for reporting) + * @csa_ie: parsed 802.11 csa elements on count, mode, chandef and mesh ttl. + All of them will be filled with if success only. + * Return: 0 on success, <0 on error and >0 if there is nothing to parse. + */ +int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *elems, bool beacon, + enum ieee80211_band current_band, + u32 sta_flags, u8 *bssid, + struct ieee80211_csa_ie *csa_ie); /* Suspend/resume and hw reconfiguration */ int ieee80211_reconfig(struct ieee80211_local *local); @@ -1629,8 +1687,10 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, u32 ieee80211_sta_get_rates(struct ieee80211_sub_if_data *sdata, struct ieee802_11_elems *elems, enum ieee80211_band band, u32 *basic_rates); -int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, - enum ieee80211_smps_mode smps_mode); +int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata, + enum ieee80211_smps_mode smps_mode); +int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, + enum ieee80211_smps_mode smps_mode); void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); size_t ieee80211_ie_split(const u8 *ies, size_t ielen, @@ -1657,6 +1717,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, const struct ieee80211_ht_operation *ht_oper, struct cfg80211_chan_def *chandef); +u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c); int __must_check ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, @@ -1685,6 +1746,8 @@ void ieee80211_dfs_cac_timer(unsigned long data); void ieee80211_dfs_cac_timer_work(struct work_struct *work); void ieee80211_dfs_cac_cancel(struct ieee80211_local *local); void ieee80211_dfs_radar_detected_work(struct work_struct *work); +int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings); #ifdef CONFIG_MAC80211_NOINLINE #define debug_noinline noinline diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index fcecd633514e2185b0a31235cac345ab08df9433..ff101ea1d9ae1e208deb5d6fe1d67c1677b398c2 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -766,6 +766,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, if (sdata->vif.type == NL80211_IFTYPE_STATION) ieee80211_mgd_stop(sdata); + if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + ieee80211_ibss_stop(sdata); + + /* * Remove all stations associated with this interface. * @@ -1289,7 +1293,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata, case NL80211_IFTYPE_AP: skb_queue_head_init(&sdata->u.ap.ps.bc_buf); INIT_LIST_HEAD(&sdata->u.ap.vlans); + INIT_WORK(&sdata->u.ap.request_smps_work, + ieee80211_request_smps_ap_work); sdata->vif.bss_conf.bssid = sdata->vif.addr; + sdata->u.ap.req_smps = IEEE80211_SMPS_OFF; break; case NL80211_IFTYPE_P2P_CLIENT: type = NL80211_IFTYPE_STATION; diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 620677e897bd0fc82fd5f4e737156096c05a9eef..3e51dd7d98b34aad114e6b927beae8e4ecdde9fb 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -879,7 +879,7 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif, keyconf->keylen, keyconf->key, 0, NULL); if (IS_ERR(key)) - return ERR_PTR(PTR_ERR(key)); + return ERR_CAST(key); if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED) key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 036d57e76a5e37d4156d5abedcd479e0aa0ffb6e..aaae0ed3700402433ae56244eb97baa2804aba5b 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -83,7 +83,7 @@ struct ieee80211_key { * Management frames. */ u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN]; - struct crypto_cipher *tfm; + struct crypto_aead *tfm; u32 replays; /* dot11RSNAStatsCCMPReplays */ } ccmp; struct { diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 707ac61d63e51a8a1528c8c65204c92c94400768..896fe3bd599e9bedd5db13dbc8ed04ab76e5bd88 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -12,6 +12,7 @@ #include #include "ieee80211_i.h" #include "mesh.h" +#include "driver-ops.h" static int mesh_allocated; static struct kmem_cache *rm_cache; @@ -610,6 +611,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) struct sk_buff *skb; struct ieee80211_mgmt *mgmt; struct ieee80211_chanctx_conf *chanctx_conf; + struct mesh_csa_settings *csa; enum ieee80211_band band; u8 *pos; struct ieee80211_sub_if_data *sdata; @@ -624,6 +626,10 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) head_len = hdr_len + 2 + /* NULL SSID */ + /* Channel Switch Announcement */ + 2 + sizeof(struct ieee80211_channel_sw_ie) + + /* Mesh Channel Swith Parameters */ + 2 + sizeof(struct ieee80211_mesh_chansw_params_ie) + 2 + 8 + /* supported rates */ 2 + 3; /* DS params */ tail_len = 2 + (IEEE80211_MAX_SUPP_RATES - 8) + @@ -665,6 +671,38 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) *pos++ = WLAN_EID_SSID; *pos++ = 0x0; + rcu_read_lock(); + csa = rcu_dereference(ifmsh->csa); + if (csa) { + __le16 pre_value; + + pos = skb_put(skb, 13); + memset(pos, 0, 13); + *pos++ = WLAN_EID_CHANNEL_SWITCH; + *pos++ = 3; + *pos++ = 0x0; + *pos++ = ieee80211_frequency_to_channel( + csa->settings.chandef.chan->center_freq); + sdata->csa_counter_offset_beacon = hdr_len + 6; + *pos++ = csa->settings.count; + *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; + *pos++ = 6; + if (ifmsh->chsw_init) { + *pos++ = ifmsh->mshcfg.dot11MeshTTL; + *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; + } else { + *pos++ = ifmsh->chsw_ttl; + } + *pos++ |= csa->settings.block_tx ? + WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; + put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); + pos += 2; + pre_value = cpu_to_le16(ifmsh->pre_value); + memcpy(pos, &pre_value, 2); + pos += 2; + } + rcu_read_unlock(); + if (ieee80211_add_srates_ie(sdata, skb, true, band) || mesh_add_ds_params_ie(sdata, skb)) goto out_free; @@ -812,6 +850,127 @@ void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata) ieee80211_configure_filter(local); } +static bool +ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *elems, bool beacon) +{ + struct cfg80211_csa_settings params; + struct ieee80211_csa_ie csa_ie; + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_chanctx *chanctx; + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + int err, num_chanctx; + u32 sta_flags; + + if (sdata->vif.csa_active) + return true; + + if (!ifmsh->mesh_id) + return false; + + sta_flags = IEEE80211_STA_DISABLE_VHT; + switch (sdata->vif.bss_conf.chandef.width) { + case NL80211_CHAN_WIDTH_20_NOHT: + sta_flags |= IEEE80211_STA_DISABLE_HT; + case NL80211_CHAN_WIDTH_20: + sta_flags |= IEEE80211_STA_DISABLE_40MHZ; + break; + default: + break; + } + + memset(¶ms, 0, sizeof(params)); + memset(&csa_ie, 0, sizeof(csa_ie)); + err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band, + sta_flags, sdata->vif.addr, + &csa_ie); + if (err < 0) + return false; + if (err) + return false; + + params.chandef = csa_ie.chandef; + params.count = csa_ie.count; + + if (sdata->vif.bss_conf.chandef.chan->band != + params.chandef.chan->band) + return false; + + if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef, + IEEE80211_CHAN_DISABLED)) { + sdata_info(sdata, + "mesh STA %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), aborting\n", + sdata->vif.addr, + params.chandef.chan->center_freq, + params.chandef.width, + params.chandef.center_freq1, + params.chandef.center_freq2); + return false; + } + + err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy, + ¶ms.chandef); + if (err < 0) + return false; + if (err) { + params.radar_required = true; + /* TODO: DFS not (yet) supported */ + return false; + } + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) + goto failed_chswitch; + + /* don't handle for multi-VIF cases */ + chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); + if (chanctx->refcount > 1) + goto failed_chswitch; + + num_chanctx = 0; + list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list) + num_chanctx++; + + if (num_chanctx > 1) + goto failed_chswitch; + + rcu_read_unlock(); + + mcsa_dbg(sdata, + "received channel switch announcement to go to channel %d MHz\n", + params.chandef.chan->center_freq); + + params.block_tx = csa_ie.mode & WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT; + if (beacon) + ifmsh->chsw_ttl = csa_ie.ttl - 1; + else + ifmsh->chsw_ttl = 0; + + if (ifmsh->chsw_ttl > 0) + if (ieee80211_mesh_csa_beacon(sdata, ¶ms, false) < 0) + return false; + + sdata->csa_radar_required = params.radar_required; + + if (params.block_tx) + ieee80211_stop_queues_by_reason(&sdata->local->hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_CSA); + + sdata->local->csa_chandef = params.chandef; + sdata->vif.csa_active = true; + + ieee80211_bss_info_change_notify(sdata, err); + drv_channel_switch_beacon(sdata, ¶ms.chandef); + + return true; +failed_chswitch: + rcu_read_unlock(); + return false; +} + static void ieee80211_mesh_rx_probe_req(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) @@ -918,6 +1077,142 @@ static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, if (ifmsh->sync_ops) ifmsh->sync_ops->rx_bcn_presp(sdata, stype, mgmt, &elems, rx_status); + + if (!ifmsh->chsw_init) + ieee80211_mesh_process_chnswitch(sdata, &elems, true); +} + +int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct mesh_csa_settings *tmp_csa_settings; + int ret = 0; + + /* Reset the TTL value and Initiator flag */ + ifmsh->chsw_init = false; + ifmsh->chsw_ttl = 0; + + /* Remove the CSA and MCSP elements from the beacon */ + tmp_csa_settings = rcu_dereference(ifmsh->csa); + rcu_assign_pointer(ifmsh->csa, NULL); + kfree_rcu(tmp_csa_settings, rcu_head); + ret = ieee80211_mesh_rebuild_beacon(sdata); + if (ret) + return -EINVAL; + + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + + mcsa_dbg(sdata, "complete switching to center freq %d MHz", + sdata->vif.bss_conf.chandef.chan->center_freq); + return 0; +} + +int ieee80211_mesh_csa_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings, + bool csa_action) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct mesh_csa_settings *tmp_csa_settings; + int ret = 0; + + tmp_csa_settings = kmalloc(sizeof(*tmp_csa_settings), + GFP_ATOMIC); + if (!tmp_csa_settings) + return -ENOMEM; + + memcpy(&tmp_csa_settings->settings, csa_settings, + sizeof(struct cfg80211_csa_settings)); + + rcu_assign_pointer(ifmsh->csa, tmp_csa_settings); + + ret = ieee80211_mesh_rebuild_beacon(sdata); + if (ret) { + tmp_csa_settings = rcu_dereference(ifmsh->csa); + rcu_assign_pointer(ifmsh->csa, NULL); + kfree_rcu(tmp_csa_settings, rcu_head); + return ret; + } + + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + + if (csa_action) + ieee80211_send_action_csa(sdata, csa_settings); + + return 0; +} + +static int mesh_fwd_csa_frame(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee80211_mgmt *mgmt_fwd; + struct sk_buff *skb; + struct ieee80211_local *local = sdata->local; + u8 *pos = mgmt->u.action.u.chan_switch.variable; + size_t offset_ttl; + + skb = dev_alloc_skb(local->tx_headroom + len); + if (!skb) + return -ENOMEM; + skb_reserve(skb, local->tx_headroom); + mgmt_fwd = (struct ieee80211_mgmt *) skb_put(skb, len); + + /* offset_ttl is based on whether the secondary channel + * offset is available or not. Substract 1 from the mesh TTL + * and disable the initiator flag before forwarding. + */ + offset_ttl = (len < 42) ? 7 : 10; + *(pos + offset_ttl) -= 1; + *(pos + offset_ttl + 1) &= ~WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; + sdata->u.mesh.chsw_ttl = *(pos + offset_ttl); + + memcpy(mgmt_fwd, mgmt, len); + eth_broadcast_addr(mgmt_fwd->da); + memcpy(mgmt_fwd->sa, sdata->vif.addr, ETH_ALEN); + memcpy(mgmt_fwd->bssid, sdata->vif.addr, ETH_ALEN); + + ieee80211_tx_skb(sdata, skb); + return 0; +} + +static void mesh_rx_csa_frame(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + struct ieee802_11_elems elems; + u16 pre_value; + bool fwd_csa = true; + size_t baselen; + u8 *pos, ttl; + + if (mgmt->u.action.u.measurement.action_code != + WLAN_ACTION_SPCT_CHL_SWITCH) + return; + + pos = mgmt->u.action.u.chan_switch.variable; + baselen = offsetof(struct ieee80211_mgmt, + u.action.u.chan_switch.variable); + ieee802_11_parse_elems(pos, len - baselen, false, &elems); + + ttl = elems.mesh_chansw_params_ie->mesh_ttl; + if (!--ttl) + fwd_csa = false; + + pre_value = le16_to_cpu(elems.mesh_chansw_params_ie->mesh_pre_value); + if (ifmsh->pre_value >= pre_value) + return; + + ifmsh->pre_value = pre_value; + + if (!ieee80211_mesh_process_chnswitch(sdata, &elems, false)) { + mcsa_dbg(sdata, "Failed to process CSA action frame"); + return; + } + + /* forward or re-broadcast the CSA frame */ + if (fwd_csa) { + if (mesh_fwd_csa_frame(sdata, mgmt, len) < 0) + mcsa_dbg(sdata, "Failed to forward the CSA frame"); + } } static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, @@ -939,6 +1234,9 @@ static void ieee80211_mesh_rx_mgmt_action(struct ieee80211_sub_if_data *sdata, if (mesh_action_is_path_sel(mgmt)) mesh_rx_path_sel_frame(sdata, mgmt, len); break; + case WLAN_CATEGORY_SPECTRUM_MGMT: + mesh_rx_csa_frame(sdata, mgmt, len); + break; } } @@ -1056,13 +1354,11 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata) (unsigned long) sdata); ifmsh->accepting_plinks = true; - ifmsh->preq_id = 0; - ifmsh->sn = 0; - ifmsh->num_gates = 0; atomic_set(&ifmsh->mpaths, 0); mesh_rmc_init(sdata); ifmsh->last_preq = jiffies; ifmsh->next_perr = jiffies; + ifmsh->chsw_init = false; /* Allocate all mesh structures when creating the first mesh interface. */ if (!mesh_allocated) ieee80211s_init(); diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 6b65d5055f5bf7572d29712c6345c88dd11b33a5..4301aa5aa227c3d539cd0bf89bdf5b004679976a 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -222,7 +222,8 @@ static u32 __mesh_plink_deactivate(struct sta_info *sta) mesh_path_flush_by_nexthop(sta); ieee80211_mps_sta_status_update(sta); - changed |= ieee80211_mps_local_status_update(sdata); + changed |= ieee80211_mps_set_sta_local_pm(sta, + NL80211_MESH_POWER_UNKNOWN); return changed; } diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c index 22290a929b94595445865baecdb692aff5e0be9c..0f79b78b5e86b45a6d7059de497b68b44944da09 100644 --- a/net/mac80211/mesh_ps.c +++ b/net/mac80211/mesh_ps.c @@ -152,6 +152,9 @@ u32 ieee80211_mps_set_sta_local_pm(struct sta_info *sta, { struct ieee80211_sub_if_data *sdata = sta->sdata; + if (sta->local_pm == pm) + return 0; + mps_dbg(sdata, "local STA operates in mode %d with %pM\n", pm, sta->sta.addr); @@ -245,6 +248,14 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta) do_buffer = (pm != NL80211_MESH_POWER_ACTIVE); + /* clear the MPSP flags for non-peers or active STA */ + if (sta->plink_state != NL80211_PLINK_ESTAB) { + clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); + clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); + } else if (!do_buffer) { + clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); + } + /* Don't let the same PS state be set twice */ if (test_sta_flag(sta, WLAN_STA_PS_STA) == do_buffer) return; @@ -257,14 +268,6 @@ void ieee80211_mps_sta_status_update(struct sta_info *sta) } else { ieee80211_sta_ps_deliver_wakeup(sta); } - - /* clear the MPSP flags for non-peers or active STA */ - if (sta->plink_state != NL80211_PLINK_ESTAB) { - clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); - clear_sta_flag(sta, WLAN_STA_MPSP_RECIPIENT); - } else if (!do_buffer) { - clear_sta_flag(sta, WLAN_STA_MPSP_OWNER); - } } static void mps_set_sta_peer_pm(struct sta_info *sta, @@ -444,8 +447,7 @@ static void mpsp_qos_null_append(struct sta_info *sta, */ static void mps_frame_deliver(struct sta_info *sta, int n_frames) { - struct ieee80211_sub_if_data *sdata = sta->sdata; - struct ieee80211_local *local = sdata->local; + struct ieee80211_local *local = sta->sdata->local; int ac; struct sk_buff_head frames; struct sk_buff *skb; @@ -558,10 +560,10 @@ void ieee80211_mpsp_trigger_process(u8 *qc, struct sta_info *sta, } /** - * ieee80211_mps_frame_release - release buffered frames in response to beacon + * ieee80211_mps_frame_release - release frames buffered due to mesh power save * * @sta: mesh STA - * @elems: beacon IEs + * @elems: IEs of beacon or probe response * * For peers if we have individually-addressed frames buffered or the peer * indicates buffered frames, send a corresponding MPSP trigger frame. Since @@ -588,9 +590,10 @@ void ieee80211_mps_frame_release(struct sta_info *sta, (!elems->awake_window || !le16_to_cpu(*elems->awake_window))) return; - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) + - skb_queue_len(&sta->tx_filtered[ac]); + if (!test_sta_flag(sta, WLAN_STA_MPSP_OWNER)) + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) + buffer_local += skb_queue_len(&sta->ps_tx_buf[ac]) + + skb_queue_len(&sta->tx_filtered[ac]); if (!has_buffered && !buffer_local) return; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 86e4ad56b573df27779831e17dce835f67396836..d7504ab61a34c7ef6a51f8adce10e58c021408d0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -145,66 +145,6 @@ static int ecw2cw(int ecw) return (1 << ecw) - 1; } -static u32 chandef_downgrade(struct cfg80211_chan_def *c) -{ - u32 ret; - int tmp; - - switch (c->width) { - case NL80211_CHAN_WIDTH_20: - c->width = NL80211_CHAN_WIDTH_20_NOHT; - ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; - break; - case NL80211_CHAN_WIDTH_40: - c->width = NL80211_CHAN_WIDTH_20; - c->center_freq1 = c->chan->center_freq; - ret = IEEE80211_STA_DISABLE_40MHZ | - IEEE80211_STA_DISABLE_VHT; - break; - case NL80211_CHAN_WIDTH_80: - tmp = (30 + c->chan->center_freq - c->center_freq1)/20; - /* n_P40 */ - tmp /= 2; - /* freq_P40 */ - c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; - c->width = NL80211_CHAN_WIDTH_40; - ret = IEEE80211_STA_DISABLE_VHT; - break; - case NL80211_CHAN_WIDTH_80P80: - c->center_freq2 = 0; - c->width = NL80211_CHAN_WIDTH_80; - ret = IEEE80211_STA_DISABLE_80P80MHZ | - IEEE80211_STA_DISABLE_160MHZ; - break; - case NL80211_CHAN_WIDTH_160: - /* n_P20 */ - tmp = (70 + c->chan->center_freq - c->center_freq1)/20; - /* n_P80 */ - tmp /= 4; - c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; - c->width = NL80211_CHAN_WIDTH_80; - ret = IEEE80211_STA_DISABLE_80P80MHZ | - IEEE80211_STA_DISABLE_160MHZ; - break; - default: - case NL80211_CHAN_WIDTH_20_NOHT: - WARN_ON_ONCE(1); - c->width = NL80211_CHAN_WIDTH_20_NOHT; - ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; - break; - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - WARN_ON_ONCE(1); - /* keep c->width */ - ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; - break; - } - - WARN_ON_ONCE(!cfg80211_chandef_valid(c)); - - return ret; -} - static u32 ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, struct ieee80211_supported_band *sband, @@ -352,7 +292,7 @@ out: break; } - ret |= chandef_downgrade(chandef); + ret |= ieee80211_chandef_downgrade(chandef); } if (chandef->width != vht_chandef.width && !tracking) @@ -406,13 +346,13 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata, */ if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ && chandef.width == NL80211_CHAN_WIDTH_80P80) - flags |= chandef_downgrade(&chandef); + flags |= ieee80211_chandef_downgrade(&chandef); if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && chandef.width == NL80211_CHAN_WIDTH_160) - flags |= chandef_downgrade(&chandef); + flags |= ieee80211_chandef_downgrade(&chandef); if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && chandef.width > NL80211_CHAN_WIDTH_20) - flags |= chandef_downgrade(&chandef); + flags |= ieee80211_chandef_downgrade(&chandef); if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef)) return 0; @@ -893,8 +833,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; - if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | - IEEE80211_STA_CONNECTION_POLL)) + if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; ieee80211_tx_skb(sdata, skb); @@ -937,6 +876,8 @@ static void ieee80211_chswitch_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work); struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u32 changed = 0; + int ret; if (!ieee80211_sdata_running(sdata)) return; @@ -945,24 +886,39 @@ static void ieee80211_chswitch_work(struct work_struct *work) if (!ifmgd->associated) goto out; - local->_oper_chandef = local->csa_chandef; + ret = ieee80211_vif_change_channel(sdata, &local->csa_chandef, + &changed); + if (ret) { + sdata_info(sdata, + "vif channel switch failed, disconnecting\n"); + ieee80211_queue_work(&sdata->local->hw, + &ifmgd->csa_connection_drop_work); + goto out; + } - if (!local->ops->channel_switch) { - /* call "hw_config" only if doing sw channel switch */ - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); - } else { - /* update the device channel directly */ - local->hw.conf.chandef = local->_oper_chandef; + if (!local->use_chanctx) { + local->_oper_chandef = local->csa_chandef; + /* Call "hw_config" only if doing sw channel switch. + * Otherwise update the channel directly + */ + if (!local->ops->channel_switch) + ieee80211_hw_config(local, 0); + else + local->hw.conf.chandef = local->_oper_chandef; } /* XXX: shouldn't really modify cfg80211-owned data! */ - ifmgd->associated->channel = local->_oper_chandef.chan; + ifmgd->associated->channel = local->csa_chandef.chan; /* XXX: wait for a beacon first? */ ieee80211_wake_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); + + ieee80211_bss_info_change_notify(sdata, changed); + out: + sdata->vif.csa_active = false; ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; sdata_unlock(sdata); } @@ -1000,20 +956,10 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct cfg80211_bss *cbss = ifmgd->associated; - struct ieee80211_bss *bss; struct ieee80211_chanctx *chanctx; - enum ieee80211_band new_band; - int new_freq; - u8 new_chan_no; - u8 count; - u8 mode; - struct ieee80211_channel *new_chan; - struct cfg80211_chan_def new_chandef = {}; - struct cfg80211_chan_def new_vht_chandef = {}; - const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; - const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; - const struct ieee80211_ht_operation *ht_oper; - int secondary_channel_offset = -1; + enum ieee80211_band current_band; + struct ieee80211_csa_ie csa_ie; + int res; sdata_assert_lock(sdata); @@ -1027,181 +973,53 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) return; - sec_chan_offs = elems->sec_chan_offs; - wide_bw_chansw_ie = elems->wide_bw_chansw_ie; - ht_oper = elems->ht_operation; - - if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | - IEEE80211_STA_DISABLE_40MHZ)) { - sec_chan_offs = NULL; - wide_bw_chansw_ie = NULL; - /* only used for bandwidth here */ - ht_oper = NULL; - } - - if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) - wide_bw_chansw_ie = NULL; - - if (elems->ext_chansw_ie) { - if (!ieee80211_operating_class_to_band( - elems->ext_chansw_ie->new_operating_class, - &new_band)) { - sdata_info(sdata, - "cannot understand ECSA IE operating class %d, disconnecting\n", - elems->ext_chansw_ie->new_operating_class); - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - } - new_chan_no = elems->ext_chansw_ie->new_ch_num; - count = elems->ext_chansw_ie->count; - mode = elems->ext_chansw_ie->mode; - } else if (elems->ch_switch_ie) { - new_band = cbss->channel->band; - new_chan_no = elems->ch_switch_ie->new_ch_num; - count = elems->ch_switch_ie->count; - mode = elems->ch_switch_ie->mode; - } else { - /* nothing here we understand */ + current_band = cbss->channel->band; + memset(&csa_ie, 0, sizeof(csa_ie)); + res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band, + ifmgd->flags, + ifmgd->associated->bssid, &csa_ie); + if (res < 0) + ieee80211_queue_work(&local->hw, + &ifmgd->csa_connection_drop_work); + if (res) return; - } - - bss = (void *)cbss->priv; - new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); - new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); - if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { + if (!cfg80211_chandef_usable(local->hw.wiphy, &csa_ie.chandef, + IEEE80211_CHAN_DISABLED)) { sdata_info(sdata, - "AP %pM switches to unsupported channel (%d MHz), disconnecting\n", - ifmgd->associated->bssid, new_freq); + "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", + ifmgd->associated->bssid, + csa_ie.chandef.chan->center_freq, + csa_ie.chandef.width, csa_ie.chandef.center_freq1, + csa_ie.chandef.center_freq2); ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); return; } - if (!beacon && sec_chan_offs) { - secondary_channel_offset = sec_chan_offs->sec_chan_offs; - } else if (beacon && ht_oper) { - secondary_channel_offset = - ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; - } else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) { - /* - * If it's not a beacon, HT is enabled and the IE not present, - * it's 20 MHz, 802.11-2012 8.5.2.6: - * This element [the Secondary Channel Offset Element] is - * present when switching to a 40 MHz channel. It may be - * present when switching to a 20 MHz channel (in which - * case the secondary channel offset is set to SCN). - */ - secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - } - - switch (secondary_channel_offset) { - default: - /* secondary_channel_offset was present but is invalid */ - case IEEE80211_HT_PARAM_CHA_SEC_NONE: - cfg80211_chandef_create(&new_chandef, new_chan, - NL80211_CHAN_HT20); - break; - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - cfg80211_chandef_create(&new_chandef, new_chan, - NL80211_CHAN_HT40PLUS); - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - cfg80211_chandef_create(&new_chandef, new_chan, - NL80211_CHAN_HT40MINUS); - break; - case -1: - cfg80211_chandef_create(&new_chandef, new_chan, - NL80211_CHAN_NO_HT); - /* keep width for 5/10 MHz channels */ - switch (sdata->vif.bss_conf.chandef.width) { - case NL80211_CHAN_WIDTH_5: - case NL80211_CHAN_WIDTH_10: - new_chandef.width = sdata->vif.bss_conf.chandef.width; - break; - default: - break; - } - break; - } + ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; + sdata->vif.csa_active = true; - if (wide_bw_chansw_ie) { - new_vht_chandef.chan = new_chan; - new_vht_chandef.center_freq1 = - ieee80211_channel_to_frequency( - wide_bw_chansw_ie->new_center_freq_seg0, - new_band); + mutex_lock(&local->chanctx_mtx); + if (local->use_chanctx) { + u32 num_chanctx = 0; + list_for_each_entry(chanctx, &local->chanctx_list, list) + num_chanctx++; - switch (wide_bw_chansw_ie->new_channel_width) { - default: - /* hmmm, ignore VHT and use HT if present */ - case IEEE80211_VHT_CHANWIDTH_USE_HT: - new_vht_chandef.chan = NULL; - break; - case IEEE80211_VHT_CHANWIDTH_80MHZ: - new_vht_chandef.width = NL80211_CHAN_WIDTH_80; - break; - case IEEE80211_VHT_CHANWIDTH_160MHZ: - new_vht_chandef.width = NL80211_CHAN_WIDTH_160; - break; - case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - /* field is otherwise reserved */ - new_vht_chandef.center_freq2 = - ieee80211_channel_to_frequency( - wide_bw_chansw_ie->new_center_freq_seg1, - new_band); - new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; - break; - } - if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ && - new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) - chandef_downgrade(&new_vht_chandef); - if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && - new_vht_chandef.width == NL80211_CHAN_WIDTH_160) - chandef_downgrade(&new_vht_chandef); - if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && - new_vht_chandef.width > NL80211_CHAN_WIDTH_20) - chandef_downgrade(&new_vht_chandef); - } - - /* if VHT data is there validate & use it */ - if (new_vht_chandef.chan) { - if (!cfg80211_chandef_compatible(&new_vht_chandef, - &new_chandef)) { + if (num_chanctx > 1 || + !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) { sdata_info(sdata, - "AP %pM CSA has inconsistent channel data, disconnecting\n", - ifmgd->associated->bssid); + "not handling chan-switch with channel contexts\n"); ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); + mutex_unlock(&local->chanctx_mtx); return; } - new_chandef = new_vht_chandef; - } - - if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef, - IEEE80211_CHAN_DISABLED)) { - sdata_info(sdata, - "AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", - ifmgd->associated->bssid, new_freq, - new_chandef.width, new_chandef.center_freq1, - new_chandef.center_freq2); - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - return; } - ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; - - if (local->use_chanctx) { - sdata_info(sdata, - "not handling channel switch with channel contexts\n"); + if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); - return; - } - - mutex_lock(&local->chanctx_mtx); - if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) { mutex_unlock(&local->chanctx_mtx); return; } @@ -1217,9 +1035,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, } mutex_unlock(&local->chanctx_mtx); - local->csa_chandef = new_chandef; + local->csa_chandef = csa_ie.chandef; - if (mode) + if (csa_ie.mode) ieee80211_stop_queues_by_reason(&local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -1228,9 +1046,9 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, /* use driver's channel switch callback */ struct ieee80211_channel_switch ch_switch = { .timestamp = timestamp, - .block_tx = mode, - .chandef = new_chandef, - .count = count, + .block_tx = csa_ie.mode, + .chandef = csa_ie.chandef, + .count = csa_ie.count, }; drv_channel_switch(local, &ch_switch); @@ -1238,11 +1056,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, } /* channel switch handled in software */ - if (count <= 1) + if (csa_ie.count <= 1) ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work); else mod_timer(&ifmgd->chswitch_timer, - TU_TO_EXP_TIME(count * cbss->beacon_interval)); + TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval)); } static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, @@ -1374,8 +1192,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata) if (!mgd->associated) return false; - if (mgd->flags & (IEEE80211_STA_BEACON_POLL | - IEEE80211_STA_CONNECTION_POLL)) + if (mgd->flags & IEEE80211_STA_CONNECTION_POLL) return false; if (!mgd->have_beacon) @@ -1691,8 +1508,7 @@ static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata) { lockdep_assert_held(&sdata->local->mtx); - sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | - IEEE80211_STA_BEACON_POLL); + sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL; ieee80211_run_deferred_scan(sdata->local); } @@ -1954,11 +1770,8 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata) struct ieee80211_local *local = sdata->local; mutex_lock(&local->mtx); - if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL | - IEEE80211_STA_CONNECTION_POLL))) { - mutex_unlock(&local->mtx); - return; - } + if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)) + goto out; __ieee80211_stop_poll(sdata); @@ -2094,15 +1907,9 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, * because otherwise we would reset the timer every time and * never check whether we received a probe response! */ - if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | - IEEE80211_STA_CONNECTION_POLL)) + if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) already = true; - if (beacon) - ifmgd->flags |= IEEE80211_STA_BEACON_POLL; - else - ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL; - mutex_unlock(&sdata->local->mtx); if (already) @@ -2174,6 +1981,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, true, frame_buf); ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; + sdata->vif.csa_active = false; ieee80211_wake_queues_by_reason(&sdata->local->hw, IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_CSA); @@ -2717,7 +2525,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, */ ifmgd->wmm_last_param_set = -1; - if (elems.wmm_param) + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && elems.wmm_param) ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len); else @@ -3061,17 +2869,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } } - if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) { + if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) { mlme_dbg_ratelimited(sdata, "cancelling AP probe due to a received beacon\n"); - mutex_lock(&local->mtx); - ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL; - ieee80211_run_deferred_scan(local); - mutex_unlock(&local->mtx); - - mutex_lock(&local->iflist_mtx); - ieee80211_recalc_ps(local, -1); - mutex_unlock(&local->iflist_mtx); + ieee80211_reset_ap_probe(sdata); } /* @@ -3152,7 +2953,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_sta_process_chanswitch(sdata, rx_status->mactime, &elems, true); - if (ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) && + ieee80211_sta_wmm_params(local, sdata, elems.wmm_param, elems.wmm_param_len)) changed |= BSS_CHANGED_QOS; @@ -3543,8 +3345,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) run_again(sdata, ifmgd->assoc_data->timeout); - if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | - IEEE80211_STA_CONNECTION_POLL) && + if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL && ifmgd->associated) { u8 bssid[ETH_ALEN]; int max_tries; @@ -3697,7 +3498,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ieee80211_beacon_connection_loss_work); INIT_WORK(&ifmgd->csa_connection_drop_work, ieee80211_csa_connection_drop_work); - INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_work); + INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, @@ -3876,7 +3677,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, return ret; while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { - ifmgd->flags |= chandef_downgrade(&chandef); + ifmgd->flags |= ieee80211_chandef_downgrade(&chandef); ret = ieee80211_vif_use_channel(sdata, &chandef, IEEE80211_CHANCTX_SHARED); } @@ -4135,6 +3936,44 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, return err; } +static bool ieee80211_usable_wmm_params(struct ieee80211_sub_if_data *sdata, + const u8 *wmm_param, int len) +{ + const u8 *pos; + size_t left; + + if (len < 8) + return false; + + if (wmm_param[5] != 1 /* version */) + return false; + + pos = wmm_param + 8; + left = len - 8; + + for (; left >= 4; left -= 4, pos += 4) { + u8 aifsn = pos[0] & 0x0f; + u8 ecwmin = pos[1] & 0x0f; + u8 ecwmax = (pos[1] & 0xf0) >> 4; + int aci = (pos[0] >> 5) & 0x03; + + if (aifsn < 2) { + sdata_info(sdata, + "AP has invalid WMM params (AIFSN=%d for ACI %d), disabling WMM\n", + aifsn, aci); + return false; + } + if (ecwmin > ecwmax) { + sdata_info(sdata, + "AP has invalid WMM params (ECWmin/max=%d/%d for ACI %d), disabling WMM\n", + ecwmin, ecwmax, aci); + return false; + } + } + + return true; +} + int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, struct cfg80211_assoc_request *req) { @@ -4192,9 +4031,45 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } /* prepare assoc data */ - + ifmgd->beacon_crc_valid = false; + assoc_data->wmm = bss->wmm_used && + (local->hw.queues >= IEEE80211_NUM_ACS); + if (assoc_data->wmm) { + /* try to check validity of WMM params IE */ + const struct cfg80211_bss_ies *ies; + const u8 *wp, *start, *end; + + rcu_read_lock(); + ies = rcu_dereference(req->bss->ies); + start = ies->data; + end = start + ies->len; + + while (true) { + wp = cfg80211_find_vendor_ie( + WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WMM, + start, end - start); + if (!wp) + break; + start = wp + wp[1] + 2; + /* if this IE is too short, try the next */ + if (wp[1] <= 4) + continue; + /* if this IE is WMM params, we found what we wanted */ + if (wp[6] == 1) + break; + } + + if (!wp || !ieee80211_usable_wmm_params(sdata, wp + 2, + wp[1] - 2)) { + assoc_data->wmm = false; + ifmgd->flags |= IEEE80211_STA_DISABLE_WMM; + } + rcu_read_unlock(); + } + /* * IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode. * We still associate in non-HT mode (11a/b/g) if any one of these @@ -4224,18 +4099,22 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, /* Also disable HT if we don't support it or the AP doesn't use WMM */ sband = local->hw.wiphy->bands[req->bss->channel->band]; if (!sband->ht_cap.ht_supported || - local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { + local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used || + ifmgd->flags & IEEE80211_STA_DISABLE_WMM) { ifmgd->flags |= IEEE80211_STA_DISABLE_HT; - if (!bss->wmm_used) + if (!bss->wmm_used && + !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM)) netdev_info(sdata->dev, "disabling HT as WMM/QoS is not supported by the AP\n"); } /* disable VHT if we don't support it or the AP doesn't use WMM */ if (!sband->vht_cap.vht_supported || - local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { + local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used || + ifmgd->flags & IEEE80211_STA_DISABLE_WMM) { ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; - if (!bss->wmm_used) + if (!bss->wmm_used && + !(ifmgd->flags & IEEE80211_STA_DISABLE_WMM)) netdev_info(sdata->dev, "disabling VHT as WMM/QoS is not supported by the AP\n"); } @@ -4264,8 +4143,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, sdata->smps_mode = ifmgd->req_smps; assoc_data->capability = req->bss->capability; - assoc_data->wmm = bss->wmm_used && - (local->hw.queues >= IEEE80211_NUM_ACS); assoc_data->supp_rates = bss->supp_rates; assoc_data->supp_rates_len = bss->supp_rates_len; diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index e126605cec66baf82aadb835856110d74105795f..22b223f13c9fa22994eba6f68769a27b2cfe4eb3 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -235,7 +235,8 @@ static void rc_send_low_basicrate(s8 *idx, u32 basic_rates, static void __rate_control_send_low(struct ieee80211_hw *hw, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, - struct ieee80211_tx_info *info) + struct ieee80211_tx_info *info, + u32 rate_mask) { int i; u32 rate_flags = @@ -247,6 +248,12 @@ static void __rate_control_send_low(struct ieee80211_hw *hw, info->control.rates[0].idx = 0; for (i = 0; i < sband->n_bitrates; i++) { + if (!(rate_mask & BIT(i))) + continue; + + if ((rate_flags & sband->bitrates[i].flags) != rate_flags) + continue; + if (!rate_supported(sta, sband->band, i)) continue; @@ -274,7 +281,8 @@ bool rate_control_send_low(struct ieee80211_sta *pubsta, bool use_basicrate = false; if (!pubsta || !priv_sta || rc_no_data_or_no_ack_use_min(txrc)) { - __rate_control_send_low(txrc->hw, sband, pubsta, info); + __rate_control_send_low(txrc->hw, sband, pubsta, info, + txrc->rate_idx_mask); if (!pubsta && txrc->bss) { mcast_rate = txrc->bss_conf->mcast_rate[sband->band]; @@ -656,7 +664,8 @@ void ieee80211_get_tx_rates(struct ieee80211_vif *vif, rate_control_apply_mask(sdata, sta, sband, info, dest, max_rates); if (dest[0].idx < 0) - __rate_control_send_low(&sdata->local->hw, sband, sta, info); + __rate_control_send_low(&sdata->local->hw, sband, sta, info, + sdata->rc_rateidx_mask[info->band]); if (sta) rate_fixup_ratelist(vif, sband, info, dest, max_rates); diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index 5dedc56c94dbe91a1b9bd6c959094ab3b494be86..505bc0dea074809f1532f322528e222a5052c061 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -144,8 +144,8 @@ void rate_control_deinitialize(struct ieee80211_local *local); /* Rate control algorithms */ #ifdef CONFIG_MAC80211_RC_PID -extern int rc80211_pid_init(void); -extern void rc80211_pid_exit(void); +int rc80211_pid_init(void); +void rc80211_pid_exit(void); #else static inline int rc80211_pid_init(void) { @@ -157,8 +157,8 @@ static inline void rc80211_pid_exit(void) #endif #ifdef CONFIG_MAC80211_RC_MINSTREL -extern int rc80211_minstrel_init(void); -extern void rc80211_minstrel_exit(void); +int rc80211_minstrel_init(void); +void rc80211_minstrel_exit(void); #else static inline int rc80211_minstrel_init(void) { @@ -170,8 +170,8 @@ static inline void rc80211_minstrel_exit(void) #endif #ifdef CONFIG_MAC80211_RC_MINSTREL_HT -extern int rc80211_minstrel_ht_init(void); -extern void rc80211_minstrel_ht_exit(void); +int rc80211_minstrel_ht_init(void); +void rc80211_minstrel_ht_exit(void); #else static inline int rc80211_minstrel_ht_init(void) { diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 8b5f7ef7c0c9f14db5dba3baf8ab82247500c1c3..7fa1b36e620247ed5c14e97d54b2d72d7a0b43cb 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -203,6 +203,15 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate)); mi->max_prob_rate = tmp_prob_rate; +#ifdef CONFIG_MAC80211_DEBUGFS + /* use fixed index if set */ + if (mp->fixed_rate_idx != -1) { + mi->max_tp_rate[0] = mp->fixed_rate_idx; + mi->max_tp_rate[1] = mp->fixed_rate_idx; + mi->max_prob_rate = mp->fixed_rate_idx; + } +#endif + /* Reset update timer */ mi->stats_update = jiffies; @@ -310,6 +319,11 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, /* increase sum packet counter */ mi->packet_count++; +#ifdef CONFIG_MAC80211_DEBUGFS + if (mp->fixed_rate_idx != -1) + return; +#endif + delta = (mi->packet_count * sampling_ratio / 100) - (mi->sample_count + mi->sample_deferred / 2); diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 7c323f27ba230a91599f34a6877caa8c17ff8605..5d60779a0c1be89e987b2cd478af865ec806d4bb 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -365,6 +365,14 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) } } +#ifdef CONFIG_MAC80211_DEBUGFS + /* use fixed index if set */ + if (mp->fixed_rate_idx != -1) { + mi->max_tp_rate = mp->fixed_rate_idx; + mi->max_tp_rate2 = mp->fixed_rate_idx; + mi->max_prob_rate = mp->fixed_rate_idx; + } +#endif mi->stats_update = jiffies; } @@ -774,6 +782,11 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, info->flags |= mi->tx_flags; minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble); +#ifdef CONFIG_MAC80211_DEBUGFS + if (mp->fixed_rate_idx != -1) + return; +#endif + /* Don't use EAPOL frames for sampling on non-mrr hw */ if (mp->hw->max_rates == 1 && (info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) @@ -781,16 +794,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, else sample_idx = minstrel_get_sample_rate(mp, mi); -#ifdef CONFIG_MAC80211_DEBUGFS - /* use fixed index if set */ - if (mp->fixed_rate_idx != -1) { - mi->max_tp_rate = mp->fixed_rate_idx; - mi->max_tp_rate2 = mp->fixed_rate_idx; - mi->max_prob_rate = mp->fixed_rate_idx; - sample_idx = -1; - } -#endif - mi->total_packets++; /* wraparound */ diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c index c97a0657c0435bece74ce534e173df74b62a16ae..6ff134650a84c195e033c62c76e9c954ac7dc245 100644 --- a/net/mac80211/rc80211_pid_debugfs.c +++ b/net/mac80211/rc80211_pid_debugfs.c @@ -167,29 +167,29 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf, * provide large enough buffers. */ length = length < RC_PID_PRINT_BUF_SIZE ? length : RC_PID_PRINT_BUF_SIZE; - p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp); + p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp); switch (ev->type) { case RC_PID_EVENT_TYPE_TX_STATUS: - p += snprintf(pb + p, length - p, "tx_status %u %u", - !(ev->data.flags & IEEE80211_TX_STAT_ACK), - ev->data.tx_status.status.rates[0].idx); + p += scnprintf(pb + p, length - p, "tx_status %u %u", + !(ev->data.flags & IEEE80211_TX_STAT_ACK), + ev->data.tx_status.status.rates[0].idx); break; case RC_PID_EVENT_TYPE_RATE_CHANGE: - p += snprintf(pb + p, length - p, "rate_change %d %d", - ev->data.index, ev->data.rate); + p += scnprintf(pb + p, length - p, "rate_change %d %d", + ev->data.index, ev->data.rate); break; case RC_PID_EVENT_TYPE_TX_RATE: - p += snprintf(pb + p, length - p, "tx_rate %d %d", - ev->data.index, ev->data.rate); + p += scnprintf(pb + p, length - p, "tx_rate %d %d", + ev->data.index, ev->data.rate); break; case RC_PID_EVENT_TYPE_PF_SAMPLE: - p += snprintf(pb + p, length - p, - "pf_sample %d %d %d %d", - ev->data.pf_sample, ev->data.prop_err, - ev->data.int_err, ev->data.der_err); + p += scnprintf(pb + p, length - p, + "pf_sample %d %d %d %d", + ev->data.pf_sample, ev->data.prop_err, + ev->data.int_err, ev->data.der_err); break; } - p += snprintf(pb + p, length - p, "\n"); + p += scnprintf(pb + p, length - p, "\n"); spin_unlock_irqrestore(&events->lock, status); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 674eac1f996c9ddd664f2f70022514efc228b109..caecef870c0e44e3562cdc0a874bcfd233fc77a1 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -995,8 +995,9 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx) rx->sta->num_duplicates++; } return RX_DROP_UNUSABLE; - } else + } else if (!(status->flag & RX_FLAG_AMSDU_MORE)) { rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl; + } } if (unlikely(rx->skb->len < 16)) { @@ -2402,7 +2403,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) return RX_DROP_UNUSABLE; if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC && - mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED) + mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED && + mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT) return RX_DROP_UNUSABLE; if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) @@ -2566,31 +2568,49 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) goto queue; case WLAN_CATEGORY_SPECTRUM_MGMT: - if (status->band != IEEE80211_BAND_5GHZ) - break; - - if (sdata->vif.type != NL80211_IFTYPE_STATION) - break; - /* verify action_code is present */ if (len < IEEE80211_MIN_ACTION_SIZE + 1) break; switch (mgmt->u.action.u.measurement.action_code) { case WLAN_ACTION_SPCT_MSR_REQ: + if (status->band != IEEE80211_BAND_5GHZ) + break; + if (len < (IEEE80211_MIN_ACTION_SIZE + sizeof(mgmt->u.action.u.measurement))) break; + + if (sdata->vif.type != NL80211_IFTYPE_STATION) + break; + ieee80211_process_measurement_req(sdata, mgmt, len); goto handled; - case WLAN_ACTION_SPCT_CHL_SWITCH: - if (sdata->vif.type != NL80211_IFTYPE_STATION) + case WLAN_ACTION_SPCT_CHL_SWITCH: { + u8 *bssid; + if (len < (IEEE80211_MIN_ACTION_SIZE + + sizeof(mgmt->u.action.u.chan_switch))) + break; + + if (sdata->vif.type != NL80211_IFTYPE_STATION && + sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) break; - if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid)) + if (sdata->vif.type == NL80211_IFTYPE_STATION) + bssid = sdata->u.mgd.bssid; + else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) + bssid = sdata->u.ibss.bssid; + else if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT) + bssid = mgmt->sa; + else + break; + + if (!ether_addr_equal(mgmt->bssid, bssid)) break; goto queue; + } } break; case WLAN_CATEGORY_SA_QUERY: diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index d2d17a4492245953413c5742d22d2af9e15fd74d..5ad66a83ef7f4d4525de163c2c2f1fe9d6931a04 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -394,8 +394,7 @@ static bool ieee80211_can_scan(struct ieee80211_local *local, return false; if (sdata->vif.type == NL80211_IFTYPE_STATION && - sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | - IEEE80211_STA_CONNECTION_POLL)) + sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL) return false; return true; diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c index 578eea3fc04d27a25eeddc84f244fe6de24dc6a8..a40da20b32e074a3923f844edf104eca33a6134c 100644 --- a/net/mac80211/spectmgmt.c +++ b/net/mac80211/spectmgmt.c @@ -21,6 +21,175 @@ #include "sta_info.h" #include "wme.h" +int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata, + struct ieee802_11_elems *elems, bool beacon, + enum ieee80211_band current_band, + u32 sta_flags, u8 *bssid, + struct ieee80211_csa_ie *csa_ie) +{ + enum ieee80211_band new_band; + int new_freq; + u8 new_chan_no; + struct ieee80211_channel *new_chan; + struct cfg80211_chan_def new_vht_chandef = {}; + const struct ieee80211_sec_chan_offs_ie *sec_chan_offs; + const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie; + const struct ieee80211_ht_operation *ht_oper; + int secondary_channel_offset = -1; + + sec_chan_offs = elems->sec_chan_offs; + wide_bw_chansw_ie = elems->wide_bw_chansw_ie; + ht_oper = elems->ht_operation; + + if (sta_flags & (IEEE80211_STA_DISABLE_HT | + IEEE80211_STA_DISABLE_40MHZ)) { + sec_chan_offs = NULL; + wide_bw_chansw_ie = NULL; + /* only used for bandwidth here */ + ht_oper = NULL; + } + + if (sta_flags & IEEE80211_STA_DISABLE_VHT) + wide_bw_chansw_ie = NULL; + + if (elems->ext_chansw_ie) { + if (!ieee80211_operating_class_to_band( + elems->ext_chansw_ie->new_operating_class, + &new_band)) { + sdata_info(sdata, + "cannot understand ECSA IE operating class %d, disconnecting\n", + elems->ext_chansw_ie->new_operating_class); + return -EINVAL; + } + new_chan_no = elems->ext_chansw_ie->new_ch_num; + csa_ie->count = elems->ext_chansw_ie->count; + csa_ie->mode = elems->ext_chansw_ie->mode; + } else if (elems->ch_switch_ie) { + new_band = current_band; + new_chan_no = elems->ch_switch_ie->new_ch_num; + csa_ie->count = elems->ch_switch_ie->count; + csa_ie->mode = elems->ch_switch_ie->mode; + } else { + /* nothing here we understand */ + return 1; + } + + /* Mesh Channel Switch Parameters Element */ + if (elems->mesh_chansw_params_ie) { + csa_ie->ttl = elems->mesh_chansw_params_ie->mesh_ttl; + csa_ie->mode = elems->mesh_chansw_params_ie->mesh_flags; + } + + new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band); + new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq); + if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) { + sdata_info(sdata, + "BSS %pM switches to unsupported channel (%d MHz), disconnecting\n", + bssid, new_freq); + return -EINVAL; + } + + if (!beacon && sec_chan_offs) { + secondary_channel_offset = sec_chan_offs->sec_chan_offs; + } else if (beacon && ht_oper) { + secondary_channel_offset = + ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET; + } else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) { + /* If it's not a beacon, HT is enabled and the IE not present, + * it's 20 MHz, 802.11-2012 8.5.2.6: + * This element [the Secondary Channel Offset Element] is + * present when switching to a 40 MHz channel. It may be + * present when switching to a 20 MHz channel (in which + * case the secondary channel offset is set to SCN). + */ + secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + } + + switch (secondary_channel_offset) { + default: + /* secondary_channel_offset was present but is invalid */ + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + cfg80211_chandef_create(&csa_ie->chandef, new_chan, + NL80211_CHAN_HT20); + break; + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + cfg80211_chandef_create(&csa_ie->chandef, new_chan, + NL80211_CHAN_HT40PLUS); + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + cfg80211_chandef_create(&csa_ie->chandef, new_chan, + NL80211_CHAN_HT40MINUS); + break; + case -1: + cfg80211_chandef_create(&csa_ie->chandef, new_chan, + NL80211_CHAN_NO_HT); + /* keep width for 5/10 MHz channels */ + switch (sdata->vif.bss_conf.chandef.width) { + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + csa_ie->chandef.width = + sdata->vif.bss_conf.chandef.width; + break; + default: + break; + } + break; + } + + if (wide_bw_chansw_ie) { + new_vht_chandef.chan = new_chan; + new_vht_chandef.center_freq1 = + ieee80211_channel_to_frequency( + wide_bw_chansw_ie->new_center_freq_seg0, + new_band); + + switch (wide_bw_chansw_ie->new_channel_width) { + default: + /* hmmm, ignore VHT and use HT if present */ + case IEEE80211_VHT_CHANWIDTH_USE_HT: + new_vht_chandef.chan = NULL; + break; + case IEEE80211_VHT_CHANWIDTH_80MHZ: + new_vht_chandef.width = NL80211_CHAN_WIDTH_80; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + new_vht_chandef.width = NL80211_CHAN_WIDTH_160; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + /* field is otherwise reserved */ + new_vht_chandef.center_freq2 = + ieee80211_channel_to_frequency( + wide_bw_chansw_ie->new_center_freq_seg1, + new_band); + new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80; + break; + } + if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ && + new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80) + ieee80211_chandef_downgrade(&new_vht_chandef); + if (sta_flags & IEEE80211_STA_DISABLE_160MHZ && + new_vht_chandef.width == NL80211_CHAN_WIDTH_160) + ieee80211_chandef_downgrade(&new_vht_chandef); + if (sta_flags & IEEE80211_STA_DISABLE_40MHZ && + new_vht_chandef.width > NL80211_CHAN_WIDTH_20) + ieee80211_chandef_downgrade(&new_vht_chandef); + } + + /* if VHT data is there validate & use it */ + if (new_vht_chandef.chan) { + if (!cfg80211_chandef_compatible(&new_vht_chandef, + &csa_ie->chandef)) { + sdata_info(sdata, + "BSS %pM: CSA has inconsistent channel data, disconnecting\n", + bssid); + return -EINVAL; + } + csa_ie->chandef = new_vht_chandef; + } + + return 0; +} + static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata, struct ieee80211_msrment_ie *request_ie, const u8 *da, const u8 *bssid, diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index aeb967a0aeed5ccc49b6c45c5ac13d0d97f97130..1eb66e26e49d0a02869ec5e5ff4789583e9760d1 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -385,6 +385,30 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); sta->sta.smps_mode = IEEE80211_SMPS_OFF; + if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + struct ieee80211_supported_band *sband = + local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; + u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> + IEEE80211_HT_CAP_SM_PS_SHIFT; + /* + * Assume that hostapd advertises our caps in the beacon and + * this is the known_smps_mode for a station that just assciated + */ + switch (smps) { + case WLAN_HT_SMPS_CONTROL_DISABLED: + sta->known_smps_mode = IEEE80211_SMPS_OFF; + break; + case WLAN_HT_SMPS_CONTROL_STATIC: + sta->known_smps_mode = IEEE80211_SMPS_STATIC; + break; + case WLAN_HT_SMPS_CONTROL_DYNAMIC: + sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC; + break; + default: + WARN_ON(1); + } + } sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); @@ -1069,6 +1093,19 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); + /* This station just woke up and isn't aware of our SMPS state */ + if (!ieee80211_smps_is_restrictive(sta->known_smps_mode, + sdata->smps_mode) && + sta->known_smps_mode != sdata->bss->req_smps && + sta_info_tx_streams(sta) != 1) { + ht_dbg(sdata, + "%pM just woke up and MIMO capable - update SMPS\n", + sta->sta.addr); + ieee80211_send_smps_action(sdata, sdata->bss->req_smps, + sta->sta.addr, + sdata->vif.bss_conf.bssid); + } + local->total_ps_buffered -= buffered; sta_info_recalc_tim(sta); @@ -1520,3 +1557,38 @@ int sta_info_move_state(struct sta_info *sta, return 0; } + +u8 sta_info_tx_streams(struct sta_info *sta) +{ + struct ieee80211_sta_ht_cap *ht_cap = &sta->sta.ht_cap; + u8 rx_streams; + + if (!sta->sta.ht_cap.ht_supported) + return 1; + + if (sta->sta.vht_cap.vht_supported) { + int i; + u16 tx_mcs_map = + le16_to_cpu(sta->sta.vht_cap.vht_mcs.tx_mcs_map); + + for (i = 7; i >= 0; i--) + if ((tx_mcs_map & (0x3 << (i * 2))) != + IEEE80211_VHT_MCS_NOT_SUPPORTED) + return i + 1; + } + + if (ht_cap->mcs.rx_mask[3]) + rx_streams = 4; + else if (ht_cap->mcs.rx_mask[2]) + rx_streams = 3; + else if (ht_cap->mcs.rx_mask[1]) + rx_streams = 2; + else + rx_streams = 1; + + if (!(ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_RX_DIFF)) + return rx_streams; + + return ((ht_cap->mcs.tx_params & IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) + >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 4208dbd5861f4d87cc8fa94aefc7c00f51e72eed..3ef06a26b9cb9feaddfd3d752dd9bd8ac971a11c 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -301,6 +301,8 @@ struct sta_ampdu_mlme { * @chains: chains ever used for RX from this station * @chain_signal_last: last signal (per chain) * @chain_signal_avg: signal average (per chain) + * @known_smps_mode: the smps_mode the client thinks we are in. Relevant for + * AP only. */ struct sta_info { /* General information, mostly static */ @@ -411,6 +413,8 @@ struct sta_info { unsigned int lost_packets; unsigned int beacon_loss_count; + enum ieee80211_smps_mode known_smps_mode; + /* keep last! */ struct ieee80211_sta sta; }; @@ -613,6 +617,7 @@ void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo); void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, unsigned long exp_time); +u8 sta_info_tx_streams(struct sta_info *sta); void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 78dc2e99027e06b8a9fc37f5bdf7f06483476f2b..52a152b01b063b226fb5a523b19a4e9863eeffaf 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -194,29 +194,36 @@ static void ieee80211_frame_acked(struct sta_info *sta, struct sk_buff *skb) if (ieee80211_is_action(mgmt->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_HT && mgmt->u.action.u.ht_smps.action == WLAN_HT_ACTION_SMPS && - sdata->vif.type == NL80211_IFTYPE_STATION && ieee80211_sdata_running(sdata)) { - /* - * This update looks racy, but isn't -- if we come - * here we've definitely got a station that we're - * talking to, and on a managed interface that can - * only be the AP. And the only other place updating - * this variable in managed mode is before association. - */ + enum ieee80211_smps_mode smps_mode; + switch (mgmt->u.action.u.ht_smps.smps_control) { case WLAN_HT_SMPS_CONTROL_DYNAMIC: - sdata->smps_mode = IEEE80211_SMPS_DYNAMIC; + smps_mode = IEEE80211_SMPS_DYNAMIC; break; case WLAN_HT_SMPS_CONTROL_STATIC: - sdata->smps_mode = IEEE80211_SMPS_STATIC; + smps_mode = IEEE80211_SMPS_STATIC; break; case WLAN_HT_SMPS_CONTROL_DISABLED: default: /* shouldn't happen since we don't send that */ - sdata->smps_mode = IEEE80211_SMPS_OFF; + smps_mode = IEEE80211_SMPS_OFF; break; } - ieee80211_queue_work(&local->hw, &sdata->recalc_smps); + if (sdata->vif.type == NL80211_IFTYPE_STATION) { + /* + * This update looks racy, but isn't -- if we come + * here we've definitely got a station that we're + * talking to, and on a managed interface that can + * only be the AP. And the only other place updating + * this variable in managed mode is before association. + */ + sdata->smps_mode = smps_mode; + ieee80211_queue_work(&local->hw, &sdata->recalc_smps); + } else if (sdata->vif.type == NL80211_IFTYPE_AP || + sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + sta->known_smps_mode = smps_mode; + } } } diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 1aba645882bd92abbf9e5a4cdd90f7544a173c4a..d4cee98533fdc5a532a8ba74e48d6158faeb5003 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -77,13 +77,13 @@ DECLARE_EVENT_CLASS(local_sdata_addr_evt, TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY - __array(char, addr, 6) + __array(char, addr, ETH_ALEN) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; - memcpy(__entry->addr, sdata->vif.addr, 6); + memcpy(__entry->addr, sdata->vif.addr, ETH_ALEN); ), TP_printk( @@ -1475,6 +1475,41 @@ DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change, ); #endif +TRACE_EVENT(drv_join_ibss, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_bss_conf *info), + + TP_ARGS(local, sdata, info), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(u8, dtimper) + __field(u16, bcnint) + __dynamic_array(u8, ssid, info->ssid_len); + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->dtimper = info->dtim_period; + __entry->bcnint = info->beacon_int; + memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len); + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG + ) +); + +DEFINE_EVENT(local_sdata_evt, drv_leave_ibss, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 70b5a05c0a4e12b8cb36252465171ffbc7ef1686..c558b246ef0036c38e6c8a00b338b43c46913f78 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1367,6 +1367,35 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) return 0; } +bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, struct sk_buff *skb, + int band, struct ieee80211_sta **sta) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_data tx; + + if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) + return false; + + info->band = band; + info->control.vif = vif; + info->hw_queue = vif->hw_queue[skb_get_queue_mapping(skb)]; + + if (invoke_tx_handlers(&tx)) + return false; + + if (sta) { + if (tx.sta) + *sta = &tx.sta->sta; + else + *sta = NULL; + } + + return true; +} +EXPORT_SYMBOL(ieee80211_tx_prepare_skb); + /* * Returns false if the frame couldn't be transmitted but was queued instead. */ @@ -1982,7 +2011,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, * EAPOL frames from the local station. */ if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) && - !is_multicast_ether_addr(hdr.addr1) && !authorized && + !multicast && !authorized && (cpu_to_be16(ethertype) != sdata->control_port_protocol || !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG @@ -2358,15 +2387,35 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata, struct probe_resp *resp; int counter_offset_beacon = sdata->csa_counter_offset_beacon; int counter_offset_presp = sdata->csa_counter_offset_presp; + u8 *beacon_data; + size_t beacon_data_len; + + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP: + beacon_data = beacon->tail; + beacon_data_len = beacon->tail_len; + break; + case NL80211_IFTYPE_ADHOC: + beacon_data = beacon->head; + beacon_data_len = beacon->head_len; + break; + case NL80211_IFTYPE_MESH_POINT: + beacon_data = beacon->head; + beacon_data_len = beacon->head_len; + break; + default: + return; + } + if (WARN_ON(counter_offset_beacon >= beacon_data_len)) + return; /* warn if the driver did not check for/react to csa completeness */ - if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0)) + if (WARN_ON(beacon_data[counter_offset_beacon] == 0)) return; - ((u8 *)beacon->tail)[counter_offset_beacon]--; + beacon_data[counter_offset_beacon]--; - if (sdata->vif.type == NL80211_IFTYPE_AP && - counter_offset_presp) { + if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) { rcu_read_lock(); resp = rcu_dereference(sdata->u.ap.probe_resp); @@ -2401,6 +2450,24 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif) goto out; beacon_data = beacon->tail; beacon_data_len = beacon->tail_len; + } else if (vif->type == NL80211_IFTYPE_ADHOC) { + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + + beacon = rcu_dereference(ifibss->presp); + if (!beacon) + goto out; + + beacon_data = beacon->head; + beacon_data_len = beacon->head_len; + } else if (vif->type == NL80211_IFTYPE_MESH_POINT) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + + beacon = rcu_dereference(ifmsh->beacon); + if (!beacon) + goto out; + + beacon_data = beacon->head; + beacon_data_len = beacon->head_len; } else { WARN_ON(1); goto out; @@ -2485,6 +2552,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (!presp) goto out; + if (sdata->vif.csa_active) + ieee80211_update_csa(sdata, presp); + + skb = dev_alloc_skb(local->tx_headroom + presp->head_len); if (!skb) goto out; @@ -2502,6 +2573,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, if (!bcn) goto out; + if (sdata->vif.csa_active) + ieee80211_update_csa(sdata, bcn); + if (ifmsh->sync_ops) ifmsh->sync_ops->adjust_tbtt( sdata); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 69e4ef5348a0e916b76fa87de9d83402ec99a5e4..592a18171f95e9ec5273b03307235dff2bd1c946 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -300,9 +300,6 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) if (!sdata->dev) continue; - if (test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) - continue; - if (sdata->vif.cab_queue != IEEE80211_INVAL_HW_QUEUE && local->queue_stop_reasons[sdata->vif.cab_queue] != 0) continue; @@ -567,18 +564,15 @@ void ieee80211_flush_queues(struct ieee80211_local *local, IEEE80211_QUEUE_STOP_REASON_FLUSH); } -void ieee80211_iterate_active_interfaces( - struct ieee80211_hw *hw, u32 iter_flags, - void (*iterator)(void *data, u8 *mac, - struct ieee80211_vif *vif), - void *data) +static void __iterate_active_interfaces(struct ieee80211_local *local, + u32 iter_flags, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data) { - struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; - mutex_lock(&local->iflist_mtx); - - list_for_each_entry(sdata, &local->interfaces, list) { + list_for_each_entry_rcu(sdata, &local->interfaces, list) { switch (sdata->vif.type) { case NL80211_IFTYPE_MONITOR: if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) @@ -597,13 +591,25 @@ void ieee80211_iterate_active_interfaces( &sdata->vif); } - sdata = rcu_dereference_protected(local->monitor_sdata, - lockdep_is_held(&local->iflist_mtx)); + sdata = rcu_dereference_check(local->monitor_sdata, + lockdep_is_held(&local->iflist_mtx) || + lockdep_rtnl_is_held()); if (sdata && (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || sdata->flags & IEEE80211_SDATA_IN_DRIVER)) iterator(data, sdata->vif.addr, &sdata->vif); +} + +void ieee80211_iterate_active_interfaces( + struct ieee80211_hw *hw, u32 iter_flags, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data) +{ + struct ieee80211_local *local = hw_to_local(hw); + mutex_lock(&local->iflist_mtx); + __iterate_active_interfaces(local, iter_flags, iterator, data); mutex_unlock(&local->iflist_mtx); } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); @@ -615,38 +621,26 @@ void ieee80211_iterate_active_interfaces_atomic( void *data) { struct ieee80211_local *local = hw_to_local(hw); - struct ieee80211_sub_if_data *sdata; rcu_read_lock(); + __iterate_active_interfaces(local, iter_flags, iterator, data); + rcu_read_unlock(); +} +EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - switch (sdata->vif.type) { - case NL80211_IFTYPE_MONITOR: - if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE)) - continue; - break; - case NL80211_IFTYPE_AP_VLAN: - continue; - default: - break; - } - if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && - !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) - continue; - if (ieee80211_sdata_running(sdata)) - iterator(data, sdata->vif.addr, - &sdata->vif); - } +void ieee80211_iterate_active_interfaces_rtnl( + struct ieee80211_hw *hw, u32 iter_flags, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data) +{ + struct ieee80211_local *local = hw_to_local(hw); - sdata = rcu_dereference(local->monitor_sdata); - if (sdata && - (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || - sdata->flags & IEEE80211_SDATA_IN_DRIVER)) - iterator(data, sdata->vif.addr, &sdata->vif); + ASSERT_RTNL(); - rcu_read_unlock(); + __iterate_active_interfaces(local, iter_flags, iterator, data); } -EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); +EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); /* * Nothing should have been stuffed into the workqueue during @@ -746,6 +740,7 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, case WLAN_EID_TIMEOUT_INTERVAL: case WLAN_EID_SECONDARY_CHANNEL_OFFSET: case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: + case WLAN_EID_CHAN_SWITCH_PARAM: /* * not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible * that if the content gets bigger it might be needed more than once @@ -911,6 +906,14 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action, } elems->sec_chan_offs = (void *)pos; break; + case WLAN_EID_CHAN_SWITCH_PARAM: + if (elen != + sizeof(*elems->mesh_chansw_params_ie)) { + elem_parse_failed = true; + break; + } + elems->mesh_chansw_params_ie = (void *)pos; + break; case WLAN_EID_WIDE_BW_CHANNEL_SWITCH: if (!action || elen != sizeof(*elems->wide_bw_chansw_ie)) { @@ -1007,14 +1010,21 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, */ enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - /* Set defaults according to 802.11-2007 Table 7-37 */ - aCWmax = 1023; - if (use_11b) - aCWmin = 31; - else - aCWmin = 15; + /* Set defaults according to 802.11-2007 Table 7-37 */ + aCWmax = 1023; + if (use_11b) + aCWmin = 31; + else + aCWmin = 15; + + /* Confiure old 802.11b/g medium access rules. */ + qparam.cw_max = aCWmax; + qparam.cw_min = aCWmin; + qparam.txop = 0; + qparam.aifs = 2; + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + /* Update if QoS is enabled. */ if (enable_qos) { switch (ac) { case IEEE80211_AC_BK: @@ -1050,12 +1060,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, qparam.aifs = 2; break; } - } else { - /* Confiure old 802.11b/g medium access rules. */ - qparam.cw_max = aCWmax; - qparam.cw_min = aCWmin; - qparam.txop = 0; - qparam.aifs = 2; } qparam.uapsd = false; @@ -1084,8 +1088,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt; int err; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + - sizeof(*mgmt) + 6 + extra_len); + /* 24 + 6 = header + auth_algo + auth_transaction + status_code */ + skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len); if (!skb) return; @@ -2296,3 +2300,175 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw) ieee80211_queue_work(hw, &local->radar_detected_work); } EXPORT_SYMBOL(ieee80211_radar_detected); + +u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c) +{ + u32 ret; + int tmp; + + switch (c->width) { + case NL80211_CHAN_WIDTH_20: + c->width = NL80211_CHAN_WIDTH_20_NOHT; + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_40: + c->width = NL80211_CHAN_WIDTH_20; + c->center_freq1 = c->chan->center_freq; + ret = IEEE80211_STA_DISABLE_40MHZ | + IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_80: + tmp = (30 + c->chan->center_freq - c->center_freq1)/20; + /* n_P40 */ + tmp /= 2; + /* freq_P40 */ + c->center_freq1 = c->center_freq1 - 20 + 40 * tmp; + c->width = NL80211_CHAN_WIDTH_40; + ret = IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_80P80: + c->center_freq2 = 0; + c->width = NL80211_CHAN_WIDTH_80; + ret = IEEE80211_STA_DISABLE_80P80MHZ | + IEEE80211_STA_DISABLE_160MHZ; + break; + case NL80211_CHAN_WIDTH_160: + /* n_P20 */ + tmp = (70 + c->chan->center_freq - c->center_freq1)/20; + /* n_P80 */ + tmp /= 4; + c->center_freq1 = c->center_freq1 - 40 + 80 * tmp; + c->width = NL80211_CHAN_WIDTH_80; + ret = IEEE80211_STA_DISABLE_80P80MHZ | + IEEE80211_STA_DISABLE_160MHZ; + break; + default: + case NL80211_CHAN_WIDTH_20_NOHT: + WARN_ON_ONCE(1); + c->width = NL80211_CHAN_WIDTH_20_NOHT; + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + break; + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + WARN_ON_ONCE(1); + /* keep c->width */ + ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT; + break; + } + + WARN_ON_ONCE(!cfg80211_chandef_valid(c)); + + return ret; +} + +/* + * Returns true if smps_mode_new is strictly more restrictive than + * smps_mode_old. + */ +bool ieee80211_smps_is_restrictive(enum ieee80211_smps_mode smps_mode_old, + enum ieee80211_smps_mode smps_mode_new) +{ + if (WARN_ON_ONCE(smps_mode_old == IEEE80211_SMPS_AUTOMATIC || + smps_mode_new == IEEE80211_SMPS_AUTOMATIC)) + return false; + + switch (smps_mode_old) { + case IEEE80211_SMPS_STATIC: + return false; + case IEEE80211_SMPS_DYNAMIC: + return smps_mode_new == IEEE80211_SMPS_STATIC; + case IEEE80211_SMPS_OFF: + return smps_mode_new != IEEE80211_SMPS_OFF; + default: + WARN_ON(1); + } + + return false; +} + +int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata, + struct cfg80211_csa_settings *csa_settings) +{ + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + struct ieee80211_local *local = sdata->local; + int freq; + int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) + + sizeof(mgmt->u.action.u.chan_switch); + u8 *pos; + + if (sdata->vif.type != NL80211_IFTYPE_ADHOC && + sdata->vif.type != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + skb = dev_alloc_skb(local->tx_headroom + hdr_len + + 5 + /* channel switch announcement element */ + 3 + /* secondary channel offset element */ + 8); /* mesh channel switch parameters element */ + if (!skb) + return -ENOMEM; + + skb_reserve(skb, local->tx_headroom); + mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len); + memset(mgmt, 0, hdr_len); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + eth_broadcast_addr(mgmt->da); + memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + if (ieee80211_vif_is_mesh(&sdata->vif)) { + memcpy(mgmt->bssid, sdata->vif.addr, ETH_ALEN); + } else { + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN); + } + mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT; + mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH; + pos = skb_put(skb, 5); + *pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */ + *pos++ = 3; /* IE length */ + *pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */ + freq = csa_settings->chandef.chan->center_freq; + *pos++ = ieee80211_frequency_to_channel(freq); /* channel */ + *pos++ = csa_settings->count; /* count */ + + if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) { + enum nl80211_channel_type ch_type; + + skb_put(skb, 3); + *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */ + *pos++ = 1; /* IE length */ + ch_type = cfg80211_get_chandef_type(&csa_settings->chandef); + if (ch_type == NL80211_CHAN_HT40PLUS) + *pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + else + *pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + } + + if (ieee80211_vif_is_mesh(&sdata->vif)) { + struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + __le16 pre_value; + + skb_put(skb, 8); + *pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */ + *pos++ = 6; /* IE length */ + *pos++ = sdata->u.mesh.mshcfg.dot11MeshTTL; /* Mesh TTL */ + *pos = 0x00; /* Mesh Flag: Tx Restrict, Initiator, Reason */ + *pos |= WLAN_EID_CHAN_SWITCH_PARAM_INITIATOR; + *pos++ |= csa_settings->block_tx ? + WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00; + put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */ + pos += 2; + if (!ifmsh->pre_value) + ifmsh->pre_value = 1; + else + ifmsh->pre_value++; + pre_value = cpu_to_le16(ifmsh->pre_value); + memcpy(pos, &pre_value, 2); /* Precedence Value */ + pos += 2; + ifmsh->chsw_init = true; + } + + ieee80211_tx_skb(sdata, skb); + return 0; +} diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 97c289414e32a5d989528dd9bba7c8b77ec982bf..de0112785aae19a8b23b2f8f84b364c80d27e8b3 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -185,13 +185,13 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) { vht_cap->cap |= cap_info & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | - IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX | IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX); } if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) vht_cap->cap |= cap_info & - IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; + (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX); if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE) vht_cap->cap |= cap_info & diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index c9edfcb7a13b65ad6d766b182e54cf6cfcc83c0b..d6572822076367cdf203207892a2880c7e4487c2 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -301,22 +301,16 @@ ieee80211_crypto_tkip_decrypt(struct ieee80211_rx_data *rx) } -static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, +static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad, int encrypted) { __le16 mask_fc; int a4_included, mgmt; u8 qos_tid; - u8 *b_0, *aad; - u16 data_len, len_a; + u16 len_a; unsigned int hdrlen; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - memset(scratch, 0, 6 * AES_BLOCK_SIZE); - - b_0 = scratch + 3 * AES_BLOCK_SIZE; - aad = scratch + 4 * AES_BLOCK_SIZE; - /* * Mask FC: zero subtype b4 b5 b6 (if not mgmt) * Retry, PwrMgt, MoreData; set Protected @@ -338,20 +332,21 @@ static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *scratch, else qos_tid = 0; - data_len = skb->len - hdrlen - IEEE80211_CCMP_HDR_LEN; - if (encrypted) - data_len -= IEEE80211_CCMP_MIC_LEN; + /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC + * mode authentication are not allowed to collide, yet both are derived + * from this vector b_0. We only set L := 1 here to indicate that the + * data size can be represented in (L+1) bytes. The CCM layer will take + * care of storing the data length in the top (L+1) bytes and setting + * and clearing the other bits as is required to derive the two IVs. + */ + b_0[0] = 0x1; - /* First block, b_0 */ - b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */ /* Nonce: Nonce Flags | A2 | PN * Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7) */ b_0[1] = qos_tid | (mgmt << 4); memcpy(&b_0[2], hdr->addr2, ETH_ALEN); memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN); - /* l(m) */ - put_unaligned_be16(data_len, &b_0[14]); /* AAD (extra authenticate-only data) / masked 802.11 header * FC | A1 | A2 | A3 | SC | [A4] | [QC] */ @@ -407,7 +402,8 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) u8 *pos; u8 pn[6]; u64 pn64; - u8 scratch[6 * AES_BLOCK_SIZE]; + u8 aad[2 * AES_BLOCK_SIZE]; + u8 b_0[AES_BLOCK_SIZE]; if (info->control.hw_key && !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) && @@ -460,9 +456,9 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb) return 0; pos += IEEE80211_CCMP_HDR_LEN; - ccmp_special_blocks(skb, pn, scratch, 0); - ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, scratch, pos, len, - pos, skb_put(skb, IEEE80211_CCMP_MIC_LEN)); + ccmp_special_blocks(skb, pn, b_0, aad, 0); + ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len, + skb_put(skb, IEEE80211_CCMP_MIC_LEN)); return 0; } @@ -525,16 +521,16 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx) } if (!(status->flag & RX_FLAG_DECRYPTED)) { - u8 scratch[6 * AES_BLOCK_SIZE]; + u8 aad[2 * AES_BLOCK_SIZE]; + u8 b_0[AES_BLOCK_SIZE]; /* hardware didn't decrypt/verify MIC */ - ccmp_special_blocks(skb, pn, scratch, 1); + ccmp_special_blocks(skb, pn, b_0, aad, 1); if (ieee80211_aes_ccm_decrypt( - key->u.ccmp.tfm, scratch, + key->u.ccmp.tfm, b_0, aad, skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN, data_len, - skb->data + skb->len - IEEE80211_CCMP_MIC_LEN, - skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN)) + skb->data + skb->len - IEEE80211_CCMP_MIC_LEN)) return RX_DROP_UNUSABLE; } diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c index b7c7f815deae81068b073636dc2a0978c1ab3c53..52ae6646a41140e065920ee80c4df626c6a58594 100644 --- a/net/mac802154/ieee802154_dev.c +++ b/net/mac802154/ieee802154_dev.c @@ -174,8 +174,7 @@ ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops) if (!ops || !ops->xmit || !ops->ed || !ops->start || !ops->stop || !ops->set_channel) { - printk(KERN_ERR - "undefined IEEE802.15.4 device operations\n"); + pr_err("undefined IEEE802.15.4 device operations\n"); return NULL; } @@ -201,8 +200,7 @@ ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops) phy = wpan_phy_alloc(priv_size); if (!phy) { - printk(KERN_ERR - "failure to allocate master IEEE802.15.4 device\n"); + pr_err("failure to allocate master IEEE802.15.4 device\n"); return NULL; } diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index 2ca2f4dceab77167dea995132ef730b6981ead2f..e24bcf9772968d9e5a8dc59bbc8597dc2dfca1e8 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -208,6 +208,8 @@ static int mac802154_header_create(struct sk_buff *skb, head[1] = fc >> 8; memcpy(skb_push(skb, pos), head, pos); + skb_reset_mac_header(skb); + skb->mac_len = pos; return pos; } diff --git a/net/mpls/mpls_gso.c b/net/mpls/mpls_gso.c index 1bec1219ab815ff37c3970a4e2473e0a4bb62809..851cd880b0c048c4dcd45e1b6652888338490cf9 100644 --- a/net/mpls/mpls_gso.c +++ b/net/mpls/mpls_gso.c @@ -33,6 +33,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb, SKB_GSO_DODGY | SKB_GSO_TCP_ECN | SKB_GSO_GRE | + SKB_GSO_IPIP | SKB_GSO_MPLS))) goto out; diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 6e839b6dff2b1349f86d87837e0a91bb486cc4bf..48acec17e27a1524ced30bd86ad865f147ef3a3e 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -413,6 +413,58 @@ config NETFILTER_SYNPROXY endif # NF_CONNTRACK +config NF_TABLES + depends on NETFILTER_NETLINK + tristate "Netfilter nf_tables support" + +config NFT_EXTHDR + depends on NF_TABLES + tristate "Netfilter nf_tables IPv6 exthdr module" + +config NFT_META + depends on NF_TABLES + tristate "Netfilter nf_tables meta module" + +config NFT_CT + depends on NF_TABLES + depends on NF_CONNTRACK + tristate "Netfilter nf_tables conntrack module" + +config NFT_RBTREE + depends on NF_TABLES + tristate "Netfilter nf_tables rbtree set module" + +config NFT_HASH + depends on NF_TABLES + tristate "Netfilter nf_tables hash set module" + +config NFT_COUNTER + depends on NF_TABLES + tristate "Netfilter nf_tables counter module" + +config NFT_LOG + depends on NF_TABLES + tristate "Netfilter nf_tables log module" + +config NFT_LIMIT + depends on NF_TABLES + tristate "Netfilter nf_tables limit module" + +config NFT_NAT + depends on NF_TABLES + depends on NF_CONNTRACK + depends on NF_NAT + tristate "Netfilter nf_tables nat module" + +config NFT_COMPAT + depends on NF_TABLES + depends on NETFILTER_XTABLES + tristate "Netfilter x_tables over nf_tables module" + help + This is required if you intend to use any of existing + x_tables match/target extensions over the nf_tables + framework. + config NETFILTER_XTABLES tristate "Netfilter Xtables support (required for ip_tables)" default m if NETFILTER_ADVANCED=n diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index c3a0a12907f693630b841400d3e47babfd338bde..394483b2c193c53fb08dd98591969db0fa995fa8 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -64,6 +64,24 @@ obj-$(CONFIG_NF_NAT_TFTP) += nf_nat_tftp.o # SYNPROXY obj-$(CONFIG_NETFILTER_SYNPROXY) += nf_synproxy_core.o +# nf_tables +nf_tables-objs += nf_tables_core.o nf_tables_api.o +nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o +nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o + +obj-$(CONFIG_NF_TABLES) += nf_tables.o +obj-$(CONFIG_NFT_COMPAT) += nft_compat.o +obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o +obj-$(CONFIG_NFT_META) += nft_meta.o +obj-$(CONFIG_NFT_CT) += nft_ct.o +obj-$(CONFIG_NFT_LIMIT) += nft_limit.o +obj-$(CONFIG_NFT_NAT) += nft_nat.o +#nf_tables-objs += nft_meta_target.o +obj-$(CONFIG_NFT_RBTREE) += nft_rbtree.o +obj-$(CONFIG_NFT_HASH) += nft_hash.o +obj-$(CONFIG_NFT_COUNTER) += nft_counter.o +obj-$(CONFIG_NFT_LOG) += nft_log.o + # generic X tables obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 593b16ea45e0d817787c4d06d9d3d703df2fc65b..1fbab0cdd302bdafe199d434fc10f2f6401ef6a8 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -146,7 +146,7 @@ unsigned int nf_iterate(struct list_head *head, /* Optimization: we don't need to hold module reference here, since function can't sleep. --RR */ repeat: - verdict = (*elemp)->hook(hook, skb, indev, outdev, okfn); + verdict = (*elemp)->hook(*elemp, skb, indev, outdev, okfn); if (verdict != NF_ACCEPT) { #ifdef CONFIG_NETFILTER_DEBUG if (unlikely((verdict & NF_VERDICT_MASK) diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index ba36c283d8372dfb3c890994c57c6bb0d46266b0..a2d6263b6c648e97c663025660ad47320c2c08ff 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig @@ -1,7 +1,7 @@ menuconfig IP_SET tristate "IP set support" depends on INET && NETFILTER - depends on NETFILTER_NETLINK + select NETFILTER_NETLINK help This option adds IP set support to the kernel. In order to define and use the sets, you need the userspace utility @@ -90,6 +90,15 @@ config IP_SET_HASH_IPPORTNET To compile it as a module, choose M here. If unsure, say N. +config IP_SET_HASH_NETPORTNET + tristate "hash:net,port,net set support" + depends on IP_SET + help + This option adds the hash:net,port,net set type support, by which + one can store two IPv4/IPv6 subnets, and a protocol/port in a set. + + To compile it as a module, choose M here. If unsure, say N. + config IP_SET_HASH_NET tristate "hash:net set support" depends on IP_SET @@ -99,6 +108,15 @@ config IP_SET_HASH_NET To compile it as a module, choose M here. If unsure, say N. +config IP_SET_HASH_NETNET + tristate "hash:net,net set support" + depends on IP_SET + help + This option adds the hash:net,net set type support, by which + one can store IPv4/IPv6 network address/prefix pairs in a set. + + To compile it as a module, choose M here. If unsure, say N. + config IP_SET_HASH_NETPORT tristate "hash:net,port set support" depends on IP_SET diff --git a/net/netfilter/ipset/Makefile b/net/netfilter/ipset/Makefile index 6e965ecd5444696b4dc7e09bb98b1d09420ce9ab..44b2d38476faeb75a95c5ac8348330a99aa866fd 100644 --- a/net/netfilter/ipset/Makefile +++ b/net/netfilter/ipset/Makefile @@ -20,6 +20,8 @@ obj-$(CONFIG_IP_SET_HASH_IPPORTNET) += ip_set_hash_ipportnet.o obj-$(CONFIG_IP_SET_HASH_NET) += ip_set_hash_net.o obj-$(CONFIG_IP_SET_HASH_NETPORT) += ip_set_hash_netport.o obj-$(CONFIG_IP_SET_HASH_NETIFACE) += ip_set_hash_netiface.o +obj-$(CONFIG_IP_SET_HASH_NETNET) += ip_set_hash_netnet.o +obj-$(CONFIG_IP_SET_HASH_NETPORTNET) += ip_set_hash_netportnet.o # list types obj-$(CONFIG_IP_SET_LIST_SET) += ip_set_list_set.o diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index 25243379b887e5724d147cac0afedab58a1dec61..f2c7d83dc23f46b83e4d24e665f2abc31317783b 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -8,38 +8,32 @@ #ifndef __IP_SET_BITMAP_IP_GEN_H #define __IP_SET_BITMAP_IP_GEN_H -#define CONCAT(a, b) a##b -#define TOKEN(a,b) CONCAT(a, b) - -#define mtype_do_test TOKEN(MTYPE, _do_test) -#define mtype_gc_test TOKEN(MTYPE, _gc_test) -#define mtype_is_filled TOKEN(MTYPE, _is_filled) -#define mtype_do_add TOKEN(MTYPE, _do_add) -#define mtype_do_del TOKEN(MTYPE, _do_del) -#define mtype_do_list TOKEN(MTYPE, _do_list) -#define mtype_do_head TOKEN(MTYPE, _do_head) -#define mtype_adt_elem TOKEN(MTYPE, _adt_elem) -#define mtype_add_timeout TOKEN(MTYPE, _add_timeout) -#define mtype_gc_init TOKEN(MTYPE, _gc_init) -#define mtype_kadt TOKEN(MTYPE, _kadt) -#define mtype_uadt TOKEN(MTYPE, _uadt) -#define mtype_destroy TOKEN(MTYPE, _destroy) -#define mtype_flush TOKEN(MTYPE, _flush) -#define mtype_head TOKEN(MTYPE, _head) -#define mtype_same_set TOKEN(MTYPE, _same_set) -#define mtype_elem TOKEN(MTYPE, _elem) -#define mtype_test TOKEN(MTYPE, _test) -#define mtype_add TOKEN(MTYPE, _add) -#define mtype_del TOKEN(MTYPE, _del) -#define mtype_list TOKEN(MTYPE, _list) -#define mtype_gc TOKEN(MTYPE, _gc) +#define mtype_do_test IPSET_TOKEN(MTYPE, _do_test) +#define mtype_gc_test IPSET_TOKEN(MTYPE, _gc_test) +#define mtype_is_filled IPSET_TOKEN(MTYPE, _is_filled) +#define mtype_do_add IPSET_TOKEN(MTYPE, _do_add) +#define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup) +#define mtype_do_del IPSET_TOKEN(MTYPE, _do_del) +#define mtype_do_list IPSET_TOKEN(MTYPE, _do_list) +#define mtype_do_head IPSET_TOKEN(MTYPE, _do_head) +#define mtype_adt_elem IPSET_TOKEN(MTYPE, _adt_elem) +#define mtype_add_timeout IPSET_TOKEN(MTYPE, _add_timeout) +#define mtype_gc_init IPSET_TOKEN(MTYPE, _gc_init) +#define mtype_kadt IPSET_TOKEN(MTYPE, _kadt) +#define mtype_uadt IPSET_TOKEN(MTYPE, _uadt) +#define mtype_destroy IPSET_TOKEN(MTYPE, _destroy) +#define mtype_flush IPSET_TOKEN(MTYPE, _flush) +#define mtype_head IPSET_TOKEN(MTYPE, _head) +#define mtype_same_set IPSET_TOKEN(MTYPE, _same_set) +#define mtype_elem IPSET_TOKEN(MTYPE, _elem) +#define mtype_test IPSET_TOKEN(MTYPE, _test) +#define mtype_add IPSET_TOKEN(MTYPE, _add) +#define mtype_del IPSET_TOKEN(MTYPE, _del) +#define mtype_list IPSET_TOKEN(MTYPE, _list) +#define mtype_gc IPSET_TOKEN(MTYPE, _gc) #define mtype MTYPE -#define ext_timeout(e, m) \ - (unsigned long *)((e) + (m)->offset[IPSET_OFFSET_TIMEOUT]) -#define ext_counter(e, m) \ - (struct ip_set_counter *)((e) + (m)->offset[IPSET_OFFSET_COUNTER]) -#define get_ext(map, id) ((map)->extensions + (map)->dsize * (id)) +#define get_ext(set, map, id) ((map)->extensions + (set)->dsize * (id)) static void mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) @@ -49,10 +43,21 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) init_timer(&map->gc); map->gc.data = (unsigned long) set; map->gc.function = gc; - map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; add_timer(&map->gc); } +static void +mtype_ext_cleanup(struct ip_set *set) +{ + struct mtype *map = set->data; + u32 id; + + for (id = 0; id < map->elements; id++) + if (test_bit(id, map->members)) + ip_set_ext_destroy(set, get_ext(set, map, id)); +} + static void mtype_destroy(struct ip_set *set) { @@ -62,8 +67,11 @@ mtype_destroy(struct ip_set *set) del_timer_sync(&map->gc); ip_set_free(map->members); - if (map->dsize) + if (set->dsize) { + if (set->extensions & IPSET_EXT_DESTROY) + mtype_ext_cleanup(set); ip_set_free(map->extensions); + } kfree(map); set->data = NULL; @@ -74,6 +82,8 @@ mtype_flush(struct ip_set *set) { struct mtype *map = set->data; + if (set->extensions & IPSET_EXT_DESTROY) + mtype_ext_cleanup(set); memset(map->members, 0, map->memsize); } @@ -91,12 +101,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(sizeof(*map) + map->memsize + - map->dsize * map->elements)) || - (SET_WITH_TIMEOUT(set) && - nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) || - (SET_WITH_COUNTER(set) && - nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, - htonl(IPSET_FLAG_WITH_COUNTERS)))) + set->dsize * map->elements))) + goto nla_put_failure; + if (unlikely(ip_set_put_flags(skb, set))) goto nla_put_failure; ipset_nest_end(skb, nested); @@ -111,16 +118,16 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, { struct mtype *map = set->data; const struct mtype_adt_elem *e = value; - void *x = get_ext(map, e->id); - int ret = mtype_do_test(e, map); + void *x = get_ext(set, map, e->id); + int ret = mtype_do_test(e, map, set->dsize); if (ret <= 0) return ret; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(x, map))) + ip_set_timeout_expired(ext_timeout(x, set))) return 0; if (SET_WITH_COUNTER(set)) - ip_set_update_counter(ext_counter(x, map), ext, mext, flags); + ip_set_update_counter(ext_counter(x, set), ext, mext, flags); return 1; } @@ -130,26 +137,30 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, { struct mtype *map = set->data; const struct mtype_adt_elem *e = value; - void *x = get_ext(map, e->id); - int ret = mtype_do_add(e, map, flags); + void *x = get_ext(set, map, e->id); + int ret = mtype_do_add(e, map, flags, set->dsize); if (ret == IPSET_ADD_FAILED) { if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(x, map))) + ip_set_timeout_expired(ext_timeout(x, set))) ret = 0; else if (!(flags & IPSET_FLAG_EXIST)) return -IPSET_ERR_EXIST; + /* Element is re-added, cleanup extensions */ + ip_set_ext_destroy(set, x); } if (SET_WITH_TIMEOUT(set)) #ifdef IP_SET_BITMAP_STORED_TIMEOUT - mtype_add_timeout(ext_timeout(x, map), e, ext, map, ret); + mtype_add_timeout(ext_timeout(x, set), e, ext, set, map, ret); #else - ip_set_timeout_set(ext_timeout(x, map), ext->timeout); + ip_set_timeout_set(ext_timeout(x, set), ext->timeout); #endif if (SET_WITH_COUNTER(set)) - ip_set_init_counter(ext_counter(x, map), ext); + ip_set_init_counter(ext_counter(x, set), ext); + if (SET_WITH_COMMENT(set)) + ip_set_init_comment(ext_comment(x, set), ext); return 0; } @@ -159,16 +170,27 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, { struct mtype *map = set->data; const struct mtype_adt_elem *e = value; - const void *x = get_ext(map, e->id); + void *x = get_ext(set, map, e->id); - if (mtype_do_del(e, map) || - (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(x, map)))) + if (mtype_do_del(e, map)) + return -IPSET_ERR_EXIST; + + ip_set_ext_destroy(set, x); + if (SET_WITH_TIMEOUT(set) && + ip_set_timeout_expired(ext_timeout(x, set))) return -IPSET_ERR_EXIST; return 0; } +#ifndef IP_SET_BITMAP_STORED_TIMEOUT +static inline bool +mtype_is_filled(const struct mtype_elem *x) +{ + return true; +} +#endif + static int mtype_list(const struct ip_set *set, struct sk_buff *skb, struct netlink_callback *cb) @@ -176,20 +198,21 @@ mtype_list(const struct ip_set *set, struct mtype *map = set->data; struct nlattr *adt, *nested; void *x; - u32 id, first = cb->args[2]; + u32 id, first = cb->args[IPSET_CB_ARG0]; adt = ipset_nest_start(skb, IPSET_ATTR_ADT); if (!adt) return -EMSGSIZE; - for (; cb->args[2] < map->elements; cb->args[2]++) { - id = cb->args[2]; - x = get_ext(map, id); + for (; cb->args[IPSET_CB_ARG0] < map->elements; + cb->args[IPSET_CB_ARG0]++) { + id = cb->args[IPSET_CB_ARG0]; + x = get_ext(set, map, id); if (!test_bit(id, map->members) || (SET_WITH_TIMEOUT(set) && #ifdef IP_SET_BITMAP_STORED_TIMEOUT mtype_is_filled((const struct mtype_elem *) x) && #endif - ip_set_timeout_expired(ext_timeout(x, map)))) + ip_set_timeout_expired(ext_timeout(x, set)))) continue; nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) { @@ -199,40 +222,27 @@ mtype_list(const struct ip_set *set, } else goto nla_put_failure; } - if (mtype_do_list(skb, map, id)) + if (mtype_do_list(skb, map, id, set->dsize)) goto nla_put_failure; - if (SET_WITH_TIMEOUT(set)) { -#ifdef IP_SET_BITMAP_STORED_TIMEOUT - if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_stored(map, id, - ext_timeout(x, map))))) - goto nla_put_failure; -#else - if (nla_put_net32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get( - ext_timeout(x, map))))) - goto nla_put_failure; -#endif - } - if (SET_WITH_COUNTER(set) && - ip_set_put_counter(skb, ext_counter(x, map))) + if (ip_set_put_extensions(skb, set, x, + mtype_is_filled((const struct mtype_elem *) x))) goto nla_put_failure; ipset_nest_end(skb, nested); } ipset_nest_end(skb, adt); /* Set listing finished */ - cb->args[2] = 0; + cb->args[IPSET_CB_ARG0] = 0; return 0; nla_put_failure: nla_nest_cancel(skb, nested); - ipset_nest_end(skb, adt); if (unlikely(id == first)) { - cb->args[2] = 0; + cb->args[IPSET_CB_ARG0] = 0; return -EMSGSIZE; } + ipset_nest_end(skb, adt); return 0; } @@ -241,21 +251,23 @@ mtype_gc(unsigned long ul_set) { struct ip_set *set = (struct ip_set *) ul_set; struct mtype *map = set->data; - const void *x; + void *x; u32 id; /* We run parallel with other readers (test element) * but adding/deleting new entries is locked out */ read_lock_bh(&set->lock); for (id = 0; id < map->elements; id++) - if (mtype_gc_test(id, map)) { - x = get_ext(map, id); - if (ip_set_timeout_expired(ext_timeout(x, map))) + if (mtype_gc_test(id, map, set->dsize)) { + x = get_ext(set, map, id); + if (ip_set_timeout_expired(ext_timeout(x, set))) { clear_bit(id, map->members); + ip_set_ext_destroy(set, x); + } } read_unlock_bh(&set->lock); - map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; add_timer(&map->gc); } diff --git a/net/netfilter/ipset/ip_set_bitmap_ip.c b/net/netfilter/ipset/ip_set_bitmap_ip.c index f1a8128bef01c3f555c23264683d9f1688b06509..6f1f9f4948084e40375ab722565e7bf20b2d972d 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ip.c +++ b/net/netfilter/ipset/ip_set_bitmap_ip.c @@ -25,12 +25,13 @@ #include #include -#define REVISION_MIN 0 -#define REVISION_MAX 1 /* Counter support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 Counter support added */ +#define IPSET_TYPE_REV_MAX 2 /* Comment support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("bitmap:ip", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("bitmap:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_bitmap:ip"); #define MTYPE bitmap_ip @@ -44,10 +45,7 @@ struct bitmap_ip { u32 elements; /* number of max elements in the set */ u32 hosts; /* number of hosts in a subnet */ size_t memsize; /* members size */ - size_t dsize; /* extensions struct size */ - size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ u8 netmask; /* subnet netmask */ - u32 timeout; /* timeout parameter */ struct timer_list gc; /* garbage collection */ }; @@ -65,20 +63,21 @@ ip_to_id(const struct bitmap_ip *m, u32 ip) /* Common functions */ static inline int -bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map) +bitmap_ip_do_test(const struct bitmap_ip_adt_elem *e, + struct bitmap_ip *map, size_t dsize) { return !!test_bit(e->id, map->members); } static inline int -bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map) +bitmap_ip_gc_test(u16 id, const struct bitmap_ip *map, size_t dsize) { return !!test_bit(id, map->members); } static inline int bitmap_ip_do_add(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map, - u32 flags) + u32 flags, size_t dsize) { return !!test_and_set_bit(e->id, map->members); } @@ -90,7 +89,8 @@ bitmap_ip_do_del(const struct bitmap_ip_adt_elem *e, struct bitmap_ip *map) } static inline int -bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id) +bitmap_ip_do_list(struct sk_buff *skb, const struct bitmap_ip *map, u32 id, + size_t dsize) { return nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip + id * map->hosts)); @@ -113,7 +113,7 @@ bitmap_ip_kadt(struct ip_set *set, const struct sk_buff *skb, struct bitmap_ip *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_ip_adt_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); u32 ip; ip = ntohl(ip4addr(skb, opt->flags & IPSET_DIM_ONE_SRC)); @@ -131,9 +131,9 @@ bitmap_ip_uadt(struct ip_set *set, struct nlattr *tb[], { struct bitmap_ip *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; - u32 ip, ip_to; + u32 ip = 0, ip_to = 0; struct bitmap_ip_adt_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(map); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); int ret = 0; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -200,7 +200,7 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b) return x->first_ip == y->first_ip && x->last_ip == y->last_ip && x->netmask == y->netmask && - x->timeout == y->timeout && + a->timeout == b->timeout && a->extensions == b->extensions; } @@ -209,25 +209,6 @@ bitmap_ip_same_set(const struct ip_set *a, const struct ip_set *b) struct bitmap_ip_elem { }; -/* Timeout variant */ - -struct bitmap_ipt_elem { - unsigned long timeout; -}; - -/* Plain variant with counter */ - -struct bitmap_ipc_elem { - struct ip_set_counter counter; -}; - -/* Timeout variant with counter */ - -struct bitmap_ipct_elem { - unsigned long timeout; - struct ip_set_counter counter; -}; - #include "ip_set_bitmap_gen.h" /* Create bitmap:ip type of sets */ @@ -240,8 +221,8 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, map->members = ip_set_alloc(map->memsize); if (!map->members) return false; - if (map->dsize) { - map->extensions = ip_set_alloc(map->dsize * elements); + if (set->dsize) { + map->extensions = ip_set_alloc(set->dsize * elements); if (!map->extensions) { kfree(map->members); return false; @@ -252,7 +233,7 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, map->elements = elements; map->hosts = hosts; map->netmask = netmask; - map->timeout = IPSET_NO_TIMEOUT; + set->timeout = IPSET_NO_TIMEOUT; set->data = map; set->family = NFPROTO_IPV4; @@ -261,10 +242,11 @@ init_map_ip(struct ip_set *set, struct bitmap_ip *map, } static int -bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) +bitmap_ip_create(struct net *net, struct ip_set *set, struct nlattr *tb[], + u32 flags) { struct bitmap_ip *map; - u32 first_ip, last_ip, hosts, cadt_flags = 0; + u32 first_ip = 0, last_ip = 0, hosts; u64 elements; u8 netmask = 32; int ret; @@ -336,61 +318,15 @@ bitmap_ip_create(struct ip_set *set, struct nlattr *tb[], u32 flags) map->memsize = bitmap_bytes(0, elements - 1); set->variant = &bitmap_ip; - if (tb[IPSET_ATTR_CADT_FLAGS]) - cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { - set->extensions |= IPSET_EXT_COUNTER; - if (tb[IPSET_ATTR_TIMEOUT]) { - map->dsize = sizeof(struct bitmap_ipct_elem); - map->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct bitmap_ipct_elem, timeout); - map->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct bitmap_ipct_elem, counter); - - if (!init_map_ip(set, map, first_ip, last_ip, - elements, hosts, netmask)) { - kfree(map); - return -ENOMEM; - } - - map->timeout = ip_set_timeout_uget( - tb[IPSET_ATTR_TIMEOUT]); - set->extensions |= IPSET_EXT_TIMEOUT; - - bitmap_ip_gc_init(set, bitmap_ip_gc); - } else { - map->dsize = sizeof(struct bitmap_ipc_elem); - map->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct bitmap_ipc_elem, counter); - - if (!init_map_ip(set, map, first_ip, last_ip, - elements, hosts, netmask)) { - kfree(map); - return -ENOMEM; - } - } - } else if (tb[IPSET_ATTR_TIMEOUT]) { - map->dsize = sizeof(struct bitmap_ipt_elem); - map->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct bitmap_ipt_elem, timeout); - - if (!init_map_ip(set, map, first_ip, last_ip, - elements, hosts, netmask)) { - kfree(map); - return -ENOMEM; - } - - map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->extensions |= IPSET_EXT_TIMEOUT; - + set->dsize = ip_set_elem_len(set, tb, 0); + if (!init_map_ip(set, map, first_ip, last_ip, + elements, hosts, netmask)) { + kfree(map); + return -ENOMEM; + } + if (tb[IPSET_ATTR_TIMEOUT]) { + set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); bitmap_ip_gc_init(set, bitmap_ip_gc); - } else { - map->dsize = 0; - if (!init_map_ip(set, map, first_ip, last_ip, - elements, hosts, netmask)) { - kfree(map); - return -ENOMEM; - } } return 0; } @@ -401,8 +337,8 @@ static struct ip_set_type bitmap_ip_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = NFPROTO_IPV4, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = bitmap_ip_create, .create_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, @@ -420,6 +356,7 @@ static struct ip_set_type bitmap_ip_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_bitmap_ipmac.c b/net/netfilter/ipset/ip_set_bitmap_ipmac.c index 3b30e0bef890424abbb3bbf71bd2df147a52eab2..740eabededd9754b7db95a9d46d48f8a1f283bf7 100644 --- a/net/netfilter/ipset/ip_set_bitmap_ipmac.c +++ b/net/netfilter/ipset/ip_set_bitmap_ipmac.c @@ -25,12 +25,13 @@ #include #include -#define REVISION_MIN 0 -#define REVISION_MAX 1 /* Counter support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 Counter support added */ +#define IPSET_TYPE_REV_MAX 2 /* Comment support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("bitmap:ip,mac", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("bitmap:ip,mac", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_bitmap:ip,mac"); #define MTYPE bitmap_ipmac @@ -48,11 +49,8 @@ struct bitmap_ipmac { u32 first_ip; /* host byte order, included in range */ u32 last_ip; /* host byte order, included in range */ u32 elements; /* number of max elements in the set */ - u32 timeout; /* timeout value */ - struct timer_list gc; /* garbage collector */ size_t memsize; /* members size */ - size_t dsize; /* size of element */ - size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ + struct timer_list gc; /* garbage collector */ }; /* ADT structure for generic function args */ @@ -82,13 +80,13 @@ get_elem(void *extensions, u16 id, size_t dsize) static inline int bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e, - const struct bitmap_ipmac *map) + const struct bitmap_ipmac *map, size_t dsize) { const struct bitmap_ipmac_elem *elem; if (!test_bit(e->id, map->members)) return 0; - elem = get_elem(map->extensions, e->id, map->dsize); + elem = get_elem(map->extensions, e->id, dsize); if (elem->filled == MAC_FILLED) return e->ether == NULL || ether_addr_equal(e->ether, elem->ether); @@ -97,13 +95,13 @@ bitmap_ipmac_do_test(const struct bitmap_ipmac_adt_elem *e, } static inline int -bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map) +bitmap_ipmac_gc_test(u16 id, const struct bitmap_ipmac *map, size_t dsize) { const struct bitmap_ipmac_elem *elem; if (!test_bit(id, map->members)) return 0; - elem = get_elem(map->extensions, id, map->dsize); + elem = get_elem(map->extensions, id, dsize); /* Timer not started for the incomplete elements */ return elem->filled == MAC_FILLED; } @@ -117,13 +115,13 @@ bitmap_ipmac_is_filled(const struct bitmap_ipmac_elem *elem) static inline int bitmap_ipmac_add_timeout(unsigned long *timeout, const struct bitmap_ipmac_adt_elem *e, - const struct ip_set_ext *ext, + const struct ip_set_ext *ext, struct ip_set *set, struct bitmap_ipmac *map, int mode) { u32 t = ext->timeout; if (mode == IPSET_ADD_START_STORED_TIMEOUT) { - if (t == map->timeout) + if (t == set->timeout) /* Timeout was not specified, get stored one */ t = *timeout; ip_set_timeout_set(timeout, t); @@ -142,11 +140,11 @@ bitmap_ipmac_add_timeout(unsigned long *timeout, static inline int bitmap_ipmac_do_add(const struct bitmap_ipmac_adt_elem *e, - struct bitmap_ipmac *map, u32 flags) + struct bitmap_ipmac *map, u32 flags, size_t dsize) { struct bitmap_ipmac_elem *elem; - elem = get_elem(map->extensions, e->id, map->dsize); + elem = get_elem(map->extensions, e->id, dsize); if (test_and_set_bit(e->id, map->members)) { if (elem->filled == MAC_FILLED) { if (e->ether && (flags & IPSET_FLAG_EXIST)) @@ -178,22 +176,12 @@ bitmap_ipmac_do_del(const struct bitmap_ipmac_adt_elem *e, return !test_and_clear_bit(e->id, map->members); } -static inline unsigned long -ip_set_timeout_stored(struct bitmap_ipmac *map, u32 id, unsigned long *timeout) -{ - const struct bitmap_ipmac_elem *elem = - get_elem(map->extensions, id, map->dsize); - - return elem->filled == MAC_FILLED ? ip_set_timeout_get(timeout) : - *timeout; -} - static inline int bitmap_ipmac_do_list(struct sk_buff *skb, const struct bitmap_ipmac *map, - u32 id) + u32 id, size_t dsize) { const struct bitmap_ipmac_elem *elem = - get_elem(map->extensions, id, map->dsize); + get_elem(map->extensions, id, dsize); return nla_put_ipaddr4(skb, IPSET_ATTR_IP, htonl(map->first_ip + id)) || @@ -216,7 +204,7 @@ bitmap_ipmac_kadt(struct ip_set *set, const struct sk_buff *skb, struct bitmap_ipmac *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_ipmac_adt_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); u32 ip; /* MAC can be src only */ @@ -245,8 +233,8 @@ bitmap_ipmac_uadt(struct ip_set *set, struct nlattr *tb[], const struct bitmap_ipmac *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_ipmac_adt_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_UEXT(map); - u32 ip; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip = 0; int ret = 0; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -285,43 +273,12 @@ bitmap_ipmac_same_set(const struct ip_set *a, const struct ip_set *b) return x->first_ip == y->first_ip && x->last_ip == y->last_ip && - x->timeout == y->timeout && + a->timeout == b->timeout && a->extensions == b->extensions; } /* Plain variant */ -/* Timeout variant */ - -struct bitmap_ipmact_elem { - struct { - unsigned char ether[ETH_ALEN]; - unsigned char filled; - } __attribute__ ((aligned)); - unsigned long timeout; -}; - -/* Plain variant with counter */ - -struct bitmap_ipmacc_elem { - struct { - unsigned char ether[ETH_ALEN]; - unsigned char filled; - } __attribute__ ((aligned)); - struct ip_set_counter counter; -}; - -/* Timeout variant with counter */ - -struct bitmap_ipmacct_elem { - struct { - unsigned char ether[ETH_ALEN]; - unsigned char filled; - } __attribute__ ((aligned)); - unsigned long timeout; - struct ip_set_counter counter; -}; - #include "ip_set_bitmap_gen.h" /* Create bitmap:ip,mac type of sets */ @@ -330,11 +287,11 @@ static bool init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, u32 first_ip, u32 last_ip, u32 elements) { - map->members = ip_set_alloc((last_ip - first_ip + 1) * map->dsize); + map->members = ip_set_alloc(map->memsize); if (!map->members) return false; - if (map->dsize) { - map->extensions = ip_set_alloc(map->dsize * elements); + if (set->dsize) { + map->extensions = ip_set_alloc(set->dsize * elements); if (!map->extensions) { kfree(map->members); return false; @@ -343,7 +300,7 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, map->first_ip = first_ip; map->last_ip = last_ip; map->elements = elements; - map->timeout = IPSET_NO_TIMEOUT; + set->timeout = IPSET_NO_TIMEOUT; set->data = map; set->family = NFPROTO_IPV4; @@ -352,10 +309,10 @@ init_map_ipmac(struct ip_set *set, struct bitmap_ipmac *map, } static int -bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[], +bitmap_ipmac_create(struct net *net, struct ip_set *set, struct nlattr *tb[], u32 flags) { - u32 first_ip, last_ip, cadt_flags = 0; + u32 first_ip = 0, last_ip = 0; u64 elements; struct bitmap_ipmac *map; int ret; @@ -399,57 +356,15 @@ bitmap_ipmac_create(struct ip_set *set, struct nlattr *tb[], map->memsize = bitmap_bytes(0, elements - 1); set->variant = &bitmap_ipmac; - if (tb[IPSET_ATTR_CADT_FLAGS]) - cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { - set->extensions |= IPSET_EXT_COUNTER; - if (tb[IPSET_ATTR_TIMEOUT]) { - map->dsize = sizeof(struct bitmap_ipmacct_elem); - map->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct bitmap_ipmacct_elem, timeout); - map->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct bitmap_ipmacct_elem, counter); - - if (!init_map_ipmac(set, map, first_ip, last_ip, - elements)) { - kfree(map); - return -ENOMEM; - } - map->timeout = ip_set_timeout_uget( - tb[IPSET_ATTR_TIMEOUT]); - set->extensions |= IPSET_EXT_TIMEOUT; - bitmap_ipmac_gc_init(set, bitmap_ipmac_gc); - } else { - map->dsize = sizeof(struct bitmap_ipmacc_elem); - map->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct bitmap_ipmacc_elem, counter); - - if (!init_map_ipmac(set, map, first_ip, last_ip, - elements)) { - kfree(map); - return -ENOMEM; - } - } - } else if (tb[IPSET_ATTR_TIMEOUT]) { - map->dsize = sizeof(struct bitmap_ipmact_elem); - map->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct bitmap_ipmact_elem, timeout); - - if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) { - kfree(map); - return -ENOMEM; - } - map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->extensions |= IPSET_EXT_TIMEOUT; + set->dsize = ip_set_elem_len(set, tb, + sizeof(struct bitmap_ipmac_elem)); + if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) { + kfree(map); + return -ENOMEM; + } + if (tb[IPSET_ATTR_TIMEOUT]) { + set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); bitmap_ipmac_gc_init(set, bitmap_ipmac_gc); - } else { - map->dsize = sizeof(struct bitmap_ipmac_elem); - - if (!init_map_ipmac(set, map, first_ip, last_ip, elements)) { - kfree(map); - return -ENOMEM; - } - set->variant = &bitmap_ipmac; } return 0; } @@ -460,8 +375,8 @@ static struct ip_set_type bitmap_ipmac_type = { .features = IPSET_TYPE_IP | IPSET_TYPE_MAC, .dimension = IPSET_DIM_TWO, .family = NFPROTO_IPV4, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = bitmap_ipmac_create, .create_policy = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, @@ -478,6 +393,7 @@ static struct ip_set_type bitmap_ipmac_type = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_bitmap_port.c b/net/netfilter/ipset/ip_set_bitmap_port.c index 8207d1fda5288dcf252a96b9db60ed5fd543144b..cf99676e69f81bdc326b2a187dc8de1450b23e87 100644 --- a/net/netfilter/ipset/ip_set_bitmap_port.c +++ b/net/netfilter/ipset/ip_set_bitmap_port.c @@ -20,12 +20,13 @@ #include #include -#define REVISION_MIN 0 -#define REVISION_MAX 1 /* Counter support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 Counter support added */ +#define IPSET_TYPE_REV_MAX 2 /* Comment support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("bitmap:port", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("bitmap:port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_bitmap:port"); #define MTYPE bitmap_port @@ -38,9 +39,6 @@ struct bitmap_port { u16 last_port; /* host byte order, included in range */ u32 elements; /* number of max elements in the set */ size_t memsize; /* members size */ - size_t dsize; /* extensions struct size */ - size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ - u32 timeout; /* timeout parameter */ struct timer_list gc; /* garbage collection */ }; @@ -59,20 +57,20 @@ port_to_id(const struct bitmap_port *m, u16 port) static inline int bitmap_port_do_test(const struct bitmap_port_adt_elem *e, - const struct bitmap_port *map) + const struct bitmap_port *map, size_t dsize) { return !!test_bit(e->id, map->members); } static inline int -bitmap_port_gc_test(u16 id, const struct bitmap_port *map) +bitmap_port_gc_test(u16 id, const struct bitmap_port *map, size_t dsize) { return !!test_bit(id, map->members); } static inline int bitmap_port_do_add(const struct bitmap_port_adt_elem *e, - struct bitmap_port *map, u32 flags) + struct bitmap_port *map, u32 flags, size_t dsize) { return !!test_and_set_bit(e->id, map->members); } @@ -85,7 +83,8 @@ bitmap_port_do_del(const struct bitmap_port_adt_elem *e, } static inline int -bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id) +bitmap_port_do_list(struct sk_buff *skb, const struct bitmap_port *map, u32 id, + size_t dsize) { return nla_put_net16(skb, IPSET_ATTR_PORT, htons(map->first_port + id)); @@ -106,7 +105,7 @@ bitmap_port_kadt(struct ip_set *set, const struct sk_buff *skb, struct bitmap_port *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_port_adt_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); __be16 __port; u16 port = 0; @@ -131,7 +130,7 @@ bitmap_port_uadt(struct ip_set *set, struct nlattr *tb[], struct bitmap_port *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct bitmap_port_adt_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_UEXT(map); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); u32 port; /* wraparound */ u16 port_to; int ret = 0; @@ -191,7 +190,7 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b) return x->first_port == y->first_port && x->last_port == y->last_port && - x->timeout == y->timeout && + a->timeout == b->timeout && a->extensions == b->extensions; } @@ -200,25 +199,6 @@ bitmap_port_same_set(const struct ip_set *a, const struct ip_set *b) struct bitmap_port_elem { }; -/* Timeout variant */ - -struct bitmap_portt_elem { - unsigned long timeout; -}; - -/* Plain variant with counter */ - -struct bitmap_portc_elem { - struct ip_set_counter counter; -}; - -/* Timeout variant with counter */ - -struct bitmap_portct_elem { - unsigned long timeout; - struct ip_set_counter counter; -}; - #include "ip_set_bitmap_gen.h" /* Create bitmap:ip type of sets */ @@ -230,8 +210,8 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, map->members = ip_set_alloc(map->memsize); if (!map->members) return false; - if (map->dsize) { - map->extensions = ip_set_alloc(map->dsize * map->elements); + if (set->dsize) { + map->extensions = ip_set_alloc(set->dsize * map->elements); if (!map->extensions) { kfree(map->members); return false; @@ -239,7 +219,7 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, } map->first_port = first_port; map->last_port = last_port; - map->timeout = IPSET_NO_TIMEOUT; + set->timeout = IPSET_NO_TIMEOUT; set->data = map; set->family = NFPROTO_UNSPEC; @@ -248,11 +228,11 @@ init_map_port(struct ip_set *set, struct bitmap_port *map, } static int -bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags) +bitmap_port_create(struct net *net, struct ip_set *set, struct nlattr *tb[], + u32 flags) { struct bitmap_port *map; u16 first_port, last_port; - u32 cadt_flags = 0; if (unlikely(!ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || !ip_set_attr_netorder(tb, IPSET_ATTR_PORT_TO) || @@ -274,55 +254,16 @@ bitmap_port_create(struct ip_set *set, struct nlattr *tb[], u32 flags) return -ENOMEM; map->elements = last_port - first_port + 1; - map->memsize = map->elements * sizeof(unsigned long); + map->memsize = bitmap_bytes(0, map->elements); set->variant = &bitmap_port; - if (tb[IPSET_ATTR_CADT_FLAGS]) - cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { - set->extensions |= IPSET_EXT_COUNTER; - if (tb[IPSET_ATTR_TIMEOUT]) { - map->dsize = sizeof(struct bitmap_portct_elem); - map->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct bitmap_portct_elem, timeout); - map->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct bitmap_portct_elem, counter); - if (!init_map_port(set, map, first_port, last_port)) { - kfree(map); - return -ENOMEM; - } - - map->timeout = - ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->extensions |= IPSET_EXT_TIMEOUT; - bitmap_port_gc_init(set, bitmap_port_gc); - } else { - map->dsize = sizeof(struct bitmap_portc_elem); - map->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct bitmap_portc_elem, counter); - if (!init_map_port(set, map, first_port, last_port)) { - kfree(map); - return -ENOMEM; - } - } - } else if (tb[IPSET_ATTR_TIMEOUT]) { - map->dsize = sizeof(struct bitmap_portt_elem); - map->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct bitmap_portt_elem, timeout); - if (!init_map_port(set, map, first_port, last_port)) { - kfree(map); - return -ENOMEM; - } - - map->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->extensions |= IPSET_EXT_TIMEOUT; + set->dsize = ip_set_elem_len(set, tb, 0); + if (!init_map_port(set, map, first_port, last_port)) { + kfree(map); + return -ENOMEM; + } + if (tb[IPSET_ATTR_TIMEOUT]) { + set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); bitmap_port_gc_init(set, bitmap_port_gc); - } else { - map->dsize = 0; - if (!init_map_port(set, map, first_port, last_port)) { - kfree(map); - return -ENOMEM; - } - } return 0; } @@ -333,8 +274,8 @@ static struct ip_set_type bitmap_port_type = { .features = IPSET_TYPE_PORT, .dimension = IPSET_DIM_ONE, .family = NFPROTO_UNSPEC, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = bitmap_port_create, .create_policy = { [IPSET_ATTR_PORT] = { .type = NLA_U16 }, @@ -349,6 +290,7 @@ static struct ip_set_type bitmap_port_type = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index f2e30fb31e78efa405156ca99ba9aeaded3ea49c..bac7e01df67fae97ea041f908a8dc316d8798459 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -27,8 +29,17 @@ static LIST_HEAD(ip_set_type_list); /* all registered set types */ static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */ -static struct ip_set * __rcu *ip_set_list; /* all individual sets */ -static ip_set_id_t ip_set_max = CONFIG_IP_SET_MAX; /* max number of sets */ +struct ip_set_net { + struct ip_set * __rcu *ip_set_list; /* all individual sets */ + ip_set_id_t ip_set_max; /* max number of sets */ + int is_deleted; /* deleted by ip_set_net_exit */ +}; +static int ip_set_net_id __read_mostly; + +static inline struct ip_set_net *ip_set_pernet(struct net *net) +{ + return net_generic(net, ip_set_net_id); +} #define IP_SET_INC 64 #define STREQ(a, b) (strncmp(a, b, IPSET_MAXNAMELEN) == 0) @@ -45,8 +56,8 @@ MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_IPSET); /* When the nfnl mutex is held: */ #define nfnl_dereference(p) \ rcu_dereference_protected(p, 1) -#define nfnl_set(id) \ - nfnl_dereference(ip_set_list)[id] +#define nfnl_set(inst, id) \ + nfnl_dereference((inst)->ip_set_list)[id] /* * The set types are implemented in modules and registered set types @@ -315,6 +326,60 @@ ip_set_get_ipaddr6(struct nlattr *nla, union nf_inet_addr *ipaddr) } EXPORT_SYMBOL_GPL(ip_set_get_ipaddr6); +typedef void (*destroyer)(void *); +/* ipset data extension types, in size order */ + +const struct ip_set_ext_type ip_set_extensions[] = { + [IPSET_EXT_ID_COUNTER] = { + .type = IPSET_EXT_COUNTER, + .flag = IPSET_FLAG_WITH_COUNTERS, + .len = sizeof(struct ip_set_counter), + .align = __alignof__(struct ip_set_counter), + }, + [IPSET_EXT_ID_TIMEOUT] = { + .type = IPSET_EXT_TIMEOUT, + .len = sizeof(unsigned long), + .align = __alignof__(unsigned long), + }, + [IPSET_EXT_ID_COMMENT] = { + .type = IPSET_EXT_COMMENT | IPSET_EXT_DESTROY, + .flag = IPSET_FLAG_WITH_COMMENT, + .len = sizeof(struct ip_set_comment), + .align = __alignof__(struct ip_set_comment), + .destroy = (destroyer) ip_set_comment_free, + }, +}; +EXPORT_SYMBOL_GPL(ip_set_extensions); + +static inline bool +add_extension(enum ip_set_ext_id id, u32 flags, struct nlattr *tb[]) +{ + return ip_set_extensions[id].flag ? + (flags & ip_set_extensions[id].flag) : + !!tb[IPSET_ATTR_TIMEOUT]; +} + +size_t +ip_set_elem_len(struct ip_set *set, struct nlattr *tb[], size_t len) +{ + enum ip_set_ext_id id; + size_t offset = 0; + u32 cadt_flags = 0; + + if (tb[IPSET_ATTR_CADT_FLAGS]) + cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + for (id = 0; id < IPSET_EXT_ID_MAX; id++) { + if (!add_extension(id, cadt_flags, tb)) + continue; + offset += ALIGN(len + offset, ip_set_extensions[id].align); + set->offset[id] = offset; + set->extensions |= ip_set_extensions[id].type; + offset += ip_set_extensions[id].len; + } + return len + offset; +} +EXPORT_SYMBOL_GPL(ip_set_elem_len); + int ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], struct ip_set_ext *ext) @@ -334,6 +399,12 @@ ip_set_get_extensions(struct ip_set *set, struct nlattr *tb[], ext->packets = be64_to_cpu(nla_get_be64( tb[IPSET_ATTR_PACKETS])); } + if (tb[IPSET_ATTR_COMMENT]) { + if (!(set->extensions & IPSET_EXT_COMMENT)) + return -IPSET_ERR_COMMENT; + ext->comment = ip_set_comment_uget(tb[IPSET_ATTR_COMMENT]); + } + return 0; } EXPORT_SYMBOL_GPL(ip_set_get_extensions); @@ -374,13 +445,14 @@ __ip_set_put(struct ip_set *set) */ static inline struct ip_set * -ip_set_rcu_get(ip_set_id_t index) +ip_set_rcu_get(struct net *net, ip_set_id_t index) { struct ip_set *set; + struct ip_set_net *inst = ip_set_pernet(net); rcu_read_lock(); /* ip_set_list itself needs to be protected */ - set = rcu_dereference(ip_set_list)[index]; + set = rcu_dereference(inst->ip_set_list)[index]; rcu_read_unlock(); return set; @@ -390,7 +462,8 @@ int ip_set_test(ip_set_id_t index, const struct sk_buff *skb, const struct xt_action_param *par, struct ip_set_adt_opt *opt) { - struct ip_set *set = ip_set_rcu_get(index); + struct ip_set *set = ip_set_rcu_get( + dev_net(par->in ? par->in : par->out), index); int ret = 0; BUG_ON(set == NULL); @@ -428,7 +501,8 @@ int ip_set_add(ip_set_id_t index, const struct sk_buff *skb, const struct xt_action_param *par, struct ip_set_adt_opt *opt) { - struct ip_set *set = ip_set_rcu_get(index); + struct ip_set *set = ip_set_rcu_get( + dev_net(par->in ? par->in : par->out), index); int ret; BUG_ON(set == NULL); @@ -450,7 +524,8 @@ int ip_set_del(ip_set_id_t index, const struct sk_buff *skb, const struct xt_action_param *par, struct ip_set_adt_opt *opt) { - struct ip_set *set = ip_set_rcu_get(index); + struct ip_set *set = ip_set_rcu_get( + dev_net(par->in ? par->in : par->out), index); int ret = 0; BUG_ON(set == NULL); @@ -474,14 +549,15 @@ EXPORT_SYMBOL_GPL(ip_set_del); * */ ip_set_id_t -ip_set_get_byname(const char *name, struct ip_set **set) +ip_set_get_byname(struct net *net, const char *name, struct ip_set **set) { ip_set_id_t i, index = IPSET_INVALID_ID; struct ip_set *s; + struct ip_set_net *inst = ip_set_pernet(net); rcu_read_lock(); - for (i = 0; i < ip_set_max; i++) { - s = rcu_dereference(ip_set_list)[i]; + for (i = 0; i < inst->ip_set_max; i++) { + s = rcu_dereference(inst->ip_set_list)[i]; if (s != NULL && STREQ(s->name, name)) { __ip_set_get(s); index = i; @@ -501,17 +577,26 @@ EXPORT_SYMBOL_GPL(ip_set_get_byname); * to be valid, after calling this function. * */ -void -ip_set_put_byindex(ip_set_id_t index) + +static inline void +__ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index) { struct ip_set *set; rcu_read_lock(); - set = rcu_dereference(ip_set_list)[index]; + set = rcu_dereference(inst->ip_set_list)[index]; if (set != NULL) __ip_set_put(set); rcu_read_unlock(); } + +void +ip_set_put_byindex(struct net *net, ip_set_id_t index) +{ + struct ip_set_net *inst = ip_set_pernet(net); + + __ip_set_put_byindex(inst, index); +} EXPORT_SYMBOL_GPL(ip_set_put_byindex); /* @@ -522,9 +607,9 @@ EXPORT_SYMBOL_GPL(ip_set_put_byindex); * */ const char * -ip_set_name_byindex(ip_set_id_t index) +ip_set_name_byindex(struct net *net, ip_set_id_t index) { - const struct ip_set *set = ip_set_rcu_get(index); + const struct ip_set *set = ip_set_rcu_get(net, index); BUG_ON(set == NULL); BUG_ON(set->ref == 0); @@ -546,14 +631,15 @@ EXPORT_SYMBOL_GPL(ip_set_name_byindex); * The nfnl mutex is used in the function. */ ip_set_id_t -ip_set_nfnl_get(const char *name) +ip_set_nfnl_get(struct net *net, const char *name) { ip_set_id_t i, index = IPSET_INVALID_ID; struct ip_set *s; + struct ip_set_net *inst = ip_set_pernet(net); nfnl_lock(NFNL_SUBSYS_IPSET); - for (i = 0; i < ip_set_max; i++) { - s = nfnl_set(i); + for (i = 0; i < inst->ip_set_max; i++) { + s = nfnl_set(inst, i); if (s != NULL && STREQ(s->name, name)) { __ip_set_get(s); index = i; @@ -573,15 +659,16 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get); * The nfnl mutex is used in the function. */ ip_set_id_t -ip_set_nfnl_get_byindex(ip_set_id_t index) +ip_set_nfnl_get_byindex(struct net *net, ip_set_id_t index) { struct ip_set *set; + struct ip_set_net *inst = ip_set_pernet(net); - if (index > ip_set_max) + if (index > inst->ip_set_max) return IPSET_INVALID_ID; nfnl_lock(NFNL_SUBSYS_IPSET); - set = nfnl_set(index); + set = nfnl_set(inst, index); if (set) __ip_set_get(set); else @@ -600,13 +687,17 @@ EXPORT_SYMBOL_GPL(ip_set_nfnl_get_byindex); * The nfnl mutex is used in the function. */ void -ip_set_nfnl_put(ip_set_id_t index) +ip_set_nfnl_put(struct net *net, ip_set_id_t index) { struct ip_set *set; + struct ip_set_net *inst = ip_set_pernet(net); + nfnl_lock(NFNL_SUBSYS_IPSET); - set = nfnl_set(index); - if (set != NULL) - __ip_set_put(set); + if (!inst->is_deleted) { /* already deleted from ip_set_net_exit() */ + set = nfnl_set(inst, index); + if (set != NULL) + __ip_set_put(set); + } nfnl_unlock(NFNL_SUBSYS_IPSET); } EXPORT_SYMBOL_GPL(ip_set_nfnl_put); @@ -664,14 +755,14 @@ static const struct nla_policy ip_set_create_policy[IPSET_ATTR_CMD_MAX + 1] = { }; static struct ip_set * -find_set_and_id(const char *name, ip_set_id_t *id) +find_set_and_id(struct ip_set_net *inst, const char *name, ip_set_id_t *id) { struct ip_set *set = NULL; ip_set_id_t i; *id = IPSET_INVALID_ID; - for (i = 0; i < ip_set_max; i++) { - set = nfnl_set(i); + for (i = 0; i < inst->ip_set_max; i++) { + set = nfnl_set(inst, i); if (set != NULL && STREQ(set->name, name)) { *id = i; break; @@ -681,22 +772,23 @@ find_set_and_id(const char *name, ip_set_id_t *id) } static inline struct ip_set * -find_set(const char *name) +find_set(struct ip_set_net *inst, const char *name) { ip_set_id_t id; - return find_set_and_id(name, &id); + return find_set_and_id(inst, name, &id); } static int -find_free_id(const char *name, ip_set_id_t *index, struct ip_set **set) +find_free_id(struct ip_set_net *inst, const char *name, ip_set_id_t *index, + struct ip_set **set) { struct ip_set *s; ip_set_id_t i; *index = IPSET_INVALID_ID; - for (i = 0; i < ip_set_max; i++) { - s = nfnl_set(i); + for (i = 0; i < inst->ip_set_max; i++) { + s = nfnl_set(inst, i); if (s == NULL) { if (*index == IPSET_INVALID_ID) *index = i; @@ -725,6 +817,8 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { + struct net *net = sock_net(ctnl); + struct ip_set_net *inst = ip_set_pernet(net); struct ip_set *set, *clash = NULL; ip_set_id_t index = IPSET_INVALID_ID; struct nlattr *tb[IPSET_ATTR_CREATE_MAX+1] = {}; @@ -783,7 +877,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, goto put_out; } - ret = set->type->create(set, tb, flags); + ret = set->type->create(net, set, tb, flags); if (ret != 0) goto put_out; @@ -794,7 +888,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, * by the nfnl mutex. Find the first free index in ip_set_list * and check clashing. */ - ret = find_free_id(set->name, &index, &clash); + ret = find_free_id(inst, set->name, &index, &clash); if (ret == -EEXIST) { /* If this is the same set and requested, ignore error */ if ((flags & IPSET_FLAG_EXIST) && @@ -807,9 +901,9 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, goto cleanup; } else if (ret == -IPSET_ERR_MAX_SETS) { struct ip_set **list, **tmp; - ip_set_id_t i = ip_set_max + IP_SET_INC; + ip_set_id_t i = inst->ip_set_max + IP_SET_INC; - if (i < ip_set_max || i == IPSET_INVALID_ID) + if (i < inst->ip_set_max || i == IPSET_INVALID_ID) /* Wraparound */ goto cleanup; @@ -817,14 +911,14 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, if (!list) goto cleanup; /* nfnl mutex is held, both lists are valid */ - tmp = nfnl_dereference(ip_set_list); - memcpy(list, tmp, sizeof(struct ip_set *) * ip_set_max); - rcu_assign_pointer(ip_set_list, list); + tmp = nfnl_dereference(inst->ip_set_list); + memcpy(list, tmp, sizeof(struct ip_set *) * inst->ip_set_max); + rcu_assign_pointer(inst->ip_set_list, list); /* Make sure all current packets have passed through */ synchronize_net(); /* Use new list */ - index = ip_set_max; - ip_set_max = i; + index = inst->ip_set_max; + inst->ip_set_max = i; kfree(tmp); ret = 0; } else if (ret) @@ -834,7 +928,7 @@ ip_set_create(struct sock *ctnl, struct sk_buff *skb, * Finally! Add our shiny new set to the list, and be done. */ pr_debug("create: '%s' created with index %u!\n", set->name, index); - nfnl_set(index) = set; + nfnl_set(inst, index) = set; return ret; @@ -857,12 +951,12 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = { }; static void -ip_set_destroy_set(ip_set_id_t index) +ip_set_destroy_set(struct ip_set_net *inst, ip_set_id_t index) { - struct ip_set *set = nfnl_set(index); + struct ip_set *set = nfnl_set(inst, index); pr_debug("set: %s\n", set->name); - nfnl_set(index) = NULL; + nfnl_set(inst, index) = NULL; /* Must call it without holding any lock */ set->variant->destroy(set); @@ -875,6 +969,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { + struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *s; ip_set_id_t i; int ret = 0; @@ -894,21 +989,22 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, */ read_lock_bh(&ip_set_ref_lock); if (!attr[IPSET_ATTR_SETNAME]) { - for (i = 0; i < ip_set_max; i++) { - s = nfnl_set(i); + for (i = 0; i < inst->ip_set_max; i++) { + s = nfnl_set(inst, i); if (s != NULL && s->ref) { ret = -IPSET_ERR_BUSY; goto out; } } read_unlock_bh(&ip_set_ref_lock); - for (i = 0; i < ip_set_max; i++) { - s = nfnl_set(i); + for (i = 0; i < inst->ip_set_max; i++) { + s = nfnl_set(inst, i); if (s != NULL) - ip_set_destroy_set(i); + ip_set_destroy_set(inst, i); } } else { - s = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &i); + s = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]), + &i); if (s == NULL) { ret = -ENOENT; goto out; @@ -918,7 +1014,7 @@ ip_set_destroy(struct sock *ctnl, struct sk_buff *skb, } read_unlock_bh(&ip_set_ref_lock); - ip_set_destroy_set(i); + ip_set_destroy_set(inst, i); } return 0; out: @@ -943,6 +1039,7 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { + struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *s; ip_set_id_t i; @@ -950,13 +1047,13 @@ ip_set_flush(struct sock *ctnl, struct sk_buff *skb, return -IPSET_ERR_PROTOCOL; if (!attr[IPSET_ATTR_SETNAME]) { - for (i = 0; i < ip_set_max; i++) { - s = nfnl_set(i); + for (i = 0; i < inst->ip_set_max; i++) { + s = nfnl_set(inst, i); if (s != NULL) ip_set_flush_set(s); } } else { - s = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + s = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); if (s == NULL) return -ENOENT; @@ -982,6 +1079,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { + struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *set, *s; const char *name2; ip_set_id_t i; @@ -992,7 +1090,7 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_SETNAME2] == NULL)) return -IPSET_ERR_PROTOCOL; - set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1003,8 +1101,8 @@ ip_set_rename(struct sock *ctnl, struct sk_buff *skb, } name2 = nla_data(attr[IPSET_ATTR_SETNAME2]); - for (i = 0; i < ip_set_max; i++) { - s = nfnl_set(i); + for (i = 0; i < inst->ip_set_max; i++) { + s = nfnl_set(inst, i); if (s != NULL && STREQ(s->name, name2)) { ret = -IPSET_ERR_EXIST_SETNAME2; goto out; @@ -1031,6 +1129,7 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { + struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *from, *to; ip_set_id_t from_id, to_id; char from_name[IPSET_MAXNAMELEN]; @@ -1040,11 +1139,13 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_SETNAME2] == NULL)) return -IPSET_ERR_PROTOCOL; - from = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME]), &from_id); + from = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME]), + &from_id); if (from == NULL) return -ENOENT; - to = find_set_and_id(nla_data(attr[IPSET_ATTR_SETNAME2]), &to_id); + to = find_set_and_id(inst, nla_data(attr[IPSET_ATTR_SETNAME2]), + &to_id); if (to == NULL) return -IPSET_ERR_EXIST_SETNAME2; @@ -1061,8 +1162,8 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, write_lock_bh(&ip_set_ref_lock); swap(from->ref, to->ref); - nfnl_set(from_id) = to; - nfnl_set(to_id) = from; + nfnl_set(inst, from_id) = to; + nfnl_set(inst, to_id) = from; write_unlock_bh(&ip_set_ref_lock); return 0; @@ -1081,9 +1182,12 @@ ip_set_swap(struct sock *ctnl, struct sk_buff *skb, static int ip_set_dump_done(struct netlink_callback *cb) { - if (cb->args[2]) { - pr_debug("release set %s\n", nfnl_set(cb->args[1])->name); - ip_set_put_byindex((ip_set_id_t) cb->args[1]); + struct ip_set_net *inst = (struct ip_set_net *)cb->args[IPSET_CB_NET]; + if (cb->args[IPSET_CB_ARG0]) { + pr_debug("release set %s\n", + nfnl_set(inst, cb->args[IPSET_CB_INDEX])->name); + __ip_set_put_byindex(inst, + (ip_set_id_t) cb->args[IPSET_CB_INDEX]); } return 0; } @@ -1101,7 +1205,7 @@ dump_attrs(struct nlmsghdr *nlh) } static int -dump_init(struct netlink_callback *cb) +dump_init(struct netlink_callback *cb, struct ip_set_net *inst) { struct nlmsghdr *nlh = nlmsg_hdr(cb->skb); int min_len = nlmsg_total_size(sizeof(struct nfgenmsg)); @@ -1114,21 +1218,22 @@ dump_init(struct netlink_callback *cb) nla_parse(cda, IPSET_ATTR_CMD_MAX, attr, nlh->nlmsg_len - min_len, ip_set_setname_policy); - /* cb->args[0] : dump single set/all sets - * [1] : set index - * [..]: type specific + /* cb->args[IPSET_CB_NET]: net namespace + * [IPSET_CB_DUMP]: dump single set/all sets + * [IPSET_CB_INDEX]: set index + * [IPSET_CB_ARG0]: type specific */ if (cda[IPSET_ATTR_SETNAME]) { struct ip_set *set; - set = find_set_and_id(nla_data(cda[IPSET_ATTR_SETNAME]), + set = find_set_and_id(inst, nla_data(cda[IPSET_ATTR_SETNAME]), &index); if (set == NULL) return -ENOENT; dump_type = DUMP_ONE; - cb->args[1] = index; + cb->args[IPSET_CB_INDEX] = index; } else dump_type = DUMP_ALL; @@ -1136,7 +1241,8 @@ dump_init(struct netlink_callback *cb) u32 f = ip_set_get_h32(cda[IPSET_ATTR_FLAGS]); dump_type |= (f << 16); } - cb->args[0] = dump_type; + cb->args[IPSET_CB_NET] = (unsigned long)inst; + cb->args[IPSET_CB_DUMP] = dump_type; return 0; } @@ -1148,11 +1254,12 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) struct ip_set *set = NULL; struct nlmsghdr *nlh = NULL; unsigned int flags = NETLINK_CB(cb->skb).portid ? NLM_F_MULTI : 0; + struct ip_set_net *inst = ip_set_pernet(sock_net(skb->sk)); u32 dump_type, dump_flags; int ret = 0; - if (!cb->args[0]) { - ret = dump_init(cb); + if (!cb->args[IPSET_CB_DUMP]) { + ret = dump_init(cb, inst); if (ret < 0) { nlh = nlmsg_hdr(cb->skb); /* We have to create and send the error message @@ -1163,18 +1270,19 @@ ip_set_dump_start(struct sk_buff *skb, struct netlink_callback *cb) } } - if (cb->args[1] >= ip_set_max) + if (cb->args[IPSET_CB_INDEX] >= inst->ip_set_max) goto out; - dump_type = DUMP_TYPE(cb->args[0]); - dump_flags = DUMP_FLAGS(cb->args[0]); - max = dump_type == DUMP_ONE ? cb->args[1] + 1 : ip_set_max; + dump_type = DUMP_TYPE(cb->args[IPSET_CB_DUMP]); + dump_flags = DUMP_FLAGS(cb->args[IPSET_CB_DUMP]); + max = dump_type == DUMP_ONE ? cb->args[IPSET_CB_INDEX] + 1 + : inst->ip_set_max; dump_last: - pr_debug("args[0]: %u %u args[1]: %ld\n", - dump_type, dump_flags, cb->args[1]); - for (; cb->args[1] < max; cb->args[1]++) { - index = (ip_set_id_t) cb->args[1]; - set = nfnl_set(index); + pr_debug("dump type, flag: %u %u index: %ld\n", + dump_type, dump_flags, cb->args[IPSET_CB_INDEX]); + for (; cb->args[IPSET_CB_INDEX] < max; cb->args[IPSET_CB_INDEX]++) { + index = (ip_set_id_t) cb->args[IPSET_CB_INDEX]; + set = nfnl_set(inst, index); if (set == NULL) { if (dump_type == DUMP_ONE) { ret = -ENOENT; @@ -1190,7 +1298,7 @@ dump_last: !!(set->type->features & IPSET_DUMP_LAST))) continue; pr_debug("List set: %s\n", set->name); - if (!cb->args[2]) { + if (!cb->args[IPSET_CB_ARG0]) { /* Start listing: make sure set won't be destroyed */ pr_debug("reference set\n"); __ip_set_get(set); @@ -1207,7 +1315,7 @@ dump_last: goto nla_put_failure; if (dump_flags & IPSET_FLAG_LIST_SETNAME) goto next_set; - switch (cb->args[2]) { + switch (cb->args[IPSET_CB_ARG0]) { case 0: /* Core header data */ if (nla_put_string(skb, IPSET_ATTR_TYPENAME, @@ -1227,7 +1335,7 @@ dump_last: read_lock_bh(&set->lock); ret = set->variant->list(set, skb, cb); read_unlock_bh(&set->lock); - if (!cb->args[2]) + if (!cb->args[IPSET_CB_ARG0]) /* Set is done, proceed with next one */ goto next_set; goto release_refcount; @@ -1236,8 +1344,8 @@ dump_last: /* If we dump all sets, continue with dumping last ones */ if (dump_type == DUMP_ALL) { dump_type = DUMP_LAST; - cb->args[0] = dump_type | (dump_flags << 16); - cb->args[1] = 0; + cb->args[IPSET_CB_DUMP] = dump_type | (dump_flags << 16); + cb->args[IPSET_CB_INDEX] = 0; goto dump_last; } goto out; @@ -1246,15 +1354,15 @@ nla_put_failure: ret = -EFAULT; next_set: if (dump_type == DUMP_ONE) - cb->args[1] = IPSET_INVALID_ID; + cb->args[IPSET_CB_INDEX] = IPSET_INVALID_ID; else - cb->args[1]++; + cb->args[IPSET_CB_INDEX]++; release_refcount: /* If there was an error or set is done, release set */ - if (ret || !cb->args[2]) { - pr_debug("release set %s\n", nfnl_set(index)->name); - ip_set_put_byindex(index); - cb->args[2] = 0; + if (ret || !cb->args[IPSET_CB_ARG0]) { + pr_debug("release set %s\n", nfnl_set(inst, index)->name); + __ip_set_put_byindex(inst, index); + cb->args[IPSET_CB_ARG0] = 0; } out: if (nlh) { @@ -1356,6 +1464,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { + struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *set; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; const struct nlattr *nla; @@ -1374,7 +1483,7 @@ ip_set_uadd(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_LINENO] == NULL)))) return -IPSET_ERR_PROTOCOL; - set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1410,6 +1519,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { + struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *set; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; const struct nlattr *nla; @@ -1428,7 +1538,7 @@ ip_set_udel(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_LINENO] == NULL)))) return -IPSET_ERR_PROTOCOL; - set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1464,6 +1574,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { + struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); struct ip_set *set; struct nlattr *tb[IPSET_ATTR_ADT_MAX+1] = {}; int ret = 0; @@ -1474,7 +1585,7 @@ ip_set_utest(struct sock *ctnl, struct sk_buff *skb, !flag_nested(attr[IPSET_ATTR_DATA]))) return -IPSET_ERR_PROTOCOL; - set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1499,6 +1610,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const attr[]) { + struct ip_set_net *inst = ip_set_pernet(sock_net(ctnl)); const struct ip_set *set; struct sk_buff *skb2; struct nlmsghdr *nlh2; @@ -1508,7 +1620,7 @@ ip_set_header(struct sock *ctnl, struct sk_buff *skb, attr[IPSET_ATTR_SETNAME] == NULL)) return -IPSET_ERR_PROTOCOL; - set = find_set(nla_data(attr[IPSET_ATTR_SETNAME])); + set = find_set(inst, nla_data(attr[IPSET_ATTR_SETNAME])); if (set == NULL) return -ENOENT; @@ -1733,8 +1845,10 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) unsigned int *op; void *data; int copylen = *len, ret = 0; + struct net *net = sock_net(sk); + struct ip_set_net *inst = ip_set_pernet(net); - if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) return -EPERM; if (optval != SO_IP_SET) return -EBADF; @@ -1783,22 +1897,39 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len) } req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; nfnl_lock(NFNL_SUBSYS_IPSET); - find_set_and_id(req_get->set.name, &id); + find_set_and_id(inst, req_get->set.name, &id); req_get->set.index = id; nfnl_unlock(NFNL_SUBSYS_IPSET); goto copy; } + case IP_SET_OP_GET_FNAME: { + struct ip_set_req_get_set_family *req_get = data; + ip_set_id_t id; + + if (*len != sizeof(struct ip_set_req_get_set_family)) { + ret = -EINVAL; + goto done; + } + req_get->set.name[IPSET_MAXNAMELEN - 1] = '\0'; + nfnl_lock(NFNL_SUBSYS_IPSET); + find_set_and_id(inst, req_get->set.name, &id); + req_get->set.index = id; + if (id != IPSET_INVALID_ID) + req_get->family = nfnl_set(inst, id)->family; + nfnl_unlock(NFNL_SUBSYS_IPSET); + goto copy; + } case IP_SET_OP_GET_BYINDEX: { struct ip_set_req_get_set *req_get = data; struct ip_set *set; if (*len != sizeof(struct ip_set_req_get_set) || - req_get->set.index >= ip_set_max) { + req_get->set.index >= inst->ip_set_max) { ret = -EINVAL; goto done; } nfnl_lock(NFNL_SUBSYS_IPSET); - set = nfnl_set(req_get->set.index); + set = nfnl_set(inst, req_get->set.index); strncpy(req_get->set.name, set ? set->name : "", IPSET_MAXNAMELEN); nfnl_unlock(NFNL_SUBSYS_IPSET); @@ -1827,49 +1958,81 @@ static struct nf_sockopt_ops so_set __read_mostly = { .owner = THIS_MODULE, }; -static int __init -ip_set_init(void) +static int __net_init +ip_set_net_init(struct net *net) { + struct ip_set_net *inst = ip_set_pernet(net); struct ip_set **list; - int ret; - if (max_sets) - ip_set_max = max_sets; - if (ip_set_max >= IPSET_INVALID_ID) - ip_set_max = IPSET_INVALID_ID - 1; + inst->ip_set_max = max_sets ? max_sets : CONFIG_IP_SET_MAX; + if (inst->ip_set_max >= IPSET_INVALID_ID) + inst->ip_set_max = IPSET_INVALID_ID - 1; - list = kzalloc(sizeof(struct ip_set *) * ip_set_max, GFP_KERNEL); + list = kzalloc(sizeof(struct ip_set *) * inst->ip_set_max, GFP_KERNEL); if (!list) return -ENOMEM; + inst->is_deleted = 0; + rcu_assign_pointer(inst->ip_set_list, list); + pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL); + return 0; +} - rcu_assign_pointer(ip_set_list, list); - ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); +static void __net_exit +ip_set_net_exit(struct net *net) +{ + struct ip_set_net *inst = ip_set_pernet(net); + + struct ip_set *set = NULL; + ip_set_id_t i; + + inst->is_deleted = 1; /* flag for ip_set_nfnl_put */ + + for (i = 0; i < inst->ip_set_max; i++) { + set = nfnl_set(inst, i); + if (set != NULL) + ip_set_destroy_set(inst, i); + } + kfree(rcu_dereference_protected(inst->ip_set_list, 1)); +} + +static struct pernet_operations ip_set_net_ops = { + .init = ip_set_net_init, + .exit = ip_set_net_exit, + .id = &ip_set_net_id, + .size = sizeof(struct ip_set_net) +}; + + +static int __init +ip_set_init(void) +{ + int ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); if (ret != 0) { pr_err("ip_set: cannot register with nfnetlink.\n"); - kfree(list); return ret; } ret = nf_register_sockopt(&so_set); if (ret != 0) { pr_err("SO_SET registry failed: %d\n", ret); nfnetlink_subsys_unregister(&ip_set_netlink_subsys); - kfree(list); return ret; } - - pr_notice("ip_set: protocol %u\n", IPSET_PROTOCOL); + ret = register_pernet_subsys(&ip_set_net_ops); + if (ret) { + pr_err("ip_set: cannot register pernet_subsys.\n"); + nf_unregister_sockopt(&so_set); + nfnetlink_subsys_unregister(&ip_set_netlink_subsys); + return ret; + } return 0; } static void __exit ip_set_fini(void) { - struct ip_set **list = rcu_dereference_protected(ip_set_list, 1); - - /* There can't be any existing set */ + unregister_pernet_subsys(&ip_set_net_ops); nf_unregister_sockopt(&so_set); nfnetlink_subsys_unregister(&ip_set_netlink_subsys); - kfree(list); pr_debug("these are the famous last words\n"); } diff --git a/net/netfilter/ipset/ip_set_getport.c b/net/netfilter/ipset/ip_set_getport.c index dac156f819ac2f2fe25c876d800aadd2c47c0752..29fb01ddff93b0a0da7ad1dc3691a141376f5007 100644 --- a/net/netfilter/ipset/ip_set_getport.c +++ b/net/netfilter/ipset/ip_set_getport.c @@ -102,9 +102,25 @@ ip_set_get_ip4_port(const struct sk_buff *skb, bool src, int protocol = iph->protocol; /* See comments at tcp_match in ip_tables.c */ - if (protocol <= 0 || (ntohs(iph->frag_off) & IP_OFFSET)) + if (protocol <= 0) return false; + if (ntohs(iph->frag_off) & IP_OFFSET) + switch (protocol) { + case IPPROTO_TCP: + case IPPROTO_SCTP: + case IPPROTO_UDP: + case IPPROTO_UDPLITE: + case IPPROTO_ICMP: + /* Port info not available for fragment offset > 0 */ + return false; + default: + /* Other protocols doesn't have ports, + so we can match fragments */ + *proto = protocol; + return true; + } + return get_port(skb, protocol, protooff, src, port, proto); } EXPORT_SYMBOL_GPL(ip_set_get_ip4_port); diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 707bc520d629f311dd9978d2e0bf222719f71c82..be6932ad3a8626d6c06e99091b32f540b7afd338 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -15,8 +15,7 @@ #define rcu_dereference_bh(p) rcu_dereference(p) #endif -#define CONCAT(a, b) a##b -#define TOKEN(a, b) CONCAT(a, b) +#define rcu_dereference_bh_nfnl(p) rcu_dereference_bh_check(p, 1) /* Hashing which uses arrays to resolve clashing. The hash table is resized * (doubled) when searching becomes too long. @@ -78,10 +77,14 @@ struct htable { #define hbucket(h, i) (&((h)->bucket[i])) +#ifndef IPSET_NET_COUNT +#define IPSET_NET_COUNT 1 +#endif + /* Book-keeping of the prefixes added to the set */ struct net_prefixes { - u8 cidr; /* the different cidr values in the set */ - u32 nets; /* number of elements per cidr */ + u32 nets[IPSET_NET_COUNT]; /* number of elements per cidr */ + u8 cidr[IPSET_NET_COUNT]; /* the different cidr values in the set */ }; /* Compute the hash table size */ @@ -114,23 +117,6 @@ htable_bits(u32 hashsize) return bits; } -/* Destroy the hashtable part of the set */ -static void -ahash_destroy(struct htable *t) -{ - struct hbucket *n; - u32 i; - - for (i = 0; i < jhash_size(t->htable_bits); i++) { - n = hbucket(t, i); - if (n->size) - /* FIXME: use slab cache */ - kfree(n->value); - } - - ip_set_free(t); -} - static int hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) { @@ -156,30 +142,30 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) } #ifdef IP_SET_HASH_WITH_NETS +#if IPSET_NET_COUNT > 1 +#define __CIDR(cidr, i) (cidr[i]) +#else +#define __CIDR(cidr, i) (cidr) +#endif #ifdef IP_SET_HASH_WITH_NETS_PACKED /* When cidr is packed with nomatch, cidr - 1 is stored in the entry */ -#define CIDR(cidr) (cidr + 1) +#define CIDR(cidr, i) (__CIDR(cidr, i) + 1) #else -#define CIDR(cidr) (cidr) +#define CIDR(cidr, i) (__CIDR(cidr, i)) #endif #define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128) #ifdef IP_SET_HASH_WITH_MULTI -#define NETS_LENGTH(family) (SET_HOST_MASK(family) + 1) +#define NLEN(family) (SET_HOST_MASK(family) + 1) #else -#define NETS_LENGTH(family) SET_HOST_MASK(family) +#define NLEN(family) SET_HOST_MASK(family) #endif #else -#define NETS_LENGTH(family) 0 +#define NLEN(family) 0 #endif /* IP_SET_HASH_WITH_NETS */ -#define ext_timeout(e, h) \ -(unsigned long *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_TIMEOUT]) -#define ext_counter(e, h) \ -(struct ip_set_counter *)(((void *)(e)) + (h)->offset[IPSET_OFFSET_COUNTER]) - #endif /* _IP_SET_HASH_GEN_H */ /* Family dependent templates */ @@ -194,6 +180,8 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) #undef mtype_data_next #undef mtype_elem +#undef mtype_ahash_destroy +#undef mtype_ext_cleanup #undef mtype_add_cidr #undef mtype_del_cidr #undef mtype_ahash_memsize @@ -220,41 +208,43 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) #undef HKEY -#define mtype_data_equal TOKEN(MTYPE, _data_equal) +#define mtype_data_equal IPSET_TOKEN(MTYPE, _data_equal) #ifdef IP_SET_HASH_WITH_NETS -#define mtype_do_data_match TOKEN(MTYPE, _do_data_match) +#define mtype_do_data_match IPSET_TOKEN(MTYPE, _do_data_match) #else #define mtype_do_data_match(d) 1 #endif -#define mtype_data_set_flags TOKEN(MTYPE, _data_set_flags) -#define mtype_data_reset_flags TOKEN(MTYPE, _data_reset_flags) -#define mtype_data_netmask TOKEN(MTYPE, _data_netmask) -#define mtype_data_list TOKEN(MTYPE, _data_list) -#define mtype_data_next TOKEN(MTYPE, _data_next) -#define mtype_elem TOKEN(MTYPE, _elem) -#define mtype_add_cidr TOKEN(MTYPE, _add_cidr) -#define mtype_del_cidr TOKEN(MTYPE, _del_cidr) -#define mtype_ahash_memsize TOKEN(MTYPE, _ahash_memsize) -#define mtype_flush TOKEN(MTYPE, _flush) -#define mtype_destroy TOKEN(MTYPE, _destroy) -#define mtype_gc_init TOKEN(MTYPE, _gc_init) -#define mtype_same_set TOKEN(MTYPE, _same_set) -#define mtype_kadt TOKEN(MTYPE, _kadt) -#define mtype_uadt TOKEN(MTYPE, _uadt) +#define mtype_data_set_flags IPSET_TOKEN(MTYPE, _data_set_flags) +#define mtype_data_reset_elem IPSET_TOKEN(MTYPE, _data_reset_elem) +#define mtype_data_reset_flags IPSET_TOKEN(MTYPE, _data_reset_flags) +#define mtype_data_netmask IPSET_TOKEN(MTYPE, _data_netmask) +#define mtype_data_list IPSET_TOKEN(MTYPE, _data_list) +#define mtype_data_next IPSET_TOKEN(MTYPE, _data_next) +#define mtype_elem IPSET_TOKEN(MTYPE, _elem) +#define mtype_ahash_destroy IPSET_TOKEN(MTYPE, _ahash_destroy) +#define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup) +#define mtype_add_cidr IPSET_TOKEN(MTYPE, _add_cidr) +#define mtype_del_cidr IPSET_TOKEN(MTYPE, _del_cidr) +#define mtype_ahash_memsize IPSET_TOKEN(MTYPE, _ahash_memsize) +#define mtype_flush IPSET_TOKEN(MTYPE, _flush) +#define mtype_destroy IPSET_TOKEN(MTYPE, _destroy) +#define mtype_gc_init IPSET_TOKEN(MTYPE, _gc_init) +#define mtype_same_set IPSET_TOKEN(MTYPE, _same_set) +#define mtype_kadt IPSET_TOKEN(MTYPE, _kadt) +#define mtype_uadt IPSET_TOKEN(MTYPE, _uadt) #define mtype MTYPE -#define mtype_elem TOKEN(MTYPE, _elem) -#define mtype_add TOKEN(MTYPE, _add) -#define mtype_del TOKEN(MTYPE, _del) -#define mtype_test_cidrs TOKEN(MTYPE, _test_cidrs) -#define mtype_test TOKEN(MTYPE, _test) -#define mtype_expire TOKEN(MTYPE, _expire) -#define mtype_resize TOKEN(MTYPE, _resize) -#define mtype_head TOKEN(MTYPE, _head) -#define mtype_list TOKEN(MTYPE, _list) -#define mtype_gc TOKEN(MTYPE, _gc) -#define mtype_variant TOKEN(MTYPE, _variant) -#define mtype_data_match TOKEN(MTYPE, _data_match) +#define mtype_add IPSET_TOKEN(MTYPE, _add) +#define mtype_del IPSET_TOKEN(MTYPE, _del) +#define mtype_test_cidrs IPSET_TOKEN(MTYPE, _test_cidrs) +#define mtype_test IPSET_TOKEN(MTYPE, _test) +#define mtype_expire IPSET_TOKEN(MTYPE, _expire) +#define mtype_resize IPSET_TOKEN(MTYPE, _resize) +#define mtype_head IPSET_TOKEN(MTYPE, _head) +#define mtype_list IPSET_TOKEN(MTYPE, _list) +#define mtype_gc IPSET_TOKEN(MTYPE, _gc) +#define mtype_variant IPSET_TOKEN(MTYPE, _variant) +#define mtype_data_match IPSET_TOKEN(MTYPE, _data_match) #ifndef HKEY_DATALEN #define HKEY_DATALEN sizeof(struct mtype_elem) @@ -269,13 +259,10 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize) /* The generic hash structure */ struct htype { - struct htable *table; /* the hash table */ + struct htable __rcu *table; /* the hash table */ u32 maxelem; /* max elements in the hash */ u32 elements; /* current element (vs timeout) */ u32 initval; /* random jhash init value */ - u32 timeout; /* timeout value, if enabled */ - size_t dsize; /* data struct size */ - size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ struct timer_list gc; /* garbage collection when timeout enabled */ struct mtype_elem next; /* temporary storage for uadd */ #ifdef IP_SET_HASH_WITH_MULTI @@ -297,49 +284,49 @@ struct htype { /* Network cidr size book keeping when the hash stores different * sized networks */ static void -mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length) +mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) { int i, j; /* Add in increasing prefix order, so larger cidr first */ - for (i = 0, j = -1; i < nets_length && h->nets[i].nets; i++) { + for (i = 0, j = -1; i < nets_length && h->nets[i].nets[n]; i++) { if (j != -1) continue; - else if (h->nets[i].cidr < cidr) + else if (h->nets[i].cidr[n] < cidr) j = i; - else if (h->nets[i].cidr == cidr) { - h->nets[i].nets++; + else if (h->nets[i].cidr[n] == cidr) { + h->nets[i].nets[n]++; return; } } if (j != -1) { for (; i > j; i--) { - h->nets[i].cidr = h->nets[i - 1].cidr; - h->nets[i].nets = h->nets[i - 1].nets; + h->nets[i].cidr[n] = h->nets[i - 1].cidr[n]; + h->nets[i].nets[n] = h->nets[i - 1].nets[n]; } } - h->nets[i].cidr = cidr; - h->nets[i].nets = 1; + h->nets[i].cidr[n] = cidr; + h->nets[i].nets[n] = 1; } static void -mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length) +mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n) { u8 i, j, net_end = nets_length - 1; for (i = 0; i < nets_length; i++) { - if (h->nets[i].cidr != cidr) + if (h->nets[i].cidr[n] != cidr) continue; - if (h->nets[i].nets > 1 || i == net_end || - h->nets[i + 1].nets == 0) { - h->nets[i].nets--; + if (h->nets[i].nets[n] > 1 || i == net_end || + h->nets[i + 1].nets[n] == 0) { + h->nets[i].nets[n]--; return; } - for (j = i; j < net_end && h->nets[j].nets; j++) { - h->nets[j].cidr = h->nets[j + 1].cidr; - h->nets[j].nets = h->nets[j + 1].nets; + for (j = i; j < net_end && h->nets[j].nets[n]; j++) { + h->nets[j].cidr[n] = h->nets[j + 1].cidr[n]; + h->nets[j].nets[n] = h->nets[j + 1].nets[n]; } - h->nets[j].nets = 0; + h->nets[j].nets[n] = 0; return; } } @@ -347,10 +334,10 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length) /* Calculate the actual memory size of the set data */ static size_t -mtype_ahash_memsize(const struct htype *h, u8 nets_length) +mtype_ahash_memsize(const struct htype *h, const struct htable *t, + u8 nets_length, size_t dsize) { u32 i; - struct htable *t = h->table; size_t memsize = sizeof(*h) + sizeof(*t) #ifdef IP_SET_HASH_WITH_NETS @@ -359,35 +346,70 @@ mtype_ahash_memsize(const struct htype *h, u8 nets_length) + jhash_size(t->htable_bits) * sizeof(struct hbucket); for (i = 0; i < jhash_size(t->htable_bits); i++) - memsize += t->bucket[i].size * h->dsize; + memsize += t->bucket[i].size * dsize; return memsize; } +/* Get the ith element from the array block n */ +#define ahash_data(n, i, dsize) \ + ((struct mtype_elem *)((n)->value + ((i) * (dsize)))) + +static void +mtype_ext_cleanup(struct ip_set *set, struct hbucket *n) +{ + int i; + + for (i = 0; i < n->pos; i++) + ip_set_ext_destroy(set, ahash_data(n, i, set->dsize)); +} + /* Flush a hash type of set: destroy all elements */ static void mtype_flush(struct ip_set *set) { struct htype *h = set->data; - struct htable *t = h->table; + struct htable *t; struct hbucket *n; u32 i; + t = rcu_dereference_bh_nfnl(h->table); for (i = 0; i < jhash_size(t->htable_bits); i++) { n = hbucket(t, i); if (n->size) { + if (set->extensions & IPSET_EXT_DESTROY) + mtype_ext_cleanup(set, n); n->size = n->pos = 0; /* FIXME: use slab cache */ kfree(n->value); } } #ifdef IP_SET_HASH_WITH_NETS - memset(h->nets, 0, sizeof(struct net_prefixes) - * NETS_LENGTH(set->family)); + memset(h->nets, 0, sizeof(struct net_prefixes) * NLEN(set->family)); #endif h->elements = 0; } +/* Destroy the hashtable part of the set */ +static void +mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy) +{ + struct hbucket *n; + u32 i; + + for (i = 0; i < jhash_size(t->htable_bits); i++) { + n = hbucket(t, i); + if (n->size) { + if (set->extensions & IPSET_EXT_DESTROY && ext_destroy) + mtype_ext_cleanup(set, n); + /* FIXME: use slab cache */ + kfree(n->value); + } + } + + ip_set_free(t); +} + /* Destroy a hash type of set */ static void mtype_destroy(struct ip_set *set) @@ -397,7 +419,7 @@ mtype_destroy(struct ip_set *set) if (set->extensions & IPSET_EXT_TIMEOUT) del_timer_sync(&h->gc); - ahash_destroy(h->table); + mtype_ahash_destroy(set, rcu_dereference_bh_nfnl(h->table), true); #ifdef IP_SET_HASH_WITH_RBTREE rbtree_destroy(&h->rbtree); #endif @@ -414,10 +436,10 @@ mtype_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) init_timer(&h->gc); h->gc.data = (unsigned long) set; h->gc.function = gc; - h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; + h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; add_timer(&h->gc); pr_debug("gc initialized, run in every %u\n", - IPSET_GC_PERIOD(h->timeout)); + IPSET_GC_PERIOD(set->timeout)); } static bool @@ -428,37 +450,40 @@ mtype_same_set(const struct ip_set *a, const struct ip_set *b) /* Resizing changes htable_bits, so we ignore it */ return x->maxelem == y->maxelem && - x->timeout == y->timeout && + a->timeout == b->timeout && #ifdef IP_SET_HASH_WITH_NETMASK x->netmask == y->netmask && #endif a->extensions == b->extensions; } -/* Get the ith element from the array block n */ -#define ahash_data(n, i, dsize) \ - ((struct mtype_elem *)((n)->value + ((i) * (dsize)))) - /* Delete expired elements from the hashtable */ static void -mtype_expire(struct htype *h, u8 nets_length, size_t dsize) +mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize) { - struct htable *t = h->table; + struct htable *t; struct hbucket *n; struct mtype_elem *data; u32 i; int j; +#ifdef IP_SET_HASH_WITH_NETS + u8 k; +#endif + rcu_read_lock_bh(); + t = rcu_dereference_bh(h->table); for (i = 0; i < jhash_size(t->htable_bits); i++) { n = hbucket(t, i); for (j = 0; j < n->pos; j++) { data = ahash_data(n, j, dsize); - if (ip_set_timeout_expired(ext_timeout(data, h))) { + if (ip_set_timeout_expired(ext_timeout(data, set))) { pr_debug("expired %u/%u\n", i, j); #ifdef IP_SET_HASH_WITH_NETS - mtype_del_cidr(h, CIDR(data->cidr), - nets_length); + for (k = 0; k < IPSET_NET_COUNT; k++) + mtype_del_cidr(h, CIDR(data->cidr, k), + nets_length, k); #endif + ip_set_ext_destroy(set, data); if (j != n->pos - 1) /* Not last one */ memcpy(data, @@ -481,6 +506,7 @@ mtype_expire(struct htype *h, u8 nets_length, size_t dsize) n->value = tmp; } } + rcu_read_unlock_bh(); } static void @@ -491,10 +517,10 @@ mtype_gc(unsigned long ul_set) pr_debug("called\n"); write_lock_bh(&set->lock); - mtype_expire(h, NETS_LENGTH(set->family), h->dsize); + mtype_expire(set, h, NLEN(set->family), set->dsize); write_unlock_bh(&set->lock); - h->gc.expires = jiffies + IPSET_GC_PERIOD(h->timeout) * HZ; + h->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; add_timer(&h->gc); } @@ -505,7 +531,7 @@ static int mtype_resize(struct ip_set *set, bool retried) { struct htype *h = set->data; - struct htable *t, *orig = h->table; + struct htable *t, *orig = rcu_dereference_bh_nfnl(h->table); u8 htable_bits = orig->htable_bits; #ifdef IP_SET_HASH_WITH_NETS u8 flags; @@ -520,8 +546,7 @@ mtype_resize(struct ip_set *set, bool retried) if (SET_WITH_TIMEOUT(set) && !retried) { i = h->elements; write_lock_bh(&set->lock); - mtype_expire(set->data, NETS_LENGTH(set->family), - h->dsize); + mtype_expire(set, set->data, NLEN(set->family), set->dsize); write_unlock_bh(&set->lock); if (h->elements < i) return 0; @@ -548,25 +573,25 @@ retry: for (i = 0; i < jhash_size(orig->htable_bits); i++) { n = hbucket(orig, i); for (j = 0; j < n->pos; j++) { - data = ahash_data(n, j, h->dsize); + data = ahash_data(n, j, set->dsize); #ifdef IP_SET_HASH_WITH_NETS flags = 0; mtype_data_reset_flags(data, &flags); #endif m = hbucket(t, HKEY(data, h->initval, htable_bits)); - ret = hbucket_elem_add(m, AHASH_MAX(h), h->dsize); + ret = hbucket_elem_add(m, AHASH_MAX(h), set->dsize); if (ret < 0) { #ifdef IP_SET_HASH_WITH_NETS mtype_data_reset_flags(data, &flags); #endif read_unlock_bh(&set->lock); - ahash_destroy(t); + mtype_ahash_destroy(set, t, false); if (ret == -EAGAIN) goto retry; return ret; } - d = ahash_data(m, m->pos++, h->dsize); - memcpy(d, data, h->dsize); + d = ahash_data(m, m->pos++, set->dsize); + memcpy(d, data, set->dsize); #ifdef IP_SET_HASH_WITH_NETS mtype_data_reset_flags(d, &flags); #endif @@ -581,7 +606,7 @@ retry: pr_debug("set %s resized from %u (%p) to %u (%p)\n", set->name, orig->htable_bits, orig, t->htable_bits, t); - ahash_destroy(orig); + mtype_ahash_destroy(set, orig, false); return 0; } @@ -604,7 +629,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem) /* FIXME: when set is full, we slow down here */ - mtype_expire(h, NETS_LENGTH(set->family), h->dsize); + mtype_expire(set, h, NLEN(set->family), set->dsize); if (h->elements >= h->maxelem) { if (net_ratelimit()) @@ -618,11 +643,11 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, key = HKEY(value, h->initval, t->htable_bits); n = hbucket(t, key); for (i = 0; i < n->pos; i++) { - data = ahash_data(n, i, h->dsize); + data = ahash_data(n, i, set->dsize); if (mtype_data_equal(data, d, &multi)) { if (flag_exist || (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(data, h)))) { + ip_set_timeout_expired(ext_timeout(data, set)))) { /* Just the extensions could be overwritten */ j = i; goto reuse_slot; @@ -633,30 +658,37 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, } /* Reuse first timed out entry */ if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(data, h)) && + ip_set_timeout_expired(ext_timeout(data, set)) && j != AHASH_MAX(h) + 1) j = i; } reuse_slot: if (j != AHASH_MAX(h) + 1) { /* Fill out reused slot */ - data = ahash_data(n, j, h->dsize); + data = ahash_data(n, j, set->dsize); #ifdef IP_SET_HASH_WITH_NETS - mtype_del_cidr(h, CIDR(data->cidr), NETS_LENGTH(set->family)); - mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); + for (i = 0; i < IPSET_NET_COUNT; i++) { + mtype_del_cidr(h, CIDR(data->cidr, i), + NLEN(set->family), i); + mtype_add_cidr(h, CIDR(d->cidr, i), + NLEN(set->family), i); + } #endif + ip_set_ext_destroy(set, data); } else { /* Use/create a new slot */ TUNE_AHASH_MAX(h, multi); - ret = hbucket_elem_add(n, AHASH_MAX(h), h->dsize); + ret = hbucket_elem_add(n, AHASH_MAX(h), set->dsize); if (ret != 0) { if (ret == -EAGAIN) mtype_data_next(&h->next, d); goto out; } - data = ahash_data(n, n->pos++, h->dsize); + data = ahash_data(n, n->pos++, set->dsize); #ifdef IP_SET_HASH_WITH_NETS - mtype_add_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); + for (i = 0; i < IPSET_NET_COUNT; i++) + mtype_add_cidr(h, CIDR(d->cidr, i), NLEN(set->family), + i); #endif h->elements++; } @@ -665,9 +697,11 @@ reuse_slot: mtype_data_set_flags(data, flags); #endif if (SET_WITH_TIMEOUT(set)) - ip_set_timeout_set(ext_timeout(data, h), ext->timeout); + ip_set_timeout_set(ext_timeout(data, set), ext->timeout); if (SET_WITH_COUNTER(set)) - ip_set_init_counter(ext_counter(data, h), ext); + ip_set_init_counter(ext_counter(data, set), ext); + if (SET_WITH_COMMENT(set)) + ip_set_init_comment(ext_comment(data, set), ext); out: rcu_read_unlock_bh(); @@ -682,47 +716,60 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, struct ip_set_ext *mext, u32 flags) { struct htype *h = set->data; - struct htable *t = h->table; + struct htable *t; const struct mtype_elem *d = value; struct mtype_elem *data; struct hbucket *n; - int i; + int i, ret = -IPSET_ERR_EXIST; +#ifdef IP_SET_HASH_WITH_NETS + u8 j; +#endif u32 key, multi = 0; + rcu_read_lock_bh(); + t = rcu_dereference_bh(h->table); key = HKEY(value, h->initval, t->htable_bits); n = hbucket(t, key); for (i = 0; i < n->pos; i++) { - data = ahash_data(n, i, h->dsize); + data = ahash_data(n, i, set->dsize); if (!mtype_data_equal(data, d, &multi)) continue; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(data, h))) - return -IPSET_ERR_EXIST; + ip_set_timeout_expired(ext_timeout(data, set))) + goto out; if (i != n->pos - 1) /* Not last one */ - memcpy(data, ahash_data(n, n->pos - 1, h->dsize), - h->dsize); + memcpy(data, ahash_data(n, n->pos - 1, set->dsize), + set->dsize); n->pos--; h->elements--; #ifdef IP_SET_HASH_WITH_NETS - mtype_del_cidr(h, CIDR(d->cidr), NETS_LENGTH(set->family)); + for (j = 0; j < IPSET_NET_COUNT; j++) + mtype_del_cidr(h, CIDR(d->cidr, j), NLEN(set->family), + j); #endif + ip_set_ext_destroy(set, data); if (n->pos + AHASH_INIT_SIZE < n->size) { void *tmp = kzalloc((n->size - AHASH_INIT_SIZE) - * h->dsize, + * set->dsize, GFP_ATOMIC); - if (!tmp) - return 0; + if (!tmp) { + ret = 0; + goto out; + } n->size -= AHASH_INIT_SIZE; - memcpy(tmp, n->value, n->size * h->dsize); + memcpy(tmp, n->value, n->size * set->dsize); kfree(n->value); n->value = tmp; } - return 0; + ret = 0; + goto out; } - return -IPSET_ERR_EXIST; +out: + rcu_read_unlock_bh(); + return ret; } static inline int @@ -730,8 +777,7 @@ mtype_data_match(struct mtype_elem *data, const struct ip_set_ext *ext, struct ip_set_ext *mext, struct ip_set *set, u32 flags) { if (SET_WITH_COUNTER(set)) - ip_set_update_counter(ext_counter(data, - (struct htype *)(set->data)), + ip_set_update_counter(ext_counter(data, set), ext, mext, flags); return mtype_do_data_match(data); } @@ -745,25 +791,38 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, struct ip_set_ext *mext, u32 flags) { struct htype *h = set->data; - struct htable *t = h->table; + struct htable *t = rcu_dereference_bh(h->table); struct hbucket *n; struct mtype_elem *data; +#if IPSET_NET_COUNT == 2 + struct mtype_elem orig = *d; + int i, j = 0, k; +#else int i, j = 0; +#endif u32 key, multi = 0; - u8 nets_length = NETS_LENGTH(set->family); + u8 nets_length = NLEN(set->family); pr_debug("test by nets\n"); - for (; j < nets_length && h->nets[j].nets && !multi; j++) { - mtype_data_netmask(d, h->nets[j].cidr); + for (; j < nets_length && h->nets[j].nets[0] && !multi; j++) { +#if IPSET_NET_COUNT == 2 + mtype_data_reset_elem(d, &orig); + mtype_data_netmask(d, h->nets[j].cidr[0], false); + for (k = 0; k < nets_length && h->nets[k].nets[1] && !multi; + k++) { + mtype_data_netmask(d, h->nets[k].cidr[1], true); +#else + mtype_data_netmask(d, h->nets[j].cidr[0]); +#endif key = HKEY(d, h->initval, t->htable_bits); n = hbucket(t, key); for (i = 0; i < n->pos; i++) { - data = ahash_data(n, i, h->dsize); + data = ahash_data(n, i, set->dsize); if (!mtype_data_equal(data, d, &multi)) continue; if (SET_WITH_TIMEOUT(set)) { if (!ip_set_timeout_expired( - ext_timeout(data, h))) + ext_timeout(data, set))) return mtype_data_match(data, ext, mext, set, flags); @@ -774,6 +833,9 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, return mtype_data_match(data, ext, mext, set, flags); } +#if IPSET_NET_COUNT == 2 + } +#endif } return 0; } @@ -785,30 +847,41 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, struct ip_set_ext *mext, u32 flags) { struct htype *h = set->data; - struct htable *t = h->table; + struct htable *t; struct mtype_elem *d = value; struct hbucket *n; struct mtype_elem *data; - int i; + int i, ret = 0; u32 key, multi = 0; + rcu_read_lock_bh(); + t = rcu_dereference_bh(h->table); #ifdef IP_SET_HASH_WITH_NETS /* If we test an IP address and not a network address, * try all possible network sizes */ - if (CIDR(d->cidr) == SET_HOST_MASK(set->family)) - return mtype_test_cidrs(set, d, ext, mext, flags); + for (i = 0; i < IPSET_NET_COUNT; i++) + if (CIDR(d->cidr, i) != SET_HOST_MASK(set->family)) + break; + if (i == IPSET_NET_COUNT) { + ret = mtype_test_cidrs(set, d, ext, mext, flags); + goto out; + } #endif key = HKEY(d, h->initval, t->htable_bits); n = hbucket(t, key); for (i = 0; i < n->pos; i++) { - data = ahash_data(n, i, h->dsize); + data = ahash_data(n, i, set->dsize); if (mtype_data_equal(data, d, &multi) && !(SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(data, h)))) - return mtype_data_match(data, ext, mext, set, flags); + ip_set_timeout_expired(ext_timeout(data, set)))) { + ret = mtype_data_match(data, ext, mext, set, flags); + goto out; + } } - return 0; +out: + rcu_read_unlock_bh(); + return ret; } /* Reply a HEADER request: fill out the header part of the set */ @@ -816,18 +889,18 @@ static int mtype_head(struct ip_set *set, struct sk_buff *skb) { const struct htype *h = set->data; + const struct htable *t; struct nlattr *nested; size_t memsize; - read_lock_bh(&set->lock); - memsize = mtype_ahash_memsize(h, NETS_LENGTH(set->family)); - read_unlock_bh(&set->lock); + t = rcu_dereference_bh_nfnl(h->table); + memsize = mtype_ahash_memsize(h, t, NLEN(set->family), set->dsize); nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, - htonl(jhash_size(h->table->htable_bits))) || + htonl(jhash_size(t->htable_bits))) || nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) goto nla_put_failure; #ifdef IP_SET_HASH_WITH_NETMASK @@ -836,12 +909,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) goto nla_put_failure; #endif if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || - nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize)) || - ((set->extensions & IPSET_EXT_TIMEOUT) && - nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(h->timeout))) || - ((set->extensions & IPSET_EXT_COUNTER) && - nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, - htonl(IPSET_FLAG_WITH_COUNTERS)))) + nla_put_net32(skb, IPSET_ATTR_MEMSIZE, htonl(memsize))) + goto nla_put_failure; + if (unlikely(ip_set_put_flags(skb, set))) goto nla_put_failure; ipset_nest_end(skb, nested); @@ -856,11 +926,11 @@ mtype_list(const struct ip_set *set, struct sk_buff *skb, struct netlink_callback *cb) { const struct htype *h = set->data; - const struct htable *t = h->table; + const struct htable *t = rcu_dereference_bh_nfnl(h->table); struct nlattr *atd, *nested; const struct hbucket *n; const struct mtype_elem *e; - u32 first = cb->args[2]; + u32 first = cb->args[IPSET_CB_ARG0]; /* We assume that one hash bucket fills into one page */ void *incomplete; int i; @@ -869,20 +939,22 @@ mtype_list(const struct ip_set *set, if (!atd) return -EMSGSIZE; pr_debug("list hash set %s\n", set->name); - for (; cb->args[2] < jhash_size(t->htable_bits); cb->args[2]++) { + for (; cb->args[IPSET_CB_ARG0] < jhash_size(t->htable_bits); + cb->args[IPSET_CB_ARG0]++) { incomplete = skb_tail_pointer(skb); - n = hbucket(t, cb->args[2]); - pr_debug("cb->args[2]: %lu, t %p n %p\n", cb->args[2], t, n); + n = hbucket(t, cb->args[IPSET_CB_ARG0]); + pr_debug("cb->arg bucket: %lu, t %p n %p\n", + cb->args[IPSET_CB_ARG0], t, n); for (i = 0; i < n->pos; i++) { - e = ahash_data(n, i, h->dsize); + e = ahash_data(n, i, set->dsize); if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, h))) + ip_set_timeout_expired(ext_timeout(e, set))) continue; pr_debug("list hash %lu hbucket %p i %u, data %p\n", - cb->args[2], n, i, e); + cb->args[IPSET_CB_ARG0], n, i, e); nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) { - if (cb->args[2] == first) { + if (cb->args[IPSET_CB_ARG0] == first) { nla_nest_cancel(skb, atd); return -EMSGSIZE; } else @@ -890,43 +962,37 @@ mtype_list(const struct ip_set *set, } if (mtype_data_list(skb, e)) goto nla_put_failure; - if (SET_WITH_TIMEOUT(set) && - nla_put_net32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get( - ext_timeout(e, h))))) - goto nla_put_failure; - if (SET_WITH_COUNTER(set) && - ip_set_put_counter(skb, ext_counter(e, h))) + if (ip_set_put_extensions(skb, set, e, true)) goto nla_put_failure; ipset_nest_end(skb, nested); } } ipset_nest_end(skb, atd); /* Set listing finished */ - cb->args[2] = 0; + cb->args[IPSET_CB_ARG0] = 0; return 0; nla_put_failure: nlmsg_trim(skb, incomplete); - ipset_nest_end(skb, atd); - if (unlikely(first == cb->args[2])) { + if (unlikely(first == cb->args[IPSET_CB_ARG0])) { pr_warning("Can't list set %s: one bucket does not fit into " "a message. Please report it!\n", set->name); - cb->args[2] = 0; + cb->args[IPSET_CB_ARG0] = 0; return -EMSGSIZE; } + ipset_nest_end(skb, atd); return 0; } static int -TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb, - const struct xt_action_param *par, - enum ipset_adt adt, struct ip_set_adt_opt *opt); +IPSET_TOKEN(MTYPE, _kadt)(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, struct ip_set_adt_opt *opt); static int -TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[], - enum ipset_adt adt, u32 *lineno, u32 flags, bool retried); +IPSET_TOKEN(MTYPE, _uadt)(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried); static const struct ip_set_type_variant mtype_variant = { .kadt = mtype_kadt, @@ -946,16 +1012,17 @@ static const struct ip_set_type_variant mtype_variant = { #ifdef IP_SET_EMIT_CREATE static int -TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags) +IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, + struct nlattr *tb[], u32 flags) { u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; - u32 cadt_flags = 0; u8 hbits; #ifdef IP_SET_HASH_WITH_NETMASK u8 netmask; #endif size_t hsize; struct HTYPE *h; + struct htable *t; if (!(set->family == NFPROTO_IPV4 || set->family == NFPROTO_IPV6)) return -IPSET_ERR_INVALID_FAMILY; @@ -1005,7 +1072,7 @@ TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags) h->netmask = netmask; #endif get_random_bytes(&h->initval, sizeof(h->initval)); - h->timeout = IPSET_NO_TIMEOUT; + set->timeout = IPSET_NO_TIMEOUT; hbits = htable_bits(hashsize); hsize = htable_size(hbits); @@ -1013,91 +1080,37 @@ TOKEN(HTYPE, _create)(struct ip_set *set, struct nlattr *tb[], u32 flags) kfree(h); return -ENOMEM; } - h->table = ip_set_alloc(hsize); - if (!h->table) { + t = ip_set_alloc(hsize); + if (!t) { kfree(h); return -ENOMEM; } - h->table->htable_bits = hbits; + t->htable_bits = hbits; + rcu_assign_pointer(h->table, t); set->data = h; - if (set->family == NFPROTO_IPV4) - set->variant = &TOKEN(HTYPE, 4_variant); - else - set->variant = &TOKEN(HTYPE, 6_variant); - - if (tb[IPSET_ATTR_CADT_FLAGS]) - cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { - set->extensions |= IPSET_EXT_COUNTER; - if (tb[IPSET_ATTR_TIMEOUT]) { - h->timeout = - ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->extensions |= IPSET_EXT_TIMEOUT; - if (set->family == NFPROTO_IPV4) { - h->dsize = - sizeof(struct TOKEN(HTYPE, 4ct_elem)); - h->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct TOKEN(HTYPE, 4ct_elem), - timeout); - h->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct TOKEN(HTYPE, 4ct_elem), - counter); - TOKEN(HTYPE, 4_gc_init)(set, - TOKEN(HTYPE, 4_gc)); - } else { - h->dsize = - sizeof(struct TOKEN(HTYPE, 6ct_elem)); - h->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct TOKEN(HTYPE, 6ct_elem), - timeout); - h->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct TOKEN(HTYPE, 6ct_elem), - counter); - TOKEN(HTYPE, 6_gc_init)(set, - TOKEN(HTYPE, 6_gc)); - } - } else { - if (set->family == NFPROTO_IPV4) { - h->dsize = - sizeof(struct TOKEN(HTYPE, 4c_elem)); - h->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct TOKEN(HTYPE, 4c_elem), - counter); - } else { - h->dsize = - sizeof(struct TOKEN(HTYPE, 6c_elem)); - h->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct TOKEN(HTYPE, 6c_elem), - counter); - } - } - } else if (tb[IPSET_ATTR_TIMEOUT]) { - h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); - set->extensions |= IPSET_EXT_TIMEOUT; - if (set->family == NFPROTO_IPV4) { - h->dsize = sizeof(struct TOKEN(HTYPE, 4t_elem)); - h->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct TOKEN(HTYPE, 4t_elem), - timeout); - TOKEN(HTYPE, 4_gc_init)(set, TOKEN(HTYPE, 4_gc)); - } else { - h->dsize = sizeof(struct TOKEN(HTYPE, 6t_elem)); - h->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct TOKEN(HTYPE, 6t_elem), - timeout); - TOKEN(HTYPE, 6_gc_init)(set, TOKEN(HTYPE, 6_gc)); - } + if (set->family == NFPROTO_IPV4) { + set->variant = &IPSET_TOKEN(HTYPE, 4_variant); + set->dsize = ip_set_elem_len(set, tb, + sizeof(struct IPSET_TOKEN(HTYPE, 4_elem))); } else { + set->variant = &IPSET_TOKEN(HTYPE, 6_variant); + set->dsize = ip_set_elem_len(set, tb, + sizeof(struct IPSET_TOKEN(HTYPE, 6_elem))); + } + if (tb[IPSET_ATTR_TIMEOUT]) { + set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); if (set->family == NFPROTO_IPV4) - h->dsize = sizeof(struct TOKEN(HTYPE, 4_elem)); + IPSET_TOKEN(HTYPE, 4_gc_init)(set, + IPSET_TOKEN(HTYPE, 4_gc)); else - h->dsize = sizeof(struct TOKEN(HTYPE, 6_elem)); + IPSET_TOKEN(HTYPE, 6_gc_init)(set, + IPSET_TOKEN(HTYPE, 6_gc)); } pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", - set->name, jhash_size(h->table->htable_bits), - h->table->htable_bits, h->maxelem, set->data, h->table); + set->name, jhash_size(t->htable_bits), + t->htable_bits, h->maxelem, set->data, t); return 0; } diff --git a/net/netfilter/ipset/ip_set_hash_ip.c b/net/netfilter/ipset/ip_set_hash_ip.c index c74e6e14cd933f4f20bbd5496312fdd0056b7e52..e65fc2423d56dd2b21cee513786eec41ceabefc9 100644 --- a/net/netfilter/ipset/ip_set_hash_ip.c +++ b/net/netfilter/ipset/ip_set_hash_ip.c @@ -23,19 +23,20 @@ #include #include -#define REVISION_MIN 0 -#define REVISION_MAX 1 /* Counters support */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 Counters support */ +#define IPSET_TYPE_REV_MAX 2 /* Comments support */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("hash:ip", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("hash:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_hash:ip"); /* Type specific function prefix */ #define HTYPE hash_ip #define IP_SET_HASH_WITH_NETMASK -/* IPv4 variants */ +/* IPv4 variant */ /* Member elements */ struct hash_ip4_elem { @@ -43,22 +44,6 @@ struct hash_ip4_elem { __be32 ip; }; -struct hash_ip4t_elem { - __be32 ip; - unsigned long timeout; -}; - -struct hash_ip4c_elem { - __be32 ip; - struct ip_set_counter counter; -}; - -struct hash_ip4ct_elem { - __be32 ip; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -99,7 +84,7 @@ hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip4_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); __be32 ip; ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip); @@ -118,8 +103,8 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip4_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); - u32 ip, ip_to, hosts; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip = 0, ip_to = 0, hosts; int ret = 0; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -178,29 +163,13 @@ hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variants */ +/* IPv6 variant */ /* Member elements */ struct hash_ip6_elem { union nf_inet_addr ip; }; -struct hash_ip6t_elem { - union nf_inet_addr ip; - unsigned long timeout; -}; - -struct hash_ip6c_elem { - union nf_inet_addr ip; - struct ip_set_counter counter; -}; - -struct hash_ip6ct_elem { - union nf_inet_addr ip; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -253,7 +222,7 @@ hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip6_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); hash_ip6_netmask(&e.ip, h->netmask); @@ -270,7 +239,7 @@ hash_ip6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ip6_elem e = {}; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); int ret; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -304,8 +273,8 @@ static struct ip_set_type hash_ip_type __read_mostly = { .features = IPSET_TYPE_IP, .dimension = IPSET_DIM_ONE, .family = NFPROTO_UNSPEC, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = hash_ip_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -324,6 +293,7 @@ static struct ip_set_type hash_ip_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_ipport.c b/net/netfilter/ipset/ip_set_hash_ipport.c index 7a2d2bd98d046f7b9eec8bc10d099c3794a1e0f6..525a595dd1fe4bf0efe6db7ca9cc06d995c66430 100644 --- a/net/netfilter/ipset/ip_set_hash_ipport.c +++ b/net/netfilter/ipset/ip_set_hash_ipport.c @@ -24,19 +24,20 @@ #include #include -#define REVISION_MIN 0 -/* 1 SCTP and UDPLITE support added */ -#define REVISION_MAX 2 /* Counters support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 SCTP and UDPLITE support added */ +/* 2 Counters support added */ +#define IPSET_TYPE_REV_MAX 3 /* Comments support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("hash:ip,port", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("hash:ip,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_hash:ip,port"); /* Type specific function prefix */ #define HTYPE hash_ipport -/* IPv4 variants */ +/* IPv4 variant */ /* Member elements */ struct hash_ipport4_elem { @@ -46,31 +47,6 @@ struct hash_ipport4_elem { u8 padding; }; -struct hash_ipport4t_elem { - __be32 ip; - __be16 port; - u8 proto; - u8 padding; - unsigned long timeout; -}; - -struct hash_ipport4c_elem { - __be32 ip; - __be16 port; - u8 proto; - u8 padding; - struct ip_set_counter counter; -}; - -struct hash_ipport4ct_elem { - __be32 ip; - __be16 port; - u8 proto; - u8 padding; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -116,10 +92,9 @@ hash_ipport4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ipport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) @@ -136,8 +111,8 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); - u32 ip, ip_to, p = 0, port, port_to; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip, ip_to = 0, p = 0, port, port_to; bool with_ports = false; int ret; @@ -222,7 +197,7 @@ hash_ipport4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variants */ +/* IPv6 variant */ struct hash_ipport6_elem { union nf_inet_addr ip; @@ -231,31 +206,6 @@ struct hash_ipport6_elem { u8 padding; }; -struct hash_ipport6t_elem { - union nf_inet_addr ip; - __be16 port; - u8 proto; - u8 padding; - unsigned long timeout; -}; - -struct hash_ipport6c_elem { - union nf_inet_addr ip; - __be16 port; - u8 proto; - u8 padding; - struct ip_set_counter counter; -}; - -struct hash_ipport6ct_elem { - union nf_inet_addr ip; - __be16 port; - u8 proto; - u8 padding; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -306,10 +256,9 @@ hash_ipport6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ipport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) @@ -326,7 +275,7 @@ hash_ipport6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipport6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); u32 port, port_to; bool with_ports = false; int ret; @@ -396,8 +345,8 @@ static struct ip_set_type hash_ipport_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT, .dimension = IPSET_DIM_TWO, .family = NFPROTO_UNSPEC, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = hash_ipport_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -419,6 +368,7 @@ static struct ip_set_type hash_ipport_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_ipportip.c b/net/netfilter/ipset/ip_set_hash_ipportip.c index 34e8a1acce42219686c897f4f1db30387b733b43..f5636631466eb3ee98509e8b832e46da4ad9a417 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportip.c +++ b/net/netfilter/ipset/ip_set_hash_ipportip.c @@ -24,19 +24,20 @@ #include #include -#define REVISION_MIN 0 -/* 1 SCTP and UDPLITE support added */ -#define REVISION_MAX 2 /* Counters support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 SCTP and UDPLITE support added */ +/* 2 Counters support added */ +#define IPSET_TYPE_REV_MAX 3 /* Comments support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("hash:ip,port,ip", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("hash:ip,port,ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_hash:ip,port,ip"); /* Type specific function prefix */ #define HTYPE hash_ipportip -/* IPv4 variants */ +/* IPv4 variant */ /* Member elements */ struct hash_ipportip4_elem { @@ -47,34 +48,6 @@ struct hash_ipportip4_elem { u8 padding; }; -struct hash_ipportip4t_elem { - __be32 ip; - __be32 ip2; - __be16 port; - u8 proto; - u8 padding; - unsigned long timeout; -}; - -struct hash_ipportip4c_elem { - __be32 ip; - __be32 ip2; - __be16 port; - u8 proto; - u8 padding; - struct ip_set_counter counter; -}; - -struct hash_ipportip4ct_elem { - __be32 ip; - __be32 ip2; - __be16 port; - u8 proto; - u8 padding; - struct ip_set_counter counter; - unsigned long timeout; -}; - static inline bool hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, const struct hash_ipportip4_elem *ip2, @@ -120,10 +93,9 @@ hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ipportip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) @@ -141,8 +113,8 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipportip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip4_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); - u32 ip, ip_to, p = 0, port, port_to; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip, ip_to = 0, p = 0, port, port_to; bool with_ports = false; int ret; @@ -231,7 +203,7 @@ hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variants */ +/* IPv6 variant */ struct hash_ipportip6_elem { union nf_inet_addr ip; @@ -241,34 +213,6 @@ struct hash_ipportip6_elem { u8 padding; }; -struct hash_ipportip6t_elem { - union nf_inet_addr ip; - union nf_inet_addr ip2; - __be16 port; - u8 proto; - u8 padding; - unsigned long timeout; -}; - -struct hash_ipportip6c_elem { - union nf_inet_addr ip; - union nf_inet_addr ip2; - __be16 port; - u8 proto; - u8 padding; - struct ip_set_counter counter; -}; - -struct hash_ipportip6ct_elem { - union nf_inet_addr ip; - union nf_inet_addr ip2; - __be16 port; - u8 proto; - u8 padding; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -319,10 +263,9 @@ hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - const struct hash_ipportip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.port, &e.proto)) @@ -340,7 +283,7 @@ hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipportip *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportip6_elem e = { }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); u32 port, port_to; bool with_ports = false; int ret; @@ -414,8 +357,8 @@ static struct ip_set_type hash_ipportip_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, .dimension = IPSET_DIM_THREE, .family = NFPROTO_UNSPEC, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = hash_ipportip_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -437,6 +380,7 @@ static struct ip_set_type hash_ipportip_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index f15f3e28b9c338c6e72d3aa949d9d241c0e7cabe..5d87fe8a41ffa4888a70b6d91897360e30c253bc 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -24,15 +24,16 @@ #include #include -#define REVISION_MIN 0 -/* 1 SCTP and UDPLITE support added */ -/* 2 Range as input support for IPv4 added */ -/* 3 nomatch flag support added */ -#define REVISION_MAX 4 /* Counters support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 SCTP and UDPLITE support added */ +/* 2 Range as input support for IPv4 added */ +/* 3 nomatch flag support added */ +/* 4 Counters support added */ +#define IPSET_TYPE_REV_MAX 5 /* Comments support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("hash:ip,port,net", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_hash:ip,port,net"); /* Type specific function prefix */ @@ -46,7 +47,7 @@ MODULE_ALIAS("ip_set_hash:ip,port,net"); #define IP_SET_HASH_WITH_PROTO #define IP_SET_HASH_WITH_NETS -/* IPv4 variants */ +/* IPv4 variant */ /* Member elements */ struct hash_ipportnet4_elem { @@ -58,37 +59,6 @@ struct hash_ipportnet4_elem { u8 proto; }; -struct hash_ipportnet4t_elem { - __be32 ip; - __be32 ip2; - __be16 port; - u8 cidr:7; - u8 nomatch:1; - u8 proto; - unsigned long timeout; -}; - -struct hash_ipportnet4c_elem { - __be32 ip; - __be32 ip2; - __be16 port; - u8 cidr:7; - u8 nomatch:1; - u8 proto; - struct ip_set_counter counter; -}; - -struct hash_ipportnet4ct_elem { - __be32 ip; - __be32 ip2; - __be16 port; - u8 cidr:7; - u8 nomatch:1; - u8 proto; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -170,9 +140,9 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ipportnet *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem e = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 + .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (adt == IPSET_TEST) e.cidr = HOST_MASK - 1; @@ -195,9 +165,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipportnet *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); - u32 ip, ip_to, p = 0, port, port_to; - u32 ip2_from, ip2_to, ip2_last, ip2; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip = 0, ip_to = 0, p = 0, port, port_to; + u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2; bool with_ports = false; u8 cidr; int ret; @@ -272,7 +242,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], if (ip > ip_to) swap(ip, ip_to); } else if (tb[IPSET_ATTR_CIDR]) { - u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); if (!cidr || cidr > 32) return -IPSET_ERR_INVALID_CIDR; @@ -306,9 +276,9 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], : port; for (; p <= port_to; p++) { e.port = htons(p); - ip2 = retried - && ip == ntohl(h->next.ip) - && p == ntohs(h->next.port) + ip2 = retried && + ip == ntohl(h->next.ip) && + p == ntohs(h->next.port) ? ntohl(h->next.ip2) : ip2_from; while (!after(ip2, ip2_to)) { e.ip2 = htonl(ip2); @@ -328,7 +298,7 @@ hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variants */ +/* IPv6 variant */ struct hash_ipportnet6_elem { union nf_inet_addr ip; @@ -339,37 +309,6 @@ struct hash_ipportnet6_elem { u8 proto; }; -struct hash_ipportnet6t_elem { - union nf_inet_addr ip; - union nf_inet_addr ip2; - __be16 port; - u8 cidr:7; - u8 nomatch:1; - u8 proto; - unsigned long timeout; -}; - -struct hash_ipportnet6c_elem { - union nf_inet_addr ip; - union nf_inet_addr ip2; - __be16 port; - u8 cidr:7; - u8 nomatch:1; - u8 proto; - struct ip_set_counter counter; -}; - -struct hash_ipportnet6ct_elem { - union nf_inet_addr ip; - union nf_inet_addr ip2; - __be16 port; - u8 cidr:7; - u8 nomatch:1; - u8 proto; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -454,9 +393,9 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ipportnet *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem e = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 + .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (adt == IPSET_TEST) e.cidr = HOST_MASK - 1; @@ -479,7 +418,7 @@ hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_ipportnet *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); u32 port, port_to; bool with_ports = false; u8 cidr; @@ -574,8 +513,8 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { IPSET_TYPE_NOMATCH, .dimension = IPSET_DIM_THREE, .family = NFPROTO_UNSPEC, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = hash_ipportnet_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -600,6 +539,7 @@ static struct ip_set_type hash_ipportnet_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index 223e9f546d0fa3e414b7a53a839b56d6835093bb..8295cf4f9fdcfdb3d3da4b72792add9fc83156ad 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -22,21 +22,22 @@ #include #include -#define REVISION_MIN 0 -/* 1 Range as input support for IPv4 added */ -/* 2 nomatch flag support added */ -#define REVISION_MAX 3 /* Counters support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 Range as input support for IPv4 added */ +/* 2 nomatch flag support added */ +/* 3 Counters support added */ +#define IPSET_TYPE_REV_MAX 4 /* Comments support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("hash:net", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("hash:net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_hash:net"); /* Type specific function prefix */ #define HTYPE hash_net #define IP_SET_HASH_WITH_NETS -/* IPv4 variants */ +/* IPv4 variant */ /* Member elements */ struct hash_net4_elem { @@ -46,31 +47,6 @@ struct hash_net4_elem { u8 cidr; }; -struct hash_net4t_elem { - __be32 ip; - u16 padding0; - u8 nomatch; - u8 cidr; - unsigned long timeout; -}; - -struct hash_net4c_elem { - __be32 ip; - u16 padding0; - u8 nomatch; - u8 cidr; - struct ip_set_counter counter; -}; - -struct hash_net4ct_elem { - __be32 ip; - u16 padding0; - u8 nomatch; - u8 cidr; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -143,9 +119,9 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_net *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem e = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (e.cidr == 0) return -EINVAL; @@ -165,8 +141,8 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_net *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem e = { .cidr = HOST_MASK }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); - u32 ip = 0, ip_to, last; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip = 0, ip_to = 0, last; int ret; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -228,7 +204,7 @@ hash_net4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variants */ +/* IPv6 variant */ struct hash_net6_elem { union nf_inet_addr ip; @@ -237,31 +213,6 @@ struct hash_net6_elem { u8 cidr; }; -struct hash_net6t_elem { - union nf_inet_addr ip; - u16 padding0; - u8 nomatch; - u8 cidr; - unsigned long timeout; -}; - -struct hash_net6c_elem { - union nf_inet_addr ip; - u16 padding0; - u8 nomatch; - u8 cidr; - struct ip_set_counter counter; -}; - -struct hash_net6ct_elem { - union nf_inet_addr ip; - u16 padding0; - u8 nomatch; - u8 cidr; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -338,9 +289,9 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_net *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net6_elem e = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK + .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (e.cidr == 0) return -EINVAL; @@ -357,10 +308,9 @@ static int hash_net6_uadt(struct ip_set *set, struct nlattr *tb[], enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) { - const struct hash_net *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net6_elem e = { .cidr = HOST_MASK }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); int ret; if (unlikely(!tb[IPSET_ATTR_IP] || @@ -406,8 +356,8 @@ static struct ip_set_type hash_net_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_NOMATCH, .dimension = IPSET_DIM_ONE, .family = NFPROTO_UNSPEC, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = hash_net_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -425,6 +375,7 @@ static struct ip_set_type hash_net_type __read_mostly = { [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index 7d798d5d5cd30a66de3d6fade0b4110eeba583f7..3f64a66bf5d9b78bfa551124cbc7bd3d7d8a8857 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -23,14 +23,15 @@ #include #include -#define REVISION_MIN 0 -/* 1 nomatch flag support added */ -/* 2 /0 support added */ -#define REVISION_MAX 3 /* Counters support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 nomatch flag support added */ +/* 2 /0 support added */ +/* 3 Counters support added */ +#define IPSET_TYPE_REV_MAX 4 /* Comments support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("hash:net,iface", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("hash:net,iface", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_hash:net,iface"); /* Interface name rbtree */ @@ -134,7 +135,7 @@ iface_add(struct rb_root *root, const char **iface) #define STREQ(a, b) (strcmp(a, b) == 0) -/* IPv4 variants */ +/* IPv4 variant */ struct hash_netiface4_elem_hashed { __be32 ip; @@ -144,7 +145,7 @@ struct hash_netiface4_elem_hashed { u8 elem; }; -/* Member elements without timeout */ +/* Member elements */ struct hash_netiface4_elem { __be32 ip; u8 physdev; @@ -154,37 +155,6 @@ struct hash_netiface4_elem { const char *iface; }; -struct hash_netiface4t_elem { - __be32 ip; - u8 physdev; - u8 cidr; - u8 nomatch; - u8 elem; - const char *iface; - unsigned long timeout; -}; - -struct hash_netiface4c_elem { - __be32 ip; - u8 physdev; - u8 cidr; - u8 nomatch; - u8 elem; - const char *iface; - struct ip_set_counter counter; -}; - -struct hash_netiface4ct_elem { - __be32 ip; - u8 physdev; - u8 cidr; - u8 nomatch; - u8 elem; - const char *iface; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -265,10 +235,10 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netiface *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface4_elem e = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, + .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), .elem = 1, }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); int ret; if (e.cidr == 0) @@ -319,8 +289,8 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], struct hash_netiface *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface4_elem e = { .cidr = HOST_MASK, .elem = 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); - u32 ip = 0, ip_to, last; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip = 0, ip_to = 0, last; char iface[IFNAMSIZ]; int ret; @@ -399,7 +369,7 @@ hash_netiface4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variants */ +/* IPv6 variant */ struct hash_netiface6_elem_hashed { union nf_inet_addr ip; @@ -418,37 +388,6 @@ struct hash_netiface6_elem { const char *iface; }; -struct hash_netiface6t_elem { - union nf_inet_addr ip; - u8 physdev; - u8 cidr; - u8 nomatch; - u8 elem; - const char *iface; - unsigned long timeout; -}; - -struct hash_netiface6c_elem { - union nf_inet_addr ip; - u8 physdev; - u8 cidr; - u8 nomatch; - u8 elem; - const char *iface; - struct ip_set_counter counter; -}; - -struct hash_netiface6ct_elem { - union nf_inet_addr ip; - u8 physdev; - u8 cidr; - u8 nomatch; - u8 elem; - const char *iface; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -534,10 +473,10 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netiface *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface6_elem e = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr : HOST_MASK, + .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), .elem = 1, }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); int ret; if (e.cidr == 0) @@ -584,7 +523,7 @@ hash_netiface6_uadt(struct ip_set *set, struct nlattr *tb[], struct hash_netiface *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface6_elem e = { .cidr = HOST_MASK, .elem = 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); char iface[IFNAMSIZ]; int ret; @@ -645,8 +584,8 @@ static struct ip_set_type hash_netiface_type __read_mostly = { IPSET_TYPE_NOMATCH, .dimension = IPSET_DIM_TWO, .family = NFPROTO_UNSPEC, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = hash_netiface_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -668,6 +607,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = { [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c new file mode 100644 index 0000000000000000000000000000000000000000..2bc2dec20b007026e78aba74451e70039c6e5988 --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_netnet.c @@ -0,0 +1,481 @@ +/* Copyright (C) 2003-2013 Jozsef Kadlecsik + * Copyright (C) 2013 Oliver Smith + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Kernel module implementing an IP set type: the hash:net type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define IPSET_TYPE_REV_MIN 0 +#define IPSET_TYPE_REV_MAX 0 + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Oliver Smith "); +IP_SET_MODULE_DESC("hash:net,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +MODULE_ALIAS("ip_set_hash:net,net"); + +/* Type specific function prefix */ +#define HTYPE hash_netnet +#define IP_SET_HASH_WITH_NETS +#define IPSET_NET_COUNT 2 + +/* IPv4 variants */ + +/* Member elements */ +struct hash_netnet4_elem { + union { + __be32 ip[2]; + __be64 ipcmp; + }; + u8 nomatch; + union { + u8 cidr[2]; + u16 ccmp; + }; +}; + +/* Common functions */ + +static inline bool +hash_netnet4_data_equal(const struct hash_netnet4_elem *ip1, + const struct hash_netnet4_elem *ip2, + u32 *multi) +{ + return ip1->ipcmp == ip2->ipcmp && + ip2->ccmp == ip2->ccmp; +} + +static inline int +hash_netnet4_do_data_match(const struct hash_netnet4_elem *elem) +{ + return elem->nomatch ? -ENOTEMPTY : 1; +} + +static inline void +hash_netnet4_data_set_flags(struct hash_netnet4_elem *elem, u32 flags) +{ + elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; +} + +static inline void +hash_netnet4_data_reset_flags(struct hash_netnet4_elem *elem, u8 *flags) +{ + swap(*flags, elem->nomatch); +} + +static inline void +hash_netnet4_data_reset_elem(struct hash_netnet4_elem *elem, + struct hash_netnet4_elem *orig) +{ + elem->ip[1] = orig->ip[1]; +} + +static inline void +hash_netnet4_data_netmask(struct hash_netnet4_elem *elem, u8 cidr, bool inner) +{ + if (inner) { + elem->ip[1] &= ip_set_netmask(cidr); + elem->cidr[1] = cidr; + } else { + elem->ip[0] &= ip_set_netmask(cidr); + elem->cidr[0] = cidr; + } +} + +static bool +hash_netnet4_data_list(struct sk_buff *skb, + const struct hash_netnet4_elem *data) +{ + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) || + nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return 1; +} + +static inline void +hash_netnet4_data_next(struct hash_netnet4_elem *next, + const struct hash_netnet4_elem *d) +{ + next->ipcmp = d->ipcmp; +} + +#define MTYPE hash_netnet4 +#define PF 4 +#define HOST_MASK 32 +#include "ip_set_hash_gen.h" + +static int +hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, struct ip_set_adt_opt *opt) +{ + const struct hash_netnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netnet4_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + + e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); + e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + if (adt == IPSET_TEST) + e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; + + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]); + ip4addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1]); + e.ip[0] &= ip_set_netmask(e.cidr[0]); + e.ip[1] &= ip_set_netmask(e.cidr[1]); + + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); +} + +static int +hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) +{ + const struct hash_netnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netnet4_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip = 0, ip_to = 0, last; + u32 ip2 = 0, ip2_from = 0, ip2_to = 0, last2; + u8 cidr, cidr2; + int ret; + + e.cidr[0] = e.cidr[1] = HOST_MASK; + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) || + ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) || + ip_set_get_extensions(set, tb, &ext); + if (ret) + return ret; + + if (tb[IPSET_ATTR_CIDR]) { + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + if (!cidr || cidr > HOST_MASK) + return -IPSET_ERR_INVALID_CIDR; + e.cidr[0] = cidr; + } + + if (tb[IPSET_ATTR_CIDR2]) { + cidr2 = nla_get_u8(tb[IPSET_ATTR_CIDR2]); + if (!cidr2 || cidr2 > HOST_MASK) + return -IPSET_ERR_INVALID_CIDR; + e.cidr[1] = cidr2; + } + + if (tb[IPSET_ATTR_CADT_FLAGS]) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (IPSET_FLAG_NOMATCH << 16); + } + + if (adt == IPSET_TEST || !(tb[IPSET_ATTR_IP_TO] && + tb[IPSET_ATTR_IP2_TO])) { + e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0])); + e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1])); + ret = adtfn(set, &e, &ext, &ext, flags); + return ip_set_enomatch(ret, flags, adt, set) ? -ret : + ip_set_eexist(ret, flags) ? 0 : ret; + } + + ip_to = ip; + if (tb[IPSET_ATTR_IP_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); + if (ret) + return ret; + if (ip_to < ip) + swap(ip, ip_to); + if (ip + UINT_MAX == ip_to) + return -IPSET_ERR_HASH_RANGE; + } + + ip2_to = ip2_from; + if (tb[IPSET_ATTR_IP2_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); + if (ret) + return ret; + if (ip2_to < ip2_from) + swap(ip2_from, ip2_to); + if (ip2_from + UINT_MAX == ip2_to) + return -IPSET_ERR_HASH_RANGE; + + } + + if (retried) + ip = ntohl(h->next.ip[0]); + + while (!after(ip, ip_to)) { + e.ip[0] = htonl(ip); + last = ip_set_range_to_cidr(ip, ip_to, &cidr); + e.cidr[0] = cidr; + ip2 = (retried && + ip == ntohl(h->next.ip[0])) ? ntohl(h->next.ip[1]) + : ip2_from; + while (!after(ip2, ip2_to)) { + e.ip[1] = htonl(ip2); + last2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr2); + e.cidr[1] = cidr2; + ret = adtfn(set, &e, &ext, &ext, flags); + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + ip2 = last2 + 1; + } + ip = last + 1; + } + return ret; +} + +/* IPv6 variants */ + +struct hash_netnet6_elem { + union nf_inet_addr ip[2]; + u8 nomatch; + union { + u8 cidr[2]; + u16 ccmp; + }; +}; + +/* Common functions */ + +static inline bool +hash_netnet6_data_equal(const struct hash_netnet6_elem *ip1, + const struct hash_netnet6_elem *ip2, + u32 *multi) +{ + return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) && + ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) && + ip1->ccmp == ip2->ccmp; +} + +static inline int +hash_netnet6_do_data_match(const struct hash_netnet6_elem *elem) +{ + return elem->nomatch ? -ENOTEMPTY : 1; +} + +static inline void +hash_netnet6_data_set_flags(struct hash_netnet6_elem *elem, u32 flags) +{ + elem->nomatch = (flags >> 16) & IPSET_FLAG_NOMATCH; +} + +static inline void +hash_netnet6_data_reset_flags(struct hash_netnet6_elem *elem, u8 *flags) +{ + swap(*flags, elem->nomatch); +} + +static inline void +hash_netnet6_data_reset_elem(struct hash_netnet6_elem *elem, + struct hash_netnet6_elem *orig) +{ + elem->ip[1] = orig->ip[1]; +} + +static inline void +hash_netnet6_data_netmask(struct hash_netnet6_elem *elem, u8 cidr, bool inner) +{ + if (inner) { + ip6_netmask(&elem->ip[1], cidr); + elem->cidr[1] = cidr; + } else { + ip6_netmask(&elem->ip[0], cidr); + elem->cidr[0] = cidr; + } +} + +static bool +hash_netnet6_data_list(struct sk_buff *skb, + const struct hash_netnet6_elem *data) +{ + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) || + nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return 1; +} + +static inline void +hash_netnet6_data_next(struct hash_netnet4_elem *next, + const struct hash_netnet6_elem *d) +{ +} + +#undef MTYPE +#undef PF +#undef HOST_MASK + +#define MTYPE hash_netnet6 +#define PF 6 +#define HOST_MASK 128 +#define IP_SET_EMIT_CREATE +#include "ip_set_hash_gen.h" + +static int +hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, struct ip_set_adt_opt *opt) +{ + const struct hash_netnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netnet6_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + + e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); + e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + if (adt == IPSET_TEST) + e.ccmp = (HOST_MASK << (sizeof(u8)*8)) | HOST_MASK; + + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_TWO_SRC, &e.ip[1].in6); + ip6_netmask(&e.ip[0], e.cidr[0]); + ip6_netmask(&e.ip[1], e.cidr[1]); + + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); +} + +static int +hash_netnet6_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) +{ + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netnet6_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + int ret; + + e.cidr[0] = e.cidr[1] = HOST_MASK; + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) + return -IPSET_ERR_PROTOCOL; + if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO])) + return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) || + ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) || + ip_set_get_extensions(set, tb, &ext); + if (ret) + return ret; + + if (tb[IPSET_ATTR_CIDR]) + e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]); + + if (tb[IPSET_ATTR_CIDR2]) + e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]); + + if (!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] || + e.cidr[1] > HOST_MASK) + return -IPSET_ERR_INVALID_CIDR; + + ip6_netmask(&e.ip[0], e.cidr[0]); + ip6_netmask(&e.ip[1], e.cidr[1]); + + if (tb[IPSET_ATTR_CADT_FLAGS]) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (IPSET_FLAG_NOMATCH << 16); + } + + ret = adtfn(set, &e, &ext, &ext, flags); + + return ip_set_enomatch(ret, flags, adt, set) ? -ret : + ip_set_eexist(ret, flags) ? 0 : ret; +} + +static struct ip_set_type hash_netnet_type __read_mostly = { + .name = "hash:net,net", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP | IPSET_TYPE_IP2 | IPSET_TYPE_NOMATCH, + .dimension = IPSET_DIM_TWO, + .family = NFPROTO_UNSPEC, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, + .create = hash_netnet_create, + .create_policy = { + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, + }, + .adt_policy = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, + [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, + [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, + [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, + }, + .me = THIS_MODULE, +}; + +static int __init +hash_netnet_init(void) +{ + return ip_set_type_register(&hash_netnet_type); +} + +static void __exit +hash_netnet_fini(void) +{ + ip_set_type_unregister(&hash_netnet_type); +} + +module_init(hash_netnet_init); +module_exit(hash_netnet_fini); diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index 09d6690bee6fd33891c4a00bbbb909f2bbf7731b..7097fb0141bf6e1363ca0b0342451e66c34773b4 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -23,15 +23,16 @@ #include #include -#define REVISION_MIN 0 -/* 1 SCTP and UDPLITE support added */ -/* 2 Range as input support for IPv4 added */ -/* 3 nomatch flag support added */ -#define REVISION_MAX 4 /* Counters support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 SCTP and UDPLITE support added */ +/* 2 Range as input support for IPv4 added */ +/* 3 nomatch flag support added */ +/* 4 Counters support added */ +#define IPSET_TYPE_REV_MAX 5 /* Comments support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("hash:net,port", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("hash:net,port", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_hash:net,port"); /* Type specific function prefix */ @@ -45,7 +46,7 @@ MODULE_ALIAS("ip_set_hash:net,port"); */ #define IP_SET_HASH_WITH_NETS_PACKED -/* IPv4 variants */ +/* IPv4 variant */ /* Member elements */ struct hash_netport4_elem { @@ -56,34 +57,6 @@ struct hash_netport4_elem { u8 nomatch:1; }; -struct hash_netport4t_elem { - __be32 ip; - __be16 port; - u8 proto; - u8 cidr:7; - u8 nomatch:1; - unsigned long timeout; -}; - -struct hash_netport4c_elem { - __be32 ip; - __be16 port; - u8 proto; - u8 cidr:7; - u8 nomatch:1; - struct ip_set_counter counter; -}; - -struct hash_netport4ct_elem { - __be32 ip; - __be16 port; - u8 proto; - u8 cidr:7; - u8 nomatch:1; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -162,9 +135,9 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_netport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem e = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1 + .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (adt == IPSET_TEST) e.cidr = HOST_MASK - 1; @@ -186,8 +159,8 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_netport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem e = { .cidr = HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); - u32 port, port_to, p = 0, ip = 0, ip_to, last; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 port, port_to, p = 0, ip = 0, ip_to = 0, last; bool with_ports = false; u8 cidr; int ret; @@ -287,7 +260,7 @@ hash_netport4_uadt(struct ip_set *set, struct nlattr *tb[], return ret; } -/* IPv6 variants */ +/* IPv6 variant */ struct hash_netport6_elem { union nf_inet_addr ip; @@ -297,34 +270,6 @@ struct hash_netport6_elem { u8 nomatch:1; }; -struct hash_netport6t_elem { - union nf_inet_addr ip; - __be16 port; - u8 proto; - u8 cidr:7; - u8 nomatch:1; - unsigned long timeout; -}; - -struct hash_netport6c_elem { - union nf_inet_addr ip; - __be16 port; - u8 proto; - u8 cidr:7; - u8 nomatch:1; - struct ip_set_counter counter; -}; - -struct hash_netport6ct_elem { - union nf_inet_addr ip; - __be16 port; - u8 proto; - u8 cidr:7; - u8 nomatch:1; - struct ip_set_counter counter; - unsigned long timeout; -}; - /* Common functions */ static inline bool @@ -407,9 +352,9 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_netport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem e = { - .cidr = h->nets[0].cidr ? h->nets[0].cidr - 1 : HOST_MASK - 1, + .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, }; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, h); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); if (adt == IPSET_TEST) e.cidr = HOST_MASK - 1; @@ -431,7 +376,7 @@ hash_netport6_uadt(struct ip_set *set, struct nlattr *tb[], const struct hash_netport *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem e = { .cidr = HOST_MASK - 1 }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(h); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); u32 port, port_to; bool with_ports = false; u8 cidr; @@ -518,8 +463,8 @@ static struct ip_set_type hash_netport_type __read_mostly = { .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_NOMATCH, .dimension = IPSET_DIM_TWO, .family = NFPROTO_UNSPEC, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = hash_netport_create, .create_policy = { [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, @@ -542,6 +487,7 @@ static struct ip_set_type hash_netport_type __read_mostly = { [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c new file mode 100644 index 0000000000000000000000000000000000000000..703d1192a6a225214f1ffd28c6ab926110942c32 --- /dev/null +++ b/net/netfilter/ipset/ip_set_hash_netportnet.c @@ -0,0 +1,586 @@ +/* Copyright (C) 2003-2013 Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Kernel module implementing an IP set type: the hash:ip,port,net type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define IPSET_TYPE_REV_MIN 0 +#define IPSET_TYPE_REV_MAX 0 /* Comments support added */ + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Oliver Smith "); +IP_SET_MODULE_DESC("hash:net,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); +MODULE_ALIAS("ip_set_hash:net,port,net"); + +/* Type specific function prefix */ +#define HTYPE hash_netportnet +#define IP_SET_HASH_WITH_PROTO +#define IP_SET_HASH_WITH_NETS +#define IPSET_NET_COUNT 2 + +/* IPv4 variant */ + +/* Member elements */ +struct hash_netportnet4_elem { + union { + __be32 ip[2]; + __be64 ipcmp; + }; + __be16 port; + union { + u8 cidr[2]; + u16 ccmp; + }; + u8 nomatch:1; + u8 proto; +}; + +/* Common functions */ + +static inline bool +hash_netportnet4_data_equal(const struct hash_netportnet4_elem *ip1, + const struct hash_netportnet4_elem *ip2, + u32 *multi) +{ + return ip1->ipcmp == ip2->ipcmp && + ip1->ccmp == ip2->ccmp && + ip1->port == ip2->port && + ip1->proto == ip2->proto; +} + +static inline int +hash_netportnet4_do_data_match(const struct hash_netportnet4_elem *elem) +{ + return elem->nomatch ? -ENOTEMPTY : 1; +} + +static inline void +hash_netportnet4_data_set_flags(struct hash_netportnet4_elem *elem, u32 flags) +{ + elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); +} + +static inline void +hash_netportnet4_data_reset_flags(struct hash_netportnet4_elem *elem, u8 *flags) +{ + swap(*flags, elem->nomatch); +} + +static inline void +hash_netportnet4_data_reset_elem(struct hash_netportnet4_elem *elem, + struct hash_netportnet4_elem *orig) +{ + elem->ip[1] = orig->ip[1]; +} + +static inline void +hash_netportnet4_data_netmask(struct hash_netportnet4_elem *elem, + u8 cidr, bool inner) +{ + if (inner) { + elem->ip[1] &= ip_set_netmask(cidr); + elem->cidr[1] = cidr; + } else { + elem->ip[0] &= ip_set_netmask(cidr); + elem->cidr[0] = cidr; + } +} + +static bool +hash_netportnet4_data_list(struct sk_buff *skb, + const struct hash_netportnet4_elem *data) +{ + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + + if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip[0]) || + nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip[1]) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return 1; +} + +static inline void +hash_netportnet4_data_next(struct hash_netportnet4_elem *next, + const struct hash_netportnet4_elem *d) +{ + next->ipcmp = d->ipcmp; + next->port = d->port; +} + +#define MTYPE hash_netportnet4 +#define PF 4 +#define HOST_MASK 32 +#include "ip_set_hash_gen.h" + +static int +hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, struct ip_set_adt_opt *opt) +{ + const struct hash_netportnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netportnet4_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + + e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); + e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + if (adt == IPSET_TEST) + e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; + + if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, + &e.port, &e.proto)) + return -EINVAL; + + ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0]); + ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1]); + e.ip[0] &= ip_set_netmask(e.cidr[0]); + e.ip[1] &= ip_set_netmask(e.cidr[1]); + + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); +} + +static int +hash_netportnet4_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) +{ + const struct hash_netportnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netportnet4_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 ip = 0, ip_to = 0, ip_last, p = 0, port, port_to; + u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2; + bool with_ports = false; + u8 cidr, cidr2; + int ret; + + e.cidr[0] = e.cidr[1] = HOST_MASK; + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip) || + ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from) || + ip_set_get_extensions(set, tb, &ext); + if (ret) + return ret; + + if (tb[IPSET_ATTR_CIDR]) { + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); + if (!cidr || cidr > HOST_MASK) + return -IPSET_ERR_INVALID_CIDR; + e.cidr[0] = cidr; + } + + if (tb[IPSET_ATTR_CIDR2]) { + cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); + if (!cidr || cidr > HOST_MASK) + return -IPSET_ERR_INVALID_CIDR; + e.cidr[1] = cidr; + } + + if (tb[IPSET_ATTR_PORT]) + e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_PROTO]) { + e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + with_ports = ip_set_proto_with_ports(e.proto); + + if (e.proto == 0) + return -IPSET_ERR_INVALID_PROTO; + } else + return -IPSET_ERR_MISSING_PROTO; + + if (!(with_ports || e.proto == IPPROTO_ICMP)) + e.port = 0; + + if (tb[IPSET_ATTR_CADT_FLAGS]) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (IPSET_FLAG_NOMATCH << 16); + } + + with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; + if (adt == IPSET_TEST || + !(tb[IPSET_ATTR_IP_TO] || with_ports || tb[IPSET_ATTR_IP2_TO])) { + e.ip[0] = htonl(ip & ip_set_hostmask(e.cidr[0])); + e.ip[1] = htonl(ip2_from & ip_set_hostmask(e.cidr[1])); + ret = adtfn(set, &e, &ext, &ext, flags); + return ip_set_enomatch(ret, flags, adt, set) ? -ret : + ip_set_eexist(ret, flags) ? 0 : ret; + } + + ip_to = ip; + if (tb[IPSET_ATTR_IP_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); + if (ret) + return ret; + if (ip > ip_to) + swap(ip, ip_to); + if (unlikely(ip + UINT_MAX == ip_to)) + return -IPSET_ERR_HASH_RANGE; + } + + port_to = port = ntohs(e.port); + if (tb[IPSET_ATTR_PORT_TO]) { + port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); + if (port > port_to) + swap(port, port_to); + } + + ip2_to = ip2_from; + if (tb[IPSET_ATTR_IP2_TO]) { + ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); + if (ret) + return ret; + if (ip2_from > ip2_to) + swap(ip2_from, ip2_to); + if (unlikely(ip2_from + UINT_MAX == ip2_to)) + return -IPSET_ERR_HASH_RANGE; + } + + if (retried) + ip = ntohl(h->next.ip[0]); + + while (!after(ip, ip_to)) { + e.ip[0] = htonl(ip); + ip_last = ip_set_range_to_cidr(ip, ip_to, &cidr); + e.cidr[0] = cidr; + p = retried && ip == ntohl(h->next.ip[0]) ? ntohs(h->next.port) + : port; + for (; p <= port_to; p++) { + e.port = htons(p); + ip2 = (retried && ip == ntohl(h->next.ip[0]) && + p == ntohs(h->next.port)) ? ntohl(h->next.ip[1]) + : ip2_from; + while (!after(ip2, ip2_to)) { + e.ip[1] = htonl(ip2); + ip2_last = ip_set_range_to_cidr(ip2, ip2_to, + &cidr2); + e.cidr[1] = cidr2; + ret = adtfn(set, &e, &ext, &ext, flags); + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + ip2 = ip2_last + 1; + } + } + ip = ip_last + 1; + } + return ret; +} + +/* IPv6 variant */ + +struct hash_netportnet6_elem { + union nf_inet_addr ip[2]; + __be16 port; + union { + u8 cidr[2]; + u16 ccmp; + }; + u8 nomatch:1; + u8 proto; +}; + +/* Common functions */ + +static inline bool +hash_netportnet6_data_equal(const struct hash_netportnet6_elem *ip1, + const struct hash_netportnet6_elem *ip2, + u32 *multi) +{ + return ipv6_addr_equal(&ip1->ip[0].in6, &ip2->ip[0].in6) && + ipv6_addr_equal(&ip1->ip[1].in6, &ip2->ip[1].in6) && + ip1->ccmp == ip2->ccmp && + ip1->port == ip2->port && + ip1->proto == ip2->proto; +} + +static inline int +hash_netportnet6_do_data_match(const struct hash_netportnet6_elem *elem) +{ + return elem->nomatch ? -ENOTEMPTY : 1; +} + +static inline void +hash_netportnet6_data_set_flags(struct hash_netportnet6_elem *elem, u32 flags) +{ + elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); +} + +static inline void +hash_netportnet6_data_reset_flags(struct hash_netportnet6_elem *elem, u8 *flags) +{ + swap(*flags, elem->nomatch); +} + +static inline void +hash_netportnet6_data_reset_elem(struct hash_netportnet6_elem *elem, + struct hash_netportnet6_elem *orig) +{ + elem->ip[1] = orig->ip[1]; +} + +static inline void +hash_netportnet6_data_netmask(struct hash_netportnet6_elem *elem, + u8 cidr, bool inner) +{ + if (inner) { + ip6_netmask(&elem->ip[1], cidr); + elem->cidr[1] = cidr; + } else { + ip6_netmask(&elem->ip[0], cidr); + elem->cidr[0] = cidr; + } +} + +static bool +hash_netportnet6_data_list(struct sk_buff *skb, + const struct hash_netportnet6_elem *data) +{ + u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; + + if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip[0].in6) || + nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip[1].in6) || + nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || + nla_put_u8(skb, IPSET_ATTR_CIDR, data->cidr[0]) || + nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr[1]) || + nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || + (flags && + nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return 1; +} + +static inline void +hash_netportnet6_data_next(struct hash_netportnet4_elem *next, + const struct hash_netportnet6_elem *d) +{ + next->port = d->port; +} + +#undef MTYPE +#undef PF +#undef HOST_MASK + +#define MTYPE hash_netportnet6 +#define PF 6 +#define HOST_MASK 128 +#define IP_SET_EMIT_CREATE +#include "ip_set_hash_gen.h" + +static int +hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, + const struct xt_action_param *par, + enum ipset_adt adt, struct ip_set_adt_opt *opt) +{ + const struct hash_netportnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netportnet6_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); + + e.cidr[0] = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); + e.cidr[1] = IP_SET_INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + if (adt == IPSET_TEST) + e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK; + + if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, + &e.port, &e.proto)) + return -EINVAL; + + ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip[0].in6); + ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip[1].in6); + ip6_netmask(&e.ip[0], e.cidr[0]); + ip6_netmask(&e.ip[1], e.cidr[1]); + + return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); +} + +static int +hash_netportnet6_uadt(struct ip_set *set, struct nlattr *tb[], + enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) +{ + const struct hash_netportnet *h = set->data; + ipset_adtfn adtfn = set->variant->adt[adt]; + struct hash_netportnet6_elem e = { }; + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); + u32 port, port_to; + bool with_ports = false; + int ret; + + e.cidr[0] = e.cidr[1] = HOST_MASK; + if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || + !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || + !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES))) + return -IPSET_ERR_PROTOCOL; + if (unlikely(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_IP2_TO])) + return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; + + if (tb[IPSET_ATTR_LINENO]) + *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); + + ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip[0]) || + ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip[1]) || + ip_set_get_extensions(set, tb, &ext); + if (ret) + return ret; + + if (tb[IPSET_ATTR_CIDR]) + e.cidr[0] = nla_get_u8(tb[IPSET_ATTR_CIDR]); + + if (tb[IPSET_ATTR_CIDR2]) + e.cidr[1] = nla_get_u8(tb[IPSET_ATTR_CIDR2]); + + if (unlikely(!e.cidr[0] || e.cidr[0] > HOST_MASK || !e.cidr[1] || + e.cidr[1] > HOST_MASK)) + return -IPSET_ERR_INVALID_CIDR; + + ip6_netmask(&e.ip[0], e.cidr[0]); + ip6_netmask(&e.ip[1], e.cidr[1]); + + if (tb[IPSET_ATTR_PORT]) + e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); + else + return -IPSET_ERR_PROTOCOL; + + if (tb[IPSET_ATTR_PROTO]) { + e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); + with_ports = ip_set_proto_with_ports(e.proto); + + if (e.proto == 0) + return -IPSET_ERR_INVALID_PROTO; + } else + return -IPSET_ERR_MISSING_PROTO; + + if (!(with_ports || e.proto == IPPROTO_ICMPV6)) + e.port = 0; + + if (tb[IPSET_ATTR_CADT_FLAGS]) { + u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); + if (cadt_flags & IPSET_FLAG_NOMATCH) + flags |= (IPSET_FLAG_NOMATCH << 16); + } + + if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { + ret = adtfn(set, &e, &ext, &ext, flags); + return ip_set_enomatch(ret, flags, adt, set) ? -ret : + ip_set_eexist(ret, flags) ? 0 : ret; + } + + port = ntohs(e.port); + port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); + if (port > port_to) + swap(port, port_to); + + if (retried) + port = ntohs(h->next.port); + for (; port <= port_to; port++) { + e.port = htons(port); + ret = adtfn(set, &e, &ext, &ext, flags); + + if (ret && !ip_set_eexist(ret, flags)) + return ret; + else + ret = 0; + } + return ret; +} + +static struct ip_set_type hash_netportnet_type __read_mostly = { + .name = "hash:net,port,net", + .protocol = IPSET_PROTOCOL, + .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 | + IPSET_TYPE_NOMATCH, + .dimension = IPSET_DIM_THREE, + .family = NFPROTO_UNSPEC, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, + .create = hash_netportnet_create, + .create_policy = { + [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, + [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, + [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, + [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, + }, + .adt_policy = { + [IPSET_ATTR_IP] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, + [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, + [IPSET_ATTR_PORT] = { .type = NLA_U16 }, + [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, + [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, + [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, + [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, + [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, + [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, + [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, + [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, + [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, + }, + .me = THIS_MODULE, +}; + +static int __init +hash_netportnet_init(void) +{ + return ip_set_type_register(&hash_netportnet_type); +} + +static void __exit +hash_netportnet_fini(void) +{ + ip_set_type_unregister(&hash_netportnet_type); +} + +module_init(hash_netportnet_init); +module_exit(hash_netportnet_fini); diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 979b8c90e42201c656faf914e38b4b30bac2d298..3e2317f3cf68625848edf5bc1d270b37d7d2af51 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -15,12 +15,13 @@ #include #include -#define REVISION_MIN 0 -#define REVISION_MAX 1 /* Counters support added */ +#define IPSET_TYPE_REV_MIN 0 +/* 1 Counters support added */ +#define IPSET_TYPE_REV_MAX 2 /* Comments support added */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jozsef Kadlecsik "); -IP_SET_MODULE_DESC("list:set", REVISION_MIN, REVISION_MAX); +IP_SET_MODULE_DESC("list:set", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); MODULE_ALIAS("ip_set_list:set"); /* Member elements */ @@ -28,28 +29,6 @@ struct set_elem { ip_set_id_t id; }; -struct sett_elem { - struct { - ip_set_id_t id; - } __attribute__ ((aligned)); - unsigned long timeout; -}; - -struct setc_elem { - struct { - ip_set_id_t id; - } __attribute__ ((aligned)); - struct ip_set_counter counter; -}; - -struct setct_elem { - struct { - ip_set_id_t id; - } __attribute__ ((aligned)); - struct ip_set_counter counter; - unsigned long timeout; -}; - struct set_adt_elem { ip_set_id_t id; ip_set_id_t refid; @@ -58,24 +37,14 @@ struct set_adt_elem { /* Type structure */ struct list_set { - size_t dsize; /* element size */ - size_t offset[IPSET_OFFSET_MAX]; /* Offsets to extensions */ u32 size; /* size of set list array */ - u32 timeout; /* timeout value */ struct timer_list gc; /* garbage collection */ + struct net *net; /* namespace */ struct set_elem members[0]; /* the set members */ }; -static inline struct set_elem * -list_set_elem(const struct list_set *map, u32 id) -{ - return (struct set_elem *)((void *)map->members + id * map->dsize); -} - -#define ext_timeout(e, m) \ -(unsigned long *)((void *)(e) + (m)->offset[IPSET_OFFSET_TIMEOUT]) -#define ext_counter(e, m) \ -(struct ip_set_counter *)((void *)(e) + (m)->offset[IPSET_OFFSET_COUNTER]) +#define list_set_elem(set, map, id) \ + (struct set_elem *)((void *)(map)->members + (id) * (set)->dsize) static int list_set_ktest(struct ip_set *set, const struct sk_buff *skb, @@ -92,16 +61,16 @@ list_set_ktest(struct ip_set *set, const struct sk_buff *skb, if (opt->cmdflags & IPSET_FLAG_SKIP_SUBCOUNTER_UPDATE) opt->cmdflags &= ~IPSET_FLAG_SKIP_COUNTER_UPDATE; for (i = 0; i < map->size; i++) { - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) return 0; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, map))) + ip_set_timeout_expired(ext_timeout(e, set))) continue; ret = ip_set_test(e->id, skb, par, opt); if (ret > 0) { if (SET_WITH_COUNTER(set)) - ip_set_update_counter(ext_counter(e, map), + ip_set_update_counter(ext_counter(e, set), ext, &opt->ext, cmdflags); return ret; @@ -121,11 +90,11 @@ list_set_kadd(struct ip_set *set, const struct sk_buff *skb, int ret; for (i = 0; i < map->size; i++) { - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) return 0; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, map))) + ip_set_timeout_expired(ext_timeout(e, set))) continue; ret = ip_set_add(e->id, skb, par, opt); if (ret == 0) @@ -145,11 +114,11 @@ list_set_kdel(struct ip_set *set, const struct sk_buff *skb, int ret; for (i = 0; i < map->size; i++) { - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) return 0; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, map))) + ip_set_timeout_expired(ext_timeout(e, set))) continue; ret = ip_set_del(e->id, skb, par, opt); if (ret == 0) @@ -163,8 +132,7 @@ list_set_kadt(struct ip_set *set, const struct sk_buff *skb, const struct xt_action_param *par, enum ipset_adt adt, struct ip_set_adt_opt *opt) { - struct list_set *map = set->data; - struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, map); + struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); switch (adt) { case IPSET_TEST: @@ -188,10 +156,10 @@ id_eq(const struct ip_set *set, u32 i, ip_set_id_t id) if (i >= map->size) return 0; - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); return !!(e->id == id && !(SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, map)))); + ip_set_timeout_expired(ext_timeout(e, set)))); } static int @@ -199,28 +167,36 @@ list_set_add(struct ip_set *set, u32 i, struct set_adt_elem *d, const struct ip_set_ext *ext) { struct list_set *map = set->data; - struct set_elem *e = list_set_elem(map, i); + struct set_elem *e = list_set_elem(set, map, i); if (e->id != IPSET_INVALID_ID) { - if (i == map->size - 1) + if (i == map->size - 1) { /* Last element replaced: e.g. add new,before,last */ - ip_set_put_byindex(e->id); - else { - struct set_elem *x = list_set_elem(map, map->size - 1); + ip_set_put_byindex(map->net, e->id); + ip_set_ext_destroy(set, e); + } else { + struct set_elem *x = list_set_elem(set, map, + map->size - 1); /* Last element pushed off */ - if (x->id != IPSET_INVALID_ID) - ip_set_put_byindex(x->id); - memmove(list_set_elem(map, i + 1), e, - map->dsize * (map->size - (i + 1))); + if (x->id != IPSET_INVALID_ID) { + ip_set_put_byindex(map->net, x->id); + ip_set_ext_destroy(set, x); + } + memmove(list_set_elem(set, map, i + 1), e, + set->dsize * (map->size - (i + 1))); + /* Extensions must be initialized to zero */ + memset(e, 0, set->dsize); } } e->id = d->id; if (SET_WITH_TIMEOUT(set)) - ip_set_timeout_set(ext_timeout(e, map), ext->timeout); + ip_set_timeout_set(ext_timeout(e, set), ext->timeout); if (SET_WITH_COUNTER(set)) - ip_set_init_counter(ext_counter(e, map), ext); + ip_set_init_counter(ext_counter(e, set), ext); + if (SET_WITH_COMMENT(set)) + ip_set_init_comment(ext_comment(e, set), ext); return 0; } @@ -228,16 +204,17 @@ static int list_set_del(struct ip_set *set, u32 i) { struct list_set *map = set->data; - struct set_elem *e = list_set_elem(map, i); + struct set_elem *e = list_set_elem(set, map, i); - ip_set_put_byindex(e->id); + ip_set_put_byindex(map->net, e->id); + ip_set_ext_destroy(set, e); if (i < map->size - 1) - memmove(e, list_set_elem(map, i + 1), - map->dsize * (map->size - (i + 1))); + memmove(e, list_set_elem(set, map, i + 1), + set->dsize * (map->size - (i + 1))); /* Last element */ - e = list_set_elem(map, map->size - 1); + e = list_set_elem(set, map, map->size - 1); e->id = IPSET_INVALID_ID; return 0; } @@ -247,13 +224,16 @@ set_cleanup_entries(struct ip_set *set) { struct list_set *map = set->data; struct set_elem *e; - u32 i; + u32 i = 0; - for (i = 0; i < map->size; i++) { - e = list_set_elem(map, i); + while (i < map->size) { + e = list_set_elem(set, map, i); if (e->id != IPSET_INVALID_ID && - ip_set_timeout_expired(ext_timeout(e, map))) + ip_set_timeout_expired(ext_timeout(e, set))) list_set_del(set, i); + /* Check element moved to position i in next loop */ + else + i++; } } @@ -268,11 +248,11 @@ list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext, int ret; for (i = 0; i < map->size; i++) { - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) return 0; else if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, map))) + ip_set_timeout_expired(ext_timeout(e, set))) continue; else if (e->id != d->id) continue; @@ -299,14 +279,14 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, bool flag_exist = flags & IPSET_FLAG_EXIST; u32 i, ret = 0; + if (SET_WITH_TIMEOUT(set)) + set_cleanup_entries(set); + /* Check already added element */ for (i = 0; i < map->size; i++) { - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) goto insert; - else if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, map))) - continue; else if (e->id != d->id) continue; @@ -319,18 +299,22 @@ list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext, /* Can't re-add */ return -IPSET_ERR_EXIST; /* Update extensions */ + ip_set_ext_destroy(set, e); + if (SET_WITH_TIMEOUT(set)) - ip_set_timeout_set(ext_timeout(e, map), ext->timeout); + ip_set_timeout_set(ext_timeout(e, set), ext->timeout); if (SET_WITH_COUNTER(set)) - ip_set_init_counter(ext_counter(e, map), ext); + ip_set_init_counter(ext_counter(e, set), ext); + if (SET_WITH_COMMENT(set)) + ip_set_init_comment(ext_comment(e, set), ext); /* Set is already added to the list */ - ip_set_put_byindex(d->id); + ip_set_put_byindex(map->net, d->id); return 0; } insert: ret = -IPSET_ERR_LIST_FULL; for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) { - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) ret = d->before != 0 ? -IPSET_ERR_REF_EXIST : list_set_add(set, i, d, ext); @@ -355,12 +339,12 @@ list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext, u32 i; for (i = 0; i < map->size; i++) { - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) return d->before != 0 ? -IPSET_ERR_REF_EXIST : -IPSET_ERR_EXIST; else if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, map))) + ip_set_timeout_expired(ext_timeout(e, set))) continue; else if (e->id != d->id) continue; @@ -386,7 +370,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], struct list_set *map = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct set_adt_elem e = { .refid = IPSET_INVALID_ID }; - struct ip_set_ext ext = IP_SET_INIT_UEXT(map); + struct ip_set_ext ext = IP_SET_INIT_UEXT(set); struct ip_set *s; int ret = 0; @@ -403,7 +387,7 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], ret = ip_set_get_extensions(set, tb, &ext); if (ret) return ret; - e.id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s); + e.id = ip_set_get_byname(map->net, nla_data(tb[IPSET_ATTR_NAME]), &s); if (e.id == IPSET_INVALID_ID) return -IPSET_ERR_NAME; /* "Loop detection" */ @@ -423,7 +407,8 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], } if (tb[IPSET_ATTR_NAMEREF]) { - e.refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]), + e.refid = ip_set_get_byname(map->net, + nla_data(tb[IPSET_ATTR_NAMEREF]), &s); if (e.refid == IPSET_INVALID_ID) { ret = -IPSET_ERR_NAMEREF; @@ -439,9 +424,9 @@ list_set_uadt(struct ip_set *set, struct nlattr *tb[], finish: if (e.refid != IPSET_INVALID_ID) - ip_set_put_byindex(e.refid); + ip_set_put_byindex(map->net, e.refid); if (adt != IPSET_ADD || ret) - ip_set_put_byindex(e.id); + ip_set_put_byindex(map->net, e.id); return ip_set_eexist(ret, flags) ? 0 : ret; } @@ -454,9 +439,10 @@ list_set_flush(struct ip_set *set) u32 i; for (i = 0; i < map->size; i++) { - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); if (e->id != IPSET_INVALID_ID) { - ip_set_put_byindex(e->id); + ip_set_put_byindex(map->net, e->id); + ip_set_ext_destroy(set, e); e->id = IPSET_INVALID_ID; } } @@ -485,14 +471,11 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) if (!nested) goto nla_put_failure; if (nla_put_net32(skb, IPSET_ATTR_SIZE, htonl(map->size)) || - (SET_WITH_TIMEOUT(set) && - nla_put_net32(skb, IPSET_ATTR_TIMEOUT, htonl(map->timeout))) || - (SET_WITH_COUNTER(set) && - nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, - htonl(IPSET_FLAG_WITH_COUNTERS))) || nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref - 1)) || nla_put_net32(skb, IPSET_ATTR_MEMSIZE, - htonl(sizeof(*map) + map->size * map->dsize))) + htonl(sizeof(*map) + map->size * set->dsize))) + goto nla_put_failure; + if (unlikely(ip_set_put_flags(skb, set))) goto nla_put_failure; ipset_nest_end(skb, nested); @@ -507,19 +490,20 @@ list_set_list(const struct ip_set *set, { const struct list_set *map = set->data; struct nlattr *atd, *nested; - u32 i, first = cb->args[2]; + u32 i, first = cb->args[IPSET_CB_ARG0]; const struct set_elem *e; atd = ipset_nest_start(skb, IPSET_ATTR_ADT); if (!atd) return -EMSGSIZE; - for (; cb->args[2] < map->size; cb->args[2]++) { - i = cb->args[2]; - e = list_set_elem(map, i); + for (; cb->args[IPSET_CB_ARG0] < map->size; + cb->args[IPSET_CB_ARG0]++) { + i = cb->args[IPSET_CB_ARG0]; + e = list_set_elem(set, map, i); if (e->id == IPSET_INVALID_ID) goto finish; if (SET_WITH_TIMEOUT(set) && - ip_set_timeout_expired(ext_timeout(e, map))) + ip_set_timeout_expired(ext_timeout(e, set))) continue; nested = ipset_nest_start(skb, IPSET_ATTR_DATA); if (!nested) { @@ -530,31 +514,25 @@ list_set_list(const struct ip_set *set, goto nla_put_failure; } if (nla_put_string(skb, IPSET_ATTR_NAME, - ip_set_name_byindex(e->id))) - goto nla_put_failure; - if (SET_WITH_TIMEOUT(set) && - nla_put_net32(skb, IPSET_ATTR_TIMEOUT, - htonl(ip_set_timeout_get( - ext_timeout(e, map))))) + ip_set_name_byindex(map->net, e->id))) goto nla_put_failure; - if (SET_WITH_COUNTER(set) && - ip_set_put_counter(skb, ext_counter(e, map))) + if (ip_set_put_extensions(skb, set, e, true)) goto nla_put_failure; ipset_nest_end(skb, nested); } finish: ipset_nest_end(skb, atd); /* Set listing finished */ - cb->args[2] = 0; + cb->args[IPSET_CB_ARG0] = 0; return 0; nla_put_failure: nla_nest_cancel(skb, nested); - ipset_nest_end(skb, atd); if (unlikely(i == first)) { - cb->args[2] = 0; + cb->args[IPSET_CB_ARG0] = 0; return -EMSGSIZE; } + ipset_nest_end(skb, atd); return 0; } @@ -565,7 +543,7 @@ list_set_same_set(const struct ip_set *a, const struct ip_set *b) const struct list_set *y = b->data; return x->size == y->size && - x->timeout == y->timeout && + a->timeout == b->timeout && a->extensions == b->extensions; } @@ -594,7 +572,7 @@ list_set_gc(unsigned long ul_set) set_cleanup_entries(set); write_unlock_bh(&set->lock); - map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; add_timer(&map->gc); } @@ -606,43 +584,40 @@ list_set_gc_init(struct ip_set *set, void (*gc)(unsigned long ul_set)) init_timer(&map->gc); map->gc.data = (unsigned long) set; map->gc.function = gc; - map->gc.expires = jiffies + IPSET_GC_PERIOD(map->timeout) * HZ; + map->gc.expires = jiffies + IPSET_GC_PERIOD(set->timeout) * HZ; add_timer(&map->gc); } /* Create list:set type of sets */ -static struct list_set * -init_list_set(struct ip_set *set, u32 size, size_t dsize, - unsigned long timeout) +static bool +init_list_set(struct net *net, struct ip_set *set, u32 size) { struct list_set *map; struct set_elem *e; u32 i; - map = kzalloc(sizeof(*map) + size * dsize, GFP_KERNEL); + map = kzalloc(sizeof(*map) + size * set->dsize, GFP_KERNEL); if (!map) - return NULL; + return false; map->size = size; - map->dsize = dsize; - map->timeout = timeout; + map->net = net; set->data = map; for (i = 0; i < size; i++) { - e = list_set_elem(map, i); + e = list_set_elem(set, map, i); e->id = IPSET_INVALID_ID; } - return map; + return true; } static int -list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) +list_set_create(struct net *net, struct ip_set *set, struct nlattr *tb[], + u32 flags) { - struct list_set *map; - u32 size = IP_SET_LIST_DEFAULT_SIZE, cadt_flags = 0; - unsigned long timeout = 0; + u32 size = IP_SET_LIST_DEFAULT_SIZE; if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_SIZE) || !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || @@ -654,45 +629,13 @@ list_set_create(struct ip_set *set, struct nlattr *tb[], u32 flags) if (size < IP_SET_LIST_MIN_SIZE) size = IP_SET_LIST_MIN_SIZE; - if (tb[IPSET_ATTR_CADT_FLAGS]) - cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); - if (tb[IPSET_ATTR_TIMEOUT]) - timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); set->variant = &set_variant; - if (cadt_flags & IPSET_FLAG_WITH_COUNTERS) { - set->extensions |= IPSET_EXT_COUNTER; - if (tb[IPSET_ATTR_TIMEOUT]) { - map = init_list_set(set, size, - sizeof(struct setct_elem), timeout); - if (!map) - return -ENOMEM; - set->extensions |= IPSET_EXT_TIMEOUT; - map->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct setct_elem, timeout); - map->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct setct_elem, counter); - list_set_gc_init(set, list_set_gc); - } else { - map = init_list_set(set, size, - sizeof(struct setc_elem), 0); - if (!map) - return -ENOMEM; - map->offset[IPSET_OFFSET_COUNTER] = - offsetof(struct setc_elem, counter); - } - } else if (tb[IPSET_ATTR_TIMEOUT]) { - map = init_list_set(set, size, - sizeof(struct sett_elem), timeout); - if (!map) - return -ENOMEM; - set->extensions |= IPSET_EXT_TIMEOUT; - map->offset[IPSET_OFFSET_TIMEOUT] = - offsetof(struct sett_elem, timeout); + set->dsize = ip_set_elem_len(set, tb, sizeof(struct set_elem)); + if (!init_list_set(net, set, size)) + return -ENOMEM; + if (tb[IPSET_ATTR_TIMEOUT]) { + set->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); list_set_gc_init(set, list_set_gc); - } else { - map = init_list_set(set, size, sizeof(struct set_elem), 0); - if (!map) - return -ENOMEM; } return 0; } @@ -703,8 +646,8 @@ static struct ip_set_type list_set_type __read_mostly = { .features = IPSET_TYPE_NAME | IPSET_DUMP_LAST, .dimension = IPSET_DIM_ONE, .family = NFPROTO_UNSPEC, - .revision_min = REVISION_MIN, - .revision_max = REVISION_MAX, + .revision_min = IPSET_TYPE_REV_MIN, + .revision_max = IPSET_TYPE_REV_MAX, .create = list_set_create, .create_policy = { [IPSET_ATTR_SIZE] = { .type = NLA_U32 }, @@ -721,6 +664,7 @@ static struct ip_set_type list_set_type __read_mostly = { [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, + [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, }, .me = THIS_MODULE, }; diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 74fd00c272100d5271533689abf074c5f9e8c454..4f26ee46b51fca7e8da77744edb816d2ac1c87aa 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -1139,12 +1139,6 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) ip_vs_fill_iph_skb(af, skb, &iph); #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { - if (!iph.fragoffs && skb_nfct_reasm(skb)) { - struct sk_buff *reasm = skb_nfct_reasm(skb); - /* Save fw mark for coming frags */ - reasm->ipvs_property = 1; - reasm->mark = skb->mark; - } if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { int related; int verdict = ip_vs_out_icmp_v6(skb, &related, @@ -1239,11 +1233,11 @@ ip_vs_out(unsigned int hooknum, struct sk_buff *skb, int af) * Check if packet is reply for established ip_vs_conn. */ static unsigned int -ip_vs_reply4(unsigned int hooknum, struct sk_buff *skb, +ip_vs_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_out(hooknum, skb, AF_INET); + return ip_vs_out(ops->hooknum, skb, AF_INET); } /* @@ -1251,11 +1245,11 @@ ip_vs_reply4(unsigned int hooknum, struct sk_buff *skb, * Check if packet is reply for established ip_vs_conn. */ static unsigned int -ip_vs_local_reply4(unsigned int hooknum, struct sk_buff *skb, +ip_vs_local_reply4(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_out(hooknum, skb, AF_INET); + return ip_vs_out(ops->hooknum, skb, AF_INET); } #ifdef CONFIG_IP_VS_IPV6 @@ -1266,11 +1260,11 @@ ip_vs_local_reply4(unsigned int hooknum, struct sk_buff *skb, * Check if packet is reply for established ip_vs_conn. */ static unsigned int -ip_vs_reply6(unsigned int hooknum, struct sk_buff *skb, +ip_vs_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_out(hooknum, skb, AF_INET6); + return ip_vs_out(ops->hooknum, skb, AF_INET6); } /* @@ -1278,11 +1272,11 @@ ip_vs_reply6(unsigned int hooknum, struct sk_buff *skb, * Check if packet is reply for established ip_vs_conn. */ static unsigned int -ip_vs_local_reply6(unsigned int hooknum, struct sk_buff *skb, +ip_vs_local_reply6(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_out(hooknum, skb, AF_INET6); + return ip_vs_out(ops->hooknum, skb, AF_INET6); } #endif @@ -1614,12 +1608,6 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) #ifdef CONFIG_IP_VS_IPV6 if (af == AF_INET6) { - if (!iph.fragoffs && skb_nfct_reasm(skb)) { - struct sk_buff *reasm = skb_nfct_reasm(skb); - /* Save fw mark for coming frags. */ - reasm->ipvs_property = 1; - reasm->mark = skb->mark; - } if (unlikely(iph.protocol == IPPROTO_ICMPV6)) { int related; int verdict = ip_vs_in_icmp_v6(skb, &related, hooknum, @@ -1671,9 +1659,8 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) /* sorry, all this trouble for a no-hit :) */ IP_VS_DBG_PKT(12, af, pp, skb, 0, "ip_vs_in: packet continues traversal as normal"); - if (iph.fragoffs && !skb_nfct_reasm(skb)) { + if (iph.fragoffs) { /* Fragment that couldn't be mapped to a conn entry - * and don't have any pointer to a reasm skb * is missing module nf_defrag_ipv6 */ IP_VS_DBG_RL("Unhandled frag, load nf_defrag_ipv6\n"); @@ -1733,12 +1720,12 @@ ip_vs_in(unsigned int hooknum, struct sk_buff *skb, int af) * Schedule and forward packets from remote clients */ static unsigned int -ip_vs_remote_request4(unsigned int hooknum, struct sk_buff *skb, +ip_vs_remote_request4(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_in(hooknum, skb, AF_INET); + return ip_vs_in(ops->hooknum, skb, AF_INET); } /* @@ -1746,58 +1733,26 @@ ip_vs_remote_request4(unsigned int hooknum, struct sk_buff *skb, * Schedule and forward packets from local clients */ static unsigned int -ip_vs_local_request4(unsigned int hooknum, struct sk_buff *skb, +ip_vs_local_request4(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_in(hooknum, skb, AF_INET); + return ip_vs_in(ops->hooknum, skb, AF_INET); } #ifdef CONFIG_IP_VS_IPV6 -/* - * AF_INET6 fragment handling - * Copy info from first fragment, to the rest of them. - */ -static unsigned int -ip_vs_preroute_frag6(unsigned int hooknum, struct sk_buff *skb, - const struct net_device *in, - const struct net_device *out, - int (*okfn)(struct sk_buff *)) -{ - struct sk_buff *reasm = skb_nfct_reasm(skb); - struct net *net; - - /* Skip if not a "replay" from nf_ct_frag6_output or first fragment. - * ipvs_property is set when checking first fragment - * in ip_vs_in() and ip_vs_out(). - */ - if (reasm) - IP_VS_DBG(2, "Fragment recv prop:%d\n", reasm->ipvs_property); - if (!reasm || !reasm->ipvs_property) - return NF_ACCEPT; - - net = skb_net(skb); - if (!net_ipvs(net)->enable) - return NF_ACCEPT; - - /* Copy stored fw mark, saved in ip_vs_{in,out} */ - skb->mark = reasm->mark; - - return NF_ACCEPT; -} - /* * AF_INET6 handler in NF_INET_LOCAL_IN chain * Schedule and forward packets from remote clients */ static unsigned int -ip_vs_remote_request6(unsigned int hooknum, struct sk_buff *skb, +ip_vs_remote_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_in(hooknum, skb, AF_INET6); + return ip_vs_in(ops->hooknum, skb, AF_INET6); } /* @@ -1805,11 +1760,11 @@ ip_vs_remote_request6(unsigned int hooknum, struct sk_buff *skb, * Schedule and forward packets from local clients */ static unsigned int -ip_vs_local_request6(unsigned int hooknum, struct sk_buff *skb, +ip_vs_local_request6(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return ip_vs_in(hooknum, skb, AF_INET6); + return ip_vs_in(ops->hooknum, skb, AF_INET6); } #endif @@ -1825,7 +1780,7 @@ ip_vs_local_request6(unsigned int hooknum, struct sk_buff *skb, * and send them to ip_vs_in_icmp. */ static unsigned int -ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb, +ip_vs_forward_icmp(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { @@ -1842,12 +1797,12 @@ ip_vs_forward_icmp(unsigned int hooknum, struct sk_buff *skb, if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable)) return NF_ACCEPT; - return ip_vs_in_icmp(skb, &r, hooknum); + return ip_vs_in_icmp(skb, &r, ops->hooknum); } #ifdef CONFIG_IP_VS_IPV6 static unsigned int -ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb, +ip_vs_forward_icmp_v6(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)) { @@ -1866,7 +1821,7 @@ ip_vs_forward_icmp_v6(unsigned int hooknum, struct sk_buff *skb, if (unlikely(sysctl_backup_only(ipvs) || !ipvs->enable)) return NF_ACCEPT; - return ip_vs_in_icmp_v6(skb, &r, hooknum, &iphdr); + return ip_vs_in_icmp_v6(skb, &r, ops->hooknum, &iphdr); } #endif @@ -1924,14 +1879,6 @@ static struct nf_hook_ops ip_vs_ops[] __read_mostly = { .priority = 100, }, #ifdef CONFIG_IP_VS_IPV6 - /* After mangle & nat fetch 2:nd fragment and following */ - { - .hook = ip_vs_preroute_frag6, - .owner = THIS_MODULE, - .pf = NFPROTO_IPV6, - .hooknum = NF_INET_PRE_ROUTING, - .priority = NF_IP6_PRI_NAT_DST + 1, - }, /* After packet filtering, change source only for VS/NAT */ { .hook = ip_vs_reply6, diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index a3df9bddc4f76251a8722792d8e9f15478546ac1..62786a495cea481a3fd18d063b7174cdb36052ad 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -704,7 +704,7 @@ static void ip_vs_dest_free(struct ip_vs_dest *dest) __ip_vs_dst_cache_reset(dest); __ip_vs_svc_put(svc, false); free_percpu(dest->stats.cpustats); - kfree(dest); + ip_vs_dest_put_and_free(dest); } /* @@ -3820,10 +3820,6 @@ void __net_exit ip_vs_control_net_cleanup(struct net *net) { struct netns_ipvs *ipvs = net_ipvs(net); - /* Some dest can be in grace period even before cleanup, we have to - * defer ip_vs_trash_cleanup until ip_vs_dest_wait_readers is called. - */ - rcu_barrier(); ip_vs_trash_cleanup(net); ip_vs_stop_estimator(net, &ipvs->tot_stats); ip_vs_control_net_cleanup_sysctl(net); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index eff13c94498e068173c66b6a481a6264d038ffea..ca056a331e60b23f1b6e542fd640b2b996878335 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -136,7 +136,7 @@ static void ip_vs_lblc_rcu_free(struct rcu_head *head) struct ip_vs_lblc_entry, rcu_head); - ip_vs_dest_put(en->dest); + ip_vs_dest_put_and_free(en->dest); kfree(en); } diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index 0b8550089a2e580e7feba0723117f1c849048a39..3f21a2f47de1ffc6be71bcc93b6c35be98f8ae3a 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -130,7 +130,7 @@ static void ip_vs_lblcr_elem_rcu_free(struct rcu_head *head) struct ip_vs_dest_set_elem *e; e = container_of(head, struct ip_vs_dest_set_elem, rcu_head); - ip_vs_dest_put(e->dest); + ip_vs_dest_put_and_free(e->dest); kfree(e); } diff --git a/net/netfilter/ipvs/ip_vs_pe_sip.c b/net/netfilter/ipvs/ip_vs_pe_sip.c index 9ef22bdce9f192344f524c5337b60878dab8e480..bed5f7042529de23ef209806c7ad5607ac363a74 100644 --- a/net/netfilter/ipvs/ip_vs_pe_sip.c +++ b/net/netfilter/ipvs/ip_vs_pe_sip.c @@ -65,7 +65,6 @@ static int get_callid(const char *dptr, unsigned int dataoff, static int ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb) { - struct sk_buff *reasm = skb_nfct_reasm(skb); struct ip_vs_iphdr iph; unsigned int dataoff, datalen, matchoff, matchlen; const char *dptr; @@ -79,15 +78,10 @@ ip_vs_sip_fill_param(struct ip_vs_conn_param *p, struct sk_buff *skb) /* todo: IPv6 fragments: * I think this only should be done for the first fragment. /HS */ - if (reasm) { - skb = reasm; - dataoff = iph.thoff_reasm + sizeof(struct udphdr); - } else - dataoff = iph.len + sizeof(struct udphdr); + dataoff = iph.len + sizeof(struct udphdr); if (dataoff >= skb->len) return -EINVAL; - /* todo: Check if this will mess-up the reasm skb !!! /HS */ retc = skb_linearize(skb); if (retc < 0) return retc; diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 23e596e438b3fb4e51a97782c4cf00ad7d33af68..2f7ea7564044ccd0d8600a2b6688cb6603645588 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -20,13 +20,18 @@ sctp_conn_schedule(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, sctp_sctphdr_t *sh, _sctph; sh = skb_header_pointer(skb, iph->len, sizeof(_sctph), &_sctph); - if (sh == NULL) + if (sh == NULL) { + *verdict = NF_DROP; return 0; + } sch = skb_header_pointer(skb, iph->len + sizeof(sctp_sctphdr_t), sizeof(_schunkh), &_schunkh); - if (sch == NULL) + if (sch == NULL) { + *verdict = NF_DROP; return 0; + } + net = skb_net(skb); ipvs = net_ipvs(net); rcu_read_lock(); @@ -76,6 +81,7 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, { sctp_sctphdr_t *sctph; unsigned int sctphoff = iph->len; + bool payload_csum = false; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6 && iph->fragoffs) @@ -87,19 +93,31 @@ sctp_snat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, return 0; if (unlikely(cp->app != NULL)) { + int ret; + /* Some checks before mangling */ if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) return 0; /* Call application helper if needed */ - if (!ip_vs_app_pkt_out(cp, skb)) + ret = ip_vs_app_pkt_out(cp, skb); + if (ret == 0) return 0; + /* ret=2: csum update is needed after payload mangling */ + if (ret == 2) + payload_csum = true; } sctph = (void *) skb_network_header(skb) + sctphoff; - sctph->source = cp->vport; - sctp_nat_csum(skb, sctph, sctphoff); + /* Only update csum if we really have to */ + if (sctph->source != cp->vport || payload_csum || + skb->ip_summed == CHECKSUM_PARTIAL) { + sctph->source = cp->vport; + sctp_nat_csum(skb, sctph, sctphoff); + } else { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } return 1; } @@ -110,6 +128,7 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, { sctp_sctphdr_t *sctph; unsigned int sctphoff = iph->len; + bool payload_csum = false; #ifdef CONFIG_IP_VS_IPV6 if (cp->af == AF_INET6 && iph->fragoffs) @@ -121,19 +140,32 @@ sctp_dnat_handler(struct sk_buff *skb, struct ip_vs_protocol *pp, return 0; if (unlikely(cp->app != NULL)) { + int ret; + /* Some checks before mangling */ if (pp->csum_check && !pp->csum_check(cp->af, skb, pp)) return 0; /* Call application helper if needed */ - if (!ip_vs_app_pkt_in(cp, skb)) + ret = ip_vs_app_pkt_in(cp, skb); + if (ret == 0) return 0; + /* ret=2: csum update is needed after payload mangling */ + if (ret == 2) + payload_csum = true; } sctph = (void *) skb_network_header(skb) + sctphoff; - sctph->dest = cp->dport; - sctp_nat_csum(skb, sctph, sctphoff); + /* Only update csum if we really have to */ + if (sctph->dest != cp->dport || payload_csum || + (skb->ip_summed == CHECKSUM_PARTIAL && + !(skb_dst(skb)->dev->features & NETIF_F_SCTP_CSUM))) { + sctph->dest = cp->dport; + sctp_nat_csum(skb, sctph, sctphoff); + } else if (skb->ip_summed != CHECKSUM_PARTIAL) { + skb->ip_summed = CHECKSUM_UNNECESSARY; + } return 1; } diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index 3588faebe5298149b918614656ca8b6ee7d12e4e..cc65b2f42cd40283fab4ed5a24a82767d8190a89 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -115,27 +115,46 @@ ip_vs_sh_get(struct ip_vs_service *svc, struct ip_vs_sh_state *s, } -/* As ip_vs_sh_get, but with fallback if selected server is unavailable */ +/* As ip_vs_sh_get, but with fallback if selected server is unavailable + * + * The fallback strategy loops around the table starting from a "random" + * point (in fact, it is chosen to be the original hash value to make the + * algorithm deterministic) to find a new server. + */ static inline struct ip_vs_dest * ip_vs_sh_get_fallback(struct ip_vs_service *svc, struct ip_vs_sh_state *s, const union nf_inet_addr *addr, __be16 port) { - unsigned int offset; - unsigned int hash; + unsigned int offset, roffset; + unsigned int hash, ihash; struct ip_vs_dest *dest; + /* first try the dest it's supposed to go to */ + ihash = ip_vs_sh_hashkey(svc->af, addr, port, 0); + dest = rcu_dereference(s->buckets[ihash].dest); + if (!dest) + return NULL; + if (!is_unavailable(dest)) + return dest; + + IP_VS_DBG_BUF(6, "SH: selected unavailable server %s:%d, reselecting", + IP_VS_DBG_ADDR(svc->af, &dest->addr), ntohs(dest->port)); + + /* if the original dest is unavailable, loop around the table + * starting from ihash to find a new dest + */ for (offset = 0; offset < IP_VS_SH_TAB_SIZE; offset++) { - hash = ip_vs_sh_hashkey(svc->af, addr, port, offset); + roffset = (offset + ihash) % IP_VS_SH_TAB_SIZE; + hash = ip_vs_sh_hashkey(svc->af, addr, port, roffset); dest = rcu_dereference(s->buckets[hash].dest); if (!dest) break; - if (is_unavailable(dest)) - IP_VS_DBG_BUF(6, "SH: selected unavailable server " - "%s:%d (offset %d)", - IP_VS_DBG_ADDR(svc->af, &dest->addr), - ntohs(dest->port), offset); - else + if (!is_unavailable(dest)) return dest; + IP_VS_DBG_BUF(6, "SH: selected unavailable " + "server %s:%d (offset %d), reselecting", + IP_VS_DBG_ADDR(svc->af, &dest->addr), + ntohs(dest->port), roffset); } return NULL; diff --git a/net/netfilter/nf_conntrack_acct.c b/net/netfilter/nf_conntrack_acct.c index 2d3030ab5b619c1a354ec328acf2e85bfd6fd250..a4b5e2a435acb4c2fafaf26ccb4fce349a151f9a 100644 --- a/net/netfilter/nf_conntrack_acct.c +++ b/net/netfilter/nf_conntrack_acct.c @@ -39,21 +39,23 @@ static struct ctl_table acct_sysctl_table[] = { unsigned int seq_print_acct(struct seq_file *s, const struct nf_conn *ct, int dir) { - struct nf_conn_counter *acct; + struct nf_conn_acct *acct; + struct nf_conn_counter *counter; acct = nf_conn_acct_find(ct); if (!acct) return 0; + counter = acct->counter; return seq_printf(s, "packets=%llu bytes=%llu ", - (unsigned long long)atomic64_read(&acct[dir].packets), - (unsigned long long)atomic64_read(&acct[dir].bytes)); + (unsigned long long)atomic64_read(&counter[dir].packets), + (unsigned long long)atomic64_read(&counter[dir].bytes)); }; EXPORT_SYMBOL_GPL(seq_print_acct); static struct nf_ct_ext_type acct_extend __read_mostly = { - .len = sizeof(struct nf_conn_counter[IP_CT_DIR_MAX]), - .align = __alignof__(struct nf_conn_counter[IP_CT_DIR_MAX]), + .len = sizeof(struct nf_conn_acct), + .align = __alignof__(struct nf_conn_acct), .id = NF_CT_EXT_ACCT, }; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 5d892febd64ca88d957d6eeffac5de12daeaff77..e22d950c60b3c2a6e4ce7112ce13b9cb54e14d7a 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1109,12 +1109,14 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, acct: if (do_acct) { - struct nf_conn_counter *acct; + struct nf_conn_acct *acct; acct = nf_conn_acct_find(ct); if (acct) { - atomic64_inc(&acct[CTINFO2DIR(ctinfo)].packets); - atomic64_add(skb->len, &acct[CTINFO2DIR(ctinfo)].bytes); + struct nf_conn_counter *counter = acct->counter; + + atomic64_inc(&counter[CTINFO2DIR(ctinfo)].packets); + atomic64_add(skb->len, &counter[CTINFO2DIR(ctinfo)].bytes); } } } @@ -1126,13 +1128,15 @@ bool __nf_ct_kill_acct(struct nf_conn *ct, int do_acct) { if (do_acct) { - struct nf_conn_counter *acct; + struct nf_conn_acct *acct; acct = nf_conn_acct_find(ct); if (acct) { - atomic64_inc(&acct[CTINFO2DIR(ctinfo)].packets); + struct nf_conn_counter *counter = acct->counter; + + atomic64_inc(&counter[CTINFO2DIR(ctinfo)].packets); atomic64_add(skb->len - skb_network_offset(skb), - &acct[CTINFO2DIR(ctinfo)].bytes); + &counter[CTINFO2DIR(ctinfo)].bytes); } } diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index eea936b70d15407be5f69681de0a8ecf0fbcbc05..08870b859046fb17f40125b7e1f8faeb42d93061 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -211,13 +211,23 @@ nla_put_failure: } static int -dump_counters(struct sk_buff *skb, u64 pkts, u64 bytes, - enum ip_conntrack_dir dir) +dump_counters(struct sk_buff *skb, struct nf_conn_acct *acct, + enum ip_conntrack_dir dir, int type) { - enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; + enum ctattr_type attr = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; + struct nf_conn_counter *counter = acct->counter; struct nlattr *nest_count; + u64 pkts, bytes; - nest_count = nla_nest_start(skb, type | NLA_F_NESTED); + if (type == IPCTNL_MSG_CT_GET_CTRZERO) { + pkts = atomic64_xchg(&counter[dir].packets, 0); + bytes = atomic64_xchg(&counter[dir].bytes, 0); + } else { + pkts = atomic64_read(&counter[dir].packets); + bytes = atomic64_read(&counter[dir].bytes); + } + + nest_count = nla_nest_start(skb, attr | NLA_F_NESTED); if (!nest_count) goto nla_put_failure; @@ -234,24 +244,19 @@ nla_put_failure: } static int -ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, - enum ip_conntrack_dir dir, int type) +ctnetlink_dump_acct(struct sk_buff *skb, const struct nf_conn *ct, int type) { - struct nf_conn_counter *acct; - u64 pkts, bytes; + struct nf_conn_acct *acct = nf_conn_acct_find(ct); - acct = nf_conn_acct_find(ct); if (!acct) return 0; - if (type == IPCTNL_MSG_CT_GET_CTRZERO) { - pkts = atomic64_xchg(&acct[dir].packets, 0); - bytes = atomic64_xchg(&acct[dir].bytes, 0); - } else { - pkts = atomic64_read(&acct[dir].packets); - bytes = atomic64_read(&acct[dir].bytes); - } - return dump_counters(skb, pkts, bytes, dir); + if (dump_counters(skb, acct, IP_CT_DIR_ORIGINAL, type) < 0) + return -1; + if (dump_counters(skb, acct, IP_CT_DIR_REPLY, type) < 0) + return -1; + + return 0; } static int @@ -488,8 +493,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, if (ctnetlink_dump_status(skb, ct) < 0 || ctnetlink_dump_timeout(skb, ct) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL, type) < 0 || - ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY, type) < 0 || + ctnetlink_dump_acct(skb, ct, type) < 0 || ctnetlink_dump_timestamp(skb, ct) < 0 || ctnetlink_dump_protoinfo(skb, ct) < 0 || ctnetlink_dump_helpinfo(skb, ct) < 0 || @@ -530,7 +534,7 @@ ctnetlink_proto_size(const struct nf_conn *ct) } static inline size_t -ctnetlink_counters_size(const struct nf_conn *ct) +ctnetlink_acct_size(const struct nf_conn *ct) { if (!nf_ct_ext_exist(ct, NF_CT_EXT_ACCT)) return 0; @@ -579,7 +583,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct) + 3 * nla_total_size(sizeof(u_int8_t)) /* CTA_PROTO_NUM */ + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */ + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */ - + ctnetlink_counters_size(ct) + + ctnetlink_acct_size(ct) + ctnetlink_timestamp_size(ct) + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */ + nla_total_size(0) /* CTA_PROTOINFO */ @@ -673,10 +677,7 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) goto nla_put_failure; if (events & (1 << IPCT_DESTROY)) { - if (ctnetlink_dump_counters(skb, ct, - IP_CT_DIR_ORIGINAL, type) < 0 || - ctnetlink_dump_counters(skb, ct, - IP_CT_DIR_REPLY, type) < 0 || + if (ctnetlink_dump_acct(skb, ct, type) < 0 || ctnetlink_dump_timestamp(skb, ct) < 0) goto nla_put_failure; } else { diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index e0c4373b47478d4d72899d95166566db34a8bcfa..466410eaa482c2a3940d3768351e92be17a644ef 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -52,66 +52,8 @@ module_param(sip_direct_media, int, 0600); MODULE_PARM_DESC(sip_direct_media, "Expect Media streams between signalling " "endpoints only (default 1)"); -unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, const char **dptr, - unsigned int *datalen) __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_sip_hook); - -void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int protoff, - s16 off) __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_sip_seq_adjust_hook); - -unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb, - unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - struct nf_conntrack_expect *exp, - unsigned int matchoff, - unsigned int matchlen) __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_sip_expect_hook); - -unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - unsigned int sdpoff, - enum sdp_header_types type, - enum sdp_header_types term, - const union nf_inet_addr *addr) - __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_sdp_addr_hook); - -unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - unsigned int matchoff, - unsigned int matchlen, - u_int16_t port) __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_sdp_port_hook); - -unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb, - unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - unsigned int sdpoff, - const union nf_inet_addr *addr) - __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_sdp_session_hook); - -unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff, - unsigned int dataoff, - const char **dptr, - unsigned int *datalen, - struct nf_conntrack_expect *rtp_exp, - struct nf_conntrack_expect *rtcp_exp, - unsigned int mediaoff, - unsigned int medialen, - union nf_inet_addr *rtp_addr) - __read_mostly; -EXPORT_SYMBOL_GPL(nf_nat_sdp_media_hook); +const struct nf_nat_sip_hooks *nf_nat_sip_hooks; +EXPORT_SYMBOL_GPL(nf_nat_sip_hooks); static int string_len(const struct nf_conn *ct, const char *dptr, const char *limit, int *shift) @@ -914,8 +856,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, int direct_rtp = 0, skip_expect = 0, ret = NF_DROP; u_int16_t base_port; __be16 rtp_port, rtcp_port; - typeof(nf_nat_sdp_port_hook) nf_nat_sdp_port; - typeof(nf_nat_sdp_media_hook) nf_nat_sdp_media; + const struct nf_nat_sip_hooks *hooks; saddr = NULL; if (sip_direct_media) { @@ -966,22 +907,23 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, #endif skip_expect = 1; } while (!skip_expect); - rcu_read_unlock(); base_port = ntohs(tuple.dst.u.udp.port) & ~1; rtp_port = htons(base_port); rtcp_port = htons(base_port + 1); if (direct_rtp) { - nf_nat_sdp_port = rcu_dereference(nf_nat_sdp_port_hook); - if (nf_nat_sdp_port && - !nf_nat_sdp_port(skb, protoff, dataoff, dptr, datalen, + hooks = rcu_dereference(nf_nat_sip_hooks); + if (hooks && + !hooks->sdp_port(skb, protoff, dataoff, dptr, datalen, mediaoff, medialen, ntohs(rtp_port))) goto err1; } - if (skip_expect) + if (skip_expect) { + rcu_read_unlock(); return NF_ACCEPT; + } rtp_exp = nf_ct_expect_alloc(ct); if (rtp_exp == NULL) @@ -995,10 +937,10 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, nf_ct_expect_init(rtcp_exp, class, nf_ct_l3num(ct), saddr, daddr, IPPROTO_UDP, NULL, &rtcp_port); - nf_nat_sdp_media = rcu_dereference(nf_nat_sdp_media_hook); - if (nf_nat_sdp_media && ct->status & IPS_NAT_MASK && !direct_rtp) - ret = nf_nat_sdp_media(skb, protoff, dataoff, dptr, datalen, - rtp_exp, rtcp_exp, + hooks = rcu_dereference(nf_nat_sip_hooks); + if (hooks && ct->status & IPS_NAT_MASK && !direct_rtp) + ret = hooks->sdp_media(skb, protoff, dataoff, dptr, + datalen, rtp_exp, rtcp_exp, mediaoff, medialen, daddr); else { if (nf_ct_expect_related(rtp_exp) == 0) { @@ -1012,6 +954,7 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, err2: nf_ct_expect_put(rtp_exp); err1: + rcu_read_unlock(); return ret; } @@ -1051,13 +994,12 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, unsigned int caddr_len, maddr_len; unsigned int i; union nf_inet_addr caddr, maddr, rtp_addr; + const struct nf_nat_sip_hooks *hooks; unsigned int port; const struct sdp_media_type *t; int ret = NF_ACCEPT; - typeof(nf_nat_sdp_addr_hook) nf_nat_sdp_addr; - typeof(nf_nat_sdp_session_hook) nf_nat_sdp_session; - nf_nat_sdp_addr = rcu_dereference(nf_nat_sdp_addr_hook); + hooks = rcu_dereference(nf_nat_sip_hooks); /* Find beginning of session description */ if (ct_sip_get_sdp_header(ct, *dptr, 0, *datalen, @@ -1125,10 +1067,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, } /* Update media connection address if present */ - if (maddr_len && nf_nat_sdp_addr && ct->status & IPS_NAT_MASK) { - ret = nf_nat_sdp_addr(skb, protoff, dataoff, + if (maddr_len && hooks && ct->status & IPS_NAT_MASK) { + ret = hooks->sdp_addr(skb, protoff, dataoff, dptr, datalen, mediaoff, - SDP_HDR_CONNECTION, SDP_HDR_MEDIA, + SDP_HDR_CONNECTION, + SDP_HDR_MEDIA, &rtp_addr); if (ret != NF_ACCEPT) { nf_ct_helper_log(skb, ct, "cannot mangle SDP"); @@ -1139,10 +1082,11 @@ static int process_sdp(struct sk_buff *skb, unsigned int protoff, } /* Update session connection and owner addresses */ - nf_nat_sdp_session = rcu_dereference(nf_nat_sdp_session_hook); - if (nf_nat_sdp_session && ct->status & IPS_NAT_MASK) - ret = nf_nat_sdp_session(skb, protoff, dataoff, - dptr, datalen, sdpoff, &rtp_addr); + hooks = rcu_dereference(nf_nat_sip_hooks); + if (hooks && ct->status & IPS_NAT_MASK) + ret = hooks->sdp_session(skb, protoff, dataoff, + dptr, datalen, sdpoff, + &rtp_addr); return ret; } @@ -1242,11 +1186,11 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, unsigned int matchoff, matchlen; struct nf_conntrack_expect *exp; union nf_inet_addr *saddr, daddr; + const struct nf_nat_sip_hooks *hooks; __be16 port; u8 proto; unsigned int expires = 0; int ret; - typeof(nf_nat_sip_expect_hook) nf_nat_sip_expect; /* Expected connections can not register again. */ if (ct->status & IPS_EXPECTED) @@ -1309,10 +1253,10 @@ static int process_register_request(struct sk_buff *skb, unsigned int protoff, exp->helper = nfct_help(ct)->helper; exp->flags = NF_CT_EXPECT_PERMANENT | NF_CT_EXPECT_INACTIVE; - nf_nat_sip_expect = rcu_dereference(nf_nat_sip_expect_hook); - if (nf_nat_sip_expect && ct->status & IPS_NAT_MASK) - ret = nf_nat_sip_expect(skb, protoff, dataoff, dptr, datalen, - exp, matchoff, matchlen); + hooks = rcu_dereference(nf_nat_sip_hooks); + if (hooks && ct->status & IPS_NAT_MASK) + ret = hooks->expect(skb, protoff, dataoff, dptr, datalen, + exp, matchoff, matchlen); else { if (nf_ct_expect_related(exp) != 0) { nf_ct_helper_log(skb, ct, "cannot add expectation"); @@ -1515,7 +1459,7 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, unsigned int protoff, unsigned int dataoff, const char **dptr, unsigned int *datalen) { - typeof(nf_nat_sip_hook) nf_nat_sip; + const struct nf_nat_sip_hooks *hooks; int ret; if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0) @@ -1524,9 +1468,9 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct, ret = process_sip_response(skb, protoff, dataoff, dptr, datalen); if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { - nf_nat_sip = rcu_dereference(nf_nat_sip_hook); - if (nf_nat_sip && !nf_nat_sip(skb, protoff, dataoff, - dptr, datalen)) { + hooks = rcu_dereference(nf_nat_sip_hooks); + if (hooks && !hooks->msg(skb, protoff, dataoff, + dptr, datalen)) { nf_ct_helper_log(skb, ct, "cannot NAT SIP message"); ret = NF_DROP; } @@ -1546,7 +1490,6 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, s16 diff, tdiff = 0; int ret = NF_ACCEPT; bool term; - typeof(nf_nat_sip_seq_adjust_hook) nf_nat_sip_seq_adjust; if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) @@ -1610,9 +1553,11 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff, } if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) { - nf_nat_sip_seq_adjust = rcu_dereference(nf_nat_sip_seq_adjust_hook); - if (nf_nat_sip_seq_adjust) - nf_nat_sip_seq_adjust(skb, protoff, tdiff); + const struct nf_nat_sip_hooks *hooks; + + hooks = rcu_dereference(nf_nat_sip_hooks); + if (hooks) + hooks->seq_adjust(skb, protoff, tdiff); } return ret; diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index 3deec997be89e32770750abc504922761d096935..61a3c927e63cf1c9f0f9b596155e5c8a74bcd71a 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -13,26 +13,20 @@ /* core.c */ -extern unsigned int nf_iterate(struct list_head *head, - struct sk_buff *skb, - unsigned int hook, - const struct net_device *indev, - const struct net_device *outdev, - struct nf_hook_ops **elemp, - int (*okfn)(struct sk_buff *), - int hook_thresh); +unsigned int nf_iterate(struct list_head *head, struct sk_buff *skb, + unsigned int hook, const struct net_device *indev, + const struct net_device *outdev, + struct nf_hook_ops **elemp, + int (*okfn)(struct sk_buff *), int hook_thresh); /* nf_queue.c */ -extern int nf_queue(struct sk_buff *skb, - struct nf_hook_ops *elem, - u_int8_t pf, unsigned int hook, - struct net_device *indev, - struct net_device *outdev, - int (*okfn)(struct sk_buff *), - unsigned int queuenum); -extern int __init netfilter_queue_init(void); +int nf_queue(struct sk_buff *skb, struct nf_hook_ops *elem, u_int8_t pf, + unsigned int hook, struct net_device *indev, + struct net_device *outdev, int (*okfn)(struct sk_buff *), + unsigned int queuenum); +int __init netfilter_queue_init(void); /* nf_log.c */ -extern int __init netfilter_log_init(void); +int __init netfilter_log_init(void); #endif diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index 6f0f4f7f68a5f4a7f85c03b09a35b456bedc33a7..63a81540221169fcc6115e8c36cc84500beb809d 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -432,6 +432,26 @@ nf_nat_setup_info(struct nf_conn *ct, } EXPORT_SYMBOL(nf_nat_setup_info); +unsigned int +nf_nat_alloc_null_binding(struct nf_conn *ct, unsigned int hooknum) +{ + /* Force range to this IP; let proto decide mapping for + * per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). + * Use reply in case it's already been mangled (eg local packet). + */ + union nf_inet_addr ip = + (HOOK2MANIP(hooknum) == NF_NAT_MANIP_SRC ? + ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3 : + ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3); + struct nf_nat_range range = { + .flags = NF_NAT_RANGE_MAP_IPS, + .min_addr = ip, + .max_addr = ip, + }; + return nf_nat_setup_info(ct, &range, HOOK2MANIP(hooknum)); +} +EXPORT_SYMBOL_GPL(nf_nat_alloc_null_binding); + /* Do packet manipulations according to nf_nat_setup_info. */ unsigned int nf_nat_packet(struct nf_conn *ct, enum ip_conntrack_info ctinfo, diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c index f9790405b7fff5ead6004bf4266fe4e96abbc6cb..b4d691db955ed451954381e01baeed57ca588d11 100644 --- a/net/netfilter/nf_nat_sip.c +++ b/net/netfilter/nf_nat_sip.c @@ -625,33 +625,26 @@ static struct nf_ct_helper_expectfn sip_nat = { static void __exit nf_nat_sip_fini(void) { - RCU_INIT_POINTER(nf_nat_sip_hook, NULL); - RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, NULL); - RCU_INIT_POINTER(nf_nat_sip_expect_hook, NULL); - RCU_INIT_POINTER(nf_nat_sdp_addr_hook, NULL); - RCU_INIT_POINTER(nf_nat_sdp_port_hook, NULL); - RCU_INIT_POINTER(nf_nat_sdp_session_hook, NULL); - RCU_INIT_POINTER(nf_nat_sdp_media_hook, NULL); + RCU_INIT_POINTER(nf_nat_sip_hooks, NULL); + nf_ct_helper_expectfn_unregister(&sip_nat); synchronize_rcu(); } +static const struct nf_nat_sip_hooks sip_hooks = { + .msg = nf_nat_sip, + .seq_adjust = nf_nat_sip_seq_adjust, + .expect = nf_nat_sip_expect, + .sdp_addr = nf_nat_sdp_addr, + .sdp_port = nf_nat_sdp_port, + .sdp_session = nf_nat_sdp_session, + .sdp_media = nf_nat_sdp_media, +}; + static int __init nf_nat_sip_init(void) { - BUG_ON(nf_nat_sip_hook != NULL); - BUG_ON(nf_nat_sip_seq_adjust_hook != NULL); - BUG_ON(nf_nat_sip_expect_hook != NULL); - BUG_ON(nf_nat_sdp_addr_hook != NULL); - BUG_ON(nf_nat_sdp_port_hook != NULL); - BUG_ON(nf_nat_sdp_session_hook != NULL); - BUG_ON(nf_nat_sdp_media_hook != NULL); - RCU_INIT_POINTER(nf_nat_sip_hook, nf_nat_sip); - RCU_INIT_POINTER(nf_nat_sip_seq_adjust_hook, nf_nat_sip_seq_adjust); - RCU_INIT_POINTER(nf_nat_sip_expect_hook, nf_nat_sip_expect); - RCU_INIT_POINTER(nf_nat_sdp_addr_hook, nf_nat_sdp_addr); - RCU_INIT_POINTER(nf_nat_sdp_port_hook, nf_nat_sdp_port); - RCU_INIT_POINTER(nf_nat_sdp_session_hook, nf_nat_sdp_session); - RCU_INIT_POINTER(nf_nat_sdp_media_hook, nf_nat_sdp_media); + BUG_ON(nf_nat_sip_hooks != NULL); + RCU_INIT_POINTER(nf_nat_sip_hooks, &sip_hooks); nf_ct_helper_expectfn_register(&sip_nat); return 0; } diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c new file mode 100644 index 0000000000000000000000000000000000000000..dcddc49c0e08363044195695138543e4277f83f0 --- /dev/null +++ b/net/netfilter/nf_tables_api.c @@ -0,0 +1,3275 @@ +/* + * Copyright (c) 2007-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(nf_tables_expressions); + +/** + * nft_register_afinfo - register nf_tables address family info + * + * @afi: address family info to register + * + * Register the address family for use with nf_tables. Returns zero on + * success or a negative errno code otherwise. + */ +int nft_register_afinfo(struct net *net, struct nft_af_info *afi) +{ + INIT_LIST_HEAD(&afi->tables); + nfnl_lock(NFNL_SUBSYS_NFTABLES); + list_add_tail(&afi->list, &net->nft.af_info); + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + return 0; +} +EXPORT_SYMBOL_GPL(nft_register_afinfo); + +/** + * nft_unregister_afinfo - unregister nf_tables address family info + * + * @afi: address family info to unregister + * + * Unregister the address family for use with nf_tables. + */ +void nft_unregister_afinfo(struct nft_af_info *afi) +{ + nfnl_lock(NFNL_SUBSYS_NFTABLES); + list_del(&afi->list); + nfnl_unlock(NFNL_SUBSYS_NFTABLES); +} +EXPORT_SYMBOL_GPL(nft_unregister_afinfo); + +static struct nft_af_info *nft_afinfo_lookup(struct net *net, int family) +{ + struct nft_af_info *afi; + + list_for_each_entry(afi, &net->nft.af_info, list) { + if (afi->family == family) + return afi; + } + return NULL; +} + +static struct nft_af_info * +nf_tables_afinfo_lookup(struct net *net, int family, bool autoload) +{ + struct nft_af_info *afi; + + afi = nft_afinfo_lookup(net, family); + if (afi != NULL) + return afi; +#ifdef CONFIG_MODULES + if (autoload) { + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + request_module("nft-afinfo-%u", family); + nfnl_lock(NFNL_SUBSYS_NFTABLES); + afi = nft_afinfo_lookup(net, family); + if (afi != NULL) + return ERR_PTR(-EAGAIN); + } +#endif + return ERR_PTR(-EAFNOSUPPORT); +} + +/* + * Tables + */ + +static struct nft_table *nft_table_lookup(const struct nft_af_info *afi, + const struct nlattr *nla) +{ + struct nft_table *table; + + list_for_each_entry(table, &afi->tables, list) { + if (!nla_strcmp(nla, table->name)) + return table; + } + return NULL; +} + +static struct nft_table *nf_tables_table_lookup(const struct nft_af_info *afi, + const struct nlattr *nla) +{ + struct nft_table *table; + + if (nla == NULL) + return ERR_PTR(-EINVAL); + + table = nft_table_lookup(afi, nla); + if (table != NULL) + return table; + + return ERR_PTR(-ENOENT); +} + +static inline u64 nf_tables_alloc_handle(struct nft_table *table) +{ + return ++table->hgenerator; +} + +static struct nf_chain_type *chain_type[AF_MAX][NFT_CHAIN_T_MAX]; + +static int __nf_tables_chain_type_lookup(int family, const struct nlattr *nla) +{ + int i; + + for (i=0; iname)) + return i; + } + return -1; +} + +static int nf_tables_chain_type_lookup(const struct nft_af_info *afi, + const struct nlattr *nla, + bool autoload) +{ + int type; + + type = __nf_tables_chain_type_lookup(afi->family, nla); +#ifdef CONFIG_MODULES + if (type < 0 && autoload) { + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + request_module("nft-chain-%u-%*.s", afi->family, + nla_len(nla)-1, (const char *)nla_data(nla)); + nfnl_lock(NFNL_SUBSYS_NFTABLES); + type = __nf_tables_chain_type_lookup(afi->family, nla); + } +#endif + return type; +} + +static const struct nla_policy nft_table_policy[NFTA_TABLE_MAX + 1] = { + [NFTA_TABLE_NAME] = { .type = NLA_STRING }, + [NFTA_TABLE_FLAGS] = { .type = NLA_U32 }, +}; + +static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq, + int event, u32 flags, int family, + const struct nft_table *table) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + + event |= NFNL_SUBSYS_NFTABLES << 8; + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); + if (nlh == NULL) + goto nla_put_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_string(skb, NFTA_TABLE_NAME, table->name) || + nla_put_be32(skb, NFTA_TABLE_FLAGS, htonl(table->flags))) + goto nla_put_failure; + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_trim(skb, nlh); + return -1; +} + +static int nf_tables_table_notify(const struct sk_buff *oskb, + const struct nlmsghdr *nlh, + const struct nft_table *table, + int event, int family) +{ + struct sk_buff *skb; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + u32 seq = nlh ? nlh->nlmsg_seq : 0; + struct net *net = oskb ? sock_net(oskb->sk) : &init_net; + bool report; + int err; + + report = nlh ? nlmsg_report(nlh) : false; + if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + return 0; + + err = -ENOBUFS; + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + goto err; + + err = nf_tables_fill_table_info(skb, portid, seq, event, 0, + family, table); + if (err < 0) { + kfree_skb(skb); + goto err; + } + + err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, + GFP_KERNEL); +err: + if (err < 0) + nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + return err; +} + +static int nf_tables_dump_tables(struct sk_buff *skb, + struct netlink_callback *cb) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); + const struct nft_af_info *afi; + const struct nft_table *table; + unsigned int idx = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + + list_for_each_entry(afi, &net->nft.af_info, list) { + if (family != NFPROTO_UNSPEC && family != afi->family) + continue; + + list_for_each_entry(table, &afi->tables, list) { + if (idx < s_idx) + goto cont; + if (idx > s_idx) + memset(&cb->args[1], 0, + sizeof(cb->args) - sizeof(cb->args[0])); + if (nf_tables_fill_table_info(skb, + NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NFT_MSG_NEWTABLE, + NLM_F_MULTI, + afi->family, table) < 0) + goto done; +cont: + idx++; + } + } +done: + cb->args[0] = idx; + return skb->len; +} + +static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_af_info *afi; + const struct nft_table *table; + struct sk_buff *skb2; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + int err; + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = nf_tables_dump_tables, + }; + return netlink_dump_start(nlsk, skb, nlh, &c); + } + + afi = nf_tables_afinfo_lookup(net, family, false); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); + if (IS_ERR(table)) + return PTR_ERR(table); + + skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb2) + return -ENOMEM; + + err = nf_tables_fill_table_info(skb2, NETLINK_CB(skb).portid, + nlh->nlmsg_seq, NFT_MSG_NEWTABLE, 0, + family, table); + if (err < 0) + goto err; + + return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); + +err: + kfree_skb(skb2); + return err; +} + +static int nf_tables_table_enable(struct nft_table *table) +{ + struct nft_chain *chain; + int err, i = 0; + + list_for_each_entry(chain, &table->chains, list) { + err = nf_register_hook(&nft_base_chain(chain)->ops); + if (err < 0) + goto err; + + i++; + } + return 0; +err: + list_for_each_entry(chain, &table->chains, list) { + if (i-- <= 0) + break; + + nf_unregister_hook(&nft_base_chain(chain)->ops); + } + return err; +} + +static int nf_tables_table_disable(struct nft_table *table) +{ + struct nft_chain *chain; + + list_for_each_entry(chain, &table->chains, list) + nf_unregister_hook(&nft_base_chain(chain)->ops); + + return 0; +} + +static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[], + struct nft_af_info *afi, struct nft_table *table) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + int family = nfmsg->nfgen_family, ret = 0; + + if (nla[NFTA_TABLE_FLAGS]) { + __be32 flags; + + flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS])); + if (flags & ~NFT_TABLE_F_DORMANT) + return -EINVAL; + + if ((flags & NFT_TABLE_F_DORMANT) && + !(table->flags & NFT_TABLE_F_DORMANT)) { + ret = nf_tables_table_disable(table); + if (ret >= 0) + table->flags |= NFT_TABLE_F_DORMANT; + } else if (!(flags & NFT_TABLE_F_DORMANT) && + table->flags & NFT_TABLE_F_DORMANT) { + ret = nf_tables_table_enable(table); + if (ret >= 0) + table->flags &= ~NFT_TABLE_F_DORMANT; + } + if (ret < 0) + goto err; + } + + nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); +err: + return ret; +} + +static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nlattr *name; + struct nft_af_info *afi; + struct nft_table *table; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + + afi = nf_tables_afinfo_lookup(net, family, true); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + name = nla[NFTA_TABLE_NAME]; + table = nf_tables_table_lookup(afi, name); + if (IS_ERR(table)) { + if (PTR_ERR(table) != -ENOENT) + return PTR_ERR(table); + table = NULL; + } + + if (table != NULL) { + if (nlh->nlmsg_flags & NLM_F_EXCL) + return -EEXIST; + if (nlh->nlmsg_flags & NLM_F_REPLACE) + return -EOPNOTSUPP; + return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table); + } + + table = kzalloc(sizeof(*table) + nla_len(name), GFP_KERNEL); + if (table == NULL) + return -ENOMEM; + + nla_strlcpy(table->name, name, nla_len(name)); + INIT_LIST_HEAD(&table->chains); + INIT_LIST_HEAD(&table->sets); + + if (nla[NFTA_TABLE_FLAGS]) { + __be32 flags; + + flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS])); + if (flags & ~NFT_TABLE_F_DORMANT) { + kfree(table); + return -EINVAL; + } + + table->flags |= flags; + } + + list_add_tail(&table->list, &afi->tables); + nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); + return 0; +} + +static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + struct nft_af_info *afi; + struct nft_table *table; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + + afi = nf_tables_afinfo_lookup(net, family, false); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); + if (IS_ERR(table)) + return PTR_ERR(table); + + if (table->use) + return -EBUSY; + + list_del(&table->list); + nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family); + kfree(table); + return 0; +} + +int nft_register_chain_type(struct nf_chain_type *ctype) +{ + int err = 0; + + nfnl_lock(NFNL_SUBSYS_NFTABLES); + if (chain_type[ctype->family][ctype->type] != NULL) { + err = -EBUSY; + goto out; + } + + if (!try_module_get(ctype->me)) + goto out; + + chain_type[ctype->family][ctype->type] = ctype; +out: + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + return err; +} +EXPORT_SYMBOL_GPL(nft_register_chain_type); + +void nft_unregister_chain_type(struct nf_chain_type *ctype) +{ + nfnl_lock(NFNL_SUBSYS_NFTABLES); + chain_type[ctype->family][ctype->type] = NULL; + module_put(ctype->me); + nfnl_unlock(NFNL_SUBSYS_NFTABLES); +} +EXPORT_SYMBOL_GPL(nft_unregister_chain_type); + +/* + * Chains + */ + +static struct nft_chain * +nf_tables_chain_lookup_byhandle(const struct nft_table *table, u64 handle) +{ + struct nft_chain *chain; + + list_for_each_entry(chain, &table->chains, list) { + if (chain->handle == handle) + return chain; + } + + return ERR_PTR(-ENOENT); +} + +static struct nft_chain *nf_tables_chain_lookup(const struct nft_table *table, + const struct nlattr *nla) +{ + struct nft_chain *chain; + + if (nla == NULL) + return ERR_PTR(-EINVAL); + + list_for_each_entry(chain, &table->chains, list) { + if (!nla_strcmp(nla, chain->name)) + return chain; + } + + return ERR_PTR(-ENOENT); +} + +static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = { + [NFTA_CHAIN_TABLE] = { .type = NLA_STRING }, + [NFTA_CHAIN_HANDLE] = { .type = NLA_U64 }, + [NFTA_CHAIN_NAME] = { .type = NLA_STRING, + .len = NFT_CHAIN_MAXNAMELEN - 1 }, + [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED }, + [NFTA_CHAIN_POLICY] = { .type = NLA_U32 }, + [NFTA_CHAIN_TYPE] = { .type = NLA_NUL_STRING }, + [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED }, +}; + +static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = { + [NFTA_HOOK_HOOKNUM] = { .type = NLA_U32 }, + [NFTA_HOOK_PRIORITY] = { .type = NLA_U32 }, +}; + +static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats) +{ + struct nft_stats *cpu_stats, total; + struct nlattr *nest; + int cpu; + + memset(&total, 0, sizeof(total)); + for_each_possible_cpu(cpu) { + cpu_stats = per_cpu_ptr(stats, cpu); + total.pkts += cpu_stats->pkts; + total.bytes += cpu_stats->bytes; + } + nest = nla_nest_start(skb, NFTA_CHAIN_COUNTERS); + if (nest == NULL) + goto nla_put_failure; + + if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.pkts)) || + nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes))) + goto nla_put_failure; + + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + return -ENOSPC; +} + +static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, + int event, u32 flags, int family, + const struct nft_table *table, + const struct nft_chain *chain) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + + event |= NFNL_SUBSYS_NFTABLES << 8; + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), flags); + if (nlh == NULL) + goto nla_put_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_string(skb, NFTA_CHAIN_TABLE, table->name)) + goto nla_put_failure; + if (nla_put_be64(skb, NFTA_CHAIN_HANDLE, cpu_to_be64(chain->handle))) + goto nla_put_failure; + if (nla_put_string(skb, NFTA_CHAIN_NAME, chain->name)) + goto nla_put_failure; + + if (chain->flags & NFT_BASE_CHAIN) { + const struct nft_base_chain *basechain = nft_base_chain(chain); + const struct nf_hook_ops *ops = &basechain->ops; + struct nlattr *nest; + + nest = nla_nest_start(skb, NFTA_CHAIN_HOOK); + if (nest == NULL) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_HOOK_PRIORITY, htonl(ops->priority))) + goto nla_put_failure; + nla_nest_end(skb, nest); + + if (nla_put_be32(skb, NFTA_CHAIN_POLICY, + htonl(basechain->policy))) + goto nla_put_failure; + + if (nla_put_string(skb, NFTA_CHAIN_TYPE, + chain_type[ops->pf][nft_base_chain(chain)->type]->name)) + goto nla_put_failure; + + if (nft_dump_stats(skb, nft_base_chain(chain)->stats)) + goto nla_put_failure; + } + + if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use))) + goto nla_put_failure; + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_trim(skb, nlh); + return -1; +} + +static int nf_tables_chain_notify(const struct sk_buff *oskb, + const struct nlmsghdr *nlh, + const struct nft_table *table, + const struct nft_chain *chain, + int event, int family) +{ + struct sk_buff *skb; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + struct net *net = oskb ? sock_net(oskb->sk) : &init_net; + u32 seq = nlh ? nlh->nlmsg_seq : 0; + bool report; + int err; + + report = nlh ? nlmsg_report(nlh) : false; + if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + return 0; + + err = -ENOBUFS; + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + goto err; + + err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, family, + table, chain); + if (err < 0) { + kfree_skb(skb); + goto err; + } + + err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, + GFP_KERNEL); +err: + if (err < 0) + nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + return err; +} + +static int nf_tables_dump_chains(struct sk_buff *skb, + struct netlink_callback *cb) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); + const struct nft_af_info *afi; + const struct nft_table *table; + const struct nft_chain *chain; + unsigned int idx = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + + list_for_each_entry(afi, &net->nft.af_info, list) { + if (family != NFPROTO_UNSPEC && family != afi->family) + continue; + + list_for_each_entry(table, &afi->tables, list) { + list_for_each_entry(chain, &table->chains, list) { + if (idx < s_idx) + goto cont; + if (idx > s_idx) + memset(&cb->args[1], 0, + sizeof(cb->args) - sizeof(cb->args[0])); + if (nf_tables_fill_chain_info(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NFT_MSG_NEWCHAIN, + NLM_F_MULTI, + afi->family, table, chain) < 0) + goto done; +cont: + idx++; + } + } + } +done: + cb->args[0] = idx; + return skb->len; +} + + +static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_af_info *afi; + const struct nft_table *table; + const struct nft_chain *chain; + struct sk_buff *skb2; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + int err; + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = nf_tables_dump_chains, + }; + return netlink_dump_start(nlsk, skb, nlh, &c); + } + + afi = nf_tables_afinfo_lookup(net, family, false); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); + if (IS_ERR(table)) + return PTR_ERR(table); + + chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); + if (IS_ERR(chain)) + return PTR_ERR(chain); + + skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb2) + return -ENOMEM; + + err = nf_tables_fill_chain_info(skb2, NETLINK_CB(skb).portid, + nlh->nlmsg_seq, NFT_MSG_NEWCHAIN, 0, + family, table, chain); + if (err < 0) + goto err; + + return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); + +err: + kfree_skb(skb2); + return err; +} + +static int +nf_tables_chain_policy(struct nft_base_chain *chain, const struct nlattr *attr) +{ + switch (ntohl(nla_get_be32(attr))) { + case NF_DROP: + chain->policy = NF_DROP; + break; + case NF_ACCEPT: + chain->policy = NF_ACCEPT; + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { + [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 }, + [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, +}; + +static int +nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) +{ + struct nlattr *tb[NFTA_COUNTER_MAX+1]; + struct nft_stats __percpu *newstats; + struct nft_stats *stats; + int err; + + err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy); + if (err < 0) + return err; + + if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS]) + return -EINVAL; + + newstats = alloc_percpu(struct nft_stats); + if (newstats == NULL) + return -ENOMEM; + + /* Restore old counters on this cpu, no problem. Per-cpu statistics + * are not exposed to userspace. + */ + stats = this_cpu_ptr(newstats); + stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); + stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); + + if (chain->stats) { + /* nfnl_lock is held, add some nfnl function for this, later */ + struct nft_stats __percpu *oldstats = + rcu_dereference_protected(chain->stats, 1); + + rcu_assign_pointer(chain->stats, newstats); + synchronize_rcu(); + free_percpu(oldstats); + } else + rcu_assign_pointer(chain->stats, newstats); + + return 0; +} + +static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nlattr * uninitialized_var(name); + const struct nft_af_info *afi; + struct nft_table *table; + struct nft_chain *chain; + struct nft_base_chain *basechain = NULL; + struct nlattr *ha[NFTA_HOOK_MAX + 1]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + u64 handle = 0; + int err; + bool create; + + create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; + + afi = nf_tables_afinfo_lookup(net, family, true); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); + if (IS_ERR(table)) + return PTR_ERR(table); + + if (table->use == UINT_MAX) + return -EOVERFLOW; + + chain = NULL; + name = nla[NFTA_CHAIN_NAME]; + + if (nla[NFTA_CHAIN_HANDLE]) { + handle = be64_to_cpu(nla_get_be64(nla[NFTA_CHAIN_HANDLE])); + chain = nf_tables_chain_lookup_byhandle(table, handle); + if (IS_ERR(chain)) + return PTR_ERR(chain); + } else { + chain = nf_tables_chain_lookup(table, name); + if (IS_ERR(chain)) { + if (PTR_ERR(chain) != -ENOENT) + return PTR_ERR(chain); + chain = NULL; + } + } + + if (chain != NULL) { + if (nlh->nlmsg_flags & NLM_F_EXCL) + return -EEXIST; + if (nlh->nlmsg_flags & NLM_F_REPLACE) + return -EOPNOTSUPP; + + if (nla[NFTA_CHAIN_HANDLE] && name && + !IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]))) + return -EEXIST; + + if (nla[NFTA_CHAIN_POLICY]) { + if (!(chain->flags & NFT_BASE_CHAIN)) + return -EOPNOTSUPP; + + err = nf_tables_chain_policy(nft_base_chain(chain), + nla[NFTA_CHAIN_POLICY]); + if (err < 0) + return err; + } + + if (nla[NFTA_CHAIN_COUNTERS]) { + if (!(chain->flags & NFT_BASE_CHAIN)) + return -EOPNOTSUPP; + + err = nf_tables_counters(nft_base_chain(chain), + nla[NFTA_CHAIN_COUNTERS]); + if (err < 0) + return err; + } + + if (nla[NFTA_CHAIN_HANDLE] && name) + nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); + + goto notify; + } + + if (nla[NFTA_CHAIN_HOOK]) { + struct nf_hook_ops *ops; + nf_hookfn *hookfn; + u32 hooknum; + int type = NFT_CHAIN_T_DEFAULT; + + if (nla[NFTA_CHAIN_TYPE]) { + type = nf_tables_chain_type_lookup(afi, + nla[NFTA_CHAIN_TYPE], + create); + if (type < 0) + return -ENOENT; + } + + err = nla_parse_nested(ha, NFTA_HOOK_MAX, nla[NFTA_CHAIN_HOOK], + nft_hook_policy); + if (err < 0) + return err; + if (ha[NFTA_HOOK_HOOKNUM] == NULL || + ha[NFTA_HOOK_PRIORITY] == NULL) + return -EINVAL; + + hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); + if (hooknum >= afi->nhooks) + return -EINVAL; + + hookfn = chain_type[family][type]->fn[hooknum]; + if (hookfn == NULL) + return -EOPNOTSUPP; + + basechain = kzalloc(sizeof(*basechain), GFP_KERNEL); + if (basechain == NULL) + return -ENOMEM; + + basechain->type = type; + chain = &basechain->chain; + + ops = &basechain->ops; + ops->pf = family; + ops->owner = afi->owner; + ops->hooknum = ntohl(nla_get_be32(ha[NFTA_HOOK_HOOKNUM])); + ops->priority = ntohl(nla_get_be32(ha[NFTA_HOOK_PRIORITY])); + ops->priv = chain; + ops->hook = hookfn; + if (afi->hooks[ops->hooknum]) + ops->hook = afi->hooks[ops->hooknum]; + + chain->flags |= NFT_BASE_CHAIN; + + if (nla[NFTA_CHAIN_POLICY]) { + err = nf_tables_chain_policy(basechain, + nla[NFTA_CHAIN_POLICY]); + if (err < 0) { + free_percpu(basechain->stats); + kfree(basechain); + return err; + } + } else + basechain->policy = NF_ACCEPT; + + if (nla[NFTA_CHAIN_COUNTERS]) { + err = nf_tables_counters(basechain, + nla[NFTA_CHAIN_COUNTERS]); + if (err < 0) { + free_percpu(basechain->stats); + kfree(basechain); + return err; + } + } else { + struct nft_stats __percpu *newstats; + + newstats = alloc_percpu(struct nft_stats); + if (newstats == NULL) + return -ENOMEM; + + rcu_assign_pointer(nft_base_chain(chain)->stats, + newstats); + } + } else { + chain = kzalloc(sizeof(*chain), GFP_KERNEL); + if (chain == NULL) + return -ENOMEM; + } + + INIT_LIST_HEAD(&chain->rules); + chain->handle = nf_tables_alloc_handle(table); + chain->net = net; + chain->table = table; + nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); + + if (!(table->flags & NFT_TABLE_F_DORMANT) && + chain->flags & NFT_BASE_CHAIN) { + err = nf_register_hook(&nft_base_chain(chain)->ops); + if (err < 0) { + free_percpu(basechain->stats); + kfree(basechain); + return err; + } + } + list_add_tail(&chain->list, &table->chains); + table->use++; +notify: + nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN, + family); + return 0; +} + +static void nf_tables_rcu_chain_destroy(struct rcu_head *head) +{ + struct nft_chain *chain = container_of(head, struct nft_chain, rcu_head); + + BUG_ON(chain->use > 0); + + if (chain->flags & NFT_BASE_CHAIN) { + free_percpu(nft_base_chain(chain)->stats); + kfree(nft_base_chain(chain)); + } else + kfree(chain); +} + +static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_af_info *afi; + struct nft_table *table; + struct nft_chain *chain; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + + afi = nf_tables_afinfo_lookup(net, family, false); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); + if (IS_ERR(table)) + return PTR_ERR(table); + + chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); + if (IS_ERR(chain)) + return PTR_ERR(chain); + + if (!list_empty(&chain->rules)) + return -EBUSY; + + list_del(&chain->list); + table->use--; + + if (!(table->flags & NFT_TABLE_F_DORMANT) && + chain->flags & NFT_BASE_CHAIN) + nf_unregister_hook(&nft_base_chain(chain)->ops); + + nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN, + family); + + /* Make sure all rule references are gone before this is released */ + call_rcu(&chain->rcu_head, nf_tables_rcu_chain_destroy); + return 0; +} + +static void nft_ctx_init(struct nft_ctx *ctx, + const struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nft_af_info *afi, + const struct nft_table *table, + const struct nft_chain *chain, + const struct nlattr * const *nla) +{ + ctx->net = sock_net(skb->sk); + ctx->skb = skb; + ctx->nlh = nlh; + ctx->afi = afi; + ctx->table = table; + ctx->chain = chain; + ctx->nla = nla; +} + +/* + * Expressions + */ + +/** + * nft_register_expr - register nf_tables expr type + * @ops: expr type + * + * Registers the expr type for use with nf_tables. Returns zero on + * success or a negative errno code otherwise. + */ +int nft_register_expr(struct nft_expr_type *type) +{ + nfnl_lock(NFNL_SUBSYS_NFTABLES); + list_add_tail(&type->list, &nf_tables_expressions); + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + return 0; +} +EXPORT_SYMBOL_GPL(nft_register_expr); + +/** + * nft_unregister_expr - unregister nf_tables expr type + * @ops: expr type + * + * Unregisters the expr typefor use with nf_tables. + */ +void nft_unregister_expr(struct nft_expr_type *type) +{ + nfnl_lock(NFNL_SUBSYS_NFTABLES); + list_del(&type->list); + nfnl_unlock(NFNL_SUBSYS_NFTABLES); +} +EXPORT_SYMBOL_GPL(nft_unregister_expr); + +static const struct nft_expr_type *__nft_expr_type_get(struct nlattr *nla) +{ + const struct nft_expr_type *type; + + list_for_each_entry(type, &nf_tables_expressions, list) { + if (!nla_strcmp(nla, type->name)) + return type; + } + return NULL; +} + +static const struct nft_expr_type *nft_expr_type_get(struct nlattr *nla) +{ + const struct nft_expr_type *type; + + if (nla == NULL) + return ERR_PTR(-EINVAL); + + type = __nft_expr_type_get(nla); + if (type != NULL && try_module_get(type->owner)) + return type; + +#ifdef CONFIG_MODULES + if (type == NULL) { + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + request_module("nft-expr-%.*s", + nla_len(nla), (char *)nla_data(nla)); + nfnl_lock(NFNL_SUBSYS_NFTABLES); + if (__nft_expr_type_get(nla)) + return ERR_PTR(-EAGAIN); + } +#endif + return ERR_PTR(-ENOENT); +} + +static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = { + [NFTA_EXPR_NAME] = { .type = NLA_STRING }, + [NFTA_EXPR_DATA] = { .type = NLA_NESTED }, +}; + +static int nf_tables_fill_expr_info(struct sk_buff *skb, + const struct nft_expr *expr) +{ + if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name)) + goto nla_put_failure; + + if (expr->ops->dump) { + struct nlattr *data = nla_nest_start(skb, NFTA_EXPR_DATA); + if (data == NULL) + goto nla_put_failure; + if (expr->ops->dump(skb, expr) < 0) + goto nla_put_failure; + nla_nest_end(skb, data); + } + + return skb->len; + +nla_put_failure: + return -1; +}; + +struct nft_expr_info { + const struct nft_expr_ops *ops; + struct nlattr *tb[NFT_EXPR_MAXATTR + 1]; +}; + +static int nf_tables_expr_parse(const struct nft_ctx *ctx, + const struct nlattr *nla, + struct nft_expr_info *info) +{ + const struct nft_expr_type *type; + const struct nft_expr_ops *ops; + struct nlattr *tb[NFTA_EXPR_MAX + 1]; + int err; + + err = nla_parse_nested(tb, NFTA_EXPR_MAX, nla, nft_expr_policy); + if (err < 0) + return err; + + type = nft_expr_type_get(tb[NFTA_EXPR_NAME]); + if (IS_ERR(type)) + return PTR_ERR(type); + + if (tb[NFTA_EXPR_DATA]) { + err = nla_parse_nested(info->tb, type->maxattr, + tb[NFTA_EXPR_DATA], type->policy); + if (err < 0) + goto err1; + } else + memset(info->tb, 0, sizeof(info->tb[0]) * (type->maxattr + 1)); + + if (type->select_ops != NULL) { + ops = type->select_ops(ctx, + (const struct nlattr * const *)info->tb); + if (IS_ERR(ops)) { + err = PTR_ERR(ops); + goto err1; + } + } else + ops = type->ops; + + info->ops = ops; + return 0; + +err1: + module_put(type->owner); + return err; +} + +static int nf_tables_newexpr(const struct nft_ctx *ctx, + const struct nft_expr_info *info, + struct nft_expr *expr) +{ + const struct nft_expr_ops *ops = info->ops; + int err; + + expr->ops = ops; + if (ops->init) { + err = ops->init(ctx, expr, (const struct nlattr **)info->tb); + if (err < 0) + goto err1; + } + + return 0; + +err1: + expr->ops = NULL; + return err; +} + +static void nf_tables_expr_destroy(struct nft_expr *expr) +{ + if (expr->ops->destroy) + expr->ops->destroy(expr); + module_put(expr->ops->type->owner); +} + +/* + * Rules + */ + +static struct nft_rule *__nf_tables_rule_lookup(const struct nft_chain *chain, + u64 handle) +{ + struct nft_rule *rule; + + // FIXME: this sucks + list_for_each_entry(rule, &chain->rules, list) { + if (handle == rule->handle) + return rule; + } + + return ERR_PTR(-ENOENT); +} + +static struct nft_rule *nf_tables_rule_lookup(const struct nft_chain *chain, + const struct nlattr *nla) +{ + if (nla == NULL) + return ERR_PTR(-EINVAL); + + return __nf_tables_rule_lookup(chain, be64_to_cpu(nla_get_be64(nla))); +} + +static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = { + [NFTA_RULE_TABLE] = { .type = NLA_STRING }, + [NFTA_RULE_CHAIN] = { .type = NLA_STRING, + .len = NFT_CHAIN_MAXNAMELEN - 1 }, + [NFTA_RULE_HANDLE] = { .type = NLA_U64 }, + [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED }, + [NFTA_RULE_COMPAT] = { .type = NLA_NESTED }, + [NFTA_RULE_POSITION] = { .type = NLA_U64 }, +}; + +static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq, + int event, u32 flags, int family, + const struct nft_table *table, + const struct nft_chain *chain, + const struct nft_rule *rule) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + const struct nft_expr *expr, *next; + struct nlattr *list; + const struct nft_rule *prule; + int type = event | NFNL_SUBSYS_NFTABLES << 8; + + nlh = nlmsg_put(skb, portid, seq, type, sizeof(struct nfgenmsg), + flags); + if (nlh == NULL) + goto nla_put_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_string(skb, NFTA_RULE_TABLE, table->name)) + goto nla_put_failure; + if (nla_put_string(skb, NFTA_RULE_CHAIN, chain->name)) + goto nla_put_failure; + if (nla_put_be64(skb, NFTA_RULE_HANDLE, cpu_to_be64(rule->handle))) + goto nla_put_failure; + + if ((event != NFT_MSG_DELRULE) && (rule->list.prev != &chain->rules)) { + prule = list_entry(rule->list.prev, struct nft_rule, list); + if (nla_put_be64(skb, NFTA_RULE_POSITION, + cpu_to_be64(prule->handle))) + goto nla_put_failure; + } + + list = nla_nest_start(skb, NFTA_RULE_EXPRESSIONS); + if (list == NULL) + goto nla_put_failure; + nft_rule_for_each_expr(expr, next, rule) { + struct nlattr *elem = nla_nest_start(skb, NFTA_LIST_ELEM); + if (elem == NULL) + goto nla_put_failure; + if (nf_tables_fill_expr_info(skb, expr) < 0) + goto nla_put_failure; + nla_nest_end(skb, elem); + } + nla_nest_end(skb, list); + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_trim(skb, nlh); + return -1; +} + +static int nf_tables_rule_notify(const struct sk_buff *oskb, + const struct nlmsghdr *nlh, + const struct nft_table *table, + const struct nft_chain *chain, + const struct nft_rule *rule, + int event, u32 flags, int family) +{ + struct sk_buff *skb; + u32 portid = NETLINK_CB(oskb).portid; + struct net *net = oskb ? sock_net(oskb->sk) : &init_net; + u32 seq = nlh->nlmsg_seq; + bool report; + int err; + + report = nlmsg_report(nlh); + if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) + return 0; + + err = -ENOBUFS; + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + goto err; + + err = nf_tables_fill_rule_info(skb, portid, seq, event, flags, + family, table, chain, rule); + if (err < 0) { + kfree_skb(skb); + goto err; + } + + err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, + GFP_KERNEL); +err: + if (err < 0) + nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); + return err; +} + +static inline bool +nft_rule_is_active(struct net *net, const struct nft_rule *rule) +{ + return (rule->genmask & (1 << net->nft.gencursor)) == 0; +} + +static inline int gencursor_next(struct net *net) +{ + return net->nft.gencursor+1 == 1 ? 1 : 0; +} + +static inline int +nft_rule_is_active_next(struct net *net, const struct nft_rule *rule) +{ + return (rule->genmask & (1 << gencursor_next(net))) == 0; +} + +static inline void +nft_rule_activate_next(struct net *net, struct nft_rule *rule) +{ + /* Now inactive, will be active in the future */ + rule->genmask = (1 << net->nft.gencursor); +} + +static inline void +nft_rule_disactivate_next(struct net *net, struct nft_rule *rule) +{ + rule->genmask = (1 << gencursor_next(net)); +} + +static inline void nft_rule_clear(struct net *net, struct nft_rule *rule) +{ + rule->genmask = 0; +} + +static int nf_tables_dump_rules(struct sk_buff *skb, + struct netlink_callback *cb) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); + const struct nft_af_info *afi; + const struct nft_table *table; + const struct nft_chain *chain; + const struct nft_rule *rule; + unsigned int idx = 0, s_idx = cb->args[0]; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + u8 genctr = ACCESS_ONCE(net->nft.genctr); + u8 gencursor = ACCESS_ONCE(net->nft.gencursor); + + list_for_each_entry(afi, &net->nft.af_info, list) { + if (family != NFPROTO_UNSPEC && family != afi->family) + continue; + + list_for_each_entry(table, &afi->tables, list) { + list_for_each_entry(chain, &table->chains, list) { + list_for_each_entry(rule, &chain->rules, list) { + if (!nft_rule_is_active(net, rule)) + goto cont; + if (idx < s_idx) + goto cont; + if (idx > s_idx) + memset(&cb->args[1], 0, + sizeof(cb->args) - sizeof(cb->args[0])); + if (nf_tables_fill_rule_info(skb, NETLINK_CB(cb->skb).portid, + cb->nlh->nlmsg_seq, + NFT_MSG_NEWRULE, + NLM_F_MULTI | NLM_F_APPEND, + afi->family, table, chain, rule) < 0) + goto done; +cont: + idx++; + } + } + } + } +done: + /* Invalidate this dump, a transition to the new generation happened */ + if (gencursor != net->nft.gencursor || genctr != net->nft.genctr) + return -EBUSY; + + cb->args[0] = idx; + return skb->len; +} + +static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_af_info *afi; + const struct nft_table *table; + const struct nft_chain *chain; + const struct nft_rule *rule; + struct sk_buff *skb2; + struct net *net = sock_net(skb->sk); + int family = nfmsg->nfgen_family; + int err; + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = nf_tables_dump_rules, + }; + return netlink_dump_start(nlsk, skb, nlh, &c); + } + + afi = nf_tables_afinfo_lookup(net, family, false); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); + if (IS_ERR(table)) + return PTR_ERR(table); + + chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); + if (IS_ERR(chain)) + return PTR_ERR(chain); + + rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); + if (IS_ERR(rule)) + return PTR_ERR(rule); + + skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb2) + return -ENOMEM; + + err = nf_tables_fill_rule_info(skb2, NETLINK_CB(skb).portid, + nlh->nlmsg_seq, NFT_MSG_NEWRULE, 0, + family, table, chain, rule); + if (err < 0) + goto err; + + return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); + +err: + kfree_skb(skb2); + return err; +} + +static void nf_tables_rcu_rule_destroy(struct rcu_head *head) +{ + struct nft_rule *rule = container_of(head, struct nft_rule, rcu_head); + struct nft_expr *expr; + + /* + * Careful: some expressions might not be initialized in case this + * is called on error from nf_tables_newrule(). + */ + expr = nft_expr_first(rule); + while (expr->ops && expr != nft_expr_last(rule)) { + nf_tables_expr_destroy(expr); + expr = nft_expr_next(expr); + } + kfree(rule); +} + +static void nf_tables_rule_destroy(struct nft_rule *rule) +{ + call_rcu(&rule->rcu_head, nf_tables_rcu_rule_destroy); +} + +#define NFT_RULE_MAXEXPRS 128 + +static struct nft_expr_info *info; + +static struct nft_rule_trans * +nf_tables_trans_add(struct nft_rule *rule, const struct nft_ctx *ctx) +{ + struct nft_rule_trans *rupd; + + rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL); + if (rupd == NULL) + return NULL; + + rupd->chain = ctx->chain; + rupd->table = ctx->table; + rupd->rule = rule; + rupd->family = ctx->afi->family; + rupd->nlh = ctx->nlh; + list_add_tail(&rupd->list, &ctx->net->nft.commit_list); + + return rupd; +} + +static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_af_info *afi; + struct net *net = sock_net(skb->sk); + struct nft_table *table; + struct nft_chain *chain; + struct nft_rule *rule, *old_rule = NULL; + struct nft_rule_trans *repl = NULL; + struct nft_expr *expr; + struct nft_ctx ctx; + struct nlattr *tmp; + unsigned int size, i, n; + int err, rem; + bool create; + u64 handle, pos_handle; + + create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; + + afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); + if (IS_ERR(table)) + return PTR_ERR(table); + + chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); + if (IS_ERR(chain)) + return PTR_ERR(chain); + + if (nla[NFTA_RULE_HANDLE]) { + handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_HANDLE])); + rule = __nf_tables_rule_lookup(chain, handle); + if (IS_ERR(rule)) + return PTR_ERR(rule); + + if (nlh->nlmsg_flags & NLM_F_EXCL) + return -EEXIST; + if (nlh->nlmsg_flags & NLM_F_REPLACE) + old_rule = rule; + else + return -EOPNOTSUPP; + } else { + if (!create || nlh->nlmsg_flags & NLM_F_REPLACE) + return -EINVAL; + handle = nf_tables_alloc_handle(table); + } + + if (nla[NFTA_RULE_POSITION]) { + if (!(nlh->nlmsg_flags & NLM_F_CREATE)) + return -EOPNOTSUPP; + + pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION])); + old_rule = __nf_tables_rule_lookup(chain, pos_handle); + if (IS_ERR(old_rule)) + return PTR_ERR(old_rule); + } + + nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); + + n = 0; + size = 0; + if (nla[NFTA_RULE_EXPRESSIONS]) { + nla_for_each_nested(tmp, nla[NFTA_RULE_EXPRESSIONS], rem) { + err = -EINVAL; + if (nla_type(tmp) != NFTA_LIST_ELEM) + goto err1; + if (n == NFT_RULE_MAXEXPRS) + goto err1; + err = nf_tables_expr_parse(&ctx, tmp, &info[n]); + if (err < 0) + goto err1; + size += info[n].ops->size; + n++; + } + } + + err = -ENOMEM; + rule = kzalloc(sizeof(*rule) + size, GFP_KERNEL); + if (rule == NULL) + goto err1; + + nft_rule_activate_next(net, rule); + + rule->handle = handle; + rule->dlen = size; + + expr = nft_expr_first(rule); + for (i = 0; i < n; i++) { + err = nf_tables_newexpr(&ctx, &info[i], expr); + if (err < 0) + goto err2; + info[i].ops = NULL; + expr = nft_expr_next(expr); + } + + if (nlh->nlmsg_flags & NLM_F_REPLACE) { + if (nft_rule_is_active_next(net, old_rule)) { + repl = nf_tables_trans_add(old_rule, &ctx); + if (repl == NULL) { + err = -ENOMEM; + goto err2; + } + nft_rule_disactivate_next(net, old_rule); + list_add_tail(&rule->list, &old_rule->list); + } else { + err = -ENOENT; + goto err2; + } + } else if (nlh->nlmsg_flags & NLM_F_APPEND) + if (old_rule) + list_add_rcu(&rule->list, &old_rule->list); + else + list_add_tail_rcu(&rule->list, &chain->rules); + else { + if (old_rule) + list_add_tail_rcu(&rule->list, &old_rule->list); + else + list_add_rcu(&rule->list, &chain->rules); + } + + if (nf_tables_trans_add(rule, &ctx) == NULL) { + err = -ENOMEM; + goto err3; + } + return 0; + +err3: + list_del_rcu(&rule->list); + if (repl) { + list_del_rcu(&repl->rule->list); + list_del(&repl->list); + nft_rule_clear(net, repl->rule); + kfree(repl); + } +err2: + nf_tables_rule_destroy(rule); +err1: + for (i = 0; i < n; i++) { + if (info[i].ops != NULL) + module_put(info[i].ops->type->owner); + } + return err; +} + +static int +nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule) +{ + /* You cannot delete the same rule twice */ + if (nft_rule_is_active_next(ctx->net, rule)) { + if (nf_tables_trans_add(rule, ctx) == NULL) + return -ENOMEM; + nft_rule_disactivate_next(ctx->net, rule); + return 0; + } + return -ENOENT; +} + +static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_af_info *afi; + struct net *net = sock_net(skb->sk); + const struct nft_table *table; + struct nft_chain *chain; + struct nft_rule *rule, *tmp; + int family = nfmsg->nfgen_family, err = 0; + struct nft_ctx ctx; + + afi = nf_tables_afinfo_lookup(net, family, false); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); + if (IS_ERR(table)) + return PTR_ERR(table); + + chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); + if (IS_ERR(chain)) + return PTR_ERR(chain); + + nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); + + if (nla[NFTA_RULE_HANDLE]) { + rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); + if (IS_ERR(rule)) + return PTR_ERR(rule); + + err = nf_tables_delrule_one(&ctx, rule); + } else { + /* Remove all rules in this chain */ + list_for_each_entry_safe(rule, tmp, &chain->rules, list) { + err = nf_tables_delrule_one(&ctx, rule); + if (err < 0) + break; + } + } + + return err; +} + +static int nf_tables_commit(struct sk_buff *skb) +{ + struct net *net = sock_net(skb->sk); + struct nft_rule_trans *rupd, *tmp; + + /* Bump generation counter, invalidate any dump in progress */ + net->nft.genctr++; + + /* A new generation has just started */ + net->nft.gencursor = gencursor_next(net); + + /* Make sure all packets have left the previous generation before + * purging old rules. + */ + synchronize_rcu(); + + list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { + /* Delete this rule from the dirty list */ + list_del(&rupd->list); + + /* This rule was inactive in the past and just became active. + * Clear the next bit of the genmask since its meaning has + * changed, now it is the future. + */ + if (nft_rule_is_active(net, rupd->rule)) { + nft_rule_clear(net, rupd->rule); + nf_tables_rule_notify(skb, rupd->nlh, rupd->table, + rupd->chain, rupd->rule, + NFT_MSG_NEWRULE, 0, + rupd->family); + kfree(rupd); + continue; + } + + /* This rule is in the past, get rid of it */ + list_del_rcu(&rupd->rule->list); + nf_tables_rule_notify(skb, rupd->nlh, rupd->table, rupd->chain, + rupd->rule, NFT_MSG_DELRULE, 0, + rupd->family); + nf_tables_rule_destroy(rupd->rule); + kfree(rupd); + } + + return 0; +} + +static int nf_tables_abort(struct sk_buff *skb) +{ + struct net *net = sock_net(skb->sk); + struct nft_rule_trans *rupd, *tmp; + + list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) { + /* Delete all rules from the dirty list */ + list_del(&rupd->list); + + if (!nft_rule_is_active_next(net, rupd->rule)) { + nft_rule_clear(net, rupd->rule); + kfree(rupd); + continue; + } + + /* This rule is inactive, get rid of it */ + list_del_rcu(&rupd->rule->list); + nf_tables_rule_destroy(rupd->rule); + kfree(rupd); + } + return 0; +} + +/* + * Sets + */ + +static LIST_HEAD(nf_tables_set_ops); + +int nft_register_set(struct nft_set_ops *ops) +{ + nfnl_lock(NFNL_SUBSYS_NFTABLES); + list_add_tail(&ops->list, &nf_tables_set_ops); + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + return 0; +} +EXPORT_SYMBOL_GPL(nft_register_set); + +void nft_unregister_set(struct nft_set_ops *ops) +{ + nfnl_lock(NFNL_SUBSYS_NFTABLES); + list_del(&ops->list); + nfnl_unlock(NFNL_SUBSYS_NFTABLES); +} +EXPORT_SYMBOL_GPL(nft_unregister_set); + +static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const nla[]) +{ + const struct nft_set_ops *ops; + u32 features; + +#ifdef CONFIG_MODULES + if (list_empty(&nf_tables_set_ops)) { + nfnl_unlock(NFNL_SUBSYS_NFTABLES); + request_module("nft-set"); + nfnl_lock(NFNL_SUBSYS_NFTABLES); + if (!list_empty(&nf_tables_set_ops)) + return ERR_PTR(-EAGAIN); + } +#endif + features = 0; + if (nla[NFTA_SET_FLAGS] != NULL) { + features = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS])); + features &= NFT_SET_INTERVAL | NFT_SET_MAP; + } + + // FIXME: implement selection properly + list_for_each_entry(ops, &nf_tables_set_ops, list) { + if ((ops->features & features) != features) + continue; + if (!try_module_get(ops->owner)) + continue; + return ops; + } + + return ERR_PTR(-EOPNOTSUPP); +} + +static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { + [NFTA_SET_TABLE] = { .type = NLA_STRING }, + [NFTA_SET_NAME] = { .type = NLA_STRING }, + [NFTA_SET_FLAGS] = { .type = NLA_U32 }, + [NFTA_SET_KEY_TYPE] = { .type = NLA_U32 }, + [NFTA_SET_KEY_LEN] = { .type = NLA_U32 }, + [NFTA_SET_DATA_TYPE] = { .type = NLA_U32 }, + [NFTA_SET_DATA_LEN] = { .type = NLA_U32 }, +}; + +static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, + const struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + struct net *net = sock_net(skb->sk); + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_af_info *afi; + const struct nft_table *table = NULL; + + afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + if (nla[NFTA_SET_TABLE] != NULL) { + table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]); + if (IS_ERR(table)) + return PTR_ERR(table); + } + + nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); + return 0; +} + +struct nft_set *nf_tables_set_lookup(const struct nft_table *table, + const struct nlattr *nla) +{ + struct nft_set *set; + + if (nla == NULL) + return ERR_PTR(-EINVAL); + + list_for_each_entry(set, &table->sets, list) { + if (!nla_strcmp(nla, set->name)) + return set; + } + return ERR_PTR(-ENOENT); +} + +static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, + const char *name) +{ + const struct nft_set *i; + const char *p; + unsigned long *inuse; + unsigned int n = 0; + + p = strnchr(name, IFNAMSIZ, '%'); + if (p != NULL) { + if (p[1] != 'd' || strchr(p + 2, '%')) + return -EINVAL; + + inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL); + if (inuse == NULL) + return -ENOMEM; + + list_for_each_entry(i, &ctx->table->sets, list) { + if (!sscanf(i->name, name, &n)) + continue; + if (n < 0 || n > BITS_PER_LONG * PAGE_SIZE) + continue; + set_bit(n, inuse); + } + + n = find_first_zero_bit(inuse, BITS_PER_LONG * PAGE_SIZE); + free_page((unsigned long)inuse); + } + + snprintf(set->name, sizeof(set->name), name, n); + list_for_each_entry(i, &ctx->table->sets, list) { + if (!strcmp(set->name, i->name)) + return -ENFILE; + } + return 0; +} + +static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, + const struct nft_set *set, u16 event, u16 flags) +{ + struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + u32 portid = NETLINK_CB(ctx->skb).portid; + u32 seq = ctx->nlh->nlmsg_seq; + + event |= NFNL_SUBSYS_NFTABLES << 8; + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), + flags); + if (nlh == NULL) + goto nla_put_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = ctx->afi->family; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name)) + goto nla_put_failure; + if (nla_put_string(skb, NFTA_SET_NAME, set->name)) + goto nla_put_failure; + if (set->flags != 0) + if (nla_put_be32(skb, NFTA_SET_FLAGS, htonl(set->flags))) + goto nla_put_failure; + + if (nla_put_be32(skb, NFTA_SET_KEY_TYPE, htonl(set->ktype))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_SET_KEY_LEN, htonl(set->klen))) + goto nla_put_failure; + if (set->flags & NFT_SET_MAP) { + if (nla_put_be32(skb, NFTA_SET_DATA_TYPE, htonl(set->dtype))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_SET_DATA_LEN, htonl(set->dlen))) + goto nla_put_failure; + } + + return nlmsg_end(skb, nlh); + +nla_put_failure: + nlmsg_trim(skb, nlh); + return -1; +} + +static int nf_tables_set_notify(const struct nft_ctx *ctx, + const struct nft_set *set, + int event) +{ + struct sk_buff *skb; + u32 portid = NETLINK_CB(ctx->skb).portid; + bool report; + int err; + + report = nlmsg_report(ctx->nlh); + if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) + return 0; + + err = -ENOBUFS; + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb == NULL) + goto err; + + err = nf_tables_fill_set(skb, ctx, set, event, 0); + if (err < 0) { + kfree_skb(skb); + goto err; + } + + err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report, + GFP_KERNEL); +err: + if (err < 0) + nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err); + return err; +} + +static int nf_tables_dump_sets_table(struct nft_ctx *ctx, struct sk_buff *skb, + struct netlink_callback *cb) +{ + const struct nft_set *set; + unsigned int idx = 0, s_idx = cb->args[0]; + + if (cb->args[1]) + return skb->len; + + list_for_each_entry(set, &ctx->table->sets, list) { + if (idx < s_idx) + goto cont; + if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET, + NLM_F_MULTI) < 0) { + cb->args[0] = idx; + goto done; + } +cont: + idx++; + } + cb->args[1] = 1; +done: + return skb->len; +} + +static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb, + struct netlink_callback *cb) +{ + const struct nft_set *set; + unsigned int idx = 0, s_idx = cb->args[0]; + struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2]; + + if (cb->args[1]) + return skb->len; + + list_for_each_entry(table, &ctx->afi->tables, list) { + if (cur_table && cur_table != table) + continue; + + ctx->table = table; + list_for_each_entry(set, &ctx->table->sets, list) { + if (idx < s_idx) + goto cont; + if (nf_tables_fill_set(skb, ctx, set, NFT_MSG_NEWSET, + NLM_F_MULTI) < 0) { + cb->args[0] = idx; + cb->args[2] = (unsigned long) table; + goto done; + } +cont: + idx++; + } + } + cb->args[1] = 1; +done: + return skb->len; +} + +static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh); + struct nlattr *nla[NFTA_SET_MAX + 1]; + struct nft_ctx ctx; + int err, ret; + + err = nlmsg_parse(cb->nlh, sizeof(*nfmsg), nla, NFTA_SET_MAX, + nft_set_policy); + if (err < 0) + return err; + + err = nft_ctx_init_from_setattr(&ctx, cb->skb, cb->nlh, (void *)nla); + if (err < 0) + return err; + + if (ctx.table == NULL) + ret = nf_tables_dump_sets_all(&ctx, skb, cb); + else + ret = nf_tables_dump_sets_table(&ctx, skb, cb); + + return ret; +} + +static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nft_set *set; + struct nft_ctx ctx; + struct sk_buff *skb2; + int err; + + /* Verify existance before starting dump */ + err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla); + if (err < 0) + return err; + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = nf_tables_dump_sets, + }; + return netlink_dump_start(nlsk, skb, nlh, &c); + } + + set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); + if (IS_ERR(set)) + return PTR_ERR(set); + + skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (skb2 == NULL) + return -ENOMEM; + + err = nf_tables_fill_set(skb2, &ctx, set, NFT_MSG_NEWSET, 0); + if (err < 0) + goto err; + + return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid); + +err: + kfree_skb(skb2); + return err; +} + +static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_set_ops *ops; + const struct nft_af_info *afi; + struct net *net = sock_net(skb->sk); + struct nft_table *table; + struct nft_set *set; + struct nft_ctx ctx; + char name[IFNAMSIZ]; + unsigned int size; + bool create; + u32 ktype, klen, dlen, dtype, flags; + int err; + + if (nla[NFTA_SET_TABLE] == NULL || + nla[NFTA_SET_NAME] == NULL || + nla[NFTA_SET_KEY_LEN] == NULL) + return -EINVAL; + + ktype = NFT_DATA_VALUE; + if (nla[NFTA_SET_KEY_TYPE] != NULL) { + ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE])); + if ((ktype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK) + return -EINVAL; + } + + klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN])); + if (klen == 0 || klen > FIELD_SIZEOF(struct nft_data, data)) + return -EINVAL; + + flags = 0; + if (nla[NFTA_SET_FLAGS] != NULL) { + flags = ntohl(nla_get_be32(nla[NFTA_SET_FLAGS])); + if (flags & ~(NFT_SET_ANONYMOUS | NFT_SET_CONSTANT | + NFT_SET_INTERVAL | NFT_SET_MAP)) + return -EINVAL; + } + + dtype = 0; + dlen = 0; + if (nla[NFTA_SET_DATA_TYPE] != NULL) { + if (!(flags & NFT_SET_MAP)) + return -EINVAL; + + dtype = ntohl(nla_get_be32(nla[NFTA_SET_DATA_TYPE])); + if ((dtype & NFT_DATA_RESERVED_MASK) == NFT_DATA_RESERVED_MASK && + dtype != NFT_DATA_VERDICT) + return -EINVAL; + + if (dtype != NFT_DATA_VERDICT) { + if (nla[NFTA_SET_DATA_LEN] == NULL) + return -EINVAL; + dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN])); + if (dlen == 0 || + dlen > FIELD_SIZEOF(struct nft_data, data)) + return -EINVAL; + } else + dlen = sizeof(struct nft_data); + } else if (flags & NFT_SET_MAP) + return -EINVAL; + + create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; + + afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]); + if (IS_ERR(table)) + return PTR_ERR(table); + + nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); + + set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]); + if (IS_ERR(set)) { + if (PTR_ERR(set) != -ENOENT) + return PTR_ERR(set); + set = NULL; + } + + if (set != NULL) { + if (nlh->nlmsg_flags & NLM_F_EXCL) + return -EEXIST; + if (nlh->nlmsg_flags & NLM_F_REPLACE) + return -EOPNOTSUPP; + return 0; + } + + if (!(nlh->nlmsg_flags & NLM_F_CREATE)) + return -ENOENT; + + ops = nft_select_set_ops(nla); + if (IS_ERR(ops)) + return PTR_ERR(ops); + + size = 0; + if (ops->privsize != NULL) + size = ops->privsize(nla); + + err = -ENOMEM; + set = kzalloc(sizeof(*set) + size, GFP_KERNEL); + if (set == NULL) + goto err1; + + nla_strlcpy(name, nla[NFTA_SET_NAME], sizeof(set->name)); + err = nf_tables_set_alloc_name(&ctx, set, name); + if (err < 0) + goto err2; + + INIT_LIST_HEAD(&set->bindings); + set->ops = ops; + set->ktype = ktype; + set->klen = klen; + set->dtype = dtype; + set->dlen = dlen; + set->flags = flags; + + err = ops->init(set, nla); + if (err < 0) + goto err2; + + list_add_tail(&set->list, &table->sets); + nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET); + return 0; + +err2: + kfree(set); +err1: + module_put(ops->owner); + return err; +} + +static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) +{ + list_del(&set->list); + if (!(set->flags & NFT_SET_ANONYMOUS)) + nf_tables_set_notify(ctx, set, NFT_MSG_DELSET); + + set->ops->destroy(set); + module_put(set->ops->owner); + kfree(set); +} + +static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + struct nft_set *set; + struct nft_ctx ctx; + int err; + + if (nla[NFTA_SET_TABLE] == NULL) + return -EINVAL; + + err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla); + if (err < 0) + return err; + + set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); + if (IS_ERR(set)) + return PTR_ERR(set); + if (!list_empty(&set->bindings)) + return -EBUSY; + + nf_tables_set_destroy(&ctx, set); + return 0; +} + +static int nf_tables_bind_check_setelem(const struct nft_ctx *ctx, + const struct nft_set *set, + const struct nft_set_iter *iter, + const struct nft_set_elem *elem) +{ + enum nft_registers dreg; + + dreg = nft_type_to_reg(set->dtype); + return nft_validate_data_load(ctx, dreg, &elem->data, set->dtype); +} + +int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding) +{ + struct nft_set_binding *i; + struct nft_set_iter iter; + + if (!list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS) + return -EBUSY; + + if (set->flags & NFT_SET_MAP) { + /* If the set is already bound to the same chain all + * jumps are already validated for that chain. + */ + list_for_each_entry(i, &set->bindings, list) { + if (i->chain == binding->chain) + goto bind; + } + + iter.skip = 0; + iter.count = 0; + iter.err = 0; + iter.fn = nf_tables_bind_check_setelem; + + set->ops->walk(ctx, set, &iter); + if (iter.err < 0) { + /* Destroy anonymous sets if binding fails */ + if (set->flags & NFT_SET_ANONYMOUS) + nf_tables_set_destroy(ctx, set); + + return iter.err; + } + } +bind: + binding->chain = ctx->chain; + list_add_tail(&binding->list, &set->bindings); + return 0; +} + +void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, + struct nft_set_binding *binding) +{ + list_del(&binding->list); + + if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS) + nf_tables_set_destroy(ctx, set); +} + +/* + * Set elements + */ + +static const struct nla_policy nft_set_elem_policy[NFTA_SET_ELEM_MAX + 1] = { + [NFTA_SET_ELEM_KEY] = { .type = NLA_NESTED }, + [NFTA_SET_ELEM_DATA] = { .type = NLA_NESTED }, + [NFTA_SET_ELEM_FLAGS] = { .type = NLA_U32 }, +}; + +static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + 1] = { + [NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING }, + [NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING }, + [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED }, +}; + +static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, + const struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nfgenmsg *nfmsg = nlmsg_data(nlh); + const struct nft_af_info *afi; + const struct nft_table *table; + struct net *net = sock_net(skb->sk); + + afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); + if (IS_ERR(afi)) + return PTR_ERR(afi); + + table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]); + if (IS_ERR(table)) + return PTR_ERR(table); + + nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); + return 0; +} + +static int nf_tables_fill_setelem(struct sk_buff *skb, + const struct nft_set *set, + const struct nft_set_elem *elem) +{ + unsigned char *b = skb_tail_pointer(skb); + struct nlattr *nest; + + nest = nla_nest_start(skb, NFTA_LIST_ELEM); + if (nest == NULL) + goto nla_put_failure; + + if (nft_data_dump(skb, NFTA_SET_ELEM_KEY, &elem->key, NFT_DATA_VALUE, + set->klen) < 0) + goto nla_put_failure; + + if (set->flags & NFT_SET_MAP && + !(elem->flags & NFT_SET_ELEM_INTERVAL_END) && + nft_data_dump(skb, NFTA_SET_ELEM_DATA, &elem->data, + set->dtype == NFT_DATA_VERDICT ? NFT_DATA_VERDICT : NFT_DATA_VALUE, + set->dlen) < 0) + goto nla_put_failure; + + if (elem->flags != 0) + if (nla_put_be32(skb, NFTA_SET_ELEM_FLAGS, htonl(elem->flags))) + goto nla_put_failure; + + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + nlmsg_trim(skb, b); + return -EMSGSIZE; +} + +struct nft_set_dump_args { + const struct netlink_callback *cb; + struct nft_set_iter iter; + struct sk_buff *skb; +}; + +static int nf_tables_dump_setelem(const struct nft_ctx *ctx, + const struct nft_set *set, + const struct nft_set_iter *iter, + const struct nft_set_elem *elem) +{ + struct nft_set_dump_args *args; + + args = container_of(iter, struct nft_set_dump_args, iter); + return nf_tables_fill_setelem(args->skb, set, elem); +} + +static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) +{ + const struct nft_set *set; + struct nft_set_dump_args args; + struct nft_ctx ctx; + struct nlattr *nla[NFTA_SET_ELEM_LIST_MAX + 1]; + struct nfgenmsg *nfmsg; + struct nlmsghdr *nlh; + struct nlattr *nest; + u32 portid, seq; + int event, err; + + nfmsg = nlmsg_data(cb->nlh); + err = nlmsg_parse(cb->nlh, sizeof(*nfmsg), nla, NFTA_SET_ELEM_LIST_MAX, + nft_set_elem_list_policy); + if (err < 0) + return err; + + err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla); + if (err < 0) + return err; + + set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); + if (IS_ERR(set)) + return PTR_ERR(set); + + event = NFT_MSG_NEWSETELEM; + event |= NFNL_SUBSYS_NFTABLES << 8; + portid = NETLINK_CB(cb->skb).portid; + seq = cb->nlh->nlmsg_seq; + + nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), + NLM_F_MULTI); + if (nlh == NULL) + goto nla_put_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = NFPROTO_UNSPEC; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_string(skb, NFTA_SET_ELEM_LIST_TABLE, ctx.table->name)) + goto nla_put_failure; + if (nla_put_string(skb, NFTA_SET_ELEM_LIST_SET, set->name)) + goto nla_put_failure; + + nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS); + if (nest == NULL) + goto nla_put_failure; + + args.cb = cb; + args.skb = skb; + args.iter.skip = cb->args[0]; + args.iter.count = 0; + args.iter.err = 0; + args.iter.fn = nf_tables_dump_setelem; + set->ops->walk(&ctx, set, &args.iter); + + nla_nest_end(skb, nest); + nlmsg_end(skb, nlh); + + if (args.iter.err && args.iter.err != -EMSGSIZE) + return args.iter.err; + if (args.iter.count == cb->args[0]) + return 0; + + cb->args[0] = args.iter.count; + return skb->len; + +nla_put_failure: + return -ENOSPC; +} + +static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nft_set *set; + struct nft_ctx ctx; + int err; + + err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); + if (err < 0) + return err; + + set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); + if (IS_ERR(set)) + return PTR_ERR(set); + + if (nlh->nlmsg_flags & NLM_F_DUMP) { + struct netlink_dump_control c = { + .dump = nf_tables_dump_set, + }; + return netlink_dump_start(nlsk, skb, nlh, &c); + } + return -EOPNOTSUPP; +} + +static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, + const struct nlattr *attr) +{ + struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; + struct nft_data_desc d1, d2; + struct nft_set_elem elem; + struct nft_set_binding *binding; + enum nft_registers dreg; + int err; + + err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, + nft_set_elem_policy); + if (err < 0) + return err; + + if (nla[NFTA_SET_ELEM_KEY] == NULL) + return -EINVAL; + + elem.flags = 0; + if (nla[NFTA_SET_ELEM_FLAGS] != NULL) { + elem.flags = ntohl(nla_get_be32(nla[NFTA_SET_ELEM_FLAGS])); + if (elem.flags & ~NFT_SET_ELEM_INTERVAL_END) + return -EINVAL; + } + + if (set->flags & NFT_SET_MAP) { + if (nla[NFTA_SET_ELEM_DATA] == NULL && + !(elem.flags & NFT_SET_ELEM_INTERVAL_END)) + return -EINVAL; + } else { + if (nla[NFTA_SET_ELEM_DATA] != NULL) + return -EINVAL; + } + + err = nft_data_init(ctx, &elem.key, &d1, nla[NFTA_SET_ELEM_KEY]); + if (err < 0) + goto err1; + err = -EINVAL; + if (d1.type != NFT_DATA_VALUE || d1.len != set->klen) + goto err2; + + err = -EEXIST; + if (set->ops->get(set, &elem) == 0) + goto err2; + + if (nla[NFTA_SET_ELEM_DATA] != NULL) { + err = nft_data_init(ctx, &elem.data, &d2, nla[NFTA_SET_ELEM_DATA]); + if (err < 0) + goto err2; + + err = -EINVAL; + if (set->dtype != NFT_DATA_VERDICT && d2.len != set->dlen) + goto err3; + + dreg = nft_type_to_reg(set->dtype); + list_for_each_entry(binding, &set->bindings, list) { + struct nft_ctx bind_ctx = { + .afi = ctx->afi, + .table = ctx->table, + .chain = binding->chain, + }; + + err = nft_validate_data_load(&bind_ctx, dreg, + &elem.data, d2.type); + if (err < 0) + goto err3; + } + } + + err = set->ops->insert(set, &elem); + if (err < 0) + goto err3; + + return 0; + +err3: + if (nla[NFTA_SET_ELEM_DATA] != NULL) + nft_data_uninit(&elem.data, d2.type); +err2: + nft_data_uninit(&elem.key, d1.type); +err1: + return err; +} + +static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nlattr *attr; + struct nft_set *set; + struct nft_ctx ctx; + int rem, err; + + err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); + if (err < 0) + return err; + + set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); + if (IS_ERR(set)) + return PTR_ERR(set); + if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) + return -EBUSY; + + nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { + err = nft_add_set_elem(&ctx, set, attr); + if (err < 0) + return err; + } + return 0; +} + +static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set, + const struct nlattr *attr) +{ + struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; + struct nft_data_desc desc; + struct nft_set_elem elem; + int err; + + err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, + nft_set_elem_policy); + if (err < 0) + goto err1; + + err = -EINVAL; + if (nla[NFTA_SET_ELEM_KEY] == NULL) + goto err1; + + err = nft_data_init(ctx, &elem.key, &desc, nla[NFTA_SET_ELEM_KEY]); + if (err < 0) + goto err1; + + err = -EINVAL; + if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) + goto err2; + + err = set->ops->get(set, &elem); + if (err < 0) + goto err2; + + set->ops->remove(set, &elem); + + nft_data_uninit(&elem.key, NFT_DATA_VALUE); + if (set->flags & NFT_SET_MAP) + nft_data_uninit(&elem.data, set->dtype); + +err2: + nft_data_uninit(&elem.key, desc.type); +err1: + return err; +} + +static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const nla[]) +{ + const struct nlattr *attr; + struct nft_set *set; + struct nft_ctx ctx; + int rem, err; + + err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); + if (err < 0) + return err; + + set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); + if (IS_ERR(set)) + return PTR_ERR(set); + if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) + return -EBUSY; + + nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { + err = nft_del_setelem(&ctx, set, attr); + if (err < 0) + return err; + } + return 0; +} + +static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { + [NFT_MSG_NEWTABLE] = { + .call = nf_tables_newtable, + .attr_count = NFTA_TABLE_MAX, + .policy = nft_table_policy, + }, + [NFT_MSG_GETTABLE] = { + .call = nf_tables_gettable, + .attr_count = NFTA_TABLE_MAX, + .policy = nft_table_policy, + }, + [NFT_MSG_DELTABLE] = { + .call = nf_tables_deltable, + .attr_count = NFTA_TABLE_MAX, + .policy = nft_table_policy, + }, + [NFT_MSG_NEWCHAIN] = { + .call = nf_tables_newchain, + .attr_count = NFTA_CHAIN_MAX, + .policy = nft_chain_policy, + }, + [NFT_MSG_GETCHAIN] = { + .call = nf_tables_getchain, + .attr_count = NFTA_CHAIN_MAX, + .policy = nft_chain_policy, + }, + [NFT_MSG_DELCHAIN] = { + .call = nf_tables_delchain, + .attr_count = NFTA_CHAIN_MAX, + .policy = nft_chain_policy, + }, + [NFT_MSG_NEWRULE] = { + .call_batch = nf_tables_newrule, + .attr_count = NFTA_RULE_MAX, + .policy = nft_rule_policy, + }, + [NFT_MSG_GETRULE] = { + .call = nf_tables_getrule, + .attr_count = NFTA_RULE_MAX, + .policy = nft_rule_policy, + }, + [NFT_MSG_DELRULE] = { + .call_batch = nf_tables_delrule, + .attr_count = NFTA_RULE_MAX, + .policy = nft_rule_policy, + }, + [NFT_MSG_NEWSET] = { + .call = nf_tables_newset, + .attr_count = NFTA_SET_MAX, + .policy = nft_set_policy, + }, + [NFT_MSG_GETSET] = { + .call = nf_tables_getset, + .attr_count = NFTA_SET_MAX, + .policy = nft_set_policy, + }, + [NFT_MSG_DELSET] = { + .call = nf_tables_delset, + .attr_count = NFTA_SET_MAX, + .policy = nft_set_policy, + }, + [NFT_MSG_NEWSETELEM] = { + .call = nf_tables_newsetelem, + .attr_count = NFTA_SET_ELEM_LIST_MAX, + .policy = nft_set_elem_list_policy, + }, + [NFT_MSG_GETSETELEM] = { + .call = nf_tables_getsetelem, + .attr_count = NFTA_SET_ELEM_LIST_MAX, + .policy = nft_set_elem_list_policy, + }, + [NFT_MSG_DELSETELEM] = { + .call = nf_tables_delsetelem, + .attr_count = NFTA_SET_ELEM_LIST_MAX, + .policy = nft_set_elem_list_policy, + }, +}; + +static const struct nfnetlink_subsystem nf_tables_subsys = { + .name = "nf_tables", + .subsys_id = NFNL_SUBSYS_NFTABLES, + .cb_count = NFT_MSG_MAX, + .cb = nf_tables_cb, + .commit = nf_tables_commit, + .abort = nf_tables_abort, +}; + +/* + * Loop detection - walk through the ruleset beginning at the destination chain + * of a new jump until either the source chain is reached (loop) or all + * reachable chains have been traversed. + * + * The loop check is performed whenever a new jump verdict is added to an + * expression or verdict map or a verdict map is bound to a new chain. + */ + +static int nf_tables_check_loops(const struct nft_ctx *ctx, + const struct nft_chain *chain); + +static int nf_tables_loop_check_setelem(const struct nft_ctx *ctx, + const struct nft_set *set, + const struct nft_set_iter *iter, + const struct nft_set_elem *elem) +{ + switch (elem->data.verdict) { + case NFT_JUMP: + case NFT_GOTO: + return nf_tables_check_loops(ctx, elem->data.chain); + default: + return 0; + } +} + +static int nf_tables_check_loops(const struct nft_ctx *ctx, + const struct nft_chain *chain) +{ + const struct nft_rule *rule; + const struct nft_expr *expr, *last; + const struct nft_set *set; + struct nft_set_binding *binding; + struct nft_set_iter iter; + + if (ctx->chain == chain) + return -ELOOP; + + list_for_each_entry(rule, &chain->rules, list) { + nft_rule_for_each_expr(expr, last, rule) { + const struct nft_data *data = NULL; + int err; + + if (!expr->ops->validate) + continue; + + err = expr->ops->validate(ctx, expr, &data); + if (err < 0) + return err; + + if (data == NULL) + continue; + + switch (data->verdict) { + case NFT_JUMP: + case NFT_GOTO: + err = nf_tables_check_loops(ctx, data->chain); + if (err < 0) + return err; + default: + break; + } + } + } + + list_for_each_entry(set, &ctx->table->sets, list) { + if (!(set->flags & NFT_SET_MAP) || + set->dtype != NFT_DATA_VERDICT) + continue; + + list_for_each_entry(binding, &set->bindings, list) { + if (binding->chain != chain) + continue; + + iter.skip = 0; + iter.count = 0; + iter.err = 0; + iter.fn = nf_tables_loop_check_setelem; + + set->ops->walk(ctx, set, &iter); + if (iter.err < 0) + return iter.err; + } + } + + return 0; +} + +/** + * nft_validate_input_register - validate an expressions' input register + * + * @reg: the register number + * + * Validate that the input register is one of the general purpose + * registers. + */ +int nft_validate_input_register(enum nft_registers reg) +{ + if (reg <= NFT_REG_VERDICT) + return -EINVAL; + if (reg > NFT_REG_MAX) + return -ERANGE; + return 0; +} +EXPORT_SYMBOL_GPL(nft_validate_input_register); + +/** + * nft_validate_output_register - validate an expressions' output register + * + * @reg: the register number + * + * Validate that the output register is one of the general purpose + * registers or the verdict register. + */ +int nft_validate_output_register(enum nft_registers reg) +{ + if (reg < NFT_REG_VERDICT) + return -EINVAL; + if (reg > NFT_REG_MAX) + return -ERANGE; + return 0; +} +EXPORT_SYMBOL_GPL(nft_validate_output_register); + +/** + * nft_validate_data_load - validate an expressions' data load + * + * @ctx: context of the expression performing the load + * @reg: the destination register number + * @data: the data to load + * @type: the data type + * + * Validate that a data load uses the appropriate data type for + * the destination register. A value of NULL for the data means + * that its runtime gathered data, which is always of type + * NFT_DATA_VALUE. + */ +int nft_validate_data_load(const struct nft_ctx *ctx, enum nft_registers reg, + const struct nft_data *data, + enum nft_data_types type) +{ + int err; + + switch (reg) { + case NFT_REG_VERDICT: + if (data == NULL || type != NFT_DATA_VERDICT) + return -EINVAL; + + if (data->verdict == NFT_GOTO || data->verdict == NFT_JUMP) { + err = nf_tables_check_loops(ctx, data->chain); + if (err < 0) + return err; + + if (ctx->chain->level + 1 > data->chain->level) { + if (ctx->chain->level + 1 == NFT_JUMP_STACK_SIZE) + return -EMLINK; + data->chain->level = ctx->chain->level + 1; + } + } + + return 0; + default: + if (data != NULL && type != NFT_DATA_VALUE) + return -EINVAL; + return 0; + } +} +EXPORT_SYMBOL_GPL(nft_validate_data_load); + +static const struct nla_policy nft_verdict_policy[NFTA_VERDICT_MAX + 1] = { + [NFTA_VERDICT_CODE] = { .type = NLA_U32 }, + [NFTA_VERDICT_CHAIN] = { .type = NLA_STRING, + .len = NFT_CHAIN_MAXNAMELEN - 1 }, +}; + +static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data, + struct nft_data_desc *desc, const struct nlattr *nla) +{ + struct nlattr *tb[NFTA_VERDICT_MAX + 1]; + struct nft_chain *chain; + int err; + + err = nla_parse_nested(tb, NFTA_VERDICT_MAX, nla, nft_verdict_policy); + if (err < 0) + return err; + + if (!tb[NFTA_VERDICT_CODE]) + return -EINVAL; + data->verdict = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE])); + + switch (data->verdict) { + case NF_ACCEPT: + case NF_DROP: + case NF_QUEUE: + case NFT_CONTINUE: + case NFT_BREAK: + case NFT_RETURN: + desc->len = sizeof(data->verdict); + break; + case NFT_JUMP: + case NFT_GOTO: + if (!tb[NFTA_VERDICT_CHAIN]) + return -EINVAL; + chain = nf_tables_chain_lookup(ctx->table, + tb[NFTA_VERDICT_CHAIN]); + if (IS_ERR(chain)) + return PTR_ERR(chain); + if (chain->flags & NFT_BASE_CHAIN) + return -EOPNOTSUPP; + + chain->use++; + data->chain = chain; + desc->len = sizeof(data); + break; + default: + return -EINVAL; + } + + desc->type = NFT_DATA_VERDICT; + return 0; +} + +static void nft_verdict_uninit(const struct nft_data *data) +{ + switch (data->verdict) { + case NFT_JUMP: + case NFT_GOTO: + data->chain->use--; + break; + } +} + +static int nft_verdict_dump(struct sk_buff *skb, const struct nft_data *data) +{ + struct nlattr *nest; + + nest = nla_nest_start(skb, NFTA_DATA_VERDICT); + if (!nest) + goto nla_put_failure; + + if (nla_put_be32(skb, NFTA_VERDICT_CODE, htonl(data->verdict))) + goto nla_put_failure; + + switch (data->verdict) { + case NFT_JUMP: + case NFT_GOTO: + if (nla_put_string(skb, NFTA_VERDICT_CHAIN, data->chain->name)) + goto nla_put_failure; + } + nla_nest_end(skb, nest); + return 0; + +nla_put_failure: + return -1; +} + +static int nft_value_init(const struct nft_ctx *ctx, struct nft_data *data, + struct nft_data_desc *desc, const struct nlattr *nla) +{ + unsigned int len; + + len = nla_len(nla); + if (len == 0) + return -EINVAL; + if (len > sizeof(data->data)) + return -EOVERFLOW; + + nla_memcpy(data->data, nla, sizeof(data->data)); + desc->type = NFT_DATA_VALUE; + desc->len = len; + return 0; +} + +static int nft_value_dump(struct sk_buff *skb, const struct nft_data *data, + unsigned int len) +{ + return nla_put(skb, NFTA_DATA_VALUE, len, data->data); +} + +static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = { + [NFTA_DATA_VALUE] = { .type = NLA_BINARY, + .len = FIELD_SIZEOF(struct nft_data, data) }, + [NFTA_DATA_VERDICT] = { .type = NLA_NESTED }, +}; + +/** + * nft_data_init - parse nf_tables data netlink attributes + * + * @ctx: context of the expression using the data + * @data: destination struct nft_data + * @desc: data description + * @nla: netlink attribute containing data + * + * Parse the netlink data attributes and initialize a struct nft_data. + * The type and length of data are returned in the data description. + * + * The caller can indicate that it only wants to accept data of type + * NFT_DATA_VALUE by passing NULL for the ctx argument. + */ +int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data, + struct nft_data_desc *desc, const struct nlattr *nla) +{ + struct nlattr *tb[NFTA_DATA_MAX + 1]; + int err; + + err = nla_parse_nested(tb, NFTA_DATA_MAX, nla, nft_data_policy); + if (err < 0) + return err; + + if (tb[NFTA_DATA_VALUE]) + return nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]); + if (tb[NFTA_DATA_VERDICT] && ctx != NULL) + return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(nft_data_init); + +/** + * nft_data_uninit - release a nft_data item + * + * @data: struct nft_data to release + * @type: type of data + * + * Release a nft_data item. NFT_DATA_VALUE types can be silently discarded, + * all others need to be released by calling this function. + */ +void nft_data_uninit(const struct nft_data *data, enum nft_data_types type) +{ + switch (type) { + case NFT_DATA_VALUE: + return; + case NFT_DATA_VERDICT: + return nft_verdict_uninit(data); + default: + WARN_ON(1); + } +} +EXPORT_SYMBOL_GPL(nft_data_uninit); + +int nft_data_dump(struct sk_buff *skb, int attr, const struct nft_data *data, + enum nft_data_types type, unsigned int len) +{ + struct nlattr *nest; + int err; + + nest = nla_nest_start(skb, attr); + if (nest == NULL) + return -1; + + switch (type) { + case NFT_DATA_VALUE: + err = nft_value_dump(skb, data, len); + break; + case NFT_DATA_VERDICT: + err = nft_verdict_dump(skb, data); + break; + default: + err = -EINVAL; + WARN_ON(1); + } + + nla_nest_end(skb, nest); + return err; +} +EXPORT_SYMBOL_GPL(nft_data_dump); + +static int nf_tables_init_net(struct net *net) +{ + INIT_LIST_HEAD(&net->nft.af_info); + INIT_LIST_HEAD(&net->nft.commit_list); + return 0; +} + +static struct pernet_operations nf_tables_net_ops = { + .init = nf_tables_init_net, +}; + +static int __init nf_tables_module_init(void) +{ + int err; + + info = kmalloc(sizeof(struct nft_expr_info) * NFT_RULE_MAXEXPRS, + GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto err1; + } + + err = nf_tables_core_module_init(); + if (err < 0) + goto err2; + + err = nfnetlink_subsys_register(&nf_tables_subsys); + if (err < 0) + goto err3; + + pr_info("nf_tables: (c) 2007-2009 Patrick McHardy \n"); + return register_pernet_subsys(&nf_tables_net_ops); +err3: + nf_tables_core_module_exit(); +err2: + kfree(info); +err1: + return err; +} + +static void __exit nf_tables_module_exit(void) +{ + unregister_pernet_subsys(&nf_tables_net_ops); + nfnetlink_subsys_unregister(&nf_tables_subsys); + nf_tables_core_module_exit(); + kfree(info); +} + +module_init(nf_tables_module_init); +module_exit(nf_tables_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFTABLES); diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c new file mode 100644 index 0000000000000000000000000000000000000000..cb9e685caae19120545a7883b6883033406971ea --- /dev/null +++ b/net/netfilter/nf_tables_core.c @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2008 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void nft_cmp_fast_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1]) +{ + const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); + u32 mask; + + mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - priv->len); + if ((data[priv->sreg].data[0] & mask) == priv->data) + return; + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static bool nft_payload_fast_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_payload *priv = nft_expr_priv(expr); + const struct sk_buff *skb = pkt->skb; + struct nft_data *dest = &data[priv->dreg]; + unsigned char *ptr; + + if (priv->base == NFT_PAYLOAD_NETWORK_HEADER) + ptr = skb_network_header(skb); + else + ptr = skb_network_header(skb) + pkt->xt.thoff; + + ptr += priv->offset; + + if (unlikely(ptr + priv->len >= skb_tail_pointer(skb))) + return false; + + if (priv->len == 2) + *(u16 *)dest->data = *(u16 *)ptr; + else if (priv->len == 4) + *(u32 *)dest->data = *(u32 *)ptr; + else + *(u8 *)dest->data = *(u8 *)ptr; + return true; +} + +struct nft_jumpstack { + const struct nft_chain *chain; + const struct nft_rule *rule; + int rulenum; +}; + +static inline void +nft_chain_stats(const struct nft_chain *this, const struct nft_pktinfo *pkt, + struct nft_jumpstack *jumpstack, unsigned int stackptr) +{ + struct nft_stats __percpu *stats; + const struct nft_chain *chain = stackptr ? jumpstack[0].chain : this; + + rcu_read_lock_bh(); + stats = rcu_dereference(nft_base_chain(chain)->stats); + __this_cpu_inc(stats->pkts); + __this_cpu_add(stats->bytes, pkt->skb->len); + rcu_read_unlock_bh(); +} + +enum nft_trace { + NFT_TRACE_RULE, + NFT_TRACE_RETURN, + NFT_TRACE_POLICY, +}; + +static const char *const comments[] = { + [NFT_TRACE_RULE] = "rule", + [NFT_TRACE_RETURN] = "return", + [NFT_TRACE_POLICY] = "policy", +}; + +static struct nf_loginfo trace_loginfo = { + .type = NF_LOG_TYPE_LOG, + .u = { + .log = { + .level = 4, + .logflags = NF_LOG_MASK, + }, + }, +}; + +static inline void nft_trace_packet(const struct nft_pktinfo *pkt, + const struct nft_chain *chain, + int rulenum, enum nft_trace type) +{ + struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); + + nf_log_packet(net, pkt->xt.family, pkt->hooknum, pkt->skb, pkt->in, + pkt->out, &trace_loginfo, "TRACE: %s:%s:%s:%u ", + chain->table->name, chain->name, comments[type], + rulenum); +} + +unsigned int +nft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) +{ + const struct nft_chain *chain = ops->priv; + const struct nft_rule *rule; + const struct nft_expr *expr, *last; + struct nft_data data[NFT_REG_MAX + 1]; + unsigned int stackptr = 0; + struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; + int rulenum = 0; + /* + * Cache cursor to avoid problems in case that the cursor is updated + * while traversing the ruleset. + */ + unsigned int gencursor = ACCESS_ONCE(chain->net->nft.gencursor); + +do_chain: + rule = list_entry(&chain->rules, struct nft_rule, list); +next_rule: + data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; + list_for_each_entry_continue_rcu(rule, &chain->rules, list) { + + /* This rule is not active, skip. */ + if (unlikely(rule->genmask & (1 << gencursor))) + continue; + + rulenum++; + + nft_rule_for_each_expr(expr, last, rule) { + if (expr->ops == &nft_cmp_fast_ops) + nft_cmp_fast_eval(expr, data); + else if (expr->ops != &nft_payload_fast_ops || + !nft_payload_fast_eval(expr, data, pkt)) + expr->ops->eval(expr, data, pkt); + + if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE) + break; + } + + switch (data[NFT_REG_VERDICT].verdict) { + case NFT_BREAK: + data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; + /* fall through */ + case NFT_CONTINUE: + continue; + } + break; + } + + switch (data[NFT_REG_VERDICT].verdict) { + case NF_ACCEPT: + case NF_DROP: + case NF_QUEUE: + if (unlikely(pkt->skb->nf_trace)) + nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); + + return data[NFT_REG_VERDICT].verdict; + case NFT_JUMP: + if (unlikely(pkt->skb->nf_trace)) + nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RULE); + + BUG_ON(stackptr >= NFT_JUMP_STACK_SIZE); + jumpstack[stackptr].chain = chain; + jumpstack[stackptr].rule = rule; + jumpstack[stackptr].rulenum = rulenum; + stackptr++; + /* fall through */ + case NFT_GOTO: + chain = data[NFT_REG_VERDICT].chain; + goto do_chain; + case NFT_RETURN: + if (unlikely(pkt->skb->nf_trace)) + nft_trace_packet(pkt, chain, rulenum, NFT_TRACE_RETURN); + + /* fall through */ + case NFT_CONTINUE: + break; + default: + WARN_ON(1); + } + + if (stackptr > 0) { + if (unlikely(pkt->skb->nf_trace)) + nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_RETURN); + + stackptr--; + chain = jumpstack[stackptr].chain; + rule = jumpstack[stackptr].rule; + rulenum = jumpstack[stackptr].rulenum; + goto next_rule; + } + nft_chain_stats(chain, pkt, jumpstack, stackptr); + + if (unlikely(pkt->skb->nf_trace)) + nft_trace_packet(pkt, chain, ++rulenum, NFT_TRACE_POLICY); + + return nft_base_chain(chain)->policy; +} +EXPORT_SYMBOL_GPL(nft_do_chain_pktinfo); + +int __init nf_tables_core_module_init(void) +{ + int err; + + err = nft_immediate_module_init(); + if (err < 0) + goto err1; + + err = nft_cmp_module_init(); + if (err < 0) + goto err2; + + err = nft_lookup_module_init(); + if (err < 0) + goto err3; + + err = nft_bitwise_module_init(); + if (err < 0) + goto err4; + + err = nft_byteorder_module_init(); + if (err < 0) + goto err5; + + err = nft_payload_module_init(); + if (err < 0) + goto err6; + + return 0; + +err6: + nft_byteorder_module_exit(); +err5: + nft_bitwise_module_exit(); +err4: + nft_lookup_module_exit(); +err3: + nft_cmp_module_exit(); +err2: + nft_immediate_module_exit(); +err1: + return err; +} + +void nf_tables_core_module_exit(void) +{ + nft_payload_module_exit(); + nft_byteorder_module_exit(); + nft_bitwise_module_exit(); + nft_lookup_module_exit(); + nft_cmp_module_exit(); + nft_immediate_module_exit(); +} diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c index 572d87dc116ffa838d2f9f8838129156add7284e..046aa13b4fea6a570b990444b7ca4c6419824f8f 100644 --- a/net/netfilter/nfnetlink.c +++ b/net/netfilter/nfnetlink.c @@ -147,9 +147,6 @@ static int nfnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) const struct nfnetlink_subsystem *ss; int type, err; - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) - return -EPERM; - /* All the messages must at least contain nfgenmsg */ if (nlmsg_len(nlh) < sizeof(struct nfgenmsg)) return 0; @@ -217,9 +214,181 @@ replay: } } +static void nfnetlink_rcv_batch(struct sk_buff *skb, struct nlmsghdr *nlh, + u_int16_t subsys_id) +{ + struct sk_buff *nskb, *oskb = skb; + struct net *net = sock_net(skb->sk); + const struct nfnetlink_subsystem *ss; + const struct nfnl_callback *nc; + bool success = true, done = false; + int err; + + if (subsys_id >= NFNL_SUBSYS_COUNT) + return netlink_ack(skb, nlh, -EINVAL); +replay: + nskb = netlink_skb_clone(oskb, GFP_KERNEL); + if (!nskb) + return netlink_ack(oskb, nlh, -ENOMEM); + + nskb->sk = oskb->sk; + skb = nskb; + + nfnl_lock(subsys_id); + ss = rcu_dereference_protected(table[subsys_id].subsys, + lockdep_is_held(&table[subsys_id].mutex)); + if (!ss) { +#ifdef CONFIG_MODULES + nfnl_unlock(subsys_id); + request_module("nfnetlink-subsys-%d", subsys_id); + nfnl_lock(subsys_id); + ss = rcu_dereference_protected(table[subsys_id].subsys, + lockdep_is_held(&table[subsys_id].mutex)); + if (!ss) +#endif + { + nfnl_unlock(subsys_id); + kfree_skb(nskb); + return netlink_ack(skb, nlh, -EOPNOTSUPP); + } + } + + if (!ss->commit || !ss->abort) { + nfnl_unlock(subsys_id); + kfree_skb(nskb); + return netlink_ack(skb, nlh, -EOPNOTSUPP); + } + + while (skb->len >= nlmsg_total_size(0)) { + int msglen, type; + + nlh = nlmsg_hdr(skb); + err = 0; + + if (nlh->nlmsg_len < NLMSG_HDRLEN) { + err = -EINVAL; + goto ack; + } + + /* Only requests are handled by the kernel */ + if (!(nlh->nlmsg_flags & NLM_F_REQUEST)) { + err = -EINVAL; + goto ack; + } + + type = nlh->nlmsg_type; + if (type == NFNL_MSG_BATCH_BEGIN) { + /* Malformed: Batch begin twice */ + success = false; + goto done; + } else if (type == NFNL_MSG_BATCH_END) { + done = true; + goto done; + } else if (type < NLMSG_MIN_TYPE) { + err = -EINVAL; + goto ack; + } + + /* We only accept a batch with messages for the same + * subsystem. + */ + if (NFNL_SUBSYS_ID(type) != subsys_id) { + err = -EINVAL; + goto ack; + } + + nc = nfnetlink_find_client(type, ss); + if (!nc) { + err = -EINVAL; + goto ack; + } + + { + int min_len = nlmsg_total_size(sizeof(struct nfgenmsg)); + u_int8_t cb_id = NFNL_MSG_TYPE(nlh->nlmsg_type); + struct nlattr *cda[ss->cb[cb_id].attr_count + 1]; + struct nlattr *attr = (void *)nlh + min_len; + int attrlen = nlh->nlmsg_len - min_len; + + err = nla_parse(cda, ss->cb[cb_id].attr_count, + attr, attrlen, ss->cb[cb_id].policy); + if (err < 0) + goto ack; + + if (nc->call_batch) { + err = nc->call_batch(net->nfnl, skb, nlh, + (const struct nlattr **)cda); + } + + /* The lock was released to autoload some module, we + * have to abort and start from scratch using the + * original skb. + */ + if (err == -EAGAIN) { + ss->abort(skb); + nfnl_unlock(subsys_id); + kfree_skb(nskb); + goto replay; + } + } +ack: + if (nlh->nlmsg_flags & NLM_F_ACK || err) { + /* We don't stop processing the batch on errors, thus, + * userspace gets all the errors that the batch + * triggers. + */ + netlink_ack(skb, nlh, err); + if (err) + success = false; + } + + msglen = NLMSG_ALIGN(nlh->nlmsg_len); + if (msglen > skb->len) + msglen = skb->len; + skb_pull(skb, msglen); + } +done: + if (success && done) + ss->commit(skb); + else + ss->abort(skb); + + nfnl_unlock(subsys_id); + kfree_skb(nskb); +} + static void nfnetlink_rcv(struct sk_buff *skb) { - netlink_rcv_skb(skb, &nfnetlink_rcv_msg); + struct nlmsghdr *nlh = nlmsg_hdr(skb); + struct net *net = sock_net(skb->sk); + int msglen; + + if (nlh->nlmsg_len < NLMSG_HDRLEN || + skb->len < nlh->nlmsg_len) + return; + + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) { + netlink_ack(skb, nlh, -EPERM); + return; + } + + if (nlh->nlmsg_type == NFNL_MSG_BATCH_BEGIN) { + struct nfgenmsg *nfgenmsg; + + msglen = NLMSG_ALIGN(nlh->nlmsg_len); + if (msglen > skb->len) + msglen = skb->len; + + if (nlh->nlmsg_len < NLMSG_HDRLEN || + skb->len < NLMSG_HDRLEN + sizeof(struct nfgenmsg)) + return; + + nfgenmsg = nlmsg_data(nlh); + skb_pull(skb, msglen); + nfnetlink_rcv_batch(skb, nlh, nfgenmsg->res_id); + } else { + netlink_rcv_skb(skb, &nfnetlink_rcv_msg); + } } #ifdef CONFIG_MODULES diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 50580494148d00433c092ee0ad2a7097a1653221..476accd171452fcfbfbe01018dcadd55fee41f67 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -49,10 +49,8 @@ static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = { }; static int -ctnl_timeout_parse_policy(struct ctnl_timeout *timeout, - struct nf_conntrack_l4proto *l4proto, - struct net *net, - const struct nlattr *attr) +ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto, + struct net *net, const struct nlattr *attr) { int ret = 0; @@ -64,8 +62,7 @@ ctnl_timeout_parse_policy(struct ctnl_timeout *timeout, if (ret < 0) return ret; - ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, - &timeout->data); + ret = l4proto->ctnl_timeout.nlattr_to_obj(tb, net, timeouts); } return ret; } @@ -123,7 +120,8 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb, goto err_proto_put; } - ret = ctnl_timeout_parse_policy(matching, l4proto, net, + ret = ctnl_timeout_parse_policy(&matching->data, + l4proto, net, cda[CTA_TIMEOUT_DATA]); return ret; } @@ -138,7 +136,7 @@ cttimeout_new_timeout(struct sock *ctnl, struct sk_buff *skb, goto err_proto_put; } - ret = ctnl_timeout_parse_policy(timeout, l4proto, net, + ret = ctnl_timeout_parse_policy(&timeout->data, l4proto, net, cda[CTA_TIMEOUT_DATA]); if (ret < 0) goto err; @@ -342,6 +340,147 @@ cttimeout_del_timeout(struct sock *ctnl, struct sk_buff *skb, return ret; } +static int +cttimeout_default_set(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) +{ + __u16 l3num; + __u8 l4num; + struct nf_conntrack_l4proto *l4proto; + struct net *net = sock_net(skb->sk); + unsigned int *timeouts; + int ret; + + if (!cda[CTA_TIMEOUT_L3PROTO] || + !cda[CTA_TIMEOUT_L4PROTO] || + !cda[CTA_TIMEOUT_DATA]) + return -EINVAL; + + l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO])); + l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]); + l4proto = nf_ct_l4proto_find_get(l3num, l4num); + + /* This protocol is not supported, skip. */ + if (l4proto->l4proto != l4num) { + ret = -EOPNOTSUPP; + goto err; + } + + timeouts = l4proto->get_timeouts(net); + + ret = ctnl_timeout_parse_policy(timeouts, l4proto, net, + cda[CTA_TIMEOUT_DATA]); + if (ret < 0) + goto err; + + nf_ct_l4proto_put(l4proto); + return 0; +err: + nf_ct_l4proto_put(l4proto); + return ret; +} + +static int +cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid, + u32 seq, u32 type, int event, + struct nf_conntrack_l4proto *l4proto) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0; + + event |= NFNL_SUBSYS_CTNETLINK_TIMEOUT << 8; + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); + if (nlh == NULL) + goto nlmsg_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = AF_UNSPEC; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_be16(skb, CTA_TIMEOUT_L3PROTO, htons(l4proto->l3proto)) || + nla_put_u8(skb, CTA_TIMEOUT_L4PROTO, l4proto->l4proto)) + goto nla_put_failure; + + if (likely(l4proto->ctnl_timeout.obj_to_nlattr)) { + struct nlattr *nest_parms; + unsigned int *timeouts = l4proto->get_timeouts(net); + int ret; + + nest_parms = nla_nest_start(skb, + CTA_TIMEOUT_DATA | NLA_F_NESTED); + if (!nest_parms) + goto nla_put_failure; + + ret = l4proto->ctnl_timeout.obj_to_nlattr(skb, timeouts); + if (ret < 0) + goto nla_put_failure; + + nla_nest_end(skb, nest_parms); + } + + nlmsg_end(skb, nlh); + return skb->len; + +nlmsg_failure: +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -1; +} + +static int cttimeout_default_get(struct sock *ctnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, + const struct nlattr * const cda[]) +{ + __u16 l3num; + __u8 l4num; + struct nf_conntrack_l4proto *l4proto; + struct net *net = sock_net(skb->sk); + struct sk_buff *skb2; + int ret, err; + + if (!cda[CTA_TIMEOUT_L3PROTO] || !cda[CTA_TIMEOUT_L4PROTO]) + return -EINVAL; + + l3num = ntohs(nla_get_be16(cda[CTA_TIMEOUT_L3PROTO])); + l4num = nla_get_u8(cda[CTA_TIMEOUT_L4PROTO]); + l4proto = nf_ct_l4proto_find_get(l3num, l4num); + + /* This protocol is not supported, skip. */ + if (l4proto->l4proto != l4num) { + err = -EOPNOTSUPP; + goto err; + } + + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb2 == NULL) { + err = -ENOMEM; + goto err; + } + + ret = cttimeout_default_fill_info(net, skb2, NETLINK_CB(skb).portid, + nlh->nlmsg_seq, + NFNL_MSG_TYPE(nlh->nlmsg_type), + IPCTNL_MSG_TIMEOUT_DEFAULT_SET, + l4proto); + if (ret <= 0) { + kfree_skb(skb2); + err = -ENOMEM; + goto err; + } + ret = netlink_unicast(ctnl, skb2, NETLINK_CB(skb).portid, MSG_DONTWAIT); + if (ret > 0) + ret = 0; + + /* this avoids a loop in nfnetlink. */ + return ret == -EAGAIN ? -ENOBUFS : ret; +err: + nf_ct_l4proto_put(l4proto); + return err; +} + #ifdef CONFIG_NF_CONNTRACK_TIMEOUT static struct ctnl_timeout *ctnl_timeout_find_get(const char *name) { @@ -384,6 +523,12 @@ static const struct nfnl_callback cttimeout_cb[IPCTNL_MSG_TIMEOUT_MAX] = { [IPCTNL_MSG_TIMEOUT_DELETE] = { .call = cttimeout_del_timeout, .attr_count = CTA_TIMEOUT_MAX, .policy = cttimeout_nla_policy }, + [IPCTNL_MSG_TIMEOUT_DEFAULT_SET]= { .call = cttimeout_default_set, + .attr_count = CTA_TIMEOUT_MAX, + .policy = cttimeout_nla_policy }, + [IPCTNL_MSG_TIMEOUT_DEFAULT_GET]= { .call = cttimeout_default_get, + .attr_count = CTA_TIMEOUT_MAX, + .policy = cttimeout_nla_policy }, }; static const struct nfnetlink_subsystem cttimeout_subsys = { diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index d92cc317bf8b25a0c371b770688c860169c9dea8..3c4b69e5fe17348b422f390bf79ab4269043fe5d 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -319,7 +319,8 @@ nfulnl_set_flags(struct nfulnl_instance *inst, u_int16_t flags) } static struct sk_buff * -nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size) +nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size, + unsigned int pkt_size) { struct sk_buff *skb; unsigned int n; @@ -328,13 +329,13 @@ nfulnl_alloc_skb(u32 peer_portid, unsigned int inst_size, unsigned int pkt_size) * message. WARNING: has to be <= 128k due to slab restrictions */ n = max(inst_size, pkt_size); - skb = nfnetlink_alloc_skb(&init_net, n, peer_portid, GFP_ATOMIC); + skb = nfnetlink_alloc_skb(net, n, peer_portid, GFP_ATOMIC); if (!skb) { if (n > pkt_size) { /* try to allocate only as much as we need for current * packet */ - skb = nfnetlink_alloc_skb(&init_net, pkt_size, + skb = nfnetlink_alloc_skb(net, pkt_size, peer_portid, GFP_ATOMIC); if (!skb) pr_err("nfnetlink_log: can't even alloc %u bytes\n", @@ -702,8 +703,8 @@ nfulnl_log_packet(struct net *net, } if (!inst->skb) { - inst->skb = nfulnl_alloc_skb(inst->peer_portid, inst->nlbufsiz, - size); + inst->skb = nfulnl_alloc_skb(net, inst->peer_portid, + inst->nlbufsiz, size); if (!inst->skb) goto alloc_failure; } diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c index ae2e5c11d01ac9887c5158d0084f193c624373ef..21258cf7009118c3820c7b9575b4a4bd1b331f1f 100644 --- a/net/netfilter/nfnetlink_queue_core.c +++ b/net/netfilter/nfnetlink_queue_core.c @@ -298,7 +298,7 @@ nfqnl_put_packet_info(struct sk_buff *nlskb, struct sk_buff *packet, } static struct sk_buff * -nfqnl_build_packet_message(struct nfqnl_instance *queue, +nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry, __be32 **packet_id_ptr) { @@ -372,7 +372,7 @@ nfqnl_build_packet_message(struct nfqnl_instance *queue, if (queue->flags & NFQA_CFG_F_CONNTRACK) ct = nfqnl_ct_get(entskb, &size, &ctinfo); - skb = nfnetlink_alloc_skb(&init_net, size, queue->peer_portid, + skb = nfnetlink_alloc_skb(net, size, queue->peer_portid, GFP_ATOMIC); if (!skb) return NULL; @@ -525,7 +525,7 @@ __nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue, __be32 *packet_id_ptr; int failopen = 0; - nskb = nfqnl_build_packet_message(queue, entry, &packet_id_ptr); + nskb = nfqnl_build_packet_message(net, queue, entry, &packet_id_ptr); if (nskb == NULL) { err = -ENOMEM; goto err_out; diff --git a/net/netfilter/nft_bitwise.c b/net/netfilter/nft_bitwise.c new file mode 100644 index 0000000000000000000000000000000000000000..4fb6ee2c1106a356775cbad15398ede0047000ed --- /dev/null +++ b/net/netfilter/nft_bitwise.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_bitwise { + enum nft_registers sreg:8; + enum nft_registers dreg:8; + u8 len; + struct nft_data mask; + struct nft_data xor; +}; + +static void nft_bitwise_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_bitwise *priv = nft_expr_priv(expr); + const struct nft_data *src = &data[priv->sreg]; + struct nft_data *dst = &data[priv->dreg]; + unsigned int i; + + for (i = 0; i < DIV_ROUND_UP(priv->len, 4); i++) { + dst->data[i] = (src->data[i] & priv->mask.data[i]) ^ + priv->xor.data[i]; + } +} + +static const struct nla_policy nft_bitwise_policy[NFTA_BITWISE_MAX + 1] = { + [NFTA_BITWISE_SREG] = { .type = NLA_U32 }, + [NFTA_BITWISE_DREG] = { .type = NLA_U32 }, + [NFTA_BITWISE_LEN] = { .type = NLA_U32 }, + [NFTA_BITWISE_MASK] = { .type = NLA_NESTED }, + [NFTA_BITWISE_XOR] = { .type = NLA_NESTED }, +}; + +static int nft_bitwise_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_bitwise *priv = nft_expr_priv(expr); + struct nft_data_desc d1, d2; + int err; + + if (tb[NFTA_BITWISE_SREG] == NULL || + tb[NFTA_BITWISE_DREG] == NULL || + tb[NFTA_BITWISE_LEN] == NULL || + tb[NFTA_BITWISE_MASK] == NULL || + tb[NFTA_BITWISE_XOR] == NULL) + return -EINVAL; + + priv->sreg = ntohl(nla_get_be32(tb[NFTA_BITWISE_SREG])); + err = nft_validate_input_register(priv->sreg); + if (err < 0) + return err; + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_BITWISE_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); + if (err < 0) + return err; + + priv->len = ntohl(nla_get_be32(tb[NFTA_BITWISE_LEN])); + + err = nft_data_init(NULL, &priv->mask, &d1, tb[NFTA_BITWISE_MASK]); + if (err < 0) + return err; + if (d1.len != priv->len) + return -EINVAL; + + err = nft_data_init(NULL, &priv->xor, &d2, tb[NFTA_BITWISE_XOR]); + if (err < 0) + return err; + if (d2.len != priv->len) + return -EINVAL; + + return 0; +} + +static int nft_bitwise_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_bitwise *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_BITWISE_SREG, htonl(priv->sreg))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_BITWISE_DREG, htonl(priv->dreg))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_BITWISE_LEN, htonl(priv->len))) + goto nla_put_failure; + + if (nft_data_dump(skb, NFTA_BITWISE_MASK, &priv->mask, + NFT_DATA_VALUE, priv->len) < 0) + goto nla_put_failure; + + if (nft_data_dump(skb, NFTA_BITWISE_XOR, &priv->xor, + NFT_DATA_VALUE, priv->len) < 0) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_bitwise_type; +static const struct nft_expr_ops nft_bitwise_ops = { + .type = &nft_bitwise_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_bitwise)), + .eval = nft_bitwise_eval, + .init = nft_bitwise_init, + .dump = nft_bitwise_dump, +}; + +static struct nft_expr_type nft_bitwise_type __read_mostly = { + .name = "bitwise", + .ops = &nft_bitwise_ops, + .policy = nft_bitwise_policy, + .maxattr = NFTA_BITWISE_MAX, + .owner = THIS_MODULE, +}; + +int __init nft_bitwise_module_init(void) +{ + return nft_register_expr(&nft_bitwise_type); +} + +void nft_bitwise_module_exit(void) +{ + nft_unregister_expr(&nft_bitwise_type); +} diff --git a/net/netfilter/nft_byteorder.c b/net/netfilter/nft_byteorder.c new file mode 100644 index 0000000000000000000000000000000000000000..c39ed8d29df1716101985000b10c4945f69384c9 --- /dev/null +++ b/net/netfilter/nft_byteorder.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_byteorder { + enum nft_registers sreg:8; + enum nft_registers dreg:8; + enum nft_byteorder_ops op:8; + u8 len; + u8 size; +}; + +static void nft_byteorder_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_byteorder *priv = nft_expr_priv(expr); + struct nft_data *src = &data[priv->sreg], *dst = &data[priv->dreg]; + union { u32 u32; u16 u16; } *s, *d; + unsigned int i; + + s = (void *)src->data; + d = (void *)dst->data; + + switch (priv->size) { + case 4: + switch (priv->op) { + case NFT_BYTEORDER_NTOH: + for (i = 0; i < priv->len / 4; i++) + d[i].u32 = ntohl((__force __be32)s[i].u32); + break; + case NFT_BYTEORDER_HTON: + for (i = 0; i < priv->len / 4; i++) + d[i].u32 = (__force __u32)htonl(s[i].u32); + break; + } + break; + case 2: + switch (priv->op) { + case NFT_BYTEORDER_NTOH: + for (i = 0; i < priv->len / 2; i++) + d[i].u16 = ntohs((__force __be16)s[i].u16); + break; + case NFT_BYTEORDER_HTON: + for (i = 0; i < priv->len / 2; i++) + d[i].u16 = (__force __u16)htons(s[i].u16); + break; + } + break; + } +} + +static const struct nla_policy nft_byteorder_policy[NFTA_BYTEORDER_MAX + 1] = { + [NFTA_BYTEORDER_SREG] = { .type = NLA_U32 }, + [NFTA_BYTEORDER_DREG] = { .type = NLA_U32 }, + [NFTA_BYTEORDER_OP] = { .type = NLA_U32 }, + [NFTA_BYTEORDER_LEN] = { .type = NLA_U32 }, + [NFTA_BYTEORDER_SIZE] = { .type = NLA_U32 }, +}; + +static int nft_byteorder_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_byteorder *priv = nft_expr_priv(expr); + int err; + + if (tb[NFTA_BYTEORDER_SREG] == NULL || + tb[NFTA_BYTEORDER_DREG] == NULL || + tb[NFTA_BYTEORDER_LEN] == NULL || + tb[NFTA_BYTEORDER_SIZE] == NULL || + tb[NFTA_BYTEORDER_OP] == NULL) + return -EINVAL; + + priv->sreg = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_SREG])); + err = nft_validate_input_register(priv->sreg); + if (err < 0) + return err; + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); + if (err < 0) + return err; + + priv->op = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_OP])); + switch (priv->op) { + case NFT_BYTEORDER_NTOH: + case NFT_BYTEORDER_HTON: + break; + default: + return -EINVAL; + } + + priv->len = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_LEN])); + if (priv->len == 0 || priv->len > FIELD_SIZEOF(struct nft_data, data)) + return -EINVAL; + + priv->size = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_SIZE])); + switch (priv->size) { + case 2: + case 4: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nft_byteorder_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_byteorder *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_BYTEORDER_SREG, htonl(priv->sreg))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_BYTEORDER_DREG, htonl(priv->dreg))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_BYTEORDER_OP, htonl(priv->op))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->len))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_BYTEORDER_SIZE, htonl(priv->size))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_byteorder_type; +static const struct nft_expr_ops nft_byteorder_ops = { + .type = &nft_byteorder_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_byteorder)), + .eval = nft_byteorder_eval, + .init = nft_byteorder_init, + .dump = nft_byteorder_dump, +}; + +static struct nft_expr_type nft_byteorder_type __read_mostly = { + .name = "byteorder", + .ops = &nft_byteorder_ops, + .policy = nft_byteorder_policy, + .maxattr = NFTA_BYTEORDER_MAX, + .owner = THIS_MODULE, +}; + +int __init nft_byteorder_module_init(void) +{ + return nft_register_expr(&nft_byteorder_type); +} + +void nft_byteorder_module_exit(void) +{ + nft_unregister_expr(&nft_byteorder_type); +} diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c new file mode 100644 index 0000000000000000000000000000000000000000..954925db414da8f973ecef119baab67dec229c12 --- /dev/null +++ b/net/netfilter/nft_cmp.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_cmp_expr { + struct nft_data data; + enum nft_registers sreg:8; + u8 len; + enum nft_cmp_ops op:8; +}; + +static void nft_cmp_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_cmp_expr *priv = nft_expr_priv(expr); + int d; + + d = nft_data_cmp(&data[priv->sreg], &priv->data, priv->len); + switch (priv->op) { + case NFT_CMP_EQ: + if (d != 0) + goto mismatch; + break; + case NFT_CMP_NEQ: + if (d == 0) + goto mismatch; + break; + case NFT_CMP_LT: + if (d == 0) + goto mismatch; + case NFT_CMP_LTE: + if (d > 0) + goto mismatch; + break; + case NFT_CMP_GT: + if (d == 0) + goto mismatch; + case NFT_CMP_GTE: + if (d < 0) + goto mismatch; + break; + } + return; + +mismatch: + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static const struct nla_policy nft_cmp_policy[NFTA_CMP_MAX + 1] = { + [NFTA_CMP_SREG] = { .type = NLA_U32 }, + [NFTA_CMP_OP] = { .type = NLA_U32 }, + [NFTA_CMP_DATA] = { .type = NLA_NESTED }, +}; + +static int nft_cmp_init(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_cmp_expr *priv = nft_expr_priv(expr); + struct nft_data_desc desc; + int err; + + priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); + priv->op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); + + err = nft_data_init(NULL, &priv->data, &desc, tb[NFTA_CMP_DATA]); + BUG_ON(err < 0); + + priv->len = desc.len; + return 0; +} + +static int nft_cmp_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_cmp_expr *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_CMP_OP, htonl(priv->op))) + goto nla_put_failure; + + if (nft_data_dump(skb, NFTA_CMP_DATA, &priv->data, + NFT_DATA_VALUE, priv->len) < 0) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_cmp_type; +static const struct nft_expr_ops nft_cmp_ops = { + .type = &nft_cmp_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_expr)), + .eval = nft_cmp_eval, + .init = nft_cmp_init, + .dump = nft_cmp_dump, +}; + +static int nft_cmp_fast_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); + struct nft_data_desc desc; + struct nft_data data; + u32 mask; + int err; + + priv->sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); + + err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); + BUG_ON(err < 0); + desc.len *= BITS_PER_BYTE; + + mask = ~0U >> (sizeof(priv->data) * BITS_PER_BYTE - desc.len); + priv->data = data.data[0] & mask; + priv->len = desc.len; + return 0; +} + +static int nft_cmp_fast_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_cmp_fast_expr *priv = nft_expr_priv(expr); + struct nft_data data; + + if (nla_put_be32(skb, NFTA_CMP_SREG, htonl(priv->sreg))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_CMP_OP, htonl(NFT_CMP_EQ))) + goto nla_put_failure; + + data.data[0] = priv->data; + if (nft_data_dump(skb, NFTA_CMP_DATA, &data, + NFT_DATA_VALUE, priv->len / BITS_PER_BYTE) < 0) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +const struct nft_expr_ops nft_cmp_fast_ops = { + .type = &nft_cmp_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_cmp_fast_expr)), + .eval = NULL, /* inlined */ + .init = nft_cmp_fast_init, + .dump = nft_cmp_fast_dump, +}; + +static const struct nft_expr_ops * +nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) +{ + struct nft_data_desc desc; + struct nft_data data; + enum nft_registers sreg; + enum nft_cmp_ops op; + int err; + + if (tb[NFTA_CMP_SREG] == NULL || + tb[NFTA_CMP_OP] == NULL || + tb[NFTA_CMP_DATA] == NULL) + return ERR_PTR(-EINVAL); + + sreg = ntohl(nla_get_be32(tb[NFTA_CMP_SREG])); + err = nft_validate_input_register(sreg); + if (err < 0) + return ERR_PTR(err); + + op = ntohl(nla_get_be32(tb[NFTA_CMP_OP])); + switch (op) { + case NFT_CMP_EQ: + case NFT_CMP_NEQ: + case NFT_CMP_LT: + case NFT_CMP_LTE: + case NFT_CMP_GT: + case NFT_CMP_GTE: + break; + default: + return ERR_PTR(-EINVAL); + } + + err = nft_data_init(NULL, &data, &desc, tb[NFTA_CMP_DATA]); + if (err < 0) + return ERR_PTR(err); + + if (desc.len <= sizeof(u32) && op == NFT_CMP_EQ) + return &nft_cmp_fast_ops; + else + return &nft_cmp_ops; +} + +static struct nft_expr_type nft_cmp_type __read_mostly = { + .name = "cmp", + .select_ops = nft_cmp_select_ops, + .policy = nft_cmp_policy, + .maxattr = NFTA_CMP_MAX, + .owner = THIS_MODULE, +}; + +int __init nft_cmp_module_init(void) +{ + return nft_register_expr(&nft_cmp_type); +} + +void nft_cmp_module_exit(void) +{ + nft_unregister_expr(&nft_cmp_type); +} diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c new file mode 100644 index 0000000000000000000000000000000000000000..a82667c64729a3964e8fbd44f0ddd69d1cefa5c3 --- /dev/null +++ b/net/netfilter/nft_compat.c @@ -0,0 +1,768 @@ +/* + * (C) 2012-2013 by Pablo Neira Ayuso + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This software has been sponsored by Sophos Astaro + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for set_fs */ +#include + +union nft_entry { + struct ipt_entry e4; + struct ip6t_entry e6; +}; + +static inline void +nft_compat_set_par(struct xt_action_param *par, void *xt, const void *xt_info) +{ + par->target = xt; + par->targinfo = xt_info; + par->hotdrop = false; +} + +static void nft_target_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + void *info = nft_expr_priv(expr); + struct xt_target *target = expr->ops->data; + struct sk_buff *skb = pkt->skb; + int ret; + + nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info); + + ret = target->target(skb, &pkt->xt); + + if (pkt->xt.hotdrop) + ret = NF_DROP; + + switch(ret) { + case XT_CONTINUE: + data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; + break; + default: + data[NFT_REG_VERDICT].verdict = ret; + break; + } + return; +} + +static const struct nla_policy nft_target_policy[NFTA_TARGET_MAX + 1] = { + [NFTA_TARGET_NAME] = { .type = NLA_NUL_STRING }, + [NFTA_TARGET_REV] = { .type = NLA_U32 }, + [NFTA_TARGET_INFO] = { .type = NLA_BINARY }, +}; + +static void +nft_target_set_tgchk_param(struct xt_tgchk_param *par, + const struct nft_ctx *ctx, + struct xt_target *target, void *info, + union nft_entry *entry, u8 proto, bool inv) +{ + par->net = &init_net; + par->table = ctx->table->name; + switch (ctx->afi->family) { + case AF_INET: + entry->e4.ip.proto = proto; + entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0; + break; + case AF_INET6: + entry->e6.ipv6.proto = proto; + entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; + break; + } + par->entryinfo = entry; + par->target = target; + par->targinfo = info; + if (ctx->chain->flags & NFT_BASE_CHAIN) { + const struct nft_base_chain *basechain = + nft_base_chain(ctx->chain); + const struct nf_hook_ops *ops = &basechain->ops; + + par->hook_mask = 1 << ops->hooknum; + } + par->family = ctx->afi->family; +} + +static void target_compat_from_user(struct xt_target *t, void *in, void *out) +{ +#ifdef CONFIG_COMPAT + if (t->compat_from_user) { + int pad; + + t->compat_from_user(out, in); + pad = XT_ALIGN(t->targetsize) - t->targetsize; + if (pad > 0) + memset(out + t->targetsize, 0, pad); + } else +#endif + memcpy(out, in, XT_ALIGN(t->targetsize)); +} + +static inline int nft_compat_target_offset(struct xt_target *target) +{ +#ifdef CONFIG_COMPAT + return xt_compat_target_offset(target); +#else + return 0; +#endif +} + +static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] = { + [NFTA_RULE_COMPAT_PROTO] = { .type = NLA_U32 }, + [NFTA_RULE_COMPAT_FLAGS] = { .type = NLA_U32 }, +}; + +static u8 nft_parse_compat(const struct nlattr *attr, bool *inv) +{ + struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1]; + u32 flags; + int err; + + err = nla_parse_nested(tb, NFTA_RULE_COMPAT_MAX, attr, + nft_rule_compat_policy); + if (err < 0) + return err; + + if (!tb[NFTA_RULE_COMPAT_PROTO] || !tb[NFTA_RULE_COMPAT_FLAGS]) + return -EINVAL; + + flags = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_FLAGS])); + if (flags & ~NFT_RULE_COMPAT_F_MASK) + return -EINVAL; + if (flags & NFT_RULE_COMPAT_F_INV) + *inv = true; + + return ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO])); +} + +static int +nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + void *info = nft_expr_priv(expr); + struct xt_target *target = expr->ops->data; + struct xt_tgchk_param par; + size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO])); + u8 proto = 0; + bool inv = false; + union nft_entry e = {}; + int ret; + + target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info); + + if (ctx->nla[NFTA_RULE_COMPAT]) + proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv); + + nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv); + + ret = xt_check_target(&par, size, proto, inv); + if (ret < 0) + goto err; + + /* The standard target cannot be used */ + if (target->target == NULL) { + ret = -EINVAL; + goto err; + } + + return 0; +err: + module_put(target->me); + return ret; +} + +static void +nft_target_destroy(const struct nft_expr *expr) +{ + struct xt_target *target = expr->ops->data; + + module_put(target->me); +} + +static int +target_dump_info(struct sk_buff *skb, const struct xt_target *t, const void *in) +{ + int ret; + +#ifdef CONFIG_COMPAT + if (t->compat_to_user) { + mm_segment_t old_fs; + void *out; + + out = kmalloc(XT_ALIGN(t->targetsize), GFP_ATOMIC); + if (out == NULL) + return -ENOMEM; + + /* We want to reuse existing compat_to_user */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + t->compat_to_user(out, in); + set_fs(old_fs); + ret = nla_put(skb, NFTA_TARGET_INFO, XT_ALIGN(t->targetsize), out); + kfree(out); + } else +#endif + ret = nla_put(skb, NFTA_TARGET_INFO, XT_ALIGN(t->targetsize), in); + + return ret; +} + +static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct xt_target *target = expr->ops->data; + void *info = nft_expr_priv(expr); + + if (nla_put_string(skb, NFTA_TARGET_NAME, target->name) || + nla_put_be32(skb, NFTA_TARGET_REV, htonl(target->revision)) || + target_dump_info(skb, target, info)) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -1; +} + +static int nft_target_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) +{ + struct xt_target *target = expr->ops->data; + unsigned int hook_mask = 0; + + if (ctx->chain->flags & NFT_BASE_CHAIN) { + const struct nft_base_chain *basechain = + nft_base_chain(ctx->chain); + const struct nf_hook_ops *ops = &basechain->ops; + + hook_mask = 1 << ops->hooknum; + if (hook_mask & target->hooks) + return 0; + + /* This target is being called from an invalid chain */ + return -EINVAL; + } + return 0; +} + +static void nft_match_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + void *info = nft_expr_priv(expr); + struct xt_match *match = expr->ops->data; + struct sk_buff *skb = pkt->skb; + bool ret; + + nft_compat_set_par((struct xt_action_param *)&pkt->xt, match, info); + + ret = match->match(skb, (struct xt_action_param *)&pkt->xt); + + if (pkt->xt.hotdrop) { + data[NFT_REG_VERDICT].verdict = NF_DROP; + return; + } + + switch(ret) { + case true: + data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; + break; + case false: + data[NFT_REG_VERDICT].verdict = NFT_BREAK; + break; + } +} + +static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = { + [NFTA_MATCH_NAME] = { .type = NLA_NUL_STRING }, + [NFTA_MATCH_REV] = { .type = NLA_U32 }, + [NFTA_MATCH_INFO] = { .type = NLA_BINARY }, +}; + +/* struct xt_mtchk_param and xt_tgchk_param look very similar */ +static void +nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, + struct xt_match *match, void *info, + union nft_entry *entry, u8 proto, bool inv) +{ + par->net = &init_net; + par->table = ctx->table->name; + switch (ctx->afi->family) { + case AF_INET: + entry->e4.ip.proto = proto; + entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0; + break; + case AF_INET6: + entry->e6.ipv6.proto = proto; + entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; + break; + } + par->entryinfo = entry; + par->match = match; + par->matchinfo = info; + if (ctx->chain->flags & NFT_BASE_CHAIN) { + const struct nft_base_chain *basechain = + nft_base_chain(ctx->chain); + const struct nf_hook_ops *ops = &basechain->ops; + + par->hook_mask = 1 << ops->hooknum; + } + par->family = ctx->afi->family; +} + +static void match_compat_from_user(struct xt_match *m, void *in, void *out) +{ +#ifdef CONFIG_COMPAT + if (m->compat_from_user) { + int pad; + + m->compat_from_user(out, in); + pad = XT_ALIGN(m->matchsize) - m->matchsize; + if (pad > 0) + memset(out + m->matchsize, 0, pad); + } else +#endif + memcpy(out, in, XT_ALIGN(m->matchsize)); +} + +static int +nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + void *info = nft_expr_priv(expr); + struct xt_match *match = expr->ops->data; + struct xt_mtchk_param par; + size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO])); + u8 proto = 0; + bool inv = false; + union nft_entry e = {}; + int ret; + + match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info); + + if (ctx->nla[NFTA_RULE_COMPAT]) + proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv); + + nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv); + + ret = xt_check_match(&par, size, proto, inv); + if (ret < 0) + goto err; + + return 0; +err: + module_put(match->me); + return ret; +} + +static void +nft_match_destroy(const struct nft_expr *expr) +{ + struct xt_match *match = expr->ops->data; + + module_put(match->me); +} + +static int +match_dump_info(struct sk_buff *skb, const struct xt_match *m, const void *in) +{ + int ret; + +#ifdef CONFIG_COMPAT + if (m->compat_to_user) { + mm_segment_t old_fs; + void *out; + + out = kmalloc(XT_ALIGN(m->matchsize), GFP_ATOMIC); + if (out == NULL) + return -ENOMEM; + + /* We want to reuse existing compat_to_user */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + m->compat_to_user(out, in); + set_fs(old_fs); + ret = nla_put(skb, NFTA_MATCH_INFO, XT_ALIGN(m->matchsize), out); + kfree(out); + } else +#endif + ret = nla_put(skb, NFTA_MATCH_INFO, XT_ALIGN(m->matchsize), in); + + return ret; +} + +static inline int nft_compat_match_offset(struct xt_match *match) +{ +#ifdef CONFIG_COMPAT + return xt_compat_match_offset(match); +#else + return 0; +#endif +} + +static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + void *info = nft_expr_priv(expr); + struct xt_match *match = expr->ops->data; + + if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) || + nla_put_be32(skb, NFTA_MATCH_REV, htonl(match->revision)) || + match_dump_info(skb, match, info)) + goto nla_put_failure; + + return 0; + +nla_put_failure: + return -1; +} + +static int nft_match_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) +{ + struct xt_match *match = expr->ops->data; + unsigned int hook_mask = 0; + + if (ctx->chain->flags & NFT_BASE_CHAIN) { + const struct nft_base_chain *basechain = + nft_base_chain(ctx->chain); + const struct nf_hook_ops *ops = &basechain->ops; + + hook_mask = 1 << ops->hooknum; + if (hook_mask & match->hooks) + return 0; + + /* This match is being called from an invalid chain */ + return -EINVAL; + } + return 0; +} + +static int +nfnl_compat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, + int event, u16 family, const char *name, + int rev, int target) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfmsg; + unsigned int flags = portid ? NLM_F_MULTI : 0; + + event |= NFNL_SUBSYS_NFT_COMPAT << 8; + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); + if (nlh == NULL) + goto nlmsg_failure; + + nfmsg = nlmsg_data(nlh); + nfmsg->nfgen_family = family; + nfmsg->version = NFNETLINK_V0; + nfmsg->res_id = 0; + + if (nla_put_string(skb, NFTA_COMPAT_NAME, name) || + nla_put_be32(skb, NFTA_COMPAT_REV, htonl(rev)) || + nla_put_be32(skb, NFTA_COMPAT_TYPE, htonl(target))) + goto nla_put_failure; + + nlmsg_end(skb, nlh); + return skb->len; + +nlmsg_failure: +nla_put_failure: + nlmsg_cancel(skb, nlh); + return -1; +} + +static int +nfnl_compat_get(struct sock *nfnl, struct sk_buff *skb, + const struct nlmsghdr *nlh, const struct nlattr * const tb[]) +{ + int ret = 0, target; + struct nfgenmsg *nfmsg; + const char *fmt; + const char *name; + u32 rev; + struct sk_buff *skb2; + + if (tb[NFTA_COMPAT_NAME] == NULL || + tb[NFTA_COMPAT_REV] == NULL || + tb[NFTA_COMPAT_TYPE] == NULL) + return -EINVAL; + + name = nla_data(tb[NFTA_COMPAT_NAME]); + rev = ntohl(nla_get_be32(tb[NFTA_COMPAT_REV])); + target = ntohl(nla_get_be32(tb[NFTA_COMPAT_TYPE])); + + nfmsg = nlmsg_data(nlh); + + switch(nfmsg->nfgen_family) { + case AF_INET: + fmt = "ipt_%s"; + break; + case AF_INET6: + fmt = "ip6t_%s"; + break; + default: + pr_err("nft_compat: unsupported protocol %d\n", + nfmsg->nfgen_family); + return -EINVAL; + } + + try_then_request_module(xt_find_revision(nfmsg->nfgen_family, name, + rev, target, &ret), + fmt, name); + + if (ret < 0) + return ret; + + skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (skb2 == NULL) + return -ENOMEM; + + /* include the best revision for this extension in the message */ + if (nfnl_compat_fill_info(skb2, NETLINK_CB(skb).portid, + nlh->nlmsg_seq, + NFNL_MSG_TYPE(nlh->nlmsg_type), + NFNL_MSG_COMPAT_GET, + nfmsg->nfgen_family, + name, ret, target) <= 0) { + kfree_skb(skb2); + return -ENOSPC; + } + + ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, + MSG_DONTWAIT); + if (ret > 0) + ret = 0; + + return ret == -EAGAIN ? -ENOBUFS : ret; +} + +static const struct nla_policy nfnl_compat_policy_get[NFTA_COMPAT_MAX+1] = { + [NFTA_COMPAT_NAME] = { .type = NLA_NUL_STRING, + .len = NFT_COMPAT_NAME_MAX-1 }, + [NFTA_COMPAT_REV] = { .type = NLA_U32 }, + [NFTA_COMPAT_TYPE] = { .type = NLA_U32 }, +}; + +static const struct nfnl_callback nfnl_nft_compat_cb[NFNL_MSG_COMPAT_MAX] = { + [NFNL_MSG_COMPAT_GET] = { .call = nfnl_compat_get, + .attr_count = NFTA_COMPAT_MAX, + .policy = nfnl_compat_policy_get }, +}; + +static const struct nfnetlink_subsystem nfnl_compat_subsys = { + .name = "nft-compat", + .subsys_id = NFNL_SUBSYS_NFT_COMPAT, + .cb_count = NFNL_MSG_COMPAT_MAX, + .cb = nfnl_nft_compat_cb, +}; + +static LIST_HEAD(nft_match_list); + +struct nft_xt { + struct list_head head; + struct nft_expr_ops ops; +}; + +static struct nft_expr_type nft_match_type; + +static const struct nft_expr_ops * +nft_match_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + struct nft_xt *nft_match; + struct xt_match *match; + char *mt_name; + __u32 rev, family; + + if (tb[NFTA_MATCH_NAME] == NULL || + tb[NFTA_MATCH_REV] == NULL || + tb[NFTA_MATCH_INFO] == NULL) + return ERR_PTR(-EINVAL); + + mt_name = nla_data(tb[NFTA_MATCH_NAME]); + rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV])); + family = ctx->afi->family; + + /* Re-use the existing match if it's already loaded. */ + list_for_each_entry(nft_match, &nft_match_list, head) { + struct xt_match *match = nft_match->ops.data; + + if (strcmp(match->name, mt_name) == 0 && + match->revision == rev && match->family == family) + return &nft_match->ops; + } + + match = xt_request_find_match(family, mt_name, rev); + if (IS_ERR(match)) + return ERR_PTR(-ENOENT); + + /* This is the first time we use this match, allocate operations */ + nft_match = kzalloc(sizeof(struct nft_xt), GFP_KERNEL); + if (nft_match == NULL) + return ERR_PTR(-ENOMEM); + + nft_match->ops.type = &nft_match_type; + nft_match->ops.size = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize) + + nft_compat_match_offset(match)); + nft_match->ops.eval = nft_match_eval; + nft_match->ops.init = nft_match_init; + nft_match->ops.destroy = nft_match_destroy; + nft_match->ops.dump = nft_match_dump; + nft_match->ops.validate = nft_match_validate; + nft_match->ops.data = match; + + list_add(&nft_match->head, &nft_match_list); + + return &nft_match->ops; +} + +static void nft_match_release(void) +{ + struct nft_xt *nft_match, *tmp; + + list_for_each_entry_safe(nft_match, tmp, &nft_match_list, head) + kfree(nft_match); +} + +static struct nft_expr_type nft_match_type __read_mostly = { + .name = "match", + .select_ops = nft_match_select_ops, + .policy = nft_match_policy, + .maxattr = NFTA_MATCH_MAX, + .owner = THIS_MODULE, +}; + +static LIST_HEAD(nft_target_list); + +static struct nft_expr_type nft_target_type; + +static const struct nft_expr_ops * +nft_target_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + struct nft_xt *nft_target; + struct xt_target *target; + char *tg_name; + __u32 rev, family; + + if (tb[NFTA_TARGET_NAME] == NULL || + tb[NFTA_TARGET_REV] == NULL || + tb[NFTA_TARGET_INFO] == NULL) + return ERR_PTR(-EINVAL); + + tg_name = nla_data(tb[NFTA_TARGET_NAME]); + rev = ntohl(nla_get_be32(tb[NFTA_TARGET_REV])); + family = ctx->afi->family; + + /* Re-use the existing target if it's already loaded. */ + list_for_each_entry(nft_target, &nft_match_list, head) { + struct xt_target *target = nft_target->ops.data; + + if (strcmp(target->name, tg_name) == 0 && + target->revision == rev && target->family == family) + return &nft_target->ops; + } + + target = xt_request_find_target(family, tg_name, rev); + if (IS_ERR(target)) + return ERR_PTR(-ENOENT); + + /* This is the first time we use this target, allocate operations */ + nft_target = kzalloc(sizeof(struct nft_xt), GFP_KERNEL); + if (nft_target == NULL) + return ERR_PTR(-ENOMEM); + + nft_target->ops.type = &nft_target_type; + nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize) + + nft_compat_target_offset(target)); + nft_target->ops.eval = nft_target_eval; + nft_target->ops.init = nft_target_init; + nft_target->ops.destroy = nft_target_destroy; + nft_target->ops.dump = nft_target_dump; + nft_target->ops.validate = nft_target_validate; + nft_target->ops.data = target; + + list_add(&nft_target->head, &nft_target_list); + + return &nft_target->ops; +} + +static void nft_target_release(void) +{ + struct nft_xt *nft_target, *tmp; + + list_for_each_entry_safe(nft_target, tmp, &nft_target_list, head) + kfree(nft_target); +} + +static struct nft_expr_type nft_target_type __read_mostly = { + .name = "target", + .select_ops = nft_target_select_ops, + .policy = nft_target_policy, + .maxattr = NFTA_TARGET_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_compat_module_init(void) +{ + int ret; + + ret = nft_register_expr(&nft_match_type); + if (ret < 0) + return ret; + + ret = nft_register_expr(&nft_target_type); + if (ret < 0) + goto err_match; + + ret = nfnetlink_subsys_register(&nfnl_compat_subsys); + if (ret < 0) { + pr_err("nft_compat: cannot register with nfnetlink.\n"); + goto err_target; + } + + pr_info("nf_tables_compat: (c) 2012 Pablo Neira Ayuso \n"); + + return ret; + +err_target: + nft_unregister_expr(&nft_target_type); +err_match: + nft_unregister_expr(&nft_match_type); + return ret; +} + +static void __exit nft_compat_module_exit(void) +{ + nfnetlink_subsys_unregister(&nfnl_compat_subsys); + nft_unregister_expr(&nft_target_type); + nft_unregister_expr(&nft_match_type); + nft_match_release(); + nft_target_release(); +} + +MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT); + +module_init(nft_compat_module_init); +module_exit(nft_compat_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pablo Neira Ayuso "); +MODULE_ALIAS_NFT_EXPR("match"); +MODULE_ALIAS_NFT_EXPR("target"); diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c new file mode 100644 index 0000000000000000000000000000000000000000..c89ee486ce54d6d5ba1064970de0c87805bf0dc9 --- /dev/null +++ b/net/netfilter/nft_counter.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_counter { + seqlock_t lock; + u64 bytes; + u64 packets; +}; + +static void nft_counter_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + struct nft_counter *priv = nft_expr_priv(expr); + + write_seqlock_bh(&priv->lock); + priv->bytes += pkt->skb->len; + priv->packets++; + write_sequnlock_bh(&priv->lock); +} + +static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + struct nft_counter *priv = nft_expr_priv(expr); + unsigned int seq; + u64 bytes; + u64 packets; + + do { + seq = read_seqbegin(&priv->lock); + bytes = priv->bytes; + packets = priv->packets; + } while (read_seqretry(&priv->lock, seq)); + + if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(bytes))) + goto nla_put_failure; + if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(packets))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { + [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 }, + [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, +}; + +static int nft_counter_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_counter *priv = nft_expr_priv(expr); + + if (tb[NFTA_COUNTER_PACKETS]) + priv->packets = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); + if (tb[NFTA_COUNTER_BYTES]) + priv->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); + + seqlock_init(&priv->lock); + return 0; +} + +static struct nft_expr_type nft_counter_type; +static const struct nft_expr_ops nft_counter_ops = { + .type = &nft_counter_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_counter)), + .eval = nft_counter_eval, + .init = nft_counter_init, + .dump = nft_counter_dump, +}; + +static struct nft_expr_type nft_counter_type __read_mostly = { + .name = "counter", + .ops = &nft_counter_ops, + .policy = nft_counter_policy, + .maxattr = NFTA_COUNTER_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_counter_module_init(void) +{ + return nft_register_expr(&nft_counter_type); +} + +static void __exit nft_counter_module_exit(void) +{ + nft_unregister_expr(&nft_counter_type); +} + +module_init(nft_counter_module_init); +module_exit(nft_counter_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_EXPR("counter"); diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c new file mode 100644 index 0000000000000000000000000000000000000000..955f4e6e708981af46ab781c806b4794740d79ac --- /dev/null +++ b/net/netfilter/nft_ct.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_ct { + enum nft_ct_keys key:8; + enum ip_conntrack_dir dir:8; + enum nft_registers dreg:8; + uint8_t family; +}; + +static void nft_ct_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_ct *priv = nft_expr_priv(expr); + struct nft_data *dest = &data[priv->dreg]; + enum ip_conntrack_info ctinfo; + const struct nf_conn *ct; + const struct nf_conn_help *help; + const struct nf_conntrack_tuple *tuple; + const struct nf_conntrack_helper *helper; + long diff; + unsigned int state; + + ct = nf_ct_get(pkt->skb, &ctinfo); + + switch (priv->key) { + case NFT_CT_STATE: + if (ct == NULL) + state = NF_CT_STATE_INVALID_BIT; + else if (nf_ct_is_untracked(ct)) + state = NF_CT_STATE_UNTRACKED_BIT; + else + state = NF_CT_STATE_BIT(ctinfo); + dest->data[0] = state; + return; + } + + if (ct == NULL) + goto err; + + switch (priv->key) { + case NFT_CT_DIRECTION: + dest->data[0] = CTINFO2DIR(ctinfo); + return; + case NFT_CT_STATUS: + dest->data[0] = ct->status; + return; +#ifdef CONFIG_NF_CONNTRACK_MARK + case NFT_CT_MARK: + dest->data[0] = ct->mark; + return; +#endif +#ifdef CONFIG_NF_CONNTRACK_SECMARK + case NFT_CT_SECMARK: + dest->data[0] = ct->secmark; + return; +#endif + case NFT_CT_EXPIRATION: + diff = (long)jiffies - (long)ct->timeout.expires; + if (diff < 0) + diff = 0; + dest->data[0] = jiffies_to_msecs(diff); + return; + case NFT_CT_HELPER: + if (ct->master == NULL) + goto err; + help = nfct_help(ct->master); + if (help == NULL) + goto err; + helper = rcu_dereference(help->helper); + if (helper == NULL) + goto err; + if (strlen(helper->name) >= sizeof(dest->data)) + goto err; + strncpy((char *)dest->data, helper->name, sizeof(dest->data)); + return; + } + + tuple = &ct->tuplehash[priv->dir].tuple; + switch (priv->key) { + case NFT_CT_L3PROTOCOL: + dest->data[0] = nf_ct_l3num(ct); + return; + case NFT_CT_SRC: + memcpy(dest->data, tuple->src.u3.all, + nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); + return; + case NFT_CT_DST: + memcpy(dest->data, tuple->dst.u3.all, + nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); + return; + case NFT_CT_PROTOCOL: + dest->data[0] = nf_ct_protonum(ct); + return; + case NFT_CT_PROTO_SRC: + dest->data[0] = (__force __u16)tuple->src.u.all; + return; + case NFT_CT_PROTO_DST: + dest->data[0] = (__force __u16)tuple->dst.u.all; + return; + } + return; +err: + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { + [NFTA_CT_DREG] = { .type = NLA_U32 }, + [NFTA_CT_KEY] = { .type = NLA_U32 }, + [NFTA_CT_DIRECTION] = { .type = NLA_U8 }, +}; + +static int nft_ct_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_ct *priv = nft_expr_priv(expr); + int err; + + if (tb[NFTA_CT_DREG] == NULL || + tb[NFTA_CT_KEY] == NULL) + return -EINVAL; + + priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); + if (tb[NFTA_CT_DIRECTION] != NULL) { + priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); + switch (priv->dir) { + case IP_CT_DIR_ORIGINAL: + case IP_CT_DIR_REPLY: + break; + default: + return -EINVAL; + } + } + + switch (priv->key) { + case NFT_CT_STATE: + case NFT_CT_DIRECTION: + case NFT_CT_STATUS: +#ifdef CONFIG_NF_CONNTRACK_MARK + case NFT_CT_MARK: +#endif +#ifdef CONFIG_NF_CONNTRACK_SECMARK + case NFT_CT_SECMARK: +#endif + case NFT_CT_EXPIRATION: + case NFT_CT_HELPER: + if (tb[NFTA_CT_DIRECTION] != NULL) + return -EINVAL; + break; + case NFT_CT_PROTOCOL: + case NFT_CT_SRC: + case NFT_CT_DST: + case NFT_CT_PROTO_SRC: + case NFT_CT_PROTO_DST: + if (tb[NFTA_CT_DIRECTION] == NULL) + return -EINVAL; + break; + default: + return -EOPNOTSUPP; + } + + err = nf_ct_l3proto_try_module_get(ctx->afi->family); + if (err < 0) + return err; + priv->family = ctx->afi->family; + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + goto err1; + + err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); + if (err < 0) + goto err1; + return 0; + +err1: + nf_ct_l3proto_module_put(ctx->afi->family); + return err; +} + +static void nft_ct_destroy(const struct nft_expr *expr) +{ + struct nft_ct *priv = nft_expr_priv(expr); + + nf_ct_l3proto_module_put(priv->family); +} + +static int nft_ct_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_ct *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) + goto nla_put_failure; + if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_ct_type; +static const struct nft_expr_ops nft_ct_ops = { + .type = &nft_ct_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), + .eval = nft_ct_eval, + .init = nft_ct_init, + .destroy = nft_ct_destroy, + .dump = nft_ct_dump, +}; + +static struct nft_expr_type nft_ct_type __read_mostly = { + .name = "ct", + .ops = &nft_ct_ops, + .policy = nft_ct_policy, + .maxattr = NFTA_CT_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_ct_module_init(void) +{ + return nft_register_expr(&nft_ct_type); +} + +static void __exit nft_ct_module_exit(void) +{ + nft_unregister_expr(&nft_ct_type); +} + +module_init(nft_ct_module_init); +module_exit(nft_ct_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_EXPR("ct"); diff --git a/net/netfilter/nft_expr_template.c b/net/netfilter/nft_expr_template.c new file mode 100644 index 0000000000000000000000000000000000000000..b6eed4d5a096277708ba534288bdd6bfa68ec8d0 --- /dev/null +++ b/net/netfilter/nft_expr_template.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include + +struct nft_template { + +}; + +static void nft_template_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + struct nft_template *priv = nft_expr_priv(expr); + +} + +static const struct nla_policy nft_template_policy[NFTA_TEMPLATE_MAX + 1] = { + [NFTA_TEMPLATE_ATTR] = { .type = NLA_U32 }, +}; + +static int nft_template_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_template *priv = nft_expr_priv(expr); + + return 0; +} + +static void nft_template_destroy(const struct nft_ctx *ctx, + const struct nft_expr *expr) +{ + struct nft_template *priv = nft_expr_priv(expr); + +} + +static int nft_template_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_template *priv = nft_expr_priv(expr); + + NLA_PUT_BE32(skb, NFTA_TEMPLATE_ATTR, priv->field); + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_template_type; +static const struct nft_expr_ops nft_template_ops = { + .type = &nft_template_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_template)), + .eval = nft_template_eval, + .init = nft_template_init, + .destroy = nft_template_destroy, + .dump = nft_template_dump, +}; + +static struct nft_expr_type nft_template_type __read_mostly = { + .name = "template", + .ops = &nft_template_ops, + .policy = nft_template_policy, + .maxattr = NFTA_TEMPLATE_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_template_module_init(void) +{ + return nft_register_expr(&nft_template_type); +} + +static void __exit nft_template_module_exit(void) +{ + nft_unregister_expr(&nft_template_type); +} + +module_init(nft_template_module_init); +module_exit(nft_template_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_EXPR("template"); diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c new file mode 100644 index 0000000000000000000000000000000000000000..8e0bb75e7c51e61092fb904dc1d93befe1e7320d --- /dev/null +++ b/net/netfilter/nft_exthdr.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2008 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +// FIXME: +#include + +struct nft_exthdr { + u8 type; + u8 offset; + u8 len; + enum nft_registers dreg:8; +}; + +static void nft_exthdr_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + struct nft_exthdr *priv = nft_expr_priv(expr); + struct nft_data *dest = &data[priv->dreg]; + unsigned int offset; + int err; + + err = ipv6_find_hdr(pkt->skb, &offset, priv->type, NULL, NULL); + if (err < 0) + goto err; + offset += priv->offset; + + if (skb_copy_bits(pkt->skb, offset, dest->data, priv->len) < 0) + goto err; + return; +err: + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = { + [NFTA_EXTHDR_DREG] = { .type = NLA_U32 }, + [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 }, + [NFTA_EXTHDR_OFFSET] = { .type = NLA_U32 }, + [NFTA_EXTHDR_LEN] = { .type = NLA_U32 }, +}; + +static int nft_exthdr_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_exthdr *priv = nft_expr_priv(expr); + int err; + + if (tb[NFTA_EXTHDR_DREG] == NULL || + tb[NFTA_EXTHDR_TYPE] == NULL || + tb[NFTA_EXTHDR_OFFSET] == NULL || + tb[NFTA_EXTHDR_LEN] == NULL) + return -EINVAL; + + priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]); + priv->offset = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OFFSET])); + priv->len = ntohl(nla_get_be32(tb[NFTA_EXTHDR_LEN])); + if (priv->len == 0 || + priv->len > FIELD_SIZEOF(struct nft_data, data)) + return -EINVAL; + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_EXTHDR_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); +} + +static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_exthdr *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_EXTHDR_DREG, htonl(priv->dreg))) + goto nla_put_failure; + if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type)) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_EXTHDR_LEN, htonl(priv->len))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_exthdr_type; +static const struct nft_expr_ops nft_exthdr_ops = { + .type = &nft_exthdr_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), + .eval = nft_exthdr_eval, + .init = nft_exthdr_init, + .dump = nft_exthdr_dump, +}; + +static struct nft_expr_type nft_exthdr_type __read_mostly = { + .name = "exthdr", + .ops = &nft_exthdr_ops, + .policy = nft_exthdr_policy, + .maxattr = NFTA_EXTHDR_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_exthdr_module_init(void) +{ + return nft_register_expr(&nft_exthdr_type); +} + +static void __exit nft_exthdr_module_exit(void) +{ + nft_unregister_expr(&nft_exthdr_type); +} + +module_init(nft_exthdr_module_init); +module_exit(nft_exthdr_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_EXPR("exthdr"); diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c new file mode 100644 index 0000000000000000000000000000000000000000..3d3f8fce10a5136391e53bd75b13498a45e82e96 --- /dev/null +++ b/net/netfilter/nft_hash.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_hash { + struct hlist_head *hash; + unsigned int hsize; +}; + +struct nft_hash_elem { + struct hlist_node hnode; + struct nft_data key; + struct nft_data data[]; +}; + +static u32 nft_hash_rnd __read_mostly; +static bool nft_hash_rnd_initted __read_mostly; + +static unsigned int nft_hash_data(const struct nft_data *data, + unsigned int hsize, unsigned int len) +{ + unsigned int h; + + h = jhash(data->data, len, nft_hash_rnd); + return ((u64)h * hsize) >> 32; +} + +static bool nft_hash_lookup(const struct nft_set *set, + const struct nft_data *key, + struct nft_data *data) +{ + const struct nft_hash *priv = nft_set_priv(set); + const struct nft_hash_elem *he; + unsigned int h; + + h = nft_hash_data(key, priv->hsize, set->klen); + hlist_for_each_entry(he, &priv->hash[h], hnode) { + if (nft_data_cmp(&he->key, key, set->klen)) + continue; + if (set->flags & NFT_SET_MAP) + nft_data_copy(data, he->data); + return true; + } + return false; +} + +static void nft_hash_elem_destroy(const struct nft_set *set, + struct nft_hash_elem *he) +{ + nft_data_uninit(&he->key, NFT_DATA_VALUE); + if (set->flags & NFT_SET_MAP) + nft_data_uninit(he->data, set->dtype); + kfree(he); +} + +static int nft_hash_insert(const struct nft_set *set, + const struct nft_set_elem *elem) +{ + struct nft_hash *priv = nft_set_priv(set); + struct nft_hash_elem *he; + unsigned int size, h; + + if (elem->flags != 0) + return -EINVAL; + + size = sizeof(*he); + if (set->flags & NFT_SET_MAP) + size += sizeof(he->data[0]); + + he = kzalloc(size, GFP_KERNEL); + if (he == NULL) + return -ENOMEM; + + nft_data_copy(&he->key, &elem->key); + if (set->flags & NFT_SET_MAP) + nft_data_copy(he->data, &elem->data); + + h = nft_hash_data(&he->key, priv->hsize, set->klen); + hlist_add_head_rcu(&he->hnode, &priv->hash[h]); + return 0; +} + +static void nft_hash_remove(const struct nft_set *set, + const struct nft_set_elem *elem) +{ + struct nft_hash_elem *he = elem->cookie; + + hlist_del_rcu(&he->hnode); + kfree(he); +} + +static int nft_hash_get(const struct nft_set *set, struct nft_set_elem *elem) +{ + const struct nft_hash *priv = nft_set_priv(set); + struct nft_hash_elem *he; + unsigned int h; + + h = nft_hash_data(&elem->key, priv->hsize, set->klen); + hlist_for_each_entry(he, &priv->hash[h], hnode) { + if (nft_data_cmp(&he->key, &elem->key, set->klen)) + continue; + + elem->cookie = he; + elem->flags = 0; + if (set->flags & NFT_SET_MAP) + nft_data_copy(&elem->data, he->data); + return 0; + } + return -ENOENT; +} + +static void nft_hash_walk(const struct nft_ctx *ctx, const struct nft_set *set, + struct nft_set_iter *iter) +{ + const struct nft_hash *priv = nft_set_priv(set); + const struct nft_hash_elem *he; + struct nft_set_elem elem; + unsigned int i; + + for (i = 0; i < priv->hsize; i++) { + hlist_for_each_entry(he, &priv->hash[i], hnode) { + if (iter->count < iter->skip) + goto cont; + + memcpy(&elem.key, &he->key, sizeof(elem.key)); + if (set->flags & NFT_SET_MAP) + memcpy(&elem.data, he->data, sizeof(elem.data)); + elem.flags = 0; + + iter->err = iter->fn(ctx, set, iter, &elem); + if (iter->err < 0) + return; +cont: + iter->count++; + } + } +} + +static unsigned int nft_hash_privsize(const struct nlattr * const nla[]) +{ + return sizeof(struct nft_hash); +} + +static int nft_hash_init(const struct nft_set *set, + const struct nlattr * const tb[]) +{ + struct nft_hash *priv = nft_set_priv(set); + unsigned int cnt, i; + + if (unlikely(!nft_hash_rnd_initted)) { + get_random_bytes(&nft_hash_rnd, 4); + nft_hash_rnd_initted = true; + } + + /* Aim for a load factor of 0.75 */ + // FIXME: temporarily broken until we have set descriptions + cnt = 100; + cnt = cnt * 4 / 3; + + priv->hash = kcalloc(cnt, sizeof(struct hlist_head), GFP_KERNEL); + if (priv->hash == NULL) + return -ENOMEM; + priv->hsize = cnt; + + for (i = 0; i < cnt; i++) + INIT_HLIST_HEAD(&priv->hash[i]); + + return 0; +} + +static void nft_hash_destroy(const struct nft_set *set) +{ + const struct nft_hash *priv = nft_set_priv(set); + const struct hlist_node *next; + struct nft_hash_elem *elem; + unsigned int i; + + for (i = 0; i < priv->hsize; i++) { + hlist_for_each_entry_safe(elem, next, &priv->hash[i], hnode) { + hlist_del(&elem->hnode); + nft_hash_elem_destroy(set, elem); + } + } + kfree(priv->hash); +} + +static struct nft_set_ops nft_hash_ops __read_mostly = { + .privsize = nft_hash_privsize, + .init = nft_hash_init, + .destroy = nft_hash_destroy, + .get = nft_hash_get, + .insert = nft_hash_insert, + .remove = nft_hash_remove, + .lookup = nft_hash_lookup, + .walk = nft_hash_walk, + .features = NFT_SET_MAP, + .owner = THIS_MODULE, +}; + +static int __init nft_hash_module_init(void) +{ + return nft_register_set(&nft_hash_ops); +} + +static void __exit nft_hash_module_exit(void) +{ + nft_unregister_set(&nft_hash_ops); +} + +module_init(nft_hash_module_init); +module_exit(nft_hash_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_SET(); diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c new file mode 100644 index 0000000000000000000000000000000000000000..f169501f1ad4389970613d1e44fb2b60759406cb --- /dev/null +++ b/net/netfilter/nft_immediate.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_immediate_expr { + struct nft_data data; + enum nft_registers dreg:8; + u8 dlen; +}; + +static void nft_immediate_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_immediate_expr *priv = nft_expr_priv(expr); + + nft_data_copy(&data[priv->dreg], &priv->data); +} + +static const struct nla_policy nft_immediate_policy[NFTA_IMMEDIATE_MAX + 1] = { + [NFTA_IMMEDIATE_DREG] = { .type = NLA_U32 }, + [NFTA_IMMEDIATE_DATA] = { .type = NLA_NESTED }, +}; + +static int nft_immediate_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_immediate_expr *priv = nft_expr_priv(expr); + struct nft_data_desc desc; + int err; + + if (tb[NFTA_IMMEDIATE_DREG] == NULL || + tb[NFTA_IMMEDIATE_DATA] == NULL) + return -EINVAL; + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_IMMEDIATE_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + + err = nft_data_init(ctx, &priv->data, &desc, tb[NFTA_IMMEDIATE_DATA]); + if (err < 0) + return err; + priv->dlen = desc.len; + + err = nft_validate_data_load(ctx, priv->dreg, &priv->data, desc.type); + if (err < 0) + goto err1; + + return 0; + +err1: + nft_data_uninit(&priv->data, desc.type); + return err; +} + +static void nft_immediate_destroy(const struct nft_expr *expr) +{ + const struct nft_immediate_expr *priv = nft_expr_priv(expr); + return nft_data_uninit(&priv->data, nft_dreg_to_type(priv->dreg)); +} + +static int nft_immediate_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_immediate_expr *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_IMMEDIATE_DREG, htonl(priv->dreg))) + goto nla_put_failure; + + return nft_data_dump(skb, NFTA_IMMEDIATE_DATA, &priv->data, + nft_dreg_to_type(priv->dreg), priv->dlen); + +nla_put_failure: + return -1; +} + +static int nft_immediate_validate(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nft_data **data) +{ + const struct nft_immediate_expr *priv = nft_expr_priv(expr); + + if (priv->dreg == NFT_REG_VERDICT) + *data = &priv->data; + + return 0; +} + +static struct nft_expr_type nft_imm_type; +static const struct nft_expr_ops nft_imm_ops = { + .type = &nft_imm_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_immediate_expr)), + .eval = nft_immediate_eval, + .init = nft_immediate_init, + .destroy = nft_immediate_destroy, + .dump = nft_immediate_dump, + .validate = nft_immediate_validate, +}; + +static struct nft_expr_type nft_imm_type __read_mostly = { + .name = "immediate", + .ops = &nft_imm_ops, + .policy = nft_immediate_policy, + .maxattr = NFTA_IMMEDIATE_MAX, + .owner = THIS_MODULE, +}; + +int __init nft_immediate_module_init(void) +{ + return nft_register_expr(&nft_imm_type); +} + +void nft_immediate_module_exit(void) +{ + nft_unregister_expr(&nft_imm_type); +} diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c new file mode 100644 index 0000000000000000000000000000000000000000..85da5bd02f64fcfebad07d1f6d7ea23d40adb260 --- /dev/null +++ b/net/netfilter/nft_limit.c @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_SPINLOCK(limit_lock); + +struct nft_limit { + u64 tokens; + u64 rate; + u64 unit; + unsigned long stamp; +}; + +static void nft_limit_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + struct nft_limit *priv = nft_expr_priv(expr); + + spin_lock_bh(&limit_lock); + if (time_after_eq(jiffies, priv->stamp)) { + priv->tokens = priv->rate; + priv->stamp = jiffies + priv->unit * HZ; + } + + if (priv->tokens >= 1) { + priv->tokens--; + spin_unlock_bh(&limit_lock); + return; + } + spin_unlock_bh(&limit_lock); + + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static const struct nla_policy nft_limit_policy[NFTA_LIMIT_MAX + 1] = { + [NFTA_LIMIT_RATE] = { .type = NLA_U64 }, + [NFTA_LIMIT_UNIT] = { .type = NLA_U64 }, +}; + +static int nft_limit_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_limit *priv = nft_expr_priv(expr); + + if (tb[NFTA_LIMIT_RATE] == NULL || + tb[NFTA_LIMIT_UNIT] == NULL) + return -EINVAL; + + priv->rate = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_RATE])); + priv->unit = be64_to_cpu(nla_get_be64(tb[NFTA_LIMIT_UNIT])); + priv->stamp = jiffies + priv->unit * HZ; + priv->tokens = priv->rate; + return 0; +} + +static int nft_limit_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_limit *priv = nft_expr_priv(expr); + + if (nla_put_be64(skb, NFTA_LIMIT_RATE, cpu_to_be64(priv->rate))) + goto nla_put_failure; + if (nla_put_be64(skb, NFTA_LIMIT_UNIT, cpu_to_be64(priv->unit))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_limit_type; +static const struct nft_expr_ops nft_limit_ops = { + .type = &nft_limit_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_limit)), + .eval = nft_limit_eval, + .init = nft_limit_init, + .dump = nft_limit_dump, +}; + +static struct nft_expr_type nft_limit_type __read_mostly = { + .name = "limit", + .ops = &nft_limit_ops, + .policy = nft_limit_policy, + .maxattr = NFTA_LIMIT_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_limit_module_init(void) +{ + return nft_register_expr(&nft_limit_type); +} + +static void __exit nft_limit_module_exit(void) +{ + nft_unregister_expr(&nft_limit_type); +} + +module_init(nft_limit_module_init); +module_exit(nft_limit_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_EXPR("limit"); diff --git a/net/netfilter/nft_log.c b/net/netfilter/nft_log.c new file mode 100644 index 0000000000000000000000000000000000000000..57cad072a13e50886ba1a2fa1da2c778953d8203 --- /dev/null +++ b/net/netfilter/nft_log.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *nft_log_null_prefix = ""; + +struct nft_log { + struct nf_loginfo loginfo; + char *prefix; + int family; +}; + +static void nft_log_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_log *priv = nft_expr_priv(expr); + struct net *net = dev_net(pkt->in ? pkt->in : pkt->out); + + nf_log_packet(net, priv->family, pkt->hooknum, pkt->skb, pkt->in, + pkt->out, &priv->loginfo, "%s", priv->prefix); +} + +static const struct nla_policy nft_log_policy[NFTA_LOG_MAX + 1] = { + [NFTA_LOG_GROUP] = { .type = NLA_U16 }, + [NFTA_LOG_PREFIX] = { .type = NLA_STRING }, + [NFTA_LOG_SNAPLEN] = { .type = NLA_U32 }, + [NFTA_LOG_QTHRESHOLD] = { .type = NLA_U16 }, +}; + +static int nft_log_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_log *priv = nft_expr_priv(expr); + struct nf_loginfo *li = &priv->loginfo; + const struct nlattr *nla; + + priv->family = ctx->afi->family; + + nla = tb[NFTA_LOG_PREFIX]; + if (nla != NULL) { + priv->prefix = kmalloc(nla_len(nla) + 1, GFP_KERNEL); + if (priv->prefix == NULL) + return -ENOMEM; + nla_strlcpy(priv->prefix, nla, nla_len(nla) + 1); + } else + priv->prefix = (char *)nft_log_null_prefix; + + li->type = NF_LOG_TYPE_ULOG; + if (tb[NFTA_LOG_GROUP] != NULL) + li->u.ulog.group = ntohs(nla_get_be16(tb[NFTA_LOG_GROUP])); + + if (tb[NFTA_LOG_SNAPLEN] != NULL) + li->u.ulog.copy_len = ntohl(nla_get_be32(tb[NFTA_LOG_SNAPLEN])); + if (tb[NFTA_LOG_QTHRESHOLD] != NULL) { + li->u.ulog.qthreshold = + ntohs(nla_get_be16(tb[NFTA_LOG_QTHRESHOLD])); + } + + return 0; +} + +static void nft_log_destroy(const struct nft_expr *expr) +{ + struct nft_log *priv = nft_expr_priv(expr); + + if (priv->prefix != nft_log_null_prefix) + kfree(priv->prefix); +} + +static int nft_log_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_log *priv = nft_expr_priv(expr); + const struct nf_loginfo *li = &priv->loginfo; + + if (priv->prefix != nft_log_null_prefix) + if (nla_put_string(skb, NFTA_LOG_PREFIX, priv->prefix)) + goto nla_put_failure; + if (li->u.ulog.group) + if (nla_put_be16(skb, NFTA_LOG_GROUP, htons(li->u.ulog.group))) + goto nla_put_failure; + if (li->u.ulog.copy_len) + if (nla_put_be32(skb, NFTA_LOG_SNAPLEN, + htonl(li->u.ulog.copy_len))) + goto nla_put_failure; + if (li->u.ulog.qthreshold) + if (nla_put_be16(skb, NFTA_LOG_QTHRESHOLD, + htons(li->u.ulog.qthreshold))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_log_type; +static const struct nft_expr_ops nft_log_ops = { + .type = &nft_log_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_log)), + .eval = nft_log_eval, + .init = nft_log_init, + .destroy = nft_log_destroy, + .dump = nft_log_dump, +}; + +static struct nft_expr_type nft_log_type __read_mostly = { + .name = "log", + .ops = &nft_log_ops, + .policy = nft_log_policy, + .maxattr = NFTA_LOG_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_log_module_init(void) +{ + return nft_register_expr(&nft_log_type); +} + +static void __exit nft_log_module_exit(void) +{ + nft_unregister_expr(&nft_log_type); +} + +module_init(nft_log_module_init); +module_exit(nft_log_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_EXPR("log"); diff --git a/net/netfilter/nft_lookup.c b/net/netfilter/nft_lookup.c new file mode 100644 index 0000000000000000000000000000000000000000..8a6116b75b5a03181e3183d5090c23059dd5a09e --- /dev/null +++ b/net/netfilter/nft_lookup.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_lookup { + struct nft_set *set; + enum nft_registers sreg:8; + enum nft_registers dreg:8; + struct nft_set_binding binding; +}; + +static void nft_lookup_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_lookup *priv = nft_expr_priv(expr); + const struct nft_set *set = priv->set; + + if (set->ops->lookup(set, &data[priv->sreg], &data[priv->dreg])) + return; + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static const struct nla_policy nft_lookup_policy[NFTA_LOOKUP_MAX + 1] = { + [NFTA_LOOKUP_SET] = { .type = NLA_STRING }, + [NFTA_LOOKUP_SREG] = { .type = NLA_U32 }, + [NFTA_LOOKUP_DREG] = { .type = NLA_U32 }, +}; + +static int nft_lookup_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_lookup *priv = nft_expr_priv(expr); + struct nft_set *set; + int err; + + if (tb[NFTA_LOOKUP_SET] == NULL || + tb[NFTA_LOOKUP_SREG] == NULL) + return -EINVAL; + + set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]); + if (IS_ERR(set)) + return PTR_ERR(set); + + priv->sreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_SREG])); + err = nft_validate_input_register(priv->sreg); + if (err < 0) + return err; + + if (tb[NFTA_LOOKUP_DREG] != NULL) { + if (!(set->flags & NFT_SET_MAP)) + return -EINVAL; + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + + if (priv->dreg == NFT_REG_VERDICT) { + if (set->dtype != NFT_DATA_VERDICT) + return -EINVAL; + } else if (set->dtype == NFT_DATA_VERDICT) + return -EINVAL; + } else if (set->flags & NFT_SET_MAP) + return -EINVAL; + + err = nf_tables_bind_set(ctx, set, &priv->binding); + if (err < 0) + return err; + + priv->set = set; + return 0; +} + +static void nft_lookup_destroy(const struct nft_expr *expr) +{ + struct nft_lookup *priv = nft_expr_priv(expr); + + nf_tables_unbind_set(NULL, priv->set, &priv->binding); +} + +static int nft_lookup_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_lookup *priv = nft_expr_priv(expr); + + if (nla_put_string(skb, NFTA_LOOKUP_SET, priv->set->name)) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_LOOKUP_SREG, htonl(priv->sreg))) + goto nla_put_failure; + if (priv->set->flags & NFT_SET_MAP) + if (nla_put_be32(skb, NFTA_LOOKUP_DREG, htonl(priv->dreg))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_lookup_type; +static const struct nft_expr_ops nft_lookup_ops = { + .type = &nft_lookup_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_lookup)), + .eval = nft_lookup_eval, + .init = nft_lookup_init, + .destroy = nft_lookup_destroy, + .dump = nft_lookup_dump, +}; + +static struct nft_expr_type nft_lookup_type __read_mostly = { + .name = "lookup", + .ops = &nft_lookup_ops, + .policy = nft_lookup_policy, + .maxattr = NFTA_LOOKUP_MAX, + .owner = THIS_MODULE, +}; + +int __init nft_lookup_module_init(void) +{ + return nft_register_expr(&nft_lookup_type); +} + +void nft_lookup_module_exit(void) +{ + nft_unregister_expr(&nft_lookup_type); +} diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c new file mode 100644 index 0000000000000000000000000000000000000000..8c28220a90b34baa013620d0ab13fdaf5355a33e --- /dev/null +++ b/net/netfilter/nft_meta.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for TCP_TIME_WAIT */ +#include + +struct nft_meta { + enum nft_meta_keys key:8; + enum nft_registers dreg:8; +}; + +static void nft_meta_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_meta *priv = nft_expr_priv(expr); + const struct sk_buff *skb = pkt->skb; + const struct net_device *in = pkt->in, *out = pkt->out; + struct nft_data *dest = &data[priv->dreg]; + + switch (priv->key) { + case NFT_META_LEN: + dest->data[0] = skb->len; + break; + case NFT_META_PROTOCOL: + *(__be16 *)dest->data = skb->protocol; + break; + case NFT_META_PRIORITY: + dest->data[0] = skb->priority; + break; + case NFT_META_MARK: + dest->data[0] = skb->mark; + break; + case NFT_META_IIF: + if (in == NULL) + goto err; + dest->data[0] = in->ifindex; + break; + case NFT_META_OIF: + if (out == NULL) + goto err; + dest->data[0] = out->ifindex; + break; + case NFT_META_IIFNAME: + if (in == NULL) + goto err; + strncpy((char *)dest->data, in->name, sizeof(dest->data)); + break; + case NFT_META_OIFNAME: + if (out == NULL) + goto err; + strncpy((char *)dest->data, out->name, sizeof(dest->data)); + break; + case NFT_META_IIFTYPE: + if (in == NULL) + goto err; + *(u16 *)dest->data = in->type; + break; + case NFT_META_OIFTYPE: + if (out == NULL) + goto err; + *(u16 *)dest->data = out->type; + break; + case NFT_META_SKUID: + if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT) + goto err; + + read_lock_bh(&skb->sk->sk_callback_lock); + if (skb->sk->sk_socket == NULL || + skb->sk->sk_socket->file == NULL) { + read_unlock_bh(&skb->sk->sk_callback_lock); + goto err; + } + + dest->data[0] = + from_kuid_munged(&init_user_ns, + skb->sk->sk_socket->file->f_cred->fsuid); + read_unlock_bh(&skb->sk->sk_callback_lock); + break; + case NFT_META_SKGID: + if (skb->sk == NULL || skb->sk->sk_state == TCP_TIME_WAIT) + goto err; + + read_lock_bh(&skb->sk->sk_callback_lock); + if (skb->sk->sk_socket == NULL || + skb->sk->sk_socket->file == NULL) { + read_unlock_bh(&skb->sk->sk_callback_lock); + goto err; + } + dest->data[0] = + from_kgid_munged(&init_user_ns, + skb->sk->sk_socket->file->f_cred->fsgid); + read_unlock_bh(&skb->sk->sk_callback_lock); + break; +#ifdef CONFIG_NET_CLS_ROUTE + case NFT_META_RTCLASSID: { + const struct dst_entry *dst = skb_dst(skb); + + if (dst == NULL) + goto err; + dest->data[0] = dst->tclassid; + break; + } +#endif +#ifdef CONFIG_NETWORK_SECMARK + case NFT_META_SECMARK: + dest->data[0] = skb->secmark; + break; +#endif + default: + WARN_ON(1); + goto err; + } + return; + +err: + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { + [NFTA_META_DREG] = { .type = NLA_U32 }, + [NFTA_META_KEY] = { .type = NLA_U32 }, +}; + +static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_meta *priv = nft_expr_priv(expr); + int err; + + if (tb[NFTA_META_DREG] == NULL || + tb[NFTA_META_KEY] == NULL) + return -EINVAL; + + priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); + switch (priv->key) { + case NFT_META_LEN: + case NFT_META_PROTOCOL: + case NFT_META_PRIORITY: + case NFT_META_MARK: + case NFT_META_IIF: + case NFT_META_OIF: + case NFT_META_IIFNAME: + case NFT_META_OIFNAME: + case NFT_META_IIFTYPE: + case NFT_META_OIFTYPE: + case NFT_META_SKUID: + case NFT_META_SKGID: +#ifdef CONFIG_NET_CLS_ROUTE + case NFT_META_RTCLASSID: +#endif +#ifdef CONFIG_NETWORK_SECMARK + case NFT_META_SECMARK: +#endif + break; + default: + return -EOPNOTSUPP; + } + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); +} + +static int nft_meta_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_meta *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_META_DREG, htonl(priv->dreg))) + goto nla_put_failure; + if (nla_put_be32(skb, NFTA_META_KEY, htonl(priv->key))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_meta_type; +static const struct nft_expr_ops nft_meta_ops = { + .type = &nft_meta_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), + .eval = nft_meta_eval, + .init = nft_meta_init, + .dump = nft_meta_dump, +}; + +static struct nft_expr_type nft_meta_type __read_mostly = { + .name = "meta", + .ops = &nft_meta_ops, + .policy = nft_meta_policy, + .maxattr = NFTA_META_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_meta_module_init(void) +{ + return nft_register_expr(&nft_meta_type); +} + +static void __exit nft_meta_module_exit(void) +{ + nft_unregister_expr(&nft_meta_type); +} + +module_init(nft_meta_module_init); +module_exit(nft_meta_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_EXPR("meta"); diff --git a/net/netfilter/nft_meta_target.c b/net/netfilter/nft_meta_target.c new file mode 100644 index 0000000000000000000000000000000000000000..71177df75ffbbbe9cbab0733f061dc4c5e559295 --- /dev/null +++ b/net/netfilter/nft_meta_target.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2008 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_meta { + enum nft_meta_keys key; +}; + +static void nft_meta_eval(const struct nft_expr *expr, + struct nft_data *nfres, + struct nft_data *data, + const struct nft_pktinfo *pkt) +{ + const struct nft_meta *meta = nft_expr_priv(expr); + struct sk_buff *skb = pkt->skb; + u32 val = data->data[0]; + + switch (meta->key) { + case NFT_META_MARK: + skb->mark = val; + break; + case NFT_META_PRIORITY: + skb->priority = val; + break; + case NFT_META_NFTRACE: + skb->nf_trace = val; + break; +#ifdef CONFIG_NETWORK_SECMARK + case NFT_META_SECMARK: + skb->secmark = val; + break; +#endif + default: + WARN_ON(1); + } +} + +static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { + [NFTA_META_KEY] = { .type = NLA_U32 }, +}; + +static int nft_meta_init(const struct nft_expr *expr, struct nlattr *tb[]) +{ + struct nft_meta *meta = nft_expr_priv(expr); + + if (tb[NFTA_META_KEY] == NULL) + return -EINVAL; + + meta->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); + switch (meta->key) { + case NFT_META_MARK: + case NFT_META_PRIORITY: + case NFT_META_NFTRACE: +#ifdef CONFIG_NETWORK_SECMARK + case NFT_META_SECMARK: +#endif + break; + default: + return -EINVAL; + } + + return 0; +} + +static int nft_meta_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + struct nft_meta *meta = nft_expr_priv(expr); + + NLA_PUT_BE32(skb, NFTA_META_KEY, htonl(meta->key)); + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_ops meta_target __read_mostly = { + .name = "meta", + .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), + .owner = THIS_MODULE, + .eval = nft_meta_eval, + .init = nft_meta_init, + .dump = nft_meta_dump, + .policy = nft_meta_policy, + .maxattr = NFTA_META_MAX, +}; + +static int __init nft_meta_target_init(void) +{ + return nft_register_expr(&meta_target); +} + +static void __exit nft_meta_target_exit(void) +{ + nft_unregister_expr(&meta_target); +} + +module_init(nft_meta_target_init); +module_exit(nft_meta_target_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_EXPR("meta"); diff --git a/net/netfilter/nft_nat.c b/net/netfilter/nft_nat.c new file mode 100644 index 0000000000000000000000000000000000000000..d3b1ffe26181b22538ab29fefadd7a41d96b76f1 --- /dev/null +++ b/net/netfilter/nft_nat.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * Copyright (c) 2012 Pablo Neira Ayuso + * Copyright (c) 2012 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_nat { + enum nft_registers sreg_addr_min:8; + enum nft_registers sreg_addr_max:8; + enum nft_registers sreg_proto_min:8; + enum nft_registers sreg_proto_max:8; + int family; + enum nf_nat_manip_type type; +}; + +static void nft_nat_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_nat *priv = nft_expr_priv(expr); + enum ip_conntrack_info ctinfo; + struct nf_conn *ct = nf_ct_get(pkt->skb, &ctinfo); + struct nf_nat_range range; + + memset(&range, 0, sizeof(range)); + if (priv->sreg_addr_min) { + if (priv->family == AF_INET) { + range.min_addr.ip = (__force __be32) + data[priv->sreg_addr_min].data[0]; + range.max_addr.ip = (__force __be32) + data[priv->sreg_addr_max].data[0]; + + } else { + memcpy(range.min_addr.ip6, + data[priv->sreg_addr_min].data, + sizeof(struct nft_data)); + memcpy(range.max_addr.ip6, + data[priv->sreg_addr_max].data, + sizeof(struct nft_data)); + } + range.flags |= NF_NAT_RANGE_MAP_IPS; + } + + if (priv->sreg_proto_min) { + range.min_proto.all = (__force __be16) + data[priv->sreg_proto_min].data[0]; + range.max_proto.all = (__force __be16) + data[priv->sreg_proto_max].data[0]; + range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + } + + data[NFT_REG_VERDICT].verdict = + nf_nat_setup_info(ct, &range, priv->type); +} + +static const struct nla_policy nft_nat_policy[NFTA_NAT_MAX + 1] = { + [NFTA_NAT_TYPE] = { .type = NLA_U32 }, + [NFTA_NAT_FAMILY] = { .type = NLA_U32 }, + [NFTA_NAT_REG_ADDR_MIN] = { .type = NLA_U32 }, + [NFTA_NAT_REG_ADDR_MAX] = { .type = NLA_U32 }, + [NFTA_NAT_REG_PROTO_MIN] = { .type = NLA_U32 }, + [NFTA_NAT_REG_PROTO_MAX] = { .type = NLA_U32 }, +}; + +static int nft_nat_init(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_nat *priv = nft_expr_priv(expr); + int err; + + if (tb[NFTA_NAT_TYPE] == NULL) + return -EINVAL; + + switch (ntohl(nla_get_be32(tb[NFTA_NAT_TYPE]))) { + case NFT_NAT_SNAT: + priv->type = NF_NAT_MANIP_SRC; + break; + case NFT_NAT_DNAT: + priv->type = NF_NAT_MANIP_DST; + break; + default: + return -EINVAL; + } + + if (tb[NFTA_NAT_FAMILY] == NULL) + return -EINVAL; + + priv->family = ntohl(nla_get_be32(tb[NFTA_NAT_FAMILY])); + if (priv->family != AF_INET && priv->family != AF_INET6) + return -EINVAL; + + if (tb[NFTA_NAT_REG_ADDR_MIN]) { + priv->sreg_addr_min = ntohl(nla_get_be32( + tb[NFTA_NAT_REG_ADDR_MIN])); + err = nft_validate_input_register(priv->sreg_addr_min); + if (err < 0) + return err; + } + + if (tb[NFTA_NAT_REG_ADDR_MAX]) { + priv->sreg_addr_max = ntohl(nla_get_be32( + tb[NFTA_NAT_REG_ADDR_MAX])); + err = nft_validate_input_register(priv->sreg_addr_max); + if (err < 0) + return err; + } else + priv->sreg_addr_max = priv->sreg_addr_min; + + if (tb[NFTA_NAT_REG_PROTO_MIN]) { + priv->sreg_proto_min = ntohl(nla_get_be32( + tb[NFTA_NAT_REG_PROTO_MIN])); + err = nft_validate_input_register(priv->sreg_proto_min); + if (err < 0) + return err; + } + + if (tb[NFTA_NAT_REG_PROTO_MAX]) { + priv->sreg_proto_max = ntohl(nla_get_be32( + tb[NFTA_NAT_REG_PROTO_MAX])); + err = nft_validate_input_register(priv->sreg_proto_max); + if (err < 0) + return err; + } else + priv->sreg_proto_max = priv->sreg_proto_min; + + return 0; +} + +static int nft_nat_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_nat *priv = nft_expr_priv(expr); + + switch (priv->type) { + case NF_NAT_MANIP_SRC: + if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_SNAT))) + goto nla_put_failure; + break; + case NF_NAT_MANIP_DST: + if (nla_put_be32(skb, NFTA_NAT_TYPE, htonl(NFT_NAT_DNAT))) + goto nla_put_failure; + break; + } + + if (nla_put_be32(skb, NFTA_NAT_FAMILY, htonl(priv->family))) + goto nla_put_failure; + if (nla_put_be32(skb, + NFTA_NAT_REG_ADDR_MIN, htonl(priv->sreg_addr_min))) + goto nla_put_failure; + if (nla_put_be32(skb, + NFTA_NAT_REG_ADDR_MAX, htonl(priv->sreg_addr_max))) + goto nla_put_failure; + if (nla_put_be32(skb, + NFTA_NAT_REG_PROTO_MIN, htonl(priv->sreg_proto_min))) + goto nla_put_failure; + if (nla_put_be32(skb, + NFTA_NAT_REG_PROTO_MAX, htonl(priv->sreg_proto_max))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_nat_type; +static const struct nft_expr_ops nft_nat_ops = { + .type = &nft_nat_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_nat)), + .eval = nft_nat_eval, + .init = nft_nat_init, + .dump = nft_nat_dump, +}; + +static struct nft_expr_type nft_nat_type __read_mostly = { + .name = "nat", + .ops = &nft_nat_ops, + .policy = nft_nat_policy, + .maxattr = NFTA_NAT_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_nat_module_init(void) +{ + int err; + + err = nft_register_expr(&nft_nat_type); + if (err < 0) + return err; + + return 0; +} + +static void __exit nft_nat_module_exit(void) +{ + nft_unregister_expr(&nft_nat_type); +} + +module_init(nft_nat_module_init); +module_exit(nft_nat_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tomasz Bursztyka "); +MODULE_ALIAS_NFT_EXPR("nat"); diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c new file mode 100644 index 0000000000000000000000000000000000000000..a2aeb318678f9e0e577801f6e42bda4df66ea805 --- /dev/null +++ b/net/netfilter/nft_payload.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static void nft_payload_eval(const struct nft_expr *expr, + struct nft_data data[NFT_REG_MAX + 1], + const struct nft_pktinfo *pkt) +{ + const struct nft_payload *priv = nft_expr_priv(expr); + const struct sk_buff *skb = pkt->skb; + struct nft_data *dest = &data[priv->dreg]; + int offset; + + switch (priv->base) { + case NFT_PAYLOAD_LL_HEADER: + if (!skb_mac_header_was_set(skb)) + goto err; + offset = skb_mac_header(skb) - skb->data; + break; + case NFT_PAYLOAD_NETWORK_HEADER: + offset = skb_network_offset(skb); + break; + case NFT_PAYLOAD_TRANSPORT_HEADER: + offset = pkt->xt.thoff; + break; + default: + BUG(); + } + offset += priv->offset; + + if (skb_copy_bits(skb, offset, dest->data, priv->len) < 0) + goto err; + return; +err: + data[NFT_REG_VERDICT].verdict = NFT_BREAK; +} + +static const struct nla_policy nft_payload_policy[NFTA_PAYLOAD_MAX + 1] = { + [NFTA_PAYLOAD_DREG] = { .type = NLA_U32 }, + [NFTA_PAYLOAD_BASE] = { .type = NLA_U32 }, + [NFTA_PAYLOAD_OFFSET] = { .type = NLA_U32 }, + [NFTA_PAYLOAD_LEN] = { .type = NLA_U32 }, +}; + +static int nft_payload_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_payload *priv = nft_expr_priv(expr); + int err; + + priv->base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); + priv->offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); + priv->len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); + + priv->dreg = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_DREG])); + err = nft_validate_output_register(priv->dreg); + if (err < 0) + return err; + return nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); +} + +static int nft_payload_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_payload *priv = nft_expr_priv(expr); + + if (nla_put_be32(skb, NFTA_PAYLOAD_DREG, htonl(priv->dreg)) || + nla_put_be32(skb, NFTA_PAYLOAD_BASE, htonl(priv->base)) || + nla_put_be32(skb, NFTA_PAYLOAD_OFFSET, htonl(priv->offset)) || + nla_put_be32(skb, NFTA_PAYLOAD_LEN, htonl(priv->len))) + goto nla_put_failure; + return 0; + +nla_put_failure: + return -1; +} + +static struct nft_expr_type nft_payload_type; +static const struct nft_expr_ops nft_payload_ops = { + .type = &nft_payload_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), + .eval = nft_payload_eval, + .init = nft_payload_init, + .dump = nft_payload_dump, +}; + +const struct nft_expr_ops nft_payload_fast_ops = { + .type = &nft_payload_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_payload)), + .eval = nft_payload_eval, + .init = nft_payload_init, + .dump = nft_payload_dump, +}; + +static const struct nft_expr_ops * +nft_payload_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + enum nft_payload_bases base; + unsigned int offset, len; + + if (tb[NFTA_PAYLOAD_DREG] == NULL || + tb[NFTA_PAYLOAD_BASE] == NULL || + tb[NFTA_PAYLOAD_OFFSET] == NULL || + tb[NFTA_PAYLOAD_LEN] == NULL) + return ERR_PTR(-EINVAL); + + base = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_BASE])); + switch (base) { + case NFT_PAYLOAD_LL_HEADER: + case NFT_PAYLOAD_NETWORK_HEADER: + case NFT_PAYLOAD_TRANSPORT_HEADER: + break; + default: + return ERR_PTR(-EOPNOTSUPP); + } + + offset = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_OFFSET])); + len = ntohl(nla_get_be32(tb[NFTA_PAYLOAD_LEN])); + if (len == 0 || len > FIELD_SIZEOF(struct nft_data, data)) + return ERR_PTR(-EINVAL); + + if (len <= 4 && IS_ALIGNED(offset, len) && base != NFT_PAYLOAD_LL_HEADER) + return &nft_payload_fast_ops; + else + return &nft_payload_ops; +} + +static struct nft_expr_type nft_payload_type __read_mostly = { + .name = "payload", + .select_ops = nft_payload_select_ops, + .policy = nft_payload_policy, + .maxattr = NFTA_PAYLOAD_MAX, + .owner = THIS_MODULE, +}; + +int __init nft_payload_module_init(void) +{ + return nft_register_expr(&nft_payload_type); +} + +void nft_payload_module_exit(void) +{ + nft_unregister_expr(&nft_payload_type); +} diff --git a/net/netfilter/nft_rbtree.c b/net/netfilter/nft_rbtree.c new file mode 100644 index 0000000000000000000000000000000000000000..ca0c1b231bfe25cfe3c341c2aea8967f0dd4a4ec --- /dev/null +++ b/net/netfilter/nft_rbtree.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2008-2009 Patrick McHardy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct nft_rbtree { + struct rb_root root; +}; + +struct nft_rbtree_elem { + struct rb_node node; + u16 flags; + struct nft_data key; + struct nft_data data[]; +}; + +static bool nft_rbtree_lookup(const struct nft_set *set, + const struct nft_data *key, + struct nft_data *data) +{ + const struct nft_rbtree *priv = nft_set_priv(set); + const struct nft_rbtree_elem *rbe, *interval = NULL; + const struct rb_node *parent = priv->root.rb_node; + int d; + + while (parent != NULL) { + rbe = rb_entry(parent, struct nft_rbtree_elem, node); + + d = nft_data_cmp(&rbe->key, key, set->klen); + if (d < 0) { + parent = parent->rb_left; + interval = rbe; + } else if (d > 0) + parent = parent->rb_right; + else { +found: + if (rbe->flags & NFT_SET_ELEM_INTERVAL_END) + goto out; + if (set->flags & NFT_SET_MAP) + nft_data_copy(data, rbe->data); + return true; + } + } + + if (set->flags & NFT_SET_INTERVAL && interval != NULL) { + rbe = interval; + goto found; + } +out: + return false; +} + +static void nft_rbtree_elem_destroy(const struct nft_set *set, + struct nft_rbtree_elem *rbe) +{ + nft_data_uninit(&rbe->key, NFT_DATA_VALUE); + if (set->flags & NFT_SET_MAP) + nft_data_uninit(rbe->data, set->dtype); + kfree(rbe); +} + +static int __nft_rbtree_insert(const struct nft_set *set, + struct nft_rbtree_elem *new) +{ + struct nft_rbtree *priv = nft_set_priv(set); + struct nft_rbtree_elem *rbe; + struct rb_node *parent, **p; + int d; + + parent = NULL; + p = &priv->root.rb_node; + while (*p != NULL) { + parent = *p; + rbe = rb_entry(parent, struct nft_rbtree_elem, node); + d = nft_data_cmp(&rbe->key, &new->key, set->klen); + if (d < 0) + p = &parent->rb_left; + else if (d > 0) + p = &parent->rb_right; + else + return -EEXIST; + } + rb_link_node(&new->node, parent, p); + rb_insert_color(&new->node, &priv->root); + return 0; +} + +static int nft_rbtree_insert(const struct nft_set *set, + const struct nft_set_elem *elem) +{ + struct nft_rbtree_elem *rbe; + unsigned int size; + int err; + + size = sizeof(*rbe); + if (set->flags & NFT_SET_MAP) + size += sizeof(rbe->data[0]); + + rbe = kzalloc(size, GFP_KERNEL); + if (rbe == NULL) + return -ENOMEM; + + rbe->flags = elem->flags; + nft_data_copy(&rbe->key, &elem->key); + if (set->flags & NFT_SET_MAP) + nft_data_copy(rbe->data, &elem->data); + + err = __nft_rbtree_insert(set, rbe); + if (err < 0) + kfree(rbe); + return err; +} + +static void nft_rbtree_remove(const struct nft_set *set, + const struct nft_set_elem *elem) +{ + struct nft_rbtree *priv = nft_set_priv(set); + struct nft_rbtree_elem *rbe = elem->cookie; + + rb_erase(&rbe->node, &priv->root); + kfree(rbe); +} + +static int nft_rbtree_get(const struct nft_set *set, struct nft_set_elem *elem) +{ + const struct nft_rbtree *priv = nft_set_priv(set); + const struct rb_node *parent = priv->root.rb_node; + struct nft_rbtree_elem *rbe; + int d; + + while (parent != NULL) { + rbe = rb_entry(parent, struct nft_rbtree_elem, node); + + d = nft_data_cmp(&rbe->key, &elem->key, set->klen); + if (d < 0) + parent = parent->rb_left; + else if (d > 0) + parent = parent->rb_right; + else { + elem->cookie = rbe; + if (set->flags & NFT_SET_MAP) + nft_data_copy(&elem->data, rbe->data); + elem->flags = rbe->flags; + return 0; + } + } + return -ENOENT; +} + +static void nft_rbtree_walk(const struct nft_ctx *ctx, + const struct nft_set *set, + struct nft_set_iter *iter) +{ + const struct nft_rbtree *priv = nft_set_priv(set); + const struct nft_rbtree_elem *rbe; + struct nft_set_elem elem; + struct rb_node *node; + + for (node = rb_first(&priv->root); node != NULL; node = rb_next(node)) { + if (iter->count < iter->skip) + goto cont; + + rbe = rb_entry(node, struct nft_rbtree_elem, node); + nft_data_copy(&elem.key, &rbe->key); + if (set->flags & NFT_SET_MAP) + nft_data_copy(&elem.data, rbe->data); + elem.flags = rbe->flags; + + iter->err = iter->fn(ctx, set, iter, &elem); + if (iter->err < 0) + return; +cont: + iter->count++; + } +} + +static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[]) +{ + return sizeof(struct nft_rbtree); +} + +static int nft_rbtree_init(const struct nft_set *set, + const struct nlattr * const nla[]) +{ + struct nft_rbtree *priv = nft_set_priv(set); + + priv->root = RB_ROOT; + return 0; +} + +static void nft_rbtree_destroy(const struct nft_set *set) +{ + struct nft_rbtree *priv = nft_set_priv(set); + struct nft_rbtree_elem *rbe; + struct rb_node *node; + + while ((node = priv->root.rb_node) != NULL) { + rb_erase(node, &priv->root); + rbe = rb_entry(node, struct nft_rbtree_elem, node); + nft_rbtree_elem_destroy(set, rbe); + } +} + +static struct nft_set_ops nft_rbtree_ops __read_mostly = { + .privsize = nft_rbtree_privsize, + .init = nft_rbtree_init, + .destroy = nft_rbtree_destroy, + .insert = nft_rbtree_insert, + .remove = nft_rbtree_remove, + .get = nft_rbtree_get, + .lookup = nft_rbtree_lookup, + .walk = nft_rbtree_walk, + .features = NFT_SET_INTERVAL | NFT_SET_MAP, + .owner = THIS_MODULE, +}; + +static int __init nft_rbtree_module_init(void) +{ + return nft_register_set(&nft_rbtree_ops); +} + +static void __exit nft_rbtree_module_exit(void) +{ + nft_unregister_set(&nft_rbtree_ops); +} + +module_init(nft_rbtree_module_init); +module_exit(nft_rbtree_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Patrick McHardy "); +MODULE_ALIAS_NFT_SET(); diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index cd24290f3b2fdbff20088a0ffd2692fb6240a9ce..e762de5ee89bfa480b1789ce164cb024ac35e91e 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -43,10 +43,42 @@ optlen(const u_int8_t *opt, unsigned int offset) return opt[offset+1]; } +static u_int32_t tcpmss_reverse_mtu(struct net *net, + const struct sk_buff *skb, + unsigned int family) +{ + struct flowi fl; + const struct nf_afinfo *ai; + struct rtable *rt = NULL; + u_int32_t mtu = ~0U; + + if (family == PF_INET) { + struct flowi4 *fl4 = &fl.u.ip4; + memset(fl4, 0, sizeof(*fl4)); + fl4->daddr = ip_hdr(skb)->saddr; + } else { + struct flowi6 *fl6 = &fl.u.ip6; + + memset(fl6, 0, sizeof(*fl6)); + fl6->daddr = ipv6_hdr(skb)->saddr; + } + rcu_read_lock(); + ai = nf_get_afinfo(family); + if (ai != NULL) + ai->route(net, (struct dst_entry **)&rt, &fl, false); + rcu_read_unlock(); + + if (rt != NULL) { + mtu = dst_mtu(&rt->dst); + dst_release(&rt->dst); + } + return mtu; +} + static int tcpmss_mangle_packet(struct sk_buff *skb, const struct xt_action_param *par, - unsigned int in_mtu, + unsigned int family, unsigned int tcphoff, unsigned int minlen) { @@ -76,6 +108,9 @@ tcpmss_mangle_packet(struct sk_buff *skb, return -1; if (info->mss == XT_TCPMSS_CLAMP_PMTU) { + struct net *net = dev_net(par->in ? par->in : par->out); + unsigned int in_mtu = tcpmss_reverse_mtu(net, skb, family); + if (dst_mtu(skb_dst(skb)) <= minlen) { net_err_ratelimited("unknown or invalid path-MTU (%u)\n", dst_mtu(skb_dst(skb))); @@ -165,37 +200,6 @@ tcpmss_mangle_packet(struct sk_buff *skb, return TCPOLEN_MSS; } -static u_int32_t tcpmss_reverse_mtu(const struct sk_buff *skb, - unsigned int family) -{ - struct flowi fl; - const struct nf_afinfo *ai; - struct rtable *rt = NULL; - u_int32_t mtu = ~0U; - - if (family == PF_INET) { - struct flowi4 *fl4 = &fl.u.ip4; - memset(fl4, 0, sizeof(*fl4)); - fl4->daddr = ip_hdr(skb)->saddr; - } else { - struct flowi6 *fl6 = &fl.u.ip6; - - memset(fl6, 0, sizeof(*fl6)); - fl6->daddr = ipv6_hdr(skb)->saddr; - } - rcu_read_lock(); - ai = nf_get_afinfo(family); - if (ai != NULL) - ai->route(&init_net, (struct dst_entry **)&rt, &fl, false); - rcu_read_unlock(); - - if (rt != NULL) { - mtu = dst_mtu(&rt->dst); - dst_release(&rt->dst); - } - return mtu; -} - static unsigned int tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) { @@ -204,7 +208,7 @@ tcpmss_tg4(struct sk_buff *skb, const struct xt_action_param *par) int ret; ret = tcpmss_mangle_packet(skb, par, - tcpmss_reverse_mtu(skb, PF_INET), + PF_INET, iph->ihl * 4, sizeof(*iph) + sizeof(struct tcphdr)); if (ret < 0) @@ -233,7 +237,7 @@ tcpmss_tg6(struct sk_buff *skb, const struct xt_action_param *par) if (tcphoff < 0) return NF_DROP; ret = tcpmss_mangle_packet(skb, par, - tcpmss_reverse_mtu(skb, PF_INET6), + PF_INET6, tcphoff, sizeof(*ipv6h) + sizeof(struct tcphdr)); if (ret < 0) diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index 5d8a3a3cd5a7cd04b714d92a1366c04ad464e716..ef8a926752a97542f6f2f8eeb378e150958bff3d 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -200,7 +200,7 @@ nf_tproxy_get_sock_v6(struct net *net, const u8 protocol, in->ifindex); if (sk) { int connected = (sk->sk_state == TCP_ESTABLISHED); - int wildcard = ipv6_addr_any(&inet6_sk(sk)->rcv_saddr); + int wildcard = ipv6_addr_any(&sk->sk_v6_rcv_saddr); /* NOTE: we return listeners even if bound to * 0.0.0.0, those are filtered out in diff --git a/net/netfilter/xt_connbytes.c b/net/netfilter/xt_connbytes.c index e595e07a759b2df8ccdce5069d1974ec6ccaa754..1e634615ab9d6e4b2a5c7db5872332c5a522f435 100644 --- a/net/netfilter/xt_connbytes.c +++ b/net/netfilter/xt_connbytes.c @@ -26,16 +26,18 @@ connbytes_mt(const struct sk_buff *skb, struct xt_action_param *par) u_int64_t what = 0; /* initialize to make gcc happy */ u_int64_t bytes = 0; u_int64_t pkts = 0; + const struct nf_conn_acct *acct; const struct nf_conn_counter *counters; ct = nf_ct_get(skb, &ctinfo); if (!ct) return false; - counters = nf_conn_acct_find(ct); - if (!counters) + acct = nf_conn_acct_find(ct); + if (!acct) return false; + counters = acct->counter; switch (sinfo->what) { case XT_CONNBYTES_PKTS: switch (sinfo->direction) { diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c index 31790e789e224f8fb758ee87e62f8dd8908e81dd..e7c4e0e01ff5de40fb32cdeddd70cb204b84ea32 100644 --- a/net/netfilter/xt_set.c +++ b/net/netfilter/xt_set.c @@ -81,7 +81,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par) struct xt_set_info_match_v0 *info = par->matchinfo; ip_set_id_t index; - index = ip_set_nfnl_get_byindex(info->match_set.index); + index = ip_set_nfnl_get_byindex(par->net, info->match_set.index); if (index == IPSET_INVALID_ID) { pr_warning("Cannot find set indentified by id %u to match\n", @@ -91,7 +91,7 @@ set_match_v0_checkentry(const struct xt_mtchk_param *par) if (info->match_set.u.flags[IPSET_DIM_MAX-1] != 0) { pr_warning("Protocol error: set match dimension " "is over the limit!\n"); - ip_set_nfnl_put(info->match_set.index); + ip_set_nfnl_put(par->net, info->match_set.index); return -ERANGE; } @@ -106,9 +106,104 @@ set_match_v0_destroy(const struct xt_mtdtor_param *par) { struct xt_set_info_match_v0 *info = par->matchinfo; - ip_set_nfnl_put(info->match_set.index); + ip_set_nfnl_put(par->net, info->match_set.index); } +/* Revision 1 match */ + +static bool +set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_set_info_match_v1 *info = par->matchinfo; + ADT_OPT(opt, par->family, info->match_set.dim, + info->match_set.flags, 0, UINT_MAX); + + if (opt.flags & IPSET_RETURN_NOMATCH) + opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH; + + return match_set(info->match_set.index, skb, par, &opt, + info->match_set.flags & IPSET_INV_MATCH); +} + +static int +set_match_v1_checkentry(const struct xt_mtchk_param *par) +{ + struct xt_set_info_match_v1 *info = par->matchinfo; + ip_set_id_t index; + + index = ip_set_nfnl_get_byindex(par->net, info->match_set.index); + + if (index == IPSET_INVALID_ID) { + pr_warning("Cannot find set indentified by id %u to match\n", + info->match_set.index); + return -ENOENT; + } + if (info->match_set.dim > IPSET_DIM_MAX) { + pr_warning("Protocol error: set match dimension " + "is over the limit!\n"); + ip_set_nfnl_put(par->net, info->match_set.index); + return -ERANGE; + } + + return 0; +} + +static void +set_match_v1_destroy(const struct xt_mtdtor_param *par) +{ + struct xt_set_info_match_v1 *info = par->matchinfo; + + ip_set_nfnl_put(par->net, info->match_set.index); +} + +/* Revision 3 match */ + +static bool +match_counter(u64 counter, const struct ip_set_counter_match *info) +{ + switch (info->op) { + case IPSET_COUNTER_NONE: + return true; + case IPSET_COUNTER_EQ: + return counter == info->value; + case IPSET_COUNTER_NE: + return counter != info->value; + case IPSET_COUNTER_LT: + return counter < info->value; + case IPSET_COUNTER_GT: + return counter > info->value; + } + return false; +} + +static bool +set_match_v3(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_set_info_match_v3 *info = par->matchinfo; + ADT_OPT(opt, par->family, info->match_set.dim, + info->match_set.flags, info->flags, UINT_MAX); + int ret; + + if (info->packets.op != IPSET_COUNTER_NONE || + info->bytes.op != IPSET_COUNTER_NONE) + opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; + + ret = match_set(info->match_set.index, skb, par, &opt, + info->match_set.flags & IPSET_INV_MATCH); + + if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS)) + return ret; + + if (!match_counter(opt.ext.packets, &info->packets)) + return 0; + return match_counter(opt.ext.bytes, &info->bytes); +} + +#define set_match_v3_checkentry set_match_v1_checkentry +#define set_match_v3_destroy set_match_v1_destroy + +/* Revision 0 interface: backward compatible with netfilter/iptables */ + static unsigned int set_target_v0(struct sk_buff *skb, const struct xt_action_param *par) { @@ -133,7 +228,7 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) ip_set_id_t index; if (info->add_set.index != IPSET_INVALID_ID) { - index = ip_set_nfnl_get_byindex(info->add_set.index); + index = ip_set_nfnl_get_byindex(par->net, info->add_set.index); if (index == IPSET_INVALID_ID) { pr_warning("Cannot find add_set index %u as target\n", info->add_set.index); @@ -142,12 +237,12 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) } if (info->del_set.index != IPSET_INVALID_ID) { - index = ip_set_nfnl_get_byindex(info->del_set.index); + index = ip_set_nfnl_get_byindex(par->net, info->del_set.index); if (index == IPSET_INVALID_ID) { pr_warning("Cannot find del_set index %u as target\n", info->del_set.index); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->add_set.index); + ip_set_nfnl_put(par->net, info->add_set.index); return -ENOENT; } } @@ -156,9 +251,9 @@ set_target_v0_checkentry(const struct xt_tgchk_param *par) pr_warning("Protocol error: SET target dimension " "is over the limit!\n"); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->add_set.index); + ip_set_nfnl_put(par->net, info->add_set.index); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->del_set.index); + ip_set_nfnl_put(par->net, info->del_set.index); return -ERANGE; } @@ -175,57 +270,12 @@ set_target_v0_destroy(const struct xt_tgdtor_param *par) const struct xt_set_info_target_v0 *info = par->targinfo; if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->add_set.index); + ip_set_nfnl_put(par->net, info->add_set.index); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->del_set.index); + ip_set_nfnl_put(par->net, info->del_set.index); } -/* Revision 1 match and target */ - -static bool -set_match_v1(const struct sk_buff *skb, struct xt_action_param *par) -{ - const struct xt_set_info_match_v1 *info = par->matchinfo; - ADT_OPT(opt, par->family, info->match_set.dim, - info->match_set.flags, 0, UINT_MAX); - - if (opt.flags & IPSET_RETURN_NOMATCH) - opt.cmdflags |= IPSET_FLAG_RETURN_NOMATCH; - - return match_set(info->match_set.index, skb, par, &opt, - info->match_set.flags & IPSET_INV_MATCH); -} - -static int -set_match_v1_checkentry(const struct xt_mtchk_param *par) -{ - struct xt_set_info_match_v1 *info = par->matchinfo; - ip_set_id_t index; - - index = ip_set_nfnl_get_byindex(info->match_set.index); - - if (index == IPSET_INVALID_ID) { - pr_warning("Cannot find set indentified by id %u to match\n", - info->match_set.index); - return -ENOENT; - } - if (info->match_set.dim > IPSET_DIM_MAX) { - pr_warning("Protocol error: set match dimension " - "is over the limit!\n"); - ip_set_nfnl_put(info->match_set.index); - return -ERANGE; - } - - return 0; -} - -static void -set_match_v1_destroy(const struct xt_mtdtor_param *par) -{ - struct xt_set_info_match_v1 *info = par->matchinfo; - - ip_set_nfnl_put(info->match_set.index); -} +/* Revision 1 target */ static unsigned int set_target_v1(struct sk_buff *skb, const struct xt_action_param *par) @@ -251,7 +301,7 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par) ip_set_id_t index; if (info->add_set.index != IPSET_INVALID_ID) { - index = ip_set_nfnl_get_byindex(info->add_set.index); + index = ip_set_nfnl_get_byindex(par->net, info->add_set.index); if (index == IPSET_INVALID_ID) { pr_warning("Cannot find add_set index %u as target\n", info->add_set.index); @@ -260,12 +310,12 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par) } if (info->del_set.index != IPSET_INVALID_ID) { - index = ip_set_nfnl_get_byindex(info->del_set.index); + index = ip_set_nfnl_get_byindex(par->net, info->del_set.index); if (index == IPSET_INVALID_ID) { pr_warning("Cannot find del_set index %u as target\n", info->del_set.index); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->add_set.index); + ip_set_nfnl_put(par->net, info->add_set.index); return -ENOENT; } } @@ -274,9 +324,9 @@ set_target_v1_checkentry(const struct xt_tgchk_param *par) pr_warning("Protocol error: SET target dimension " "is over the limit!\n"); if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->add_set.index); + ip_set_nfnl_put(par->net, info->add_set.index); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->del_set.index); + ip_set_nfnl_put(par->net, info->del_set.index); return -ERANGE; } @@ -289,9 +339,9 @@ set_target_v1_destroy(const struct xt_tgdtor_param *par) const struct xt_set_info_target_v1 *info = par->targinfo; if (info->add_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->add_set.index); + ip_set_nfnl_put(par->net, info->add_set.index); if (info->del_set.index != IPSET_INVALID_ID) - ip_set_nfnl_put(info->del_set.index); + ip_set_nfnl_put(par->net, info->del_set.index); } /* Revision 2 target */ @@ -320,52 +370,6 @@ set_target_v2(struct sk_buff *skb, const struct xt_action_param *par) #define set_target_v2_checkentry set_target_v1_checkentry #define set_target_v2_destroy set_target_v1_destroy -/* Revision 3 match */ - -static bool -match_counter(u64 counter, const struct ip_set_counter_match *info) -{ - switch (info->op) { - case IPSET_COUNTER_NONE: - return true; - case IPSET_COUNTER_EQ: - return counter == info->value; - case IPSET_COUNTER_NE: - return counter != info->value; - case IPSET_COUNTER_LT: - return counter < info->value; - case IPSET_COUNTER_GT: - return counter > info->value; - } - return false; -} - -static bool -set_match_v3(const struct sk_buff *skb, struct xt_action_param *par) -{ - const struct xt_set_info_match_v3 *info = par->matchinfo; - ADT_OPT(opt, par->family, info->match_set.dim, - info->match_set.flags, info->flags, UINT_MAX); - int ret; - - if (info->packets.op != IPSET_COUNTER_NONE || - info->bytes.op != IPSET_COUNTER_NONE) - opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS; - - ret = match_set(info->match_set.index, skb, par, &opt, - info->match_set.flags & IPSET_INV_MATCH); - - if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS)) - return ret; - - if (!match_counter(opt.ext.packets, &info->packets)) - return 0; - return match_counter(opt.ext.bytes, &info->bytes); -} - -#define set_match_v3_checkentry set_match_v1_checkentry -#define set_match_v3_destroy set_match_v1_destroy - static struct xt_match set_matches[] __read_mostly = { { .name = "set", diff --git a/net/netfilter/xt_socket.c b/net/netfilter/xt_socket.c index 06df2b9110f5f2b9376c19e34241c257b31e5f63..1ba67931eb1b168fabfa78790f5ed53713188f9d 100644 --- a/net/netfilter/xt_socket.c +++ b/net/netfilter/xt_socket.c @@ -35,15 +35,6 @@ #include #endif -static void -xt_socket_put_sk(struct sock *sk) -{ - if (sk->sk_state == TCP_TIME_WAIT) - inet_twsk_put(inet_twsk(sk)); - else - sock_put(sk); -} - static int extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol, @@ -216,7 +207,7 @@ socket_match(const struct sk_buff *skb, struct xt_action_param *par, inet_twsk(sk)->tw_transparent)); if (sk != skb->sk) - xt_socket_put_sk(sk); + sock_gen_put(sk); if (wildcard || !transparent) sk = NULL; @@ -370,7 +361,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) */ wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) && sk->sk_state != TCP_TIME_WAIT && - ipv6_addr_any(&inet6_sk(sk)->rcv_saddr)); + ipv6_addr_any(&sk->sk_v6_rcv_saddr)); /* Ignore non-transparent sockets, if XT_SOCKET_TRANSPARENT is used */ @@ -381,7 +372,7 @@ socket_mt6_v1_v2(const struct sk_buff *skb, struct xt_action_param *par) inet_twsk(sk)->tw_transparent)); if (sk != skb->sk) - xt_socket_put_sk(sk); + sock_gen_put(sk); if (wildcard || !transparent) sk = NULL; diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 96a458e12f60ada2186c08ebbafe22c6e68ba06e..dce1bebf7aecb98f4cfae0beb45a9b3c576c21d3 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -817,7 +817,7 @@ int netlbl_req_setattr(struct request_sock *req, switch (req->rsk_ops->family) { case AF_INET: entry = netlbl_domhsh_getentry_af4(secattr->domain, - inet_rsk(req)->rmt_addr); + inet_rsk(req)->ir_rmt_addr); if (entry == NULL) { ret_val = -ENOENT; goto req_setattr_return; diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig index 5948b2fc72f63d3a9994e962b1366f8913c61ed7..6e0fa0cce1982a8825d36e7966851a46fe1bcb2e 100644 --- a/net/nfc/Kconfig +++ b/net/nfc/Kconfig @@ -14,6 +14,20 @@ menuconfig NFC To compile this support as a module, choose M here: the module will be called nfc. +config NFC_DIGITAL + depends on NFC + select CRC_CCITT + select CRC_ITU_T + tristate "NFC Digital Protocol stack support" + default n + help + Say Y if you want to build NFC digital protocol stack support. + This is needed by NFC chipsets whose firmware only implement + the NFC analog layer. + + To compile this support as a module, choose M here: the module will + be called nfc_digital. + source "net/nfc/nci/Kconfig" source "net/nfc/hci/Kconfig" diff --git a/net/nfc/Makefile b/net/nfc/Makefile index a76f4533cb6ce5a1b51e2199bbfd3c8bf91a86a4..2555ff8e7219b19ecdc06da62d5bef6ce555ae20 100644 --- a/net/nfc/Makefile +++ b/net/nfc/Makefile @@ -5,7 +5,9 @@ obj-$(CONFIG_NFC) += nfc.o obj-$(CONFIG_NFC_NCI) += nci/ obj-$(CONFIG_NFC_HCI) += hci/ +obj-$(CONFIG_NFC_DIGITAL) += nfc_digital.o nfc-objs := core.o netlink.o af_nfc.o rawsock.o llcp_core.o llcp_commands.o \ llcp_sock.o +nfc_digital-objs := digital_core.o digital_technology.o digital_dep.o diff --git a/net/nfc/core.c b/net/nfc/core.c index e92923cf3e0374f950417e5039ba2e61369c8e27..872529105abc7c3a2e41b9d579e934e019e8e5ff 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -384,6 +384,19 @@ int nfc_dep_link_is_up(struct nfc_dev *dev, u32 target_idx, { dev->dep_link_up = true; + if (!dev->active_target) { + struct nfc_target *target; + + target = nfc_find_target(dev, target_idx); + if (target == NULL) + return -ENOTCONN; + + dev->active_target = target; + } + + dev->polling = false; + dev->rf_mode = rf_mode; + nfc_llcp_mac_is_up(dev, target_idx, comm_mode, rf_mode); return nfc_genl_dep_link_up_event(dev, target_idx, comm_mode, rf_mode); @@ -536,7 +549,7 @@ error: return rc; } -static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx) +struct nfc_se *nfc_find_se(struct nfc_dev *dev, u32 se_idx) { struct nfc_se *se, *n; @@ -546,6 +559,7 @@ static struct nfc_se *find_se(struct nfc_dev *dev, u32 se_idx) return NULL; } +EXPORT_SYMBOL(nfc_find_se); int nfc_enable_se(struct nfc_dev *dev, u32 se_idx) { @@ -577,7 +591,7 @@ int nfc_enable_se(struct nfc_dev *dev, u32 se_idx) goto error; } - se = find_se(dev, se_idx); + se = nfc_find_se(dev, se_idx); if (!se) { rc = -EINVAL; goto error; @@ -622,7 +636,7 @@ int nfc_disable_se(struct nfc_dev *dev, u32 se_idx) goto error; } - se = find_se(dev, se_idx); + se = nfc_find_se(dev, se_idx); if (!se) { rc = -EINVAL; goto error; @@ -881,7 +895,7 @@ int nfc_add_se(struct nfc_dev *dev, u32 se_idx, u16 type) pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx); - se = find_se(dev, se_idx); + se = nfc_find_se(dev, se_idx); if (se) return -EALREADY; diff --git a/net/nfc/digital.h b/net/nfc/digital.h new file mode 100644 index 0000000000000000000000000000000000000000..08b29b55ea63acdff30d2ba29c1cda51a52f7cb8 --- /dev/null +++ b/net/nfc/digital.h @@ -0,0 +1,170 @@ +/* + * NFC Digital Protocol stack + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __DIGITAL_H +#define __DIGITAL_H + +#include +#include + +#include +#include + +#define PROTOCOL_ERR(req) pr_err("%d: NFC Digital Protocol error: %s\n", \ + __LINE__, req) + +#define DIGITAL_CMD_IN_SEND 0 +#define DIGITAL_CMD_TG_SEND 1 +#define DIGITAL_CMD_TG_LISTEN 2 +#define DIGITAL_CMD_TG_LISTEN_MDAA 3 + +#define DIGITAL_MAX_HEADER_LEN 7 +#define DIGITAL_CRC_LEN 2 + +#define DIGITAL_SENSF_NFCID2_NFC_DEP_B1 0x01 +#define DIGITAL_SENSF_NFCID2_NFC_DEP_B2 0xFE + +#define DIGITAL_SENS_RES_NFC_DEP 0x0100 +#define DIGITAL_SEL_RES_NFC_DEP 0x40 +#define DIGITAL_SENSF_FELICA_SC 0xFFFF + +#define DIGITAL_DRV_CAPS_IN_CRC(ddev) \ + ((ddev)->driver_capabilities & NFC_DIGITAL_DRV_CAPS_IN_CRC) +#define DIGITAL_DRV_CAPS_TG_CRC(ddev) \ + ((ddev)->driver_capabilities & NFC_DIGITAL_DRV_CAPS_TG_CRC) + +struct digital_data_exch { + data_exchange_cb_t cb; + void *cb_context; +}; + +struct sk_buff *digital_skb_alloc(struct nfc_digital_dev *ddev, + unsigned int len); + +int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type, + struct sk_buff *skb, struct digital_tg_mdaa_params *params, + u16 timeout, nfc_digital_cmd_complete_t cmd_cb, + void *cb_context); + +int digital_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param); +static inline int digital_in_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 timeout, + nfc_digital_cmd_complete_t cmd_cb, + void *cb_context) +{ + return digital_send_cmd(ddev, DIGITAL_CMD_IN_SEND, skb, NULL, timeout, + cmd_cb, cb_context); +} + +void digital_poll_next_tech(struct nfc_digital_dev *ddev); + +int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech); +int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech); + +int digital_target_found(struct nfc_digital_dev *ddev, + struct nfc_target *target, u8 protocol); + +int digital_in_recv_mifare_res(struct sk_buff *resp); + +int digital_in_send_atr_req(struct nfc_digital_dev *ddev, + struct nfc_target *target, __u8 comm_mode, __u8 *gb, + size_t gb_len); +int digital_in_send_dep_req(struct nfc_digital_dev *ddev, + struct nfc_target *target, struct sk_buff *skb, + struct digital_data_exch *data_exch); + +int digital_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param); +static inline int digital_tg_send_cmd(struct nfc_digital_dev *ddev, + struct sk_buff *skb, u16 timeout, + nfc_digital_cmd_complete_t cmd_cb, void *cb_context) +{ + return digital_send_cmd(ddev, DIGITAL_CMD_TG_SEND, skb, NULL, timeout, + cmd_cb, cb_context); +} + +void digital_tg_recv_sens_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp); + +void digital_tg_recv_sensf_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp); + +static inline int digital_tg_listen(struct nfc_digital_dev *ddev, u16 timeout, + nfc_digital_cmd_complete_t cb, void *arg) +{ + return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN, NULL, NULL, + timeout, cb, arg); +} + +void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp); + +int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb); + +int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech); +int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech); + +typedef u16 (*crc_func_t)(u16, const u8 *, size_t); + +#define CRC_A_INIT 0x6363 +#define CRC_B_INIT 0xFFFF +#define CRC_F_INIT 0x0000 + +void digital_skb_add_crc(struct sk_buff *skb, crc_func_t crc_func, u16 init, + u8 bitwise_inv, u8 msb_first); + +static inline void digital_skb_add_crc_a(struct sk_buff *skb) +{ + digital_skb_add_crc(skb, crc_ccitt, CRC_A_INIT, 0, 0); +} + +static inline void digital_skb_add_crc_b(struct sk_buff *skb) +{ + digital_skb_add_crc(skb, crc_ccitt, CRC_B_INIT, 1, 0); +} + +static inline void digital_skb_add_crc_f(struct sk_buff *skb) +{ + digital_skb_add_crc(skb, crc_itu_t, CRC_F_INIT, 0, 1); +} + +static inline void digital_skb_add_crc_none(struct sk_buff *skb) +{ + return; +} + +int digital_skb_check_crc(struct sk_buff *skb, crc_func_t crc_func, + u16 crc_init, u8 bitwise_inv, u8 msb_first); + +static inline int digital_skb_check_crc_a(struct sk_buff *skb) +{ + return digital_skb_check_crc(skb, crc_ccitt, CRC_A_INIT, 0, 0); +} + +static inline int digital_skb_check_crc_b(struct sk_buff *skb) +{ + return digital_skb_check_crc(skb, crc_ccitt, CRC_B_INIT, 1, 0); +} + +static inline int digital_skb_check_crc_f(struct sk_buff *skb) +{ + return digital_skb_check_crc(skb, crc_itu_t, CRC_F_INIT, 0, 1); +} + +static inline int digital_skb_check_crc_none(struct sk_buff *skb) +{ + return 0; +} + +#endif /* __DIGITAL_H */ diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c new file mode 100644 index 0000000000000000000000000000000000000000..09fc95439955052c2b2f6f178ae0ea421f3fab24 --- /dev/null +++ b/net/nfc/digital_core.c @@ -0,0 +1,737 @@ +/* + * NFC Digital Protocol stack + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#define pr_fmt(fmt) "digital: %s: " fmt, __func__ + +#include + +#include "digital.h" + +#define DIGITAL_PROTO_NFCA_RF_TECH \ + (NFC_PROTO_JEWEL_MASK | NFC_PROTO_MIFARE_MASK | NFC_PROTO_NFC_DEP_MASK) + +#define DIGITAL_PROTO_NFCF_RF_TECH \ + (NFC_PROTO_FELICA_MASK | NFC_PROTO_NFC_DEP_MASK) + +struct digital_cmd { + struct list_head queue; + + u8 type; + u8 pending; + + u16 timeout; + struct sk_buff *req; + struct sk_buff *resp; + struct digital_tg_mdaa_params *mdaa_params; + + nfc_digital_cmd_complete_t cmd_cb; + void *cb_context; +}; + +struct sk_buff *digital_skb_alloc(struct nfc_digital_dev *ddev, + unsigned int len) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + ddev->tx_headroom + ddev->tx_tailroom, + GFP_KERNEL); + if (skb) + skb_reserve(skb, ddev->tx_headroom); + + return skb; +} + +void digital_skb_add_crc(struct sk_buff *skb, crc_func_t crc_func, u16 init, + u8 bitwise_inv, u8 msb_first) +{ + u16 crc; + + crc = crc_func(init, skb->data, skb->len); + + if (bitwise_inv) + crc = ~crc; + + if (msb_first) + crc = __fswab16(crc); + + *skb_put(skb, 1) = crc & 0xFF; + *skb_put(skb, 1) = (crc >> 8) & 0xFF; +} + +int digital_skb_check_crc(struct sk_buff *skb, crc_func_t crc_func, + u16 crc_init, u8 bitwise_inv, u8 msb_first) +{ + int rc; + u16 crc; + + if (skb->len <= 2) + return -EIO; + + crc = crc_func(crc_init, skb->data, skb->len - 2); + + if (bitwise_inv) + crc = ~crc; + + if (msb_first) + crc = __swab16(crc); + + rc = (skb->data[skb->len - 2] - (crc & 0xFF)) + + (skb->data[skb->len - 1] - ((crc >> 8) & 0xFF)); + + if (rc) + return -EIO; + + skb_trim(skb, skb->len - 2); + + return 0; +} + +static inline void digital_switch_rf(struct nfc_digital_dev *ddev, bool on) +{ + ddev->ops->switch_rf(ddev, on); +} + +static inline void digital_abort_cmd(struct nfc_digital_dev *ddev) +{ + ddev->ops->abort_cmd(ddev); +} + +static void digital_wq_cmd_complete(struct work_struct *work) +{ + struct digital_cmd *cmd; + struct nfc_digital_dev *ddev = container_of(work, + struct nfc_digital_dev, + cmd_complete_work); + + mutex_lock(&ddev->cmd_lock); + + cmd = list_first_entry_or_null(&ddev->cmd_queue, struct digital_cmd, + queue); + if (!cmd) { + mutex_unlock(&ddev->cmd_lock); + return; + } + + list_del(&cmd->queue); + + mutex_unlock(&ddev->cmd_lock); + + if (!IS_ERR(cmd->resp)) + print_hex_dump_debug("DIGITAL RX: ", DUMP_PREFIX_NONE, 16, 1, + cmd->resp->data, cmd->resp->len, false); + + cmd->cmd_cb(ddev, cmd->cb_context, cmd->resp); + + kfree(cmd->mdaa_params); + kfree(cmd); + + schedule_work(&ddev->cmd_work); +} + +static void digital_send_cmd_complete(struct nfc_digital_dev *ddev, + void *arg, struct sk_buff *resp) +{ + struct digital_cmd *cmd = arg; + + cmd->resp = resp; + + schedule_work(&ddev->cmd_complete_work); +} + +static void digital_wq_cmd(struct work_struct *work) +{ + int rc; + struct digital_cmd *cmd; + struct digital_tg_mdaa_params *params; + struct nfc_digital_dev *ddev = container_of(work, + struct nfc_digital_dev, + cmd_work); + + mutex_lock(&ddev->cmd_lock); + + cmd = list_first_entry_or_null(&ddev->cmd_queue, struct digital_cmd, + queue); + if (!cmd || cmd->pending) { + mutex_unlock(&ddev->cmd_lock); + return; + } + + mutex_unlock(&ddev->cmd_lock); + + if (cmd->req) + print_hex_dump_debug("DIGITAL TX: ", DUMP_PREFIX_NONE, 16, 1, + cmd->req->data, cmd->req->len, false); + + switch (cmd->type) { + case DIGITAL_CMD_IN_SEND: + rc = ddev->ops->in_send_cmd(ddev, cmd->req, cmd->timeout, + digital_send_cmd_complete, cmd); + break; + + case DIGITAL_CMD_TG_SEND: + rc = ddev->ops->tg_send_cmd(ddev, cmd->req, cmd->timeout, + digital_send_cmd_complete, cmd); + break; + + case DIGITAL_CMD_TG_LISTEN: + rc = ddev->ops->tg_listen(ddev, cmd->timeout, + digital_send_cmd_complete, cmd); + break; + + case DIGITAL_CMD_TG_LISTEN_MDAA: + params = cmd->mdaa_params; + + rc = ddev->ops->tg_listen_mdaa(ddev, params, cmd->timeout, + digital_send_cmd_complete, cmd); + break; + + default: + pr_err("Unknown cmd type %d\n", cmd->type); + return; + } + + if (!rc) + return; + + pr_err("in_send_command returned err %d\n", rc); + + mutex_lock(&ddev->cmd_lock); + list_del(&cmd->queue); + mutex_unlock(&ddev->cmd_lock); + + kfree_skb(cmd->req); + kfree(cmd->mdaa_params); + kfree(cmd); + + schedule_work(&ddev->cmd_work); +} + +int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type, + struct sk_buff *skb, struct digital_tg_mdaa_params *params, + u16 timeout, nfc_digital_cmd_complete_t cmd_cb, + void *cb_context) +{ + struct digital_cmd *cmd; + + cmd = kzalloc(sizeof(struct digital_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->type = cmd_type; + cmd->timeout = timeout; + cmd->req = skb; + cmd->mdaa_params = params; + cmd->cmd_cb = cmd_cb; + cmd->cb_context = cb_context; + INIT_LIST_HEAD(&cmd->queue); + + mutex_lock(&ddev->cmd_lock); + list_add_tail(&cmd->queue, &ddev->cmd_queue); + mutex_unlock(&ddev->cmd_lock); + + schedule_work(&ddev->cmd_work); + + return 0; +} + +int digital_in_configure_hw(struct nfc_digital_dev *ddev, int type, int param) +{ + int rc; + + rc = ddev->ops->in_configure_hw(ddev, type, param); + if (rc) + pr_err("in_configure_hw failed: %d\n", rc); + + return rc; +} + +int digital_tg_configure_hw(struct nfc_digital_dev *ddev, int type, int param) +{ + int rc; + + rc = ddev->ops->tg_configure_hw(ddev, type, param); + if (rc) + pr_err("tg_configure_hw failed: %d\n", rc); + + return rc; +} + +static int digital_tg_listen_mdaa(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + struct digital_tg_mdaa_params *params; + + params = kzalloc(sizeof(struct digital_tg_mdaa_params), GFP_KERNEL); + if (!params) + return -ENOMEM; + + params->sens_res = DIGITAL_SENS_RES_NFC_DEP; + get_random_bytes(params->nfcid1, sizeof(params->nfcid1)); + params->sel_res = DIGITAL_SEL_RES_NFC_DEP; + + params->nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1; + params->nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2; + get_random_bytes(params->nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2); + params->sc = DIGITAL_SENSF_FELICA_SC; + + return digital_send_cmd(ddev, DIGITAL_CMD_TG_LISTEN_MDAA, NULL, params, + 500, digital_tg_recv_atr_req, NULL); +} + +int digital_target_found(struct nfc_digital_dev *ddev, + struct nfc_target *target, u8 protocol) +{ + int rc; + u8 framing; + u8 rf_tech; + int (*check_crc)(struct sk_buff *skb); + void (*add_crc)(struct sk_buff *skb); + + rf_tech = ddev->poll_techs[ddev->poll_tech_index].rf_tech; + + switch (protocol) { + case NFC_PROTO_JEWEL: + framing = NFC_DIGITAL_FRAMING_NFCA_T1T; + check_crc = digital_skb_check_crc_b; + add_crc = digital_skb_add_crc_b; + break; + + case NFC_PROTO_MIFARE: + framing = NFC_DIGITAL_FRAMING_NFCA_T2T; + check_crc = digital_skb_check_crc_a; + add_crc = digital_skb_add_crc_a; + break; + + case NFC_PROTO_FELICA: + framing = NFC_DIGITAL_FRAMING_NFCF_T3T; + check_crc = digital_skb_check_crc_f; + add_crc = digital_skb_add_crc_f; + break; + + case NFC_PROTO_NFC_DEP: + if (rf_tech == NFC_DIGITAL_RF_TECH_106A) { + framing = NFC_DIGITAL_FRAMING_NFCA_NFC_DEP; + check_crc = digital_skb_check_crc_a; + add_crc = digital_skb_add_crc_a; + } else { + framing = NFC_DIGITAL_FRAMING_NFCF_NFC_DEP; + check_crc = digital_skb_check_crc_f; + add_crc = digital_skb_add_crc_f; + } + break; + + default: + pr_err("Invalid protocol %d\n", protocol); + return -EINVAL; + } + + pr_debug("rf_tech=%d, protocol=%d\n", rf_tech, protocol); + + ddev->curr_rf_tech = rf_tech; + ddev->curr_protocol = protocol; + + if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) { + ddev->skb_add_crc = digital_skb_add_crc_none; + ddev->skb_check_crc = digital_skb_check_crc_none; + } else { + ddev->skb_add_crc = add_crc; + ddev->skb_check_crc = check_crc; + } + + rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, framing); + if (rc) + return rc; + + target->supported_protocols = (1 << protocol); + rc = nfc_targets_found(ddev->nfc_dev, target, 1); + if (rc) + return rc; + + ddev->poll_tech_count = 0; + + return 0; +} + +void digital_poll_next_tech(struct nfc_digital_dev *ddev) +{ + digital_switch_rf(ddev, 0); + + mutex_lock(&ddev->poll_lock); + + if (!ddev->poll_tech_count) { + mutex_unlock(&ddev->poll_lock); + return; + } + + ddev->poll_tech_index = (ddev->poll_tech_index + 1) % + ddev->poll_tech_count; + + mutex_unlock(&ddev->poll_lock); + + schedule_work(&ddev->poll_work); +} + +static void digital_wq_poll(struct work_struct *work) +{ + int rc; + struct digital_poll_tech *poll_tech; + struct nfc_digital_dev *ddev = container_of(work, + struct nfc_digital_dev, + poll_work); + mutex_lock(&ddev->poll_lock); + + if (!ddev->poll_tech_count) { + mutex_unlock(&ddev->poll_lock); + return; + } + + poll_tech = &ddev->poll_techs[ddev->poll_tech_index]; + + mutex_unlock(&ddev->poll_lock); + + rc = poll_tech->poll_func(ddev, poll_tech->rf_tech); + if (rc) + digital_poll_next_tech(ddev); +} + +static void digital_add_poll_tech(struct nfc_digital_dev *ddev, u8 rf_tech, + digital_poll_t poll_func) +{ + struct digital_poll_tech *poll_tech; + + if (ddev->poll_tech_count >= NFC_DIGITAL_POLL_MODE_COUNT_MAX) + return; + + poll_tech = &ddev->poll_techs[ddev->poll_tech_count++]; + + poll_tech->rf_tech = rf_tech; + poll_tech->poll_func = poll_func; +} + +/** + * start_poll operation + * + * For every supported protocol, the corresponding polling function is added + * to the table of polling technologies (ddev->poll_techs[]) using + * digital_add_poll_tech(). + * When a polling function fails (by timeout or protocol error) the next one is + * schedule by digital_poll_next_tech() on the poll workqueue (ddev->poll_work). + */ +static int digital_start_poll(struct nfc_dev *nfc_dev, __u32 im_protocols, + __u32 tm_protocols) +{ + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + u32 matching_im_protocols, matching_tm_protocols; + + pr_debug("protocols: im 0x%x, tm 0x%x, supported 0x%x\n", im_protocols, + tm_protocols, ddev->protocols); + + matching_im_protocols = ddev->protocols & im_protocols; + matching_tm_protocols = ddev->protocols & tm_protocols; + + if (!matching_im_protocols && !matching_tm_protocols) { + pr_err("Unknown protocol\n"); + return -EINVAL; + } + + if (ddev->poll_tech_count) { + pr_err("Already polling\n"); + return -EBUSY; + } + + if (ddev->curr_protocol) { + pr_err("A target is already active\n"); + return -EBUSY; + } + + ddev->poll_tech_count = 0; + ddev->poll_tech_index = 0; + + if (matching_im_protocols & DIGITAL_PROTO_NFCA_RF_TECH) + digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A, + digital_in_send_sens_req); + + if (im_protocols & DIGITAL_PROTO_NFCF_RF_TECH) { + digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F, + digital_in_send_sensf_req); + + digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_424F, + digital_in_send_sensf_req); + } + + if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) { + if (ddev->ops->tg_listen_mdaa) { + digital_add_poll_tech(ddev, 0, + digital_tg_listen_mdaa); + } else { + digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_106A, + digital_tg_listen_nfca); + + digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_212F, + digital_tg_listen_nfcf); + + digital_add_poll_tech(ddev, NFC_DIGITAL_RF_TECH_424F, + digital_tg_listen_nfcf); + } + } + + if (!ddev->poll_tech_count) { + pr_err("Unsupported protocols: im=0x%x, tm=0x%x\n", + matching_im_protocols, matching_tm_protocols); + return -EINVAL; + } + + schedule_work(&ddev->poll_work); + + return 0; +} + +static void digital_stop_poll(struct nfc_dev *nfc_dev) +{ + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + + mutex_lock(&ddev->poll_lock); + + if (!ddev->poll_tech_count) { + pr_err("Polling operation was not running\n"); + mutex_unlock(&ddev->poll_lock); + return; + } + + ddev->poll_tech_count = 0; + + mutex_unlock(&ddev->poll_lock); + + cancel_work_sync(&ddev->poll_work); + + digital_abort_cmd(ddev); +} + +static int digital_dev_up(struct nfc_dev *nfc_dev) +{ + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + + digital_switch_rf(ddev, 1); + + return 0; +} + +static int digital_dev_down(struct nfc_dev *nfc_dev) +{ + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + + digital_switch_rf(ddev, 0); + + return 0; +} + +static int digital_dep_link_up(struct nfc_dev *nfc_dev, + struct nfc_target *target, + __u8 comm_mode, __u8 *gb, size_t gb_len) +{ + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + + return digital_in_send_atr_req(ddev, target, comm_mode, gb, gb_len); +} + +static int digital_dep_link_down(struct nfc_dev *nfc_dev) +{ + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + + ddev->curr_protocol = 0; + + return 0; +} + +static int digital_activate_target(struct nfc_dev *nfc_dev, + struct nfc_target *target, __u32 protocol) +{ + return 0; +} + +static void digital_deactivate_target(struct nfc_dev *nfc_dev, + struct nfc_target *target) +{ + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + + ddev->curr_protocol = 0; +} + +static int digital_tg_send(struct nfc_dev *dev, struct sk_buff *skb) +{ + struct nfc_digital_dev *ddev = nfc_get_drvdata(dev); + + return digital_tg_send_dep_res(ddev, skb); +} + +static void digital_in_send_complete(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct digital_data_exch *data_exch = arg; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + goto done; + } + + if (ddev->curr_protocol == NFC_PROTO_MIFARE) + rc = digital_in_recv_mifare_res(resp); + else + rc = ddev->skb_check_crc(resp); + + if (rc) { + kfree_skb(resp); + resp = NULL; + } + +done: + data_exch->cb(data_exch->cb_context, resp, rc); + + kfree(data_exch); +} + +static int digital_in_send(struct nfc_dev *nfc_dev, struct nfc_target *target, + struct sk_buff *skb, data_exchange_cb_t cb, + void *cb_context) +{ + struct nfc_digital_dev *ddev = nfc_get_drvdata(nfc_dev); + struct digital_data_exch *data_exch; + + data_exch = kzalloc(sizeof(struct digital_data_exch), GFP_KERNEL); + if (!data_exch) { + pr_err("Failed to allocate data_exch struct\n"); + return -ENOMEM; + } + + data_exch->cb = cb; + data_exch->cb_context = cb_context; + + if (ddev->curr_protocol == NFC_PROTO_NFC_DEP) + return digital_in_send_dep_req(ddev, target, skb, data_exch); + + ddev->skb_add_crc(skb); + + return digital_in_send_cmd(ddev, skb, 500, digital_in_send_complete, + data_exch); +} + +static struct nfc_ops digital_nfc_ops = { + .dev_up = digital_dev_up, + .dev_down = digital_dev_down, + .start_poll = digital_start_poll, + .stop_poll = digital_stop_poll, + .dep_link_up = digital_dep_link_up, + .dep_link_down = digital_dep_link_down, + .activate_target = digital_activate_target, + .deactivate_target = digital_deactivate_target, + .tm_send = digital_tg_send, + .im_transceive = digital_in_send, +}; + +struct nfc_digital_dev *nfc_digital_allocate_device(struct nfc_digital_ops *ops, + __u32 supported_protocols, + __u32 driver_capabilities, + int tx_headroom, int tx_tailroom) +{ + struct nfc_digital_dev *ddev; + + if (!ops->in_configure_hw || !ops->in_send_cmd || !ops->tg_listen || + !ops->tg_configure_hw || !ops->tg_send_cmd || !ops->abort_cmd || + !ops->switch_rf) + return NULL; + + ddev = kzalloc(sizeof(struct nfc_digital_dev), GFP_KERNEL); + if (!ddev) + return NULL; + + ddev->driver_capabilities = driver_capabilities; + ddev->ops = ops; + + mutex_init(&ddev->cmd_lock); + INIT_LIST_HEAD(&ddev->cmd_queue); + + INIT_WORK(&ddev->cmd_work, digital_wq_cmd); + INIT_WORK(&ddev->cmd_complete_work, digital_wq_cmd_complete); + + mutex_init(&ddev->poll_lock); + INIT_WORK(&ddev->poll_work, digital_wq_poll); + + if (supported_protocols & NFC_PROTO_JEWEL_MASK) + ddev->protocols |= NFC_PROTO_JEWEL_MASK; + if (supported_protocols & NFC_PROTO_MIFARE_MASK) + ddev->protocols |= NFC_PROTO_MIFARE_MASK; + if (supported_protocols & NFC_PROTO_FELICA_MASK) + ddev->protocols |= NFC_PROTO_FELICA_MASK; + if (supported_protocols & NFC_PROTO_NFC_DEP_MASK) + ddev->protocols |= NFC_PROTO_NFC_DEP_MASK; + + ddev->tx_headroom = tx_headroom + DIGITAL_MAX_HEADER_LEN; + ddev->tx_tailroom = tx_tailroom + DIGITAL_CRC_LEN; + + ddev->nfc_dev = nfc_allocate_device(&digital_nfc_ops, ddev->protocols, + ddev->tx_headroom, + ddev->tx_tailroom); + if (!ddev->nfc_dev) { + pr_err("nfc_allocate_device failed\n"); + goto free_dev; + } + + nfc_set_drvdata(ddev->nfc_dev, ddev); + + return ddev; + +free_dev: + kfree(ddev); + + return NULL; +} +EXPORT_SYMBOL(nfc_digital_allocate_device); + +void nfc_digital_free_device(struct nfc_digital_dev *ddev) +{ + nfc_free_device(ddev->nfc_dev); + kfree(ddev); +} +EXPORT_SYMBOL(nfc_digital_free_device); + +int nfc_digital_register_device(struct nfc_digital_dev *ddev) +{ + return nfc_register_device(ddev->nfc_dev); +} +EXPORT_SYMBOL(nfc_digital_register_device); + +void nfc_digital_unregister_device(struct nfc_digital_dev *ddev) +{ + struct digital_cmd *cmd, *n; + + nfc_unregister_device(ddev->nfc_dev); + + mutex_lock(&ddev->poll_lock); + ddev->poll_tech_count = 0; + mutex_unlock(&ddev->poll_lock); + + cancel_work_sync(&ddev->poll_work); + cancel_work_sync(&ddev->cmd_work); + cancel_work_sync(&ddev->cmd_complete_work); + + list_for_each_entry_safe(cmd, n, &ddev->cmd_queue, queue) { + list_del(&cmd->queue); + kfree(cmd->mdaa_params); + kfree(cmd); + } +} +EXPORT_SYMBOL(nfc_digital_unregister_device); + +MODULE_LICENSE("GPL"); diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c new file mode 100644 index 0000000000000000000000000000000000000000..07bbc24fb4c73e39f79c8e5b70c34d95338b04c3 --- /dev/null +++ b/net/nfc/digital_dep.c @@ -0,0 +1,729 @@ +/* + * NFC Digital Protocol stack + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#define pr_fmt(fmt) "digital: %s: " fmt, __func__ + +#include "digital.h" + +#define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4 +#define DIGITAL_NFC_DEP_FRAME_DIR_IN 0xD5 + +#define DIGITAL_NFC_DEP_NFCA_SOD_SB 0xF0 + +#define DIGITAL_CMD_ATR_REQ 0x00 +#define DIGITAL_CMD_ATR_RES 0x01 +#define DIGITAL_CMD_PSL_REQ 0x04 +#define DIGITAL_CMD_PSL_RES 0x05 +#define DIGITAL_CMD_DEP_REQ 0x06 +#define DIGITAL_CMD_DEP_RES 0x07 + +#define DIGITAL_ATR_REQ_MIN_SIZE 16 +#define DIGITAL_ATR_REQ_MAX_SIZE 64 + +#define DIGITAL_NFCID3_LEN ((u8)8) +#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30 +#define DIGITAL_GB_BIT 0x02 + +#define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0) + +#define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10 + +#define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \ + ((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT) +#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb) ((pfb) & 0x10) +#define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08) +#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04) +#define DIGITAL_NFC_DEP_PFB_PNI(pfb) ((pfb) & 0x03) + +#define DIGITAL_NFC_DEP_PFB_I_PDU 0x00 +#define DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU 0x40 +#define DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU 0x80 + +struct digital_atr_req { + u8 dir; + u8 cmd; + u8 nfcid3[10]; + u8 did; + u8 bs; + u8 br; + u8 pp; + u8 gb[0]; +} __packed; + +struct digital_atr_res { + u8 dir; + u8 cmd; + u8 nfcid3[10]; + u8 did; + u8 bs; + u8 br; + u8 to; + u8 pp; + u8 gb[0]; +} __packed; + +struct digital_psl_req { + u8 dir; + u8 cmd; + u8 did; + u8 brs; + u8 fsl; +} __packed; + +struct digital_psl_res { + u8 dir; + u8 cmd; + u8 did; +} __packed; + +struct digital_dep_req_res { + u8 dir; + u8 cmd; + u8 pfb; +} __packed; + +static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp); + +static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev, + struct sk_buff *skb) +{ + skb_push(skb, sizeof(u8)); + + skb->data[0] = skb->len; + + if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A) + *skb_push(skb, sizeof(u8)) = DIGITAL_NFC_DEP_NFCA_SOD_SB; +} + +static int digital_skb_pull_dep_sod(struct nfc_digital_dev *ddev, + struct sk_buff *skb) +{ + u8 size; + + if (skb->len < 2) + return -EIO; + + if (ddev->curr_rf_tech == NFC_DIGITAL_RF_TECH_106A) + skb_pull(skb, sizeof(u8)); + + size = skb->data[0]; + if (size != skb->len) + return -EIO; + + skb_pull(skb, sizeof(u8)); + + return 0; +} + +static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct nfc_target *target = arg; + struct digital_atr_res *atr_res; + u8 gb_len; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + rc = ddev->skb_check_crc(resp); + if (rc) { + PROTOCOL_ERR("14.4.1.6"); + goto exit; + } + + rc = digital_skb_pull_dep_sod(ddev, resp); + if (rc) { + PROTOCOL_ERR("14.4.1.2"); + goto exit; + } + + if (resp->len < sizeof(struct digital_atr_res)) { + rc = -EIO; + goto exit; + } + + gb_len = resp->len - sizeof(struct digital_atr_res); + + atr_res = (struct digital_atr_res *)resp->data; + + rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len); + if (rc) + goto exit; + + rc = nfc_dep_link_is_up(ddev->nfc_dev, target->idx, NFC_COMM_ACTIVE, + NFC_RF_INITIATOR); + + ddev->curr_nfc_dep_pni = 0; + +exit: + dev_kfree_skb(resp); + + if (rc) + ddev->curr_protocol = 0; +} + +int digital_in_send_atr_req(struct nfc_digital_dev *ddev, + struct nfc_target *target, __u8 comm_mode, __u8 *gb, + size_t gb_len) +{ + struct sk_buff *skb; + struct digital_atr_req *atr_req; + uint size; + + size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len; + + if (size > DIGITAL_ATR_REQ_MAX_SIZE) { + PROTOCOL_ERR("14.6.1.1"); + return -EINVAL; + } + + skb = digital_skb_alloc(ddev, size); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(struct digital_atr_req)); + + atr_req = (struct digital_atr_req *)skb->data; + memset(atr_req, 0, sizeof(struct digital_atr_req)); + + atr_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; + atr_req->cmd = DIGITAL_CMD_ATR_REQ; + if (target->nfcid2_len) + memcpy(atr_req->nfcid3, target->nfcid2, + max(target->nfcid2_len, DIGITAL_NFCID3_LEN)); + else + get_random_bytes(atr_req->nfcid3, DIGITAL_NFCID3_LEN); + + atr_req->did = 0; + atr_req->bs = 0; + atr_req->br = 0; + + atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B; + + if (gb_len) { + atr_req->pp |= DIGITAL_GB_BIT; + memcpy(skb_put(skb, gb_len), gb, gb_len); + } + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res, target); + + return 0; +} + +static int digital_in_send_rtox(struct nfc_digital_dev *ddev, + struct digital_data_exch *data_exch, u8 rtox) +{ + struct digital_dep_req_res *dep_req; + struct sk_buff *skb; + int rc; + + skb = digital_skb_alloc(ddev, 1); + if (!skb) + return -ENOMEM; + + *skb_put(skb, 1) = rtox; + + skb_push(skb, sizeof(struct digital_dep_req_res)); + + dep_req = (struct digital_dep_req_res *)skb->data; + + dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; + dep_req->cmd = DIGITAL_CMD_DEP_REQ; + dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU | + DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT; + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, + data_exch); + + return rc; +} + +static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct digital_data_exch *data_exch = arg; + struct digital_dep_req_res *dep_res; + u8 pfb; + uint size; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + rc = ddev->skb_check_crc(resp); + if (rc) { + PROTOCOL_ERR("14.4.1.6"); + goto error; + } + + rc = digital_skb_pull_dep_sod(ddev, resp); + if (rc) { + PROTOCOL_ERR("14.4.1.2"); + goto exit; + } + + dep_res = (struct digital_dep_req_res *)resp->data; + + if (resp->len < sizeof(struct digital_dep_req_res) || + dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN || + dep_res->cmd != DIGITAL_CMD_DEP_RES) { + rc = -EIO; + goto error; + } + + pfb = dep_res->pfb; + + switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) { + case DIGITAL_NFC_DEP_PFB_I_PDU: + if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) { + PROTOCOL_ERR("14.12.3.3"); + rc = -EIO; + goto error; + } + + ddev->curr_nfc_dep_pni = + DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1); + rc = 0; + break; + + case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: + pr_err("Received a ACK/NACK PDU\n"); + rc = -EIO; + goto error; + + case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: + if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { + rc = -EINVAL; + goto error; + } + + rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]); + if (rc) + goto error; + + kfree_skb(resp); + return; + } + + if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) { + pr_err("MI bit set. Chained PDU not supported\n"); + rc = -EIO; + goto error; + } + + size = sizeof(struct digital_dep_req_res); + + if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) + size++; + + if (size > resp->len) { + rc = -EIO; + goto error; + } + + skb_pull(resp, size); + +exit: + data_exch->cb(data_exch->cb_context, resp, rc); + +error: + kfree(data_exch); + + if (rc) + kfree_skb(resp); +} + +int digital_in_send_dep_req(struct nfc_digital_dev *ddev, + struct nfc_target *target, struct sk_buff *skb, + struct digital_data_exch *data_exch) +{ + struct digital_dep_req_res *dep_req; + + skb_push(skb, sizeof(struct digital_dep_req_res)); + + dep_req = (struct digital_dep_req_res *)skb->data; + dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT; + dep_req->cmd = DIGITAL_CMD_DEP_REQ; + dep_req->pfb = ddev->curr_nfc_dep_pni; + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res, + data_exch); +} + +static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + int rc; + struct digital_dep_req_res *dep_req; + size_t size; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + rc = ddev->skb_check_crc(resp); + if (rc) { + PROTOCOL_ERR("14.4.1.6"); + goto exit; + } + + rc = digital_skb_pull_dep_sod(ddev, resp); + if (rc) { + PROTOCOL_ERR("14.4.1.2"); + goto exit; + } + + size = sizeof(struct digital_dep_req_res); + dep_req = (struct digital_dep_req_res *)resp->data; + + if (resp->len < size || dep_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT || + dep_req->cmd != DIGITAL_CMD_DEP_REQ) { + rc = -EIO; + goto exit; + } + + if (DIGITAL_NFC_DEP_DID_BIT_SET(dep_req->pfb)) + size++; + + if (resp->len < size) { + rc = -EIO; + goto exit; + } + + switch (DIGITAL_NFC_DEP_PFB_TYPE(dep_req->pfb)) { + case DIGITAL_NFC_DEP_PFB_I_PDU: + pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n"); + ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(dep_req->pfb); + break; + case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU: + pr_err("Received a ACK/NACK PDU\n"); + rc = -EINVAL; + goto exit; + break; + case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU: + pr_err("Received a SUPERVISOR PDU\n"); + rc = -EINVAL; + goto exit; + break; + } + + skb_pull(resp, size); + + rc = nfc_tm_data_received(ddev->nfc_dev, resp); + +exit: + if (rc) + kfree_skb(resp); +} + +int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb) +{ + struct digital_dep_req_res *dep_res; + + skb_push(skb, sizeof(struct digital_dep_req_res)); + dep_res = (struct digital_dep_req_res *)skb->data; + + dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; + dep_res->cmd = DIGITAL_CMD_DEP_RES; + dep_res->pfb = ddev->curr_nfc_dep_pni; + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + return digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req, + NULL); +} + +static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev, + void *arg, struct sk_buff *resp) +{ + u8 rf_tech = PTR_ERR(arg); + + if (IS_ERR(resp)) + return; + + digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); + + digital_tg_listen(ddev, 1500, digital_tg_recv_dep_req, NULL); + + dev_kfree_skb(resp); +} + +static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did, + u8 rf_tech) +{ + struct digital_psl_res *psl_res; + struct sk_buff *skb; + int rc; + + skb = digital_skb_alloc(ddev, sizeof(struct digital_psl_res)); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(struct digital_psl_res)); + + psl_res = (struct digital_psl_res *)skb->data; + + psl_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; + psl_res->cmd = DIGITAL_CMD_PSL_RES; + psl_res->did = did; + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete, + ERR_PTR(rf_tech)); + + if (rc) + kfree_skb(skb); + + return rc; +} + +static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + int rc; + struct digital_psl_req *psl_req; + u8 rf_tech; + u8 dsi; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + rc = ddev->skb_check_crc(resp); + if (rc) { + PROTOCOL_ERR("14.4.1.6"); + goto exit; + } + + rc = digital_skb_pull_dep_sod(ddev, resp); + if (rc) { + PROTOCOL_ERR("14.4.1.2"); + goto exit; + } + + psl_req = (struct digital_psl_req *)resp->data; + + if (resp->len != sizeof(struct digital_psl_req) || + psl_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT || + psl_req->cmd != DIGITAL_CMD_PSL_REQ) { + rc = -EIO; + goto exit; + } + + dsi = (psl_req->brs >> 3) & 0x07; + switch (dsi) { + case 0: + rf_tech = NFC_DIGITAL_RF_TECH_106A; + break; + case 1: + rf_tech = NFC_DIGITAL_RF_TECH_212F; + break; + case 2: + rf_tech = NFC_DIGITAL_RF_TECH_424F; + break; + default: + pr_err("Unsuported dsi value %d\n", dsi); + goto exit; + } + + rc = digital_tg_send_psl_res(ddev, psl_req->did, rf_tech); + +exit: + kfree_skb(resp); +} + +static void digital_tg_send_atr_res_complete(struct nfc_digital_dev *ddev, + void *arg, struct sk_buff *resp) +{ + int offset; + + if (IS_ERR(resp)) { + digital_poll_next_tech(ddev); + return; + } + + offset = 2; + if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) + offset++; + + if (resp->data[offset] == DIGITAL_CMD_PSL_REQ) + digital_tg_recv_psl_req(ddev, arg, resp); + else + digital_tg_recv_dep_req(ddev, arg, resp); +} + +static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev, + struct digital_atr_req *atr_req) +{ + struct digital_atr_res *atr_res; + struct sk_buff *skb; + u8 *gb; + size_t gb_len; + int rc; + + gb = nfc_get_local_general_bytes(ddev->nfc_dev, &gb_len); + if (!gb) + gb_len = 0; + + skb = digital_skb_alloc(ddev, sizeof(struct digital_atr_res) + gb_len); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(struct digital_atr_res)); + atr_res = (struct digital_atr_res *)skb->data; + + memset(atr_res, 0, sizeof(struct digital_atr_res)); + + atr_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN; + atr_res->cmd = DIGITAL_CMD_ATR_RES; + memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3)); + atr_res->to = 8; + atr_res->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B; + if (gb_len) { + skb_put(skb, gb_len); + + atr_res->pp |= DIGITAL_GB_BIT; + memcpy(atr_res->gb, gb, gb_len); + } + + digital_skb_push_dep_sod(ddev, skb); + + ddev->skb_add_crc(skb); + + rc = digital_tg_send_cmd(ddev, skb, 999, + digital_tg_send_atr_res_complete, NULL); + if (rc) { + kfree_skb(skb); + return rc; + } + + return rc; +} + +void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + int rc; + struct digital_atr_req *atr_req; + size_t gb_len, min_size; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (!resp->len) { + rc = -EIO; + goto exit; + } + + if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB) { + min_size = DIGITAL_ATR_REQ_MIN_SIZE + 2; + + ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_106A; + ddev->skb_add_crc = digital_skb_add_crc_a; + ddev->skb_check_crc = digital_skb_check_crc_a; + } else { + min_size = DIGITAL_ATR_REQ_MIN_SIZE + 1; + + ddev->curr_rf_tech = NFC_DIGITAL_RF_TECH_212F; + ddev->skb_add_crc = digital_skb_add_crc_f; + ddev->skb_check_crc = digital_skb_check_crc_f; + } + + if (resp->len < min_size) { + rc = -EIO; + goto exit; + } + + if (DIGITAL_DRV_CAPS_TG_CRC(ddev)) { + ddev->skb_add_crc = digital_skb_add_crc_none; + ddev->skb_check_crc = digital_skb_check_crc_none; + } + + rc = ddev->skb_check_crc(resp); + if (rc) { + PROTOCOL_ERR("14.4.1.6"); + goto exit; + } + + rc = digital_skb_pull_dep_sod(ddev, resp); + if (rc) { + PROTOCOL_ERR("14.4.1.2"); + goto exit; + } + + atr_req = (struct digital_atr_req *)resp->data; + + if (atr_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT || + atr_req->cmd != DIGITAL_CMD_ATR_REQ) { + rc = -EINVAL; + goto exit; + } + + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED); + if (rc) + goto exit; + + rc = digital_tg_send_atr_res(ddev, atr_req); + if (rc) + goto exit; + + gb_len = resp->len - sizeof(struct digital_atr_req); + rc = nfc_tm_activated(ddev->nfc_dev, NFC_PROTO_NFC_DEP_MASK, + NFC_COMM_PASSIVE, atr_req->gb, gb_len); + if (rc) + goto exit; + + ddev->poll_tech_count = 0; + + rc = 0; +exit: + if (rc) + digital_poll_next_tech(ddev); + + dev_kfree_skb(resp); +} diff --git a/net/nfc/digital_technology.c b/net/nfc/digital_technology.c new file mode 100644 index 0000000000000000000000000000000000000000..251c8c753ebe0f48be67beadff61ec10b8a843b1 --- /dev/null +++ b/net/nfc/digital_technology.c @@ -0,0 +1,770 @@ +/* + * NFC Digital Protocol stack + * Copyright (c) 2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#define pr_fmt(fmt) "digital: %s: " fmt, __func__ + +#include "digital.h" + +#define DIGITAL_CMD_SENS_REQ 0x26 +#define DIGITAL_CMD_ALL_REQ 0x52 +#define DIGITAL_CMD_SEL_REQ_CL1 0x93 +#define DIGITAL_CMD_SEL_REQ_CL2 0x95 +#define DIGITAL_CMD_SEL_REQ_CL3 0x97 + +#define DIGITAL_SDD_REQ_SEL_PAR 0x20 + +#define DIGITAL_SDD_RES_CT 0x88 +#define DIGITAL_SDD_RES_LEN 5 + +#define DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res) (!((sel_res) & 0x04)) +#define DIGITAL_SEL_RES_IS_T2T(sel_res) (!((sel_res) & 0x60)) +#define DIGITAL_SEL_RES_IS_NFC_DEP(sel_res) ((sel_res) & 0x40) + +#define DIGITAL_SENS_RES_IS_T1T(sens_res) (((sens_res) & 0x0C00) == 0x0C00) +#define DIGITAL_SENS_RES_IS_VALID(sens_res) \ + ((!((sens_res) & 0x001F) && (((sens_res) & 0x0C00) == 0x0C00)) || \ + (((sens_res) & 0x001F) && ((sens_res) & 0x0C00) != 0x0C00)) + +#define DIGITAL_MIFARE_READ_RES_LEN 16 +#define DIGITAL_MIFARE_ACK_RES 0x0A + +#define DIGITAL_CMD_SENSF_REQ 0x00 +#define DIGITAL_CMD_SENSF_RES 0x01 + +#define DIGITAL_SENSF_RES_MIN_LENGTH 17 +#define DIGITAL_SENSF_RES_RD_AP_B1 0x00 +#define DIGITAL_SENSF_RES_RD_AP_B2 0x8F + +#define DIGITAL_SENSF_REQ_RC_NONE 0 +#define DIGITAL_SENSF_REQ_RC_SC 1 +#define DIGITAL_SENSF_REQ_RC_AP 2 + +struct digital_sdd_res { + u8 nfcid1[4]; + u8 bcc; +} __packed; + +struct digital_sel_req { + u8 sel_cmd; + u8 b2; + u8 nfcid1[4]; + u8 bcc; +} __packed; + +struct digital_sensf_req { + u8 cmd; + u8 sc1; + u8 sc2; + u8 rc; + u8 tsn; +} __packed; + +struct digital_sensf_res { + u8 cmd; + u8 nfcid2[8]; + u8 pad0[2]; + u8 pad1[3]; + u8 mrti_check; + u8 mrti_update; + u8 pad2; + u8 rd[2]; +} __packed; + +static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev, + struct nfc_target *target); + +static void digital_in_recv_sel_res(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct nfc_target *target = arg; + int rc; + u8 sel_res; + u8 nfc_proto; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) { + rc = digital_skb_check_crc_a(resp); + if (rc) { + PROTOCOL_ERR("4.4.1.3"); + goto exit; + } + } + + if (!resp->len) { + rc = -EIO; + goto exit; + } + + sel_res = resp->data[0]; + + if (!DIGITAL_SEL_RES_NFCID1_COMPLETE(sel_res)) { + rc = digital_in_send_sdd_req(ddev, target); + if (rc) + goto exit; + + goto exit_free_skb; + } + + if (DIGITAL_SEL_RES_IS_T2T(sel_res)) { + nfc_proto = NFC_PROTO_MIFARE; + } else if (DIGITAL_SEL_RES_IS_NFC_DEP(sel_res)) { + nfc_proto = NFC_PROTO_NFC_DEP; + } else { + rc = -EOPNOTSUPP; + goto exit; + } + + target->sel_res = sel_res; + + rc = digital_target_found(ddev, target, nfc_proto); + +exit: + kfree(target); + +exit_free_skb: + dev_kfree_skb(resp); + + if (rc) + digital_poll_next_tech(ddev); +} + +static int digital_in_send_sel_req(struct nfc_digital_dev *ddev, + struct nfc_target *target, + struct digital_sdd_res *sdd_res) +{ + struct sk_buff *skb; + struct digital_sel_req *sel_req; + u8 sel_cmd; + int rc; + + skb = digital_skb_alloc(ddev, sizeof(struct digital_sel_req)); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(struct digital_sel_req)); + sel_req = (struct digital_sel_req *)skb->data; + + if (target->nfcid1_len <= 4) + sel_cmd = DIGITAL_CMD_SEL_REQ_CL1; + else if (target->nfcid1_len < 10) + sel_cmd = DIGITAL_CMD_SEL_REQ_CL2; + else + sel_cmd = DIGITAL_CMD_SEL_REQ_CL3; + + sel_req->sel_cmd = sel_cmd; + sel_req->b2 = 0x70; + memcpy(sel_req->nfcid1, sdd_res->nfcid1, 4); + sel_req->bcc = sdd_res->bcc; + + if (DIGITAL_DRV_CAPS_IN_CRC(ddev)) { + rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_STANDARD_WITH_CRC_A); + if (rc) + goto exit; + } else { + digital_skb_add_crc_a(skb); + } + + rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sel_res, + target); +exit: + if (rc) + kfree_skb(skb); + + return rc; +} + +static void digital_in_recv_sdd_res(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct nfc_target *target = arg; + struct digital_sdd_res *sdd_res; + int rc; + u8 offset, size; + u8 i, bcc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (resp->len < DIGITAL_SDD_RES_LEN) { + PROTOCOL_ERR("4.7.2.8"); + rc = -EINVAL; + goto exit; + } + + sdd_res = (struct digital_sdd_res *)resp->data; + + for (i = 0, bcc = 0; i < 4; i++) + bcc ^= sdd_res->nfcid1[i]; + + if (bcc != sdd_res->bcc) { + PROTOCOL_ERR("4.7.2.6"); + rc = -EINVAL; + goto exit; + } + + if (sdd_res->nfcid1[0] == DIGITAL_SDD_RES_CT) { + offset = 1; + size = 3; + } else { + offset = 0; + size = 4; + } + + memcpy(target->nfcid1 + target->nfcid1_len, sdd_res->nfcid1 + offset, + size); + target->nfcid1_len += size; + + rc = digital_in_send_sel_req(ddev, target, sdd_res); + +exit: + dev_kfree_skb(resp); + + if (rc) { + kfree(target); + digital_poll_next_tech(ddev); + } +} + +static int digital_in_send_sdd_req(struct nfc_digital_dev *ddev, + struct nfc_target *target) +{ + int rc; + struct sk_buff *skb; + u8 sel_cmd; + + rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_STANDARD); + if (rc) + return rc; + + skb = digital_skb_alloc(ddev, 2); + if (!skb) + return -ENOMEM; + + if (target->nfcid1_len == 0) + sel_cmd = DIGITAL_CMD_SEL_REQ_CL1; + else if (target->nfcid1_len == 3) + sel_cmd = DIGITAL_CMD_SEL_REQ_CL2; + else + sel_cmd = DIGITAL_CMD_SEL_REQ_CL3; + + *skb_put(skb, sizeof(u8)) = sel_cmd; + *skb_put(skb, sizeof(u8)) = DIGITAL_SDD_REQ_SEL_PAR; + + return digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sdd_res, + target); +} + +static void digital_in_recv_sens_res(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct nfc_target *target = NULL; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (resp->len < sizeof(u16)) { + rc = -EIO; + goto exit; + } + + target = kzalloc(sizeof(struct nfc_target), GFP_KERNEL); + if (!target) { + rc = -ENOMEM; + goto exit; + } + + target->sens_res = __le16_to_cpu(*(__le16 *)resp->data); + + if (!DIGITAL_SENS_RES_IS_VALID(target->sens_res)) { + PROTOCOL_ERR("4.6.3.3"); + rc = -EINVAL; + goto exit; + } + + if (DIGITAL_SENS_RES_IS_T1T(target->sens_res)) + rc = digital_target_found(ddev, target, NFC_PROTO_JEWEL); + else + rc = digital_in_send_sdd_req(ddev, target); + +exit: + dev_kfree_skb(resp); + + if (rc) { + kfree(target); + digital_poll_next_tech(ddev); + } +} + +int digital_in_send_sens_req(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + struct sk_buff *skb; + int rc; + + rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, + NFC_DIGITAL_RF_TECH_106A); + if (rc) + return rc; + + rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_SHORT); + if (rc) + return rc; + + skb = digital_skb_alloc(ddev, 1); + if (!skb) + return -ENOMEM; + + *skb_put(skb, sizeof(u8)) = DIGITAL_CMD_SENS_REQ; + + rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sens_res, NULL); + if (rc) + kfree_skb(skb); + + return rc; +} + +int digital_in_recv_mifare_res(struct sk_buff *resp) +{ + /* Successful READ command response is 16 data bytes + 2 CRC bytes long. + * Since the driver can't differentiate a ACK/NACK response from a valid + * READ response, the CRC calculation must be handled at digital level + * even if the driver supports it for this technology. + */ + if (resp->len == DIGITAL_MIFARE_READ_RES_LEN + DIGITAL_CRC_LEN) { + if (digital_skb_check_crc_a(resp)) { + PROTOCOL_ERR("9.4.1.2"); + return -EIO; + } + + return 0; + } + + /* ACK response (i.e. successful WRITE). */ + if (resp->len == 1 && resp->data[0] == DIGITAL_MIFARE_ACK_RES) { + resp->data[0] = 0; + return 0; + } + + /* NACK and any other responses are treated as error. */ + return -EIO; +} + +static void digital_in_recv_sensf_res(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + int rc; + u8 proto; + struct nfc_target target; + struct digital_sensf_res *sensf_res; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (resp->len < DIGITAL_SENSF_RES_MIN_LENGTH) { + rc = -EIO; + goto exit; + } + + if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) { + rc = digital_skb_check_crc_f(resp); + if (rc) { + PROTOCOL_ERR("6.4.1.8"); + goto exit; + } + } + + skb_pull(resp, 1); + + memset(&target, 0, sizeof(struct nfc_target)); + + sensf_res = (struct digital_sensf_res *)resp->data; + + memcpy(target.sensf_res, sensf_res, resp->len); + target.sensf_res_len = resp->len; + + memcpy(target.nfcid2, sensf_res->nfcid2, NFC_NFCID2_MAXSIZE); + target.nfcid2_len = NFC_NFCID2_MAXSIZE; + + if (target.nfcid2[0] == DIGITAL_SENSF_NFCID2_NFC_DEP_B1 && + target.nfcid2[1] == DIGITAL_SENSF_NFCID2_NFC_DEP_B2) + proto = NFC_PROTO_NFC_DEP; + else + proto = NFC_PROTO_FELICA; + + rc = digital_target_found(ddev, &target, proto); + +exit: + dev_kfree_skb(resp); + + if (rc) + digital_poll_next_tech(ddev); +} + +int digital_in_send_sensf_req(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + struct digital_sensf_req *sensf_req; + struct sk_buff *skb; + int rc; + u8 size; + + rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); + if (rc) + return rc; + + rc = digital_in_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCF); + if (rc) + return rc; + + size = sizeof(struct digital_sensf_req); + + skb = digital_skb_alloc(ddev, size); + if (!skb) + return -ENOMEM; + + skb_put(skb, size); + + sensf_req = (struct digital_sensf_req *)skb->data; + sensf_req->cmd = DIGITAL_CMD_SENSF_REQ; + sensf_req->sc1 = 0xFF; + sensf_req->sc2 = 0xFF; + sensf_req->rc = 0; + sensf_req->tsn = 0; + + *skb_push(skb, 1) = size + 1; + + if (!DIGITAL_DRV_CAPS_IN_CRC(ddev)) + digital_skb_add_crc_f(skb); + + rc = digital_in_send_cmd(ddev, skb, 30, digital_in_recv_sensf_res, + NULL); + if (rc) + kfree_skb(skb); + + return rc; +} + +static int digital_tg_send_sel_res(struct nfc_digital_dev *ddev) +{ + struct sk_buff *skb; + int rc; + + skb = digital_skb_alloc(ddev, 1); + if (!skb) + return -ENOMEM; + + *skb_put(skb, 1) = DIGITAL_SEL_RES_NFC_DEP; + + if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) + digital_skb_add_crc_a(skb); + + rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_atr_req, + NULL); + if (rc) + kfree_skb(skb); + + return rc; +} + +static void digital_tg_recv_sel_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) { + rc = digital_skb_check_crc_a(resp); + if (rc) { + PROTOCOL_ERR("4.4.1.3"); + goto exit; + } + } + + /* Silently ignore SEL_REQ content and send a SEL_RES for NFC-DEP */ + + rc = digital_tg_send_sel_res(ddev); + +exit: + if (rc) + digital_poll_next_tech(ddev); + + dev_kfree_skb(resp); +} + +static int digital_tg_send_sdd_res(struct nfc_digital_dev *ddev) +{ + struct sk_buff *skb; + struct digital_sdd_res *sdd_res; + int rc, i; + + skb = digital_skb_alloc(ddev, sizeof(struct digital_sdd_res)); + if (!skb) + return -ENOMEM; + + skb_put(skb, sizeof(struct digital_sdd_res)); + sdd_res = (struct digital_sdd_res *)skb->data; + + sdd_res->nfcid1[0] = 0x08; + get_random_bytes(sdd_res->nfcid1 + 1, 3); + + sdd_res->bcc = 0; + for (i = 0; i < 4; i++) + sdd_res->bcc ^= sdd_res->nfcid1[i]; + + rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sel_req, + NULL); + if (rc) + kfree_skb(skb); + + return rc; +} + +static void digital_tg_recv_sdd_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + u8 *sdd_req; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + sdd_req = resp->data; + + if (resp->len < 2 || sdd_req[0] != DIGITAL_CMD_SEL_REQ_CL1 || + sdd_req[1] != DIGITAL_SDD_REQ_SEL_PAR) { + rc = -EINVAL; + goto exit; + } + + rc = digital_tg_send_sdd_res(ddev); + +exit: + if (rc) + digital_poll_next_tech(ddev); + + dev_kfree_skb(resp); +} + +static int digital_tg_send_sens_res(struct nfc_digital_dev *ddev) +{ + struct sk_buff *skb; + u8 *sens_res; + int rc; + + skb = digital_skb_alloc(ddev, 2); + if (!skb) + return -ENOMEM; + + sens_res = skb_put(skb, 2); + + sens_res[0] = (DIGITAL_SENS_RES_NFC_DEP >> 8) & 0xFF; + sens_res[1] = DIGITAL_SENS_RES_NFC_DEP & 0xFF; + + rc = digital_tg_send_cmd(ddev, skb, 300, digital_tg_recv_sdd_req, + NULL); + if (rc) + kfree_skb(skb); + + return rc; +} + +void digital_tg_recv_sens_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + u8 sens_req; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + sens_req = resp->data[0]; + + if (!resp->len || (sens_req != DIGITAL_CMD_SENS_REQ && + sens_req != DIGITAL_CMD_ALL_REQ)) { + rc = -EINVAL; + goto exit; + } + + rc = digital_tg_send_sens_res(ddev); + +exit: + if (rc) + digital_poll_next_tech(ddev); + + dev_kfree_skb(resp); +} + +static int digital_tg_send_sensf_res(struct nfc_digital_dev *ddev, + struct digital_sensf_req *sensf_req) +{ + struct sk_buff *skb; + u8 size; + int rc; + struct digital_sensf_res *sensf_res; + + size = sizeof(struct digital_sensf_res); + + if (sensf_req->rc != DIGITAL_SENSF_REQ_RC_NONE) + size -= sizeof(sensf_res->rd); + + skb = digital_skb_alloc(ddev, size); + if (!skb) + return -ENOMEM; + + skb_put(skb, size); + + sensf_res = (struct digital_sensf_res *)skb->data; + + memset(sensf_res, 0, size); + + sensf_res->cmd = DIGITAL_CMD_SENSF_RES; + sensf_res->nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1; + sensf_res->nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2; + get_random_bytes(&sensf_res->nfcid2[2], 6); + + switch (sensf_req->rc) { + case DIGITAL_SENSF_REQ_RC_SC: + sensf_res->rd[0] = sensf_req->sc1; + sensf_res->rd[1] = sensf_req->sc2; + break; + case DIGITAL_SENSF_REQ_RC_AP: + sensf_res->rd[0] = DIGITAL_SENSF_RES_RD_AP_B1; + sensf_res->rd[1] = DIGITAL_SENSF_RES_RD_AP_B2; + break; + } + + *skb_push(skb, sizeof(u8)) = size + 1; + + if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) + digital_skb_add_crc_f(skb); + + rc = digital_tg_send_cmd(ddev, skb, 300, + digital_tg_recv_atr_req, NULL); + if (rc) + kfree_skb(skb); + + return rc; +} + +void digital_tg_recv_sensf_req(struct nfc_digital_dev *ddev, void *arg, + struct sk_buff *resp) +{ + struct digital_sensf_req *sensf_req; + int rc; + + if (IS_ERR(resp)) { + rc = PTR_ERR(resp); + resp = NULL; + goto exit; + } + + if (!DIGITAL_DRV_CAPS_TG_CRC(ddev)) { + rc = digital_skb_check_crc_f(resp); + if (rc) { + PROTOCOL_ERR("6.4.1.8"); + goto exit; + } + } + + if (resp->len != sizeof(struct digital_sensf_req) + 1) { + rc = -EINVAL; + goto exit; + } + + skb_pull(resp, 1); + sensf_req = (struct digital_sensf_req *)resp->data; + + if (sensf_req->cmd != DIGITAL_CMD_SENSF_REQ) { + rc = -EINVAL; + goto exit; + } + + rc = digital_tg_send_sensf_res(ddev, sensf_req); + +exit: + if (rc) + digital_poll_next_tech(ddev); + + dev_kfree_skb(resp); +} + +int digital_tg_listen_nfca(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + int rc; + + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); + if (rc) + return rc; + + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCA_NFC_DEP); + if (rc) + return rc; + + return digital_tg_listen(ddev, 300, digital_tg_recv_sens_req, NULL); +} + +int digital_tg_listen_nfcf(struct nfc_digital_dev *ddev, u8 rf_tech) +{ + int rc; + u8 *nfcid2; + + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_RF_TECH, rf_tech); + if (rc) + return rc; + + rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING, + NFC_DIGITAL_FRAMING_NFCF_NFC_DEP); + if (rc) + return rc; + + nfcid2 = kzalloc(NFC_NFCID2_MAXSIZE, GFP_KERNEL); + if (!nfcid2) + return -ENOMEM; + + nfcid2[0] = DIGITAL_SENSF_NFCID2_NFC_DEP_B1; + nfcid2[1] = DIGITAL_SENSF_NFCID2_NFC_DEP_B2; + get_random_bytes(nfcid2 + 2, NFC_NFCID2_MAXSIZE - 2); + + return digital_tg_listen(ddev, 300, digital_tg_recv_sensf_req, nfcid2); +} diff --git a/net/nfc/nci/spi.c b/net/nfc/nci/spi.c index c7cf37ba729819ab49c22f5b462320f633cdab5c..f1d426f10cceac10b0befc9f1c98caa6ad5b7793 100644 --- a/net/nfc/nci/spi.c +++ b/net/nfc/nci/spi.c @@ -21,11 +21,8 @@ #include #include #include -#include #include -#define NCI_SPI_HDR_LEN 4 -#define NCI_SPI_CRC_LEN 2 #define NCI_SPI_ACK_SHIFT 6 #define NCI_SPI_MSB_PAYLOAD_MASK 0x3F @@ -41,54 +38,48 @@ #define CRC_INIT 0xFFFF -static int nci_spi_open(struct nci_dev *nci_dev) -{ - struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev); - - return ndev->ops->open(ndev); -} - -static int nci_spi_close(struct nci_dev *nci_dev) -{ - struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev); - - return ndev->ops->close(ndev); -} - -static int __nci_spi_send(struct nci_spi_dev *ndev, struct sk_buff *skb) +static int __nci_spi_send(struct nci_spi *nspi, struct sk_buff *skb, + int cs_change) { struct spi_message m; struct spi_transfer t; - t.tx_buf = skb->data; - t.len = skb->len; - t.cs_change = 0; - t.delay_usecs = ndev->xfer_udelay; + memset(&t, 0, sizeof(struct spi_transfer)); + /* a NULL skb means we just want the SPI chip select line to raise */ + if (skb) { + t.tx_buf = skb->data; + t.len = skb->len; + } else { + /* still set tx_buf non NULL to make the driver happy */ + t.tx_buf = &t; + t.len = 0; + } + t.cs_change = cs_change; + t.delay_usecs = nspi->xfer_udelay; spi_message_init(&m); spi_message_add_tail(&t, &m); - return spi_sync(ndev->spi, &m); + return spi_sync(nspi->spi, &m); } -static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb) +int nci_spi_send(struct nci_spi *nspi, + struct completion *write_handshake_completion, + struct sk_buff *skb) { - struct nci_spi_dev *ndev = nci_get_drvdata(nci_dev); unsigned int payload_len = skb->len; unsigned char *hdr; int ret; long completion_rc; - ndev->ops->deassert_int(ndev); - /* add the NCI SPI header to the start of the buffer */ hdr = skb_push(skb, NCI_SPI_HDR_LEN); hdr[0] = NCI_SPI_DIRECT_WRITE; - hdr[1] = ndev->acknowledge_mode; + hdr[1] = nspi->acknowledge_mode; hdr[2] = payload_len >> 8; hdr[3] = payload_len & 0xFF; - if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) { + if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) { u16 crc; crc = crc_ccitt(CRC_INIT, skb->data, skb->len); @@ -96,123 +87,77 @@ static int nci_spi_send(struct nci_dev *nci_dev, struct sk_buff *skb) *skb_put(skb, 1) = crc & 0xFF; } - ret = __nci_spi_send(ndev, skb); + if (write_handshake_completion) { + /* Trick SPI driver to raise chip select */ + ret = __nci_spi_send(nspi, NULL, 1); + if (ret) + goto done; - kfree_skb(skb); - ndev->ops->assert_int(ndev); + /* wait for NFC chip hardware handshake to complete */ + if (wait_for_completion_timeout(write_handshake_completion, + msecs_to_jiffies(1000)) == 0) { + ret = -ETIME; + goto done; + } + } - if (ret != 0 || ndev->acknowledge_mode == NCI_SPI_CRC_DISABLED) + ret = __nci_spi_send(nspi, skb, 0); + if (ret != 0 || nspi->acknowledge_mode == NCI_SPI_CRC_DISABLED) goto done; - init_completion(&ndev->req_completion); - completion_rc = - wait_for_completion_interruptible_timeout(&ndev->req_completion, - NCI_SPI_SEND_TIMEOUT); + init_completion(&nspi->req_completion); + completion_rc = wait_for_completion_interruptible_timeout( + &nspi->req_completion, + NCI_SPI_SEND_TIMEOUT); - if (completion_rc <= 0 || ndev->req_result == ACKNOWLEDGE_NACK) + if (completion_rc <= 0 || nspi->req_result == ACKNOWLEDGE_NACK) ret = -EIO; done: + kfree_skb(skb); + return ret; } - -static struct nci_ops nci_spi_ops = { - .open = nci_spi_open, - .close = nci_spi_close, - .send = nci_spi_send, -}; +EXPORT_SYMBOL_GPL(nci_spi_send); /* ---- Interface to NCI SPI drivers ---- */ /** - * nci_spi_allocate_device - allocate a new nci spi device + * nci_spi_allocate_spi - allocate a new nci spi * * @spi: SPI device - * @ops: device operations - * @supported_protocols: NFC protocols supported by the device - * @supported_se: NFC Secure Elements supported by the device - * @acknowledge_mode: Acknowledge mode used by the device + * @acknowledge_mode: Acknowledge mode used by the NFC device * @delay: delay between transactions in us + * @ndev: nci dev to send incoming nci frames to */ -struct nci_spi_dev *nci_spi_allocate_device(struct spi_device *spi, - struct nci_spi_ops *ops, - u32 supported_protocols, - u32 supported_se, - u8 acknowledge_mode, - unsigned int delay) +struct nci_spi *nci_spi_allocate_spi(struct spi_device *spi, + u8 acknowledge_mode, unsigned int delay, + struct nci_dev *ndev) { - struct nci_spi_dev *ndev; - int tailroom = 0; + struct nci_spi *nspi; - if (!ops->open || !ops->close || !ops->assert_int || !ops->deassert_int) + nspi = devm_kzalloc(&spi->dev, sizeof(struct nci_spi), GFP_KERNEL); + if (!nspi) return NULL; - if (!supported_protocols) - return NULL; - - ndev = devm_kzalloc(&spi->dev, sizeof(struct nci_dev), GFP_KERNEL); - if (!ndev) - return NULL; + nspi->acknowledge_mode = acknowledge_mode; + nspi->xfer_udelay = delay; - ndev->ops = ops; - ndev->acknowledge_mode = acknowledge_mode; - ndev->xfer_udelay = delay; + nspi->spi = spi; + nspi->ndev = ndev; - if (acknowledge_mode == NCI_SPI_CRC_ENABLED) - tailroom += NCI_SPI_CRC_LEN; - - ndev->nci_dev = nci_allocate_device(&nci_spi_ops, supported_protocols, - NCI_SPI_HDR_LEN, tailroom); - if (!ndev->nci_dev) - return NULL; - - nci_set_drvdata(ndev->nci_dev, ndev); - - return ndev; + return nspi; } -EXPORT_SYMBOL_GPL(nci_spi_allocate_device); +EXPORT_SYMBOL_GPL(nci_spi_allocate_spi); -/** - * nci_spi_free_device - deallocate nci spi device - * - * @ndev: The nci spi device to deallocate - */ -void nci_spi_free_device(struct nci_spi_dev *ndev) -{ - nci_free_device(ndev->nci_dev); -} -EXPORT_SYMBOL_GPL(nci_spi_free_device); - -/** - * nci_spi_register_device - register a nci spi device in the nfc subsystem - * - * @pdev: The nci spi device to register - */ -int nci_spi_register_device(struct nci_spi_dev *ndev) -{ - return nci_register_device(ndev->nci_dev); -} -EXPORT_SYMBOL_GPL(nci_spi_register_device); - -/** - * nci_spi_unregister_device - unregister a nci spi device in the nfc subsystem - * - * @dev: The nci spi device to unregister - */ -void nci_spi_unregister_device(struct nci_spi_dev *ndev) -{ - nci_unregister_device(ndev->nci_dev); -} -EXPORT_SYMBOL_GPL(nci_spi_unregister_device); - -static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge) +static int send_acknowledge(struct nci_spi *nspi, u8 acknowledge) { struct sk_buff *skb; unsigned char *hdr; u16 crc; int ret; - skb = nci_skb_alloc(ndev->nci_dev, 0, GFP_KERNEL); + skb = nci_skb_alloc(nspi->ndev, 0, GFP_KERNEL); /* add the NCI SPI header to the start of the buffer */ hdr = skb_push(skb, NCI_SPI_HDR_LEN); @@ -225,14 +170,14 @@ static int send_acknowledge(struct nci_spi_dev *ndev, u8 acknowledge) *skb_put(skb, 1) = crc >> 8; *skb_put(skb, 1) = crc & 0xFF; - ret = __nci_spi_send(ndev, skb); + ret = __nci_spi_send(nspi, skb, 0); kfree_skb(skb); return ret; } -static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev) +static struct sk_buff *__nci_spi_read(struct nci_spi *nspi) { struct sk_buff *skb; struct spi_message m; @@ -242,43 +187,49 @@ static struct sk_buff *__nci_spi_recv_frame(struct nci_spi_dev *ndev) int ret; spi_message_init(&m); + + memset(&tx, 0, sizeof(struct spi_transfer)); req[0] = NCI_SPI_DIRECT_READ; - req[1] = ndev->acknowledge_mode; + req[1] = nspi->acknowledge_mode; tx.tx_buf = req; tx.len = 2; tx.cs_change = 0; spi_message_add_tail(&tx, &m); + + memset(&rx, 0, sizeof(struct spi_transfer)); rx.rx_buf = resp_hdr; rx.len = 2; rx.cs_change = 1; spi_message_add_tail(&rx, &m); - ret = spi_sync(ndev->spi, &m); + ret = spi_sync(nspi->spi, &m); if (ret) return NULL; - if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) + if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) rx_len = ((resp_hdr[0] & NCI_SPI_MSB_PAYLOAD_MASK) << 8) + resp_hdr[1] + NCI_SPI_CRC_LEN; else rx_len = (resp_hdr[0] << 8) | resp_hdr[1]; - skb = nci_skb_alloc(ndev->nci_dev, rx_len, GFP_KERNEL); + skb = nci_skb_alloc(nspi->ndev, rx_len, GFP_KERNEL); if (!skb) return NULL; spi_message_init(&m); + + memset(&rx, 0, sizeof(struct spi_transfer)); rx.rx_buf = skb_put(skb, rx_len); rx.len = rx_len; rx.cs_change = 0; - rx.delay_usecs = ndev->xfer_udelay; + rx.delay_usecs = nspi->xfer_udelay; spi_message_add_tail(&rx, &m); - ret = spi_sync(ndev->spi, &m); + ret = spi_sync(nspi->spi, &m); if (ret) goto receive_error; - if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) { + if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) { *skb_push(skb, 1) = resp_hdr[1]; *skb_push(skb, 1) = resp_hdr[0]; } @@ -318,61 +269,53 @@ static u8 nci_spi_get_ack(struct sk_buff *skb) } /** - * nci_spi_recv_frame - receive frame from NCI SPI drivers + * nci_spi_read - read frame from NCI SPI drivers * - * @ndev: The nci spi device + * @nspi: The nci spi * Context: can sleep * * This call may only be used from a context that may sleep. The sleep * is non-interruptible, and has no timeout. * - * It returns zero on success, else a negative error code. + * It returns an allocated skb containing the frame on success, or NULL. */ -int nci_spi_recv_frame(struct nci_spi_dev *ndev) +struct sk_buff *nci_spi_read(struct nci_spi *nspi) { struct sk_buff *skb; - int ret = 0; - - ndev->ops->deassert_int(ndev); /* Retrieve frame from SPI */ - skb = __nci_spi_recv_frame(ndev); - if (!skb) { - ret = -EIO; + skb = __nci_spi_read(nspi); + if (!skb) goto done; - } - if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) { + if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) { if (!nci_spi_check_crc(skb)) { - send_acknowledge(ndev, ACKNOWLEDGE_NACK); + send_acknowledge(nspi, ACKNOWLEDGE_NACK); goto done; } /* In case of acknowledged mode: if ACK or NACK received, * unblock completion of latest frame sent. */ - ndev->req_result = nci_spi_get_ack(skb); - if (ndev->req_result) - complete(&ndev->req_completion); + nspi->req_result = nci_spi_get_ack(skb); + if (nspi->req_result) + complete(&nspi->req_completion); } /* If there is no payload (ACK/NACK only frame), * free the socket buffer */ - if (skb->len == 0) { + if (!skb->len) { kfree_skb(skb); + skb = NULL; goto done; } - if (ndev->acknowledge_mode == NCI_SPI_CRC_ENABLED) - send_acknowledge(ndev, ACKNOWLEDGE_ACK); - - /* Forward skb to NCI core layer */ - ret = nci_recv_frame(ndev->nci_dev, skb); + if (nspi->acknowledge_mode == NCI_SPI_CRC_ENABLED) + send_acknowledge(nspi, ACKNOWLEDGE_ACK); done: - ndev->ops->assert_int(ndev); - return ret; + return skb; } -EXPORT_SYMBOL_GPL(nci_spi_recv_frame); +EXPORT_SYMBOL_GPL(nci_spi_read); diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index 68063b2025da2750519dac5a652cdbbea36dac74..84b7e3ea7b7ad7ce09e9256916589e4724d55969 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -58,6 +58,7 @@ static const struct nla_policy nfc_genl_policy[NFC_ATTR_MAX + 1] = { [NFC_ATTR_LLC_SDP] = { .type = NLA_NESTED }, [NFC_ATTR_FIRMWARE_NAME] = { .type = NLA_STRING, .len = NFC_FIRMWARE_NAME_MAXSIZE }, + [NFC_ATTR_SE_APDU] = { .type = NLA_BINARY }, }; static const struct nla_policy nfc_sdp_genl_policy[NFC_SDP_ATTR_MAX + 1] = { @@ -1278,6 +1279,91 @@ static int nfc_genl_dump_ses_done(struct netlink_callback *cb) return 0; } +struct se_io_ctx { + u32 dev_idx; + u32 se_idx; +}; + +static void se_io_cb(void *context, u8 *apdu, size_t apdu_len, int err) +{ + struct se_io_ctx *ctx = context; + struct sk_buff *msg; + void *hdr; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) { + kfree(ctx); + return; + } + + hdr = genlmsg_put(msg, 0, 0, &nfc_genl_family, 0, + NFC_CMD_SE_IO); + if (!hdr) + goto free_msg; + + if (nla_put_u32(msg, NFC_ATTR_DEVICE_INDEX, ctx->dev_idx) || + nla_put_u32(msg, NFC_ATTR_SE_INDEX, ctx->se_idx) || + nla_put(msg, NFC_ATTR_SE_APDU, apdu_len, apdu)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); + + genlmsg_multicast(msg, 0, nfc_genl_event_mcgrp.id, GFP_KERNEL); + + kfree(ctx); + + return; + +nla_put_failure: + genlmsg_cancel(msg, hdr); +free_msg: + nlmsg_free(msg); + kfree(ctx); + + return; +} + +static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + struct se_io_ctx *ctx; + u32 dev_idx, se_idx; + u8 *apdu; + size_t apdu_len; + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX] || + !info->attrs[NFC_ATTR_SE_INDEX] || + !info->attrs[NFC_ATTR_SE_APDU]) + return -EINVAL; + + dev_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + se_idx = nla_get_u32(info->attrs[NFC_ATTR_SE_INDEX]); + + dev = nfc_get_device(dev_idx); + if (!dev) + return -ENODEV; + + if (!dev->ops || !dev->ops->se_io) + return -ENOTSUPP; + + apdu_len = nla_len(info->attrs[NFC_ATTR_SE_APDU]); + if (apdu_len == 0) + return -EINVAL; + + apdu = nla_data(info->attrs[NFC_ATTR_SE_APDU]); + if (!apdu) + return -EINVAL; + + ctx = kzalloc(sizeof(struct se_io_ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->dev_idx = dev_idx; + ctx->se_idx = se_idx; + + return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx); +} + static struct genl_ops nfc_genl_ops[] = { { .cmd = NFC_CMD_GET_DEVICE, @@ -1358,6 +1444,11 @@ static struct genl_ops nfc_genl_ops[] = { .done = nfc_genl_dump_ses_done, .policy = nfc_genl_policy, }, + { + .cmd = NFC_CMD_SE_IO, + .doit = nfc_genl_se_io, + .policy = nfc_genl_policy, + }, }; diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c index 313bf1bc848a8c45b755e838fc9cd595e1a833c2..cd958b381f9615911b1acfc6da6aceb39d373401 100644 --- a/net/nfc/rawsock.c +++ b/net/nfc/rawsock.c @@ -142,11 +142,11 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb, err = rawsock_add_header(skb); if (err) - goto error; + goto error_skb; err = sock_queue_rcv_skb(sk, skb); if (err) - goto error; + goto error_skb; spin_lock_bh(&sk->sk_write_queue.lock); if (!skb_queue_empty(&sk->sk_write_queue)) @@ -158,6 +158,9 @@ static void rawsock_data_exchange_complete(void *context, struct sk_buff *skb, sock_put(sk); return; +error_skb: + kfree_skb(skb); + error: rawsock_report_error(sk, err); sock_put(sk); diff --git a/net/openvswitch/Makefile b/net/openvswitch/Makefile index ea36e99089af57246c28f68a8e007d8e55c726ba..3591cb5dae9125570012c1c6dc1ab13694663db8 100644 --- a/net/openvswitch/Makefile +++ b/net/openvswitch/Makefile @@ -9,6 +9,8 @@ openvswitch-y := \ datapath.o \ dp_notify.o \ flow.o \ + flow_netlink.o \ + flow_table.o \ vport.o \ vport-internal_dev.o \ vport-netdev.o diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 2aa13bd7f2b2c53e1e999109df3b51c4326cdba1..1408adc2a2a7d2c47f4e32f71863bbd3a04cfdf2 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -55,14 +55,10 @@ #include "datapath.h" #include "flow.h" +#include "flow_netlink.h" #include "vport-internal_dev.h" #include "vport-netdev.h" - -#define REHASH_FLOW_INTERVAL (10 * 60 * HZ) -static void rehash_flow_table(struct work_struct *work); -static DECLARE_DELAYED_WORK(rehash_flow_wq, rehash_flow_table); - int ovs_net_id __read_mostly; static void ovs_notify(struct sk_buff *skb, struct genl_info *info, @@ -165,7 +161,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu) { struct datapath *dp = container_of(rcu, struct datapath, rcu); - ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, false); + ovs_flow_tbl_destroy(&dp->table); free_percpu(dp->stats_percpu); release_net(ovs_dp_get_net(dp)); kfree(dp->ports); @@ -225,6 +221,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) struct dp_stats_percpu *stats; struct sw_flow_key key; u64 *stats_counter; + u32 n_mask_hit; int error; stats = this_cpu_ptr(dp->stats_percpu); @@ -237,7 +234,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) } /* Look up flow. */ - flow = ovs_flow_lookup(rcu_dereference(dp->table), &key); + flow = ovs_flow_tbl_lookup(&dp->table, &key, &n_mask_hit); if (unlikely(!flow)) { struct dp_upcall_info upcall; @@ -262,6 +259,7 @@ out: /* Update datapath statistics. */ u64_stats_update_begin(&stats->sync); (*stats_counter)++; + stats->n_mask_hit += n_mask_hit; u64_stats_update_end(&stats->sync); } @@ -435,7 +433,7 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex, upcall->dp_ifindex = dp_ifindex; nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY); - ovs_flow_to_nlattrs(upcall_info->key, upcall_info->key, user_skb); + ovs_nla_put_flow(upcall_info->key, upcall_info->key, user_skb); nla_nest_end(user_skb, nla); if (upcall_info->userdata) @@ -455,398 +453,6 @@ out: return err; } -/* Called with ovs_mutex. */ -static int flush_flows(struct datapath *dp) -{ - struct flow_table *old_table; - struct flow_table *new_table; - - old_table = ovsl_dereference(dp->table); - new_table = ovs_flow_tbl_alloc(TBL_MIN_BUCKETS); - if (!new_table) - return -ENOMEM; - - rcu_assign_pointer(dp->table, new_table); - - ovs_flow_tbl_destroy(old_table, true); - return 0; -} - -static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, int attr_len) -{ - - struct sw_flow_actions *acts; - int new_acts_size; - int req_size = NLA_ALIGN(attr_len); - int next_offset = offsetof(struct sw_flow_actions, actions) + - (*sfa)->actions_len; - - if (req_size <= (ksize(*sfa) - next_offset)) - goto out; - - new_acts_size = ksize(*sfa) * 2; - - if (new_acts_size > MAX_ACTIONS_BUFSIZE) { - if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size) - return ERR_PTR(-EMSGSIZE); - new_acts_size = MAX_ACTIONS_BUFSIZE; - } - - acts = ovs_flow_actions_alloc(new_acts_size); - if (IS_ERR(acts)) - return (void *)acts; - - memcpy(acts->actions, (*sfa)->actions, (*sfa)->actions_len); - acts->actions_len = (*sfa)->actions_len; - kfree(*sfa); - *sfa = acts; - -out: - (*sfa)->actions_len += req_size; - return (struct nlattr *) ((unsigned char *)(*sfa) + next_offset); -} - -static int add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len) -{ - struct nlattr *a; - - a = reserve_sfa_size(sfa, nla_attr_size(len)); - if (IS_ERR(a)) - return PTR_ERR(a); - - a->nla_type = attrtype; - a->nla_len = nla_attr_size(len); - - if (data) - memcpy(nla_data(a), data, len); - memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len)); - - return 0; -} - -static inline int add_nested_action_start(struct sw_flow_actions **sfa, int attrtype) -{ - int used = (*sfa)->actions_len; - int err; - - err = add_action(sfa, attrtype, NULL, 0); - if (err) - return err; - - return used; -} - -static inline void add_nested_action_end(struct sw_flow_actions *sfa, int st_offset) -{ - struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + st_offset); - - a->nla_len = sfa->actions_len - st_offset; -} - -static int validate_and_copy_actions(const struct nlattr *attr, - const struct sw_flow_key *key, int depth, - struct sw_flow_actions **sfa); - -static int validate_and_copy_sample(const struct nlattr *attr, - const struct sw_flow_key *key, int depth, - struct sw_flow_actions **sfa) -{ - const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; - const struct nlattr *probability, *actions; - const struct nlattr *a; - int rem, start, err, st_acts; - - memset(attrs, 0, sizeof(attrs)); - nla_for_each_nested(a, attr, rem) { - int type = nla_type(a); - if (!type || type > OVS_SAMPLE_ATTR_MAX || attrs[type]) - return -EINVAL; - attrs[type] = a; - } - if (rem) - return -EINVAL; - - probability = attrs[OVS_SAMPLE_ATTR_PROBABILITY]; - if (!probability || nla_len(probability) != sizeof(u32)) - return -EINVAL; - - actions = attrs[OVS_SAMPLE_ATTR_ACTIONS]; - if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN)) - return -EINVAL; - - /* validation done, copy sample action. */ - start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE); - if (start < 0) - return start; - err = add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY, nla_data(probability), sizeof(u32)); - if (err) - return err; - st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS); - if (st_acts < 0) - return st_acts; - - err = validate_and_copy_actions(actions, key, depth + 1, sfa); - if (err) - return err; - - add_nested_action_end(*sfa, st_acts); - add_nested_action_end(*sfa, start); - - return 0; -} - -static int validate_tp_port(const struct sw_flow_key *flow_key) -{ - if (flow_key->eth.type == htons(ETH_P_IP)) { - if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst) - return 0; - } else if (flow_key->eth.type == htons(ETH_P_IPV6)) { - if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst) - return 0; - } - - return -EINVAL; -} - -static int validate_and_copy_set_tun(const struct nlattr *attr, - struct sw_flow_actions **sfa) -{ - struct sw_flow_match match; - struct sw_flow_key key; - int err, start; - - ovs_match_init(&match, &key, NULL); - err = ovs_ipv4_tun_from_nlattr(nla_data(attr), &match, false); - if (err) - return err; - - start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET); - if (start < 0) - return start; - - err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key, - sizeof(match.key->tun_key)); - add_nested_action_end(*sfa, start); - - return err; -} - -static int validate_set(const struct nlattr *a, - const struct sw_flow_key *flow_key, - struct sw_flow_actions **sfa, - bool *set_tun) -{ - const struct nlattr *ovs_key = nla_data(a); - int key_type = nla_type(ovs_key); - - /* There can be only one key in a action */ - if (nla_total_size(nla_len(ovs_key)) != nla_len(a)) - return -EINVAL; - - if (key_type > OVS_KEY_ATTR_MAX || - (ovs_key_lens[key_type] != nla_len(ovs_key) && - ovs_key_lens[key_type] != -1)) - return -EINVAL; - - switch (key_type) { - const struct ovs_key_ipv4 *ipv4_key; - const struct ovs_key_ipv6 *ipv6_key; - int err; - - case OVS_KEY_ATTR_PRIORITY: - case OVS_KEY_ATTR_SKB_MARK: - case OVS_KEY_ATTR_ETHERNET: - break; - - case OVS_KEY_ATTR_TUNNEL: - *set_tun = true; - err = validate_and_copy_set_tun(a, sfa); - if (err) - return err; - break; - - case OVS_KEY_ATTR_IPV4: - if (flow_key->eth.type != htons(ETH_P_IP)) - return -EINVAL; - - if (!flow_key->ip.proto) - return -EINVAL; - - ipv4_key = nla_data(ovs_key); - if (ipv4_key->ipv4_proto != flow_key->ip.proto) - return -EINVAL; - - if (ipv4_key->ipv4_frag != flow_key->ip.frag) - return -EINVAL; - - break; - - case OVS_KEY_ATTR_IPV6: - if (flow_key->eth.type != htons(ETH_P_IPV6)) - return -EINVAL; - - if (!flow_key->ip.proto) - return -EINVAL; - - ipv6_key = nla_data(ovs_key); - if (ipv6_key->ipv6_proto != flow_key->ip.proto) - return -EINVAL; - - if (ipv6_key->ipv6_frag != flow_key->ip.frag) - return -EINVAL; - - if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000) - return -EINVAL; - - break; - - case OVS_KEY_ATTR_TCP: - if (flow_key->ip.proto != IPPROTO_TCP) - return -EINVAL; - - return validate_tp_port(flow_key); - - case OVS_KEY_ATTR_UDP: - if (flow_key->ip.proto != IPPROTO_UDP) - return -EINVAL; - - return validate_tp_port(flow_key); - - case OVS_KEY_ATTR_SCTP: - if (flow_key->ip.proto != IPPROTO_SCTP) - return -EINVAL; - - return validate_tp_port(flow_key); - - default: - return -EINVAL; - } - - return 0; -} - -static int validate_userspace(const struct nlattr *attr) -{ - static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] = { - [OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 }, - [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC }, - }; - struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1]; - int error; - - error = nla_parse_nested(a, OVS_USERSPACE_ATTR_MAX, - attr, userspace_policy); - if (error) - return error; - - if (!a[OVS_USERSPACE_ATTR_PID] || - !nla_get_u32(a[OVS_USERSPACE_ATTR_PID])) - return -EINVAL; - - return 0; -} - -static int copy_action(const struct nlattr *from, - struct sw_flow_actions **sfa) -{ - int totlen = NLA_ALIGN(from->nla_len); - struct nlattr *to; - - to = reserve_sfa_size(sfa, from->nla_len); - if (IS_ERR(to)) - return PTR_ERR(to); - - memcpy(to, from, totlen); - return 0; -} - -static int validate_and_copy_actions(const struct nlattr *attr, - const struct sw_flow_key *key, - int depth, - struct sw_flow_actions **sfa) -{ - const struct nlattr *a; - int rem, err; - - if (depth >= SAMPLE_ACTION_DEPTH) - return -EOVERFLOW; - - nla_for_each_nested(a, attr, rem) { - /* Expected argument lengths, (u32)-1 for variable length. */ - static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { - [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32), - [OVS_ACTION_ATTR_USERSPACE] = (u32)-1, - [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan), - [OVS_ACTION_ATTR_POP_VLAN] = 0, - [OVS_ACTION_ATTR_SET] = (u32)-1, - [OVS_ACTION_ATTR_SAMPLE] = (u32)-1 - }; - const struct ovs_action_push_vlan *vlan; - int type = nla_type(a); - bool skip_copy; - - if (type > OVS_ACTION_ATTR_MAX || - (action_lens[type] != nla_len(a) && - action_lens[type] != (u32)-1)) - return -EINVAL; - - skip_copy = false; - switch (type) { - case OVS_ACTION_ATTR_UNSPEC: - return -EINVAL; - - case OVS_ACTION_ATTR_USERSPACE: - err = validate_userspace(a); - if (err) - return err; - break; - - case OVS_ACTION_ATTR_OUTPUT: - if (nla_get_u32(a) >= DP_MAX_PORTS) - return -EINVAL; - break; - - - case OVS_ACTION_ATTR_POP_VLAN: - break; - - case OVS_ACTION_ATTR_PUSH_VLAN: - vlan = nla_data(a); - if (vlan->vlan_tpid != htons(ETH_P_8021Q)) - return -EINVAL; - if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT))) - return -EINVAL; - break; - - case OVS_ACTION_ATTR_SET: - err = validate_set(a, key, sfa, &skip_copy); - if (err) - return err; - break; - - case OVS_ACTION_ATTR_SAMPLE: - err = validate_and_copy_sample(a, key, depth, sfa); - if (err) - return err; - skip_copy = true; - break; - - default: - return -EINVAL; - } - if (!skip_copy) { - err = copy_action(a, sfa); - if (err) - return err; - } - } - - if (rem > 0) - return -EINVAL; - - return 0; -} - static void clear_stats(struct sw_flow *flow) { flow->used = 0; @@ -902,15 +508,16 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) if (err) goto err_flow_free; - err = ovs_flow_metadata_from_nlattrs(flow, a[OVS_PACKET_ATTR_KEY]); + err = ovs_nla_get_flow_metadata(flow, a[OVS_PACKET_ATTR_KEY]); if (err) goto err_flow_free; - acts = ovs_flow_actions_alloc(nla_len(a[OVS_PACKET_ATTR_ACTIONS])); + acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_PACKET_ATTR_ACTIONS])); err = PTR_ERR(acts); if (IS_ERR(acts)) goto err_flow_free; - err = validate_and_copy_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0, &acts); + err = ovs_nla_copy_actions(a[OVS_PACKET_ATTR_ACTIONS], + &flow->key, 0, &acts); rcu_assign_pointer(flow->sf_acts, acts); if (err) goto err_flow_free; @@ -958,15 +565,18 @@ static struct genl_ops dp_packet_genl_ops[] = { } }; -static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats) +static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats, + struct ovs_dp_megaflow_stats *mega_stats) { - struct flow_table *table; int i; - table = rcu_dereference_check(dp->table, lockdep_ovsl_is_held()); - stats->n_flows = ovs_flow_tbl_count(table); + memset(mega_stats, 0, sizeof(*mega_stats)); + + stats->n_flows = ovs_flow_tbl_count(&dp->table); + mega_stats->n_masks = ovs_flow_tbl_num_masks(&dp->table); stats->n_hit = stats->n_missed = stats->n_lost = 0; + for_each_possible_cpu(i) { const struct dp_stats_percpu *percpu_stats; struct dp_stats_percpu local_stats; @@ -982,6 +592,7 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats) stats->n_hit += local_stats.n_hit; stats->n_missed += local_stats.n_missed; stats->n_lost += local_stats.n_lost; + mega_stats->n_mask_hit += local_stats.n_mask_hit; } } @@ -1005,100 +616,6 @@ static struct genl_multicast_group ovs_dp_flow_multicast_group = { .name = OVS_FLOW_MCGROUP }; -static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb); -static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb) -{ - const struct nlattr *a; - struct nlattr *start; - int err = 0, rem; - - start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE); - if (!start) - return -EMSGSIZE; - - nla_for_each_nested(a, attr, rem) { - int type = nla_type(a); - struct nlattr *st_sample; - - switch (type) { - case OVS_SAMPLE_ATTR_PROBABILITY: - if (nla_put(skb, OVS_SAMPLE_ATTR_PROBABILITY, sizeof(u32), nla_data(a))) - return -EMSGSIZE; - break; - case OVS_SAMPLE_ATTR_ACTIONS: - st_sample = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS); - if (!st_sample) - return -EMSGSIZE; - err = actions_to_attr(nla_data(a), nla_len(a), skb); - if (err) - return err; - nla_nest_end(skb, st_sample); - break; - } - } - - nla_nest_end(skb, start); - return err; -} - -static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb) -{ - const struct nlattr *ovs_key = nla_data(a); - int key_type = nla_type(ovs_key); - struct nlattr *start; - int err; - - switch (key_type) { - case OVS_KEY_ATTR_IPV4_TUNNEL: - start = nla_nest_start(skb, OVS_ACTION_ATTR_SET); - if (!start) - return -EMSGSIZE; - - err = ovs_ipv4_tun_to_nlattr(skb, nla_data(ovs_key), - nla_data(ovs_key)); - if (err) - return err; - nla_nest_end(skb, start); - break; - default: - if (nla_put(skb, OVS_ACTION_ATTR_SET, nla_len(a), ovs_key)) - return -EMSGSIZE; - break; - } - - return 0; -} - -static int actions_to_attr(const struct nlattr *attr, int len, struct sk_buff *skb) -{ - const struct nlattr *a; - int rem, err; - - nla_for_each_attr(a, attr, len, rem) { - int type = nla_type(a); - - switch (type) { - case OVS_ACTION_ATTR_SET: - err = set_action_to_attr(a, skb); - if (err) - return err; - break; - - case OVS_ACTION_ATTR_SAMPLE: - err = sample_action_to_attr(a, skb); - if (err) - return err; - break; - default: - if (nla_put(skb, type, nla_len(a), nla_data(a))) - return -EMSGSIZE; - break; - } - } - - return 0; -} - static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts) { return NLMSG_ALIGN(sizeof(struct ovs_header)) @@ -1135,8 +652,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, if (!nla) goto nla_put_failure; - err = ovs_flow_to_nlattrs(&flow->unmasked_key, - &flow->unmasked_key, skb); + err = ovs_nla_put_flow(&flow->unmasked_key, &flow->unmasked_key, skb); if (err) goto error; nla_nest_end(skb, nla); @@ -1145,7 +661,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, if (!nla) goto nla_put_failure; - err = ovs_flow_to_nlattrs(&flow->key, &flow->mask->key, skb); + err = ovs_nla_put_flow(&flow->key, &flow->mask->key, skb); if (err) goto error; @@ -1155,7 +671,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, used = flow->used; stats.n_packets = flow->packet_count; stats.n_bytes = flow->byte_count; - tcp_flags = flow->tcp_flags; + tcp_flags = (u8)ntohs(flow->tcp_flags); spin_unlock_bh(&flow->lock); if (used && @@ -1188,7 +704,8 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp, sf_acts = rcu_dereference_check(flow->sf_acts, lockdep_ovsl_is_held()); - err = actions_to_attr(sf_acts->actions, sf_acts->actions_len, skb); + err = ovs_nla_put_actions(sf_acts->actions, + sf_acts->actions_len, skb); if (!err) nla_nest_end(skb, start); else { @@ -1234,6 +751,14 @@ static struct sk_buff *ovs_flow_cmd_build_info(struct sw_flow *flow, return skb; } +static struct sw_flow *__ovs_flow_tbl_lookup(struct flow_table *tbl, + const struct sw_flow_key *key) +{ + u32 __always_unused n_mask_hit; + + return ovs_flow_tbl_lookup(tbl, key, &n_mask_hit); +} + static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) { struct nlattr **a = info->attrs; @@ -1243,7 +768,6 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) struct sw_flow_mask mask; struct sk_buff *reply; struct datapath *dp; - struct flow_table *table; struct sw_flow_actions *acts = NULL; struct sw_flow_match match; int error; @@ -1254,21 +778,21 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) goto error; ovs_match_init(&match, &key, &mask); - error = ovs_match_from_nlattrs(&match, - a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); + error = ovs_nla_get_match(&match, + a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]); if (error) goto error; /* Validate actions. */ if (a[OVS_FLOW_ATTR_ACTIONS]) { - acts = ovs_flow_actions_alloc(nla_len(a[OVS_FLOW_ATTR_ACTIONS])); + acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS])); error = PTR_ERR(acts); if (IS_ERR(acts)) goto error; - ovs_flow_key_mask(&masked_key, &key, &mask); - error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], - &masked_key, 0, &acts); + ovs_flow_mask_key(&masked_key, &key, &mask); + error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], + &masked_key, 0, &acts); if (error) { OVS_NLERR("Flow actions may not be safe on all matching packets.\n"); goto err_kfree; @@ -1284,29 +808,14 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) if (!dp) goto err_unlock_ovs; - table = ovsl_dereference(dp->table); - /* Check if this is a duplicate flow */ - flow = ovs_flow_lookup(table, &key); + flow = __ovs_flow_tbl_lookup(&dp->table, &key); if (!flow) { - struct sw_flow_mask *mask_p; /* Bail out if we're not allowed to create a new flow. */ error = -ENOENT; if (info->genlhdr->cmd == OVS_FLOW_CMD_SET) goto err_unlock_ovs; - /* Expand table, if necessary, to make room. */ - if (ovs_flow_tbl_need_to_expand(table)) { - struct flow_table *new_table; - - new_table = ovs_flow_tbl_expand(table); - if (!IS_ERR(new_table)) { - rcu_assign_pointer(dp->table, new_table); - ovs_flow_tbl_destroy(table, true); - table = ovsl_dereference(dp->table); - } - } - /* Allocate flow. */ flow = ovs_flow_alloc(); if (IS_ERR(flow)) { @@ -1317,25 +826,14 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) flow->key = masked_key; flow->unmasked_key = key; - - /* Make sure mask is unique in the system */ - mask_p = ovs_sw_flow_mask_find(table, &mask); - if (!mask_p) { - /* Allocate a new mask if none exsits. */ - mask_p = ovs_sw_flow_mask_alloc(); - if (!mask_p) - goto err_flow_free; - mask_p->key = mask.key; - mask_p->range = mask.range; - ovs_sw_flow_mask_insert(table, mask_p); - } - - ovs_sw_flow_mask_add_ref(mask_p); - flow->mask = mask_p; rcu_assign_pointer(flow->sf_acts, acts); /* Put flow in bucket. */ - ovs_flow_insert(table, flow); + error = ovs_flow_tbl_insert(&dp->table, flow, &mask); + if (error) { + acts = NULL; + goto err_flow_free; + } reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, info->snd_seq, OVS_FLOW_CMD_NEW); @@ -1356,7 +854,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) /* The unmasked key has to be the same for flow updates. */ error = -EINVAL; - if (!ovs_flow_cmp_unmasked_key(flow, &key, match.range.end)) { + if (!ovs_flow_cmp_unmasked_key(flow, &match)) { OVS_NLERR("Flow modification message rejected, unmasked key does not match.\n"); goto err_unlock_ovs; } @@ -1364,7 +862,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) /* Update actions. */ old_acts = ovsl_dereference(flow->sf_acts); rcu_assign_pointer(flow->sf_acts, acts); - ovs_flow_deferred_free_acts(old_acts); + ovs_nla_free_flow_actions(old_acts); reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid, info->snd_seq, OVS_FLOW_CMD_NEW); @@ -1403,7 +901,6 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) struct sk_buff *reply; struct sw_flow *flow; struct datapath *dp; - struct flow_table *table; struct sw_flow_match match; int err; @@ -1413,7 +910,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) } ovs_match_init(&match, &key, NULL); - err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL); + err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); if (err) return err; @@ -1424,9 +921,8 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info) goto unlock; } - table = ovsl_dereference(dp->table); - flow = ovs_flow_lookup_unmasked_key(table, &match); - if (!flow) { + flow = __ovs_flow_tbl_lookup(&dp->table, &key); + if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { err = -ENOENT; goto unlock; } @@ -1453,7 +949,6 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) struct sk_buff *reply; struct sw_flow *flow; struct datapath *dp; - struct flow_table *table; struct sw_flow_match match; int err; @@ -1465,18 +960,17 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) } if (!a[OVS_FLOW_ATTR_KEY]) { - err = flush_flows(dp); + err = ovs_flow_tbl_flush(&dp->table); goto unlock; } ovs_match_init(&match, &key, NULL); - err = ovs_match_from_nlattrs(&match, a[OVS_FLOW_ATTR_KEY], NULL); + err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL); if (err) goto unlock; - table = ovsl_dereference(dp->table); - flow = ovs_flow_lookup_unmasked_key(table, &match); - if (!flow) { + flow = __ovs_flow_tbl_lookup(&dp->table, &key); + if (!flow || !ovs_flow_cmp_unmasked_key(flow, &match)) { err = -ENOENT; goto unlock; } @@ -1487,7 +981,7 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info) goto unlock; } - ovs_flow_remove(table, flow); + ovs_flow_tbl_remove(&dp->table, flow); err = ovs_flow_cmd_fill_info(flow, dp, reply, info->snd_portid, info->snd_seq, 0, OVS_FLOW_CMD_DEL); @@ -1506,8 +1000,8 @@ unlock: static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) { struct ovs_header *ovs_header = genlmsg_data(nlmsg_data(cb->nlh)); + struct table_instance *ti; struct datapath *dp; - struct flow_table *table; rcu_read_lock(); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); @@ -1516,14 +1010,14 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb) return -ENODEV; } - table = rcu_dereference(dp->table); + ti = rcu_dereference(dp->table.ti); for (;;) { struct sw_flow *flow; u32 bucket, obj; bucket = cb->args[0]; obj = cb->args[1]; - flow = ovs_flow_dump_next(table, &bucket, &obj); + flow = ovs_flow_tbl_dump_next(ti, &bucket, &obj); if (!flow) break; @@ -1589,6 +1083,7 @@ static size_t ovs_dp_cmd_msg_size(void) msgsize += nla_total_size(IFNAMSIZ); msgsize += nla_total_size(sizeof(struct ovs_dp_stats)); + msgsize += nla_total_size(sizeof(struct ovs_dp_megaflow_stats)); return msgsize; } @@ -1598,6 +1093,7 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, { struct ovs_header *ovs_header; struct ovs_dp_stats dp_stats; + struct ovs_dp_megaflow_stats dp_megaflow_stats; int err; ovs_header = genlmsg_put(skb, portid, seq, &dp_datapath_genl_family, @@ -1613,8 +1109,14 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb, if (err) goto nla_put_failure; - get_dp_stats(dp, &dp_stats); - if (nla_put(skb, OVS_DP_ATTR_STATS, sizeof(struct ovs_dp_stats), &dp_stats)) + get_dp_stats(dp, &dp_stats, &dp_megaflow_stats); + if (nla_put(skb, OVS_DP_ATTR_STATS, sizeof(struct ovs_dp_stats), + &dp_stats)) + goto nla_put_failure; + + if (nla_put(skb, OVS_DP_ATTR_MEGAFLOW_STATS, + sizeof(struct ovs_dp_megaflow_stats), + &dp_megaflow_stats)) goto nla_put_failure; return genlmsg_end(skb, ovs_header); @@ -1687,9 +1189,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) ovs_dp_set_net(dp, hold_net(sock_net(skb->sk))); /* Allocate table. */ - err = -ENOMEM; - rcu_assign_pointer(dp->table, ovs_flow_tbl_alloc(TBL_MIN_BUCKETS)); - if (!dp->table) + err = ovs_flow_tbl_init(&dp->table); + if (err) goto err_free_dp; dp->stats_percpu = alloc_percpu(struct dp_stats_percpu); @@ -1699,7 +1200,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info) } dp->ports = kmalloc(DP_VPORT_HASH_BUCKETS * sizeof(struct hlist_head), - GFP_KERNEL); + GFP_KERNEL); if (!dp->ports) { err = -ENOMEM; goto err_destroy_percpu; @@ -1746,7 +1247,7 @@ err_destroy_ports_array: err_destroy_percpu: free_percpu(dp->stats_percpu); err_destroy_table: - ovs_flow_tbl_destroy(ovsl_dereference(dp->table), false); + ovs_flow_tbl_destroy(&dp->table); err_free_dp: release_net(ovs_dp_get_net(dp)); kfree(dp); @@ -2336,32 +1837,6 @@ error: return err; } -static void rehash_flow_table(struct work_struct *work) -{ - struct datapath *dp; - struct net *net; - - ovs_lock(); - rtnl_lock(); - for_each_net(net) { - struct ovs_net *ovs_net = net_generic(net, ovs_net_id); - - list_for_each_entry(dp, &ovs_net->dps, list_node) { - struct flow_table *old_table = ovsl_dereference(dp->table); - struct flow_table *new_table; - - new_table = ovs_flow_tbl_rehash(old_table); - if (!IS_ERR(new_table)) { - rcu_assign_pointer(dp->table, new_table); - ovs_flow_tbl_destroy(old_table, true); - } - } - } - rtnl_unlock(); - ovs_unlock(); - schedule_delayed_work(&rehash_flow_wq, REHASH_FLOW_INTERVAL); -} - static int __net_init ovs_init_net(struct net *net) { struct ovs_net *ovs_net = net_generic(net, ovs_net_id); @@ -2419,8 +1894,6 @@ static int __init dp_init(void) if (err < 0) goto error_unreg_notifier; - schedule_delayed_work(&rehash_flow_wq, REHASH_FLOW_INTERVAL); - return 0; error_unreg_notifier: @@ -2437,7 +1910,6 @@ error: static void dp_cleanup(void) { - cancel_delayed_work_sync(&rehash_flow_wq); dp_unregister_genl(ARRAY_SIZE(dp_genl_families)); unregister_netdevice_notifier(&ovs_dp_device_notifier); unregister_pernet_device(&ovs_net_ops); diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 4d109c176ef3ce2bd7be0419a15839ab4f8a03f1..d3d14a58aa91413bb18dae53bbb8aa1babd42c73 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h @@ -27,6 +27,7 @@ #include #include "flow.h" +#include "flow_table.h" #include "vport.h" #define DP_MAX_PORTS USHRT_MAX @@ -45,11 +46,15 @@ * @n_lost: Number of received packets that had no matching flow in the flow * table that could not be sent to userspace (normally due to an overflow in * one of the datapath's queues). + * @n_mask_hit: Number of masks looked up for flow match. + * @n_mask_hit / (@n_hit + @n_missed) will be the average masks looked + * up per packet. */ struct dp_stats_percpu { u64 n_hit; u64 n_missed; u64 n_lost; + u64 n_mask_hit; struct u64_stats_sync sync; }; @@ -57,7 +62,7 @@ struct dp_stats_percpu { * struct datapath - datapath for flow-based packet switching * @rcu: RCU callback head for deferred destruction. * @list_node: Element in global 'dps' list. - * @table: Current flow table. Protected by ovs_mutex and RCU. + * @table: flow table. * @ports: Hash table for ports. %OVSP_LOCAL port always exists. Protected by * ovs_mutex and RCU. * @stats_percpu: Per-CPU datapath statistics. @@ -71,7 +76,7 @@ struct datapath { struct list_head list_node; /* Flow table. */ - struct flow_table __rcu *table; + struct flow_table table; /* Switch ports. */ struct hlist_head *ports; diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 410db90db73d32493a525a530cd04ad57a92fadf..b409f527960178bdef6936361952a8d320d85859 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -45,202 +45,38 @@ #include #include -static struct kmem_cache *flow_cache; - -static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask, - struct sw_flow_key_range *range, u8 val); - -static void update_range__(struct sw_flow_match *match, - size_t offset, size_t size, bool is_mask) +u64 ovs_flow_used_time(unsigned long flow_jiffies) { - struct sw_flow_key_range *range = NULL; - size_t start = rounddown(offset, sizeof(long)); - size_t end = roundup(offset + size, sizeof(long)); - - if (!is_mask) - range = &match->range; - else if (match->mask) - range = &match->mask->range; - - if (!range) - return; - - if (range->start == range->end) { - range->start = start; - range->end = end; - return; - } - - if (range->start > start) - range->start = start; + struct timespec cur_ts; + u64 cur_ms, idle_ms; - if (range->end < end) - range->end = end; -} + ktime_get_ts(&cur_ts); + idle_ms = jiffies_to_msecs(jiffies - flow_jiffies); + cur_ms = (u64)cur_ts.tv_sec * MSEC_PER_SEC + + cur_ts.tv_nsec / NSEC_PER_MSEC; -#define SW_FLOW_KEY_PUT(match, field, value, is_mask) \ - do { \ - update_range__(match, offsetof(struct sw_flow_key, field), \ - sizeof((match)->key->field), is_mask); \ - if (is_mask) { \ - if ((match)->mask) \ - (match)->mask->key.field = value; \ - } else { \ - (match)->key->field = value; \ - } \ - } while (0) - -#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \ - do { \ - update_range__(match, offsetof(struct sw_flow_key, field), \ - len, is_mask); \ - if (is_mask) { \ - if ((match)->mask) \ - memcpy(&(match)->mask->key.field, value_p, len);\ - } else { \ - memcpy(&(match)->key->field, value_p, len); \ - } \ - } while (0) - -static u16 range_n_bytes(const struct sw_flow_key_range *range) -{ - return range->end - range->start; + return cur_ms - idle_ms; } -void ovs_match_init(struct sw_flow_match *match, - struct sw_flow_key *key, - struct sw_flow_mask *mask) -{ - memset(match, 0, sizeof(*match)); - match->key = key; - match->mask = mask; +#define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF)) - memset(key, 0, sizeof(*key)); - - if (mask) { - memset(&mask->key, 0, sizeof(mask->key)); - mask->range.start = mask->range.end = 0; - } -} - -static bool ovs_match_validate(const struct sw_flow_match *match, - u64 key_attrs, u64 mask_attrs) +void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb) { - u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET; - u64 mask_allowed = key_attrs; /* At most allow all key attributes */ - - /* The following mask attributes allowed only if they - * pass the validation tests. */ - mask_allowed &= ~((1 << OVS_KEY_ATTR_IPV4) - | (1 << OVS_KEY_ATTR_IPV6) - | (1 << OVS_KEY_ATTR_TCP) - | (1 << OVS_KEY_ATTR_UDP) - | (1 << OVS_KEY_ATTR_SCTP) - | (1 << OVS_KEY_ATTR_ICMP) - | (1 << OVS_KEY_ATTR_ICMPV6) - | (1 << OVS_KEY_ATTR_ARP) - | (1 << OVS_KEY_ATTR_ND)); - - /* Always allowed mask fields. */ - mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL) - | (1 << OVS_KEY_ATTR_IN_PORT) - | (1 << OVS_KEY_ATTR_ETHERTYPE)); - - /* Check key attributes. */ - if (match->key->eth.type == htons(ETH_P_ARP) - || match->key->eth.type == htons(ETH_P_RARP)) { - key_expected |= 1 << OVS_KEY_ATTR_ARP; - if (match->mask && (match->mask->key.eth.type == htons(0xffff))) - mask_allowed |= 1 << OVS_KEY_ATTR_ARP; - } + __be16 tcp_flags = 0; - if (match->key->eth.type == htons(ETH_P_IP)) { - key_expected |= 1 << OVS_KEY_ATTR_IPV4; - if (match->mask && (match->mask->key.eth.type == htons(0xffff))) - mask_allowed |= 1 << OVS_KEY_ATTR_IPV4; - - if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) { - if (match->key->ip.proto == IPPROTO_UDP) { - key_expected |= 1 << OVS_KEY_ATTR_UDP; - if (match->mask && (match->mask->key.ip.proto == 0xff)) - mask_allowed |= 1 << OVS_KEY_ATTR_UDP; - } - - if (match->key->ip.proto == IPPROTO_SCTP) { - key_expected |= 1 << OVS_KEY_ATTR_SCTP; - if (match->mask && (match->mask->key.ip.proto == 0xff)) - mask_allowed |= 1 << OVS_KEY_ATTR_SCTP; - } - - if (match->key->ip.proto == IPPROTO_TCP) { - key_expected |= 1 << OVS_KEY_ATTR_TCP; - if (match->mask && (match->mask->key.ip.proto == 0xff)) - mask_allowed |= 1 << OVS_KEY_ATTR_TCP; - } - - if (match->key->ip.proto == IPPROTO_ICMP) { - key_expected |= 1 << OVS_KEY_ATTR_ICMP; - if (match->mask && (match->mask->key.ip.proto == 0xff)) - mask_allowed |= 1 << OVS_KEY_ATTR_ICMP; - } - } - } - - if (match->key->eth.type == htons(ETH_P_IPV6)) { - key_expected |= 1 << OVS_KEY_ATTR_IPV6; - if (match->mask && (match->mask->key.eth.type == htons(0xffff))) - mask_allowed |= 1 << OVS_KEY_ATTR_IPV6; - - if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) { - if (match->key->ip.proto == IPPROTO_UDP) { - key_expected |= 1 << OVS_KEY_ATTR_UDP; - if (match->mask && (match->mask->key.ip.proto == 0xff)) - mask_allowed |= 1 << OVS_KEY_ATTR_UDP; - } - - if (match->key->ip.proto == IPPROTO_SCTP) { - key_expected |= 1 << OVS_KEY_ATTR_SCTP; - if (match->mask && (match->mask->key.ip.proto == 0xff)) - mask_allowed |= 1 << OVS_KEY_ATTR_SCTP; - } - - if (match->key->ip.proto == IPPROTO_TCP) { - key_expected |= 1 << OVS_KEY_ATTR_TCP; - if (match->mask && (match->mask->key.ip.proto == 0xff)) - mask_allowed |= 1 << OVS_KEY_ATTR_TCP; - } - - if (match->key->ip.proto == IPPROTO_ICMPV6) { - key_expected |= 1 << OVS_KEY_ATTR_ICMPV6; - if (match->mask && (match->mask->key.ip.proto == 0xff)) - mask_allowed |= 1 << OVS_KEY_ATTR_ICMPV6; - - if (match->key->ipv6.tp.src == - htons(NDISC_NEIGHBOUR_SOLICITATION) || - match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) { - key_expected |= 1 << OVS_KEY_ATTR_ND; - if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff))) - mask_allowed |= 1 << OVS_KEY_ATTR_ND; - } - } - } - } - - if ((key_attrs & key_expected) != key_expected) { - /* Key attributes check failed. */ - OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n", - key_attrs, key_expected); - return false; - } - - if ((mask_attrs & mask_allowed) != mask_attrs) { - /* Mask attributes check failed. */ - OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n", - mask_attrs, mask_allowed); - return false; + if ((flow->key.eth.type == htons(ETH_P_IP) || + flow->key.eth.type == htons(ETH_P_IPV6)) && + flow->key.ip.proto == IPPROTO_TCP && + likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) { + tcp_flags = TCP_FLAGS_BE16(tcp_hdr(skb)); } - return true; + spin_lock(&flow->lock); + flow->used = jiffies; + flow->packet_count++; + flow->byte_count += skb->len; + flow->tcp_flags |= tcp_flags; + spin_unlock(&flow->lock); } static int check_header(struct sk_buff *skb, int len) @@ -311,19 +147,6 @@ static bool icmphdr_ok(struct sk_buff *skb) sizeof(struct icmphdr)); } -u64 ovs_flow_used_time(unsigned long flow_jiffies) -{ - struct timespec cur_ts; - u64 cur_ms, idle_ms; - - ktime_get_ts(&cur_ts); - idle_ms = jiffies_to_msecs(jiffies - flow_jiffies); - cur_ms = (u64)cur_ts.tv_sec * MSEC_PER_SEC + - cur_ts.tv_nsec / NSEC_PER_MSEC; - - return cur_ms - idle_ms; -} - static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key) { unsigned int nh_ofs = skb_network_offset(skb); @@ -372,311 +195,6 @@ static bool icmp6hdr_ok(struct sk_buff *skb) sizeof(struct icmp6hdr)); } -void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src, - const struct sw_flow_mask *mask) -{ - const long *m = (long *)((u8 *)&mask->key + mask->range.start); - const long *s = (long *)((u8 *)src + mask->range.start); - long *d = (long *)((u8 *)dst + mask->range.start); - int i; - - /* The memory outside of the 'mask->range' are not set since - * further operations on 'dst' only uses contents within - * 'mask->range'. - */ - for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long)) - *d++ = *s++ & *m++; -} - -#define TCP_FLAGS_OFFSET 13 -#define TCP_FLAG_MASK 0x3f - -void ovs_flow_used(struct sw_flow *flow, struct sk_buff *skb) -{ - u8 tcp_flags = 0; - - if ((flow->key.eth.type == htons(ETH_P_IP) || - flow->key.eth.type == htons(ETH_P_IPV6)) && - flow->key.ip.proto == IPPROTO_TCP && - likely(skb->len >= skb_transport_offset(skb) + sizeof(struct tcphdr))) { - u8 *tcp = (u8 *)tcp_hdr(skb); - tcp_flags = *(tcp + TCP_FLAGS_OFFSET) & TCP_FLAG_MASK; - } - - spin_lock(&flow->lock); - flow->used = jiffies; - flow->packet_count++; - flow->byte_count += skb->len; - flow->tcp_flags |= tcp_flags; - spin_unlock(&flow->lock); -} - -struct sw_flow_actions *ovs_flow_actions_alloc(int size) -{ - struct sw_flow_actions *sfa; - - if (size > MAX_ACTIONS_BUFSIZE) - return ERR_PTR(-EINVAL); - - sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL); - if (!sfa) - return ERR_PTR(-ENOMEM); - - sfa->actions_len = 0; - return sfa; -} - -struct sw_flow *ovs_flow_alloc(void) -{ - struct sw_flow *flow; - - flow = kmem_cache_alloc(flow_cache, GFP_KERNEL); - if (!flow) - return ERR_PTR(-ENOMEM); - - spin_lock_init(&flow->lock); - flow->sf_acts = NULL; - flow->mask = NULL; - - return flow; -} - -static struct hlist_head *find_bucket(struct flow_table *table, u32 hash) -{ - hash = jhash_1word(hash, table->hash_seed); - return flex_array_get(table->buckets, - (hash & (table->n_buckets - 1))); -} - -static struct flex_array *alloc_buckets(unsigned int n_buckets) -{ - struct flex_array *buckets; - int i, err; - - buckets = flex_array_alloc(sizeof(struct hlist_head), - n_buckets, GFP_KERNEL); - if (!buckets) - return NULL; - - err = flex_array_prealloc(buckets, 0, n_buckets, GFP_KERNEL); - if (err) { - flex_array_free(buckets); - return NULL; - } - - for (i = 0; i < n_buckets; i++) - INIT_HLIST_HEAD((struct hlist_head *) - flex_array_get(buckets, i)); - - return buckets; -} - -static void free_buckets(struct flex_array *buckets) -{ - flex_array_free(buckets); -} - -static struct flow_table *__flow_tbl_alloc(int new_size) -{ - struct flow_table *table = kmalloc(sizeof(*table), GFP_KERNEL); - - if (!table) - return NULL; - - table->buckets = alloc_buckets(new_size); - - if (!table->buckets) { - kfree(table); - return NULL; - } - table->n_buckets = new_size; - table->count = 0; - table->node_ver = 0; - table->keep_flows = false; - get_random_bytes(&table->hash_seed, sizeof(u32)); - table->mask_list = NULL; - - return table; -} - -static void __flow_tbl_destroy(struct flow_table *table) -{ - int i; - - if (table->keep_flows) - goto skip_flows; - - for (i = 0; i < table->n_buckets; i++) { - struct sw_flow *flow; - struct hlist_head *head = flex_array_get(table->buckets, i); - struct hlist_node *n; - int ver = table->node_ver; - - hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { - hlist_del(&flow->hash_node[ver]); - ovs_flow_free(flow, false); - } - } - - BUG_ON(!list_empty(table->mask_list)); - kfree(table->mask_list); - -skip_flows: - free_buckets(table->buckets); - kfree(table); -} - -struct flow_table *ovs_flow_tbl_alloc(int new_size) -{ - struct flow_table *table = __flow_tbl_alloc(new_size); - - if (!table) - return NULL; - - table->mask_list = kmalloc(sizeof(struct list_head), GFP_KERNEL); - if (!table->mask_list) { - table->keep_flows = true; - __flow_tbl_destroy(table); - return NULL; - } - INIT_LIST_HEAD(table->mask_list); - - return table; -} - -static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) -{ - struct flow_table *table = container_of(rcu, struct flow_table, rcu); - - __flow_tbl_destroy(table); -} - -void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred) -{ - if (!table) - return; - - if (deferred) - call_rcu(&table->rcu, flow_tbl_destroy_rcu_cb); - else - __flow_tbl_destroy(table); -} - -struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *last) -{ - struct sw_flow *flow; - struct hlist_head *head; - int ver; - int i; - - ver = table->node_ver; - while (*bucket < table->n_buckets) { - i = 0; - head = flex_array_get(table->buckets, *bucket); - hlist_for_each_entry_rcu(flow, head, hash_node[ver]) { - if (i < *last) { - i++; - continue; - } - *last = i + 1; - return flow; - } - (*bucket)++; - *last = 0; - } - - return NULL; -} - -static void __tbl_insert(struct flow_table *table, struct sw_flow *flow) -{ - struct hlist_head *head; - - head = find_bucket(table, flow->hash); - hlist_add_head_rcu(&flow->hash_node[table->node_ver], head); - - table->count++; -} - -static void flow_table_copy_flows(struct flow_table *old, struct flow_table *new) -{ - int old_ver; - int i; - - old_ver = old->node_ver; - new->node_ver = !old_ver; - - /* Insert in new table. */ - for (i = 0; i < old->n_buckets; i++) { - struct sw_flow *flow; - struct hlist_head *head; - - head = flex_array_get(old->buckets, i); - - hlist_for_each_entry(flow, head, hash_node[old_ver]) - __tbl_insert(new, flow); - } - - new->mask_list = old->mask_list; - old->keep_flows = true; -} - -static struct flow_table *__flow_tbl_rehash(struct flow_table *table, int n_buckets) -{ - struct flow_table *new_table; - - new_table = __flow_tbl_alloc(n_buckets); - if (!new_table) - return ERR_PTR(-ENOMEM); - - flow_table_copy_flows(table, new_table); - - return new_table; -} - -struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table) -{ - return __flow_tbl_rehash(table, table->n_buckets); -} - -struct flow_table *ovs_flow_tbl_expand(struct flow_table *table) -{ - return __flow_tbl_rehash(table, table->n_buckets * 2); -} - -static void __flow_free(struct sw_flow *flow) -{ - kfree((struct sf_flow_acts __force *)flow->sf_acts); - kmem_cache_free(flow_cache, flow); -} - -static void rcu_free_flow_callback(struct rcu_head *rcu) -{ - struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu); - - __flow_free(flow); -} - -void ovs_flow_free(struct sw_flow *flow, bool deferred) -{ - if (!flow) - return; - - ovs_sw_flow_mask_del_ref(flow->mask, deferred); - - if (deferred) - call_rcu(&flow->rcu, rcu_free_flow_callback); - else - __flow_free(flow); -} - -/* Schedules 'sf_acts' to be freed after the next RCU grace period. - * The caller must hold rcu_read_lock for this to be sensible. */ -void ovs_flow_deferred_free_acts(struct sw_flow_actions *sf_acts) -{ - kfree_rcu(sf_acts, rcu); -} - static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key) { struct qtag_prefix { @@ -910,6 +428,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) struct tcphdr *tcp = tcp_hdr(skb); key->ipv4.tp.src = tcp->source; key->ipv4.tp.dst = tcp->dest; + key->ipv4.tp.flags = TCP_FLAGS_BE16(tcp); } } else if (key->ip.proto == IPPROTO_UDP) { if (udphdr_ok(skb)) { @@ -978,6 +497,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) struct tcphdr *tcp = tcp_hdr(skb); key->ipv6.tp.src = tcp->source; key->ipv6.tp.dst = tcp->dest; + key->ipv6.tp.flags = TCP_FLAGS_BE16(tcp); } } else if (key->ip.proto == NEXTHDR_UDP) { if (udphdr_ok(skb)) { @@ -1002,1080 +522,3 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key) return 0; } - -static u32 ovs_flow_hash(const struct sw_flow_key *key, int key_start, - int key_end) -{ - u32 *hash_key = (u32 *)((u8 *)key + key_start); - int hash_u32s = (key_end - key_start) >> 2; - - /* Make sure number of hash bytes are multiple of u32. */ - BUILD_BUG_ON(sizeof(long) % sizeof(u32)); - - return jhash2(hash_key, hash_u32s, 0); -} - -static int flow_key_start(const struct sw_flow_key *key) -{ - if (key->tun_key.ipv4_dst) - return 0; - else - return rounddown(offsetof(struct sw_flow_key, phy), - sizeof(long)); -} - -static bool __cmp_key(const struct sw_flow_key *key1, - const struct sw_flow_key *key2, int key_start, int key_end) -{ - const long *cp1 = (long *)((u8 *)key1 + key_start); - const long *cp2 = (long *)((u8 *)key2 + key_start); - long diffs = 0; - int i; - - for (i = key_start; i < key_end; i += sizeof(long)) - diffs |= *cp1++ ^ *cp2++; - - return diffs == 0; -} - -static bool __flow_cmp_masked_key(const struct sw_flow *flow, - const struct sw_flow_key *key, int key_start, int key_end) -{ - return __cmp_key(&flow->key, key, key_start, key_end); -} - -static bool __flow_cmp_unmasked_key(const struct sw_flow *flow, - const struct sw_flow_key *key, int key_start, int key_end) -{ - return __cmp_key(&flow->unmasked_key, key, key_start, key_end); -} - -bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, - const struct sw_flow_key *key, int key_end) -{ - int key_start; - key_start = flow_key_start(key); - - return __flow_cmp_unmasked_key(flow, key, key_start, key_end); - -} - -struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table, - struct sw_flow_match *match) -{ - struct sw_flow_key *unmasked = match->key; - int key_end = match->range.end; - struct sw_flow *flow; - - flow = ovs_flow_lookup(table, unmasked); - if (flow && (!ovs_flow_cmp_unmasked_key(flow, unmasked, key_end))) - flow = NULL; - - return flow; -} - -static struct sw_flow *ovs_masked_flow_lookup(struct flow_table *table, - const struct sw_flow_key *unmasked, - struct sw_flow_mask *mask) -{ - struct sw_flow *flow; - struct hlist_head *head; - int key_start = mask->range.start; - int key_end = mask->range.end; - u32 hash; - struct sw_flow_key masked_key; - - ovs_flow_key_mask(&masked_key, unmasked, mask); - hash = ovs_flow_hash(&masked_key, key_start, key_end); - head = find_bucket(table, hash); - hlist_for_each_entry_rcu(flow, head, hash_node[table->node_ver]) { - if (flow->mask == mask && - __flow_cmp_masked_key(flow, &masked_key, - key_start, key_end)) - return flow; - } - return NULL; -} - -struct sw_flow *ovs_flow_lookup(struct flow_table *tbl, - const struct sw_flow_key *key) -{ - struct sw_flow *flow = NULL; - struct sw_flow_mask *mask; - - list_for_each_entry_rcu(mask, tbl->mask_list, list) { - flow = ovs_masked_flow_lookup(tbl, key, mask); - if (flow) /* Found */ - break; - } - - return flow; -} - - -void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow) -{ - flow->hash = ovs_flow_hash(&flow->key, flow->mask->range.start, - flow->mask->range.end); - __tbl_insert(table, flow); -} - -void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow) -{ - BUG_ON(table->count == 0); - hlist_del_rcu(&flow->hash_node[table->node_ver]); - table->count--; -} - -/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ -const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { - [OVS_KEY_ATTR_ENCAP] = -1, - [OVS_KEY_ATTR_PRIORITY] = sizeof(u32), - [OVS_KEY_ATTR_IN_PORT] = sizeof(u32), - [OVS_KEY_ATTR_SKB_MARK] = sizeof(u32), - [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet), - [OVS_KEY_ATTR_VLAN] = sizeof(__be16), - [OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16), - [OVS_KEY_ATTR_IPV4] = sizeof(struct ovs_key_ipv4), - [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6), - [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp), - [OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp), - [OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp), - [OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp), - [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6), - [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp), - [OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd), - [OVS_KEY_ATTR_TUNNEL] = -1, -}; - -static bool is_all_zero(const u8 *fp, size_t size) -{ - int i; - - if (!fp) - return false; - - for (i = 0; i < size; i++) - if (fp[i]) - return false; - - return true; -} - -static int __parse_flow_nlattrs(const struct nlattr *attr, - const struct nlattr *a[], - u64 *attrsp, bool nz) -{ - const struct nlattr *nla; - u32 attrs; - int rem; - - attrs = *attrsp; - nla_for_each_nested(nla, attr, rem) { - u16 type = nla_type(nla); - int expected_len; - - if (type > OVS_KEY_ATTR_MAX) { - OVS_NLERR("Unknown key attribute (type=%d, max=%d).\n", - type, OVS_KEY_ATTR_MAX); - return -EINVAL; - } - - if (attrs & (1 << type)) { - OVS_NLERR("Duplicate key attribute (type %d).\n", type); - return -EINVAL; - } - - expected_len = ovs_key_lens[type]; - if (nla_len(nla) != expected_len && expected_len != -1) { - OVS_NLERR("Key attribute has unexpected length (type=%d" - ", length=%d, expected=%d).\n", type, - nla_len(nla), expected_len); - return -EINVAL; - } - - if (!nz || !is_all_zero(nla_data(nla), expected_len)) { - attrs |= 1 << type; - a[type] = nla; - } - } - if (rem) { - OVS_NLERR("Message has %d unknown bytes.\n", rem); - return -EINVAL; - } - - *attrsp = attrs; - return 0; -} - -static int parse_flow_mask_nlattrs(const struct nlattr *attr, - const struct nlattr *a[], u64 *attrsp) -{ - return __parse_flow_nlattrs(attr, a, attrsp, true); -} - -static int parse_flow_nlattrs(const struct nlattr *attr, - const struct nlattr *a[], u64 *attrsp) -{ - return __parse_flow_nlattrs(attr, a, attrsp, false); -} - -int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr, - struct sw_flow_match *match, bool is_mask) -{ - struct nlattr *a; - int rem; - bool ttl = false; - __be16 tun_flags = 0; - - nla_for_each_nested(a, attr, rem) { - int type = nla_type(a); - static const u32 ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = { - [OVS_TUNNEL_KEY_ATTR_ID] = sizeof(u64), - [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = sizeof(u32), - [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = sizeof(u32), - [OVS_TUNNEL_KEY_ATTR_TOS] = 1, - [OVS_TUNNEL_KEY_ATTR_TTL] = 1, - [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0, - [OVS_TUNNEL_KEY_ATTR_CSUM] = 0, - }; - - if (type > OVS_TUNNEL_KEY_ATTR_MAX) { - OVS_NLERR("Unknown IPv4 tunnel attribute (type=%d, max=%d).\n", - type, OVS_TUNNEL_KEY_ATTR_MAX); - return -EINVAL; - } - - if (ovs_tunnel_key_lens[type] != nla_len(a)) { - OVS_NLERR("IPv4 tunnel attribute type has unexpected " - " length (type=%d, length=%d, expected=%d).\n", - type, nla_len(a), ovs_tunnel_key_lens[type]); - return -EINVAL; - } - - switch (type) { - case OVS_TUNNEL_KEY_ATTR_ID: - SW_FLOW_KEY_PUT(match, tun_key.tun_id, - nla_get_be64(a), is_mask); - tun_flags |= TUNNEL_KEY; - break; - case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: - SW_FLOW_KEY_PUT(match, tun_key.ipv4_src, - nla_get_be32(a), is_mask); - break; - case OVS_TUNNEL_KEY_ATTR_IPV4_DST: - SW_FLOW_KEY_PUT(match, tun_key.ipv4_dst, - nla_get_be32(a), is_mask); - break; - case OVS_TUNNEL_KEY_ATTR_TOS: - SW_FLOW_KEY_PUT(match, tun_key.ipv4_tos, - nla_get_u8(a), is_mask); - break; - case OVS_TUNNEL_KEY_ATTR_TTL: - SW_FLOW_KEY_PUT(match, tun_key.ipv4_ttl, - nla_get_u8(a), is_mask); - ttl = true; - break; - case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: - tun_flags |= TUNNEL_DONT_FRAGMENT; - break; - case OVS_TUNNEL_KEY_ATTR_CSUM: - tun_flags |= TUNNEL_CSUM; - break; - default: - return -EINVAL; - } - } - - SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask); - - if (rem > 0) { - OVS_NLERR("IPv4 tunnel attribute has %d unknown bytes.\n", rem); - return -EINVAL; - } - - if (!is_mask) { - if (!match->key->tun_key.ipv4_dst) { - OVS_NLERR("IPv4 tunnel destination address is zero.\n"); - return -EINVAL; - } - - if (!ttl) { - OVS_NLERR("IPv4 tunnel TTL not specified.\n"); - return -EINVAL; - } - } - - return 0; -} - -int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb, - const struct ovs_key_ipv4_tunnel *tun_key, - const struct ovs_key_ipv4_tunnel *output) -{ - struct nlattr *nla; - - nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL); - if (!nla) - return -EMSGSIZE; - - if (output->tun_flags & TUNNEL_KEY && - nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id)) - return -EMSGSIZE; - if (output->ipv4_src && - nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src)) - return -EMSGSIZE; - if (output->ipv4_dst && - nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst)) - return -EMSGSIZE; - if (output->ipv4_tos && - nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos)) - return -EMSGSIZE; - if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ipv4_ttl)) - return -EMSGSIZE; - if ((output->tun_flags & TUNNEL_DONT_FRAGMENT) && - nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT)) - return -EMSGSIZE; - if ((output->tun_flags & TUNNEL_CSUM) && - nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM)) - return -EMSGSIZE; - - nla_nest_end(skb, nla); - return 0; -} - -static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, - const struct nlattr **a, bool is_mask) -{ - if (*attrs & (1 << OVS_KEY_ATTR_PRIORITY)) { - SW_FLOW_KEY_PUT(match, phy.priority, - nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]), is_mask); - *attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY); - } - - if (*attrs & (1 << OVS_KEY_ATTR_IN_PORT)) { - u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]); - - if (is_mask) - in_port = 0xffffffff; /* Always exact match in_port. */ - else if (in_port >= DP_MAX_PORTS) - return -EINVAL; - - SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask); - *attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT); - } else if (!is_mask) { - SW_FLOW_KEY_PUT(match, phy.in_port, DP_MAX_PORTS, is_mask); - } - - if (*attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) { - uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]); - - SW_FLOW_KEY_PUT(match, phy.skb_mark, mark, is_mask); - *attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK); - } - if (*attrs & (1 << OVS_KEY_ATTR_TUNNEL)) { - if (ovs_ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match, - is_mask)) - return -EINVAL; - *attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL); - } - return 0; -} - -static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, - const struct nlattr **a, bool is_mask) -{ - int err; - u64 orig_attrs = attrs; - - err = metadata_from_nlattrs(match, &attrs, a, is_mask); - if (err) - return err; - - if (attrs & (1 << OVS_KEY_ATTR_ETHERNET)) { - const struct ovs_key_ethernet *eth_key; - - eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]); - SW_FLOW_KEY_MEMCPY(match, eth.src, - eth_key->eth_src, ETH_ALEN, is_mask); - SW_FLOW_KEY_MEMCPY(match, eth.dst, - eth_key->eth_dst, ETH_ALEN, is_mask); - attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET); - } - - if (attrs & (1 << OVS_KEY_ATTR_VLAN)) { - __be16 tci; - - tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); - if (!(tci & htons(VLAN_TAG_PRESENT))) { - if (is_mask) - OVS_NLERR("VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.\n"); - else - OVS_NLERR("VLAN TCI does not have VLAN_TAG_PRESENT bit set.\n"); - - return -EINVAL; - } - - SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask); - attrs &= ~(1 << OVS_KEY_ATTR_VLAN); - } else if (!is_mask) - SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true); - - if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) { - __be16 eth_type; - - eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); - if (is_mask) { - /* Always exact match EtherType. */ - eth_type = htons(0xffff); - } else if (ntohs(eth_type) < ETH_P_802_3_MIN) { - OVS_NLERR("EtherType is less than minimum (type=%x, min=%x).\n", - ntohs(eth_type), ETH_P_802_3_MIN); - return -EINVAL; - } - - SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask); - attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); - } else if (!is_mask) { - SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); - } - - if (attrs & (1 << OVS_KEY_ATTR_IPV4)) { - const struct ovs_key_ipv4 *ipv4_key; - - ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]); - if (!is_mask && ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) { - OVS_NLERR("Unknown IPv4 fragment type (value=%d, max=%d).\n", - ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX); - return -EINVAL; - } - SW_FLOW_KEY_PUT(match, ip.proto, - ipv4_key->ipv4_proto, is_mask); - SW_FLOW_KEY_PUT(match, ip.tos, - ipv4_key->ipv4_tos, is_mask); - SW_FLOW_KEY_PUT(match, ip.ttl, - ipv4_key->ipv4_ttl, is_mask); - SW_FLOW_KEY_PUT(match, ip.frag, - ipv4_key->ipv4_frag, is_mask); - SW_FLOW_KEY_PUT(match, ipv4.addr.src, - ipv4_key->ipv4_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv4.addr.dst, - ipv4_key->ipv4_dst, is_mask); - attrs &= ~(1 << OVS_KEY_ATTR_IPV4); - } - - if (attrs & (1 << OVS_KEY_ATTR_IPV6)) { - const struct ovs_key_ipv6 *ipv6_key; - - ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]); - if (!is_mask && ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) { - OVS_NLERR("Unknown IPv6 fragment type (value=%d, max=%d).\n", - ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX); - return -EINVAL; - } - SW_FLOW_KEY_PUT(match, ipv6.label, - ipv6_key->ipv6_label, is_mask); - SW_FLOW_KEY_PUT(match, ip.proto, - ipv6_key->ipv6_proto, is_mask); - SW_FLOW_KEY_PUT(match, ip.tos, - ipv6_key->ipv6_tclass, is_mask); - SW_FLOW_KEY_PUT(match, ip.ttl, - ipv6_key->ipv6_hlimit, is_mask); - SW_FLOW_KEY_PUT(match, ip.frag, - ipv6_key->ipv6_frag, is_mask); - SW_FLOW_KEY_MEMCPY(match, ipv6.addr.src, - ipv6_key->ipv6_src, - sizeof(match->key->ipv6.addr.src), - is_mask); - SW_FLOW_KEY_MEMCPY(match, ipv6.addr.dst, - ipv6_key->ipv6_dst, - sizeof(match->key->ipv6.addr.dst), - is_mask); - - attrs &= ~(1 << OVS_KEY_ATTR_IPV6); - } - - if (attrs & (1 << OVS_KEY_ATTR_ARP)) { - const struct ovs_key_arp *arp_key; - - arp_key = nla_data(a[OVS_KEY_ATTR_ARP]); - if (!is_mask && (arp_key->arp_op & htons(0xff00))) { - OVS_NLERR("Unknown ARP opcode (opcode=%d).\n", - arp_key->arp_op); - return -EINVAL; - } - - SW_FLOW_KEY_PUT(match, ipv4.addr.src, - arp_key->arp_sip, is_mask); - SW_FLOW_KEY_PUT(match, ipv4.addr.dst, - arp_key->arp_tip, is_mask); - SW_FLOW_KEY_PUT(match, ip.proto, - ntohs(arp_key->arp_op), is_mask); - SW_FLOW_KEY_MEMCPY(match, ipv4.arp.sha, - arp_key->arp_sha, ETH_ALEN, is_mask); - SW_FLOW_KEY_MEMCPY(match, ipv4.arp.tha, - arp_key->arp_tha, ETH_ALEN, is_mask); - - attrs &= ~(1 << OVS_KEY_ATTR_ARP); - } - - if (attrs & (1 << OVS_KEY_ATTR_TCP)) { - const struct ovs_key_tcp *tcp_key; - - tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]); - if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { - SW_FLOW_KEY_PUT(match, ipv4.tp.src, - tcp_key->tcp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv4.tp.dst, - tcp_key->tcp_dst, is_mask); - } else { - SW_FLOW_KEY_PUT(match, ipv6.tp.src, - tcp_key->tcp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv6.tp.dst, - tcp_key->tcp_dst, is_mask); - } - attrs &= ~(1 << OVS_KEY_ATTR_TCP); - } - - if (attrs & (1 << OVS_KEY_ATTR_UDP)) { - const struct ovs_key_udp *udp_key; - - udp_key = nla_data(a[OVS_KEY_ATTR_UDP]); - if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { - SW_FLOW_KEY_PUT(match, ipv4.tp.src, - udp_key->udp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv4.tp.dst, - udp_key->udp_dst, is_mask); - } else { - SW_FLOW_KEY_PUT(match, ipv6.tp.src, - udp_key->udp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv6.tp.dst, - udp_key->udp_dst, is_mask); - } - attrs &= ~(1 << OVS_KEY_ATTR_UDP); - } - - if (attrs & (1 << OVS_KEY_ATTR_SCTP)) { - const struct ovs_key_sctp *sctp_key; - - sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]); - if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { - SW_FLOW_KEY_PUT(match, ipv4.tp.src, - sctp_key->sctp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv4.tp.dst, - sctp_key->sctp_dst, is_mask); - } else { - SW_FLOW_KEY_PUT(match, ipv6.tp.src, - sctp_key->sctp_src, is_mask); - SW_FLOW_KEY_PUT(match, ipv6.tp.dst, - sctp_key->sctp_dst, is_mask); - } - attrs &= ~(1 << OVS_KEY_ATTR_SCTP); - } - - if (attrs & (1 << OVS_KEY_ATTR_ICMP)) { - const struct ovs_key_icmp *icmp_key; - - icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]); - SW_FLOW_KEY_PUT(match, ipv4.tp.src, - htons(icmp_key->icmp_type), is_mask); - SW_FLOW_KEY_PUT(match, ipv4.tp.dst, - htons(icmp_key->icmp_code), is_mask); - attrs &= ~(1 << OVS_KEY_ATTR_ICMP); - } - - if (attrs & (1 << OVS_KEY_ATTR_ICMPV6)) { - const struct ovs_key_icmpv6 *icmpv6_key; - - icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]); - SW_FLOW_KEY_PUT(match, ipv6.tp.src, - htons(icmpv6_key->icmpv6_type), is_mask); - SW_FLOW_KEY_PUT(match, ipv6.tp.dst, - htons(icmpv6_key->icmpv6_code), is_mask); - attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6); - } - - if (attrs & (1 << OVS_KEY_ATTR_ND)) { - const struct ovs_key_nd *nd_key; - - nd_key = nla_data(a[OVS_KEY_ATTR_ND]); - SW_FLOW_KEY_MEMCPY(match, ipv6.nd.target, - nd_key->nd_target, - sizeof(match->key->ipv6.nd.target), - is_mask); - SW_FLOW_KEY_MEMCPY(match, ipv6.nd.sll, - nd_key->nd_sll, ETH_ALEN, is_mask); - SW_FLOW_KEY_MEMCPY(match, ipv6.nd.tll, - nd_key->nd_tll, ETH_ALEN, is_mask); - attrs &= ~(1 << OVS_KEY_ATTR_ND); - } - - if (attrs != 0) - return -EINVAL; - - return 0; -} - -/** - * ovs_match_from_nlattrs - parses Netlink attributes into a flow key and - * mask. In case the 'mask' is NULL, the flow is treated as exact match - * flow. Otherwise, it is treated as a wildcarded flow, except the mask - * does not include any don't care bit. - * @match: receives the extracted flow match information. - * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute - * sequence. The fields should of the packet that triggered the creation - * of this flow. - * @mask: Optional. Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink - * attribute specifies the mask field of the wildcarded flow. - */ -int ovs_match_from_nlattrs(struct sw_flow_match *match, - const struct nlattr *key, - const struct nlattr *mask) -{ - const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; - const struct nlattr *encap; - u64 key_attrs = 0; - u64 mask_attrs = 0; - bool encap_valid = false; - int err; - - err = parse_flow_nlattrs(key, a, &key_attrs); - if (err) - return err; - - if ((key_attrs & (1 << OVS_KEY_ATTR_ETHERNET)) && - (key_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) && - (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) { - __be16 tci; - - if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) && - (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) { - OVS_NLERR("Invalid Vlan frame.\n"); - return -EINVAL; - } - - key_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); - tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); - encap = a[OVS_KEY_ATTR_ENCAP]; - key_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP); - encap_valid = true; - - if (tci & htons(VLAN_TAG_PRESENT)) { - err = parse_flow_nlattrs(encap, a, &key_attrs); - if (err) - return err; - } else if (!tci) { - /* Corner case for truncated 802.1Q header. */ - if (nla_len(encap)) { - OVS_NLERR("Truncated 802.1Q header has non-zero encap attribute.\n"); - return -EINVAL; - } - } else { - OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n"); - return -EINVAL; - } - } - - err = ovs_key_from_nlattrs(match, key_attrs, a, false); - if (err) - return err; - - if (mask) { - err = parse_flow_mask_nlattrs(mask, a, &mask_attrs); - if (err) - return err; - - if (mask_attrs & 1ULL << OVS_KEY_ATTR_ENCAP) { - __be16 eth_type = 0; - __be16 tci = 0; - - if (!encap_valid) { - OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n"); - return -EINVAL; - } - - mask_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP); - if (a[OVS_KEY_ATTR_ETHERTYPE]) - eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); - - if (eth_type == htons(0xffff)) { - mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); - encap = a[OVS_KEY_ATTR_ENCAP]; - err = parse_flow_mask_nlattrs(encap, a, &mask_attrs); - } else { - OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n", - ntohs(eth_type)); - return -EINVAL; - } - - if (a[OVS_KEY_ATTR_VLAN]) - tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); - - if (!(tci & htons(VLAN_TAG_PRESENT))) { - OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci)); - return -EINVAL; - } - } - - err = ovs_key_from_nlattrs(match, mask_attrs, a, true); - if (err) - return err; - } else { - /* Populate exact match flow's key mask. */ - if (match->mask) - ovs_sw_flow_mask_set(match->mask, &match->range, 0xff); - } - - if (!ovs_match_validate(match, key_attrs, mask_attrs)) - return -EINVAL; - - return 0; -} - -/** - * ovs_flow_metadata_from_nlattrs - parses Netlink attributes into a flow key. - * @flow: Receives extracted in_port, priority, tun_key and skb_mark. - * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute - * sequence. - * - * This parses a series of Netlink attributes that form a flow key, which must - * take the same form accepted by flow_from_nlattrs(), but only enough of it to - * get the metadata, that is, the parts of the flow key that cannot be - * extracted from the packet itself. - */ - -int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, - const struct nlattr *attr) -{ - struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key; - const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; - u64 attrs = 0; - int err; - struct sw_flow_match match; - - flow->key.phy.in_port = DP_MAX_PORTS; - flow->key.phy.priority = 0; - flow->key.phy.skb_mark = 0; - memset(tun_key, 0, sizeof(flow->key.tun_key)); - - err = parse_flow_nlattrs(attr, a, &attrs); - if (err) - return -EINVAL; - - memset(&match, 0, sizeof(match)); - match.key = &flow->key; - - err = metadata_from_nlattrs(&match, &attrs, a, false); - if (err) - return err; - - return 0; -} - -int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, - const struct sw_flow_key *output, struct sk_buff *skb) -{ - struct ovs_key_ethernet *eth_key; - struct nlattr *nla, *encap; - bool is_mask = (swkey != output); - - if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority)) - goto nla_put_failure; - - if ((swkey->tun_key.ipv4_dst || is_mask) && - ovs_ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key)) - goto nla_put_failure; - - if (swkey->phy.in_port == DP_MAX_PORTS) { - if (is_mask && (output->phy.in_port == 0xffff)) - if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff)) - goto nla_put_failure; - } else { - u16 upper_u16; - upper_u16 = !is_mask ? 0 : 0xffff; - - if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, - (upper_u16 << 16) | output->phy.in_port)) - goto nla_put_failure; - } - - if (nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark)) - goto nla_put_failure; - - nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); - if (!nla) - goto nla_put_failure; - - eth_key = nla_data(nla); - memcpy(eth_key->eth_src, output->eth.src, ETH_ALEN); - memcpy(eth_key->eth_dst, output->eth.dst, ETH_ALEN); - - if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) { - __be16 eth_type; - eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff); - if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) || - nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci)) - goto nla_put_failure; - encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); - if (!swkey->eth.tci) - goto unencap; - } else - encap = NULL; - - if (swkey->eth.type == htons(ETH_P_802_2)) { - /* - * Ethertype 802.2 is represented in the netlink with omitted - * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and - * 0xffff in the mask attribute. Ethertype can also - * be wildcarded. - */ - if (is_mask && output->eth.type) - if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, - output->eth.type)) - goto nla_put_failure; - goto unencap; - } - - if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type)) - goto nla_put_failure; - - if (swkey->eth.type == htons(ETH_P_IP)) { - struct ovs_key_ipv4 *ipv4_key; - - nla = nla_reserve(skb, OVS_KEY_ATTR_IPV4, sizeof(*ipv4_key)); - if (!nla) - goto nla_put_failure; - ipv4_key = nla_data(nla); - ipv4_key->ipv4_src = output->ipv4.addr.src; - ipv4_key->ipv4_dst = output->ipv4.addr.dst; - ipv4_key->ipv4_proto = output->ip.proto; - ipv4_key->ipv4_tos = output->ip.tos; - ipv4_key->ipv4_ttl = output->ip.ttl; - ipv4_key->ipv4_frag = output->ip.frag; - } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - struct ovs_key_ipv6 *ipv6_key; - - nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key)); - if (!nla) - goto nla_put_failure; - ipv6_key = nla_data(nla); - memcpy(ipv6_key->ipv6_src, &output->ipv6.addr.src, - sizeof(ipv6_key->ipv6_src)); - memcpy(ipv6_key->ipv6_dst, &output->ipv6.addr.dst, - sizeof(ipv6_key->ipv6_dst)); - ipv6_key->ipv6_label = output->ipv6.label; - ipv6_key->ipv6_proto = output->ip.proto; - ipv6_key->ipv6_tclass = output->ip.tos; - ipv6_key->ipv6_hlimit = output->ip.ttl; - ipv6_key->ipv6_frag = output->ip.frag; - } else if (swkey->eth.type == htons(ETH_P_ARP) || - swkey->eth.type == htons(ETH_P_RARP)) { - struct ovs_key_arp *arp_key; - - nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key)); - if (!nla) - goto nla_put_failure; - arp_key = nla_data(nla); - memset(arp_key, 0, sizeof(struct ovs_key_arp)); - arp_key->arp_sip = output->ipv4.addr.src; - arp_key->arp_tip = output->ipv4.addr.dst; - arp_key->arp_op = htons(output->ip.proto); - memcpy(arp_key->arp_sha, output->ipv4.arp.sha, ETH_ALEN); - memcpy(arp_key->arp_tha, output->ipv4.arp.tha, ETH_ALEN); - } - - if ((swkey->eth.type == htons(ETH_P_IP) || - swkey->eth.type == htons(ETH_P_IPV6)) && - swkey->ip.frag != OVS_FRAG_TYPE_LATER) { - - if (swkey->ip.proto == IPPROTO_TCP) { - struct ovs_key_tcp *tcp_key; - - nla = nla_reserve(skb, OVS_KEY_ATTR_TCP, sizeof(*tcp_key)); - if (!nla) - goto nla_put_failure; - tcp_key = nla_data(nla); - if (swkey->eth.type == htons(ETH_P_IP)) { - tcp_key->tcp_src = output->ipv4.tp.src; - tcp_key->tcp_dst = output->ipv4.tp.dst; - } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - tcp_key->tcp_src = output->ipv6.tp.src; - tcp_key->tcp_dst = output->ipv6.tp.dst; - } - } else if (swkey->ip.proto == IPPROTO_UDP) { - struct ovs_key_udp *udp_key; - - nla = nla_reserve(skb, OVS_KEY_ATTR_UDP, sizeof(*udp_key)); - if (!nla) - goto nla_put_failure; - udp_key = nla_data(nla); - if (swkey->eth.type == htons(ETH_P_IP)) { - udp_key->udp_src = output->ipv4.tp.src; - udp_key->udp_dst = output->ipv4.tp.dst; - } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - udp_key->udp_src = output->ipv6.tp.src; - udp_key->udp_dst = output->ipv6.tp.dst; - } - } else if (swkey->ip.proto == IPPROTO_SCTP) { - struct ovs_key_sctp *sctp_key; - - nla = nla_reserve(skb, OVS_KEY_ATTR_SCTP, sizeof(*sctp_key)); - if (!nla) - goto nla_put_failure; - sctp_key = nla_data(nla); - if (swkey->eth.type == htons(ETH_P_IP)) { - sctp_key->sctp_src = swkey->ipv4.tp.src; - sctp_key->sctp_dst = swkey->ipv4.tp.dst; - } else if (swkey->eth.type == htons(ETH_P_IPV6)) { - sctp_key->sctp_src = swkey->ipv6.tp.src; - sctp_key->sctp_dst = swkey->ipv6.tp.dst; - } - } else if (swkey->eth.type == htons(ETH_P_IP) && - swkey->ip.proto == IPPROTO_ICMP) { - struct ovs_key_icmp *icmp_key; - - nla = nla_reserve(skb, OVS_KEY_ATTR_ICMP, sizeof(*icmp_key)); - if (!nla) - goto nla_put_failure; - icmp_key = nla_data(nla); - icmp_key->icmp_type = ntohs(output->ipv4.tp.src); - icmp_key->icmp_code = ntohs(output->ipv4.tp.dst); - } else if (swkey->eth.type == htons(ETH_P_IPV6) && - swkey->ip.proto == IPPROTO_ICMPV6) { - struct ovs_key_icmpv6 *icmpv6_key; - - nla = nla_reserve(skb, OVS_KEY_ATTR_ICMPV6, - sizeof(*icmpv6_key)); - if (!nla) - goto nla_put_failure; - icmpv6_key = nla_data(nla); - icmpv6_key->icmpv6_type = ntohs(output->ipv6.tp.src); - icmpv6_key->icmpv6_code = ntohs(output->ipv6.tp.dst); - - if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION || - icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) { - struct ovs_key_nd *nd_key; - - nla = nla_reserve(skb, OVS_KEY_ATTR_ND, sizeof(*nd_key)); - if (!nla) - goto nla_put_failure; - nd_key = nla_data(nla); - memcpy(nd_key->nd_target, &output->ipv6.nd.target, - sizeof(nd_key->nd_target)); - memcpy(nd_key->nd_sll, output->ipv6.nd.sll, ETH_ALEN); - memcpy(nd_key->nd_tll, output->ipv6.nd.tll, ETH_ALEN); - } - } - } - -unencap: - if (encap) - nla_nest_end(skb, encap); - - return 0; - -nla_put_failure: - return -EMSGSIZE; -} - -/* Initializes the flow module. - * Returns zero if successful or a negative error code. */ -int ovs_flow_init(void) -{ - BUILD_BUG_ON(__alignof__(struct sw_flow_key) % __alignof__(long)); - BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long)); - - flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0, - 0, NULL); - if (flow_cache == NULL) - return -ENOMEM; - - return 0; -} - -/* Uninitializes the flow module. */ -void ovs_flow_exit(void) -{ - kmem_cache_destroy(flow_cache); -} - -struct sw_flow_mask *ovs_sw_flow_mask_alloc(void) -{ - struct sw_flow_mask *mask; - - mask = kmalloc(sizeof(*mask), GFP_KERNEL); - if (mask) - mask->ref_count = 0; - - return mask; -} - -void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *mask) -{ - mask->ref_count++; -} - -void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred) -{ - if (!mask) - return; - - BUG_ON(!mask->ref_count); - mask->ref_count--; - - if (!mask->ref_count) { - list_del_rcu(&mask->list); - if (deferred) - kfree_rcu(mask, rcu); - else - kfree(mask); - } -} - -static bool ovs_sw_flow_mask_equal(const struct sw_flow_mask *a, - const struct sw_flow_mask *b) -{ - u8 *a_ = (u8 *)&a->key + a->range.start; - u8 *b_ = (u8 *)&b->key + b->range.start; - - return (a->range.end == b->range.end) - && (a->range.start == b->range.start) - && (memcmp(a_, b_, range_n_bytes(&a->range)) == 0); -} - -struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *tbl, - const struct sw_flow_mask *mask) -{ - struct list_head *ml; - - list_for_each(ml, tbl->mask_list) { - struct sw_flow_mask *m; - m = container_of(ml, struct sw_flow_mask, list); - if (ovs_sw_flow_mask_equal(mask, m)) - return m; - } - - return NULL; -} - -/** - * add a new mask into the mask list. - * The caller needs to make sure that 'mask' is not the same - * as any masks that are already on the list. - */ -void ovs_sw_flow_mask_insert(struct flow_table *tbl, struct sw_flow_mask *mask) -{ - list_add_rcu(&mask->list, tbl->mask_list); -} - -/** - * Set 'range' fields in the mask to the value of 'val'. - */ -static void ovs_sw_flow_mask_set(struct sw_flow_mask *mask, - struct sw_flow_key_range *range, u8 val) -{ - u8 *m = (u8 *)&mask->key + range->start; - - mask->range = *range; - memset(m, val, range_n_bytes(range)); -} diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 212fbf7510c42d02dc13915024839843ba498c96..1510f51dbf74b9f3850ac3ef39fc3ec5d31e6dd3 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -33,14 +33,6 @@ #include struct sk_buff; -struct sw_flow_mask; -struct flow_table; - -struct sw_flow_actions { - struct rcu_head rcu; - u32 actions_len; - struct nlattr actions[]; -}; /* Used to memset ovs_key_ipv4_tunnel padding. */ #define OVS_TUNNEL_KEY_SIZE \ @@ -101,6 +93,7 @@ struct sw_flow_key { struct { __be16 src; /* TCP/UDP/SCTP source port. */ __be16 dst; /* TCP/UDP/SCTP destination port. */ + __be16 flags; /* TCP flags. */ } tp; struct { u8 sha[ETH_ALEN]; /* ARP source hardware address. */ @@ -117,6 +110,7 @@ struct sw_flow_key { struct { __be16 src; /* TCP/UDP/SCTP source port. */ __be16 dst; /* TCP/UDP/SCTP destination port. */ + __be16 flags; /* TCP flags. */ } tp; struct { struct in6_addr target; /* ND target address. */ @@ -127,6 +121,31 @@ struct sw_flow_key { }; } __aligned(BITS_PER_LONG/8); /* Ensure that we can do comparisons as longs. */ +struct sw_flow_key_range { + size_t start; + size_t end; +}; + +struct sw_flow_mask { + int ref_count; + struct rcu_head rcu; + struct list_head list; + struct sw_flow_key_range range; + struct sw_flow_key key; +}; + +struct sw_flow_match { + struct sw_flow_key *key; + struct sw_flow_key_range range; + struct sw_flow_mask *mask; +}; + +struct sw_flow_actions { + struct rcu_head rcu; + u32 actions_len; + struct nlattr actions[]; +}; + struct sw_flow { struct rcu_head rcu; struct hlist_node hash_node[2]; @@ -141,23 +160,9 @@ struct sw_flow { unsigned long used; /* Last used time (in jiffies). */ u64 packet_count; /* Number of packets matched. */ u64 byte_count; /* Number of bytes matched. */ - u8 tcp_flags; /* Union of seen TCP flags. */ -}; - -struct sw_flow_key_range { - size_t start; - size_t end; + __be16 tcp_flags; /* Union of seen TCP flags. */ }; -struct sw_flow_match { - struct sw_flow_key *key; - struct sw_flow_key_range range; - struct sw_flow_mask *mask; -}; - -void ovs_match_init(struct sw_flow_match *match, - struct sw_flow_key *key, struct sw_flow_mask *mask); - struct arp_eth_header { __be16 ar_hrd; /* format of hardware address */ __be16 ar_pro; /* format of protocol address */ @@ -172,88 +177,9 @@ struct arp_eth_header { unsigned char ar_tip[4]; /* target IP address */ } __packed; -int ovs_flow_init(void); -void ovs_flow_exit(void); - -struct sw_flow *ovs_flow_alloc(void); -void ovs_flow_deferred_free(struct sw_flow *); -void ovs_flow_free(struct sw_flow *, bool deferred); - -struct sw_flow_actions *ovs_flow_actions_alloc(int actions_len); -void ovs_flow_deferred_free_acts(struct sw_flow_actions *); - -int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *); void ovs_flow_used(struct sw_flow *, struct sk_buff *); u64 ovs_flow_used_time(unsigned long flow_jiffies); -int ovs_flow_to_nlattrs(const struct sw_flow_key *, - const struct sw_flow_key *, struct sk_buff *); -int ovs_match_from_nlattrs(struct sw_flow_match *match, - const struct nlattr *, - const struct nlattr *); -int ovs_flow_metadata_from_nlattrs(struct sw_flow *flow, - const struct nlattr *attr); -#define MAX_ACTIONS_BUFSIZE (32 * 1024) -#define TBL_MIN_BUCKETS 1024 - -struct flow_table { - struct flex_array *buckets; - unsigned int count, n_buckets; - struct rcu_head rcu; - struct list_head *mask_list; - int node_ver; - u32 hash_seed; - bool keep_flows; -}; - -static inline int ovs_flow_tbl_count(struct flow_table *table) -{ - return table->count; -} - -static inline int ovs_flow_tbl_need_to_expand(struct flow_table *table) -{ - return (table->count > table->n_buckets); -} - -struct sw_flow *ovs_flow_lookup(struct flow_table *, - const struct sw_flow_key *); -struct sw_flow *ovs_flow_lookup_unmasked_key(struct flow_table *table, - struct sw_flow_match *match); - -void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred); -struct flow_table *ovs_flow_tbl_alloc(int new_size); -struct flow_table *ovs_flow_tbl_expand(struct flow_table *table); -struct flow_table *ovs_flow_tbl_rehash(struct flow_table *table); - -void ovs_flow_insert(struct flow_table *table, struct sw_flow *flow); -void ovs_flow_remove(struct flow_table *table, struct sw_flow *flow); - -struct sw_flow *ovs_flow_dump_next(struct flow_table *table, u32 *bucket, u32 *idx); -extern const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1]; -int ovs_ipv4_tun_from_nlattr(const struct nlattr *attr, - struct sw_flow_match *match, bool is_mask); -int ovs_ipv4_tun_to_nlattr(struct sk_buff *skb, - const struct ovs_key_ipv4_tunnel *tun_key, - const struct ovs_key_ipv4_tunnel *output); - -bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, - const struct sw_flow_key *key, int key_end); - -struct sw_flow_mask { - int ref_count; - struct rcu_head rcu; - struct list_head list; - struct sw_flow_key_range range; - struct sw_flow_key key; -}; +int ovs_flow_extract(struct sk_buff *, u16 in_port, struct sw_flow_key *); -struct sw_flow_mask *ovs_sw_flow_mask_alloc(void); -void ovs_sw_flow_mask_add_ref(struct sw_flow_mask *); -void ovs_sw_flow_mask_del_ref(struct sw_flow_mask *, bool deferred); -void ovs_sw_flow_mask_insert(struct flow_table *, struct sw_flow_mask *); -struct sw_flow_mask *ovs_sw_flow_mask_find(const struct flow_table *, - const struct sw_flow_mask *); -void ovs_flow_key_mask(struct sw_flow_key *dst, const struct sw_flow_key *src, - const struct sw_flow_mask *mask); #endif /* flow.h */ diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c new file mode 100644 index 0000000000000000000000000000000000000000..2bc1bc1aca3bfc2fd149ba88c86b4eaf88aed00e --- /dev/null +++ b/net/openvswitch/flow_netlink.c @@ -0,0 +1,1630 @@ +/* + * Copyright (c) 2007-2013 Nicira, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "flow.h" +#include "datapath.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "flow_netlink.h" + +static void update_range__(struct sw_flow_match *match, + size_t offset, size_t size, bool is_mask) +{ + struct sw_flow_key_range *range = NULL; + size_t start = rounddown(offset, sizeof(long)); + size_t end = roundup(offset + size, sizeof(long)); + + if (!is_mask) + range = &match->range; + else if (match->mask) + range = &match->mask->range; + + if (!range) + return; + + if (range->start == range->end) { + range->start = start; + range->end = end; + return; + } + + if (range->start > start) + range->start = start; + + if (range->end < end) + range->end = end; +} + +#define SW_FLOW_KEY_PUT(match, field, value, is_mask) \ + do { \ + update_range__(match, offsetof(struct sw_flow_key, field), \ + sizeof((match)->key->field), is_mask); \ + if (is_mask) { \ + if ((match)->mask) \ + (match)->mask->key.field = value; \ + } else { \ + (match)->key->field = value; \ + } \ + } while (0) + +#define SW_FLOW_KEY_MEMCPY(match, field, value_p, len, is_mask) \ + do { \ + update_range__(match, offsetof(struct sw_flow_key, field), \ + len, is_mask); \ + if (is_mask) { \ + if ((match)->mask) \ + memcpy(&(match)->mask->key.field, value_p, len);\ + } else { \ + memcpy(&(match)->key->field, value_p, len); \ + } \ + } while (0) + +static u16 range_n_bytes(const struct sw_flow_key_range *range) +{ + return range->end - range->start; +} + +static bool match_validate(const struct sw_flow_match *match, + u64 key_attrs, u64 mask_attrs) +{ + u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET; + u64 mask_allowed = key_attrs; /* At most allow all key attributes */ + + /* The following mask attributes allowed only if they + * pass the validation tests. */ + mask_allowed &= ~((1 << OVS_KEY_ATTR_IPV4) + | (1 << OVS_KEY_ATTR_IPV6) + | (1 << OVS_KEY_ATTR_TCP) + | (1 << OVS_KEY_ATTR_TCP_FLAGS) + | (1 << OVS_KEY_ATTR_UDP) + | (1 << OVS_KEY_ATTR_SCTP) + | (1 << OVS_KEY_ATTR_ICMP) + | (1 << OVS_KEY_ATTR_ICMPV6) + | (1 << OVS_KEY_ATTR_ARP) + | (1 << OVS_KEY_ATTR_ND)); + + /* Always allowed mask fields. */ + mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL) + | (1 << OVS_KEY_ATTR_IN_PORT) + | (1 << OVS_KEY_ATTR_ETHERTYPE)); + + /* Check key attributes. */ + if (match->key->eth.type == htons(ETH_P_ARP) + || match->key->eth.type == htons(ETH_P_RARP)) { + key_expected |= 1 << OVS_KEY_ATTR_ARP; + if (match->mask && (match->mask->key.eth.type == htons(0xffff))) + mask_allowed |= 1 << OVS_KEY_ATTR_ARP; + } + + if (match->key->eth.type == htons(ETH_P_IP)) { + key_expected |= 1 << OVS_KEY_ATTR_IPV4; + if (match->mask && (match->mask->key.eth.type == htons(0xffff))) + mask_allowed |= 1 << OVS_KEY_ATTR_IPV4; + + if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) { + if (match->key->ip.proto == IPPROTO_UDP) { + key_expected |= 1 << OVS_KEY_ATTR_UDP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_UDP; + } + + if (match->key->ip.proto == IPPROTO_SCTP) { + key_expected |= 1 << OVS_KEY_ATTR_SCTP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_SCTP; + } + + if (match->key->ip.proto == IPPROTO_TCP) { + key_expected |= 1 << OVS_KEY_ATTR_TCP; + key_expected |= 1 << OVS_KEY_ATTR_TCP_FLAGS; + if (match->mask && (match->mask->key.ip.proto == 0xff)) { + mask_allowed |= 1 << OVS_KEY_ATTR_TCP; + mask_allowed |= 1 << OVS_KEY_ATTR_TCP_FLAGS; + } + } + + if (match->key->ip.proto == IPPROTO_ICMP) { + key_expected |= 1 << OVS_KEY_ATTR_ICMP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_ICMP; + } + } + } + + if (match->key->eth.type == htons(ETH_P_IPV6)) { + key_expected |= 1 << OVS_KEY_ATTR_IPV6; + if (match->mask && (match->mask->key.eth.type == htons(0xffff))) + mask_allowed |= 1 << OVS_KEY_ATTR_IPV6; + + if (match->key->ip.frag != OVS_FRAG_TYPE_LATER) { + if (match->key->ip.proto == IPPROTO_UDP) { + key_expected |= 1 << OVS_KEY_ATTR_UDP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_UDP; + } + + if (match->key->ip.proto == IPPROTO_SCTP) { + key_expected |= 1 << OVS_KEY_ATTR_SCTP; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_SCTP; + } + + if (match->key->ip.proto == IPPROTO_TCP) { + key_expected |= 1 << OVS_KEY_ATTR_TCP; + key_expected |= 1 << OVS_KEY_ATTR_TCP_FLAGS; + if (match->mask && (match->mask->key.ip.proto == 0xff)) { + mask_allowed |= 1 << OVS_KEY_ATTR_TCP; + mask_allowed |= 1 << OVS_KEY_ATTR_TCP_FLAGS; + } + } + + if (match->key->ip.proto == IPPROTO_ICMPV6) { + key_expected |= 1 << OVS_KEY_ATTR_ICMPV6; + if (match->mask && (match->mask->key.ip.proto == 0xff)) + mask_allowed |= 1 << OVS_KEY_ATTR_ICMPV6; + + if (match->key->ipv6.tp.src == + htons(NDISC_NEIGHBOUR_SOLICITATION) || + match->key->ipv6.tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) { + key_expected |= 1 << OVS_KEY_ATTR_ND; + if (match->mask && (match->mask->key.ipv6.tp.src == htons(0xffff))) + mask_allowed |= 1 << OVS_KEY_ATTR_ND; + } + } + } + } + + if ((key_attrs & key_expected) != key_expected) { + /* Key attributes check failed. */ + OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n", + key_attrs, key_expected); + return false; + } + + if ((mask_attrs & mask_allowed) != mask_attrs) { + /* Mask attributes check failed. */ + OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n", + mask_attrs, mask_allowed); + return false; + } + + return true; +} + +/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */ +static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { + [OVS_KEY_ATTR_ENCAP] = -1, + [OVS_KEY_ATTR_PRIORITY] = sizeof(u32), + [OVS_KEY_ATTR_IN_PORT] = sizeof(u32), + [OVS_KEY_ATTR_SKB_MARK] = sizeof(u32), + [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet), + [OVS_KEY_ATTR_VLAN] = sizeof(__be16), + [OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16), + [OVS_KEY_ATTR_IPV4] = sizeof(struct ovs_key_ipv4), + [OVS_KEY_ATTR_IPV6] = sizeof(struct ovs_key_ipv6), + [OVS_KEY_ATTR_TCP] = sizeof(struct ovs_key_tcp), + [OVS_KEY_ATTR_TCP_FLAGS] = sizeof(__be16), + [OVS_KEY_ATTR_UDP] = sizeof(struct ovs_key_udp), + [OVS_KEY_ATTR_SCTP] = sizeof(struct ovs_key_sctp), + [OVS_KEY_ATTR_ICMP] = sizeof(struct ovs_key_icmp), + [OVS_KEY_ATTR_ICMPV6] = sizeof(struct ovs_key_icmpv6), + [OVS_KEY_ATTR_ARP] = sizeof(struct ovs_key_arp), + [OVS_KEY_ATTR_ND] = sizeof(struct ovs_key_nd), + [OVS_KEY_ATTR_TUNNEL] = -1, +}; + +static bool is_all_zero(const u8 *fp, size_t size) +{ + int i; + + if (!fp) + return false; + + for (i = 0; i < size; i++) + if (fp[i]) + return false; + + return true; +} + +static int __parse_flow_nlattrs(const struct nlattr *attr, + const struct nlattr *a[], + u64 *attrsp, bool nz) +{ + const struct nlattr *nla; + u64 attrs; + int rem; + + attrs = *attrsp; + nla_for_each_nested(nla, attr, rem) { + u16 type = nla_type(nla); + int expected_len; + + if (type > OVS_KEY_ATTR_MAX) { + OVS_NLERR("Unknown key attribute (type=%d, max=%d).\n", + type, OVS_KEY_ATTR_MAX); + return -EINVAL; + } + + if (attrs & (1 << type)) { + OVS_NLERR("Duplicate key attribute (type %d).\n", type); + return -EINVAL; + } + + expected_len = ovs_key_lens[type]; + if (nla_len(nla) != expected_len && expected_len != -1) { + OVS_NLERR("Key attribute has unexpected length (type=%d" + ", length=%d, expected=%d).\n", type, + nla_len(nla), expected_len); + return -EINVAL; + } + + if (!nz || !is_all_zero(nla_data(nla), expected_len)) { + attrs |= 1 << type; + a[type] = nla; + } + } + if (rem) { + OVS_NLERR("Message has %d unknown bytes.\n", rem); + return -EINVAL; + } + + *attrsp = attrs; + return 0; +} + +static int parse_flow_mask_nlattrs(const struct nlattr *attr, + const struct nlattr *a[], u64 *attrsp) +{ + return __parse_flow_nlattrs(attr, a, attrsp, true); +} + +static int parse_flow_nlattrs(const struct nlattr *attr, + const struct nlattr *a[], u64 *attrsp) +{ + return __parse_flow_nlattrs(attr, a, attrsp, false); +} + +static int ipv4_tun_from_nlattr(const struct nlattr *attr, + struct sw_flow_match *match, bool is_mask) +{ + struct nlattr *a; + int rem; + bool ttl = false; + __be16 tun_flags = 0; + + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + static const u32 ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = { + [OVS_TUNNEL_KEY_ATTR_ID] = sizeof(u64), + [OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = sizeof(u32), + [OVS_TUNNEL_KEY_ATTR_IPV4_DST] = sizeof(u32), + [OVS_TUNNEL_KEY_ATTR_TOS] = 1, + [OVS_TUNNEL_KEY_ATTR_TTL] = 1, + [OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0, + [OVS_TUNNEL_KEY_ATTR_CSUM] = 0, + }; + + if (type > OVS_TUNNEL_KEY_ATTR_MAX) { + OVS_NLERR("Unknown IPv4 tunnel attribute (type=%d, max=%d).\n", + type, OVS_TUNNEL_KEY_ATTR_MAX); + return -EINVAL; + } + + if (ovs_tunnel_key_lens[type] != nla_len(a)) { + OVS_NLERR("IPv4 tunnel attribute type has unexpected " + " length (type=%d, length=%d, expected=%d).\n", + type, nla_len(a), ovs_tunnel_key_lens[type]); + return -EINVAL; + } + + switch (type) { + case OVS_TUNNEL_KEY_ATTR_ID: + SW_FLOW_KEY_PUT(match, tun_key.tun_id, + nla_get_be64(a), is_mask); + tun_flags |= TUNNEL_KEY; + break; + case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: + SW_FLOW_KEY_PUT(match, tun_key.ipv4_src, + nla_get_be32(a), is_mask); + break; + case OVS_TUNNEL_KEY_ATTR_IPV4_DST: + SW_FLOW_KEY_PUT(match, tun_key.ipv4_dst, + nla_get_be32(a), is_mask); + break; + case OVS_TUNNEL_KEY_ATTR_TOS: + SW_FLOW_KEY_PUT(match, tun_key.ipv4_tos, + nla_get_u8(a), is_mask); + break; + case OVS_TUNNEL_KEY_ATTR_TTL: + SW_FLOW_KEY_PUT(match, tun_key.ipv4_ttl, + nla_get_u8(a), is_mask); + ttl = true; + break; + case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: + tun_flags |= TUNNEL_DONT_FRAGMENT; + break; + case OVS_TUNNEL_KEY_ATTR_CSUM: + tun_flags |= TUNNEL_CSUM; + break; + default: + return -EINVAL; + } + } + + SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask); + + if (rem > 0) { + OVS_NLERR("IPv4 tunnel attribute has %d unknown bytes.\n", rem); + return -EINVAL; + } + + if (!is_mask) { + if (!match->key->tun_key.ipv4_dst) { + OVS_NLERR("IPv4 tunnel destination address is zero.\n"); + return -EINVAL; + } + + if (!ttl) { + OVS_NLERR("IPv4 tunnel TTL not specified.\n"); + return -EINVAL; + } + } + + return 0; +} + +static int ipv4_tun_to_nlattr(struct sk_buff *skb, + const struct ovs_key_ipv4_tunnel *tun_key, + const struct ovs_key_ipv4_tunnel *output) +{ + struct nlattr *nla; + + nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL); + if (!nla) + return -EMSGSIZE; + + if (output->tun_flags & TUNNEL_KEY && + nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id)) + return -EMSGSIZE; + if (output->ipv4_src && + nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, output->ipv4_src)) + return -EMSGSIZE; + if (output->ipv4_dst && + nla_put_be32(skb, OVS_TUNNEL_KEY_ATTR_IPV4_DST, output->ipv4_dst)) + return -EMSGSIZE; + if (output->ipv4_tos && + nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TOS, output->ipv4_tos)) + return -EMSGSIZE; + if (nla_put_u8(skb, OVS_TUNNEL_KEY_ATTR_TTL, output->ipv4_ttl)) + return -EMSGSIZE; + if ((output->tun_flags & TUNNEL_DONT_FRAGMENT) && + nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT)) + return -EMSGSIZE; + if ((output->tun_flags & TUNNEL_CSUM) && + nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM)) + return -EMSGSIZE; + + nla_nest_end(skb, nla); + return 0; +} + + +static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs, + const struct nlattr **a, bool is_mask) +{ + if (*attrs & (1 << OVS_KEY_ATTR_PRIORITY)) { + SW_FLOW_KEY_PUT(match, phy.priority, + nla_get_u32(a[OVS_KEY_ATTR_PRIORITY]), is_mask); + *attrs &= ~(1 << OVS_KEY_ATTR_PRIORITY); + } + + if (*attrs & (1 << OVS_KEY_ATTR_IN_PORT)) { + u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]); + + if (is_mask) + in_port = 0xffffffff; /* Always exact match in_port. */ + else if (in_port >= DP_MAX_PORTS) + return -EINVAL; + + SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask); + *attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT); + } else if (!is_mask) { + SW_FLOW_KEY_PUT(match, phy.in_port, DP_MAX_PORTS, is_mask); + } + + if (*attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) { + uint32_t mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]); + + SW_FLOW_KEY_PUT(match, phy.skb_mark, mark, is_mask); + *attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK); + } + if (*attrs & (1 << OVS_KEY_ATTR_TUNNEL)) { + if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match, + is_mask)) + return -EINVAL; + *attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL); + } + return 0; +} + +static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs, + const struct nlattr **a, bool is_mask) +{ + int err; + u64 orig_attrs = attrs; + + err = metadata_from_nlattrs(match, &attrs, a, is_mask); + if (err) + return err; + + if (attrs & (1 << OVS_KEY_ATTR_ETHERNET)) { + const struct ovs_key_ethernet *eth_key; + + eth_key = nla_data(a[OVS_KEY_ATTR_ETHERNET]); + SW_FLOW_KEY_MEMCPY(match, eth.src, + eth_key->eth_src, ETH_ALEN, is_mask); + SW_FLOW_KEY_MEMCPY(match, eth.dst, + eth_key->eth_dst, ETH_ALEN, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ETHERNET); + } + + if (attrs & (1 << OVS_KEY_ATTR_VLAN)) { + __be16 tci; + + tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); + if (!(tci & htons(VLAN_TAG_PRESENT))) { + if (is_mask) + OVS_NLERR("VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.\n"); + else + OVS_NLERR("VLAN TCI does not have VLAN_TAG_PRESENT bit set.\n"); + + return -EINVAL; + } + + SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_VLAN); + } else if (!is_mask) + SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true); + + if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) { + __be16 eth_type; + + eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); + if (is_mask) { + /* Always exact match EtherType. */ + eth_type = htons(0xffff); + } else if (ntohs(eth_type) < ETH_P_802_3_MIN) { + OVS_NLERR("EtherType is less than minimum (type=%x, min=%x).\n", + ntohs(eth_type), ETH_P_802_3_MIN); + return -EINVAL; + } + + SW_FLOW_KEY_PUT(match, eth.type, eth_type, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); + } else if (!is_mask) { + SW_FLOW_KEY_PUT(match, eth.type, htons(ETH_P_802_2), is_mask); + } + + if (attrs & (1 << OVS_KEY_ATTR_IPV4)) { + const struct ovs_key_ipv4 *ipv4_key; + + ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]); + if (!is_mask && ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) { + OVS_NLERR("Unknown IPv4 fragment type (value=%d, max=%d).\n", + ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX); + return -EINVAL; + } + SW_FLOW_KEY_PUT(match, ip.proto, + ipv4_key->ipv4_proto, is_mask); + SW_FLOW_KEY_PUT(match, ip.tos, + ipv4_key->ipv4_tos, is_mask); + SW_FLOW_KEY_PUT(match, ip.ttl, + ipv4_key->ipv4_ttl, is_mask); + SW_FLOW_KEY_PUT(match, ip.frag, + ipv4_key->ipv4_frag, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.addr.src, + ipv4_key->ipv4_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.addr.dst, + ipv4_key->ipv4_dst, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_IPV4); + } + + if (attrs & (1 << OVS_KEY_ATTR_IPV6)) { + const struct ovs_key_ipv6 *ipv6_key; + + ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]); + if (!is_mask && ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) { + OVS_NLERR("Unknown IPv6 fragment type (value=%d, max=%d).\n", + ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX); + return -EINVAL; + } + SW_FLOW_KEY_PUT(match, ipv6.label, + ipv6_key->ipv6_label, is_mask); + SW_FLOW_KEY_PUT(match, ip.proto, + ipv6_key->ipv6_proto, is_mask); + SW_FLOW_KEY_PUT(match, ip.tos, + ipv6_key->ipv6_tclass, is_mask); + SW_FLOW_KEY_PUT(match, ip.ttl, + ipv6_key->ipv6_hlimit, is_mask); + SW_FLOW_KEY_PUT(match, ip.frag, + ipv6_key->ipv6_frag, is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv6.addr.src, + ipv6_key->ipv6_src, + sizeof(match->key->ipv6.addr.src), + is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv6.addr.dst, + ipv6_key->ipv6_dst, + sizeof(match->key->ipv6.addr.dst), + is_mask); + + attrs &= ~(1 << OVS_KEY_ATTR_IPV6); + } + + if (attrs & (1 << OVS_KEY_ATTR_ARP)) { + const struct ovs_key_arp *arp_key; + + arp_key = nla_data(a[OVS_KEY_ATTR_ARP]); + if (!is_mask && (arp_key->arp_op & htons(0xff00))) { + OVS_NLERR("Unknown ARP opcode (opcode=%d).\n", + arp_key->arp_op); + return -EINVAL; + } + + SW_FLOW_KEY_PUT(match, ipv4.addr.src, + arp_key->arp_sip, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.addr.dst, + arp_key->arp_tip, is_mask); + SW_FLOW_KEY_PUT(match, ip.proto, + ntohs(arp_key->arp_op), is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv4.arp.sha, + arp_key->arp_sha, ETH_ALEN, is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv4.arp.tha, + arp_key->arp_tha, ETH_ALEN, is_mask); + + attrs &= ~(1 << OVS_KEY_ATTR_ARP); + } + + if (attrs & (1 << OVS_KEY_ATTR_TCP)) { + const struct ovs_key_tcp *tcp_key; + + tcp_key = nla_data(a[OVS_KEY_ATTR_TCP]); + if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { + SW_FLOW_KEY_PUT(match, ipv4.tp.src, + tcp_key->tcp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.tp.dst, + tcp_key->tcp_dst, is_mask); + } else { + SW_FLOW_KEY_PUT(match, ipv6.tp.src, + tcp_key->tcp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv6.tp.dst, + tcp_key->tcp_dst, is_mask); + } + attrs &= ~(1 << OVS_KEY_ATTR_TCP); + } + + if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) { + if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { + SW_FLOW_KEY_PUT(match, ipv4.tp.flags, + nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]), + is_mask); + } else { + SW_FLOW_KEY_PUT(match, ipv6.tp.flags, + nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]), + is_mask); + } + attrs &= ~(1 << OVS_KEY_ATTR_TCP_FLAGS); + } + + if (attrs & (1 << OVS_KEY_ATTR_UDP)) { + const struct ovs_key_udp *udp_key; + + udp_key = nla_data(a[OVS_KEY_ATTR_UDP]); + if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { + SW_FLOW_KEY_PUT(match, ipv4.tp.src, + udp_key->udp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.tp.dst, + udp_key->udp_dst, is_mask); + } else { + SW_FLOW_KEY_PUT(match, ipv6.tp.src, + udp_key->udp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv6.tp.dst, + udp_key->udp_dst, is_mask); + } + attrs &= ~(1 << OVS_KEY_ATTR_UDP); + } + + if (attrs & (1 << OVS_KEY_ATTR_SCTP)) { + const struct ovs_key_sctp *sctp_key; + + sctp_key = nla_data(a[OVS_KEY_ATTR_SCTP]); + if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) { + SW_FLOW_KEY_PUT(match, ipv4.tp.src, + sctp_key->sctp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv4.tp.dst, + sctp_key->sctp_dst, is_mask); + } else { + SW_FLOW_KEY_PUT(match, ipv6.tp.src, + sctp_key->sctp_src, is_mask); + SW_FLOW_KEY_PUT(match, ipv6.tp.dst, + sctp_key->sctp_dst, is_mask); + } + attrs &= ~(1 << OVS_KEY_ATTR_SCTP); + } + + if (attrs & (1 << OVS_KEY_ATTR_ICMP)) { + const struct ovs_key_icmp *icmp_key; + + icmp_key = nla_data(a[OVS_KEY_ATTR_ICMP]); + SW_FLOW_KEY_PUT(match, ipv4.tp.src, + htons(icmp_key->icmp_type), is_mask); + SW_FLOW_KEY_PUT(match, ipv4.tp.dst, + htons(icmp_key->icmp_code), is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ICMP); + } + + if (attrs & (1 << OVS_KEY_ATTR_ICMPV6)) { + const struct ovs_key_icmpv6 *icmpv6_key; + + icmpv6_key = nla_data(a[OVS_KEY_ATTR_ICMPV6]); + SW_FLOW_KEY_PUT(match, ipv6.tp.src, + htons(icmpv6_key->icmpv6_type), is_mask); + SW_FLOW_KEY_PUT(match, ipv6.tp.dst, + htons(icmpv6_key->icmpv6_code), is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ICMPV6); + } + + if (attrs & (1 << OVS_KEY_ATTR_ND)) { + const struct ovs_key_nd *nd_key; + + nd_key = nla_data(a[OVS_KEY_ATTR_ND]); + SW_FLOW_KEY_MEMCPY(match, ipv6.nd.target, + nd_key->nd_target, + sizeof(match->key->ipv6.nd.target), + is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv6.nd.sll, + nd_key->nd_sll, ETH_ALEN, is_mask); + SW_FLOW_KEY_MEMCPY(match, ipv6.nd.tll, + nd_key->nd_tll, ETH_ALEN, is_mask); + attrs &= ~(1 << OVS_KEY_ATTR_ND); + } + + if (attrs != 0) + return -EINVAL; + + return 0; +} + +static void sw_flow_mask_set(struct sw_flow_mask *mask, + struct sw_flow_key_range *range, u8 val) +{ + u8 *m = (u8 *)&mask->key + range->start; + + mask->range = *range; + memset(m, val, range_n_bytes(range)); +} + +/** + * ovs_nla_get_match - parses Netlink attributes into a flow key and + * mask. In case the 'mask' is NULL, the flow is treated as exact match + * flow. Otherwise, it is treated as a wildcarded flow, except the mask + * does not include any don't care bit. + * @match: receives the extracted flow match information. + * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute + * sequence. The fields should of the packet that triggered the creation + * of this flow. + * @mask: Optional. Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink + * attribute specifies the mask field of the wildcarded flow. + */ +int ovs_nla_get_match(struct sw_flow_match *match, + const struct nlattr *key, + const struct nlattr *mask) +{ + const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; + const struct nlattr *encap; + u64 key_attrs = 0; + u64 mask_attrs = 0; + bool encap_valid = false; + int err; + + err = parse_flow_nlattrs(key, a, &key_attrs); + if (err) + return err; + + if ((key_attrs & (1 << OVS_KEY_ATTR_ETHERNET)) && + (key_attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) && + (nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]) == htons(ETH_P_8021Q))) { + __be16 tci; + + if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) && + (key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) { + OVS_NLERR("Invalid Vlan frame.\n"); + return -EINVAL; + } + + key_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); + tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); + encap = a[OVS_KEY_ATTR_ENCAP]; + key_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP); + encap_valid = true; + + if (tci & htons(VLAN_TAG_PRESENT)) { + err = parse_flow_nlattrs(encap, a, &key_attrs); + if (err) + return err; + } else if (!tci) { + /* Corner case for truncated 802.1Q header. */ + if (nla_len(encap)) { + OVS_NLERR("Truncated 802.1Q header has non-zero encap attribute.\n"); + return -EINVAL; + } + } else { + OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n"); + return -EINVAL; + } + } + + err = ovs_key_from_nlattrs(match, key_attrs, a, false); + if (err) + return err; + + if (mask) { + err = parse_flow_mask_nlattrs(mask, a, &mask_attrs); + if (err) + return err; + + if (mask_attrs & 1 << OVS_KEY_ATTR_ENCAP) { + __be16 eth_type = 0; + __be16 tci = 0; + + if (!encap_valid) { + OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n"); + return -EINVAL; + } + + mask_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP); + if (a[OVS_KEY_ATTR_ETHERTYPE]) + eth_type = nla_get_be16(a[OVS_KEY_ATTR_ETHERTYPE]); + + if (eth_type == htons(0xffff)) { + mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE); + encap = a[OVS_KEY_ATTR_ENCAP]; + err = parse_flow_mask_nlattrs(encap, a, &mask_attrs); + } else { + OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n", + ntohs(eth_type)); + return -EINVAL; + } + + if (a[OVS_KEY_ATTR_VLAN]) + tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]); + + if (!(tci & htons(VLAN_TAG_PRESENT))) { + OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci)); + return -EINVAL; + } + } + + err = ovs_key_from_nlattrs(match, mask_attrs, a, true); + if (err) + return err; + } else { + /* Populate exact match flow's key mask. */ + if (match->mask) + sw_flow_mask_set(match->mask, &match->range, 0xff); + } + + if (!match_validate(match, key_attrs, mask_attrs)) + return -EINVAL; + + return 0; +} + +/** + * ovs_nla_get_flow_metadata - parses Netlink attributes into a flow key. + * @flow: Receives extracted in_port, priority, tun_key and skb_mark. + * @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute + * sequence. + * + * This parses a series of Netlink attributes that form a flow key, which must + * take the same form accepted by flow_from_nlattrs(), but only enough of it to + * get the metadata, that is, the parts of the flow key that cannot be + * extracted from the packet itself. + */ + +int ovs_nla_get_flow_metadata(struct sw_flow *flow, + const struct nlattr *attr) +{ + struct ovs_key_ipv4_tunnel *tun_key = &flow->key.tun_key; + const struct nlattr *a[OVS_KEY_ATTR_MAX + 1]; + u64 attrs = 0; + int err; + struct sw_flow_match match; + + flow->key.phy.in_port = DP_MAX_PORTS; + flow->key.phy.priority = 0; + flow->key.phy.skb_mark = 0; + memset(tun_key, 0, sizeof(flow->key.tun_key)); + + err = parse_flow_nlattrs(attr, a, &attrs); + if (err) + return -EINVAL; + + memset(&match, 0, sizeof(match)); + match.key = &flow->key; + + err = metadata_from_nlattrs(&match, &attrs, a, false); + if (err) + return err; + + return 0; +} + +int ovs_nla_put_flow(const struct sw_flow_key *swkey, + const struct sw_flow_key *output, struct sk_buff *skb) +{ + struct ovs_key_ethernet *eth_key; + struct nlattr *nla, *encap; + bool is_mask = (swkey != output); + + if (nla_put_u32(skb, OVS_KEY_ATTR_PRIORITY, output->phy.priority)) + goto nla_put_failure; + + if ((swkey->tun_key.ipv4_dst || is_mask) && + ipv4_tun_to_nlattr(skb, &swkey->tun_key, &output->tun_key)) + goto nla_put_failure; + + if (swkey->phy.in_port == DP_MAX_PORTS) { + if (is_mask && (output->phy.in_port == 0xffff)) + if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, 0xffffffff)) + goto nla_put_failure; + } else { + u16 upper_u16; + upper_u16 = !is_mask ? 0 : 0xffff; + + if (nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, + (upper_u16 << 16) | output->phy.in_port)) + goto nla_put_failure; + } + + if (nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, output->phy.skb_mark)) + goto nla_put_failure; + + nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); + if (!nla) + goto nla_put_failure; + + eth_key = nla_data(nla); + memcpy(eth_key->eth_src, output->eth.src, ETH_ALEN); + memcpy(eth_key->eth_dst, output->eth.dst, ETH_ALEN); + + if (swkey->eth.tci || swkey->eth.type == htons(ETH_P_8021Q)) { + __be16 eth_type; + eth_type = !is_mask ? htons(ETH_P_8021Q) : htons(0xffff); + if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, eth_type) || + nla_put_be16(skb, OVS_KEY_ATTR_VLAN, output->eth.tci)) + goto nla_put_failure; + encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP); + if (!swkey->eth.tci) + goto unencap; + } else + encap = NULL; + + if (swkey->eth.type == htons(ETH_P_802_2)) { + /* + * Ethertype 802.2 is represented in the netlink with omitted + * OVS_KEY_ATTR_ETHERTYPE in the flow key attribute, and + * 0xffff in the mask attribute. Ethertype can also + * be wildcarded. + */ + if (is_mask && output->eth.type) + if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, + output->eth.type)) + goto nla_put_failure; + goto unencap; + } + + if (nla_put_be16(skb, OVS_KEY_ATTR_ETHERTYPE, output->eth.type)) + goto nla_put_failure; + + if (swkey->eth.type == htons(ETH_P_IP)) { + struct ovs_key_ipv4 *ipv4_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_IPV4, sizeof(*ipv4_key)); + if (!nla) + goto nla_put_failure; + ipv4_key = nla_data(nla); + ipv4_key->ipv4_src = output->ipv4.addr.src; + ipv4_key->ipv4_dst = output->ipv4.addr.dst; + ipv4_key->ipv4_proto = output->ip.proto; + ipv4_key->ipv4_tos = output->ip.tos; + ipv4_key->ipv4_ttl = output->ip.ttl; + ipv4_key->ipv4_frag = output->ip.frag; + } else if (swkey->eth.type == htons(ETH_P_IPV6)) { + struct ovs_key_ipv6 *ipv6_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key)); + if (!nla) + goto nla_put_failure; + ipv6_key = nla_data(nla); + memcpy(ipv6_key->ipv6_src, &output->ipv6.addr.src, + sizeof(ipv6_key->ipv6_src)); + memcpy(ipv6_key->ipv6_dst, &output->ipv6.addr.dst, + sizeof(ipv6_key->ipv6_dst)); + ipv6_key->ipv6_label = output->ipv6.label; + ipv6_key->ipv6_proto = output->ip.proto; + ipv6_key->ipv6_tclass = output->ip.tos; + ipv6_key->ipv6_hlimit = output->ip.ttl; + ipv6_key->ipv6_frag = output->ip.frag; + } else if (swkey->eth.type == htons(ETH_P_ARP) || + swkey->eth.type == htons(ETH_P_RARP)) { + struct ovs_key_arp *arp_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key)); + if (!nla) + goto nla_put_failure; + arp_key = nla_data(nla); + memset(arp_key, 0, sizeof(struct ovs_key_arp)); + arp_key->arp_sip = output->ipv4.addr.src; + arp_key->arp_tip = output->ipv4.addr.dst; + arp_key->arp_op = htons(output->ip.proto); + memcpy(arp_key->arp_sha, output->ipv4.arp.sha, ETH_ALEN); + memcpy(arp_key->arp_tha, output->ipv4.arp.tha, ETH_ALEN); + } + + if ((swkey->eth.type == htons(ETH_P_IP) || + swkey->eth.type == htons(ETH_P_IPV6)) && + swkey->ip.frag != OVS_FRAG_TYPE_LATER) { + + if (swkey->ip.proto == IPPROTO_TCP) { + struct ovs_key_tcp *tcp_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_TCP, sizeof(*tcp_key)); + if (!nla) + goto nla_put_failure; + tcp_key = nla_data(nla); + if (swkey->eth.type == htons(ETH_P_IP)) { + tcp_key->tcp_src = output->ipv4.tp.src; + tcp_key->tcp_dst = output->ipv4.tp.dst; + if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS, + output->ipv4.tp.flags)) + goto nla_put_failure; + } else if (swkey->eth.type == htons(ETH_P_IPV6)) { + tcp_key->tcp_src = output->ipv6.tp.src; + tcp_key->tcp_dst = output->ipv6.tp.dst; + if (nla_put_be16(skb, OVS_KEY_ATTR_TCP_FLAGS, + output->ipv6.tp.flags)) + goto nla_put_failure; + } + } else if (swkey->ip.proto == IPPROTO_UDP) { + struct ovs_key_udp *udp_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_UDP, sizeof(*udp_key)); + if (!nla) + goto nla_put_failure; + udp_key = nla_data(nla); + if (swkey->eth.type == htons(ETH_P_IP)) { + udp_key->udp_src = output->ipv4.tp.src; + udp_key->udp_dst = output->ipv4.tp.dst; + } else if (swkey->eth.type == htons(ETH_P_IPV6)) { + udp_key->udp_src = output->ipv6.tp.src; + udp_key->udp_dst = output->ipv6.tp.dst; + } + } else if (swkey->ip.proto == IPPROTO_SCTP) { + struct ovs_key_sctp *sctp_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_SCTP, sizeof(*sctp_key)); + if (!nla) + goto nla_put_failure; + sctp_key = nla_data(nla); + if (swkey->eth.type == htons(ETH_P_IP)) { + sctp_key->sctp_src = swkey->ipv4.tp.src; + sctp_key->sctp_dst = swkey->ipv4.tp.dst; + } else if (swkey->eth.type == htons(ETH_P_IPV6)) { + sctp_key->sctp_src = swkey->ipv6.tp.src; + sctp_key->sctp_dst = swkey->ipv6.tp.dst; + } + } else if (swkey->eth.type == htons(ETH_P_IP) && + swkey->ip.proto == IPPROTO_ICMP) { + struct ovs_key_icmp *icmp_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_ICMP, sizeof(*icmp_key)); + if (!nla) + goto nla_put_failure; + icmp_key = nla_data(nla); + icmp_key->icmp_type = ntohs(output->ipv4.tp.src); + icmp_key->icmp_code = ntohs(output->ipv4.tp.dst); + } else if (swkey->eth.type == htons(ETH_P_IPV6) && + swkey->ip.proto == IPPROTO_ICMPV6) { + struct ovs_key_icmpv6 *icmpv6_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_ICMPV6, + sizeof(*icmpv6_key)); + if (!nla) + goto nla_put_failure; + icmpv6_key = nla_data(nla); + icmpv6_key->icmpv6_type = ntohs(output->ipv6.tp.src); + icmpv6_key->icmpv6_code = ntohs(output->ipv6.tp.dst); + + if (icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_SOLICITATION || + icmpv6_key->icmpv6_type == NDISC_NEIGHBOUR_ADVERTISEMENT) { + struct ovs_key_nd *nd_key; + + nla = nla_reserve(skb, OVS_KEY_ATTR_ND, sizeof(*nd_key)); + if (!nla) + goto nla_put_failure; + nd_key = nla_data(nla); + memcpy(nd_key->nd_target, &output->ipv6.nd.target, + sizeof(nd_key->nd_target)); + memcpy(nd_key->nd_sll, output->ipv6.nd.sll, ETH_ALEN); + memcpy(nd_key->nd_tll, output->ipv6.nd.tll, ETH_ALEN); + } + } + } + +unencap: + if (encap) + nla_nest_end(skb, encap); + + return 0; + +nla_put_failure: + return -EMSGSIZE; +} + +#define MAX_ACTIONS_BUFSIZE (32 * 1024) + +struct sw_flow_actions *ovs_nla_alloc_flow_actions(int size) +{ + struct sw_flow_actions *sfa; + + if (size > MAX_ACTIONS_BUFSIZE) + return ERR_PTR(-EINVAL); + + sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL); + if (!sfa) + return ERR_PTR(-ENOMEM); + + sfa->actions_len = 0; + return sfa; +} + +/* RCU callback used by ovs_nla_free_flow_actions. */ +static void rcu_free_acts_callback(struct rcu_head *rcu) +{ + struct sw_flow_actions *sf_acts = container_of(rcu, + struct sw_flow_actions, rcu); + kfree(sf_acts); +} + +/* Schedules 'sf_acts' to be freed after the next RCU grace period. + * The caller must hold rcu_read_lock for this to be sensible. */ +void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts) +{ + call_rcu(&sf_acts->rcu, rcu_free_acts_callback); +} + +static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa, + int attr_len) +{ + + struct sw_flow_actions *acts; + int new_acts_size; + int req_size = NLA_ALIGN(attr_len); + int next_offset = offsetof(struct sw_flow_actions, actions) + + (*sfa)->actions_len; + + if (req_size <= (ksize(*sfa) - next_offset)) + goto out; + + new_acts_size = ksize(*sfa) * 2; + + if (new_acts_size > MAX_ACTIONS_BUFSIZE) { + if ((MAX_ACTIONS_BUFSIZE - next_offset) < req_size) + return ERR_PTR(-EMSGSIZE); + new_acts_size = MAX_ACTIONS_BUFSIZE; + } + + acts = ovs_nla_alloc_flow_actions(new_acts_size); + if (IS_ERR(acts)) + return (void *)acts; + + memcpy(acts->actions, (*sfa)->actions, (*sfa)->actions_len); + acts->actions_len = (*sfa)->actions_len; + kfree(*sfa); + *sfa = acts; + +out: + (*sfa)->actions_len += req_size; + return (struct nlattr *) ((unsigned char *)(*sfa) + next_offset); +} + +static int add_action(struct sw_flow_actions **sfa, int attrtype, void *data, int len) +{ + struct nlattr *a; + + a = reserve_sfa_size(sfa, nla_attr_size(len)); + if (IS_ERR(a)) + return PTR_ERR(a); + + a->nla_type = attrtype; + a->nla_len = nla_attr_size(len); + + if (data) + memcpy(nla_data(a), data, len); + memset((unsigned char *) a + a->nla_len, 0, nla_padlen(len)); + + return 0; +} + +static inline int add_nested_action_start(struct sw_flow_actions **sfa, + int attrtype) +{ + int used = (*sfa)->actions_len; + int err; + + err = add_action(sfa, attrtype, NULL, 0); + if (err) + return err; + + return used; +} + +static inline void add_nested_action_end(struct sw_flow_actions *sfa, + int st_offset) +{ + struct nlattr *a = (struct nlattr *) ((unsigned char *)sfa->actions + + st_offset); + + a->nla_len = sfa->actions_len - st_offset; +} + +static int validate_and_copy_sample(const struct nlattr *attr, + const struct sw_flow_key *key, int depth, + struct sw_flow_actions **sfa) +{ + const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1]; + const struct nlattr *probability, *actions; + const struct nlattr *a; + int rem, start, err, st_acts; + + memset(attrs, 0, sizeof(attrs)); + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + if (!type || type > OVS_SAMPLE_ATTR_MAX || attrs[type]) + return -EINVAL; + attrs[type] = a; + } + if (rem) + return -EINVAL; + + probability = attrs[OVS_SAMPLE_ATTR_PROBABILITY]; + if (!probability || nla_len(probability) != sizeof(u32)) + return -EINVAL; + + actions = attrs[OVS_SAMPLE_ATTR_ACTIONS]; + if (!actions || (nla_len(actions) && nla_len(actions) < NLA_HDRLEN)) + return -EINVAL; + + /* validation done, copy sample action. */ + start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE); + if (start < 0) + return start; + err = add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY, + nla_data(probability), sizeof(u32)); + if (err) + return err; + st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS); + if (st_acts < 0) + return st_acts; + + err = ovs_nla_copy_actions(actions, key, depth + 1, sfa); + if (err) + return err; + + add_nested_action_end(*sfa, st_acts); + add_nested_action_end(*sfa, start); + + return 0; +} + +static int validate_tp_port(const struct sw_flow_key *flow_key) +{ + if (flow_key->eth.type == htons(ETH_P_IP)) { + if (flow_key->ipv4.tp.src || flow_key->ipv4.tp.dst) + return 0; + } else if (flow_key->eth.type == htons(ETH_P_IPV6)) { + if (flow_key->ipv6.tp.src || flow_key->ipv6.tp.dst) + return 0; + } + + return -EINVAL; +} + +void ovs_match_init(struct sw_flow_match *match, + struct sw_flow_key *key, + struct sw_flow_mask *mask) +{ + memset(match, 0, sizeof(*match)); + match->key = key; + match->mask = mask; + + memset(key, 0, sizeof(*key)); + + if (mask) { + memset(&mask->key, 0, sizeof(mask->key)); + mask->range.start = mask->range.end = 0; + } +} + +static int validate_and_copy_set_tun(const struct nlattr *attr, + struct sw_flow_actions **sfa) +{ + struct sw_flow_match match; + struct sw_flow_key key; + int err, start; + + ovs_match_init(&match, &key, NULL); + err = ipv4_tun_from_nlattr(nla_data(attr), &match, false); + if (err) + return err; + + start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET); + if (start < 0) + return start; + + err = add_action(sfa, OVS_KEY_ATTR_IPV4_TUNNEL, &match.key->tun_key, + sizeof(match.key->tun_key)); + add_nested_action_end(*sfa, start); + + return err; +} + +static int validate_set(const struct nlattr *a, + const struct sw_flow_key *flow_key, + struct sw_flow_actions **sfa, + bool *set_tun) +{ + const struct nlattr *ovs_key = nla_data(a); + int key_type = nla_type(ovs_key); + + /* There can be only one key in a action */ + if (nla_total_size(nla_len(ovs_key)) != nla_len(a)) + return -EINVAL; + + if (key_type > OVS_KEY_ATTR_MAX || + (ovs_key_lens[key_type] != nla_len(ovs_key) && + ovs_key_lens[key_type] != -1)) + return -EINVAL; + + switch (key_type) { + const struct ovs_key_ipv4 *ipv4_key; + const struct ovs_key_ipv6 *ipv6_key; + int err; + + case OVS_KEY_ATTR_PRIORITY: + case OVS_KEY_ATTR_SKB_MARK: + case OVS_KEY_ATTR_ETHERNET: + break; + + case OVS_KEY_ATTR_TUNNEL: + *set_tun = true; + err = validate_and_copy_set_tun(a, sfa); + if (err) + return err; + break; + + case OVS_KEY_ATTR_IPV4: + if (flow_key->eth.type != htons(ETH_P_IP)) + return -EINVAL; + + if (!flow_key->ip.proto) + return -EINVAL; + + ipv4_key = nla_data(ovs_key); + if (ipv4_key->ipv4_proto != flow_key->ip.proto) + return -EINVAL; + + if (ipv4_key->ipv4_frag != flow_key->ip.frag) + return -EINVAL; + + break; + + case OVS_KEY_ATTR_IPV6: + if (flow_key->eth.type != htons(ETH_P_IPV6)) + return -EINVAL; + + if (!flow_key->ip.proto) + return -EINVAL; + + ipv6_key = nla_data(ovs_key); + if (ipv6_key->ipv6_proto != flow_key->ip.proto) + return -EINVAL; + + if (ipv6_key->ipv6_frag != flow_key->ip.frag) + return -EINVAL; + + if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000) + return -EINVAL; + + break; + + case OVS_KEY_ATTR_TCP: + if (flow_key->ip.proto != IPPROTO_TCP) + return -EINVAL; + + return validate_tp_port(flow_key); + + case OVS_KEY_ATTR_UDP: + if (flow_key->ip.proto != IPPROTO_UDP) + return -EINVAL; + + return validate_tp_port(flow_key); + + case OVS_KEY_ATTR_SCTP: + if (flow_key->ip.proto != IPPROTO_SCTP) + return -EINVAL; + + return validate_tp_port(flow_key); + + default: + return -EINVAL; + } + + return 0; +} + +static int validate_userspace(const struct nlattr *attr) +{ + static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] = { + [OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 }, + [OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC }, + }; + struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1]; + int error; + + error = nla_parse_nested(a, OVS_USERSPACE_ATTR_MAX, + attr, userspace_policy); + if (error) + return error; + + if (!a[OVS_USERSPACE_ATTR_PID] || + !nla_get_u32(a[OVS_USERSPACE_ATTR_PID])) + return -EINVAL; + + return 0; +} + +static int copy_action(const struct nlattr *from, + struct sw_flow_actions **sfa) +{ + int totlen = NLA_ALIGN(from->nla_len); + struct nlattr *to; + + to = reserve_sfa_size(sfa, from->nla_len); + if (IS_ERR(to)) + return PTR_ERR(to); + + memcpy(to, from, totlen); + return 0; +} + +int ovs_nla_copy_actions(const struct nlattr *attr, + const struct sw_flow_key *key, + int depth, + struct sw_flow_actions **sfa) +{ + const struct nlattr *a; + int rem, err; + + if (depth >= SAMPLE_ACTION_DEPTH) + return -EOVERFLOW; + + nla_for_each_nested(a, attr, rem) { + /* Expected argument lengths, (u32)-1 for variable length. */ + static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { + [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32), + [OVS_ACTION_ATTR_USERSPACE] = (u32)-1, + [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan), + [OVS_ACTION_ATTR_POP_VLAN] = 0, + [OVS_ACTION_ATTR_SET] = (u32)-1, + [OVS_ACTION_ATTR_SAMPLE] = (u32)-1 + }; + const struct ovs_action_push_vlan *vlan; + int type = nla_type(a); + bool skip_copy; + + if (type > OVS_ACTION_ATTR_MAX || + (action_lens[type] != nla_len(a) && + action_lens[type] != (u32)-1)) + return -EINVAL; + + skip_copy = false; + switch (type) { + case OVS_ACTION_ATTR_UNSPEC: + return -EINVAL; + + case OVS_ACTION_ATTR_USERSPACE: + err = validate_userspace(a); + if (err) + return err; + break; + + case OVS_ACTION_ATTR_OUTPUT: + if (nla_get_u32(a) >= DP_MAX_PORTS) + return -EINVAL; + break; + + + case OVS_ACTION_ATTR_POP_VLAN: + break; + + case OVS_ACTION_ATTR_PUSH_VLAN: + vlan = nla_data(a); + if (vlan->vlan_tpid != htons(ETH_P_8021Q)) + return -EINVAL; + if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT))) + return -EINVAL; + break; + + case OVS_ACTION_ATTR_SET: + err = validate_set(a, key, sfa, &skip_copy); + if (err) + return err; + break; + + case OVS_ACTION_ATTR_SAMPLE: + err = validate_and_copy_sample(a, key, depth, sfa); + if (err) + return err; + skip_copy = true; + break; + + default: + return -EINVAL; + } + if (!skip_copy) { + err = copy_action(a, sfa); + if (err) + return err; + } + } + + if (rem > 0) + return -EINVAL; + + return 0; +} + +static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb) +{ + const struct nlattr *a; + struct nlattr *start; + int err = 0, rem; + + start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE); + if (!start) + return -EMSGSIZE; + + nla_for_each_nested(a, attr, rem) { + int type = nla_type(a); + struct nlattr *st_sample; + + switch (type) { + case OVS_SAMPLE_ATTR_PROBABILITY: + if (nla_put(skb, OVS_SAMPLE_ATTR_PROBABILITY, + sizeof(u32), nla_data(a))) + return -EMSGSIZE; + break; + case OVS_SAMPLE_ATTR_ACTIONS: + st_sample = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS); + if (!st_sample) + return -EMSGSIZE; + err = ovs_nla_put_actions(nla_data(a), nla_len(a), skb); + if (err) + return err; + nla_nest_end(skb, st_sample); + break; + } + } + + nla_nest_end(skb, start); + return err; +} + +static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb) +{ + const struct nlattr *ovs_key = nla_data(a); + int key_type = nla_type(ovs_key); + struct nlattr *start; + int err; + + switch (key_type) { + case OVS_KEY_ATTR_IPV4_TUNNEL: + start = nla_nest_start(skb, OVS_ACTION_ATTR_SET); + if (!start) + return -EMSGSIZE; + + err = ipv4_tun_to_nlattr(skb, nla_data(ovs_key), + nla_data(ovs_key)); + if (err) + return err; + nla_nest_end(skb, start); + break; + default: + if (nla_put(skb, OVS_ACTION_ATTR_SET, nla_len(a), ovs_key)) + return -EMSGSIZE; + break; + } + + return 0; +} + +int ovs_nla_put_actions(const struct nlattr *attr, int len, struct sk_buff *skb) +{ + const struct nlattr *a; + int rem, err; + + nla_for_each_attr(a, attr, len, rem) { + int type = nla_type(a); + + switch (type) { + case OVS_ACTION_ATTR_SET: + err = set_action_to_attr(a, skb); + if (err) + return err; + break; + + case OVS_ACTION_ATTR_SAMPLE: + err = sample_action_to_attr(a, skb); + if (err) + return err; + break; + default: + if (nla_put(skb, type, nla_len(a), nla_data(a))) + return -EMSGSIZE; + break; + } + } + + return 0; +} diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h new file mode 100644 index 0000000000000000000000000000000000000000..440151045d3946329bf01e4fd5a1c81f0fd4e906 --- /dev/null +++ b/net/openvswitch/flow_netlink.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2007-2013 Nicira, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +#ifndef FLOW_NETLINK_H +#define FLOW_NETLINK_H 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "flow.h" + +void ovs_match_init(struct sw_flow_match *match, + struct sw_flow_key *key, struct sw_flow_mask *mask); + +int ovs_nla_put_flow(const struct sw_flow_key *, + const struct sw_flow_key *, struct sk_buff *); +int ovs_nla_get_flow_metadata(struct sw_flow *flow, + const struct nlattr *attr); +int ovs_nla_get_match(struct sw_flow_match *match, + const struct nlattr *, + const struct nlattr *); + +int ovs_nla_copy_actions(const struct nlattr *attr, + const struct sw_flow_key *key, int depth, + struct sw_flow_actions **sfa); +int ovs_nla_put_actions(const struct nlattr *attr, + int len, struct sk_buff *skb); + +struct sw_flow_actions *ovs_nla_alloc_flow_actions(int actions_len); +void ovs_nla_free_flow_actions(struct sw_flow_actions *); + +#endif /* flow_netlink.h */ diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c new file mode 100644 index 0000000000000000000000000000000000000000..e4254270608703adb6ca41194cb9f110e26e3758 --- /dev/null +++ b/net/openvswitch/flow_table.c @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2007-2013 Nicira, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include "flow.h" +#include "datapath.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "datapath.h" + +#define TBL_MIN_BUCKETS 1024 +#define REHASH_INTERVAL (10 * 60 * HZ) + +static struct kmem_cache *flow_cache; + +static u16 range_n_bytes(const struct sw_flow_key_range *range) +{ + return range->end - range->start; +} + +void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, + const struct sw_flow_mask *mask) +{ + const long *m = (long *)((u8 *)&mask->key + mask->range.start); + const long *s = (long *)((u8 *)src + mask->range.start); + long *d = (long *)((u8 *)dst + mask->range.start); + int i; + + /* The memory outside of the 'mask->range' are not set since + * further operations on 'dst' only uses contents within + * 'mask->range'. + */ + for (i = 0; i < range_n_bytes(&mask->range); i += sizeof(long)) + *d++ = *s++ & *m++; +} + +struct sw_flow *ovs_flow_alloc(void) +{ + struct sw_flow *flow; + + flow = kmem_cache_alloc(flow_cache, GFP_KERNEL); + if (!flow) + return ERR_PTR(-ENOMEM); + + spin_lock_init(&flow->lock); + flow->sf_acts = NULL; + flow->mask = NULL; + + return flow; +} + +int ovs_flow_tbl_count(struct flow_table *table) +{ + return table->count; +} + +static struct flex_array *alloc_buckets(unsigned int n_buckets) +{ + struct flex_array *buckets; + int i, err; + + buckets = flex_array_alloc(sizeof(struct hlist_head), + n_buckets, GFP_KERNEL); + if (!buckets) + return NULL; + + err = flex_array_prealloc(buckets, 0, n_buckets, GFP_KERNEL); + if (err) { + flex_array_free(buckets); + return NULL; + } + + for (i = 0; i < n_buckets; i++) + INIT_HLIST_HEAD((struct hlist_head *) + flex_array_get(buckets, i)); + + return buckets; +} + +static void flow_free(struct sw_flow *flow) +{ + kfree((struct sf_flow_acts __force *)flow->sf_acts); + kmem_cache_free(flow_cache, flow); +} + +static void rcu_free_flow_callback(struct rcu_head *rcu) +{ + struct sw_flow *flow = container_of(rcu, struct sw_flow, rcu); + + flow_free(flow); +} + +static void rcu_free_sw_flow_mask_cb(struct rcu_head *rcu) +{ + struct sw_flow_mask *mask = container_of(rcu, struct sw_flow_mask, rcu); + + kfree(mask); +} + +static void flow_mask_del_ref(struct sw_flow_mask *mask, bool deferred) +{ + if (!mask) + return; + + BUG_ON(!mask->ref_count); + mask->ref_count--; + + if (!mask->ref_count) { + list_del_rcu(&mask->list); + if (deferred) + call_rcu(&mask->rcu, rcu_free_sw_flow_mask_cb); + else + kfree(mask); + } +} + +void ovs_flow_free(struct sw_flow *flow, bool deferred) +{ + if (!flow) + return; + + flow_mask_del_ref(flow->mask, deferred); + + if (deferred) + call_rcu(&flow->rcu, rcu_free_flow_callback); + else + flow_free(flow); +} + +static void free_buckets(struct flex_array *buckets) +{ + flex_array_free(buckets); +} + +static void __table_instance_destroy(struct table_instance *ti) +{ + int i; + + if (ti->keep_flows) + goto skip_flows; + + for (i = 0; i < ti->n_buckets; i++) { + struct sw_flow *flow; + struct hlist_head *head = flex_array_get(ti->buckets, i); + struct hlist_node *n; + int ver = ti->node_ver; + + hlist_for_each_entry_safe(flow, n, head, hash_node[ver]) { + hlist_del(&flow->hash_node[ver]); + ovs_flow_free(flow, false); + } + } + +skip_flows: + free_buckets(ti->buckets); + kfree(ti); +} + +static struct table_instance *table_instance_alloc(int new_size) +{ + struct table_instance *ti = kmalloc(sizeof(*ti), GFP_KERNEL); + + if (!ti) + return NULL; + + ti->buckets = alloc_buckets(new_size); + + if (!ti->buckets) { + kfree(ti); + return NULL; + } + ti->n_buckets = new_size; + ti->node_ver = 0; + ti->keep_flows = false; + get_random_bytes(&ti->hash_seed, sizeof(u32)); + + return ti; +} + +int ovs_flow_tbl_init(struct flow_table *table) +{ + struct table_instance *ti; + + ti = table_instance_alloc(TBL_MIN_BUCKETS); + + if (!ti) + return -ENOMEM; + + rcu_assign_pointer(table->ti, ti); + INIT_LIST_HEAD(&table->mask_list); + table->last_rehash = jiffies; + table->count = 0; + return 0; +} + +static void flow_tbl_destroy_rcu_cb(struct rcu_head *rcu) +{ + struct table_instance *ti = container_of(rcu, struct table_instance, rcu); + + __table_instance_destroy(ti); +} + +static void table_instance_destroy(struct table_instance *ti, bool deferred) +{ + if (!ti) + return; + + if (deferred) + call_rcu(&ti->rcu, flow_tbl_destroy_rcu_cb); + else + __table_instance_destroy(ti); +} + +void ovs_flow_tbl_destroy(struct flow_table *table) +{ + struct table_instance *ti = ovsl_dereference(table->ti); + + table_instance_destroy(ti, false); +} + +struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti, + u32 *bucket, u32 *last) +{ + struct sw_flow *flow; + struct hlist_head *head; + int ver; + int i; + + ver = ti->node_ver; + while (*bucket < ti->n_buckets) { + i = 0; + head = flex_array_get(ti->buckets, *bucket); + hlist_for_each_entry_rcu(flow, head, hash_node[ver]) { + if (i < *last) { + i++; + continue; + } + *last = i + 1; + return flow; + } + (*bucket)++; + *last = 0; + } + + return NULL; +} + +static struct hlist_head *find_bucket(struct table_instance *ti, u32 hash) +{ + hash = jhash_1word(hash, ti->hash_seed); + return flex_array_get(ti->buckets, + (hash & (ti->n_buckets - 1))); +} + +static void table_instance_insert(struct table_instance *ti, struct sw_flow *flow) +{ + struct hlist_head *head; + + head = find_bucket(ti, flow->hash); + hlist_add_head_rcu(&flow->hash_node[ti->node_ver], head); +} + +static void flow_table_copy_flows(struct table_instance *old, + struct table_instance *new) +{ + int old_ver; + int i; + + old_ver = old->node_ver; + new->node_ver = !old_ver; + + /* Insert in new table. */ + for (i = 0; i < old->n_buckets; i++) { + struct sw_flow *flow; + struct hlist_head *head; + + head = flex_array_get(old->buckets, i); + + hlist_for_each_entry(flow, head, hash_node[old_ver]) + table_instance_insert(new, flow); + } + + old->keep_flows = true; +} + +static struct table_instance *table_instance_rehash(struct table_instance *ti, + int n_buckets) +{ + struct table_instance *new_ti; + + new_ti = table_instance_alloc(n_buckets); + if (!new_ti) + return NULL; + + flow_table_copy_flows(ti, new_ti); + + return new_ti; +} + +int ovs_flow_tbl_flush(struct flow_table *flow_table) +{ + struct table_instance *old_ti; + struct table_instance *new_ti; + + old_ti = ovsl_dereference(flow_table->ti); + new_ti = table_instance_alloc(TBL_MIN_BUCKETS); + if (!new_ti) + return -ENOMEM; + + rcu_assign_pointer(flow_table->ti, new_ti); + flow_table->last_rehash = jiffies; + flow_table->count = 0; + + table_instance_destroy(old_ti, true); + return 0; +} + +static u32 flow_hash(const struct sw_flow_key *key, int key_start, + int key_end) +{ + u32 *hash_key = (u32 *)((u8 *)key + key_start); + int hash_u32s = (key_end - key_start) >> 2; + + /* Make sure number of hash bytes are multiple of u32. */ + BUILD_BUG_ON(sizeof(long) % sizeof(u32)); + + return jhash2(hash_key, hash_u32s, 0); +} + +static int flow_key_start(const struct sw_flow_key *key) +{ + if (key->tun_key.ipv4_dst) + return 0; + else + return rounddown(offsetof(struct sw_flow_key, phy), + sizeof(long)); +} + +static bool cmp_key(const struct sw_flow_key *key1, + const struct sw_flow_key *key2, + int key_start, int key_end) +{ + const long *cp1 = (long *)((u8 *)key1 + key_start); + const long *cp2 = (long *)((u8 *)key2 + key_start); + long diffs = 0; + int i; + + for (i = key_start; i < key_end; i += sizeof(long)) + diffs |= *cp1++ ^ *cp2++; + + return diffs == 0; +} + +static bool flow_cmp_masked_key(const struct sw_flow *flow, + const struct sw_flow_key *key, + int key_start, int key_end) +{ + return cmp_key(&flow->key, key, key_start, key_end); +} + +bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, + struct sw_flow_match *match) +{ + struct sw_flow_key *key = match->key; + int key_start = flow_key_start(key); + int key_end = match->range.end; + + return cmp_key(&flow->unmasked_key, key, key_start, key_end); +} + +static struct sw_flow *masked_flow_lookup(struct table_instance *ti, + const struct sw_flow_key *unmasked, + struct sw_flow_mask *mask) +{ + struct sw_flow *flow; + struct hlist_head *head; + int key_start = mask->range.start; + int key_end = mask->range.end; + u32 hash; + struct sw_flow_key masked_key; + + ovs_flow_mask_key(&masked_key, unmasked, mask); + hash = flow_hash(&masked_key, key_start, key_end); + head = find_bucket(ti, hash); + hlist_for_each_entry_rcu(flow, head, hash_node[ti->node_ver]) { + if (flow->mask == mask && flow->hash == hash && + flow_cmp_masked_key(flow, &masked_key, + key_start, key_end)) + return flow; + } + return NULL; +} + +struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl, + const struct sw_flow_key *key, + u32 *n_mask_hit) +{ + struct table_instance *ti = rcu_dereference(tbl->ti); + struct sw_flow_mask *mask; + struct sw_flow *flow; + + *n_mask_hit = 0; + list_for_each_entry_rcu(mask, &tbl->mask_list, list) { + (*n_mask_hit)++; + flow = masked_flow_lookup(ti, key, mask); + if (flow) /* Found */ + return flow; + } + return NULL; +} + +int ovs_flow_tbl_num_masks(const struct flow_table *table) +{ + struct sw_flow_mask *mask; + int num = 0; + + list_for_each_entry(mask, &table->mask_list, list) + num++; + + return num; +} + +static struct table_instance *table_instance_expand(struct table_instance *ti) +{ + return table_instance_rehash(ti, ti->n_buckets * 2); +} + +void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow) +{ + struct table_instance *ti = ovsl_dereference(table->ti); + + BUG_ON(table->count == 0); + hlist_del_rcu(&flow->hash_node[ti->node_ver]); + table->count--; +} + +static struct sw_flow_mask *mask_alloc(void) +{ + struct sw_flow_mask *mask; + + mask = kmalloc(sizeof(*mask), GFP_KERNEL); + if (mask) + mask->ref_count = 0; + + return mask; +} + +static void mask_add_ref(struct sw_flow_mask *mask) +{ + mask->ref_count++; +} + +static bool mask_equal(const struct sw_flow_mask *a, + const struct sw_flow_mask *b) +{ + u8 *a_ = (u8 *)&a->key + a->range.start; + u8 *b_ = (u8 *)&b->key + b->range.start; + + return (a->range.end == b->range.end) + && (a->range.start == b->range.start) + && (memcmp(a_, b_, range_n_bytes(&a->range)) == 0); +} + +static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl, + const struct sw_flow_mask *mask) +{ + struct list_head *ml; + + list_for_each(ml, &tbl->mask_list) { + struct sw_flow_mask *m; + m = container_of(ml, struct sw_flow_mask, list); + if (mask_equal(mask, m)) + return m; + } + + return NULL; +} + +/** + * add a new mask into the mask list. + * The caller needs to make sure that 'mask' is not the same + * as any masks that are already on the list. + */ +static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow, + struct sw_flow_mask *new) +{ + struct sw_flow_mask *mask; + mask = flow_mask_find(tbl, new); + if (!mask) { + /* Allocate a new mask if none exsits. */ + mask = mask_alloc(); + if (!mask) + return -ENOMEM; + mask->key = new->key; + mask->range = new->range; + list_add_rcu(&mask->list, &tbl->mask_list); + } + + mask_add_ref(mask); + flow->mask = mask; + return 0; +} + +int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, + struct sw_flow_mask *mask) +{ + struct table_instance *new_ti = NULL; + struct table_instance *ti; + int err; + + err = flow_mask_insert(table, flow, mask); + if (err) + return err; + + flow->hash = flow_hash(&flow->key, flow->mask->range.start, + flow->mask->range.end); + ti = ovsl_dereference(table->ti); + table_instance_insert(ti, flow); + table->count++; + + /* Expand table, if necessary, to make room. */ + if (table->count > ti->n_buckets) + new_ti = table_instance_expand(ti); + else if (time_after(jiffies, table->last_rehash + REHASH_INTERVAL)) + new_ti = table_instance_rehash(ti, ti->n_buckets); + + if (new_ti) { + rcu_assign_pointer(table->ti, new_ti); + table_instance_destroy(ti, true); + table->last_rehash = jiffies; + } + return 0; +} + +/* Initializes the flow module. + * Returns zero if successful or a negative error code. */ +int ovs_flow_init(void) +{ + BUILD_BUG_ON(__alignof__(struct sw_flow_key) % __alignof__(long)); + BUILD_BUG_ON(sizeof(struct sw_flow_key) % sizeof(long)); + + flow_cache = kmem_cache_create("sw_flow", sizeof(struct sw_flow), 0, + 0, NULL); + if (flow_cache == NULL) + return -ENOMEM; + + return 0; +} + +/* Uninitializes the flow module. */ +void ovs_flow_exit(void) +{ + kmem_cache_destroy(flow_cache); +} diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h new file mode 100644 index 0000000000000000000000000000000000000000..fbe45d5ad07d6eca424dbf04fcd6870539648079 --- /dev/null +++ b/net/openvswitch/flow_table.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2007-2013 Nicira, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef FLOW_TABLE_H +#define FLOW_TABLE_H 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "flow.h" + +struct table_instance { + struct flex_array *buckets; + unsigned int n_buckets; + struct rcu_head rcu; + int node_ver; + u32 hash_seed; + bool keep_flows; +}; + +struct flow_table { + struct table_instance __rcu *ti; + struct list_head mask_list; + unsigned long last_rehash; + unsigned int count; +}; + +int ovs_flow_init(void); +void ovs_flow_exit(void); + +struct sw_flow *ovs_flow_alloc(void); +void ovs_flow_free(struct sw_flow *, bool deferred); + +int ovs_flow_tbl_init(struct flow_table *); +int ovs_flow_tbl_count(struct flow_table *table); +void ovs_flow_tbl_destroy(struct flow_table *table); +int ovs_flow_tbl_flush(struct flow_table *flow_table); + +int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow, + struct sw_flow_mask *mask); +void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow); +int ovs_flow_tbl_num_masks(const struct flow_table *table); +struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *table, + u32 *bucket, u32 *idx); +struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *, + const struct sw_flow_key *, + u32 *n_mask_hit); + +bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow, + struct sw_flow_match *match); + +void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src, + const struct sw_flow_mask *mask); +#endif /* flow_table.h */ diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c index c99dea543d648dbd377c7168cad517c8bc76bf76..a3d6951602db59c58e6086862d8db2f3a0cd306d 100644 --- a/net/openvswitch/vport-gre.c +++ b/net/openvswitch/vport-gre.c @@ -24,8 +24,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c index 98d3edbbc2356e1296fcc86b59956387db916ca4..729c68763fe70d150793e1f01a023d4d8f1b78c0 100644 --- a/net/openvswitch/vport-internal_dev.c +++ b/net/openvswitch/vport-internal_dev.c @@ -134,7 +134,7 @@ static void do_setup(struct net_device *netdev) netdev->tx_queue_len = 0; netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST | - NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_TSO; + NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_GSO_SOFTWARE; netdev->vlan_features = netdev->features; netdev->features |= NETIF_F_HW_VLAN_CTAG_TX; diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c index a481c03e2861f7bcc1de1ec65b56cbc142a2f73d..e797a50ac2beec3a019bacf15e2e7c593355a46f 100644 --- a/net/openvswitch/vport-vxlan.c +++ b/net/openvswitch/vport-vxlan.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -173,7 +172,7 @@ static int vxlan_tnl_send(struct vport *vport, struct sk_buff *skb) skb->local_df = 1; - inet_get_local_port_range(&port_min, &port_max); + inet_get_local_port_range(net, &port_min, &port_max); src_port = vxlan_src_port(port_min, port_max, skb); err = vxlan_xmit_skb(vxlan_port->vs, rt, skb, diff --git a/net/rds/connection.c b/net/rds/connection.c index 642ad42c416ba915680645d8bc3539e5bdee4bf5..378c3a6acf84cab59346ab832d2fffba33e6e543 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -51,10 +51,16 @@ static struct kmem_cache *rds_conn_slab; static struct hlist_head *rds_conn_bucket(__be32 laddr, __be32 faddr) { + static u32 rds_hash_secret __read_mostly; + + unsigned long hash; + + net_get_random_once(&rds_hash_secret, sizeof(rds_hash_secret)); + /* Pass NULL, don't need struct net for hash */ - unsigned long hash = inet_ehashfn(NULL, - be32_to_cpu(laddr), 0, - be32_to_cpu(faddr), 0); + hash = __inet_ehashfn(be32_to_cpu(laddr), 0, + be32_to_cpu(faddr), 0, + rds_hash_secret); return &rds_conn_hash[hash & RDS_CONNECTION_HASH_MASK]; } diff --git a/net/rds/rds.h b/net/rds/rds.h index ec1d731ecff0e2d103b1933f5997317c2dd1403b..48f8ffc60f8f1cee8e63448fe3aa7aaac0d5d4c1 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -749,7 +749,7 @@ void rds_atomic_send_complete(struct rds_message *rm, int wc_status); int rds_cmsg_atomic(struct rds_sock *rs, struct rds_message *rm, struct cmsghdr *cmsg); -extern void __rds_put_mr_final(struct rds_mr *mr); +void __rds_put_mr_final(struct rds_mr *mr); static inline void rds_mr_put(struct rds_mr *mr) { if (atomic_dec_and_test(&mr->r_refcount)) diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig index 78efe895b6636c4a57af6d72f156501e2c778992..4c10e7e6c9f6ae53291d3f72d82b6c9b0539b552 100644 --- a/net/rfkill/Kconfig +++ b/net/rfkill/Kconfig @@ -36,7 +36,7 @@ config RFKILL_REGULATOR config RFKILL_GPIO tristate "GPIO RFKILL driver" - depends on RFKILL && GPIOLIB && HAVE_CLK + depends on RFKILL && GPIOLIB default n help If you say yes here you get support of a generic gpio RFKILL diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c index fb076cd6f808ad9b409a9670bffa699a7435de34..5620d3c07479654e64fd0b7d1b325562b880b174 100644 --- a/net/rfkill/rfkill-gpio.c +++ b/net/rfkill/rfkill-gpio.c @@ -24,27 +24,23 @@ #include #include #include +#include +#include #include -enum rfkill_gpio_clk_state { - UNSPECIFIED = 0, - PWR_ENABLED, - PWR_DISABLED -}; +struct rfkill_gpio_data { + const char *name; + enum rfkill_type type; + int reset_gpio; + int shutdown_gpio; -#define PWR_CLK_SET(_RF, _EN) \ - ((_RF)->pwr_clk_enabled = (!(_EN) ? PWR_ENABLED : PWR_DISABLED)) -#define PWR_CLK_ENABLED(_RF) ((_RF)->pwr_clk_enabled == PWR_ENABLED) -#define PWR_CLK_DISABLED(_RF) ((_RF)->pwr_clk_enabled != PWR_ENABLED) + struct rfkill *rfkill_dev; + char *reset_name; + char *shutdown_name; + struct clk *clk; -struct rfkill_gpio_data { - struct rfkill_gpio_platform_data *pdata; - struct rfkill *rfkill_dev; - char *reset_name; - char *shutdown_name; - enum rfkill_gpio_clk_state pwr_clk_enabled; - struct clk *pwr_clk; + bool clk_enabled; }; static int rfkill_gpio_set_power(void *data, bool blocked) @@ -52,23 +48,22 @@ static int rfkill_gpio_set_power(void *data, bool blocked) struct rfkill_gpio_data *rfkill = data; if (blocked) { - if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) - gpio_direction_output(rfkill->pdata->shutdown_gpio, 0); - if (gpio_is_valid(rfkill->pdata->reset_gpio)) - gpio_direction_output(rfkill->pdata->reset_gpio, 0); - if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) - clk_disable(rfkill->pwr_clk); + if (gpio_is_valid(rfkill->shutdown_gpio)) + gpio_set_value(rfkill->shutdown_gpio, 0); + if (gpio_is_valid(rfkill->reset_gpio)) + gpio_set_value(rfkill->reset_gpio, 0); + if (!IS_ERR(rfkill->clk) && rfkill->clk_enabled) + clk_disable(rfkill->clk); } else { - if (rfkill->pwr_clk && PWR_CLK_DISABLED(rfkill)) - clk_enable(rfkill->pwr_clk); - if (gpio_is_valid(rfkill->pdata->reset_gpio)) - gpio_direction_output(rfkill->pdata->reset_gpio, 1); - if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) - gpio_direction_output(rfkill->pdata->shutdown_gpio, 1); + if (!IS_ERR(rfkill->clk) && !rfkill->clk_enabled) + clk_enable(rfkill->clk); + if (gpio_is_valid(rfkill->reset_gpio)) + gpio_set_value(rfkill->reset_gpio, 1); + if (gpio_is_valid(rfkill->shutdown_gpio)) + gpio_set_value(rfkill->shutdown_gpio, 1); } - if (rfkill->pwr_clk) - PWR_CLK_SET(rfkill, blocked); + rfkill->clk_enabled = blocked; return 0; } @@ -77,117 +72,112 @@ static const struct rfkill_ops rfkill_gpio_ops = { .set_block = rfkill_gpio_set_power, }; +static int rfkill_gpio_acpi_probe(struct device *dev, + struct rfkill_gpio_data *rfkill) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + rfkill->name = dev_name(dev); + rfkill->type = (unsigned)id->driver_data; + rfkill->reset_gpio = acpi_get_gpio_by_index(dev, 0, NULL); + rfkill->shutdown_gpio = acpi_get_gpio_by_index(dev, 1, NULL); + + return 0; +} + static int rfkill_gpio_probe(struct platform_device *pdev) { - struct rfkill_gpio_data *rfkill; struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; + struct rfkill_gpio_data *rfkill; + const char *clk_name = NULL; int ret = 0; int len = 0; - if (!pdata) { - pr_warn("%s: No platform data specified\n", __func__); - return -EINVAL; + rfkill = devm_kzalloc(&pdev->dev, sizeof(*rfkill), GFP_KERNEL); + if (!rfkill) + return -ENOMEM; + + if (ACPI_HANDLE(&pdev->dev)) { + ret = rfkill_gpio_acpi_probe(&pdev->dev, rfkill); + if (ret) + return ret; + } else if (pdata) { + clk_name = pdata->power_clk_name; + rfkill->name = pdata->name; + rfkill->type = pdata->type; + rfkill->reset_gpio = pdata->reset_gpio; + rfkill->shutdown_gpio = pdata->shutdown_gpio; + } else { + return -ENODEV; } /* make sure at-least one of the GPIO is defined and that * a name is specified for this instance */ - if (!pdata->name || (!gpio_is_valid(pdata->reset_gpio) && - !gpio_is_valid(pdata->shutdown_gpio))) { + if ((!gpio_is_valid(rfkill->reset_gpio) && + !gpio_is_valid(rfkill->shutdown_gpio)) || !rfkill->name) { pr_warn("%s: invalid platform data\n", __func__); return -EINVAL; } - rfkill = kzalloc(sizeof(*rfkill), GFP_KERNEL); - if (!rfkill) - return -ENOMEM; - - if (pdata->gpio_runtime_setup) { + if (pdata && pdata->gpio_runtime_setup) { ret = pdata->gpio_runtime_setup(pdev); if (ret) { pr_warn("%s: can't set up gpio\n", __func__); - goto fail_alloc; + return ret; } } - rfkill->pdata = pdata; - - len = strlen(pdata->name); - rfkill->reset_name = kzalloc(len + 7, GFP_KERNEL); - if (!rfkill->reset_name) { - ret = -ENOMEM; - goto fail_alloc; - } + len = strlen(rfkill->name); + rfkill->reset_name = devm_kzalloc(&pdev->dev, len + 7, GFP_KERNEL); + if (!rfkill->reset_name) + return -ENOMEM; - rfkill->shutdown_name = kzalloc(len + 10, GFP_KERNEL); - if (!rfkill->shutdown_name) { - ret = -ENOMEM; - goto fail_reset_name; - } + rfkill->shutdown_name = devm_kzalloc(&pdev->dev, len + 10, GFP_KERNEL); + if (!rfkill->shutdown_name) + return -ENOMEM; - snprintf(rfkill->reset_name, len + 6 , "%s_reset", pdata->name); - snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", pdata->name); + snprintf(rfkill->reset_name, len + 6 , "%s_reset", rfkill->name); + snprintf(rfkill->shutdown_name, len + 9, "%s_shutdown", rfkill->name); - if (pdata->power_clk_name) { - rfkill->pwr_clk = clk_get(&pdev->dev, pdata->power_clk_name); - if (IS_ERR(rfkill->pwr_clk)) { - pr_warn("%s: can't find pwr_clk.\n", __func__); - ret = PTR_ERR(rfkill->pwr_clk); - goto fail_shutdown_name; - } - } + rfkill->clk = devm_clk_get(&pdev->dev, clk_name); - if (gpio_is_valid(pdata->reset_gpio)) { - ret = gpio_request(pdata->reset_gpio, rfkill->reset_name); + if (gpio_is_valid(rfkill->reset_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, rfkill->reset_gpio, + 0, rfkill->reset_name); if (ret) { pr_warn("%s: failed to get reset gpio.\n", __func__); - goto fail_clock; + return ret; } } - if (gpio_is_valid(pdata->shutdown_gpio)) { - ret = gpio_request(pdata->shutdown_gpio, rfkill->shutdown_name); + if (gpio_is_valid(rfkill->shutdown_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, rfkill->shutdown_gpio, + 0, rfkill->shutdown_name); if (ret) { pr_warn("%s: failed to get shutdown gpio.\n", __func__); - goto fail_reset; + return ret; } } - rfkill->rfkill_dev = rfkill_alloc(pdata->name, &pdev->dev, pdata->type, - &rfkill_gpio_ops, rfkill); - if (!rfkill->rfkill_dev) { - ret = -ENOMEM; - goto fail_shutdown; - } + rfkill->rfkill_dev = rfkill_alloc(rfkill->name, &pdev->dev, + rfkill->type, &rfkill_gpio_ops, + rfkill); + if (!rfkill->rfkill_dev) + return -ENOMEM; ret = rfkill_register(rfkill->rfkill_dev); if (ret < 0) - goto fail_rfkill; + return ret; platform_set_drvdata(pdev, rfkill); - dev_info(&pdev->dev, "%s device registered.\n", pdata->name); + dev_info(&pdev->dev, "%s device registered.\n", rfkill->name); return 0; - -fail_rfkill: - rfkill_destroy(rfkill->rfkill_dev); -fail_shutdown: - if (gpio_is_valid(pdata->shutdown_gpio)) - gpio_free(pdata->shutdown_gpio); -fail_reset: - if (gpio_is_valid(pdata->reset_gpio)) - gpio_free(pdata->reset_gpio); -fail_clock: - if (rfkill->pwr_clk) - clk_put(rfkill->pwr_clk); -fail_shutdown_name: - kfree(rfkill->shutdown_name); -fail_reset_name: - kfree(rfkill->reset_name); -fail_alloc: - kfree(rfkill); - - return ret; } static int rfkill_gpio_remove(struct platform_device *pdev) @@ -195,31 +185,26 @@ static int rfkill_gpio_remove(struct platform_device *pdev) struct rfkill_gpio_data *rfkill = platform_get_drvdata(pdev); struct rfkill_gpio_platform_data *pdata = pdev->dev.platform_data; - if (pdata->gpio_runtime_close) + if (pdata && pdata->gpio_runtime_close) pdata->gpio_runtime_close(pdev); rfkill_unregister(rfkill->rfkill_dev); rfkill_destroy(rfkill->rfkill_dev); - if (gpio_is_valid(rfkill->pdata->shutdown_gpio)) - gpio_free(rfkill->pdata->shutdown_gpio); - if (gpio_is_valid(rfkill->pdata->reset_gpio)) - gpio_free(rfkill->pdata->reset_gpio); - if (rfkill->pwr_clk && PWR_CLK_ENABLED(rfkill)) - clk_disable(rfkill->pwr_clk); - if (rfkill->pwr_clk) - clk_put(rfkill->pwr_clk); - kfree(rfkill->shutdown_name); - kfree(rfkill->reset_name); - kfree(rfkill); return 0; } +static const struct acpi_device_id rfkill_acpi_match[] = { + { "BCM4752", RFKILL_TYPE_GPS }, + { }, +}; + static struct platform_driver rfkill_gpio_driver = { .probe = rfkill_gpio_probe, .remove = rfkill_gpio_remove, .driver = { - .name = "rfkill_gpio", - .owner = THIS_MODULE, + .name = "rfkill_gpio", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(rfkill_acpi_match), }, }; diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index a693aca2ae2eb61a5c258fa21aa07b05e20c8e53..5f43675ee1df3822ecf98e432797f9b498047b58 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -426,17 +426,16 @@ extern struct workqueue_struct *rxrpc_workqueue; /* * ar-accept.c */ -extern void rxrpc_accept_incoming_calls(struct work_struct *); -extern struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *, - unsigned long); -extern int rxrpc_reject_call(struct rxrpc_sock *); +void rxrpc_accept_incoming_calls(struct work_struct *); +struct rxrpc_call *rxrpc_accept_call(struct rxrpc_sock *, unsigned long); +int rxrpc_reject_call(struct rxrpc_sock *); /* * ar-ack.c */ -extern void __rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool); -extern void rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool); -extern void rxrpc_process_call(struct work_struct *); +void __rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool); +void rxrpc_propose_ACK(struct rxrpc_call *, u8, __be32, bool); +void rxrpc_process_call(struct work_struct *); /* * ar-call.c @@ -445,19 +444,18 @@ extern struct kmem_cache *rxrpc_call_jar; extern struct list_head rxrpc_calls; extern rwlock_t rxrpc_call_lock; -extern struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *, - struct rxrpc_transport *, - struct rxrpc_conn_bundle *, - unsigned long, int, gfp_t); -extern struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *, - struct rxrpc_connection *, - struct rxrpc_header *, gfp_t); -extern struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *, - unsigned long); -extern void rxrpc_release_call(struct rxrpc_call *); -extern void rxrpc_release_calls_on_socket(struct rxrpc_sock *); -extern void __rxrpc_put_call(struct rxrpc_call *); -extern void __exit rxrpc_destroy_all_calls(void); +struct rxrpc_call *rxrpc_get_client_call(struct rxrpc_sock *, + struct rxrpc_transport *, + struct rxrpc_conn_bundle *, + unsigned long, int, gfp_t); +struct rxrpc_call *rxrpc_incoming_call(struct rxrpc_sock *, + struct rxrpc_connection *, + struct rxrpc_header *, gfp_t); +struct rxrpc_call *rxrpc_find_server_call(struct rxrpc_sock *, unsigned long); +void rxrpc_release_call(struct rxrpc_call *); +void rxrpc_release_calls_on_socket(struct rxrpc_sock *); +void __rxrpc_put_call(struct rxrpc_call *); +void __exit rxrpc_destroy_all_calls(void); /* * ar-connection.c @@ -465,19 +463,16 @@ extern void __exit rxrpc_destroy_all_calls(void); extern struct list_head rxrpc_connections; extern rwlock_t rxrpc_connection_lock; -extern struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *, - struct rxrpc_transport *, - struct key *, - __be16, gfp_t); -extern void rxrpc_put_bundle(struct rxrpc_transport *, - struct rxrpc_conn_bundle *); -extern int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_transport *, - struct rxrpc_conn_bundle *, struct rxrpc_call *, - gfp_t); -extern void rxrpc_put_connection(struct rxrpc_connection *); -extern void __exit rxrpc_destroy_all_connections(void); -extern struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *, - struct rxrpc_header *); +struct rxrpc_conn_bundle *rxrpc_get_bundle(struct rxrpc_sock *, + struct rxrpc_transport *, + struct key *, __be16, gfp_t); +void rxrpc_put_bundle(struct rxrpc_transport *, struct rxrpc_conn_bundle *); +int rxrpc_connect_call(struct rxrpc_sock *, struct rxrpc_transport *, + struct rxrpc_conn_bundle *, struct rxrpc_call *, gfp_t); +void rxrpc_put_connection(struct rxrpc_connection *); +void __exit rxrpc_destroy_all_connections(void); +struct rxrpc_connection *rxrpc_find_connection(struct rxrpc_transport *, + struct rxrpc_header *); extern struct rxrpc_connection * rxrpc_incoming_connection(struct rxrpc_transport *, struct rxrpc_header *, gfp_t); @@ -485,15 +480,15 @@ rxrpc_incoming_connection(struct rxrpc_transport *, struct rxrpc_header *, /* * ar-connevent.c */ -extern void rxrpc_process_connection(struct work_struct *); -extern void rxrpc_reject_packet(struct rxrpc_local *, struct sk_buff *); -extern void rxrpc_reject_packets(struct work_struct *); +void rxrpc_process_connection(struct work_struct *); +void rxrpc_reject_packet(struct rxrpc_local *, struct sk_buff *); +void rxrpc_reject_packets(struct work_struct *); /* * ar-error.c */ -extern void rxrpc_UDP_error_report(struct sock *); -extern void rxrpc_UDP_error_handler(struct work_struct *); +void rxrpc_UDP_error_report(struct sock *); +void rxrpc_UDP_error_handler(struct work_struct *); /* * ar-input.c @@ -501,18 +496,17 @@ extern void rxrpc_UDP_error_handler(struct work_struct *); extern unsigned long rxrpc_ack_timeout; extern const char *rxrpc_pkts[]; -extern void rxrpc_data_ready(struct sock *, int); -extern int rxrpc_queue_rcv_skb(struct rxrpc_call *, struct sk_buff *, bool, - bool); -extern void rxrpc_fast_process_packet(struct rxrpc_call *, struct sk_buff *); +void rxrpc_data_ready(struct sock *, int); +int rxrpc_queue_rcv_skb(struct rxrpc_call *, struct sk_buff *, bool, bool); +void rxrpc_fast_process_packet(struct rxrpc_call *, struct sk_buff *); /* * ar-local.c */ extern rwlock_t rxrpc_local_lock; -extern struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *); -extern void rxrpc_put_local(struct rxrpc_local *); -extern void __exit rxrpc_destroy_all_locals(void); +struct rxrpc_local *rxrpc_lookup_local(struct sockaddr_rxrpc *); +void rxrpc_put_local(struct rxrpc_local *); +void __exit rxrpc_destroy_all_locals(void); /* * ar-key.c @@ -520,31 +514,29 @@ extern void __exit rxrpc_destroy_all_locals(void); extern struct key_type key_type_rxrpc; extern struct key_type key_type_rxrpc_s; -extern int rxrpc_request_key(struct rxrpc_sock *, char __user *, int); -extern int rxrpc_server_keyring(struct rxrpc_sock *, char __user *, int); -extern int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, - time_t, u32); +int rxrpc_request_key(struct rxrpc_sock *, char __user *, int); +int rxrpc_server_keyring(struct rxrpc_sock *, char __user *, int); +int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, time_t, + u32); /* * ar-output.c */ extern int rxrpc_resend_timeout; -extern int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *); -extern int rxrpc_client_sendmsg(struct kiocb *, struct rxrpc_sock *, - struct rxrpc_transport *, struct msghdr *, - size_t); -extern int rxrpc_server_sendmsg(struct kiocb *, struct rxrpc_sock *, - struct msghdr *, size_t); +int rxrpc_send_packet(struct rxrpc_transport *, struct sk_buff *); +int rxrpc_client_sendmsg(struct kiocb *, struct rxrpc_sock *, + struct rxrpc_transport *, struct msghdr *, size_t); +int rxrpc_server_sendmsg(struct kiocb *, struct rxrpc_sock *, struct msghdr *, + size_t); /* * ar-peer.c */ -extern struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t); -extern void rxrpc_put_peer(struct rxrpc_peer *); -extern struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *, - __be32, __be16); -extern void __exit rxrpc_destroy_all_peers(void); +struct rxrpc_peer *rxrpc_get_peer(struct sockaddr_rxrpc *, gfp_t); +void rxrpc_put_peer(struct rxrpc_peer *); +struct rxrpc_peer *rxrpc_find_peer(struct rxrpc_local *, __be32, __be16); +void __exit rxrpc_destroy_all_peers(void); /* * ar-proc.c @@ -556,38 +548,36 @@ extern const struct file_operations rxrpc_connection_seq_fops; /* * ar-recvmsg.c */ -extern void rxrpc_remove_user_ID(struct rxrpc_sock *, struct rxrpc_call *); -extern int rxrpc_recvmsg(struct kiocb *, struct socket *, struct msghdr *, - size_t, int); +void rxrpc_remove_user_ID(struct rxrpc_sock *, struct rxrpc_call *); +int rxrpc_recvmsg(struct kiocb *, struct socket *, struct msghdr *, size_t, + int); /* * ar-security.c */ -extern int rxrpc_register_security(struct rxrpc_security *); -extern void rxrpc_unregister_security(struct rxrpc_security *); -extern int rxrpc_init_client_conn_security(struct rxrpc_connection *); -extern int rxrpc_init_server_conn_security(struct rxrpc_connection *); -extern int rxrpc_secure_packet(const struct rxrpc_call *, struct sk_buff *, - size_t, void *); -extern int rxrpc_verify_packet(const struct rxrpc_call *, struct sk_buff *, - u32 *); -extern void rxrpc_clear_conn_security(struct rxrpc_connection *); +int rxrpc_register_security(struct rxrpc_security *); +void rxrpc_unregister_security(struct rxrpc_security *); +int rxrpc_init_client_conn_security(struct rxrpc_connection *); +int rxrpc_init_server_conn_security(struct rxrpc_connection *); +int rxrpc_secure_packet(const struct rxrpc_call *, struct sk_buff *, size_t, + void *); +int rxrpc_verify_packet(const struct rxrpc_call *, struct sk_buff *, u32 *); +void rxrpc_clear_conn_security(struct rxrpc_connection *); /* * ar-skbuff.c */ -extern void rxrpc_packet_destructor(struct sk_buff *); +void rxrpc_packet_destructor(struct sk_buff *); /* * ar-transport.c */ -extern struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *, - struct rxrpc_peer *, - gfp_t); -extern void rxrpc_put_transport(struct rxrpc_transport *); -extern void __exit rxrpc_destroy_all_transports(void); -extern struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *, - struct rxrpc_peer *); +struct rxrpc_transport *rxrpc_get_transport(struct rxrpc_local *, + struct rxrpc_peer *, gfp_t); +void rxrpc_put_transport(struct rxrpc_transport *); +void __exit rxrpc_destroy_all_transports(void); +struct rxrpc_transport *rxrpc_find_transport(struct rxrpc_local *, + struct rxrpc_peer *); /* * debug tracing diff --git a/net/sched/Kconfig b/net/sched/Kconfig index c03a32a0418e6f95477f106637d96231c2e6af42..ad1f1d819203698cc5cb483cfc7edbfd15131b9a 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -443,6 +443,16 @@ config NET_CLS_CGROUP To compile this code as a module, choose M here: the module will be called cls_cgroup. +config NET_CLS_BPF + tristate "BPF-based classifier" + select NET_CLS + ---help--- + If you say Y here, you will be able to classify packets based on + programmable BPF (JIT'ed) filters as an alternative to ematches. + + To compile this code as a module, choose M here: the module will + be called cls_bpf. + config NET_EMATCH bool "Extended Matches" select NET_CLS diff --git a/net/sched/Makefile b/net/sched/Makefile index e5f9abe9a5db6a2b9554b3a3cbf20c47ecd486b0..35fa47a494abed4947032760e56e91d8637152b1 100644 --- a/net/sched/Makefile +++ b/net/sched/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_NET_CLS_RSVP6) += cls_rsvp6.o obj-$(CONFIG_NET_CLS_BASIC) += cls_basic.o obj-$(CONFIG_NET_CLS_FLOW) += cls_flow.o obj-$(CONFIG_NET_CLS_CGROUP) += cls_cgroup.o +obj-$(CONFIG_NET_CLS_BPF) += cls_bpf.o obj-$(CONFIG_NET_EMATCH) += ematch.o obj-$(CONFIG_NET_EMATCH_CMP) += em_cmp.o obj-$(CONFIG_NET_EMATCH_NBYTE) += em_nbyte.o diff --git a/net/sched/act_police.c b/net/sched/act_police.c index 189e3c5b3d098a0d224a8fb2a04b1bd76e47b3ac..272d8e924cf6b2467e2772898aca2d7895131545 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -231,14 +231,14 @@ override: } if (R_tab) { police->rate_present = true; - psched_ratecfg_precompute(&police->rate, &R_tab->rate); + psched_ratecfg_precompute(&police->rate, &R_tab->rate, 0); qdisc_put_rtab(R_tab); } else { police->rate_present = false; } if (P_tab) { police->peak_present = true; - psched_ratecfg_precompute(&police->peak, &P_tab->rate); + psched_ratecfg_precompute(&police->peak, &P_tab->rate, 0); qdisc_put_rtab(P_tab); } else { police->peak_present = false; diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index d76a35d0dc85b82ddd9b8f2cc037b1ff0d31f5d5..636d9131d87016a46597af5cadd3471d21e1798f 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -137,7 +137,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp, struct nlattr **tb, struct nlattr *est) { - int err = -EINVAL; + int err; struct tcf_exts e; struct tcf_ematch_tree t; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c new file mode 100644 index 0000000000000000000000000000000000000000..1002a8226281ba6439b13c7a65c2b7e91945b539 --- /dev/null +++ b/net/sched/cls_bpf.c @@ -0,0 +1,385 @@ +/* + * Berkeley Packet Filter based traffic classifier + * + * Might be used to classify traffic through flexible, user-defined and + * possibly JIT-ed BPF filters for traffic control as an alternative to + * ematches. + * + * (C) 2013 Daniel Borkmann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel Borkmann "); +MODULE_DESCRIPTION("TC BPF based classifier"); + +struct cls_bpf_head { + struct list_head plist; + u32 hgen; +}; + +struct cls_bpf_prog { + struct sk_filter *filter; + struct sock_filter *bpf_ops; + struct tcf_exts exts; + struct tcf_result res; + struct list_head link; + u32 handle; + u16 bpf_len; +}; + +static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = { + [TCA_BPF_CLASSID] = { .type = NLA_U32 }, + [TCA_BPF_OPS_LEN] = { .type = NLA_U16 }, + [TCA_BPF_OPS] = { .type = NLA_BINARY, + .len = sizeof(struct sock_filter) * BPF_MAXINSNS }, +}; + +static const struct tcf_ext_map bpf_ext_map = { + .action = TCA_BPF_ACT, + .police = TCA_BPF_POLICE, +}; + +static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp, + struct tcf_result *res) +{ + struct cls_bpf_head *head = tp->root; + struct cls_bpf_prog *prog; + int ret; + + list_for_each_entry(prog, &head->plist, link) { + int filter_res = SK_RUN_FILTER(prog->filter, skb); + + if (filter_res == 0) + continue; + + *res = prog->res; + if (filter_res != -1) + res->classid = filter_res; + + ret = tcf_exts_exec(skb, &prog->exts, res); + if (ret < 0) + continue; + + return ret; + } + + return -1; +} + +static int cls_bpf_init(struct tcf_proto *tp) +{ + struct cls_bpf_head *head; + + head = kzalloc(sizeof(*head), GFP_KERNEL); + if (head == NULL) + return -ENOBUFS; + + INIT_LIST_HEAD(&head->plist); + tp->root = head; + + return 0; +} + +static void cls_bpf_delete_prog(struct tcf_proto *tp, struct cls_bpf_prog *prog) +{ + tcf_unbind_filter(tp, &prog->res); + tcf_exts_destroy(tp, &prog->exts); + + sk_unattached_filter_destroy(prog->filter); + + kfree(prog->bpf_ops); + kfree(prog); +} + +static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg) +{ + struct cls_bpf_head *head = tp->root; + struct cls_bpf_prog *prog, *todel = (struct cls_bpf_prog *) arg; + + list_for_each_entry(prog, &head->plist, link) { + if (prog == todel) { + tcf_tree_lock(tp); + list_del(&prog->link); + tcf_tree_unlock(tp); + + cls_bpf_delete_prog(tp, prog); + return 0; + } + } + + return -ENOENT; +} + +static void cls_bpf_destroy(struct tcf_proto *tp) +{ + struct cls_bpf_head *head = tp->root; + struct cls_bpf_prog *prog, *tmp; + + list_for_each_entry_safe(prog, tmp, &head->plist, link) { + list_del(&prog->link); + cls_bpf_delete_prog(tp, prog); + } + + kfree(head); +} + +static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) +{ + struct cls_bpf_head *head = tp->root; + struct cls_bpf_prog *prog; + unsigned long ret = 0UL; + + if (head == NULL) + return 0UL; + + list_for_each_entry(prog, &head->plist, link) { + if (prog->handle == handle) { + ret = (unsigned long) prog; + break; + } + } + + return ret; +} + +static void cls_bpf_put(struct tcf_proto *tp, unsigned long f) +{ +} + +static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, + struct cls_bpf_prog *prog, + unsigned long base, struct nlattr **tb, + struct nlattr *est) +{ + struct sock_filter *bpf_ops, *bpf_old; + struct tcf_exts exts; + struct sock_fprog tmp; + struct sk_filter *fp, *fp_old; + u16 bpf_size, bpf_len; + u32 classid; + int ret; + + if (!tb[TCA_BPF_OPS_LEN] || !tb[TCA_BPF_OPS] || !tb[TCA_BPF_CLASSID]) + return -EINVAL; + + ret = tcf_exts_validate(net, tp, tb, est, &exts, &bpf_ext_map); + if (ret < 0) + return ret; + + classid = nla_get_u32(tb[TCA_BPF_CLASSID]); + bpf_len = nla_get_u16(tb[TCA_BPF_OPS_LEN]); + if (bpf_len > BPF_MAXINSNS || bpf_len == 0) { + ret = -EINVAL; + goto errout; + } + + bpf_size = bpf_len * sizeof(*bpf_ops); + bpf_ops = kzalloc(bpf_size, GFP_KERNEL); + if (bpf_ops == NULL) { + ret = -ENOMEM; + goto errout; + } + + memcpy(bpf_ops, nla_data(tb[TCA_BPF_OPS]), bpf_size); + + tmp.len = bpf_len; + tmp.filter = (struct sock_filter __user *) bpf_ops; + + ret = sk_unattached_filter_create(&fp, &tmp); + if (ret) + goto errout_free; + + tcf_tree_lock(tp); + fp_old = prog->filter; + bpf_old = prog->bpf_ops; + + prog->bpf_len = bpf_len; + prog->bpf_ops = bpf_ops; + prog->filter = fp; + prog->res.classid = classid; + tcf_tree_unlock(tp); + + tcf_bind_filter(tp, &prog->res, base); + tcf_exts_change(tp, &prog->exts, &exts); + + if (fp_old) + sk_unattached_filter_destroy(fp_old); + if (bpf_old) + kfree(bpf_old); + + return 0; + +errout_free: + kfree(bpf_ops); +errout: + tcf_exts_destroy(tp, &exts); + return ret; +} + +static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp, + struct cls_bpf_head *head) +{ + unsigned int i = 0x80000000; + + do { + if (++head->hgen == 0x7FFFFFFF) + head->hgen = 1; + } while (--i > 0 && cls_bpf_get(tp, head->hgen)); + if (i == 0) + pr_err("Insufficient number of handles\n"); + + return i; +} + +static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, + struct tcf_proto *tp, unsigned long base, + u32 handle, struct nlattr **tca, + unsigned long *arg) +{ + struct cls_bpf_head *head = tp->root; + struct cls_bpf_prog *prog = (struct cls_bpf_prog *) *arg; + struct nlattr *tb[TCA_BPF_MAX + 1]; + int ret; + + if (tca[TCA_OPTIONS] == NULL) + return -EINVAL; + + ret = nla_parse_nested(tb, TCA_BPF_MAX, tca[TCA_OPTIONS], bpf_policy); + if (ret < 0) + return ret; + + if (prog != NULL) { + if (handle && prog->handle != handle) + return -EINVAL; + return cls_bpf_modify_existing(net, tp, prog, base, tb, + tca[TCA_RATE]); + } + + prog = kzalloc(sizeof(*prog), GFP_KERNEL); + if (prog == NULL) + return -ENOBUFS; + + if (handle == 0) + prog->handle = cls_bpf_grab_new_handle(tp, head); + else + prog->handle = handle; + if (prog->handle == 0) { + ret = -EINVAL; + goto errout; + } + + ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE]); + if (ret < 0) + goto errout; + + tcf_tree_lock(tp); + list_add(&prog->link, &head->plist); + tcf_tree_unlock(tp); + + *arg = (unsigned long) prog; + + return 0; +errout: + if (*arg == 0UL && prog) + kfree(prog); + + return ret; +} + +static int cls_bpf_dump(struct tcf_proto *tp, unsigned long fh, + struct sk_buff *skb, struct tcmsg *tm) +{ + struct cls_bpf_prog *prog = (struct cls_bpf_prog *) fh; + struct nlattr *nest, *nla; + + if (prog == NULL) + return skb->len; + + tm->tcm_handle = prog->handle; + + nest = nla_nest_start(skb, TCA_OPTIONS); + if (nest == NULL) + goto nla_put_failure; + + if (nla_put_u32(skb, TCA_BPF_CLASSID, prog->res.classid)) + goto nla_put_failure; + if (nla_put_u16(skb, TCA_BPF_OPS_LEN, prog->bpf_len)) + goto nla_put_failure; + + nla = nla_reserve(skb, TCA_BPF_OPS, prog->bpf_len * + sizeof(struct sock_filter)); + if (nla == NULL) + goto nla_put_failure; + + memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla)); + + if (tcf_exts_dump(skb, &prog->exts, &bpf_ext_map) < 0) + goto nla_put_failure; + + nla_nest_end(skb, nest); + + if (tcf_exts_dump_stats(skb, &prog->exts, &bpf_ext_map) < 0) + goto nla_put_failure; + + return skb->len; + +nla_put_failure: + nla_nest_cancel(skb, nest); + return -1; +} + +static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg) +{ + struct cls_bpf_head *head = tp->root; + struct cls_bpf_prog *prog; + + list_for_each_entry(prog, &head->plist, link) { + if (arg->count < arg->skip) + goto skip; + if (arg->fn(tp, (unsigned long) prog, arg) < 0) { + arg->stop = 1; + break; + } +skip: + arg->count++; + } +} + +static struct tcf_proto_ops cls_bpf_ops __read_mostly = { + .kind = "bpf", + .owner = THIS_MODULE, + .classify = cls_bpf_classify, + .init = cls_bpf_init, + .destroy = cls_bpf_destroy, + .get = cls_bpf_get, + .put = cls_bpf_put, + .change = cls_bpf_change, + .delete = cls_bpf_delete, + .walk = cls_bpf_walk, + .dump = cls_bpf_dump, +}; + +static int __init cls_bpf_init_mod(void) +{ + return register_tcf_proto_ops(&cls_bpf_ops); +} + +static void __exit cls_bpf_exit_mod(void) +{ + unregister_tcf_proto_ops(&cls_bpf_ops); +} + +module_init(cls_bpf_init_mod); +module_exit(cls_bpf_exit_mod); diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 867b4a3e39800fb44819e3cfd68ad0fb30d6ea05..16006c92c3fde515d9047a4e32f0ec3f0df2f005 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -72,11 +72,11 @@ static void cgrp_attach(struct cgroup_subsys_state *css, struct cgroup_taskset *tset) { struct task_struct *p; - void *v; + struct cgroup_cls_state *cs = css_cls_state(css); + void *v = (void *)(unsigned long)cs->classid; cgroup_taskset_for_each(p, css, tset) { task_lock(p); - v = (void *)(unsigned long)task_cls_classid(p); iterate_fd(p->files, 0, update_classid, v); task_unlock(p); } diff --git a/net/sched/em_ipset.c b/net/sched/em_ipset.c index 938b7cbf56278b34c6d5574a64c8c32fa4a1d3a2..527aeb7a3ff0b3b558873d0c7d93fc2c26e100b8 100644 --- a/net/sched/em_ipset.c +++ b/net/sched/em_ipset.c @@ -24,11 +24,12 @@ static int em_ipset_change(struct tcf_proto *tp, void *data, int data_len, { struct xt_set_info *set = data; ip_set_id_t index; + struct net *net = dev_net(qdisc_dev(tp->q)); if (data_len != sizeof(*set)) return -EINVAL; - index = ip_set_nfnl_get_byindex(set->index); + index = ip_set_nfnl_get_byindex(net, set->index); if (index == IPSET_INVALID_ID) return -ENOENT; @@ -37,7 +38,7 @@ static int em_ipset_change(struct tcf_proto *tp, void *data, int data_len, if (em->data) return 0; - ip_set_nfnl_put(index); + ip_set_nfnl_put(net, index); return -ENOMEM; } @@ -45,7 +46,7 @@ static void em_ipset_destroy(struct tcf_proto *p, struct tcf_ematch *em) { const struct xt_set_info *set = (const void *) em->data; if (set) { - ip_set_nfnl_put(set->index); + ip_set_nfnl_put(dev_net(qdisc_dev(p->q)), set->index); kfree((void *) em->data); } } diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index 7c3de6ffa5164db0f7abd3f0e2cc0ca2e92ecda9..e5cef95672256b650249c80bef36930f78be9f13 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -793,8 +793,10 @@ static int em_meta_change(struct tcf_proto *tp, void *data, int len, goto errout; meta = kzalloc(sizeof(*meta), GFP_KERNEL); - if (meta == NULL) + if (meta == NULL) { + err = -ENOMEM; goto errout; + } memcpy(&meta->lvalue.hdr, &hdr->left, sizeof(hdr->left)); memcpy(&meta->rvalue.hdr, &hdr->right, sizeof(hdr->right)); diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 2adda7fa2d390c4bb2aec883cf049df7e7cef9ef..cd81505662b8a3bcc201a18bce0d6c9bd421b650 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -737,9 +737,11 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n) const struct Qdisc_class_ops *cops; unsigned long cl; u32 parentid; + int drops; if (n == 0) return; + drops = max_t(int, n, 0); while ((parentid = sch->parent)) { if (TC_H_MAJ(parentid) == TC_H_MAJ(TC_H_INGRESS)) return; @@ -756,6 +758,7 @@ void qdisc_tree_decrease_qlen(struct Qdisc *sch, unsigned int n) cops->put(sch, cl); } sch->q.qlen -= n; + sch->qstats.drops += drops; } } EXPORT_SYMBOL(qdisc_tree_decrease_qlen); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index a74e278654aa2290a0ef3061d1392c5cf405e5f5..922a09406ba70573499877ac3e561c9ee7b61a5c 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -126,7 +126,7 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q, HARD_TX_LOCK(dev, txq, smp_processor_id()); if (!netif_xmit_frozen_or_stopped(txq)) - ret = dev_hard_start_xmit(skb, dev, txq); + ret = dev_hard_start_xmit(skb, dev, txq, NULL); HARD_TX_UNLOCK(dev, txq); @@ -829,7 +829,7 @@ void dev_deactivate_many(struct list_head *head) struct net_device *dev; bool sync_needed = false; - list_for_each_entry(dev, head, unreg_list) { + list_for_each_entry(dev, head, close_list) { netdev_for_each_tx_queue(dev, dev_deactivate_queue, &noop_qdisc); if (dev_ingress_queue(dev)) @@ -848,7 +848,7 @@ void dev_deactivate_many(struct list_head *head) synchronize_net(); /* Wait for outstanding qdisc_run calls. */ - list_for_each_entry(dev, head, unreg_list) + list_for_each_entry(dev, head, close_list) while (some_qdisc_is_busy(dev)) yield(); } @@ -857,7 +857,7 @@ void dev_deactivate(struct net_device *dev) { LIST_HEAD(single); - list_add(&dev->unreg_list, &single); + list_add(&dev->close_list, &single); dev_deactivate_many(&single); list_del(&single); } @@ -910,11 +910,12 @@ void dev_shutdown(struct net_device *dev) } void psched_ratecfg_precompute(struct psched_ratecfg *r, - const struct tc_ratespec *conf) + const struct tc_ratespec *conf, + u64 rate64) { memset(r, 0, sizeof(*r)); r->overhead = conf->overhead; - r->rate_bytes_ps = conf->rate; + r->rate_bytes_ps = max_t(u64, conf->rate, rate64); r->linklayer = (conf->linklayer & TC_LINKLAYER_MASK); r->mult = 1; /* diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 863846cc5513b5cd136264265d226a811bd868c6..0e1e38b40025fd111f50bfce339a6d2e7cae1252 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -997,6 +997,8 @@ static const struct nla_policy htb_policy[TCA_HTB_MAX + 1] = { [TCA_HTB_CTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE }, [TCA_HTB_RTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE }, [TCA_HTB_DIRECT_QLEN] = { .type = NLA_U32 }, + [TCA_HTB_RATE64] = { .type = NLA_U64 }, + [TCA_HTB_CEIL64] = { .type = NLA_U64 }, }; static void htb_work_func(struct work_struct *work) @@ -1114,6 +1116,12 @@ static int htb_dump_class(struct Qdisc *sch, unsigned long arg, opt.level = cl->level; if (nla_put(skb, TCA_HTB_PARMS, sizeof(opt), &opt)) goto nla_put_failure; + if ((cl->rate.rate_bytes_ps >= (1ULL << 32)) && + nla_put_u64(skb, TCA_HTB_RATE64, cl->rate.rate_bytes_ps)) + goto nla_put_failure; + if ((cl->ceil.rate_bytes_ps >= (1ULL << 32)) && + nla_put_u64(skb, TCA_HTB_CEIL64, cl->ceil.rate_bytes_ps)) + goto nla_put_failure; nla_nest_end(skb, nest); spin_unlock_bh(root_lock); @@ -1332,6 +1340,7 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, struct qdisc_rate_table *rtab = NULL, *ctab = NULL; struct nlattr *tb[TCA_HTB_MAX + 1]; struct tc_htb_opt *hopt; + u64 rate64, ceil64; /* extract all subattrs from opt attr */ if (!opt) @@ -1491,8 +1500,12 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, cl->prio = TC_HTB_NUMPRIO - 1; } - psched_ratecfg_precompute(&cl->rate, &hopt->rate); - psched_ratecfg_precompute(&cl->ceil, &hopt->ceil); + rate64 = tb[TCA_HTB_RATE64] ? nla_get_u64(tb[TCA_HTB_RATE64]) : 0; + + ceil64 = tb[TCA_HTB_CEIL64] ? nla_get_u64(tb[TCA_HTB_CEIL64]) : 0; + + psched_ratecfg_precompute(&cl->rate, &hopt->rate, rate64); + psched_ratecfg_precompute(&cl->ceil, &hopt->ceil, ceil64); cl->buffer = PSCHED_TICKS2NS(hopt->buffer); cl->cbuffer = PSCHED_TICKS2NS(hopt->cbuffer); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index b87e83d0747821bc2251c3b099f91a3358c211d9..75c94e59a3bd3889cd87460afe87ac611eeb024e 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -235,7 +235,6 @@ static bool loss_4state(struct netem_sched_data *q) clg->state = 2; else if (clg->a3 < rnd && rnd < clg->a2 + clg->a3) { clg->state = 1; - return true; } else if (clg->a2 + clg->a3 < rnd) { clg->state = 3; return true; diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index 1aaf1b6e51a2be238bc47797597ed3bcb76de4b1..68f98595819c1224f7e64ad368ad51155ff07fea 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -266,20 +266,23 @@ static const struct nla_policy tbf_policy[TCA_TBF_MAX + 1] = { [TCA_TBF_PARMS] = { .len = sizeof(struct tc_tbf_qopt) }, [TCA_TBF_RTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE }, [TCA_TBF_PTAB] = { .type = NLA_BINARY, .len = TC_RTAB_SIZE }, + [TCA_TBF_RATE64] = { .type = NLA_U64 }, + [TCA_TBF_PRATE64] = { .type = NLA_U64 }, }; static int tbf_change(struct Qdisc *sch, struct nlattr *opt) { int err; struct tbf_sched_data *q = qdisc_priv(sch); - struct nlattr *tb[TCA_TBF_PTAB + 1]; + struct nlattr *tb[TCA_TBF_MAX + 1]; struct tc_tbf_qopt *qopt; struct qdisc_rate_table *rtab = NULL; struct qdisc_rate_table *ptab = NULL; struct Qdisc *child = NULL; int max_size, n; + u64 rate64 = 0, prate64 = 0; - err = nla_parse_nested(tb, TCA_TBF_PTAB, opt, tbf_policy); + err = nla_parse_nested(tb, TCA_TBF_MAX, opt, tbf_policy); if (err < 0) return err; @@ -341,9 +344,13 @@ static int tbf_change(struct Qdisc *sch, struct nlattr *opt) q->tokens = q->buffer; q->ptokens = q->mtu; - psched_ratecfg_precompute(&q->rate, &rtab->rate); + if (tb[TCA_TBF_RATE64]) + rate64 = nla_get_u64(tb[TCA_TBF_RATE64]); + psched_ratecfg_precompute(&q->rate, &rtab->rate, rate64); if (ptab) { - psched_ratecfg_precompute(&q->peak, &ptab->rate); + if (tb[TCA_TBF_PRATE64]) + prate64 = nla_get_u64(tb[TCA_TBF_PRATE64]); + psched_ratecfg_precompute(&q->peak, &ptab->rate, prate64); q->peak_present = true; } else { q->peak_present = false; @@ -402,6 +409,13 @@ static int tbf_dump(struct Qdisc *sch, struct sk_buff *skb) opt.buffer = PSCHED_NS2TICKS(q->buffer); if (nla_put(skb, TCA_TBF_PARMS, sizeof(opt), &opt)) goto nla_put_failure; + if (q->rate.rate_bytes_ps >= (1ULL << 32) && + nla_put_u64(skb, TCA_TBF_RATE64, q->rate.rate_bytes_ps)) + goto nla_put_failure; + if (q->peak_present && + q->peak.rate_bytes_ps >= (1ULL << 32) && + nla_put_u64(skb, TCA_TBF_PRATE64, q->peak.rate_bytes_ps)) + goto nla_put_failure; nla_nest_end(skb, nest); return skb->len; diff --git a/net/sctp/associola.c b/net/sctp/associola.c index cef509985192a041f8d437adc35cd540e32f58bd..c9b91cb1cb0dfdd4561ef3632d7443416a66b19d 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -602,7 +602,7 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc, /* Start a T3 timer here in case it wasn't running so * that these migrated packets have a chance to get - * retrnasmitted. + * retransmitted. */ if (!timer_pending(&active->T3_rtx_timer)) if (!mod_timer(&active->T3_rtx_timer, @@ -665,7 +665,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, /* Set the path max_retrans. */ peer->pathmaxrxt = asoc->pathmaxrxt; - /* And the partial failure retrnas threshold */ + /* And the partial failure retrans threshold */ peer->pf_retrans = asoc->pf_retrans; /* Initialize the peer's SACK delay timeout based on the diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 8c4fa5dec8245e984ddf637134ca82265c5ddfe5..46b5977978a131b15c073f3b23ab49fa511e92bc 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -539,18 +539,14 @@ struct sctp_hmac *sctp_auth_asoc_get_hmac(const struct sctp_association *asoc) for (i = 0; i < n_elt; i++) { id = ntohs(hmacs->hmac_ids[i]); - /* Check the id is in the supported range */ - if (id > SCTP_AUTH_HMAC_ID_MAX) { - id = 0; - continue; - } - - /* See is we support the id. Supported IDs have name and - * length fields set, so that we can allocated and use + /* Check the id is in the supported range. And + * see if we support the id. Supported IDs have name and + * length fields set, so that we can allocate and use * them. We can safely just check for name, for without the * name, we can't allocate the TFM. */ - if (!sctp_hmac_list[id].hmac_name) { + if (id > SCTP_AUTH_HMAC_ID_MAX || + !sctp_hmac_list[id].hmac_name) { id = 0; continue; } diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 7bd5ed4a865727c87e729ec129a4350f93354a9a..f2044fcb9dd113d4764877f08bde77146be5d1a3 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -201,7 +201,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, max = asoc->frag_point; /* If the the peer requested that we authenticate DATA chunks - * we need to accound for bundling of the AUTH chunks along with + * we need to account for bundling of the AUTH chunks along with * DATA. */ if (sctp_auth_send_cid(SCTP_CID_DATA, asoc)) { diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 96a55910262c4e958bef93d203ce802dede835df..7567e6f1a9205bb0a37a29becc243d9ed6352fe4 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -428,20 +428,20 @@ static void sctp_v6_from_sk(union sctp_addr *addr, struct sock *sk) { addr->v6.sin6_family = AF_INET6; addr->v6.sin6_port = 0; - addr->v6.sin6_addr = inet6_sk(sk)->rcv_saddr; + addr->v6.sin6_addr = sk->sk_v6_rcv_saddr; } /* Initialize sk->sk_rcv_saddr from sctp_addr. */ static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk) { if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) { - inet6_sk(sk)->rcv_saddr.s6_addr32[0] = 0; - inet6_sk(sk)->rcv_saddr.s6_addr32[1] = 0; - inet6_sk(sk)->rcv_saddr.s6_addr32[2] = htonl(0x0000ffff); - inet6_sk(sk)->rcv_saddr.s6_addr32[3] = + sk->sk_v6_rcv_saddr.s6_addr32[0] = 0; + sk->sk_v6_rcv_saddr.s6_addr32[1] = 0; + sk->sk_v6_rcv_saddr.s6_addr32[2] = htonl(0x0000ffff); + sk->sk_v6_rcv_saddr.s6_addr32[3] = addr->v4.sin_addr.s_addr; } else { - inet6_sk(sk)->rcv_saddr = addr->v6.sin6_addr; + sk->sk_v6_rcv_saddr = addr->v6.sin6_addr; } } @@ -449,12 +449,12 @@ static void sctp_v6_to_sk_saddr(union sctp_addr *addr, struct sock *sk) static void sctp_v6_to_sk_daddr(union sctp_addr *addr, struct sock *sk) { if (addr->sa.sa_family == AF_INET && sctp_sk(sk)->v4mapped) { - inet6_sk(sk)->daddr.s6_addr32[0] = 0; - inet6_sk(sk)->daddr.s6_addr32[1] = 0; - inet6_sk(sk)->daddr.s6_addr32[2] = htonl(0x0000ffff); - inet6_sk(sk)->daddr.s6_addr32[3] = addr->v4.sin_addr.s_addr; + sk->sk_v6_daddr.s6_addr32[0] = 0; + sk->sk_v6_daddr.s6_addr32[1] = 0; + sk->sk_v6_daddr.s6_addr32[2] = htonl(0x0000ffff); + sk->sk_v6_daddr.s6_addr32[3] = addr->v4.sin_addr.s_addr; } else { - inet6_sk(sk)->daddr = addr->v6.sin6_addr; + sk->sk_v6_daddr = addr->v6.sin6_addr; } } diff --git a/net/sctp/output.c b/net/sctp/output.c index 319137340d158df36094c45b8eae1fc13df50825..e650978daf279abe9b1277dabb22f2e865c64a7a 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -390,7 +390,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) __u8 has_data = 0; struct dst_entry *dst = tp->dst; unsigned char *auth = NULL; /* pointer to auth in skb data */ - __u32 cksum_buf_len = sizeof(struct sctphdr); pr_debug("%s: packet:%p\n", __func__, packet); @@ -493,7 +492,6 @@ int sctp_packet_transmit(struct sctp_packet *packet) if (chunk == packet->auth) auth = skb_tail_pointer(nskb); - cksum_buf_len += chunk->skb->len; memcpy(skb_put(nskb, chunk->skb->len), chunk->skb->data, chunk->skb->len); @@ -538,12 +536,7 @@ int sctp_packet_transmit(struct sctp_packet *packet) if (!sctp_checksum_disable) { if (!(dst->dev->features & NETIF_F_SCTP_CSUM) || (dst_xfrm(dst) != NULL) || packet->ipfragok) { - __u32 crc32 = sctp_start_cksum((__u8 *)sh, cksum_buf_len); - - /* 3) Put the resultant value into the checksum field in the - * common header, and leave the rest of the bits unchanged. - */ - sh->checksum = sctp_end_cksum(crc32); + sh->checksum = sctp_compute_cksum(nskb, 0); } else { /* no need to seed pseudo checksum for SCTP */ nskb->ip_summed = CHECKSUM_PARTIAL; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index d244a23ab8d33c95e0b8133e212c35bc4a47875a..fe690320b1e430a7915e35d36d26a72847368438 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1297,6 +1297,13 @@ struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc) /* Turn an skb into a chunk. * FIXME: Eventually move the structure directly inside the skb->cb[]. + * + * sctpimpguide-05.txt Section 2.8.2 + * M1) Each time a new DATA chunk is transmitted + * set the 'TSN.Missing.Report' count for that TSN to 0. The + * 'TSN.Missing.Report' count will be used to determine missing chunks + * and when to fast retransmit. + * */ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, const struct sctp_association *asoc, @@ -1314,29 +1321,9 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, INIT_LIST_HEAD(&retval->list); retval->skb = skb; retval->asoc = (struct sctp_association *)asoc; - retval->has_tsn = 0; - retval->has_ssn = 0; - retval->rtt_in_progress = 0; - retval->sent_at = 0; retval->singleton = 1; - retval->end_of_packet = 0; - retval->ecn_ce_done = 0; - retval->pdiscard = 0; - - /* sctpimpguide-05.txt Section 2.8.2 - * M1) Each time a new DATA chunk is transmitted - * set the 'TSN.Missing.Report' count for that TSN to 0. The - * 'TSN.Missing.Report' count will be used to determine missing chunks - * and when to fast retransmit. - */ - retval->tsn_missing_report = 0; - retval->tsn_gap_acked = 0; - retval->fast_retransmit = SCTP_CAN_FRTX; - /* If this is a fragmented message, track all fragments - * of the message (for SEND_FAILED). - */ - retval->msg = NULL; + retval->fast_retransmit = SCTP_CAN_FRTX; /* Polish the bead hole. */ INIT_LIST_HEAD(&retval->transmitted_list); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 911b71b26b0e6670bfac1a7f8d450aea510f7333..72046b9729a8a6a669fb9c75446de8ef2fe345c6 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -5890,7 +5890,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr) int low, high, remaining, index; unsigned int rover; - inet_get_local_port_range(&low, &high); + inet_get_local_port_range(sock_net(sk), &low, &high); remaining = (high - low) + 1; rover = net_random() % remaining + low; diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 9c9caaa5e0d3129f8872350ded706321fb2509c2..b6e59f0a9475d665ea1952a8428e56ed6b06b89e 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -291,12 +291,14 @@ static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining) &inet_sk(sk)->inet_rcv_saddr, inet_sk(sk)->inet_num); break; +#if IS_ENABLED(CONFIG_IPV6) case PF_INET6: len = snprintf(buf, remaining, "ipv6 %s %pI6 %d\n", proto_name, - &inet6_sk(sk)->rcv_saddr, + &sk->sk_v6_rcv_saddr, inet_sk(sk)->inet_num); break; +#endif default: len = snprintf(buf, remaining, "*unknown-%d*\n", sk->sk_family); diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c index 716de1ac6cb560c8cc55a257a0d76d39230634f3..0d4402587fdf9fec2c1768cfdff6356e24dbb4fd 100644 --- a/net/tipc/bcast.c +++ b/net/tipc/bcast.c @@ -480,18 +480,24 @@ receive: tipc_node_unlock(node); tipc_link_recv_bundle(buf); } else if (msg_user(msg) == MSG_FRAGMENTER) { - int ret = tipc_link_recv_fragment(&node->bclink.defragm, - &buf, &msg); - if (ret < 0) + int ret; + ret = tipc_link_recv_fragment(&node->bclink.reasm_head, + &node->bclink.reasm_tail, + &buf); + if (ret == LINK_REASM_ERROR) goto unlock; spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); bcl->stats.recv_fragments++; - if (ret > 0) + if (ret == LINK_REASM_COMPLETE) { bcl->stats.recv_fragmented++; + /* Point msg to inner header */ + msg = buf_msg(buf); + spin_unlock_bh(&bc_lock); + goto receive; + } spin_unlock_bh(&bc_lock); tipc_node_unlock(node); - tipc_net_route_msg(buf); } else if (msg_user(msg) == NAME_DISTRIBUTOR) { spin_lock_bh(&bc_lock); bclink_accept_pkt(node, seqno); diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 609c30c808165191a69899e750439666addc29f8..3f9707a16d0650d6367ad716ab2a110318a03e96 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -387,7 +387,7 @@ restart: b_ptr = &tipc_bearers[bearer_id]; strcpy(b_ptr->name, name); - res = m_ptr->enable_bearer(b_ptr); + res = m_ptr->enable_media(b_ptr); if (res) { pr_warn("Bearer <%s> rejected, enable failure (%d)\n", name, -res); @@ -420,23 +420,15 @@ exit: } /** - * tipc_block_bearer - Block the bearer with the given name, and reset all its links + * tipc_block_bearer - Block the bearer, and reset all its links */ -int tipc_block_bearer(const char *name) +int tipc_block_bearer(struct tipc_bearer *b_ptr) { - struct tipc_bearer *b_ptr = NULL; struct tipc_link *l_ptr; struct tipc_link *temp_l_ptr; read_lock_bh(&tipc_net_lock); - b_ptr = tipc_bearer_find(name); - if (!b_ptr) { - pr_warn("Attempt to block unknown bearer <%s>\n", name); - read_unlock_bh(&tipc_net_lock); - return -EINVAL; - } - - pr_info("Blocking bearer <%s>\n", name); + pr_info("Blocking bearer <%s>\n", b_ptr->name); spin_lock_bh(&b_ptr->lock); b_ptr->blocked = 1; list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) { @@ -465,7 +457,7 @@ static void bearer_disable(struct tipc_bearer *b_ptr) pr_info("Disabling bearer <%s>\n", b_ptr->name); spin_lock_bh(&b_ptr->lock); b_ptr->blocked = 1; - b_ptr->media->disable_bearer(b_ptr); + b_ptr->media->disable_media(b_ptr); list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) { tipc_link_delete(l_ptr); } diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 09c869adcfcf4fd7f3e6cac8423c4e3ccd7762c1..e5e04be6fffa3782c01f37db795c7f84c8b71793 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -75,8 +75,8 @@ struct tipc_bearer; /** * struct tipc_media - TIPC media information available to internal users * @send_msg: routine which handles buffer transmission - * @enable_bearer: routine which enables a bearer - * @disable_bearer: routine which disables a bearer + * @enable_media: routine which enables a media + * @disable_media: routine which disables a media * @addr2str: routine which converts media address to string * @addr2msg: routine which converts media address to protocol message area * @msg2addr: routine which converts media address from protocol message area @@ -91,8 +91,8 @@ struct tipc_media { int (*send_msg)(struct sk_buff *buf, struct tipc_bearer *b_ptr, struct tipc_media_addr *dest); - int (*enable_bearer)(struct tipc_bearer *b_ptr); - void (*disable_bearer)(struct tipc_bearer *b_ptr); + int (*enable_media)(struct tipc_bearer *b_ptr); + void (*disable_media)(struct tipc_bearer *b_ptr); int (*addr2str)(struct tipc_media_addr *a, char *str_buf, int str_size); int (*addr2msg)(struct tipc_media_addr *a, char *msg_area); int (*msg2addr)(const struct tipc_bearer *b_ptr, @@ -163,7 +163,7 @@ int tipc_register_media(struct tipc_media *m_ptr); void tipc_recv_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr); -int tipc_block_bearer(const char *name); +int tipc_block_bearer(struct tipc_bearer *b_ptr); void tipc_continue(struct tipc_bearer *tb_ptr); int tipc_enable_bearer(const char *bearer_name, u32 disc_domain, u32 priority); diff --git a/net/tipc/core.h b/net/tipc/core.h index be72f8cebc536a425a79d99a5435fa10f8e58b89..94895d4e86abe249f4ef954d527e119647665c92 100644 --- a/net/tipc/core.h +++ b/net/tipc/core.h @@ -90,21 +90,21 @@ extern int tipc_random __read_mostly; /* * Routines available to privileged subsystems */ -extern int tipc_core_start_net(unsigned long); -extern int tipc_handler_start(void); -extern void tipc_handler_stop(void); -extern int tipc_netlink_start(void); -extern void tipc_netlink_stop(void); -extern int tipc_socket_init(void); -extern void tipc_socket_stop(void); -extern int tipc_sock_create_local(int type, struct socket **res); -extern void tipc_sock_release_local(struct socket *sock); -extern int tipc_sock_accept_local(struct socket *sock, - struct socket **newsock, int flags); +int tipc_core_start_net(unsigned long); +int tipc_handler_start(void); +void tipc_handler_stop(void); +int tipc_netlink_start(void); +void tipc_netlink_stop(void); +int tipc_socket_init(void); +void tipc_socket_stop(void); +int tipc_sock_create_local(int type, struct socket **res); +void tipc_sock_release_local(struct socket *sock); +int tipc_sock_accept_local(struct socket *sock, struct socket **newsock, + int flags); #ifdef CONFIG_SYSCTL -extern int tipc_register_sysctl(void); -extern void tipc_unregister_sysctl(void); +int tipc_register_sysctl(void); +void tipc_unregister_sysctl(void); #else #define tipc_register_sysctl() 0 #define tipc_unregister_sysctl() @@ -201,6 +201,6 @@ static inline struct tipc_msg *buf_msg(struct sk_buff *skb) return (struct tipc_msg *)skb->data; } -extern struct sk_buff *tipc_buf_acquire(u32 size); +struct sk_buff *tipc_buf_acquire(u32 size); #endif diff --git a/net/tipc/eth_media.c b/net/tipc/eth_media.c index 40ea40cf6204506f4a579f99d2a0c3d8e79652ca..f80d59f5a161c33199ef4d00494117b8115e2676 100644 --- a/net/tipc/eth_media.c +++ b/net/tipc/eth_media.c @@ -2,7 +2,7 @@ * net/tipc/eth_media.c: Ethernet bearer support for TIPC * * Copyright (c) 2001-2007, Ericsson AB - * Copyright (c) 2005-2008, 2011, Wind River Systems + * Copyright (c) 2005-2008, 2011-2013, Wind River Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,19 +37,19 @@ #include "core.h" #include "bearer.h" -#define MAX_ETH_BEARERS MAX_BEARERS +#define MAX_ETH_MEDIA MAX_BEARERS #define ETH_ADDR_OFFSET 4 /* message header offset of MAC address */ /** - * struct eth_bearer - Ethernet bearer data structure + * struct eth_media - Ethernet bearer data structure * @bearer: ptr to associated "generic" bearer structure * @dev: ptr to associated Ethernet network device * @tipc_packet_type: used in binding TIPC to Ethernet driver * @setup: work item used when enabling bearer * @cleanup: work item used when disabling bearer */ -struct eth_bearer { +struct eth_media { struct tipc_bearer *bearer; struct net_device *dev; struct packet_type tipc_packet_type; @@ -58,7 +58,7 @@ struct eth_bearer { }; static struct tipc_media eth_media_info; -static struct eth_bearer eth_bearers[MAX_ETH_BEARERS]; +static struct eth_media eth_media_array[MAX_ETH_MEDIA]; static int eth_started; static int recv_notification(struct notifier_block *nb, unsigned long evt, @@ -100,7 +100,7 @@ static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr, if (!clone) return 0; - dev = ((struct eth_bearer *)(tb_ptr->usr_handle))->dev; + dev = ((struct eth_media *)(tb_ptr->usr_handle))->dev; delta = dev->hard_header_len - skb_headroom(buf); if ((delta > 0) && @@ -128,43 +128,43 @@ static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr, static int recv_msg(struct sk_buff *buf, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - struct eth_bearer *eb_ptr = (struct eth_bearer *)pt->af_packet_priv; + struct eth_media *eb_ptr = (struct eth_media *)pt->af_packet_priv; if (!net_eq(dev_net(dev), &init_net)) { kfree_skb(buf); - return 0; + return NET_RX_DROP; } if (likely(eb_ptr->bearer)) { if (likely(buf->pkt_type <= PACKET_BROADCAST)) { buf->next = NULL; tipc_recv_msg(buf, eb_ptr->bearer); - return 0; + return NET_RX_SUCCESS; } } kfree_skb(buf); - return 0; + return NET_RX_DROP; } /** - * setup_bearer - setup association between Ethernet bearer and interface + * setup_media - setup association between Ethernet bearer and interface */ -static void setup_bearer(struct work_struct *work) +static void setup_media(struct work_struct *work) { - struct eth_bearer *eb_ptr = - container_of(work, struct eth_bearer, setup); + struct eth_media *eb_ptr = + container_of(work, struct eth_media, setup); dev_add_pack(&eb_ptr->tipc_packet_type); } /** - * enable_bearer - attach TIPC bearer to an Ethernet interface + * enable_media - attach TIPC bearer to an Ethernet interface */ -static int enable_bearer(struct tipc_bearer *tb_ptr) +static int enable_media(struct tipc_bearer *tb_ptr) { struct net_device *dev; - struct eth_bearer *eb_ptr = ð_bearers[0]; - struct eth_bearer *stop = ð_bearers[MAX_ETH_BEARERS]; + struct eth_media *eb_ptr = ð_media_array[0]; + struct eth_media *stop = ð_media_array[MAX_ETH_MEDIA]; char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1; int pending_dev = 0; @@ -188,7 +188,7 @@ static int enable_bearer(struct tipc_bearer *tb_ptr) eb_ptr->tipc_packet_type.func = recv_msg; eb_ptr->tipc_packet_type.af_packet_priv = eb_ptr; INIT_LIST_HEAD(&(eb_ptr->tipc_packet_type.list)); - INIT_WORK(&eb_ptr->setup, setup_bearer); + INIT_WORK(&eb_ptr->setup, setup_media); schedule_work(&eb_ptr->setup); /* Associate TIPC bearer with Ethernet bearer */ @@ -205,14 +205,14 @@ static int enable_bearer(struct tipc_bearer *tb_ptr) } /** - * cleanup_bearer - break association between Ethernet bearer and interface + * cleanup_media - break association between Ethernet bearer and interface * * This routine must be invoked from a work queue because it can sleep. */ -static void cleanup_bearer(struct work_struct *work) +static void cleanup_media(struct work_struct *work) { - struct eth_bearer *eb_ptr = - container_of(work, struct eth_bearer, cleanup); + struct eth_media *eb_ptr = + container_of(work, struct eth_media, cleanup); dev_remove_pack(&eb_ptr->tipc_packet_type); dev_put(eb_ptr->dev); @@ -220,18 +220,18 @@ static void cleanup_bearer(struct work_struct *work) } /** - * disable_bearer - detach TIPC bearer from an Ethernet interface + * disable_media - detach TIPC bearer from an Ethernet interface * * Mark Ethernet bearer as inactive so that incoming buffers are thrown away, * then get worker thread to complete bearer cleanup. (Can't do cleanup * here because cleanup code needs to sleep and caller holds spinlocks.) */ -static void disable_bearer(struct tipc_bearer *tb_ptr) +static void disable_media(struct tipc_bearer *tb_ptr) { - struct eth_bearer *eb_ptr = (struct eth_bearer *)tb_ptr->usr_handle; + struct eth_media *eb_ptr = (struct eth_media *)tb_ptr->usr_handle; eb_ptr->bearer = NULL; - INIT_WORK(&eb_ptr->cleanup, cleanup_bearer); + INIT_WORK(&eb_ptr->cleanup, cleanup_media); schedule_work(&eb_ptr->cleanup); } @@ -245,8 +245,8 @@ static int recv_notification(struct notifier_block *nb, unsigned long evt, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct eth_bearer *eb_ptr = ð_bearers[0]; - struct eth_bearer *stop = ð_bearers[MAX_ETH_BEARERS]; + struct eth_media *eb_ptr = ð_media_array[0]; + struct eth_media *stop = ð_media_array[MAX_ETH_MEDIA]; if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; @@ -265,17 +265,17 @@ static int recv_notification(struct notifier_block *nb, unsigned long evt, if (netif_carrier_ok(dev)) tipc_continue(eb_ptr->bearer); else - tipc_block_bearer(eb_ptr->bearer->name); + tipc_block_bearer(eb_ptr->bearer); break; case NETDEV_UP: tipc_continue(eb_ptr->bearer); break; case NETDEV_DOWN: - tipc_block_bearer(eb_ptr->bearer->name); + tipc_block_bearer(eb_ptr->bearer); break; case NETDEV_CHANGEMTU: case NETDEV_CHANGEADDR: - tipc_block_bearer(eb_ptr->bearer->name); + tipc_block_bearer(eb_ptr->bearer); tipc_continue(eb_ptr->bearer); break; case NETDEV_UNREGISTER: @@ -327,8 +327,8 @@ static int eth_msg2addr(const struct tipc_bearer *tb_ptr, */ static struct tipc_media eth_media_info = { .send_msg = send_msg, - .enable_bearer = enable_bearer, - .disable_bearer = disable_bearer, + .enable_media = enable_media, + .disable_media = disable_media, .addr2str = eth_addr2str, .addr2msg = eth_addr2msg, .msg2addr = eth_msg2addr, diff --git a/net/tipc/ib_media.c b/net/tipc/ib_media.c index 9934a32bfa877a8a8eae83d1fc74e7f31a7a8399..c139892974644925c4eca8e225e0c262b99cf4c2 100644 --- a/net/tipc/ib_media.c +++ b/net/tipc/ib_media.c @@ -42,17 +42,17 @@ #include "core.h" #include "bearer.h" -#define MAX_IB_BEARERS MAX_BEARERS +#define MAX_IB_MEDIA MAX_BEARERS /** - * struct ib_bearer - Infiniband bearer data structure + * struct ib_media - Infiniband media data structure * @bearer: ptr to associated "generic" bearer structure * @dev: ptr to associated Infiniband network device * @tipc_packet_type: used in binding TIPC to Infiniband driver * @cleanup: work item used when disabling bearer */ -struct ib_bearer { +struct ib_media { struct tipc_bearer *bearer; struct net_device *dev; struct packet_type tipc_packet_type; @@ -61,7 +61,7 @@ struct ib_bearer { }; static struct tipc_media ib_media_info; -static struct ib_bearer ib_bearers[MAX_IB_BEARERS]; +static struct ib_media ib_media_array[MAX_IB_MEDIA]; static int ib_started; /** @@ -93,7 +93,7 @@ static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr, if (!clone) return 0; - dev = ((struct ib_bearer *)(tb_ptr->usr_handle))->dev; + dev = ((struct ib_media *)(tb_ptr->usr_handle))->dev; delta = dev->hard_header_len - skb_headroom(buf); if ((delta > 0) && @@ -121,43 +121,43 @@ static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr, static int recv_msg(struct sk_buff *buf, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev) { - struct ib_bearer *ib_ptr = (struct ib_bearer *)pt->af_packet_priv; + struct ib_media *ib_ptr = (struct ib_media *)pt->af_packet_priv; if (!net_eq(dev_net(dev), &init_net)) { kfree_skb(buf); - return 0; + return NET_RX_DROP; } if (likely(ib_ptr->bearer)) { if (likely(buf->pkt_type <= PACKET_BROADCAST)) { buf->next = NULL; tipc_recv_msg(buf, ib_ptr->bearer); - return 0; + return NET_RX_SUCCESS; } } kfree_skb(buf); - return 0; + return NET_RX_DROP; } /** * setup_bearer - setup association between InfiniBand bearer and interface */ -static void setup_bearer(struct work_struct *work) +static void setup_media(struct work_struct *work) { - struct ib_bearer *ib_ptr = - container_of(work, struct ib_bearer, setup); + struct ib_media *ib_ptr = + container_of(work, struct ib_media, setup); dev_add_pack(&ib_ptr->tipc_packet_type); } /** - * enable_bearer - attach TIPC bearer to an InfiniBand interface + * enable_media - attach TIPC bearer to an InfiniBand interface */ -static int enable_bearer(struct tipc_bearer *tb_ptr) +static int enable_media(struct tipc_bearer *tb_ptr) { struct net_device *dev; - struct ib_bearer *ib_ptr = &ib_bearers[0]; - struct ib_bearer *stop = &ib_bearers[MAX_IB_BEARERS]; + struct ib_media *ib_ptr = &ib_media_array[0]; + struct ib_media *stop = &ib_media_array[MAX_IB_MEDIA]; char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1; int pending_dev = 0; @@ -181,7 +181,7 @@ static int enable_bearer(struct tipc_bearer *tb_ptr) ib_ptr->tipc_packet_type.func = recv_msg; ib_ptr->tipc_packet_type.af_packet_priv = ib_ptr; INIT_LIST_HEAD(&(ib_ptr->tipc_packet_type.list)); - INIT_WORK(&ib_ptr->setup, setup_bearer); + INIT_WORK(&ib_ptr->setup, setup_media); schedule_work(&ib_ptr->setup); /* Associate TIPC bearer with InfiniBand bearer */ @@ -204,8 +204,8 @@ static int enable_bearer(struct tipc_bearer *tb_ptr) */ static void cleanup_bearer(struct work_struct *work) { - struct ib_bearer *ib_ptr = - container_of(work, struct ib_bearer, cleanup); + struct ib_media *ib_ptr = + container_of(work, struct ib_media, cleanup); dev_remove_pack(&ib_ptr->tipc_packet_type); dev_put(ib_ptr->dev); @@ -213,15 +213,15 @@ static void cleanup_bearer(struct work_struct *work) } /** - * disable_bearer - detach TIPC bearer from an InfiniBand interface + * disable_media - detach TIPC bearer from an InfiniBand interface * * Mark InfiniBand bearer as inactive so that incoming buffers are thrown away, * then get worker thread to complete bearer cleanup. (Can't do cleanup * here because cleanup code needs to sleep and caller holds spinlocks.) */ -static void disable_bearer(struct tipc_bearer *tb_ptr) +static void disable_media(struct tipc_bearer *tb_ptr) { - struct ib_bearer *ib_ptr = (struct ib_bearer *)tb_ptr->usr_handle; + struct ib_media *ib_ptr = (struct ib_media *)tb_ptr->usr_handle; ib_ptr->bearer = NULL; INIT_WORK(&ib_ptr->cleanup, cleanup_bearer); @@ -238,8 +238,8 @@ static int recv_notification(struct notifier_block *nb, unsigned long evt, void *ptr) { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct ib_bearer *ib_ptr = &ib_bearers[0]; - struct ib_bearer *stop = &ib_bearers[MAX_IB_BEARERS]; + struct ib_media *ib_ptr = &ib_media_array[0]; + struct ib_media *stop = &ib_media_array[MAX_IB_MEDIA]; if (!net_eq(dev_net(dev), &init_net)) return NOTIFY_DONE; @@ -258,17 +258,17 @@ static int recv_notification(struct notifier_block *nb, unsigned long evt, if (netif_carrier_ok(dev)) tipc_continue(ib_ptr->bearer); else - tipc_block_bearer(ib_ptr->bearer->name); + tipc_block_bearer(ib_ptr->bearer); break; case NETDEV_UP: tipc_continue(ib_ptr->bearer); break; case NETDEV_DOWN: - tipc_block_bearer(ib_ptr->bearer->name); + tipc_block_bearer(ib_ptr->bearer); break; case NETDEV_CHANGEMTU: case NETDEV_CHANGEADDR: - tipc_block_bearer(ib_ptr->bearer->name); + tipc_block_bearer(ib_ptr->bearer); tipc_continue(ib_ptr->bearer); break; case NETDEV_UNREGISTER: @@ -323,8 +323,8 @@ static int ib_msg2addr(const struct tipc_bearer *tb_ptr, */ static struct tipc_media ib_media_info = { .send_msg = send_msg, - .enable_bearer = enable_bearer, - .disable_bearer = disable_bearer, + .enable_media = enable_media, + .disable_media = disable_media, .addr2str = ib_addr2str, .addr2msg = ib_addr2msg, .msg2addr = ib_msg2addr, diff --git a/net/tipc/link.c b/net/tipc/link.c index 0cc3d9015c5d5bb6a1629251d7a636f41239db6f..cf465d66ccdec9506bab01cb7390fcd169b50dfc 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -75,20 +75,6 @@ static const char *link_unk_evt = "Unknown link event "; */ #define START_CHANGEOVER 100000u -/** - * struct tipc_link_name - deconstructed link name - * @addr_local: network address of node at this end - * @if_local: name of interface at this end - * @addr_peer: network address of node at far end - * @if_peer: name of interface at far end - */ -struct tipc_link_name { - u32 addr_local; - char if_local[TIPC_MAX_IF_NAME]; - u32 addr_peer; - char if_peer[TIPC_MAX_IF_NAME]; -}; - static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr, struct sk_buff *buf); static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf); @@ -97,8 +83,7 @@ static int link_recv_changeover_msg(struct tipc_link **l_ptr, static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance); static int link_send_sections_long(struct tipc_port *sender, struct iovec const *msg_sect, - u32 num_sect, unsigned int total_len, - u32 destnode); + unsigned int len, u32 destnode); static void link_state_event(struct tipc_link *l_ptr, u32 event); static void link_reset_statistics(struct tipc_link *l_ptr); static void link_print(struct tipc_link *l_ptr, const char *str); @@ -160,72 +145,6 @@ int tipc_link_is_active(struct tipc_link *l_ptr) (l_ptr->owner->active_links[1] == l_ptr); } -/** - * link_name_validate - validate & (optionally) deconstruct tipc_link name - * @name: ptr to link name string - * @name_parts: ptr to area for link name components (or NULL if not needed) - * - * Returns 1 if link name is valid, otherwise 0. - */ -static int link_name_validate(const char *name, - struct tipc_link_name *name_parts) -{ - char name_copy[TIPC_MAX_LINK_NAME]; - char *addr_local; - char *if_local; - char *addr_peer; - char *if_peer; - char dummy; - u32 z_local, c_local, n_local; - u32 z_peer, c_peer, n_peer; - u32 if_local_len; - u32 if_peer_len; - - /* copy link name & ensure length is OK */ - name_copy[TIPC_MAX_LINK_NAME - 1] = 0; - /* need above in case non-Posix strncpy() doesn't pad with nulls */ - strncpy(name_copy, name, TIPC_MAX_LINK_NAME); - if (name_copy[TIPC_MAX_LINK_NAME - 1] != 0) - return 0; - - /* ensure all component parts of link name are present */ - addr_local = name_copy; - if_local = strchr(addr_local, ':'); - if (if_local == NULL) - return 0; - *(if_local++) = 0; - addr_peer = strchr(if_local, '-'); - if (addr_peer == NULL) - return 0; - *(addr_peer++) = 0; - if_local_len = addr_peer - if_local; - if_peer = strchr(addr_peer, ':'); - if (if_peer == NULL) - return 0; - *(if_peer++) = 0; - if_peer_len = strlen(if_peer) + 1; - - /* validate component parts of link name */ - if ((sscanf(addr_local, "%u.%u.%u%c", - &z_local, &c_local, &n_local, &dummy) != 3) || - (sscanf(addr_peer, "%u.%u.%u%c", - &z_peer, &c_peer, &n_peer, &dummy) != 3) || - (z_local > 255) || (c_local > 4095) || (n_local > 4095) || - (z_peer > 255) || (c_peer > 4095) || (n_peer > 4095) || - (if_local_len <= 1) || (if_local_len > TIPC_MAX_IF_NAME) || - (if_peer_len <= 1) || (if_peer_len > TIPC_MAX_IF_NAME)) - return 0; - - /* return link name components, if necessary */ - if (name_parts) { - name_parts->addr_local = tipc_addr(z_local, c_local, n_local); - strcpy(name_parts->if_local, if_local); - name_parts->addr_peer = tipc_addr(z_peer, c_peer, n_peer); - strcpy(name_parts->if_peer, if_peer); - } - return 1; -} - /** * link_timeout - handle expiration of link timer * @l_ptr: pointer to link @@ -485,15 +404,9 @@ static void link_release_outqueue(struct tipc_link *l_ptr) */ void tipc_link_reset_fragments(struct tipc_link *l_ptr) { - struct sk_buff *buf = l_ptr->defragm_buf; - struct sk_buff *next; - - while (buf) { - next = buf->next; - kfree_skb(buf); - buf = next; - } - l_ptr->defragm_buf = NULL; + kfree_skb(l_ptr->reasm_head); + l_ptr->reasm_head = NULL; + l_ptr->reasm_tail = NULL; } /** @@ -1065,8 +978,7 @@ static int link_send_buf_fast(struct tipc_link *l_ptr, struct sk_buff *buf, */ int tipc_link_send_sections_fast(struct tipc_port *sender, struct iovec const *msg_sect, - const u32 num_sect, unsigned int total_len, - u32 destaddr) + unsigned int len, u32 destaddr) { struct tipc_msg *hdr = &sender->phdr; struct tipc_link *l_ptr; @@ -1080,8 +992,7 @@ again: * Try building message using port's max_pkt hint. * (Must not hold any locks while building message.) */ - res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, - sender->max_pkt, &buf); + res = tipc_msg_build(hdr, msg_sect, len, sender->max_pkt, &buf); /* Exit if build request was invalid */ if (unlikely(res < 0)) return res; @@ -1121,8 +1032,7 @@ exit: if ((msg_hdr_sz(hdr) + res) <= sender->max_pkt) goto again; - return link_send_sections_long(sender, msg_sect, - num_sect, total_len, + return link_send_sections_long(sender, msg_sect, len, destaddr); } tipc_node_unlock(node); @@ -1133,8 +1043,8 @@ exit: if (buf) return tipc_reject_msg(buf, TIPC_ERR_NO_NODE); if (res >= 0) - return tipc_port_reject_sections(sender, hdr, msg_sect, num_sect, - total_len, TIPC_ERR_NO_NODE); + return tipc_port_reject_sections(sender, hdr, msg_sect, + len, TIPC_ERR_NO_NODE); return res; } @@ -1154,18 +1064,17 @@ exit: */ static int link_send_sections_long(struct tipc_port *sender, struct iovec const *msg_sect, - u32 num_sect, unsigned int total_len, - u32 destaddr) + unsigned int len, u32 destaddr) { struct tipc_link *l_ptr; struct tipc_node *node; struct tipc_msg *hdr = &sender->phdr; - u32 dsz = total_len; + u32 dsz = len; u32 max_pkt, fragm_sz, rest; struct tipc_msg fragm_hdr; struct sk_buff *buf, *buf_chain, *prev; u32 fragm_crs, fragm_rest, hsz, sect_rest; - const unchar *sect_crs; + const unchar __user *sect_crs; int curr_sect; u32 fragm_no; int res = 0; @@ -1207,7 +1116,7 @@ again: if (!sect_rest) { sect_rest = msg_sect[++curr_sect].iov_len; - sect_crs = (const unchar *)msg_sect[curr_sect].iov_base; + sect_crs = msg_sect[curr_sect].iov_base; } if (sect_rest < fragm_rest) @@ -1283,8 +1192,8 @@ reject: buf = buf_chain->next; kfree_skb(buf_chain); } - return tipc_port_reject_sections(sender, hdr, msg_sect, num_sect, - total_len, TIPC_ERR_NO_NODE); + return tipc_port_reject_sections(sender, hdr, msg_sect, + len, TIPC_ERR_NO_NODE); } /* Append chain of fragments to send queue & send them */ @@ -1592,15 +1501,15 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr) /* Ensure bearer is still enabled */ if (unlikely(!b_ptr->active)) - goto cont; + goto discard; /* Ensure message is well-formed */ if (unlikely(!link_recv_buf_validate(buf))) - goto cont; + goto discard; /* Ensure message data is a single contiguous unit */ if (unlikely(skb_linearize(buf))) - goto cont; + goto discard; /* Handle arrival of a non-unicast link message */ msg = buf_msg(buf); @@ -1616,20 +1525,18 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr) /* Discard unicast link messages destined for another node */ if (unlikely(!msg_short(msg) && (msg_destnode(msg) != tipc_own_addr))) - goto cont; + goto discard; /* Locate neighboring node that sent message */ n_ptr = tipc_node_find(msg_prevnode(msg)); if (unlikely(!n_ptr)) - goto cont; + goto discard; tipc_node_lock(n_ptr); /* Locate unicast link endpoint that should handle message */ l_ptr = n_ptr->links[b_ptr->identity]; - if (unlikely(!l_ptr)) { - tipc_node_unlock(n_ptr); - goto cont; - } + if (unlikely(!l_ptr)) + goto unlock_discard; /* Verify that communication with node is currently allowed */ if ((n_ptr->block_setup & WAIT_PEER_DOWN) && @@ -1639,10 +1546,8 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr) !msg_redundant_link(msg)) n_ptr->block_setup &= ~WAIT_PEER_DOWN; - if (n_ptr->block_setup) { - tipc_node_unlock(n_ptr); - goto cont; - } + if (n_ptr->block_setup) + goto unlock_discard; /* Validate message sequence number info */ seq_no = msg_seqno(msg); @@ -1678,98 +1583,100 @@ void tipc_recv_msg(struct sk_buff *head, struct tipc_bearer *b_ptr) /* Now (finally!) process the incoming message */ protocol_check: - if (likely(link_working_working(l_ptr))) { - if (likely(seq_no == mod(l_ptr->next_in_no))) { - l_ptr->next_in_no++; - if (unlikely(l_ptr->oldest_deferred_in)) - head = link_insert_deferred_queue(l_ptr, - head); -deliver: - if (likely(msg_isdata(msg))) { - tipc_node_unlock(n_ptr); - tipc_port_recv_msg(buf); - continue; - } - switch (msg_user(msg)) { - int ret; - case MSG_BUNDLER: - l_ptr->stats.recv_bundles++; - l_ptr->stats.recv_bundled += - msg_msgcnt(msg); - tipc_node_unlock(n_ptr); - tipc_link_recv_bundle(buf); - continue; - case NAME_DISTRIBUTOR: - n_ptr->bclink.recv_permitted = true; - tipc_node_unlock(n_ptr); - tipc_named_recv(buf); - continue; - case BCAST_PROTOCOL: - tipc_link_recv_sync(n_ptr, buf); - tipc_node_unlock(n_ptr); - continue; - case CONN_MANAGER: - tipc_node_unlock(n_ptr); - tipc_port_recv_proto_msg(buf); - continue; - case MSG_FRAGMENTER: - l_ptr->stats.recv_fragments++; - ret = tipc_link_recv_fragment( - &l_ptr->defragm_buf, - &buf, &msg); - if (ret == 1) { - l_ptr->stats.recv_fragmented++; - goto deliver; - } - if (ret == -1) - l_ptr->next_in_no--; - break; - case CHANGEOVER_PROTOCOL: - type = msg_type(msg); - if (link_recv_changeover_msg(&l_ptr, - &buf)) { - msg = buf_msg(buf); - seq_no = msg_seqno(msg); - if (type == ORIGINAL_MSG) - goto deliver; - goto protocol_check; - } - break; - default: - kfree_skb(buf); - buf = NULL; - break; - } + if (unlikely(!link_working_working(l_ptr))) { + if (msg_user(msg) == LINK_PROTOCOL) { + link_recv_proto_msg(l_ptr, buf); + head = link_insert_deferred_queue(l_ptr, head); + tipc_node_unlock(n_ptr); + continue; + } + + /* Traffic message. Conditionally activate link */ + link_state_event(l_ptr, TRAFFIC_MSG_EVT); + + if (link_working_working(l_ptr)) { + /* Re-insert buffer in front of queue */ + buf->next = head; + head = buf; tipc_node_unlock(n_ptr); - tipc_net_route_msg(buf); continue; } + goto unlock_discard; + } + + /* Link is now in state WORKING_WORKING */ + if (unlikely(seq_no != mod(l_ptr->next_in_no))) { link_handle_out_of_seq_msg(l_ptr, buf); head = link_insert_deferred_queue(l_ptr, head); tipc_node_unlock(n_ptr); continue; } - - /* Link is not in state WORKING_WORKING */ - if (msg_user(msg) == LINK_PROTOCOL) { - link_recv_proto_msg(l_ptr, buf); + l_ptr->next_in_no++; + if (unlikely(l_ptr->oldest_deferred_in)) head = link_insert_deferred_queue(l_ptr, head); +deliver: + if (likely(msg_isdata(msg))) { tipc_node_unlock(n_ptr); + tipc_port_recv_msg(buf); continue; } - - /* Traffic message. Conditionally activate link */ - link_state_event(l_ptr, TRAFFIC_MSG_EVT); - - if (link_working_working(l_ptr)) { - /* Re-insert buffer in front of queue */ - buf->next = head; - head = buf; + switch (msg_user(msg)) { + int ret; + case MSG_BUNDLER: + l_ptr->stats.recv_bundles++; + l_ptr->stats.recv_bundled += msg_msgcnt(msg); + tipc_node_unlock(n_ptr); + tipc_link_recv_bundle(buf); + continue; + case NAME_DISTRIBUTOR: + n_ptr->bclink.recv_permitted = true; + tipc_node_unlock(n_ptr); + tipc_named_recv(buf); + continue; + case BCAST_PROTOCOL: + tipc_link_recv_sync(n_ptr, buf); + tipc_node_unlock(n_ptr); + continue; + case CONN_MANAGER: + tipc_node_unlock(n_ptr); + tipc_port_recv_proto_msg(buf); + continue; + case MSG_FRAGMENTER: + l_ptr->stats.recv_fragments++; + ret = tipc_link_recv_fragment(&l_ptr->reasm_head, + &l_ptr->reasm_tail, + &buf); + if (ret == LINK_REASM_COMPLETE) { + l_ptr->stats.recv_fragmented++; + msg = buf_msg(buf); + goto deliver; + } + if (ret == LINK_REASM_ERROR) + tipc_link_reset(l_ptr); tipc_node_unlock(n_ptr); continue; + case CHANGEOVER_PROTOCOL: + type = msg_type(msg); + if (link_recv_changeover_msg(&l_ptr, &buf)) { + msg = buf_msg(buf); + seq_no = msg_seqno(msg); + if (type == ORIGINAL_MSG) + goto deliver; + goto protocol_check; + } + break; + default: + kfree_skb(buf); + buf = NULL; + break; } tipc_node_unlock(n_ptr); -cont: + tipc_net_route_msg(buf); + continue; +unlock_discard: + + tipc_node_unlock(n_ptr); +discard: kfree_skb(buf); } read_unlock_bh(&tipc_net_lock); @@ -2431,115 +2338,48 @@ static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf) return dsz; } -/* - * A pending message being re-assembled must store certain values - * to handle subsequent fragments correctly. The following functions - * help storing these values in unused, available fields in the - * pending message. This makes dynamic memory allocation unnecessary. - */ -static void set_long_msg_seqno(struct sk_buff *buf, u32 seqno) -{ - msg_set_seqno(buf_msg(buf), seqno); -} - -static u32 get_fragm_size(struct sk_buff *buf) -{ - return msg_ack(buf_msg(buf)); -} - -static void set_fragm_size(struct sk_buff *buf, u32 sz) -{ - msg_set_ack(buf_msg(buf), sz); -} - -static u32 get_expected_frags(struct sk_buff *buf) -{ - return msg_bcast_ack(buf_msg(buf)); -} - -static void set_expected_frags(struct sk_buff *buf, u32 exp) -{ - msg_set_bcast_ack(buf_msg(buf), exp); -} - /* * tipc_link_recv_fragment(): Called with node lock on. Returns * the reassembled buffer if message is complete. */ -int tipc_link_recv_fragment(struct sk_buff **pending, struct sk_buff **fb, - struct tipc_msg **m) -{ - struct sk_buff *prev = NULL; - struct sk_buff *fbuf = *fb; - struct tipc_msg *fragm = buf_msg(fbuf); - struct sk_buff *pbuf = *pending; - u32 long_msg_seq_no = msg_long_msgno(fragm); - - *fb = NULL; - - /* Is there an incomplete message waiting for this fragment? */ - while (pbuf && ((buf_seqno(pbuf) != long_msg_seq_no) || - (msg_orignode(fragm) != msg_orignode(buf_msg(pbuf))))) { - prev = pbuf; - pbuf = pbuf->next; - } - - if (!pbuf && (msg_type(fragm) == FIRST_FRAGMENT)) { - struct tipc_msg *imsg = (struct tipc_msg *)msg_data(fragm); - u32 msg_sz = msg_size(imsg); - u32 fragm_sz = msg_data_sz(fragm); - u32 exp_fragm_cnt; - u32 max = TIPC_MAX_USER_MSG_SIZE + NAMED_H_SIZE; - - if (msg_type(imsg) == TIPC_MCAST_MSG) - max = TIPC_MAX_USER_MSG_SIZE + MCAST_H_SIZE; - if (fragm_sz == 0 || msg_size(imsg) > max) { - kfree_skb(fbuf); - return 0; - } - exp_fragm_cnt = msg_sz / fragm_sz + !!(msg_sz % fragm_sz); - pbuf = tipc_buf_acquire(msg_size(imsg)); - if (pbuf != NULL) { - pbuf->next = *pending; - *pending = pbuf; - skb_copy_to_linear_data(pbuf, imsg, - msg_data_sz(fragm)); - /* Prepare buffer for subsequent fragments. */ - set_long_msg_seqno(pbuf, long_msg_seq_no); - set_fragm_size(pbuf, fragm_sz); - set_expected_frags(pbuf, exp_fragm_cnt - 1); - } else { - pr_debug("Link unable to reassemble fragmented message\n"); - kfree_skb(fbuf); - return -1; - } - kfree_skb(fbuf); - return 0; - } else if (pbuf && (msg_type(fragm) != FIRST_FRAGMENT)) { - u32 dsz = msg_data_sz(fragm); - u32 fsz = get_fragm_size(pbuf); - u32 crs = ((msg_fragm_no(fragm) - 1) * fsz); - u32 exp_frags = get_expected_frags(pbuf) - 1; - skb_copy_to_linear_data_offset(pbuf, crs, - msg_data(fragm), dsz); - kfree_skb(fbuf); - - /* Is message complete? */ - if (exp_frags == 0) { - if (prev) - prev->next = pbuf->next; - else - *pending = pbuf->next; - msg_reset_reroute_cnt(buf_msg(pbuf)); - *fb = pbuf; - *m = buf_msg(pbuf); - return 1; - } - set_expected_frags(pbuf, exp_frags); +int tipc_link_recv_fragment(struct sk_buff **head, struct sk_buff **tail, + struct sk_buff **fbuf) +{ + struct sk_buff *frag = *fbuf; + struct tipc_msg *msg = buf_msg(frag); + u32 fragid = msg_type(msg); + bool headstolen; + int delta; + + skb_pull(frag, msg_hdr_sz(msg)); + if (fragid == FIRST_FRAGMENT) { + if (*head || skb_unclone(frag, GFP_ATOMIC)) + goto out_free; + *head = frag; + skb_frag_list_init(*head); return 0; + } else if (skb_try_coalesce(*head, frag, &headstolen, &delta)) { + kfree_skb_partial(frag, headstolen); + } else { + if (!*head) + goto out_free; + if (!skb_has_frag_list(*head)) + skb_shinfo(*head)->frag_list = frag; + else + (*tail)->next = frag; + *tail = frag; + (*head)->truesize += frag->truesize; + } + if (fragid == LAST_FRAGMENT) { + *fbuf = *head; + *tail = *head = NULL; + return LINK_REASM_COMPLETE; } - kfree_skb(fbuf); return 0; +out_free: + pr_warn_ratelimited("Link unable to reassemble fragmented message\n"); + kfree_skb(*fbuf); + return LINK_REASM_ERROR; } static void link_set_supervision_props(struct tipc_link *l_ptr, u32 tolerance) @@ -2585,25 +2425,21 @@ void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window) static struct tipc_link *link_find_link(const char *name, struct tipc_node **node) { - struct tipc_link_name link_name_parts; - struct tipc_bearer *b_ptr; struct tipc_link *l_ptr; + struct tipc_node *n_ptr; + int i; - if (!link_name_validate(name, &link_name_parts)) - return NULL; - - b_ptr = tipc_bearer_find_interface(link_name_parts.if_local); - if (!b_ptr) - return NULL; - - *node = tipc_node_find(link_name_parts.addr_peer); - if (!*node) - return NULL; - - l_ptr = (*node)->links[b_ptr->identity]; - if (!l_ptr || strcmp(l_ptr->name, name)) - return NULL; - + list_for_each_entry(n_ptr, &tipc_node_list, list) { + for (i = 0; i < MAX_BEARERS; i++) { + l_ptr = n_ptr->links[i]; + if (l_ptr && !strcmp(l_ptr->name, name)) + goto found; + } + } + l_ptr = NULL; + n_ptr = NULL; +found: + *node = n_ptr; return l_ptr; } @@ -2646,6 +2482,7 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd) struct tipc_link *l_ptr; struct tipc_bearer *b_ptr; struct tipc_media *m_ptr; + int res = 0; l_ptr = link_find_link(name, &node); if (l_ptr) { @@ -2668,9 +2505,12 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd) case TIPC_CMD_SET_LINK_WINDOW: tipc_link_set_queue_limits(l_ptr, new_value); break; + default: + res = -EINVAL; + break; } tipc_node_unlock(node); - return 0; + return res; } b_ptr = tipc_bearer_find(name); @@ -2678,15 +2518,18 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd) switch (cmd) { case TIPC_CMD_SET_LINK_TOL: b_ptr->tolerance = new_value; - return 0; + break; case TIPC_CMD_SET_LINK_PRI: b_ptr->priority = new_value; - return 0; + break; case TIPC_CMD_SET_LINK_WINDOW: b_ptr->window = new_value; - return 0; + break; + default: + res = -EINVAL; + break; } - return -EINVAL; + return res; } m_ptr = tipc_media_find(name); @@ -2695,15 +2538,18 @@ static int link_cmd_set_value(const char *name, u32 new_value, u16 cmd) switch (cmd) { case TIPC_CMD_SET_LINK_TOL: m_ptr->tolerance = new_value; - return 0; + break; case TIPC_CMD_SET_LINK_PRI: m_ptr->priority = new_value; - return 0; + break; case TIPC_CMD_SET_LINK_WINDOW: m_ptr->window = new_value; - return 0; + break; + default: + res = -EINVAL; + break; } - return -EINVAL; + return res; } struct sk_buff *tipc_link_cmd_config(const void *req_tlv_area, int req_tlv_space, diff --git a/net/tipc/link.h b/net/tipc/link.h index c048ed1cbd765aa3417c945d2913d0c994cb7d3f..8a6c1026644db438a1a83b7c2ce5679ac9e5a888 100644 --- a/net/tipc/link.h +++ b/net/tipc/link.h @@ -40,6 +40,12 @@ #include "msg.h" #include "node.h" +/* + * Link reassembly status codes + */ +#define LINK_REASM_ERROR -1 +#define LINK_REASM_COMPLETE 1 + /* * Out-of-range value for link sequence numbers */ @@ -134,7 +140,8 @@ struct tipc_stats { * @next_out: ptr to first unsent outbound message in queue * @waiting_ports: linked list of ports waiting for link congestion to abate * @long_msg_seq_no: next identifier to use for outbound fragmented messages - * @defragm_buf: list of partially reassembled inbound message fragments + * @reasm_head: list head of partially reassembled inbound message fragments + * @reasm_tail: last fragment received * @stats: collects statistics regarding link activity */ struct tipc_link { @@ -196,9 +203,10 @@ struct tipc_link { struct sk_buff *next_out; struct list_head waiting_ports; - /* Fragmentation/defragmentation */ + /* Fragmentation/reassembly */ u32 long_msg_seq_no; - struct sk_buff *defragm_buf; + struct sk_buff *reasm_head; + struct sk_buff *reasm_tail; /* Statistics */ struct tipc_stats stats; @@ -227,13 +235,11 @@ int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf); u32 tipc_link_get_max_pkt(u32 dest, u32 selector); int tipc_link_send_sections_fast(struct tipc_port *sender, struct iovec const *msg_sect, - const u32 num_sect, - unsigned int total_len, - u32 destnode); + unsigned int len, u32 destnode); void tipc_link_recv_bundle(struct sk_buff *buf); -int tipc_link_recv_fragment(struct sk_buff **pending, - struct sk_buff **fb, - struct tipc_msg **msg); +int tipc_link_recv_fragment(struct sk_buff **reasm_head, + struct sk_buff **reasm_tail, + struct sk_buff **fbuf); void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ, int prob, u32 gap, u32 tolerance, u32 priority, u32 acked_mtu); diff --git a/net/tipc/msg.c b/net/tipc/msg.c index ced60e2fc4f7fbee929874594ee524c41835cd3e..e525f8ce1dee09ce0d9baf214bd3dd8e13daa9d8 100644 --- a/net/tipc/msg.c +++ b/net/tipc/msg.c @@ -73,13 +73,13 @@ void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, * Returns message data size or errno */ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect, - u32 num_sect, unsigned int total_len, int max_size, - struct sk_buff **buf) + unsigned int len, int max_size, struct sk_buff **buf) { - int dsz, sz, hsz, pos, res, cnt; + int dsz, sz, hsz; + unsigned char *to; - dsz = total_len; - pos = hsz = msg_hdr_sz(hdr); + dsz = len; + hsz = msg_hdr_sz(hdr); sz = hsz + dsz; msg_set_size(hdr, sz); if (unlikely(sz > max_size)) { @@ -91,16 +91,11 @@ int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect, if (!(*buf)) return -ENOMEM; skb_copy_to_linear_data(*buf, hdr, hsz); - for (res = 1, cnt = 0; res && (cnt < num_sect); cnt++) { - skb_copy_to_linear_data_offset(*buf, pos, - msg_sect[cnt].iov_base, - msg_sect[cnt].iov_len); - pos += msg_sect[cnt].iov_len; + to = (*buf)->data + hsz; + if (len && memcpy_fromiovecend(to, msg_sect, 0, dsz)) { + kfree_skb(*buf); + *buf = NULL; + return -EFAULT; } - if (likely(res)) - return dsz; - - kfree_skb(*buf); - *buf = NULL; - return -EFAULT; + return dsz; } diff --git a/net/tipc/msg.h b/net/tipc/msg.h index 5e4ccf5c27df0361aaf26df8023ba79f20057936..76d1269b944361076855876219d6e59b16c3b23e 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h @@ -554,12 +554,6 @@ static inline void msg_set_last_bcast(struct tipc_msg *m, u32 n) msg_set_bits(m, 4, 16, 0xffff, n); } - -static inline u32 msg_fragm_no(struct tipc_msg *m) -{ - return msg_bits(m, 4, 16, 0xffff); -} - static inline void msg_set_fragm_no(struct tipc_msg *m, u32 n) { msg_set_bits(m, 4, 16, 0xffff, n); @@ -576,12 +570,6 @@ static inline void msg_set_next_sent(struct tipc_msg *m, u32 n) msg_set_bits(m, 4, 0, 0xffff, n); } - -static inline u32 msg_long_msgno(struct tipc_msg *m) -{ - return msg_bits(m, 4, 0, 0xffff); -} - static inline void msg_set_long_msgno(struct tipc_msg *m, u32 n) { msg_set_bits(m, 4, 0, 0xffff, n); @@ -722,6 +710,5 @@ u32 tipc_msg_tot_importance(struct tipc_msg *m); void tipc_msg_init(struct tipc_msg *m, u32 user, u32 type, u32 hsize, u32 destnode); int tipc_msg_build(struct tipc_msg *hdr, struct iovec const *msg_sect, - u32 num_sect, unsigned int total_len, int max_size, - struct sk_buff **buf); + unsigned int len, int max_size, struct sk_buff **buf); #endif diff --git a/net/tipc/node.c b/net/tipc/node.c index 6e6c434872e82534e0d48f6dceb0c212f1541896..25100c0a6fe84757ee8c0312a86e643d05d6276d 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -298,9 +298,10 @@ static void node_lost_contact(struct tipc_node *n_ptr) } n_ptr->bclink.deferred_size = 0; - if (n_ptr->bclink.defragm) { - kfree_skb(n_ptr->bclink.defragm); - n_ptr->bclink.defragm = NULL; + if (n_ptr->bclink.reasm_head) { + kfree_skb(n_ptr->bclink.reasm_head); + n_ptr->bclink.reasm_head = NULL; + n_ptr->bclink.reasm_tail = NULL; } tipc_bclink_remove_node(n_ptr->addr); diff --git a/net/tipc/node.h b/net/tipc/node.h index 3c189b35b102a378914b59236f555438f7c198d4..e5e96c04e1676a91af811c8c270ce42296b27636 100644 --- a/net/tipc/node.h +++ b/net/tipc/node.h @@ -74,7 +74,8 @@ * @deferred_size: number of OOS b'cast messages in deferred queue * @deferred_head: oldest OOS b'cast message received from node * @deferred_tail: newest OOS b'cast message received from node - * @defragm: list of partially reassembled b'cast message fragments from node + * @reasm_head: broadcast reassembly queue head from node + * @reasm_tail: last broadcast fragment received from node * @recv_permitted: true if node is allowed to receive b'cast messages */ struct tipc_node { @@ -98,7 +99,8 @@ struct tipc_node { u32 deferred_size; struct sk_buff *deferred_head; struct sk_buff *deferred_tail; - struct sk_buff *defragm; + struct sk_buff *reasm_head; + struct sk_buff *reasm_tail; bool recv_permitted; } bclink; }; diff --git a/net/tipc/port.c b/net/tipc/port.c index b3ed2fcab4fbd3a947b6419856641c9a7b315872..c081a7632302ca0798c193b039214466c3180184 100644 --- a/net/tipc/port.c +++ b/net/tipc/port.c @@ -90,8 +90,7 @@ int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg) * tipc_multicast - send a multicast message to local and remote destinations */ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq, - u32 num_sect, struct iovec const *msg_sect, - unsigned int total_len) + struct iovec const *msg_sect, unsigned int len) { struct tipc_msg *hdr; struct sk_buff *buf; @@ -114,8 +113,7 @@ int tipc_multicast(u32 ref, struct tipc_name_seq const *seq, msg_set_namelower(hdr, seq->lower); msg_set_nameupper(hdr, seq->upper); msg_set_hdr_sz(hdr, MCAST_H_SIZE); - res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE, - &buf); + res = tipc_msg_build(hdr, msg_sect, len, MAX_MSG_SIZE, &buf); if (unlikely(!buf)) return res; @@ -436,14 +434,13 @@ exit: } int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr, - struct iovec const *msg_sect, u32 num_sect, - unsigned int total_len, int err) + struct iovec const *msg_sect, unsigned int len, + int err) { struct sk_buff *buf; int res; - res = tipc_msg_build(hdr, msg_sect, num_sect, total_len, MAX_MSG_SIZE, - &buf); + res = tipc_msg_build(hdr, msg_sect, len, MAX_MSG_SIZE, &buf); if (!buf) return res; @@ -918,15 +915,14 @@ int tipc_port_recv_msg(struct sk_buff *buf) * tipc_port_recv_sections(): Concatenate and deliver sectioned * message for this node. */ -static int tipc_port_recv_sections(struct tipc_port *sender, unsigned int num_sect, +static int tipc_port_recv_sections(struct tipc_port *sender, struct iovec const *msg_sect, - unsigned int total_len) + unsigned int len) { struct sk_buff *buf; int res; - res = tipc_msg_build(&sender->phdr, msg_sect, num_sect, total_len, - MAX_MSG_SIZE, &buf); + res = tipc_msg_build(&sender->phdr, msg_sect, len, MAX_MSG_SIZE, &buf); if (likely(buf)) tipc_port_recv_msg(buf); return res; @@ -935,8 +931,7 @@ static int tipc_port_recv_sections(struct tipc_port *sender, unsigned int num_se /** * tipc_send - send message sections on connection */ -int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect, - unsigned int total_len) +int tipc_send(u32 ref, struct iovec const *msg_sect, unsigned int len) { struct tipc_port *p_ptr; u32 destnode; @@ -950,11 +945,10 @@ int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect, if (!tipc_port_congested(p_ptr)) { destnode = port_peernode(p_ptr); if (likely(!in_own_node(destnode))) - res = tipc_link_send_sections_fast(p_ptr, msg_sect, num_sect, - total_len, destnode); + res = tipc_link_send_sections_fast(p_ptr, msg_sect, + len, destnode); else - res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect, - total_len); + res = tipc_port_recv_sections(p_ptr, msg_sect, len); if (likely(res != -ELINKCONG)) { p_ptr->congested = 0; @@ -965,7 +959,7 @@ int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect, } if (port_unreliable(p_ptr)) { p_ptr->congested = 0; - return total_len; + return len; } return -ELINKCONG; } @@ -974,8 +968,7 @@ int tipc_send(u32 ref, unsigned int num_sect, struct iovec const *msg_sect, * tipc_send2name - send message sections to port name */ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain, - unsigned int num_sect, struct iovec const *msg_sect, - unsigned int total_len) + struct iovec const *msg_sect, unsigned int len) { struct tipc_port *p_ptr; struct tipc_msg *msg; @@ -999,36 +992,32 @@ int tipc_send2name(u32 ref, struct tipc_name const *name, unsigned int domain, if (likely(destport || destnode)) { if (likely(in_own_node(destnode))) - res = tipc_port_recv_sections(p_ptr, num_sect, - msg_sect, total_len); + res = tipc_port_recv_sections(p_ptr, msg_sect, len); else if (tipc_own_addr) res = tipc_link_send_sections_fast(p_ptr, msg_sect, - num_sect, total_len, - destnode); + len, destnode); else res = tipc_port_reject_sections(p_ptr, msg, msg_sect, - num_sect, total_len, - TIPC_ERR_NO_NODE); + len, TIPC_ERR_NO_NODE); if (likely(res != -ELINKCONG)) { if (res > 0) p_ptr->sent++; return res; } if (port_unreliable(p_ptr)) { - return total_len; + return len; } return -ELINKCONG; } - return tipc_port_reject_sections(p_ptr, msg, msg_sect, num_sect, - total_len, TIPC_ERR_NO_NAME); + return tipc_port_reject_sections(p_ptr, msg, msg_sect, len, + TIPC_ERR_NO_NAME); } /** * tipc_send2port - send message sections to port identity */ int tipc_send2port(u32 ref, struct tipc_portid const *dest, - unsigned int num_sect, struct iovec const *msg_sect, - unsigned int total_len) + struct iovec const *msg_sect, unsigned int len) { struct tipc_port *p_ptr; struct tipc_msg *msg; @@ -1046,21 +1035,20 @@ int tipc_send2port(u32 ref, struct tipc_portid const *dest, msg_set_hdr_sz(msg, BASIC_H_SIZE); if (in_own_node(dest->node)) - res = tipc_port_recv_sections(p_ptr, num_sect, msg_sect, - total_len); + res = tipc_port_recv_sections(p_ptr, msg_sect, len); else if (tipc_own_addr) - res = tipc_link_send_sections_fast(p_ptr, msg_sect, num_sect, - total_len, dest->node); + res = tipc_link_send_sections_fast(p_ptr, msg_sect, len, + dest->node); else - res = tipc_port_reject_sections(p_ptr, msg, msg_sect, num_sect, - total_len, TIPC_ERR_NO_NODE); + res = tipc_port_reject_sections(p_ptr, msg, msg_sect, len, + TIPC_ERR_NO_NODE); if (likely(res != -ELINKCONG)) { if (res > 0) p_ptr->sent++; return res; } if (port_unreliable(p_ptr)) { - return total_len; + return len; } return -ELINKCONG; } diff --git a/net/tipc/port.h b/net/tipc/port.h index 5a7026b9c3456b716dd8a173b345d11355f1b3be..9122535973430edff99e8ee6070a091975dea457 100644 --- a/net/tipc/port.h +++ b/net/tipc/port.h @@ -151,24 +151,20 @@ int tipc_port_peer_msg(struct tipc_port *p_ptr, struct tipc_msg *msg); * TIPC messaging routines */ int tipc_port_recv_msg(struct sk_buff *buf); -int tipc_send(u32 portref, unsigned int num_sect, struct iovec const *msg_sect, - unsigned int total_len); +int tipc_send(u32 portref, struct iovec const *msg_sect, unsigned int len); int tipc_send2name(u32 portref, struct tipc_name const *name, u32 domain, - unsigned int num_sect, struct iovec const *msg_sect, - unsigned int total_len); + struct iovec const *msg_sect, unsigned int len); int tipc_send2port(u32 portref, struct tipc_portid const *dest, - unsigned int num_sect, struct iovec const *msg_sect, - unsigned int total_len); + struct iovec const *msg_sect, unsigned int len); int tipc_multicast(u32 portref, struct tipc_name_seq const *seq, - unsigned int section_count, struct iovec const *msg, - unsigned int total_len); + struct iovec const *msg, unsigned int len); int tipc_port_reject_sections(struct tipc_port *p_ptr, struct tipc_msg *hdr, - struct iovec const *msg_sect, u32 num_sect, - unsigned int total_len, int err); + struct iovec const *msg_sect, unsigned int len, + int err); struct sk_buff *tipc_port_get_ports(void); void tipc_port_recv_proto_msg(struct sk_buff *buf); void tipc_port_recv_mcast(struct sk_buff *buf, struct tipc_port_list *dp); diff --git a/net/tipc/socket.c b/net/tipc/socket.c index 6cc7ddd2fb7c9cd26eb3e7508adb9f4bc9093866..3906527259d19f2d18d06d3641eda0b755d34968 100644 --- a/net/tipc/socket.c +++ b/net/tipc/socket.c @@ -338,7 +338,7 @@ static int release(struct socket *sock) buf = __skb_dequeue(&sk->sk_receive_queue); if (buf == NULL) break; - if (TIPC_SKB_CB(buf)->handle != 0) + if (TIPC_SKB_CB(buf)->handle != NULL) kfree_skb(buf); else { if ((sock->state == SS_CONNECTING) || @@ -622,13 +622,11 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, res = tipc_send2name(tport->ref, &dest->addr.name.name, dest->addr.name.domain, - m->msg_iovlen, m->msg_iov, total_len); } else if (dest->addrtype == TIPC_ADDR_ID) { res = tipc_send2port(tport->ref, &dest->addr.id, - m->msg_iovlen, m->msg_iov, total_len); } else if (dest->addrtype == TIPC_ADDR_MCAST) { @@ -641,7 +639,6 @@ static int send_msg(struct kiocb *iocb, struct socket *sock, break; res = tipc_multicast(tport->ref, &dest->addr.nameseq, - m->msg_iovlen, m->msg_iov, total_len); } @@ -707,8 +704,7 @@ static int send_packet(struct kiocb *iocb, struct socket *sock, break; } - res = tipc_send(tport->ref, m->msg_iovlen, m->msg_iov, - total_len); + res = tipc_send(tport->ref, m->msg_iov, total_len); if (likely(res != -ELINKCONG)) break; if (timeout_val <= 0L) { @@ -1368,7 +1364,7 @@ static u32 filter_rcv(struct sock *sk, struct sk_buff *buf) return TIPC_ERR_OVERLOAD; /* Enqueue message */ - TIPC_SKB_CB(buf)->handle = 0; + TIPC_SKB_CB(buf)->handle = NULL; __skb_queue_tail(&sk->sk_receive_queue, buf); skb_set_owner_r(buf, sk); @@ -1691,7 +1687,7 @@ restart: /* Disconnect and send a 'FIN+' or 'FIN-' message to peer */ buf = __skb_dequeue(&sk->sk_receive_queue); if (buf) { - if (TIPC_SKB_CB(buf)->handle != 0) { + if (TIPC_SKB_CB(buf)->handle != NULL) { kfree_skb(buf); goto restart; } diff --git a/net/wimax/wimax-internal.h b/net/wimax/wimax-internal.h index 1e743d2148565a1010114b468366b5285a56f02e..5dcd9c067bf0ac72a3c6e1067818053e31f7b748 100644 --- a/net/wimax/wimax-internal.h +++ b/net/wimax/wimax-internal.h @@ -63,11 +63,11 @@ void __wimax_state_set(struct wimax_dev *wimax_dev, enum wimax_st state) { wimax_dev->state = state; } -extern void __wimax_state_change(struct wimax_dev *, enum wimax_st); +void __wimax_state_change(struct wimax_dev *, enum wimax_st); #ifdef CONFIG_DEBUG_FS -extern int wimax_debugfs_add(struct wimax_dev *); -extern void wimax_debugfs_rm(struct wimax_dev *); +int wimax_debugfs_add(struct wimax_dev *); +void wimax_debugfs_rm(struct wimax_dev *); #else static inline int wimax_debugfs_add(struct wimax_dev *wimax_dev) { @@ -76,13 +76,13 @@ static inline int wimax_debugfs_add(struct wimax_dev *wimax_dev) static inline void wimax_debugfs_rm(struct wimax_dev *wimax_dev) {} #endif -extern void wimax_id_table_add(struct wimax_dev *); -extern struct wimax_dev *wimax_dev_get_by_genl_info(struct genl_info *, int); -extern void wimax_id_table_rm(struct wimax_dev *); -extern void wimax_id_table_release(void); +void wimax_id_table_add(struct wimax_dev *); +struct wimax_dev *wimax_dev_get_by_genl_info(struct genl_info *, int); +void wimax_id_table_rm(struct wimax_dev *); +void wimax_id_table_release(void); -extern int wimax_rfkill_add(struct wimax_dev *); -extern void wimax_rfkill_rm(struct wimax_dev *); +int wimax_rfkill_add(struct wimax_dev *); +void wimax_rfkill_rm(struct wimax_dev *); extern struct genl_family wimax_gnl_family; extern struct genl_multicast_group wimax_gnl_mcg; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 50f6195c8b70b816ab35ade4075c5f71de105b35..9b8cc877eb191a2a62370c9f2617bed513200d24 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -328,6 +328,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy, return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2, width); } +EXPORT_SYMBOL(cfg80211_chandef_dfs_required); static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, u32 center_freq, u32 bandwidth, @@ -503,7 +504,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, case NL80211_IFTYPE_ADHOC: if (wdev->current_bss) { *chan = wdev->current_bss->pub.channel; - *chanmode = wdev->ibss_fixed + *chanmode = (wdev->ibss_fixed && + !wdev->ibss_dfs_possible) ? CHAN_MODE_SHARED : CHAN_MODE_EXCLUSIVE; return; diff --git a/net/wireless/core.h b/net/wireless/core.h index 3159e9c284c5b10c3b7cd0cf4d384d278b0a6245..af10e59af2d86f418bd7cc4e69914c9bb31831c7 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -234,10 +234,10 @@ struct cfg80211_beacon_registration { }; /* free object */ -extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); +void cfg80211_dev_free(struct cfg80211_registered_device *rdev); -extern int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, - char *newname); +int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, + char *newname); void ieee80211_set_bitrate_flags(struct wiphy *wiphy); @@ -382,15 +382,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, enum cfg80211_chan_mode chanmode, u8 radar_detect); -/** - * cfg80211_chandef_dfs_required - checks if radar detection is required - * @wiphy: the wiphy to validate against - * @chandef: the channel definition to check - * Return: 1 if radar detection is required, 0 if it is not, < 0 on error - */ -int cfg80211_chandef_dfs_required(struct wiphy *wiphy, - const struct cfg80211_chan_def *c); - void cfg80211_set_dfs_state(struct wiphy *wiphy, const struct cfg80211_chan_def *chandef, enum nl80211_dfs_state dfs_state); diff --git a/net/wireless/debugfs.c b/net/wireless/debugfs.c index 90d0500366248900f2aebcac8d57f295d229d764..454157717efaf8654cae9cdee39e827381dd20c3 100644 --- a/net/wireless/debugfs.c +++ b/net/wireless/debugfs.c @@ -47,17 +47,19 @@ static int ht_print_chan(struct ieee80211_channel *chan, return 0; if (chan->flags & IEEE80211_CHAN_DISABLED) - return snprintf(buf + offset, - buf_size - offset, - "%d Disabled\n", - chan->center_freq); - - return snprintf(buf + offset, - buf_size - offset, - "%d HT40 %c%c\n", - chan->center_freq, - (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-', - (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+'); + return scnprintf(buf + offset, + buf_size - offset, + "%d Disabled\n", + chan->center_freq); + + return scnprintf(buf + offset, + buf_size - offset, + "%d HT40 %c%c\n", + chan->center_freq, + (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? + ' ' : '-', + (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? + ' ' : '+'); } static ssize_t ht40allow_map_read(struct file *file, diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index 9392f8cbb901d605769bbd6bb0fd048b3f884328..42ed274e81f4ca7debc59c4ae17137c35a0993d8 100644 --- a/net/wireless/genregdb.awk +++ b/net/wireless/genregdb.awk @@ -46,6 +46,12 @@ BEGIN { sub(/:/, "", country) printf "static const struct ieee80211_regdomain regdom_%s = {\n", country printf "\t.alpha2 = \"%s\",\n", country + if ($NF ~ /DFS-ETSI/) + printf "\t.dfs_region = NL80211_DFS_ETSI,\n" + else if ($NF ~ /DFS-FCC/) + printf "\t.dfs_region = NL80211_DFS_FCC,\n" + else if ($NF ~ /DFS-JP/) + printf "\t.dfs_region = NL80211_DFS_JP,\n" printf "\t.reg_rules = {\n" active = 1 regdb = regdb "\t®dom_" country ",\n" diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 403fe29c024db86f732b0c0d74b8d97d4cfc84bf..9d797df56649c5a47fdf1f61e665ee31e4b77e7c 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -83,6 +83,8 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, struct cfg80211_cached_keys *connkeys) { struct wireless_dev *wdev = dev->ieee80211_ptr; + struct ieee80211_channel *check_chan; + u8 radar_detect_width = 0; int err; ASSERT_WDEV_LOCK(wdev); @@ -114,14 +116,28 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, wdev->connect_keys = connkeys; wdev->ibss_fixed = params->channel_fixed; + wdev->ibss_dfs_possible = params->userspace_handles_dfs; #ifdef CONFIG_CFG80211_WEXT wdev->wext.ibss.chandef = params->chandef; #endif + check_chan = params->chandef.chan; + if (params->userspace_handles_dfs) { + /* use channel NULL to check for radar even if the current + * channel is not a radar channel - it might decide to change + * to DFS channel later. + */ + radar_detect_width = BIT(params->chandef.width); + check_chan = NULL; + } + + err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, + check_chan, + (params->channel_fixed && + !radar_detect_width) + ? CHAN_MODE_SHARED + : CHAN_MODE_EXCLUSIVE, + radar_detect_width); - err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan, - params->channel_fixed - ? CHAN_MODE_SHARED - : CHAN_MODE_EXCLUSIVE); if (err) { wdev->connect_keys = NULL; return err; diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 8d49c1ce3deacbbb7ec9fe534e081ef7953607f5..6a6b1c8e907df93e73b44a8912a3ae1fda5f80a5 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -707,11 +707,13 @@ void cfg80211_dfs_channels_update_work(struct work_struct *work) if (c->dfs_state != NL80211_DFS_UNAVAILABLE) continue; - timeout = c->dfs_state_entered + - IEEE80211_DFS_MIN_NOP_TIME_MS; + timeout = c->dfs_state_entered + msecs_to_jiffies( + IEEE80211_DFS_MIN_NOP_TIME_MS); if (time_after_eq(jiffies, timeout)) { c->dfs_state = NL80211_DFS_USABLE; + c->dfs_state_entered = jiffies; + cfg80211_chandef_create(&chandef, c, NL80211_CHAN_NO_HT); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 626dc3b5fd8d205305b8ddfecaf106c33adcb9d6..a7f4e7902104907adf86c9fffcfc0ab956d36095 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -354,6 +354,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED }, [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 }, [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 }, + [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY }, + [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, + [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -3896,9 +3899,45 @@ static int nl80211_parse_sta_wme(struct genl_info *info, return 0; } +static int nl80211_parse_sta_channel_info(struct genl_info *info, + struct station_parameters *params) +{ + if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) { + params->supported_channels = + nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); + params->supported_channels_len = + nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); + /* + * Need to include at least one (first channel, number of + * channels) tuple for each subband, and must have proper + * tuples for the rest of the data as well. + */ + if (params->supported_channels_len < 2) + return -EINVAL; + if (params->supported_channels_len % 2) + return -EINVAL; + } + + if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) { + params->supported_oper_classes = + nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); + params->supported_oper_classes_len = + nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); + /* + * The value of the Length field of the Supported Operating + * Classes element is between 2 and 253. + */ + if (params->supported_oper_classes_len < 2 || + params->supported_oper_classes_len > 253) + return -EINVAL; + } + return 0; +} + static int nl80211_set_station_tdls(struct genl_info *info, struct station_parameters *params) { + int err; /* Dummy STA entry gets updated once the peer capabilities are known */ if (info->attrs[NL80211_ATTR_PEER_AID]) params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]); @@ -3909,6 +3948,10 @@ static int nl80211_set_station_tdls(struct genl_info *info, params->vht_capa = nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); + err = nl80211_parse_sta_channel_info(info, params); + if (err) + return err; + return nl80211_parse_sta_wme(info, params); } @@ -4089,6 +4132,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } + err = nl80211_parse_sta_channel_info(info, ¶ms); + if (err) + return err; + err = nl80211_parse_sta_wme(info, ¶ms); if (err) return err; @@ -5591,6 +5638,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, if (err) return err; + if (netif_carrier_ok(dev)) + return -EBUSY; + if (wdev->cac_started) return -EBUSY; @@ -5634,15 +5684,27 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1]; u8 radar_detect_width = 0; int err; + bool need_new_beacon = false; if (!rdev->ops->channel_switch || !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) return -EOPNOTSUPP; - /* may add IBSS support later */ - if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && - dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + switch (dev->ieee80211_ptr->iftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + need_new_beacon = true; + + /* useless if AP is not running */ + if (!wdev->beacon_interval) + return -EINVAL; + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + break; + default: return -EOPNOTSUPP; + } memset(¶ms, 0, sizeof(params)); @@ -5651,15 +5713,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) return -EINVAL; /* only important for AP, IBSS and mesh create IEs internally */ - if (!info->attrs[NL80211_ATTR_CSA_IES]) - return -EINVAL; - - /* useless if AP is not running */ - if (!wdev->beacon_interval) + if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES]) return -EINVAL; params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); + if (!need_new_beacon) + goto skip_beacons; + err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after); if (err) return err; @@ -5699,6 +5760,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) return -EINVAL; } +skip_beacons: err = nl80211_parse_chandef(rdev, info, ¶ms.chandef); if (err) return err; @@ -5706,12 +5768,17 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) return -EINVAL; - err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef); - if (err < 0) { - return err; - } else if (err) { - radar_detect_width = BIT(params.chandef.width); - params.radar_required = true; + if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP || + dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO || + dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) { + err = cfg80211_chandef_dfs_required(wdev->wiphy, + ¶ms.chandef); + if (err < 0) { + return err; + } else if (err) { + radar_detect_width = BIT(params.chandef.width); + params.radar_required = true; + } } err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, @@ -6535,6 +6602,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.control_port = nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]); + ibss.userspace_handles_dfs = + nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]); + err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys); if (err) kfree(connkeys); @@ -10740,7 +10810,9 @@ void cfg80211_ch_switch_notify(struct net_device *dev, wdev_lock(wdev); if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && - wdev->iftype != NL80211_IFTYPE_P2P_GO)) + wdev->iftype != NL80211_IFTYPE_P2P_GO && + wdev->iftype != NL80211_IFTYPE_ADHOC && + wdev->iftype != NL80211_IFTYPE_MESH_POINT)) goto out; wdev->channel = chandef->chan; diff --git a/net/wireless/reg.c b/net/wireless/reg.c index de06d5d1287f97b6f6c8a1fd6b3e59371d80ae3e..7da67fd0b4188a7cf3e3ded0ae2a64d565274947 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -172,11 +172,21 @@ static const struct ieee80211_regdomain world_regdom = { NL80211_RRF_NO_IBSS | NL80211_RRF_NO_OFDM), /* IEEE 802.11a, channel 36..48 */ - REG_RULE(5180-10, 5240+10, 80, 6, 20, + REG_RULE(5180-10, 5240+10, 160, 6, 20, NL80211_RRF_PASSIVE_SCAN | NL80211_RRF_NO_IBSS), - /* NB: 5260 MHz - 5700 MHz requires DFS */ + /* IEEE 802.11a, channel 52..64 - DFS required */ + REG_RULE(5260-10, 5320+10, 160, 6, 20, + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS | + NL80211_RRF_DFS), + + /* IEEE 802.11a, channel 100..144 - DFS required */ + REG_RULE(5500-10, 5720+10, 160, 6, 20, + NL80211_RRF_PASSIVE_SCAN | + NL80211_RRF_NO_IBSS | + NL80211_RRF_DFS), /* IEEE 802.11a, channel 149..165 */ REG_RULE(5745-10, 5825+10, 80, 6, 20, @@ -758,24 +768,25 @@ const struct ieee80211_reg_rule *freq_reg_info(struct wiphy *wiphy, } EXPORT_SYMBOL(freq_reg_info); -#ifdef CONFIG_CFG80211_REG_DEBUG -static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) +const char *reg_initiator_name(enum nl80211_reg_initiator initiator) { switch (initiator) { case NL80211_REGDOM_SET_BY_CORE: - return "Set by core"; + return "core"; case NL80211_REGDOM_SET_BY_USER: - return "Set by user"; + return "user"; case NL80211_REGDOM_SET_BY_DRIVER: - return "Set by driver"; + return "driver"; case NL80211_REGDOM_SET_BY_COUNTRY_IE: - return "Set by country IE"; + return "country IE"; default: WARN_ON(1); - return "Set by bug"; + return "bug"; } } +EXPORT_SYMBOL(reg_initiator_name); +#ifdef CONFIG_CFG80211_REG_DEBUG static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, const struct ieee80211_reg_rule *reg_rule) { @@ -962,6 +973,13 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy) } #endif +static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy) +{ + if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && + !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)) + return true; + return false; +} static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) @@ -969,14 +987,17 @@ static bool ignore_reg_update(struct wiphy *wiphy, struct regulatory_request *lr = get_last_request(); if (!lr) { - REG_DBG_PRINT("Ignoring regulatory request %s since last_request is not set\n", + REG_DBG_PRINT("Ignoring regulatory request set by %s " + "since last_request is not set\n", reg_initiator_name(initiator)); return true; } if (initiator == NL80211_REGDOM_SET_BY_CORE && wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { - REG_DBG_PRINT("Ignoring regulatory request %s since the driver uses its own custom regulatory domain\n", + REG_DBG_PRINT("Ignoring regulatory request set by %s " + "since the driver uses its own custom " + "regulatory domain\n", reg_initiator_name(initiator)); return true; } @@ -985,10 +1006,12 @@ static bool ignore_reg_update(struct wiphy *wiphy, * wiphy->regd will be set once the device has its own * desired regulatory domain set */ - if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && + if (wiphy_strict_alpha2_regd(wiphy) && !wiphy->regd && initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && !is_world_regdom(lr->alpha2)) { - REG_DBG_PRINT("Ignoring regulatory request %s since the driver requires its own regulatory domain to be set first\n", + REG_DBG_PRINT("Ignoring regulatory request set by %s " + "since the driver requires its own regulatory " + "domain to be set first\n", reg_initiator_name(initiator)); return true; } @@ -1689,8 +1712,8 @@ int regulatory_hint(struct wiphy *wiphy, const char *alpha2) } EXPORT_SYMBOL(regulatory_hint); -void regulatory_hint_11d(struct wiphy *wiphy, enum ieee80211_band band, - const u8 *country_ie, u8 country_ie_len) +void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band, + const u8 *country_ie, u8 country_ie_len) { char alpha2[2]; enum environment_cap env = ENVIRON_ANY; diff --git a/net/wireless/reg.h b/net/wireless/reg.h index af2d5f8a5d828481e4036cba15d7577674b0e303..9677e3c13da98da5b00f34e112b0562512b6fc65 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -58,7 +58,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, gfp_t gfp); /** - * regulatory_hint_11d - hints a country IE as a regulatory domain + * regulatory_hint_country_ie - hints a country IE as a regulatory domain * @wiphy: the wireless device giving the hint (used only for reporting * conflicts) * @band: the band on which the country IE was received on. This determines @@ -78,7 +78,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, * not observed. For this reason if a triplet is seen with channel * information for a band the BSS is not present in it will be ignored. */ -void regulatory_hint_11d(struct wiphy *wiphy, +void regulatory_hint_country_ie(struct wiphy *wiphy, enum ieee80211_band band, const u8 *country_ie, u8 country_ie_len); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index eeb71480f1af2cecb575780c29dff0c15de3f197..d4397eba5408ea4325ac3404a251b8617d878309 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -254,10 +254,10 @@ void __cfg80211_sched_scan_results(struct work_struct *wk) rdev = container_of(wk, struct cfg80211_registered_device, sched_scan_results_wk); - request = rdev->sched_scan_req; - rtnl_lock(); + request = rdev->sched_scan_req; + /* we don't have sched_scan_req anymore if the scan is stopping */ if (request) { if (request->flags & NL80211_SCAN_FLAG_FLUSH) { diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 20e86a95dc4e0ed358485f04208c670297ee6517..65f800890d70d857c9b42d379caad887f5a98c74 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -682,8 +682,8 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * - country_ie + 2, the start of the country ie data, and * - and country_ie[1] which is the IE length */ - regulatory_hint_11d(wdev->wiphy, bss->channel->band, - country_ie + 2, country_ie[1]); + regulatory_hint_country_ie(wdev->wiphy, bss->channel->band, + country_ie + 2, country_ie[1]); kfree(country_ie); } diff --git a/net/wireless/sysfs.h b/net/wireless/sysfs.h index 65acbebd371127b5eeb70f5bc82a6c1028924c26..b533ed71dafff1d0b3f7c0a6049630d9e2cdda38 100644 --- a/net/wireless/sysfs.h +++ b/net/wireless/sysfs.h @@ -1,8 +1,8 @@ #ifndef __WIRELESS_SYSFS_H #define __WIRELESS_SYSFS_H -extern int wiphy_sysfs_init(void); -extern void wiphy_sysfs_exit(void); +int wiphy_sysfs_init(void); +void wiphy_sysfs_exit(void); extern struct class ieee80211_class; diff --git a/net/wireless/util.c b/net/wireless/util.c index ce090c1c5e4fdb36459c4f6fde6b5af241a75f0e..935dea9485da01f7a0fb82cacc6e187058ff1ce0 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "core.h" #include "rdev-ops.h" @@ -691,6 +692,7 @@ EXPORT_SYMBOL(ieee80211_amsdu_to_8023s); unsigned int cfg80211_classify8021d(struct sk_buff *skb) { unsigned int dscp; + unsigned char vlan_priority; /* skb->priority values from 256->263 are magic values to * directly indicate a specific 802.1d priority. This is used @@ -700,6 +702,13 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb) if (skb->priority >= 256 && skb->priority <= 263) return skb->priority - 256; + if (vlan_tx_tag_present(skb)) { + vlan_priority = (vlan_tx_tag_get(skb) & VLAN_PRIO_MASK) + >> VLAN_PRIO_SHIFT; + if (vlan_priority > 0) + return vlan_priority; + } + switch (skb->protocol) { case htons(ETH_P_IP): dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc; @@ -1240,7 +1249,7 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, enum cfg80211_chan_mode chmode; int num_different_channels = 0; int total = 1; - bool radar_required; + bool radar_required = false; int i, j; ASSERT_RTNL(); @@ -1255,14 +1264,20 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev, case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_WDS: - radar_required = !!(chan && - (chan->flags & IEEE80211_CHAN_RADAR)); + /* if the interface could potentially choose a DFS channel, + * then mark DFS as required. + */ + if (!chan) { + if (chanmode != CHAN_MODE_UNDEFINED && radar_detect) + radar_required = true; + break; + } + radar_required = !!(chan->flags & IEEE80211_CHAN_RADAR); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_MONITOR: - radar_required = false; break; case NUM_NL80211_IFTYPES: case NL80211_IFTYPE_UNSPECIFIED: diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index ab4ef72f0b1daefd4b46765d9009975cd9330676..debe733386f859b9456db893c78b8a44e9fce14c 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -802,17 +802,4 @@ int xfrm_count_pfkey_enc_supported(void) } EXPORT_SYMBOL_GPL(xfrm_count_pfkey_enc_supported); -#if defined(CONFIG_INET_ESP) || defined(CONFIG_INET_ESP_MODULE) || defined(CONFIG_INET6_ESP) || defined(CONFIG_INET6_ESP_MODULE) - -void *pskb_put(struct sk_buff *skb, struct sk_buff *tail, int len) -{ - if (tail != skb) { - skb->data_len += len; - skb->len += len; - } - return skb_put(tail, len); -} -EXPORT_SYMBOL_GPL(pskb_put); -#endif - MODULE_LICENSE("GPL"); diff --git a/net/xfrm/xfrm_hash.h b/net/xfrm/xfrm_hash.h index 716502ada53ba00b1877c2bc6d140fd50915ee7a..0622d319e1f269c31e8a6af27e4c986832948534 100644 --- a/net/xfrm/xfrm_hash.h +++ b/net/xfrm/xfrm_hash.h @@ -130,7 +130,7 @@ static inline unsigned int __addr_hash(const xfrm_address_t *daddr, return h & hmask; } -extern struct hlist_head *xfrm_hash_alloc(unsigned int sz); -extern void xfrm_hash_free(struct hlist_head *n, unsigned int sz); +struct hlist_head *xfrm_hash_alloc(unsigned int sz); +void xfrm_hash_free(struct hlist_head *n, unsigned int sz); #endif /* _XFRM_HASH_H */ diff --git a/net/xfrm/xfrm_ipcomp.c b/net/xfrm/xfrm_ipcomp.c index 3be02b680268d1ccb7e3672a92777b12963b8562..ccfdc7115a83f709e2a5980c5dc0d65cd5859467 100644 --- a/net/xfrm/xfrm_ipcomp.c +++ b/net/xfrm/xfrm_ipcomp.c @@ -220,8 +220,8 @@ static void ipcomp_free_scratches(void) static void * __percpu *ipcomp_alloc_scratches(void) { - int i; void * __percpu *scratches; + int i; if (ipcomp_scratch_users++) return ipcomp_scratches; @@ -233,7 +233,9 @@ static void * __percpu *ipcomp_alloc_scratches(void) ipcomp_scratches = scratches; for_each_possible_cpu(i) { - void *scratch = vmalloc(IPCOMP_SCRATCH_SIZE); + void *scratch; + + scratch = vmalloc_node(IPCOMP_SCRATCH_SIZE, cpu_to_node(i)); if (!scratch) return NULL; *per_cpu_ptr(scratches, i) = scratch; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 76e1873811d4cac098cd4d563e865e847997a4a4..9a91f7431c411b706810a40bccc3167617c74aec 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1844,6 +1844,13 @@ static int xdst_queue_output(struct sk_buff *skb) struct xfrm_dst *xdst = (struct xfrm_dst *) dst; struct xfrm_policy *pol = xdst->pols[0]; struct xfrm_policy_queue *pq = &pol->polq; + const struct sk_buff *fclone = skb + 1; + + if (unlikely(skb->fclone == SKB_FCLONE_ORIG && + fclone->fclone == SKB_FCLONE_CLONE)) { + kfree_skb(skb); + return 0; + } if (pq->hold_queue.qlen > XFRM_MAX_QUEUE_LEN) { kfree_skb(skb); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index b9c3f9e943a9159d1617feec49c751055ea4dd55..68c2f357a18389d9debc35401afe1295c4341959 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -468,7 +468,7 @@ expired: } err = __xfrm_state_delete(x); - if (!err && x->id.spi) + if (!err) km_state_expired(x, 1, 0); xfrm_audit_state_delete(x, err ? 0 : 1, @@ -815,7 +815,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, xfrm_state_look_at(pol, x, fl, encap_family, &best, &acquire_in_progress, &error); } - if (best) + if (best || acquire_in_progress) goto found; h_wildcard = xfrm_dst_hash(net, daddr, &saddr_wildcard, tmpl->reqid, encap_family); @@ -824,7 +824,7 @@ xfrm_state_find(const xfrm_address_t *daddr, const xfrm_address_t *saddr, x->props.reqid == tmpl->reqid && (mark & x->mark.m) == x->mark.v && !(x->props.flags & XFRM_STATE_WILDRECV) && - xfrm_state_addr_check(x, daddr, saddr, encap_family) && + xfrm_addr_equal(&x->id.daddr, daddr, encap_family) && tmpl->mode == x->props.mode && tmpl->id.proto == x->id.proto && (tmpl->id.spi == x->id.spi || !tmpl->id.spi)) diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 8d8d97dbb389b1d305352cee29e4dbcc0191af64..234bc2ab450c61b42b1db2b53f631ab72bc48a39 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -302,18 +302,19 @@ static void dump_common_audit_data(struct audit_buffer *ab, "faddr", "fport"); break; } +#if IS_ENABLED(CONFIG_IPV6) case AF_INET6: { struct inet_sock *inet = inet_sk(sk); - struct ipv6_pinfo *inet6 = inet6_sk(sk); - print_ipv6_addr(ab, &inet6->rcv_saddr, + print_ipv6_addr(ab, &sk->sk_v6_rcv_saddr, inet->inet_sport, "laddr", "lport"); - print_ipv6_addr(ab, &inet6->daddr, + print_ipv6_addr(ab, &sk->sk_v6_daddr, inet->inet_dport, "faddr", "fport"); break; } +#endif case AF_UNIX: u = unix_sk(sk); if (u->path.dentry) { diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5b5231068516e46bb529efb3df38051124d2ef85..c540795fb3f2647619cb4705281872e93592e21e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3928,7 +3928,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (snum) { int low, high; - inet_get_local_port_range(&low, &high); + inet_get_local_port_range(sock_net(sk), &low, &high); if (snum < max(PROT_SOCK, low) || snum > high) { err = sel_netport_sid(sk->sk_protocol, @@ -4667,7 +4667,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, return NF_ACCEPT; } -static unsigned int selinux_ipv4_forward(unsigned int hooknum, +static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -4677,7 +4677,7 @@ static unsigned int selinux_ipv4_forward(unsigned int hooknum, } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static unsigned int selinux_ipv6_forward(unsigned int hooknum, +static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -4709,7 +4709,7 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, return NF_ACCEPT; } -static unsigned int selinux_ipv4_output(unsigned int hooknum, +static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -4836,7 +4836,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, return NF_ACCEPT; } -static unsigned int selinux_ipv4_postroute(unsigned int hooknum, +static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out, @@ -4846,7 +4846,7 @@ static unsigned int selinux_ipv4_postroute(unsigned int hooknum, } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static unsigned int selinux_ipv6_postroute(unsigned int hooknum, +static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops, struct sk_buff *skb, const struct net_device *in, const struct net_device *out,