Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Bailey Forrest | 3693 | 97.96% | 3 | 37.50% |
David Awogbemila | 74 | 1.96% | 2 | 25.00% |
Dan Carpenter | 1 | 0.03% | 1 | 12.50% |
Catherine Sullivan | 1 | 0.03% | 1 | 12.50% |
Shailend Chand | 1 | 0.03% | 1 | 12.50% |
Total | 3770 | 8 |
// SPDX-License-Identifier: (GPL-2.0 OR MIT) /* Google virtual Ethernet (gve) driver * * Copyright (C) 2015-2021 Google, Inc. */ #include "gve.h" #include "gve_dqo.h" #include "gve_adminq.h" #include "gve_utils.h" #include <linux/ip.h> #include <linux/ipv6.h> #include <linux/skbuff.h> #include <linux/slab.h> #include <net/ip6_checksum.h> #include <net/ipv6.h> #include <net/tcp.h> static int gve_buf_ref_cnt(struct gve_rx_buf_state_dqo *bs) { return page_count(bs->page_info.page) - bs->page_info.pagecnt_bias; } static void gve_free_page_dqo(struct gve_priv *priv, struct gve_rx_buf_state_dqo *bs) { page_ref_sub(bs->page_info.page, bs->page_info.pagecnt_bias - 1); gve_free_page(&priv->pdev->dev, bs->page_info.page, bs->addr, DMA_FROM_DEVICE); bs->page_info.page = NULL; } static struct gve_rx_buf_state_dqo *gve_alloc_buf_state(struct gve_rx_ring *rx) { struct gve_rx_buf_state_dqo *buf_state; s16 buffer_id; buffer_id = rx->dqo.free_buf_states; if (unlikely(buffer_id == -1)) return NULL; buf_state = &rx->dqo.buf_states[buffer_id]; /* Remove buf_state from free list */ rx->dqo.free_buf_states = buf_state->next; /* Point buf_state to itself to mark it as allocated */ buf_state->next = buffer_id; return buf_state; } static bool gve_buf_state_is_allocated(struct gve_rx_ring *rx, struct gve_rx_buf_state_dqo *buf_state) { s16 buffer_id = buf_state - rx->dqo.buf_states; return buf_state->next == buffer_id; } static void gve_free_buf_state(struct gve_rx_ring *rx, struct gve_rx_buf_state_dqo *buf_state) { s16 buffer_id = buf_state - rx->dqo.buf_states; buf_state->next = rx->dqo.free_buf_states; rx->dqo.free_buf_states = buffer_id; } static struct gve_rx_buf_state_dqo * gve_dequeue_buf_state(struct gve_rx_ring *rx, struct gve_index_list *list) { struct gve_rx_buf_state_dqo *buf_state; s16 buffer_id; buffer_id = list->head; if (unlikely(buffer_id == -1)) return NULL; buf_state = &rx->dqo.buf_states[buffer_id]; /* Remove buf_state from list */ list->head = buf_state->next; if (buf_state->next == -1) list->tail = -1; /* Point buf_state to itself to mark it as allocated */ buf_state->next = buffer_id; return buf_state; } static void gve_enqueue_buf_state(struct gve_rx_ring *rx, struct gve_index_list *list, struct gve_rx_buf_state_dqo *buf_state) { s16 buffer_id = buf_state - rx->dqo.buf_states; buf_state->next = -1; if (list->head == -1) { list->head = buffer_id; list->tail = buffer_id; } else { int tail = list->tail; rx->dqo.buf_states[tail].next = buffer_id; list->tail = buffer_id; } } static struct gve_rx_buf_state_dqo * gve_get_recycled_buf_state(struct gve_rx_ring *rx) { struct gve_rx_buf_state_dqo *buf_state; int i; /* Recycled buf states are immediately usable. */ buf_state = gve_dequeue_buf_state(rx, &rx->dqo.recycled_buf_states); if (likely(buf_state)) return buf_state; if (unlikely(rx->dqo.used_buf_states.head == -1)) return NULL; /* Used buf states are only usable when ref count reaches 0, which means * no SKBs refer to them. * * Search a limited number before giving up. */ for (i = 0; i < 5; i++) { buf_state = gve_dequeue_buf_state(rx, &rx->dqo.used_buf_states); if (gve_buf_ref_cnt(buf_state) == 0) return buf_state; gve_enqueue_buf_state(rx, &rx->dqo.used_buf_states, buf_state); } /* If there are no free buf states discard an entry from * `used_buf_states` so it can be used. */ if (unlikely(rx->dqo.free_buf_states == -1)) { buf_state = gve_dequeue_buf_state(rx, &rx->dqo.used_buf_states); if (gve_buf_ref_cnt(buf_state) == 0) return buf_state; gve_free_page_dqo(rx->gve, buf_state); gve_free_buf_state(rx, buf_state); } return NULL; } static int gve_alloc_page_dqo(struct gve_priv *priv, struct gve_rx_buf_state_dqo *buf_state) { int err; err = gve_alloc_page(priv, &priv->pdev->dev, &buf_state->page_info.page, &buf_state->addr, DMA_FROM_DEVICE, GFP_ATOMIC); if (err) return err; buf_state->page_info.page_offset = 0; buf_state->page_info.page_address = page_address(buf_state->page_info.page); buf_state->last_single_ref_offset = 0; /* The page already has 1 ref. */ page_ref_add(buf_state->page_info.page, INT_MAX - 1); buf_state->page_info.pagecnt_bias = INT_MAX; return 0; } static void gve_rx_free_ring_dqo(struct gve_priv *priv, int idx) { struct gve_rx_ring *rx = &priv->rx[idx]; struct device *hdev = &priv->pdev->dev; size_t completion_queue_slots; size_t buffer_queue_slots; size_t size; int i; completion_queue_slots = rx->dqo.complq.mask + 1; buffer_queue_slots = rx->dqo.bufq.mask + 1; gve_rx_remove_from_block(priv, idx); if (rx->q_resources) { dma_free_coherent(hdev, sizeof(*rx->q_resources), rx->q_resources, rx->q_resources_bus); rx->q_resources = NULL; } for (i = 0; i < rx->dqo.num_buf_states; i++) { struct gve_rx_buf_state_dqo *bs = &rx->dqo.buf_states[i]; if (bs->page_info.page) gve_free_page_dqo(priv, bs); } if (rx->dqo.bufq.desc_ring) { size = sizeof(rx->dqo.bufq.desc_ring[0]) * buffer_queue_slots; dma_free_coherent(hdev, size, rx->dqo.bufq.desc_ring, rx->dqo.bufq.bus); rx->dqo.bufq.desc_ring = NULL; } if (rx->dqo.complq.desc_ring) { size = sizeof(rx->dqo.complq.desc_ring[0]) * completion_queue_slots; dma_free_coherent(hdev, size, rx->dqo.complq.desc_ring, rx->dqo.complq.bus); rx->dqo.complq.desc_ring = NULL; } kvfree(rx->dqo.buf_states); rx->dqo.buf_states = NULL; netif_dbg(priv, drv, priv->dev, "freed rx ring %d\n", idx); } static int gve_rx_alloc_ring_dqo(struct gve_priv *priv, int idx) { struct gve_rx_ring *rx = &priv->rx[idx]; struct device *hdev = &priv->pdev->dev; size_t size; int i; const u32 buffer_queue_slots = priv->options_dqo_rda.rx_buff_ring_entries; const u32 completion_queue_slots = priv->rx_desc_cnt; netif_dbg(priv, drv, priv->dev, "allocating rx ring DQO\n"); memset(rx, 0, sizeof(*rx)); rx->gve = priv; rx->q_num = idx; rx->dqo.bufq.mask = buffer_queue_slots - 1; rx->dqo.complq.num_free_slots = completion_queue_slots; rx->dqo.complq.mask = completion_queue_slots - 1; rx->ctx.skb_head = NULL; rx->ctx.skb_tail = NULL; rx->dqo.num_buf_states = min_t(s16, S16_MAX, buffer_queue_slots * 4); rx->dqo.buf_states = kvcalloc(rx->dqo.num_buf_states, sizeof(rx->dqo.buf_states[0]), GFP_KERNEL); if (!rx->dqo.buf_states) return -ENOMEM; /* Set up linked list of buffer IDs */ for (i = 0; i < rx->dqo.num_buf_states - 1; i++) rx->dqo.buf_states[i].next = i + 1; rx->dqo.buf_states[rx->dqo.num_buf_states - 1].next = -1; rx->dqo.recycled_buf_states.head = -1; rx->dqo.recycled_buf_states.tail = -1; rx->dqo.used_buf_states.head = -1; rx->dqo.used_buf_states.tail = -1; /* Allocate RX completion queue */ size = sizeof(rx->dqo.complq.desc_ring[0]) * completion_queue_slots; rx->dqo.complq.desc_ring = dma_alloc_coherent(hdev, size, &rx->dqo.complq.bus, GFP_KERNEL); if (!rx->dqo.complq.desc_ring) goto err; /* Allocate RX buffer queue */ size = sizeof(rx->dqo.bufq.desc_ring[0]) * buffer_queue_slots; rx->dqo.bufq.desc_ring = dma_alloc_coherent(hdev, size, &rx->dqo.bufq.bus, GFP_KERNEL); if (!rx->dqo.bufq.desc_ring) goto err; rx->q_resources = dma_alloc_coherent(hdev, sizeof(*rx->q_resources), &rx->q_resources_bus, GFP_KERNEL); if (!rx->q_resources) goto err; gve_rx_add_to_block(priv, idx); return 0; err: gve_rx_free_ring_dqo(priv, idx); return -ENOMEM; } void gve_rx_write_doorbell_dqo(const struct gve_priv *priv, int queue_idx) { const struct gve_rx_ring *rx = &priv->rx[queue_idx]; u64 index = be32_to_cpu(rx->q_resources->db_index); iowrite32(rx->dqo.bufq.tail, &priv->db_bar2[index]); } int gve_rx_alloc_rings_dqo(struct gve_priv *priv) { int err = 0; int i; for (i = 0; i < priv->rx_cfg.num_queues; i++) { err = gve_rx_alloc_ring_dqo(priv, i); if (err) { netif_err(priv, drv, priv->dev, "Failed to alloc rx ring=%d: err=%d\n", i, err); goto err; } } return 0; err: for (i--; i >= 0; i--) gve_rx_free_ring_dqo(priv, i); return err; } void gve_rx_free_rings_dqo(struct gve_priv *priv) { int i; for (i = 0; i < priv->rx_cfg.num_queues; i++) gve_rx_free_ring_dqo(priv, i); } void gve_rx_post_buffers_dqo(struct gve_rx_ring *rx) { struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq; struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq; struct gve_priv *priv = rx->gve; u32 num_avail_slots; u32 num_full_slots; u32 num_posted = 0; num_full_slots = (bufq->tail - bufq->head) & bufq->mask; num_avail_slots = bufq->mask - num_full_slots; num_avail_slots = min_t(u32, num_avail_slots, complq->num_free_slots); while (num_posted < num_avail_slots) { struct gve_rx_desc_dqo *desc = &bufq->desc_ring[bufq->tail]; struct gve_rx_buf_state_dqo *buf_state; buf_state = gve_get_recycled_buf_state(rx); if (unlikely(!buf_state)) { buf_state = gve_alloc_buf_state(rx); if (unlikely(!buf_state)) break; if (unlikely(gve_alloc_page_dqo(priv, buf_state))) { u64_stats_update_begin(&rx->statss); rx->rx_buf_alloc_fail++; u64_stats_update_end(&rx->statss); gve_free_buf_state(rx, buf_state); break; } } desc->buf_id = cpu_to_le16(buf_state - rx->dqo.buf_states); desc->buf_addr = cpu_to_le64(buf_state->addr + buf_state->page_info.page_offset); bufq->tail = (bufq->tail + 1) & bufq->mask; complq->num_free_slots--; num_posted++; if ((bufq->tail & (GVE_RX_BUF_THRESH_DQO - 1)) == 0) gve_rx_write_doorbell_dqo(priv, rx->q_num); } rx->fill_cnt += num_posted; } static void gve_try_recycle_buf(struct gve_priv *priv, struct gve_rx_ring *rx, struct gve_rx_buf_state_dqo *buf_state) { const int data_buffer_size = priv->data_buffer_size_dqo; int pagecount; /* Can't reuse if we only fit one buffer per page */ if (data_buffer_size * 2 > PAGE_SIZE) goto mark_used; pagecount = gve_buf_ref_cnt(buf_state); /* Record the offset when we have a single remaining reference. * * When this happens, we know all of the other offsets of the page are * usable. */ if (pagecount == 1) { buf_state->last_single_ref_offset = buf_state->page_info.page_offset; } /* Use the next buffer sized chunk in the page. */ buf_state->page_info.page_offset += data_buffer_size; buf_state->page_info.page_offset &= (PAGE_SIZE - 1); /* If we wrap around to the same offset without ever dropping to 1 * reference, then we don't know if this offset was ever freed. */ if (buf_state->page_info.page_offset == buf_state->last_single_ref_offset) { goto mark_used; } gve_enqueue_buf_state(rx, &rx->dqo.recycled_buf_states, buf_state); return; mark_used: gve_enqueue_buf_state(rx, &rx->dqo.used_buf_states, buf_state); } static void gve_rx_skb_csum(struct sk_buff *skb, const struct gve_rx_compl_desc_dqo *desc, struct gve_ptype ptype) { skb->ip_summed = CHECKSUM_NONE; /* HW did not identify and process L3 and L4 headers. */ if (unlikely(!desc->l3_l4_processed)) return; if (ptype.l3_type == GVE_L3_TYPE_IPV4) { if (unlikely(desc->csum_ip_err || desc->csum_external_ip_err)) return; } else if (ptype.l3_type == GVE_L3_TYPE_IPV6) { /* Checksum should be skipped if this flag is set. */ if (unlikely(desc->ipv6_ex_add)) return; } if (unlikely(desc->csum_l4_err)) return; switch (ptype.l4_type) { case GVE_L4_TYPE_TCP: case GVE_L4_TYPE_UDP: case GVE_L4_TYPE_ICMP: case GVE_L4_TYPE_SCTP: skb->ip_summed = CHECKSUM_UNNECESSARY; break; default: break; } } static void gve_rx_skb_hash(struct sk_buff *skb, const struct gve_rx_compl_desc_dqo *compl_desc, struct gve_ptype ptype) { enum pkt_hash_types hash_type = PKT_HASH_TYPE_L2; if (ptype.l4_type != GVE_L4_TYPE_UNKNOWN) hash_type = PKT_HASH_TYPE_L4; else if (ptype.l3_type != GVE_L3_TYPE_UNKNOWN) hash_type = PKT_HASH_TYPE_L3; skb_set_hash(skb, le32_to_cpu(compl_desc->hash), hash_type); } static void gve_rx_free_skb(struct gve_rx_ring *rx) { if (!rx->ctx.skb_head) return; dev_kfree_skb_any(rx->ctx.skb_head); rx->ctx.skb_head = NULL; rx->ctx.skb_tail = NULL; } /* Chains multi skbs for single rx packet. * Returns 0 if buffer is appended, -1 otherwise. */ static int gve_rx_append_frags(struct napi_struct *napi, struct gve_rx_buf_state_dqo *buf_state, u16 buf_len, struct gve_rx_ring *rx, struct gve_priv *priv) { int num_frags = skb_shinfo(rx->ctx.skb_tail)->nr_frags; if (unlikely(num_frags == MAX_SKB_FRAGS)) { struct sk_buff *skb; skb = napi_alloc_skb(napi, 0); if (!skb) return -1; skb_shinfo(rx->ctx.skb_tail)->frag_list = skb; rx->ctx.skb_tail = skb; num_frags = 0; } if (rx->ctx.skb_tail != rx->ctx.skb_head) { rx->ctx.skb_head->len += buf_len; rx->ctx.skb_head->data_len += buf_len; rx->ctx.skb_head->truesize += priv->data_buffer_size_dqo; } skb_add_rx_frag(rx->ctx.skb_tail, num_frags, buf_state->page_info.page, buf_state->page_info.page_offset, buf_len, priv->data_buffer_size_dqo); gve_dec_pagecnt_bias(&buf_state->page_info); return 0; } /* Returns 0 if descriptor is completed successfully. * Returns -EINVAL if descriptor is invalid. * Returns -ENOMEM if data cannot be copied to skb. */ static int gve_rx_dqo(struct napi_struct *napi, struct gve_rx_ring *rx, const struct gve_rx_compl_desc_dqo *compl_desc, int queue_idx) { const u16 buffer_id = le16_to_cpu(compl_desc->buf_id); const bool eop = compl_desc->end_of_packet != 0; struct gve_rx_buf_state_dqo *buf_state; struct gve_priv *priv = rx->gve; u16 buf_len; if (unlikely(buffer_id >= rx->dqo.num_buf_states)) { net_err_ratelimited("%s: Invalid RX buffer_id=%u\n", priv->dev->name, buffer_id); return -EINVAL; } buf_state = &rx->dqo.buf_states[buffer_id]; if (unlikely(!gve_buf_state_is_allocated(rx, buf_state))) { net_err_ratelimited("%s: RX buffer_id is not allocated: %u\n", priv->dev->name, buffer_id); return -EINVAL; } if (unlikely(compl_desc->rx_error)) { gve_enqueue_buf_state(rx, &rx->dqo.recycled_buf_states, buf_state); return -EINVAL; } buf_len = compl_desc->packet_len; /* Page might have not been used for awhile and was likely last written * by a different thread. */ prefetch(buf_state->page_info.page); /* Sync the portion of dma buffer for CPU to read. */ dma_sync_single_range_for_cpu(&priv->pdev->dev, buf_state->addr, buf_state->page_info.page_offset, buf_len, DMA_FROM_DEVICE); /* Append to current skb if one exists. */ if (rx->ctx.skb_head) { if (unlikely(gve_rx_append_frags(napi, buf_state, buf_len, rx, priv)) != 0) { goto error; } gve_try_recycle_buf(priv, rx, buf_state); return 0; } if (eop && buf_len <= priv->rx_copybreak) { rx->ctx.skb_head = gve_rx_copy(priv->dev, napi, &buf_state->page_info, buf_len, 0, NULL); if (unlikely(!rx->ctx.skb_head)) goto error; rx->ctx.skb_tail = rx->ctx.skb_head; u64_stats_update_begin(&rx->statss); rx->rx_copied_pkt++; rx->rx_copybreak_pkt++; u64_stats_update_end(&rx->statss); gve_enqueue_buf_state(rx, &rx->dqo.recycled_buf_states, buf_state); return 0; } rx->ctx.skb_head = napi_get_frags(napi); if (unlikely(!rx->ctx.skb_head)) goto error; rx->ctx.skb_tail = rx->ctx.skb_head; skb_add_rx_frag(rx->ctx.skb_head, 0, buf_state->page_info.page, buf_state->page_info.page_offset, buf_len, priv->data_buffer_size_dqo); gve_dec_pagecnt_bias(&buf_state->page_info); gve_try_recycle_buf(priv, rx, buf_state); return 0; error: gve_enqueue_buf_state(rx, &rx->dqo.recycled_buf_states, buf_state); return -ENOMEM; } static int gve_rx_complete_rsc(struct sk_buff *skb, const struct gve_rx_compl_desc_dqo *desc, struct gve_ptype ptype) { struct skb_shared_info *shinfo = skb_shinfo(skb); /* Only TCP is supported right now. */ if (ptype.l4_type != GVE_L4_TYPE_TCP) return -EINVAL; switch (ptype.l3_type) { case GVE_L3_TYPE_IPV4: shinfo->gso_type = SKB_GSO_TCPV4; break; case GVE_L3_TYPE_IPV6: shinfo->gso_type = SKB_GSO_TCPV6; break; default: return -EINVAL; } shinfo->gso_size = le16_to_cpu(desc->rsc_seg_len); return 0; } /* Returns 0 if skb is completed successfully, -1 otherwise. */ static int gve_rx_complete_skb(struct gve_rx_ring *rx, struct napi_struct *napi, const struct gve_rx_compl_desc_dqo *desc, netdev_features_t feat) { struct gve_ptype ptype = rx->gve->ptype_lut_dqo->ptypes[desc->packet_type]; int err; skb_record_rx_queue(rx->ctx.skb_head, rx->q_num); if (feat & NETIF_F_RXHASH) gve_rx_skb_hash(rx->ctx.skb_head, desc, ptype); if (feat & NETIF_F_RXCSUM) gve_rx_skb_csum(rx->ctx.skb_head, desc, ptype); /* RSC packets must set gso_size otherwise the TCP stack will complain * that packets are larger than MTU. */ if (desc->rsc) { err = gve_rx_complete_rsc(rx->ctx.skb_head, desc, ptype); if (err < 0) return err; } if (skb_headlen(rx->ctx.skb_head) == 0) napi_gro_frags(napi); else napi_gro_receive(napi, rx->ctx.skb_head); return 0; } int gve_rx_poll_dqo(struct gve_notify_block *block, int budget) { struct napi_struct *napi = &block->napi; netdev_features_t feat = napi->dev->features; struct gve_rx_ring *rx = block->rx; struct gve_rx_compl_queue_dqo *complq = &rx->dqo.complq; u32 work_done = 0; u64 bytes = 0; int err; while (work_done < budget) { struct gve_rx_compl_desc_dqo *compl_desc = &complq->desc_ring[complq->head]; u32 pkt_bytes; /* No more new packets */ if (compl_desc->generation == complq->cur_gen_bit) break; /* Prefetch the next two descriptors. */ prefetch(&complq->desc_ring[(complq->head + 1) & complq->mask]); prefetch(&complq->desc_ring[(complq->head + 2) & complq->mask]); /* Do not read data until we own the descriptor */ dma_rmb(); err = gve_rx_dqo(napi, rx, compl_desc, rx->q_num); if (err < 0) { gve_rx_free_skb(rx); u64_stats_update_begin(&rx->statss); if (err == -ENOMEM) rx->rx_skb_alloc_fail++; else if (err == -EINVAL) rx->rx_desc_err_dropped_pkt++; u64_stats_update_end(&rx->statss); } complq->head = (complq->head + 1) & complq->mask; complq->num_free_slots++; /* When the ring wraps, the generation bit is flipped. */ complq->cur_gen_bit ^= (complq->head == 0); /* Receiving a completion means we have space to post another * buffer on the buffer queue. */ { struct gve_rx_buf_queue_dqo *bufq = &rx->dqo.bufq; bufq->head = (bufq->head + 1) & bufq->mask; } /* Free running counter of completed descriptors */ rx->cnt++; if (!rx->ctx.skb_head) continue; if (!compl_desc->end_of_packet) continue; work_done++; pkt_bytes = rx->ctx.skb_head->len; /* The ethernet header (first ETH_HLEN bytes) is snipped off * by eth_type_trans. */ if (skb_headlen(rx->ctx.skb_head)) pkt_bytes += ETH_HLEN; /* gve_rx_complete_skb() will consume skb if successful */ if (gve_rx_complete_skb(rx, napi, compl_desc, feat) != 0) { gve_rx_free_skb(rx); u64_stats_update_begin(&rx->statss); rx->rx_desc_err_dropped_pkt++; u64_stats_update_end(&rx->statss); continue; } bytes += pkt_bytes; rx->ctx.skb_head = NULL; rx->ctx.skb_tail = NULL; } gve_rx_post_buffers_dqo(rx); u64_stats_update_begin(&rx->statss); rx->rpackets += work_done; rx->rbytes += bytes; u64_stats_update_end(&rx->statss); return work_done; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1