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