brintos

brintos / linux-shallow public Read only

0
0
Text · 18.0 KiB · 59bfbfa Raw
756 lines · c
1/*2 * Copyright (c) 2013, Cisco Systems, Inc. All rights reserved.3 *4 * This software is available to you under a choice of one of two5 * licenses.  You may choose to be licensed under the terms of the GNU6 * General Public License (GPL) Version 2, available from the file7 * COPYING in the main directory of this source tree, or the8 * BSD license below:9 *10 *     Redistribution and use in source and binary forms, with or11 *     without modification, are permitted provided that the following12 *     conditions are met:13 *14 *      - Redistributions of source code must retain the above15 *        copyright notice, this list of conditions and the following16 *        disclaimer.17 *18 *      - Redistributions in binary form must reproduce the above19 *        copyright notice, this list of conditions and the following20 *        disclaimer in the documentation and/or other materials21 *        provided with the distribution.22 *23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30 * SOFTWARE.31 *32 */33#include <linux/bug.h>34#include <linux/errno.h>35#include <linux/spinlock.h>36 37#include "usnic_log.h"38#include "usnic_vnic.h"39#include "usnic_fwd.h"40#include "usnic_uiom.h"41#include "usnic_debugfs.h"42#include "usnic_ib_qp_grp.h"43#include "usnic_ib_sysfs.h"44#include "usnic_transport.h"45 46#define DFLT_RQ_IDX	047 48const char *usnic_ib_qp_grp_state_to_string(enum ib_qp_state state)49{50	switch (state) {51	case IB_QPS_RESET:52		return "Rst";53	case IB_QPS_INIT:54		return "Init";55	case IB_QPS_RTR:56		return "RTR";57	case IB_QPS_RTS:58		return "RTS";59	case IB_QPS_SQD:60		return "SQD";61	case IB_QPS_SQE:62		return "SQE";63	case IB_QPS_ERR:64		return "ERR";65	default:66		return "UNKNOWN STATE";67 68	}69}70 71int usnic_ib_qp_grp_dump_hdr(char *buf, int buf_sz)72{73	return scnprintf(buf, buf_sz, "|QPN\t|State\t|PID\t|VF Idx\t|Fil ID");74}75 76int usnic_ib_qp_grp_dump_rows(void *obj, char *buf, int buf_sz)77{78	struct usnic_ib_qp_grp *qp_grp = obj;79	struct usnic_ib_qp_grp_flow *default_flow;80	if (obj) {81		default_flow = list_first_entry(&qp_grp->flows_lst,82					struct usnic_ib_qp_grp_flow, link);83		return scnprintf(buf, buf_sz, "|%d\t|%s\t|%d\t|%hu\t|%d",84					qp_grp->ibqp.qp_num,85					usnic_ib_qp_grp_state_to_string(86							qp_grp->state),87					qp_grp->owner_pid,88					usnic_vnic_get_index(qp_grp->vf->vnic),89					default_flow->flow->flow_id);90	} else {91		return scnprintf(buf, buf_sz, "|N/A\t|N/A\t|N/A\t|N/A\t|N/A");92	}93}94 95static struct usnic_vnic_res_chunk *96get_qp_res_chunk(struct usnic_ib_qp_grp *qp_grp)97{98	lockdep_assert_held(&qp_grp->lock);99	/*100	 * The QP res chunk, used to derive qp indices,101	 * are just indices of the RQs102	 */103	return usnic_ib_qp_grp_get_chunk(qp_grp, USNIC_VNIC_RES_TYPE_RQ);104}105 106static int enable_qp_grp(struct usnic_ib_qp_grp *qp_grp)107{108 109	int status;110	int i, vnic_idx;111	struct usnic_vnic_res_chunk *res_chunk;112	struct usnic_vnic_res *res;113 114	lockdep_assert_held(&qp_grp->lock);115 116	vnic_idx = usnic_vnic_get_index(qp_grp->vf->vnic);117 118	res_chunk = get_qp_res_chunk(qp_grp);119	if (IS_ERR(res_chunk)) {120		usnic_err("Unable to get qp res with err %ld\n",121				PTR_ERR(res_chunk));122		return PTR_ERR(res_chunk);123	}124 125	for (i = 0; i < res_chunk->cnt; i++) {126		res = res_chunk->res[i];127		status = usnic_fwd_enable_qp(qp_grp->ufdev, vnic_idx,128						res->vnic_idx);129		if (status) {130			usnic_err("Failed to enable qp %d of %s:%d\n with err %d\n",131					res->vnic_idx, qp_grp->ufdev->name,132					vnic_idx, status);133			goto out_err;134		}135	}136 137	return 0;138 139out_err:140	for (i--; i >= 0; i--) {141		res = res_chunk->res[i];142		usnic_fwd_disable_qp(qp_grp->ufdev, vnic_idx,143					res->vnic_idx);144	}145 146	return status;147}148 149static int disable_qp_grp(struct usnic_ib_qp_grp *qp_grp)150{151	int i, vnic_idx;152	struct usnic_vnic_res_chunk *res_chunk;153	struct usnic_vnic_res *res;154	int status = 0;155 156	lockdep_assert_held(&qp_grp->lock);157	vnic_idx = usnic_vnic_get_index(qp_grp->vf->vnic);158 159	res_chunk = get_qp_res_chunk(qp_grp);160	if (IS_ERR(res_chunk)) {161		usnic_err("Unable to get qp res with err %ld\n",162			PTR_ERR(res_chunk));163		return PTR_ERR(res_chunk);164	}165 166	for (i = 0; i < res_chunk->cnt; i++) {167		res = res_chunk->res[i];168		status = usnic_fwd_disable_qp(qp_grp->ufdev, vnic_idx,169						res->vnic_idx);170		if (status) {171			usnic_err("Failed to disable rq %d of %s:%d\n with err %d\n",172					res->vnic_idx,173					qp_grp->ufdev->name,174					vnic_idx, status);175		}176	}177 178	return status;179 180}181 182static int init_filter_action(struct usnic_ib_qp_grp *qp_grp,183				struct usnic_filter_action *uaction)184{185	struct usnic_vnic_res_chunk *res_chunk;186 187	res_chunk = usnic_ib_qp_grp_get_chunk(qp_grp, USNIC_VNIC_RES_TYPE_RQ);188	if (IS_ERR(res_chunk)) {189		usnic_err("Unable to get %s with err %ld\n",190			usnic_vnic_res_type_to_str(USNIC_VNIC_RES_TYPE_RQ),191			PTR_ERR(res_chunk));192		return PTR_ERR(res_chunk);193	}194 195	uaction->vnic_idx = usnic_vnic_get_index(qp_grp->vf->vnic);196	uaction->action.type = FILTER_ACTION_RQ_STEERING;197	uaction->action.u.rq_idx = res_chunk->res[DFLT_RQ_IDX]->vnic_idx;198 199	return 0;200}201 202static struct usnic_ib_qp_grp_flow*203create_roce_custom_flow(struct usnic_ib_qp_grp *qp_grp,204			struct usnic_transport_spec *trans_spec)205{206	uint16_t port_num;207	int err;208	struct filter filter;209	struct usnic_filter_action uaction;210	struct usnic_ib_qp_grp_flow *qp_flow;211	struct usnic_fwd_flow *flow;212	enum usnic_transport_type trans_type;213 214	trans_type = trans_spec->trans_type;215	port_num = trans_spec->usnic_roce.port_num;216 217	/* Reserve Port */218	port_num = usnic_transport_rsrv_port(trans_type, port_num);219	if (port_num == 0)220		return ERR_PTR(-EINVAL);221 222	/* Create Flow */223	usnic_fwd_init_usnic_filter(&filter, port_num);224	err = init_filter_action(qp_grp, &uaction);225	if (err)226		goto out_unreserve_port;227 228	flow = usnic_fwd_alloc_flow(qp_grp->ufdev, &filter, &uaction);229	if (IS_ERR_OR_NULL(flow)) {230		err = flow ? PTR_ERR(flow) : -EFAULT;231		goto out_unreserve_port;232	}233 234	/* Create Flow Handle */235	qp_flow = kzalloc(sizeof(*qp_flow), GFP_ATOMIC);236	if (!qp_flow) {237		err = -ENOMEM;238		goto out_dealloc_flow;239	}240	qp_flow->flow = flow;241	qp_flow->trans_type = trans_type;242	qp_flow->usnic_roce.port_num = port_num;243	qp_flow->qp_grp = qp_grp;244	return qp_flow;245 246out_dealloc_flow:247	usnic_fwd_dealloc_flow(flow);248out_unreserve_port:249	usnic_transport_unrsrv_port(trans_type, port_num);250	return ERR_PTR(err);251}252 253static void release_roce_custom_flow(struct usnic_ib_qp_grp_flow *qp_flow)254{255	usnic_fwd_dealloc_flow(qp_flow->flow);256	usnic_transport_unrsrv_port(qp_flow->trans_type,257					qp_flow->usnic_roce.port_num);258	kfree(qp_flow);259}260 261static struct usnic_ib_qp_grp_flow*262create_udp_flow(struct usnic_ib_qp_grp *qp_grp,263		struct usnic_transport_spec *trans_spec)264{265	struct socket *sock;266	int sock_fd;267	int err;268	struct filter filter;269	struct usnic_filter_action uaction;270	struct usnic_ib_qp_grp_flow *qp_flow;271	struct usnic_fwd_flow *flow;272	enum usnic_transport_type trans_type;273	uint32_t addr;274	uint16_t port_num;275	int proto;276 277	trans_type = trans_spec->trans_type;278	sock_fd = trans_spec->udp.sock_fd;279 280	/* Get and check socket */281	sock = usnic_transport_get_socket(sock_fd);282	if (IS_ERR_OR_NULL(sock))283		return ERR_CAST(sock);284 285	err = usnic_transport_sock_get_addr(sock, &proto, &addr, &port_num);286	if (err)287		goto out_put_sock;288 289	if (proto != IPPROTO_UDP) {290		usnic_err("Protocol for fd %d is not UDP", sock_fd);291		err = -EPERM;292		goto out_put_sock;293	}294 295	/* Create flow */296	usnic_fwd_init_udp_filter(&filter, addr, port_num);297	err = init_filter_action(qp_grp, &uaction);298	if (err)299		goto out_put_sock;300 301	flow = usnic_fwd_alloc_flow(qp_grp->ufdev, &filter, &uaction);302	if (IS_ERR_OR_NULL(flow)) {303		err = flow ? PTR_ERR(flow) : -EFAULT;304		goto out_put_sock;305	}306 307	/* Create qp_flow */308	qp_flow = kzalloc(sizeof(*qp_flow), GFP_ATOMIC);309	if (!qp_flow) {310		err = -ENOMEM;311		goto out_dealloc_flow;312	}313	qp_flow->flow = flow;314	qp_flow->trans_type = trans_type;315	qp_flow->udp.sock = sock;316	qp_flow->qp_grp = qp_grp;317	return qp_flow;318 319out_dealloc_flow:320	usnic_fwd_dealloc_flow(flow);321out_put_sock:322	usnic_transport_put_socket(sock);323	return ERR_PTR(err);324}325 326static void release_udp_flow(struct usnic_ib_qp_grp_flow *qp_flow)327{328	usnic_fwd_dealloc_flow(qp_flow->flow);329	usnic_transport_put_socket(qp_flow->udp.sock);330	kfree(qp_flow);331}332 333static struct usnic_ib_qp_grp_flow*334create_and_add_flow(struct usnic_ib_qp_grp *qp_grp,335			struct usnic_transport_spec *trans_spec)336{337	struct usnic_ib_qp_grp_flow *qp_flow;338	enum usnic_transport_type trans_type;339 340	trans_type = trans_spec->trans_type;341	switch (trans_type) {342	case USNIC_TRANSPORT_ROCE_CUSTOM:343		qp_flow = create_roce_custom_flow(qp_grp, trans_spec);344		break;345	case USNIC_TRANSPORT_IPV4_UDP:346		qp_flow = create_udp_flow(qp_grp, trans_spec);347		break;348	default:349		usnic_err("Unsupported transport %u\n",350				trans_spec->trans_type);351		return ERR_PTR(-EINVAL);352	}353 354	if (!IS_ERR_OR_NULL(qp_flow)) {355		list_add_tail(&qp_flow->link, &qp_grp->flows_lst);356		usnic_debugfs_flow_add(qp_flow);357	}358 359 360	return qp_flow;361}362 363static void release_and_remove_flow(struct usnic_ib_qp_grp_flow *qp_flow)364{365	usnic_debugfs_flow_remove(qp_flow);366	list_del(&qp_flow->link);367 368	switch (qp_flow->trans_type) {369	case USNIC_TRANSPORT_ROCE_CUSTOM:370		release_roce_custom_flow(qp_flow);371		break;372	case USNIC_TRANSPORT_IPV4_UDP:373		release_udp_flow(qp_flow);374		break;375	default:376		WARN(1, "Unsupported transport %u\n",377				qp_flow->trans_type);378		break;379	}380}381 382static void release_and_remove_all_flows(struct usnic_ib_qp_grp *qp_grp)383{384	struct usnic_ib_qp_grp_flow *qp_flow, *tmp;385	list_for_each_entry_safe(qp_flow, tmp, &qp_grp->flows_lst, link)386		release_and_remove_flow(qp_flow);387}388 389int usnic_ib_qp_grp_modify(struct usnic_ib_qp_grp *qp_grp,390				enum ib_qp_state new_state,391				void *data)392{393	int status = 0;394	struct ib_event ib_event;395	enum ib_qp_state old_state;396	struct usnic_transport_spec *trans_spec;397	struct usnic_ib_qp_grp_flow *qp_flow;398 399	old_state = qp_grp->state;400	trans_spec = (struct usnic_transport_spec *) data;401 402	spin_lock(&qp_grp->lock);403	switch (new_state) {404	case IB_QPS_RESET:405		switch (old_state) {406		case IB_QPS_RESET:407			/* NO-OP */408			break;409		case IB_QPS_INIT:410			release_and_remove_all_flows(qp_grp);411			status = 0;412			break;413		case IB_QPS_RTR:414		case IB_QPS_RTS:415		case IB_QPS_ERR:416			status = disable_qp_grp(qp_grp);417			release_and_remove_all_flows(qp_grp);418			break;419		default:420			status = -EINVAL;421		}422		break;423	case IB_QPS_INIT:424		switch (old_state) {425		case IB_QPS_RESET:426			if (trans_spec) {427				qp_flow = create_and_add_flow(qp_grp,428								trans_spec);429				if (IS_ERR_OR_NULL(qp_flow)) {430					status = qp_flow ? PTR_ERR(qp_flow) : -EFAULT;431					break;432				}433			} else {434				/*435				 * Optional to specify filters.436				 */437				status = 0;438			}439			break;440		case IB_QPS_INIT:441			if (trans_spec) {442				qp_flow = create_and_add_flow(qp_grp,443								trans_spec);444				if (IS_ERR_OR_NULL(qp_flow)) {445					status = qp_flow ? PTR_ERR(qp_flow) : -EFAULT;446					break;447				}448			} else {449				/*450				 * Doesn't make sense to go into INIT state451				 * from INIT state w/o adding filters.452				 */453				status = -EINVAL;454			}455			break;456		case IB_QPS_RTR:457			status = disable_qp_grp(qp_grp);458			break;459		case IB_QPS_RTS:460			status = disable_qp_grp(qp_grp);461			break;462		default:463			status = -EINVAL;464		}465		break;466	case IB_QPS_RTR:467		switch (old_state) {468		case IB_QPS_INIT:469			status = enable_qp_grp(qp_grp);470			break;471		default:472			status = -EINVAL;473		}474		break;475	case IB_QPS_RTS:476		switch (old_state) {477		case IB_QPS_RTR:478			/* NO-OP FOR NOW */479			break;480		default:481			status = -EINVAL;482		}483		break;484	case IB_QPS_ERR:485		ib_event.device = &qp_grp->vf->pf->ib_dev;486		ib_event.element.qp = &qp_grp->ibqp;487		ib_event.event = IB_EVENT_QP_FATAL;488 489		switch (old_state) {490		case IB_QPS_RESET:491			qp_grp->ibqp.event_handler(&ib_event,492					qp_grp->ibqp.qp_context);493			break;494		case IB_QPS_INIT:495			release_and_remove_all_flows(qp_grp);496			qp_grp->ibqp.event_handler(&ib_event,497					qp_grp->ibqp.qp_context);498			break;499		case IB_QPS_RTR:500		case IB_QPS_RTS:501			status = disable_qp_grp(qp_grp);502			release_and_remove_all_flows(qp_grp);503			qp_grp->ibqp.event_handler(&ib_event,504					qp_grp->ibqp.qp_context);505			break;506		default:507			status = -EINVAL;508		}509		break;510	default:511		status = -EINVAL;512	}513	spin_unlock(&qp_grp->lock);514 515	if (!status) {516		qp_grp->state = new_state;517		usnic_info("Transitioned %u from %s to %s",518		qp_grp->grp_id,519		usnic_ib_qp_grp_state_to_string(old_state),520		usnic_ib_qp_grp_state_to_string(new_state));521	} else {522		usnic_err("Failed to transition %u from %s to %s",523		qp_grp->grp_id,524		usnic_ib_qp_grp_state_to_string(old_state),525		usnic_ib_qp_grp_state_to_string(new_state));526	}527 528	return status;529}530 531static struct usnic_vnic_res_chunk**532alloc_res_chunk_list(struct usnic_vnic *vnic,533			struct usnic_vnic_res_spec *res_spec, void *owner_obj)534{535	enum usnic_vnic_res_type res_type;536	struct usnic_vnic_res_chunk **res_chunk_list;537	int err, i, res_cnt, res_lst_sz;538 539	for (res_lst_sz = 0;540		res_spec->resources[res_lst_sz].type != USNIC_VNIC_RES_TYPE_EOL;541		res_lst_sz++) {542		/* Do Nothing */543	}544 545	res_chunk_list = kcalloc(res_lst_sz + 1, sizeof(*res_chunk_list),546					GFP_ATOMIC);547	if (!res_chunk_list)548		return ERR_PTR(-ENOMEM);549 550	for (i = 0; res_spec->resources[i].type != USNIC_VNIC_RES_TYPE_EOL;551		i++) {552		res_type = res_spec->resources[i].type;553		res_cnt = res_spec->resources[i].cnt;554 555		res_chunk_list[i] = usnic_vnic_get_resources(vnic, res_type,556					res_cnt, owner_obj);557		if (IS_ERR_OR_NULL(res_chunk_list[i])) {558			err = res_chunk_list[i] ?559					PTR_ERR(res_chunk_list[i]) : -ENOMEM;560			usnic_err("Failed to get %s from %s with err %d\n",561				usnic_vnic_res_type_to_str(res_type),562				usnic_vnic_pci_name(vnic),563				err);564			goto out_free_res;565		}566	}567 568	return res_chunk_list;569 570out_free_res:571	for (i--; i >= 0; i--)572		usnic_vnic_put_resources(res_chunk_list[i]);573	kfree(res_chunk_list);574	return ERR_PTR(err);575}576 577static void free_qp_grp_res(struct usnic_vnic_res_chunk **res_chunk_list)578{579	int i;580	for (i = 0; res_chunk_list[i]; i++)581		usnic_vnic_put_resources(res_chunk_list[i]);582	kfree(res_chunk_list);583}584 585static int qp_grp_and_vf_bind(struct usnic_ib_vf *vf,586				struct usnic_ib_pd *pd,587				struct usnic_ib_qp_grp *qp_grp)588{589	int err;590	struct pci_dev *pdev;591 592	lockdep_assert_held(&vf->lock);593 594	pdev = usnic_vnic_get_pdev(vf->vnic);595	if (vf->qp_grp_ref_cnt == 0) {596		err = usnic_uiom_attach_dev_to_pd(pd->umem_pd, &pdev->dev);597		if (err) {598			usnic_err("Failed to attach %s to domain\n",599					pci_name(pdev));600			return err;601		}602		vf->pd = pd;603	}604	vf->qp_grp_ref_cnt++;605 606	WARN_ON(vf->pd != pd);607	qp_grp->vf = vf;608 609	return 0;610}611 612static void qp_grp_and_vf_unbind(struct usnic_ib_qp_grp *qp_grp)613{614	struct pci_dev *pdev;615	struct usnic_ib_pd *pd;616 617	lockdep_assert_held(&qp_grp->vf->lock);618 619	pd = qp_grp->vf->pd;620	pdev = usnic_vnic_get_pdev(qp_grp->vf->vnic);621	if (--qp_grp->vf->qp_grp_ref_cnt == 0) {622		qp_grp->vf->pd = NULL;623		usnic_uiom_detach_dev_from_pd(pd->umem_pd, &pdev->dev);624	}625	qp_grp->vf = NULL;626}627 628static void log_spec(struct usnic_vnic_res_spec *res_spec)629{630	char buf[512];631	usnic_vnic_spec_dump(buf, sizeof(buf), res_spec);632	usnic_dbg("%s\n", buf);633}634 635static int qp_grp_id_from_flow(struct usnic_ib_qp_grp_flow *qp_flow,636				uint32_t *id)637{638	enum usnic_transport_type trans_type = qp_flow->trans_type;639	int err;640	uint16_t port_num = 0;641 642	switch (trans_type) {643	case USNIC_TRANSPORT_ROCE_CUSTOM:644		*id = qp_flow->usnic_roce.port_num;645		break;646	case USNIC_TRANSPORT_IPV4_UDP:647		err = usnic_transport_sock_get_addr(qp_flow->udp.sock,648							NULL, NULL,649							&port_num);650		if (err)651			return err;652		/*653		 * Copy port_num to stack first and then to *id,654		 * so that the short to int cast works for little655		 * and big endian systems.656		 */657		*id = port_num;658		break;659	default:660		usnic_err("Unsupported transport %u\n", trans_type);661		return -EINVAL;662	}663 664	return 0;665}666 667int usnic_ib_qp_grp_create(struct usnic_ib_qp_grp *qp_grp,668			   struct usnic_fwd_dev *ufdev, struct usnic_ib_vf *vf,669			   struct usnic_ib_pd *pd,670			   struct usnic_vnic_res_spec *res_spec,671			   struct usnic_transport_spec *transport_spec)672{673	int err;674	enum usnic_transport_type transport = transport_spec->trans_type;675	struct usnic_ib_qp_grp_flow *qp_flow;676 677	lockdep_assert_held(&vf->lock);678 679	err = usnic_vnic_res_spec_satisfied(&min_transport_spec[transport],680						res_spec);681	if (err) {682		usnic_err("Spec does not meet minimum req for transport %d\n",683				transport);684		log_spec(res_spec);685		return err;686	}687 688	qp_grp->res_chunk_list = alloc_res_chunk_list(vf->vnic, res_spec,689							qp_grp);690	if (IS_ERR_OR_NULL(qp_grp->res_chunk_list))691		return qp_grp->res_chunk_list ?692				     PTR_ERR(qp_grp->res_chunk_list) :693				     -ENOMEM;694 695	err = qp_grp_and_vf_bind(vf, pd, qp_grp);696	if (err)697		goto out_free_res;698 699	INIT_LIST_HEAD(&qp_grp->flows_lst);700	spin_lock_init(&qp_grp->lock);701	qp_grp->ufdev = ufdev;702	qp_grp->state = IB_QPS_RESET;703	qp_grp->owner_pid = current->pid;704 705	qp_flow = create_and_add_flow(qp_grp, transport_spec);706	if (IS_ERR_OR_NULL(qp_flow)) {707		usnic_err("Unable to create and add flow with err %ld\n",708				PTR_ERR(qp_flow));709		err = qp_flow ? PTR_ERR(qp_flow) : -EFAULT;710		goto out_qp_grp_vf_unbind;711	}712 713	err = qp_grp_id_from_flow(qp_flow, &qp_grp->grp_id);714	if (err)715		goto out_release_flow;716	qp_grp->ibqp.qp_num = qp_grp->grp_id;717 718	usnic_ib_sysfs_qpn_add(qp_grp);719 720	return 0;721 722out_release_flow:723	release_and_remove_flow(qp_flow);724out_qp_grp_vf_unbind:725	qp_grp_and_vf_unbind(qp_grp);726out_free_res:727	free_qp_grp_res(qp_grp->res_chunk_list);728	return err;729}730 731void usnic_ib_qp_grp_destroy(struct usnic_ib_qp_grp *qp_grp)732{733 734	WARN_ON(qp_grp->state != IB_QPS_RESET);735	lockdep_assert_held(&qp_grp->vf->lock);736 737	release_and_remove_all_flows(qp_grp);738	usnic_ib_sysfs_qpn_remove(qp_grp);739	qp_grp_and_vf_unbind(qp_grp);740	free_qp_grp_res(qp_grp->res_chunk_list);741}742 743struct usnic_vnic_res_chunk*744usnic_ib_qp_grp_get_chunk(struct usnic_ib_qp_grp *qp_grp,745				enum usnic_vnic_res_type res_type)746{747	int i;748 749	for (i = 0; qp_grp->res_chunk_list[i]; i++) {750		if (qp_grp->res_chunk_list[i]->type == res_type)751			return qp_grp->res_chunk_list[i];752	}753 754	return ERR_PTR(-EINVAL);755}756