mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	Including fixes from CAN, netfilter, wireguard and IPsec.
Current release - regressions:
 
  - rxrpc: fix use of page_frag_alloc_align(), it changed semantics
    and we added a new caller in a different subtree
 
  - xfrm: allow UDP encapsulation only in offload modes
 
 Current release - new code bugs:
 
  - tcp: fix refcnt handling in __inet_hash_connect()
 
  - Revert "net: Re-use and set mono_delivery_time bit for userspace tstamp
    packets", conflicted with some expectations in BPF uAPI
 
 Previous releases - regressions:
 
  - ipv4: raw: fix sending packets from raw sockets via IPsec tunnels
 
  - devlink: fix devlink's parallel command processing
 
  - veth: do not manipulate GRO when using XDP
 
  - esp: fix bad handling of pages from page_pool
 
 Previous releases - always broken:
 
  - report RCU QS for busy network kthreads (with Paul McK's blessing)
 
  - tcp/rds: fix use-after-free on netns with kernel TCP reqsk
 
  - virt: vmxnet3: fix missing reserved tailroom with XDP
 
 Misc:
 
  - couple of build fixes for Documentation
 
 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE6jPA+I1ugmIBA4hXMUZtbf5SIrsFAmX8bXsACgkQMUZtbf5S
 IrsfBg/+KzrEx0tB/Af57ZZGZ5PMjPy+XFDox4iFfHm338UFuGXVvZrXd7G+6YkH
 ZwWeF5YDPKzwIEiZ5D3hewZPlkLH0Eg88q74chlE0gUv7t1jhuQHUdIVeFnPcLbN
 t/8AcCZCJ2fENbr1iNnzZON1RW0fVOl+SDxhiSYFeFqii6FywDfqWL/h0u86H/AF
 KRktgb0LzH0waH6IiefVV1NZyjnZwmQ6+UVQerTzUnQmWhV1xQKoO3MQpZuFRvr6
 O+kPZMkrqnTCCy7RO1BexS5cefqc80i5Z25FLGcaHgpnYd2pDNDMMxqrhqO9Y0Pv
 6u/tLgRxzVUDXWouzREIRe50Z9GJswkg78zilAhpqYiHRjd8jaBH6y+9mhGFc7F8
 iVAx02WfJhlk0aynFf2qZmR7PQIb9XjtFJ7OAeJrno9UD7zAubtikGM/6m6IZfRV
 TD1mze95RVnNjbHZMeg6oNLFUMJXVTobtvtqk5pTQvsNsmSYGFvkvWC5/P6ycyYt
 pMx6E0PA/ZCnQAlThCOCzFa5BO+It3RJHcQJhgbOzHrlWKwmrjBKcKJcLLcxFSUt
 4wwjdEcG1Bo2wdnsjwsQwJDHQW+M9TSLdLM3YVptM9jbqOMizoqr6/xSykg3H4wZ
 t/dSiYSsEr06z7lvwbAjUXJ/mfszZ+JsVAFXAN7ahcM4OZb5WTQ=
 =gpLl
 -----END PGP SIGNATURE-----
Merge tag 'net-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from Jakub Kicinski:
 "Including fixes from CAN, netfilter, wireguard and IPsec.
  I'd like to highlight [ lowlight? - Linus ] Florian W stepping down as
  a netfilter maintainer due to constant stream of bug reports. Not sure
  what we can do but IIUC this is not the first such case.
  Current release - regressions:
   - rxrpc: fix use of page_frag_alloc_align(), it changed semantics and
     we added a new caller in a different subtree
   - xfrm: allow UDP encapsulation only in offload modes
  Current release - new code bugs:
   - tcp: fix refcnt handling in __inet_hash_connect()
   - Revert "net: Re-use and set mono_delivery_time bit for userspace
     tstamp packets", conflicted with some expectations in BPF uAPI
  Previous releases - regressions:
   - ipv4: raw: fix sending packets from raw sockets via IPsec tunnels
   - devlink: fix devlink's parallel command processing
   - veth: do not manipulate GRO when using XDP
   - esp: fix bad handling of pages from page_pool
  Previous releases - always broken:
   - report RCU QS for busy network kthreads (with Paul McK's blessing)
   - tcp/rds: fix use-after-free on netns with kernel TCP reqsk
   - virt: vmxnet3: fix missing reserved tailroom with XDP
  Misc:
   - couple of build fixes for Documentation"
* tag 'net-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (59 commits)
  selftests: forwarding: Fix ping failure due to short timeout
  MAINTAINERS: step down as netfilter maintainer
  netfilter: nf_tables: Fix a memory leak in nf_tables_updchain
  net: dsa: mt7530: fix handling of all link-local frames
  net: dsa: mt7530: fix link-local frames that ingress vlan filtering ports
  bpf: report RCU QS in cpumap kthread
  net: report RCU QS on threaded NAPI repolling
  rcu: add a helper to report consolidated flavor QS
  ionic: update documentation for XDP support
  lib/bitmap: Fix bitmap_scatter() and bitmap_gather() kernel doc
  netfilter: nf_tables: do not compare internal table flags on updates
  netfilter: nft_set_pipapo: release elements in clone only from destroy path
  octeontx2-af: Use separate handlers for interrupts
  octeontx2-pf: Send UP messages to VF only when VF is up.
  octeontx2-pf: Use default max_active works instead of one
  octeontx2-pf: Wait till detach_resources msg is complete
  octeontx2: Detect the mbox up or down message via register
  devlink: fix port new reply cmd type
  tcp: Clear req->syncookie in reqsk_alloc().
  net/bnx2x: Prevent access to a freed page in page_pool
  ...
			
			
This commit is contained in:
		
						commit
						cba9ffdb99
					
				
					 71 changed files with 608 additions and 344 deletions
				
			
		|  | @ -99,6 +99,12 @@ Minimal SR-IOV support is currently offered and can be enabled by setting | ||||||
| the sysfs 'sriov_numvfs' value, if supported by your particular firmware | the sysfs 'sriov_numvfs' value, if supported by your particular firmware | ||||||
| configuration. | configuration. | ||||||
| 
 | 
 | ||||||
|  | XDP | ||||||
|  | --- | ||||||
|  | 
 | ||||||
|  | Support for XDP includes the basics, plus Jumbo frames, Redirect and | ||||||
|  | ndo_xmit.  There is no current support for zero-copy sockets or HW offload. | ||||||
|  | 
 | ||||||
| Statistics | Statistics | ||||||
| ========== | ========== | ||||||
| 
 | 
 | ||||||
|  | @ -138,6 +144,12 @@ Driver port specific:: | ||||||
|      rx_csum_none: 0 |      rx_csum_none: 0 | ||||||
|      rx_csum_complete: 3 |      rx_csum_complete: 3 | ||||||
|      rx_csum_error: 0 |      rx_csum_error: 0 | ||||||
|  |      xdp_drop: 0 | ||||||
|  |      xdp_aborted: 0 | ||||||
|  |      xdp_pass: 0 | ||||||
|  |      xdp_tx: 0 | ||||||
|  |      xdp_redirect: 0 | ||||||
|  |      xdp_frames: 0 | ||||||
| 
 | 
 | ||||||
| Driver queue specific:: | Driver queue specific:: | ||||||
| 
 | 
 | ||||||
|  | @ -149,9 +161,12 @@ Driver queue specific:: | ||||||
|      tx_0_frags: 0 |      tx_0_frags: 0 | ||||||
|      tx_0_tso: 0 |      tx_0_tso: 0 | ||||||
|      tx_0_tso_bytes: 0 |      tx_0_tso_bytes: 0 | ||||||
|  |      tx_0_hwstamp_valid: 0 | ||||||
|  |      tx_0_hwstamp_invalid: 0 | ||||||
|      tx_0_csum_none: 3 |      tx_0_csum_none: 3 | ||||||
|      tx_0_csum: 0 |      tx_0_csum: 0 | ||||||
|      tx_0_vlan_inserted: 0 |      tx_0_vlan_inserted: 0 | ||||||
|  |      tx_0_xdp_frames: 0 | ||||||
|      rx_0_pkts: 2 |      rx_0_pkts: 2 | ||||||
|      rx_0_bytes: 120 |      rx_0_bytes: 120 | ||||||
|      rx_0_dma_map_err: 0 |      rx_0_dma_map_err: 0 | ||||||
|  | @ -159,8 +174,15 @@ Driver queue specific:: | ||||||
|      rx_0_csum_none: 0 |      rx_0_csum_none: 0 | ||||||
|      rx_0_csum_complete: 0 |      rx_0_csum_complete: 0 | ||||||
|      rx_0_csum_error: 0 |      rx_0_csum_error: 0 | ||||||
|  |      rx_0_hwstamp_valid: 0 | ||||||
|  |      rx_0_hwstamp_invalid: 0 | ||||||
|      rx_0_dropped: 0 |      rx_0_dropped: 0 | ||||||
|      rx_0_vlan_stripped: 0 |      rx_0_vlan_stripped: 0 | ||||||
|  |      rx_0_xdp_drop: 0 | ||||||
|  |      rx_0_xdp_aborted: 0 | ||||||
|  |      rx_0_xdp_pass: 0 | ||||||
|  |      rx_0_xdp_tx: 0 | ||||||
|  |      rx_0_xdp_redirect: 0 | ||||||
| 
 | 
 | ||||||
| Firmware port specific:: | Firmware port specific:: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -87,35 +87,35 @@ all using the same instance under "priv->mdev". | ||||||
| 
 | 
 | ||||||
| Observability | Observability | ||||||
| ============= | ============= | ||||||
| The relation between PF, irq, napi, and queue can be observed via netlink spec: | The relation between PF, irq, napi, and queue can be observed via netlink spec:: | ||||||
| 
 | 
 | ||||||
| $ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/netdev.yaml --dump queue-get --json='{"ifindex": 13}' |   $ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/netdev.yaml --dump queue-get --json='{"ifindex": 13}' | ||||||
| [{'id': 0, 'ifindex': 13, 'napi-id': 539, 'type': 'rx'}, |   [{'id': 0, 'ifindex': 13, 'napi-id': 539, 'type': 'rx'}, | ||||||
|  {'id': 1, 'ifindex': 13, 'napi-id': 540, 'type': 'rx'}, |    {'id': 1, 'ifindex': 13, 'napi-id': 540, 'type': 'rx'}, | ||||||
|  {'id': 2, 'ifindex': 13, 'napi-id': 541, 'type': 'rx'}, |    {'id': 2, 'ifindex': 13, 'napi-id': 541, 'type': 'rx'}, | ||||||
|  {'id': 3, 'ifindex': 13, 'napi-id': 542, 'type': 'rx'}, |    {'id': 3, 'ifindex': 13, 'napi-id': 542, 'type': 'rx'}, | ||||||
|  {'id': 4, 'ifindex': 13, 'napi-id': 543, 'type': 'rx'}, |    {'id': 4, 'ifindex': 13, 'napi-id': 543, 'type': 'rx'}, | ||||||
|  {'id': 0, 'ifindex': 13, 'napi-id': 539, 'type': 'tx'}, |    {'id': 0, 'ifindex': 13, 'napi-id': 539, 'type': 'tx'}, | ||||||
|  {'id': 1, 'ifindex': 13, 'napi-id': 540, 'type': 'tx'}, |    {'id': 1, 'ifindex': 13, 'napi-id': 540, 'type': 'tx'}, | ||||||
|  {'id': 2, 'ifindex': 13, 'napi-id': 541, 'type': 'tx'}, |    {'id': 2, 'ifindex': 13, 'napi-id': 541, 'type': 'tx'}, | ||||||
|  {'id': 3, 'ifindex': 13, 'napi-id': 542, 'type': 'tx'}, |    {'id': 3, 'ifindex': 13, 'napi-id': 542, 'type': 'tx'}, | ||||||
|  {'id': 4, 'ifindex': 13, 'napi-id': 543, 'type': 'tx'}] |    {'id': 4, 'ifindex': 13, 'napi-id': 543, 'type': 'tx'}] | ||||||
| 
 | 
 | ||||||
| $ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/netdev.yaml --dump napi-get --json='{"ifindex": 13}' |   $ ./tools/net/ynl/cli.py --spec Documentation/netlink/specs/netdev.yaml --dump napi-get --json='{"ifindex": 13}' | ||||||
| [{'id': 543, 'ifindex': 13, 'irq': 42}, |   [{'id': 543, 'ifindex': 13, 'irq': 42}, | ||||||
|  {'id': 542, 'ifindex': 13, 'irq': 41}, |    {'id': 542, 'ifindex': 13, 'irq': 41}, | ||||||
|  {'id': 541, 'ifindex': 13, 'irq': 40}, |    {'id': 541, 'ifindex': 13, 'irq': 40}, | ||||||
|  {'id': 540, 'ifindex': 13, 'irq': 39}, |    {'id': 540, 'ifindex': 13, 'irq': 39}, | ||||||
|  {'id': 539, 'ifindex': 13, 'irq': 36}] |    {'id': 539, 'ifindex': 13, 'irq': 36}] | ||||||
| 
 | 
 | ||||||
| Here you can clearly observe our channels distribution policy: | Here you can clearly observe our channels distribution policy:: | ||||||
| 
 | 
 | ||||||
| $ ls /proc/irq/{36,39,40,41,42}/mlx5* -d -1 |   $ ls /proc/irq/{36,39,40,41,42}/mlx5* -d -1 | ||||||
| /proc/irq/36/mlx5_comp1@pci:0000:08:00.0 |   /proc/irq/36/mlx5_comp1@pci:0000:08:00.0 | ||||||
| /proc/irq/39/mlx5_comp1@pci:0000:09:00.0 |   /proc/irq/39/mlx5_comp1@pci:0000:09:00.0 | ||||||
| /proc/irq/40/mlx5_comp2@pci:0000:08:00.0 |   /proc/irq/40/mlx5_comp2@pci:0000:08:00.0 | ||||||
| /proc/irq/41/mlx5_comp2@pci:0000:09:00.0 |   /proc/irq/41/mlx5_comp2@pci:0000:09:00.0 | ||||||
| /proc/irq/42/mlx5_comp3@pci:0000:08:00.0 |   /proc/irq/42/mlx5_comp3@pci:0000:08:00.0 | ||||||
| 
 | 
 | ||||||
| Steering | Steering | ||||||
| ======== | ======== | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ struct_dev_ifalias*                 ifalias | ||||||
| unsigned_long                       mem_end                                                          | unsigned_long                       mem_end                                                          | ||||||
| unsigned_long                       mem_start                                                        | unsigned_long                       mem_start                                                        | ||||||
| unsigned_long                       base_addr                                                        | unsigned_long                       base_addr                                                        | ||||||
| unsigned_long                       state                                                            | unsigned_long                       state                   read_mostly         read_mostly         netif_running(dev) | ||||||
| struct_list_head                    dev_list                                                         | struct_list_head                    dev_list                                                         | ||||||
| struct_list_head                    napi_list                                                        | struct_list_head                    napi_list                                                        | ||||||
| struct_list_head                    unreg_list                                                       | struct_list_head                    unreg_list                                                       | ||||||
|  |  | ||||||
|  | @ -15237,7 +15237,6 @@ F:	drivers/net/ethernet/neterion/ | ||||||
| NETFILTER | NETFILTER | ||||||
| M:	Pablo Neira Ayuso <pablo@netfilter.org> | M:	Pablo Neira Ayuso <pablo@netfilter.org> | ||||||
| M:	Jozsef Kadlecsik <kadlec@netfilter.org> | M:	Jozsef Kadlecsik <kadlec@netfilter.org> | ||||||
| M:	Florian Westphal <fw@strlen.de> |  | ||||||
| L:	netfilter-devel@vger.kernel.org | L:	netfilter-devel@vger.kernel.org | ||||||
| L:	coreteam@netfilter.org | L:	coreteam@netfilter.org | ||||||
| S:	Maintained | S:	Maintained | ||||||
|  |  | ||||||
|  | @ -370,8 +370,8 @@ static const struct kvaser_pciefd_irq_mask kvaser_pciefd_sf2_irq_mask = { | ||||||
| 
 | 
 | ||||||
| static const struct kvaser_pciefd_irq_mask kvaser_pciefd_xilinx_irq_mask = { | static const struct kvaser_pciefd_irq_mask kvaser_pciefd_xilinx_irq_mask = { | ||||||
| 	.kcan_rx0 = BIT(4), | 	.kcan_rx0 = BIT(4), | ||||||
| 	.kcan_tx = { BIT(16), BIT(17), BIT(18), BIT(19) }, | 	.kcan_tx = { BIT(16), BIT(17), BIT(18), BIT(19), BIT(20), BIT(21), BIT(22), BIT(23) }, | ||||||
| 	.all = GENMASK(19, 16) | BIT(4), | 	.all = GENMASK(23, 16) | BIT(4), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const struct kvaser_pciefd_dev_ops kvaser_pciefd_altera_dev_ops = { | static const struct kvaser_pciefd_dev_ops kvaser_pciefd_altera_dev_ops = { | ||||||
|  |  | ||||||
|  | @ -950,20 +950,56 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) | ||||||
| 	mutex_unlock(&priv->reg_mutex); | 	mutex_unlock(&priv->reg_mutex); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* On page 205, section "8.6.3 Frame filtering" of the active standard, IEEE Std
 | ||||||
|  |  * 802.1Q™-2022, it is stated that frames with 01:80:C2:00:00:00-0F as MAC DA | ||||||
|  |  * must only be propagated to C-VLAN and MAC Bridge components. That means | ||||||
|  |  * VLAN-aware and VLAN-unaware bridges. On the switch designs with CPU ports, | ||||||
|  |  * these frames are supposed to be processed by the CPU (software). So we make | ||||||
|  |  * the switch only forward them to the CPU port. And if received from a CPU | ||||||
|  |  * port, forward to a single port. The software is responsible of making the | ||||||
|  |  * switch conform to the latter by setting a single port as destination port on | ||||||
|  |  * the special tag. | ||||||
|  |  * | ||||||
|  |  * This switch intellectual property cannot conform to this part of the standard | ||||||
|  |  * fully. Whilst the REV_UN frame tag covers the remaining :04-0D and :0F MAC | ||||||
|  |  * DAs, it also includes :22-FF which the scope of propagation is not supposed | ||||||
|  |  * to be restricted for these MAC DAs. | ||||||
|  |  */ | ||||||
| static void | static void | ||||||
| mt753x_trap_frames(struct mt7530_priv *priv) | mt753x_trap_frames(struct mt7530_priv *priv) | ||||||
| { | { | ||||||
| 	/* Trap BPDUs to the CPU port(s) */ | 	/* Trap 802.1X PAE frames and BPDUs to the CPU port(s) and egress them
 | ||||||
| 	mt7530_rmw(priv, MT753X_BPC, MT753X_BPDU_PORT_FW_MASK, | 	 * VLAN-untagged. | ||||||
|  | 	 */ | ||||||
|  | 	mt7530_rmw(priv, MT753X_BPC, MT753X_PAE_EG_TAG_MASK | | ||||||
|  | 		   MT753X_PAE_PORT_FW_MASK | MT753X_BPDU_EG_TAG_MASK | | ||||||
|  | 		   MT753X_BPDU_PORT_FW_MASK, | ||||||
|  | 		   MT753X_PAE_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | | ||||||
|  | 		   MT753X_PAE_PORT_FW(MT753X_BPDU_CPU_ONLY) | | ||||||
|  | 		   MT753X_BPDU_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | | ||||||
| 		   MT753X_BPDU_CPU_ONLY); | 		   MT753X_BPDU_CPU_ONLY); | ||||||
| 
 | 
 | ||||||
| 	/* Trap 802.1X PAE frames to the CPU port(s) */ | 	/* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and egress
 | ||||||
| 	mt7530_rmw(priv, MT753X_BPC, MT753X_PAE_PORT_FW_MASK, | 	 * them VLAN-untagged. | ||||||
| 		   MT753X_PAE_PORT_FW(MT753X_BPDU_CPU_ONLY)); | 	 */ | ||||||
|  | 	mt7530_rmw(priv, MT753X_RGAC1, MT753X_R02_EG_TAG_MASK | | ||||||
|  | 		   MT753X_R02_PORT_FW_MASK | MT753X_R01_EG_TAG_MASK | | ||||||
|  | 		   MT753X_R01_PORT_FW_MASK, | ||||||
|  | 		   MT753X_R02_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | | ||||||
|  | 		   MT753X_R02_PORT_FW(MT753X_BPDU_CPU_ONLY) | | ||||||
|  | 		   MT753X_R01_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | | ||||||
|  | 		   MT753X_BPDU_CPU_ONLY); | ||||||
| 
 | 
 | ||||||
| 	/* Trap LLDP frames with :0E MAC DA to the CPU port(s) */ | 	/* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and egress
 | ||||||
| 	mt7530_rmw(priv, MT753X_RGAC2, MT753X_R0E_PORT_FW_MASK, | 	 * them VLAN-untagged. | ||||||
| 		   MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY)); | 	 */ | ||||||
|  | 	mt7530_rmw(priv, MT753X_RGAC2, MT753X_R0E_EG_TAG_MASK | | ||||||
|  | 		   MT753X_R0E_PORT_FW_MASK | MT753X_R03_EG_TAG_MASK | | ||||||
|  | 		   MT753X_R03_PORT_FW_MASK, | ||||||
|  | 		   MT753X_R0E_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | | ||||||
|  | 		   MT753X_R0E_PORT_FW(MT753X_BPDU_CPU_ONLY) | | ||||||
|  | 		   MT753X_R03_EG_TAG(MT7530_VLAN_EG_UNTAGGED) | | ||||||
|  | 		   MT753X_BPDU_CPU_ONLY); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
|  | @ -2192,22 +2228,16 @@ mt7530_setup(struct dsa_switch *ds) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Disable LEDs before reset to prevent the MT7530 sampling a
 |  | ||||||
| 	 * potentially incorrect HT_XTAL_FSEL value. |  | ||||||
| 	 */ |  | ||||||
| 	mt7530_write(priv, MT7530_LED_EN, 0); |  | ||||||
| 	usleep_range(1000, 1100); |  | ||||||
| 
 |  | ||||||
| 	/* Reset whole chip through gpio pin or memory-mapped registers for
 | 	/* Reset whole chip through gpio pin or memory-mapped registers for
 | ||||||
| 	 * different type of hardware | 	 * different type of hardware | ||||||
| 	 */ | 	 */ | ||||||
| 	if (priv->mcm) { | 	if (priv->mcm) { | ||||||
| 		reset_control_assert(priv->rstc); | 		reset_control_assert(priv->rstc); | ||||||
| 		usleep_range(1000, 1100); | 		usleep_range(5000, 5100); | ||||||
| 		reset_control_deassert(priv->rstc); | 		reset_control_deassert(priv->rstc); | ||||||
| 	} else { | 	} else { | ||||||
| 		gpiod_set_value_cansleep(priv->reset, 0); | 		gpiod_set_value_cansleep(priv->reset, 0); | ||||||
| 		usleep_range(1000, 1100); | 		usleep_range(5000, 5100); | ||||||
| 		gpiod_set_value_cansleep(priv->reset, 1); | 		gpiod_set_value_cansleep(priv->reset, 1); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -2420,11 +2450,11 @@ mt7531_setup(struct dsa_switch *ds) | ||||||
| 	 */ | 	 */ | ||||||
| 	if (priv->mcm) { | 	if (priv->mcm) { | ||||||
| 		reset_control_assert(priv->rstc); | 		reset_control_assert(priv->rstc); | ||||||
| 		usleep_range(1000, 1100); | 		usleep_range(5000, 5100); | ||||||
| 		reset_control_deassert(priv->rstc); | 		reset_control_deassert(priv->rstc); | ||||||
| 	} else { | 	} else { | ||||||
| 		gpiod_set_value_cansleep(priv->reset, 0); | 		gpiod_set_value_cansleep(priv->reset, 0); | ||||||
| 		usleep_range(1000, 1100); | 		usleep_range(5000, 5100); | ||||||
| 		gpiod_set_value_cansleep(priv->reset, 1); | 		gpiod_set_value_cansleep(priv->reset, 1); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -65,14 +65,33 @@ enum mt753x_id { | ||||||
| 
 | 
 | ||||||
| /* Registers for BPDU and PAE frame control*/ | /* Registers for BPDU and PAE frame control*/ | ||||||
| #define MT753X_BPC			0x24 | #define MT753X_BPC			0x24 | ||||||
| #define  MT753X_BPDU_PORT_FW_MASK	GENMASK(2, 0) | #define  MT753X_PAE_EG_TAG_MASK		GENMASK(24, 22) | ||||||
|  | #define  MT753X_PAE_EG_TAG(x)		FIELD_PREP(MT753X_PAE_EG_TAG_MASK, x) | ||||||
| #define  MT753X_PAE_PORT_FW_MASK	GENMASK(18, 16) | #define  MT753X_PAE_PORT_FW_MASK	GENMASK(18, 16) | ||||||
| #define  MT753X_PAE_PORT_FW(x)		FIELD_PREP(MT753X_PAE_PORT_FW_MASK, x) | #define  MT753X_PAE_PORT_FW(x)		FIELD_PREP(MT753X_PAE_PORT_FW_MASK, x) | ||||||
|  | #define  MT753X_BPDU_EG_TAG_MASK	GENMASK(8, 6) | ||||||
|  | #define  MT753X_BPDU_EG_TAG(x)		FIELD_PREP(MT753X_BPDU_EG_TAG_MASK, x) | ||||||
|  | #define  MT753X_BPDU_PORT_FW_MASK	GENMASK(2, 0) | ||||||
|  | 
 | ||||||
|  | /* Register for :01 and :02 MAC DA frame control */ | ||||||
|  | #define MT753X_RGAC1			0x28 | ||||||
|  | #define  MT753X_R02_EG_TAG_MASK		GENMASK(24, 22) | ||||||
|  | #define  MT753X_R02_EG_TAG(x)		FIELD_PREP(MT753X_R02_EG_TAG_MASK, x) | ||||||
|  | #define  MT753X_R02_PORT_FW_MASK	GENMASK(18, 16) | ||||||
|  | #define  MT753X_R02_PORT_FW(x)		FIELD_PREP(MT753X_R02_PORT_FW_MASK, x) | ||||||
|  | #define  MT753X_R01_EG_TAG_MASK		GENMASK(8, 6) | ||||||
|  | #define  MT753X_R01_EG_TAG(x)		FIELD_PREP(MT753X_R01_EG_TAG_MASK, x) | ||||||
|  | #define  MT753X_R01_PORT_FW_MASK	GENMASK(2, 0) | ||||||
| 
 | 
 | ||||||
| /* Register for :03 and :0E MAC DA frame control */ | /* Register for :03 and :0E MAC DA frame control */ | ||||||
| #define MT753X_RGAC2			0x2c | #define MT753X_RGAC2			0x2c | ||||||
|  | #define  MT753X_R0E_EG_TAG_MASK		GENMASK(24, 22) | ||||||
|  | #define  MT753X_R0E_EG_TAG(x)		FIELD_PREP(MT753X_R0E_EG_TAG_MASK, x) | ||||||
| #define  MT753X_R0E_PORT_FW_MASK	GENMASK(18, 16) | #define  MT753X_R0E_PORT_FW_MASK	GENMASK(18, 16) | ||||||
| #define  MT753X_R0E_PORT_FW(x)		FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x) | #define  MT753X_R0E_PORT_FW(x)		FIELD_PREP(MT753X_R0E_PORT_FW_MASK, x) | ||||||
|  | #define  MT753X_R03_EG_TAG_MASK		GENMASK(8, 6) | ||||||
|  | #define  MT753X_R03_EG_TAG(x)		FIELD_PREP(MT753X_R03_EG_TAG_MASK, x) | ||||||
|  | #define  MT753X_R03_PORT_FW_MASK	GENMASK(2, 0) | ||||||
| 
 | 
 | ||||||
| enum mt753x_bpdu_port_fw { | enum mt753x_bpdu_port_fw { | ||||||
| 	MT753X_BPDU_FOLLOW_MFC, | 	MT753X_BPDU_FOLLOW_MFC, | ||||||
|  | @ -253,6 +272,7 @@ enum mt7530_port_mode { | ||||||
| enum mt7530_vlan_port_eg_tag { | enum mt7530_vlan_port_eg_tag { | ||||||
| 	MT7530_VLAN_EG_DISABLED = 0, | 	MT7530_VLAN_EG_DISABLED = 0, | ||||||
| 	MT7530_VLAN_EG_CONSISTENT = 1, | 	MT7530_VLAN_EG_CONSISTENT = 1, | ||||||
|  | 	MT7530_VLAN_EG_UNTAGGED = 4, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| enum mt7530_vlan_port_attr { | enum mt7530_vlan_port_attr { | ||||||
|  |  | ||||||
|  | @ -1002,9 +1002,6 @@ static inline void bnx2x_set_fw_mac_addr(__le16 *fw_hi, __le16 *fw_mid, | ||||||
| static inline void bnx2x_free_rx_mem_pool(struct bnx2x *bp, | static inline void bnx2x_free_rx_mem_pool(struct bnx2x *bp, | ||||||
| 					  struct bnx2x_alloc_pool *pool) | 					  struct bnx2x_alloc_pool *pool) | ||||||
| { | { | ||||||
| 	if (!pool->page) |  | ||||||
| 		return; |  | ||||||
| 
 |  | ||||||
| 	put_page(pool->page); | 	put_page(pool->page); | ||||||
| 
 | 
 | ||||||
| 	pool->page = NULL; | 	pool->page = NULL; | ||||||
|  | @ -1015,6 +1012,9 @@ static inline void bnx2x_free_rx_sge_range(struct bnx2x *bp, | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
| 
 | 
 | ||||||
|  | 	if (!fp->page_pool.page) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
| 	if (fp->mode == TPA_MODE_DISABLED) | 	if (fp->mode == TPA_MODE_DISABLED) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1338,7 +1338,7 @@ static irqreturn_t cgx_fwi_event_handler(int irq, void *data) | ||||||
| 
 | 
 | ||||||
| 		/* Release thread waiting for completion  */ | 		/* Release thread waiting for completion  */ | ||||||
| 		lmac->cmd_pend = false; | 		lmac->cmd_pend = false; | ||||||
| 		wake_up_interruptible(&lmac->wq_cmd_cmplt); | 		wake_up(&lmac->wq_cmd_cmplt); | ||||||
| 		break; | 		break; | ||||||
| 	case CGX_EVT_ASYNC: | 	case CGX_EVT_ASYNC: | ||||||
| 		if (cgx_event_is_linkevent(event)) | 		if (cgx_event_is_linkevent(event)) | ||||||
|  |  | ||||||
|  | @ -214,11 +214,12 @@ int otx2_mbox_busy_poll_for_rsp(struct otx2_mbox *mbox, int devid) | ||||||
| } | } | ||||||
| EXPORT_SYMBOL(otx2_mbox_busy_poll_for_rsp); | EXPORT_SYMBOL(otx2_mbox_busy_poll_for_rsp); | ||||||
| 
 | 
 | ||||||
| void otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid) | static void otx2_mbox_msg_send_data(struct otx2_mbox *mbox, int devid, u64 data) | ||||||
| { | { | ||||||
| 	struct otx2_mbox_dev *mdev = &mbox->dev[devid]; | 	struct otx2_mbox_dev *mdev = &mbox->dev[devid]; | ||||||
| 	struct mbox_hdr *tx_hdr, *rx_hdr; | 	struct mbox_hdr *tx_hdr, *rx_hdr; | ||||||
| 	void *hw_mbase = mdev->hwbase; | 	void *hw_mbase = mdev->hwbase; | ||||||
|  | 	u64 intr_val; | ||||||
| 
 | 
 | ||||||
| 	tx_hdr = hw_mbase + mbox->tx_start; | 	tx_hdr = hw_mbase + mbox->tx_start; | ||||||
| 	rx_hdr = hw_mbase + mbox->rx_start; | 	rx_hdr = hw_mbase + mbox->rx_start; | ||||||
|  | @ -254,14 +255,52 @@ void otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid) | ||||||
| 
 | 
 | ||||||
| 	spin_unlock(&mdev->mbox_lock); | 	spin_unlock(&mdev->mbox_lock); | ||||||
| 
 | 
 | ||||||
|  | 	/* Check if interrupt pending */ | ||||||
|  | 	intr_val = readq((void __iomem *)mbox->reg_base + | ||||||
|  | 		     (mbox->trigger | (devid << mbox->tr_shift))); | ||||||
|  | 
 | ||||||
|  | 	intr_val |= data; | ||||||
| 	/* The interrupt should be fired after num_msgs is written
 | 	/* The interrupt should be fired after num_msgs is written
 | ||||||
| 	 * to the shared memory | 	 * to the shared memory | ||||||
| 	 */ | 	 */ | ||||||
| 	writeq(1, (void __iomem *)mbox->reg_base + | 	writeq(intr_val, (void __iomem *)mbox->reg_base + | ||||||
| 	       (mbox->trigger | (devid << mbox->tr_shift))); | 	       (mbox->trigger | (devid << mbox->tr_shift))); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid) | ||||||
|  | { | ||||||
|  | 	otx2_mbox_msg_send_data(mbox, devid, MBOX_DOWN_MSG); | ||||||
|  | } | ||||||
| EXPORT_SYMBOL(otx2_mbox_msg_send); | EXPORT_SYMBOL(otx2_mbox_msg_send); | ||||||
| 
 | 
 | ||||||
|  | void otx2_mbox_msg_send_up(struct otx2_mbox *mbox, int devid) | ||||||
|  | { | ||||||
|  | 	otx2_mbox_msg_send_data(mbox, devid, MBOX_UP_MSG); | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(otx2_mbox_msg_send_up); | ||||||
|  | 
 | ||||||
|  | bool otx2_mbox_wait_for_zero(struct otx2_mbox *mbox, int devid) | ||||||
|  | { | ||||||
|  | 	u64 data; | ||||||
|  | 
 | ||||||
|  | 	data = readq((void __iomem *)mbox->reg_base + | ||||||
|  | 		     (mbox->trigger | (devid << mbox->tr_shift))); | ||||||
|  | 
 | ||||||
|  | 	/* If data is non-zero wait for ~1ms and return to caller
 | ||||||
|  | 	 * whether data has changed to zero or not after the wait. | ||||||
|  | 	 */ | ||||||
|  | 	if (!data) | ||||||
|  | 		return true; | ||||||
|  | 
 | ||||||
|  | 	usleep_range(950, 1000); | ||||||
|  | 
 | ||||||
|  | 	data = readq((void __iomem *)mbox->reg_base + | ||||||
|  | 		     (mbox->trigger | (devid << mbox->tr_shift))); | ||||||
|  | 
 | ||||||
|  | 	return data == 0; | ||||||
|  | } | ||||||
|  | EXPORT_SYMBOL(otx2_mbox_wait_for_zero); | ||||||
|  | 
 | ||||||
| struct mbox_msghdr *otx2_mbox_alloc_msg_rsp(struct otx2_mbox *mbox, int devid, | struct mbox_msghdr *otx2_mbox_alloc_msg_rsp(struct otx2_mbox *mbox, int devid, | ||||||
| 					    int size, int size_rsp) | 					    int size, int size_rsp) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -16,6 +16,9 @@ | ||||||
| 
 | 
 | ||||||
| #define MBOX_SIZE		SZ_64K | #define MBOX_SIZE		SZ_64K | ||||||
| 
 | 
 | ||||||
|  | #define MBOX_DOWN_MSG		1 | ||||||
|  | #define MBOX_UP_MSG		2 | ||||||
|  | 
 | ||||||
| /* AF/PF: PF initiated, PF/VF VF initiated */ | /* AF/PF: PF initiated, PF/VF VF initiated */ | ||||||
| #define MBOX_DOWN_RX_START	0 | #define MBOX_DOWN_RX_START	0 | ||||||
| #define MBOX_DOWN_RX_SIZE	(46 * SZ_1K) | #define MBOX_DOWN_RX_SIZE	(46 * SZ_1K) | ||||||
|  | @ -101,6 +104,7 @@ int otx2_mbox_regions_init(struct otx2_mbox *mbox, void __force **hwbase, | ||||||
| 			   struct pci_dev *pdev, void __force *reg_base, | 			   struct pci_dev *pdev, void __force *reg_base, | ||||||
| 			   int direction, int ndevs, unsigned long *bmap); | 			   int direction, int ndevs, unsigned long *bmap); | ||||||
| void otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid); | void otx2_mbox_msg_send(struct otx2_mbox *mbox, int devid); | ||||||
|  | void otx2_mbox_msg_send_up(struct otx2_mbox *mbox, int devid); | ||||||
| int otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid); | int otx2_mbox_wait_for_rsp(struct otx2_mbox *mbox, int devid); | ||||||
| int otx2_mbox_busy_poll_for_rsp(struct otx2_mbox *mbox, int devid); | int otx2_mbox_busy_poll_for_rsp(struct otx2_mbox *mbox, int devid); | ||||||
| struct mbox_msghdr *otx2_mbox_alloc_msg_rsp(struct otx2_mbox *mbox, int devid, | struct mbox_msghdr *otx2_mbox_alloc_msg_rsp(struct otx2_mbox *mbox, int devid, | ||||||
|  | @ -118,6 +122,8 @@ static inline struct mbox_msghdr *otx2_mbox_alloc_msg(struct otx2_mbox *mbox, | ||||||
| 	return otx2_mbox_alloc_msg_rsp(mbox, devid, size, 0); | 	return otx2_mbox_alloc_msg_rsp(mbox, devid, size, 0); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool otx2_mbox_wait_for_zero(struct otx2_mbox *mbox, int devid); | ||||||
|  | 
 | ||||||
| /* Mailbox message types */ | /* Mailbox message types */ | ||||||
| #define MBOX_MSG_MASK				0xFFFF | #define MBOX_MSG_MASK				0xFFFF | ||||||
| #define MBOX_MSG_INVALID			0xFFFE | #define MBOX_MSG_INVALID			0xFFFE | ||||||
|  |  | ||||||
|  | @ -121,13 +121,17 @@ int mcs_add_intr_wq_entry(struct mcs *mcs, struct mcs_intr_event *event) | ||||||
| static int mcs_notify_pfvf(struct mcs_intr_event *event, struct rvu *rvu) | static int mcs_notify_pfvf(struct mcs_intr_event *event, struct rvu *rvu) | ||||||
| { | { | ||||||
| 	struct mcs_intr_info *req; | 	struct mcs_intr_info *req; | ||||||
| 	int err, pf; | 	int pf; | ||||||
| 
 | 
 | ||||||
| 	pf = rvu_get_pf(event->pcifunc); | 	pf = rvu_get_pf(event->pcifunc); | ||||||
| 
 | 
 | ||||||
|  | 	mutex_lock(&rvu->mbox_lock); | ||||||
|  | 
 | ||||||
| 	req = otx2_mbox_alloc_msg_mcs_intr_notify(rvu, pf); | 	req = otx2_mbox_alloc_msg_mcs_intr_notify(rvu, pf); | ||||||
| 	if (!req) | 	if (!req) { | ||||||
|  | 		mutex_unlock(&rvu->mbox_lock); | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	req->mcs_id = event->mcs_id; | 	req->mcs_id = event->mcs_id; | ||||||
| 	req->intr_mask = event->intr_mask; | 	req->intr_mask = event->intr_mask; | ||||||
|  | @ -135,10 +139,11 @@ static int mcs_notify_pfvf(struct mcs_intr_event *event, struct rvu *rvu) | ||||||
| 	req->hdr.pcifunc = event->pcifunc; | 	req->hdr.pcifunc = event->pcifunc; | ||||||
| 	req->lmac_id = event->lmac_id; | 	req->lmac_id = event->lmac_id; | ||||||
| 
 | 
 | ||||||
| 	otx2_mbox_msg_send(&rvu->afpf_wq_info.mbox_up, pf); | 	otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, pf); | ||||||
| 	err = otx2_mbox_wait_for_rsp(&rvu->afpf_wq_info.mbox_up, pf); | 
 | ||||||
| 	if (err) | 	otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, pf); | ||||||
| 		dev_warn(rvu->dev, "MCS notification to pf %d failed\n", pf); | 
 | ||||||
|  | 	mutex_unlock(&rvu->mbox_lock); | ||||||
| 
 | 
 | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2119,7 +2119,7 @@ MBOX_MESSAGES | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void __rvu_mbox_handler(struct rvu_work *mwork, int type) | static void __rvu_mbox_handler(struct rvu_work *mwork, int type, bool poll) | ||||||
| { | { | ||||||
| 	struct rvu *rvu = mwork->rvu; | 	struct rvu *rvu = mwork->rvu; | ||||||
| 	int offset, err, id, devid; | 	int offset, err, id, devid; | ||||||
|  | @ -2186,6 +2186,9 @@ static void __rvu_mbox_handler(struct rvu_work *mwork, int type) | ||||||
| 	} | 	} | ||||||
| 	mw->mbox_wrk[devid].num_msgs = 0; | 	mw->mbox_wrk[devid].num_msgs = 0; | ||||||
| 
 | 
 | ||||||
|  | 	if (poll) | ||||||
|  | 		otx2_mbox_wait_for_zero(mbox, devid); | ||||||
|  | 
 | ||||||
| 	/* Send mbox responses to VF/PF */ | 	/* Send mbox responses to VF/PF */ | ||||||
| 	otx2_mbox_msg_send(mbox, devid); | 	otx2_mbox_msg_send(mbox, devid); | ||||||
| } | } | ||||||
|  | @ -2193,15 +2196,18 @@ static void __rvu_mbox_handler(struct rvu_work *mwork, int type) | ||||||
| static inline void rvu_afpf_mbox_handler(struct work_struct *work) | static inline void rvu_afpf_mbox_handler(struct work_struct *work) | ||||||
| { | { | ||||||
| 	struct rvu_work *mwork = container_of(work, struct rvu_work, work); | 	struct rvu_work *mwork = container_of(work, struct rvu_work, work); | ||||||
|  | 	struct rvu *rvu = mwork->rvu; | ||||||
| 
 | 
 | ||||||
| 	__rvu_mbox_handler(mwork, TYPE_AFPF); | 	mutex_lock(&rvu->mbox_lock); | ||||||
|  | 	__rvu_mbox_handler(mwork, TYPE_AFPF, true); | ||||||
|  | 	mutex_unlock(&rvu->mbox_lock); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static inline void rvu_afvf_mbox_handler(struct work_struct *work) | static inline void rvu_afvf_mbox_handler(struct work_struct *work) | ||||||
| { | { | ||||||
| 	struct rvu_work *mwork = container_of(work, struct rvu_work, work); | 	struct rvu_work *mwork = container_of(work, struct rvu_work, work); | ||||||
| 
 | 
 | ||||||
| 	__rvu_mbox_handler(mwork, TYPE_AFVF); | 	__rvu_mbox_handler(mwork, TYPE_AFVF, false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void __rvu_mbox_up_handler(struct rvu_work *mwork, int type) | static void __rvu_mbox_up_handler(struct rvu_work *mwork, int type) | ||||||
|  | @ -2376,6 +2382,8 @@ static int rvu_mbox_init(struct rvu *rvu, struct mbox_wq_info *mw, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	mutex_init(&rvu->mbox_lock); | ||||||
|  | 
 | ||||||
| 	mbox_regions = kcalloc(num, sizeof(void *), GFP_KERNEL); | 	mbox_regions = kcalloc(num, sizeof(void *), GFP_KERNEL); | ||||||
| 	if (!mbox_regions) { | 	if (!mbox_regions) { | ||||||
| 		err = -ENOMEM; | 		err = -ENOMEM; | ||||||
|  | @ -2525,10 +2533,9 @@ static void rvu_queue_work(struct mbox_wq_info *mw, int first, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static irqreturn_t rvu_mbox_intr_handler(int irq, void *rvu_irq) | static irqreturn_t rvu_mbox_pf_intr_handler(int irq, void *rvu_irq) | ||||||
| { | { | ||||||
| 	struct rvu *rvu = (struct rvu *)rvu_irq; | 	struct rvu *rvu = (struct rvu *)rvu_irq; | ||||||
| 	int vfs = rvu->vfs; |  | ||||||
| 	u64 intr; | 	u64 intr; | ||||||
| 
 | 
 | ||||||
| 	intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT); | 	intr = rvu_read64(rvu, BLKADDR_RVUM, RVU_AF_PFAF_MBOX_INT); | ||||||
|  | @ -2542,6 +2549,18 @@ static irqreturn_t rvu_mbox_intr_handler(int irq, void *rvu_irq) | ||||||
| 
 | 
 | ||||||
| 	rvu_queue_work(&rvu->afpf_wq_info, 0, rvu->hw->total_pfs, intr); | 	rvu_queue_work(&rvu->afpf_wq_info, 0, rvu->hw->total_pfs, intr); | ||||||
| 
 | 
 | ||||||
|  | 	return IRQ_HANDLED; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static irqreturn_t rvu_mbox_intr_handler(int irq, void *rvu_irq) | ||||||
|  | { | ||||||
|  | 	struct rvu *rvu = (struct rvu *)rvu_irq; | ||||||
|  | 	int vfs = rvu->vfs; | ||||||
|  | 	u64 intr; | ||||||
|  | 
 | ||||||
|  | 	/* Sync with mbox memory region */ | ||||||
|  | 	rmb(); | ||||||
|  | 
 | ||||||
| 	/* Handle VF interrupts */ | 	/* Handle VF interrupts */ | ||||||
| 	if (vfs > 64) { | 	if (vfs > 64) { | ||||||
| 		intr = rvupf_read64(rvu, RVU_PF_VFPF_MBOX_INTX(1)); | 		intr = rvupf_read64(rvu, RVU_PF_VFPF_MBOX_INTX(1)); | ||||||
|  | @ -2886,7 +2905,7 @@ static int rvu_register_interrupts(struct rvu *rvu) | ||||||
| 	/* Register mailbox interrupt handler */ | 	/* Register mailbox interrupt handler */ | ||||||
| 	sprintf(&rvu->irq_name[RVU_AF_INT_VEC_MBOX * NAME_SIZE], "RVUAF Mbox"); | 	sprintf(&rvu->irq_name[RVU_AF_INT_VEC_MBOX * NAME_SIZE], "RVUAF Mbox"); | ||||||
| 	ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_MBOX), | 	ret = request_irq(pci_irq_vector(rvu->pdev, RVU_AF_INT_VEC_MBOX), | ||||||
| 			  rvu_mbox_intr_handler, 0, | 			  rvu_mbox_pf_intr_handler, 0, | ||||||
| 			  &rvu->irq_name[RVU_AF_INT_VEC_MBOX * NAME_SIZE], rvu); | 			  &rvu->irq_name[RVU_AF_INT_VEC_MBOX * NAME_SIZE], rvu); | ||||||
| 	if (ret) { | 	if (ret) { | ||||||
| 		dev_err(rvu->dev, | 		dev_err(rvu->dev, | ||||||
|  |  | ||||||
|  | @ -591,6 +591,8 @@ struct rvu { | ||||||
| 	spinlock_t		mcs_intrq_lock; | 	spinlock_t		mcs_intrq_lock; | ||||||
| 	/* CPT interrupt lock */ | 	/* CPT interrupt lock */ | ||||||
| 	spinlock_t		cpt_intr_lock; | 	spinlock_t		cpt_intr_lock; | ||||||
|  | 
 | ||||||
|  | 	struct mutex		mbox_lock; /* Serialize mbox up and down msgs */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static inline void rvu_write64(struct rvu *rvu, u64 block, u64 offset, u64 val) | static inline void rvu_write64(struct rvu *rvu, u64 block, u64 offset, u64 val) | ||||||
|  |  | ||||||
|  | @ -232,7 +232,7 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu) | ||||||
| 	struct cgx_link_user_info *linfo; | 	struct cgx_link_user_info *linfo; | ||||||
| 	struct cgx_link_info_msg *msg; | 	struct cgx_link_info_msg *msg; | ||||||
| 	unsigned long pfmap; | 	unsigned long pfmap; | ||||||
| 	int err, pfid; | 	int pfid; | ||||||
| 
 | 
 | ||||||
| 	linfo = &event->link_uinfo; | 	linfo = &event->link_uinfo; | ||||||
| 	pfmap = cgxlmac_to_pfmap(rvu, event->cgx_id, event->lmac_id); | 	pfmap = cgxlmac_to_pfmap(rvu, event->cgx_id, event->lmac_id); | ||||||
|  | @ -255,16 +255,22 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu) | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		mutex_lock(&rvu->mbox_lock); | ||||||
|  | 
 | ||||||
| 		/* Send mbox message to PF */ | 		/* Send mbox message to PF */ | ||||||
| 		msg = otx2_mbox_alloc_msg_cgx_link_event(rvu, pfid); | 		msg = otx2_mbox_alloc_msg_cgx_link_event(rvu, pfid); | ||||||
| 		if (!msg) | 		if (!msg) { | ||||||
|  | 			mutex_unlock(&rvu->mbox_lock); | ||||||
| 			continue; | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		msg->link_info = *linfo; | 		msg->link_info = *linfo; | ||||||
| 		otx2_mbox_msg_send(&rvu->afpf_wq_info.mbox_up, pfid); | 
 | ||||||
| 		err = otx2_mbox_wait_for_rsp(&rvu->afpf_wq_info.mbox_up, pfid); | 		otx2_mbox_wait_for_zero(&rvu->afpf_wq_info.mbox_up, pfid); | ||||||
| 		if (err) | 
 | ||||||
| 			dev_warn(rvu->dev, "notification to pf %d failed\n", | 		otx2_mbox_msg_send_up(&rvu->afpf_wq_info.mbox_up, pfid); | ||||||
| 				 pfid); | 
 | ||||||
|  | 		mutex_unlock(&rvu->mbox_lock); | ||||||
| 	} while (pfmap); | 	} while (pfmap); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1592,7 +1592,7 @@ int otx2_detach_resources(struct mbox *mbox) | ||||||
| 	detach->partial = false; | 	detach->partial = false; | ||||||
| 
 | 
 | ||||||
| 	/* Send detach request to AF */ | 	/* Send detach request to AF */ | ||||||
| 	otx2_mbox_msg_send(&mbox->mbox, 0); | 	otx2_sync_mbox_msg(mbox); | ||||||
| 	mutex_unlock(&mbox->lock); | 	mutex_unlock(&mbox->lock); | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -815,7 +815,7 @@ static inline int otx2_sync_mbox_up_msg(struct mbox *mbox, int devid) | ||||||
| 
 | 
 | ||||||
| 	if (!otx2_mbox_nonempty(&mbox->mbox_up, devid)) | 	if (!otx2_mbox_nonempty(&mbox->mbox_up, devid)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	otx2_mbox_msg_send(&mbox->mbox_up, devid); | 	otx2_mbox_msg_send_up(&mbox->mbox_up, devid); | ||||||
| 	err = otx2_mbox_wait_for_rsp(&mbox->mbox_up, devid); | 	err = otx2_mbox_wait_for_rsp(&mbox->mbox_up, devid); | ||||||
| 	if (err) | 	if (err) | ||||||
| 		return err; | 		return err; | ||||||
|  |  | ||||||
|  | @ -292,8 +292,8 @@ static int otx2_pf_flr_init(struct otx2_nic *pf, int num_vfs) | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void otx2_queue_work(struct mbox *mw, struct workqueue_struct *mbox_wq, | static void otx2_queue_vf_work(struct mbox *mw, struct workqueue_struct *mbox_wq, | ||||||
| 			    int first, int mdevs, u64 intr, int type) | 			       int first, int mdevs, u64 intr) | ||||||
| { | { | ||||||
| 	struct otx2_mbox_dev *mdev; | 	struct otx2_mbox_dev *mdev; | ||||||
| 	struct otx2_mbox *mbox; | 	struct otx2_mbox *mbox; | ||||||
|  | @ -307,40 +307,26 @@ static void otx2_queue_work(struct mbox *mw, struct workqueue_struct *mbox_wq, | ||||||
| 
 | 
 | ||||||
| 		mbox = &mw->mbox; | 		mbox = &mw->mbox; | ||||||
| 		mdev = &mbox->dev[i]; | 		mdev = &mbox->dev[i]; | ||||||
| 		if (type == TYPE_PFAF) |  | ||||||
| 			otx2_sync_mbox_bbuf(mbox, i); |  | ||||||
| 		hdr = mdev->mbase + mbox->rx_start; | 		hdr = mdev->mbase + mbox->rx_start; | ||||||
| 		/* The hdr->num_msgs is set to zero immediately in the interrupt
 | 		/* The hdr->num_msgs is set to zero immediately in the interrupt
 | ||||||
| 		 * handler to  ensure that it holds a correct value next time | 		 * handler to ensure that it holds a correct value next time | ||||||
| 		 * when the interrupt handler is called. | 		 * when the interrupt handler is called. pf->mw[i].num_msgs | ||||||
| 		 * pf->mbox.num_msgs holds the data for use in pfaf_mbox_handler | 		 * holds the data for use in otx2_pfvf_mbox_handler and | ||||||
| 		 * pf>mbox.up_num_msgs holds the data for use in | 		 * pf->mw[i].up_num_msgs holds the data for use in | ||||||
| 		 * pfaf_mbox_up_handler. | 		 * otx2_pfvf_mbox_up_handler. | ||||||
| 		 */ | 		 */ | ||||||
| 		if (hdr->num_msgs) { | 		if (hdr->num_msgs) { | ||||||
| 			mw[i].num_msgs = hdr->num_msgs; | 			mw[i].num_msgs = hdr->num_msgs; | ||||||
| 			hdr->num_msgs = 0; | 			hdr->num_msgs = 0; | ||||||
| 			if (type == TYPE_PFAF) |  | ||||||
| 				memset(mbox->hwbase + mbox->rx_start, 0, |  | ||||||
| 				       ALIGN(sizeof(struct mbox_hdr), |  | ||||||
| 					     sizeof(u64))); |  | ||||||
| 
 |  | ||||||
| 			queue_work(mbox_wq, &mw[i].mbox_wrk); | 			queue_work(mbox_wq, &mw[i].mbox_wrk); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		mbox = &mw->mbox_up; | 		mbox = &mw->mbox_up; | ||||||
| 		mdev = &mbox->dev[i]; | 		mdev = &mbox->dev[i]; | ||||||
| 		if (type == TYPE_PFAF) |  | ||||||
| 			otx2_sync_mbox_bbuf(mbox, i); |  | ||||||
| 		hdr = mdev->mbase + mbox->rx_start; | 		hdr = mdev->mbase + mbox->rx_start; | ||||||
| 		if (hdr->num_msgs) { | 		if (hdr->num_msgs) { | ||||||
| 			mw[i].up_num_msgs = hdr->num_msgs; | 			mw[i].up_num_msgs = hdr->num_msgs; | ||||||
| 			hdr->num_msgs = 0; | 			hdr->num_msgs = 0; | ||||||
| 			if (type == TYPE_PFAF) |  | ||||||
| 				memset(mbox->hwbase + mbox->rx_start, 0, |  | ||||||
| 				       ALIGN(sizeof(struct mbox_hdr), |  | ||||||
| 					     sizeof(u64))); |  | ||||||
| 
 |  | ||||||
| 			queue_work(mbox_wq, &mw[i].mbox_up_wrk); | 			queue_work(mbox_wq, &mw[i].mbox_up_wrk); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -356,8 +342,10 @@ static void otx2_forward_msg_pfvf(struct otx2_mbox_dev *mdev, | ||||||
| 	/* Msgs are already copied, trigger VF's mbox irq */ | 	/* Msgs are already copied, trigger VF's mbox irq */ | ||||||
| 	smp_wmb(); | 	smp_wmb(); | ||||||
| 
 | 
 | ||||||
|  | 	otx2_mbox_wait_for_zero(pfvf_mbox, devid); | ||||||
|  | 
 | ||||||
| 	offset = pfvf_mbox->trigger | (devid << pfvf_mbox->tr_shift); | 	offset = pfvf_mbox->trigger | (devid << pfvf_mbox->tr_shift); | ||||||
| 	writeq(1, (void __iomem *)pfvf_mbox->reg_base + offset); | 	writeq(MBOX_DOWN_MSG, (void __iomem *)pfvf_mbox->reg_base + offset); | ||||||
| 
 | 
 | ||||||
| 	/* Restore VF's mbox bounce buffer region address */ | 	/* Restore VF's mbox bounce buffer region address */ | ||||||
| 	src_mdev->mbase = bbuf_base; | 	src_mdev->mbase = bbuf_base; | ||||||
|  | @ -547,7 +535,7 @@ static void otx2_pfvf_mbox_up_handler(struct work_struct *work) | ||||||
| end: | end: | ||||||
| 		offset = mbox->rx_start + msg->next_msgoff; | 		offset = mbox->rx_start + msg->next_msgoff; | ||||||
| 		if (mdev->msgs_acked == (vf_mbox->up_num_msgs - 1)) | 		if (mdev->msgs_acked == (vf_mbox->up_num_msgs - 1)) | ||||||
| 			__otx2_mbox_reset(mbox, 0); | 			__otx2_mbox_reset(mbox, vf_idx); | ||||||
| 		mdev->msgs_acked++; | 		mdev->msgs_acked++; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -564,8 +552,7 @@ static irqreturn_t otx2_pfvf_mbox_intr_handler(int irq, void *pf_irq) | ||||||
| 	if (vfs > 64) { | 	if (vfs > 64) { | ||||||
| 		intr = otx2_read64(pf, RVU_PF_VFPF_MBOX_INTX(1)); | 		intr = otx2_read64(pf, RVU_PF_VFPF_MBOX_INTX(1)); | ||||||
| 		otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(1), intr); | 		otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(1), intr); | ||||||
| 		otx2_queue_work(mbox, pf->mbox_pfvf_wq, 64, vfs, intr, | 		otx2_queue_vf_work(mbox, pf->mbox_pfvf_wq, 64, vfs, intr); | ||||||
| 				TYPE_PFVF); |  | ||||||
| 		if (intr) | 		if (intr) | ||||||
| 			trace_otx2_msg_interrupt(mbox->mbox.pdev, "VF(s) to PF", intr); | 			trace_otx2_msg_interrupt(mbox->mbox.pdev, "VF(s) to PF", intr); | ||||||
| 		vfs = 64; | 		vfs = 64; | ||||||
|  | @ -574,7 +561,7 @@ static irqreturn_t otx2_pfvf_mbox_intr_handler(int irq, void *pf_irq) | ||||||
| 	intr = otx2_read64(pf, RVU_PF_VFPF_MBOX_INTX(0)); | 	intr = otx2_read64(pf, RVU_PF_VFPF_MBOX_INTX(0)); | ||||||
| 	otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(0), intr); | 	otx2_write64(pf, RVU_PF_VFPF_MBOX_INTX(0), intr); | ||||||
| 
 | 
 | ||||||
| 	otx2_queue_work(mbox, pf->mbox_pfvf_wq, 0, vfs, intr, TYPE_PFVF); | 	otx2_queue_vf_work(mbox, pf->mbox_pfvf_wq, 0, vfs, intr); | ||||||
| 
 | 
 | ||||||
| 	if (intr) | 	if (intr) | ||||||
| 		trace_otx2_msg_interrupt(mbox->mbox.pdev, "VF(s) to PF", intr); | 		trace_otx2_msg_interrupt(mbox->mbox.pdev, "VF(s) to PF", intr); | ||||||
|  | @ -597,8 +584,9 @@ static int otx2_pfvf_mbox_init(struct otx2_nic *pf, int numvfs) | ||||||
| 	if (!pf->mbox_pfvf) | 	if (!pf->mbox_pfvf) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
| 	pf->mbox_pfvf_wq = alloc_ordered_workqueue("otx2_pfvf_mailbox", | 	pf->mbox_pfvf_wq = alloc_workqueue("otx2_pfvf_mailbox", | ||||||
| 						   WQ_HIGHPRI | WQ_MEM_RECLAIM); | 					   WQ_UNBOUND | WQ_HIGHPRI | | ||||||
|  | 					   WQ_MEM_RECLAIM, 0); | ||||||
| 	if (!pf->mbox_pfvf_wq) | 	if (!pf->mbox_pfvf_wq) | ||||||
| 		return -ENOMEM; | 		return -ENOMEM; | ||||||
| 
 | 
 | ||||||
|  | @ -821,20 +809,22 @@ static void otx2_pfaf_mbox_handler(struct work_struct *work) | ||||||
| 	struct mbox *af_mbox; | 	struct mbox *af_mbox; | ||||||
| 	struct otx2_nic *pf; | 	struct otx2_nic *pf; | ||||||
| 	int offset, id; | 	int offset, id; | ||||||
|  | 	u16 num_msgs; | ||||||
| 
 | 
 | ||||||
| 	af_mbox = container_of(work, struct mbox, mbox_wrk); | 	af_mbox = container_of(work, struct mbox, mbox_wrk); | ||||||
| 	mbox = &af_mbox->mbox; | 	mbox = &af_mbox->mbox; | ||||||
| 	mdev = &mbox->dev[0]; | 	mdev = &mbox->dev[0]; | ||||||
| 	rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | 	rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | ||||||
|  | 	num_msgs = rsp_hdr->num_msgs; | ||||||
| 
 | 
 | ||||||
| 	offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); | 	offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); | ||||||
| 	pf = af_mbox->pfvf; | 	pf = af_mbox->pfvf; | ||||||
| 
 | 
 | ||||||
| 	for (id = 0; id < af_mbox->num_msgs; id++) { | 	for (id = 0; id < num_msgs; id++) { | ||||||
| 		msg = (struct mbox_msghdr *)(mdev->mbase + offset); | 		msg = (struct mbox_msghdr *)(mdev->mbase + offset); | ||||||
| 		otx2_process_pfaf_mbox_msg(pf, msg); | 		otx2_process_pfaf_mbox_msg(pf, msg); | ||||||
| 		offset = mbox->rx_start + msg->next_msgoff; | 		offset = mbox->rx_start + msg->next_msgoff; | ||||||
| 		if (mdev->msgs_acked == (af_mbox->num_msgs - 1)) | 		if (mdev->msgs_acked == (num_msgs - 1)) | ||||||
| 			__otx2_mbox_reset(mbox, 0); | 			__otx2_mbox_reset(mbox, 0); | ||||||
| 		mdev->msgs_acked++; | 		mdev->msgs_acked++; | ||||||
| 	} | 	} | ||||||
|  | @ -945,12 +935,14 @@ static void otx2_pfaf_mbox_up_handler(struct work_struct *work) | ||||||
| 	int offset, id, devid = 0; | 	int offset, id, devid = 0; | ||||||
| 	struct mbox_hdr *rsp_hdr; | 	struct mbox_hdr *rsp_hdr; | ||||||
| 	struct mbox_msghdr *msg; | 	struct mbox_msghdr *msg; | ||||||
|  | 	u16 num_msgs; | ||||||
| 
 | 
 | ||||||
| 	rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | 	rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | ||||||
|  | 	num_msgs = rsp_hdr->num_msgs; | ||||||
| 
 | 
 | ||||||
| 	offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); | 	offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); | ||||||
| 
 | 
 | ||||||
| 	for (id = 0; id < af_mbox->up_num_msgs; id++) { | 	for (id = 0; id < num_msgs; id++) { | ||||||
| 		msg = (struct mbox_msghdr *)(mdev->mbase + offset); | 		msg = (struct mbox_msghdr *)(mdev->mbase + offset); | ||||||
| 
 | 
 | ||||||
| 		devid = msg->pcifunc & RVU_PFVF_FUNC_MASK; | 		devid = msg->pcifunc & RVU_PFVF_FUNC_MASK; | ||||||
|  | @ -959,10 +951,11 @@ static void otx2_pfaf_mbox_up_handler(struct work_struct *work) | ||||||
| 			otx2_process_mbox_msg_up(pf, msg); | 			otx2_process_mbox_msg_up(pf, msg); | ||||||
| 		offset = mbox->rx_start + msg->next_msgoff; | 		offset = mbox->rx_start + msg->next_msgoff; | ||||||
| 	} | 	} | ||||||
| 	if (devid) { | 	/* Forward to VF iff VFs are really present */ | ||||||
|  | 	if (devid && pci_num_vf(pf->pdev)) { | ||||||
| 		otx2_forward_vf_mbox_msgs(pf, &pf->mbox.mbox_up, | 		otx2_forward_vf_mbox_msgs(pf, &pf->mbox.mbox_up, | ||||||
| 					  MBOX_DIR_PFVF_UP, devid - 1, | 					  MBOX_DIR_PFVF_UP, devid - 1, | ||||||
| 					  af_mbox->up_num_msgs); | 					  num_msgs); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -972,16 +965,49 @@ static void otx2_pfaf_mbox_up_handler(struct work_struct *work) | ||||||
| static irqreturn_t otx2_pfaf_mbox_intr_handler(int irq, void *pf_irq) | static irqreturn_t otx2_pfaf_mbox_intr_handler(int irq, void *pf_irq) | ||||||
| { | { | ||||||
| 	struct otx2_nic *pf = (struct otx2_nic *)pf_irq; | 	struct otx2_nic *pf = (struct otx2_nic *)pf_irq; | ||||||
| 	struct mbox *mbox; | 	struct mbox *mw = &pf->mbox; | ||||||
|  | 	struct otx2_mbox_dev *mdev; | ||||||
|  | 	struct otx2_mbox *mbox; | ||||||
|  | 	struct mbox_hdr *hdr; | ||||||
|  | 	u64 mbox_data; | ||||||
| 
 | 
 | ||||||
| 	/* Clear the IRQ */ | 	/* Clear the IRQ */ | ||||||
| 	otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); | 	otx2_write64(pf, RVU_PF_INT, BIT_ULL(0)); | ||||||
| 
 | 
 | ||||||
| 	mbox = &pf->mbox; |  | ||||||
| 
 | 
 | ||||||
| 	trace_otx2_msg_interrupt(mbox->mbox.pdev, "AF to PF", BIT_ULL(0)); | 	mbox_data = otx2_read64(pf, RVU_PF_PFAF_MBOX0); | ||||||
| 
 | 
 | ||||||
| 	otx2_queue_work(mbox, pf->mbox_wq, 0, 1, 1, TYPE_PFAF); | 	if (mbox_data & MBOX_UP_MSG) { | ||||||
|  | 		mbox_data &= ~MBOX_UP_MSG; | ||||||
|  | 		otx2_write64(pf, RVU_PF_PFAF_MBOX0, mbox_data); | ||||||
|  | 
 | ||||||
|  | 		mbox = &mw->mbox_up; | ||||||
|  | 		mdev = &mbox->dev[0]; | ||||||
|  | 		otx2_sync_mbox_bbuf(mbox, 0); | ||||||
|  | 
 | ||||||
|  | 		hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | ||||||
|  | 		if (hdr->num_msgs) | ||||||
|  | 			queue_work(pf->mbox_wq, &mw->mbox_up_wrk); | ||||||
|  | 
 | ||||||
|  | 		trace_otx2_msg_interrupt(pf->pdev, "UP message from AF to PF", | ||||||
|  | 					 BIT_ULL(0)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (mbox_data & MBOX_DOWN_MSG) { | ||||||
|  | 		mbox_data &= ~MBOX_DOWN_MSG; | ||||||
|  | 		otx2_write64(pf, RVU_PF_PFAF_MBOX0, mbox_data); | ||||||
|  | 
 | ||||||
|  | 		mbox = &mw->mbox; | ||||||
|  | 		mdev = &mbox->dev[0]; | ||||||
|  | 		otx2_sync_mbox_bbuf(mbox, 0); | ||||||
|  | 
 | ||||||
|  | 		hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | ||||||
|  | 		if (hdr->num_msgs) | ||||||
|  | 			queue_work(pf->mbox_wq, &mw->mbox_wrk); | ||||||
|  | 
 | ||||||
|  | 		trace_otx2_msg_interrupt(pf->pdev, "DOWN reply from AF to PF", | ||||||
|  | 					 BIT_ULL(0)); | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	return IRQ_HANDLED; | 	return IRQ_HANDLED; | ||||||
| } | } | ||||||
|  | @ -3087,6 +3113,7 @@ static void otx2_vf_link_event_task(struct work_struct *work) | ||||||
| 	struct otx2_vf_config *config; | 	struct otx2_vf_config *config; | ||||||
| 	struct cgx_link_info_msg *req; | 	struct cgx_link_info_msg *req; | ||||||
| 	struct mbox_msghdr *msghdr; | 	struct mbox_msghdr *msghdr; | ||||||
|  | 	struct delayed_work *dwork; | ||||||
| 	struct otx2_nic *pf; | 	struct otx2_nic *pf; | ||||||
| 	int vf_idx; | 	int vf_idx; | ||||||
| 
 | 
 | ||||||
|  | @ -3095,10 +3122,24 @@ static void otx2_vf_link_event_task(struct work_struct *work) | ||||||
| 	vf_idx = config - config->pf->vf_configs; | 	vf_idx = config - config->pf->vf_configs; | ||||||
| 	pf = config->pf; | 	pf = config->pf; | ||||||
| 
 | 
 | ||||||
|  | 	if (config->intf_down) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	mutex_lock(&pf->mbox.lock); | ||||||
|  | 
 | ||||||
|  | 	dwork = &config->link_event_work; | ||||||
|  | 
 | ||||||
|  | 	if (!otx2_mbox_wait_for_zero(&pf->mbox_pfvf[0].mbox_up, vf_idx)) { | ||||||
|  | 		schedule_delayed_work(dwork, msecs_to_jiffies(100)); | ||||||
|  | 		mutex_unlock(&pf->mbox.lock); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	msghdr = otx2_mbox_alloc_msg_rsp(&pf->mbox_pfvf[0].mbox_up, vf_idx, | 	msghdr = otx2_mbox_alloc_msg_rsp(&pf->mbox_pfvf[0].mbox_up, vf_idx, | ||||||
| 					 sizeof(*req), sizeof(struct msg_rsp)); | 					 sizeof(*req), sizeof(struct msg_rsp)); | ||||||
| 	if (!msghdr) { | 	if (!msghdr) { | ||||||
| 		dev_err(pf->dev, "Failed to create VF%d link event\n", vf_idx); | 		dev_err(pf->dev, "Failed to create VF%d link event\n", vf_idx); | ||||||
|  | 		mutex_unlock(&pf->mbox.lock); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -3107,7 +3148,11 @@ static void otx2_vf_link_event_task(struct work_struct *work) | ||||||
| 	req->hdr.sig = OTX2_MBOX_REQ_SIG; | 	req->hdr.sig = OTX2_MBOX_REQ_SIG; | ||||||
| 	memcpy(&req->link_info, &pf->linfo, sizeof(req->link_info)); | 	memcpy(&req->link_info, &pf->linfo, sizeof(req->link_info)); | ||||||
| 
 | 
 | ||||||
|  | 	otx2_mbox_wait_for_zero(&pf->mbox_pfvf[0].mbox_up, vf_idx); | ||||||
|  | 
 | ||||||
| 	otx2_sync_mbox_up_msg(&pf->mbox_pfvf[0], vf_idx); | 	otx2_sync_mbox_up_msg(&pf->mbox_pfvf[0], vf_idx); | ||||||
|  | 
 | ||||||
|  | 	mutex_unlock(&pf->mbox.lock); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int otx2_sriov_enable(struct pci_dev *pdev, int numvfs) | static int otx2_sriov_enable(struct pci_dev *pdev, int numvfs) | ||||||
|  |  | ||||||
|  | @ -89,16 +89,20 @@ static void otx2vf_vfaf_mbox_handler(struct work_struct *work) | ||||||
| 	struct otx2_mbox *mbox; | 	struct otx2_mbox *mbox; | ||||||
| 	struct mbox *af_mbox; | 	struct mbox *af_mbox; | ||||||
| 	int offset, id; | 	int offset, id; | ||||||
|  | 	u16 num_msgs; | ||||||
| 
 | 
 | ||||||
| 	af_mbox = container_of(work, struct mbox, mbox_wrk); | 	af_mbox = container_of(work, struct mbox, mbox_wrk); | ||||||
| 	mbox = &af_mbox->mbox; | 	mbox = &af_mbox->mbox; | ||||||
| 	mdev = &mbox->dev[0]; | 	mdev = &mbox->dev[0]; | ||||||
| 	rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | 	rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | ||||||
| 	if (af_mbox->num_msgs == 0) | 	num_msgs = rsp_hdr->num_msgs; | ||||||
|  | 
 | ||||||
|  | 	if (num_msgs == 0) | ||||||
| 		return; | 		return; | ||||||
|  | 
 | ||||||
| 	offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); | 	offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); | ||||||
| 
 | 
 | ||||||
| 	for (id = 0; id < af_mbox->num_msgs; id++) { | 	for (id = 0; id < num_msgs; id++) { | ||||||
| 		msg = (struct mbox_msghdr *)(mdev->mbase + offset); | 		msg = (struct mbox_msghdr *)(mdev->mbase + offset); | ||||||
| 		otx2vf_process_vfaf_mbox_msg(af_mbox->pfvf, msg); | 		otx2vf_process_vfaf_mbox_msg(af_mbox->pfvf, msg); | ||||||
| 		offset = mbox->rx_start + msg->next_msgoff; | 		offset = mbox->rx_start + msg->next_msgoff; | ||||||
|  | @ -151,6 +155,7 @@ static void otx2vf_vfaf_mbox_up_handler(struct work_struct *work) | ||||||
| 	struct mbox *vf_mbox; | 	struct mbox *vf_mbox; | ||||||
| 	struct otx2_nic *vf; | 	struct otx2_nic *vf; | ||||||
| 	int offset, id; | 	int offset, id; | ||||||
|  | 	u16 num_msgs; | ||||||
| 
 | 
 | ||||||
| 	vf_mbox = container_of(work, struct mbox, mbox_up_wrk); | 	vf_mbox = container_of(work, struct mbox, mbox_up_wrk); | ||||||
| 	vf = vf_mbox->pfvf; | 	vf = vf_mbox->pfvf; | ||||||
|  | @ -158,12 +163,14 @@ static void otx2vf_vfaf_mbox_up_handler(struct work_struct *work) | ||||||
| 	mdev = &mbox->dev[0]; | 	mdev = &mbox->dev[0]; | ||||||
| 
 | 
 | ||||||
| 	rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | 	rsp_hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | ||||||
| 	if (vf_mbox->up_num_msgs == 0) | 	num_msgs = rsp_hdr->num_msgs; | ||||||
|  | 
 | ||||||
|  | 	if (num_msgs == 0) | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); | 	offset = mbox->rx_start + ALIGN(sizeof(*rsp_hdr), MBOX_MSG_ALIGN); | ||||||
| 
 | 
 | ||||||
| 	for (id = 0; id < vf_mbox->up_num_msgs; id++) { | 	for (id = 0; id < num_msgs; id++) { | ||||||
| 		msg = (struct mbox_msghdr *)(mdev->mbase + offset); | 		msg = (struct mbox_msghdr *)(mdev->mbase + offset); | ||||||
| 		otx2vf_process_mbox_msg_up(vf, msg); | 		otx2vf_process_mbox_msg_up(vf, msg); | ||||||
| 		offset = mbox->rx_start + msg->next_msgoff; | 		offset = mbox->rx_start + msg->next_msgoff; | ||||||
|  | @ -178,40 +185,48 @@ static irqreturn_t otx2vf_vfaf_mbox_intr_handler(int irq, void *vf_irq) | ||||||
| 	struct otx2_mbox_dev *mdev; | 	struct otx2_mbox_dev *mdev; | ||||||
| 	struct otx2_mbox *mbox; | 	struct otx2_mbox *mbox; | ||||||
| 	struct mbox_hdr *hdr; | 	struct mbox_hdr *hdr; | ||||||
|  | 	u64 mbox_data; | ||||||
| 
 | 
 | ||||||
| 	/* Clear the IRQ */ | 	/* Clear the IRQ */ | ||||||
| 	otx2_write64(vf, RVU_VF_INT, BIT_ULL(0)); | 	otx2_write64(vf, RVU_VF_INT, BIT_ULL(0)); | ||||||
| 
 | 
 | ||||||
|  | 	mbox_data = otx2_read64(vf, RVU_VF_VFPF_MBOX0); | ||||||
|  | 
 | ||||||
| 	/* Read latest mbox data */ | 	/* Read latest mbox data */ | ||||||
| 	smp_rmb(); | 	smp_rmb(); | ||||||
| 
 | 
 | ||||||
| 	/* Check for PF => VF response messages */ | 	if (mbox_data & MBOX_DOWN_MSG) { | ||||||
| 	mbox = &vf->mbox.mbox; | 		mbox_data &= ~MBOX_DOWN_MSG; | ||||||
| 	mdev = &mbox->dev[0]; | 		otx2_write64(vf, RVU_VF_VFPF_MBOX0, mbox_data); | ||||||
| 	otx2_sync_mbox_bbuf(mbox, 0); |  | ||||||
| 
 | 
 | ||||||
| 	trace_otx2_msg_interrupt(mbox->pdev, "PF to VF", BIT_ULL(0)); | 		/* Check for PF => VF response messages */ | ||||||
|  | 		mbox = &vf->mbox.mbox; | ||||||
|  | 		mdev = &mbox->dev[0]; | ||||||
|  | 		otx2_sync_mbox_bbuf(mbox, 0); | ||||||
| 
 | 
 | ||||||
| 	hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | 		hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | ||||||
| 	if (hdr->num_msgs) { | 		if (hdr->num_msgs) | ||||||
| 		vf->mbox.num_msgs = hdr->num_msgs; | 			queue_work(vf->mbox_wq, &vf->mbox.mbox_wrk); | ||||||
| 		hdr->num_msgs = 0; | 
 | ||||||
| 		memset(mbox->hwbase + mbox->rx_start, 0, | 		trace_otx2_msg_interrupt(mbox->pdev, "DOWN reply from PF to VF", | ||||||
| 		       ALIGN(sizeof(struct mbox_hdr), sizeof(u64))); | 					 BIT_ULL(0)); | ||||||
| 		queue_work(vf->mbox_wq, &vf->mbox.mbox_wrk); |  | ||||||
| 	} | 	} | ||||||
| 	/* Check for PF => VF notification messages */ |  | ||||||
| 	mbox = &vf->mbox.mbox_up; |  | ||||||
| 	mdev = &mbox->dev[0]; |  | ||||||
| 	otx2_sync_mbox_bbuf(mbox, 0); |  | ||||||
| 
 | 
 | ||||||
| 	hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | 	if (mbox_data & MBOX_UP_MSG) { | ||||||
| 	if (hdr->num_msgs) { | 		mbox_data &= ~MBOX_UP_MSG; | ||||||
| 		vf->mbox.up_num_msgs = hdr->num_msgs; | 		otx2_write64(vf, RVU_VF_VFPF_MBOX0, mbox_data); | ||||||
| 		hdr->num_msgs = 0; | 
 | ||||||
| 		memset(mbox->hwbase + mbox->rx_start, 0, | 		/* Check for PF => VF notification messages */ | ||||||
| 		       ALIGN(sizeof(struct mbox_hdr), sizeof(u64))); | 		mbox = &vf->mbox.mbox_up; | ||||||
| 		queue_work(vf->mbox_wq, &vf->mbox.mbox_up_wrk); | 		mdev = &mbox->dev[0]; | ||||||
|  | 		otx2_sync_mbox_bbuf(mbox, 0); | ||||||
|  | 
 | ||||||
|  | 		hdr = (struct mbox_hdr *)(mdev->mbase + mbox->rx_start); | ||||||
|  | 		if (hdr->num_msgs) | ||||||
|  | 			queue_work(vf->mbox_wq, &vf->mbox.mbox_up_wrk); | ||||||
|  | 
 | ||||||
|  | 		trace_otx2_msg_interrupt(mbox->pdev, "UP message from PF to VF", | ||||||
|  | 					 BIT_ULL(0)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return IRQ_HANDLED; | 	return IRQ_HANDLED; | ||||||
|  | @ -760,8 +775,8 @@ static void otx2vf_remove(struct pci_dev *pdev) | ||||||
| 	otx2_mcam_flow_del(vf); | 	otx2_mcam_flow_del(vf); | ||||||
| 	otx2_shutdown_tc(vf); | 	otx2_shutdown_tc(vf); | ||||||
| 	otx2_shutdown_qos(vf); | 	otx2_shutdown_qos(vf); | ||||||
| 	otx2vf_disable_mbox_intr(vf); |  | ||||||
| 	otx2_detach_resources(&vf->mbox); | 	otx2_detach_resources(&vf->mbox); | ||||||
|  | 	otx2vf_disable_mbox_intr(vf); | ||||||
| 	free_percpu(vf->hw.lmt_info); | 	free_percpu(vf->hw.lmt_info); | ||||||
| 	if (test_bit(CN10K_LMTST, &vf->hw.cap_flag)) | 	if (test_bit(CN10K_LMTST, &vf->hw.cap_flag)) | ||||||
| 		qmem_free(vf->dev, vf->dync_lmt); | 		qmem_free(vf->dev, vf->dync_lmt); | ||||||
|  |  | ||||||
|  | @ -677,8 +677,7 @@ static int mtk_mac_finish(struct phylink_config *config, unsigned int mode, | ||||||
| 	mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); | 	mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); | ||||||
| 	mcr_new = mcr_cur; | 	mcr_new = mcr_cur; | ||||||
| 	mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | | 	mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | | ||||||
| 		   MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK | | 		   MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_RX_FIFO_CLR_DIS; | ||||||
| 		   MAC_MCR_RX_FIFO_CLR_DIS; |  | ||||||
| 
 | 
 | ||||||
| 	/* Only update control register when needed! */ | 	/* Only update control register when needed! */ | ||||||
| 	if (mcr_new != mcr_cur) | 	if (mcr_new != mcr_cur) | ||||||
|  | @ -694,7 +693,7 @@ static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, | ||||||
| 					   phylink_config); | 					   phylink_config); | ||||||
| 	u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); | 	u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); | ||||||
| 
 | 
 | ||||||
| 	mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN); | 	mcr &= ~(MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK); | ||||||
| 	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); | 	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -803,7 +802,7 @@ static void mtk_mac_link_up(struct phylink_config *config, | ||||||
| 	if (rx_pause) | 	if (rx_pause) | ||||||
| 		mcr |= MAC_MCR_FORCE_RX_FC; | 		mcr |= MAC_MCR_FORCE_RX_FC; | ||||||
| 
 | 
 | ||||||
| 	mcr |= MAC_MCR_TX_EN | MAC_MCR_RX_EN; | 	mcr |= MAC_MCR_TX_EN | MAC_MCR_RX_EN | MAC_MCR_FORCE_LINK; | ||||||
| 	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); | 	mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -994,7 +994,7 @@ void mtk_ppe_start(struct mtk_ppe *ppe) | ||||||
| 			 MTK_PPE_KEEPALIVE_DISABLE) | | 			 MTK_PPE_KEEPALIVE_DISABLE) | | ||||||
| 	      FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) | | 	      FIELD_PREP(MTK_PPE_TB_CFG_HASH_MODE, 1) | | ||||||
| 	      FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE, | 	      FIELD_PREP(MTK_PPE_TB_CFG_SCAN_MODE, | ||||||
| 			 MTK_PPE_SCAN_MODE_KEEPALIVE_AGE) | | 			 MTK_PPE_SCAN_MODE_CHECK_AGE) | | ||||||
| 	      FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM, | 	      FIELD_PREP(MTK_PPE_TB_CFG_ENTRY_NUM, | ||||||
| 			 MTK_PPE_ENTRIES_SHIFT); | 			 MTK_PPE_ENTRIES_SHIFT); | ||||||
| 	if (mtk_is_netsys_v2_or_greater(ppe->eth)) | 	if (mtk_is_netsys_v2_or_greater(ppe->eth)) | ||||||
|  | @ -1090,17 +1090,21 @@ int mtk_ppe_stop(struct mtk_ppe *ppe) | ||||||
| 
 | 
 | ||||||
| 	mtk_ppe_cache_enable(ppe, false); | 	mtk_ppe_cache_enable(ppe, false); | ||||||
| 
 | 
 | ||||||
| 	/* disable offload engine */ |  | ||||||
| 	ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN); |  | ||||||
| 	ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0); |  | ||||||
| 
 |  | ||||||
| 	/* disable aging */ | 	/* disable aging */ | ||||||
| 	val = MTK_PPE_TB_CFG_AGE_NON_L4 | | 	val = MTK_PPE_TB_CFG_AGE_NON_L4 | | ||||||
| 	      MTK_PPE_TB_CFG_AGE_UNBIND | | 	      MTK_PPE_TB_CFG_AGE_UNBIND | | ||||||
| 	      MTK_PPE_TB_CFG_AGE_TCP | | 	      MTK_PPE_TB_CFG_AGE_TCP | | ||||||
| 	      MTK_PPE_TB_CFG_AGE_UDP | | 	      MTK_PPE_TB_CFG_AGE_UDP | | ||||||
| 	      MTK_PPE_TB_CFG_AGE_TCP_FIN; | 	      MTK_PPE_TB_CFG_AGE_TCP_FIN | | ||||||
|  | 		  MTK_PPE_TB_CFG_SCAN_MODE; | ||||||
| 	ppe_clear(ppe, MTK_PPE_TB_CFG, val); | 	ppe_clear(ppe, MTK_PPE_TB_CFG, val); | ||||||
| 
 | 
 | ||||||
| 	return mtk_ppe_wait_busy(ppe); | 	if (mtk_ppe_wait_busy(ppe)) | ||||||
|  | 		return -ETIMEDOUT; | ||||||
|  | 
 | ||||||
|  | 	/* disable offload engine */ | ||||||
|  | 	ppe_clear(ppe, MTK_PPE_GLO_CFG, MTK_PPE_GLO_CFG_EN); | ||||||
|  | 	ppe_w32(ppe, MTK_PPE_FLOW_CFG, 0); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -571,7 +571,7 @@ static int txgbe_clock_register(struct txgbe *txgbe) | ||||||
| 	char clk_name[32]; | 	char clk_name[32]; | ||||||
| 	struct clk *clk; | 	struct clk *clk; | ||||||
| 
 | 
 | ||||||
| 	snprintf(clk_name, sizeof(clk_name), "i2c_designware.%d", | 	snprintf(clk_name, sizeof(clk_name), "i2c_dw.%d", | ||||||
| 		 pci_dev_id(pdev)); | 		 pci_dev_id(pdev)); | ||||||
| 
 | 
 | ||||||
| 	clk = clk_register_fixed_rate(NULL, clk_name, NULL, 0, 156250000); | 	clk = clk_register_fixed_rate(NULL, clk_name, NULL, 0, 156250000); | ||||||
|  |  | ||||||
|  | @ -2831,8 +2831,8 @@ EXPORT_SYMBOL(genphy_resume); | ||||||
| int genphy_loopback(struct phy_device *phydev, bool enable) | int genphy_loopback(struct phy_device *phydev, bool enable) | ||||||
| { | { | ||||||
| 	if (enable) { | 	if (enable) { | ||||||
| 		u16 val, ctl = BMCR_LOOPBACK; | 		u16 ctl = BMCR_LOOPBACK; | ||||||
| 		int ret; | 		int ret, val; | ||||||
| 
 | 
 | ||||||
| 		ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); | 		ctl |= mii_bmcr_encode_fixed(phydev->speed, phydev->duplex); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1464,8 +1464,6 @@ static netdev_features_t veth_fix_features(struct net_device *dev, | ||||||
| 		if (peer_priv->_xdp_prog) | 		if (peer_priv->_xdp_prog) | ||||||
| 			features &= ~NETIF_F_GSO_SOFTWARE; | 			features &= ~NETIF_F_GSO_SOFTWARE; | ||||||
| 	} | 	} | ||||||
| 	if (priv->_xdp_prog) |  | ||||||
| 		features |= NETIF_F_GRO; |  | ||||||
| 
 | 
 | ||||||
| 	return features; | 	return features; | ||||||
| } | } | ||||||
|  | @ -1569,14 +1567,6 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (!old_prog) { | 		if (!old_prog) { | ||||||
| 			if (!veth_gro_requested(dev)) { |  | ||||||
| 				/* user-space did not require GRO, but adding
 |  | ||||||
| 				 * XDP is supposed to get GRO working |  | ||||||
| 				 */ |  | ||||||
| 				dev->features |= NETIF_F_GRO; |  | ||||||
| 				netdev_features_change(dev); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			peer->hw_features &= ~NETIF_F_GSO_SOFTWARE; | 			peer->hw_features &= ~NETIF_F_GSO_SOFTWARE; | ||||||
| 			peer->max_mtu = max_mtu; | 			peer->max_mtu = max_mtu; | ||||||
| 		} | 		} | ||||||
|  | @ -1592,14 +1582,6 @@ static int veth_xdp_set(struct net_device *dev, struct bpf_prog *prog, | ||||||
| 			if (dev->flags & IFF_UP) | 			if (dev->flags & IFF_UP) | ||||||
| 				veth_disable_xdp(dev); | 				veth_disable_xdp(dev); | ||||||
| 
 | 
 | ||||||
| 			/* if user-space did not require GRO, since adding XDP
 |  | ||||||
| 			 * enabled it, clear it now |  | ||||||
| 			 */ |  | ||||||
| 			if (!veth_gro_requested(dev)) { |  | ||||||
| 				dev->features &= ~NETIF_F_GRO; |  | ||||||
| 				netdev_features_change(dev); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if (peer) { | 			if (peer) { | ||||||
| 				peer->hw_features |= NETIF_F_GSO_SOFTWARE; | 				peer->hw_features |= NETIF_F_GSO_SOFTWARE; | ||||||
| 				peer->max_mtu = ETH_MAX_MTU; | 				peer->max_mtu = ETH_MAX_MTU; | ||||||
|  |  | ||||||
|  | @ -382,12 +382,12 @@ vmxnet3_process_xdp(struct vmxnet3_adapter *adapter, | ||||||
| 	page = rbi->page; | 	page = rbi->page; | ||||||
| 	dma_sync_single_for_cpu(&adapter->pdev->dev, | 	dma_sync_single_for_cpu(&adapter->pdev->dev, | ||||||
| 				page_pool_get_dma_addr(page) + | 				page_pool_get_dma_addr(page) + | ||||||
| 				rq->page_pool->p.offset, rcd->len, | 				rq->page_pool->p.offset, rbi->len, | ||||||
| 				page_pool_get_dma_dir(rq->page_pool)); | 				page_pool_get_dma_dir(rq->page_pool)); | ||||||
| 
 | 
 | ||||||
| 	xdp_init_buff(&xdp, rbi->len, &rq->xdp_rxq); | 	xdp_init_buff(&xdp, PAGE_SIZE, &rq->xdp_rxq); | ||||||
| 	xdp_prepare_buff(&xdp, page_address(page), rq->page_pool->p.offset, | 	xdp_prepare_buff(&xdp, page_address(page), rq->page_pool->p.offset, | ||||||
| 			 rcd->len, false); | 			 rbi->len, false); | ||||||
| 	xdp_buff_clear_frags_flag(&xdp); | 	xdp_buff_clear_frags_flag(&xdp); | ||||||
| 
 | 
 | ||||||
| 	xdp_prog = rcu_dereference(rq->adapter->xdp_bpf_prog); | 	xdp_prog = rcu_dereference(rq->adapter->xdp_bpf_prog); | ||||||
|  |  | ||||||
|  | @ -780,7 +780,7 @@ static const struct of_device_id qmc_hdlc_id_table[] = { | ||||||
| 	{ .compatible = "fsl,qmc-hdlc" }, | 	{ .compatible = "fsl,qmc-hdlc" }, | ||||||
| 	{} /* sentinel */ | 	{} /* sentinel */ | ||||||
| }; | }; | ||||||
| MODULE_DEVICE_TABLE(of, qmc_hdlc_driver); | MODULE_DEVICE_TABLE(of, qmc_hdlc_id_table); | ||||||
| 
 | 
 | ||||||
| static struct platform_driver qmc_hdlc_driver = { | static struct platform_driver qmc_hdlc_driver = { | ||||||
| 	.driver = { | 	.driver = { | ||||||
|  |  | ||||||
|  | @ -237,7 +237,6 @@ static const struct net_device_ops netdev_ops = { | ||||||
| 	.ndo_open		= wg_open, | 	.ndo_open		= wg_open, | ||||||
| 	.ndo_stop		= wg_stop, | 	.ndo_stop		= wg_stop, | ||||||
| 	.ndo_start_xmit		= wg_xmit, | 	.ndo_start_xmit		= wg_xmit, | ||||||
| 	.ndo_get_stats64	= dev_get_tstats64 |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static void wg_destruct(struct net_device *dev) | static void wg_destruct(struct net_device *dev) | ||||||
|  | @ -262,7 +261,6 @@ static void wg_destruct(struct net_device *dev) | ||||||
| 	rcu_barrier(); /* Wait for all the peers to be actually freed. */ | 	rcu_barrier(); /* Wait for all the peers to be actually freed. */ | ||||||
| 	wg_ratelimiter_uninit(); | 	wg_ratelimiter_uninit(); | ||||||
| 	memzero_explicit(&wg->static_identity, sizeof(wg->static_identity)); | 	memzero_explicit(&wg->static_identity, sizeof(wg->static_identity)); | ||||||
| 	free_percpu(dev->tstats); |  | ||||||
| 	kvfree(wg->index_hashtable); | 	kvfree(wg->index_hashtable); | ||||||
| 	kvfree(wg->peer_hashtable); | 	kvfree(wg->peer_hashtable); | ||||||
| 	mutex_unlock(&wg->device_update_lock); | 	mutex_unlock(&wg->device_update_lock); | ||||||
|  | @ -297,6 +295,7 @@ static void wg_setup(struct net_device *dev) | ||||||
| 	dev->hw_enc_features |= WG_NETDEV_FEATURES; | 	dev->hw_enc_features |= WG_NETDEV_FEATURES; | ||||||
| 	dev->mtu = ETH_DATA_LEN - overhead; | 	dev->mtu = ETH_DATA_LEN - overhead; | ||||||
| 	dev->max_mtu = round_down(INT_MAX, MESSAGE_PADDING_MULTIPLE) - overhead; | 	dev->max_mtu = round_down(INT_MAX, MESSAGE_PADDING_MULTIPLE) - overhead; | ||||||
|  | 	dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; | ||||||
| 
 | 
 | ||||||
| 	SET_NETDEV_DEVTYPE(dev, &device_type); | 	SET_NETDEV_DEVTYPE(dev, &device_type); | ||||||
| 
 | 
 | ||||||
|  | @ -331,14 +330,10 @@ static int wg_newlink(struct net *src_net, struct net_device *dev, | ||||||
| 	if (!wg->index_hashtable) | 	if (!wg->index_hashtable) | ||||||
| 		goto err_free_peer_hashtable; | 		goto err_free_peer_hashtable; | ||||||
| 
 | 
 | ||||||
| 	dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); |  | ||||||
| 	if (!dev->tstats) |  | ||||||
| 		goto err_free_index_hashtable; |  | ||||||
| 
 |  | ||||||
| 	wg->handshake_receive_wq = alloc_workqueue("wg-kex-%s", | 	wg->handshake_receive_wq = alloc_workqueue("wg-kex-%s", | ||||||
| 			WQ_CPU_INTENSIVE | WQ_FREEZABLE, 0, dev->name); | 			WQ_CPU_INTENSIVE | WQ_FREEZABLE, 0, dev->name); | ||||||
| 	if (!wg->handshake_receive_wq) | 	if (!wg->handshake_receive_wq) | ||||||
| 		goto err_free_tstats; | 		goto err_free_index_hashtable; | ||||||
| 
 | 
 | ||||||
| 	wg->handshake_send_wq = alloc_workqueue("wg-kex-%s", | 	wg->handshake_send_wq = alloc_workqueue("wg-kex-%s", | ||||||
| 			WQ_UNBOUND | WQ_FREEZABLE, 0, dev->name); | 			WQ_UNBOUND | WQ_FREEZABLE, 0, dev->name); | ||||||
|  | @ -397,8 +392,6 @@ static int wg_newlink(struct net *src_net, struct net_device *dev, | ||||||
| 	destroy_workqueue(wg->handshake_send_wq); | 	destroy_workqueue(wg->handshake_send_wq); | ||||||
| err_destroy_handshake_receive: | err_destroy_handshake_receive: | ||||||
| 	destroy_workqueue(wg->handshake_receive_wq); | 	destroy_workqueue(wg->handshake_receive_wq); | ||||||
| err_free_tstats: |  | ||||||
| 	free_percpu(dev->tstats); |  | ||||||
| err_free_index_hashtable: | err_free_index_hashtable: | ||||||
| 	kvfree(wg->index_hashtable); | 	kvfree(wg->index_hashtable); | ||||||
| err_free_peer_hashtable: | err_free_peer_hashtable: | ||||||
|  |  | ||||||
|  | @ -164,8 +164,8 @@ get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx) | ||||||
| 	if (!allowedips_node) | 	if (!allowedips_node) | ||||||
| 		goto no_allowedips; | 		goto no_allowedips; | ||||||
| 	if (!ctx->allowedips_seq) | 	if (!ctx->allowedips_seq) | ||||||
| 		ctx->allowedips_seq = peer->device->peer_allowedips.seq; | 		ctx->allowedips_seq = ctx->wg->peer_allowedips.seq; | ||||||
| 	else if (ctx->allowedips_seq != peer->device->peer_allowedips.seq) | 	else if (ctx->allowedips_seq != ctx->wg->peer_allowedips.seq) | ||||||
| 		goto no_allowedips; | 		goto no_allowedips; | ||||||
| 
 | 
 | ||||||
| 	allowedips_nest = nla_nest_start(skb, WGPEER_A_ALLOWEDIPS); | 	allowedips_nest = nla_nest_start(skb, WGPEER_A_ALLOWEDIPS); | ||||||
|  | @ -255,17 +255,17 @@ static int wg_get_device_dump(struct sk_buff *skb, struct netlink_callback *cb) | ||||||
| 	if (!peers_nest) | 	if (!peers_nest) | ||||||
| 		goto out; | 		goto out; | ||||||
| 	ret = 0; | 	ret = 0; | ||||||
| 	/* If the last cursor was removed via list_del_init in peer_remove, then
 | 	lockdep_assert_held(&wg->device_update_lock); | ||||||
|  | 	/* If the last cursor was removed in peer_remove or peer_remove_all, then
 | ||||||
| 	 * we just treat this the same as there being no more peers left. The | 	 * we just treat this the same as there being no more peers left. The | ||||||
| 	 * reason is that seq_nr should indicate to userspace that this isn't a | 	 * reason is that seq_nr should indicate to userspace that this isn't a | ||||||
| 	 * coherent dump anyway, so they'll try again. | 	 * coherent dump anyway, so they'll try again. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (list_empty(&wg->peer_list) || | 	if (list_empty(&wg->peer_list) || | ||||||
| 	    (ctx->next_peer && list_empty(&ctx->next_peer->peer_list))) { | 	    (ctx->next_peer && ctx->next_peer->is_dead)) { | ||||||
| 		nla_nest_cancel(skb, peers_nest); | 		nla_nest_cancel(skb, peers_nest); | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
| 	lockdep_assert_held(&wg->device_update_lock); |  | ||||||
| 	peer = list_prepare_entry(ctx->next_peer, &wg->peer_list, peer_list); | 	peer = list_prepare_entry(ctx->next_peer, &wg->peer_list, peer_list); | ||||||
| 	list_for_each_entry_continue(peer, &wg->peer_list, peer_list) { | 	list_for_each_entry_continue(peer, &wg->peer_list, peer_list) { | ||||||
| 		if (get_peer(peer, skb, ctx)) { | 		if (get_peer(peer, skb, ctx)) { | ||||||
|  |  | ||||||
|  | @ -251,7 +251,7 @@ static bool decrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair) | ||||||
| 
 | 
 | ||||||
| 	if (unlikely(!READ_ONCE(keypair->receiving.is_valid) || | 	if (unlikely(!READ_ONCE(keypair->receiving.is_valid) || | ||||||
| 		  wg_birthdate_has_expired(keypair->receiving.birthdate, REJECT_AFTER_TIME) || | 		  wg_birthdate_has_expired(keypair->receiving.birthdate, REJECT_AFTER_TIME) || | ||||||
| 		  keypair->receiving_counter.counter >= REJECT_AFTER_MESSAGES)) { | 		  READ_ONCE(keypair->receiving_counter.counter) >= REJECT_AFTER_MESSAGES)) { | ||||||
| 		WRITE_ONCE(keypair->receiving.is_valid, false); | 		WRITE_ONCE(keypair->receiving.is_valid, false); | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
|  | @ -318,7 +318,7 @@ static bool counter_validate(struct noise_replay_counter *counter, u64 their_cou | ||||||
| 		for (i = 1; i <= top; ++i) | 		for (i = 1; i <= top; ++i) | ||||||
| 			counter->backtrack[(i + index_current) & | 			counter->backtrack[(i + index_current) & | ||||||
| 				((COUNTER_BITS_TOTAL / BITS_PER_LONG) - 1)] = 0; | 				((COUNTER_BITS_TOTAL / BITS_PER_LONG) - 1)] = 0; | ||||||
| 		counter->counter = their_counter; | 		WRITE_ONCE(counter->counter, their_counter); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	index &= (COUNTER_BITS_TOTAL / BITS_PER_LONG) - 1; | 	index &= (COUNTER_BITS_TOTAL / BITS_PER_LONG) - 1; | ||||||
|  | @ -463,7 +463,7 @@ int wg_packet_rx_poll(struct napi_struct *napi, int budget) | ||||||
| 			net_dbg_ratelimited("%s: Packet has invalid nonce %llu (max %llu)\n", | 			net_dbg_ratelimited("%s: Packet has invalid nonce %llu (max %llu)\n", | ||||||
| 					    peer->device->dev->name, | 					    peer->device->dev->name, | ||||||
| 					    PACKET_CB(skb)->nonce, | 					    PACKET_CB(skb)->nonce, | ||||||
| 					    keypair->receiving_counter.counter); | 					    READ_ONCE(keypair->receiving_counter.counter)); | ||||||
| 			goto next; | 			goto next; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -991,7 +991,7 @@ struct qman_portal { | ||||||
| 	/* linked-list of CSCN handlers. */ | 	/* linked-list of CSCN handlers. */ | ||||||
| 	struct list_head cgr_cbs; | 	struct list_head cgr_cbs; | ||||||
| 	/* list lock */ | 	/* list lock */ | ||||||
| 	spinlock_t cgr_lock; | 	raw_spinlock_t cgr_lock; | ||||||
| 	struct work_struct congestion_work; | 	struct work_struct congestion_work; | ||||||
| 	struct work_struct mr_work; | 	struct work_struct mr_work; | ||||||
| 	char irqname[MAX_IRQNAME]; | 	char irqname[MAX_IRQNAME]; | ||||||
|  | @ -1281,7 +1281,7 @@ static int qman_create_portal(struct qman_portal *portal, | ||||||
| 		/* if the given mask is NULL, assume all CGRs can be seen */ | 		/* if the given mask is NULL, assume all CGRs can be seen */ | ||||||
| 		qman_cgrs_fill(&portal->cgrs[0]); | 		qman_cgrs_fill(&portal->cgrs[0]); | ||||||
| 	INIT_LIST_HEAD(&portal->cgr_cbs); | 	INIT_LIST_HEAD(&portal->cgr_cbs); | ||||||
| 	spin_lock_init(&portal->cgr_lock); | 	raw_spin_lock_init(&portal->cgr_lock); | ||||||
| 	INIT_WORK(&portal->congestion_work, qm_congestion_task); | 	INIT_WORK(&portal->congestion_work, qm_congestion_task); | ||||||
| 	INIT_WORK(&portal->mr_work, qm_mr_process_task); | 	INIT_WORK(&portal->mr_work, qm_mr_process_task); | ||||||
| 	portal->bits = 0; | 	portal->bits = 0; | ||||||
|  | @ -1456,11 +1456,14 @@ static void qm_congestion_task(struct work_struct *work) | ||||||
| 	union qm_mc_result *mcr; | 	union qm_mc_result *mcr; | ||||||
| 	struct qman_cgr *cgr; | 	struct qman_cgr *cgr; | ||||||
| 
 | 
 | ||||||
| 	spin_lock(&p->cgr_lock); | 	/*
 | ||||||
|  | 	 * FIXME: QM_MCR_TIMEOUT is 10ms, which is too long for a raw spinlock! | ||||||
|  | 	 */ | ||||||
|  | 	raw_spin_lock_irq(&p->cgr_lock); | ||||||
| 	qm_mc_start(&p->p); | 	qm_mc_start(&p->p); | ||||||
| 	qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCONGESTION); | 	qm_mc_commit(&p->p, QM_MCC_VERB_QUERYCONGESTION); | ||||||
| 	if (!qm_mc_result_timeout(&p->p, &mcr)) { | 	if (!qm_mc_result_timeout(&p->p, &mcr)) { | ||||||
| 		spin_unlock(&p->cgr_lock); | 		raw_spin_unlock_irq(&p->cgr_lock); | ||||||
| 		dev_crit(p->config->dev, "QUERYCONGESTION timeout\n"); | 		dev_crit(p->config->dev, "QUERYCONGESTION timeout\n"); | ||||||
| 		qman_p_irqsource_add(p, QM_PIRQ_CSCI); | 		qman_p_irqsource_add(p, QM_PIRQ_CSCI); | ||||||
| 		return; | 		return; | ||||||
|  | @ -1476,7 +1479,7 @@ static void qm_congestion_task(struct work_struct *work) | ||||||
| 	list_for_each_entry(cgr, &p->cgr_cbs, node) | 	list_for_each_entry(cgr, &p->cgr_cbs, node) | ||||||
| 		if (cgr->cb && qman_cgrs_get(&c, cgr->cgrid)) | 		if (cgr->cb && qman_cgrs_get(&c, cgr->cgrid)) | ||||||
| 			cgr->cb(p, cgr, qman_cgrs_get(&rr, cgr->cgrid)); | 			cgr->cb(p, cgr, qman_cgrs_get(&rr, cgr->cgrid)); | ||||||
| 	spin_unlock(&p->cgr_lock); | 	raw_spin_unlock_irq(&p->cgr_lock); | ||||||
| 	qman_p_irqsource_add(p, QM_PIRQ_CSCI); | 	qman_p_irqsource_add(p, QM_PIRQ_CSCI); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2440,7 +2443,7 @@ int qman_create_cgr(struct qman_cgr *cgr, u32 flags, | ||||||
| 	preempt_enable(); | 	preempt_enable(); | ||||||
| 
 | 
 | ||||||
| 	cgr->chan = p->config->channel; | 	cgr->chan = p->config->channel; | ||||||
| 	spin_lock(&p->cgr_lock); | 	raw_spin_lock_irq(&p->cgr_lock); | ||||||
| 
 | 
 | ||||||
| 	if (opts) { | 	if (opts) { | ||||||
| 		struct qm_mcc_initcgr local_opts = *opts; | 		struct qm_mcc_initcgr local_opts = *opts; | ||||||
|  | @ -2477,7 +2480,7 @@ int qman_create_cgr(struct qman_cgr *cgr, u32 flags, | ||||||
| 	    qman_cgrs_get(&p->cgrs[1], cgr->cgrid)) | 	    qman_cgrs_get(&p->cgrs[1], cgr->cgrid)) | ||||||
| 		cgr->cb(p, cgr, 1); | 		cgr->cb(p, cgr, 1); | ||||||
| out: | out: | ||||||
| 	spin_unlock(&p->cgr_lock); | 	raw_spin_unlock_irq(&p->cgr_lock); | ||||||
| 	put_affine_portal(); | 	put_affine_portal(); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  | @ -2512,7 +2515,7 @@ int qman_delete_cgr(struct qman_cgr *cgr) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr)); | 	memset(&local_opts, 0, sizeof(struct qm_mcc_initcgr)); | ||||||
| 	spin_lock_irqsave(&p->cgr_lock, irqflags); | 	raw_spin_lock_irqsave(&p->cgr_lock, irqflags); | ||||||
| 	list_del(&cgr->node); | 	list_del(&cgr->node); | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * If there are no other CGR objects for this CGRID in the list, | 	 * If there are no other CGR objects for this CGRID in the list, | ||||||
|  | @ -2537,7 +2540,7 @@ int qman_delete_cgr(struct qman_cgr *cgr) | ||||||
| 		/* add back to the list */ | 		/* add back to the list */ | ||||||
| 		list_add(&cgr->node, &p->cgr_cbs); | 		list_add(&cgr->node, &p->cgr_cbs); | ||||||
| release_lock: | release_lock: | ||||||
| 	spin_unlock_irqrestore(&p->cgr_lock, irqflags); | 	raw_spin_unlock_irqrestore(&p->cgr_lock, irqflags); | ||||||
| 	put_affine_portal(); | 	put_affine_portal(); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  | @ -2577,9 +2580,9 @@ static int qman_update_cgr(struct qman_cgr *cgr, struct qm_mcc_initcgr *opts) | ||||||
| 	if (!p) | 	if (!p) | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 	spin_lock_irqsave(&p->cgr_lock, irqflags); | 	raw_spin_lock_irqsave(&p->cgr_lock, irqflags); | ||||||
| 	ret = qm_modify_cgr(cgr, 0, opts); | 	ret = qm_modify_cgr(cgr, 0, opts); | ||||||
| 	spin_unlock_irqrestore(&p->cgr_lock, irqflags); | 	raw_spin_unlock_irqrestore(&p->cgr_lock, irqflags); | ||||||
| 	put_affine_portal(); | 	put_affine_portal(); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -522,17 +522,18 @@ static inline void bitmap_replace(unsigned long *dst, | ||||||
|  * |  * | ||||||
|  * (Bits 0, 1, 2, 3, 4, 5 are copied to the bits 0, 1, 4, 8, 9, 12) |  * (Bits 0, 1, 2, 3, 4, 5 are copied to the bits 0, 1, 4, 8, 9, 12) | ||||||
|  * |  * | ||||||
|  * A more 'visual' description of the operation: |  * A more 'visual' description of the operation:: | ||||||
|  * src:  0000000001011010 |  * | ||||||
|  *                 |||||| |  *	src:  0000000001011010 | ||||||
|  *          +------+||||| |  *	                |||||| | ||||||
|  *          |  +----+|||| |  *	         +------+||||| | ||||||
|  *          |  |+----+||| |  *	         |  +----+|||| | ||||||
|  *          |  ||   +-+|| |  *	         |  |+----+||| | ||||||
|  *          |  ||   |  || |  *	         |  ||   +-+|| | ||||||
|  * mask: ...v..vv...v..vv |  *	         |  ||   |  || | ||||||
|  *       ...0..11...0..10 |  *	mask: ...v..vv...v..vv | ||||||
|  * dst:  0000001100000010 |  *	      ...0..11...0..10 | ||||||
|  |  *	dst:  0000001100000010 | ||||||
|  * |  * | ||||||
|  * A relationship exists between bitmap_scatter() and bitmap_gather(). |  * A relationship exists between bitmap_scatter() and bitmap_gather(). | ||||||
|  * bitmap_gather() can be seen as the 'reverse' bitmap_scatter() operation. |  * bitmap_gather() can be seen as the 'reverse' bitmap_scatter() operation. | ||||||
|  | @ -568,16 +569,17 @@ static inline void bitmap_scatter(unsigned long *dst, const unsigned long *src, | ||||||
|  * |  * | ||||||
|  * (Bits 0, 1, 4, 8, 9, 12 are copied to the bits 0, 1, 2, 3, 4, 5) |  * (Bits 0, 1, 4, 8, 9, 12 are copied to the bits 0, 1, 2, 3, 4, 5) | ||||||
|  * |  * | ||||||
|  * A more 'visual' description of the operation: |  * A more 'visual' description of the operation:: | ||||||
|  * mask: ...v..vv...v..vv |  * | ||||||
|  * src:  0000001100000010 |  *	mask: ...v..vv...v..vv | ||||||
|  *          ^  ^^   ^   0 |  *	src:  0000001100000010 | ||||||
|  *          |  ||   |  10 |  *	         ^  ^^   ^   0 | ||||||
|  *          |  ||   > 010 |  *	         |  ||   |  10 | ||||||
|  *          |  |+--> 1010 |  *	         |  ||   > 010 | ||||||
|  *          |  +--> 11010 |  *	         |  |+--> 1010 | ||||||
|  *          +----> 011010 |  *	         |  +--> 11010 | ||||||
|  * dst:  0000000000011010 |  *	         +----> 011010 | ||||||
|  |  *	dst:  0000000000011010 | ||||||
|  * |  * | ||||||
|  * A relationship exists between bitmap_gather() and bitmap_scatter(). See |  * A relationship exists between bitmap_gather() and bitmap_scatter(). See | ||||||
|  * bitmap_scatter() for the bitmap scatter detailed operations. |  * bitmap_scatter() for the bitmap scatter detailed operations. | ||||||
|  |  | ||||||
|  | @ -2072,6 +2072,7 @@ struct net_device { | ||||||
| 		struct pcpu_sw_netstats __percpu	*tstats; | 		struct pcpu_sw_netstats __percpu	*tstats; | ||||||
| 		struct pcpu_dstats __percpu		*dstats; | 		struct pcpu_dstats __percpu		*dstats; | ||||||
| 	}; | 	}; | ||||||
|  | 	unsigned long		state; | ||||||
| 	unsigned int		flags; | 	unsigned int		flags; | ||||||
| 	unsigned short		hard_header_len; | 	unsigned short		hard_header_len; | ||||||
| 	netdev_features_t	features; | 	netdev_features_t	features; | ||||||
|  | @ -2117,7 +2118,6 @@ struct net_device { | ||||||
| 	 *	part of the usual set specified in Space.c. | 	 *	part of the usual set specified in Space.c. | ||||||
| 	 */ | 	 */ | ||||||
| 
 | 
 | ||||||
| 	unsigned long		state; |  | ||||||
| 
 | 
 | ||||||
| 	struct list_head	dev_list; | 	struct list_head	dev_list; | ||||||
| 	struct list_head	napi_list; | 	struct list_head	napi_list; | ||||||
|  |  | ||||||
|  | @ -247,6 +247,37 @@ do { \ | ||||||
| 	cond_resched(); \ | 	cond_resched(); \ | ||||||
| } while (0) | } while (0) | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * rcu_softirq_qs_periodic - Report RCU and RCU-Tasks quiescent states | ||||||
|  |  * @old_ts: jiffies at start of processing. | ||||||
|  |  * | ||||||
|  |  * This helper is for long-running softirq handlers, such as NAPI threads in | ||||||
|  |  * networking. The caller should initialize the variable passed in as @old_ts | ||||||
|  |  * at the beginning of the softirq handler. When invoked frequently, this macro | ||||||
|  |  * will invoke rcu_softirq_qs() every 100 milliseconds thereafter, which will | ||||||
|  |  * provide both RCU and RCU-Tasks quiescent states. Note that this macro | ||||||
|  |  * modifies its old_ts argument. | ||||||
|  |  * | ||||||
|  |  * Because regions of code that have disabled softirq act as RCU read-side | ||||||
|  |  * critical sections, this macro should be invoked with softirq (and | ||||||
|  |  * preemption) enabled. | ||||||
|  |  * | ||||||
|  |  * The macro is not needed when CONFIG_PREEMPT_RT is defined. RT kernels would | ||||||
|  |  * have more chance to invoke schedule() calls and provide necessary quiescent | ||||||
|  |  * states. As a contrast, calling cond_resched() only won't achieve the same | ||||||
|  |  * effect because cond_resched() does not provide RCU-Tasks quiescent states. | ||||||
|  |  */ | ||||||
|  | #define rcu_softirq_qs_periodic(old_ts) \ | ||||||
|  | do { \ | ||||||
|  | 	if (!IS_ENABLED(CONFIG_PREEMPT_RT) && \ | ||||||
|  | 	    time_after(jiffies, (old_ts) + HZ / 10)) { \ | ||||||
|  | 		preempt_disable(); \ | ||||||
|  | 		rcu_softirq_qs(); \ | ||||||
|  | 		preempt_enable(); \ | ||||||
|  | 		(old_ts) = jiffies; \ | ||||||
|  | 	} \ | ||||||
|  | } while (0) | ||||||
|  | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Infrastructure to implement the synchronize_() primitives in |  * Infrastructure to implement the synchronize_() primitives in | ||||||
|  * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. |  * TREE_RCU and rcu_barrier_() primitives in TINY_RCU. | ||||||
|  |  | ||||||
|  | @ -822,9 +822,9 @@ typedef unsigned char *sk_buff_data_t; | ||||||
|  *	@decrypted: Decrypted SKB |  *	@decrypted: Decrypted SKB | ||||||
|  *	@slow_gro: state present at GRO time, slower prepare step required |  *	@slow_gro: state present at GRO time, slower prepare step required | ||||||
|  *	@mono_delivery_time: When set, skb->tstamp has the |  *	@mono_delivery_time: When set, skb->tstamp has the | ||||||
|  *		delivery_time in mono clock base (i.e., EDT) or a clock base chosen |  *		delivery_time in mono clock base (i.e. EDT).  Otherwise, the | ||||||
|  *		by SO_TXTIME. If zero, skb->tstamp has the (rcv) timestamp at |  *		skb->tstamp has the (rcv) timestamp at ingress and | ||||||
|  *		ingress. |  *		delivery_time at egress. | ||||||
|  *	@napi_id: id of the NAPI struct this skb came from |  *	@napi_id: id of the NAPI struct this skb came from | ||||||
|  *	@sender_cpu: (aka @napi_id) source CPU in XPS |  *	@sender_cpu: (aka @napi_id) source CPU in XPS | ||||||
|  *	@alloc_cpu: CPU which did the skb allocation. |  *	@alloc_cpu: CPU which did the skb allocation. | ||||||
|  | @ -3523,6 +3523,16 @@ int skb_cow_data_for_xdp(struct page_pool *pool, struct sk_buff **pskb, | ||||||
| 			 struct bpf_prog *prog); | 			 struct bpf_prog *prog); | ||||||
| bool napi_pp_put_page(struct page *page, bool napi_safe); | bool napi_pp_put_page(struct page *page, bool napi_safe); | ||||||
| 
 | 
 | ||||||
|  | static inline void | ||||||
|  | skb_page_unref(const struct sk_buff *skb, struct page *page, bool napi_safe) | ||||||
|  | { | ||||||
|  | #ifdef CONFIG_PAGE_POOL | ||||||
|  | 	if (skb->pp_recycle && napi_pp_put_page(page, napi_safe)) | ||||||
|  | 		return; | ||||||
|  | #endif | ||||||
|  | 	put_page(page); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static inline void | static inline void | ||||||
| napi_frag_unref(skb_frag_t *frag, bool recycle, bool napi_safe) | napi_frag_unref(skb_frag_t *frag, bool recycle, bool napi_safe) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -422,13 +422,6 @@ extern long __sys_recvmsg_sock(struct socket *sock, struct msghdr *msg, | ||||||
| 			       struct user_msghdr __user *umsg, | 			       struct user_msghdr __user *umsg, | ||||||
| 			       struct sockaddr __user *uaddr, | 			       struct sockaddr __user *uaddr, | ||||||
| 			       unsigned int flags); | 			       unsigned int flags); | ||||||
| extern int sendmsg_copy_msghdr(struct msghdr *msg, |  | ||||||
| 			       struct user_msghdr __user *umsg, unsigned flags, |  | ||||||
| 			       struct iovec **iov); |  | ||||||
| extern int recvmsg_copy_msghdr(struct msghdr *msg, |  | ||||||
| 			       struct user_msghdr __user *umsg, unsigned flags, |  | ||||||
| 			       struct sockaddr __user **uaddr, |  | ||||||
| 			       struct iovec **iov); |  | ||||||
| extern int __copy_msghdr(struct msghdr *kmsg, | extern int __copy_msghdr(struct msghdr *kmsg, | ||||||
| 			 struct user_msghdr *umsg, | 			 struct user_msghdr *umsg, | ||||||
| 			 struct sockaddr __user **save_addr); | 			 struct sockaddr __user **save_addr); | ||||||
|  |  | ||||||
|  | @ -61,7 +61,11 @@ struct request_sock { | ||||||
| 	struct request_sock		*dl_next; | 	struct request_sock		*dl_next; | ||||||
| 	u16				mss; | 	u16				mss; | ||||||
| 	u8				num_retrans; /* number of retransmits */ | 	u8				num_retrans; /* number of retransmits */ | ||||||
| 	u8				syncookie:1; /* syncookie: encode tcpopts in timestamp */ | 	u8				syncookie:1; /* True if
 | ||||||
|  | 						      * 1) tcpopts needs to be encoded in | ||||||
|  | 						      *    TS of SYN+ACK | ||||||
|  | 						      * 2) ACK is validated by BPF kfunc. | ||||||
|  | 						      */ | ||||||
| 	u8				num_timeout:7; /* number of timeouts */ | 	u8				num_timeout:7; /* number of timeouts */ | ||||||
| 	u32				ts_recent; | 	u32				ts_recent; | ||||||
| 	struct timer_list		rsk_timer; | 	struct timer_list		rsk_timer; | ||||||
|  | @ -144,6 +148,7 @@ reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener, | ||||||
| 	sk_node_init(&req_to_sk(req)->sk_node); | 	sk_node_init(&req_to_sk(req)->sk_node); | ||||||
| 	sk_tx_queue_clear(req_to_sk(req)); | 	sk_tx_queue_clear(req_to_sk(req)); | ||||||
| 	req->saved_syn = NULL; | 	req->saved_syn = NULL; | ||||||
|  | 	req->syncookie = 0; | ||||||
| 	req->timeout = 0; | 	req->timeout = 0; | ||||||
| 	req->num_timeout = 0; | 	req->num_timeout = 0; | ||||||
| 	req->num_retrans = 0; | 	req->num_retrans = 0; | ||||||
|  |  | ||||||
|  | @ -263,6 +263,7 @@ static int cpu_map_bpf_prog_run(struct bpf_cpu_map_entry *rcpu, void **frames, | ||||||
| static int cpu_map_kthread_run(void *data) | static int cpu_map_kthread_run(void *data) | ||||||
| { | { | ||||||
| 	struct bpf_cpu_map_entry *rcpu = data; | 	struct bpf_cpu_map_entry *rcpu = data; | ||||||
|  | 	unsigned long last_qs = jiffies; | ||||||
| 
 | 
 | ||||||
| 	complete(&rcpu->kthread_running); | 	complete(&rcpu->kthread_running); | ||||||
| 	set_current_state(TASK_INTERRUPTIBLE); | 	set_current_state(TASK_INTERRUPTIBLE); | ||||||
|  | @ -288,10 +289,12 @@ static int cpu_map_kthread_run(void *data) | ||||||
| 			if (__ptr_ring_empty(rcpu->queue)) { | 			if (__ptr_ring_empty(rcpu->queue)) { | ||||||
| 				schedule(); | 				schedule(); | ||||||
| 				sched = 1; | 				sched = 1; | ||||||
|  | 				last_qs = jiffies; | ||||||
| 			} else { | 			} else { | ||||||
| 				__set_current_state(TASK_RUNNING); | 				__set_current_state(TASK_RUNNING); | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
|  | 			rcu_softirq_qs_periodic(last_qs); | ||||||
| 			sched = cond_resched(); | 			sched = cond_resched(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2245,7 +2245,7 @@ void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev) | ||||||
| 	rcu_read_lock(); | 	rcu_read_lock(); | ||||||
| again: | again: | ||||||
| 	list_for_each_entry_rcu(ptype, ptype_list, list) { | 	list_for_each_entry_rcu(ptype, ptype_list, list) { | ||||||
| 		if (ptype->ignore_outgoing) | 		if (READ_ONCE(ptype->ignore_outgoing)) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		/* Never send packets back to the socket
 | 		/* Never send packets back to the socket
 | ||||||
|  | @ -6743,6 +6743,8 @@ static int napi_threaded_poll(void *data) | ||||||
| 	void *have; | 	void *have; | ||||||
| 
 | 
 | ||||||
| 	while (!napi_thread_wait(napi)) { | 	while (!napi_thread_wait(napi)) { | ||||||
|  | 		unsigned long last_qs = jiffies; | ||||||
|  | 
 | ||||||
| 		for (;;) { | 		for (;;) { | ||||||
| 			bool repoll = false; | 			bool repoll = false; | ||||||
| 
 | 
 | ||||||
|  | @ -6767,6 +6769,7 @@ static int napi_threaded_poll(void *data) | ||||||
| 			if (!repoll) | 			if (!repoll) | ||||||
| 				break; | 				break; | ||||||
| 
 | 
 | ||||||
|  | 			rcu_softirq_qs_periodic(last_qs); | ||||||
| 			cond_resched(); | 			cond_resched(); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -11665,11 +11668,12 @@ static void __init net_dev_struct_check(void) | ||||||
| 
 | 
 | ||||||
| 	/* TXRX read-mostly hotpath */ | 	/* TXRX read-mostly hotpath */ | ||||||
| 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, lstats); | 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, lstats); | ||||||
|  | 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, state); | ||||||
| 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, flags); | 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, flags); | ||||||
| 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, hard_header_len); | 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, hard_header_len); | ||||||
| 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, features); | 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, features); | ||||||
| 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, ip6_ptr); | 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_txrx, ip6_ptr); | ||||||
| 	CACHELINE_ASSERT_GROUP_SIZE(struct net_device, net_device_read_txrx, 38); | 	CACHELINE_ASSERT_GROUP_SIZE(struct net_device, net_device_read_txrx, 46); | ||||||
| 
 | 
 | ||||||
| 	/* RX read-mostly hotpath */ | 	/* RX read-mostly hotpath */ | ||||||
| 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_rx, ptype_specific); | 	CACHELINE_ASSERT_GROUP_MEMBER(struct net_device, net_device_read_rx, ptype_specific); | ||||||
|  |  | ||||||
|  | @ -193,12 +193,13 @@ devlink_get_from_attrs_lock(struct net *net, struct nlattr **attrs, | ||||||
| 	devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); | 	devname = nla_data(attrs[DEVLINK_ATTR_DEV_NAME]); | ||||||
| 
 | 
 | ||||||
| 	devlinks_xa_for_each_registered_get(net, index, devlink) { | 	devlinks_xa_for_each_registered_get(net, index, devlink) { | ||||||
| 		devl_dev_lock(devlink, dev_lock); | 		if (strcmp(devlink->dev->bus->name, busname) == 0 && | ||||||
| 		if (devl_is_registered(devlink) && | 		    strcmp(dev_name(devlink->dev), devname) == 0) { | ||||||
| 		    strcmp(devlink->dev->bus->name, busname) == 0 && | 			devl_dev_lock(devlink, dev_lock); | ||||||
| 		    strcmp(dev_name(devlink->dev), devname) == 0) | 			if (devl_is_registered(devlink)) | ||||||
| 			return devlink; | 				return devlink; | ||||||
| 		devl_dev_unlock(devlink, dev_lock); | 			devl_dev_unlock(devlink, dev_lock); | ||||||
|  | 		} | ||||||
| 		devlink_put(devlink); | 		devlink_put(devlink); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -889,7 +889,7 @@ int devlink_nl_port_new_doit(struct sk_buff *skb, struct genl_info *info) | ||||||
| 		err = -ENOMEM; | 		err = -ENOMEM; | ||||||
| 		goto err_out_port_del; | 		goto err_out_port_del; | ||||||
| 	} | 	} | ||||||
| 	err = devlink_nl_port_fill(msg, devlink_port, DEVLINK_CMD_NEW, | 	err = devlink_nl_port_fill(msg, devlink_port, DEVLINK_CMD_PORT_NEW, | ||||||
| 				   info->snd_portid, info->snd_seq, 0, NULL); | 				   info->snd_portid, info->snd_seq, 0, NULL); | ||||||
| 	if (WARN_ON_ONCE(err)) | 	if (WARN_ON_ONCE(err)) | ||||||
| 		goto err_out_msg_free; | 		goto err_out_msg_free; | ||||||
|  |  | ||||||
|  | @ -228,6 +228,10 @@ struct hsr_node *hsr_get_node(struct hsr_port *port, struct list_head *node_db, | ||||||
| 	 */ | 	 */ | ||||||
| 	if (ethhdr->h_proto == htons(ETH_P_PRP) || | 	if (ethhdr->h_proto == htons(ETH_P_PRP) || | ||||||
| 	    ethhdr->h_proto == htons(ETH_P_HSR)) { | 	    ethhdr->h_proto == htons(ETH_P_HSR)) { | ||||||
|  | 		/* Check if skb contains hsr_ethhdr */ | ||||||
|  | 		if (skb->mac_len < sizeof(struct hsr_ethhdr)) | ||||||
|  | 			return NULL; | ||||||
|  | 
 | ||||||
| 		/* Use the existing sequence_nr from the tag as starting point
 | 		/* Use the existing sequence_nr from the tag as starting point
 | ||||||
| 		 * for filtering duplicate frames. | 		 * for filtering duplicate frames. | ||||||
| 		 */ | 		 */ | ||||||
|  |  | ||||||
|  | @ -148,14 +148,21 @@ static struct notifier_block hsr_nb = { | ||||||
| 
 | 
 | ||||||
| static int __init hsr_init(void) | static int __init hsr_init(void) | ||||||
| { | { | ||||||
| 	int res; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); | 	BUILD_BUG_ON(sizeof(struct hsr_tag) != HSR_HLEN); | ||||||
| 
 | 
 | ||||||
| 	register_netdevice_notifier(&hsr_nb); | 	err = register_netdevice_notifier(&hsr_nb); | ||||||
| 	res = hsr_netlink_init(); | 	if (err) | ||||||
|  | 		return err; | ||||||
| 
 | 
 | ||||||
| 	return res; | 	err = hsr_netlink_init(); | ||||||
|  | 	if (err) { | ||||||
|  | 		unregister_netdevice_notifier(&hsr_nb); | ||||||
|  | 		return err; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void __exit hsr_exit(void) | static void __exit hsr_exit(void) | ||||||
|  |  | ||||||
|  | @ -95,7 +95,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, | ||||||
| 			     __alignof__(struct scatterlist)); | 			     __alignof__(struct scatterlist)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void esp_ssg_unref(struct xfrm_state *x, void *tmp) | static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb) | ||||||
| { | { | ||||||
| 	struct crypto_aead *aead = x->data; | 	struct crypto_aead *aead = x->data; | ||||||
| 	int extralen = 0; | 	int extralen = 0; | ||||||
|  | @ -114,7 +114,7 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp) | ||||||
| 	 */ | 	 */ | ||||||
| 	if (req->src != req->dst) | 	if (req->src != req->dst) | ||||||
| 		for (sg = sg_next(req->src); sg; sg = sg_next(sg)) | 		for (sg = sg_next(req->src); sg; sg = sg_next(sg)) | ||||||
| 			put_page(sg_page(sg)); | 			skb_page_unref(skb, sg_page(sg), false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_INET_ESPINTCP | #ifdef CONFIG_INET_ESPINTCP | ||||||
|  | @ -260,7 +260,7 @@ static void esp_output_done(void *data, int err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tmp = ESP_SKB_CB(skb)->tmp; | 	tmp = ESP_SKB_CB(skb)->tmp; | ||||||
| 	esp_ssg_unref(x, tmp); | 	esp_ssg_unref(x, tmp, skb); | ||||||
| 	kfree(tmp); | 	kfree(tmp); | ||||||
| 
 | 
 | ||||||
| 	if (xo && (xo->flags & XFRM_DEV_RESUME)) { | 	if (xo && (xo->flags & XFRM_DEV_RESUME)) { | ||||||
|  | @ -639,7 +639,7 @@ int esp_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info * | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (sg != dsg) | 	if (sg != dsg) | ||||||
| 		esp_ssg_unref(x, tmp); | 		esp_ssg_unref(x, tmp, skb); | ||||||
| 
 | 
 | ||||||
| 	if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) | 	if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) | ||||||
| 		err = esp_output_tail_tcp(x, skb); | 		err = esp_output_tail_tcp(x, skb); | ||||||
|  |  | ||||||
|  | @ -1135,7 +1135,7 @@ int __inet_hash_connect(struct inet_timewait_death_row *death_row, | ||||||
| 		sock_prot_inuse_add(net, sk->sk_prot, -1); | 		sock_prot_inuse_add(net, sk->sk_prot, -1); | ||||||
| 
 | 
 | ||||||
| 		spin_lock(lock); | 		spin_lock(lock); | ||||||
| 		sk_nulls_del_node_init_rcu(sk); | 		__sk_nulls_del_node_init_rcu(sk); | ||||||
| 		spin_unlock(lock); | 		spin_unlock(lock); | ||||||
| 
 | 
 | ||||||
| 		sk->sk_hash = 0; | 		sk->sk_hash = 0; | ||||||
|  |  | ||||||
|  | @ -263,12 +263,12 @@ void __inet_twsk_schedule(struct inet_timewait_sock *tw, int timeo, bool rearm) | ||||||
| } | } | ||||||
| EXPORT_SYMBOL_GPL(__inet_twsk_schedule); | EXPORT_SYMBOL_GPL(__inet_twsk_schedule); | ||||||
| 
 | 
 | ||||||
|  | /* Remove all non full sockets (TIME_WAIT and NEW_SYN_RECV) for dead netns */ | ||||||
| void inet_twsk_purge(struct inet_hashinfo *hashinfo, int family) | void inet_twsk_purge(struct inet_hashinfo *hashinfo, int family) | ||||||
| { | { | ||||||
| 	struct inet_timewait_sock *tw; |  | ||||||
| 	struct sock *sk; |  | ||||||
| 	struct hlist_nulls_node *node; | 	struct hlist_nulls_node *node; | ||||||
| 	unsigned int slot; | 	unsigned int slot; | ||||||
|  | 	struct sock *sk; | ||||||
| 
 | 
 | ||||||
| 	for (slot = 0; slot <= hashinfo->ehash_mask; slot++) { | 	for (slot = 0; slot <= hashinfo->ehash_mask; slot++) { | ||||||
| 		struct inet_ehash_bucket *head = &hashinfo->ehash[slot]; | 		struct inet_ehash_bucket *head = &hashinfo->ehash[slot]; | ||||||
|  | @ -277,38 +277,35 @@ void inet_twsk_purge(struct inet_hashinfo *hashinfo, int family) | ||||||
| 		rcu_read_lock(); | 		rcu_read_lock(); | ||||||
| restart: | restart: | ||||||
| 		sk_nulls_for_each_rcu(sk, node, &head->chain) { | 		sk_nulls_for_each_rcu(sk, node, &head->chain) { | ||||||
| 			if (sk->sk_state != TCP_TIME_WAIT) { | 			int state = inet_sk_state_load(sk); | ||||||
| 				/* A kernel listener socket might not hold refcnt for net,
 |  | ||||||
| 				 * so reqsk_timer_handler() could be fired after net is |  | ||||||
| 				 * freed.  Userspace listener and reqsk never exist here. |  | ||||||
| 				 */ |  | ||||||
| 				if (unlikely(sk->sk_state == TCP_NEW_SYN_RECV && |  | ||||||
| 					     hashinfo->pernet)) { |  | ||||||
| 					struct request_sock *req = inet_reqsk(sk); |  | ||||||
| 
 | 
 | ||||||
| 					inet_csk_reqsk_queue_drop_and_put(req->rsk_listener, req); | 			if ((1 << state) & ~(TCPF_TIME_WAIT | | ||||||
| 				} | 					     TCPF_NEW_SYN_RECV)) | ||||||
| 
 |  | ||||||
| 				continue; |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			tw = inet_twsk(sk); |  | ||||||
| 			if ((tw->tw_family != family) || |  | ||||||
| 				refcount_read(&twsk_net(tw)->ns.count)) |  | ||||||
| 				continue; | 				continue; | ||||||
| 
 | 
 | ||||||
| 			if (unlikely(!refcount_inc_not_zero(&tw->tw_refcnt))) | 			if (sk->sk_family != family || | ||||||
|  | 			    refcount_read(&sock_net(sk)->ns.count)) | ||||||
| 				continue; | 				continue; | ||||||
| 
 | 
 | ||||||
| 			if (unlikely((tw->tw_family != family) || | 			if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt))) | ||||||
| 				     refcount_read(&twsk_net(tw)->ns.count))) { | 				continue; | ||||||
| 				inet_twsk_put(tw); | 
 | ||||||
|  | 			if (unlikely(sk->sk_family != family || | ||||||
|  | 				     refcount_read(&sock_net(sk)->ns.count))) { | ||||||
|  | 				sock_gen_put(sk); | ||||||
| 				goto restart; | 				goto restart; | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			rcu_read_unlock(); | 			rcu_read_unlock(); | ||||||
| 			local_bh_disable(); | 			local_bh_disable(); | ||||||
| 			inet_twsk_deschedule_put(tw); | 			if (state == TCP_TIME_WAIT) { | ||||||
|  | 				inet_twsk_deschedule_put(inet_twsk(sk)); | ||||||
|  | 			} else { | ||||||
|  | 				struct request_sock *req = inet_reqsk(sk); | ||||||
|  | 
 | ||||||
|  | 				inet_csk_reqsk_queue_drop_and_put(req->rsk_listener, | ||||||
|  | 								  req); | ||||||
|  | 			} | ||||||
| 			local_bh_enable(); | 			local_bh_enable(); | ||||||
| 			goto restart_rcu; | 			goto restart_rcu; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -1458,7 +1458,6 @@ struct sk_buff *__ip_make_skb(struct sock *sk, | ||||||
| 	skb->priority = (cork->tos != -1) ? cork->priority: READ_ONCE(sk->sk_priority); | 	skb->priority = (cork->tos != -1) ? cork->priority: READ_ONCE(sk->sk_priority); | ||||||
| 	skb->mark = cork->mark; | 	skb->mark = cork->mark; | ||||||
| 	skb->tstamp = cork->transmit_time; | 	skb->tstamp = cork->transmit_time; | ||||||
| 	skb->mono_delivery_time = !!skb->tstamp; |  | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec | 	 * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec | ||||||
| 	 * on dst refcount | 	 * on dst refcount | ||||||
|  |  | ||||||
|  | @ -357,10 +357,10 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, | ||||||
| 		goto error; | 		goto error; | ||||||
| 	skb_reserve(skb, hlen); | 	skb_reserve(skb, hlen); | ||||||
| 
 | 
 | ||||||
|  | 	skb->protocol = htons(ETH_P_IP); | ||||||
| 	skb->priority = READ_ONCE(sk->sk_priority); | 	skb->priority = READ_ONCE(sk->sk_priority); | ||||||
| 	skb->mark = sockc->mark; | 	skb->mark = sockc->mark; | ||||||
| 	skb->tstamp = sockc->transmit_time; | 	skb->tstamp = sockc->transmit_time; | ||||||
| 	skb->mono_delivery_time = !!skb->tstamp; |  | ||||||
| 	skb_dst_set(skb, &rt->dst); | 	skb_dst_set(skb, &rt->dst); | ||||||
| 	*rtp = NULL; | 	*rtp = NULL; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -474,6 +474,9 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) | ||||||
| 				  ireq->wscale_ok, &rcv_wscale, | 				  ireq->wscale_ok, &rcv_wscale, | ||||||
| 				  dst_metric(&rt->dst, RTAX_INITRWND)); | 				  dst_metric(&rt->dst, RTAX_INITRWND)); | ||||||
| 
 | 
 | ||||||
|  | 	/* req->syncookie is set true only if ACK is validated
 | ||||||
|  | 	 * by BPF kfunc, then, rcv_wscale is already configured. | ||||||
|  | 	 */ | ||||||
| 	if (!req->syncookie) | 	if (!req->syncookie) | ||||||
| 		ireq->rcv_wscale = rcv_wscale; | 		ireq->rcv_wscale = rcv_wscale; | ||||||
| 	ireq->ecn_ok &= cookie_ecn_ok(net, &rt->dst); | 	ireq->ecn_ok &= cookie_ecn_ok(net, &rt->dst); | ||||||
|  |  | ||||||
|  | @ -398,10 +398,6 @@ void tcp_twsk_purge(struct list_head *net_exit_list, int family) | ||||||
| 			/* Even if tw_refcount == 1, we must clean up kernel reqsk */ | 			/* Even if tw_refcount == 1, we must clean up kernel reqsk */ | ||||||
| 			inet_twsk_purge(net->ipv4.tcp_death_row.hashinfo, family); | 			inet_twsk_purge(net->ipv4.tcp_death_row.hashinfo, family); | ||||||
| 		} else if (!purged_once) { | 		} else if (!purged_once) { | ||||||
| 			/* The last refcount is decremented in tcp_sk_exit_batch() */ |  | ||||||
| 			if (refcount_read(&net->ipv4.tcp_death_row.tw_refcount) == 1) |  | ||||||
| 				continue; |  | ||||||
| 
 |  | ||||||
| 			inet_twsk_purge(&tcp_hashinfo, family); | 			inet_twsk_purge(&tcp_hashinfo, family); | ||||||
| 			purged_once = true; | 			purged_once = true; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ static inline struct scatterlist *esp_req_sg(struct crypto_aead *aead, | ||||||
| 			     __alignof__(struct scatterlist)); | 			     __alignof__(struct scatterlist)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void esp_ssg_unref(struct xfrm_state *x, void *tmp) | static void esp_ssg_unref(struct xfrm_state *x, void *tmp, struct sk_buff *skb) | ||||||
| { | { | ||||||
| 	struct crypto_aead *aead = x->data; | 	struct crypto_aead *aead = x->data; | ||||||
| 	int extralen = 0; | 	int extralen = 0; | ||||||
|  | @ -131,7 +131,7 @@ static void esp_ssg_unref(struct xfrm_state *x, void *tmp) | ||||||
| 	 */ | 	 */ | ||||||
| 	if (req->src != req->dst) | 	if (req->src != req->dst) | ||||||
| 		for (sg = sg_next(req->src); sg; sg = sg_next(sg)) | 		for (sg = sg_next(req->src); sg; sg = sg_next(sg)) | ||||||
| 			put_page(sg_page(sg)); | 			skb_page_unref(skb, sg_page(sg), false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #ifdef CONFIG_INET6_ESPINTCP | #ifdef CONFIG_INET6_ESPINTCP | ||||||
|  | @ -294,7 +294,7 @@ static void esp_output_done(void *data, int err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tmp = ESP_SKB_CB(skb)->tmp; | 	tmp = ESP_SKB_CB(skb)->tmp; | ||||||
| 	esp_ssg_unref(x, tmp); | 	esp_ssg_unref(x, tmp, skb); | ||||||
| 	kfree(tmp); | 	kfree(tmp); | ||||||
| 
 | 
 | ||||||
| 	esp_output_encap_csum(skb); | 	esp_output_encap_csum(skb); | ||||||
|  | @ -677,7 +677,7 @@ int esp6_output_tail(struct xfrm_state *x, struct sk_buff *skb, struct esp_info | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (sg != dsg) | 	if (sg != dsg) | ||||||
| 		esp_ssg_unref(x, tmp); | 		esp_ssg_unref(x, tmp, skb); | ||||||
| 
 | 
 | ||||||
| 	if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) | 	if (!err && x->encap && x->encap->encap_type == TCP_ENCAP_ESPINTCP) | ||||||
| 		err = esp_output_tail_tcp(x, skb); | 		err = esp_output_tail_tcp(x, skb); | ||||||
|  |  | ||||||
|  | @ -1925,7 +1925,7 @@ struct sk_buff *__ip6_make_skb(struct sock *sk, | ||||||
| 	skb->priority = READ_ONCE(sk->sk_priority); | 	skb->priority = READ_ONCE(sk->sk_priority); | ||||||
| 	skb->mark = cork->base.mark; | 	skb->mark = cork->base.mark; | ||||||
| 	skb->tstamp = cork->base.transmit_time; | 	skb->tstamp = cork->base.transmit_time; | ||||||
| 	skb->mono_delivery_time = !!skb->tstamp; | 
 | ||||||
| 	ip6_cork_steal_dst(skb, cork); | 	ip6_cork_steal_dst(skb, cork); | ||||||
| 	IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS); | 	IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTREQUESTS); | ||||||
| 	if (proto == IPPROTO_ICMPV6) { | 	if (proto == IPPROTO_ICMPV6) { | ||||||
|  |  | ||||||
|  | @ -622,7 +622,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length, | ||||||
| 	skb->priority = READ_ONCE(sk->sk_priority); | 	skb->priority = READ_ONCE(sk->sk_priority); | ||||||
| 	skb->mark = sockc->mark; | 	skb->mark = sockc->mark; | ||||||
| 	skb->tstamp = sockc->transmit_time; | 	skb->tstamp = sockc->transmit_time; | ||||||
| 	skb->mono_delivery_time = !!skb->tstamp; | 
 | ||||||
| 	skb_put(skb, length); | 	skb_put(skb, length); | ||||||
| 	skb_reset_network_header(skb); | 	skb_reset_network_header(skb); | ||||||
| 	iph = ipv6_hdr(skb); | 	iph = ipv6_hdr(skb); | ||||||
|  |  | ||||||
|  | @ -258,6 +258,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) | ||||||
| 				  ireq->wscale_ok, &rcv_wscale, | 				  ireq->wscale_ok, &rcv_wscale, | ||||||
| 				  dst_metric(dst, RTAX_INITRWND)); | 				  dst_metric(dst, RTAX_INITRWND)); | ||||||
| 
 | 
 | ||||||
|  | 	/* req->syncookie is set true only if ACK is validated
 | ||||||
|  | 	 * by BPF kfunc, then, rcv_wscale is already configured. | ||||||
|  | 	 */ | ||||||
| 	if (!req->syncookie) | 	if (!req->syncookie) | ||||||
| 		ireq->rcv_wscale = rcv_wscale; | 		ireq->rcv_wscale = rcv_wscale; | ||||||
| 	ireq->ecn_ok &= cookie_ecn_ok(net, dst); | 	ireq->ecn_ok &= cookie_ecn_ok(net, dst); | ||||||
|  |  | ||||||
|  | @ -1213,7 +1213,7 @@ static int nf_tables_updtable(struct nft_ctx *ctx) | ||||||
| 	if (flags & ~NFT_TABLE_F_MASK) | 	if (flags & ~NFT_TABLE_F_MASK) | ||||||
| 		return -EOPNOTSUPP; | 		return -EOPNOTSUPP; | ||||||
| 
 | 
 | ||||||
| 	if (flags == ctx->table->flags) | 	if (flags == (ctx->table->flags & NFT_TABLE_F_MASK)) | ||||||
| 		return 0; | 		return 0; | ||||||
| 
 | 
 | ||||||
| 	if ((nft_table_has_owner(ctx->table) && | 	if ((nft_table_has_owner(ctx->table) && | ||||||
|  | @ -2631,19 +2631,6 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (nla[NFTA_CHAIN_COUNTERS]) { |  | ||||||
| 		if (!nft_is_base_chain(chain)) { |  | ||||||
| 			err = -EOPNOTSUPP; |  | ||||||
| 			goto err_hooks; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); |  | ||||||
| 		if (IS_ERR(stats)) { |  | ||||||
| 			err = PTR_ERR(stats); |  | ||||||
| 			goto err_hooks; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (!(table->flags & NFT_TABLE_F_DORMANT) && | 	if (!(table->flags & NFT_TABLE_F_DORMANT) && | ||||||
| 	    nft_is_base_chain(chain) && | 	    nft_is_base_chain(chain) && | ||||||
| 	    !list_empty(&hook.list)) { | 	    !list_empty(&hook.list)) { | ||||||
|  | @ -2658,6 +2645,20 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	unregister = true; | 	unregister = true; | ||||||
|  | 
 | ||||||
|  | 	if (nla[NFTA_CHAIN_COUNTERS]) { | ||||||
|  | 		if (!nft_is_base_chain(chain)) { | ||||||
|  | 			err = -EOPNOTSUPP; | ||||||
|  | 			goto err_hooks; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); | ||||||
|  | 		if (IS_ERR(stats)) { | ||||||
|  | 			err = PTR_ERR(stats); | ||||||
|  | 			goto err_hooks; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	err = -ENOMEM; | 	err = -ENOMEM; | ||||||
| 	trans = nft_trans_alloc(ctx, NFT_MSG_NEWCHAIN, | 	trans = nft_trans_alloc(ctx, NFT_MSG_NEWCHAIN, | ||||||
| 				sizeof(struct nft_trans_chain)); | 				sizeof(struct nft_trans_chain)); | ||||||
|  |  | ||||||
|  | @ -2329,8 +2329,6 @@ static void nft_pipapo_destroy(const struct nft_ctx *ctx, | ||||||
| 	if (m) { | 	if (m) { | ||||||
| 		rcu_barrier(); | 		rcu_barrier(); | ||||||
| 
 | 
 | ||||||
| 		nft_set_pipapo_match_destroy(ctx, set, m); |  | ||||||
| 
 |  | ||||||
| 		for_each_possible_cpu(cpu) | 		for_each_possible_cpu(cpu) | ||||||
| 			pipapo_free_scratch(m, cpu); | 			pipapo_free_scratch(m, cpu); | ||||||
| 		free_percpu(m->scratch); | 		free_percpu(m->scratch); | ||||||
|  | @ -2342,8 +2340,7 @@ static void nft_pipapo_destroy(const struct nft_ctx *ctx, | ||||||
| 	if (priv->clone) { | 	if (priv->clone) { | ||||||
| 		m = priv->clone; | 		m = priv->clone; | ||||||
| 
 | 
 | ||||||
| 		if (priv->dirty) | 		nft_set_pipapo_match_destroy(ctx, set, m); | ||||||
| 			nft_set_pipapo_match_destroy(ctx, set, m); |  | ||||||
| 
 | 
 | ||||||
| 		for_each_possible_cpu(cpu) | 		for_each_possible_cpu(cpu) | ||||||
| 			pipapo_free_scratch(priv->clone, cpu); | 			pipapo_free_scratch(priv->clone, cpu); | ||||||
|  |  | ||||||
|  | @ -2057,7 +2057,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, | ||||||
| 	skb->priority = READ_ONCE(sk->sk_priority); | 	skb->priority = READ_ONCE(sk->sk_priority); | ||||||
| 	skb->mark = READ_ONCE(sk->sk_mark); | 	skb->mark = READ_ONCE(sk->sk_mark); | ||||||
| 	skb->tstamp = sockc.transmit_time; | 	skb->tstamp = sockc.transmit_time; | ||||||
| 	skb->mono_delivery_time = !!skb->tstamp; | 
 | ||||||
| 	skb_setup_tx_timestamp(skb, sockc.tsflags); | 	skb_setup_tx_timestamp(skb, sockc.tsflags); | ||||||
| 
 | 
 | ||||||
| 	if (unlikely(extra_len == 4)) | 	if (unlikely(extra_len == 4)) | ||||||
|  | @ -2586,7 +2586,6 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, | ||||||
| 	skb->priority = READ_ONCE(po->sk.sk_priority); | 	skb->priority = READ_ONCE(po->sk.sk_priority); | ||||||
| 	skb->mark = READ_ONCE(po->sk.sk_mark); | 	skb->mark = READ_ONCE(po->sk.sk_mark); | ||||||
| 	skb->tstamp = sockc->transmit_time; | 	skb->tstamp = sockc->transmit_time; | ||||||
| 	skb->mono_delivery_time = !!skb->tstamp; |  | ||||||
| 	skb_setup_tx_timestamp(skb, sockc->tsflags); | 	skb_setup_tx_timestamp(skb, sockc->tsflags); | ||||||
| 	skb_zcopy_set_nouarg(skb, ph.raw); | 	skb_zcopy_set_nouarg(skb, ph.raw); | ||||||
| 
 | 
 | ||||||
|  | @ -3065,7 +3064,6 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) | ||||||
| 	skb->priority = READ_ONCE(sk->sk_priority); | 	skb->priority = READ_ONCE(sk->sk_priority); | ||||||
| 	skb->mark = sockc.mark; | 	skb->mark = sockc.mark; | ||||||
| 	skb->tstamp = sockc.transmit_time; | 	skb->tstamp = sockc.transmit_time; | ||||||
| 	skb->mono_delivery_time = !!skb->tstamp; |  | ||||||
| 
 | 
 | ||||||
| 	if (unlikely(extra_len == 4)) | 	if (unlikely(extra_len == 4)) | ||||||
| 		skb->no_fcs = 1; | 		skb->no_fcs = 1; | ||||||
|  | @ -4000,7 +3998,7 @@ packet_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, | ||||||
| 		if (val < 0 || val > 1) | 		if (val < 0 || val > 1) | ||||||
| 			return -EINVAL; | 			return -EINVAL; | ||||||
| 
 | 
 | ||||||
| 		po->prot_hook.ignore_outgoing = !!val; | 		WRITE_ONCE(po->prot_hook.ignore_outgoing, !!val); | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} | 	} | ||||||
| 	case PACKET_TX_HAS_OFF: | 	case PACKET_TX_HAS_OFF: | ||||||
|  | @ -4134,7 +4132,7 @@ static int packet_getsockopt(struct socket *sock, int level, int optname, | ||||||
| 		       0); | 		       0); | ||||||
| 		break; | 		break; | ||||||
| 	case PACKET_IGNORE_OUTGOING: | 	case PACKET_IGNORE_OUTGOING: | ||||||
| 		val = po->prot_hook.ignore_outgoing; | 		val = READ_ONCE(po->prot_hook.ignore_outgoing); | ||||||
| 		break; | 		break; | ||||||
| 	case PACKET_ROLLOVER_STATS: | 	case PACKET_ROLLOVER_STATS: | ||||||
| 		if (!po->rollover) | 		if (!po->rollover) | ||||||
|  |  | ||||||
|  | @ -103,13 +103,12 @@ EXPORT_SYMBOL_GPL(rds_send_path_reset); | ||||||
| 
 | 
 | ||||||
| static int acquire_in_xmit(struct rds_conn_path *cp) | static int acquire_in_xmit(struct rds_conn_path *cp) | ||||||
| { | { | ||||||
| 	return test_and_set_bit(RDS_IN_XMIT, &cp->cp_flags) == 0; | 	return test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags) == 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void release_in_xmit(struct rds_conn_path *cp) | static void release_in_xmit(struct rds_conn_path *cp) | ||||||
| { | { | ||||||
| 	clear_bit(RDS_IN_XMIT, &cp->cp_flags); | 	clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags); | ||||||
| 	smp_mb__after_atomic(); |  | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * We don't use wait_on_bit()/wake_up_bit() because our waking is in a | 	 * We don't use wait_on_bit()/wake_up_bit() because our waking is in a | ||||||
| 	 * hot path and finding waiters is very rare.  We don't want to walk | 	 * hot path and finding waiters is very rare.  We don't want to walk | ||||||
|  |  | ||||||
|  | @ -349,8 +349,8 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, | ||||||
| 			 */ | 			 */ | ||||||
| 			remain = more ? INT_MAX : msg_data_left(msg); | 			remain = more ? INT_MAX : msg_data_left(msg); | ||||||
| 			txb = call->conn->security->alloc_txbuf(call, remain, sk->sk_allocation); | 			txb = call->conn->security->alloc_txbuf(call, remain, sk->sk_allocation); | ||||||
| 			if (IS_ERR(txb)) { | 			if (!txb) { | ||||||
| 				ret = PTR_ERR(txb); | 				ret = -ENOMEM; | ||||||
| 				goto maybe_error; | 				goto maybe_error; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -33,8 +33,8 @@ struct rxrpc_txbuf *rxrpc_alloc_data_txbuf(struct rxrpc_call *call, size_t data_ | ||||||
| 	total = hoff + sizeof(*whdr) + data_size; | 	total = hoff + sizeof(*whdr) + data_size; | ||||||
| 
 | 
 | ||||||
| 	mutex_lock(&call->conn->tx_data_alloc_lock); | 	mutex_lock(&call->conn->tx_data_alloc_lock); | ||||||
| 	buf = page_frag_alloc_align(&call->conn->tx_data_alloc, total, gfp, | 	buf = __page_frag_alloc_align(&call->conn->tx_data_alloc, total, gfp, | ||||||
| 				    ~(data_align - 1) & ~(L1_CACHE_BYTES - 1)); | 				      ~(data_align - 1) & ~(L1_CACHE_BYTES - 1)); | ||||||
| 	mutex_unlock(&call->conn->tx_data_alloc_lock); | 	mutex_unlock(&call->conn->tx_data_alloc_lock); | ||||||
| 	if (!buf) { | 	if (!buf) { | ||||||
| 		kfree(txb); | 		kfree(txb); | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <linux/jhash.h> | #include <linux/jhash.h> | ||||||
|  | #include <linux/module.h> | ||||||
| #include <linux/sizes.h> | #include <linux/sizes.h> | ||||||
| #include <linux/vmalloc.h> | #include <linux/vmalloc.h> | ||||||
| #include <net/pkt_cls.h> | #include <net/pkt_cls.h> | ||||||
|  | @ -563,6 +564,7 @@ static struct Qdisc_ops fq_pie_qdisc_ops __read_mostly = { | ||||||
| 	.dump_stats	= fq_pie_dump_stats, | 	.dump_stats	= fq_pie_dump_stats, | ||||||
| 	.owner		= THIS_MODULE, | 	.owner		= THIS_MODULE, | ||||||
| }; | }; | ||||||
|  | MODULE_ALIAS_NET_SCH("fq_pie"); | ||||||
| 
 | 
 | ||||||
| static int __init fq_pie_module_init(void) | static int __init fq_pie_module_init(void) | ||||||
| { | { | ||||||
|  |  | ||||||
|  | @ -997,7 +997,8 @@ static const struct nla_policy entry_policy[TCA_TAPRIO_SCHED_ENTRY_MAX + 1] = { | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static const struct nla_policy taprio_tc_policy[TCA_TAPRIO_TC_ENTRY_MAX + 1] = { | static const struct nla_policy taprio_tc_policy[TCA_TAPRIO_TC_ENTRY_MAX + 1] = { | ||||||
| 	[TCA_TAPRIO_TC_ENTRY_INDEX]	   = { .type = NLA_U32 }, | 	[TCA_TAPRIO_TC_ENTRY_INDEX]	   = NLA_POLICY_MAX(NLA_U32, | ||||||
|  | 							    TC_QOPT_MAX_QUEUE), | ||||||
| 	[TCA_TAPRIO_TC_ENTRY_MAX_SDU]	   = { .type = NLA_U32 }, | 	[TCA_TAPRIO_TC_ENTRY_MAX_SDU]	   = { .type = NLA_U32 }, | ||||||
| 	[TCA_TAPRIO_TC_ENTRY_FP]	   = NLA_POLICY_RANGE(NLA_U32, | 	[TCA_TAPRIO_TC_ENTRY_FP]	   = NLA_POLICY_RANGE(NLA_U32, | ||||||
| 							      TC_FP_EXPRESS, | 							      TC_FP_EXPRESS, | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								net/socket.c
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								net/socket.c
									
									
									
									
									
								
							|  | @ -2600,9 +2600,9 @@ static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys, | ||||||
| 	return err; | 	return err; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int sendmsg_copy_msghdr(struct msghdr *msg, | static int sendmsg_copy_msghdr(struct msghdr *msg, | ||||||
| 			struct user_msghdr __user *umsg, unsigned flags, | 			       struct user_msghdr __user *umsg, unsigned flags, | ||||||
| 			struct iovec **iov) | 			       struct iovec **iov) | ||||||
| { | { | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
|  | @ -2753,10 +2753,10 @@ SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg, | ||||||
| 	return __sys_sendmmsg(fd, mmsg, vlen, flags, true); | 	return __sys_sendmmsg(fd, mmsg, vlen, flags, true); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int recvmsg_copy_msghdr(struct msghdr *msg, | static int recvmsg_copy_msghdr(struct msghdr *msg, | ||||||
| 			struct user_msghdr __user *umsg, unsigned flags, | 			       struct user_msghdr __user *umsg, unsigned flags, | ||||||
| 			struct sockaddr __user **uaddr, | 			       struct sockaddr __user **uaddr, | ||||||
| 			struct iovec **iov) | 			       struct iovec **iov) | ||||||
| { | { | ||||||
| 	ssize_t err; | 	ssize_t err; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -407,7 +407,8 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x) | ||||||
| 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst; | 	struct xfrm_dst *xdst = (struct xfrm_dst *)dst; | ||||||
| 	struct net_device *dev = x->xso.dev; | 	struct net_device *dev = x->xso.dev; | ||||||
| 
 | 
 | ||||||
| 	if (!x->type_offload) | 	if (!x->type_offload || | ||||||
|  | 	    (x->xso.type == XFRM_DEV_OFFLOAD_UNSPECIFIED && x->encap)) | ||||||
| 		return false; | 		return false; | ||||||
| 
 | 
 | ||||||
| 	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET || | 	if (x->xso.type == XFRM_DEV_OFFLOAD_PACKET || | ||||||
|  |  | ||||||
|  | @ -20,6 +20,7 @@ CFLAGS_ethtool:=$(call get_hdr_inc,_LINUX_ETHTOOL_NETLINK_H_,ethtool_netlink.h) | ||||||
| CFLAGS_handshake:=$(call get_hdr_inc,_LINUX_HANDSHAKE_H,handshake.h) | CFLAGS_handshake:=$(call get_hdr_inc,_LINUX_HANDSHAKE_H,handshake.h) | ||||||
| CFLAGS_mptcp_pm:=$(call get_hdr_inc,_LINUX_MPTCP_PM_H,mptcp_pm.h) | CFLAGS_mptcp_pm:=$(call get_hdr_inc,_LINUX_MPTCP_PM_H,mptcp_pm.h) | ||||||
| CFLAGS_netdev:=$(call get_hdr_inc,_LINUX_NETDEV_H,netdev.h) | CFLAGS_netdev:=$(call get_hdr_inc,_LINUX_NETDEV_H,netdev.h) | ||||||
|  | CFLAGS_nlctrl:=$(call get_hdr_inc,__LINUX_GENERIC_NETLINK_H,genetlink.h) | ||||||
| CFLAGS_nfsd:=$(call get_hdr_inc,_LINUX_NFSD_NETLINK_H,nfsd_netlink.h) | CFLAGS_nfsd:=$(call get_hdr_inc,_LINUX_NFSD_NETLINK_H,nfsd_netlink.h) | ||||||
| CFLAGS_ovs_datapath:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h) | CFLAGS_ovs_datapath:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h) | ||||||
| CFLAGS_ovs_flow:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h) | CFLAGS_ovs_flow:=$(call get_hdr_inc,__LINUX_OPENVSWITCH_H,openvswitch.h) | ||||||
|  |  | ||||||
|  | @ -422,13 +422,6 @@ extern long __sys_recvmsg_sock(struct socket *sock, struct msghdr *msg, | ||||||
| 			       struct user_msghdr __user *umsg, | 			       struct user_msghdr __user *umsg, | ||||||
| 			       struct sockaddr __user *uaddr, | 			       struct sockaddr __user *uaddr, | ||||||
| 			       unsigned int flags); | 			       unsigned int flags); | ||||||
| extern int sendmsg_copy_msghdr(struct msghdr *msg, |  | ||||||
| 			       struct user_msghdr __user *umsg, unsigned flags, |  | ||||||
| 			       struct iovec **iov); |  | ||||||
| extern int recvmsg_copy_msghdr(struct msghdr *msg, |  | ||||||
| 			       struct user_msghdr __user *umsg, unsigned flags, |  | ||||||
| 			       struct sockaddr __user **uaddr, |  | ||||||
| 			       struct iovec **iov); |  | ||||||
| extern int __copy_msghdr(struct msghdr *kmsg, | extern int __copy_msghdr(struct msghdr *kmsg, | ||||||
| 			 struct user_msghdr *umsg, | 			 struct user_msghdr *umsg, | ||||||
| 			 struct sockaddr __user **save_addr); | 			 struct sockaddr __user **save_addr); | ||||||
|  |  | ||||||
|  | @ -354,7 +354,7 @@ __ping_ipv4() | ||||||
| 
 | 
 | ||||||
| 	# Send 100 packets and verify that at least 100 packets hit the rule, | 	# Send 100 packets and verify that at least 100 packets hit the rule, | ||||||
| 	# to overcome ARP noise. | 	# to overcome ARP noise. | ||||||
| 	PING_COUNT=100 PING_TIMEOUT=11 ping_do $dev $dst_ip | 	PING_COUNT=100 PING_TIMEOUT=20 ping_do $dev $dst_ip | ||||||
| 	check_err $? "Ping failed" | 	check_err $? "Ping failed" | ||||||
| 
 | 
 | ||||||
| 	tc_check_at_least_x_packets "dev $rp1 egress" 101 10 100 | 	tc_check_at_least_x_packets "dev $rp1 egress" 101 10 100 | ||||||
|  | @ -410,7 +410,7 @@ __ping_ipv6() | ||||||
| 
 | 
 | ||||||
| 	# Send 100 packets and verify that at least 100 packets hit the rule, | 	# Send 100 packets and verify that at least 100 packets hit the rule, | ||||||
| 	# to overcome neighbor discovery noise. | 	# to overcome neighbor discovery noise. | ||||||
| 	PING_COUNT=100 PING_TIMEOUT=11 ping6_do $dev $dst_ip | 	PING_COUNT=100 PING_TIMEOUT=20 ping6_do $dev $dst_ip | ||||||
| 	check_err $? "Ping failed" | 	check_err $? "Ping failed" | ||||||
| 
 | 
 | ||||||
| 	tc_check_at_least_x_packets "dev $rp1 egress" 101 100 | 	tc_check_at_least_x_packets "dev $rp1 egress" 101 100 | ||||||
|  |  | ||||||
|  | @ -457,7 +457,7 @@ __ping_ipv4() | ||||||
| 
 | 
 | ||||||
| 	# Send 100 packets and verify that at least 100 packets hit the rule, | 	# Send 100 packets and verify that at least 100 packets hit the rule, | ||||||
| 	# to overcome ARP noise. | 	# to overcome ARP noise. | ||||||
| 	PING_COUNT=100 PING_TIMEOUT=11 ping_do $dev $dst_ip | 	PING_COUNT=100 PING_TIMEOUT=20 ping_do $dev $dst_ip | ||||||
| 	check_err $? "Ping failed" | 	check_err $? "Ping failed" | ||||||
| 
 | 
 | ||||||
| 	tc_check_at_least_x_packets "dev $rp1 egress" 101 10 100 | 	tc_check_at_least_x_packets "dev $rp1 egress" 101 10 100 | ||||||
|  | @ -522,7 +522,7 @@ __ping_ipv6() | ||||||
| 
 | 
 | ||||||
| 	# Send 100 packets and verify that at least 100 packets hit the rule, | 	# Send 100 packets and verify that at least 100 packets hit the rule, | ||||||
| 	# to overcome neighbor discovery noise. | 	# to overcome neighbor discovery noise. | ||||||
| 	PING_COUNT=100 PING_TIMEOUT=11 ping6_do $dev $dst_ip | 	PING_COUNT=100 PING_TIMEOUT=20 ping6_do $dev $dst_ip | ||||||
| 	check_err $? "Ping failed" | 	check_err $? "Ping failed" | ||||||
| 
 | 
 | ||||||
| 	tc_check_at_least_x_packets "dev $rp1 egress" 101 100 | 	tc_check_at_least_x_packets "dev $rp1 egress" 101 100 | ||||||
|  |  | ||||||
|  | @ -217,6 +217,7 @@ for family in 4 6; do | ||||||
| 	cleanup | 	cleanup | ||||||
| 
 | 
 | ||||||
| 	create_ns | 	create_ns | ||||||
|  | 	ip netns exec $NS_DST ethtool -K veth$DST generic-receive-offload on | ||||||
| 	ip netns exec $NS_DST ethtool -K veth$DST rx-gro-list on | 	ip netns exec $NS_DST ethtool -K veth$DST rx-gro-list on | ||||||
| 	run_test "GRO frag list" $BM_NET$DST 1 0 | 	run_test "GRO frag list" $BM_NET$DST 1 0 | ||||||
| 	cleanup | 	cleanup | ||||||
|  | @ -227,6 +228,7 @@ for family in 4 6; do | ||||||
| 	# use NAT to circumvent GRO FWD check | 	# use NAT to circumvent GRO FWD check | ||||||
| 	create_ns | 	create_ns | ||||||
| 	ip -n $NS_DST addr add dev veth$DST $BM_NET$DST_NAT/$SUFFIX | 	ip -n $NS_DST addr add dev veth$DST $BM_NET$DST_NAT/$SUFFIX | ||||||
|  | 	ip netns exec $NS_DST ethtool -K veth$DST generic-receive-offload on | ||||||
| 	ip netns exec $NS_DST ethtool -K veth$DST rx-udp-gro-forwarding on | 	ip netns exec $NS_DST ethtool -K veth$DST rx-udp-gro-forwarding on | ||||||
| 	ip netns exec $NS_DST $IPT -t nat -I PREROUTING -d $BM_NET$DST_NAT \ | 	ip netns exec $NS_DST $IPT -t nat -I PREROUTING -d $BM_NET$DST_NAT \ | ||||||
| 					-j DNAT --to-destination $BM_NET$DST | 					-j DNAT --to-destination $BM_NET$DST | ||||||
|  | @ -240,6 +242,7 @@ for family in 4 6; do | ||||||
| 	cleanup | 	cleanup | ||||||
| 
 | 
 | ||||||
| 	create_vxlan_pair | 	create_vxlan_pair | ||||||
|  | 	ip netns exec $NS_DST ethtool -K veth$DST generic-receive-offload on | ||||||
| 	ip netns exec $NS_DST ethtool -K veth$DST rx-gro-list on | 	ip netns exec $NS_DST ethtool -K veth$DST rx-gro-list on | ||||||
| 	run_test "GRO frag list over UDP tunnel" $OL_NET$DST 1 1 | 	run_test "GRO frag list over UDP tunnel" $OL_NET$DST 1 1 | ||||||
| 	cleanup | 	cleanup | ||||||
|  | @ -247,6 +250,7 @@ for family in 4 6; do | ||||||
| 	# use NAT to circumvent GRO FWD check | 	# use NAT to circumvent GRO FWD check | ||||||
| 	create_vxlan_pair | 	create_vxlan_pair | ||||||
| 	ip -n $NS_DST addr add dev $VXDEV$DST $OL_NET$DST_NAT/$SUFFIX | 	ip -n $NS_DST addr add dev $VXDEV$DST $OL_NET$DST_NAT/$SUFFIX | ||||||
|  | 	ip netns exec $NS_DST ethtool -K veth$DST generic-receive-offload on | ||||||
| 	ip netns exec $NS_DST ethtool -K veth$DST rx-udp-gro-forwarding on | 	ip netns exec $NS_DST ethtool -K veth$DST rx-udp-gro-forwarding on | ||||||
| 	ip netns exec $NS_DST $IPT -t nat -I PREROUTING -d $OL_NET$DST_NAT \ | 	ip netns exec $NS_DST $IPT -t nat -I PREROUTING -d $OL_NET$DST_NAT \ | ||||||
| 					-j DNAT --to-destination $OL_NET$DST | 					-j DNAT --to-destination $OL_NET$DST | ||||||
|  |  | ||||||
|  | @ -249,9 +249,9 @@ cleanup | ||||||
| create_ns | create_ns | ||||||
| ip -n $NS_DST link set dev veth$DST up | ip -n $NS_DST link set dev veth$DST up | ||||||
| ip -n $NS_DST link set dev veth$DST xdp object ${BPF_FILE} section xdp | ip -n $NS_DST link set dev veth$DST xdp object ${BPF_FILE} section xdp | ||||||
| chk_gro_flag "gro vs xdp while down - gro flag on" $DST on | chk_gro_flag "gro vs xdp while down - gro flag off" $DST off | ||||||
| ip -n $NS_DST link set dev veth$DST down | ip -n $NS_DST link set dev veth$DST down | ||||||
| chk_gro_flag "                      - after down" $DST on | chk_gro_flag "                      - after down" $DST off | ||||||
| ip -n $NS_DST link set dev veth$DST xdp off | ip -n $NS_DST link set dev veth$DST xdp off | ||||||
| chk_gro_flag "                      - after xdp off" $DST off | chk_gro_flag "                      - after xdp off" $DST off | ||||||
| ip -n $NS_DST link set dev veth$DST up | ip -n $NS_DST link set dev veth$DST up | ||||||
|  | @ -260,6 +260,21 @@ ip -n $NS_SRC link set dev veth$SRC xdp object ${BPF_FILE} section xdp | ||||||
| chk_gro_flag "                      - after peer xdp" $DST off | chk_gro_flag "                      - after peer xdp" $DST off | ||||||
| cleanup | cleanup | ||||||
| 
 | 
 | ||||||
|  | create_ns | ||||||
|  | ip -n $NS_DST link set dev veth$DST up | ||||||
|  | ip -n $NS_DST link set dev veth$DST xdp object ${BPF_FILE} section xdp | ||||||
|  | ip netns exec $NS_DST ethtool -K veth$DST generic-receive-offload on | ||||||
|  | chk_gro_flag "gro vs xdp while down - gro flag on" $DST on | ||||||
|  | ip -n $NS_DST link set dev veth$DST down | ||||||
|  | chk_gro_flag "                      - after down" $DST on | ||||||
|  | ip -n $NS_DST link set dev veth$DST xdp off | ||||||
|  | chk_gro_flag "                      - after xdp off" $DST on | ||||||
|  | ip -n $NS_DST link set dev veth$DST up | ||||||
|  | chk_gro_flag "                      - after up" $DST on | ||||||
|  | ip -n $NS_SRC link set dev veth$SRC xdp object ${BPF_FILE} section xdp | ||||||
|  | chk_gro_flag "                      - after peer xdp" $DST on | ||||||
|  | cleanup | ||||||
|  | 
 | ||||||
| create_ns | create_ns | ||||||
| chk_channels "default channels" $DST 1 1 | chk_channels "default channels" $DST 1 1 | ||||||
| 
 | 
 | ||||||
|  | @ -327,11 +342,14 @@ if [ $CPUS -gt 2 ]; then | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| ip -n $NS_DST link set dev veth$DST xdp object ${BPF_FILE} section xdp 2>/dev/null | ip -n $NS_DST link set dev veth$DST xdp object ${BPF_FILE} section xdp 2>/dev/null | ||||||
| chk_gro_flag "with xdp attached - gro flag" $DST on | chk_gro_flag "with xdp attached - gro flag" $DST off | ||||||
| chk_gro_flag "        - peer gro flag" $SRC off | chk_gro_flag "        - peer gro flag" $SRC off | ||||||
| chk_tso_flag "        - tso flag" $SRC off | chk_tso_flag "        - tso flag" $SRC off | ||||||
| chk_tso_flag "        - peer tso flag" $DST on | chk_tso_flag "        - peer tso flag" $DST on | ||||||
| ip netns exec $NS_DST ethtool -K veth$DST rx-udp-gro-forwarding on | ip netns exec $NS_DST ethtool -K veth$DST rx-udp-gro-forwarding on | ||||||
|  | chk_gro "        - no aggregation" 10 | ||||||
|  | ip netns exec $NS_DST ethtool -K veth$DST generic-receive-offload on | ||||||
|  | chk_gro_flag "        - gro flag with GRO on" $DST on | ||||||
| chk_gro "        - aggregation" 1 | chk_gro "        - aggregation" 1 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ CONFIG_ARCH_RV32I=y | ||||||
| CONFIG_MMU=y | CONFIG_MMU=y | ||||||
| CONFIG_FPU=y | CONFIG_FPU=y | ||||||
| CONFIG_SOC_VIRT=y | CONFIG_SOC_VIRT=y | ||||||
|  | CONFIG_RISCV_ISA_FALLBACK=y | ||||||
| CONFIG_SERIAL_8250=y | CONFIG_SERIAL_8250=y | ||||||
| CONFIG_SERIAL_8250_CONSOLE=y | CONFIG_SERIAL_8250_CONSOLE=y | ||||||
| CONFIG_SERIAL_OF_PLATFORM=y | CONFIG_SERIAL_OF_PLATFORM=y | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ CONFIG_ARCH_RV64I=y | ||||||
| CONFIG_MMU=y | CONFIG_MMU=y | ||||||
| CONFIG_FPU=y | CONFIG_FPU=y | ||||||
| CONFIG_SOC_VIRT=y | CONFIG_SOC_VIRT=y | ||||||
|  | CONFIG_RISCV_ISA_FALLBACK=y | ||||||
| CONFIG_SERIAL_8250=y | CONFIG_SERIAL_8250=y | ||||||
| CONFIG_SERIAL_8250_CONSOLE=y | CONFIG_SERIAL_8250_CONSOLE=y | ||||||
| CONFIG_SERIAL_OF_PLATFORM=y | CONFIG_SERIAL_OF_PLATFORM=y | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Linus Torvalds
						Linus Torvalds