diff options
| author | Rusty Russell <rusty@rustcorp.com.au> | 2014-12-08 03:50:37 -0500 |
|---|---|---|
| committer | Herbert Xu <herbert@gondor.apana.org.au> | 2014-12-22 07:02:39 -0500 |
| commit | 3a2c0ba5ad00c018c0bef39a2224aca950aa33f2 (patch) | |
| tree | 1fbf69fb820c0fa2e6050f8caaecff424c73bd8d /drivers/char/hw_random/core.c | |
| parent | 1dacb395d68a14825ee48c0843335e3181aea675 (diff) | |
hwrng: use reference counts on each struct hwrng.
current_rng holds one reference, and we bump it every time we want
to do a read from it.
This means we only hold the rng_mutex to grab or drop a reference,
so accessing /sys/devices/virtual/misc/hw_random/rng_current doesn't
block on read of /dev/hwrng.
Using a kref is overkill (we're always under the rng_mutex), but
a standard pattern.
This also solves the problem that the hwrng_fillfn thread was
accessing current_rng without a lock, which could change (eg. to NULL)
underneath it.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Amos Kong <akong@redhat.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'drivers/char/hw_random/core.c')
| -rw-r--r-- | drivers/char/hw_random/core.c | 135 |
1 files changed, 92 insertions, 43 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index b4c0e873d362..089c18dc579e 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c | |||
| @@ -42,6 +42,7 @@ | |||
| 42 | #include <linux/delay.h> | 42 | #include <linux/delay.h> |
| 43 | #include <linux/slab.h> | 43 | #include <linux/slab.h> |
| 44 | #include <linux/random.h> | 44 | #include <linux/random.h> |
| 45 | #include <linux/err.h> | ||
| 45 | #include <asm/uaccess.h> | 46 | #include <asm/uaccess.h> |
| 46 | 47 | ||
| 47 | 48 | ||
| @@ -91,6 +92,60 @@ static void add_early_randomness(struct hwrng *rng) | |||
| 91 | add_device_randomness(bytes, bytes_read); | 92 | add_device_randomness(bytes, bytes_read); |
| 92 | } | 93 | } |
| 93 | 94 | ||
| 95 | static inline void cleanup_rng(struct kref *kref) | ||
| 96 | { | ||
| 97 | struct hwrng *rng = container_of(kref, struct hwrng, ref); | ||
| 98 | |||
| 99 | if (rng->cleanup) | ||
| 100 | rng->cleanup(rng); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void set_current_rng(struct hwrng *rng) | ||
| 104 | { | ||
| 105 | BUG_ON(!mutex_is_locked(&rng_mutex)); | ||
| 106 | kref_get(&rng->ref); | ||
| 107 | current_rng = rng; | ||
| 108 | } | ||
| 109 | |||
| 110 | static void drop_current_rng(void) | ||
| 111 | { | ||
| 112 | BUG_ON(!mutex_is_locked(&rng_mutex)); | ||
| 113 | if (!current_rng) | ||
| 114 | return; | ||
| 115 | |||
| 116 | /* decrease last reference for triggering the cleanup */ | ||
| 117 | kref_put(¤t_rng->ref, cleanup_rng); | ||
| 118 | current_rng = NULL; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Returns ERR_PTR(), NULL or refcounted hwrng */ | ||
| 122 | static struct hwrng *get_current_rng(void) | ||
| 123 | { | ||
| 124 | struct hwrng *rng; | ||
| 125 | |||
| 126 | if (mutex_lock_interruptible(&rng_mutex)) | ||
| 127 | return ERR_PTR(-ERESTARTSYS); | ||
| 128 | |||
| 129 | rng = current_rng; | ||
| 130 | if (rng) | ||
| 131 | kref_get(&rng->ref); | ||
| 132 | |||
| 133 | mutex_unlock(&rng_mutex); | ||
| 134 | return rng; | ||
| 135 | } | ||
| 136 | |||
| 137 | static void put_rng(struct hwrng *rng) | ||
| 138 | { | ||
| 139 | /* | ||
| 140 | * Hold rng_mutex here so we serialize in case they set_current_rng | ||
| 141 | * on rng again immediately. | ||
| 142 | */ | ||
| 143 | mutex_lock(&rng_mutex); | ||
| 144 | if (rng) | ||
| 145 | kref_put(&rng->ref, cleanup_rng); | ||
| 146 | mutex_unlock(&rng_mutex); | ||
| 147 | } | ||
| 148 | |||
| 94 | static inline int hwrng_init(struct hwrng *rng) | 149 | static inline int hwrng_init(struct hwrng *rng) |
| 95 | { | 150 | { |
| 96 | if (rng->init) { | 151 | if (rng->init) { |
| @@ -113,12 +168,6 @@ static inline int hwrng_init(struct hwrng *rng) | |||
| 113 | return 0; | 168 | return 0; |
| 114 | } | 169 | } |
| 115 | 170 | ||
| 116 | static inline void hwrng_cleanup(struct hwrng *rng) | ||
| 117 | { | ||
| 118 | if (rng && rng->cleanup) | ||
| 119 | rng->cleanup(rng); | ||
| 120 | } | ||
| 121 | |||
| 122 | static int rng_dev_open(struct inode *inode, struct file *filp) | 171 | static int rng_dev_open(struct inode *inode, struct file *filp) |
| 123 | { | 172 | { |
| 124 | /* enforce read-only access to this chrdev */ | 173 | /* enforce read-only access to this chrdev */ |
| @@ -154,21 +203,22 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, | |||
| 154 | ssize_t ret = 0; | 203 | ssize_t ret = 0; |
| 155 | int err = 0; | 204 | int err = 0; |
| 156 | int bytes_read, len; | 205 | int bytes_read, len; |
| 206 | struct hwrng *rng; | ||
| 157 | 207 | ||
| 158 | while (size) { | 208 | while (size) { |
| 159 | if (mutex_lock_interruptible(&rng_mutex)) { | 209 | rng = get_current_rng(); |
| 160 | err = -ERESTARTSYS; | 210 | if (IS_ERR(rng)) { |
| 211 | err = PTR_ERR(rng); | ||
| 161 | goto out; | 212 | goto out; |
| 162 | } | 213 | } |
| 163 | 214 | if (!rng) { | |
| 164 | if (!current_rng) { | ||
| 165 | err = -ENODEV; | 215 | err = -ENODEV; |
| 166 | goto out_unlock; | 216 | goto out; |
| 167 | } | 217 | } |
| 168 | 218 | ||
| 169 | mutex_lock(&reading_mutex); | 219 | mutex_lock(&reading_mutex); |
| 170 | if (!data_avail) { | 220 | if (!data_avail) { |
| 171 | bytes_read = rng_get_data(current_rng, rng_buffer, | 221 | bytes_read = rng_get_data(rng, rng_buffer, |
| 172 | rng_buffer_size(), | 222 | rng_buffer_size(), |
| 173 | !(filp->f_flags & O_NONBLOCK)); | 223 | !(filp->f_flags & O_NONBLOCK)); |
| 174 | if (bytes_read < 0) { | 224 | if (bytes_read < 0) { |
| @@ -200,8 +250,8 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, | |||
| 200 | ret += len; | 250 | ret += len; |
| 201 | } | 251 | } |
| 202 | 252 | ||
| 203 | mutex_unlock(&rng_mutex); | ||
| 204 | mutex_unlock(&reading_mutex); | 253 | mutex_unlock(&reading_mutex); |
| 254 | put_rng(rng); | ||
| 205 | 255 | ||
| 206 | if (need_resched()) | 256 | if (need_resched()) |
| 207 | schedule_timeout_interruptible(1); | 257 | schedule_timeout_interruptible(1); |
| @@ -213,12 +263,11 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf, | |||
| 213 | } | 263 | } |
| 214 | out: | 264 | out: |
| 215 | return ret ? : err; | 265 | return ret ? : err; |
| 216 | out_unlock: | 266 | |
| 217 | mutex_unlock(&rng_mutex); | ||
| 218 | goto out; | ||
| 219 | out_unlock_reading: | 267 | out_unlock_reading: |
| 220 | mutex_unlock(&reading_mutex); | 268 | mutex_unlock(&reading_mutex); |
| 221 | goto out_unlock; | 269 | put_rng(rng); |
| 270 | goto out; | ||
| 222 | } | 271 | } |
| 223 | 272 | ||
| 224 | 273 | ||
| @@ -257,8 +306,8 @@ static ssize_t hwrng_attr_current_store(struct device *dev, | |||
| 257 | err = hwrng_init(rng); | 306 | err = hwrng_init(rng); |
| 258 | if (err) | 307 | if (err) |
| 259 | break; | 308 | break; |
| 260 | hwrng_cleanup(current_rng); | 309 | drop_current_rng(); |
| 261 | current_rng = rng; | 310 | set_current_rng(rng); |
| 262 | err = 0; | 311 | err = 0; |
| 263 | break; | 312 | break; |
| 264 | } | 313 | } |
| @@ -272,17 +321,15 @@ static ssize_t hwrng_attr_current_show(struct device *dev, | |||
| 272 | struct device_attribute *attr, | 321 | struct device_attribute *attr, |
| 273 | char *buf) | 322 | char *buf) |
| 274 | { | 323 | { |
| 275 | int err; | ||
| 276 | ssize_t ret; | 324 | ssize_t ret; |
| 277 | const char *name = "none"; | 325 | struct hwrng *rng; |
| 278 | 326 | ||
| 279 | err = mutex_lock_interruptible(&rng_mutex); | 327 | rng = get_current_rng(); |
| 280 | if (err) | 328 | if (IS_ERR(rng)) |
| 281 | return -ERESTARTSYS; | 329 | return PTR_ERR(rng); |
| 282 | if (current_rng) | 330 | |
| 283 | name = current_rng->name; | 331 | ret = snprintf(buf, PAGE_SIZE, "%s\n", rng ? rng->name : "none"); |
| 284 | ret = snprintf(buf, PAGE_SIZE, "%s\n", name); | 332 | put_rng(rng); |
| 285 | mutex_unlock(&rng_mutex); | ||
| 286 | 333 | ||
| 287 | return ret; | 334 | return ret; |
| 288 | } | 335 | } |
| @@ -353,12 +400,16 @@ static int hwrng_fillfn(void *unused) | |||
| 353 | long rc; | 400 | long rc; |
| 354 | 401 | ||
| 355 | while (!kthread_should_stop()) { | 402 | while (!kthread_should_stop()) { |
| 356 | if (!current_rng) | 403 | struct hwrng *rng; |
| 404 | |||
| 405 | rng = get_current_rng(); | ||
| 406 | if (IS_ERR(rng) || !rng) | ||
| 357 | break; | 407 | break; |
| 358 | mutex_lock(&reading_mutex); | 408 | mutex_lock(&reading_mutex); |
| 359 | rc = rng_get_data(current_rng, rng_fillbuf, | 409 | rc = rng_get_data(rng, rng_fillbuf, |
| 360 | rng_buffer_size(), 1); | 410 | rng_buffer_size(), 1); |
| 361 | mutex_unlock(&reading_mutex); | 411 | mutex_unlock(&reading_mutex); |
| 412 | put_rng(rng); | ||
| 362 | if (rc <= 0) { | 413 | if (rc <= 0) { |
| 363 | pr_warn("hwrng: no data available\n"); | 414 | pr_warn("hwrng: no data available\n"); |
| 364 | msleep_interruptible(10000); | 415 | msleep_interruptible(10000); |
| @@ -419,14 +470,13 @@ int hwrng_register(struct hwrng *rng) | |||
| 419 | err = hwrng_init(rng); | 470 | err = hwrng_init(rng); |
| 420 | if (err) | 471 | if (err) |
| 421 | goto out_unlock; | 472 | goto out_unlock; |
| 422 | current_rng = rng; | 473 | set_current_rng(rng); |
| 423 | } | 474 | } |
| 424 | err = 0; | 475 | err = 0; |
| 425 | if (!old_rng) { | 476 | if (!old_rng) { |
| 426 | err = register_miscdev(); | 477 | err = register_miscdev(); |
| 427 | if (err) { | 478 | if (err) { |
| 428 | hwrng_cleanup(rng); | 479 | drop_current_rng(); |
| 429 | current_rng = NULL; | ||
| 430 | goto out_unlock; | 480 | goto out_unlock; |
| 431 | } | 481 | } |
| 432 | } | 482 | } |
| @@ -453,22 +503,21 @@ EXPORT_SYMBOL_GPL(hwrng_register); | |||
| 453 | 503 | ||
| 454 | void hwrng_unregister(struct hwrng *rng) | 504 | void hwrng_unregister(struct hwrng *rng) |
| 455 | { | 505 | { |
| 456 | int err; | ||
| 457 | |||
| 458 | mutex_lock(&rng_mutex); | 506 | mutex_lock(&rng_mutex); |
| 459 | 507 | ||
| 460 | list_del(&rng->list); | 508 | list_del(&rng->list); |
| 461 | if (current_rng == rng) { | 509 | if (current_rng == rng) { |
| 462 | hwrng_cleanup(rng); | 510 | drop_current_rng(); |
| 463 | if (list_empty(&rng_list)) { | 511 | if (!list_empty(&rng_list)) { |
| 464 | current_rng = NULL; | 512 | struct hwrng *tail; |
| 465 | } else { | 513 | |
| 466 | current_rng = list_entry(rng_list.prev, struct hwrng, list); | 514 | tail = list_entry(rng_list.prev, struct hwrng, list); |
| 467 | err = hwrng_init(current_rng); | 515 | |
| 468 | if (err) | 516 | if (hwrng_init(tail) == 0) |
| 469 | current_rng = NULL; | 517 | set_current_rng(tail); |
| 470 | } | 518 | } |
| 471 | } | 519 | } |
| 520 | |||
| 472 | if (list_empty(&rng_list)) { | 521 | if (list_empty(&rng_list)) { |
| 473 | mutex_unlock(&rng_mutex); | 522 | mutex_unlock(&rng_mutex); |
| 474 | unregister_miscdev(); | 523 | unregister_miscdev(); |
