Re: [PATCH v3 3/7] net: wwan: t9xx: Add control DMA interface
From: Andrew Lunn
Date: Wed Jun 24 2026 - 12:16:14 EST
> diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma.c b/drivers/net/wwan/t9xx/pcie/mtk_cldma.c
> +static inline void mtk_cldma_clr_bd_dsc(struct cldma_drv_info *drv_info,
> + struct bd_dsc *bd_dsc_pool, int nr_bds)
No inline functions in C files. Please let the compiler decide.
> +static int mtk_cldma_reload_rx_skb(struct mtk_md_dev *mdev, struct rxq *rxq,
> + struct rx_req *req)
> +{
> + struct sk_buff *tail = NULL;
> + struct bd_dsc *bd_dsc;
> + int nr_bds;
> + int i, ret;
> +
> + nr_bds = rxq->nr_bds;
> +
> + for (i = 0; i < nr_bds; i++) {
> + bd_dsc = req->bd_dsc_pool + i;
> + bd_dsc->skb = __dev_alloc_skb(req->frag_size, GFP_KERNEL);
> + if (!bd_dsc->skb) {
> + dev_warn((mdev)->dev, "Failed to alloc SKB\n");
You might want to rate limit this, and the other similar messages in
the data path, otherwise it could be a DOS.
> +u32 mtk_cldma_stop_queue(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno)
> +{
> + u32 val = (qno == ALLQ) ? qno : BIT(qno);
> + struct cldma_hw_regs *hw_regs;
> + unsigned int active;
> + int cnt = 0;
> + int base;
> + u32 addr;
> +
> + hw_regs = drv_info->hw_regs;
> + base = drv_info->base_addr;
> +
> + if (dir == DIR_TX)
> + addr = base + hw_regs->reg_cldma_ul_stop_cmd;
> + else
> + addr = base + hw_regs->reg_cldma_so_stop_cmd;
> +
> + mtk_pci_write32(drv_info->mdev, addr, val);
> +
> + do {
> + active = drv_info->drv_ops->cldma_queue_status(drv_info, dir, qno);
> + if (active == LINK_ERROR_VAL || !active)
> + break;
> + usleep_range(WAIT_QUEUE_STOP, 2 * WAIT_QUEUE_STOP);
> + } while (++cnt < 10);
Please use one of the helpers from iopoll.h. Any loops waiting for an
event to happen should use those macros, since the open code
implementation is often wrong.
> +static int mtk_ctrl_trb_srv_init(struct mtk_ctrl_trans *trans)
> +{
> + struct srv_que *srv_que;
> + struct trb_srv *srv;
> + int i, j;
> + int ret;
> +
> + for (i = 0; i < trans->trb_srv_num; i++) {
> + srv = devm_kzalloc(trans->mdev->dev, sizeof(*srv), GFP_KERNEL);
> + if (!srv) {
> + ret = -ENOMEM;
> + goto err_free_srv;
> + }
> +
> + srv->trans = trans;
> + srv->srv_id = i;
> + trans->trb_srv[i] = srv;
> +
> + init_waitqueue_head(&srv->trb_waitq);
> + for (j = 0; j < NR_CLDMA; j++)
> + INIT_LIST_HEAD(&srv->srv_q_list[j]);
> + }
> +
> + for (i = 0; i < NR_CLDMA; i++)
> + for (j = 0; j < HW_QUE_NUM; j++) {
> + if (trans->srv_cfg[i][j] < 0 ||
> + trans->srv_cfg[i][j] >= trans->trb_srv_num)
> + trans->srv_cfg[i][j] = 0;
> + srv_que = devm_kzalloc(trans->mdev->dev, sizeof(*srv_que), GFP_KERNEL);
> + if (!srv_que) {
> + ret = -ENOMEM;
> + goto err_free_srv_que;
> + }
> + srv_que->hif_id = i;
> + srv_que->qno = j;
> + list_add_tail(&srv_que->list,
> + &trans->trb_srv[trans->srv_cfg[i][j]]->srv_q_list[i]);
> + }
> +
> + for (i = 0; i < trans->trb_srv_num; i++) {
> + trans->trb_srv[i]->trb_thread = kthread_run(mtk_ctrl_trb_thread, trans->trb_srv[i],
> + "mtk_trb_srv%d_%s", i,
> + trans->mdev->dev_str);
> + if (IS_ERR(trans->trb_srv[i]->trb_thread)) {
> + ret = PTR_ERR(trans->trb_srv[i]->trb_thread);
> + trans->trb_srv[i]->trb_thread = NULL;
> + goto err_stop_kthread;
> + }
> + }
> +
> + return 0;
> +err_stop_kthread:
> + while (--i >= 0)
> + kthread_stop(trans->trb_srv[i]->trb_thread);
> +err_free_srv_que:
> + for (i = 0; i < trans->trb_srv_num; i++) {
> + for (j = 0; j < NR_CLDMA; j++) {
> + struct srv_que *next_srv_que;
> +
> + list_for_each_entry_safe(srv_que, next_srv_que,
> + &trans->trb_srv[i]->srv_q_list[j], list) {
> + list_del(&srv_que->list);
> + devm_kfree(trans->mdev->dev, srv_que);
It is unusual to see devm_kfree(). Why is it needed?
> +static unsigned int ctrl_port_chl_mtu;
Is this a global variable? Why is it not part of priv?
> +module_param(ctrl_port_chl_mtu, uint, 0644);
> +MODULE_PARM_DESC(ctrl_port_chl_mtu, "This is used to config the ctrl port mtu!\n");
Ah. No modules parameters please. If this is an MTU, why not use the
normal networking interfaces to set the MTU?
Andrew