mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-11 03:31:00 -04:00
Merge tag 'net-7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Pull networking fixes from Paolo Abeni:
"Including fixes from bluetooth.
Previous releases - regressions:
- page_pool: keep frag_offset aligned for odd-sized requests
- sched: fix u32 duplicate handle when node ID pool is exhausted
- udp: create exceptions before socket matching
- igmp: convert struct ip_sf_list to RCU
- ip6_gre: check tunnel info before xmit in ip6gre_tunnel_xmit
- rds: acquire the fastpath locks in rds_conn_shutdown()
- tipc:
- protect node reset trace dump with node lock
- fix NULL deref in tipc_named_node_up() on empty publication
list
- bluetooth:
- L2CAP: fix out-of-bounds write in l2cap_ecred_connect
- hci_core: fix race condition during device registration
- eth:
- mlx5e: prevent stale XSK buffer release on refill retries
- bridge: don't truncate the port group walk on teardown
Previous releases - always broken:
- gro: fix nesting of TCP GSO SKBs in skb_gro_receive_list()
- sched: fix skb sizing and action leak on reoffload delete
- tcp: fix use-after-free in do_tcp_getsockopt()
- af_packet: don't cast tpacket_hdr.tp_len to int in
tpacket_parse_header()
- sctp: fix soft lockup from unpadded ASCONF-ACK parameter iteration
- iptunnel: fix stale transport header during tunnel decapsulation
- eth:
- vxlan: fix use-after-free in vxlan_mdb_remote_src_del()
- bonding: fix uninitialized transport header access in
alb_determine_nd()"
* tag 'net-7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (83 commits)
net: gro: Fix nesting of TCP GSO SKBs in skb_gro_receive_list()
net: stmmac: reconfigure RX packet parser table in stmmac_hw_setup() after reset
net: airoha: enable RX_DONE interrupt for RX queue 31
net/rds: don't let rds_conn_shutdown() consume a concurrent drop
net/rds: acquire the fastpath locks in rds_conn_shutdown()
net/rds: acquire RDS_IN_XMIT in rds_tcp_reset_callbacks()
net/rds: tcp: don't force RDS_CONN_RESETTING over a concurrent shutdown
net/rds: clear cp_flags bits individually in rds_conn_path_reset()
net/rds: use clear_bit_unlock() in release_refill()
net/rds: use wq_has_sleeper() in release_in_xmit()
net: usb: qmi_wwan: add Compal EXM-G1x support
net: macb: exclude software FCS from TX byte statistics
net: Remove conflicting altnames for dying netns in __dev_change_net_namespace().
net: bridge: mcast: don't truncate the port group walk on teardown
bonding: do not clear curr_active_slave prematurely when releasing all slaves
net: qrtr: Send HELLO message on endpoint register
octeontx2-af: Fix limiting SRIOV VF count logic
bonding: alb: fix uninitialized transport header access in alb_determine_nd()
s390/ctcm: Prevent XID null dereference
net: psp: do not inherit the Rx association on clone
...
This commit is contained in:
@@ -360,6 +360,17 @@ attribute-sets:
|
||||
name: tsoff
|
||||
type: u32
|
||||
byte-order: big-endian
|
||||
-
|
||||
name: filter-attrs
|
||||
attributes:
|
||||
-
|
||||
name: orig-flags
|
||||
type: u32
|
||||
doc: bitmask of tuple fields to filter on, original direction
|
||||
-
|
||||
name: reply-flags
|
||||
type: u32
|
||||
doc: bitmask of tuple fields to filter on, reply direction
|
||||
-
|
||||
name: conntrack-attrs
|
||||
attributes:
|
||||
@@ -466,7 +477,7 @@ attribute-sets:
|
||||
-
|
||||
name: filter
|
||||
type: nest
|
||||
nested-attributes: tuple-attrs
|
||||
nested-attributes: filter-attrs
|
||||
-
|
||||
name: status-mask
|
||||
type: u32
|
||||
@@ -591,10 +602,14 @@ operations:
|
||||
request:
|
||||
value: 0x101
|
||||
attributes:
|
||||
- mark
|
||||
- filter
|
||||
- tuple-orig
|
||||
- tuple-reply
|
||||
- status
|
||||
- mark
|
||||
- zone
|
||||
- mark-mask
|
||||
- filter
|
||||
- status-mask
|
||||
reply:
|
||||
value: 0x100
|
||||
attributes:
|
||||
|
||||
@@ -571,12 +571,44 @@ int btintel_version_info_tlv(struct hci_dev *hdev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_version_info_tlv);
|
||||
|
||||
static u8 btintel_version_tlv_min_len(u8 type)
|
||||
{
|
||||
switch (type) {
|
||||
case INTEL_TLV_CNVI_TOP:
|
||||
case INTEL_TLV_CNVR_TOP:
|
||||
case INTEL_TLV_CNVI_BT:
|
||||
case INTEL_TLV_CNVR_BT:
|
||||
case INTEL_TLV_BUILD_NUM:
|
||||
case INTEL_TLV_GIT_SHA1:
|
||||
return sizeof(u32);
|
||||
case INTEL_TLV_DEV_REV_ID:
|
||||
case INTEL_TLV_TIME_STAMP:
|
||||
return sizeof(u16);
|
||||
case INTEL_TLV_IMAGE_TYPE:
|
||||
case INTEL_TLV_BUILD_TYPE:
|
||||
case INTEL_TLV_SECURE_BOOT:
|
||||
case INTEL_TLV_OTP_LOCK:
|
||||
case INTEL_TLV_API_LOCK:
|
||||
case INTEL_TLV_DEBUG_LOCK:
|
||||
case INTEL_TLV_LIMITED_CCE:
|
||||
case INTEL_TLV_SBE_TYPE:
|
||||
return sizeof(u8);
|
||||
case INTEL_TLV_MIN_FW:
|
||||
return 3;
|
||||
case INTEL_TLV_OTP_BDADDR:
|
||||
return sizeof(bdaddr_t);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int btintel_parse_version_tlv(struct hci_dev *hdev,
|
||||
struct intel_version_tlv *version,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
/* Consume Command Complete Status field */
|
||||
skb_pull(skb, 1);
|
||||
if (!skb_pull(skb, 1))
|
||||
return -EINVAL;
|
||||
|
||||
/* Event parameters contain multiple TLVs. Read each of them
|
||||
* and only keep the required data. Also, it use existing legacy
|
||||
@@ -596,6 +628,9 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,
|
||||
if (skb->len < tlv->len + sizeof(*tlv))
|
||||
return -EINVAL;
|
||||
|
||||
if (tlv->len < btintel_version_tlv_min_len(tlv->type))
|
||||
return -EINVAL;
|
||||
|
||||
switch (tlv->type) {
|
||||
case INTEL_TLV_CNVI_TOP:
|
||||
version->cnvi_top = get_unaligned_le32(tlv->val);
|
||||
@@ -667,7 +702,7 @@ int btintel_parse_version_tlv(struct hci_dev *hdev,
|
||||
break;
|
||||
case INTEL_TLV_FW_ID:
|
||||
snprintf(version->fw_id, sizeof(version->fw_id),
|
||||
"%s", tlv->val);
|
||||
"%.*s", tlv->len, tlv->val);
|
||||
break;
|
||||
default:
|
||||
/* Ignore rest of information */
|
||||
@@ -686,6 +721,7 @@ static int btintel_read_version_tlv(struct hci_dev *hdev,
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
const u8 param[1] = { 0xFF };
|
||||
int err;
|
||||
|
||||
if (!version)
|
||||
return -EINVAL;
|
||||
@@ -704,10 +740,10 @@ static int btintel_read_version_tlv(struct hci_dev *hdev,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
btintel_parse_version_tlv(hdev, version, skb);
|
||||
err = btintel_parse_version_tlv(hdev, version, skb);
|
||||
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
/* ------- REGMAP IBT SUPPORT ------- */
|
||||
|
||||
@@ -1696,6 +1696,9 @@ static irqreturn_t btintel_pcie_irq_msix_handler(int irq, void *dev_id)
|
||||
|
||||
if (unlikely(!(intr_fh | intr_hw))) {
|
||||
/* Ignore interrupt, inta == 0 */
|
||||
bt_warn_ratelimited("Bluetooth: btintel_pcie: Received spurious interrupt\n");
|
||||
btintel_pcie_wr_reg32(data, BTINTEL_PCIE_CSR_MSIX_AUTOMASK_ST,
|
||||
BIT(entry->entry));
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -307,9 +307,8 @@ static int mrvl_load_firmware(struct hci_dev *hdev, const char *name)
|
||||
err = wait_on_bit_timeout(&mrvl->flags, STATE_FW_REQ_PENDING,
|
||||
TASK_INTERRUPTIBLE,
|
||||
msecs_to_jiffies(2000));
|
||||
if (err == 1) {
|
||||
if (err == -EINTR) {
|
||||
bt_dev_err(hdev, "Firmware load interrupted");
|
||||
err = -EINTR;
|
||||
break;
|
||||
} else if (err) {
|
||||
bt_dev_err(hdev, "Firmware request timeout");
|
||||
|
||||
@@ -875,7 +875,7 @@ static int rlb_initialize(struct bonding *bond)
|
||||
spin_unlock_bh(&bond->mode_lock);
|
||||
|
||||
/* register to receive ARPs */
|
||||
bond->recv_probe = rlb_arp_recv;
|
||||
WRITE_ONCE(bond->recv_probe, rlb_arp_recv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1281,10 +1281,10 @@ static int alb_set_mac_address(struct bonding *bond, void *addr)
|
||||
}
|
||||
|
||||
/* determine if the packet is NA or NS */
|
||||
static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)
|
||||
static bool alb_determine_nd(struct sk_buff *skb)
|
||||
{
|
||||
struct ipv6hdr *ip6hdr;
|
||||
struct icmp6hdr *hdr;
|
||||
const struct ipv6hdr *ip6hdr;
|
||||
const struct icmp6hdr *hdr;
|
||||
|
||||
if (!pskb_network_may_pull(skb, sizeof(*ip6hdr)))
|
||||
return true;
|
||||
@@ -1296,7 +1296,8 @@ static bool alb_determine_nd(struct sk_buff *skb, struct bonding *bond)
|
||||
if (!pskb_network_may_pull(skb, sizeof(*ip6hdr) + sizeof(*hdr)))
|
||||
return true;
|
||||
|
||||
hdr = icmp6_hdr(skb);
|
||||
ip6hdr = ipv6_hdr(skb);
|
||||
hdr = (const struct icmp6hdr *)(ip6hdr + 1);
|
||||
return hdr->icmp6_type == NDISC_NEIGHBOUR_ADVERTISEMENT ||
|
||||
hdr->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION;
|
||||
}
|
||||
@@ -1381,7 +1382,7 @@ struct slave *bond_xmit_tlb_slave_get(struct bonding *bond,
|
||||
if (!is_multicast_ether_addr(eth_data->h_dest)) {
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IPV6):
|
||||
if (alb_determine_nd(skb, bond))
|
||||
if (alb_determine_nd(skb))
|
||||
break;
|
||||
fallthrough;
|
||||
case htons(ETH_P_IP):
|
||||
@@ -1467,7 +1468,7 @@ struct slave *bond_xmit_alb_slave_get(struct bonding *bond,
|
||||
break;
|
||||
}
|
||||
|
||||
if (alb_determine_nd(skb, bond)) {
|
||||
if (alb_determine_nd(skb)) {
|
||||
do_tx_balance = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1245,7 +1245,7 @@ static void bond_peer_notify_may_events(struct bonding *bond, bool force)
|
||||
}
|
||||
|
||||
if (notified || force)
|
||||
bond->send_peer_notif--;
|
||||
WRITE_ONCE(bond->send_peer_notif, bond->send_peer_notif - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2284,7 +2284,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev,
|
||||
}
|
||||
}
|
||||
|
||||
bond->slave_cnt++;
|
||||
WRITE_ONCE(bond->slave_cnt, bond->slave_cnt + 1);
|
||||
netdev_compute_master_upper_features(bond->dev, true);
|
||||
bond_set_carrier(bond);
|
||||
|
||||
@@ -2517,9 +2517,7 @@ static int __bond_release_one(struct net_device *bond_dev,
|
||||
bond_alb_deinit_slave(bond, slave);
|
||||
}
|
||||
|
||||
if (all) {
|
||||
RCU_INIT_POINTER(bond->curr_active_slave, NULL);
|
||||
} else if (oldcurrent == slave) {
|
||||
if (!all && oldcurrent == slave) {
|
||||
/* Note that we hold RTNL over this sequence, so there
|
||||
* is no concern that another slave add/remove event
|
||||
* will interfere.
|
||||
@@ -2533,7 +2531,7 @@ static int __bond_release_one(struct net_device *bond_dev,
|
||||
|
||||
unblock_netpoll_tx();
|
||||
synchronize_rcu();
|
||||
bond->slave_cnt--;
|
||||
WRITE_ONCE(bond->slave_cnt, bond->slave_cnt - 1);
|
||||
|
||||
if (!bond_has_slaves(bond)) {
|
||||
call_netdevice_notifiers(NETDEV_CHANGEADDR, bond->dev);
|
||||
@@ -4385,13 +4383,13 @@ static int bond_open(struct net_device *bond_dev)
|
||||
|
||||
if (bond->params.arp_interval) { /* arp interval, in milliseconds. */
|
||||
queue_delayed_work(bond->wq, &bond->arp_work, 0);
|
||||
bond->recv_probe = bond_rcv_validate;
|
||||
WRITE_ONCE(bond->recv_probe, bond_rcv_validate);
|
||||
}
|
||||
|
||||
if (BOND_MODE(bond) == BOND_MODE_8023AD) {
|
||||
queue_delayed_work(bond->wq, &bond->ad_work, 0);
|
||||
/* register to receive LACPDUs */
|
||||
bond->recv_probe = bond_3ad_lacpdu_recv;
|
||||
WRITE_ONCE(bond->recv_probe, bond_3ad_lacpdu_recv);
|
||||
bond_3ad_initiate_agg_selection(bond, 1);
|
||||
|
||||
bond_for_each_slave(bond, slave, iter)
|
||||
@@ -4413,7 +4411,7 @@ static int bond_close(struct net_device *bond_dev)
|
||||
struct slave *slave;
|
||||
|
||||
bond_work_cancel_all(bond);
|
||||
bond->send_peer_notif = 0;
|
||||
WRITE_ONCE(bond->send_peer_notif, 0);
|
||||
WRITE_ONCE(bond->recv_probe, NULL);
|
||||
|
||||
/* Wait for any in-flight RX handlers */
|
||||
@@ -5118,7 +5116,7 @@ static void bond_skip_slave(struct bond_up_slave *slaves,
|
||||
if (skipslave == slaves->arr[idx]) {
|
||||
slaves->arr[idx] =
|
||||
slaves->arr[slaves->count - 1];
|
||||
slaves->count--;
|
||||
WRITE_ONCE(slaves->count, slaves->count - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1147,11 +1147,11 @@ static int bond_option_arp_interval_set(struct bonding *bond,
|
||||
*/
|
||||
if (!newval->value) {
|
||||
if (bond->params.arp_validate)
|
||||
bond->recv_probe = NULL;
|
||||
WRITE_ONCE(bond->recv_probe, NULL);
|
||||
cancel_delayed_work_sync(&bond->arp_work);
|
||||
} else {
|
||||
/* arp_validate can be set only in active-backup mode */
|
||||
bond->recv_probe = bond_rcv_validate;
|
||||
WRITE_ONCE(bond->recv_probe, bond_rcv_validate);
|
||||
cancel_delayed_work_sync(&bond->mii_work);
|
||||
queue_delayed_work(bond->wq, &bond->arp_work, 0);
|
||||
}
|
||||
|
||||
@@ -538,7 +538,7 @@ struct airoha_wdma_info {
|
||||
|
||||
/* RX queue to IRQ mapping: BIT(q) in IRQ(n) */
|
||||
#define RX_IRQ0_BANK_PIN_MASK 0x839f
|
||||
#define RX_IRQ1_BANK_PIN_MASK 0x7fe00000
|
||||
#define RX_IRQ1_BANK_PIN_MASK 0xffe00000
|
||||
#define RX_IRQ2_BANK_PIN_MASK 0x20
|
||||
#define RX_IRQ3_BANK_PIN_MASK 0x40
|
||||
#define RX_IRQ_BANK_PIN_MASK(_n) \
|
||||
|
||||
@@ -3400,7 +3400,7 @@ static void xgbe_enable_rx(struct xgbe_prv_data *pdata)
|
||||
XGMAC_IOWRITE(pdata, MAC_RQC0R, reg_val);
|
||||
|
||||
/* Enable MAC Rx */
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 1);
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0);
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 1);
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 1);
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 1);
|
||||
@@ -3411,7 +3411,6 @@ static void xgbe_disable_rx(struct xgbe_prv_data *pdata)
|
||||
unsigned int i;
|
||||
|
||||
/* Disable MAC Rx */
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, DCRCC, 0);
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, CST, 0);
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, ACS, 0);
|
||||
XGMAC_IOWRITE_BITS(pdata, MAC_RCR, RE, 0);
|
||||
|
||||
@@ -968,6 +968,8 @@ struct macb_dma_desc_ptp {
|
||||
* of the frame
|
||||
* @mapping: DMA address of the skb's fragment buffer
|
||||
* @size: size of the DMA mapped buffer
|
||||
* @fcs_len: FCS bytes appended in software, 0 or ETH_FCS_LEN, only
|
||||
* set for the last buffer of the frame
|
||||
* @mapped_as_page: true when buffer was mapped with skb_frag_dma_map(),
|
||||
* false when buffer was mapped with dma_map_single()
|
||||
*/
|
||||
@@ -975,6 +977,7 @@ struct macb_tx_skb {
|
||||
struct sk_buff *skb;
|
||||
dma_addr_t mapping;
|
||||
size_t size;
|
||||
u8 fcs_len;
|
||||
bool mapped_as_page;
|
||||
};
|
||||
|
||||
|
||||
@@ -1322,8 +1322,8 @@ static void macb_tx_error_task(struct work_struct *work)
|
||||
bp->netdev->stats.tx_packets++;
|
||||
queue->stats.tx_packets++;
|
||||
packets++;
|
||||
bp->netdev->stats.tx_bytes += skb->len;
|
||||
queue->stats.tx_bytes += skb->len;
|
||||
bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len;
|
||||
queue->stats.tx_bytes += skb->len - tx_skb->fcs_len;
|
||||
bytes += skb->len;
|
||||
}
|
||||
} else {
|
||||
@@ -1450,8 +1450,8 @@ static int macb_tx_complete(struct macb_queue *queue, int budget)
|
||||
skb->data);
|
||||
bp->netdev->stats.tx_packets++;
|
||||
queue->stats.tx_packets++;
|
||||
bp->netdev->stats.tx_bytes += skb->len;
|
||||
queue->stats.tx_bytes += skb->len;
|
||||
bp->netdev->stats.tx_bytes += skb->len - tx_skb->fcs_len;
|
||||
queue->stats.tx_bytes += skb->len - tx_skb->fcs_len;
|
||||
packets++;
|
||||
bytes += skb->len;
|
||||
}
|
||||
@@ -2199,7 +2199,8 @@ static void macb_poll_controller(struct net_device *netdev)
|
||||
static unsigned int macb_tx_map(struct macb *bp,
|
||||
struct macb_queue *queue,
|
||||
struct sk_buff *skb,
|
||||
unsigned int hdrlen)
|
||||
unsigned int hdrlen,
|
||||
u8 fcs_len)
|
||||
{
|
||||
unsigned int f, nr_frags = skb_shinfo(skb)->nr_frags;
|
||||
unsigned int len, i, tx_head = queue->tx_head;
|
||||
@@ -2284,6 +2285,7 @@ static unsigned int macb_tx_map(struct macb *bp,
|
||||
|
||||
/* This is the last buffer of the frame: save socket buffer */
|
||||
tx_skb->skb = skb;
|
||||
tx_skb->fcs_len = fcs_len;
|
||||
|
||||
/* Update TX ring: update buffer descriptors in reverse order
|
||||
* to avoid race condition
|
||||
@@ -2417,6 +2419,7 @@ static inline int macb_clear_csum(struct sk_buff *skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns a negative errno, or the FCS bytes appended (0 or ETH_FCS_LEN). */
|
||||
static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *netdev)
|
||||
{
|
||||
bool cloned = skb_cloned(*skb) || skb_header_cloned(*skb) ||
|
||||
@@ -2465,7 +2468,7 @@ static int macb_pad_and_fcs(struct sk_buff **skb, struct net_device *netdev)
|
||||
skb_put_u8(*skb, (fcs >> 16) & 0xff);
|
||||
skb_put_u8(*skb, (fcs >> 24) & 0xff);
|
||||
|
||||
return 0;
|
||||
return ETH_FCS_LEN;
|
||||
}
|
||||
|
||||
static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
|
||||
@@ -2478,6 +2481,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
|
||||
netdev_tx_t ret = NETDEV_TX_OK;
|
||||
unsigned int hdrlen;
|
||||
unsigned long flags;
|
||||
int fcs_len;
|
||||
bool is_lso;
|
||||
|
||||
if (macb_clear_csum(skb)) {
|
||||
@@ -2485,7 +2489,8 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (macb_pad_and_fcs(&skb, netdev)) {
|
||||
fcs_len = macb_pad_and_fcs(&skb, netdev);
|
||||
if (fcs_len < 0) {
|
||||
dev_kfree_skb_any(skb);
|
||||
return ret;
|
||||
}
|
||||
@@ -2548,7 +2553,7 @@ static netdev_tx_t macb_start_xmit(struct sk_buff *skb,
|
||||
}
|
||||
|
||||
/* Map socket buffer for DMA transfer */
|
||||
if (macb_tx_map(bp, queue, skb, hdrlen)) {
|
||||
if (macb_tx_map(bp, queue, skb, hdrlen, fcs_len)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
@@ -3468,6 +3468,8 @@ int rvu_get_num_lbk_chans(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define PCI_DEVID_OCTEONTX2_RVU_AFVF 0xA0F8
|
||||
|
||||
static int rvu_enable_sriov(struct rvu *rvu)
|
||||
{
|
||||
struct pci_dev *pdev = rvu->pdev;
|
||||
@@ -3486,24 +3488,27 @@ static int rvu_enable_sriov(struct rvu *rvu)
|
||||
return 0;
|
||||
pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &rvu->vf_devid);
|
||||
|
||||
chans = rvu_get_num_lbk_chans();
|
||||
if (chans < 0)
|
||||
return chans;
|
||||
|
||||
vfs = pci_sriov_get_totalvfs(pdev);
|
||||
|
||||
/* Limit VFs in case we have more VFs than LBK channels available. */
|
||||
if (vfs > chans)
|
||||
vfs = chans;
|
||||
|
||||
if (!vfs)
|
||||
return 0;
|
||||
|
||||
/* LBK channel number 63 is used for switching packets between
|
||||
* CGX mapped VFs. Hence limit LBK pairs till 62 only.
|
||||
*/
|
||||
if (vfs > 62)
|
||||
vfs = 62;
|
||||
if (rvu->vf_devid == PCI_DEVID_OCTEONTX2_RVU_AFVF) {
|
||||
chans = rvu_get_num_lbk_chans();
|
||||
if (chans < 0)
|
||||
return chans;
|
||||
|
||||
/* The last LBK channel is reserved for switching packets between
|
||||
* CGX mapped VFs. Also, since LBK VFs work in pairs, limit VF
|
||||
* count to available LBK channels minus 2.
|
||||
*/
|
||||
vfs = min(vfs, chans - 2);
|
||||
|
||||
if (vfs <= 0) {
|
||||
dev_warn(&pdev->dev,
|
||||
"Skipping SRIOV enablement, not enough LBK channels available\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save VFs number for reference in VF interrupts handlers.
|
||||
* Since interrupts might start arriving during SRIOV enablement
|
||||
|
||||
@@ -2957,10 +2957,9 @@ static void npc_mcam_free_all_entries(struct rvu *rvu, struct npc_mcam *mcam,
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable the entry */
|
||||
npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
|
||||
|
||||
if (!cn20k_dft_rl) {
|
||||
/* Disable the entry */
|
||||
npc_enable_mcam_entry(rvu, mcam, blkaddr, index, false);
|
||||
mcam->entry2pfvf_map[index] = NPC_MCAM_INVALID_MAP;
|
||||
/* Free the entry in bitmap */
|
||||
npc_mcam_clear_bit(mcam, index);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "rx.h"
|
||||
#include "en/xdp.h"
|
||||
#include <linux/bitmap.h>
|
||||
#include <net/xdp_sock_drv.h>
|
||||
#include <linux/filter.h>
|
||||
|
||||
@@ -156,6 +157,7 @@ int mlx5e_xsk_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix)
|
||||
xsk_buff_free(xsk_buffs[batch]);
|
||||
|
||||
err:
|
||||
bitmap_fill(wi->skip_release_bitmap, rq->mpwqe.pages_per_wqe);
|
||||
rq->stats->buff_alloc_err++;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@@ -410,8 +410,11 @@ static inline void mlx5e_free_rx_wqe(struct mlx5e_rq *rq,
|
||||
|
||||
static void mlx5e_xsk_free_rx_wqe(struct mlx5e_wqe_frag_info *wi)
|
||||
{
|
||||
if (!(wi->flags & BIT(MLX5E_WQE_FRAG_SKIP_RELEASE)))
|
||||
xsk_buff_free(*wi->xskp);
|
||||
if (wi->flags & BIT(MLX5E_WQE_FRAG_SKIP_RELEASE))
|
||||
return;
|
||||
|
||||
xsk_buff_free(*wi->xskp);
|
||||
wi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE);
|
||||
}
|
||||
|
||||
static void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix)
|
||||
|
||||
@@ -83,7 +83,7 @@ struct oa_tc6 {
|
||||
u16 spi_data_tx_buf_offset;
|
||||
u16 tx_credits;
|
||||
u8 rx_chunks_available;
|
||||
bool rx_buf_overflow;
|
||||
bool wait_until_start_valid;
|
||||
bool int_flag;
|
||||
bool disable_traffic;
|
||||
bool prot_ctrl;
|
||||
@@ -455,7 +455,7 @@ static int oa_tc6_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
|
||||
{
|
||||
struct oa_tc6 *tc6 = bus->priv;
|
||||
u32 regval;
|
||||
bool ret;
|
||||
int ret;
|
||||
|
||||
ret = oa_tc6_read_register(tc6, OA_TC6_PHY_STD_REG_ADDR_BASE |
|
||||
(regnum & OA_TC6_PHY_STD_REG_ADDR_MASK),
|
||||
@@ -693,6 +693,26 @@ static int oa_tc6_enable_data_transfer(struct oa_tc6 *tc6)
|
||||
return oa_tc6_write_register(tc6, OA_TC6_REG_CONFIG0, value);
|
||||
}
|
||||
|
||||
/* Called when a frame that is meant to be transmitted, is dropped. */
|
||||
static void oa_tc6_drop_tx_skb(struct oa_tc6 *tc6, struct sk_buff *skb)
|
||||
{
|
||||
if (skb) {
|
||||
tc6->netdev->stats.tx_dropped++;
|
||||
dev_kfree_skb_any(skb);
|
||||
}
|
||||
}
|
||||
|
||||
static struct sk_buff *oa_tc6_detach_waiting_tx_skb(struct oa_tc6 *tc6)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
lockdep_assert_held(&tc6->tx_skb_lock);
|
||||
skb = tc6->waiting_tx_skb;
|
||||
tc6->waiting_tx_skb = NULL;
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6)
|
||||
{
|
||||
if (tc6->rx_skb) {
|
||||
@@ -704,29 +724,39 @@ static void oa_tc6_cleanup_ongoing_rx_skb(struct oa_tc6 *tc6)
|
||||
|
||||
static void oa_tc6_cleanup_ongoing_tx_skb(struct oa_tc6 *tc6)
|
||||
{
|
||||
if (tc6->ongoing_tx_skb) {
|
||||
tc6->netdev->stats.tx_dropped++;
|
||||
kfree_skb(tc6->ongoing_tx_skb);
|
||||
tc6->ongoing_tx_skb = NULL;
|
||||
}
|
||||
oa_tc6_drop_tx_skb(tc6, tc6->ongoing_tx_skb);
|
||||
tc6->ongoing_tx_skb = NULL;
|
||||
}
|
||||
|
||||
static void oa_tc6_cleanup_waiting_tx_skb(struct oa_tc6 *tc6)
|
||||
{
|
||||
if (tc6->waiting_tx_skb) {
|
||||
tc6->netdev->stats.tx_dropped++;
|
||||
kfree_skb(tc6->waiting_tx_skb);
|
||||
tc6->waiting_tx_skb = NULL;
|
||||
}
|
||||
struct sk_buff *skb;
|
||||
|
||||
spin_lock_bh(&tc6->tx_skb_lock);
|
||||
skb = oa_tc6_detach_waiting_tx_skb(tc6);
|
||||
spin_unlock_bh(&tc6->tx_skb_lock);
|
||||
|
||||
oa_tc6_drop_tx_skb(tc6, skb);
|
||||
}
|
||||
|
||||
static void oa_tc6_free_ongoing_skbs(struct oa_tc6 *tc6)
|
||||
{
|
||||
oa_tc6_cleanup_ongoing_tx_skb(tc6);
|
||||
oa_tc6_cleanup_ongoing_rx_skb(tc6);
|
||||
}
|
||||
|
||||
static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)
|
||||
{
|
||||
oa_tc6_cleanup_ongoing_tx_skb(tc6);
|
||||
oa_tc6_cleanup_ongoing_rx_skb(tc6);
|
||||
oa_tc6_free_ongoing_skbs(tc6);
|
||||
oa_tc6_cleanup_waiting_tx_skb(tc6);
|
||||
}
|
||||
|
||||
static void oa_tc6_look_for_new_frame(struct oa_tc6 *tc6)
|
||||
{
|
||||
tc6->wait_until_start_valid = true;
|
||||
oa_tc6_cleanup_ongoing_rx_skb(tc6);
|
||||
}
|
||||
|
||||
/* If the failure is at SPI interface level, masking and clearing
|
||||
* the interrupt of the device won't work. Since SPI interrupt is
|
||||
* disabled, it should stop the repeated interrupts.
|
||||
@@ -734,9 +764,19 @@ static void oa_tc6_free_pending_skbs(struct oa_tc6 *tc6)
|
||||
static void oa_tc6_disable_traffic(struct oa_tc6 *tc6)
|
||||
{
|
||||
u32 regval = OA_TC6_INT_MASK0_ALL_INTERRUPTS;
|
||||
struct sk_buff *skb;
|
||||
|
||||
spin_lock_bh(&tc6->tx_skb_lock);
|
||||
tc6->disable_traffic = true;
|
||||
oa_tc6_free_pending_skbs(tc6);
|
||||
skb = oa_tc6_detach_waiting_tx_skb(tc6);
|
||||
spin_unlock_bh(&tc6->tx_skb_lock);
|
||||
|
||||
/* disable_traffic, when set, is a point of no return to
|
||||
* working state. Keeping the TX queues disabled.
|
||||
*/
|
||||
netif_tx_disable(tc6->netdev);
|
||||
oa_tc6_drop_tx_skb(tc6, skb);
|
||||
oa_tc6_free_ongoing_skbs(tc6);
|
||||
oa_tc6_write_register(tc6, OA_TC6_REG_INT_MASK0, regval);
|
||||
oa_tc6_read_register(tc6, OA_TC6_REG_STATUS0, ®val);
|
||||
oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, regval);
|
||||
@@ -755,6 +795,13 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* This function is called for each chunk received in a given SPI
|
||||
* transaction. In case, extended status bit is set in more than
|
||||
* one chunk, skip the write, if status0 is already cleared.
|
||||
*/
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
/* Clear the error interrupts status */
|
||||
ret = oa_tc6_write_register(tc6, OA_TC6_REG_STATUS0, value);
|
||||
if (ret) {
|
||||
@@ -764,8 +811,7 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
|
||||
}
|
||||
|
||||
if (FIELD_GET(OA_TC6_STATUS0_RX_BUFFER_OVERFLOW_ERROR, value)) {
|
||||
tc6->rx_buf_overflow = true;
|
||||
oa_tc6_cleanup_ongoing_rx_skb(tc6);
|
||||
oa_tc6_look_for_new_frame(tc6);
|
||||
net_err_ratelimited("%s: Receive buffer overflow error\n",
|
||||
tc6->netdev->name);
|
||||
return -EAGAIN;
|
||||
@@ -791,6 +837,8 @@ static int oa_tc6_process_extended_status(struct oa_tc6 *tc6)
|
||||
|
||||
static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
/* Process rx chunk footer for the following,
|
||||
* 1. tx credits
|
||||
* 2. errors if any from MAC-PHY
|
||||
@@ -801,9 +849,11 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
|
||||
footer);
|
||||
|
||||
if (FIELD_GET(OA_TC6_DATA_FOOTER_EXTENDED_STS, footer)) {
|
||||
int ret = oa_tc6_process_extended_status(tc6);
|
||||
|
||||
if (ret)
|
||||
ret = oa_tc6_process_extended_status(tc6);
|
||||
/* EAGAIN error is recoverable. Move on to check
|
||||
* HEADER and SYNC errors before returning.
|
||||
*/
|
||||
if (ret && ret != -EAGAIN)
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -821,7 +871,7 @@ static int oa_tc6_process_rx_chunk_footer(struct oa_tc6 *tc6, u32 footer)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6)
|
||||
@@ -846,13 +896,35 @@ static void oa_tc6_submit_rx_skb(struct oa_tc6 *tc6)
|
||||
tc6->rx_skb = NULL;
|
||||
}
|
||||
|
||||
static void oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length)
|
||||
/* On oversubscribed traffic condition, particularly with overwhelming rx
|
||||
* buffer overflow errors, there could be data chunk loss. If tail + length
|
||||
* goes beyond end pointer, that is an indication that the data chunk with
|
||||
* end_valid bit is lost. Time to look for a data chunk with start_valid bit.
|
||||
*
|
||||
* If rx_skb is NULL, it is time to start looking for data chunk with
|
||||
* start_bit.
|
||||
*/
|
||||
static int oa_tc6_update_rx_skb(struct oa_tc6 *tc6, u8 *payload, u8 length)
|
||||
{
|
||||
if (!tc6->rx_skb ||
|
||||
skb_tailroom(tc6->rx_skb) < length) {
|
||||
oa_tc6_look_for_new_frame(tc6);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
memcpy(skb_put(tc6->rx_skb, length), payload, length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* On overwhelming rx buffer overflow errors, due to data chunk loss, it is
|
||||
* possible that we get two data chunks with start_valid bit set, without
|
||||
* end_valid bit set in between. In this case, rx_skb would have a valid
|
||||
* buffer pointer. We should release, if a valid pointer is found before
|
||||
* allocating a new one.
|
||||
*/
|
||||
static int oa_tc6_allocate_rx_skb(struct oa_tc6 *tc6)
|
||||
{
|
||||
oa_tc6_cleanup_ongoing_rx_skb(tc6);
|
||||
tc6->rx_skb = netdev_alloc_skb_ip_align(tc6->netdev, tc6->netdev->mtu +
|
||||
ETH_HLEN + ETH_FCS_LEN);
|
||||
if (!tc6->rx_skb) {
|
||||
@@ -872,7 +944,9 @@ static int oa_tc6_prcs_complete_rx_frame(struct oa_tc6 *tc6, u8 *payload,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
oa_tc6_update_rx_skb(tc6, payload, size);
|
||||
ret = oa_tc6_update_rx_skb(tc6, payload, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
oa_tc6_submit_rx_skb(tc6);
|
||||
|
||||
@@ -887,22 +961,24 @@ static int oa_tc6_prcs_rx_frame_start(struct oa_tc6 *tc6, u8 *payload, u16 size)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
oa_tc6_update_rx_skb(tc6, payload, size);
|
||||
|
||||
return 0;
|
||||
return oa_tc6_update_rx_skb(tc6, payload, size);
|
||||
}
|
||||
|
||||
static void oa_tc6_prcs_rx_frame_end(struct oa_tc6 *tc6, u8 *payload, u16 size)
|
||||
static int oa_tc6_prcs_rx_frame_end(struct oa_tc6 *tc6, u8 *payload, u16 size)
|
||||
{
|
||||
oa_tc6_update_rx_skb(tc6, payload, size);
|
||||
int ret;
|
||||
|
||||
oa_tc6_submit_rx_skb(tc6);
|
||||
ret = oa_tc6_update_rx_skb(tc6, payload, size);
|
||||
if (!ret)
|
||||
oa_tc6_submit_rx_skb(tc6);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void oa_tc6_prcs_ongoing_rx_frame(struct oa_tc6 *tc6, u8 *payload,
|
||||
u32 footer)
|
||||
static int oa_tc6_prcs_ongoing_rx_frame(struct oa_tc6 *tc6, u8 *payload,
|
||||
u32 footer)
|
||||
{
|
||||
oa_tc6_update_rx_skb(tc6, payload, OA_TC6_CHUNK_PAYLOAD_SIZE);
|
||||
return oa_tc6_update_rx_skb(tc6, payload,
|
||||
OA_TC6_CHUNK_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
|
||||
@@ -917,10 +993,10 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
|
||||
u16 size;
|
||||
|
||||
/* Restart the new rx frame after receiving rx buffer overflow error */
|
||||
if (start_valid && tc6->rx_buf_overflow)
|
||||
tc6->rx_buf_overflow = false;
|
||||
if (start_valid && tc6->wait_until_start_valid)
|
||||
tc6->wait_until_start_valid = false;
|
||||
|
||||
if (tc6->rx_buf_overflow)
|
||||
if (tc6->wait_until_start_valid)
|
||||
return 0;
|
||||
|
||||
/* Process the chunk with complete rx frame */
|
||||
@@ -942,8 +1018,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
|
||||
/* Process the chunk with only rx frame end */
|
||||
if (end_valid && !start_valid) {
|
||||
size = end_byte_offset + 1;
|
||||
oa_tc6_prcs_rx_frame_end(tc6, data, size);
|
||||
return 0;
|
||||
return oa_tc6_prcs_rx_frame_end(tc6, data, size);
|
||||
}
|
||||
|
||||
/* Process the chunk with previous rx frame end and next rx frame
|
||||
@@ -957,6 +1032,15 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
|
||||
if (tc6->rx_skb) {
|
||||
size = end_byte_offset + 1;
|
||||
oa_tc6_prcs_rx_frame_end(tc6, data, size);
|
||||
|
||||
/* Return value from oa_tc6_prcs_rx_frame_end is not
|
||||
* checked. If it returned an error, it is to make
|
||||
* the code to look for new frame. At this stage,
|
||||
* code below is going to process a new frame. So,
|
||||
* error condition is set to false, in case it is
|
||||
* set before proceeding.
|
||||
*/
|
||||
tc6->wait_until_start_valid = false;
|
||||
}
|
||||
size = OA_TC6_CHUNK_PAYLOAD_SIZE - start_byte_offset;
|
||||
return oa_tc6_prcs_rx_frame_start(tc6,
|
||||
@@ -965,9 +1049,7 @@ static int oa_tc6_prcs_rx_chunk_payload(struct oa_tc6 *tc6, u8 *data,
|
||||
}
|
||||
|
||||
/* Process the chunk with ongoing rx frame data */
|
||||
oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer);
|
||||
|
||||
return 0;
|
||||
return oa_tc6_prcs_ongoing_rx_frame(tc6, data, footer);
|
||||
}
|
||||
|
||||
static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset)
|
||||
@@ -983,8 +1065,9 @@ static u32 oa_tc6_get_rx_chunk_footer(struct oa_tc6 *tc6, u16 footer_offset)
|
||||
static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
|
||||
{
|
||||
u16 no_of_rx_chunks = length / OA_TC6_CHUNK_SIZE;
|
||||
bool retry = false;
|
||||
int ret = 0;
|
||||
u32 footer;
|
||||
int ret;
|
||||
|
||||
/* All the rx chunks in the receive SPI data buffer are examined here */
|
||||
for (int i = 0; i < no_of_rx_chunks; i++) {
|
||||
@@ -993,8 +1076,11 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
|
||||
OA_TC6_CHUNK_PAYLOAD_SIZE);
|
||||
|
||||
ret = oa_tc6_process_rx_chunk_footer(tc6, footer);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret) {
|
||||
if (ret != -EAGAIN)
|
||||
return ret;
|
||||
retry = true;
|
||||
}
|
||||
|
||||
/* If there is a data valid chunks then process it for the
|
||||
* information needed to determine the validity and the location
|
||||
@@ -1006,12 +1092,35 @@ static int oa_tc6_process_spi_data_rx_buf(struct oa_tc6 *tc6, u16 length)
|
||||
|
||||
ret = oa_tc6_prcs_rx_chunk_payload(tc6, payload,
|
||||
footer);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (ret) {
|
||||
if (ret != -ENOMEM && ret != -EAGAIN)
|
||||
return ret;
|
||||
retry = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
/* Not bailing out on recoverable error codes, -EAGAIN and
|
||||
* -ENOMEM. If subsequent loop iterations, if any, succeeds,
|
||||
* error code would be overwritten. retry flag helps to
|
||||
* make the caller to continue and retry. Since recovery
|
||||
* action for -ENOMEM and -EAGAIN are same, we are returning
|
||||
* one of the error codes, that is -EAGAIN.
|
||||
*
|
||||
* Successful recovery depends on how small the frames are,
|
||||
* how many chunks, among the received chunks triggered the
|
||||
* error, whether data is intact even with error conditions.
|
||||
* As a result, there is no single, best method to recover
|
||||
* most data when error conditions hit. We do our best by
|
||||
* processing all the chunks with good "footer header" and
|
||||
* "data valid" bit set.
|
||||
*/
|
||||
if (retry) {
|
||||
ret = -EAGAIN;
|
||||
oa_tc6_look_for_new_frame(tc6);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __be32 oa_tc6_prepare_data_header(bool data_valid, bool start_valid,
|
||||
@@ -1173,12 +1282,9 @@ static int oa_tc6_try_spi_transfer(struct oa_tc6 *tc6)
|
||||
}
|
||||
|
||||
ret = oa_tc6_process_spi_data_rx_buf(tc6, spi_len);
|
||||
if (ret) {
|
||||
if (ret == -EAGAIN)
|
||||
continue;
|
||||
|
||||
oa_tc6_cleanup_ongoing_tx_skb(tc6);
|
||||
oa_tc6_cleanup_ongoing_rx_skb(tc6);
|
||||
if (ret && ret != -EAGAIN) {
|
||||
oa_tc6_free_ongoing_skbs(tc6);
|
||||
netdev_err(tc6->netdev, "Device error: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
@@ -1200,15 +1306,20 @@ static irqreturn_t oa_tc6_macphy_threaded_irq(int irq, void *data)
|
||||
* no need to attempt spi transfer, once it fails. Pending skbs
|
||||
* are already freed.
|
||||
*/
|
||||
if (!tc6->disable_traffic) {
|
||||
while (tc6->int_flag ||
|
||||
(tc6->waiting_tx_skb && tc6->tx_credits)) {
|
||||
ret = oa_tc6_try_spi_transfer(tc6);
|
||||
if (ret) {
|
||||
disable_irq_nosync(tc6->spi->irq);
|
||||
oa_tc6_disable_traffic(tc6);
|
||||
break;
|
||||
}
|
||||
spin_lock_bh(&tc6->tx_skb_lock);
|
||||
if (tc6->disable_traffic) {
|
||||
spin_unlock_bh(&tc6->tx_skb_lock);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
spin_unlock_bh(&tc6->tx_skb_lock);
|
||||
|
||||
while (tc6->int_flag ||
|
||||
(tc6->waiting_tx_skb && tc6->tx_credits)) {
|
||||
ret = oa_tc6_try_spi_transfer(tc6);
|
||||
if (ret) {
|
||||
disable_irq_nosync(tc6->spi->irq);
|
||||
oa_tc6_disable_traffic(tc6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1287,23 +1398,30 @@ EXPORT_SYMBOL_GPL(oa_tc6_zero_align_receive_frame_enable);
|
||||
* @tc6: oa_tc6 struct.
|
||||
* @skb: socket buffer in which the ethernet frame is stored.
|
||||
*
|
||||
* Return: NETDEV_TX_OK if the transmit ethernet frame skb added in the tx_skb_q
|
||||
* otherwise returns NETDEV_TX_BUSY.
|
||||
* Return: NETDEV_TX_OK either on successful queueing of the packet for
|
||||
* transmission, or on packet getting dropped. Packet can be dropped due to
|
||||
* failure in linearizing the buffer or disable_traffic is set due to
|
||||
* earlier fatal error. Returns NETDEV_TX_BUSY when there is no room
|
||||
* to queue the packet.
|
||||
*/
|
||||
netdev_tx_t oa_tc6_start_xmit(struct oa_tc6 *tc6, struct sk_buff *skb)
|
||||
{
|
||||
if (tc6->disable_traffic || tc6->waiting_tx_skb) {
|
||||
netif_stop_queue(tc6->netdev);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
|
||||
if (skb_linearize(skb)) {
|
||||
dev_kfree_skb_any(skb);
|
||||
tc6->netdev->stats.tx_dropped++;
|
||||
oa_tc6_drop_tx_skb(tc6, skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
spin_lock_bh(&tc6->tx_skb_lock);
|
||||
if (tc6->waiting_tx_skb) {
|
||||
netif_stop_queue(tc6->netdev);
|
||||
spin_unlock_bh(&tc6->tx_skb_lock);
|
||||
return NETDEV_TX_BUSY;
|
||||
}
|
||||
if (tc6->disable_traffic) {
|
||||
spin_unlock_bh(&tc6->tx_skb_lock);
|
||||
oa_tc6_drop_tx_skb(tc6, skb);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
tc6->waiting_tx_skb = skb;
|
||||
spin_unlock_bh(&tc6->tx_skb_lock);
|
||||
|
||||
@@ -1462,8 +1580,10 @@ EXPORT_SYMBOL_GPL(oa_tc6_init);
|
||||
*/
|
||||
void oa_tc6_exit(struct oa_tc6 *tc6)
|
||||
{
|
||||
tc6->disable_traffic = true;
|
||||
disable_irq(tc6->spi->irq);
|
||||
spin_lock_bh(&tc6->tx_skb_lock);
|
||||
tc6->disable_traffic = true;
|
||||
spin_unlock_bh(&tc6->tx_skb_lock);
|
||||
oa_tc6_phy_exit(tc6);
|
||||
oa_tc6_free_pending_skbs(tc6);
|
||||
}
|
||||
|
||||
@@ -3676,6 +3676,14 @@ static int stmmac_hw_setup(struct net_device *dev)
|
||||
/* Initialize MTL*/
|
||||
stmmac_mtl_configuration(priv);
|
||||
|
||||
/* Apply the RX packet parser table */
|
||||
if (priv->tc_entries) {
|
||||
ret = stmmac_rxp_config(priv, priv->hw->pcsr, priv->tc_entries,
|
||||
priv->tc_entries_max);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize Safety Features */
|
||||
stmmac_safety_feat_configuration(priv);
|
||||
|
||||
@@ -4319,6 +4327,7 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
|
||||
/**
|
||||
* stmmac_tso_allocator - close entry point of the driver
|
||||
* @priv: driver private structure
|
||||
* @entry: TX queue buffer index
|
||||
* @des: buffer start address
|
||||
* @total_len: total length to fill in descriptors
|
||||
* @last_segment: condition for the last descriptor
|
||||
@@ -4327,8 +4336,9 @@ static bool stmmac_vlan_insert(struct stmmac_priv *priv, struct sk_buff *skb,
|
||||
* This function fills descriptor and request new descriptors according to
|
||||
* buffer length to fill
|
||||
*/
|
||||
static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
|
||||
int total_len, bool last_segment, u32 queue)
|
||||
static void stmmac_tso_allocator(struct stmmac_priv *priv, u32 *entry,
|
||||
dma_addr_t des, int total_len,
|
||||
bool last_segment, u32 queue)
|
||||
{
|
||||
struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue];
|
||||
struct dma_desc *desc;
|
||||
@@ -4340,14 +4350,13 @@ static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des,
|
||||
while (tmp_len > 0) {
|
||||
dma_addr_t curr_addr;
|
||||
|
||||
tx_q->cur_tx = STMMAC_NEXT_ENTRY(tx_q->cur_tx,
|
||||
priv->dma_conf.dma_tx_size);
|
||||
WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]);
|
||||
*entry = STMMAC_NEXT_ENTRY(*entry, priv->dma_conf.dma_tx_size);
|
||||
WARN_ON(tx_q->tx_skbuff[*entry]);
|
||||
|
||||
if (tx_q->tbs & STMMAC_TBS_AVAIL)
|
||||
desc = &tx_q->dma_entx[tx_q->cur_tx].basic;
|
||||
desc = &tx_q->dma_entx[*entry].basic;
|
||||
else
|
||||
desc = &tx_q->dma_tx[tx_q->cur_tx];
|
||||
desc = &tx_q->dma_tx[*entry];
|
||||
|
||||
curr_addr = des + (total_len - tmp_len);
|
||||
stmmac_set_desc_addr(priv, desc, curr_addr);
|
||||
@@ -4486,7 +4495,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct dma_desc *desc, *first, *mss_desc = NULL;
|
||||
struct stmmac_priv *priv = netdev_priv(dev);
|
||||
unsigned int first_entry, tx_packets;
|
||||
unsigned int first_entry, entry, tx_packets;
|
||||
struct stmmac_txq_stats *txq_stats;
|
||||
struct stmmac_tx_queue *tx_q;
|
||||
bool set_ic, is_last_segment;
|
||||
@@ -4549,22 +4558,24 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
}
|
||||
|
||||
first_entry = tx_q->cur_tx;
|
||||
WARN_ON(tx_q->tx_skbuff[first_entry]);
|
||||
entry = first_entry;
|
||||
|
||||
WARN_ON(tx_q->tx_skbuff[entry]);
|
||||
|
||||
if (tx_q->tbs & STMMAC_TBS_AVAIL)
|
||||
desc = &tx_q->dma_entx[first_entry].basic;
|
||||
desc = &tx_q->dma_entx[entry].basic;
|
||||
else
|
||||
desc = &tx_q->dma_tx[first_entry];
|
||||
desc = &tx_q->dma_tx[entry];
|
||||
first = desc;
|
||||
|
||||
/* first descriptor: fill Headers on Buf1 */
|
||||
des = dma_map_single(priv->device, skb->data, skb_headlen(skb),
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(priv->device, des))
|
||||
goto dma_map_err;
|
||||
goto error;
|
||||
|
||||
stmmac_set_desc_addr(priv, first, des);
|
||||
stmmac_tso_allocator(priv, des + proto_hdr_len, pay_len,
|
||||
stmmac_tso_allocator(priv, &entry, des + proto_hdr_len, pay_len,
|
||||
(nfrags == 0), queue);
|
||||
|
||||
/* In case two or more DMA transmit descriptors are allocated for this
|
||||
@@ -4579,8 +4590,7 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
* this DMA buffer right after the DMA engine completely finishes the
|
||||
* full buffer transmission.
|
||||
*/
|
||||
stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des, skb_headlen(skb),
|
||||
false);
|
||||
stmmac_set_tx_skb_dma_entry(tx_q, entry, des, skb_headlen(skb), false);
|
||||
|
||||
/* Prepare fragments */
|
||||
for (i = 0; i < nfrags; i++) {
|
||||
@@ -4590,14 +4600,15 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
skb_frag_size(frag),
|
||||
DMA_TO_DEVICE);
|
||||
if (dma_mapping_error(priv->device, des))
|
||||
goto dma_map_err;
|
||||
goto error_dma_unmap;
|
||||
|
||||
stmmac_tso_allocator(priv, des, skb_frag_size(frag),
|
||||
stmmac_tso_allocator(priv, &entry, des, skb_frag_size(frag),
|
||||
(i == nfrags - 1), queue);
|
||||
|
||||
stmmac_set_tx_skb_dma_entry(tx_q, tx_q->cur_tx, des,
|
||||
stmmac_set_tx_skb_dma_entry(tx_q, entry, des,
|
||||
skb_frag_size(frag), true);
|
||||
}
|
||||
tx_q->cur_tx = entry;
|
||||
|
||||
stmmac_set_tx_dma_last_segment(tx_q, tx_q->cur_tx);
|
||||
|
||||
@@ -4702,7 +4713,19 @@ static netdev_tx_t stmmac_tso_xmit(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
dma_map_err:
|
||||
error_dma_unmap:
|
||||
for (;;) {
|
||||
desc = stmmac_get_tx_desc(priv, tx_q, first_entry);
|
||||
stmmac_release_tx_desc(priv, desc, priv->descriptor_mode);
|
||||
stmmac_free_tx_buffer(priv, &priv->dma_conf, queue,
|
||||
first_entry);
|
||||
if (first_entry == entry)
|
||||
break;
|
||||
|
||||
first_entry = STMMAC_NEXT_ENTRY(first_entry,
|
||||
priv->dma_conf.dma_tx_size);
|
||||
}
|
||||
error:
|
||||
dev_err(priv->device, "Tx dma map failed\n");
|
||||
dev_kfree_skb(skb);
|
||||
priv->xstats.tx_dropped++;
|
||||
|
||||
@@ -318,6 +318,11 @@ static int gtp_inner_proto(struct sk_buff *skb, unsigned int hdrlen,
|
||||
static int gtp_rx(struct pdp_ctx *pctx, struct sk_buff *skb,
|
||||
unsigned int hdrlen, unsigned int role, __u16 inner_proto)
|
||||
{
|
||||
if (skb_is_gso(skb)) {
|
||||
netdev_dbg(pctx->dev, "GSO is not supported in GTP\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!gtp_check_ms(skb, pctx, hdrlen, role, inner_proto)) {
|
||||
netdev_dbg(pctx->dev, "No PDP ctx for this MS\n");
|
||||
return 1;
|
||||
|
||||
@@ -848,7 +848,6 @@ static int ipvlan_device_event(struct notifier_block *unused,
|
||||
__ipvlan_link_delete(net, ipvlan->dev, &lst_kill);
|
||||
}
|
||||
|
||||
unregister_netdevice_many(&lst_kill);
|
||||
break;
|
||||
}
|
||||
case NETDEV_FEAT_CHANGE:
|
||||
@@ -899,6 +898,9 @@ static int ipvlan_device_event(struct notifier_block *unused,
|
||||
|
||||
mutex_unlock(&port->pnodes_lock);
|
||||
|
||||
/* Avoid invoking nested netdevice notifiers under pnodes_lock. */
|
||||
unregister_netdevice_many(&lst_kill);
|
||||
|
||||
ipvlan_port_put(port);
|
||||
|
||||
return ret;
|
||||
|
||||
@@ -127,8 +127,10 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
|
||||
{
|
||||
struct ntb_netdev_queue *q = qp_data;
|
||||
struct ntb_netdev *dev = q->ntdev;
|
||||
struct pcpu_sw_netstats *tstats;
|
||||
struct sk_buff *skb, *new_skb;
|
||||
struct net_device *ndev;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
ndev = dev->ndev;
|
||||
@@ -139,17 +141,20 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
|
||||
netdev_dbg(ndev, "%s: %d byte payload received\n", __func__, len);
|
||||
|
||||
if (len < 0) {
|
||||
ndev->stats.rx_errors++;
|
||||
ndev->stats.rx_length_errors++;
|
||||
DEV_STATS_INC(ndev, rx_errors);
|
||||
DEV_STATS_INC(ndev, rx_length_errors);
|
||||
goto enqueue_again;
|
||||
}
|
||||
|
||||
ndev->stats.rx_packets++;
|
||||
ndev->stats.rx_bytes += len;
|
||||
tstats = this_cpu_ptr(ndev->tstats);
|
||||
flags = u64_stats_update_begin_irqsave(&tstats->syncp);
|
||||
u64_stats_inc(&tstats->rx_packets);
|
||||
u64_stats_add(&tstats->rx_bytes, len);
|
||||
u64_stats_update_end_irqrestore(&tstats->syncp, flags);
|
||||
|
||||
new_skb = netdev_alloc_skb(ndev, ndev->mtu + ETH_HLEN);
|
||||
if (!new_skb) {
|
||||
ndev->stats.rx_dropped++;
|
||||
DEV_STATS_INC(ndev, rx_dropped);
|
||||
goto enqueue_again;
|
||||
}
|
||||
|
||||
@@ -166,8 +171,8 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data,
|
||||
rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN);
|
||||
if (rc) {
|
||||
dev_kfree_skb_any(skb);
|
||||
ndev->stats.rx_errors++;
|
||||
ndev->stats.rx_fifo_errors++;
|
||||
DEV_STATS_INC(ndev, rx_errors);
|
||||
DEV_STATS_INC(ndev, rx_fifo_errors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,25 +215,39 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
|
||||
{
|
||||
struct ntb_netdev_queue *q = qp_data;
|
||||
struct ntb_netdev *dev = q->ntdev;
|
||||
struct pcpu_sw_netstats *tstats;
|
||||
struct net_device *ndev;
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
bool registered;
|
||||
|
||||
ndev = dev->ndev;
|
||||
skb = data;
|
||||
if (!skb || !ndev)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
registered = READ_ONCE(ndev->reg_state) == NETREG_REGISTERED;
|
||||
if (!registered)
|
||||
goto free_skb;
|
||||
|
||||
if (len > 0) {
|
||||
ndev->stats.tx_packets++;
|
||||
ndev->stats.tx_bytes += skb->len;
|
||||
/* The memcpy kthread can migrate, so pin the per-CPU update. */
|
||||
tstats = get_cpu_ptr(ndev->tstats);
|
||||
flags = u64_stats_update_begin_irqsave(&tstats->syncp);
|
||||
u64_stats_inc(&tstats->tx_packets);
|
||||
u64_stats_add(&tstats->tx_bytes, skb->len);
|
||||
u64_stats_update_end_irqrestore(&tstats->syncp, flags);
|
||||
put_cpu_ptr(ndev->tstats);
|
||||
} else {
|
||||
ndev->stats.tx_errors++;
|
||||
ndev->stats.tx_aborted_errors++;
|
||||
DEV_STATS_INC(ndev, tx_errors);
|
||||
DEV_STATS_INC(ndev, tx_aborted_errors);
|
||||
}
|
||||
|
||||
free_skb:
|
||||
dev_kfree_skb_any(skb);
|
||||
|
||||
if (ntb_transport_tx_free_entry(qp) >= tx_start) {
|
||||
if (registered && ntb_transport_tx_free_entry(qp) >= tx_start) {
|
||||
/* Make sure anybody stopping the queue after this sees the new
|
||||
* value of ntb_transport_tx_free_entry()
|
||||
*/
|
||||
@@ -237,6 +256,7 @@ static void ntb_netdev_tx_handler(struct ntb_transport_qp *qp, void *qp_data,
|
||||
ntb_transport_link_query(q->qp))
|
||||
netif_wake_subqueue(ndev, q->qid);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static const struct ntb_queue_handlers ntb_netdev_handlers = {
|
||||
@@ -277,7 +297,7 @@ static netdev_tx_t ntb_netdev_start_xmit(struct sk_buff *skb,
|
||||
|
||||
drop:
|
||||
dev_kfree_skb_any(skb);
|
||||
ndev->stats.tx_dropped++;
|
||||
DEV_STATS_INC(ndev, tx_dropped);
|
||||
return NETDEV_TX_OK;
|
||||
}
|
||||
|
||||
@@ -647,6 +667,7 @@ static int ntb_netdev_probe(struct device *client_dev)
|
||||
}
|
||||
|
||||
ndev->features = NETIF_F_HIGHDMA;
|
||||
ndev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
|
||||
|
||||
ndev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
|
||||
|
||||
|
||||
@@ -63,8 +63,6 @@ struct asyncppp {
|
||||
|
||||
struct tasklet_struct tsk;
|
||||
|
||||
refcount_t refcnt;
|
||||
struct completion dead;
|
||||
struct ppp_channel chan; /* interface to generic ppp layer */
|
||||
unsigned char obuf[OBUFSIZE];
|
||||
};
|
||||
@@ -114,38 +112,6 @@ static const struct ppp_channel_ops async_ops = {
|
||||
* Routines implementing the PPP line discipline.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We have a potential race on dereferencing tty->disc_data,
|
||||
* because the tty layer provides no locking at all - thus one
|
||||
* cpu could be running ppp_asynctty_receive while another
|
||||
* calls ppp_asynctty_close, which zeroes tty->disc_data and
|
||||
* frees the memory that ppp_asynctty_receive is using. The best
|
||||
* way to fix this is to use a rwlock in the tty struct, but for now
|
||||
* we use a single global rwlock for all ttys in ppp line discipline.
|
||||
*
|
||||
* FIXME: this is no longer true. The _close path for the ldisc is
|
||||
* now guaranteed to be sane.
|
||||
*/
|
||||
static DEFINE_RWLOCK(disc_data_lock);
|
||||
|
||||
static struct asyncppp *ap_get(struct tty_struct *tty)
|
||||
{
|
||||
struct asyncppp *ap;
|
||||
|
||||
read_lock(&disc_data_lock);
|
||||
ap = tty->disc_data;
|
||||
if (ap != NULL)
|
||||
refcount_inc(&ap->refcnt);
|
||||
read_unlock(&disc_data_lock);
|
||||
return ap;
|
||||
}
|
||||
|
||||
static void ap_put(struct asyncppp *ap)
|
||||
{
|
||||
if (refcount_dec_and_test(&ap->refcnt))
|
||||
complete(&ap->dead);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a tty is put into PPP line discipline. Called in process
|
||||
* context.
|
||||
@@ -180,9 +146,6 @@ ppp_asynctty_open(struct tty_struct *tty)
|
||||
skb_queue_head_init(&ap->rqueue);
|
||||
tasklet_setup(&ap->tsk, ppp_async_process);
|
||||
|
||||
refcount_set(&ap->refcnt, 1);
|
||||
init_completion(&ap->dead);
|
||||
|
||||
ap->chan.private = ap;
|
||||
ap->chan.ops = &async_ops;
|
||||
ap->chan.mtu = PPP_MRU;
|
||||
@@ -203,34 +166,18 @@ ppp_asynctty_open(struct tty_struct *tty)
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the tty is put into another line discipline
|
||||
* or it hangs up. We have to wait for any cpu currently
|
||||
* executing in any of the other ppp_asynctty_* routines to
|
||||
* finish before we can call ppp_unregister_channel and free
|
||||
* the asyncppp struct. This routine must be called from
|
||||
* process context, not interrupt or softirq context.
|
||||
* Called when the tty is put into another line discipline or it hangs up.
|
||||
* This call is serialized against other ldisc functions.
|
||||
*/
|
||||
static void
|
||||
ppp_asynctty_close(struct tty_struct *tty)
|
||||
{
|
||||
struct asyncppp *ap;
|
||||
struct asyncppp *ap = tty->disc_data;
|
||||
|
||||
write_lock_irq(&disc_data_lock);
|
||||
ap = tty->disc_data;
|
||||
tty->disc_data = NULL;
|
||||
write_unlock_irq(&disc_data_lock);
|
||||
if (!ap)
|
||||
return;
|
||||
|
||||
/*
|
||||
* We have now ensured that nobody can start using ap from now
|
||||
* on, but we have to wait for all existing users to finish.
|
||||
* Note that ppp_unregister_channel ensures that no calls to
|
||||
* our channel ops (i.e. ppp_async_send/ioctl) are in progress
|
||||
* by the time it returns.
|
||||
*/
|
||||
if (!refcount_dec_and_test(&ap->refcnt))
|
||||
wait_for_completion(&ap->dead);
|
||||
tty->disc_data = NULL;
|
||||
tasklet_kill(&ap->tsk);
|
||||
|
||||
ppp_unregister_channel(&ap->chan);
|
||||
@@ -240,17 +187,6 @@ ppp_asynctty_close(struct tty_struct *tty)
|
||||
kfree(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on tty hangup in process context.
|
||||
*
|
||||
* Wait for I/O to driver to complete and unregister PPP channel.
|
||||
* This is already done by the close routine, so just call that.
|
||||
*/
|
||||
static void ppp_asynctty_hangup(struct tty_struct *tty)
|
||||
{
|
||||
ppp_asynctty_close(tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read does nothing - no data is ever available this way.
|
||||
* Pppd reads and writes packets via /dev/ppp instead.
|
||||
@@ -281,7 +217,7 @@ ppp_asynctty_write(struct tty_struct *tty, struct file *file, const u8 *buf,
|
||||
static int
|
||||
ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct asyncppp *ap = ap_get(tty);
|
||||
struct asyncppp *ap = tty->disc_data;
|
||||
int err, val;
|
||||
int __user *p = (int __user *)arg;
|
||||
|
||||
@@ -322,7 +258,6 @@ ppp_asynctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
|
||||
err = tty_mode_ioctl(tty, cmd, arg);
|
||||
}
|
||||
|
||||
ap_put(ap);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -331,7 +266,7 @@ static void
|
||||
ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
|
||||
size_t count)
|
||||
{
|
||||
struct asyncppp *ap = ap_get(tty);
|
||||
struct asyncppp *ap = tty->disc_data;
|
||||
unsigned long flags;
|
||||
|
||||
if (!ap)
|
||||
@@ -341,21 +276,19 @@ ppp_asynctty_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
|
||||
spin_unlock_irqrestore(&ap->recv_lock, flags);
|
||||
if (!skb_queue_empty(&ap->rqueue))
|
||||
tasklet_schedule(&ap->tsk);
|
||||
ap_put(ap);
|
||||
tty_unthrottle(tty);
|
||||
}
|
||||
|
||||
static void
|
||||
ppp_asynctty_wakeup(struct tty_struct *tty)
|
||||
{
|
||||
struct asyncppp *ap = ap_get(tty);
|
||||
struct asyncppp *ap = tty->disc_data;
|
||||
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
if (!ap)
|
||||
return;
|
||||
set_bit(XMIT_WAKEUP, &ap->xmit_flags);
|
||||
tasklet_schedule(&ap->tsk);
|
||||
ap_put(ap);
|
||||
}
|
||||
|
||||
|
||||
@@ -365,7 +298,6 @@ static struct tty_ldisc_ops ppp_ldisc = {
|
||||
.name = "ppp",
|
||||
.open = ppp_asynctty_open,
|
||||
.close = ppp_asynctty_close,
|
||||
.hangup = ppp_asynctty_hangup,
|
||||
.read = ppp_asynctty_read,
|
||||
.write = ppp_asynctty_write,
|
||||
.ioctl = ppp_asynctty_ioctl,
|
||||
|
||||
@@ -38,11 +38,9 @@
|
||||
#include <linux/ppp-ioctl.h>
|
||||
#include <linux/ppp_channel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/unaligned.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
@@ -67,8 +65,6 @@ struct syncppp {
|
||||
|
||||
struct tasklet_struct tsk;
|
||||
|
||||
refcount_t refcnt;
|
||||
struct completion dead_cmp;
|
||||
struct ppp_channel chan; /* interface to generic ppp layer */
|
||||
};
|
||||
|
||||
@@ -116,37 +112,6 @@ ppp_print_buffer (const char *name, const __u8 *buf, int count)
|
||||
* Routines implementing the synchronous PPP line discipline.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We have a potential race on dereferencing tty->disc_data,
|
||||
* because the tty layer provides no locking at all - thus one
|
||||
* cpu could be running ppp_synctty_receive while another
|
||||
* calls ppp_synctty_close, which zeroes tty->disc_data and
|
||||
* frees the memory that ppp_synctty_receive is using. The best
|
||||
* way to fix this is to use a rwlock in the tty struct, but for now
|
||||
* we use a single global rwlock for all ttys in ppp line discipline.
|
||||
*
|
||||
* FIXME: Fixed in tty_io nowadays.
|
||||
*/
|
||||
static DEFINE_RWLOCK(disc_data_lock);
|
||||
|
||||
static struct syncppp *sp_get(struct tty_struct *tty)
|
||||
{
|
||||
struct syncppp *ap;
|
||||
|
||||
read_lock(&disc_data_lock);
|
||||
ap = tty->disc_data;
|
||||
if (ap != NULL)
|
||||
refcount_inc(&ap->refcnt);
|
||||
read_unlock(&disc_data_lock);
|
||||
return ap;
|
||||
}
|
||||
|
||||
static void sp_put(struct syncppp *ap)
|
||||
{
|
||||
if (refcount_dec_and_test(&ap->refcnt))
|
||||
complete(&ap->dead_cmp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a tty is put into sync-PPP line discipline.
|
||||
*/
|
||||
@@ -177,9 +142,6 @@ ppp_sync_open(struct tty_struct *tty)
|
||||
skb_queue_head_init(&ap->rqueue);
|
||||
tasklet_setup(&ap->tsk, ppp_sync_process);
|
||||
|
||||
refcount_set(&ap->refcnt, 1);
|
||||
init_completion(&ap->dead_cmp);
|
||||
|
||||
ap->chan.private = ap;
|
||||
ap->chan.ops = &sync_ops;
|
||||
ap->chan.mtu = PPP_MRU;
|
||||
@@ -201,34 +163,18 @@ ppp_sync_open(struct tty_struct *tty)
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the tty is put into another line discipline
|
||||
* or it hangs up. We have to wait for any cpu currently
|
||||
* executing in any of the other ppp_synctty_* routines to
|
||||
* finish before we can call ppp_unregister_channel and free
|
||||
* the syncppp struct. This routine must be called from
|
||||
* process context, not interrupt or softirq context.
|
||||
* Called when the tty is put into another line discipline or it hangs up.
|
||||
* This call is serialized against other ldisc functions.
|
||||
*/
|
||||
static void
|
||||
ppp_sync_close(struct tty_struct *tty)
|
||||
{
|
||||
struct syncppp *ap;
|
||||
struct syncppp *ap = tty->disc_data;
|
||||
|
||||
write_lock_irq(&disc_data_lock);
|
||||
ap = tty->disc_data;
|
||||
tty->disc_data = NULL;
|
||||
write_unlock_irq(&disc_data_lock);
|
||||
if (!ap)
|
||||
return;
|
||||
|
||||
/*
|
||||
* We have now ensured that nobody can start using ap from now
|
||||
* on, but we have to wait for all existing users to finish.
|
||||
* Note that ppp_unregister_channel ensures that no calls to
|
||||
* our channel ops (i.e. ppp_sync_send/ioctl) are in progress
|
||||
* by the time it returns.
|
||||
*/
|
||||
if (!refcount_dec_and_test(&ap->refcnt))
|
||||
wait_for_completion(&ap->dead_cmp);
|
||||
tty->disc_data = NULL;
|
||||
tasklet_kill(&ap->tsk);
|
||||
|
||||
ppp_unregister_channel(&ap->chan);
|
||||
@@ -237,17 +183,6 @@ ppp_sync_close(struct tty_struct *tty)
|
||||
kfree(ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called on tty hangup in process context.
|
||||
*
|
||||
* Wait for I/O to driver to complete and unregister PPP channel.
|
||||
* This is already done by the close routine, so just call that.
|
||||
*/
|
||||
static void ppp_sync_hangup(struct tty_struct *tty)
|
||||
{
|
||||
ppp_sync_close(tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read does nothing - no data is ever available this way.
|
||||
* Pppd reads and writes packets via /dev/ppp instead.
|
||||
@@ -273,7 +208,7 @@ ppp_sync_write(struct tty_struct *tty, struct file *file, const u8 *buf,
|
||||
static int
|
||||
ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct syncppp *ap = sp_get(tty);
|
||||
struct syncppp *ap = tty->disc_data;
|
||||
int __user *p = (int __user *)arg;
|
||||
int err, val;
|
||||
|
||||
@@ -314,7 +249,6 @@ ppp_synctty_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
|
||||
break;
|
||||
}
|
||||
|
||||
sp_put(ap);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -323,7 +257,7 @@ static void
|
||||
ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
|
||||
size_t count)
|
||||
{
|
||||
struct syncppp *ap = sp_get(tty);
|
||||
struct syncppp *ap = tty->disc_data;
|
||||
unsigned long flags;
|
||||
|
||||
if (!ap)
|
||||
@@ -333,21 +267,19 @@ ppp_sync_receive(struct tty_struct *tty, const u8 *buf, const u8 *cflags,
|
||||
spin_unlock_irqrestore(&ap->recv_lock, flags);
|
||||
if (!skb_queue_empty(&ap->rqueue))
|
||||
tasklet_schedule(&ap->tsk);
|
||||
sp_put(ap);
|
||||
tty_unthrottle(tty);
|
||||
}
|
||||
|
||||
static void
|
||||
ppp_sync_wakeup(struct tty_struct *tty)
|
||||
{
|
||||
struct syncppp *ap = sp_get(tty);
|
||||
struct syncppp *ap = tty->disc_data;
|
||||
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
if (!ap)
|
||||
return;
|
||||
set_bit(XMIT_WAKEUP, &ap->xmit_flags);
|
||||
tasklet_schedule(&ap->tsk);
|
||||
sp_put(ap);
|
||||
}
|
||||
|
||||
|
||||
@@ -357,7 +289,6 @@ static struct tty_ldisc_ops ppp_sync_ldisc = {
|
||||
.name = "pppsync",
|
||||
.open = ppp_sync_open,
|
||||
.close = ppp_sync_close,
|
||||
.hangup = ppp_sync_hangup,
|
||||
.read = ppp_sync_read,
|
||||
.write = ppp_sync_write,
|
||||
.ioctl = ppp_synctty_ioctl,
|
||||
|
||||
@@ -1446,6 +1446,7 @@ static const struct usb_device_id products[] = {
|
||||
{QMI_QUIRK_SET_DTR(0x2c7c, 0x0316, 3)}, /* Quectel RG255C */
|
||||
{QMI_QUIRK_SET_DTR(0x2cb7, 0x0104, 4)}, /* Fibocom NL678 series */
|
||||
{QMI_QUIRK_SET_DTR(0x2cb7, 0x0112, 0)}, /* Fibocom FG132 */
|
||||
{QMI_QUIRK_SET_DTR(0x04b7, 0x8217, 8)}, /* Compal EXM-G1x */
|
||||
{QMI_FIXED_INTF(0x0489, 0xe0b4, 0)}, /* Foxconn T77W968 LTE */
|
||||
{QMI_FIXED_INTF(0x0489, 0xe0b5, 0)}, /* Foxconn T77W968 LTE with eSIM support*/
|
||||
{QMI_FIXED_INTF(0x2692, 0x9025, 4)}, /* Cellient MPL200 (rebranded Qualcomm 05c6:9025) */
|
||||
|
||||
@@ -354,6 +354,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,
|
||||
NL_SET_ERR_MSG_MOD(extack, "IPv4 multicast source address is not allowed");
|
||||
return false;
|
||||
}
|
||||
if (ipv4_is_zeronet(nla_get_in_addr(attr))) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "IPv4 all-zeros source address is not allowed");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
case htons(ETH_P_IPV6): {
|
||||
@@ -368,6 +372,10 @@ static bool vxlan_mdb_is_valid_source(const struct nlattr *attr, __be16 proto,
|
||||
NL_SET_ERR_MSG_MOD(extack, "IPv6 multicast source address is not allowed");
|
||||
return false;
|
||||
}
|
||||
if (ipv6_addr_any(&src)) {
|
||||
NL_SET_ERR_MSG_MOD(extack, "IPv6 all-zeros source address is not allowed");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -826,7 +826,7 @@ static void mpc_action_go_ready(fsm_instance *fsm, int event, void *arg)
|
||||
|
||||
fsm_deltimer(&grp->timer);
|
||||
|
||||
if (grp->saved_xid2->xid2_flag2 == 0x40) {
|
||||
if (priv->xid->xid2_flag2 == 0x40) {
|
||||
priv->xid->xid2_flag2 = 0x00;
|
||||
if (grp->estconnfunc) {
|
||||
grp->estconnfunc(grp->port_num, 1,
|
||||
@@ -1636,7 +1636,6 @@ static int mpc_validate_xid(struct mpcg_info *mpcginfo)
|
||||
"The XID used in the MPC protocol is not valid, "
|
||||
"rc = %d\n", rc);
|
||||
priv->xid->xid2_flag2 = 0x40;
|
||||
grp->saved_xid2->xid2_flag2 = 0x40;
|
||||
}
|
||||
|
||||
return rc;
|
||||
|
||||
@@ -57,20 +57,21 @@ struct ip_mc_socklist {
|
||||
};
|
||||
|
||||
struct ip_sf_list {
|
||||
struct ip_sf_list *sf_next;
|
||||
struct ip_sf_list __rcu *sf_next;
|
||||
unsigned long sf_count[2]; /* include/exclude counts */
|
||||
__be32 sf_inaddr;
|
||||
unsigned char sf_gsresp; /* include in g & s response? */
|
||||
unsigned char sf_oldin; /* change state */
|
||||
unsigned char sf_crcount; /* retrans. left to send */
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
|
||||
struct ip_mc_list {
|
||||
struct in_device *interface;
|
||||
__be32 multiaddr;
|
||||
unsigned int sfmode;
|
||||
struct ip_sf_list *sources;
|
||||
struct ip_sf_list *tomb;
|
||||
struct ip_sf_list __rcu *sources;
|
||||
struct ip_sf_list __rcu *tomb;
|
||||
unsigned long sfcount[2];
|
||||
union {
|
||||
struct ip_mc_list *next;
|
||||
|
||||
@@ -3082,6 +3082,11 @@ static inline bool skb_transport_header_was_set(const struct sk_buff *skb)
|
||||
return skb->transport_header != (typeof(skb->transport_header))~0U;
|
||||
}
|
||||
|
||||
static inline void skb_unset_transport_header(struct sk_buff *skb)
|
||||
{
|
||||
skb->transport_header = (typeof(skb->transport_header))~0U;
|
||||
}
|
||||
|
||||
static inline unsigned char *skb_transport_header(const struct sk_buff *skb)
|
||||
{
|
||||
DEBUG_NET_WARN_ON_ONCE(!skb_transport_header_was_set(skb));
|
||||
|
||||
@@ -229,6 +229,9 @@ struct sock *vsock_find_bound_socket_net(struct sockaddr_vm *addr,
|
||||
struct sock *vsock_find_connected_socket_net(struct sockaddr_vm *src,
|
||||
struct sockaddr_vm *dst,
|
||||
struct net *net);
|
||||
bool vsock_check_source(const struct vsock_sock *vsk,
|
||||
const struct vsock_transport *transport,
|
||||
const struct sockaddr_vm *src);
|
||||
void vsock_remove_sock(struct vsock_sock *vsk);
|
||||
void vsock_for_each_connected_socket(struct vsock_transport *transport,
|
||||
void (*fn)(struct sock *sk));
|
||||
|
||||
@@ -88,8 +88,6 @@ struct ip6_sf_socklist {
|
||||
struct in6_addr sl_addr[] __counted_by(sl_max);
|
||||
};
|
||||
|
||||
#define IP6_SFBLOCK 10 /* allocate this many at once */
|
||||
|
||||
struct ipv6_mc_socklist {
|
||||
struct in6_addr addr;
|
||||
int ifindex;
|
||||
|
||||
@@ -704,7 +704,8 @@ static inline void ip_ipgre_mc_map(__be32 naddr, const unsigned char *broadcast,
|
||||
|
||||
static __inline__ void inet_reset_saddr(struct sock *sk)
|
||||
{
|
||||
inet_sk(sk)->inet_rcv_saddr = inet_sk(sk)->inet_saddr = 0;
|
||||
inet_sk(sk)->inet_saddr = 0;
|
||||
WRITE_ONCE(inet_sk(sk)->inet_rcv_saddr, 0);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
if (sk->sk_family == PF_INET6) {
|
||||
struct ipv6_pinfo *np = inet6_sk(sk);
|
||||
|
||||
@@ -765,8 +765,7 @@ int tcp_fragment(struct sock *sk, enum tcp_queue tcp_queue,
|
||||
void tcp_send_probe0(struct sock *);
|
||||
int tcp_write_wakeup(struct sock *, int mib);
|
||||
void tcp_send_fin(struct sock *sk);
|
||||
void tcp_send_active_reset(struct sock *sk, gfp_t priority,
|
||||
enum sk_rst_reason reason);
|
||||
void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason);
|
||||
int tcp_send_synack(struct sock *);
|
||||
void tcp_push_one(struct sock *, unsigned int mss_now);
|
||||
void __tcp_send_ack(struct sock *sk, u32 rcv_nxt, u16 flags);
|
||||
|
||||
@@ -27,17 +27,20 @@ TRACE_EVENT(icmp_send,
|
||||
|
||||
TP_fast_assign(
|
||||
struct iphdr *iph = ip_hdr(skb);
|
||||
struct udphdr *uh = udp_hdr(skb);
|
||||
int proto_4 = iph->protocol;
|
||||
struct udphdr _uh, *uh = NULL;
|
||||
__be32 *p32;
|
||||
|
||||
__entry->skbaddr = skb;
|
||||
__entry->type = type;
|
||||
__entry->code = code;
|
||||
|
||||
if (proto_4 != IPPROTO_UDP || (u8 *)uh < skb->head ||
|
||||
(u8 *)uh + sizeof(struct udphdr)
|
||||
> skb_tail_pointer(skb)) {
|
||||
if (iph->protocol == IPPROTO_UDP)
|
||||
uh = skb_header_pointer(skb,
|
||||
skb_network_offset(skb) +
|
||||
(iph->ihl << 2),
|
||||
sizeof(_uh), &_uh);
|
||||
|
||||
if (!uh) {
|
||||
__entry->sport = 0;
|
||||
__entry->dport = 0;
|
||||
__entry->ulen = 0;
|
||||
|
||||
@@ -2632,11 +2632,11 @@ int hci_register_dev(struct hci_dev *hdev)
|
||||
if (error)
|
||||
BT_WARN("register suspend notifier failed error:%d\n", error);
|
||||
|
||||
queue_work(hdev->req_workqueue, &hdev->power_on);
|
||||
|
||||
idr_init(&hdev->adv_monitors_idr);
|
||||
msft_register(hdev);
|
||||
|
||||
queue_work(hdev->req_workqueue, &hdev->power_on);
|
||||
|
||||
return id;
|
||||
|
||||
err_wqueue:
|
||||
|
||||
@@ -1337,7 +1337,7 @@ static void l2cap_le_connect(struct l2cap_chan *chan)
|
||||
struct l2cap_ecred_conn_data {
|
||||
struct {
|
||||
struct l2cap_ecred_conn_req_hdr req;
|
||||
__le16 scid[5];
|
||||
__le16 scid[L2CAP_ECRED_CONN_SCID_MAX];
|
||||
} __packed pdu;
|
||||
struct l2cap_chan *chan;
|
||||
struct pid *pid;
|
||||
@@ -1352,7 +1352,7 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)
|
||||
if (chan == conn->chan)
|
||||
return;
|
||||
|
||||
if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
|
||||
if (!test_bit(FLAG_DEFER_SETUP, &chan->flags))
|
||||
return;
|
||||
|
||||
pid = chan->ops->get_peer_pid(chan);
|
||||
@@ -1362,9 +1362,16 @@ static void l2cap_ecred_defer_connect(struct l2cap_chan *chan, void *data)
|
||||
chan->mode != L2CAP_MODE_EXT_FLOWCTL || chan->state != BT_CONNECT)
|
||||
return;
|
||||
|
||||
if (!test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
|
||||
return;
|
||||
|
||||
if (test_and_set_bit(FLAG_ECRED_CONN_REQ_SENT, &chan->flags))
|
||||
return;
|
||||
|
||||
/* Unreachable, checked in l2cap_connect (+timer drops it if reached) */
|
||||
if (WARN_ON_ONCE(conn->count >= ARRAY_SIZE(conn->pdu.scid)))
|
||||
return;
|
||||
|
||||
l2cap_ecred_init(chan, 0);
|
||||
|
||||
/* Set the same ident so we can match on the rsp */
|
||||
@@ -3894,6 +3901,9 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
|
||||
struct l2cap_ecred_conn_rsp *rsp_flex =
|
||||
container_of(&rsp->pdu.rsp, struct l2cap_ecred_conn_rsp, hdr);
|
||||
|
||||
if (chan->mode != L2CAP_MODE_EXT_FLOWCTL)
|
||||
return;
|
||||
|
||||
/* Check if channel for outgoing connection or if it wasn't deferred
|
||||
* since in those cases it must be skipped.
|
||||
*/
|
||||
@@ -3904,6 +3914,10 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
|
||||
/* Reset ident so only one response is sent */
|
||||
chan->ident = 0;
|
||||
|
||||
/* Unreachable, check in l2cap_ecred_conn_req. If reached, drop rest */
|
||||
if (WARN_ON_ONCE(rsp->count >= ARRAY_SIZE(rsp->pdu.scid)))
|
||||
rsp->pdu.rsp.result = cpu_to_le16(L2CAP_CR_LE_NO_MEM);
|
||||
|
||||
/* Include all channels pending with the same ident */
|
||||
if (!rsp->pdu.rsp.result)
|
||||
rsp_flex->dcid[rsp->count++] = cpu_to_le16(chan->scid);
|
||||
@@ -5063,6 +5077,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
|
||||
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
|
||||
|
||||
chan->ident = cmd->ident;
|
||||
chan->mode = L2CAP_MODE_LE_FLOWCTL;
|
||||
|
||||
if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
|
||||
l2cap_state_change(chan, BT_CONNECT2);
|
||||
@@ -7369,6 +7384,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
||||
goto done;
|
||||
}
|
||||
|
||||
mutex_lock(&conn->lock);
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
if (chan->mode == L2CAP_MODE_EXT_FLOWCTL) {
|
||||
struct l2cap_chan_data data;
|
||||
|
||||
@@ -7376,19 +7394,20 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
||||
data.pid = chan->ops->get_peer_pid(chan);
|
||||
data.count = 1;
|
||||
|
||||
l2cap_chan_list(conn, l2cap_chan_by_pid, &data);
|
||||
__l2cap_chan_list(conn, l2cap_chan_by_pid, &data);
|
||||
|
||||
/* Leave room for non-deferred channel that ends the group. */
|
||||
if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
|
||||
data.count += 1;
|
||||
|
||||
/* Check if there isn't too many channels being connected */
|
||||
if (data.count > L2CAP_ECRED_CONN_SCID_MAX) {
|
||||
hci_conn_drop(hcon);
|
||||
err = -EPROTO;
|
||||
goto done;
|
||||
goto chan_unlock;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&conn->lock);
|
||||
l2cap_chan_lock(chan);
|
||||
|
||||
if (cid && __l2cap_get_chan_by_dcid(conn, cid)) {
|
||||
hci_conn_drop(hcon);
|
||||
err = -EBUSY;
|
||||
|
||||
@@ -769,8 +769,8 @@ void msft_register(struct hci_dev *hdev)
|
||||
|
||||
INIT_LIST_HEAD(&msft->handle_map);
|
||||
INIT_LIST_HEAD(&msft->address_filters);
|
||||
hdev->msft_data = msft;
|
||||
mutex_init(&msft->filter_lock);
|
||||
hdev->msft_data = msft;
|
||||
}
|
||||
|
||||
void msft_release(struct hci_dev *hdev)
|
||||
|
||||
@@ -808,7 +808,11 @@ void br_multicast_del_pg(struct net_bridge_mdb_entry *mp,
|
||||
struct hlist_node *tmp;
|
||||
|
||||
rcu_assign_pointer(*pp, pg->next);
|
||||
hlist_del_init(&pg->mglist);
|
||||
/* Keep ->next (held under multicast_lock, freed later by the GC work):
|
||||
* a port->mglist teardown walk may have latched this node as its next,
|
||||
* and deleting other groups of the same port must not truncate it.
|
||||
*/
|
||||
hlist_del_init_rcu(&pg->mglist);
|
||||
br_multicast_eht_clean_sets(pg);
|
||||
hlist_for_each_entry_safe(ent, tmp, &pg->src_list, node)
|
||||
br_multicast_del_group_src(ent, false);
|
||||
@@ -835,6 +839,13 @@ static void br_multicast_find_del_pg(struct net_bridge *br,
|
||||
struct net_bridge_mdb_entry *mp;
|
||||
struct net_bridge_port_group *p;
|
||||
|
||||
/* A teardown walk over port->mglist can reach a group that an earlier
|
||||
* iteration already deleted as a side effect. It is off mp->ports by
|
||||
* now, so skip it instead of falling through to the WARN_ON() below.
|
||||
*/
|
||||
if (hlist_unhashed(&pg->mglist))
|
||||
return;
|
||||
|
||||
mp = br_mdb_ip_get(br, &pg->key.addr);
|
||||
if (WARN_ON(!mp))
|
||||
return;
|
||||
|
||||
@@ -12703,7 +12703,7 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
|
||||
const char *pat, int new_ifindex,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct netdev_name_node *name_node;
|
||||
struct netdev_name_node *name_node, *tmp;
|
||||
struct net *net_old = dev_net(dev);
|
||||
char new_name[IFNAMSIZ] = {};
|
||||
int err, new_nsid;
|
||||
@@ -12749,13 +12749,19 @@ int __dev_change_net_namespace(struct net_device *dev, struct net *net,
|
||||
}
|
||||
/* Check that none of the altnames conflicts. */
|
||||
err = -EEXIST;
|
||||
netdev_for_each_altname(dev, name_node) {
|
||||
if (netdev_name_in_use(net, name_node->name)) {
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"An interface with the altname %s exists in the target netns",
|
||||
name_node->name);
|
||||
goto out;
|
||||
netdev_for_each_altname_safe(dev, name_node, tmp) {
|
||||
if (!netdev_name_in_use(net, name_node->name))
|
||||
continue;
|
||||
|
||||
if (!check_net(net_old)) {
|
||||
__netdev_name_node_alt_destroy(name_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
NL_SET_ERR_MSG_FMT(extack,
|
||||
"An interface with the altname %s exists in the target netns",
|
||||
name_node->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check that new_ifindex isn't used yet. */
|
||||
@@ -13210,7 +13216,6 @@ static struct pernet_operations __net_initdata netdev_net_ops = {
|
||||
|
||||
static void __net_exit default_device_exit_net(struct net *net)
|
||||
{
|
||||
struct netdev_name_node *name_node, *tmp;
|
||||
struct net_device *dev, *aux;
|
||||
/*
|
||||
* Push all migratable network devices back to the
|
||||
@@ -13234,10 +13239,6 @@ static void __net_exit default_device_exit_net(struct net *net)
|
||||
if (netdev_name_in_use(&init_net, fb_name))
|
||||
snprintf(fb_name, IFNAMSIZ, "dev%%d");
|
||||
|
||||
netdev_for_each_altname_safe(dev, name_node, tmp)
|
||||
if (netdev_name_in_use(&init_net, name_node->name))
|
||||
__netdev_name_node_alt_destroy(name_node);
|
||||
|
||||
err = dev_change_net_namespace(dev, &init_net, fb_name);
|
||||
if (err) {
|
||||
pr_emerg("%s: failed to move %s to init_net: %d\n",
|
||||
|
||||
@@ -22,6 +22,8 @@ int gro_cells_receive(struct gro_cells *gcells, struct sk_buff *skb)
|
||||
if (unlikely(!(dev->flags & IFF_UP)))
|
||||
goto drop;
|
||||
|
||||
skb_unset_transport_header(skb);
|
||||
|
||||
if (!gcells->cells || skb_cloned(skb) || netif_elide_gro(dev)) {
|
||||
res = netif_rx(skb);
|
||||
goto unlock;
|
||||
|
||||
@@ -1073,7 +1073,8 @@ netmem_ref page_pool_alloc_frag_netmem(struct page_pool *pool,
|
||||
if (WARN_ON(size > max_size))
|
||||
return 0;
|
||||
|
||||
size = ALIGN(size, dma_get_cache_alignment());
|
||||
size = ALIGN(size, max_t(unsigned int, dma_get_cache_alignment(),
|
||||
__alignof__(struct skb_shared_info)));
|
||||
*offset = pool->frag_offset;
|
||||
|
||||
if (netmem && *offset + size > max_size) {
|
||||
|
||||
@@ -2494,6 +2494,9 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority,
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
RCU_INIT_POINTER(newsk->sk_bpf_storage, NULL);
|
||||
#endif
|
||||
#if IS_ENABLED(CONFIG_INET_PSP)
|
||||
RCU_INIT_POINTER(newsk->psp_assoc, NULL);
|
||||
#endif
|
||||
|
||||
/* SANITY */
|
||||
if (likely(newsk->sk_net_refcnt)) {
|
||||
|
||||
@@ -874,7 +874,7 @@ static void fib_rebalance(struct fib_info *fi)
|
||||
change_nexthops(fi) {
|
||||
int upper_bound;
|
||||
|
||||
if (nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
|
||||
if (!total || nexthop_nh->fib_nh_flags & RTNH_F_DEAD) {
|
||||
upper_bound = -1;
|
||||
} else if (ip_ignore_linkdown(nexthop_nh->fib_nh_dev) &&
|
||||
nexthop_nh->fib_nh_flags & RTNH_F_LINKDOWN) {
|
||||
|
||||
210
net/ipv4/igmp.c
210
net/ipv4/igmp.c
@@ -188,6 +188,10 @@ static void ip_ma_put(struct ip_mc_list *im)
|
||||
}
|
||||
}
|
||||
|
||||
#define pmc_dereference(e, pmc) \
|
||||
rcu_dereference_protected(e, lockdep_is_held(&(pmc)->lock) || \
|
||||
lockdep_is_held(&(pmc)->interface->mc_tomb_lock))
|
||||
|
||||
#define for_each_pmc_rcu(in_dev, pmc) \
|
||||
for (pmc = rcu_dereference(in_dev->mc_list); \
|
||||
pmc != NULL; \
|
||||
@@ -198,13 +202,28 @@ static void ip_ma_put(struct ip_mc_list *im)
|
||||
pmc != NULL; \
|
||||
pmc = rtnl_dereference(pmc->next_rcu))
|
||||
|
||||
#define for_each_psf_mclock(pmc, psf) \
|
||||
for (psf = pmc_dereference((pmc)->sources, pmc); \
|
||||
psf; \
|
||||
psf = pmc_dereference(psf->sf_next, pmc))
|
||||
|
||||
#define for_each_psf_rcu(im, psf) \
|
||||
for (psf = rcu_dereference((im)->sources); \
|
||||
psf; \
|
||||
psf = rcu_dereference(psf->sf_next))
|
||||
|
||||
#define for_each_psf_tomb(pmc, psf) \
|
||||
for (psf = pmc_dereference((pmc)->tomb, pmc); \
|
||||
psf; \
|
||||
psf = pmc_dereference(psf->sf_next, pmc))
|
||||
|
||||
static void ip_sf_list_clear_all(struct ip_sf_list *psf)
|
||||
{
|
||||
struct ip_sf_list *next;
|
||||
|
||||
while (psf) {
|
||||
next = psf->sf_next;
|
||||
kfree(psf);
|
||||
next = rcu_dereference_protected(psf->sf_next, 1);
|
||||
kfree_rcu(psf, rcu);
|
||||
psf = next;
|
||||
}
|
||||
}
|
||||
@@ -349,7 +368,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
|
||||
struct ip_sf_list *psf;
|
||||
int scount = 0;
|
||||
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next) {
|
||||
for_each_psf_mclock(pmc, psf) {
|
||||
if (!is_in(pmc, psf, type, gdeleted, sdeleted))
|
||||
continue;
|
||||
scount++;
|
||||
@@ -494,7 +513,8 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
|
||||
struct net *net = dev_net(dev);
|
||||
struct igmpv3_report *pih;
|
||||
struct igmpv3_grec *pgr = NULL;
|
||||
struct ip_sf_list *psf, *psf_next, *psf_prev, **psf_list;
|
||||
struct ip_sf_list *psf, *psf_next, *psf_prev;
|
||||
struct ip_sf_list __rcu **psf_list;
|
||||
int scount, stotal, first, isquery, truncate;
|
||||
unsigned int mtu;
|
||||
|
||||
@@ -517,7 +537,7 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
|
||||
|
||||
psf_list = sdeleted ? &pmc->tomb : &pmc->sources;
|
||||
|
||||
if (!*psf_list)
|
||||
if (!rcu_access_pointer(*psf_list))
|
||||
goto empty_source;
|
||||
|
||||
pih = skb ? igmpv3_report_hdr(skb) : NULL;
|
||||
@@ -533,10 +553,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
|
||||
}
|
||||
first = 1;
|
||||
psf_prev = NULL;
|
||||
for (psf = *psf_list; psf; psf = psf_next) {
|
||||
for (psf = pmc_dereference(*psf_list, pmc);
|
||||
psf;
|
||||
psf = psf_next) {
|
||||
__be32 *psrc;
|
||||
|
||||
psf_next = psf->sf_next;
|
||||
psf_next = pmc_dereference(psf->sf_next, pmc);
|
||||
|
||||
if (!is_in(pmc, psf, type, gdeleted, sdeleted)) {
|
||||
psf_prev = psf;
|
||||
@@ -583,10 +605,12 @@ static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
|
||||
psf->sf_crcount--;
|
||||
if ((sdeleted || gdeleted) && psf->sf_crcount == 0) {
|
||||
if (psf_prev)
|
||||
psf_prev->sf_next = psf->sf_next;
|
||||
rcu_assign_pointer(psf_prev->sf_next,
|
||||
psf_next);
|
||||
else
|
||||
*psf_list = psf->sf_next;
|
||||
kfree(psf);
|
||||
rcu_assign_pointer(*psf_list,
|
||||
psf_next);
|
||||
kfree_rcu(psf, rcu);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -655,28 +679,29 @@ static int igmpv3_send_report(struct in_device *in_dev, struct ip_mc_list *pmc)
|
||||
/*
|
||||
* remove zero-count source records from a source filter list
|
||||
*/
|
||||
static void igmpv3_clear_zeros(struct ip_sf_list **ppsf)
|
||||
static void igmpv3_clear_zeros(struct ip_sf_list __rcu **ppsf)
|
||||
{
|
||||
struct ip_sf_list *psf_prev, *psf_next, *psf;
|
||||
|
||||
psf_prev = NULL;
|
||||
for (psf = *ppsf; psf; psf = psf_next) {
|
||||
psf_next = psf->sf_next;
|
||||
for (psf = rcu_dereference_protected(*ppsf, 1); psf; psf = psf_next) {
|
||||
psf_next = rcu_dereference_protected(psf->sf_next, 1);
|
||||
if (psf->sf_crcount == 0) {
|
||||
if (psf_prev)
|
||||
psf_prev->sf_next = psf->sf_next;
|
||||
rcu_assign_pointer(psf_prev->sf_next, psf_next);
|
||||
else
|
||||
*ppsf = psf->sf_next;
|
||||
kfree(psf);
|
||||
} else
|
||||
rcu_assign_pointer(*ppsf, psf_next);
|
||||
kfree_rcu(psf, rcu);
|
||||
} else {
|
||||
psf_prev = psf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void kfree_pmc(struct ip_mc_list *pmc)
|
||||
{
|
||||
ip_sf_list_clear_all(pmc->sources);
|
||||
ip_sf_list_clear_all(pmc->tomb);
|
||||
ip_sf_list_clear_all(rcu_dereference_protected(pmc->sources, 1));
|
||||
ip_sf_list_clear_all(rcu_dereference_protected(pmc->tomb, 1));
|
||||
kfree(pmc);
|
||||
}
|
||||
|
||||
@@ -710,7 +735,8 @@ static void igmpv3_send_cr(struct in_device *in_dev)
|
||||
igmpv3_clear_zeros(&pmc->sources);
|
||||
}
|
||||
}
|
||||
if (pmc->crcount == 0 && !pmc->tomb && !pmc->sources) {
|
||||
if (pmc->crcount == 0 && !rcu_access_pointer(pmc->tomb) &&
|
||||
!rcu_access_pointer(pmc->sources)) {
|
||||
if (pmc_prev)
|
||||
pmc_prev->next = pmc_next;
|
||||
else
|
||||
@@ -896,7 +922,7 @@ static int igmp_xmarksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
|
||||
int i, scount;
|
||||
|
||||
scount = 0;
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next) {
|
||||
for_each_psf_mclock(pmc, psf) {
|
||||
if (scount == nsrcs)
|
||||
break;
|
||||
for (i = 0; i < nsrcs; i++) {
|
||||
@@ -927,7 +953,7 @@ static int igmp_marksources(struct ip_mc_list *pmc, int nsrcs, __be32 *srcs)
|
||||
|
||||
/* mark INCLUDE-mode sources */
|
||||
scount = 0;
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next) {
|
||||
for_each_psf_mclock(pmc, psf) {
|
||||
if (scount == nsrcs)
|
||||
break;
|
||||
for (i = 0; i < nsrcs; i++)
|
||||
@@ -1228,11 +1254,12 @@ static void igmpv3_add_delrec(struct in_device *in_dev, struct ip_mc_list *im,
|
||||
if (pmc->sfmode == MCAST_INCLUDE) {
|
||||
struct ip_sf_list *psf;
|
||||
|
||||
for_each_psf_mclock(im, psf)
|
||||
psf->sf_crcount = pmc->crcount;
|
||||
pmc->tomb = im->tomb;
|
||||
pmc->sources = im->sources;
|
||||
im->tomb = im->sources = NULL;
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next)
|
||||
psf->sf_crcount = pmc->crcount;
|
||||
RCU_INIT_POINTER(im->tomb, NULL);
|
||||
RCU_INIT_POINTER(im->sources, NULL);
|
||||
}
|
||||
spin_unlock_bh(&im->lock);
|
||||
|
||||
@@ -1271,9 +1298,18 @@ static void igmpv3_del_delrec(struct in_device *in_dev, struct ip_mc_list *im)
|
||||
if (pmc) {
|
||||
im->interface = pmc->interface;
|
||||
if (im->sfmode == MCAST_INCLUDE) {
|
||||
swap(im->tomb, pmc->tomb);
|
||||
swap(im->sources, pmc->sources);
|
||||
for (psf = im->sources; psf; psf = psf->sf_next)
|
||||
struct ip_sf_list *sources, *tomb;
|
||||
|
||||
tomb = rcu_replace_pointer(im->tomb,
|
||||
rcu_dereference_protected(pmc->tomb, 1),
|
||||
lockdep_is_held(&im->lock));
|
||||
rcu_assign_pointer(pmc->tomb, tomb);
|
||||
|
||||
sources = rcu_replace_pointer(im->sources,
|
||||
rcu_dereference_protected(pmc->sources, 1),
|
||||
lockdep_is_held(&im->lock));
|
||||
rcu_assign_pointer(pmc->sources, sources);
|
||||
for_each_psf_mclock(im, psf)
|
||||
psf->sf_crcount = in_dev->mr_qrv ?:
|
||||
READ_ONCE(net->ipv4.sysctl_igmp_qrv);
|
||||
} else {
|
||||
@@ -1310,8 +1346,8 @@ static void igmpv3_clear_delrec(struct in_device *in_dev)
|
||||
struct ip_sf_list *psf;
|
||||
|
||||
spin_lock_bh(&pmc->lock);
|
||||
psf = pmc->tomb;
|
||||
pmc->tomb = NULL;
|
||||
psf = pmc_dereference(pmc->tomb, pmc);
|
||||
RCU_INIT_POINTER(pmc->tomb, NULL);
|
||||
spin_unlock_bh(&pmc->lock);
|
||||
ip_sf_list_clear_all(psf);
|
||||
}
|
||||
@@ -1990,7 +2026,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
|
||||
int rv = 0;
|
||||
|
||||
psf_prev = NULL;
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next) {
|
||||
for_each_psf_mclock(pmc, psf) {
|
||||
if (psf->sf_inaddr == *psfsrc)
|
||||
break;
|
||||
psf_prev = psf;
|
||||
@@ -1999,7 +2035,7 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
|
||||
/* source filter not found, or count wrong => bug */
|
||||
return -ESRCH;
|
||||
}
|
||||
psf->sf_count[sfmode]--;
|
||||
WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] - 1);
|
||||
if (psf->sf_count[sfmode] == 0) {
|
||||
ip_rt_multicast_event(pmc->interface);
|
||||
}
|
||||
@@ -2011,19 +2047,28 @@ static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
|
||||
|
||||
/* no more filters for this source */
|
||||
if (psf_prev)
|
||||
psf_prev->sf_next = psf->sf_next;
|
||||
rcu_assign_pointer(psf_prev->sf_next,
|
||||
pmc_dereference(psf->sf_next, pmc));
|
||||
else
|
||||
pmc->sources = psf->sf_next;
|
||||
rcu_assign_pointer(pmc->sources,
|
||||
pmc_dereference(psf->sf_next, pmc));
|
||||
#ifdef CONFIG_IP_MULTICAST
|
||||
if (psf->sf_oldin &&
|
||||
!IGMP_V1_SEEN(in_dev) && !IGMP_V2_SEEN(in_dev)) {
|
||||
psf->sf_crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
|
||||
psf->sf_next = pmc->tomb;
|
||||
pmc->tomb = psf;
|
||||
rv = 1;
|
||||
} else
|
||||
struct ip_sf_list *dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
|
||||
|
||||
if (dpsf) {
|
||||
*dpsf = *psf;
|
||||
dpsf->sf_crcount = in_dev->mr_qrv ?:
|
||||
READ_ONCE(net->ipv4.sysctl_igmp_qrv);
|
||||
rcu_assign_pointer(dpsf->sf_next,
|
||||
pmc_dereference(pmc->tomb, pmc));
|
||||
rcu_assign_pointer(pmc->tomb, dpsf);
|
||||
rv = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
kfree(psf);
|
||||
kfree_rcu(psf, rcu);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -2060,7 +2105,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
|
||||
err = -EINVAL;
|
||||
if (!pmc->sfcount[sfmode])
|
||||
goto out_unlock;
|
||||
pmc->sfcount[sfmode]--;
|
||||
WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
|
||||
}
|
||||
err = 0;
|
||||
for (i = 0; i < sfcount; i++) {
|
||||
@@ -2083,7 +2128,7 @@ static int ip_mc_del_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
|
||||
#ifdef CONFIG_IP_MULTICAST
|
||||
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
|
||||
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next)
|
||||
for_each_psf_mclock(pmc, psf)
|
||||
psf->sf_crcount = 0;
|
||||
igmp_ifc_event(pmc->interface);
|
||||
} else if (sf_setstate(pmc) || changerec) {
|
||||
@@ -2104,7 +2149,7 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
|
||||
struct ip_sf_list *psf, *psf_prev;
|
||||
|
||||
psf_prev = NULL;
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next) {
|
||||
for_each_psf_mclock(pmc, psf) {
|
||||
if (psf->sf_inaddr == *psfsrc)
|
||||
break;
|
||||
psf_prev = psf;
|
||||
@@ -2114,12 +2159,12 @@ static int ip_mc_add1_src(struct ip_mc_list *pmc, int sfmode,
|
||||
if (!psf)
|
||||
return -ENOBUFS;
|
||||
psf->sf_inaddr = *psfsrc;
|
||||
if (psf_prev) {
|
||||
psf_prev->sf_next = psf;
|
||||
} else
|
||||
pmc->sources = psf;
|
||||
if (psf_prev)
|
||||
rcu_assign_pointer(psf_prev->sf_next, psf);
|
||||
else
|
||||
rcu_assign_pointer(pmc->sources, psf);
|
||||
}
|
||||
psf->sf_count[sfmode]++;
|
||||
WRITE_ONCE(psf->sf_count[sfmode], psf->sf_count[sfmode] + 1);
|
||||
if (psf->sf_count[sfmode] == 1) {
|
||||
ip_rt_multicast_event(pmc->interface);
|
||||
}
|
||||
@@ -2132,13 +2177,15 @@ static void sf_markstate(struct ip_mc_list *pmc)
|
||||
struct ip_sf_list *psf;
|
||||
int mca_xcount = pmc->sfcount[MCAST_EXCLUDE];
|
||||
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next)
|
||||
for_each_psf_mclock(pmc, psf) {
|
||||
if (pmc->sfcount[MCAST_EXCLUDE]) {
|
||||
psf->sf_oldin = mca_xcount ==
|
||||
psf->sf_count[MCAST_EXCLUDE] &&
|
||||
!psf->sf_count[MCAST_INCLUDE];
|
||||
} else
|
||||
} else {
|
||||
psf->sf_oldin = psf->sf_count[MCAST_INCLUDE] != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int sf_setstate(struct ip_mc_list *pmc)
|
||||
@@ -2149,27 +2196,31 @@ static int sf_setstate(struct ip_mc_list *pmc)
|
||||
int new_in, rv;
|
||||
|
||||
rv = 0;
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next) {
|
||||
for_each_psf_mclock(pmc, psf) {
|
||||
if (pmc->sfcount[MCAST_EXCLUDE]) {
|
||||
new_in = mca_xcount == psf->sf_count[MCAST_EXCLUDE] &&
|
||||
!psf->sf_count[MCAST_INCLUDE];
|
||||
} else
|
||||
} else {
|
||||
new_in = psf->sf_count[MCAST_INCLUDE] != 0;
|
||||
}
|
||||
if (new_in) {
|
||||
if (!psf->sf_oldin) {
|
||||
struct ip_sf_list *prev = NULL;
|
||||
|
||||
for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next) {
|
||||
for_each_psf_tomb(pmc, dpsf) {
|
||||
if (dpsf->sf_inaddr == psf->sf_inaddr)
|
||||
break;
|
||||
prev = dpsf;
|
||||
}
|
||||
if (dpsf) {
|
||||
struct ip_sf_list *dpsf_next;
|
||||
|
||||
dpsf_next = pmc_dereference(dpsf->sf_next, pmc);
|
||||
if (prev)
|
||||
prev->sf_next = dpsf->sf_next;
|
||||
rcu_assign_pointer(prev->sf_next, dpsf_next);
|
||||
else
|
||||
pmc->tomb = dpsf->sf_next;
|
||||
kfree(dpsf);
|
||||
rcu_assign_pointer(pmc->tomb, dpsf_next);
|
||||
kfree_rcu(dpsf, rcu);
|
||||
}
|
||||
psf->sf_crcount = qrv;
|
||||
rv++;
|
||||
@@ -2181,17 +2232,19 @@ static int sf_setstate(struct ip_mc_list *pmc)
|
||||
* add or update "delete" records if an active filter
|
||||
* is now inactive
|
||||
*/
|
||||
for (dpsf = pmc->tomb; dpsf; dpsf = dpsf->sf_next)
|
||||
for_each_psf_tomb(pmc, dpsf) {
|
||||
if (dpsf->sf_inaddr == psf->sf_inaddr)
|
||||
break;
|
||||
}
|
||||
if (!dpsf) {
|
||||
dpsf = kmalloc_obj(*dpsf, GFP_ATOMIC);
|
||||
if (!dpsf)
|
||||
continue;
|
||||
*dpsf = *psf;
|
||||
/* pmc->lock held by callers */
|
||||
dpsf->sf_next = pmc->tomb;
|
||||
pmc->tomb = dpsf;
|
||||
rcu_assign_pointer(dpsf->sf_next,
|
||||
pmc_dereference(pmc->tomb, pmc));
|
||||
rcu_assign_pointer(pmc->tomb, dpsf);
|
||||
}
|
||||
dpsf->sf_crcount = qrv;
|
||||
rv++;
|
||||
@@ -2231,7 +2284,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
|
||||
#endif
|
||||
isexclude = pmc->sfmode == MCAST_EXCLUDE;
|
||||
if (!delta)
|
||||
pmc->sfcount[sfmode]++;
|
||||
WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] + 1);
|
||||
err = 0;
|
||||
for (i = 0; i < sfcount; i++) {
|
||||
err = ip_mc_add1_src(pmc, sfmode, &psfsrc[i]);
|
||||
@@ -2242,7 +2295,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
|
||||
int j;
|
||||
|
||||
if (!delta)
|
||||
pmc->sfcount[sfmode]--;
|
||||
WRITE_ONCE(pmc->sfcount[sfmode], pmc->sfcount[sfmode] - 1);
|
||||
for (j = 0; j < i; j++)
|
||||
(void) ip_mc_del1_src(pmc, sfmode, &psfsrc[j]);
|
||||
} else if (isexclude != (pmc->sfcount[MCAST_EXCLUDE] != 0)) {
|
||||
@@ -2262,7 +2315,7 @@ static int ip_mc_add_src(struct in_device *in_dev, __be32 *pmca, int sfmode,
|
||||
|
||||
pmc->crcount = in_dev->mr_qrv ?: READ_ONCE(net->ipv4.sysctl_igmp_qrv);
|
||||
WRITE_ONCE(in_dev->mr_ifc_count, pmc->crcount);
|
||||
for (psf = pmc->sources; psf; psf = psf->sf_next)
|
||||
for_each_psf_mclock(pmc, psf)
|
||||
psf->sf_crcount = 0;
|
||||
igmp_ifc_event(in_dev);
|
||||
} else if (sf_setstate(pmc)) {
|
||||
@@ -2278,13 +2331,13 @@ static void ip_mc_clear_src(struct ip_mc_list *pmc)
|
||||
struct ip_sf_list *tomb, *sources;
|
||||
|
||||
spin_lock_bh(&pmc->lock);
|
||||
tomb = pmc->tomb;
|
||||
pmc->tomb = NULL;
|
||||
sources = pmc->sources;
|
||||
pmc->sources = NULL;
|
||||
tomb = pmc_dereference(pmc->tomb, pmc);
|
||||
RCU_INIT_POINTER(pmc->tomb, NULL);
|
||||
sources = pmc_dereference(pmc->sources, pmc);
|
||||
RCU_INIT_POINTER(pmc->sources, NULL);
|
||||
pmc->sfmode = MCAST_EXCLUDE;
|
||||
pmc->sfcount[MCAST_INCLUDE] = 0;
|
||||
pmc->sfcount[MCAST_EXCLUDE] = 1;
|
||||
WRITE_ONCE(pmc->sfcount[MCAST_INCLUDE], 0);
|
||||
WRITE_ONCE(pmc->sfcount[MCAST_EXCLUDE], 1);
|
||||
spin_unlock_bh(&pmc->lock);
|
||||
|
||||
ip_sf_list_clear_all(tomb);
|
||||
@@ -2866,20 +2919,19 @@ int ip_check_mc_rcu(struct in_device *in_dev, __be32 mc_addr, __be32 src_addr, u
|
||||
rv = 1;
|
||||
} else if (im) {
|
||||
if (src_addr) {
|
||||
spin_lock_bh(&im->lock);
|
||||
for (psf = im->sources; psf; psf = psf->sf_next) {
|
||||
for_each_psf_rcu(im, psf) {
|
||||
if (psf->sf_inaddr == src_addr)
|
||||
break;
|
||||
}
|
||||
if (psf)
|
||||
rv = psf->sf_count[MCAST_INCLUDE] ||
|
||||
psf->sf_count[MCAST_EXCLUDE] !=
|
||||
im->sfcount[MCAST_EXCLUDE];
|
||||
rv = READ_ONCE(psf->sf_count[MCAST_INCLUDE]) ||
|
||||
READ_ONCE(psf->sf_count[MCAST_EXCLUDE]) !=
|
||||
READ_ONCE(im->sfcount[MCAST_EXCLUDE]);
|
||||
else
|
||||
rv = im->sfcount[MCAST_EXCLUDE] != 0;
|
||||
spin_unlock_bh(&im->lock);
|
||||
} else
|
||||
rv = READ_ONCE(im->sfcount[MCAST_EXCLUDE]) != 0;
|
||||
} else {
|
||||
rv = 1; /* unspecified source; tentatively allow */
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -3043,7 +3095,7 @@ static inline struct ip_sf_list *igmp_mcf_get_first(struct seq_file *seq)
|
||||
im = rcu_dereference(idev->mc_list);
|
||||
if (likely(im)) {
|
||||
spin_lock_bh(&im->lock);
|
||||
psf = im->sources;
|
||||
psf = pmc_dereference(im->sources, im);
|
||||
if (likely(psf)) {
|
||||
state->im = im;
|
||||
state->idev = idev;
|
||||
@@ -3059,7 +3111,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
|
||||
{
|
||||
struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
|
||||
|
||||
psf = psf->sf_next;
|
||||
psf = pmc_dereference(psf->sf_next, state->im);
|
||||
while (!psf) {
|
||||
spin_unlock_bh(&state->im->lock);
|
||||
state->im = state->im->next;
|
||||
@@ -3075,7 +3127,7 @@ static struct ip_sf_list *igmp_mcf_get_next(struct seq_file *seq, struct ip_sf_l
|
||||
state->im = rcu_dereference(state->idev->mc_list);
|
||||
}
|
||||
spin_lock_bh(&state->im->lock);
|
||||
psf = state->im->sources;
|
||||
psf = pmc_dereference(state->im->sources, state->im);
|
||||
}
|
||||
out:
|
||||
return psf;
|
||||
|
||||
@@ -3182,8 +3182,7 @@ void __tcp_close(struct sock *sk, long timeout)
|
||||
/* Unread data was tossed, zap the connection. */
|
||||
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
|
||||
tcp_set_state(sk, TCP_CLOSE);
|
||||
tcp_send_active_reset(sk, sk->sk_allocation,
|
||||
SK_RST_REASON_TCP_ABORT_ON_CLOSE);
|
||||
tcp_send_active_reset(sk, SK_RST_REASON_TCP_ABORT_ON_CLOSE);
|
||||
} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
|
||||
/* Check zero linger _after_ checking for unread data. */
|
||||
sk->sk_prot->disconnect(sk, 0);
|
||||
@@ -3257,7 +3256,7 @@ void __tcp_close(struct sock *sk, long timeout)
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
if (READ_ONCE(tp->linger2) < 0) {
|
||||
tcp_set_state(sk, TCP_CLOSE);
|
||||
tcp_send_active_reset(sk, GFP_ATOMIC,
|
||||
tcp_send_active_reset(sk,
|
||||
SK_RST_REASON_TCP_ABORT_ON_LINGER);
|
||||
__NET_INC_STATS(sock_net(sk),
|
||||
LINUX_MIB_TCPABORTONLINGER);
|
||||
@@ -3276,7 +3275,7 @@ void __tcp_close(struct sock *sk, long timeout)
|
||||
if (sk->sk_state != TCP_CLOSE) {
|
||||
if (tcp_check_oom(sk, 0)) {
|
||||
tcp_set_state(sk, TCP_CLOSE);
|
||||
tcp_send_active_reset(sk, GFP_ATOMIC,
|
||||
tcp_send_active_reset(sk,
|
||||
SK_RST_REASON_TCP_ABORT_ON_MEMORY);
|
||||
__NET_INC_STATS(sock_net(sk),
|
||||
LINUX_MIB_TCPABORTONMEMORY);
|
||||
@@ -3377,14 +3376,14 @@ int tcp_disconnect(struct sock *sk, int flags)
|
||||
} else if (unlikely(tp->repair)) {
|
||||
WRITE_ONCE(sk->sk_err, ECONNABORTED);
|
||||
} else if (tcp_need_reset(old_state)) {
|
||||
tcp_send_active_reset(sk, gfp_any(), SK_RST_REASON_TCP_STATE);
|
||||
tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
|
||||
WRITE_ONCE(sk->sk_err, ECONNRESET);
|
||||
} else if (tp->snd_nxt != tp->write_seq &&
|
||||
(1 << old_state) & (TCPF_CLOSING | TCPF_LAST_ACK)) {
|
||||
/* The last check adjusts for discrepancy of Linux wrt. RFC
|
||||
* states
|
||||
*/
|
||||
tcp_send_active_reset(sk, gfp_any(),
|
||||
tcp_send_active_reset(sk,
|
||||
SK_RST_REASON_TCP_DISCONNECT_WITH_DATA);
|
||||
WRITE_ONCE(sk->sk_err, ECONNRESET);
|
||||
} else if (old_state == TCP_SYN_SENT)
|
||||
@@ -4562,9 +4561,11 @@ int do_tcp_getsockopt(struct sock *sk, int level,
|
||||
if (copy_from_sockptr(&len, optlen, sizeof(int)))
|
||||
return -EFAULT;
|
||||
|
||||
ca_ops = icsk->icsk_ca_ops;
|
||||
rcu_read_lock();
|
||||
ca_ops = READ_ONCE(icsk->icsk_ca_ops);
|
||||
if (ca_ops && ca_ops->get_info)
|
||||
sz = ca_ops->get_info(sk, ~0U, &attr, &info);
|
||||
rcu_read_unlock();
|
||||
|
||||
len = min_t(unsigned int, len, sz);
|
||||
if (copy_to_sockptr(optlen, &len, sizeof(int)))
|
||||
@@ -4577,16 +4578,24 @@ int do_tcp_getsockopt(struct sock *sk, int level,
|
||||
val = !inet_csk_in_pingpong_mode(sk);
|
||||
break;
|
||||
|
||||
case TCP_CONGESTION:
|
||||
case TCP_CONGESTION: {
|
||||
char ca_name[TCP_CA_NAME_MAX] = {};
|
||||
|
||||
if (copy_from_sockptr(&len, optlen, sizeof(int)))
|
||||
return -EFAULT;
|
||||
len = min_t(unsigned int, len, TCP_CA_NAME_MAX);
|
||||
if (copy_to_sockptr(optlen, &len, sizeof(int)))
|
||||
return -EFAULT;
|
||||
if (copy_to_sockptr(optval, icsk->icsk_ca_ops->name, len))
|
||||
|
||||
rcu_read_lock();
|
||||
memcpy(ca_name, READ_ONCE(icsk->icsk_ca_ops)->name,
|
||||
sizeof(ca_name));
|
||||
rcu_read_unlock();
|
||||
|
||||
if (copy_to_sockptr(optval, ca_name, len))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
|
||||
}
|
||||
case TCP_ULP:
|
||||
if (copy_from_sockptr(&len, optlen, sizeof(int)))
|
||||
return -EFAULT;
|
||||
@@ -5147,8 +5156,7 @@ int tcp_abort(struct sock *sk, int err)
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if (tcp_need_reset(sk->sk_state))
|
||||
tcp_send_active_reset(sk, GFP_ATOMIC,
|
||||
SK_RST_REASON_TCP_STATE);
|
||||
tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
|
||||
tcp_done_with_error(sk, err);
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
|
||||
@@ -223,7 +223,7 @@ void tcp_assign_congestion_control(struct sock *sk)
|
||||
ca = rcu_dereference(net->ipv4.tcp_congestion_control);
|
||||
if (unlikely(!bpf_try_module_get(ca, ca->owner)))
|
||||
ca = &tcp_reno;
|
||||
icsk->icsk_ca_ops = ca;
|
||||
WRITE_ONCE(icsk->icsk_ca_ops, ca);
|
||||
rcu_read_unlock();
|
||||
|
||||
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
|
||||
@@ -253,7 +253,7 @@ static void tcp_reinit_congestion_control(struct sock *sk,
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
|
||||
tcp_cleanup_congestion_control(sk);
|
||||
icsk->icsk_ca_ops = ca;
|
||||
WRITE_ONCE(icsk->icsk_ca_ops, ca);
|
||||
icsk->icsk_ca_setsockopt = 1;
|
||||
memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ __bpf_kfunc static void dctcp_init(struct sock *sk)
|
||||
/* No ECN support? Fall back to Reno. Also need to clear
|
||||
* ECT from sk since it is set during 3WHS for DCTCP.
|
||||
*/
|
||||
inet_csk(sk)->icsk_ca_ops = &dctcp_reno;
|
||||
WRITE_ONCE(inet_csk(sk)->icsk_ca_ops, &dctcp_reno);
|
||||
INET_ECN_dontxmit(sk);
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ static size_t dctcp_get_info(struct sock *sk, u32 ext, int *attr,
|
||||
if (ext & (1 << (INET_DIAG_DCTCPINFO - 1)) ||
|
||||
ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
|
||||
memset(&info->dctcp, 0, sizeof(info->dctcp));
|
||||
if (inet_csk(sk)->icsk_ca_ops != &dctcp_reno) {
|
||||
if (READ_ONCE(inet_csk(sk)->icsk_ca_ops) != &dctcp_reno) {
|
||||
info->dctcp.dctcp_enabled = 1;
|
||||
info->dctcp.dctcp_ce_state = (u16) ca->ce_state;
|
||||
info->dctcp.dctcp_alpha = ca->dctcp_alpha;
|
||||
|
||||
@@ -507,7 +507,7 @@ void tcp_ca_openreq_child(struct sock *sk, const struct dst_entry *dst)
|
||||
ca = tcp_ca_find_key(ca_key);
|
||||
if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
|
||||
icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
|
||||
icsk->icsk_ca_ops = ca;
|
||||
WRITE_ONCE(icsk->icsk_ca_ops, ca);
|
||||
ca_got_dst = true;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
@@ -332,6 +332,7 @@ struct sk_buff *tcp_gro_receive(struct list_head *head, struct sk_buff *skb,
|
||||
flush |= skb->ip_summed != p->ip_summed;
|
||||
flush |= skb->csum_level != p->csum_level;
|
||||
flush |= NAPI_GRO_CB(p)->count >= 64;
|
||||
flush |= NAPI_GRO_CB(p)->is_flist != NAPI_GRO_CB(skb)->is_flist;
|
||||
skb_set_network_header(skb, skb_gro_receive_network_offset(skb));
|
||||
|
||||
if (flush || skb_gro_receive_list(p, skb))
|
||||
@@ -395,12 +396,20 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
|
||||
struct net *net;
|
||||
int iif, sdif;
|
||||
|
||||
if (likely(!(skb->dev->features & NETIF_F_GRO_FRAGLIST)))
|
||||
return;
|
||||
|
||||
p = tcp_gro_lookup(head, th);
|
||||
if (p) {
|
||||
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
|
||||
/* flist GRO applies to consecutive non-GSO skbs */
|
||||
if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) {
|
||||
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fall back to the regular GRO path */
|
||||
if (NAPI_GRO_CB(p)->count == 1)
|
||||
NAPI_GRO_CB(p)->is_flist = 0;
|
||||
|
||||
NAPI_GRO_CB(skb)->is_flist = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -410,7 +419,7 @@ static void tcp4_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
|
||||
sk = __inet_lookup_established(net, iph->saddr, th->source,
|
||||
iph->daddr, ntohs(th->dest),
|
||||
iif, sdif);
|
||||
NAPI_GRO_CB(skb)->is_flist = !sk;
|
||||
NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb);
|
||||
if (sk)
|
||||
sock_gen_put(sk);
|
||||
}
|
||||
@@ -430,7 +439,8 @@ struct sk_buff *tcp4_gro_receive(struct list_head *head, struct sk_buff *skb)
|
||||
if (!th)
|
||||
goto flush;
|
||||
|
||||
tcp4_check_fraglist_gro(head, skb, th);
|
||||
if (unlikely(skb->dev->features & NETIF_F_GRO_FRAGLIST))
|
||||
tcp4_check_fraglist_gro(head, skb, th);
|
||||
|
||||
return tcp_gro_receive(head, skb, th);
|
||||
|
||||
|
||||
@@ -3849,9 +3849,9 @@ void tcp_send_fin(struct sock *sk)
|
||||
* was unread data in the receive queue. This behavior is recommended
|
||||
* by RFC 2525, section 2.17. -DaveM
|
||||
*/
|
||||
void tcp_send_active_reset(struct sock *sk, gfp_t priority,
|
||||
enum sk_rst_reason reason)
|
||||
void tcp_send_active_reset(struct sock *sk, enum sk_rst_reason reason)
|
||||
{
|
||||
gfp_t priority = sk_gfp_mask(sk, GFP_ATOMIC | __GFP_NOWARN);
|
||||
struct sk_buff *skb;
|
||||
|
||||
TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS);
|
||||
@@ -4092,7 +4092,7 @@ static void tcp_ca_dst_init(struct sock *sk, const struct dst_entry *dst)
|
||||
if (likely(ca && bpf_try_module_get(ca, ca->owner))) {
|
||||
bpf_module_put(icsk->icsk_ca_ops, icsk->icsk_ca_ops->owner);
|
||||
icsk->icsk_ca_dst_locked = tcp_ca_dst_locked(dst);
|
||||
icsk->icsk_ca_ops = ca;
|
||||
WRITE_ONCE(icsk->icsk_ca_ops, ca);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ static int tcp_out_of_resources(struct sock *sk, bool do_reset)
|
||||
(!tp->snd_wnd && !tp->packets_out))
|
||||
do_reset = true;
|
||||
if (do_reset)
|
||||
tcp_send_active_reset(sk, GFP_ATOMIC,
|
||||
tcp_send_active_reset(sk,
|
||||
SK_RST_REASON_TCP_ABORT_ON_MEMORY);
|
||||
tcp_done(sk);
|
||||
__NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPABORTONMEMORY);
|
||||
@@ -809,7 +809,7 @@ static void tcp_keepalive_timer(struct timer_list *t)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
tcp_send_active_reset(sk, GFP_ATOMIC, SK_RST_REASON_TCP_STATE);
|
||||
tcp_send_active_reset(sk, SK_RST_REASON_TCP_STATE);
|
||||
goto death;
|
||||
}
|
||||
|
||||
@@ -836,7 +836,7 @@ static void tcp_keepalive_timer(struct timer_list *t)
|
||||
icsk->icsk_probes_out > 0) ||
|
||||
(user_timeout == 0 &&
|
||||
icsk->icsk_probes_out >= keepalive_probes(tp))) {
|
||||
tcp_send_active_reset(sk, GFP_ATOMIC,
|
||||
tcp_send_active_reset(sk,
|
||||
SK_RST_REASON_TCP_KEEPALIVE_TIMEOUT);
|
||||
tcp_write_err(sk);
|
||||
goto out;
|
||||
|
||||
@@ -900,6 +900,15 @@ static struct sock *__udp4_lib_err_encap(struct net *net,
|
||||
return sk;
|
||||
}
|
||||
|
||||
static void udp_err_update_exception(struct net *net, struct sk_buff *skb,
|
||||
int type, int code, u32 info)
|
||||
{
|
||||
if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED)
|
||||
ipv4_update_pmtu(skb, net, info, 0, IPPROTO_UDP);
|
||||
else if (type == ICMP_REDIRECT)
|
||||
ipv4_redirect(skb, net, 0, IPPROTO_UDP);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called by the ICMP module when it gets some
|
||||
* sort of error condition. If err < 0 then the socket should
|
||||
@@ -923,6 +932,8 @@ int udp_err(struct sk_buff *skb, u32 info)
|
||||
int harderr;
|
||||
int err;
|
||||
|
||||
udp_err_update_exception(net, skb, type, code, info);
|
||||
|
||||
uh = (struct udphdr *)(skb->data + (iph->ihl << 2));
|
||||
sk = __udp4_lib_lookup(net, iph->daddr, uh->dest,
|
||||
iph->saddr, uh->source, skb->dev->ifindex,
|
||||
@@ -2166,10 +2177,10 @@ int __udp_disconnect(struct sock *sk, int flags)
|
||||
*/
|
||||
|
||||
sk->sk_state = TCP_CLOSE;
|
||||
inet->inet_daddr = 0;
|
||||
WRITE_ONCE(inet->inet_daddr, 0);
|
||||
inet->inet_dport = 0;
|
||||
sock_rps_reset_rxhash(sk);
|
||||
sk->sk_bound_dev_if = 0;
|
||||
WRITE_ONCE(sk->sk_bound_dev_if, 0);
|
||||
if (!(sk->sk_userlocks & SOCK_BINDADDR_LOCK)) {
|
||||
inet_reset_saddr(sk);
|
||||
if (sk->sk_prot->rehash &&
|
||||
|
||||
@@ -445,7 +445,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
|
||||
hdr->segments_left--;
|
||||
addr = hdr->segments + hdr->segments_left;
|
||||
|
||||
skb_push(skb, sizeof(struct ipv6hdr));
|
||||
skb_push(skb, -skb_network_offset(skb));
|
||||
|
||||
if (skb->ip_summed == CHECKSUM_COMPLETE)
|
||||
seg6_update_csum(skb);
|
||||
@@ -469,7 +469,7 @@ static int ipv6_srh_rcv(struct sk_buff *skb, struct inet6_dev *idev)
|
||||
}
|
||||
ipv6_hdr(skb)->hop_limit--;
|
||||
|
||||
skb_pull(skb, sizeof(struct ipv6hdr));
|
||||
skb_pull(skb, skb_transport_offset(skb));
|
||||
goto looped_back;
|
||||
}
|
||||
|
||||
|
||||
@@ -878,6 +878,7 @@ static int ip6gre_xmit_other(struct sk_buff *skb, struct net_device *dev)
|
||||
static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
struct ip_tunnel_info *tun_info = NULL;
|
||||
struct ip6_tnl *t = netdev_priv(dev);
|
||||
__be16 payload_protocol;
|
||||
int ret;
|
||||
@@ -888,6 +889,9 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
|
||||
if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
|
||||
goto tx_err;
|
||||
|
||||
if (t->parms.collect_md)
|
||||
tun_info = skb_tunnel_info_txcheck(skb);
|
||||
|
||||
payload_protocol = skb_protocol(skb, true);
|
||||
switch (payload_protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
@@ -907,7 +911,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
|
||||
return NETDEV_TX_OK;
|
||||
|
||||
tx_err:
|
||||
if (!t->parms.collect_md || !IS_ERR(skb_tunnel_info_txcheck(skb)))
|
||||
if (!IS_ERR(tun_info))
|
||||
DEV_STATS_INC(dev, tx_errors);
|
||||
DEV_STATS_INC(dev, tx_dropped);
|
||||
kfree_skb(skb);
|
||||
|
||||
148
net/ipv6/mcast.c
148
net/ipv6/mcast.c
@@ -240,7 +240,8 @@ static int __ipv6_sock_mc_join(struct sock *sk, int ifindex,
|
||||
return err;
|
||||
}
|
||||
|
||||
mc_lst->next = np->ipv6_mc_list;
|
||||
rcu_assign_pointer(mc_lst->next,
|
||||
sock_dereference(np->ipv6_mc_list, sk));
|
||||
rcu_assign_pointer(np->ipv6_mc_list, mc_lst);
|
||||
|
||||
return 0;
|
||||
@@ -300,7 +301,8 @@ int ipv6_sock_mc_drop(struct sock *sk, int ifindex, const struct in6_addr *addr)
|
||||
lnk = &mc_lst->next) {
|
||||
if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
|
||||
ipv6_addr_equal(&mc_lst->addr, addr)) {
|
||||
*lnk = mc_lst->next;
|
||||
rcu_assign_pointer(*lnk,
|
||||
sock_dereference(mc_lst->next, sk));
|
||||
__ipv6_sock_mc_drop(sk, mc_lst);
|
||||
return 0;
|
||||
}
|
||||
@@ -333,7 +335,8 @@ void __ipv6_sock_mc_close(struct sock *sk)
|
||||
struct ipv6_mc_socklist *mc_lst;
|
||||
|
||||
while ((mc_lst = sock_dereference(np->ipv6_mc_list, sk)) != NULL) {
|
||||
np->ipv6_mc_list = mc_lst->next;
|
||||
rcu_assign_pointer(np->ipv6_mc_list,
|
||||
sock_dereference(mc_lst->next, sk));
|
||||
__ipv6_sock_mc_drop(sk, mc_lst);
|
||||
}
|
||||
}
|
||||
@@ -355,12 +358,12 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
|
||||
{
|
||||
struct ipv6_pinfo *inet6 = inet6_sk(sk);
|
||||
struct in6_addr *source, *group;
|
||||
struct ip6_sf_socklist *newpsl, *psl;
|
||||
struct net *net = sock_net(sk);
|
||||
struct ipv6_mc_socklist *pmc;
|
||||
struct ip6_sf_socklist *psl;
|
||||
struct inet6_dev *idev;
|
||||
int leavegroup = 0;
|
||||
int i, j, rv;
|
||||
int i, j;
|
||||
int err;
|
||||
|
||||
source = &((struct sockaddr_in6 *)&pgsr->gsr_source)->sin6_addr;
|
||||
@@ -409,13 +412,11 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
|
||||
if (!add) {
|
||||
if (!psl)
|
||||
goto done; /* err = -EADDRNOTAVAIL */
|
||||
rv = !0;
|
||||
for (i = 0; i < psl->sl_count; i++) {
|
||||
rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
|
||||
if (rv == 0)
|
||||
if (ipv6_addr_equal(&psl->sl_addr[i], source))
|
||||
break;
|
||||
}
|
||||
if (rv) /* source not found */
|
||||
if (i == psl->sl_count) /* source not found */
|
||||
goto done; /* err = -EADDRNOTAVAIL */
|
||||
|
||||
/* special case - (INCLUDE, empty) == LEAVE_GROUP */
|
||||
@@ -424,58 +425,74 @@ int ip6_mc_source(int add, int omode, struct sock *sk,
|
||||
goto done;
|
||||
}
|
||||
|
||||
atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
|
||||
&sk->sk_omem_alloc);
|
||||
|
||||
if (psl->sl_count == 1) {
|
||||
newpsl = NULL;
|
||||
} else {
|
||||
newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr,
|
||||
psl->sl_count - 1),
|
||||
GFP_KERNEL);
|
||||
if (!newpsl) {
|
||||
atomic_add(struct_size(psl, sl_addr, psl->sl_max),
|
||||
&sk->sk_omem_alloc);
|
||||
err = -ENOBUFS;
|
||||
goto done;
|
||||
}
|
||||
newpsl->sl_max = psl->sl_count - 1;
|
||||
newpsl->sl_count = psl->sl_count - 1;
|
||||
for (j = 0; j < i; j++)
|
||||
newpsl->sl_addr[j] = psl->sl_addr[j];
|
||||
for (j = i + 1; j < psl->sl_count; j++)
|
||||
newpsl->sl_addr[j - 1] = psl->sl_addr[j];
|
||||
}
|
||||
|
||||
/* update the interface filter */
|
||||
ip6_mc_del_src(idev, group, omode, 1, source, 1);
|
||||
|
||||
for (j = i+1; j < psl->sl_count; j++)
|
||||
psl->sl_addr[j-1] = psl->sl_addr[j];
|
||||
psl->sl_count--;
|
||||
rcu_assign_pointer(pmc->sflist, newpsl);
|
||||
kfree_rcu(psl, rcu);
|
||||
err = 0;
|
||||
goto done;
|
||||
}
|
||||
/* else, add a new source to the filter */
|
||||
|
||||
if (psl && psl->sl_count >= sysctl_mld_max_msf) {
|
||||
if (psl && psl->sl_count >= READ_ONCE(sysctl_mld_max_msf)) {
|
||||
err = -ENOBUFS;
|
||||
goto done;
|
||||
}
|
||||
if (!psl || psl->sl_count == psl->sl_max) {
|
||||
struct ip6_sf_socklist *newpsl;
|
||||
int count = IP6_SFBLOCK;
|
||||
if (psl) {
|
||||
for (i = 0; i < psl->sl_count; i++) {
|
||||
if (ipv6_addr_equal(&psl->sl_addr[i], source))
|
||||
goto done; /* err = -EADDRNOTAVAIL */
|
||||
}
|
||||
}
|
||||
|
||||
if (psl)
|
||||
count += psl->sl_max;
|
||||
newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, count),
|
||||
GFP_KERNEL);
|
||||
if (!newpsl) {
|
||||
err = -ENOBUFS;
|
||||
goto done;
|
||||
}
|
||||
newpsl->sl_max = count;
|
||||
newpsl->sl_count = count - IP6_SFBLOCK;
|
||||
if (psl) {
|
||||
for (i = 0; i < psl->sl_count; i++)
|
||||
newpsl->sl_addr[i] = psl->sl_addr[i];
|
||||
atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
|
||||
&sk->sk_omem_alloc);
|
||||
}
|
||||
rcu_assign_pointer(pmc->sflist, newpsl);
|
||||
kfree_rcu(psl, rcu);
|
||||
psl = newpsl;
|
||||
i = psl ? psl->sl_count + 1 : 1;
|
||||
newpsl = sock_kmalloc(sk, struct_size(newpsl, sl_addr, i),
|
||||
GFP_KERNEL);
|
||||
if (!newpsl) {
|
||||
err = -ENOBUFS;
|
||||
goto done;
|
||||
}
|
||||
rv = 1; /* > 0 for insert logic below if sl_count is 0 */
|
||||
for (i = 0; i < psl->sl_count; i++) {
|
||||
rv = !ipv6_addr_equal(&psl->sl_addr[i], source);
|
||||
if (rv == 0) /* There is an error in the address. */
|
||||
goto done;
|
||||
newpsl->sl_max = i;
|
||||
newpsl->sl_count = i;
|
||||
if (psl) {
|
||||
for (j = 0; j < psl->sl_count; j++)
|
||||
newpsl->sl_addr[j] = psl->sl_addr[j];
|
||||
}
|
||||
for (j = psl->sl_count-1; j >= i; j--)
|
||||
psl->sl_addr[j+1] = psl->sl_addr[j];
|
||||
psl->sl_addr[i] = *source;
|
||||
psl->sl_count++;
|
||||
err = 0;
|
||||
newpsl->sl_addr[i - 1] = *source;
|
||||
|
||||
/* update the interface list */
|
||||
ip6_mc_add_src(idev, group, omode, 1, source, 1);
|
||||
|
||||
if (psl)
|
||||
atomic_sub(struct_size(psl, sl_addr, psl->sl_max),
|
||||
&sk->sk_omem_alloc);
|
||||
rcu_assign_pointer(pmc->sflist, newpsl);
|
||||
kfree_rcu(psl, rcu);
|
||||
err = 0;
|
||||
done:
|
||||
mutex_unlock(&idev->mc_lock);
|
||||
in6_dev_put(idev);
|
||||
@@ -784,9 +801,11 @@ static void mld_del_delrec(struct inet6_dev *idev, struct ifmcaddr6 *im)
|
||||
if (!pmc)
|
||||
return;
|
||||
if (pmc_prev)
|
||||
rcu_assign_pointer(pmc_prev->next, pmc->next);
|
||||
rcu_assign_pointer(pmc_prev->next,
|
||||
mc_dereference(pmc->next, idev));
|
||||
else
|
||||
rcu_assign_pointer(idev->mc_tomb, pmc->next);
|
||||
rcu_assign_pointer(idev->mc_tomb,
|
||||
mc_dereference(pmc->next, idev));
|
||||
|
||||
im->idev = pmc->idev;
|
||||
if (im->mca_sfmode == MCAST_INCLUDE) {
|
||||
@@ -966,7 +985,7 @@ static int __ipv6_dev_mc_inc(struct net_device *dev,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(mc->next, idev->mc_list);
|
||||
rcu_assign_pointer(mc->next, mc_dereference(idev->mc_list, idev));
|
||||
rcu_assign_pointer(idev->mc_list, mc);
|
||||
|
||||
mld_del_delrec(idev, mc);
|
||||
@@ -1000,7 +1019,8 @@ int __ipv6_dev_mc_dec(struct inet6_dev *idev, const struct in6_addr *addr)
|
||||
|
||||
WRITE_ONCE(ma->mca_users, new_users);
|
||||
if (new_users == 0) {
|
||||
*map = ma->next;
|
||||
rcu_assign_pointer(*map,
|
||||
mc_dereference(ma->next, idev));
|
||||
|
||||
igmp6_group_dropped(ma);
|
||||
inet6_ifmcaddr_notify(idev->dev, ma,
|
||||
@@ -2351,14 +2371,18 @@ static int ip6_mc_del1_src(struct ifmcaddr6 *pmc, int sfmode,
|
||||
|
||||
if (psf->sf_oldin && !(pmc->mca_flags & MAF_NOREPORT) &&
|
||||
!mld_in_v1_mode(idev)) {
|
||||
psf->sf_crcount = idev->mc_qrv;
|
||||
rcu_assign_pointer(psf->sf_next,
|
||||
mc_dereference(pmc->mca_tomb, idev));
|
||||
rcu_assign_pointer(pmc->mca_tomb, psf);
|
||||
rv = 1;
|
||||
} else {
|
||||
kfree_rcu(psf, rcu);
|
||||
struct ip6_sf_list *dpsf = kmalloc_obj(*dpsf);
|
||||
|
||||
if (dpsf) {
|
||||
*dpsf = *psf;
|
||||
dpsf->sf_crcount = idev->mc_qrv;
|
||||
rcu_assign_pointer(dpsf->sf_next,
|
||||
mc_dereference(pmc->mca_tomb, idev));
|
||||
rcu_assign_pointer(pmc->mca_tomb, dpsf);
|
||||
rv = 1;
|
||||
}
|
||||
}
|
||||
kfree_rcu(psf, rcu);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -2621,7 +2645,7 @@ static void ip6_mc_clear_src(struct ifmcaddr6 *pmc)
|
||||
|
||||
static void igmp6_join_group(struct ifmcaddr6 *ma)
|
||||
{
|
||||
unsigned long delay;
|
||||
unsigned long delay, interval;
|
||||
|
||||
mc_assert_locked(ma->idev);
|
||||
|
||||
@@ -2630,13 +2654,17 @@ static void igmp6_join_group(struct ifmcaddr6 *ma)
|
||||
|
||||
igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
|
||||
|
||||
delay = get_random_u32_below(unsolicited_report_interval(ma->idev));
|
||||
interval = unsolicited_report_interval(ma->idev);
|
||||
delay = interval;
|
||||
|
||||
if (cancel_delayed_work(&ma->mca_work)) {
|
||||
refcount_dec(&ma->mca_refcnt);
|
||||
delay = ma->mca_work.timer.expires - jiffies;
|
||||
}
|
||||
|
||||
if (delay >= interval)
|
||||
delay = get_random_u32_below(interval);
|
||||
|
||||
if (!mod_delayed_work(mld_wq, &ma->mca_work, delay))
|
||||
refcount_inc(&ma->mca_refcnt);
|
||||
WRITE_ONCE(ma->mca_flags, ma->mca_flags |
|
||||
@@ -3001,7 +3029,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
|
||||
struct ifmcaddr6 *im = (struct ifmcaddr6 *)v;
|
||||
struct igmp6_mc_iter_state *state = igmp6_mc_seq_private(seq);
|
||||
unsigned int mca_flags = READ_ONCE(im->mca_flags);
|
||||
unsigned long expires = READ_ONCE(im->mca_work.timer.expires);
|
||||
long delta = READ_ONCE(im->mca_work.timer.expires) - jiffies;
|
||||
|
||||
seq_printf(seq,
|
||||
"%-4d %-15s %pi6 %5d %08X %ld\n",
|
||||
@@ -3009,7 +3037,7 @@ static int igmp6_mc_seq_show(struct seq_file *seq, void *v)
|
||||
&im->mca_addr,
|
||||
READ_ONCE(im->mca_users), mca_flags,
|
||||
(mca_flags & MAF_TIMER_RUNNING) ?
|
||||
jiffies_to_clock_t(expires - jiffies) : 0);
|
||||
jiffies_delta_to_clock_t(delta) : 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -3255,7 +3255,7 @@ void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif)
|
||||
|
||||
void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
|
||||
{
|
||||
ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if,
|
||||
ip6_redirect(skb, sock_net(sk), skb->dev->ifindex,
|
||||
READ_ONCE(sk->sk_mark), sk_uid(sk));
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,18 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
|
||||
|
||||
p = tcp_gro_lookup(head, th);
|
||||
if (p) {
|
||||
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
|
||||
/* flist GRO applies to consecutive non-GSO skbs */
|
||||
if (!skb_is_gso(skb) || !NAPI_GRO_CB(p)->is_flist) {
|
||||
NAPI_GRO_CB(skb)->is_flist = NAPI_GRO_CB(p)->is_flist;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fall back to the regular GRO path */
|
||||
if (NAPI_GRO_CB(p)->count == 1)
|
||||
NAPI_GRO_CB(p)->is_flist = 0;
|
||||
|
||||
NAPI_GRO_CB(skb)->is_flist = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -36,7 +47,7 @@ static void tcp6_check_fraglist_gro(struct list_head *head, struct sk_buff *skb,
|
||||
sk = __inet6_lookup_established(net, &hdr->saddr, th->source,
|
||||
&hdr->daddr, ntohs(th->dest),
|
||||
iif, sdif);
|
||||
NAPI_GRO_CB(skb)->is_flist = !sk;
|
||||
NAPI_GRO_CB(skb)->is_flist = !sk && !skb_is_gso(skb);
|
||||
if (sk)
|
||||
sock_gen_put(sk);
|
||||
#endif /* IS_ENABLED(CONFIG_IPV6) */
|
||||
|
||||
@@ -690,6 +690,17 @@ static struct sock *__udp6_lib_err_encap(struct net *net,
|
||||
return sk;
|
||||
}
|
||||
|
||||
static void udpv6_err_update_exception(struct net *net, struct sk_buff *skb,
|
||||
u8 type, __be32 info)
|
||||
{
|
||||
if (type == ICMPV6_PKT_TOOBIG)
|
||||
ip6_update_pmtu(skb, net, info, skb->dev->ifindex, 0,
|
||||
sock_net_uid(net, NULL));
|
||||
else if (type == NDISC_REDIRECT)
|
||||
ip6_redirect(skb, net, skb->dev->ifindex, 0,
|
||||
sock_net_uid(net, NULL));
|
||||
}
|
||||
|
||||
static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
u8 type, u8 code, int offset, __be32 info)
|
||||
{
|
||||
@@ -703,6 +714,8 @@ static int udpv6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
||||
int harderr;
|
||||
int err;
|
||||
|
||||
udpv6_err_update_exception(net, skb, type, info);
|
||||
|
||||
daddr = seg6_get_daddr(skb, opt) ? : &hdr->daddr;
|
||||
saddr = &hdr->saddr;
|
||||
sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
|
||||
|
||||
@@ -210,12 +210,6 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
|
||||
phs_hdr->flags = flags;
|
||||
if (flags == AF_IUCV_FLAG_SYN)
|
||||
phs_hdr->window = iucv->msglimit;
|
||||
else if ((flags == AF_IUCV_FLAG_WIN) || !flags) {
|
||||
confirm_recv = atomic_read(&iucv->msg_recv);
|
||||
phs_hdr->window = confirm_recv;
|
||||
if (confirm_recv)
|
||||
phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
|
||||
}
|
||||
memcpy(phs_hdr->destUserID, iucv->dst_user_id, 8);
|
||||
memcpy(phs_hdr->destAppName, iucv->dst_name, 8);
|
||||
memcpy(phs_hdr->srcUserID, iucv->src_user_id, 8);
|
||||
@@ -250,13 +244,22 @@ static int afiucv_hs_send(struct iucv_message *imsg, struct sock *sock,
|
||||
}
|
||||
skb->protocol = cpu_to_be16(ETH_P_AF_IUCV);
|
||||
|
||||
/* Claim the receive credit here, not while building the header: every
|
||||
* way this frame can be dropped has now been ruled out, so the window
|
||||
* is zeroed only for as long as the transmit itself takes.
|
||||
*/
|
||||
if (flags == AF_IUCV_FLAG_WIN || !flags) {
|
||||
confirm_recv = atomic_xchg(&iucv->msg_recv, 0);
|
||||
phs_hdr->window = confirm_recv;
|
||||
if (confirm_recv)
|
||||
phs_hdr->flags = phs_hdr->flags | AF_IUCV_FLAG_WIN;
|
||||
}
|
||||
|
||||
atomic_inc(&iucv->skbs_in_xmit);
|
||||
err = dev_queue_xmit(skb);
|
||||
if (net_xmit_eval(err)) {
|
||||
atomic_dec(&iucv->skbs_in_xmit);
|
||||
} else {
|
||||
atomic_sub(confirm_recv, &iucv->msg_recv);
|
||||
WARN_ON(atomic_read(&iucv->msg_recv) < 0);
|
||||
atomic_add(confirm_recv, &iucv->msg_recv);
|
||||
}
|
||||
return net_xmit_eval(err);
|
||||
|
||||
@@ -1241,6 +1244,7 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
struct iucv_sock *iucv = iucv_sk(sk);
|
||||
unsigned int copied, rlen;
|
||||
struct sk_buff *skb, *rskb, *cskb;
|
||||
bool send_win = false;
|
||||
int err = 0;
|
||||
u32 offset;
|
||||
|
||||
@@ -1331,16 +1335,20 @@ static int iucv_sock_recvmsg(struct socket *sock, struct msghdr *msg,
|
||||
if (skb_queue_empty(&iucv->backlog_skb_q)) {
|
||||
if (!list_empty(&iucv->message_q.list))
|
||||
iucv_process_message_q(sk);
|
||||
if (atomic_read(&iucv->msg_recv) >=
|
||||
iucv->msglimit / 2) {
|
||||
err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
|
||||
if (err) {
|
||||
sk->sk_state = IUCV_DISCONN;
|
||||
sk->sk_state_change(sk);
|
||||
}
|
||||
}
|
||||
if (iucv->transport == AF_IUCV_TRANS_HIPER &&
|
||||
atomic_read(&iucv->msg_recv) >=
|
||||
iucv->msglimit / 2)
|
||||
send_win = true;
|
||||
}
|
||||
spin_unlock_bh(&iucv->message_q.lock);
|
||||
|
||||
if (send_win) {
|
||||
err = iucv_send_ctrl(sk, AF_IUCV_FLAG_WIN);
|
||||
if (err) {
|
||||
sk->sk_state = IUCV_DISCONN;
|
||||
sk->sk_state_change(sk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
@@ -76,7 +76,12 @@ struct ieee802154_local {
|
||||
struct work_struct rx_mac_cmd_work;
|
||||
|
||||
/* Association */
|
||||
struct ieee802154_pan_device *assoc_dev;
|
||||
/* assoc_lock protects assoc_dev_extended_addr, assoc_addr,
|
||||
* assoc_status, the assoc_done reinit/complete pairing and the
|
||||
* IEEE802154_IS_ASSOCIATING bit in @ongoing.
|
||||
*/
|
||||
spinlock_t assoc_lock;
|
||||
__le64 assoc_dev_extended_addr;
|
||||
struct completion assoc_done;
|
||||
__le16 assoc_addr;
|
||||
u8 assoc_status;
|
||||
|
||||
@@ -104,6 +104,7 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
|
||||
INIT_WORK(&local->rx_mac_cmd_work, mac802154_rx_mac_cmd_worker);
|
||||
|
||||
init_completion(&local->assoc_done);
|
||||
spin_lock_init(&local->assoc_lock);
|
||||
|
||||
/* init supported flags with 802.15.4 default ranges */
|
||||
phy->supported.max_minbe = 8;
|
||||
|
||||
@@ -536,7 +536,9 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
|
||||
struct ieee802154_association_req_frame frame = {};
|
||||
struct ieee802154_local *local = sdata->local;
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
__le16 resp_short_addr;
|
||||
struct sk_buff *skb;
|
||||
u8 resp_status;
|
||||
int ret;
|
||||
|
||||
frame.mhr.fc.type = IEEE802154_FC_TYPE_MAC_CMD;
|
||||
@@ -578,9 +580,11 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
|
||||
return ret;
|
||||
}
|
||||
|
||||
local->assoc_dev = coord;
|
||||
spin_lock(&local->assoc_lock);
|
||||
reinit_completion(&local->assoc_done);
|
||||
local->assoc_dev_extended_addr = coord->extended_addr;
|
||||
set_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
|
||||
spin_unlock(&local->assoc_lock);
|
||||
|
||||
ret = ieee802154_mlme_tx_one_locked(local, sdata, skb);
|
||||
if (ret) {
|
||||
@@ -599,25 +603,37 @@ int mac802154_perform_association(struct ieee802154_sub_if_data *sdata,
|
||||
goto clear_assoc;
|
||||
}
|
||||
|
||||
if (local->assoc_status != IEEE802154_ASSOCIATION_SUCCESSFUL) {
|
||||
if (local->assoc_status == IEEE802154_PAN_AT_CAPACITY)
|
||||
/* The association is complete: mac802154_process_association_resp()
|
||||
* cleared the associating bit before waking us, so a second (e.g.
|
||||
* malicious) ASSOC RESP can no longer pass the recheck and overwrite
|
||||
* the result. Snapshot assoc_status/assoc_addr under the lock.
|
||||
*/
|
||||
spin_lock(&local->assoc_lock);
|
||||
resp_status = local->assoc_status;
|
||||
resp_short_addr = local->assoc_addr;
|
||||
spin_unlock(&local->assoc_lock);
|
||||
|
||||
if (resp_status != IEEE802154_ASSOCIATION_SUCCESSFUL) {
|
||||
if (resp_status == IEEE802154_PAN_AT_CAPACITY)
|
||||
ret = -ERANGE;
|
||||
else
|
||||
ret = -EPERM;
|
||||
|
||||
dev_warn(&sdata->dev->dev,
|
||||
"Negative ASSOC RESP received from %8phC: %s\n", &ceaddr,
|
||||
local->assoc_status == IEEE802154_PAN_AT_CAPACITY ?
|
||||
resp_status == IEEE802154_PAN_AT_CAPACITY ?
|
||||
"PAN at capacity" : "access denied");
|
||||
goto clear_assoc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
*short_addr = local->assoc_addr;
|
||||
*short_addr = resp_short_addr;
|
||||
|
||||
return 0;
|
||||
|
||||
clear_assoc:
|
||||
spin_lock(&local->assoc_lock);
|
||||
clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
|
||||
local->assoc_dev = NULL;
|
||||
spin_unlock(&local->assoc_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -639,19 +655,28 @@ int mac802154_process_association_resp(struct ieee802154_sub_if_data *sdata,
|
||||
dest->mode != IEEE802154_EXTENDED_ADDRESSING))
|
||||
return -EINVAL;
|
||||
|
||||
if (unlikely(dest->extended_addr != wpan_dev->extended_addr ||
|
||||
src->extended_addr != local->assoc_dev->extended_addr))
|
||||
spin_lock(&local->assoc_lock);
|
||||
if (unlikely(!test_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing) ||
|
||||
dest->extended_addr != wpan_dev->extended_addr ||
|
||||
src->extended_addr != local->assoc_dev_extended_addr)) {
|
||||
spin_unlock(&local->assoc_lock);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
memcpy(&resp_pl, skb->data, sizeof(resp_pl));
|
||||
local->assoc_addr = resp_pl.short_addr;
|
||||
local->assoc_status = resp_pl.status;
|
||||
/* Clear the associating bit before waking the waiter: once the result
|
||||
* is saved, any subsequent (e.g. malicious) ASSOC RESP must fail the
|
||||
* test_bit() recheck above and can no longer overwrite the result.
|
||||
*/
|
||||
clear_bit(IEEE802154_IS_ASSOCIATING, &local->ongoing);
|
||||
complete(&local->assoc_done);
|
||||
spin_unlock(&local->assoc_lock);
|
||||
|
||||
dev_dbg(&skb->dev->dev,
|
||||
"ASSOC RESP 0x%x received from %8phC, getting short address %04x\n",
|
||||
local->assoc_status, &deaddr, local->assoc_addr);
|
||||
|
||||
complete(&local->assoc_done);
|
||||
resp_pl.status, &deaddr, resp_pl.short_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3109,8 +3109,7 @@ static void mptcp_do_fastclose(struct sock *sk)
|
||||
*/
|
||||
inet_csk(ssk)->icsk_ack.rcv_mss = TCP_MIN_MSS;
|
||||
|
||||
tcp_send_active_reset(ssk, ssk->sk_allocation,
|
||||
SK_RST_REASON_TCP_ABORT_ON_CLOSE);
|
||||
tcp_send_active_reset(ssk, SK_RST_REASON_TCP_ABORT_ON_CLOSE);
|
||||
unlock:
|
||||
release_sock(ssk);
|
||||
}
|
||||
|
||||
@@ -690,7 +690,7 @@ mptcp_send_active_reset_reason(struct sock *sk)
|
||||
enum sk_rst_reason reason;
|
||||
|
||||
reason = sk_rst_convert_mptcp_reason(subflow->reset_reason);
|
||||
tcp_send_active_reset(sk, GFP_ATOMIC, reason);
|
||||
tcp_send_active_reset(sk, reason);
|
||||
}
|
||||
|
||||
/* Made the fwd mem carried by the given skb available to the msk,
|
||||
|
||||
@@ -2675,7 +2675,8 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,
|
||||
int size_max, void **data)
|
||||
{
|
||||
union tpacket_uhdr ph;
|
||||
int tp_len, off;
|
||||
u32 tp_len;
|
||||
int off;
|
||||
|
||||
ph.raw = frame;
|
||||
|
||||
@@ -2695,7 +2696,7 @@ static int tpacket_parse_header(struct packet_sock *po, void *frame,
|
||||
break;
|
||||
}
|
||||
if (unlikely(tp_len > size_max)) {
|
||||
pr_err("packet size is too long (%d > %d)\n", tp_len, size_max);
|
||||
pr_err("packet size is too long (%u > %d)\n", tp_len, size_max);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <net/sock.h>
|
||||
|
||||
@@ -120,8 +121,10 @@ static DEFINE_XARRAY_ALLOC(qrtr_ports);
|
||||
* @nid: node id
|
||||
* @qrtr_tx_flow: xarray of qrtr_tx_flow, keyed by node << 32 | port
|
||||
* @qrtr_tx_lock: lock for qrtr_tx_flow inserts
|
||||
* @hello_sent: hello packet send successful
|
||||
* @rx_queue: receive queue
|
||||
* @item: list item for broadcast list
|
||||
* @say_hello: delayed work for sending hello packet
|
||||
*/
|
||||
struct qrtr_node {
|
||||
struct mutex ep_lock;
|
||||
@@ -132,8 +135,11 @@ struct qrtr_node {
|
||||
struct xarray qrtr_tx_flow;
|
||||
struct mutex qrtr_tx_lock; /* for qrtr_tx_flow */
|
||||
|
||||
bool hello_sent;
|
||||
|
||||
struct sk_buff_head rx_queue;
|
||||
struct list_head item;
|
||||
struct delayed_work say_hello;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -187,6 +193,8 @@ static void __qrtr_node_release(struct kref *kref)
|
||||
list_del(&node->item);
|
||||
mutex_unlock(&qrtr_node_lock);
|
||||
|
||||
cancel_delayed_work_sync(&node->say_hello);
|
||||
|
||||
skb_queue_purge(&node->rx_queue);
|
||||
|
||||
/* Free tx flow counters */
|
||||
@@ -341,6 +349,14 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
|
||||
size_t len = skb->len;
|
||||
int rc, confirm_rx;
|
||||
|
||||
mutex_lock(&node->ep_lock);
|
||||
if (!node->hello_sent && type != QRTR_TYPE_HELLO) {
|
||||
mutex_unlock(&node->ep_lock);
|
||||
kfree_skb(skb);
|
||||
return -EAGAIN;
|
||||
}
|
||||
mutex_unlock(&node->ep_lock);
|
||||
|
||||
confirm_rx = qrtr_tx_wait(node, to->sq_node, to->sq_port, type);
|
||||
if (confirm_rx < 0) {
|
||||
kfree_skb(skb);
|
||||
@@ -353,7 +369,7 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
|
||||
hdr->src_node_id = cpu_to_le32(from->sq_node);
|
||||
hdr->src_port_id = cpu_to_le32(from->sq_port);
|
||||
if (to->sq_port == QRTR_PORT_CTRL) {
|
||||
hdr->dst_node_id = cpu_to_le32(node->nid);
|
||||
hdr->dst_node_id = cpu_to_le32(READ_ONCE(node->nid));
|
||||
hdr->dst_port_id = cpu_to_le32(QRTR_PORT_CTRL);
|
||||
} else {
|
||||
hdr->dst_node_id = cpu_to_le32(to->sq_node);
|
||||
@@ -372,6 +388,8 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
|
||||
rc = node->ep->xmit(node->ep, skb);
|
||||
else
|
||||
kfree_skb(skb);
|
||||
if (!rc && type == QRTR_TYPE_HELLO)
|
||||
node->hello_sent = true;
|
||||
mutex_unlock(&node->ep_lock);
|
||||
}
|
||||
/* Need to ensure that a subsequent message carries the otherwise lost
|
||||
@@ -379,6 +397,9 @@ static int qrtr_node_enqueue(struct qrtr_node *node, struct sk_buff *skb,
|
||||
if (rc && confirm_rx)
|
||||
qrtr_tx_flow_failed(node, to->sq_node, to->sq_port);
|
||||
|
||||
if (rc == -EAGAIN && type == QRTR_TYPE_HELLO)
|
||||
schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -416,7 +437,7 @@ static void qrtr_node_assign(struct qrtr_node *node, unsigned int nid)
|
||||
spin_lock_irqsave(&qrtr_nodes_lock, flags);
|
||||
radix_tree_insert(&qrtr_nodes, nid, node);
|
||||
if (node->nid == QRTR_EP_NID_AUTO)
|
||||
node->nid = nid;
|
||||
WRITE_ONCE(node->nid, nid);
|
||||
spin_unlock_irqrestore(&qrtr_nodes_lock, flags);
|
||||
}
|
||||
|
||||
@@ -570,6 +591,38 @@ static struct sk_buff *qrtr_alloc_ctrl_packet(struct qrtr_ctrl_pkt **pkt,
|
||||
return skb;
|
||||
}
|
||||
|
||||
static void qrtr_hello_work(struct work_struct *work)
|
||||
{
|
||||
struct sockaddr_qrtr from = {AF_QIPCRTR, 0, QRTR_PORT_CTRL};
|
||||
struct sockaddr_qrtr to = {AF_QIPCRTR, 0, QRTR_PORT_CTRL};
|
||||
struct qrtr_ctrl_pkt *pkt;
|
||||
struct qrtr_node *node;
|
||||
struct qrtr_sock *ctrl;
|
||||
struct sk_buff *skb;
|
||||
|
||||
node = container_of(to_delayed_work(work), struct qrtr_node, say_hello);
|
||||
|
||||
/* NS must be bound before we can send; retry with backoff if not ready */
|
||||
ctrl = qrtr_port_lookup(QRTR_PORT_CTRL);
|
||||
if (!ctrl) {
|
||||
schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
|
||||
return;
|
||||
}
|
||||
|
||||
skb = qrtr_alloc_ctrl_packet(&pkt, GFP_KERNEL);
|
||||
if (!skb) {
|
||||
qrtr_port_put(ctrl);
|
||||
schedule_delayed_work(&node->say_hello, msecs_to_jiffies(100));
|
||||
return;
|
||||
}
|
||||
|
||||
pkt->cmd = cpu_to_le32(QRTR_TYPE_HELLO);
|
||||
from.sq_node = qrtr_local_nid;
|
||||
to.sq_node = node->nid;
|
||||
qrtr_node_enqueue(node, skb, QRTR_TYPE_HELLO, &from, &to);
|
||||
qrtr_port_put(ctrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* qrtr_endpoint_register() - register a new endpoint
|
||||
* @ep: endpoint to register
|
||||
@@ -595,6 +648,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
|
||||
node->nid = QRTR_EP_NID_AUTO;
|
||||
node->ep = ep;
|
||||
|
||||
node->hello_sent = false;
|
||||
INIT_DELAYED_WORK(&node->say_hello, qrtr_hello_work);
|
||||
|
||||
xa_init(&node->qrtr_tx_flow);
|
||||
mutex_init(&node->qrtr_tx_lock);
|
||||
|
||||
@@ -605,6 +661,9 @@ int qrtr_endpoint_register(struct qrtr_endpoint *ep, unsigned int nid)
|
||||
mutex_unlock(&qrtr_node_lock);
|
||||
ep->node = node;
|
||||
|
||||
/* Initiate HELLO handshake from the core layer */
|
||||
schedule_delayed_work(&node->say_hello, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qrtr_endpoint_register);
|
||||
@@ -879,6 +938,9 @@ static int qrtr_bcast_enqueue(struct qrtr_node *node, struct sk_buff *skb,
|
||||
|
||||
mutex_lock(&qrtr_node_lock);
|
||||
list_for_each_entry(node, &qrtr_all_nodes, item) {
|
||||
/* Skip nodes with no assigned node ID yet. */
|
||||
if (READ_ONCE(node->nid) == QRTR_EP_NID_AUTO)
|
||||
continue;
|
||||
skbn = pskb_copy(skb, GFP_KERNEL);
|
||||
if (!skbn)
|
||||
break;
|
||||
|
||||
@@ -212,6 +212,7 @@ static void lookup_notify(struct sockaddr_qrtr *to, struct qrtr_server *srv,
|
||||
pr_err("failed to send lookup notification\n");
|
||||
}
|
||||
|
||||
/* Announce the list of servers registered on the local node */
|
||||
static int announce_servers(struct sockaddr_qrtr *sq)
|
||||
{
|
||||
struct qrtr_server *srv;
|
||||
@@ -326,38 +327,8 @@ static int server_del(struct qrtr_node *node, unsigned int port, bool bcast)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int say_hello(struct sockaddr_qrtr *dest)
|
||||
{
|
||||
struct qrtr_ctrl_pkt pkt;
|
||||
struct msghdr msg = { };
|
||||
struct kvec iv;
|
||||
int ret;
|
||||
|
||||
iv.iov_base = &pkt;
|
||||
iv.iov_len = sizeof(pkt);
|
||||
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
pkt.cmd = cpu_to_le32(QRTR_TYPE_HELLO);
|
||||
|
||||
msg.msg_name = (struct sockaddr *)dest;
|
||||
msg.msg_namelen = sizeof(*dest);
|
||||
|
||||
ret = kernel_sendmsg(qrtr_ns.sock, &msg, &iv, 1, sizeof(pkt));
|
||||
if (ret < 0)
|
||||
pr_err("failed to send hello msg\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Announce the list of servers registered on the local node */
|
||||
static int ctrl_cmd_hello(struct sockaddr_qrtr *sq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = say_hello(sq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return announce_servers(sq);
|
||||
}
|
||||
|
||||
@@ -774,10 +745,6 @@ int qrtr_ns_init(void)
|
||||
qrtr_ns.bcast_sq.sq_node = QRTR_NODE_BCAST;
|
||||
qrtr_ns.bcast_sq.sq_port = QRTR_PORT_CTRL;
|
||||
|
||||
ret = say_hello(&qrtr_ns.bcast_sq);
|
||||
if (ret < 0)
|
||||
goto err_wq;
|
||||
|
||||
/* As the qrtr ns socket owner and creator is the same module, we have
|
||||
* to decrease the qrtr module reference count to guarantee that it
|
||||
* remains zero after the ns socket is created, otherwise, executing
|
||||
|
||||
@@ -106,10 +106,12 @@ static struct rds_connection *rds_conn_lookup(struct net *net,
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called by transports as they're bringing down a connection.
|
||||
* It clears partial message state so that the transport can start sending
|
||||
* and receiving over this connection again in the future. It is up to
|
||||
* the transport to have serialized this call with its send and recv.
|
||||
* This is called by rds_conn_shutdown() once the transport has brought
|
||||
* a path down. It clears partial message state so that the transport
|
||||
* can start sending and receiving over this path again in the future.
|
||||
* The caller owns RDS_IN_XMIT and RDS_RECV_REFILL across this call,
|
||||
* which is what serializes it against the send and receive-refill
|
||||
* paths.
|
||||
*/
|
||||
static void rds_conn_path_reset(struct rds_conn_path *cp)
|
||||
{
|
||||
@@ -120,7 +122,16 @@ static void rds_conn_path_reset(struct rds_conn_path *cp)
|
||||
|
||||
rds_stats_inc(s_conn_reset);
|
||||
rds_send_path_reset(cp);
|
||||
cp->cp_flags = 0;
|
||||
|
||||
/* Clear the bits the reset is responsible for individually: a
|
||||
* blanket cp_flags = 0 is a plain store that can clobber a
|
||||
* concurrent atomic read-modify-write on the same word.
|
||||
* RDS_IN_XMIT and RDS_RECV_REFILL are held as locks by the
|
||||
* caller, rds_conn_shutdown(), which releases them once the
|
||||
* teardown is complete.
|
||||
*/
|
||||
clear_bit(RDS_LL_SEND_FULL, &cp->cp_flags);
|
||||
clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
|
||||
|
||||
/* Do not clear next_rx_seq here, else we cannot distinguish
|
||||
* retransmitted packets from new packets, and will hand all
|
||||
@@ -406,28 +417,70 @@ void rds_conn_shutdown(struct rds_conn_path *cp)
|
||||
}
|
||||
mutex_unlock(&cp->cp_cm_lock);
|
||||
|
||||
/* Quiesce the transmit and receive-refill paths by
|
||||
* acquiring their bit locks, not merely waiting for
|
||||
* them to be released: with a plain wait, either path
|
||||
* can re-take its lock the instant after we sample it
|
||||
* clear and then run concurrently with the transport
|
||||
* shutdown and the path reset below. Holding both
|
||||
* locks across the teardown makes that structurally
|
||||
* impossible.
|
||||
*/
|
||||
wait_event(cp->cp_waitq,
|
||||
!test_bit(RDS_IN_XMIT, &cp->cp_flags));
|
||||
!test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags));
|
||||
wait_event(cp->cp_waitq,
|
||||
!test_bit(RDS_RECV_REFILL, &cp->cp_flags));
|
||||
!test_and_set_bit(RDS_RECV_REFILL, &cp->cp_flags));
|
||||
|
||||
conn->c_trans->conn_path_shutdown(cp);
|
||||
rds_conn_path_reset(cp);
|
||||
|
||||
/* Release the two locks and wake any waiter (e.g.
|
||||
* rds_tcp_reset_callbacks()) that blocked on them while
|
||||
* we held them. The unlock orders the transport's ring
|
||||
* re-initialization and the path reset above before
|
||||
* either bit is seen clear. rds_conn_path_reset() leaves
|
||||
* both bits alone: ownership ends here, not inside the
|
||||
* reset.
|
||||
*/
|
||||
clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags);
|
||||
clear_bit_unlock(RDS_RECV_REFILL, &cp->cp_flags);
|
||||
wake_up_all(&cp->cp_waitq);
|
||||
|
||||
if (!rds_conn_path_transition(cp, RDS_CONN_DISCONNECTING,
|
||||
RDS_CONN_DOWN) &&
|
||||
!rds_conn_path_transition(cp, RDS_CONN_ERROR,
|
||||
RDS_CONN_DOWN)) {
|
||||
/* This can happen - eg when we're in the middle of tearing
|
||||
* down the connection, and someone unloads the rds module.
|
||||
* Quite reproducible with loopback connections.
|
||||
* Mostly harmless.
|
||||
/* The path was dropped again while we tore it
|
||||
* down: by a socket state-change callback in
|
||||
* irq context on receipt of a FIN, or by an
|
||||
* accept that claimed the path just before a
|
||||
* drop put it back to RDS_CONN_ERROR and then
|
||||
* installed a fresh socket on it. Unless a
|
||||
* pending destroy suppressed it, the drop also
|
||||
* queued another shutdown pass, and that pass
|
||||
* must run, because it is what tears down
|
||||
* whatever attached to the path after the
|
||||
* transport shutdown above sampled its state.
|
||||
* Consuming the RDS_CONN_ERROR here would turn
|
||||
* that pass into a no-op: leave the state
|
||||
* alone, and let the pass finish the job.
|
||||
*
|
||||
* Note that this also happens with rds-tcp because
|
||||
* we could have triggered rds_conn_path_drop in irq
|
||||
* mode from rds_tcp_state change on the receipt of
|
||||
* a FIN, thus we need to recheck for RDS_CONN_ERROR
|
||||
* here.
|
||||
* Quiesce the reconnect timer before bailing
|
||||
* out, though. When a pending destroy did
|
||||
* suppress the queue, no later pass runs, and
|
||||
* rds_conn_path_destroy() is about to flush
|
||||
* cp_down_w and free the path: it must not
|
||||
* find cp_conn_w still armed. A successor
|
||||
* pass, when there is one, re-arms the
|
||||
* reconnect from its own tail.
|
||||
*/
|
||||
cancel_delayed_work_sync(&cp->cp_conn_w);
|
||||
clear_bit(RDS_RECONNECT_PENDING, &cp->cp_flags);
|
||||
|
||||
if (rds_conn_path_state(cp) == RDS_CONN_ERROR)
|
||||
return;
|
||||
/* No current cp_state writer leaves a
|
||||
* DISCONNECTING path in any state but
|
||||
* RDS_CONN_ERROR; report loudly if one ever
|
||||
* does.
|
||||
*/
|
||||
rds_conn_path_error(cp, "%s: failed to transition "
|
||||
"to state DOWN, current state "
|
||||
|
||||
@@ -363,15 +363,14 @@ static int acquire_refill(struct rds_connection *conn)
|
||||
|
||||
static void release_refill(struct rds_connection *conn)
|
||||
{
|
||||
clear_bit(RDS_RECV_REFILL, &conn->c_flags);
|
||||
smp_mb__after_atomic();
|
||||
clear_bit_unlock(RDS_RECV_REFILL, &conn->c_flags);
|
||||
|
||||
/* 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
|
||||
* the system-wide hashed waitqueue buckets in the fast path only to
|
||||
* almost never find waiters.
|
||||
*/
|
||||
if (waitqueue_active(&conn->c_waitq))
|
||||
if (wq_has_sleeper(&conn->c_waitq))
|
||||
wake_up_all(&conn->c_waitq);
|
||||
}
|
||||
|
||||
@@ -392,7 +391,9 @@ void rds_ib_recv_refill(struct rds_connection *conn, int prefill, gfp_t gfp)
|
||||
|
||||
/* the goal here is to just make sure that someone, somewhere
|
||||
* is posting buffers. If we can't get the refill lock,
|
||||
* let them do their thing
|
||||
* let them do their thing. The holder may also be
|
||||
* rds_conn_shutdown() tearing the path down, in which case
|
||||
* there is nothing to post.
|
||||
*/
|
||||
if (!acquire_refill(conn))
|
||||
return;
|
||||
|
||||
@@ -114,8 +114,13 @@ static void release_in_xmit(struct rds_conn_path *cp)
|
||||
* hot path and finding waiters is very rare. We don't want to walk
|
||||
* the system-wide hashed waitqueue buckets in the fast path only to
|
||||
* almost never find waiters.
|
||||
*
|
||||
* wq_has_sleeper() supplies the full barrier that orders the wait
|
||||
* queue read after the bit clear; clear_bit_unlock() alone is only
|
||||
* a release and would let this check read a stale empty queue,
|
||||
* losing the wake-up.
|
||||
*/
|
||||
if (waitqueue_active(&cp->cp_waitq))
|
||||
if (wq_has_sleeper(&cp->cp_waitq))
|
||||
wake_up_all(&cp->cp_waitq);
|
||||
}
|
||||
|
||||
@@ -239,8 +244,11 @@ int rds_send_xmit(struct rds_conn_path *cp)
|
||||
WRITE_ONCE(cp->cp_send_gen, send_gen);
|
||||
|
||||
/*
|
||||
* rds_conn_shutdown() sets the conn state and then tests RDS_IN_XMIT,
|
||||
* we do the opposite to avoid races.
|
||||
* rds_conn_shutdown() sets the conn state and then acquires
|
||||
* RDS_IN_XMIT; we take the lock first and then check the state.
|
||||
* Ownership is decided by the atomic RMW on the cp_flags word:
|
||||
* if the teardown won the bit we back off here, and if we won
|
||||
* it the teardown waits until we release it.
|
||||
*/
|
||||
if (!rds_conn_path_up(cp)) {
|
||||
release_in_xmit(cp);
|
||||
|
||||
101
net/rds/tcp.c
101
net/rds/tcp.c
@@ -115,46 +115,90 @@ void rds_tcp_restore_callbacks(struct socket *sock,
|
||||
}
|
||||
|
||||
/*
|
||||
* rds_tcp_reset_callbacks() switches the to the new sock and
|
||||
* returns the existing tc->t_sock.
|
||||
* rds_tcp_reset_callbacks() switches a path to a new socket and
|
||||
* releases the old one it finds in tc->t_sock, resolving a duelling
|
||||
* SYN.
|
||||
*
|
||||
* The only functions that set tc->t_sock are rds_tcp_set_callbacks
|
||||
* and rds_tcp_reset_callbacks. Send and receive trust that
|
||||
* it is set. The absence of RDS_CONN_UP bit protects those paths
|
||||
* from being called while it isn't set.
|
||||
* tc->t_sock is set by rds_tcp_set_callbacks() and cleared by
|
||||
* rds_tcp_restore_callbacks(). Four paths write it: the active
|
||||
* connect in rds_tcp_conn_path_connect(), which sets it and clears it
|
||||
* again on failure; the accept path in rds_tcp_accept_one(), which
|
||||
* sets it for a path with no socket yet; the teardown in
|
||||
* rds_tcp_conn_path_shutdown(), which clears it; and the swap done
|
||||
* here, which does both. The connect and accept paths are serialized
|
||||
* against each other by t_conn_path_lock. Send and receive trust
|
||||
* that it is set: the absence of RDS_CONN_UP protects those paths
|
||||
* from being called while it isn't, and the swap done here runs under
|
||||
* RDS_IN_XMIT so that it cannot interleave with a sender already
|
||||
* inside rds_send_xmit().
|
||||
*/
|
||||
void rds_tcp_reset_callbacks(struct socket *sock,
|
||||
struct rds_conn_path *cp)
|
||||
{
|
||||
struct rds_tcp_connection *tc = cp->cp_transport_data;
|
||||
struct socket *osock = tc->t_sock;
|
||||
|
||||
if (!osock)
|
||||
goto newsock;
|
||||
struct socket *osock;
|
||||
|
||||
/* Need to resolve a duelling SYN between peers.
|
||||
* We have an outstanding SYN to this peer, which may
|
||||
* potentially have transitioned to the RDS_CONN_UP state,
|
||||
* so we must quiesce any send threads before resetting
|
||||
* cp_transport_data. We quiesce these threads by setting
|
||||
* cp_state to something other than RDS_CONN_UP, and then
|
||||
* waiting for any existing threads in rds_send_xmit to
|
||||
* complete release_in_xmit(). (Subsequent threads entering
|
||||
* rds_send_xmit() will bail on !rds_conn_up().
|
||||
* cp_transport_data. Setting cp_state to something other
|
||||
* than RDS_CONN_UP stops new senders, and owning RDS_IN_XMIT
|
||||
* excludes any thread already inside rds_send_xmit() - or a
|
||||
* teardown in rds_conn_shutdown(), which holds the same lock
|
||||
* for the duration of the transport shutdown - for the whole
|
||||
* socket swap and the rds_send_path_reset() below.
|
||||
*
|
||||
* However an incoming syn-ack at this point would end up
|
||||
* marking the conn as RDS_CONN_UP, and would again permit
|
||||
* rds_send_xmi() threads through, so ideally we would
|
||||
* synchronize on RDS_CONN_UP after lock_sock(), but cannot
|
||||
* do that: waiting on !RDS_IN_XMIT after lock_sock() may
|
||||
* end up deadlocking with tcp_sendmsg(), and the RDS_IN_XMIT
|
||||
* would not get set. As a result, we set c_state to
|
||||
* RDS_CONN_RESETTTING, to ensure that rds_tcp_state_change
|
||||
* cannot mark rds_conn_path_up() in the window before lock_sock()
|
||||
* An incoming syn-ack at this point would end up marking the
|
||||
* conn as RDS_CONN_UP, and would again permit rds_send_xmit()
|
||||
* threads through, so ideally we would synchronize on
|
||||
* RDS_CONN_UP after lock_sock(), but cannot do that: acquiring
|
||||
* RDS_IN_XMIT after lock_sock() may end up deadlocking with
|
||||
* tcp_sendmsg(), which takes the socket lock while holding
|
||||
* RDS_IN_XMIT. As a result, we set c_state to
|
||||
* RDS_CONN_RESETTING, to ensure that rds_tcp_state_change
|
||||
* cannot mark rds_conn_path_up() in the window before
|
||||
* lock_sock().
|
||||
*
|
||||
* Only make that transition if the path is still connecting
|
||||
* (or already resetting from an earlier duel). A path in any
|
||||
* other state - typically RDS_CONN_DISCONNECTING or
|
||||
* RDS_CONN_ERROR with a shutdown in flight - is dropped
|
||||
* instead. That still replaces its state, with RDS_CONN_ERROR,
|
||||
* and, unless a pending destroy is about to reap the whole
|
||||
* connection anyway, queues one more shutdown pass. A shutdown
|
||||
* already in flight leaves that RDS_CONN_ERROR alone when it
|
||||
* finishes; the queued pass then completes the transition to
|
||||
* RDS_CONN_DOWN and tears down anything that attached to the
|
||||
* path in the meantime.
|
||||
*/
|
||||
if (!rds_conn_path_transition(cp, RDS_CONN_CONNECTING,
|
||||
RDS_CONN_RESETTING) &&
|
||||
!rds_conn_path_transition(cp, RDS_CONN_RESETTING,
|
||||
RDS_CONN_RESETTING))
|
||||
rds_conn_path_drop(cp, 0);
|
||||
wait_event(cp->cp_waitq,
|
||||
!test_and_set_bit_lock(RDS_IN_XMIT, &cp->cp_flags));
|
||||
|
||||
/* Read t_sock only while owning RDS_IN_XMIT, never before the
|
||||
* wait: the teardown in rds_conn_shutdown() releases the old
|
||||
* socket and clears t_sock, so a pointer sampled earlier can
|
||||
* be stale by the time we wake up. The teardown holds the
|
||||
* same lock while it does so, so what we read here cannot
|
||||
* change under us until we release it.
|
||||
*/
|
||||
osock = tc->t_sock;
|
||||
if (!osock)
|
||||
goto newsock;
|
||||
|
||||
/* reset receive side state for rds_tcp_data_recv() for osock.
|
||||
*
|
||||
* The sync cancels while owning RDS_IN_XMIT rely on cp_wq
|
||||
* being ordered: a teardown blocked on the bit occupies
|
||||
* cp_wq's only execution slot, so cp_send_w and cp_recv_w are
|
||||
* pending at most and the cancels never flush. Nothing here
|
||||
* may flush or wait on cp_wq itself.
|
||||
*/
|
||||
atomic_set(&cp->cp_state, RDS_CONN_RESETTING);
|
||||
wait_event(cp->cp_waitq, !test_bit(RDS_IN_XMIT, &cp->cp_flags));
|
||||
/* reset receive side state for rds_tcp_data_recv() for osock */
|
||||
cancel_delayed_work_sync(&cp->cp_send_w);
|
||||
cancel_delayed_work_sync(&cp->cp_recv_w);
|
||||
lock_sock(osock->sk);
|
||||
@@ -172,6 +216,9 @@ void rds_tcp_reset_callbacks(struct socket *sock,
|
||||
lock_sock(sock->sk);
|
||||
rds_tcp_set_callbacks(sock, cp);
|
||||
release_sock(sock->sk);
|
||||
|
||||
clear_bit_unlock(RDS_IN_XMIT, &cp->cp_flags);
|
||||
wake_up_all(&cp->cp_waitq);
|
||||
}
|
||||
|
||||
/* Add tc to rds_tcp_tc_list and set tc->t_sock. See comments
|
||||
|
||||
@@ -295,7 +295,11 @@ int rds_tcp_accept_one(struct rds_tcp_net *rtn)
|
||||
if (rs_tcp->t_sock) {
|
||||
/* Duelling SYN has been handled in rds_tcp_accept_one() */
|
||||
rds_tcp_reset_callbacks(new_sock, cp);
|
||||
/* rds_connect_path_complete() marks RDS_CONN_UP */
|
||||
/* rds_connect_path_complete() marks RDS_CONN_UP, or,
|
||||
* if a concurrent shutdown won the duel, drops the
|
||||
* path again and the pass that drop queues reaps the
|
||||
* socket installed above.
|
||||
*/
|
||||
rds_connect_path_complete(cp, RDS_CONN_RESETTING);
|
||||
} else {
|
||||
rds_tcp_set_callbacks(new_sock, cp);
|
||||
|
||||
@@ -443,12 +443,21 @@ static size_t tcf_action_shared_attrs_size(const struct tc_action *act)
|
||||
+ nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */
|
||||
+ cookie_len /* TCA_ACT_COOKIE */
|
||||
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_HW_STATS */
|
||||
/* TCA_ACT_USED_HW_STATS */
|
||||
+ nla_total_size(sizeof(struct nla_bitfield32))
|
||||
+ nla_total_size(sizeof(u32)) /* TCA_ACT_IN_HW_COUNT */
|
||||
+ nla_total_size(0) /* TCA_ACT_STATS nested */
|
||||
+ nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_FLAGS */
|
||||
/* TCA_STATS_BASIC */
|
||||
+ nla_total_size_64bit(sizeof(struct gnet_stats_basic))
|
||||
/* TCA_STATS_PKT64 */
|
||||
+ nla_total_size_64bit(sizeof(u64))
|
||||
/* TCA_STATS_BASIC_HW */
|
||||
+ nla_total_size_64bit(sizeof(struct gnet_stats_basic))
|
||||
/* TCA_STATS_PKT64, emitted by both of the basic copies above */
|
||||
+ 2 * nla_total_size_64bit(sizeof(u64))
|
||||
/* TCA_STATS_RATE_EST */
|
||||
+ nla_total_size_64bit(sizeof(struct gnet_stats_rate_est))
|
||||
/* TCA_STATS_RATE_EST64 */
|
||||
+ nla_total_size_64bit(sizeof(struct gnet_stats_rate_est64))
|
||||
/* TCA_STATS_QUEUE */
|
||||
+ nla_total_size_64bit(sizeof(struct gnet_stats_queue))
|
||||
+ nla_total_size(0) /* TCA_ACT_OPTIONS nested */
|
||||
@@ -1688,12 +1697,12 @@ static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[],
|
||||
|
||||
static int
|
||||
tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
|
||||
struct tc_action *actions[], int event,
|
||||
struct tc_action *actions[], size_t attr_size, int event,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
|
||||
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOBUFS;
|
||||
if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
|
||||
@@ -1858,11 +1867,13 @@ static int tcf_action_delete(struct net *net, struct tc_action *actions[])
|
||||
static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
|
||||
struct tc_action *action)
|
||||
{
|
||||
size_t attr_size = tcf_action_fill_size(action);
|
||||
struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
|
||||
[0] = action,
|
||||
};
|
||||
struct sk_buff *skb;
|
||||
size_t attr_size;
|
||||
|
||||
attr_size = tcf_action_full_attrs_size(tcf_action_fill_size(action));
|
||||
|
||||
skb = alloc_skb(max(attr_size, NLMSG_GOODSIZE), GFP_KERNEL);
|
||||
if (!skb)
|
||||
@@ -1879,15 +1890,18 @@ static struct sk_buff *tcf_reoffload_del_notify_msg(struct net *net,
|
||||
static int tcf_reoffload_del_notify(struct net *net, struct tc_action *action)
|
||||
{
|
||||
const struct tc_action_ops *ops = action->ops;
|
||||
struct sk_buff *skb;
|
||||
struct sk_buff *skb = NULL;
|
||||
int ret;
|
||||
|
||||
if (!rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
|
||||
skb = NULL;
|
||||
} else {
|
||||
if (rtnl_notify_needed(net, 0, RTNLGRP_TC)) {
|
||||
skb = tcf_reoffload_del_notify_msg(net, action);
|
||||
/* The action has already lost its hardware instance and is
|
||||
* skip_sw, so it must be released whether or not the
|
||||
* notification can be built. Drop the notification rather
|
||||
* than leave an action behind that processes no packets.
|
||||
*/
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
skb = NULL;
|
||||
}
|
||||
|
||||
ret = tcf_idr_release_unsafe(action);
|
||||
@@ -2044,7 +2058,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
|
||||
attr_size = tcf_action_full_attrs_size(attr_size);
|
||||
|
||||
if (event == RTM_GETACTION)
|
||||
ret = tcf_get_notify(net, portid, n, actions, event, extack);
|
||||
ret = tcf_get_notify(net, portid, n, actions, attr_size, event,
|
||||
extack);
|
||||
else { /* delete */
|
||||
ret = tcf_del_notify(net, n, actions, portid, attr_size, extack);
|
||||
if (ret)
|
||||
|
||||
@@ -1703,6 +1703,11 @@ static int fl_set_enc_opt(struct nlattr **tb, struct fl_flow_key *key,
|
||||
return -EINVAL;
|
||||
}
|
||||
nla_opt_msk = nla_next(nla_opt_msk, &msk_depth);
|
||||
|
||||
if (msk_depth && !nla_ok(nla_opt_msk, msk_depth)) {
|
||||
NL_SET_ERR_MSG(extack, "A mask attribute is invalid");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -370,6 +370,10 @@ static int u32_init(struct tcf_proto *tp)
|
||||
|
||||
refcount_set(&root_ht->refcnt, 1);
|
||||
root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : id2handle(0);
|
||||
if (root_ht->handle == 0) {
|
||||
kfree(root_ht);
|
||||
return -ENOMEM;
|
||||
}
|
||||
root_ht->prio = tp->prio;
|
||||
root_ht->is_root = true;
|
||||
idr_init(&root_ht->handle_idr);
|
||||
@@ -695,21 +699,33 @@ static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid)
|
||||
static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid, int *err)
|
||||
{
|
||||
u32 index = htid | 0x800;
|
||||
u32 max = htid | 0xFFF;
|
||||
|
||||
*err = 0;
|
||||
|
||||
if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) {
|
||||
index = htid + 1;
|
||||
if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
|
||||
GFP_KERNEL))
|
||||
index = max;
|
||||
*err = idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
|
||||
GFP_KERNEL);
|
||||
if (*err)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
static int u32_kid_extack(int err, struct netlink_ext_ack *extack)
|
||||
{
|
||||
if (err == -ENOSPC)
|
||||
NL_SET_ERR_MSG_MOD(extack, "Hash table node ID pool exhausted");
|
||||
else
|
||||
NL_SET_ERR_MSG_MOD(extack, "Failed to allocate node ID");
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
|
||||
[TCA_U32_CLASSID] = { .type = NLA_U32 },
|
||||
[TCA_U32_HASH] = { .type = NLA_U32 },
|
||||
@@ -1079,7 +1095,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
|
||||
* handle which is used to uniquely identify the match entry.
|
||||
*/
|
||||
if (!TC_U32_NODE(handle)) {
|
||||
handle = gen_new_kid(ht, htid);
|
||||
handle = gen_new_kid(ht, htid, &err);
|
||||
if (err)
|
||||
return u32_kid_extack(err, extack);
|
||||
} else {
|
||||
handle = htid | TC_U32_NODE(handle);
|
||||
err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
|
||||
@@ -1091,7 +1109,9 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
|
||||
/* The user did not give us a handle; lets just generate one
|
||||
* from the table's pool of nodeids.
|
||||
*/
|
||||
handle = gen_new_kid(ht, htid);
|
||||
handle = gen_new_kid(ht, htid, &err);
|
||||
if (err)
|
||||
return u32_kid_extack(err, extack);
|
||||
}
|
||||
|
||||
if (tb[TCA_U32_SEL] == NULL) {
|
||||
|
||||
@@ -212,8 +212,10 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
|
||||
chunk->chunk_end = ((__u8 *)ch) + SCTP_PAD4(ntohs(ch->length));
|
||||
skb_pull(chunk->skb, sizeof(*ch));
|
||||
chunk->subh.v = NULL; /* Subheader is no longer valid. */
|
||||
|
||||
if (chunk->chunk_end + sizeof(*ch) <= skb_tail_pointer(chunk->skb)) {
|
||||
if (unlikely(ntohs(ch->length) < sizeof(*ch))) {
|
||||
chunk->pdiscard = 1;
|
||||
} else if (chunk->chunk_end + sizeof(*ch) <=
|
||||
skb_tail_pointer(chunk->skb)) {
|
||||
/* This is not a singleton */
|
||||
chunk->singleton = 0;
|
||||
} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
|
||||
|
||||
@@ -3215,6 +3215,9 @@ bool sctp_verify_asconf(const struct sctp_association *asoc,
|
||||
*errp = param.p;
|
||||
switch (param.p->type) {
|
||||
case SCTP_PARAM_ERR_CAUSE:
|
||||
if (length < sizeof(struct sctp_addip_param) +
|
||||
sizeof(struct sctp_errhdr))
|
||||
return false;
|
||||
break;
|
||||
case SCTP_PARAM_IPV4_ADDRESS:
|
||||
if (length != sizeof(struct sctp_ipv4addr_param))
|
||||
@@ -3448,20 +3451,15 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack,
|
||||
case SCTP_PARAM_ERR_CAUSE:
|
||||
length = sizeof(*asconf_ack_param);
|
||||
err_param = (void *)asconf_ack_param + length;
|
||||
asconf_ack_len -= length;
|
||||
if (asconf_ack_len > 0)
|
||||
return err_param->cause;
|
||||
else
|
||||
return SCTP_ERROR_INV_PARAM;
|
||||
break;
|
||||
return err_param->cause;
|
||||
default:
|
||||
return SCTP_ERROR_INV_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
length = ntohs(asconf_ack_param->param_hdr.length);
|
||||
asconf_ack_param = (void *)asconf_ack_param + length;
|
||||
asconf_ack_len -= length;
|
||||
asconf_ack_param = (void *)asconf_ack_param + SCTP_PAD4(length);
|
||||
asconf_ack_len -= SCTP_PAD4(length);
|
||||
}
|
||||
|
||||
return err_code;
|
||||
|
||||
@@ -1545,17 +1545,8 @@ static int sctp_cmd_interpreter(enum sctp_event_type event_type,
|
||||
timeout = asoc->timeouts[cmd->obj.to];
|
||||
BUG_ON(!timeout);
|
||||
|
||||
/*
|
||||
* SCTP has a hard time with timer starts. Because we process
|
||||
* timer starts as side effects, it can be hard to tell if we
|
||||
* have already started a timer or not, which leads to BUG
|
||||
* halts when we call add_timer. So here, instead of just starting
|
||||
* a timer, if the timer is already started, and just mod
|
||||
* the timer with the shorter of the two expiration times
|
||||
*/
|
||||
if (!timer_pending(timer))
|
||||
if (!timer_reduce(timer, jiffies + timeout))
|
||||
sctp_association_hold(asoc);
|
||||
timer_reduce(timer, jiffies + timeout);
|
||||
break;
|
||||
|
||||
case SCTP_CMD_TIMER_RESTART:
|
||||
|
||||
@@ -504,7 +504,7 @@ bool tipc_link_create(struct net *net, char *if_name, int bearer_id,
|
||||
snprintf(l->name, sizeof(l->name), "%s:%s-%s:unknown",
|
||||
self_str, if_name, peer_str);
|
||||
|
||||
strcpy(l->if_name, if_name);
|
||||
strscpy(l->if_name, if_name);
|
||||
l->addr = peer;
|
||||
l->peer_caps = peer_caps;
|
||||
l->net = net;
|
||||
@@ -574,7 +574,7 @@ bool tipc_link_bc_create(struct net *net, u32 ownnode, u32 peer, u8 *peer_id,
|
||||
snprintf(l->name, sizeof(l->name), "%s:%s", tipc_bclink_name,
|
||||
peer_str);
|
||||
} else {
|
||||
strcpy(l->name, tipc_bclink_name);
|
||||
strscpy(l->name, tipc_bclink_name);
|
||||
}
|
||||
trace_tipc_link_reset(l, TIPC_DUMP_ALL, "bclink created!");
|
||||
tipc_link_reset(l);
|
||||
@@ -1898,7 +1898,7 @@ static void tipc_link_build_proto_msg(struct tipc_link *l, int mtyp, bool probe,
|
||||
msg_set_dest_session(hdr, l->peer_session);
|
||||
}
|
||||
msg_set_max_pkt(hdr, l->advertised_mtu);
|
||||
strcpy(data, l->if_name);
|
||||
memcpy(data, l->if_name, TIPC_MAX_IF_NAME);
|
||||
msg_set_size(hdr, INT_H_SIZE + TIPC_MAX_IF_NAME);
|
||||
skb_trim(skb, INT_H_SIZE + TIPC_MAX_IF_NAME);
|
||||
}
|
||||
|
||||
@@ -763,21 +763,40 @@ struct publication *tipc_nametbl_publish(struct net *net, struct tipc_uaddr *ua,
|
||||
struct tipc_socket_addr *sk, u32 key)
|
||||
{
|
||||
struct name_table *nt = tipc_name_table(net);
|
||||
u32 max_user_pub = TIPC_MAX_PUBL - 1;
|
||||
struct tipc_net *tn = tipc_net(net);
|
||||
struct publication *p = NULL;
|
||||
struct sk_buff *skb = NULL;
|
||||
bool protocol_type = false;
|
||||
u32 rc_dests;
|
||||
|
||||
spin_lock_bh(&tn->nametbl_lock);
|
||||
if (ua->sr.type == TIPC_NODE_STATE || ua->sr.type == TIPC_LINK_STATE ||
|
||||
ua->sr.type == TIPC_TOP_SRV)
|
||||
protocol_type = true;
|
||||
|
||||
if (nt->local_publ_count >= TIPC_MAX_PUBL) {
|
||||
pr_warn("Bind failed, max limit %u reached\n", TIPC_MAX_PUBL);
|
||||
spin_lock_bh(&tn->nametbl_lock);
|
||||
if (protocol_type)
|
||||
goto insert;
|
||||
|
||||
/* Reserve one entry for node state service type because it has cluster
|
||||
* scope and it is distributed in bulk. So, the maximum number of user's
|
||||
* publications is (TIPC_MAX_PUBL - 1).
|
||||
*/
|
||||
if (nt->local_publ_count >= max_user_pub) {
|
||||
pr_warn("Bind failed, max limit %u reached\n", max_user_pub);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
insert:
|
||||
p = tipc_nametbl_insert_publ(net, ua, sk, key);
|
||||
if (p) {
|
||||
nt->local_publ_count++;
|
||||
/* Not count node state, link state and topology server types
|
||||
* so that maximum nt->local_publ_count does not prevent
|
||||
* protocol service types from being inserted into the name
|
||||
* table.
|
||||
*/
|
||||
if (!protocol_type)
|
||||
nt->local_publ_count++;
|
||||
skb = tipc_named_publish(net, p);
|
||||
}
|
||||
rc_dests = nt->rc_dests;
|
||||
@@ -810,7 +829,10 @@ void tipc_nametbl_withdraw(struct net *net, struct tipc_uaddr *ua,
|
||||
|
||||
p = tipc_nametbl_remove_publ(net, ua, sk, key);
|
||||
if (p) {
|
||||
nt->local_publ_count--;
|
||||
if (p->sr.type != TIPC_NODE_STATE &&
|
||||
p->sr.type != TIPC_LINK_STATE &&
|
||||
p->sr.type != TIPC_TOP_SRV)
|
||||
nt->local_publ_count--;
|
||||
skb = tipc_named_withdraw(net, p);
|
||||
list_del_init(&p->binding_sock);
|
||||
kfree_rcu(p, rcu);
|
||||
|
||||
@@ -1333,7 +1333,9 @@ static void tipc_node_reset_links(struct tipc_node *n)
|
||||
|
||||
pr_warn("Resetting all links to %x\n", n->addr);
|
||||
|
||||
tipc_node_write_lock(n);
|
||||
trace_tipc_node_reset_links(n, true, " ");
|
||||
tipc_node_write_unlock_fast(n);
|
||||
for (i = 0; i < MAX_BEARERS; i++) {
|
||||
tipc_node_link_down(n, i, false);
|
||||
}
|
||||
|
||||
@@ -438,6 +438,38 @@ struct sock *vsock_find_connected_socket(struct sockaddr_vm *src,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vsock_find_connected_socket);
|
||||
|
||||
/**
|
||||
* vsock_check_source - validate a packet source against a socket peer
|
||||
* @vsk: socket receiving the packet
|
||||
* @transport: transport receiving the packet
|
||||
* @src: source address from the packet
|
||||
*
|
||||
* Return: true if the packet arrived on the socket's assigned transport and
|
||||
* its source matches the stored peer. Loopback packets are generated
|
||||
* internally and always use the local CID as their source, including
|
||||
* connections using a valid CID alias.
|
||||
*
|
||||
* The caller must hold the socket lock and must not call this for listening
|
||||
* sockets, which accept packets from any source and have no assigned
|
||||
* transport.
|
||||
*/
|
||||
bool vsock_check_source(const struct vsock_sock *vsk,
|
||||
const struct vsock_transport *transport,
|
||||
const struct sockaddr_vm *src)
|
||||
{
|
||||
if (vsk->transport != transport)
|
||||
return false;
|
||||
|
||||
if (src->svm_port != vsk->remote_addr.svm_port)
|
||||
return false;
|
||||
|
||||
if (src->svm_cid == vsk->remote_addr.svm_cid)
|
||||
return true;
|
||||
|
||||
return transport->get_local_cid() == VMADDR_CID_LOCAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vsock_check_source);
|
||||
|
||||
void vsock_remove_sock(struct vsock_sock *vsk)
|
||||
{
|
||||
/* Transport reassignment must not remove the binding. */
|
||||
|
||||
@@ -1836,7 +1836,8 @@ void virtio_transport_recv_pkt(struct virtio_transport *t,
|
||||
* lock_sock (note: listener sockets are not assigned to any transport)
|
||||
*/
|
||||
if (sock_flag(sk, SOCK_DONE) ||
|
||||
(sk->sk_state != TCP_LISTEN && vsk->transport != &t->transport)) {
|
||||
(sk->sk_state != TCP_LISTEN &&
|
||||
!vsock_check_source(vsk, &t->transport, &src))) {
|
||||
(void)virtio_transport_reset_no_sock(t, skb, net);
|
||||
release_sock(sk);
|
||||
sock_put(sk);
|
||||
|
||||
@@ -680,11 +680,13 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
|
||||
struct vmci_transport_packet *pkt;
|
||||
struct vsock_sock *vsk;
|
||||
bool bh_process_pkt;
|
||||
bool drop_pkt;
|
||||
int err;
|
||||
|
||||
sk = NULL;
|
||||
err = VMCI_SUCCESS;
|
||||
bh_process_pkt = false;
|
||||
drop_pkt = false;
|
||||
|
||||
/* Ignore incoming packets from resources that aren't vsock
|
||||
* implementations.
|
||||
@@ -765,17 +767,29 @@ static int vmci_transport_recv_stream_cb(void *data, struct vmci_datagram *dg)
|
||||
bh_lock_sock(sk);
|
||||
|
||||
if (!sock_owned_by_user(sk)) {
|
||||
/* The local context ID may be out of date, update it. */
|
||||
vsk->local_addr.svm_cid = dst.svm_cid;
|
||||
if (sk->sk_state != TCP_LISTEN &&
|
||||
!vsock_check_source(vsk, &vmci_transport, &src)) {
|
||||
drop_pkt = true;
|
||||
err = VMCI_ERROR_NO_ACCESS;
|
||||
} else {
|
||||
/* The local context ID may be out of date, update it. */
|
||||
vsk->local_addr.svm_cid = dst.svm_cid;
|
||||
|
||||
if (sk->sk_state == TCP_ESTABLISHED)
|
||||
vmci_trans(vsk)->notify_ops->handle_notify_pkt(
|
||||
sk, pkt, true, &dst, &src,
|
||||
&bh_process_pkt);
|
||||
if (sk->sk_state == TCP_ESTABLISHED)
|
||||
vmci_trans(vsk)->notify_ops->handle_notify_pkt(sk, pkt, true,
|
||||
&dst, &src,
|
||||
&bh_process_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
bh_unlock_sock(sk);
|
||||
|
||||
if (drop_pkt) {
|
||||
if (vmci_transport_send_reset_bh(&dst, &src, pkt) < 0)
|
||||
pr_err("unable to send reset\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!bh_process_pkt) {
|
||||
struct vmci_transport_recv_pkt_info *recv_pkt_info;
|
||||
|
||||
@@ -900,6 +914,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
|
||||
{
|
||||
struct vmci_transport_recv_pkt_info *recv_pkt_info;
|
||||
struct vmci_transport_packet *pkt;
|
||||
struct sockaddr_vm src;
|
||||
struct sock *sk;
|
||||
|
||||
recv_pkt_info =
|
||||
@@ -908,6 +923,12 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
|
||||
pkt = &recv_pkt_info->pkt;
|
||||
|
||||
lock_sock(sk);
|
||||
vsock_addr_init(&src, pkt->dg.src.context, pkt->src_port);
|
||||
if (sk->sk_state != TCP_LISTEN &&
|
||||
!vsock_check_source(vsock_sk(sk), &vmci_transport, &src)) {
|
||||
vmci_transport_reply_reset(pkt);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* The local context ID may be out of date. */
|
||||
vsock_sk(sk)->local_addr.svm_cid = pkt->dg.dst.context;
|
||||
@@ -937,6 +958,7 @@ static void vmci_transport_recv_pkt_work(struct work_struct *work)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
release_sock(sk);
|
||||
kfree(recv_pkt_info);
|
||||
/* Release reference obtained in the stream callback when we fetched
|
||||
|
||||
@@ -28,6 +28,7 @@ TEST_PROGS := \
|
||||
double_udp_encap.sh \
|
||||
drop_monitor_tests.sh \
|
||||
ecmp_rehash.sh \
|
||||
exception_cache.sh \
|
||||
fcnal-ipv4.sh \
|
||||
fcnal-ipv6.sh \
|
||||
fcnal-other.sh \
|
||||
|
||||
521
tools/testing/selftests/net/exception_cache.sh
Executable file
521
tools/testing/selftests/net/exception_cache.sh
Executable file
@@ -0,0 +1,521 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Test that the state of the route exception cache after an ICMP error is
|
||||
# processed does not depend on whether the quoted packet was matched to a
|
||||
# socket. Otherwise, an off-path attacker can probe the cache to discover the
|
||||
# ephemeral port used by a connected UDP socket.
|
||||
#
|
||||
# When the quoted packet is not matched to a socket, the same exception is
|
||||
# created as when it is matched, so that neither its presence nor its contents
|
||||
# reveal the result of socket matching.
|
||||
#
|
||||
# +----+
|
||||
# +---------| r1 |
|
||||
# | +----+
|
||||
# +----+ +--------+ | .1
|
||||
# | h1 |---| bridge | | 198.51.100.0/30
|
||||
# +----+ +--------+ | 2001:db8:2::/64
|
||||
# .1 | | .2
|
||||
# | +----+ +----+
|
||||
# +---------| r2 |-----------| h2 |
|
||||
# .2 .3 +----+ .1 .2 +----+
|
||||
# 203.0.113.0/24
|
||||
# 2001:db8:3::/64
|
||||
# 192.0.2.0/24
|
||||
# 2001:db8:1::/64
|
||||
#
|
||||
# Traffic from h1 to h2 is routed via r1, which reaches h2's network via r2
|
||||
# over the point-to-point link. The MTU of the r2 - h2 link is lowered so that
|
||||
# r2 emits ICMP errors towards h1.
|
||||
#
|
||||
# For the redirect tests r1's route to h2's network is replaced with one via r2
|
||||
# on the shared segment, so that r1 forwards the packet back to the segment it
|
||||
# arrived from and emits a redirect towards h1.
|
||||
#
|
||||
# The packets that provoke the ICMP errors are injected with a packet socket so
|
||||
# that no socket is ever associated with them. A socket is created separately,
|
||||
# with socat, when a test needs the ICMP error to be matched.
|
||||
|
||||
# shellcheck disable=SC1091,SC2034,SC2154,SC2329
|
||||
source lib.sh
|
||||
|
||||
require_command jq
|
||||
require_command mausezahn
|
||||
require_command nstat
|
||||
require_command socat
|
||||
|
||||
ALL_TESTS="
|
||||
pmtu_no_socket_ipv4
|
||||
pmtu_no_socket_ipv6
|
||||
pmtu_socket_ipv4
|
||||
pmtu_socket_ipv6
|
||||
pmtu_omit_ipv4
|
||||
pmtu_omit_ipv6
|
||||
redirect_no_socket_ipv4
|
||||
redirect_no_socket_ipv6
|
||||
redirect_socket_ipv4
|
||||
redirect_socket_ipv6
|
||||
"
|
||||
|
||||
# Shared segment.
|
||||
H1_ADDR4=192.0.2.1
|
||||
R1_ADDR4=192.0.2.2
|
||||
R2_ADDR4=192.0.2.3
|
||||
H1_ADDR6=2001:db8:1::1
|
||||
R1_ADDR6=2001:db8:1::2
|
||||
R2_ADDR6=2001:db8:1::3
|
||||
|
||||
# r1 - r2 link.
|
||||
R2_R1_ADDR4=198.51.100.2
|
||||
R2_R1_ADDR6=2001:db8:2::2
|
||||
|
||||
# r2 - h2 link.
|
||||
H2_ADDR4=203.0.113.2
|
||||
H2_NET4=203.0.113.0/24
|
||||
H2_ADDR6=2001:db8:3::2
|
||||
H2_NET6=2001:db8:3::/64
|
||||
|
||||
SPORT=12345
|
||||
DPORT=54321
|
||||
|
||||
# The MTU of the shared segment and of the r1 - r2 link. Large enough for the
|
||||
# injected packets to reach r2 intact.
|
||||
SEGMENT_MTU=2000
|
||||
# Size of the injected packets. The PMTU tests need a size that exceeds every
|
||||
# MTU used for the r2 - h2 link, so that r2 responds with an ICMP error. The
|
||||
# redirect tests need a size that does not, otherwise r2 would respond with an
|
||||
# ICMP error in addition to the redirect emitted by r1.
|
||||
PMTU_PACKET_SIZE=1800
|
||||
REDIRECT_PACKET_SIZE=100
|
||||
|
||||
# The MTUs used for the r2 - h2 link. All of them must be at least
|
||||
# IPV6_MIN_MTU, otherwise IPv6 silently ignores the error instead of creating
|
||||
# an exception.
|
||||
MTU_MID=1400
|
||||
MTU_LOW=1300
|
||||
|
||||
# Values for the IP{,V6}_MTU_DISCOVER socket option.
|
||||
PMTUDISC_DONT=0
|
||||
PMTUDISC_OMIT=5
|
||||
|
||||
SOCAT_PID=
|
||||
|
||||
linklocal_get()
|
||||
{
|
||||
local ns=$1; shift
|
||||
local dev=$1; shift
|
||||
|
||||
ip -n "$ns" -j -6 addr show dev "$dev" | \
|
||||
jq -r '.[]["addr_info"][] | select(.scope == "link") | .local'
|
||||
}
|
||||
|
||||
linklocal_exists()
|
||||
{
|
||||
local ns=$1; shift
|
||||
local dev=$1; shift
|
||||
|
||||
[ -n "$(linklocal_get "$ns" "$dev")" ]
|
||||
}
|
||||
|
||||
family_vars_set()
|
||||
{
|
||||
local family=$1; shift
|
||||
|
||||
FAMILY=$family
|
||||
|
||||
if [ "$family" -eq 4 ]; then
|
||||
H1_ADDR=$H1_ADDR4
|
||||
H2_ADDR=$H2_ADDR4
|
||||
MZ_FAMILY_OPT=()
|
||||
# Without the Don't Fragment bit set r2 fragments the packet
|
||||
# instead of reporting the MTU of the next hop.
|
||||
MZ_IP_OPTS="df,"
|
||||
SOCAT_DST="UDP4-CONNECT:$H2_ADDR4:$DPORT"
|
||||
SOCAT_BIND="bind=$H1_ADDR4:$SPORT"
|
||||
SOCAT_PMTUDISC="ip-mtu-discover"
|
||||
else
|
||||
H1_ADDR=$H1_ADDR6
|
||||
H2_ADDR=$H2_ADDR6
|
||||
MZ_FAMILY_OPT=(-6)
|
||||
MZ_IP_OPTS=
|
||||
SOCAT_DST="UDP6-CONNECT:[$H2_ADDR6]:$DPORT"
|
||||
SOCAT_BIND="bind=[$H1_ADDR6]:$SPORT"
|
||||
SOCAT_PMTUDISC="ipv6-mtu-discover"
|
||||
fi
|
||||
}
|
||||
|
||||
topology_setup()
|
||||
{
|
||||
local ns
|
||||
|
||||
setup_ns h1 r1 r2 h2 sw
|
||||
defer cleanup_all_ns
|
||||
|
||||
# Link-local addresses are generated from the MAC address and read
|
||||
# back during setup, so request that generation mode explicitly and
|
||||
# make the addresses available as soon as the devices are brought up.
|
||||
for ns in "$h1" "$r1" "$r2" "$h2" "$sw"; do
|
||||
ip netns exec "$ns" sysctl -qw \
|
||||
net.ipv6.conf.default.addr_gen_mode=0 \
|
||||
net.ipv6.conf.default.accept_dad=0 \
|
||||
net.ipv6.conf.all.accept_dad=0
|
||||
done
|
||||
|
||||
ip -n "$sw" link add name br0 type bridge
|
||||
ip -n "$sw" link set dev br0 mtu "$SEGMENT_MTU" up
|
||||
|
||||
ip -n "$h1" link add name eth0 mtu "$SEGMENT_MTU" type veth \
|
||||
peer name swp1 mtu "$SEGMENT_MTU" netns "$sw"
|
||||
ip -n "$r1" link add name eth0 mtu "$SEGMENT_MTU" type veth \
|
||||
peer name swp2 mtu "$SEGMENT_MTU" netns "$sw"
|
||||
ip -n "$r2" link add name eth0 mtu "$SEGMENT_MTU" type veth \
|
||||
peer name swp3 mtu "$SEGMENT_MTU" netns "$sw"
|
||||
ip -n "$r1" link add name eth1 mtu "$SEGMENT_MTU" type veth \
|
||||
peer name eth1 mtu "$SEGMENT_MTU" netns "$r2"
|
||||
ip -n "$r2" link add name eth2 type veth peer name eth0 netns "$h2"
|
||||
|
||||
ip -n "$sw" link set dev swp1 master br0 up
|
||||
ip -n "$sw" link set dev swp2 master br0 up
|
||||
ip -n "$sw" link set dev swp3 master br0 up
|
||||
|
||||
ip -n "$h1" link set dev eth0 up
|
||||
ip -n "$r1" link set dev eth0 up
|
||||
ip -n "$r1" link set dev eth1 up
|
||||
ip -n "$r2" link set dev eth0 up
|
||||
ip -n "$r2" link set dev eth1 up
|
||||
ip -n "$r2" link set dev eth2 up
|
||||
ip -n "$h2" link set dev eth0 up
|
||||
|
||||
ip -n "$h1" address add "$H1_ADDR4/24" dev eth0
|
||||
ip -n "$r1" address add "$R1_ADDR4/24" dev eth0
|
||||
ip -n "$r2" address add "$R2_ADDR4/24" dev eth0
|
||||
ip -n "$r1" address add 198.51.100.1/30 dev eth1
|
||||
ip -n "$r2" address add "$R2_R1_ADDR4/30" dev eth1
|
||||
ip -n "$r2" address add 203.0.113.1/24 dev eth2
|
||||
ip -n "$h2" address add "$H2_ADDR4/24" dev eth0
|
||||
|
||||
ip -n "$h1" -6 address add "$H1_ADDR6/64" dev eth0 nodad
|
||||
ip -n "$r1" -6 address add "$R1_ADDR6/64" dev eth0 nodad
|
||||
ip -n "$r2" -6 address add "$R2_ADDR6/64" dev eth0 nodad
|
||||
ip -n "$r1" -6 address add 2001:db8:2::1/64 dev eth1 nodad
|
||||
ip -n "$r2" -6 address add "$R2_R1_ADDR6/64" dev eth1 nodad
|
||||
ip -n "$r2" -6 address add 2001:db8:3::1/64 dev eth2 nodad
|
||||
ip -n "$h2" -6 address add "$H2_ADDR6/64" dev eth0 nodad
|
||||
|
||||
ip netns exec "$r1" sysctl -qw net.ipv4.ip_forward=1
|
||||
ip netns exec "$r1" sysctl -qw net.ipv4.conf.all.send_redirects=1
|
||||
ip netns exec "$r1" sysctl -qw net.ipv6.conf.all.forwarding=1
|
||||
ip netns exec "$r2" sysctl -qw net.ipv4.ip_forward=1
|
||||
ip netns exec "$r2" sysctl -qw net.ipv6.conf.all.forwarding=1
|
||||
|
||||
ip netns exec "$h1" sysctl -qw net.ipv4.conf.all.accept_redirects=1
|
||||
ip netns exec "$h1" sysctl -qw net.ipv4.conf.eth0.accept_redirects=1
|
||||
ip netns exec "$h1" sysctl -qw net.ipv6.conf.all.accept_redirects=1
|
||||
ip netns exec "$h1" sysctl -qw net.ipv6.conf.eth0.accept_redirects=1
|
||||
|
||||
slowwait 5 linklocal_exists "$r1" eth0
|
||||
check_err $? "r1: link-local address was not generated"
|
||||
slowwait 5 linklocal_exists "$r2" eth0
|
||||
check_err $? "r2: link-local address was not generated"
|
||||
|
||||
R1_LLADDR=$(linklocal_get "$r1" eth0)
|
||||
R2_LLADDR=$(linklocal_get "$r2" eth0)
|
||||
R1_MAC=$(ip -n "$r1" -j link show dev eth0 | jq -r '.[]["address"]')
|
||||
R2_MAC=$(ip -n "$r2" -j link show dev eth0 | jq -r '.[]["address"]')
|
||||
|
||||
ip -n "$h1" route add "$H2_NET4" via "$R1_ADDR4" dev eth0
|
||||
ip -n "$h1" -6 route add "$H2_NET6" via "$R1_LLADDR" dev eth0
|
||||
ip -n "$r1" route add "$H2_NET4" via "$R2_R1_ADDR4" dev eth1
|
||||
ip -n "$r1" -6 route add "$H2_NET6" via "$R2_R1_ADDR6" dev eth1
|
||||
ip -n "$h2" route add default via 203.0.113.1 dev eth0
|
||||
ip -n "$h2" -6 route add default via 2001:db8:3::1 dev eth0
|
||||
|
||||
far_mtu_set "$MTU_MID"
|
||||
}
|
||||
|
||||
# Make r1 forward towards h2's network over the segment it receives the packet
|
||||
# from, so that it emits a redirect towards h1.
|
||||
redirect_route_set()
|
||||
{
|
||||
ip -n "$r1" route replace "$H2_NET4" via "$R2_ADDR4" dev eth0
|
||||
ip -n "$r1" -6 route replace "$H2_NET6" via "$R2_LLADDR" dev eth0
|
||||
|
||||
# __ip_do_redirect() only creates an exception if the new gateway is
|
||||
# already a valid neighbour. Otherwise it merely triggers address
|
||||
# resolution. IPv6 resolves the target itself, in rt6_do_redirect().
|
||||
ip -n "$h1" neigh replace "$R2_ADDR4" lladdr "$R2_MAC" dev eth0 \
|
||||
nud permanent
|
||||
}
|
||||
|
||||
far_mtu_set()
|
||||
{
|
||||
local mtu=$1; shift
|
||||
|
||||
ip -n "$r2" link set dev eth2 mtu "$mtu"
|
||||
ip -n "$h2" link set dev eth0 mtu "$mtu"
|
||||
}
|
||||
|
||||
socket_is_open()
|
||||
{
|
||||
ip netns exec "$h1" ss -uHn "sport = :$SPORT" | grep -q .
|
||||
}
|
||||
|
||||
socket_start()
|
||||
{
|
||||
# Disable PMTU discovery by default so that ICMP errors are not
|
||||
# reported to the socket. Otherwise socat would exit when the first one
|
||||
# arrives and later packets in the same test would not be matched to a
|
||||
# socket. The exception is still created, as ip{,6}_sk_accept_pmtu()
|
||||
# only rejects IP{,V6}_PMTUDISC_{INTERFACE,OMIT}.
|
||||
local pmtudisc=${1:-$PMTUDISC_DONT}
|
||||
|
||||
# Send socat's diagnostics to /dev/null. It reports the ICMP errors
|
||||
# that reach the socket, which is exactly what the tests provoke.
|
||||
ip netns exec "$h1" socat -u -lf/dev/null \
|
||||
"$SOCAT_DST,$SOCAT_BIND,$SOCAT_PMTUDISC=$pmtudisc" \
|
||||
OPEN:/dev/null,wronly=1 &
|
||||
SOCAT_PID=$!
|
||||
defer socket_stop
|
||||
|
||||
slowwait 5 socket_is_open
|
||||
check_err $? "socket did not open"
|
||||
}
|
||||
|
||||
socket_stop()
|
||||
{
|
||||
[ -z "$SOCAT_PID" ] && return 0
|
||||
|
||||
kill "$SOCAT_PID" &> /dev/null
|
||||
wait "$SOCAT_PID" 2> /dev/null
|
||||
SOCAT_PID=
|
||||
}
|
||||
|
||||
# Inject a packet towards h2 with a packet socket. No socket is associated with
|
||||
# it, so an ICMP error quoting it is matched to a socket only if one was
|
||||
# created separately with the same source port.
|
||||
packet_send()
|
||||
{
|
||||
local size=$1; shift
|
||||
|
||||
ip netns exec "$h1" mausezahn "${MZ_FAMILY_OPT[@]}" eth0 \
|
||||
-a own -b "$R1_MAC" -A "$H1_ADDR" -B "$H2_ADDR" \
|
||||
-t udp "${MZ_IP_OPTS}sp=$SPORT,dp=$DPORT" \
|
||||
-p "$size" -c 1 -q
|
||||
}
|
||||
|
||||
exception_show()
|
||||
{
|
||||
if [ "$FAMILY" -eq 4 ]; then
|
||||
# IPv4 exceptions without a bound route are not dumped, but
|
||||
# "route get" reports the exception and binds a route to it.
|
||||
ip -n "$h1" route get "$H2_ADDR"
|
||||
else
|
||||
# IPv6 does not report a cache indication in "route get"
|
||||
# output, so dump the exceptions instead.
|
||||
ip -n "$h1" -6 route show cache | grep -F "$H2_ADDR" || true
|
||||
fi
|
||||
}
|
||||
|
||||
exception_mtu_get()
|
||||
{
|
||||
exception_show | grep -o "mtu [0-9]*" | cut -d ' ' -f 2
|
||||
}
|
||||
|
||||
exception_gw_get()
|
||||
{
|
||||
exception_show | grep -o "via [0-9a-f.:]*" | cut -d ' ' -f 2
|
||||
}
|
||||
|
||||
exception_mtu_check()
|
||||
{
|
||||
local expected=$1; shift
|
||||
|
||||
[ "$(exception_mtu_get)" = "$expected" ]
|
||||
}
|
||||
|
||||
icmp_errors_get()
|
||||
{
|
||||
local ctr=IcmpInDestUnreachs
|
||||
|
||||
[ "$FAMILY" -eq 6 ] && ctr=Icmp6InPktTooBigs
|
||||
|
||||
ip netns exec "$h1" nstat -asz "$ctr" | \
|
||||
awk -v ctr="$ctr" '$1 == ctr { print $2 }'
|
||||
}
|
||||
|
||||
exception_pmtu_check()
|
||||
{
|
||||
local mtu=$1; shift
|
||||
local desc=$1; shift
|
||||
|
||||
busywait "$BUSYWAIT_TIMEOUT" exception_mtu_check "$mtu"
|
||||
check_err $? "$desc: exception does not carry an MTU of $mtu"
|
||||
}
|
||||
|
||||
pmtu_no_socket()
|
||||
{
|
||||
local family=$1; shift
|
||||
|
||||
RET=0
|
||||
family_vars_set "$family"
|
||||
topology_setup
|
||||
|
||||
packet_send "$PMTU_PACKET_SIZE"
|
||||
exception_pmtu_check "$MTU_MID" "No socket"
|
||||
|
||||
log_test "IPv$family: PMTU: exception without a matching socket"
|
||||
}
|
||||
|
||||
pmtu_no_socket_ipv4()
|
||||
{
|
||||
pmtu_no_socket 4
|
||||
}
|
||||
|
||||
pmtu_no_socket_ipv6()
|
||||
{
|
||||
pmtu_no_socket 6
|
||||
}
|
||||
|
||||
pmtu_socket()
|
||||
{
|
||||
local family=$1; shift
|
||||
local t0
|
||||
|
||||
RET=0
|
||||
family_vars_set "$family"
|
||||
topology_setup
|
||||
socket_start
|
||||
|
||||
packet_send "$PMTU_PACKET_SIZE"
|
||||
exception_pmtu_check "$MTU_MID" "Matching socket"
|
||||
|
||||
# A lower PMTU replaces the one currently stored in the exception.
|
||||
far_mtu_set "$MTU_LOW"
|
||||
packet_send "$PMTU_PACKET_SIZE"
|
||||
exception_pmtu_check "$MTU_LOW" "Lower PMTU"
|
||||
|
||||
# A higher PMTU is ignored, so the exception is left as it is. Wait
|
||||
# for the error to be received, as otherwise the check below would
|
||||
# pass even if it never was.
|
||||
far_mtu_set "$MTU_MID"
|
||||
t0=$(icmp_errors_get)
|
||||
packet_send "$PMTU_PACKET_SIZE"
|
||||
busywait "$BUSYWAIT_TIMEOUT" until_counter_is ">= $((t0 + 1))" \
|
||||
icmp_errors_get > /dev/null
|
||||
check_err $? "Higher PMTU: ICMP error was not received"
|
||||
|
||||
exception_mtu_check "$MTU_LOW"
|
||||
check_err $? "Higher PMTU: exception does not carry an MTU of $MTU_LOW"
|
||||
|
||||
log_test "IPv$family: PMTU: exception with a matching socket"
|
||||
}
|
||||
|
||||
pmtu_socket_ipv4()
|
||||
{
|
||||
pmtu_socket 4
|
||||
}
|
||||
|
||||
pmtu_socket_ipv6()
|
||||
{
|
||||
pmtu_socket 6
|
||||
}
|
||||
|
||||
pmtu_omit()
|
||||
{
|
||||
local family=$1; shift
|
||||
|
||||
RET=0
|
||||
family_vars_set "$family"
|
||||
topology_setup
|
||||
socket_start "$PMTUDISC_OMIT"
|
||||
|
||||
packet_send "$PMTU_PACKET_SIZE"
|
||||
exception_pmtu_check "$MTU_MID" "PMTU discovery disabled"
|
||||
|
||||
log_test "IPv$family: PMTU: exception with a socket ignoring it"
|
||||
}
|
||||
|
||||
pmtu_omit_ipv4()
|
||||
{
|
||||
pmtu_omit 4
|
||||
}
|
||||
|
||||
pmtu_omit_ipv6()
|
||||
{
|
||||
pmtu_omit 6
|
||||
}
|
||||
|
||||
exception_gw_check()
|
||||
{
|
||||
local expected=$1; shift
|
||||
|
||||
[ -n "$expected" ] && [ "$(exception_gw_get)" = "$expected" ]
|
||||
}
|
||||
|
||||
redirect_gw_new()
|
||||
{
|
||||
if [ "$FAMILY" -eq 4 ]; then
|
||||
echo "$R2_ADDR4"
|
||||
else
|
||||
echo "$R2_LLADDR"
|
||||
fi
|
||||
}
|
||||
|
||||
redirect_no_socket()
|
||||
{
|
||||
local family=$1; shift
|
||||
|
||||
RET=0
|
||||
family_vars_set "$family"
|
||||
topology_setup
|
||||
redirect_route_set
|
||||
|
||||
packet_send "$REDIRECT_PACKET_SIZE"
|
||||
busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)"
|
||||
check_err $? "No socket: exception does not carry the new gateway"
|
||||
|
||||
log_test "IPv$family: Redirect: exception without a matching socket"
|
||||
}
|
||||
|
||||
redirect_no_socket_ipv4()
|
||||
{
|
||||
redirect_no_socket 4
|
||||
}
|
||||
|
||||
redirect_no_socket_ipv6()
|
||||
{
|
||||
redirect_no_socket 6
|
||||
}
|
||||
|
||||
redirect_socket()
|
||||
{
|
||||
local family=$1; shift
|
||||
|
||||
RET=0
|
||||
family_vars_set "$family"
|
||||
topology_setup
|
||||
redirect_route_set
|
||||
socket_start
|
||||
|
||||
packet_send "$REDIRECT_PACKET_SIZE"
|
||||
busywait "$BUSYWAIT_TIMEOUT" exception_gw_check "$(redirect_gw_new)"
|
||||
check_err $? "Matching socket: exception does not carry the new gateway"
|
||||
|
||||
log_test "IPv$family: Redirect: exception with a matching socket"
|
||||
}
|
||||
|
||||
redirect_socket_ipv4()
|
||||
{
|
||||
redirect_socket 4
|
||||
}
|
||||
|
||||
redirect_socket_ipv6()
|
||||
{
|
||||
redirect_socket 6
|
||||
}
|
||||
|
||||
trap defer_scopes_cleanup EXIT
|
||||
tests_run
|
||||
|
||||
exit "$EXIT_STATUS"
|
||||
@@ -685,6 +685,9 @@ star_g_common()
|
||||
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $grp dst $vtep_ip src_vni 10010"
|
||||
log_test $? 255 "Invalid source in source list"
|
||||
|
||||
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent filter_mode exclude source_list $all_zeros_grp dst $vtep_ip src_vni 10010"
|
||||
log_test $? 255 "All-zeros source in source list"
|
||||
|
||||
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp permanent source_list $src1 dst $vtep_ip src_vni 10010"
|
||||
log_test $? 255 "Source list without filter mode"
|
||||
}
|
||||
@@ -784,6 +787,9 @@ sg_common()
|
||||
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $grp permanent dst $vtep_ip src_vni 10010"
|
||||
log_test $? 255 "(S, G) with an invalid source list"
|
||||
|
||||
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $grp src $all_zeros_grp permanent dst $vtep_ip src_vni 10010"
|
||||
log_test $? 255 "(S, G) with an all-zeros source"
|
||||
|
||||
run_cmd "bridge -n $ns1 mdb add dev vx0 port vx0 grp $all_zeros_grp src $src permanent dst $vtep_ip src_vni 10010"
|
||||
log_test $? 255 "All-zeros group with source"
|
||||
}
|
||||
|
||||
@@ -353,5 +353,28 @@
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DEV1 parent root drr"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "70fd",
|
||||
"name": "Add u32 filter when node ID pool is exhausted (4096th filter rejected)",
|
||||
"category": [
|
||||
"filter",
|
||||
"u32"
|
||||
],
|
||||
"plugins": {
|
||||
"requires": "nsPlugin"
|
||||
},
|
||||
"setup": [
|
||||
"$TC qdisc add dev $DUMMY clsact",
|
||||
"bash -c 'for i in {1..4095}; do echo filter add dev $DUMMY ingress prio 1 protocol ip u32 match u8 0 0 at 0; done | $TC -b -'"
|
||||
],
|
||||
"cmdUnderTest": "$TC filter add dev $DUMMY ingress prio 1 protocol ip u32 match u8 0 0 at 0",
|
||||
"expExitCode": "2",
|
||||
"verifyCmd": "$TC -d filter show dev $DUMMY ingress",
|
||||
"matchPattern": "fh 800::",
|
||||
"matchCount": "4095",
|
||||
"teardown": [
|
||||
"$TC qdisc del dev $DUMMY clsact"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user