Linux RDMA and InfiniBand development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Xin Tian" <tianx@yunsilicon.com>
Cc: linux-rdma@vger.kernel.org
Subject: Re: [PATCH v1 10/15] RDMA/xsc: add RDMA device support
Date: Thu, 05 Nov 2026 16:09:35 +0000	[thread overview]
Message-ID: <sashiko-outbox-199432@kernel.org> (raw)
In-Reply-To: <20261104155712.37053-11-tianx@yunsilicon.com>

Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [Critical] RDMA/xsc: xsc_ib_mmap() exposes privileged BAR0 command queue registers
- [High] RDMA/xsc: unsynchronized netdev attachment and missing NETDEV_UNREGISTER
- [High] RDMA/xsc: xsc_ib_query_device() miscalculates resp.response_length
- [High] RDMA/xsc: IOVA page offset corruption and identity domain misclassification
- [Medium] RDMA/xsc: xsc_ib_query_port() leaves max_mtu as 0 and misencodes link info
- [Medium] RDMA/xsc: init_one() sets ib_active after ib_register_device() and copies unexpanded name
- [Medium] RDMA/xsc: wrong masked_atomic_cap and 32-bit shifts in xsc_ib_query_device()
- [Medium] RDMA/xsc: implicit padding, raw int types, and unvalidated udata in UAPI
- [Low] RDMA/xsc: missing select AUXILIARY_BUS in Kconfig
- [Low] RDMA/xsc: endianness warning in xsc_get_link_speed() and helper duplication
--

commit 78a9c302585dd86ed70444386c757ac8476b121c
Author: Xin Tian <tianx@yunsilicon.com>

RDMA/xsc: add RDMA device support

This commit introduces the xsc_ib auxiliary RDMA driver, registering an
ib_device backed by the shared xsc core device and netdev, and exposes the
userspace ABI in include/uapi/rdma/xsc-abi.h.

> diff --git a/drivers/infiniband/hw/xsc/Kconfig b/drivers/infiniband/hw/xsc/Kconfig
> new file mode 100644
> index 0000000000000..5cf46df2d4f12
> --- /dev/null
> +++ b/drivers/infiniband/hw/xsc/Kconfig
> @@ -0,0 +1,17 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (C) 2021 - 2026, Shanghai Yunsilicon Technology Co., Ltd.
> +# All rights reserved.
> +
> +config INFINIBAND_XSC
> +	tristate "Yunsilicon XSC RDMA driver"
> +	depends on INFINIBAND && PCI && INET
> +	depends on YUNSILICON_XSC_PCI && YUNSILICON_XSC_ETH

[Severity: Low]
Since xsc_ib_init() and xsc_ib_cleanup() call auxiliary_driver_register()
and auxiliary_driver_unregister(), and neither YUNSILICON_XSC_PCI nor
YUNSILICON_XSC_ETH selects AUXILIARY_BUS, can building with
CONFIG_INFINIBAND_XSC fail at link/modpost time with undefined references
to __auxiliary_driver_register and auxiliary_driver_unregister when no
other enabled driver selects CONFIG_AUXILIARY_BUS? Should INFINIBAND_XSC
(or YUNSILICON_XSC_PCI) select AUXILIARY_BUS?

> diff --git a/drivers/infiniband/hw/xsc/main.c b/drivers/infiniband/hw/xsc/main.c
> new file mode 100644
> index 0000000000000..322fad38b5df2
> --- /dev/null
> +++ b/drivers/infiniband/hw/xsc/main.c

[ ... ]

> +static int xsc_ib_query_device(struct ib_device *ibdev,
> +			       struct ib_device_attr *props,
> +			       struct ib_udata *udata)
> +{

[ ... ]

> +	props->max_cq		   = dev->xdev->caps.max_cq;
> +	props->max_cqe		   = dev->xdev->caps.max_cqes;
> +	props->max_mr		   = (1 << dev->xdev->caps.log_max_mkey) - 1;
> +	props->max_pd		   = dev->xdev->caps.max_pd;
> +	props->max_qp_rd_atom	   = dev->xdev->caps.max_ra_res_qp;
> +	props->max_qp_init_rd_atom = dev->xdev->caps.max_ra_req_qp;
> +	props->max_res_rd_atom	   = props->max_qp_rd_atom * props->max_qp;
> +	props->max_srq		   =
> +		dev->xdev->caps.log_max_srq ? (1 << dev->xdev->caps.log_max_srq) : 0;
> +	props->max_srq_wr	   = dev->xdev->caps.max_srq_wqes - 1;
> +	props->max_srq_sge	   = dev->xdev->caps.log_max_srq ? (max_rq_sg - 1) : 0;
> +	props->local_ca_ack_delay  = dev->xdev->caps.local_ca_ack_delay;
> +	props->atomic_cap	   = is_support_rdma_atomic(dev->xdev) ?
> +		IB_ATOMIC_HCA : IB_ATOMIC_NONE;
> +	props->masked_atomic_cap   = IB_ATOMIC_HCA;

[Severity: Medium]
In xsc_ib_query_device(), props->atomic_cap is gated on
is_support_rdma_atomic(dev->xdev), but props->masked_atomic_cap is
unconditionally set to IB_ATOMIC_HCA. Does this advertise masked atomic
support on hardware where RDMA atomics are not supported?

Also, in the shifts for props->max_mr = (1 << dev->xdev->caps.log_max_mkey) - 1
and max_tso = dev->xdev->caps.log_max_tso ? (1 << dev->xdev->caps.log_max_tso) : 0
below, can the 32-bit signed literal 1 overflow if log_max_mkey or
log_max_tso is >= 31, instead of using BIT_ULL()?

[ ... ]

> +	if (offsetofend(typeof(resp), tso_caps) <= udata_outlen) {
> +		max_tso = dev->xdev->caps.log_max_tso ? (1 << dev->xdev->caps.log_max_tso) : 0;
> +		if (max_tso) {
> +			resp.tso_caps.max_tso = max_tso;
> +			resp.tso_caps.supported_qpts |= 1 << IB_QPT_RAW_PACKET;
> +			resp.response_length += sizeof(resp.tso_caps);
> +		}
> +	}
> +
> +	if (offsetofend(typeof(resp), rss_caps) <= udata_outlen) {
> +		resp.rss_caps.rx_hash_function = XSC_RX_HASH_FUNC_TOEPLITZ;
> +		resp.rss_caps.rx_hash_fields_mask =
> +			XSC_RX_HASH_SRC_IPV4 |
> +			XSC_RX_HASH_DST_IPV4 |
> +			XSC_RX_HASH_SRC_IPV6 |
> +			XSC_RX_HASH_DST_IPV6 |
> +			XSC_RX_HASH_SRC_PORT_TCP |
> +			XSC_RX_HASH_DST_PORT_TCP |
> +			XSC_RX_HASH_SRC_PORT_UDP |
> +			XSC_RX_HASH_DST_PORT_UDP |
> +			XSC_RX_HASH_INNER;
> +		resp.response_length += sizeof(resp.rss_caps);
> +	}
> +
> +	if (offsetofend(typeof(resp), packet_pacing_caps) <= udata_outlen) {
> +		resp.packet_pacing_caps.qp_rate_limit_max =
> +			dev->xdev->caps.qp_rate_limit_max;
> +		resp.packet_pacing_caps.qp_rate_limit_min =
> +			dev->xdev->caps.qp_rate_limit_min;
> +		resp.packet_pacing_caps.supported_qpts |= 1 << IB_QPT_RAW_PACKET;
> +
> +		resp.response_length += sizeof(resp.packet_pacing_caps);
> +	}
> +
> +	if (udata_outlen) {
> +		err = ib_copy_to_udata(udata, &resp, resp.response_length);

[Severity: High]
Does xsc_ib_query_device() miscalculate resp.response_length and truncate
the response copied to userspace?

In struct xsc_ib_query_device_resp:

include/uapi/rdma/xsc-abi.h:
    struct xsc_ib_query_device_resp {
        __u32   comp_mask;
        __u32   response_length;
        struct  xsc_ib_tso_caps tso_caps;
        struct  xsc_ib_rss_caps rss_caps;
        struct  xsc_ib_cqe_comp_caps cqe_comp_caps;
        struct  xsc_packet_pacing_caps packet_pacing_caps;
        ...
    };

First, when dev->xdev->caps.log_max_tso is 0, sizeof(resp.tso_caps) is not
added to resp.response_length, even though rss_caps and packet_pacing_caps
sit at fixed offsets after tso_caps.

Second, cqe_comp_caps sits between rss_caps and packet_pacing_caps, but
sizeof(resp.cqe_comp_caps) is never added to resp.response_length.

When udata_outlen is large enough to include packet_pacing_caps (56 bytes),
resp.response_length only reaches 48 (or 40 when max_tso is 0), so
ib_copy_to_udata(udata, &resp, resp.response_length) fails to copy the full
packet_pacing_caps (and truncates rss_caps when max_tso is 0). Should each
block set resp.response_length = offsetofend(typeof(resp), <field>) instead
of incrementing it by sizeof()?

[ ... ]

> +static void xsc_calc_link_info(struct xsc_core_device *xdev,
> +			       struct ib_port_attr *props)
> +{
> +	switch (xsc_get_link_speed(xdev)) {
> +	case XSC_MODULE_SPEED_10G:
> +		props->active_speed = XSC_RDMA_LINK_SPEED_10GB;
> +		props->active_width = 1;
> +		break;
> +	case XSC_MODULE_SPEED_25G:
> +		props->active_speed = XSC_RDMA_LINK_SPEED_25GB;
> +		props->active_width = 1;
> +		break;
> +	case XSC_MODULE_SPEED_40G_R4:
> +		props->active_speed = XSC_RDMA_LINK_SPEED_10GB;
> +		props->active_width = 2;
> +		break;
> +	case XSC_MODULE_SPEED_50G_R:
> +		props->active_speed = XSC_RDMA_LINK_SPEED_50GB;
> +		props->active_width = 1;
> +		break;
> +	case XSC_MODULE_SPEED_50G_R2:
> +		props->active_speed = XSC_RDMA_LINK_SPEED_50GB;
> +		props->active_width = 1;
> +		break;
> +	case XSC_MODULE_SPEED_100G_R2:
> +		props->active_speed = XSC_RDMA_LINK_SPEED_25GB;
> +		props->active_width = 2;
> +		break;

[ ... ]

> +int xsc_ib_query_port(struct ib_device *ibdev, u32 port,
> +		      struct ib_port_attr *props)
> +{
> +	struct xsc_ib_dev *dev = to_xdev(ibdev);
> +	struct xsc_core_device *xdev = dev->xdev;
> +	struct net_device *ndev;
> +
> +	if (port < 1 || port > xdev->caps.num_ports) {
> +		ibdev_warn(&dev->ib_dev, "invalid port number %d\n", port);
> +		return -EINVAL;
> +	}
> +
> +	ndev = ib_device_get_netdev(ibdev, port);
> +	if (!ndev)
> +		return -ENODEV;
> +
> +	memset(props, 0, sizeof(*props));
> +
> +	props->state = ib_get_curr_port_state(ndev);
> +
> +	props->active_mtu = iboe_get_mtu(ndev->mtu);
> +	props->gid_tbl_len = XSC_IB_GID_TBL_LEN;

[Severity: Medium]
In xsc_ib_query_port(), props is zeroed with memset() and props->active_mtu
is populated, but props->max_mtu is never set. Doesn't leaving
props->max_mtu as 0 cause ib_mtu_enum_to_int(props->max_mtu) to return -1
and break callers and ULPs that validate path_mtu <= max_mtu?

Additionally, in xsc_calc_link_info() and xsc_ib_query_port(), why are
driver-private UAPI enums (XSC_RDMA_LINK_SPEED_*, XSC_RDMA_PHY_STATE_*) and
raw width numbers used instead of the standard enum ib_port_speed, enum
ib_port_width, and enum ib_port_phys_state constants? In particular, for
XSC_MODULE_SPEED_50G_R2 (2x25G) it sets speed 50G with width 1 (1x50G), and
for XSC_MODULE_SPEED_100G_R2 (2x50G) it sets speed 25G with width 2
(IB_WIDTH_4X, i.e., 4x25G instead of IB_WIDTH_2X = 16 with 50G).

[ ... ]

> +int xsc_ib_alloc_ucontext(struct ib_ucontext *uctx,
> +			  struct ib_udata *udata)
> +{
> +	struct ib_device *ibdev = uctx->device;
> +	struct xsc_ib_dev *dev = to_xdev(ibdev);
> +	struct xsc_ib_alloc_ucontext_req req = {};
> +	struct xsc_ib_alloc_ucontext_resp resp;
> +	int err;
> +
> +	if (!dev->ib_active)
> +		return -EAGAIN;
> +
> +	err = ib_copy_from_udata(&req, udata, min(udata->inlen, sizeof(req)));
> +	if (err)
> +		return err;

[ ... ]

> +static int xsc_ib_mmap(struct ib_ucontext *ibcontext, struct vm_area_struct *vma)
> +{
> +	struct xsc_ib_dev *dev = to_xdev(ibcontext->device);
> +	struct xsc_core_device *xdev = dev->xdev;
> +	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
> +	resource_size_t reg_base;
> +	resource_size_t reg_size = PAGE_SIZE;
> +	resource_size_t bar_end;
> +	bool db_arm = false;
> +	u64 addr;
> +	u64 tx_db = 0;
> +	u64 rx_db = 0;
> +	u64 cq_db = 0;
> +	u64 cq_reg = 0;
> +
> +	xsc_get_db_addr(xdev, &tx_db, &rx_db, &cq_db, &cq_reg, NULL);
> +	dev_dbg(&xdev->pdev->dev, "offset:0x%lx\n", offset);
> +
> +	if (offset == (tx_db & PAGE_MASK)) {
> +		addr = tx_db;
> +		db_arm = true;
> +	} else if (offset == (rx_db & PAGE_MASK)) {
> +		addr = rx_db;
> +		db_arm = true;
> +	} else if (offset == (cq_reg & PAGE_MASK)) {
> +		addr = cq_reg;
> +		db_arm = true;
> +	} else if (offset == (cq_db & PAGE_MASK)) {
> +		addr = cq_db;
> +		db_arm = true;
> +	} else if (offset == (QPM_TX_MDB_BASE_REG_ADDR & PAGE_MASK)) {
> +		addr = QPM_TX_MDB_BASE_REG_ADDR;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * On an uncompressed PF the doorbell addresses are hardware
> +	 * addresses (BAR offset + 0xa0000000 window base); the mdb values
> +	 * are BAR offsets already.
> +	 */
> +	if (db_arm && xsc_core_is_pf(xdev) && !is_pf_bar_compressed(xdev))
> +		addr -= 0xA0000000;
> +
> +	reg_base = pci_resource_start(xdev->pdev, xdev->bar_num) + (addr & PAGE_MASK);
> +	bar_end = pci_resource_start(xdev->pdev, xdev->bar_num) +
> +		  pci_resource_len(xdev->pdev, xdev->bar_num);
> +	dev_dbg(&xdev->pdev->dev, "regbase:0x%llx", reg_base);
> +
> +	if (vma->vm_end - vma->vm_start != reg_size)
> +		return -EINVAL;
> +
> +	if (reg_base + reg_size > bar_end)
> +		return -EINVAL;
> +
> +	return rdma_user_mmap_io(ibcontext, vma, reg_base >> PAGE_SHIFT,
> +				 reg_size, pgprot_noncached(vma->vm_page_prot),
> +				 NULL);
> +}

[Severity: Critical]
Does xsc_ib_mmap() expose the privileged BAR0 firmware command queue
registers to unprivileged userspace?

In drivers/net/ethernet/yunsilicon/xsc/common/xsc_reg.h, the firmware
command queue registers and EQ doorbell sit in the first 4KB page of the
BAR alongside the compressed doorbells and QPM_TX_MDB_BASE_REG_ADDR:

    #define CMDQM_HOST_REQ_BUF_BASE_H_ADDR_MEM_ADDR 0x0
    #define CMDQM_HOST_REQ_BUF_BASE_L_ADDR_MEM_ADDR 0x4
    #define CMDQM_HOST_REQ_PID_MEM_ADDR 0x8
    #define CMDQM_HOST_REQ_CID_MEM_ADDR 0xc
    #define CMDQM_HOST_RSP_BUF_BASE_H_ADDR_MEM_ADDR 0x10
    #define CMDQM_HOST_RSP_BUF_BASE_L_ADDR_MEM_ADDR 0x14
    #define CMDQM_HOST_RSP_PID_MEM_ADDR 0x18
    #define CMDQM_HOST_RSP_CID_MEM_ADDR 0x1c
    #define CMDQM_HOST_VF_ERR_STS_MEM_ADDR 0x20
    #define CMDQM_VECTOR_ID_MEM_ADDR 0x24
    ...
    #define DB_CQ_FUNC_MEM_ADDR 0x8c4
    #define DB_EQ_FUNC_MEM_ADDR 0x8c8
    #define DB_CQ_CID_DIRECT_MEM_ADDR 0x8cc
    #define TX_DB_FUNC_MEM_ADDR 0x8d0
    #define RX_DB_FUNC_MEM_ADDR 0x8d4
    ...
    #define QPM_TX_MDB_BASE_REG_ADDR 0xd00

Because xsc_ib_mmap() accepts offset == (QPM_TX_MDB_BASE_REG_ADDR & PAGE_MASK)
(which is 0x0 on all functions, without even checking
is_support_multi_doorbell(xdev)), and on compressed-BAR PFs and VFs tx_db,
rx_db, cq_db, and cq_reg are also at 0x8c4..0x8d4, any unprivileged user
with access to /dev/infiniband/uverbsX can mmap BAR offset 0 writable via
rdma_user_mmap_io() and overwrite the command queue DMA ring base physical
addresses and producer/consumer indices.

Also, in xsc_ib_mmap(), computing offset = vma->vm_pgoff << PAGE_SHIFT
truncates the upper PAGE_SHIFT bits of vma->vm_pgoff.

[ ... ]

> +static void init_iommu_state(struct xsc_ib_dev *dev, struct device *dma_dev)
> +{
> +	struct iommu_domain *domain;
> +
> +	dev->iommu_state = XSC_IB_IOMMU_MAP_DISABLE;
> +	domain = iommu_get_domain_for_dev(dma_dev);
> +	if (domain) {
> +		if (domain->type & __IOMMU_DOMAIN_DMA_API)
> +			dev->iommu_state = XSC_IB_IOMMU_MAP_NORMAL;
> +		else
> +			dev->iommu_state = XSC_IB_IOMMU_MAP_UNKNOWN_DOMAIN;
> +	} else {

[Severity: High]
In init_iommu_state(), when the system runs with IOMMU passthrough enabled
(such as iommu.passthrough=1 or iommu=pt), iommu_get_domain_for_dev()
returns an identity domain whose type is IOMMU_DOMAIN_IDENTITY
(__IOMMU_DOMAIN_PT), which does not have __IOMMU_DOMAIN_DMA_API set. Doesn't
this misclassify identity-mapped devices as XSC_IB_IOMMU_MAP_UNKNOWN_DOMAIN
instead of treating them as direct-mapped?

[ ... ]

> +static int init_one(struct xsc_core_device *xdev,
> +		    struct xsc_ib_dev **ibdev)
> +{
> +	struct xsc_ib_dev *dev;
> +	int err;
> +
> +	dev = (struct xsc_ib_dev *)ib_alloc_device(xsc_ib_dev, ib_dev);
> +	if (!dev)
> +		return -ENOMEM;
> +
> +	dev->xdev = xdev;
> +	strscpy(dev->ib_dev.name, "xscale_%d", IB_DEVICE_NAME_MAX);
> +
> +	memcpy(dev->xdev->priv.ibdev_name, dev->ib_dev.name, IB_DEVICE_NAME_MAX);
> +
> +	dev->num_comp_vectors = xdev->dev_res->eq_table.num_comp_vectors;
> +
> +	dev->ib_dev.node_type		= RDMA_NODE_IB_CA;
> +	dev->ib_dev.local_dma_lkey	= 0xFF;
> +	dev->num_ports		= xdev->caps.num_ports;
> +	dev->ib_dev.phys_port_cnt     = dev->num_ports;
> +	dev->ib_dev.num_comp_vectors	= dev->num_comp_vectors;
> +	dev->ib_dev.dev.parent = &xdev->pdev->dev;
> +	err = ib_device_set_netdev(&dev->ib_dev, xdev->netdev, 1);
> +	if (err)
> +		goto err_free;

[Severity: High]
How is the lifetime of xdev->netdev synchronized between the ethernet driver
and xsc_ib?

First, in init_one(), ib_device_set_netdev(&dev->ib_dev, xdev->netdev, 1)
is called before init_node_data(dev) checks whether dev->xdev->netdev is
NULL to return -EPROBE_DEFER. If xdev->netdev is NULL when
ib_device_set_netdev() runs and xsc_eth_probe() assigns xdev->netdev before
init_node_data() runs, ib_device_set_netdev() binds NULL while
init_node_data() succeeds, causing ib_register_device() ->
xsc_port_immutable() -> ib_query_port() -> xsc_ib_query_port() to fail probe
with -ENODEV instead of deferring.

Second, in xsc_eth_remove():

drivers/net/ethernet/yunsilicon/xsc/net/main.c:xsc_eth_remove() {
    ...
    unregister_netdev(adapter->netdev);
    ...
    free_netdev(adapter->netdev);

    xdev->netdev = NULL;
    ...
}

Because ib_device_set_netdev() takes a netdev_hold() reference on
xdev->netdev and xsc_ib never registers a netdevice notifier to clear the
netdev on NETDEV_UNREGISTER, won't unbinding xsc_pci.eth or unloading
xsc_eth while xsc_ib is loaded hang indefinitely in unregister_netdev()
waiting for xsc_ib's reference to be released? And if xsc_ib_probe() runs
concurrently with xsc_eth_remove(), can init_one() and init_node_data()
access an unregistering or already-freed net_device before xdev->netdev is
set to NULL?

> +
> +	xsc_ib_dev_setting(dev);

[ ... ]

> +	err = init_node_data(dev);
> +	if (err)
> +		goto err_free;
> +
> +	init_iommu_state(dev, xdev->device);
> +	err = ib_register_device(&dev->ib_dev, dev->ib_dev.name,
> +				 dev->xdev->device);
> +	if (err)
> +		goto err_free;
> +
> +	rdma_roce_rescan_device(&dev->ib_dev);
> +	dev->ib_active = true;
> +	*ibdev = dev;

[Severity: Medium]
In init_one(), dev->ib_active = true is set only after ib_register_device()
and a redundant rdma_roce_rescan_device() have already completed. Since
ib_register_device() exposes the uverbs device to userspace and sends device
addition events before returning, can a userspace process open the device
immediately and hit:

drivers/infiniband/hw/xsc/main.c:xsc_ib_alloc_ucontext() {
    if (!dev->ib_active)
        return -EAGAIN;
}

Also, earlier in init_one(), memcpy(dev->xdev->priv.ibdev_name,
dev->ib_dev.name, IB_DEVICE_NAME_MAX) runs before ib_register_device()
expands the format string, leaving the literal "xscale_%d" in
dev->xdev->priv.ibdev_name instead of the assigned device name.

> diff --git a/drivers/infiniband/hw/xsc/xsc_ib.h b/drivers/infiniband/hw/xsc/xsc_ib.h
> new file mode 100644
> index 0000000000000..b91d4aa371bcf
> --- /dev/null
> +++ b/drivers/infiniband/hw/xsc/xsc_ib.h

[ ... ]

> +#define IB_MTU_8192	6
> +
> +static inline enum ib_mtu xsc_net_to_ib_mtu(unsigned int mtu)
> +{
> +	mtu = mtu - (IB_GRH_BYTES + IB_UDP_BYTES + IB_BTH_BYTES +
> +		     IB_EXT_XRC_BYTES + IB_EXT_ATOMICETH_BYTES +
> +		     IB_ICRC_BYTES);
> +	if (mtu >= 8192)
> +		return IB_MTU_8192;
> +	else if (mtu >= ib_mtu_enum_to_int(IB_MTU_4096))
> +		return IB_MTU_4096;
> +	else if (mtu >= ib_mtu_enum_to_int(IB_MTU_1024))
> +		return IB_MTU_1024;
> +	else
> +		return 0;
> +}

[Severity: Medium]
In xsc_net_to_ib_mtu(), mtu is an unsigned int, so if mtu is less than the
96-byte header overhead, won't the subtraction underflow to a huge unsigned
value and return IB_MTU_8192 (which is also outside enum ib_mtu)?

> +
> +#define XSC_IB_IOMMU_MAP_DISABLE 0
> +#define XSC_IB_IOMMU_MAP_UNKNOWN_DOMAIN 1
> +#define XSC_IB_IOMMU_MAP_NORMAL 2
> +
> +static inline int xsc_ib_iommu_dma_map(struct ib_device *ibdev)
> +{
> +	return to_xdev(ibdev)->iommu_state;
> +}
> +
> +static inline void *xsc_ib_iova_to_virt(struct ib_device *ibdev, dma_addr_t iova)
> +{
> +	phys_addr_t phyaddr;
> +	struct iommu_domain *domain;
> +
> +	domain = iommu_get_domain_for_dev(ibdev->dma_device);
> +	if (likely(domain)) {
> +		phyaddr = iommu_iova_to_phys(domain, iova);
> +		phyaddr |= iova & (PAGE_SIZE - 1);
> +	} else {
> +		phyaddr = dma_to_phys(ibdev->dma_device, iova);
> +	}
> +
> +	return phys_to_virt(phyaddr);
> +}

[Severity: High]
In xsc_ib_iova_to_virt(), iommu_iova_to_phys(domain, iova) already preserves
the sub-granule offset in the returned physical address. On architectures
where the CPU PAGE_SIZE is larger than the IOMMU page granule (for example,
64KB CPU pages with a 4KB IOMMU granule), IOVA bits between the IOMMU page
shift and CPU PAGE_SHIFT do not match the physical page offset. Doesn't
ORing iova & (PAGE_SIZE - 1) into phyaddr corrupt those physical address
bits before calling phys_to_virt()? Also, what happens if
iommu_iova_to_phys() returns 0 on an unmapped IOVA?

> diff --git a/drivers/net/ethernet/yunsilicon/xsc/common/driver.h b/drivers/net/ethernet/yunsilicon/xsc/common/driver.h
> new file mode 100644
> index 0000000000000..362099a7c0918
> --- /dev/null
> +++ b/drivers/net/ethernet/yunsilicon/xsc/common/driver.h

[ ... ]

> +static inline void *xsc_vzalloc(unsigned long size)
> +{
> +	void *rtn;
> +
> +	rtn = kzalloc(size, GFP_KERNEL | __GFP_NOWARN);
> +	if (!rtn)
> +		rtn = vzalloc(size);
> +	return rtn;
> +}
> +
> +static inline void xsc_vfree(const void *addr)
> +{
> +	if (addr && is_vmalloc_addr(addr))
> +		vfree(addr);
> +	else
> +		kfree(addr);
> +}

[Severity: Low]
This isn't a bug, but xsc_vzalloc() and xsc_vfree() duplicate the standard
kvzalloc() and kvfree() helpers. Similarly, in drivers/infiniband/hw/xsc/main.c,
xsc_get_guid() duplicates addrconf_addr_eui48(), xsc_ib_dev_setting() sets
dev->ib_dev.ops fields individually instead of using a static const struct
ib_device_ops with ib_set_device_ops(), xsc_ib_init()/xsc_ib_cleanup() can
use module_auxiliary_driver(), and dev_dbg() in xsc_ib_mmap() prints
resource_size_t reg_base with 0x%llx instead of %pa.

> diff --git a/drivers/net/ethernet/yunsilicon/xsc/pci/link.c b/drivers/net/ethernet/yunsilicon/xsc/pci/link.c
> new file mode 100644
> index 0000000000000..272ddc3a29b10
> --- /dev/null
> +++ b/drivers/net/ethernet/yunsilicon/xsc/pci/link.c
> @@ -0,0 +1,34 @@

[ ... ]

> +int xsc_get_link_speed(struct xsc_core_device *xdev)
> +{
> +	struct xsc_event_query_linkinfo_mbox_out out;
> +	struct xsc_event_linkinfo linkinfo;
> +	struct xsc_inbox_hdr in;
> +	int err;
> +
> +	memset(&in, 0, sizeof(in));
> +	memset(&out, 0, sizeof(out));
> +	in.opcode = cpu_to_be16(XSC_CMD_OP_QUERY_LINK_INFO);
> +
> +	err = xsc_cmd_exec(xdev, &in, sizeof(in), &out, sizeof(out));
> +	if (err) {
> +		pci_err(xdev->pdev, "failed to get link info, err=%d\n", err);
> +		return XSC_MODULE_SPEED_25G;
> +	}
> +
> +	linkinfo = out.ctx;
> +	linkinfo.linkspeed = be32_to_cpu(linkinfo.linkspeed);
> +
> +	return linkinfo.linkspeed;
> +}

[Severity: Low]
In struct xsc_event_linkinfo, linkspeed is declared as __be32. Doesn't
assigning be32_to_cpu(linkinfo.linkspeed) back to linkinfo.linkspeed trigger
a sparse endianness warning instead of returning
be32_to_cpu(out.ctx.linkspeed) directly?

> diff --git a/include/uapi/rdma/xsc-abi.h b/include/uapi/rdma/xsc-abi.h
> new file mode 100644
> index 0000000000000..7e19b23a3842b
> --- /dev/null
> +++ b/include/uapi/rdma/xsc-abi.h

[ ... ]

> +struct xsc_ib_alloc_ucontext_req {
> +	__u32	abi_ver;
> +	__u32	rsvd1;
> +};

[ ... ]

> +struct xsc_ib_create_qp {
> +	__u64	buf_addr;
> +	__u64	db_addr;
> +	__u32	sq_wqe_count;
> +	__u32	rq_wqe_count;
> +	__u32	rq_wqe_shift;
> +	__u32	flags;
> +
> +	int	dmabuf_fd;
> +	__aligned_u64	dmabuf_sz;
> +};

[ ... ]

> +struct xsc_ib_create_cq {
> +	__u64 buf_addr;
> +	__u64 db_addr;
> +	__u32	cqe_size;
> +
> +	int	dmabuf_fd;
> +	__aligned_u64	dmabuf_sz;
> +};

[ ... ]

> +struct xsc_ib_alloc_pd_resp {
> +	__u32	pdn;
> +};

[Severity: Medium]
In include/uapi/rdma/xsc-abi.h, struct xsc_ib_create_qp has an unnamed
4-byte compiler padding hole between int dmabuf_fd and __aligned_u64
dmabuf_sz, both struct xsc_ib_create_qp and struct xsc_ib_create_cq use raw
int instead of __s32 for dmabuf_fd and __u64 instead of __aligned_u64 for
the 64-bit fields, and struct xsc_ib_alloc_pd_resp is not padded to an
8-byte boundary with a trailing __u32 reserved field.

Also, in xsc_ib_alloc_ucontext(), should req.rsvd1 and any trailing bytes
beyond sizeof(req) in udata be checked for zero (e.g., via
ib_is_udata_cleared()) so future ABI extensions can rely on them?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261104155650.37053-1-tianx@yunsilicon.com?part=10

  reply	other threads:[~2026-11-05 16:09 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-04 15:57 [PATCH v1 00/15] Yunsilicon XSC ethernet and RDMA drivers Xin Tian
2026-11-04 15:56 ` [PATCH v1 01/15] MAINTAINERS: add the Yunsilicon XSC drivers Xin Tian
2026-11-04 15:56 ` [PATCH v1 02/15] net/xsc: add PCI core driver skeleton Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:56 ` [PATCH v1 03/15] net/xsc: add the register HAL Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 04/15] net/xsc: add the command queue Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 05/15] net/xsc: add event queues, interrupts and the Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 06/15] net/xsc: add the CQ and QP resource pools Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 07/15] net/xsc: add ethernet netdev registration Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 08/15] net/xsc: add ethernet TX path Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 09/15] net/xsc: add ethernet RX path and statistics Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 10/15] RDMA/xsc: add RDMA device support Xin Tian
2026-11-05 16:09   ` sashiko-bot [this message]
2026-11-04 15:57 ` [PATCH v1 11/15] RDMA/xsc: add RDMA PD/MR Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 12/15] RDMA/xsc: add RDMA CQ/SRQ/AH Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 13/15] RDMA/xsc: add QP verbs Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 14/15] RDMA/xsc: add the RDMA send path Xin Tian
2026-11-05 16:09   ` sashiko-bot
2026-11-04 15:57 ` [PATCH v1 15/15] RDMA/xsc: add RDMA counters and sysfs Xin Tian
2026-11-05 16:09   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-199432@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=tianx@yunsilicon.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox