diff options
Diffstat (limited to 'crypto/algapi.c')
| -rw-r--r-- | crypto/algapi.c | 248 | 
1 files changed, 119 insertions, 129 deletions
diff --git a/crypto/algapi.c b/crypto/algapi.c index b052f38edba6..69605e21af92 100644 --- a/crypto/algapi.c +++ b/crypto/algapi.c @@ -65,11 +65,6 @@ static int crypto_check_alg(struct crypto_alg *alg)  static void crypto_free_instance(struct crypto_instance *inst)  { -	if (!inst->alg.cra_type->free) { -		inst->tmpl->free(inst); -		return; -	} -  	inst->alg.cra_type->free(inst);  } @@ -82,6 +77,15 @@ static void crypto_destroy_instance(struct crypto_alg *alg)  	crypto_tmpl_put(tmpl);  } +/* + * This function adds a spawn to the list secondary_spawns which + * will be used at the end of crypto_remove_spawns to unregister + * instances, unless the spawn happens to be one that is depended + * on by the new algorithm (nalg in crypto_remove_spawns). + * + * This function is also responsible for resurrecting any algorithms + * in the dependency chain of nalg by unsetting n->dead. + */  static struct list_head *crypto_more_spawns(struct crypto_alg *alg,  					    struct list_head *stack,  					    struct list_head *top, @@ -93,15 +97,17 @@ static struct list_head *crypto_more_spawns(struct crypto_alg *alg,  	if (!spawn)  		return NULL; -	n = list_next_entry(spawn, list); +	n = list_prev_entry(spawn, list); +	list_move(&spawn->list, secondary_spawns); -	if (spawn->alg && &n->list != stack && !n->alg) -		n->alg = (n->list.next == stack) ? alg : -			 &list_next_entry(n, list)->inst->alg; +	if (list_is_last(&n->list, stack)) +		return top; -	list_move(&spawn->list, secondary_spawns); +	n = list_next_entry(n, list); +	if (!spawn->dead) +		n->dead = false; -	return &n->list == stack ? top : &n->inst->alg.cra_users; +	return &n->inst->alg.cra_users;  }  static void crypto_remove_instance(struct crypto_instance *inst, @@ -113,8 +119,6 @@ static void crypto_remove_instance(struct crypto_instance *inst,  		return;  	inst->alg.cra_flags |= CRYPTO_ALG_DEAD; -	if (hlist_unhashed(&inst->list)) -		return;  	if (!tmpl || !crypto_tmpl_get(tmpl))  		return; @@ -126,6 +130,12 @@ static void crypto_remove_instance(struct crypto_instance *inst,  	BUG_ON(!list_empty(&inst->alg.cra_users));  } +/* + * Given an algorithm alg, remove all algorithms that depend on it + * through spawns.  If nalg is not null, then exempt any algorithms + * that is depended on by nalg.  This is useful when nalg itself + * depends on alg. + */  void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list,  			  struct crypto_alg *nalg)  { @@ -144,6 +154,11 @@ void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list,  		list_move(&spawn->list, &top);  	} +	/* +	 * Perform a depth-first walk starting from alg through +	 * the cra_users tree.  The list stack records the path +	 * from alg to the current spawn. +	 */  	spawns = ⊤  	do {  		while (!list_empty(spawns)) { @@ -153,17 +168,26 @@ void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list,  						 list);  			inst = spawn->inst; -			BUG_ON(&inst->alg == alg); -  			list_move(&spawn->list, &stack); +			spawn->dead = !spawn->registered || &inst->alg != nalg; + +			if (!spawn->registered) +				break; + +			BUG_ON(&inst->alg == alg);  			if (&inst->alg == nalg)  				break; -			spawn->alg = NULL;  			spawns = &inst->alg.cra_users;  			/* +			 * Even if spawn->registered is true, the +			 * instance itself may still be unregistered. +			 * This is because it may have failed during +			 * registration.  Therefore we still need to +			 * make the following test. +			 *  			 * We may encounter an unregistered instance here, since  			 * an instance's spawns are set up prior to the instance  			 * being registered.  An unregistered instance will have @@ -178,10 +202,15 @@ void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list,  	} while ((spawns = crypto_more_spawns(alg, &stack, &top,  					      &secondary_spawns))); +	/* +	 * Remove all instances that are marked as dead.  Also +	 * complete the resurrection of the others by moving them +	 * back to the cra_users list. +	 */  	list_for_each_entry_safe(spawn, n, &secondary_spawns, list) { -		if (spawn->alg) +		if (!spawn->dead)  			list_move(&spawn->list, &spawn->alg->cra_users); -		else +		else if (spawn->registered)  			crypto_remove_instance(spawn->inst, list);  	}  } @@ -257,6 +286,7 @@ void crypto_alg_tested(const char *name, int err)  	struct crypto_alg *alg;  	struct crypto_alg *q;  	LIST_HEAD(list); +	bool best;  	down_write(&crypto_alg_sem);  	list_for_each_entry(q, &crypto_alg_list, cra_list) { @@ -280,6 +310,21 @@ found:  	alg->cra_flags |= CRYPTO_ALG_TESTED; +	/* Only satisfy larval waiters if we are the best. */ +	best = true; +	list_for_each_entry(q, &crypto_alg_list, cra_list) { +		if (crypto_is_moribund(q) || !crypto_is_larval(q)) +			continue; + +		if (strcmp(alg->cra_name, q->cra_name)) +			continue; + +		if (q->cra_priority > alg->cra_priority) { +			best = false; +			break; +		} +	} +  	list_for_each_entry(q, &crypto_alg_list, cra_list) {  		if (q == alg)  			continue; @@ -303,10 +348,12 @@ found:  				continue;  			if ((q->cra_flags ^ alg->cra_flags) & larval->mask)  				continue; -			if (!crypto_mod_get(alg)) -				continue; -			larval->adult = alg; +			if (best && crypto_mod_get(alg)) +				larval->adult = alg; +			else +				larval->adult = ERR_PTR(-EAGAIN); +  			continue;  		} @@ -397,7 +444,7 @@ static int crypto_remove_alg(struct crypto_alg *alg, struct list_head *list)  	return 0;  } -int crypto_unregister_alg(struct crypto_alg *alg) +void crypto_unregister_alg(struct crypto_alg *alg)  {  	int ret;  	LIST_HEAD(list); @@ -406,15 +453,14 @@ int crypto_unregister_alg(struct crypto_alg *alg)  	ret = crypto_remove_alg(alg, &list);  	up_write(&crypto_alg_sem); -	if (ret) -		return ret; +	if (WARN(ret, "Algorithm %s is not registered", alg->cra_driver_name)) +		return;  	BUG_ON(refcount_read(&alg->cra_refcnt) != 1);  	if (alg->cra_destroy)  		alg->cra_destroy(alg);  	crypto_remove_final(&list); -	return 0;  }  EXPORT_SYMBOL_GPL(crypto_unregister_alg); @@ -438,18 +484,12 @@ err:  }  EXPORT_SYMBOL_GPL(crypto_register_algs); -int crypto_unregister_algs(struct crypto_alg *algs, int count) +void crypto_unregister_algs(struct crypto_alg *algs, int count)  { -	int i, ret; - -	for (i = 0; i < count; i++) { -		ret = crypto_unregister_alg(&algs[i]); -		if (ret) -			pr_err("Failed to unregister %s %s: %d\n", -			       algs[i].cra_driver_name, algs[i].cra_name, ret); -	} +	int i; -	return 0; +	for (i = 0; i < count; i++) +		crypto_unregister_alg(&algs[i]);  }  EXPORT_SYMBOL_GPL(crypto_unregister_algs); @@ -561,6 +601,7 @@ int crypto_register_instance(struct crypto_template *tmpl,  			     struct crypto_instance *inst)  {  	struct crypto_larval *larval; +	struct crypto_spawn *spawn;  	int err;  	err = crypto_check_alg(&inst->alg); @@ -572,6 +613,22 @@ int crypto_register_instance(struct crypto_template *tmpl,  	down_write(&crypto_alg_sem); +	larval = ERR_PTR(-EAGAIN); +	for (spawn = inst->spawns; spawn;) { +		struct crypto_spawn *next; + +		if (spawn->dead) +			goto unlock; + +		next = spawn->next; +		spawn->inst = inst; +		spawn->registered = true; + +		crypto_mod_put(spawn->alg); + +		spawn = next; +	} +  	larval = __crypto_register_alg(&inst->alg);  	if (IS_ERR(larval))  		goto unlock; @@ -594,7 +651,7 @@ err:  }  EXPORT_SYMBOL_GPL(crypto_register_instance); -int crypto_unregister_instance(struct crypto_instance *inst) +void crypto_unregister_instance(struct crypto_instance *inst)  {  	LIST_HEAD(list); @@ -606,97 +663,70 @@ int crypto_unregister_instance(struct crypto_instance *inst)  	up_write(&crypto_alg_sem);  	crypto_remove_final(&list); - -	return 0;  }  EXPORT_SYMBOL_GPL(crypto_unregister_instance); -int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg, -		      struct crypto_instance *inst, u32 mask) +int crypto_grab_spawn(struct crypto_spawn *spawn, struct crypto_instance *inst, +		      const char *name, u32 type, u32 mask)  { +	struct crypto_alg *alg;  	int err = -EAGAIN;  	if (WARN_ON_ONCE(inst == NULL))  		return -EINVAL; -	spawn->inst = inst; -	spawn->mask = mask; +	/* Allow the result of crypto_attr_alg_name() to be passed directly */ +	if (IS_ERR(name)) +		return PTR_ERR(name); + +	alg = crypto_find_alg(name, spawn->frontend, type, mask); +	if (IS_ERR(alg)) +		return PTR_ERR(alg);  	down_write(&crypto_alg_sem);  	if (!crypto_is_moribund(alg)) {  		list_add(&spawn->list, &alg->cra_users);  		spawn->alg = alg; +		spawn->mask = mask; +		spawn->next = inst->spawns; +		inst->spawns = spawn;  		err = 0;  	}  	up_write(&crypto_alg_sem); - -	return err; -} -EXPORT_SYMBOL_GPL(crypto_init_spawn); - -int crypto_init_spawn2(struct crypto_spawn *spawn, struct crypto_alg *alg, -		       struct crypto_instance *inst, -		       const struct crypto_type *frontend) -{ -	int err = -EINVAL; - -	if ((alg->cra_flags ^ frontend->type) & frontend->maskset) -		goto out; - -	spawn->frontend = frontend; -	err = crypto_init_spawn(spawn, alg, inst, frontend->maskset); - -out: -	return err; -} -EXPORT_SYMBOL_GPL(crypto_init_spawn2); - -int crypto_grab_spawn(struct crypto_spawn *spawn, const char *name, -		      u32 type, u32 mask) -{ -	struct crypto_alg *alg; -	int err; - -	alg = crypto_find_alg(name, spawn->frontend, type, mask); -	if (IS_ERR(alg)) -		return PTR_ERR(alg); - -	err = crypto_init_spawn(spawn, alg, spawn->inst, mask); -	crypto_mod_put(alg); +	if (err) +		crypto_mod_put(alg);  	return err;  }  EXPORT_SYMBOL_GPL(crypto_grab_spawn);  void crypto_drop_spawn(struct crypto_spawn *spawn)  { -	if (!spawn->alg) +	if (!spawn->alg) /* not yet initialized? */  		return;  	down_write(&crypto_alg_sem); -	list_del(&spawn->list); +	if (!spawn->dead) +		list_del(&spawn->list);  	up_write(&crypto_alg_sem); + +	if (!spawn->registered) +		crypto_mod_put(spawn->alg);  }  EXPORT_SYMBOL_GPL(crypto_drop_spawn);  static struct crypto_alg *crypto_spawn_alg(struct crypto_spawn *spawn)  {  	struct crypto_alg *alg; -	struct crypto_alg *alg2;  	down_read(&crypto_alg_sem);  	alg = spawn->alg; -	alg2 = alg; -	if (alg2) -		alg2 = crypto_mod_get(alg2); -	up_read(&crypto_alg_sem); - -	if (!alg2) { -		if (alg) -			crypto_shoot_alg(alg); -		return ERR_PTR(-EAGAIN); +	if (!spawn->dead && !crypto_mod_get(alg)) { +		alg->cra_flags |= CRYPTO_ALG_DYING; +		alg = NULL;  	} +	up_read(&crypto_alg_sem); -	return alg; +	return alg ?: ERR_PTR(-EAGAIN);  }  struct crypto_tfm *crypto_spawn_tfm(struct crypto_spawn *spawn, u32 type, @@ -809,20 +839,6 @@ const char *crypto_attr_alg_name(struct rtattr *rta)  }  EXPORT_SYMBOL_GPL(crypto_attr_alg_name); -struct crypto_alg *crypto_attr_alg2(struct rtattr *rta, -				    const struct crypto_type *frontend, -				    u32 type, u32 mask) -{ -	const char *name; - -	name = crypto_attr_alg_name(rta); -	if (IS_ERR(name)) -		return ERR_CAST(name); - -	return crypto_find_alg(name, frontend, type, mask); -} -EXPORT_SYMBOL_GPL(crypto_attr_alg2); -  int crypto_attr_u32(struct rtattr *rta, u32 *num)  {  	struct crypto_attr_u32 *nu32; @@ -856,32 +872,6 @@ int crypto_inst_setname(struct crypto_instance *inst, const char *name,  }  EXPORT_SYMBOL_GPL(crypto_inst_setname); -void *crypto_alloc_instance(const char *name, struct crypto_alg *alg, -			    unsigned int head) -{ -	struct crypto_instance *inst; -	char *p; -	int err; - -	p = kzalloc(head + sizeof(*inst) + sizeof(struct crypto_spawn), -		    GFP_KERNEL); -	if (!p) -		return ERR_PTR(-ENOMEM); - -	inst = (void *)(p + head); - -	err = crypto_inst_setname(inst, name, alg); -	if (err) -		goto err_free_inst; - -	return p; - -err_free_inst: -	kfree(p); -	return ERR_PTR(err); -} -EXPORT_SYMBOL_GPL(crypto_alloc_instance); -  void crypto_init_queue(struct crypto_queue *queue, unsigned int max_qlen)  {  	INIT_LIST_HEAD(&queue->list);  |