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