xref: /freebsd/sys/dev/gve/gve_qpl.c (revision f8ed8382daf4b9a97056b1dba4fe4e5cb4f7485c)
154dfc97bSShailend Chand /*-
254dfc97bSShailend Chand  * SPDX-License-Identifier: BSD-3-Clause
354dfc97bSShailend Chand  *
454dfc97bSShailend Chand  * Copyright (c) 2023 Google LLC
554dfc97bSShailend Chand  *
654dfc97bSShailend Chand  * Redistribution and use in source and binary forms, with or without modification,
754dfc97bSShailend Chand  * are permitted provided that the following conditions are met:
854dfc97bSShailend Chand  *
954dfc97bSShailend Chand  * 1. Redistributions of source code must retain the above copyright notice, this
1054dfc97bSShailend Chand  *    list of conditions and the following disclaimer.
1154dfc97bSShailend Chand  *
1254dfc97bSShailend Chand  * 2. Redistributions in binary form must reproduce the above copyright notice,
1354dfc97bSShailend Chand  *    this list of conditions and the following disclaimer in the documentation
1454dfc97bSShailend Chand  *    and/or other materials provided with the distribution.
1554dfc97bSShailend Chand  *
1654dfc97bSShailend Chand  * 3. Neither the name of the copyright holder nor the names of its contributors
1754dfc97bSShailend Chand  *    may be used to endorse or promote products derived from this software without
1854dfc97bSShailend Chand  *    specific prior written permission.
1954dfc97bSShailend Chand  *
2054dfc97bSShailend Chand  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
2154dfc97bSShailend Chand  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
2254dfc97bSShailend Chand  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
2354dfc97bSShailend Chand  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
2454dfc97bSShailend Chand  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2554dfc97bSShailend Chand  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2654dfc97bSShailend Chand  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
2754dfc97bSShailend Chand  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2854dfc97bSShailend Chand  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
2954dfc97bSShailend Chand  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3054dfc97bSShailend Chand  */
3154dfc97bSShailend Chand #include <sys/malloc.h>
3254dfc97bSShailend Chand 
3354dfc97bSShailend Chand #include "gve.h"
3454dfc97bSShailend Chand #include "gve_adminq.h"
352348ac89SShailend Chand #include "gve_dqo.h"
3654dfc97bSShailend Chand 
3754dfc97bSShailend Chand static MALLOC_DEFINE(M_GVE_QPL, "gve qpl", "gve qpl allocations");
3854dfc97bSShailend Chand 
39*f8ed8382SVee Agarwal void
gve_free_qpl(struct gve_priv * priv,struct gve_queue_page_list * qpl)40*f8ed8382SVee Agarwal gve_free_qpl(struct gve_priv *priv, struct gve_queue_page_list *qpl)
4154dfc97bSShailend Chand {
4254dfc97bSShailend Chand 	int i;
4354dfc97bSShailend Chand 
4454dfc97bSShailend Chand 	for (i = 0; i < qpl->num_dmas; i++) {
4554dfc97bSShailend Chand 		gve_dmamap_destroy(&qpl->dmas[i]);
4654dfc97bSShailend Chand 	}
4754dfc97bSShailend Chand 
4854dfc97bSShailend Chand 	if (qpl->kva) {
4954dfc97bSShailend Chand 		pmap_qremove(qpl->kva, qpl->num_pages);
5054dfc97bSShailend Chand 		kva_free(qpl->kva, PAGE_SIZE * qpl->num_pages);
5154dfc97bSShailend Chand 	}
5254dfc97bSShailend Chand 
5354dfc97bSShailend Chand 	for (i = 0; i < qpl->num_pages; i++) {
5454dfc97bSShailend Chand 		/*
5554dfc97bSShailend Chand 		 * Free the page only if this is the last ref.
5654dfc97bSShailend Chand 		 * Tx pages are known to have no other refs at
5754dfc97bSShailend Chand 		 * this point, but Rx pages might still be in
5854dfc97bSShailend Chand 		 * use by the networking stack, see gve_mextadd_free.
5954dfc97bSShailend Chand 		 */
6054dfc97bSShailend Chand 		if (vm_page_unwire_noq(qpl->pages[i])) {
6154dfc97bSShailend Chand 			if (!qpl->kva) {
6254dfc97bSShailend Chand 				pmap_qremove((vm_offset_t)qpl->dmas[i].cpu_addr, 1);
6354dfc97bSShailend Chand 				kva_free((vm_offset_t)qpl->dmas[i].cpu_addr, PAGE_SIZE);
6454dfc97bSShailend Chand 			}
6554dfc97bSShailend Chand 			vm_page_free(qpl->pages[i]);
6654dfc97bSShailend Chand 		}
6754dfc97bSShailend Chand 
6854dfc97bSShailend Chand 		priv->num_registered_pages--;
6954dfc97bSShailend Chand 	}
7054dfc97bSShailend Chand 
7154dfc97bSShailend Chand 	if (qpl->pages != NULL)
7254dfc97bSShailend Chand 		free(qpl->pages, M_GVE_QPL);
7354dfc97bSShailend Chand 
7454dfc97bSShailend Chand 	if (qpl->dmas != NULL)
7554dfc97bSShailend Chand 		free(qpl->dmas, M_GVE_QPL);
76*f8ed8382SVee Agarwal 
77*f8ed8382SVee Agarwal 	free(qpl, M_GVE_QPL);
7854dfc97bSShailend Chand }
7954dfc97bSShailend Chand 
80*f8ed8382SVee Agarwal struct gve_queue_page_list *
gve_alloc_qpl(struct gve_priv * priv,uint32_t id,int npages,bool single_kva)8154dfc97bSShailend Chand gve_alloc_qpl(struct gve_priv *priv, uint32_t id, int npages, bool single_kva)
8254dfc97bSShailend Chand {
83*f8ed8382SVee Agarwal 	struct gve_queue_page_list *qpl;
8454dfc97bSShailend Chand 	int err;
8554dfc97bSShailend Chand 	int i;
8654dfc97bSShailend Chand 
8754dfc97bSShailend Chand 	if (npages + priv->num_registered_pages > priv->max_registered_pages) {
884d779448SXin LI 		device_printf(priv->dev, "Reached max number of registered pages %ju > %ju\n",
894d779448SXin LI 		    (uintmax_t)npages + priv->num_registered_pages,
904d779448SXin LI 		    (uintmax_t)priv->max_registered_pages);
91*f8ed8382SVee Agarwal 		return (NULL);
9254dfc97bSShailend Chand 	}
9354dfc97bSShailend Chand 
94*f8ed8382SVee Agarwal 	qpl = malloc(sizeof(struct gve_queue_page_list), M_GVE_QPL,
95*f8ed8382SVee Agarwal 	    M_WAITOK | M_ZERO);
96*f8ed8382SVee Agarwal 
9754dfc97bSShailend Chand 	qpl->id = id;
9854dfc97bSShailend Chand 	qpl->num_pages = 0;
9954dfc97bSShailend Chand 	qpl->num_dmas = 0;
10054dfc97bSShailend Chand 
10154dfc97bSShailend Chand 	qpl->dmas = malloc(npages * sizeof(*qpl->dmas), M_GVE_QPL,
10254dfc97bSShailend Chand 	    M_WAITOK | M_ZERO);
10354dfc97bSShailend Chand 
10454dfc97bSShailend Chand 	qpl->pages = malloc(npages * sizeof(*qpl->pages), M_GVE_QPL,
10554dfc97bSShailend Chand 	    M_WAITOK | M_ZERO);
10654dfc97bSShailend Chand 
10754dfc97bSShailend Chand 	qpl->kva = 0;
10854dfc97bSShailend Chand 	if (single_kva) {
10954dfc97bSShailend Chand 		qpl->kva = kva_alloc(PAGE_SIZE * npages);
11054dfc97bSShailend Chand 		if (!qpl->kva) {
11154dfc97bSShailend Chand 			device_printf(priv->dev, "Failed to create the single kva for QPL %d\n", id);
11254dfc97bSShailend Chand 			err = ENOMEM;
11354dfc97bSShailend Chand 			goto abort;
11454dfc97bSShailend Chand 		}
11554dfc97bSShailend Chand 	}
11654dfc97bSShailend Chand 
11754dfc97bSShailend Chand 	for (i = 0; i < npages; i++) {
11854dfc97bSShailend Chand 		qpl->pages[i] = vm_page_alloc_noobj(VM_ALLOC_WIRED |
11954dfc97bSShailend Chand 						    VM_ALLOC_WAITOK |
12054dfc97bSShailend Chand 						    VM_ALLOC_ZERO);
12154dfc97bSShailend Chand 
12254dfc97bSShailend Chand 		if (!single_kva) {
12354dfc97bSShailend Chand 			qpl->dmas[i].cpu_addr = (void *)kva_alloc(PAGE_SIZE);
12454dfc97bSShailend Chand 			if (!qpl->dmas[i].cpu_addr) {
12554dfc97bSShailend Chand 				device_printf(priv->dev, "Failed to create kva for page %d in QPL %d", i, id);
12654dfc97bSShailend Chand 				err = ENOMEM;
12754dfc97bSShailend Chand 				goto abort;
12854dfc97bSShailend Chand 			}
12954dfc97bSShailend Chand 			pmap_qenter((vm_offset_t)qpl->dmas[i].cpu_addr, &(qpl->pages[i]), 1);
13054dfc97bSShailend Chand 		} else
13154dfc97bSShailend Chand 			qpl->dmas[i].cpu_addr = (void *)(qpl->kva + (PAGE_SIZE * i));
13254dfc97bSShailend Chand 
13354dfc97bSShailend Chand 
13454dfc97bSShailend Chand 		qpl->num_pages++;
13554dfc97bSShailend Chand 	}
13654dfc97bSShailend Chand 
13754dfc97bSShailend Chand 	if (single_kva)
13854dfc97bSShailend Chand 		pmap_qenter(qpl->kva, qpl->pages, npages);
13954dfc97bSShailend Chand 
14054dfc97bSShailend Chand 	for (i = 0; i < npages; i++) {
14154dfc97bSShailend Chand 		err = gve_dmamap_create(priv, /*size=*/PAGE_SIZE, /*align=*/PAGE_SIZE,
14254dfc97bSShailend Chand 		    &qpl->dmas[i]);
14354dfc97bSShailend Chand 		if (err != 0) {
14454dfc97bSShailend Chand 			device_printf(priv->dev, "Failed to dma-map page %d in QPL %d\n", i, id);
14554dfc97bSShailend Chand 			goto abort;
14654dfc97bSShailend Chand 		}
14754dfc97bSShailend Chand 
14854dfc97bSShailend Chand 		qpl->num_dmas++;
14954dfc97bSShailend Chand 		priv->num_registered_pages++;
15054dfc97bSShailend Chand 	}
15154dfc97bSShailend Chand 
152*f8ed8382SVee Agarwal 	return (qpl);
15354dfc97bSShailend Chand 
15454dfc97bSShailend Chand abort:
155*f8ed8382SVee Agarwal 	gve_free_qpl(priv, qpl);
156*f8ed8382SVee Agarwal 	return (NULL);
15754dfc97bSShailend Chand }
15854dfc97bSShailend Chand 
15954dfc97bSShailend Chand int
gve_register_qpls(struct gve_priv * priv)16054dfc97bSShailend Chand gve_register_qpls(struct gve_priv *priv)
16154dfc97bSShailend Chand {
162*f8ed8382SVee Agarwal 	struct gve_ring_com *com;
163*f8ed8382SVee Agarwal 	struct gve_tx_ring *tx;
164*f8ed8382SVee Agarwal 	struct gve_rx_ring *rx;
16554dfc97bSShailend Chand 	int err;
16654dfc97bSShailend Chand 	int i;
16754dfc97bSShailend Chand 
16854dfc97bSShailend Chand 	if (gve_get_state_flag(priv, GVE_STATE_FLAG_QPLREG_OK))
16954dfc97bSShailend Chand 		return (0);
17054dfc97bSShailend Chand 
171*f8ed8382SVee Agarwal 	/* Register TX qpls */
172*f8ed8382SVee Agarwal 	for (i = 0; i < priv->tx_cfg.num_queues; i++) {
173*f8ed8382SVee Agarwal 		tx = &priv->tx[i];
174*f8ed8382SVee Agarwal 		com = &tx->com;
175*f8ed8382SVee Agarwal 		err = gve_adminq_register_page_list(priv, com->qpl);
17654dfc97bSShailend Chand 		if (err != 0) {
17754dfc97bSShailend Chand 			device_printf(priv->dev,
17854dfc97bSShailend Chand 			    "Failed to register qpl %d, err: %d\n",
179*f8ed8382SVee Agarwal 			    com->qpl->id, err);
180*f8ed8382SVee Agarwal 			/* Caller schedules a reset when this fails */
181*f8ed8382SVee Agarwal 			return (err);
18254dfc97bSShailend Chand 		}
18354dfc97bSShailend Chand 	}
18454dfc97bSShailend Chand 
185*f8ed8382SVee Agarwal 	/* Register RX qpls */
186*f8ed8382SVee Agarwal 	for (i = 0; i < priv->rx_cfg.num_queues; i++) {
187*f8ed8382SVee Agarwal 		rx = &priv->rx[i];
188*f8ed8382SVee Agarwal 		com = &rx->com;
189*f8ed8382SVee Agarwal 		err = gve_adminq_register_page_list(priv, com->qpl);
190*f8ed8382SVee Agarwal 		if (err != 0) {
191*f8ed8382SVee Agarwal 			device_printf(priv->dev,
192*f8ed8382SVee Agarwal 			    "Failed to register qpl %d, err: %d\n",
193*f8ed8382SVee Agarwal 			    com->qpl->id, err);
194*f8ed8382SVee Agarwal 			/* Caller schedules a reset when this fails */
195*f8ed8382SVee Agarwal 			return (err);
196*f8ed8382SVee Agarwal 		}
197*f8ed8382SVee Agarwal 	}
19854dfc97bSShailend Chand 	gve_set_state_flag(priv, GVE_STATE_FLAG_QPLREG_OK);
19954dfc97bSShailend Chand 	return (0);
20054dfc97bSShailend Chand }
20154dfc97bSShailend Chand 
20254dfc97bSShailend Chand int
gve_unregister_qpls(struct gve_priv * priv)20354dfc97bSShailend Chand gve_unregister_qpls(struct gve_priv *priv)
20454dfc97bSShailend Chand {
20554dfc97bSShailend Chand 	int err;
206*f8ed8382SVee Agarwal 	int i;
207*f8ed8382SVee Agarwal 	struct gve_ring_com *com;
208*f8ed8382SVee Agarwal 	struct gve_tx_ring *tx;
209*f8ed8382SVee Agarwal 	struct gve_rx_ring *rx;
21054dfc97bSShailend Chand 
21154dfc97bSShailend Chand 	if (!gve_get_state_flag(priv, GVE_STATE_FLAG_QPLREG_OK))
21254dfc97bSShailend Chand 		return (0);
21354dfc97bSShailend Chand 
214*f8ed8382SVee Agarwal 	for (i = 0; i < priv->tx_cfg.num_queues; i++) {
215*f8ed8382SVee Agarwal 		tx = &priv->tx[i];
216*f8ed8382SVee Agarwal 		com = &tx->com;
217*f8ed8382SVee Agarwal 		err = gve_adminq_unregister_page_list(priv, com->qpl->id);
218*f8ed8382SVee Agarwal 		if (err != 0) {
219*f8ed8382SVee Agarwal 			device_printf(priv->dev,
220*f8ed8382SVee Agarwal 			    "Failed to unregister qpl %d, err: %d\n",
221*f8ed8382SVee Agarwal 			    com->qpl->id, err);
222*f8ed8382SVee Agarwal 		}
223*f8ed8382SVee Agarwal 	}
224*f8ed8382SVee Agarwal 
225*f8ed8382SVee Agarwal 	for (i = 0; i < priv->rx_cfg.num_queues; i++) {
226*f8ed8382SVee Agarwal 		rx = &priv->rx[i];
227*f8ed8382SVee Agarwal 		com = &rx->com;
228*f8ed8382SVee Agarwal 		err = gve_adminq_unregister_page_list(priv, com->qpl->id);
229*f8ed8382SVee Agarwal 		if (err != 0) {
230*f8ed8382SVee Agarwal 			device_printf(priv->dev,
231*f8ed8382SVee Agarwal 			    "Failed to unregister qpl %d, err: %d\n",
232*f8ed8382SVee Agarwal 			    com->qpl->id, err);
233*f8ed8382SVee Agarwal 		}
234*f8ed8382SVee Agarwal 	}
235*f8ed8382SVee Agarwal 
23654dfc97bSShailend Chand 	if (err != 0)
23754dfc97bSShailend Chand 		return (err);
23854dfc97bSShailend Chand 
23954dfc97bSShailend Chand 	gve_clear_state_flag(priv, GVE_STATE_FLAG_QPLREG_OK);
24054dfc97bSShailend Chand 	return (0);
24154dfc97bSShailend Chand }
2422348ac89SShailend Chand 
2432348ac89SShailend Chand void
gve_mextadd_free(struct mbuf * mbuf)2442348ac89SShailend Chand gve_mextadd_free(struct mbuf *mbuf)
2452348ac89SShailend Chand {
2462348ac89SShailend Chand 	vm_page_t page = (vm_page_t)mbuf->m_ext.ext_arg1;
2472348ac89SShailend Chand 	vm_offset_t va = (vm_offset_t)mbuf->m_ext.ext_arg2;
2482348ac89SShailend Chand 
2492348ac89SShailend Chand 	/*
2502348ac89SShailend Chand 	 * Free the page only if this is the last ref.
2512348ac89SShailend Chand 	 * The interface might no longer exist by the time
2522348ac89SShailend Chand 	 * this callback is called, see gve_free_qpl.
2532348ac89SShailend Chand 	 */
2542348ac89SShailend Chand 	if (__predict_false(vm_page_unwire_noq(page))) {
2552348ac89SShailend Chand 		pmap_qremove(va, 1);
2562348ac89SShailend Chand 		kva_free(va, PAGE_SIZE);
2572348ac89SShailend Chand 		vm_page_free(page);
2582348ac89SShailend Chand 	}
2592348ac89SShailend Chand }
260