forked from luck/tmp_suning_uos_patched
0ad0c3c644
tree b69e983266840983183a00f5ac02c66d5270ca47 parent cdd6372949b76694622ed74fe36e1dd17a92eb71 author Zhu Yi <jketreno@io.(none)> 1124435425 -0500 committer James Ketrenos <jketreno@linux.intel.com> 1127312421 -0500 Fix kernel Oops when module unload. Export a new function ieee80211_crypt_quiescing from ieee80211. Device drivers call it to make the host crypto stack enter the quiescence state, which means "process existing requests, but don't accept new ones". This is usually called during a driver's host crypto data structure free (module unload) path. Signed-off-by: James Ketrenos <jketreno@linux.intel.com> Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
281 lines
6.6 KiB
C
281 lines
6.6 KiB
C
/*
|
|
* Host AP crypto routines
|
|
*
|
|
* Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
|
|
* Portions Copyright (C) 2004, Intel Corporation <jketreno@linux.intel.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation. See README and COPYING for
|
|
* more details.
|
|
*
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
#include <linux/version.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <asm/string.h>
|
|
#include <asm/errno.h>
|
|
|
|
#include <net/ieee80211.h>
|
|
|
|
MODULE_AUTHOR("Jouni Malinen");
|
|
MODULE_DESCRIPTION("HostAP crypto");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
struct ieee80211_crypto_alg {
|
|
struct list_head list;
|
|
struct ieee80211_crypto_ops *ops;
|
|
};
|
|
|
|
struct ieee80211_crypto {
|
|
struct list_head algs;
|
|
spinlock_t lock;
|
|
};
|
|
|
|
static struct ieee80211_crypto *hcrypt;
|
|
|
|
void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, int force)
|
|
{
|
|
struct list_head *ptr, *n;
|
|
struct ieee80211_crypt_data *entry;
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&ieee->lock, flags);
|
|
|
|
if (list_empty(&ieee->crypt_deinit_list))
|
|
goto unlock;
|
|
|
|
for (ptr = ieee->crypt_deinit_list.next, n = ptr->next;
|
|
ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) {
|
|
entry = list_entry(ptr, struct ieee80211_crypt_data, list);
|
|
|
|
if (atomic_read(&entry->refcnt) != 0 && !force)
|
|
continue;
|
|
|
|
list_del(ptr);
|
|
|
|
if (entry->ops) {
|
|
entry->ops->deinit(entry->priv);
|
|
module_put(entry->ops->owner);
|
|
}
|
|
kfree(entry);
|
|
}
|
|
unlock:
|
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
}
|
|
|
|
/* After this, crypt_deinit_list won't accept new members */
|
|
void ieee80211_crypt_quiescing(struct ieee80211_device *ieee)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&ieee->lock, flags);
|
|
ieee->crypt_quiesced = 1;
|
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
}
|
|
|
|
void ieee80211_crypt_deinit_handler(unsigned long data)
|
|
{
|
|
struct ieee80211_device *ieee = (struct ieee80211_device *)data;
|
|
unsigned long flags;
|
|
|
|
ieee80211_crypt_deinit_entries(ieee, 0);
|
|
|
|
spin_lock_irqsave(&ieee->lock, flags);
|
|
if (!list_empty(&ieee->crypt_deinit_list) && !ieee->crypt_quiesced) {
|
|
printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
|
|
"deletion list\n", ieee->dev->name);
|
|
ieee->crypt_deinit_timer.expires = jiffies + HZ;
|
|
add_timer(&ieee->crypt_deinit_timer);
|
|
}
|
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
}
|
|
|
|
void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee,
|
|
struct ieee80211_crypt_data **crypt)
|
|
{
|
|
struct ieee80211_crypt_data *tmp;
|
|
unsigned long flags;
|
|
|
|
if (*crypt == NULL)
|
|
return;
|
|
|
|
tmp = *crypt;
|
|
*crypt = NULL;
|
|
|
|
/* must not run ops->deinit() while there may be pending encrypt or
|
|
* decrypt operations. Use a list of delayed deinits to avoid needing
|
|
* locking. */
|
|
|
|
spin_lock_irqsave(&ieee->lock, flags);
|
|
if (!ieee->crypt_quiesced) {
|
|
list_add(&tmp->list, &ieee->crypt_deinit_list);
|
|
if (!timer_pending(&ieee->crypt_deinit_timer)) {
|
|
ieee->crypt_deinit_timer.expires = jiffies + HZ;
|
|
add_timer(&ieee->crypt_deinit_timer);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&ieee->lock, flags);
|
|
}
|
|
|
|
int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops)
|
|
{
|
|
unsigned long flags;
|
|
struct ieee80211_crypto_alg *alg;
|
|
|
|
if (hcrypt == NULL)
|
|
return -1;
|
|
|
|
alg = kmalloc(sizeof(*alg), GFP_KERNEL);
|
|
if (alg == NULL)
|
|
return -ENOMEM;
|
|
|
|
memset(alg, 0, sizeof(*alg));
|
|
alg->ops = ops;
|
|
|
|
spin_lock_irqsave(&hcrypt->lock, flags);
|
|
list_add(&alg->list, &hcrypt->algs);
|
|
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
|
|
|
printk(KERN_DEBUG "ieee80211_crypt: registered algorithm '%s'\n",
|
|
ops->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops)
|
|
{
|
|
unsigned long flags;
|
|
struct list_head *ptr;
|
|
struct ieee80211_crypto_alg *del_alg = NULL;
|
|
|
|
if (hcrypt == NULL)
|
|
return -1;
|
|
|
|
spin_lock_irqsave(&hcrypt->lock, flags);
|
|
for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
|
|
struct ieee80211_crypto_alg *alg =
|
|
(struct ieee80211_crypto_alg *)ptr;
|
|
if (alg->ops == ops) {
|
|
list_del(&alg->list);
|
|
del_alg = alg;
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
|
|
|
if (del_alg) {
|
|
printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm "
|
|
"'%s'\n", ops->name);
|
|
kfree(del_alg);
|
|
}
|
|
|
|
return del_alg ? 0 : -1;
|
|
}
|
|
|
|
struct ieee80211_crypto_ops *ieee80211_get_crypto_ops(const char *name)
|
|
{
|
|
unsigned long flags;
|
|
struct list_head *ptr;
|
|
struct ieee80211_crypto_alg *found_alg = NULL;
|
|
|
|
if (hcrypt == NULL)
|
|
return NULL;
|
|
|
|
spin_lock_irqsave(&hcrypt->lock, flags);
|
|
for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) {
|
|
struct ieee80211_crypto_alg *alg =
|
|
(struct ieee80211_crypto_alg *)ptr;
|
|
if (strcmp(alg->ops->name, name) == 0) {
|
|
found_alg = alg;
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&hcrypt->lock, flags);
|
|
|
|
if (found_alg)
|
|
return found_alg->ops;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static void *ieee80211_crypt_null_init(struct ieee80211_device *ieee,
|
|
int keyidx)
|
|
{
|
|
return (void *)1;
|
|
}
|
|
static void ieee80211_crypt_null_deinit(void *priv)
|
|
{
|
|
}
|
|
|
|
static struct ieee80211_crypto_ops ieee80211_crypt_null = {
|
|
.name = "NULL",
|
|
.init = ieee80211_crypt_null_init,
|
|
.deinit = ieee80211_crypt_null_deinit,
|
|
.encrypt_mpdu = NULL,
|
|
.decrypt_mpdu = NULL,
|
|
.encrypt_msdu = NULL,
|
|
.decrypt_msdu = NULL,
|
|
.set_key = NULL,
|
|
.get_key = NULL,
|
|
.extra_prefix_len = 0,
|
|
.extra_postfix_len = 0,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
static int __init ieee80211_crypto_init(void)
|
|
{
|
|
int ret = -ENOMEM;
|
|
|
|
hcrypt = kmalloc(sizeof(*hcrypt), GFP_KERNEL);
|
|
if (!hcrypt)
|
|
goto out;
|
|
|
|
memset(hcrypt, 0, sizeof(*hcrypt));
|
|
INIT_LIST_HEAD(&hcrypt->algs);
|
|
spin_lock_init(&hcrypt->lock);
|
|
|
|
ret = ieee80211_register_crypto_ops(&ieee80211_crypt_null);
|
|
if (ret < 0) {
|
|
kfree(hcrypt);
|
|
hcrypt = NULL;
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static void __exit ieee80211_crypto_deinit(void)
|
|
{
|
|
struct list_head *ptr, *n;
|
|
|
|
if (hcrypt == NULL)
|
|
return;
|
|
|
|
for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs;
|
|
ptr = n, n = ptr->next) {
|
|
struct ieee80211_crypto_alg *alg =
|
|
(struct ieee80211_crypto_alg *)ptr;
|
|
list_del(ptr);
|
|
printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm "
|
|
"'%s' (deinit)\n", alg->ops->name);
|
|
kfree(alg);
|
|
}
|
|
|
|
kfree(hcrypt);
|
|
}
|
|
|
|
EXPORT_SYMBOL(ieee80211_crypt_deinit_entries);
|
|
EXPORT_SYMBOL(ieee80211_crypt_deinit_handler);
|
|
EXPORT_SYMBOL(ieee80211_crypt_delayed_deinit);
|
|
EXPORT_SYMBOL(ieee80211_crypt_quiescing);
|
|
|
|
EXPORT_SYMBOL(ieee80211_register_crypto_ops);
|
|
EXPORT_SYMBOL(ieee80211_unregister_crypto_ops);
|
|
EXPORT_SYMBOL(ieee80211_get_crypto_ops);
|
|
|
|
module_init(ieee80211_crypto_init);
|
|
module_exit(ieee80211_crypto_deinit);
|