1dbaf0624SGonglei  /* Management for virtio crypto devices (refer to adf_dev_mgr.c)
2dbaf0624SGonglei   *
3dbaf0624SGonglei   * Copyright 2016 HUAWEI TECHNOLOGIES CO., LTD.
4dbaf0624SGonglei   *
5dbaf0624SGonglei   * This program is free software; you can redistribute it and/or modify
6dbaf0624SGonglei   * it under the terms of the GNU General Public License as published by
7dbaf0624SGonglei   * the Free Software Foundation; either version 2 of the License, or
8dbaf0624SGonglei   * (at your option) any later version.
9dbaf0624SGonglei   *
10dbaf0624SGonglei   * This program is distributed in the hope that it will be useful,
11dbaf0624SGonglei   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12dbaf0624SGonglei   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13dbaf0624SGonglei   * GNU General Public License for more details.
14dbaf0624SGonglei   *
15dbaf0624SGonglei   * You should have received a copy of the GNU General Public License
16dbaf0624SGonglei   * along with this program; if not, see <http://www.gnu.org/licenses/>.
17dbaf0624SGonglei   */
18dbaf0624SGonglei 
19dbaf0624SGonglei #include <linux/mutex.h>
20dbaf0624SGonglei #include <linux/list.h>
21dbaf0624SGonglei #include <linux/module.h>
22dbaf0624SGonglei 
23dbaf0624SGonglei #include <uapi/linux/virtio_crypto.h>
24dbaf0624SGonglei #include "virtio_crypto_common.h"
25dbaf0624SGonglei 
26dbaf0624SGonglei static LIST_HEAD(virtio_crypto_table);
27dbaf0624SGonglei static uint32_t num_devices;
28dbaf0624SGonglei 
29dbaf0624SGonglei /* The table_lock protects the above global list and num_devices */
30dbaf0624SGonglei static DEFINE_MUTEX(table_lock);
31dbaf0624SGonglei 
32dbaf0624SGonglei #define VIRTIO_CRYPTO_MAX_DEVICES 32
33dbaf0624SGonglei 
34dbaf0624SGonglei 
35dbaf0624SGonglei /*
36dbaf0624SGonglei  * virtcrypto_devmgr_add_dev() - Add vcrypto_dev to the acceleration
37dbaf0624SGonglei  * framework.
38dbaf0624SGonglei  * @vcrypto_dev:  Pointer to virtio crypto device.
39dbaf0624SGonglei  *
40dbaf0624SGonglei  * Function adds virtio crypto device to the global list.
41dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
42dbaf0624SGonglei  *
43dbaf0624SGonglei  * Return: 0 on success, error code othewise.
44dbaf0624SGonglei  */
45dbaf0624SGonglei int virtcrypto_devmgr_add_dev(struct virtio_crypto *vcrypto_dev)
46dbaf0624SGonglei {
47dbaf0624SGonglei 	struct list_head *itr;
48dbaf0624SGonglei 
49dbaf0624SGonglei 	mutex_lock(&table_lock);
50dbaf0624SGonglei 	if (num_devices == VIRTIO_CRYPTO_MAX_DEVICES) {
51dbaf0624SGonglei 		pr_info("virtio_crypto: only support up to %d devices\n",
52dbaf0624SGonglei 			    VIRTIO_CRYPTO_MAX_DEVICES);
53dbaf0624SGonglei 		mutex_unlock(&table_lock);
54dbaf0624SGonglei 		return -EFAULT;
55dbaf0624SGonglei 	}
56dbaf0624SGonglei 
57dbaf0624SGonglei 	list_for_each(itr, &virtio_crypto_table) {
58dbaf0624SGonglei 		struct virtio_crypto *ptr =
59dbaf0624SGonglei 				list_entry(itr, struct virtio_crypto, list);
60dbaf0624SGonglei 
61dbaf0624SGonglei 		if (ptr == vcrypto_dev) {
62dbaf0624SGonglei 			mutex_unlock(&table_lock);
63dbaf0624SGonglei 			return -EEXIST;
64dbaf0624SGonglei 		}
65dbaf0624SGonglei 	}
66dbaf0624SGonglei 	atomic_set(&vcrypto_dev->ref_count, 0);
67dbaf0624SGonglei 	list_add_tail(&vcrypto_dev->list, &virtio_crypto_table);
68dbaf0624SGonglei 	vcrypto_dev->dev_id = num_devices++;
69dbaf0624SGonglei 	mutex_unlock(&table_lock);
70dbaf0624SGonglei 	return 0;
71dbaf0624SGonglei }
72dbaf0624SGonglei 
73dbaf0624SGonglei struct list_head *virtcrypto_devmgr_get_head(void)
74dbaf0624SGonglei {
75dbaf0624SGonglei 	return &virtio_crypto_table;
76dbaf0624SGonglei }
77dbaf0624SGonglei 
78dbaf0624SGonglei /*
79dbaf0624SGonglei  * virtcrypto_devmgr_rm_dev() - Remove vcrypto_dev from the acceleration
80dbaf0624SGonglei  * framework.
81dbaf0624SGonglei  * @vcrypto_dev:  Pointer to virtio crypto device.
82dbaf0624SGonglei  *
83dbaf0624SGonglei  * Function removes virtio crypto device from the acceleration framework.
84dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
85dbaf0624SGonglei  *
86dbaf0624SGonglei  * Return: void
87dbaf0624SGonglei  */
88dbaf0624SGonglei void virtcrypto_devmgr_rm_dev(struct virtio_crypto *vcrypto_dev)
89dbaf0624SGonglei {
90dbaf0624SGonglei 	mutex_lock(&table_lock);
91dbaf0624SGonglei 	list_del(&vcrypto_dev->list);
92dbaf0624SGonglei 	num_devices--;
93dbaf0624SGonglei 	mutex_unlock(&table_lock);
94dbaf0624SGonglei }
95dbaf0624SGonglei 
96dbaf0624SGonglei /*
97dbaf0624SGonglei  * virtcrypto_devmgr_get_first()
98dbaf0624SGonglei  *
99dbaf0624SGonglei  * Function returns the first virtio crypto device from the acceleration
100dbaf0624SGonglei  * framework.
101dbaf0624SGonglei  *
102dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
103dbaf0624SGonglei  *
104dbaf0624SGonglei  * Return: pointer to vcrypto_dev or NULL if not found.
105dbaf0624SGonglei  */
106dbaf0624SGonglei struct virtio_crypto *virtcrypto_devmgr_get_first(void)
107dbaf0624SGonglei {
108dbaf0624SGonglei 	struct virtio_crypto *dev = NULL;
109dbaf0624SGonglei 
110dbaf0624SGonglei 	mutex_lock(&table_lock);
111dbaf0624SGonglei 	if (!list_empty(&virtio_crypto_table))
112dbaf0624SGonglei 		dev = list_first_entry(&virtio_crypto_table,
113dbaf0624SGonglei 					struct virtio_crypto,
114dbaf0624SGonglei 				    list);
115dbaf0624SGonglei 	mutex_unlock(&table_lock);
116dbaf0624SGonglei 	return dev;
117dbaf0624SGonglei }
118dbaf0624SGonglei 
119dbaf0624SGonglei /*
120dbaf0624SGonglei  * virtcrypto_dev_in_use() - Check whether vcrypto_dev is currently in use
121dbaf0624SGonglei  * @vcrypto_dev: Pointer to virtio crypto device.
122dbaf0624SGonglei  *
123dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
124dbaf0624SGonglei  *
125dbaf0624SGonglei  * Return: 1 when device is in use, 0 otherwise.
126dbaf0624SGonglei  */
127dbaf0624SGonglei int virtcrypto_dev_in_use(struct virtio_crypto *vcrypto_dev)
128dbaf0624SGonglei {
129dbaf0624SGonglei 	return atomic_read(&vcrypto_dev->ref_count) != 0;
130dbaf0624SGonglei }
131dbaf0624SGonglei 
132dbaf0624SGonglei /*
133dbaf0624SGonglei  * virtcrypto_dev_get() - Increment vcrypto_dev reference count
134dbaf0624SGonglei  * @vcrypto_dev: Pointer to virtio crypto device.
135dbaf0624SGonglei  *
136dbaf0624SGonglei  * Increment the vcrypto_dev refcount and if this is the first time
137dbaf0624SGonglei  * incrementing it during this period the vcrypto_dev is in use,
138dbaf0624SGonglei  * increment the module refcount too.
139dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
140dbaf0624SGonglei  *
141dbaf0624SGonglei  * Return: 0 when successful, EFAULT when fail to bump module refcount
142dbaf0624SGonglei  */
143dbaf0624SGonglei int virtcrypto_dev_get(struct virtio_crypto *vcrypto_dev)
144dbaf0624SGonglei {
145dbaf0624SGonglei 	if (atomic_add_return(1, &vcrypto_dev->ref_count) == 1)
146dbaf0624SGonglei 		if (!try_module_get(vcrypto_dev->owner))
147dbaf0624SGonglei 			return -EFAULT;
148dbaf0624SGonglei 	return 0;
149dbaf0624SGonglei }
150dbaf0624SGonglei 
151dbaf0624SGonglei /*
152dbaf0624SGonglei  * virtcrypto_dev_put() - Decrement vcrypto_dev reference count
153dbaf0624SGonglei  * @vcrypto_dev: Pointer to virtio crypto device.
154dbaf0624SGonglei  *
155dbaf0624SGonglei  * Decrement the vcrypto_dev refcount and if this is the last time
156dbaf0624SGonglei  * decrementing it during this period the vcrypto_dev is in use,
157dbaf0624SGonglei  * decrement the module refcount too.
158dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
159dbaf0624SGonglei  *
160dbaf0624SGonglei  * Return: void
161dbaf0624SGonglei  */
162dbaf0624SGonglei void virtcrypto_dev_put(struct virtio_crypto *vcrypto_dev)
163dbaf0624SGonglei {
164dbaf0624SGonglei 	if (atomic_sub_return(1, &vcrypto_dev->ref_count) == 0)
165dbaf0624SGonglei 		module_put(vcrypto_dev->owner);
166dbaf0624SGonglei }
167dbaf0624SGonglei 
168dbaf0624SGonglei /*
169dbaf0624SGonglei  * virtcrypto_dev_started() - Check whether device has started
170dbaf0624SGonglei  * @vcrypto_dev: Pointer to virtio crypto device.
171dbaf0624SGonglei  *
172dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
173dbaf0624SGonglei  *
174dbaf0624SGonglei  * Return: 1 when the device has started, 0 otherwise
175dbaf0624SGonglei  */
176dbaf0624SGonglei int virtcrypto_dev_started(struct virtio_crypto *vcrypto_dev)
177dbaf0624SGonglei {
178dbaf0624SGonglei 	return (vcrypto_dev->status & VIRTIO_CRYPTO_S_HW_READY);
179dbaf0624SGonglei }
180dbaf0624SGonglei 
181dbaf0624SGonglei /*
182dbaf0624SGonglei  * virtcrypto_get_dev_node() - Get vcrypto_dev on the node.
183dbaf0624SGonglei  * @node:  Node id the driver works.
184dbaf0624SGonglei  *
185dbaf0624SGonglei  * Function returns the virtio crypto device used fewest on the node.
186dbaf0624SGonglei  *
187dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
188dbaf0624SGonglei  *
189dbaf0624SGonglei  * Return: pointer to vcrypto_dev or NULL if not found.
190dbaf0624SGonglei  */
191dbaf0624SGonglei struct virtio_crypto *virtcrypto_get_dev_node(int node)
192dbaf0624SGonglei {
193dbaf0624SGonglei 	struct virtio_crypto *vcrypto_dev = NULL, *tmp_dev;
194dbaf0624SGonglei 	unsigned long best = ~0;
195dbaf0624SGonglei 	unsigned long ctr;
196dbaf0624SGonglei 
197dbaf0624SGonglei 	mutex_lock(&table_lock);
198dbaf0624SGonglei 	list_for_each_entry(tmp_dev, virtcrypto_devmgr_get_head(), list) {
199dbaf0624SGonglei 
200dbaf0624SGonglei 		if ((node == dev_to_node(&tmp_dev->vdev->dev) ||
201dbaf0624SGonglei 		     dev_to_node(&tmp_dev->vdev->dev) < 0) &&
202dbaf0624SGonglei 		    virtcrypto_dev_started(tmp_dev)) {
203dbaf0624SGonglei 			ctr = atomic_read(&tmp_dev->ref_count);
204dbaf0624SGonglei 			if (best > ctr) {
205dbaf0624SGonglei 				vcrypto_dev = tmp_dev;
206dbaf0624SGonglei 				best = ctr;
207dbaf0624SGonglei 			}
208dbaf0624SGonglei 		}
209dbaf0624SGonglei 	}
210dbaf0624SGonglei 
211dbaf0624SGonglei 	if (!vcrypto_dev) {
212dbaf0624SGonglei 		pr_info("virtio_crypto: Could not find a device on node %d\n",
213dbaf0624SGonglei 				node);
214dbaf0624SGonglei 		/* Get any started device */
215dbaf0624SGonglei 		list_for_each_entry(tmp_dev,
216dbaf0624SGonglei 				virtcrypto_devmgr_get_head(), list) {
217dbaf0624SGonglei 			if (virtcrypto_dev_started(tmp_dev)) {
218dbaf0624SGonglei 				vcrypto_dev = tmp_dev;
219dbaf0624SGonglei 				break;
220dbaf0624SGonglei 			}
221dbaf0624SGonglei 		}
222dbaf0624SGonglei 	}
223dbaf0624SGonglei 	mutex_unlock(&table_lock);
224dbaf0624SGonglei 	if (!vcrypto_dev)
225dbaf0624SGonglei 		return NULL;
226dbaf0624SGonglei 
227dbaf0624SGonglei 	virtcrypto_dev_get(vcrypto_dev);
228dbaf0624SGonglei 	return vcrypto_dev;
229dbaf0624SGonglei }
230dbaf0624SGonglei 
231dbaf0624SGonglei /*
232dbaf0624SGonglei  * virtcrypto_dev_start() - Start virtio crypto device
233dbaf0624SGonglei  * @vcrypto:    Pointer to virtio crypto device.
234dbaf0624SGonglei  *
235dbaf0624SGonglei  * Function notifies all the registered services that the virtio crypto device
236dbaf0624SGonglei  * is ready to be used.
237dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
238dbaf0624SGonglei  *
239dbaf0624SGonglei  * Return: 0 on success, EFAULT when fail to register algorithms
240dbaf0624SGonglei  */
241dbaf0624SGonglei int virtcrypto_dev_start(struct virtio_crypto *vcrypto)
242dbaf0624SGonglei {
243dbaf0624SGonglei 	if (virtio_crypto_algs_register()) {
244dbaf0624SGonglei 		pr_err("virtio_crypto: Failed to register crypto algs\n");
245dbaf0624SGonglei 		return -EFAULT;
246dbaf0624SGonglei 	}
247dbaf0624SGonglei 
248dbaf0624SGonglei 	return 0;
249dbaf0624SGonglei }
250dbaf0624SGonglei 
251dbaf0624SGonglei /*
252dbaf0624SGonglei  * virtcrypto_dev_stop() - Stop virtio crypto device
253dbaf0624SGonglei  * @vcrypto:    Pointer to virtio crypto device.
254dbaf0624SGonglei  *
255dbaf0624SGonglei  * Function notifies all the registered services that the virtio crypto device
256dbaf0624SGonglei  * is ready to be used.
257dbaf0624SGonglei  * To be used by virtio crypto device specific drivers.
258dbaf0624SGonglei  *
259dbaf0624SGonglei  * Return: void
260dbaf0624SGonglei  */
261dbaf0624SGonglei void virtcrypto_dev_stop(struct virtio_crypto *vcrypto)
262dbaf0624SGonglei {
263dbaf0624SGonglei 	virtio_crypto_algs_unregister();
264dbaf0624SGonglei }
265