drivers/net/ethernet/google/gve/gve_buffer_mgmt_dqo.c

Source file repositories/reference/linux-study-clean/drivers/net/ethernet/google/gve/gve_buffer_mgmt_dqo.c

File Facts

System
Linux kernel
Corpus path
drivers/net/ethernet/google/gve/gve_buffer_mgmt_dqo.c
Extension
.c
Size
8975 bytes
Lines
345
Domain
Driver Families
Bucket
drivers/net
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (gve_buf_ref_cnt(buf_state) == 0) {
			rx->dqo.used_buf_states_cnt--;
			return buf_state;
		}

		gve_enqueue_buf_state(rx, &rx->dqo.used_buf_states, buf_state);
	}

	return NULL;
}

int gve_alloc_qpl_page_dqo(struct gve_rx_ring *rx,
			   struct gve_rx_buf_state_dqo *buf_state)
{
	struct gve_priv *priv = rx->gve;
	u32 idx;

	idx = rx->dqo.next_qpl_page_idx;
	if (idx >= priv->rx_pages_per_qpl) {
		net_err_ratelimited("%s: Out of QPL pages\n",
				    priv->dev->name);
		return -ENOMEM;
	}
	buf_state->page_info.page = rx->dqo.qpl->pages[idx];
	buf_state->addr = rx->dqo.qpl->page_buses[idx];
	rx->dqo.next_qpl_page_idx++;
	buf_state->page_info.page_offset = 0;
	buf_state->page_info.page_address =
		page_address(buf_state->page_info.page);
	buf_state->page_info.buf_size = rx->packet_buffer_truesize;
	buf_state->page_info.pad = rx->rx_headroom;
	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;
}

void gve_free_qpl_page_dqo(struct gve_rx_buf_state_dqo *buf_state)
{
	if (!buf_state->page_info.page)
		return;

	page_ref_sub(buf_state->page_info.page,
		     buf_state->page_info.pagecnt_bias - 1);
	buf_state->page_info.page = NULL;
}

void gve_try_recycle_buf(struct gve_priv *priv, struct gve_rx_ring *rx,
			 struct gve_rx_buf_state_dqo *buf_state)
{
	const u16 data_buffer_size = rx->packet_buffer_truesize;
	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);
	rx->dqo.used_buf_states_cnt++;

Annotation

Implementation Notes