xref: /freebsd/sys/contrib/openzfs/module/icp/core/kcf_callprov.c (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or https://opensource.org/licenses/CDDL-1.0.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/crypto/common.h>
28 #include <sys/crypto/impl.h>
29 #include <sys/crypto/sched_impl.h>
30 
31 void
kcf_free_triedlist(kcf_prov_tried_t * list)32 kcf_free_triedlist(kcf_prov_tried_t *list)
33 {
34 	kcf_prov_tried_t *l;
35 
36 	while ((l = list) != NULL) {
37 		list = list->pt_next;
38 		KCF_PROV_REFRELE(l->pt_pd);
39 		kmem_free(l, sizeof (kcf_prov_tried_t));
40 	}
41 }
42 
43 kcf_prov_tried_t *
kcf_insert_triedlist(kcf_prov_tried_t ** list,kcf_provider_desc_t * pd,int kmflag)44 kcf_insert_triedlist(kcf_prov_tried_t **list, kcf_provider_desc_t *pd,
45     int kmflag)
46 {
47 	kcf_prov_tried_t *l;
48 
49 	l = kmem_alloc(sizeof (kcf_prov_tried_t), kmflag);
50 	if (l == NULL)
51 		return (NULL);
52 
53 	l->pt_pd = pd;
54 	l->pt_next = *list;
55 	*list = l;
56 
57 	return (l);
58 }
59 
60 static boolean_t
is_in_triedlist(kcf_provider_desc_t * pd,kcf_prov_tried_t * triedl)61 is_in_triedlist(kcf_provider_desc_t *pd, kcf_prov_tried_t *triedl)
62 {
63 	while (triedl != NULL) {
64 		if (triedl->pt_pd == pd)
65 			return (B_TRUE);
66 		triedl = triedl->pt_next;
67 	}
68 
69 	return (B_FALSE);
70 }
71 
72 /*
73  * Return the best provider for the specified mechanism. The provider
74  * is held and it is the caller's responsibility to release it when done.
75  * The fg input argument is used as a search criterion to pick a provider.
76  * A provider has to support this function group to be picked.
77  *
78  * Find the least loaded provider in the list of providers. We do a linear
79  * search to find one. This is fine as we assume there are only a few
80  * number of providers in this list. If this assumption ever changes,
81  * we should revisit this.
82  */
83 kcf_provider_desc_t *
kcf_get_mech_provider(crypto_mech_type_t mech_type,kcf_mech_entry_t ** mepp,int * error,kcf_prov_tried_t * triedl,crypto_func_group_t fg)84 kcf_get_mech_provider(crypto_mech_type_t mech_type, kcf_mech_entry_t **mepp,
85     int *error, kcf_prov_tried_t *triedl, crypto_func_group_t fg)
86 {
87 	kcf_provider_desc_t *pd = NULL;
88 	kcf_prov_mech_desc_t *mdesc;
89 	kcf_ops_class_t class;
90 	int index;
91 	kcf_mech_entry_t *me;
92 	const kcf_mech_entry_tab_t *me_tab;
93 
94 	class = KCF_MECH2CLASS(mech_type);
95 	if ((class < KCF_FIRST_OPSCLASS) || (class > KCF_LAST_OPSCLASS)) {
96 		*error = CRYPTO_MECHANISM_INVALID;
97 		return (NULL);
98 	}
99 
100 	me_tab = &kcf_mech_tabs_tab[class];
101 	index = KCF_MECH2INDEX(mech_type);
102 	if ((index < 0) || (index >= me_tab->met_size)) {
103 		*error = CRYPTO_MECHANISM_INVALID;
104 		return (NULL);
105 	}
106 
107 	me = &((me_tab->met_tab)[index]);
108 	if (mepp != NULL)
109 		*mepp = me;
110 
111 	/* Is there a provider? */
112 	if (pd == NULL && (mdesc = me->me_sw_prov) != NULL) {
113 		pd = mdesc->pm_prov_desc;
114 		if (!IS_FG_SUPPORTED(mdesc, fg) ||
115 		    !KCF_IS_PROV_USABLE(pd) ||
116 		    IS_PROVIDER_TRIED(pd, triedl))
117 			pd = NULL;
118 	}
119 
120 	if (pd == NULL) {
121 		/*
122 		 * We do not want to report CRYPTO_MECH_NOT_SUPPORTED, when
123 		 * we are in the "fallback to the next provider" case. Rather
124 		 * we preserve the error, so that the client gets the right
125 		 * error code.
126 		 */
127 		if (triedl == NULL)
128 			*error = CRYPTO_MECH_NOT_SUPPORTED;
129 	} else
130 		KCF_PROV_REFHOLD(pd);
131 
132 	return (pd);
133 }
134