Merge branch 'testing' of git://git.kernel.org/pub/scm/linux/kernel/git/klassert...
authorDavid S. Miller <davem@davemloft.net>
Wed, 23 Jan 2013 19:00:16 +0000 (14:00 -0500)
committerDavid S. Miller <davem@davemloft.net>
Wed, 23 Jan 2013 19:00:16 +0000 (14:00 -0500)
Steffen Klassert says:

====================
1) Add a statistic counter for invalid output states and
   remove a superfluous state valid check, from Li RongQing.

2) Probe for asynchronous block ciphers instead of synchronous block
   ciphers to make the asynchronous variants available even if no
   synchronous block ciphers are found, from Jussi Kivilinna.

3) Make rfc3686 asynchronous block cipher and make use of
   the new asynchronous variant, from Jussi Kivilinna.

4) Replace some rwlocks by rcu, from Cong Wang.

5) Remove some unused defines.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
arch/x86/crypto/aesni-intel_glue.c
crypto/ctr.c
crypto/tcrypt.c
crypto/tcrypt.h
include/net/xfrm.h
include/uapi/linux/snmp.h
net/xfrm/xfrm_algo.c
net/xfrm/xfrm_output.c
net/xfrm/xfrm_proc.c
net/xfrm/xfrm_state.c

index 1b9c22bea8a7de8f6e63b200d77733bd2bb0f3c5..a0795da22c0273286119312018f5dfe21c4ac9ca 100644 (file)
 #include <linux/workqueue.h>
 #include <linux/spinlock.h>
 
-#if defined(CONFIG_CRYPTO_CTR) || defined(CONFIG_CRYPTO_CTR_MODULE)
-#define HAS_CTR
-#endif
-
 #if defined(CONFIG_CRYPTO_PCBC) || defined(CONFIG_CRYPTO_PCBC_MODULE)
 #define HAS_PCBC
 #endif
@@ -395,12 +391,6 @@ static int ablk_ctr_init(struct crypto_tfm *tfm)
        return ablk_init_common(tfm, "__driver-ctr-aes-aesni");
 }
 
-#ifdef HAS_CTR
-static int ablk_rfc3686_ctr_init(struct crypto_tfm *tfm)
-{
-       return ablk_init_common(tfm, "rfc3686(__driver-ctr-aes-aesni)");
-}
-#endif
 #endif
 
 #ifdef HAS_PCBC
@@ -1158,33 +1148,6 @@ static struct crypto_alg aesni_algs[] = { {
                        .maxauthsize    = 16,
                },
        },
-#ifdef HAS_CTR
-}, {
-       .cra_name               = "rfc3686(ctr(aes))",
-       .cra_driver_name        = "rfc3686-ctr-aes-aesni",
-       .cra_priority           = 400,
-       .cra_flags              = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
-       .cra_blocksize          = 1,
-       .cra_ctxsize            = sizeof(struct async_helper_ctx),
-       .cra_alignmask          = 0,
-       .cra_type               = &crypto_ablkcipher_type,
-       .cra_module             = THIS_MODULE,
-       .cra_init               = ablk_rfc3686_ctr_init,
-       .cra_exit               = ablk_exit,
-       .cra_u = {
-               .ablkcipher = {
-                       .min_keysize = AES_MIN_KEY_SIZE +
-                                      CTR_RFC3686_NONCE_SIZE,
-                       .max_keysize = AES_MAX_KEY_SIZE +
-                                      CTR_RFC3686_NONCE_SIZE,
-                       .ivsize      = CTR_RFC3686_IV_SIZE,
-                       .setkey      = ablk_set_key,
-                       .encrypt     = ablk_encrypt,
-                       .decrypt     = ablk_decrypt,
-                       .geniv       = "seqiv",
-               },
-       },
-#endif
 #endif
 #ifdef HAS_PCBC
 }, {
index 4ca7222cfeb6ac4bfcf32e4a97a072e1535577a7..1f2997cbfdd480937f6ca0c86abd3e9d7e73d5b0 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <crypto/algapi.h>
 #include <crypto/ctr.h>
+#include <crypto/internal/skcipher.h>
 #include <linux/err.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -25,10 +26,15 @@ struct crypto_ctr_ctx {
 };
 
 struct crypto_rfc3686_ctx {
-       struct crypto_blkcipher *child;
+       struct crypto_ablkcipher *child;
        u8 nonce[CTR_RFC3686_NONCE_SIZE];
 };
 
+struct crypto_rfc3686_req_ctx {
+       u8 iv[CTR_RFC3686_BLOCK_SIZE];
+       struct ablkcipher_request subreq CRYPTO_MINALIGN_ATTR;
+};
+
 static int crypto_ctr_setkey(struct crypto_tfm *parent, const u8 *key,
                             unsigned int keylen)
 {
@@ -243,11 +249,11 @@ static struct crypto_template crypto_ctr_tmpl = {
        .module = THIS_MODULE,
 };
 
-static int crypto_rfc3686_setkey(struct crypto_tfm *parent, const u8 *key,
-                                unsigned int keylen)
+static int crypto_rfc3686_setkey(struct crypto_ablkcipher *parent,
+                                const u8 *key, unsigned int keylen)
 {
-       struct crypto_rfc3686_ctx *ctx = crypto_tfm_ctx(parent);
-       struct crypto_blkcipher *child = ctx->child;
+       struct crypto_rfc3686_ctx *ctx = crypto_ablkcipher_ctx(parent);
+       struct crypto_ablkcipher *child = ctx->child;
        int err;
 
        /* the nonce is stored in bytes at end of key */
@@ -259,59 +265,64 @@ static int crypto_rfc3686_setkey(struct crypto_tfm *parent, const u8 *key,
 
        keylen -= CTR_RFC3686_NONCE_SIZE;
 
-       crypto_blkcipher_clear_flags(child, CRYPTO_TFM_REQ_MASK);
-       crypto_blkcipher_set_flags(child, crypto_tfm_get_flags(parent) &
-                                         CRYPTO_TFM_REQ_MASK);
-       err = crypto_blkcipher_setkey(child, key, keylen);
-       crypto_tfm_set_flags(parent, crypto_blkcipher_get_flags(child) &
-                                    CRYPTO_TFM_RES_MASK);
+       crypto_ablkcipher_clear_flags(child, CRYPTO_TFM_REQ_MASK);
+       crypto_ablkcipher_set_flags(child, crypto_ablkcipher_get_flags(parent) &
+                                   CRYPTO_TFM_REQ_MASK);
+       err = crypto_ablkcipher_setkey(child, key, keylen);
+       crypto_ablkcipher_set_flags(parent, crypto_ablkcipher_get_flags(child) &
+                                   CRYPTO_TFM_RES_MASK);
 
        return err;
 }
 
-static int crypto_rfc3686_crypt(struct blkcipher_desc *desc,
-                               struct scatterlist *dst,
-                               struct scatterlist *src, unsigned int nbytes)
+static int crypto_rfc3686_crypt(struct ablkcipher_request *req)
 {
-       struct crypto_blkcipher *tfm = desc->tfm;
-       struct crypto_rfc3686_ctx *ctx = crypto_blkcipher_ctx(tfm);
-       struct crypto_blkcipher *child = ctx->child;
-       unsigned long alignmask = crypto_blkcipher_alignmask(tfm);
-       u8 ivblk[CTR_RFC3686_BLOCK_SIZE + alignmask];
-       u8 *iv = PTR_ALIGN(ivblk + 0, alignmask + 1);
-       u8 *info = desc->info;
-       int err;
+       struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+       struct crypto_rfc3686_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+       struct crypto_ablkcipher *child = ctx->child;
+       unsigned long align = crypto_ablkcipher_alignmask(tfm);
+       struct crypto_rfc3686_req_ctx *rctx =
+               (void *)PTR_ALIGN((u8 *)ablkcipher_request_ctx(req), align + 1);
+       struct ablkcipher_request *subreq = &rctx->subreq;
+       u8 *iv = rctx->iv;
 
        /* set up counter block */
        memcpy(iv, ctx->nonce, CTR_RFC3686_NONCE_SIZE);
-       memcpy(iv + CTR_RFC3686_NONCE_SIZE, info, CTR_RFC3686_IV_SIZE);
+       memcpy(iv + CTR_RFC3686_NONCE_SIZE, req->info, CTR_RFC3686_IV_SIZE);
 
        /* initialize counter portion of counter block */
        *(__be32 *)(iv + CTR_RFC3686_NONCE_SIZE + CTR_RFC3686_IV_SIZE) =
                cpu_to_be32(1);
 
-       desc->tfm = child;
-       desc->info = iv;
-       err = crypto_blkcipher_encrypt_iv(desc, dst, src, nbytes);
-       desc->tfm = tfm;
-       desc->info = info;
+       ablkcipher_request_set_tfm(subreq, child);
+       ablkcipher_request_set_callback(subreq, req->base.flags,
+                                       req->base.complete, req->base.data);
+       ablkcipher_request_set_crypt(subreq, req->src, req->dst, req->nbytes,
+                                    iv);
 
-       return err;
+       return crypto_ablkcipher_encrypt(subreq);
 }
 
 static int crypto_rfc3686_init_tfm(struct crypto_tfm *tfm)
 {
        struct crypto_instance *inst = (void *)tfm->__crt_alg;
-       struct crypto_spawn *spawn = crypto_instance_ctx(inst);
+       struct crypto_skcipher_spawn *spawn = crypto_instance_ctx(inst);
        struct crypto_rfc3686_ctx *ctx = crypto_tfm_ctx(tfm);
-       struct crypto_blkcipher *cipher;
+       struct crypto_ablkcipher *cipher;
+       unsigned long align;
 
-       cipher = crypto_spawn_blkcipher(spawn);
+       cipher = crypto_spawn_skcipher(spawn);
        if (IS_ERR(cipher))
                return PTR_ERR(cipher);
 
        ctx->child = cipher;
 
+       align = crypto_tfm_alg_alignmask(tfm);
+       align &= ~(crypto_tfm_ctx_alignment() - 1);
+       tfm->crt_ablkcipher.reqsize = align +
+               sizeof(struct crypto_rfc3686_req_ctx) +
+               crypto_ablkcipher_reqsize(cipher);
+
        return 0;
 }
 
@@ -319,74 +330,110 @@ static void crypto_rfc3686_exit_tfm(struct crypto_tfm *tfm)
 {
        struct crypto_rfc3686_ctx *ctx = crypto_tfm_ctx(tfm);
 
-       crypto_free_blkcipher(ctx->child);
+       crypto_free_ablkcipher(ctx->child);
 }
 
 static struct crypto_instance *crypto_rfc3686_alloc(struct rtattr **tb)
 {
+       struct crypto_attr_type *algt;
        struct crypto_instance *inst;
        struct crypto_alg *alg;
+       struct crypto_skcipher_spawn *spawn;
+       const char *cipher_name;
        int err;
 
-       err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_BLKCIPHER);
-       if (err)
+       algt = crypto_get_attr_type(tb);
+       err = PTR_ERR(algt);
+       if (IS_ERR(algt))
                return ERR_PTR(err);
 
-       alg = crypto_attr_alg(tb[1], CRYPTO_ALG_TYPE_BLKCIPHER,
-                                 CRYPTO_ALG_TYPE_MASK);
-       err = PTR_ERR(alg);
-       if (IS_ERR(alg))
+       if ((algt->type ^ CRYPTO_ALG_TYPE_BLKCIPHER) & algt->mask)
+               return ERR_PTR(-EINVAL);
+
+       cipher_name = crypto_attr_alg_name(tb[1]);
+       err = PTR_ERR(cipher_name);
+       if (IS_ERR(cipher_name))
                return ERR_PTR(err);
 
+       inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);
+       if (!inst)
+               return ERR_PTR(-ENOMEM);
+
+       spawn = crypto_instance_ctx(inst);
+
+       crypto_set_skcipher_spawn(spawn, inst);
+       err = crypto_grab_skcipher(spawn, cipher_name, 0,
+                                  crypto_requires_sync(algt->type,
+                                                       algt->mask));
+       if (err)
+               goto err_free_inst;
+
+       alg = crypto_skcipher_spawn_alg(spawn);
+
        /* We only support 16-byte blocks. */
        err = -EINVAL;
-       if (alg->cra_blkcipher.ivsize != CTR_RFC3686_BLOCK_SIZE)
-               goto out_put_alg;
+       if (alg->cra_ablkcipher.ivsize != CTR_RFC3686_BLOCK_SIZE)
+               goto err_drop_spawn;
 
        /* Not a stream cipher? */
        if (alg->cra_blocksize != 1)
-               goto out_put_alg;
+               goto err_drop_spawn;
 
-       inst = crypto_alloc_instance("rfc3686", alg);
-       if (IS_ERR(inst))
-               goto out;
+       err = -ENAMETOOLONG;
+       if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME, "rfc3686(%s)",
+                    alg->cra_name) >= CRYPTO_MAX_ALG_NAME)
+               goto err_drop_spawn;
+       if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+                    "rfc3686(%s)", alg->cra_driver_name) >=
+                       CRYPTO_MAX_ALG_NAME)
+               goto err_drop_spawn;
 
-       inst->alg.cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER;
        inst->alg.cra_priority = alg->cra_priority;
        inst->alg.cra_blocksize = 1;
        inst->alg.cra_alignmask = alg->cra_alignmask;
-       inst->alg.cra_type = &crypto_blkcipher_type;
 
-       inst->alg.cra_blkcipher.ivsize = CTR_RFC3686_IV_SIZE;
-       inst->alg.cra_blkcipher.min_keysize = alg->cra_blkcipher.min_keysize
-                                             + CTR_RFC3686_NONCE_SIZE;
-       inst->alg.cra_blkcipher.max_keysize = alg->cra_blkcipher.max_keysize
-                                             + CTR_RFC3686_NONCE_SIZE;
+       inst->alg.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER |
+                             (alg->cra_flags & CRYPTO_ALG_ASYNC);
+       inst->alg.cra_type = &crypto_ablkcipher_type;
+
+       inst->alg.cra_ablkcipher.ivsize = CTR_RFC3686_IV_SIZE;
+       inst->alg.cra_ablkcipher.min_keysize =
+               alg->cra_ablkcipher.min_keysize + CTR_RFC3686_NONCE_SIZE;
+       inst->alg.cra_ablkcipher.max_keysize =
+               alg->cra_ablkcipher.max_keysize + CTR_RFC3686_NONCE_SIZE;
 
-       inst->alg.cra_blkcipher.geniv = "seqiv";
+       inst->alg.cra_ablkcipher.geniv = "seqiv";
+
+       inst->alg.cra_ablkcipher.setkey = crypto_rfc3686_setkey;
+       inst->alg.cra_ablkcipher.encrypt = crypto_rfc3686_crypt;
+       inst->alg.cra_ablkcipher.decrypt = crypto_rfc3686_crypt;
 
        inst->alg.cra_ctxsize = sizeof(struct crypto_rfc3686_ctx);
 
        inst->alg.cra_init = crypto_rfc3686_init_tfm;
        inst->alg.cra_exit = crypto_rfc3686_exit_tfm;
 
-       inst->alg.cra_blkcipher.setkey = crypto_rfc3686_setkey;
-       inst->alg.cra_blkcipher.encrypt = crypto_rfc3686_crypt;
-       inst->alg.cra_blkcipher.decrypt = crypto_rfc3686_crypt;
-
-out:
-       crypto_mod_put(alg);
        return inst;
 
-out_put_alg:
-       inst = ERR_PTR(err);
-       goto out;
+err_drop_spawn:
+       crypto_drop_skcipher(spawn);
+err_free_inst:
+       kfree(inst);
+       return ERR_PTR(err);
+}
+
+static void crypto_rfc3686_free(struct crypto_instance *inst)
+{
+       struct crypto_skcipher_spawn *spawn = crypto_instance_ctx(inst);
+
+       crypto_drop_skcipher(spawn);
+       kfree(inst);
 }
 
 static struct crypto_template crypto_rfc3686_tmpl = {
        .name = "rfc3686",
        .alloc = crypto_rfc3686_alloc,
-       .free = crypto_ctr_free,
+       .free = crypto_rfc3686_free,
        .module = THIS_MODULE,
 };
 
index 7ae2130e1b002890900907fc415b1eb51094e8a7..87ef7d66bc20e05d6247cb42de5372fcaea3c9f6 100644 (file)
@@ -1591,6 +1591,10 @@ static int do_test(int m)
                                   speed_template_16_24_32);
                test_acipher_speed("ofb(aes)", DECRYPT, sec, NULL, 0,
                                   speed_template_16_24_32);
+               test_acipher_speed("rfc3686(ctr(aes))", ENCRYPT, sec, NULL, 0,
+                                  speed_template_20_28_36);
+               test_acipher_speed("rfc3686(ctr(aes))", DECRYPT, sec, NULL, 0,
+                                  speed_template_20_28_36);
                break;
 
        case 501:
index cd2068524f3f6766696cf5f350d546bb2c9c644b..ecdeeb1a7b05116301c2b6bf315319e463d09264 100644 (file)
@@ -51,6 +51,7 @@ static u8 speed_template_8_16[] = {8, 16, 0};
 static u8 speed_template_8_32[] = {8, 32, 0};
 static u8 speed_template_16_32[] = {16, 32, 0};
 static u8 speed_template_16_24_32[] = {16, 24, 32, 0};
+static u8 speed_template_20_28_36[] = {20, 28, 36, 0};
 static u8 speed_template_32_40_48[] = {32, 40, 48, 0};
 static u8 speed_template_32_48[] = {32, 48, 0};
 static u8 speed_template_32_48_64[] = {32, 48, 64, 0};
index 63445ede48bb7fd04b175c111935fb77464568b6..421f764794d59f5100ef3fe26190554a2e8a00c0 100644 (file)
@@ -557,10 +557,6 @@ struct xfrm_migrate {
 };
 
 #define XFRM_KM_TIMEOUT                30
-/* which seqno */
-#define XFRM_REPLAY_SEQ                1
-#define XFRM_REPLAY_OSEQ       2
-#define XFRM_REPLAY_SEQ_MASK   3
 /* what happened */
 #define XFRM_REPLAY_UPDATE     XFRM_AE_CR
 #define XFRM_REPLAY_TIMEOUT    XFRM_AE_CE
index fdfba235f9f1a9bb6b8fc03958ae1982404dd125..b49eab89c9fd4461c155ec1b60083aeab1234e44 100644 (file)
@@ -278,6 +278,7 @@ enum
        LINUX_MIB_XFRMOUTPOLDEAD,               /* XfrmOutPolDead */
        LINUX_MIB_XFRMOUTPOLERROR,              /* XfrmOutPolError */
        LINUX_MIB_XFRMFWDHDRERROR,              /* XfrmFwdHdrError*/
+       LINUX_MIB_XFRMOUTSTATEINVALID,          /* XfrmOutStateInvalid */
        __LINUX_MIB_XFRMMAX
 };
 
index 4ce2d93162c12acbf1b43577a6e1198af2cba9c5..f9a54955474081733669f3eb8af11ec7fc2432ea 100644 (file)
@@ -700,8 +700,7 @@ void xfrm_probe_algs(void)
        }
 
        for (i = 0; i < ealg_entries(); i++) {
-               status = crypto_has_blkcipher(ealg_list[i].name, 0,
-                                             CRYPTO_ALG_ASYNC);
+               status = crypto_has_ablkcipher(ealg_list[i].name, 0, 0);
                if (ealg_list[i].available != status)
                        ealg_list[i].available = status;
        }
index 95a338c89f99d87af24ffa97faf17e1a7ef700e8..3670526e70b97e804e90404036c89a5e54b5dcf5 100644 (file)
@@ -61,6 +61,12 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
                }
 
                spin_lock_bh(&x->lock);
+
+               if (unlikely(x->km.state != XFRM_STATE_VALID)) {
+                       XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEINVALID);
+                       goto error_nolock;
+               }
+
                err = xfrm_state_check_expire(x);
                if (err) {
                        XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEEXPIRED);
index d0a1af8ed5846d07e73c605f0d119b9f8d87d0b4..603903853e89802099ca81aaba6a9f38bc38c161 100644 (file)
@@ -43,6 +43,7 @@ static const struct snmp_mib xfrm_mib_list[] = {
        SNMP_MIB_ITEM("XfrmOutPolDead", LINUX_MIB_XFRMOUTPOLDEAD),
        SNMP_MIB_ITEM("XfrmOutPolError", LINUX_MIB_XFRMOUTPOLERROR),
        SNMP_MIB_ITEM("XfrmFwdHdrError", LINUX_MIB_XFRMFWDHDRERROR),
+       SNMP_MIB_ITEM("XfrmOutStateInvalid", LINUX_MIB_XFRMOUTSTATEINVALID),
        SNMP_MIB_SENTINEL
 };
 
index 3459692092ec1f44220d02a4d73d82613896793d..0adae918a7a24c02838d807c254e2cdb5ec3cdc9 100644 (file)
@@ -158,8 +158,8 @@ out_unlock:
        mutex_unlock(&hash_resize_mutex);
 }
 
-static DEFINE_RWLOCK(xfrm_state_afinfo_lock);
-static struct xfrm_state_afinfo *xfrm_state_afinfo[NPROTO];
+static DEFINE_SPINLOCK(xfrm_state_afinfo_lock);
+static struct xfrm_state_afinfo __rcu *xfrm_state_afinfo[NPROTO];
 
 static DEFINE_SPINLOCK(xfrm_state_gc_lock);
 
@@ -168,58 +168,45 @@ int __xfrm_state_delete(struct xfrm_state *x);
 int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol);
 void km_state_expired(struct xfrm_state *x, int hard, u32 portid);
 
-static struct xfrm_state_afinfo *xfrm_state_lock_afinfo(unsigned int family)
-{
-       struct xfrm_state_afinfo *afinfo;
-       if (unlikely(family >= NPROTO))
-               return NULL;
-       write_lock_bh(&xfrm_state_afinfo_lock);
-       afinfo = xfrm_state_afinfo[family];
-       if (unlikely(!afinfo))
-               write_unlock_bh(&xfrm_state_afinfo_lock);
-       return afinfo;
-}
-
-static void xfrm_state_unlock_afinfo(struct xfrm_state_afinfo *afinfo)
-       __releases(xfrm_state_afinfo_lock)
-{
-       write_unlock_bh(&xfrm_state_afinfo_lock);
-}
-
+static DEFINE_SPINLOCK(xfrm_type_lock);
 int xfrm_register_type(const struct xfrm_type *type, unsigned short family)
 {
-       struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
+       struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
        const struct xfrm_type **typemap;
        int err = 0;
 
        if (unlikely(afinfo == NULL))
                return -EAFNOSUPPORT;
        typemap = afinfo->type_map;
+       spin_lock_bh(&xfrm_type_lock);
 
        if (likely(typemap[type->proto] == NULL))
                typemap[type->proto] = type;
        else
                err = -EEXIST;
-       xfrm_state_unlock_afinfo(afinfo);
+       spin_unlock_bh(&xfrm_type_lock);
+       xfrm_state_put_afinfo(afinfo);
        return err;
 }
 EXPORT_SYMBOL(xfrm_register_type);
 
 int xfrm_unregister_type(const struct xfrm_type *type, unsigned short family)
 {
-       struct xfrm_state_afinfo *afinfo = xfrm_state_lock_afinfo(family);
+       struct xfrm_state_afinfo *afinfo = xfrm_state_get_afinfo(family);
        const struct xfrm_type **typemap;
        int err = 0;
 
        if (unlikely(afinfo == NULL))
                return -EAFNOSUPPORT;
        typemap = afinfo->type_map;
+       spin_lock_bh(&xfrm_type_lock);
 
        if (unlikely(typemap[type->proto] != type))
                err = -ENOENT;
        else
                typemap[type->proto] = NULL;
-       xfrm_state_unlock_afinfo(afinfo);
+       spin_unlock_bh(&xfrm_type_lock);
+       xfrm_state_put_afinfo(afinfo);
        return err;
 }
 EXPORT_SYMBOL(xfrm_unregister_type);
@@ -256,6 +243,7 @@ static void xfrm_put_type(const struct xfrm_type *type)
        module_put(type->owner);
 }
 
+static DEFINE_SPINLOCK(xfrm_mode_lock);
 int xfrm_register_mode(struct xfrm_mode *mode, int family)
 {
        struct xfrm_state_afinfo *afinfo;
@@ -265,12 +253,13 @@ int xfrm_register_mode(struct xfrm_mode *mode, int family)
        if (unlikely(mode->encap >= XFRM_MODE_MAX))
                return -EINVAL;
 
-       afinfo = xfrm_state_lock_afinfo(family);
+       afinfo = xfrm_state_get_afinfo(family);
        if (unlikely(afinfo == NULL))
                return -EAFNOSUPPORT;
 
        err = -EEXIST;
        modemap = afinfo->mode_map;
+       spin_lock_bh(&xfrm_mode_lock);
        if (modemap[mode->encap])
                goto out;
 
@@ -283,7 +272,8 @@ int xfrm_register_mode(struct xfrm_mode *mode, int family)
        err = 0;
 
 out:
-       xfrm_state_unlock_afinfo(afinfo);
+       spin_unlock_bh(&xfrm_mode_lock);
+       xfrm_state_put_afinfo(afinfo);
        return err;
 }
 EXPORT_SYMBOL(xfrm_register_mode);
@@ -297,19 +287,21 @@ int xfrm_unregister_mode(struct xfrm_mode *mode, int family)
        if (unlikely(mode->encap >= XFRM_MODE_MAX))
                return -EINVAL;
 
-       afinfo = xfrm_state_lock_afinfo(family);
+       afinfo = xfrm_state_get_afinfo(family);
        if (unlikely(afinfo == NULL))
                return -EAFNOSUPPORT;
 
        err = -ENOENT;
        modemap = afinfo->mode_map;
+       spin_lock_bh(&xfrm_mode_lock);
        if (likely(modemap[mode->encap] == mode)) {
                modemap[mode->encap] = NULL;
                module_put(mode->afinfo->owner);
                err = 0;
        }
 
-       xfrm_state_unlock_afinfo(afinfo);
+       spin_unlock_bh(&xfrm_mode_lock);
+       xfrm_state_put_afinfo(afinfo);
        return err;
 }
 EXPORT_SYMBOL(xfrm_unregister_mode);
@@ -1370,9 +1362,6 @@ int xfrm_state_check_expire(struct xfrm_state *x)
        if (!x->curlft.use_time)
                x->curlft.use_time = get_seconds();
 
-       if (x->km.state != XFRM_STATE_VALID)
-               return -EINVAL;
-
        if (x->curlft.bytes >= x->lft.hard_byte_limit ||
            x->curlft.packets >= x->lft.hard_packet_limit) {
                x->km.state = XFRM_STATE_EXPIRED;
@@ -1648,27 +1637,26 @@ static void xfrm_replay_timer_handler(unsigned long data)
 }
 
 static LIST_HEAD(xfrm_km_list);
-static DEFINE_RWLOCK(xfrm_km_lock);
 
 void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c)
 {
        struct xfrm_mgr *km;
 
-       read_lock(&xfrm_km_lock);
-       list_for_each_entry(km, &xfrm_km_list, list)
+       rcu_read_lock();
+       list_for_each_entry_rcu(km, &xfrm_km_list, list)
                if (km->notify_policy)
                        km->notify_policy(xp, dir, c);
-       read_unlock(&xfrm_km_lock);
+       rcu_read_unlock();
 }
 
 void km_state_notify(struct xfrm_state *x, const struct km_event *c)
 {
        struct xfrm_mgr *km;
-       read_lock(&xfrm_km_lock);
-       list_for_each_entry(km, &xfrm_km_list, list)
+       rcu_read_lock();
+       list_for_each_entry_rcu(km, &xfrm_km_list, list)
                if (km->notify)
                        km->notify(x, c);
-       read_unlock(&xfrm_km_lock);
+       rcu_read_unlock();
 }
 
 EXPORT_SYMBOL(km_policy_notify);
@@ -1698,13 +1686,13 @@ int km_query(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *pol)
        int err = -EINVAL, acqret;
        struct xfrm_mgr *km;
 
-       read_lock(&xfrm_km_lock);
-       list_for_each_entry(km, &xfrm_km_list, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(km, &xfrm_km_list, list) {
                acqret = km->acquire(x, t, pol);
                if (!acqret)
                        err = acqret;
        }
-       read_unlock(&xfrm_km_lock);
+       rcu_read_unlock();
        return err;
 }
 EXPORT_SYMBOL(km_query);
@@ -1714,14 +1702,14 @@ int km_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, __be16 sport)
        int err = -EINVAL;
        struct xfrm_mgr *km;
 
-       read_lock(&xfrm_km_lock);
-       list_for_each_entry(km, &xfrm_km_list, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(km, &xfrm_km_list, list) {
                if (km->new_mapping)
                        err = km->new_mapping(x, ipaddr, sport);
                if (!err)
                        break;
        }
-       read_unlock(&xfrm_km_lock);
+       rcu_read_unlock();
        return err;
 }
 EXPORT_SYMBOL(km_new_mapping);
@@ -1750,15 +1738,15 @@ int km_migrate(const struct xfrm_selector *sel, u8 dir, u8 type,
        int ret;
        struct xfrm_mgr *km;
 
-       read_lock(&xfrm_km_lock);
-       list_for_each_entry(km, &xfrm_km_list, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(km, &xfrm_km_list, list) {
                if (km->migrate) {
                        ret = km->migrate(sel, dir, type, m, num_migrate, k);
                        if (!ret)
                                err = ret;
                }
        }
-       read_unlock(&xfrm_km_lock);
+       rcu_read_unlock();
        return err;
 }
 EXPORT_SYMBOL(km_migrate);
@@ -1770,15 +1758,15 @@ int km_report(struct net *net, u8 proto, struct xfrm_selector *sel, xfrm_address
        int ret;
        struct xfrm_mgr *km;
 
-       read_lock(&xfrm_km_lock);
-       list_for_each_entry(km, &xfrm_km_list, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(km, &xfrm_km_list, list) {
                if (km->report) {
                        ret = km->report(net, proto, sel, addr);
                        if (!ret)
                                err = ret;
                }
        }
-       read_unlock(&xfrm_km_lock);
+       rcu_read_unlock();
        return err;
 }
 EXPORT_SYMBOL(km_report);
@@ -1802,14 +1790,14 @@ int xfrm_user_policy(struct sock *sk, int optname, u8 __user *optval, int optlen
                goto out;
 
        err = -EINVAL;
-       read_lock(&xfrm_km_lock);
-       list_for_each_entry(km, &xfrm_km_list, list) {
+       rcu_read_lock();
+       list_for_each_entry_rcu(km, &xfrm_km_list, list) {
                pol = km->compile_policy(sk, optname, data,
                                         optlen, &err);
                if (err >= 0)
                        break;
        }
-       read_unlock(&xfrm_km_lock);
+       rcu_read_unlock();
 
        if (err >= 0) {
                xfrm_sk_policy_insert(sk, err, pol);
@@ -1823,20 +1811,23 @@ out:
 }
 EXPORT_SYMBOL(xfrm_user_policy);
 
+static DEFINE_SPINLOCK(xfrm_km_lock);
+
 int xfrm_register_km(struct xfrm_mgr *km)
 {
-       write_lock_bh(&xfrm_km_lock);
-       list_add_tail(&km->list, &xfrm_km_list);
-       write_unlock_bh(&xfrm_km_lock);
+       spin_lock_bh(&xfrm_km_lock);
+       list_add_tail_rcu(&km->list, &xfrm_km_list);
+       spin_unlock_bh(&xfrm_km_lock);
        return 0;
 }
 EXPORT_SYMBOL(xfrm_register_km);
 
 int xfrm_unregister_km(struct xfrm_mgr *km)
 {
-       write_lock_bh(&xfrm_km_lock);
-       list_del(&km->list);
-       write_unlock_bh(&xfrm_km_lock);
+       spin_lock_bh(&xfrm_km_lock);
+       list_del_rcu(&km->list);
+       spin_unlock_bh(&xfrm_km_lock);
+       synchronize_rcu();
        return 0;
 }
 EXPORT_SYMBOL(xfrm_unregister_km);
@@ -1848,12 +1839,12 @@ int xfrm_state_register_afinfo(struct xfrm_state_afinfo *afinfo)
                return -EINVAL;
        if (unlikely(afinfo->family >= NPROTO))
                return -EAFNOSUPPORT;
-       write_lock_bh(&xfrm_state_afinfo_lock);
+       spin_lock_bh(&xfrm_state_afinfo_lock);
        if (unlikely(xfrm_state_afinfo[afinfo->family] != NULL))
                err = -ENOBUFS;
        else
-               xfrm_state_afinfo[afinfo->family] = afinfo;
-       write_unlock_bh(&xfrm_state_afinfo_lock);
+               rcu_assign_pointer(xfrm_state_afinfo[afinfo->family], afinfo);
+       spin_unlock_bh(&xfrm_state_afinfo_lock);
        return err;
 }
 EXPORT_SYMBOL(xfrm_state_register_afinfo);
@@ -1865,14 +1856,15 @@ int xfrm_state_unregister_afinfo(struct xfrm_state_afinfo *afinfo)
                return -EINVAL;
        if (unlikely(afinfo->family >= NPROTO))
                return -EAFNOSUPPORT;
-       write_lock_bh(&xfrm_state_afinfo_lock);
+       spin_lock_bh(&xfrm_state_afinfo_lock);
        if (likely(xfrm_state_afinfo[afinfo->family] != NULL)) {
                if (unlikely(xfrm_state_afinfo[afinfo->family] != afinfo))
                        err = -EINVAL;
                else
-                       xfrm_state_afinfo[afinfo->family] = NULL;
+                       RCU_INIT_POINTER(xfrm_state_afinfo[afinfo->family], NULL);
        }
-       write_unlock_bh(&xfrm_state_afinfo_lock);
+       spin_unlock_bh(&xfrm_state_afinfo_lock);
+       synchronize_rcu();
        return err;
 }
 EXPORT_SYMBOL(xfrm_state_unregister_afinfo);
@@ -1882,17 +1874,16 @@ static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned int family)
        struct xfrm_state_afinfo *afinfo;
        if (unlikely(family >= NPROTO))
                return NULL;
-       read_lock(&xfrm_state_afinfo_lock);
-       afinfo = xfrm_state_afinfo[family];
+       rcu_read_lock();
+       afinfo = rcu_dereference(xfrm_state_afinfo[family]);
        if (unlikely(!afinfo))
-               read_unlock(&xfrm_state_afinfo_lock);
+               rcu_read_unlock();
        return afinfo;
 }
 
 static void xfrm_state_put_afinfo(struct xfrm_state_afinfo *afinfo)
-       __releases(xfrm_state_afinfo_lock)
 {
-       read_unlock(&xfrm_state_afinfo_lock);
+       rcu_read_unlock();
 }
 
 /* Temporarily located here until net/xfrm/xfrm_tunnel.c is created */