Re: [PATCH stable v1 05/13] net: ethernet: aquantia: Support for NIC-specific code
From: Lino Sanfilippo <LinoSanfilippo@gmx.de>
Date: 2017-01-29 15:15:03
On 29.01.2017 06:09, Alexander Loktionov wrote:
+
+static int aq_ndev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ struct aq_nic_s *aq_nic = netdev_priv(ndev);
+ int err = 0;
+
+ err = aq_nic_xmit(aq_nic, skb);Initialization of err is superfluous.
+ if (err < 0)
+ goto err_exit;
+
+err_exit:
+ return err;
+}
+
+static int aq_ndev_change_mtu(struct net_device *ndev, int new_mtu)
+{
+ struct aq_nic_s *aq_nic = netdev_priv(ndev);
+ int err = 0;
+
+ if (new_mtu == ndev->mtu) {This check is not needed. This function wont be called if mtu has not changed.
+
+static int aq_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ struct aq_hw_ops *aq_hw_ops = NULL;
+ struct aq_pci_func_s *aq_pci_func = NULL;
+ int err = 0;
+
+ err = pci_enable_device(pdev);
+ if (err < 0)
+ goto err_exit;
+ aq_hw_ops = aq_pci_probe_get_hw_ops_by_id(pdev);
+ aq_pci_func = aq_pci_func_alloc(aq_hw_ops, pdev,
+ &aq_ndev_ops, &aq_ethtool_ops);
+ if (!aq_pci_func) {pci_disable_device() is missing.
+
+static int __init aq_module_init(void)
+{
+ int err = 0;
+
+ err = pci_register_driver(&aq_pci_ops);
+ if (err < 0)
+ goto err_exit;
+
+err_exit:
+ return err;
+}
+
+static void __exit aq_module_exit(void)
+{
+ pci_unregister_driver(&aq_pci_ops);
+}This can be reduced to a single line: module_pci_driver(aq_pci_ops);
+
+static void aq_nic_service_timer_cb(unsigned long param)
+{
+ struct aq_nic_s *self = (struct aq_nic_s *)param;
+ struct net_device *ndev = aq_nic_get_ndev(self);
+ int err = 0;
+ bool is_busy = false;
+ unsigned int i = 0U;
+ struct aq_hw_link_status_s link_status;
+ struct aq_ring_stats_rx_s stats_rx;
+ struct aq_ring_stats_tx_s stats_tx;
+
+ atomic_inc(&self->header.busy_count);
+ is_busy = true;What is "is_busy" needed for? Furthermore busy_count seems to be meant for synchronization with the xmit function. Why do they have to be synchronized?
+ + ndev->stats.rx_packets = stats_rx.packets; + ndev->stats.rx_bytes = stats_rx.bytes; + ndev->stats.rx_errors = stats_rx.errors; + ndev->stats.tx_packets = stats_tx.packets; + ndev->stats.tx_bytes = stats_tx.bytes; + ndev->stats.tx_errors = stats_tx.errors;
You should consider the use of u64_stats_update_begin() and friends to update and retrieve statistics. Also why do you have to update the stats in a timer?
+
+struct aq_nic_s *aq_nic_alloc_cold(const struct net_device_ops *ndev_ops,
+ const struct ethtool_ops *et_ops,
+ struct device *dev,
+ struct aq_pci_func_s *aq_pci_func,
+ unsigned int port,
+ const struct aq_hw_ops *aq_hw_ops)
+{
+ struct net_device *ndev = NULL;
+ struct aq_nic_s *self = NULL;
+ int err = 0;
+
+ ndev = aq_nic_ndev_alloc();
+ self = netdev_priv(ndev);
+ if (!self) {For this and all other checks of self: how can self ever be NULL?
+
+int aq_nic_ndev_register(struct aq_nic_s *self)
+{
+ int err = 0;
+ unsigned int i = 0U;
+
+ if (!self->ndev) {
+ err = -EINVAL;
+ goto err_exit;
+ }
+ err = self->aq_hw_ops.hw_get_mac_permanent(self->aq_hw,
+ self->aq_nic_cfg.aq_hw_caps,
+ self->ndev->dev_addr);
+ if (err < 0)
+ goto err_exit;
+
+#if defined(AQ_CFG_MAC_ADDR_PERMANENT)
+ {
+ static u8 mac_addr_permanent[] = AQ_CFG_MAC_ADDR_PERMANENT;
+
+ ether_addr_copy(self->ndev->dev_addr, mac_addr_permanent);
+ }
+#endif
+ err = register_netdev(self->ndev);
+ if (err < 0)
+ goto err_exit;
+This looks racy. Note that as soon as you call register_netdev() your drivers open() function can be called. You have to setup everything _before_ you call register_netdev().
+ self->is_ndev_registered = true; + netif_carrier_off(self->ndev); + + for (i = AQ_CFG_VECS_MAX; i--;) + aq_nic_ndev_queue_stop(self, i); +
+
+static unsigned int aq_nic_map_skb_frag(struct aq_nic_s *self,
+ struct sk_buff *skb,
+ struct aq_ring_buff_s *dx)
+{
+ unsigned int ret = 0U;
+ unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+ unsigned int frag_count = 0U;
+
+ dx->flags = 0U;
+ dx->len = skb_headlen(skb);
+ dx->pa = dma_map_single(aq_nic_get_dev(self), skb->data, dx->len,
+ DMA_TO_DEVICE);Mapping can fail. You have to check the result.
+ dx->len_pkt = skb->len;
+ dx->is_sop = 1U;
+ dx->is_mapped = 1U;
+
+ ++ret;
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL) {
+ dx->is_ip_cso = (htons(ETH_P_IP) == skb->protocol) ? 1U : 0U;
+ dx->is_tcp_cso =
+ (ip_hdr(skb)->protocol == IPPROTO_TCP) ? 1U : 0U;
+ dx->is_udp_cso =
+ (ip_hdr(skb)->protocol == IPPROTO_UDP) ? 1U : 0U;
+ }
+
+ for (; nr_frags--; ++frag_count) {
+ unsigned int frag_len;
+ dma_addr_t frag_pa;
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[frag_count];
+
+ frag_len = skb_frag_size(frag);
+
+ frag_pa = skb_frag_dma_map(aq_nic_get_dev(self), frag, 0,
+ frag_len, DMA_TO_DEVICE);Same here, you have to check if mapping was successful.
+
+int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb)
+__releases(&ring->lock)
+__acquires(&ring->lock)
+{
+ struct aq_ring_s *ring = NULL;
+ unsigned int frags = 0U;
+ unsigned int vec = skb->queue_mapping % self->aq_nic_cfg.vecs;
+ unsigned int tc = 0U;
+ unsigned int trys = AQ_CFG_LOCK_TRYS;
+ int err = 0;Please use NETDEV_TX_OK instead of 0 as the return value for successful transmission.
+ bool is_nic_in_bad_state; + bool is_busy = false;
What is "is_busy" needed for?
+ struct aq_ring_buff_s buffers[AQ_CFG_SKB_FRAGS_MAX];
You allocate quite large buffers on a very limited resource (the kernel stack) only to copy the data from the local buffers to the xmit buffer via aq_ring_tx_append_buffs(). Why dont map the xmit buffers directly?
+
+ frags = skb_shinfo(skb)->nr_frags + 1;
+
+ ring = self->aq_ring_tx[AQ_NIC_TCVEC2RING(self, tc, vec)];
+
+ atomic_inc(&self->header.busy_count);
+ is_busy = true;
+
+ if (frags > AQ_CFG_SKB_FRAGS_MAX) {
+ dev_kfree_skb_any(skb);
+ goto err_exit;
+ }
+
+ is_nic_in_bad_state = aq_utils_obj_test(&self->header.flags,
+ AQ_NIC_FLAGS_IS_NOT_TX_READY) ||
+ (aq_ring_avail_dx(ring) <
+ AQ_CFG_SKB_FRAGS_MAX);
+
+ if (is_nic_in_bad_state) {
+ aq_nic_ndev_queue_stop(self, ring->idx);
+ err = NETDEV_TX_BUSY;
+ goto err_exit;
+ }
+
+ do {
+ if (spin_trylock(&ring->header.lock)) {
+ frags = aq_nic_map_skb(self, skb, &buffers[0]);
+
+ aq_ring_tx_append_buffs(ring, &buffers[0], frags);
+
+ err = self->aq_hw_ops.hw_ring_tx_xmit(self->aq_hw,
+ ring, frags);
+ if (err >= 0) {
+ if (aq_ring_avail_dx(ring) <
+ AQ_CFG_SKB_FRAGS_MAX + 1)
+ aq_nic_ndev_queue_stop(self, ring->idx);
+ }
+ spin_unlock(&ring->header.lock);
+
+ if (err >= 0) {
+ ++ring->stats.tx.packets;
+ ring->stats.tx.bytes += skb->len;
+ }
+ break;
+ }
+ } while (--trys);AFAICS busy_count is only used in the timer callback. Why do they have to be synchronized?
+
+int aq_nic_stop(struct aq_nic_s *self)
+{
+ struct aq_vec_s *aq_vec = NULL;
+ unsigned int i = 0U;
+
+ for (i = 0U, aq_vec = self->aq_vec[0];
+ self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i])
+ aq_nic_ndev_queue_stop(self, i);
+
+ del_timer_sync(&self->service_timer);This is not sufficient. You also have to make sure that the timer callback does not restart the timer.
+
+int aq_nic_change_pm_state(struct aq_nic_s *self, pm_message_t *pm_msg)
+{
+ int err = 0;
+
+ if (!netif_running(self->ndev)) {
+ err = 0;
+ goto err_exit;
+ }
+ rtnl_lock();
+ if (pm_msg->event & PM_EVENT_SLEEP || pm_msg->event & PM_EVENT_FREEZE) {
+ self->power_state = AQ_HW_POWER_STATE_D3;
+ netif_device_detach(self->ndev);
+ netif_tx_stop_all_queues(self->ndev);
+
+ err = aq_nic_stop(self);
+ if (err < 0)
+ goto err_exit;rtnl_unlock() is missing.
+
+ aq_nic_deinit(self);
+ } else {
+ err = aq_nic_init(self);
+ if (err < 0)
+ goto err_exit;
+
+ err = aq_nic_start(self);
+ if (err < 0)
+ goto err_exit;
+
+ netif_device_attach(self->ndev);
+ netif_tx_start_all_queues(self->ndev);
+ }
+ rtnl_unlock();
+
+err_exit:
+ return err;
+}Regards, Lino