On Mon, 2023-10-09 at 10:54 +0800, Matt Johnston wrote: > +static int mctp_i3c_setup(struct mctp_i3c_device *mi) > +{ > + bool ibi_set = false, ibi_enabled = false; > + const struct i3c_ibi_setup ibi = { > + .max_payload_len = 1, > + .num_slots = MCTP_I3C_IBI_SLOTS, > + .handler = mctp_i3c_ibi_handler, > + }; > + struct i3c_device_info info; > + int rc; > + > + i3c_device_get_info(mi->i3c, &info); > + mi->have_mdb = info.bcr & BIT(2); > + mi->addr = info.dyn_addr; > + mi->mwl = info.max_write_len; > + mi->mrl = info.max_read_len; > + mi->pid = info.pid; > + > + rc = i3c_device_request_ibi(mi->i3c, &ibi); > + if (rc == 0) { > + ibi_set = true; > + } else if (rc == -ENOTSUPP) { > + /* This driver only supports In-Band Interrupt mode > + * (ENOTSUPP is from the i3c layer, not EOPNOTSUPP). > + * Support for Polling Mode could be added if required. > + */ > + dev_warn(i3cdev_to_dev(mi->i3c), "Failed, bus driver doesn't support In-Band Interrupts"); > + goto err; > + } else { > + dev_err(i3cdev_to_dev(mi->i3c), > + "Failed requesting IBI (%d)\n", rc); > + goto err; > + } > + > + if (ibi_set) { > + /* Device setup must be complete when IBI is enabled */ > + rc = i3c_device_enable_ibi(mi->i3c); > + if (rc < 0) { > + /* Assume a driver supporting request_ibi also > + * supports enable_ibi. > + */ > + dev_err(i3cdev_to_dev(mi->i3c), > + "Failed enabling IBI (%d)\n", rc); > + goto err; > + } > + ibi_enabled = true; > + } > + > + return 0; > +err: > + if (ibi_enabled) > + i3c_device_disable_ibi(mi->i3c); Apparently no error code path can reach here with 'ibi_enabled == true', if so please drop the above lines. > + if (ibi_set) > + i3c_device_free_ibi(mi->i3c); > + return rc; > +} > + > +/* Adds a new MCTP i3c_device to a bus */ > +static int mctp_i3c_add_device(struct mctp_i3c_bus *mbus, > + struct i3c_device *i3c) > +__must_hold(&busdevs_lock) > +{ > + struct mctp_i3c_device *mi = NULL; > + int rc; > + > + mi = kzalloc(sizeof(*mi), GFP_KERNEL); > + if (!mi) { > + rc = -ENOMEM; > + goto err; > + } > + mi->mbus = mbus; > + mi->i3c = i3c; > + mutex_init(&mi->lock); > + list_add(&mi->list, &mbus->devs); > + > + i3cdev_set_drvdata(i3c, mi); > + rc = mctp_i3c_setup(mi); > + if (rc < 0) > + goto err; You can make the code simpler with: goto free; > + > + return 0; and here: free: list_del(&mi->list); kfree(mi); err: dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc); return rc; > +err: > + dev_warn(i3cdev_to_dev(i3c), "Error adding mctp-i3c device, %d\n", rc); > + if (mi) { > + list_del(&mi->list); > + kfree(mi); > + } > + return rc; > +} > + > +static int mctp_i3c_probe(struct i3c_device *i3c) > +{ > + struct mctp_i3c_bus *b = NULL, *mbus = NULL; > + int rc; > + > + /* Look for a known bus */ > + mutex_lock(&busdevs_lock); > + list_for_each_entry(b, &busdevs, list) > + if (b->bus == i3c->bus) { > + mbus = b; > + break; > + } > + mutex_unlock(&busdevs_lock); > + > + if (!mbus) { > + /* probably no "mctp-controller" property on the i3c bus */ > + return -ENODEV; > + } > + > + rc = mctp_i3c_add_device(mbus, i3c); > + if (!rc) > + goto err; This is confusing: if 'rc' is zero (!rc) the function will return 0 ('return rc' later), and otherwise it will return zero again. Either ignore the return code, or more likely the error path need some change. > +static void mctp_i3c_xmit(struct mctp_i3c_bus *mbus, struct sk_buff *skb) > +{ > + struct net_device_stats *stats = &mbus->ndev->stats; > + struct i3c_priv_xfer xfer = { .rnw = false }; > + struct mctp_i3c_internal_hdr *ihdr = NULL; > + struct mctp_i3c_device *mi = NULL; > + unsigned int data_len; > + u8 *data = NULL; > + u8 addr, pec; > + int rc = 0; > + u64 pid; > + > + skb_pull(skb, sizeof(struct mctp_i3c_internal_hdr)); > + data_len = skb->len; > + > + ihdr = (void *)skb_mac_header(skb); > + > + pid = get_unaligned_be48(ihdr->dest); > + mi = mctp_i3c_lookup(mbus, pid); > + if (!mi) { > + /* I3C endpoint went away after the packet was enqueued? */ > + stats->tx_dropped++; > + goto out; > + } > + > + if (WARN_ON_ONCE(data_len + 1 > MCTP_I3C_MAXBUF)) > + goto out; > + > + if (data_len + 1 > (unsigned int)mi->mwl) { > + /* Route MTU was larger than supported by the endpoint */ > + stats->tx_dropped++; > + goto out; > + } > + > + /* Need a linear buffer with space for the PEC */ > + xfer.len = data_len + 1; > + if (skb_tailroom(skb) >= 1) { > + skb_put(skb, 1); > + data = skb->data; > + } else { > + // TODO: test this I hope this comment is a left over? In any case please avoid c++ style comments. > + /* Otherwise need to copy the buffer */ > + skb_copy_bits(skb, 0, mbus->tx_scratch, skb->len); > + data = mbus->tx_scratch; > + } > + > + /* PEC calculation */ > + addr = mi->addr << 1; > + pec = i2c_smbus_pec(0, &addr, 1); > + pec = i2c_smbus_pec(pec, data, data_len); > + data[data_len] = pec; > + > + xfer.data.out = data; > + rc = i3c_device_do_priv_xfers(mi->i3c, &xfer, 1); > + if (rc == 0) { > + stats->tx_bytes += data_len; > + stats->tx_packets++; > + } else { > + stats->tx_errors++; > + } > + > +out: > + if (mi) > + mutex_unlock(&mi->lock); > +} > + > +static int mctp_i3c_tx_thread(void *data) > +{ > + struct mctp_i3c_bus *mbus = data; > + struct sk_buff *skb; > + unsigned long flags; > + > + for (;;) { > + if (kthread_should_stop()) > + break; > + > + spin_lock_irqsave(&mbus->tx_lock, flags); > + skb = mbus->tx_skb; > + mbus->tx_skb = NULL; > + spin_unlock_irqrestore(&mbus->tx_lock, flags); > + > + if (netif_queue_stopped(mbus->ndev)) > + netif_wake_queue(mbus->ndev); > + > + if (skb) { > + mctp_i3c_xmit(mbus, skb); > + kfree_skb(skb); > + } else { > + wait_event_idle(mbus->tx_wq, > + mbus->tx_skb || kthread_should_stop()); > + } > + } > + > + return 0; > +} > + > +static netdev_tx_t mctp_i3c_start_xmit(struct sk_buff *skb, > + struct net_device *ndev) > +{ > + struct mctp_i3c_bus *mbus = netdev_priv(ndev); > + unsigned long flags; > + netdev_tx_t ret; > + > + spin_lock_irqsave(&mbus->tx_lock, flags); Why are you using the _irqsave variant? The only other path acquiring this lock is mctp_i3c_tx_thread(), in process context, while here we have BH disabled. Plain spin_lock should suffice here, paired with _BH variant in mctp_i3c_tx_thread. > + netif_stop_queue(ndev); > + if (mbus->tx_skb) { > + dev_warn_ratelimited(&ndev->dev, "TX with queue stopped"); > + ret = NETDEV_TX_BUSY; > + } else { > + mbus->tx_skb = skb; > + ret = NETDEV_TX_OK; > + } > + spin_unlock_irqrestore(&mbus->tx_lock, flags); > + > + if (ret == NETDEV_TX_OK) > + wake_up(&mbus->tx_wq); > + > + return ret; > +} [...] > +/* Returns an ERR_PTR on failure */ > +static struct mctp_i3c_bus *mctp_i3c_bus_add(struct i3c_bus *bus) > +__must_hold(&busdevs_lock) > +{ > + struct mctp_i3c_bus *mbus = NULL; > + struct net_device *ndev = NULL; > + char namebuf[IFNAMSIZ]; > + u8 addr[PID_SIZE]; > + int rc; > + > + if (!mctp_i3c_is_mctp_controller(bus)) > + return ERR_PTR(-ENOENT); > + > + snprintf(namebuf, sizeof(namebuf), "mctpi3c%d", bus->id); > + ndev = alloc_netdev(sizeof(*mbus), namebuf, NET_NAME_ENUM, > + mctp_i3c_net_setup); > + if (!ndev) { > + rc = -ENOMEM; > + goto err; > + } > + > + mbus = netdev_priv(ndev); > + mbus->ndev = ndev; > + mbus->bus = bus; > + INIT_LIST_HEAD(&mbus->devs); > + list_add(&mbus->list, &busdevs); > + > + rc = mctp_i3c_bus_local_pid(bus, &mbus->pid); > + if (rc < 0) { > + dev_err(&ndev->dev, "No I3C PID available\n"); > + goto err; > + } > + put_unaligned_be48(mbus->pid, addr); > + dev_addr_set(ndev, addr); > + > + init_waitqueue_head(&mbus->tx_wq); > + spin_lock_init(&mbus->tx_lock); > + mbus->tx_thread = kthread_run(mctp_i3c_tx_thread, mbus, > + "%s/tx", ndev->name); > + if (IS_ERR(mbus->tx_thread)) { > + dev_warn(&ndev->dev, "Error creating thread: %pe\n", > + mbus->tx_thread); > + rc = PTR_ERR(mbus->tx_thread); > + mbus->tx_thread = NULL; > + goto err; > + } > + > + rc = mctp_register_netdev(ndev, NULL); > + if (rc < 0) { > + dev_warn(&ndev->dev, "netdev register failed: %d\n", rc); > + goto err; > + } > + return mbus; > +err: > + /* uninit will not get called if a netdev has not been registered, > + * so we perform the same mbus cleanup manually. > + */ > + if (mbus) > + mctp_i3c_bus_free(mbus); > + if (ndev) > + free_netdev(ndev); A more conventional way of handling the error paths would be using multiple labels, e.g.: err_free_bus: mctp_i3c_bus_free(mbus); err_free_ndev: free_netdev(ndev); err: > + return ERR_PTR(rc); > +} Cheers, Paolo