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