Merge be1236fce5 ("Merge tag 'for-netdev' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf") into android-mainline
Steps on the way to 6.2-rc1 Change-Id: I6ec05b053b71c289a2365847e322e023e271f7e3 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
@@ -659,3 +659,19 @@
|
||||
interrupts = <16 2 1 9>;
|
||||
};
|
||||
};
|
||||
|
||||
&fman0_rx_0x08 {
|
||||
/delete-property/ fsl,fman-10g-port;
|
||||
};
|
||||
|
||||
&fman0_tx_0x28 {
|
||||
/delete-property/ fsl,fman-10g-port;
|
||||
};
|
||||
|
||||
&fman0_rx_0x09 {
|
||||
/delete-property/ fsl,fman-10g-port;
|
||||
};
|
||||
|
||||
&fman0_tx_0x29 {
|
||||
/delete-property/ fsl,fman-10g-port;
|
||||
};
|
||||
|
||||
@@ -2654,10 +2654,12 @@ static void bond_miimon_link_change(struct bonding *bond,
|
||||
|
||||
static void bond_miimon_commit(struct bonding *bond)
|
||||
{
|
||||
struct slave *slave, *primary;
|
||||
struct slave *slave, *primary, *active;
|
||||
bool do_failover = false;
|
||||
struct list_head *iter;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
bond_for_each_slave(bond, slave, iter) {
|
||||
switch (slave->link_new_state) {
|
||||
case BOND_LINK_NOCHANGE:
|
||||
@@ -2700,8 +2702,8 @@ static void bond_miimon_commit(struct bonding *bond)
|
||||
|
||||
bond_miimon_link_change(bond, slave, BOND_LINK_UP);
|
||||
|
||||
if (!rcu_access_pointer(bond->curr_active_slave) || slave == primary ||
|
||||
slave->prio > rcu_dereference(bond->curr_active_slave)->prio)
|
||||
active = rtnl_dereference(bond->curr_active_slave);
|
||||
if (!active || slave == primary || slave->prio > active->prio)
|
||||
do_failover = true;
|
||||
|
||||
continue;
|
||||
|
||||
@@ -3855,18 +3855,16 @@ static int hns3_gro_complete(struct sk_buff *skb, u32 l234info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool hns3_checksum_complete(struct hns3_enet_ring *ring,
|
||||
static void hns3_checksum_complete(struct hns3_enet_ring *ring,
|
||||
struct sk_buff *skb, u32 ptype, u16 csum)
|
||||
{
|
||||
if (ptype == HNS3_INVALID_PTYPE ||
|
||||
hns3_rx_ptype_tbl[ptype].ip_summed != CHECKSUM_COMPLETE)
|
||||
return false;
|
||||
return;
|
||||
|
||||
hns3_ring_stats_update(ring, csum_complete);
|
||||
skb->ip_summed = CHECKSUM_COMPLETE;
|
||||
skb->csum = csum_unfold((__force __sum16)csum);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hns3_rx_handle_csum(struct sk_buff *skb, u32 l234info,
|
||||
@@ -3926,8 +3924,7 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
|
||||
ptype = hnae3_get_field(ol_info, HNS3_RXD_PTYPE_M,
|
||||
HNS3_RXD_PTYPE_S);
|
||||
|
||||
if (hns3_checksum_complete(ring, skb, ptype, csum))
|
||||
return;
|
||||
hns3_checksum_complete(ring, skb, ptype, csum);
|
||||
|
||||
/* check if hardware has done checksum */
|
||||
if (!(bd_base_info & BIT(HNS3_RXD_L3L4P_B)))
|
||||
@@ -3936,6 +3933,7 @@ static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb,
|
||||
if (unlikely(l234info & (BIT(HNS3_RXD_L3E_B) | BIT(HNS3_RXD_L4E_B) |
|
||||
BIT(HNS3_RXD_OL3E_B) |
|
||||
BIT(HNS3_RXD_OL4E_B)))) {
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
hns3_ring_stats_update(ring, l3l4_csum_err);
|
||||
|
||||
return;
|
||||
|
||||
@@ -12754,60 +12754,71 @@ static int hclge_gro_en(struct hnae3_handle *handle, bool enable)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void hclge_sync_promisc_mode(struct hclge_dev *hdev)
|
||||
static int hclge_sync_vport_promisc_mode(struct hclge_vport *vport)
|
||||
{
|
||||
struct hclge_vport *vport = &hdev->vport[0];
|
||||
struct hnae3_handle *handle = &vport->nic;
|
||||
struct hclge_dev *hdev = vport->back;
|
||||
bool uc_en = false;
|
||||
bool mc_en = false;
|
||||
u8 tmp_flags;
|
||||
bool bc_en;
|
||||
int ret;
|
||||
u16 i;
|
||||
|
||||
if (vport->last_promisc_flags != vport->overflow_promisc_flags) {
|
||||
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
|
||||
vport->last_promisc_flags = vport->overflow_promisc_flags;
|
||||
}
|
||||
|
||||
if (test_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state)) {
|
||||
if (!test_and_clear_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
|
||||
&vport->state))
|
||||
return 0;
|
||||
|
||||
/* for PF */
|
||||
if (!vport->vport_id) {
|
||||
tmp_flags = handle->netdev_flags | vport->last_promisc_flags;
|
||||
ret = hclge_set_promisc_mode(handle, tmp_flags & HNAE3_UPE,
|
||||
tmp_flags & HNAE3_MPE);
|
||||
if (!ret) {
|
||||
clear_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
|
||||
&vport->state);
|
||||
if (!ret)
|
||||
set_bit(HCLGE_VPORT_STATE_VLAN_FLTR_CHANGE,
|
||||
&vport->state);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 1; i < hdev->num_alloc_vport; i++) {
|
||||
bool uc_en = false;
|
||||
bool mc_en = false;
|
||||
bool bc_en;
|
||||
|
||||
vport = &hdev->vport[i];
|
||||
|
||||
if (!test_and_clear_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
|
||||
&vport->state))
|
||||
continue;
|
||||
|
||||
if (vport->vf_info.trusted) {
|
||||
uc_en = vport->vf_info.request_uc_en > 0 ||
|
||||
vport->overflow_promisc_flags &
|
||||
HNAE3_OVERFLOW_UPE;
|
||||
mc_en = vport->vf_info.request_mc_en > 0 ||
|
||||
vport->overflow_promisc_flags &
|
||||
HNAE3_OVERFLOW_MPE;
|
||||
}
|
||||
bc_en = vport->vf_info.request_bc_en > 0;
|
||||
|
||||
ret = hclge_cmd_set_promisc_mode(hdev, vport->vport_id, uc_en,
|
||||
mc_en, bc_en);
|
||||
if (ret) {
|
||||
else
|
||||
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE,
|
||||
&vport->state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* for VF */
|
||||
if (vport->vf_info.trusted) {
|
||||
uc_en = vport->vf_info.request_uc_en > 0 ||
|
||||
vport->overflow_promisc_flags & HNAE3_OVERFLOW_UPE;
|
||||
mc_en = vport->vf_info.request_mc_en > 0 ||
|
||||
vport->overflow_promisc_flags & HNAE3_OVERFLOW_MPE;
|
||||
}
|
||||
bc_en = vport->vf_info.request_bc_en > 0;
|
||||
|
||||
ret = hclge_cmd_set_promisc_mode(hdev, vport->vport_id, uc_en,
|
||||
mc_en, bc_en);
|
||||
if (ret) {
|
||||
set_bit(HCLGE_VPORT_STATE_PROMISC_CHANGE, &vport->state);
|
||||
return ret;
|
||||
}
|
||||
hclge_set_vport_vlan_fltr_change(vport);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hclge_sync_promisc_mode(struct hclge_dev *hdev)
|
||||
{
|
||||
struct hclge_vport *vport;
|
||||
int ret;
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < hdev->num_alloc_vport; i++) {
|
||||
vport = &hdev->vport[i];
|
||||
|
||||
ret = hclge_sync_vport_promisc_mode(vport);
|
||||
if (ret)
|
||||
return;
|
||||
}
|
||||
hclge_set_vport_vlan_fltr_change(vport);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2767,7 +2767,8 @@ static int hclgevf_pci_reset(struct hclgevf_dev *hdev)
|
||||
struct pci_dev *pdev = hdev->pdev;
|
||||
int ret = 0;
|
||||
|
||||
if (hdev->reset_type == HNAE3_VF_FULL_RESET &&
|
||||
if ((hdev->reset_type == HNAE3_VF_FULL_RESET ||
|
||||
hdev->reset_type == HNAE3_FLR_RESET) &&
|
||||
test_bit(HCLGEVF_STATE_IRQ_INITED, &hdev->state)) {
|
||||
hclgevf_misc_irq_uninit(hdev);
|
||||
hclgevf_uninit_msi(hdev);
|
||||
|
||||
@@ -783,7 +783,7 @@ construct_skb:
|
||||
static void
|
||||
ice_clean_xdp_tx_buf(struct ice_tx_ring *xdp_ring, struct ice_tx_buf *tx_buf)
|
||||
{
|
||||
xdp_return_frame((struct xdp_frame *)tx_buf->raw_buf);
|
||||
page_frag_free(tx_buf->raw_buf);
|
||||
xdp_ring->xdp_tx_active--;
|
||||
dma_unmap_single(xdp_ring->dev, dma_unmap_addr(tx_buf, dma),
|
||||
dma_unmap_len(tx_buf, len), DMA_TO_DEVICE);
|
||||
|
||||
@@ -381,7 +381,7 @@ int lan966x_port_pcs_set(struct lan966x_port *port,
|
||||
}
|
||||
|
||||
/* Take PCS out of reset */
|
||||
lan_rmw(DEV_CLOCK_CFG_LINK_SPEED_SET(2) |
|
||||
lan_rmw(DEV_CLOCK_CFG_LINK_SPEED_SET(LAN966X_SPEED_1000) |
|
||||
DEV_CLOCK_CFG_PCS_RX_RST_SET(0) |
|
||||
DEV_CLOCK_CFG_PCS_TX_RST_SET(0),
|
||||
DEV_CLOCK_CFG_LINK_SPEED |
|
||||
|
||||
@@ -617,6 +617,9 @@ struct nfp_net_dp {
|
||||
* @vnic_no_name: For non-port PF vNIC make ndo_get_phys_port_name return
|
||||
* -EOPNOTSUPP to keep backwards compatibility (set by app)
|
||||
* @port: Pointer to nfp_port structure if vNIC is a port
|
||||
* @mc_lock: Protect mc_addrs list
|
||||
* @mc_addrs: List of mc addrs to add/del to HW
|
||||
* @mc_work: Work to update mc addrs
|
||||
* @app_priv: APP private data for this vNIC
|
||||
*/
|
||||
struct nfp_net {
|
||||
@@ -718,6 +721,10 @@ struct nfp_net {
|
||||
|
||||
struct nfp_port *port;
|
||||
|
||||
spinlock_t mc_lock;
|
||||
struct list_head mc_addrs;
|
||||
struct work_struct mc_work;
|
||||
|
||||
void *app_priv;
|
||||
};
|
||||
|
||||
|
||||
@@ -1334,9 +1334,14 @@ err_unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nfp_net_mc_cfg(struct net_device *netdev, const unsigned char *addr, const u32 cmd)
|
||||
struct nfp_mc_addr_entry {
|
||||
u8 addr[ETH_ALEN];
|
||||
u32 cmd;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static int nfp_net_mc_cfg(struct nfp_net *nn, const unsigned char *addr, const u32 cmd)
|
||||
{
|
||||
struct nfp_net *nn = netdev_priv(netdev);
|
||||
int ret;
|
||||
|
||||
ret = nfp_net_mbox_lock(nn, NFP_NET_CFG_MULTICAST_SZ);
|
||||
@@ -1351,6 +1356,25 @@ static int nfp_net_mc_cfg(struct net_device *netdev, const unsigned char *addr,
|
||||
return nfp_net_mbox_reconfig_and_unlock(nn, cmd);
|
||||
}
|
||||
|
||||
static int nfp_net_mc_prep(struct nfp_net *nn, const unsigned char *addr, const u32 cmd)
|
||||
{
|
||||
struct nfp_mc_addr_entry *entry;
|
||||
|
||||
entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
|
||||
if (!entry)
|
||||
return -ENOMEM;
|
||||
|
||||
ether_addr_copy(entry->addr, addr);
|
||||
entry->cmd = cmd;
|
||||
spin_lock_bh(&nn->mc_lock);
|
||||
list_add_tail(&entry->list, &nn->mc_addrs);
|
||||
spin_unlock_bh(&nn->mc_lock);
|
||||
|
||||
schedule_work(&nn->mc_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfp_net_mc_sync(struct net_device *netdev, const unsigned char *addr)
|
||||
{
|
||||
struct nfp_net *nn = netdev_priv(netdev);
|
||||
@@ -1361,12 +1385,35 @@ static int nfp_net_mc_sync(struct net_device *netdev, const unsigned char *addr)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return nfp_net_mc_cfg(netdev, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_ADD);
|
||||
return nfp_net_mc_prep(nn, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_ADD);
|
||||
}
|
||||
|
||||
static int nfp_net_mc_unsync(struct net_device *netdev, const unsigned char *addr)
|
||||
{
|
||||
return nfp_net_mc_cfg(netdev, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_DEL);
|
||||
struct nfp_net *nn = netdev_priv(netdev);
|
||||
|
||||
return nfp_net_mc_prep(nn, addr, NFP_NET_CFG_MBOX_CMD_MULTICAST_DEL);
|
||||
}
|
||||
|
||||
static void nfp_net_mc_addr_config(struct work_struct *work)
|
||||
{
|
||||
struct nfp_net *nn = container_of(work, struct nfp_net, mc_work);
|
||||
struct nfp_mc_addr_entry *entry, *tmp;
|
||||
struct list_head tmp_list;
|
||||
|
||||
INIT_LIST_HEAD(&tmp_list);
|
||||
|
||||
spin_lock_bh(&nn->mc_lock);
|
||||
list_splice_init(&nn->mc_addrs, &tmp_list);
|
||||
spin_unlock_bh(&nn->mc_lock);
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &tmp_list, list) {
|
||||
if (nfp_net_mc_cfg(nn, entry->addr, entry->cmd))
|
||||
nn_err(nn, "Config mc address to HW failed.\n");
|
||||
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void nfp_net_set_rx_mode(struct net_device *netdev)
|
||||
@@ -2633,6 +2680,11 @@ int nfp_net_init(struct nfp_net *nn)
|
||||
|
||||
if (!nn->dp.netdev)
|
||||
return 0;
|
||||
|
||||
spin_lock_init(&nn->mc_lock);
|
||||
INIT_LIST_HEAD(&nn->mc_addrs);
|
||||
INIT_WORK(&nn->mc_work, nfp_net_mc_addr_config);
|
||||
|
||||
return register_netdev(nn->dp.netdev);
|
||||
|
||||
err_clean_mbox:
|
||||
@@ -2652,5 +2704,6 @@ void nfp_net_clean(struct nfp_net *nn)
|
||||
unregister_netdev(nn->dp.netdev);
|
||||
nfp_net_ipsec_clean(nn);
|
||||
nfp_ccm_mbox_clean(nn);
|
||||
flush_work(&nn->mc_work);
|
||||
nfp_net_reconfig_wait_posted(nn);
|
||||
}
|
||||
|
||||
+3
-2
@@ -974,6 +974,9 @@ static int veth_poll(struct napi_struct *napi, int budget)
|
||||
xdp_set_return_frame_no_direct();
|
||||
done = veth_xdp_rcv(rq, budget, &bq, &stats);
|
||||
|
||||
if (stats.xdp_redirect > 0)
|
||||
xdp_do_flush();
|
||||
|
||||
if (done < budget && napi_complete_done(napi, done)) {
|
||||
/* Write rx_notify_masked before reading ptr_ring */
|
||||
smp_store_mb(rq->rx_notify_masked, false);
|
||||
@@ -987,8 +990,6 @@ static int veth_poll(struct napi_struct *napi, int budget)
|
||||
|
||||
if (stats.xdp_tx > 0)
|
||||
veth_xdp_flush(rq, &bq);
|
||||
if (stats.xdp_redirect > 0)
|
||||
xdp_do_flush();
|
||||
xdp_clear_return_frame_no_direct();
|
||||
|
||||
return done;
|
||||
|
||||
@@ -1288,6 +1288,10 @@ vmxnet3_rx_csum(struct vmxnet3_adapter *adapter,
|
||||
(le32_to_cpu(gdesc->dword[3]) &
|
||||
VMXNET3_RCD_CSUM_OK) == VMXNET3_RCD_CSUM_OK) {
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
if ((le32_to_cpu(gdesc->dword[0]) &
|
||||
(1UL << VMXNET3_RCD_HDR_INNER_SHIFT))) {
|
||||
skb->csum_level = 1;
|
||||
}
|
||||
WARN_ON_ONCE(!(gdesc->rcd.tcp || gdesc->rcd.udp) &&
|
||||
!(le32_to_cpu(gdesc->dword[0]) &
|
||||
(1UL << VMXNET3_RCD_HDR_INNER_SHIFT)));
|
||||
@@ -1297,6 +1301,10 @@ vmxnet3_rx_csum(struct vmxnet3_adapter *adapter,
|
||||
} else if (gdesc->rcd.v6 && (le32_to_cpu(gdesc->dword[3]) &
|
||||
(1 << VMXNET3_RCD_TUC_SHIFT))) {
|
||||
skb->ip_summed = CHECKSUM_UNNECESSARY;
|
||||
if ((le32_to_cpu(gdesc->dword[0]) &
|
||||
(1UL << VMXNET3_RCD_HDR_INNER_SHIFT))) {
|
||||
skb->csum_level = 1;
|
||||
}
|
||||
WARN_ON_ONCE(!(gdesc->rcd.tcp || gdesc->rcd.udp) &&
|
||||
!(le32_to_cpu(gdesc->dword[0]) &
|
||||
(1UL << VMXNET3_RCD_HDR_INNER_SHIFT)));
|
||||
|
||||
+3
-3
@@ -1385,8 +1385,8 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
|
||||
|
||||
/* loopback, multicast & non-ND link-local traffic; do not push through
|
||||
* packet taps again. Reset pkt_type for upper layers to process skb.
|
||||
* For strict packets with a source LLA, determine the dst using the
|
||||
* original ifindex.
|
||||
* For non-loopback strict packets, determine the dst using the original
|
||||
* ifindex.
|
||||
*/
|
||||
if (skb->pkt_type == PACKET_LOOPBACK || (need_strict && !is_ndisc)) {
|
||||
skb->dev = vrf_dev;
|
||||
@@ -1395,7 +1395,7 @@ static struct sk_buff *vrf_ip6_rcv(struct net_device *vrf_dev,
|
||||
|
||||
if (skb->pkt_type == PACKET_LOOPBACK)
|
||||
skb->pkt_type = PACKET_HOST;
|
||||
else if (ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)
|
||||
else
|
||||
vrf_ip6_input_dst(skb, vrf_dev, orig_iif);
|
||||
|
||||
goto out;
|
||||
|
||||
@@ -327,9 +327,9 @@ static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ATH9K_HTC_DEBUGFS
|
||||
#define __STAT_SAFE(hif_dev, expr) ((hif_dev)->htc_handle->drv_priv ? (expr) : 0)
|
||||
#define CAB_STAT_INC(priv) ((priv)->debug.tx_stats.cab_queued++)
|
||||
#define TX_QSTAT_INC(priv, q) ((priv)->debug.tx_stats.queue_stats[q]++)
|
||||
#define __STAT_SAFE(hif_dev, expr) do { ((hif_dev)->htc_handle->drv_priv ? (expr) : 0); } while (0)
|
||||
#define CAB_STAT_INC(priv) do { ((priv)->debug.tx_stats.cab_queued++); } while (0)
|
||||
#define TX_QSTAT_INC(priv, q) do { ((priv)->debug.tx_stats.queue_stats[q]++); } while (0)
|
||||
|
||||
#define TX_STAT_INC(hif_dev, c) \
|
||||
__STAT_SAFE((hif_dev), (hif_dev)->htc_handle->drv_priv->debug.tx_stats.c++)
|
||||
@@ -378,10 +378,10 @@ void ath9k_htc_get_et_stats(struct ieee80211_hw *hw,
|
||||
struct ethtool_stats *stats, u64 *data);
|
||||
#else
|
||||
|
||||
#define TX_STAT_INC(hif_dev, c)
|
||||
#define TX_STAT_ADD(hif_dev, c, a)
|
||||
#define RX_STAT_INC(hif_dev, c)
|
||||
#define RX_STAT_ADD(hif_dev, c, a)
|
||||
#define TX_STAT_INC(hif_dev, c) do { } while (0)
|
||||
#define TX_STAT_ADD(hif_dev, c, a) do { } while (0)
|
||||
#define RX_STAT_INC(hif_dev, c) do { } while (0)
|
||||
#define RX_STAT_ADD(hif_dev, c, a) do { } while (0)
|
||||
|
||||
#define CAB_STAT_INC(priv)
|
||||
#define TX_QSTAT_INC(priv, c)
|
||||
|
||||
@@ -1106,6 +1106,11 @@ int iwl_read_ppag_table(struct iwl_fw_runtime *fwrt, union iwl_ppag_table_cmd *c
|
||||
int i, j, num_sub_bands;
|
||||
s8 *gain;
|
||||
|
||||
/* many firmware images for JF lie about this */
|
||||
if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) ==
|
||||
CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SET_PPAG)) {
|
||||
IWL_DEBUG_RADIO(fwrt,
|
||||
"PPAG capability not supported by FW, command not sent.\n");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
config MT7996E
|
||||
tristate "MediaTek MT7996 (PCIe) support"
|
||||
select MT76_CONNAC_LIB
|
||||
select RELAY
|
||||
depends on MAC80211
|
||||
depends on PCI
|
||||
help
|
||||
|
||||
@@ -3,6 +3,3 @@ obj-$(CONFIG_WLCORE) += wlcore/
|
||||
obj-$(CONFIG_WL12XX) += wl12xx/
|
||||
obj-$(CONFIG_WL1251) += wl1251/
|
||||
obj-$(CONFIG_WL18XX) += wl18xx/
|
||||
|
||||
# small builtin driver bit
|
||||
obj-$(CONFIG_WILINK_PLATFORM_DATA) += wilink_platform_data.o
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
*/
|
||||
#define BR2684_ENCAPS_VC (0) /* VC-mux */
|
||||
#define BR2684_ENCAPS_LLC (1)
|
||||
#define BR2684_ENCAPS_AUTODETECT (2) /* Unsuported */
|
||||
#define BR2684_ENCAPS_AUTODETECT (2) /* Unsupported */
|
||||
|
||||
/*
|
||||
* Is this VC bridged or routed?
|
||||
|
||||
@@ -351,8 +351,10 @@ BTF_ID(func, bpf_lsm_bpf_prog_alloc_security)
|
||||
BTF_ID(func, bpf_lsm_bpf_prog_free_security)
|
||||
BTF_ID(func, bpf_lsm_file_alloc_security)
|
||||
BTF_ID(func, bpf_lsm_file_free_security)
|
||||
#ifdef CONFIG_SECURITY_NETWORK
|
||||
BTF_ID(func, bpf_lsm_sk_alloc_security)
|
||||
BTF_ID(func, bpf_lsm_sk_free_security)
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
BTF_ID(func, bpf_lsm_task_free)
|
||||
BTF_SET_END(untrusted_lsm_hooks)
|
||||
|
||||
|
||||
@@ -11822,10 +11822,17 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
|
||||
* register B - not null
|
||||
* for JNE A, B, ... - A is not null in the false branch;
|
||||
* for JEQ A, B, ... - A is not null in the true branch.
|
||||
*
|
||||
* Since PTR_TO_BTF_ID points to a kernel struct that does
|
||||
* not need to be null checked by the BPF program, i.e.,
|
||||
* could be null even without PTR_MAYBE_NULL marking, so
|
||||
* only propagate nullness when neither reg is that type.
|
||||
*/
|
||||
if (!is_jmp32 && BPF_SRC(insn->code) == BPF_X &&
|
||||
__is_pointer_value(false, src_reg) && __is_pointer_value(false, dst_reg) &&
|
||||
type_may_be_null(src_reg->type) != type_may_be_null(dst_reg->type)) {
|
||||
type_may_be_null(src_reg->type) != type_may_be_null(dst_reg->type) &&
|
||||
base_type(src_reg->type) != PTR_TO_BTF_ID &&
|
||||
base_type(dst_reg->type) != PTR_TO_BTF_ID) {
|
||||
eq_branch_regs = NULL;
|
||||
switch (opcode) {
|
||||
case BPF_JEQ:
|
||||
|
||||
+5
-2
@@ -3180,15 +3180,18 @@ static int bpf_skb_generic_push(struct sk_buff *skb, u32 off, u32 len)
|
||||
|
||||
static int bpf_skb_generic_pop(struct sk_buff *skb, u32 off, u32 len)
|
||||
{
|
||||
void *old_data;
|
||||
|
||||
/* skb_ensure_writable() is not needed here, as we're
|
||||
* already working on an uncloned skb.
|
||||
*/
|
||||
if (unlikely(!pskb_may_pull(skb, off + len)))
|
||||
return -ENOMEM;
|
||||
|
||||
skb_postpull_rcsum(skb, skb->data + off, len);
|
||||
memmove(skb->data + len, skb->data, off);
|
||||
old_data = skb->data;
|
||||
__skb_pull(skb, len);
|
||||
skb_postpull_rcsum(skb, old_data + off, len);
|
||||
memmove(skb->data, old_data, off);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+16
-4
@@ -1662,6 +1662,8 @@ static void mptcp_set_nospace(struct sock *sk)
|
||||
set_bit(MPTCP_NOSPACE, &mptcp_sk(sk)->flags);
|
||||
}
|
||||
|
||||
static int mptcp_disconnect(struct sock *sk, int flags);
|
||||
|
||||
static int mptcp_sendmsg_fastopen(struct sock *sk, struct sock *ssk, struct msghdr *msg,
|
||||
size_t len, int *copied_syn)
|
||||
{
|
||||
@@ -1672,9 +1674,9 @@ static int mptcp_sendmsg_fastopen(struct sock *sk, struct sock *ssk, struct msgh
|
||||
lock_sock(ssk);
|
||||
msg->msg_flags |= MSG_DONTWAIT;
|
||||
msk->connect_flags = O_NONBLOCK;
|
||||
msk->is_sendmsg = 1;
|
||||
msk->fastopening = 1;
|
||||
ret = tcp_sendmsg_fastopen(ssk, msg, copied_syn, len, NULL);
|
||||
msk->is_sendmsg = 0;
|
||||
msk->fastopening = 0;
|
||||
msg->msg_flags = saved_flags;
|
||||
release_sock(ssk);
|
||||
|
||||
@@ -1688,6 +1690,8 @@ static int mptcp_sendmsg_fastopen(struct sock *sk, struct sock *ssk, struct msgh
|
||||
*/
|
||||
if (ret && ret != -EINPROGRESS && ret != -ERESTARTSYS && ret != -EINTR)
|
||||
*copied_syn = 0;
|
||||
} else if (ret && ret != -EINPROGRESS) {
|
||||
mptcp_disconnect(sk, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -2353,7 +2357,7 @@ static void __mptcp_close_ssk(struct sock *sk, struct sock *ssk,
|
||||
/* otherwise tcp will dispose of the ssk and subflow ctx */
|
||||
if (ssk->sk_state == TCP_LISTEN) {
|
||||
tcp_set_state(ssk, TCP_CLOSE);
|
||||
mptcp_subflow_queue_clean(ssk);
|
||||
mptcp_subflow_queue_clean(sk, ssk);
|
||||
inet_csk_listen_stop(ssk);
|
||||
mptcp_event_pm_listener(ssk, MPTCP_EVENT_LISTENER_CLOSED);
|
||||
}
|
||||
@@ -2989,6 +2993,14 @@ static int mptcp_disconnect(struct sock *sk, int flags)
|
||||
{
|
||||
struct mptcp_sock *msk = mptcp_sk(sk);
|
||||
|
||||
/* We are on the fastopen error path. We can't call straight into the
|
||||
* subflows cleanup code due to lock nesting (we are already under
|
||||
* msk->firstsocket lock). Do nothing and leave the cleanup to the
|
||||
* caller.
|
||||
*/
|
||||
if (msk->fastopening)
|
||||
return 0;
|
||||
|
||||
inet_sk_state_store(sk, TCP_CLOSE);
|
||||
|
||||
mptcp_stop_timer(sk);
|
||||
@@ -3532,7 +3544,7 @@ static int mptcp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
|
||||
/* if reaching here via the fastopen/sendmsg path, the caller already
|
||||
* acquired the subflow socket lock, too.
|
||||
*/
|
||||
if (msk->is_sendmsg)
|
||||
if (msk->fastopening)
|
||||
err = __inet_stream_connect(ssock, uaddr, addr_len, msk->connect_flags, 1);
|
||||
else
|
||||
err = inet_stream_connect(ssock, uaddr, addr_len, msk->connect_flags);
|
||||
|
||||
@@ -295,7 +295,7 @@ struct mptcp_sock {
|
||||
u8 recvmsg_inq:1,
|
||||
cork:1,
|
||||
nodelay:1,
|
||||
is_sendmsg:1;
|
||||
fastopening:1;
|
||||
int connect_flags;
|
||||
struct work_struct work;
|
||||
struct sk_buff *ooo_last_skb;
|
||||
@@ -628,7 +628,7 @@ void mptcp_close_ssk(struct sock *sk, struct sock *ssk,
|
||||
struct mptcp_subflow_context *subflow);
|
||||
void __mptcp_subflow_send_ack(struct sock *ssk);
|
||||
void mptcp_subflow_reset(struct sock *ssk);
|
||||
void mptcp_subflow_queue_clean(struct sock *ssk);
|
||||
void mptcp_subflow_queue_clean(struct sock *sk, struct sock *ssk);
|
||||
void mptcp_sock_graft(struct sock *sk, struct socket *parent);
|
||||
struct socket *__mptcp_nmpc_socket(const struct mptcp_sock *msk);
|
||||
bool __mptcp_close(struct sock *sk, long timeout);
|
||||
|
||||
+17
-2
@@ -1791,7 +1791,7 @@ static void subflow_state_change(struct sock *sk)
|
||||
}
|
||||
}
|
||||
|
||||
void mptcp_subflow_queue_clean(struct sock *listener_ssk)
|
||||
void mptcp_subflow_queue_clean(struct sock *listener_sk, struct sock *listener_ssk)
|
||||
{
|
||||
struct request_sock_queue *queue = &inet_csk(listener_ssk)->icsk_accept_queue;
|
||||
struct mptcp_sock *msk, *next, *head = NULL;
|
||||
@@ -1840,8 +1840,23 @@ void mptcp_subflow_queue_clean(struct sock *listener_ssk)
|
||||
|
||||
do_cancel_work = __mptcp_close(sk, 0);
|
||||
release_sock(sk);
|
||||
if (do_cancel_work)
|
||||
if (do_cancel_work) {
|
||||
/* lockdep will report a false positive ABBA deadlock
|
||||
* between cancel_work_sync and the listener socket.
|
||||
* The involved locks belong to different sockets WRT
|
||||
* the existing AB chain.
|
||||
* Using a per socket key is problematic as key
|
||||
* deregistration requires process context and must be
|
||||
* performed at socket disposal time, in atomic
|
||||
* context.
|
||||
* Just tell lockdep to consider the listener socket
|
||||
* released here.
|
||||
*/
|
||||
mutex_release(&listener_sk->sk_lock.dep_map, _RET_IP_);
|
||||
mptcp_cancel_work(sk);
|
||||
mutex_acquire(&listener_sk->sk_lock.dep_map,
|
||||
SINGLE_DEPTH_NESTING, 0, _RET_IP_);
|
||||
}
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
|
||||
@@ -1861,7 +1861,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
|
||||
vport->upcall_stats = netdev_alloc_pcpu_stats(struct vport_upcall_stats_percpu);
|
||||
if (!vport->upcall_stats) {
|
||||
err = -ENOMEM;
|
||||
goto err_destroy_portids;
|
||||
goto err_destroy_vport;
|
||||
}
|
||||
|
||||
err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
|
||||
@@ -1876,6 +1876,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
|
||||
ovs_notify(&dp_datapath_genl_family, reply, info);
|
||||
return 0;
|
||||
|
||||
err_destroy_vport:
|
||||
ovs_dp_detach_port(vport);
|
||||
err_destroy_portids:
|
||||
kfree(rcu_dereference_raw(dp->upcall_portids));
|
||||
err_unlock_and_destroy_meters:
|
||||
@@ -2323,7 +2325,7 @@ restart:
|
||||
vport->upcall_stats = netdev_alloc_pcpu_stats(struct vport_upcall_stats_percpu);
|
||||
if (!vport->upcall_stats) {
|
||||
err = -ENOMEM;
|
||||
goto exit_unlock_free;
|
||||
goto exit_unlock_free_vport;
|
||||
}
|
||||
|
||||
err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
|
||||
@@ -2343,6 +2345,8 @@ restart:
|
||||
ovs_notify(&dp_vport_genl_family, reply, info);
|
||||
return 0;
|
||||
|
||||
exit_unlock_free_vport:
|
||||
ovs_dp_detach_port(vport);
|
||||
exit_unlock_free:
|
||||
ovs_unlock();
|
||||
kfree_skb(reply);
|
||||
|
||||
@@ -145,6 +145,10 @@ ifeq ($(feature-llvm),1)
|
||||
LLVM_CONFIG_LIB_COMPONENTS := mcdisassembler all-targets
|
||||
CFLAGS += $(shell $(LLVM_CONFIG) --cflags --libs $(LLVM_CONFIG_LIB_COMPONENTS))
|
||||
LIBS += $(shell $(LLVM_CONFIG) --libs $(LLVM_CONFIG_LIB_COMPONENTS))
|
||||
ifeq ($(shell $(LLVM_CONFIG) --shared-mode),static)
|
||||
LIBS += $(shell $(LLVM_CONFIG) --system-libs $(LLVM_CONFIG_LIB_COMPONENTS))
|
||||
LIBS += -lstdc++
|
||||
endif
|
||||
LDFLAGS += $(shell $(LLVM_CONFIG) --ldflags)
|
||||
else
|
||||
# Fall back on libbfd
|
||||
|
||||
@@ -36,6 +36,7 @@ test_cpp
|
||||
*.lskel.h
|
||||
/no_alu32
|
||||
/bpf_gcc
|
||||
/host-tools
|
||||
/tools
|
||||
/runqslower
|
||||
/bench
|
||||
|
||||
@@ -14,6 +14,7 @@ cgrp_kfunc # JIT does not support calling kernel f
|
||||
cgrp_local_storage # prog_attach unexpected error: -524 (trampoline)
|
||||
core_read_macros # unknown func bpf_probe_read#4 (overlapping)
|
||||
d_path # failed to auto-attach program 'prog_stat': -524 (trampoline)
|
||||
decap_sanity # JIT does not support calling kernel function (kfunc)
|
||||
deny_namespace # failed to attach: ERROR: strerror_r(-524)=22 (trampoline)
|
||||
dummy_st_ops # test_run unexpected error: -524 (errno 524) (trampoline)
|
||||
fentry_fexit # fentry attach failed: -524 (trampoline)
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/in6.h>
|
||||
|
||||
#include "test_progs.h"
|
||||
#include "network_helpers.h"
|
||||
#include "decap_sanity.skel.h"
|
||||
|
||||
#define SYS(fmt, ...) \
|
||||
({ \
|
||||
char cmd[1024]; \
|
||||
snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__); \
|
||||
if (!ASSERT_OK(system(cmd), cmd)) \
|
||||
goto fail; \
|
||||
})
|
||||
|
||||
#define NS_TEST "decap_sanity_ns"
|
||||
#define IPV6_IFACE_ADDR "face::1"
|
||||
#define UDP_TEST_PORT 7777
|
||||
|
||||
void test_decap_sanity(void)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_tc_hook, qdisc_hook, .attach_point = BPF_TC_EGRESS);
|
||||
LIBBPF_OPTS(bpf_tc_opts, tc_attach);
|
||||
struct nstoken *nstoken = NULL;
|
||||
struct decap_sanity *skel;
|
||||
struct sockaddr_in6 addr;
|
||||
socklen_t addrlen;
|
||||
char buf[128] = {};
|
||||
int sockfd, err;
|
||||
|
||||
skel = decap_sanity__open_and_load();
|
||||
if (!ASSERT_OK_PTR(skel, "skel open_and_load"))
|
||||
return;
|
||||
|
||||
SYS("ip netns add %s", NS_TEST);
|
||||
SYS("ip -net %s -6 addr add %s/128 dev lo nodad", NS_TEST, IPV6_IFACE_ADDR);
|
||||
SYS("ip -net %s link set dev lo up", NS_TEST);
|
||||
|
||||
nstoken = open_netns(NS_TEST);
|
||||
if (!ASSERT_OK_PTR(nstoken, "open_netns"))
|
||||
goto fail;
|
||||
|
||||
qdisc_hook.ifindex = if_nametoindex("lo");
|
||||
if (!ASSERT_GT(qdisc_hook.ifindex, 0, "if_nametoindex lo"))
|
||||
goto fail;
|
||||
|
||||
err = bpf_tc_hook_create(&qdisc_hook);
|
||||
if (!ASSERT_OK(err, "create qdisc hook"))
|
||||
goto fail;
|
||||
|
||||
tc_attach.prog_fd = bpf_program__fd(skel->progs.decap_sanity);
|
||||
err = bpf_tc_attach(&qdisc_hook, &tc_attach);
|
||||
if (!ASSERT_OK(err, "attach filter"))
|
||||
goto fail;
|
||||
|
||||
addrlen = sizeof(addr);
|
||||
err = make_sockaddr(AF_INET6, IPV6_IFACE_ADDR, UDP_TEST_PORT,
|
||||
(void *)&addr, &addrlen);
|
||||
if (!ASSERT_OK(err, "make_sockaddr"))
|
||||
goto fail;
|
||||
sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||
if (!ASSERT_NEQ(sockfd, -1, "socket"))
|
||||
goto fail;
|
||||
err = sendto(sockfd, buf, sizeof(buf), 0, (void *)&addr, addrlen);
|
||||
close(sockfd);
|
||||
if (!ASSERT_EQ(err, sizeof(buf), "send"))
|
||||
goto fail;
|
||||
|
||||
ASSERT_TRUE(skel->bss->init_csum_partial, "init_csum_partial");
|
||||
ASSERT_TRUE(skel->bss->final_csum_none, "final_csum_none");
|
||||
ASSERT_FALSE(skel->bss->broken_csum_start, "broken_csum_start");
|
||||
|
||||
fail:
|
||||
if (nstoken) {
|
||||
bpf_tc_hook_destroy(&qdisc_hook);
|
||||
close_netns(nstoken);
|
||||
}
|
||||
system("ip netns del " NS_TEST " >& /dev/null");
|
||||
decap_sanity__destroy(skel);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <test_progs.h>
|
||||
#include "jeq_infer_not_null_fail.skel.h"
|
||||
|
||||
void test_jeq_infer_not_null(void)
|
||||
{
|
||||
RUN_TESTS(jeq_infer_not_null_fail);
|
||||
}
|
||||
@@ -50,6 +50,12 @@
|
||||
#define ICSK_TIME_LOSS_PROBE 5
|
||||
#define ICSK_TIME_REO_TIMEOUT 6
|
||||
|
||||
#define ETH_HLEN 14
|
||||
#define ETH_P_IPV6 0x86DD
|
||||
|
||||
#define CHECKSUM_NONE 0
|
||||
#define CHECKSUM_PARTIAL 3
|
||||
|
||||
#define IFNAMSIZ 16
|
||||
|
||||
#define RTF_GATEWAY 0x0002
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include "bpf_tracing_net.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_endian.h>
|
||||
|
||||
#define UDP_TEST_PORT 7777
|
||||
|
||||
void *bpf_cast_to_kern_ctx(void *) __ksym;
|
||||
bool init_csum_partial = false;
|
||||
bool final_csum_none = false;
|
||||
bool broken_csum_start = false;
|
||||
|
||||
static unsigned int skb_headlen(const struct sk_buff *skb)
|
||||
{
|
||||
return skb->len - skb->data_len;
|
||||
}
|
||||
|
||||
static unsigned int skb_headroom(const struct sk_buff *skb)
|
||||
{
|
||||
return skb->data - skb->head;
|
||||
}
|
||||
|
||||
static int skb_checksum_start_offset(const struct sk_buff *skb)
|
||||
{
|
||||
return skb->csum_start - skb_headroom(skb);
|
||||
}
|
||||
|
||||
SEC("tc")
|
||||
int decap_sanity(struct __sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *kskb;
|
||||
struct ipv6hdr ip6h;
|
||||
struct udphdr udph;
|
||||
int err;
|
||||
|
||||
if (skb->protocol != __bpf_constant_htons(ETH_P_IPV6))
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (bpf_skb_load_bytes(skb, ETH_HLEN, &ip6h, sizeof(ip6h)))
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (ip6h.nexthdr != IPPROTO_UDP)
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (bpf_skb_load_bytes(skb, ETH_HLEN + sizeof(ip6h), &udph, sizeof(udph)))
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
if (udph.dest != __bpf_constant_htons(UDP_TEST_PORT))
|
||||
return TC_ACT_SHOT;
|
||||
|
||||
kskb = bpf_cast_to_kern_ctx(skb);
|
||||
init_csum_partial = (kskb->ip_summed == CHECKSUM_PARTIAL);
|
||||
err = bpf_skb_adjust_room(skb, -(s32)(ETH_HLEN + sizeof(ip6h) + sizeof(udph)),
|
||||
1, BPF_F_ADJ_ROOM_FIXED_GSO);
|
||||
if (err)
|
||||
return TC_ACT_SHOT;
|
||||
final_csum_none = (kskb->ip_summed == CHECKSUM_NONE);
|
||||
if (kskb->ip_summed == CHECKSUM_PARTIAL &&
|
||||
(unsigned int)skb_checksum_start_offset(kskb) >= skb_headlen(kskb))
|
||||
broken_csum_start = true;
|
||||
|
||||
return TC_ACT_SHOT;
|
||||
}
|
||||
|
||||
char __license[] SEC("license") = "GPL";
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_misc.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, u64);
|
||||
__type(value, u64);
|
||||
} m_hash SEC(".maps");
|
||||
|
||||
SEC("?raw_tp")
|
||||
__failure __msg("R8 invalid mem access 'map_value_or_null")
|
||||
int jeq_infer_not_null_ptr_to_btfid(void *ctx)
|
||||
{
|
||||
struct bpf_map *map = (struct bpf_map *)&m_hash;
|
||||
struct bpf_map *inner_map = map->inner_map_meta;
|
||||
u64 key = 0, ret = 0, *val;
|
||||
|
||||
val = bpf_map_lookup_elem(map, &key);
|
||||
/* Do not mark ptr as non-null if one of them is
|
||||
* PTR_TO_BTF_ID (R9), reject because of invalid
|
||||
* access to map value (R8).
|
||||
*
|
||||
* Here, we need to inline those insns to access
|
||||
* R8 directly, since compiler may use other reg
|
||||
* once it figures out val==inner_map.
|
||||
*/
|
||||
asm volatile("r8 = %[val];\n"
|
||||
"r9 = %[inner_map];\n"
|
||||
"if r8 != r9 goto +1;\n"
|
||||
"%[ret] = *(u64 *)(r8 +0);\n"
|
||||
: [ret] "+r"(ret)
|
||||
: [inner_map] "r"(inner_map), [val] "r"(val)
|
||||
: "r8", "r9");
|
||||
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user