diff --git a/lustre/autoconf/lustre-core.m4 b/lustre/autoconf/lustre-core.m4
index 200fe7a1fedc8e66711304200fdf50a13eadc0cd..fa84b1fb1e3bc8233dd337c2bb88eba4bf15818a 100644
--- a/lustre/autoconf/lustre-core.m4
+++ b/lustre/autoconf/lustre-core.m4
@@ -8,7 +8,7 @@
 AC_DEFUN([LC_CONFIG_SRCDIR],
 [AC_CONFIG_SRCDIR([lustre/obdclass/obdo.c])
 ])
-                           
+
 #
 # LC_PATH_DEFAULTS
 #
@@ -699,17 +699,24 @@ AC_DEFUN([LC_CONFIG_GSS],
 	LC_CONFIG_GSS_KEYRING
         LC_CONFIG_SUNRPC
 
-        LB_LINUX_CONFIG_IM([CRYPTO_DES],[],
-                           [AC_MSG_WARN([kernel DES support is recommended by using GSS.])])
         LB_LINUX_CONFIG_IM([CRYPTO_MD5],[],
                            [AC_MSG_WARN([kernel MD5 support is recommended by using GSS.])])
+	LB_LINUX_CONFIG_IM([CRYPTO_SHA1],[],
+                           [AC_MSG_WARN([kernel SHA1 support is recommended by using GSS.])])
 	LB_LINUX_CONFIG_IM([CRYPTO_SHA256],[],
                            [AC_MSG_WARN([kernel SHA256 support is recommended by using GSS.])])
 	LB_LINUX_CONFIG_IM([CRYPTO_SHA512],[],
                            [AC_MSG_WARN([kernel SHA512 support is recommended by using GSS.])])
+	LB_LINUX_CONFIG_IM([CRYPTO_WP512],[],
+                           [AC_MSG_WARN([kernel WP512 support is recommended by using GSS.])])
 	LB_LINUX_CONFIG_IM([CRYPTO_ARC4],[],
                            [AC_MSG_WARN([kernel ARC4 support is recommended by using GSS.])])
-
+        LB_LINUX_CONFIG_IM([CRYPTO_DES],[],
+                           [AC_MSG_WARN([kernel DES support is recommended by using GSS.])])
+        LB_LINUX_CONFIG_IM([CRYPTO_TWOFISH],[],
+                           [AC_MSG_WARN([kernel TWOFISH support is recommended by using GSS.])])
+        LB_LINUX_CONFIG_IM([CRYPTO_CAST6],[],
+                           [AC_MSG_WARN([kernel CAST6 support is recommended by using GSS.])])
 	dnl FIXME
 	dnl the AES symbol usually tied with arch, e.g. CRYPTO_AES_586
 	dnl FIXME
@@ -1739,6 +1746,21 @@ LC_READLINK_SSIZE_T
 # utils/llverfs.c
 AC_CHECK_HEADERS([ext2fs/ext2fs.h])
 
+# include/linux/obd_support.h
+AC_CHECK_HEADERS([zlib.h])
+
+# check for -lz support
+AC_CHECK_LIB(z, [adler32],
+        [
+                ZLIB="-lz"
+                AC_DEFINE([HAVE_ADLER], 1, [support alder32 checksum type])
+        ],
+        [
+                ZLIB=""
+                AC_MSG_WARN([No zlib-devel package found, unable to use adler32 checksum])
+        ])
+AC_SUBST(ZLIB)
+
 # Super safe df
 AC_ARG_ENABLE([mindf],
       AC_HELP_STRING([--enable-mindf],
diff --git a/lustre/include/linux/obd.h b/lustre/include/linux/obd.h
index 5bc3c4716992e7f31bbc239510b8b451900101b3..93fbf786b71fc507cd0cde0f1d2f095656332f59 100644
--- a/lustre/include/linux/obd.h
+++ b/lustre/include/linux/obd.h
@@ -42,4 +42,8 @@ static inline void client_obd_list_unlock(client_obd_lock_t *lock)
         spin_unlock(lock);
 }
 
+#if defined(__KERNEL__) && !defined(HAVE_ADLER)
+/* zlib_adler() is an inline function defined in zutil.h */
+#define HAVE_ADLER
+#endif
 #endif /* __LINUX_OBD_H */
diff --git a/lustre/include/linux/obd_support.h b/lustre/include/linux/obd_support.h
index 5f89994add359fef51393812ed632847bda31cd6..d6f57c3f9cba345ca841891160f6e7b7af1fe371 100644
--- a/lustre/include/linux/obd_support.h
+++ b/lustre/include/linux/obd_support.h
@@ -70,6 +70,23 @@ static inline __u32 crc32_le(__u32 crc, unsigned char const *p, size_t len)
 }
 #endif
 
+#ifdef __KERNEL__
+# include <linux/zutil.h>
+# ifndef HAVE_ADLER
+#  define HAVE_ADLER
+# endif
+#else /* ! __KERNEL__ */
+# ifdef HAVE_ADLER
+#  include <zlib.h>
+
+static inline __u32 zlib_adler32(__u32 adler, unsigned char const *p,
+                                 size_t len)
+{
+        return adler32(adler, p, len);
+}
+# endif
+#endif /* __KERNEL__ */
+
 #ifdef __KERNEL__
 # include <linux/types.h>
 # include <linux/blkdev.h>
diff --git a/lustre/include/lustre_sec.h b/lustre/include/lustre_sec.h
index 64558588bf2c716a56659aca54cea25ac1f6ad82..033a0cfafbd048444665bb0c2c35e8c06f100c3f 100644
--- a/lustre/include/lustre_sec.h
+++ b/lustre/include/lustre_sec.h
@@ -168,8 +168,8 @@ static inline void rpc_flvr_set_svc(__u16 *flvr, __u16 svc)
 
 struct sptlrpc_flavor {
         __u16   sf_rpc;         /* rpc flavor */
-        __u8    sf_bulk_priv;   /* bulk encrypt alg */
-        __u8    sf_bulk_csum;   /* bulk checksum alg */
+        __u8    sf_bulk_ciph;   /* bulk cipher alg */
+        __u8    sf_bulk_hash;   /* bulk hash alg */
         __u32   sf_flags;       /* general flags */
 };
 
@@ -467,35 +467,66 @@ struct ptlrpc_user_desc {
 /*
  * bulk flavors
  */
-enum bulk_checksum_alg {
-        BULK_CSUM_ALG_NULL      = 0,
-        BULK_CSUM_ALG_CRC32,
-        BULK_CSUM_ALG_MD5,
-        BULK_CSUM_ALG_SHA1,
-        BULK_CSUM_ALG_SHA256,
-        BULK_CSUM_ALG_SHA384,
-        BULK_CSUM_ALG_SHA512,
-        BULK_CSUM_ALG_MAX
+enum sptlrpc_bulk_hash_alg {
+        BULK_HASH_ALG_NULL      = 0,
+        BULK_HASH_ALG_ADLER32,
+        BULK_HASH_ALG_CRC32,
+        BULK_HASH_ALG_MD5,
+        BULK_HASH_ALG_SHA1,
+        BULK_HASH_ALG_SHA256,
+        BULK_HASH_ALG_SHA384,
+        BULK_HASH_ALG_SHA512,
+        BULK_HASH_ALG_WP256,
+        BULK_HASH_ALG_WP384,
+        BULK_HASH_ALG_WP512,
+        BULK_HASH_ALG_MAX
 };
 
-enum bulk_encrypt_alg {
-        BULK_PRIV_ALG_NULL      = 0,
-        BULK_PRIV_ALG_ARC4,
-        BULK_PRIV_ALG_MAX
+enum sptlrpc_bulk_cipher_alg {
+        BULK_CIPH_ALG_NULL      = 0,
+        BULK_CIPH_ALG_ARC4,
+        BULK_CIPH_ALG_AES128,
+        BULK_CIPH_ALG_AES192,
+        BULK_CIPH_ALG_AES256,
+        BULK_CIPH_ALG_CAST128,
+        BULK_CIPH_ALG_CAST256,
+        BULK_CIPH_ALG_TWOFISH128,
+        BULK_CIPH_ALG_TWOFISH256,
+        BULK_CIPH_ALG_MAX
 };
 
+struct sptlrpc_hash_type {
+        char           *sht_name;
+        char           *sht_tfm_name;
+        unsigned int    sht_size;
+};
+
+struct sptlrpc_ciph_type {
+        char           *sct_name;
+        char           *sct_tfm_name;
+        __u32           sct_tfm_flags;
+        unsigned int    sct_ivsize;
+        unsigned int    sct_keysize;
+};
+
+const struct sptlrpc_hash_type *sptlrpc_get_hash_type(__u8 hash_alg);
+const char * sptlrpc_get_hash_name(__u8 hash_alg);
+const struct sptlrpc_ciph_type *sptlrpc_get_ciph_type(__u8 ciph_alg);
+const char *sptlrpc_get_ciph_name(__u8 ciph_alg);
+
+#define CIPHER_MAX_BLKSIZE      (16)
+#define CIPHER_MAX_KEYSIZE      (64)
+
 struct ptlrpc_bulk_sec_desc {
-        __u32           bsd_version;
-        __u8            bsd_csum_alg;   /* checksum algorithm */
-        __u8            bsd_priv_alg;   /* encrypt algorithm */
-        __u16           bsd_pad;
-        __u8            bsd_iv[16];     /* encrypt iv */
+        __u8            bsd_version;
+        __u8            bsd_flags;
+        __u8            bsd_pad[4];
+        __u8            bsd_hash_alg;                /* hash algorithm */
+        __u8            bsd_ciph_alg;                /* cipher algorithm */
+        __u8            bsd_key[CIPHER_MAX_KEYSIZE]; /* encrypt key seed */
         __u8            bsd_csum[0];
 };
 
-const char * sptlrpc_bulk_csum_alg2name(__u8 csum_alg);
-const char * sptlrpc_bulk_priv_alg2name(__u8 priv_alg);
-__u32 sptlrpc_bulk_priv_alg2flags(__u8 priv_alg);
 
 /*
  * lprocfs
@@ -717,7 +748,7 @@ int sptlrpc_pack_user_desc(struct lustre_msg *msg, int offset);
 int sptlrpc_unpack_user_desc(struct lustre_msg *msg, int offset);
 
 /* bulk helpers (internal use only by policies) */
-int bulk_sec_desc_size(__u8 csum_alg, int request, int read);
+int bulk_sec_desc_size(__u8 hash_alg, int request, int read);
 int bulk_sec_desc_unpack(struct lustre_msg *msg, int offset);
 
 int bulk_csum_cli_request(struct ptlrpc_bulk_desc *desc, int read,
diff --git a/lustre/ldlm/ldlm_lib.c b/lustre/ldlm/ldlm_lib.c
index b4df9c678fa49dc34f653e93dcc0655869ba0ff7..a5ce738c27af4eec3aedad3c546efded20786d17 100644
--- a/lustre/ldlm/ldlm_lib.c
+++ b/lustre/ldlm/ldlm_lib.c
@@ -412,8 +412,9 @@ int client_connect_import(const struct lu_env *env,
         LASSERT(exp->exp_connection);
 
         if (data) {
-                LASSERT((ocd->ocd_connect_flags & data->ocd_connect_flags) ==
-                        ocd->ocd_connect_flags);
+                LASSERTF((ocd->ocd_connect_flags & data->ocd_connect_flags) ==
+                         ocd->ocd_connect_flags, "old "LPX64", new "LPX64"\n",
+                         data->ocd_connect_flags, ocd->ocd_connect_flags);
                 data->ocd_connect_flags = ocd->ocd_connect_flags;
         }
 
diff --git a/lustre/liblustre/genlib.sh b/lustre/liblustre/genlib.sh
index dcc0009b8d94ed3e1e7572c1717d257f499904e0..2fc5bb5ac7845ea49f533cd56aa5d0f09ae019b4 100755
--- a/lustre/liblustre/genlib.sh
+++ b/lustre/liblustre/genlib.sh
@@ -108,7 +108,7 @@ OS=`uname`
 if test x$OS = xAIX; then
 $LD -shared -o $CWD/liblustre.so $ALL_OBJS -lpthread -Xlinker -bnoipath ../../libsyscall.so
 else
-$LD -shared -nostdlib -o $CWD/liblustre.so $ALL_OBJS $CAP_LIBS $PTHREAD_LIBS
+$LD -shared -nostdlib -o $CWD/liblustre.so $ALL_OBJS $CAP_LIBS $PTHREAD_LIBS $ZLIB
 fi
 
 rm -rf $sysio_tmp
diff --git a/lustre/liblustre/tests/Makefile.am b/lustre/liblustre/tests/Makefile.am
index d1fd8cd62da2244439ebb6ade60bb0995bcd70df..f3906bfb9b83a392353dad4bcc7250a8e741d7d7 100644
--- a/lustre/liblustre/tests/Makefile.am
+++ b/lustre/liblustre/tests/Makefile.am
@@ -4,7 +4,7 @@ AM_CPPFLAGS = -I$(SYSIO)/include -I/opt/lam/include $(LLCPPFLAGS) -I$(top_srcdir
 AM_CFLAGS = $(LLCFLAGS)
 AM_LIBS = $(LIBEFENCE) $(LIBREADLINE)
 
-LLIB_EXEC = $(top_builddir)/lustre/utils/liblustreapi.a $(top_builddir)/lustre/liblustre/liblustre.a $(CAP_LIBS) $(PTHREAD_LIBS)
+LLIB_EXEC = $(top_builddir)/lustre/utils/liblustreapi.a $(top_builddir)/lustre/liblustre/liblustre.a $(CAP_LIBS) $(PTHREAD_LIBS) $(ZLIB)
 
 if LIBLUSTRE
 noinst_LIBRARIES = libtestcommon.a
@@ -31,7 +31,7 @@ libtestcommon_a_SOURCES = test_common.c test_common.h
 
 echo_test_SOURCES = echo_test.c  $(top_srcdir)/lustre/utils/parser.c $(top_srcdir)/lustre/utils/obd.c $(top_srcdir)/lustre/utils/lustre_cfg.c
 echo_test_CFLAGS = $(LL_CFLAGS)
-echo_test_LDADD = $(top_builddir)/lustre/liblustre/liblsupport.a $(LIBREADLINE) $(CAP_LIBS) $(PTHREAD_LIBS) 
+echo_test_LDADD = $(top_builddir)/lustre/liblustre/liblsupport.a $(LIBREADLINE) $(CAP_LIBS) $(PTHREAD_LIBS) $(ZLIB)
 echo_test_DEPENDENCIES=$(top_builddir)/lustre/liblustre/liblsupport.a
 
 sanity_SOURCES = sanity.c
diff --git a/lustre/osc/osc_request.c b/lustre/osc/osc_request.c
index d8d5be7c78f3c1d866c691df0d75f78540b8455f..06ec3ddaf4c8a65e99a2a825569b2fa18c2ef7ca 100644
--- a/lustre/osc/osc_request.c
+++ b/lustre/osc/osc_request.c
@@ -1125,7 +1125,7 @@ static int osc_brw_prep_request(int cmd, struct client_obd *cli,struct obdo *oa,
         /* size[REQ_REC_OFF] still sizeof (*body) */
         if (opc == OST_WRITE) {
                 if (unlikely(cli->cl_checksum) &&
-                    req->rq_flvr.sf_bulk_csum == BULK_CSUM_ALG_NULL) {
+                    req->rq_flvr.sf_bulk_hash == BULK_HASH_ALG_NULL) {
                         body->oa.o_valid |= OBD_MD_FLCKSUM;
                         body->oa.o_cksum = osc_checksum_bulk(requested_nob,
                                                              page_count, pga,
@@ -1145,7 +1145,7 @@ static int osc_brw_prep_request(int cmd, struct client_obd *cli,struct obdo *oa,
                                      sizeof(__u32) * niocount);
         } else {
                 if (unlikely(cli->cl_checksum) &&
-                    req->rq_flvr.sf_bulk_csum == BULK_CSUM_ALG_NULL)
+                    req->rq_flvr.sf_bulk_hash == BULK_HASH_ALG_NULL)
                         body->oa.o_valid |= OBD_MD_FLCKSUM;
                 req_capsule_set_size(pill, &RMF_NIOBUF_REMOTE, RCL_SERVER, 0);
                 /* 1 RC for the whole I/O */
diff --git a/lustre/ptlrpc/gss/gss_bulk.c b/lustre/ptlrpc/gss/gss_bulk.c
index 9f829adca1180950b73900e86b184bea9c36bc6d..e8ede290545e1030eee55b07579498766084d58f 100644
--- a/lustre/ptlrpc/gss/gss_bulk.c
+++ b/lustre/ptlrpc/gss/gss_bulk.c
@@ -1,7 +1,9 @@
 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
  * vim:expandtab:shiftwidth=8:tabstop=8:
  *
- * Copyright (C) 2006 Cluster File Systems, Inc.
+ * Copyright (C) 2008 Sun Microsystems. Inc.
+ *   Author: Eric Mei <eric.mei@sun.com>
+ * Copyright (C) 2006,2007 Cluster File Systems, Inc.
  *   Author: Eric Mei <ericm@clusterfs.com>
  *
  *   This file is part of Lustre, http://www.lustre.org.
@@ -49,88 +51,362 @@
 #include "gss_internal.h"
 #include "gss_api.h"
 
-static
-int do_bulk_privacy(struct gss_ctx *gctx,
-                    struct ptlrpc_bulk_desc *desc,
-                    int encrypt, __u32 alg,
-                    struct ptlrpc_bulk_sec_desc *bsd)
+static __u8 zero_iv[CIPHER_MAX_BLKSIZE] = { 0, };
+
+static void buf_to_sl(struct scatterlist *sl,
+                      void *buf, unsigned int len)
+{
+        sl->page = virt_to_page(buf);
+        sl->offset = offset_in_page(buf);
+        sl->length = len;
+}
+
+/*
+ * CTS CBC encryption:
+ * 1. X(n-1) = P(n-1)
+ * 2. E(n-1) = Encrypt(K, X(n-1))
+ * 3. C(n)   = HEAD(E(n-1))
+ * 4. P      = P(n) | 0
+ * 5. D(n)   = E(n-1) XOR P
+ * 6. C(n-1) = Encrypt(K, D(n))
+ *
+ * CTS encryption using standard CBC interface:
+ * 1. pad the last partial block with 0.
+ * 2. do CBC encryption.
+ * 3. swap the last two ciphertext blocks.
+ * 4. truncate to original plaintext size.
+ */
+static int cbc_cts_encrypt(struct crypto_tfm *tfm,
+                           struct scatterlist *sld,
+                           struct scatterlist *sls)
+{
+        struct scatterlist      slst, sldt;
+        void                   *data;
+        __u8                    sbuf[CIPHER_MAX_BLKSIZE];
+        __u8                    dbuf[CIPHER_MAX_BLKSIZE];
+        unsigned int            blksize, blks, tail;
+        int                     rc;
+
+        blksize = crypto_tfm_alg_blocksize(tfm);
+        blks = sls->length / blksize;
+        tail = sls->length % blksize;
+        LASSERT(blks > 0 && tail > 0);
+
+        /* pad tail block with 0, copy to sbuf */
+        data = cfs_kmap(sls->page);
+        memcpy(sbuf, data + sls->offset + blks * blksize, tail);
+        memset(sbuf + tail, 0, blksize - tail);
+        cfs_kunmap(sls->page);
+
+        buf_to_sl(&slst, sbuf, blksize);
+        buf_to_sl(&sldt, dbuf, blksize);
+
+        /* encrypt head */
+        rc = crypto_cipher_encrypt(tfm, sld, sls, sls->length - tail);
+        if (unlikely(rc)) {
+                CERROR("encrypt head (%u) data: %d\n", sls->length - tail, rc);
+                return rc;
+        }
+        /* encrypt tail */
+        rc = crypto_cipher_encrypt(tfm, &sldt, &slst, blksize);
+        if (unlikely(rc)) {
+                CERROR("encrypt tail (%u) data: %d\n", slst.length, rc);
+                return rc;
+        }
+
+        /* swab C(n) and C(n-1), if n == 1, then C(n-1) is the IV */
+        data = cfs_kmap(sld->page);
+
+        memcpy(data + sld->offset + blks * blksize,
+               data + sld->offset + (blks - 1) * blksize, tail);
+        memcpy(data + sld->offset + (blks - 1) * blksize, dbuf, blksize);
+        cfs_kunmap(sld->page);
+
+        return 0;
+}
+
+/*
+ * CTS CBC decryption:
+ * 1. D(n)   = Decrypt(K, C(n-1))
+ * 2. C      = C(n) | 0
+ * 3. X(n)   = D(n) XOR C
+ * 4. P(n)   = HEAD(X(n))
+ * 5. E(n-1) = C(n) | TAIL(X(n))
+ * 6. X(n-1) = Decrypt(K, E(n-1))
+ * 7. P(n-1) = X(n-1) XOR C(n-2)
+ *
+ * CTS decryption using standard CBC interface:
+ * 1. D(n)   = Decrypt(K, C(n-1))
+ * 2. C(n)   = C(n) | TAIL(D(n))
+ * 3. swap the last two ciphertext blocks.
+ * 4. do CBC decryption.
+ * 5. truncate to original ciphertext size.
+ */
+static int cbc_cts_decrypt(struct crypto_tfm *tfm,
+                           struct scatterlist *sld,
+                           struct scatterlist *sls)
+{
+        struct scatterlist      slst, sldt;
+        void                   *data;
+        __u8                    sbuf[CIPHER_MAX_BLKSIZE];
+        __u8                    dbuf[CIPHER_MAX_BLKSIZE];
+        unsigned int            blksize, blks, tail;
+        int                     rc;
+
+        blksize = crypto_tfm_alg_blocksize(tfm);
+        blks = sls->length / blksize;
+        tail = sls->length % blksize;
+        LASSERT(blks > 0 && tail > 0);
+
+        /* save current IV, and set IV to zero */
+        crypto_cipher_get_iv(tfm, sbuf, blksize);
+        crypto_cipher_set_iv(tfm, zero_iv, blksize);
+
+        /* D(n) = Decrypt(K, C(n-1)) */
+        slst = *sls;
+        slst.offset += (blks - 1) * blksize;
+        slst.length = blksize;
+
+        buf_to_sl(&sldt, dbuf, blksize);
+
+        rc = crypto_cipher_decrypt(tfm, &sldt, &slst, blksize);
+        if (unlikely(rc)) {
+                CERROR("decrypt C(n-1) (%u): %d\n", slst.length, rc);
+                return rc;
+        }
+
+        /* restore IV */
+        crypto_cipher_set_iv(tfm, sbuf, blksize);
+
+        data = cfs_kmap(sls->page);
+        /* C(n) = C(n) | TAIL(D(n)) */
+        memcpy(dbuf, data + sls->offset + blks * blksize, tail);
+        /* swab C(n) and C(n-1) */
+        memcpy(sbuf, data + sls->offset + (blks - 1) * blksize, blksize);
+        memcpy(data + sls->offset + (blks - 1) * blksize, dbuf, blksize);
+        cfs_kunmap(sls->page);
+
+        /* do cbc decrypt */
+        buf_to_sl(&slst, sbuf, blksize);
+        buf_to_sl(&sldt, dbuf, blksize);
+
+        /* decrypt head */
+        rc = crypto_cipher_decrypt(tfm, sld, sls, sls->length - tail);
+        if (unlikely(rc)) {
+                CERROR("decrypt head (%u) data: %d\n", sls->length - tail, rc);
+                return rc;
+        }
+        /* decrypt tail */
+        rc = crypto_cipher_decrypt(tfm, &sldt, &slst, blksize);
+        if (unlikely(rc)) {
+                CERROR("decrypt tail (%u) data: %d\n", slst.length, rc);
+                return rc;
+        }
+
+        /* truncate to original ciphertext size */
+        data = cfs_kmap(sld->page);
+        memcpy(data + sld->offset + blks * blksize, dbuf, tail);
+        cfs_kunmap(sld->page);
+
+        return 0;
+}
+
+static inline int do_cts_tfm(struct crypto_tfm *tfm,
+                             int encrypt,
+                             struct scatterlist *sld,
+                             struct scatterlist *sls)
+{
+        LASSERT(tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_CBC);
+
+        if (encrypt)
+                return cbc_cts_encrypt(tfm, sld, sls);
+        else
+                return cbc_cts_decrypt(tfm, sld, sls);
+}
+
+/*
+ * normal encrypt/decrypt of data of even blocksize
+ */
+static inline int do_cipher_tfm(struct crypto_tfm *tfm,
+                                int encrypt,
+                                struct scatterlist *sld,
+                                struct scatterlist *sls)
+{
+        if (encrypt)
+                return crypto_cipher_encrypt(tfm, sld, sls, sls->length);
+        else
+                return crypto_cipher_decrypt(tfm, sld, sls, sls->length);
+}
+
+static struct crypto_tfm *get_stream_cipher(__u8 *key, unsigned int keylen)
+{
+        const struct sptlrpc_ciph_type *ct;
+        struct crypto_tfm              *tfm;
+        int                             rc;
+
+        /* using ARC4, the only stream cipher in linux for now */
+        ct = sptlrpc_get_ciph_type(BULK_CIPH_ALG_ARC4);
+        LASSERT(ct);
+
+        tfm = crypto_alloc_tfm(ct->sct_tfm_name, ct->sct_tfm_flags);
+        if (tfm == NULL) {
+                CERROR("Failed to allocate stream TFM %s\n", ct->sct_name);
+                return NULL;
+        }
+        LASSERT(crypto_tfm_alg_blocksize(tfm));
+
+        if (keylen > ct->sct_keysize)
+                keylen = ct->sct_keysize;
+
+        LASSERT(keylen >= crypto_tfm_alg_min_keysize(tfm));
+        LASSERT(keylen <= crypto_tfm_alg_max_keysize(tfm));
+
+        rc = crypto_cipher_setkey(tfm, key, keylen);
+        if (rc) {
+                CERROR("Failed to set key for TFM %s: %d\n", ct->sct_name, rc);
+                crypto_free_tfm(tfm);
+                return NULL;
+        }
+
+        return tfm;
+}
+
+static int do_bulk_privacy(struct gss_ctx *gctx,
+                           struct ptlrpc_bulk_desc *desc,
+                           int encrypt, __u32 alg,
+                           struct ptlrpc_bulk_sec_desc *bsd)
 {
+        const struct sptlrpc_ciph_type *ct = sptlrpc_get_ciph_type(alg);
         struct crypto_tfm  *tfm;
-        struct scatterlist  sg, sg2, *sgd;
-        unsigned int        blksize;
+        struct crypto_tfm  *stfm = NULL; /* backup stream cipher */
+        struct scatterlist  sls, sld, *sldp;
+        unsigned int        blksize, keygen_size;
         int                 i, rc;
-        __u8                local_iv[sizeof(bsd->bsd_iv)];
+        __u8                key[CIPHER_MAX_KEYSIZE];
 
-        LASSERT(alg < BULK_PRIV_ALG_MAX);
+        LASSERT(ct);
 
         if (encrypt)
-                bsd->bsd_priv_alg = BULK_PRIV_ALG_NULL;
+                bsd->bsd_ciph_alg = BULK_CIPH_ALG_NULL;
 
-        if (alg == BULK_PRIV_ALG_NULL)
+        if (alg == BULK_CIPH_ALG_NULL)
                 return 0;
 
-        tfm = crypto_alloc_tfm(sptlrpc_bulk_priv_alg2name(alg),
-                               sptlrpc_bulk_priv_alg2flags(alg));
+        if (desc->bd_iov_count <= 0) {
+                if (encrypt)
+                        bsd->bsd_ciph_alg = alg;
+                return 0;
+        }
+
+        tfm = crypto_alloc_tfm(ct->sct_tfm_name, ct->sct_tfm_flags);
         if (tfm == NULL) {
-                CERROR("Failed to allocate TFM %s\n",
-                       sptlrpc_bulk_priv_alg2name(alg));
+                CERROR("Failed to allocate TFM %s\n", ct->sct_name);
                 return -ENOMEM;
         }
-
         blksize = crypto_tfm_alg_blocksize(tfm);
-        LASSERT(blksize <= sizeof(local_iv));
 
-        if (encrypt)
-                get_random_bytes(bsd->bsd_iv, sizeof(bsd->bsd_iv));
+        LASSERT(crypto_tfm_alg_max_keysize(tfm) >= ct->sct_keysize);
+        LASSERT(crypto_tfm_alg_min_keysize(tfm) <= ct->sct_keysize);
+        LASSERT(ct->sct_ivsize == 0 ||
+                crypto_tfm_alg_ivsize(tfm) == ct->sct_ivsize);
+        LASSERT(ct->sct_keysize <= CIPHER_MAX_KEYSIZE);
+        LASSERT(blksize <= CIPHER_MAX_BLKSIZE);
+
+        /* generate ramdom key seed and compute the secret key based on it.
+         * note determined by algorithm which lgss_plain_encrypt use, it
+         * might require the key size be its (blocksize * n). so here for
+         * simplicity, we force it's be n * MAX_BLKSIZE by padding 0 */
+        keygen_size = (ct->sct_keysize + CIPHER_MAX_BLKSIZE - 1) &
+                      ~(CIPHER_MAX_BLKSIZE - 1);
+        if (encrypt) {
+                get_random_bytes(bsd->bsd_key, ct->sct_keysize);
+                if (ct->sct_keysize < keygen_size)
+                        memset(bsd->bsd_key + ct->sct_keysize, 0,
+                               keygen_size - ct->sct_keysize);
+        }
 
-        /* compute the secret iv */
-        rc = lgss_plain_encrypt(gctx, 0,
-                                sizeof(local_iv), bsd->bsd_iv, local_iv);
+        rc = lgss_plain_encrypt(gctx, 0, keygen_size, bsd->bsd_key, key);
         if (rc) {
-                CERROR("failed to compute secret iv: %d\n", rc);
+                CERROR("failed to compute secret key: %d\n", rc);
                 goto out;
         }
 
-        rc = crypto_cipher_setkey(tfm, local_iv, sizeof(local_iv));
+        rc = crypto_cipher_setkey(tfm, key, ct->sct_keysize);
         if (rc) {
-                CERROR("Failed to set key for TFM %s: %d\n",
-                       sptlrpc_bulk_priv_alg2name(alg), rc);
+                CERROR("Failed to set key for TFM %s: %d\n", ct->sct_name, rc);
                 goto out;
         }
 
+        /* stream cipher doesn't need iv */
+        if (blksize > 1)
+                crypto_cipher_set_iv(tfm, zero_iv, blksize);
+
         for (i = 0; i < desc->bd_iov_count; i++) {
-                sg.page = desc->bd_iov[i].kiov_page;
-                sg.offset = desc->bd_iov[i].kiov_offset;
-                sg.length = desc->bd_iov[i].kiov_len;
+                sls.page = desc->bd_iov[i].kiov_page;
+                sls.offset = desc->bd_iov[i].kiov_offset;
+                sls.length = desc->bd_iov[i].kiov_len;
+
+                if (unlikely(sls.length == 0)) {
+                        CWARN("page %d with 0 length data?\n", i);
+                        continue;
+                }
+
+                if (unlikely(sls.offset % blksize)) {
+                        CERROR("page %d with odd offset %u, TFM %s\n",
+                               i, sls.offset, ct->sct_name);
+                        rc = -EINVAL;
+                        goto out;
+                }
 
                 if (desc->bd_enc_pages) {
-                        sg2.page = desc->bd_enc_pages[i];
-                        sg2.offset = desc->bd_iov[i].kiov_offset;
-                        sg2.length = desc->bd_iov[i].kiov_len;
+                        sld.page = desc->bd_enc_pages[i];
+                        sld.offset = desc->bd_iov[i].kiov_offset;
+                        sld.length = desc->bd_iov[i].kiov_len;
 
-                        sgd = &sg2;
-                } else
-                        sgd = &sg;
+                        sldp = &sld;
+                } else {
+                        sldp = &sls;
+                }
 
-                if (encrypt)
-                        rc = crypto_cipher_encrypt(tfm, sgd, &sg, sg.length);
-                else
-                        rc = crypto_cipher_decrypt(tfm, sgd, &sg, sg.length);
+                if (likely(sls.length % blksize == 0)) {
+                        /* data length is n * blocksize, do the normal tfm */
+                        rc = do_cipher_tfm(tfm, encrypt, sldp, &sls);
+                } else if (sls.length < blksize) {
+                        /* odd data length, and smaller than 1 block, CTS
+                         * doesn't work in this case because it requires
+                         * transfer a modified IV to peer. here we use a
+                         * "backup" stream cipher to do the tfm */
+                        if (stfm == NULL) {
+                                stfm = get_stream_cipher(key, ct->sct_keysize);
+                                if (tfm == NULL) {
+                                        rc = -ENOMEM;
+                                        goto out;
+                                }
+                        }
+                        rc = do_cipher_tfm(stfm, encrypt, sldp, &sls);
+                } else {
+                        /* odd data length but > 1 block, do CTS tfm */
+                        rc = do_cts_tfm(tfm, encrypt, sldp, &sls);
+                }
 
-                LASSERT(rc == 0);
+                if (unlikely(rc)) {
+                        CERROR("error %s page %d/%d: %d\n",
+                               encrypt ? "encrypt" : "decrypt",
+                               i + 1, desc->bd_iov_count, rc);
+                        goto out;
+                }
 
                 if (desc->bd_enc_pages)
                         desc->bd_iov[i].kiov_page = desc->bd_enc_pages[i];
-
-                /* although the procedure might be lengthy, the crypto functions
-                 * internally called cond_resched() from time to time.
-                 */
         }
 
         if (encrypt)
-                bsd->bsd_priv_alg = alg;
+                bsd->bsd_ciph_alg = alg;
 
 out:
+        if (stfm)
+                crypto_free_tfm(stfm);
+
         crypto_free_tfm(tfm);
         return rc;
 }
@@ -171,21 +447,21 @@ int gss_cli_ctx_wrap_bulk(struct ptlrpc_cli_ctx *ctx,
 
         /* make checksum */
         rc = bulk_csum_cli_request(desc, req->rq_bulk_read,
-                                   req->rq_flvr.sf_bulk_csum, msg, offset);
+                                   req->rq_flvr.sf_bulk_hash, msg, offset);
         if (rc) {
                 CERROR("client bulk %s: failed to generate checksum: %d\n",
                        req->rq_bulk_read ? "read" : "write", rc);
                 RETURN(rc);
         }
 
-        if (req->rq_flvr.sf_bulk_priv == BULK_PRIV_ALG_NULL)
+        if (req->rq_flvr.sf_bulk_ciph == BULK_CIPH_ALG_NULL)
                 RETURN(0);
 
         /* previous bulk_csum_cli_request() has verified bsdr is good */
         bsdr = lustre_msg_buf(msg, offset, 0);
 
         if (req->rq_bulk_read) {
-                bsdr->bsd_priv_alg = req->rq_flvr.sf_bulk_priv;
+                bsdr->bsd_ciph_alg = req->rq_flvr.sf_bulk_ciph;
                 RETURN(0);
         }
 
@@ -200,7 +476,7 @@ int gss_cli_ctx_wrap_bulk(struct ptlrpc_cli_ctx *ctx,
         LASSERT(gctx->gc_mechctx);
 
         rc = do_bulk_privacy(gctx->gc_mechctx, desc, 1,
-                             req->rq_flvr.sf_bulk_priv, bsdr);
+                             req->rq_flvr.sf_bulk_ciph, bsdr);
         if (rc)
                 CERROR("bulk write: client failed to encrypt pages\n");
 
@@ -255,23 +531,23 @@ int gss_cli_ctx_unwrap_bulk(struct ptlrpc_cli_ctx *ctx,
 
         if (req->rq_bulk_read) {
                 bsdr = lustre_msg_buf(rmsg, roff, 0);
-                if (bsdr->bsd_priv_alg == BULK_PRIV_ALG_NULL)
+                if (bsdr->bsd_ciph_alg == BULK_CIPH_ALG_NULL)
                         goto verify_csum;
 
                 bsdv = lustre_msg_buf(vmsg, voff, 0);
-                if (bsdr->bsd_priv_alg != bsdv->bsd_priv_alg) {
+                if (bsdr->bsd_ciph_alg != bsdv->bsd_ciph_alg) {
                         CERROR("bulk read: cipher algorithm mismatch: client "
                                "request %s but server reply with %s. try to "
                                "use the new one for decryption\n",
-                               sptlrpc_bulk_priv_alg2name(bsdr->bsd_priv_alg),
-                               sptlrpc_bulk_priv_alg2name(bsdv->bsd_priv_alg));
+                               sptlrpc_get_ciph_name(bsdr->bsd_ciph_alg),
+                               sptlrpc_get_ciph_name(bsdv->bsd_ciph_alg));
                 }
 
                 gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
                 LASSERT(gctx->gc_mechctx);
 
                 rc = do_bulk_privacy(gctx->gc_mechctx, desc, 0,
-                                     bsdv->bsd_priv_alg, bsdv);
+                                     bsdv->bsd_ciph_alg, bsdv);
                 if (rc) {
                         CERROR("bulk read: client failed to decrypt data\n");
                         RETURN(rc);
@@ -303,9 +579,9 @@ int gss_svc_unwrap_bulk(struct ptlrpc_request *req,
         LASSERT(grctx->src_ctx->gsc_mechctx);
 
         /* decrypt bulk data if it's encrypted */
-        if (grctx->src_reqbsd->bsd_priv_alg != BULK_PRIV_ALG_NULL) {
+        if (grctx->src_reqbsd->bsd_ciph_alg != BULK_CIPH_ALG_NULL) {
                 rc = do_bulk_privacy(grctx->src_ctx->gsc_mechctx, desc, 0,
-                                     grctx->src_reqbsd->bsd_priv_alg,
+                                     grctx->src_reqbsd->bsd_ciph_alg,
                                      grctx->src_reqbsd);
                 if (rc) {
                         CERROR("bulk write: server failed to decrypt data\n");
@@ -347,9 +623,9 @@ int gss_svc_wrap_bulk(struct ptlrpc_request *req,
                 RETURN(rc);
 
         /* encrypt bulk data if required */
-        if (grctx->src_reqbsd->bsd_priv_alg != BULK_PRIV_ALG_NULL) {
+        if (grctx->src_reqbsd->bsd_ciph_alg != BULK_CIPH_ALG_NULL) {
                 rc = do_bulk_privacy(grctx->src_ctx->gsc_mechctx, desc, 1,
-                                     grctx->src_reqbsd->bsd_priv_alg,
+                                     grctx->src_reqbsd->bsd_ciph_alg,
                                      grctx->src_repbsd);
                 if (rc)
                         CERROR("bulk read: server failed to encrypt data: "
diff --git a/lustre/ptlrpc/gss/gss_cli_upcall.c b/lustre/ptlrpc/gss/gss_cli_upcall.c
index fedbc0192911ad6e1410a3bb6a67c5a3461b3957..ccc000fc3c65025a7a2352e0d40eb2694e9579f2 100644
--- a/lustre/ptlrpc/gss/gss_cli_upcall.c
+++ b/lustre/ptlrpc/gss/gss_cli_upcall.c
@@ -388,11 +388,9 @@ int gss_do_ctx_fini_rpc(struct gss_cli_ctx *gctx)
 
         req->rq_phase = RQ_PHASE_RPC;
         rc = ptl_send_rpc(req, 1);
-        if (rc) {
-                CWARN("ctx %p(%u->%s): rpc error %d, destroy locally\n",
-                      ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
-                      rc);
-        }
+        if (rc)
+                CWARN("ctx %p(%u->%s): rpc error %d, destroy locally\n", ctx,
+                      ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec), rc);
 
 out_ref:
         ptlrpc_req_finished(req);
diff --git a/lustre/ptlrpc/gss/gss_keyring.c b/lustre/ptlrpc/gss/gss_keyring.c
index f469791ba2288b3b73e8cb143d11dff20df9dc14..5ea2f62a788197efc9ad81b5e43211a52070782d 100644
--- a/lustre/ptlrpc/gss/gss_keyring.c
+++ b/lustre/ptlrpc/gss/gss_keyring.c
@@ -431,10 +431,26 @@ static void dispose_ctx_list_kr(struct hlist_head *freelist)
 {
         struct hlist_node      *pos, *next;
         struct ptlrpc_cli_ctx  *ctx;
+        struct gss_cli_ctx     *gctx;
 
         hlist_for_each_entry_safe(ctx, pos, next, freelist, cc_cache) {
                 hlist_del_init(&ctx->cc_cache);
 
+                /* reverse ctx: update current seq to buddy svcctx if exist.
+                 * ideally this should be done at gss_cli_ctx_finalize(), but
+                 * the ctx destroy could be delayed by:
+                 *  1) ctx still has reference;
+                 *  2) ctx destroy is asynchronous;
+                 * and reverse import call inval_all_ctx() require this be done
+                 *_immediately_ otherwise newly created reverse ctx might copy
+                 * the very old sequence number from svcctx. */
+                gctx = ctx2gctx(ctx);
+                if (!rawobj_empty(&gctx->gc_svc_handle) &&
+                    sec_is_reverse(gctx->gc_base.cc_sec)) {
+                        gss_svc_upcall_update_sequence(&gctx->gc_svc_handle,
+                                        (__u32) atomic_read(&gctx->gc_seq));
+                }
+
                 /* we need to wakeup waiting reqs here. the context might
                  * be forced released before upcall finished, then the
                  * late-arrived downcall can't find the ctx even. */
@@ -1022,7 +1038,6 @@ void gss_cli_ctx_die_kr(struct ptlrpc_cli_ctx *ctx, int grace)
         LASSERT(atomic_read(&ctx->cc_refcount) > 0);
         LASSERT(ctx->cc_sec);
 
-        CWARN("ctx %p(%d)\n", ctx, atomic_read(&ctx->cc_refcount));
         cli_ctx_expire(ctx);
         kill_ctx_kr(ctx);
 }
diff --git a/lustre/ptlrpc/gss/sec_gss.c b/lustre/ptlrpc/gss/sec_gss.c
index 8f19122f044dc45028c895057aa2554ea62bb760..f33cddb19758e62c7ae18e0e87b53bad3fbe171d 100644
--- a/lustre/ptlrpc/gss/sec_gss.c
+++ b/lustre/ptlrpc/gss/sec_gss.c
@@ -412,13 +412,10 @@ static void gss_cli_ctx_finalize(struct gss_cli_ctx *gctx)
         }
 
         if (!rawobj_empty(&gctx->gc_svc_handle)) {
-                /* forward ctx: mark buddy reverse svcctx soon-expire.
-                 * reverse ctx: update current seq to buddy svcctx. */
-                if (!sec_is_reverse(gctx->gc_base.cc_sec))
+                /* forward ctx: mark buddy reverse svcctx soon-expire. */
+                if (!sec_is_reverse(gctx->gc_base.cc_sec) &&
+                    !rawobj_empty(&gctx->gc_svc_handle))
                         gss_svc_upcall_expire_rvs_ctx(&gctx->gc_svc_handle);
-                else
-                        gss_svc_upcall_update_sequence(&gctx->gc_svc_handle,
-                                        (__u32) atomic_read(&gctx->gc_seq));
 
                 rawobj_free(&gctx->gc_svc_handle);
         }
@@ -676,51 +673,55 @@ int gss_cli_ctx_handle_err_notify(struct ptlrpc_cli_ctx *ctx,
 
         errhdr = (struct gss_err_header *) ghdr;
 
+        CWARN("req x"LPU64"/t"LPU64", ctx %p idx "LPX64"(%u->%s): "
+              "%sserver respond (%08x/%08x)\n",
+              req->rq_xid, req->rq_transno, ctx,
+              gss_handle_to_u64(&ctx2gctx(ctx)->gc_handle),
+              ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
+              sec_is_reverse(ctx->cc_sec) ? "reverse" : "",
+              errhdr->gh_major, errhdr->gh_minor);
+
+        /* context fini rpc, let it failed */
+        if (req->rq_ctx_fini) {
+                CWARN("context fini rpc failed\n");
+                return -EINVAL;
+        }
+
+        /* reverse sec, just return error, don't expire this ctx because it's
+         * crucial to callback rpcs. note if the callback rpc failed because
+         * of bit flip during network transfer, the client will be evicted
+         * directly. so more gracefully we probably want let it retry for
+         * number of times. */
+        if (sec_is_reverse(ctx->cc_sec))
+                return -EINVAL;
+
+        if (errhdr->gh_major != GSS_S_NO_CONTEXT &&
+            errhdr->gh_major != GSS_S_BAD_SIG)
+                return -EACCES;
+
         /* server return NO_CONTEXT might be caused by context expire
-         * or server reboot/failover. we refresh the cred transparently
-         * to upper layer.
+         * or server reboot/failover. we try to refresh a new ctx which
+         * be transparent to upper layer.
+         *
          * In some cases, our gss handle is possible to be incidentally
          * identical to another handle since the handle itself is not
          * fully random. In krb5 case, the GSS_S_BAD_SIG will be
          * returned, maybe other gss error for other mechanism.
          *
          * if we add new mechanism, make sure the correct error are
-         * returned in this case.
-         *
-         * but in any cases, don't resend ctx destroying rpc, don't resend
-         * reverse rpc. */
-        if (req->rq_ctx_fini) {
-                CWARN("server respond error (%08x/%08x) for ctx fini\n",
-                      errhdr->gh_major, errhdr->gh_minor);
-                rc = -EINVAL;
-        } else if (sec_is_reverse(ctx->cc_sec)) {
-                CWARN("reverse server respond error (%08x/%08x)\n",
-                      errhdr->gh_major, errhdr->gh_minor);
-                sptlrpc_cli_ctx_expire(ctx);
-                rc = -EINVAL;
-        } else if (errhdr->gh_major == GSS_S_NO_CONTEXT ||
-                   errhdr->gh_major == GSS_S_BAD_SIG) {
-                CWARN("req x"LPU64"/t"LPU64": server respond ctx %p(%u->%s) "
-                      "%s, server might lost the context.\n",
-                      req->rq_xid, req->rq_transno, ctx, ctx->cc_vcred.vc_uid,
-                      sec2target_str(ctx->cc_sec),
-                      errhdr->gh_major == GSS_S_NO_CONTEXT ?
-                      "NO_CONTEXT" : "BAD_SIG");
-
-                sptlrpc_cli_ctx_expire(ctx);
-
-                /* we need replace the ctx right here, otherwise during
-                 * resent we'll hit the logic in sptlrpc_req_refresh_ctx()
-                 * which keep the ctx with RESEND flag, thus we'll never
-                 * get rid of this ctx. */
-                rc = sptlrpc_req_replace_dead_ctx(req);
-                if (rc == 0)
-                        req->rq_resend = 1;
-        } else {
-                CERROR("req %p: server report gss error (%x/%x)\n",
-                        req, errhdr->gh_major, errhdr->gh_minor);
-                rc = -EACCES;
-        }
+         * returned in this case. */
+        CWARN("%s: server might lost the context, retrying\n",
+              errhdr->gh_major == GSS_S_NO_CONTEXT ?  "NO_CONTEXT" : "BAD_SIG");
+
+        sptlrpc_cli_ctx_expire(ctx);
+
+        /* we need replace the ctx right here, otherwise during
+         * resent we'll hit the logic in sptlrpc_req_refresh_ctx()
+         * which keep the ctx with RESEND flag, thus we'll never
+         * get rid of this ctx. */
+        rc = sptlrpc_req_replace_dead_ctx(req);
+        if (rc == 0)
+                req->rq_resend = 1;
 
         return rc;
 }
@@ -802,14 +803,15 @@ int gss_cli_ctx_verify(struct ptlrpc_cli_ctx *ctx,
                 req->rq_replen = msg->lm_buflens[1];
 
                 if (req->rq_pack_bulk) {
-                        if (msg->lm_bufcount < 4) {
+                        /* FIXME */
+                        /* bulk checksum is right after the lustre msg */
+                        if (msg->lm_bufcount < 3) {
                                 CERROR("Invalid reply bufcount %u\n",
                                        msg->lm_bufcount);
                                 RETURN(-EPROTO);
                         }
 
-                        /* bulk checksum is the second last segment */
-                        rc = bulk_sec_desc_unpack(msg, msg->lm_bufcount - 2);
+                        rc = bulk_sec_desc_unpack(msg, 2);
                 }
                 break;
         case PTLRPC_GSS_PROC_ERR:
@@ -1082,7 +1084,7 @@ int gss_sec_create_common(struct gss_sec *gsec,
                 sec->ps_gc_interval = 0;
         }
 
-        if (sec->ps_flvr.sf_bulk_priv != BULK_PRIV_ALG_NULL &&
+        if (sec->ps_flvr.sf_bulk_ciph != BULK_CIPH_ALG_NULL &&
             sec->ps_flvr.sf_flags & PTLRPC_SEC_FL_BULK)
                 sptlrpc_enc_pool_add_user();
 
@@ -1107,7 +1109,7 @@ void gss_sec_destroy_common(struct gss_sec *gsec)
 
         class_import_put(sec->ps_import);
 
-        if (sec->ps_flvr.sf_bulk_priv != BULK_PRIV_ALG_NULL &&
+        if (sec->ps_flvr.sf_bulk_ciph != BULK_CIPH_ALG_NULL &&
             sec->ps_flvr.sf_flags & PTLRPC_SEC_FL_BULK)
                 sptlrpc_enc_pool_del_user();
 
@@ -1230,7 +1232,7 @@ int gss_alloc_reqbuf_intg(struct ptlrpc_sec *sec,
 
         if (req->rq_pack_bulk) {
                 buflens[bufcnt] = bulk_sec_desc_size(
-                                                req->rq_flvr.sf_bulk_csum, 1,
+                                                req->rq_flvr.sf_bulk_hash, 1,
                                                 req->rq_bulk_read);
                 if (svc == SPTLRPC_SVC_INTG)
                         txtsize += buflens[bufcnt];
@@ -1297,7 +1299,7 @@ int gss_alloc_reqbuf_priv(struct ptlrpc_sec *sec,
                 ibuflens[ibufcnt++] = sptlrpc_current_user_desc_size();
         if (req->rq_pack_bulk)
                 ibuflens[ibufcnt++] = bulk_sec_desc_size(
-                                                req->rq_flvr.sf_bulk_csum, 1,
+                                                req->rq_flvr.sf_bulk_hash, 1,
                                                 req->rq_bulk_read);
 
         clearsize = lustre_msg_size_v2(ibufcnt, ibuflens);
@@ -1462,7 +1464,7 @@ int gss_alloc_repbuf_intg(struct ptlrpc_sec *sec,
 
         if (req->rq_pack_bulk) {
                 buflens[bufcnt] = bulk_sec_desc_size(
-                                                req->rq_flvr.sf_bulk_csum, 0,
+                                                req->rq_flvr.sf_bulk_hash, 0,
                                                 req->rq_bulk_read);
                 if (svc == SPTLRPC_SVC_INTG)
                         txtsize += buflens[bufcnt];
@@ -1495,7 +1497,7 @@ int gss_alloc_repbuf_priv(struct ptlrpc_sec *sec,
 
         if (req->rq_pack_bulk) {
                 buflens[bufcnt++] = bulk_sec_desc_size(
-                                                req->rq_flvr.sf_bulk_csum, 0,
+                                                req->rq_flvr.sf_bulk_hash, 0,
                                                 req->rq_bulk_read);
         }
         txtsize = lustre_msg_size_v2(bufcnt, buflens);
@@ -2190,9 +2192,10 @@ int gss_svc_handle_data(struct ptlrpc_request *req,
         if (rc == 0)
                 RETURN(SECSVC_OK);
 
-        CERROR("svc %u failed: major 0x%08x: ctx %p(%u->%s)\n",
-               gw->gw_svc, major, grctx->src_ctx, grctx->src_ctx->gsc_uid,
-               libcfs_nid2str(req->rq_peer.nid));
+        CERROR("svc %u failed: major 0x%08x: req xid "LPU64" ctx %p idx "
+               LPX64"(%u->%s)\n", gw->gw_svc, major, req->rq_xid,
+               grctx->src_ctx, gss_handle_to_u64(&gw->gw_handle),
+               grctx->src_ctx->gsc_uid, libcfs_nid2str(req->rq_peer.nid));
 error:
         /* we only notify client in case of NO_CONTEXT/BAD_SIG, which
          * might happen after server reboot, to allow recovery. */
@@ -2406,7 +2409,7 @@ int gss_svc_alloc_rs(struct ptlrpc_request *req, int msglen)
 
                         bsd_off = ibufcnt;
                         ibuflens[ibufcnt++] = bulk_sec_desc_size(
-                                                grctx->src_reqbsd->bsd_csum_alg,
+                                                grctx->src_reqbsd->bsd_hash_alg,
                                                 0, req->rq_bulk_read);
                 }
 
@@ -2432,7 +2435,7 @@ int gss_svc_alloc_rs(struct ptlrpc_request *req, int msglen)
 
                         bsd_off = bufcnt;
                         buflens[bufcnt] = bulk_sec_desc_size(
-                                                grctx->src_reqbsd->bsd_csum_alg,
+                                                grctx->src_reqbsd->bsd_hash_alg,
                                                 0, req->rq_bulk_read);
                         if (svc == SPTLRPC_SVC_INTG)
                                 txtsize += buflens[bufcnt];
diff --git a/lustre/ptlrpc/sec.c b/lustre/ptlrpc/sec.c
index 059c3f343b11f6fcc12537559712c440bf828c02..5bd17687d894e5733eefb3aec98ed8cf24834032 100644
--- a/lustre/ptlrpc/sec.c
+++ b/lustre/ptlrpc/sec.c
@@ -184,17 +184,17 @@ int sptlrpc_flavor2name(struct sptlrpc_flavor *sf, char *buf, int bufsize)
 {
         char           *bulk;
 
-        if (sf->sf_bulk_priv != BULK_PRIV_ALG_NULL)
+        if (sf->sf_bulk_ciph != BULK_CIPH_ALG_NULL)
                 bulk = "bulkp";
-        else if (sf->sf_bulk_csum != BULK_CSUM_ALG_NULL)
+        else if (sf->sf_bulk_hash != BULK_HASH_ALG_NULL)
                 bulk = "bulki";
         else
                 bulk = "bulkn";
 
         snprintf(buf, bufsize, "%s-%s:%s/%s",
                  sptlrpc_rpcflavor2name(sf->sf_rpc), bulk,
-                 sptlrpc_bulk_csum_alg2name(sf->sf_bulk_csum),
-                 sptlrpc_bulk_priv_alg2name(sf->sf_bulk_priv));
+                 sptlrpc_get_hash_name(sf->sf_bulk_hash),
+                 sptlrpc_get_ciph_name(sf->sf_bulk_ciph));
         return 0;
 }
 EXPORT_SYMBOL(sptlrpc_flavor2name);
@@ -735,8 +735,8 @@ void sptlrpc_req_set_flavor(struct ptlrpc_request *req, int opcode)
 
         /* bulk security flag */
         if ((req->rq_bulk_read || req->rq_bulk_write) &&
-            (req->rq_flvr.sf_bulk_priv != BULK_PRIV_ALG_NULL ||
-             req->rq_flvr.sf_bulk_csum != BULK_CSUM_ALG_NULL))
+            (req->rq_flvr.sf_bulk_ciph != BULK_CIPH_ALG_NULL ||
+             req->rq_flvr.sf_bulk_hash != BULK_HASH_ALG_NULL))
                 req->rq_pack_bulk = 1;
 }
 
@@ -1099,19 +1099,19 @@ static void sptlrpc_import_sec_adapt_inplace(struct obd_import *imp,
                                              struct ptlrpc_sec *sec,
                                              struct sptlrpc_flavor *sf)
 {
-        if (sf->sf_bulk_priv != sec->ps_flvr.sf_bulk_priv ||
-            sf->sf_bulk_csum != sec->ps_flvr.sf_bulk_csum) {
+        if (sf->sf_bulk_ciph != sec->ps_flvr.sf_bulk_ciph ||
+            sf->sf_bulk_hash != sec->ps_flvr.sf_bulk_hash) {
                 CWARN("imp %p (%s->%s): changing bulk flavor %s/%s -> %s/%s\n",
                       imp, imp->imp_obd->obd_name,
                       obd_uuid2str(&imp->imp_connection->c_remote_uuid),
-                      sptlrpc_bulk_priv_alg2name(sec->ps_flvr.sf_bulk_priv),
-                      sptlrpc_bulk_csum_alg2name(sec->ps_flvr.sf_bulk_csum),
-                      sptlrpc_bulk_priv_alg2name(sf->sf_bulk_priv),
-                      sptlrpc_bulk_csum_alg2name(sf->sf_bulk_csum));
+                      sptlrpc_get_ciph_name(sec->ps_flvr.sf_bulk_ciph),
+                      sptlrpc_get_hash_name(sec->ps_flvr.sf_bulk_hash),
+                      sptlrpc_get_ciph_name(sf->sf_bulk_ciph),
+                      sptlrpc_get_hash_name(sf->sf_bulk_hash));
 
                 spin_lock(&sec->ps_lock);
-                sec->ps_flvr.sf_bulk_priv = sf->sf_bulk_priv;
-                sec->ps_flvr.sf_bulk_csum = sf->sf_bulk_csum;
+                sec->ps_flvr.sf_bulk_ciph = sf->sf_bulk_ciph;
+                sec->ps_flvr.sf_bulk_hash = sf->sf_bulk_hash;
                 spin_unlock(&sec->ps_lock);
         }
 
@@ -1157,8 +1157,8 @@ int sptlrpc_import_sec_adapt(struct obd_import *imp,
         } else {
                 /* reverse import, determine flavor from incoming reqeust */
                 sf.sf_rpc = rpc_flavor;
-                sf.sf_bulk_priv = BULK_PRIV_ALG_NULL;
-                sf.sf_bulk_csum = BULK_CSUM_ALG_NULL;
+                sf.sf_bulk_ciph = BULK_CIPH_ALG_NULL;
+                sf.sf_bulk_hash = BULK_HASH_ALG_NULL;
                 sf.sf_flags = PTLRPC_SEC_FL_REVERSE | PTLRPC_SEC_FL_ROOTONLY;
 
                 sp = sptlrpc_target_sec_part(imp->imp_obd);
@@ -1191,11 +1191,11 @@ int sptlrpc_import_sec_adapt(struct obd_import *imp,
                       svc_ctx == NULL ? "->" : "<-",
                       obd_uuid2str(&conn->c_remote_uuid),
                       sptlrpc_rpcflavor2name(sec->ps_flvr.sf_rpc),
-                      sptlrpc_bulk_csum_alg2name(sec->ps_flvr.sf_bulk_csum),
-                      sptlrpc_bulk_priv_alg2name(sec->ps_flvr.sf_bulk_priv),
+                      sptlrpc_get_hash_name(sec->ps_flvr.sf_bulk_hash),
+                      sptlrpc_get_ciph_name(sec->ps_flvr.sf_bulk_ciph),
                       sptlrpc_rpcflavor2name(sf.sf_rpc),
-                      sptlrpc_bulk_csum_alg2name(sf.sf_bulk_csum),
-                      sptlrpc_bulk_priv_alg2name(sf.sf_bulk_priv));
+                      sptlrpc_get_hash_name(sf.sf_bulk_hash),
+                      sptlrpc_get_ciph_name(sf.sf_bulk_ciph));
         } else {
                 CWARN("%simport %p (%s%s%s) netid %x: "
                       "select initial flavor (%s, %s/%s)\n",
@@ -1205,8 +1205,8 @@ int sptlrpc_import_sec_adapt(struct obd_import *imp,
                       obd_uuid2str(&conn->c_remote_uuid),
                       LNET_NIDNET(conn->c_self),
                       sptlrpc_rpcflavor2name(sf.sf_rpc),
-                      sptlrpc_bulk_csum_alg2name(sf.sf_bulk_csum),
-                      sptlrpc_bulk_priv_alg2name(sf.sf_bulk_priv));
+                      sptlrpc_get_hash_name(sf.sf_bulk_hash),
+                      sptlrpc_get_ciph_name(sf.sf_bulk_ciph));
         }
 
         mutex_down(&imp->imp_sec_mutex);
@@ -1551,6 +1551,15 @@ int sptlrpc_target_export_check(struct obd_export *exp,
                         return 0;
                 }
 
+                /* if flavor just changed, we should not proceed, just leave
+                 * it and current flavor will be discovered and replaced
+                 * shortly, and let _this_ rpc pass through */
+                if (exp->exp_flvr_changed) {
+                        LASSERT(exp->exp_flvr_adapt);
+                        spin_unlock(&exp->exp_lock);
+                        return 0;
+                }
+
                 if (exp->exp_flvr_adapt) {
                         exp->exp_flvr_adapt = 0;
                         CDEBUG(D_SEC, "exp %p (%x|%x|%x): do delayed adapt\n",
diff --git a/lustre/ptlrpc/sec_bulk.c b/lustre/ptlrpc/sec_bulk.c
index 57402202f5173d9398b01af537e0e16f6b204fc8..c7f41f7582f44830ffc5b6b756d17526eaf38416 100644
--- a/lustre/ptlrpc/sec_bulk.c
+++ b/lustre/ptlrpc/sec_bulk.c
@@ -29,8 +29,10 @@
 #ifndef __KERNEL__
 #include <liblustre.h>
 #include <libcfs/list.h>
+#include <zlib.h>
 #else
 #include <linux/crypto.h>
+#include <linux/zutil.h>
 #endif
 
 #include <obd.h>
@@ -751,36 +753,54 @@ void sptlrpc_enc_pool_fini(void)
  * implement checksum funcationality    *
  ****************************************/
 
-static struct {
-        char    *name;
-        int      size;
-} csum_types[] = {
-        [BULK_CSUM_ALG_NULL]    = { "null",     0 },
-        [BULK_CSUM_ALG_CRC32]   = { "crc32",    4 },
-        [BULK_CSUM_ALG_MD5]     = { "md5",     16 },
-        [BULK_CSUM_ALG_SHA1]    = { "sha1",    20 },
-        [BULK_CSUM_ALG_SHA256]  = { "sha256",  32 },
-        [BULK_CSUM_ALG_SHA384]  = { "sha384",  48 },
-        [BULK_CSUM_ALG_SHA512]  = { "sha512",  64 },
+static struct sptlrpc_hash_type hash_types[] = {
+        [BULK_HASH_ALG_NULL]    = { "null",     "null",         0 },
+        [BULK_HASH_ALG_ADLER32] = { "adler32",  "adler32",      4 },
+        [BULK_HASH_ALG_CRC32]   = { "crc32",    "crc32",        4 },
+        [BULK_HASH_ALG_MD5]     = { "md5",      "md5",          16 },
+        [BULK_HASH_ALG_SHA1]    = { "sha1",     "sha1",         20 },
+        [BULK_HASH_ALG_SHA256]  = { "sha256",   "sha256",       32 },
+        [BULK_HASH_ALG_SHA384]  = { "sha384",   "sha384",       48 },
+        [BULK_HASH_ALG_SHA512]  = { "sha512",   "sha512",       64 },
+        [BULK_HASH_ALG_WP256]   = { "wp256",    "wp256",        32 },
+        [BULK_HASH_ALG_WP384]   = { "wp384",    "wp384",        48 },
+        [BULK_HASH_ALG_WP512]   = { "wp512",    "wp512",        64 },
 };
 
-const char * sptlrpc_bulk_csum_alg2name(__u8 csum_alg)
+const struct sptlrpc_hash_type *sptlrpc_get_hash_type(__u8 hash_alg)
 {
-        if (csum_alg < BULK_CSUM_ALG_MAX)
-                return csum_types[csum_alg].name;
-        return "unknown";
+        struct sptlrpc_hash_type *ht;
+
+        if (hash_alg < BULK_HASH_ALG_MAX) {
+                ht = &hash_types[hash_alg];
+                if (ht->sht_tfm_name)
+                        return ht;
+        }
+        return NULL;
+}
+EXPORT_SYMBOL(sptlrpc_get_hash_type);
+
+const char * sptlrpc_get_hash_name(__u8 hash_alg)
+{
+        const struct sptlrpc_hash_type *ht;
+
+        ht = sptlrpc_get_hash_type(hash_alg);
+        if (ht)
+                return ht->sht_name;
+        else
+                return "unknown";
 }
-EXPORT_SYMBOL(sptlrpc_bulk_csum_alg2name);
+EXPORT_SYMBOL(sptlrpc_get_hash_name);
 
-int bulk_sec_desc_size(__u8 csum_alg, int request, int read)
+int bulk_sec_desc_size(__u8 hash_alg, int request, int read)
 {
         int size = sizeof(struct ptlrpc_bulk_sec_desc);
 
-        LASSERT(csum_alg < BULK_CSUM_ALG_MAX);
+        LASSERT(hash_alg < BULK_HASH_ALG_MAX);
 
         /* read request don't need extra data */
         if (!(read && request))
-                size += csum_types[csum_alg].size;
+                size += hash_types[hash_alg].sht_size;
 
         return size;
 }
@@ -797,31 +817,34 @@ int bulk_sec_desc_unpack(struct lustre_msg *msg, int offset)
                 return -EINVAL;
         }
 
-        if (lustre_msg_swabbed(msg)) {
-                __swab32s(&bsd->bsd_version);
-                __swab16s(&bsd->bsd_pad);
-        }
+        /* nothing to swab */
 
-        if (bsd->bsd_version != 0) {
+        if (unlikely(bsd->bsd_version != 0)) {
                 CERROR("Unexpected version %u\n", bsd->bsd_version);
                 return -EPROTO;
         }
 
-        if (bsd->bsd_csum_alg >= BULK_CSUM_ALG_MAX) {
+        if (unlikely(bsd->bsd_flags != 0)) {
+                CERROR("Unexpected flags %x\n", bsd->bsd_flags);
+                return -EPROTO;
+        }
+
+        if (unlikely(!sptlrpc_get_hash_type(bsd->bsd_hash_alg))) {
                 CERROR("Unsupported checksum algorithm %u\n",
-                       bsd->bsd_csum_alg);
+                       bsd->bsd_hash_alg);
                 return -EINVAL;
         }
-        if (bsd->bsd_priv_alg >= BULK_PRIV_ALG_MAX) {
+
+        if (unlikely(!sptlrpc_get_ciph_type(bsd->bsd_ciph_alg))) {
                 CERROR("Unsupported cipher algorithm %u\n",
-                       bsd->bsd_priv_alg);
+                       bsd->bsd_ciph_alg);
                 return -EINVAL;
         }
 
-        if (size > sizeof(*bsd) &&
-            size < sizeof(*bsd) + csum_types[bsd->bsd_csum_alg].size) {
+        if (unlikely(size > sizeof(*bsd)) &&
+            size < sizeof(*bsd) + hash_types[bsd->bsd_hash_alg].sht_size) {
                 CERROR("Mal-formed checksum data: csum alg %u, size %d\n",
-                       bsd->bsd_csum_alg, size);
+                       bsd->bsd_hash_alg, size);
                 return -EINVAL;
         }
 
@@ -830,14 +853,38 @@ int bulk_sec_desc_unpack(struct lustre_msg *msg, int offset)
 EXPORT_SYMBOL(bulk_sec_desc_unpack);
 
 #ifdef __KERNEL__
-static
-int do_bulk_checksum_crc32(struct ptlrpc_bulk_desc *desc, void *buf)
+
+static int do_bulk_checksum_adler32(struct ptlrpc_bulk_desc *desc, void *buf)
 {
-        struct page *page;
-        int off;
-        char *ptr;
-        __u32 crc32 = ~0;
-        int len, i;
+        struct page    *page;
+        int             off;
+        char           *ptr;
+        __u32           adler32 = 1;
+        int             len, i;
+
+        for (i = 0; i < desc->bd_iov_count; i++) {
+                page = desc->bd_iov[i].kiov_page;
+                off = desc->bd_iov[i].kiov_offset & ~CFS_PAGE_MASK;
+                ptr = cfs_kmap(page) + off;
+                len = desc->bd_iov[i].kiov_len;
+
+                adler32 = zlib_adler32(adler32, ptr, len);
+
+                cfs_kunmap(page);
+        }
+
+        adler32 = cpu_to_le32(adler32);
+        memcpy(buf, &adler32, sizeof(adler32));
+        return 0;
+}
+
+static int do_bulk_checksum_crc32(struct ptlrpc_bulk_desc *desc, void *buf)
+{
+        struct page    *page;
+        int             off;
+        char           *ptr;
+        __u32           crc32 = ~0;
+        int             len, i;
 
         for (i = 0; i < desc->bd_iov_count; i++) {
                 page = desc->bd_iov[i].kiov_page;
@@ -850,26 +897,28 @@ int do_bulk_checksum_crc32(struct ptlrpc_bulk_desc *desc, void *buf)
                 cfs_kunmap(page);
         }
 
-        *((__u32 *) buf) = crc32;
+        crc32 = cpu_to_le32(crc32);
+        memcpy(buf, &crc32, sizeof(crc32));
         return 0;
 }
 
-static
-int do_bulk_checksum(struct ptlrpc_bulk_desc *desc, __u32 alg, void *buf)
+static int do_bulk_checksum(struct ptlrpc_bulk_desc *desc, __u32 alg, void *buf)
 {
         struct crypto_tfm *tfm;
         struct scatterlist *sl;
         int i, rc = 0;
 
-        LASSERT(alg > BULK_CSUM_ALG_NULL &&
-                alg < BULK_CSUM_ALG_MAX);
+        LASSERT(alg > BULK_HASH_ALG_NULL &&
+                alg < BULK_HASH_ALG_MAX);
 
-        if (alg == BULK_CSUM_ALG_CRC32)
+        if (alg == BULK_HASH_ALG_ADLER32)
+                return do_bulk_checksum_adler32(desc, buf);
+        if (alg == BULK_HASH_ALG_CRC32)
                 return do_bulk_checksum_crc32(desc, buf);
 
-        tfm = crypto_alloc_tfm(csum_types[alg].name, 0);
+        tfm = crypto_alloc_tfm(hash_types[alg].sht_tfm_name, 0);
         if (tfm == NULL) {
-                CERROR("Unable to allocate tfm %s\n", csum_types[alg].name);
+                CERROR("Unable to allocate TFM %s\n", hash_types[alg].sht_name);
                 return -ENOMEM;
         }
 
@@ -895,31 +944,40 @@ out_tfm:
         crypto_free_tfm(tfm);
         return rc;
 }
-                         
+
 #else /* !__KERNEL__ */
-static
-int do_bulk_checksum(struct ptlrpc_bulk_desc *desc, __u32 alg, void *buf)
+
+static int do_bulk_checksum(struct ptlrpc_bulk_desc *desc, __u32 alg, void *buf)
 {
-        __u32 crc32 = ~0;
-        int i;
+        __u32   csum32 = ~0;
+        int     i;
+
+        LASSERT(alg == BULK_HASH_ALG_ADLER32 || alg == BULK_HASH_ALG_CRC32);
 
-        LASSERT(alg == BULK_CSUM_ALG_CRC32);
+        if (alg == BULK_HASH_ALG_ADLER32)
+                csum32 = 1;
+        else
+                csum32 = ~0;
 
         for (i = 0; i < desc->bd_iov_count; i++) {
                 char *ptr = desc->bd_iov[i].iov_base;
                 int len = desc->bd_iov[i].iov_len;
 
-                crc32 = crc32_le(crc32, ptr, len);
+                if (alg == BULK_HASH_ALG_ADLER32)
+                        csum32 = zlib_adler32(csum32, ptr, len);
+                else
+                        csum32 = crc32_le(csum32, ptr, len);
         }
 
-        *((__u32 *) buf) = crc32;
+        *((__u32 *) buf) = csum32;
         return 0;
 }
+
 #endif
 
 /*
  * perform algorithm @alg checksum on @desc, store result in @buf.
- * if anything goes wrong, leave 'alg' be BULK_CSUM_ALG_NULL.
+ * if anything goes wrong, leave 'alg' be BULK_HASH_ALG_NULL.
  */
 static
 int generate_bulk_csum(struct ptlrpc_bulk_desc *desc, __u32 alg,
@@ -928,18 +986,18 @@ int generate_bulk_csum(struct ptlrpc_bulk_desc *desc, __u32 alg,
         int rc;
 
         LASSERT(bsd);
-        LASSERT(alg < BULK_CSUM_ALG_MAX);
+        LASSERT(alg < BULK_HASH_ALG_MAX);
 
-        bsd->bsd_csum_alg = BULK_CSUM_ALG_NULL;
+        bsd->bsd_hash_alg = BULK_HASH_ALG_NULL;
 
-        if (alg == BULK_CSUM_ALG_NULL)
+        if (alg == BULK_HASH_ALG_NULL)
                 return 0;
 
-        LASSERT(bsdsize >= sizeof(*bsd) + csum_types[alg].size);
+        LASSERT(bsdsize >= sizeof(*bsd) + hash_types[alg].sht_size);
 
         rc = do_bulk_checksum(desc, alg, bsd->bsd_csum);
         if (rc == 0)
-                bsd->bsd_csum_alg = alg;
+                bsd->bsd_hash_alg = alg;
 
         return rc;
 }
@@ -954,16 +1012,16 @@ int verify_bulk_csum(struct ptlrpc_bulk_desc *desc, int read,
         int   csum_size, rc = 0;
 
         LASSERT(bsdv);
-        LASSERT(bsdv->bsd_csum_alg < BULK_CSUM_ALG_MAX);
+        LASSERT(bsdv->bsd_hash_alg < BULK_HASH_ALG_MAX);
 
         if (bsdr)
-                bsdr->bsd_csum_alg = BULK_CSUM_ALG_NULL;
+                bsdr->bsd_hash_alg = BULK_HASH_ALG_NULL;
 
-        if (bsdv->bsd_csum_alg == BULK_CSUM_ALG_NULL)
+        if (bsdv->bsd_hash_alg == BULK_HASH_ALG_NULL)
                 return 0;
 
         /* for all supported algorithms */
-        csum_size = csum_types[bsdv->bsd_csum_alg].size;
+        csum_size = hash_types[bsdv->bsd_hash_alg].sht_size;
 
         if (bsdvsize < sizeof(*bsdv) + csum_size) {
                 CERROR("verifier size %d too small, require %d\n",
@@ -981,21 +1039,21 @@ int verify_bulk_csum(struct ptlrpc_bulk_desc *desc, int read,
                 csum_p = buf;
         }
 
-        rc = do_bulk_checksum(desc, bsdv->bsd_csum_alg, csum_p);
+        rc = do_bulk_checksum(desc, bsdv->bsd_hash_alg, csum_p);
 
         if (memcmp(bsdv->bsd_csum, csum_p, csum_size)) {
                 CERROR("BAD %s CHECKSUM (%s), data mutated during "
                        "transfer!\n", read ? "READ" : "WRITE",
-                       csum_types[bsdv->bsd_csum_alg].name);
+                       hash_types[bsdv->bsd_hash_alg].sht_name);
                 rc = -EINVAL;
         } else {
                 CDEBUG(D_SEC, "bulk %s checksum (%s) verified\n",
                       read ? "read" : "write",
-                      csum_types[bsdv->bsd_csum_alg].name);
+                      hash_types[bsdv->bsd_hash_alg].sht_name);
         }
 
         if (bsdr) {
-                bsdr->bsd_csum_alg = bsdv->bsd_csum_alg;
+                bsdr->bsd_hash_alg = bsdv->bsd_hash_alg;
                 memcpy(bsdr->bsd_csum, csum_p, csum_size);
         } else {
                 LASSERT(buf);
@@ -1016,10 +1074,10 @@ int bulk_csum_cli_request(struct ptlrpc_bulk_desc *desc, int read,
 
         LASSERT(bsdr);
         LASSERT(rsize >= sizeof(*bsdr));
-        LASSERT(alg < BULK_CSUM_ALG_MAX);
+        LASSERT(alg < BULK_HASH_ALG_MAX);
 
         if (read) {
-                bsdr->bsd_csum_alg = alg;
+                bsdr->bsd_hash_alg = alg;
         } else {
                 rc = generate_bulk_csum(desc, alg, bsdr, rsize);
                 if (rc)
@@ -1029,7 +1087,7 @@ int bulk_csum_cli_request(struct ptlrpc_bulk_desc *desc, int read,
                 /* For sending we only compute the wrong checksum instead
                  * of corrupting the data so it is still correct on a redo */
                 if (rc == 0 && OBD_FAIL_CHECK(OBD_FAIL_OSC_CHECKSUM_SEND) &&
-                    bsdr->bsd_csum_alg != BULK_CSUM_ALG_NULL)
+                    bsdr->bsd_hash_alg != BULK_HASH_ALG_NULL)
                         bsdr->bsd_csum[0] ^= 0x1;
         }
 
@@ -1059,23 +1117,23 @@ int bulk_csum_cli_reply(struct ptlrpc_bulk_desc *desc, int read,
         LASSERT(rsize >= sizeof(*bsdr));
         LASSERT(vsize >= sizeof(*bsdv));
 
-        if (bsdr->bsd_csum_alg != bsdv->bsd_csum_alg) {
+        if (bsdr->bsd_hash_alg != bsdv->bsd_hash_alg) {
                 CERROR("bulk %s: checksum algorithm mismatch: client request "
                        "%s but server reply with %s. try to use the new one "
                        "for checksum verification\n",
                        read ? "read" : "write",
-                       csum_types[bsdr->bsd_csum_alg].name,
-                       csum_types[bsdv->bsd_csum_alg].name);
+                       hash_types[bsdr->bsd_hash_alg].sht_name,
+                       hash_types[bsdv->bsd_hash_alg].sht_name);
         }
 
         if (read)
                 return verify_bulk_csum(desc, 1, bsdv, vsize, NULL, 0);
         else {
                 char *cli, *srv, *new = NULL;
-                int csum_size = csum_types[bsdr->bsd_csum_alg].size;
+                int csum_size = hash_types[bsdr->bsd_hash_alg].sht_size;
 
-                LASSERT(bsdr->bsd_csum_alg < BULK_CSUM_ALG_MAX);
-                if (bsdr->bsd_csum_alg == BULK_CSUM_ALG_NULL)
+                LASSERT(bsdr->bsd_hash_alg < BULK_HASH_ALG_MAX);
+                if (bsdr->bsd_hash_alg == BULK_HASH_ALG_NULL)
                         return 0;
 
                 if (vsize < sizeof(*bsdv) + csum_size) {
@@ -1090,7 +1148,7 @@ int bulk_csum_cli_reply(struct ptlrpc_bulk_desc *desc, int read,
                 if (!memcmp(cli, srv, csum_size)) {
                         /* checksum confirmed */
                         CDEBUG(D_SEC, "bulk write checksum (%s) confirmed\n",
-                               csum_types[bsdr->bsd_csum_alg].name);
+                               hash_types[bsdr->bsd_hash_alg].sht_name);
                         return 0;
                 }
 
@@ -1100,22 +1158,22 @@ int bulk_csum_cli_reply(struct ptlrpc_bulk_desc *desc, int read,
                 if (new == NULL)
                         return -ENOMEM;
 
-                do_bulk_checksum(desc, bsdr->bsd_csum_alg, new);
+                do_bulk_checksum(desc, bsdr->bsd_hash_alg, new);
 
                 if (!memcmp(new, srv, csum_size)) {
                         CERROR("BAD WRITE CHECKSUM (%s): pages were mutated "
                                "on the client after we checksummed them\n",
-                               csum_types[bsdr->bsd_csum_alg].name);
+                               hash_types[bsdr->bsd_hash_alg].sht_name);
                 } else if (!memcmp(new, cli, csum_size)) {
                         CERROR("BAD WRITE CHECKSUM (%s): pages were mutated "
                                "in transit\n",
-                               csum_types[bsdr->bsd_csum_alg].name);
+                               hash_types[bsdr->bsd_hash_alg].sht_name);
                 } else {
                         CERROR("BAD WRITE CHECKSUM (%s): pages were mutated "
                                "in transit, and the current page contents "
                                "don't match the originals and what the server "
                                "received\n",
-                               csum_types[bsdr->bsd_csum_alg].name);
+                               hash_types[bsdr->bsd_hash_alg].sht_name);
                 }
                 OBD_FREE(new, csum_size);
 
@@ -1158,11 +1216,11 @@ int bulk_csum_svc(struct ptlrpc_bulk_desc *desc, int read,
         LASSERT(bsdv && bsdr);
 
         if (read) {
-                rc = generate_bulk_csum(desc, bsdv->bsd_csum_alg, bsdr, rsize);
+                rc = generate_bulk_csum(desc, bsdv->bsd_hash_alg, bsdr, rsize);
                 if (rc)
                         CERROR("bulk read: server failed to generate %s "
                                "checksum: %d\n",
-                               csum_types[bsdv->bsd_csum_alg].name, rc);
+                               hash_types[bsdv->bsd_hash_alg].sht_name, rc);
 
                 /* corrupt the data after we compute the checksum, to
                  * simulate an OST->client data error */
@@ -1181,29 +1239,63 @@ EXPORT_SYMBOL(bulk_csum_svc);
  * implement encryption funcationality  *
  ****************************************/
 
-/*
- * NOTE: These algorithms must be stream cipher!
- */
-static struct {
-        char    *name;
-        __u32    flags;
-} priv_types[] = {
-        [BULK_PRIV_ALG_NULL]   = { "null", 0   },
-        [BULK_PRIV_ALG_ARC4]   = { "arc4", 0   },
+/* FIXME */
+#ifndef __KERNEL__
+#define CRYPTO_TFM_MODE_ECB     (0)
+#define CRYPTO_TFM_MODE_CBC     (1)
+#endif
+
+static struct sptlrpc_ciph_type cipher_types[] = {
+        [BULK_CIPH_ALG_NULL]    = {
+                "null",         "null",       0,                   0,  0
+        },
+        [BULK_CIPH_ALG_ARC4]    = {
+                "arc4",         "arc4",       CRYPTO_TFM_MODE_ECB, 0,  16
+        },
+        [BULK_CIPH_ALG_AES128]  = {
+                "aes128",       "aes",        CRYPTO_TFM_MODE_CBC, 16, 16
+        },
+        [BULK_CIPH_ALG_AES192]  = {
+                "aes192",       "aes",        CRYPTO_TFM_MODE_CBC, 16, 24
+        },
+        [BULK_CIPH_ALG_AES256]  = {
+                "aes256",       "aes",        CRYPTO_TFM_MODE_CBC, 16, 32
+        },
+        [BULK_CIPH_ALG_CAST128] = {
+                "cast128",      "cast5",      CRYPTO_TFM_MODE_CBC, 8,  16
+        },
+        [BULK_CIPH_ALG_CAST256] = {
+                "cast256",      "cast6",      CRYPTO_TFM_MODE_CBC, 16, 32
+        },
+        [BULK_CIPH_ALG_TWOFISH128] = {
+                "twofish128",   "twofish",    CRYPTO_TFM_MODE_CBC, 16, 16
+        },
+        [BULK_CIPH_ALG_TWOFISH256] = {
+                "twofish256",   "twofish",    CRYPTO_TFM_MODE_CBC, 16, 32
+        },
 };
 
-const char * sptlrpc_bulk_priv_alg2name(__u8 priv_alg)
+const struct sptlrpc_ciph_type *sptlrpc_get_ciph_type(__u8 ciph_alg)
 {
-        if (priv_alg < BULK_PRIV_ALG_MAX)
-                return priv_types[priv_alg].name;
-        return "unknown";
+        struct sptlrpc_ciph_type *ct;
+
+        if (ciph_alg < BULK_CIPH_ALG_MAX) {
+                ct = &cipher_types[ciph_alg];
+                if (ct->sct_tfm_name)
+                        return ct;
+        }
+        return NULL;
 }
-EXPORT_SYMBOL(sptlrpc_bulk_priv_alg2name);
+EXPORT_SYMBOL(sptlrpc_get_ciph_type);
 
-__u32 sptlrpc_bulk_priv_alg2flags(__u8 priv_alg)
+const char *sptlrpc_get_ciph_name(__u8 ciph_alg)
 {
-        if (priv_alg < BULK_PRIV_ALG_MAX)
-                return priv_types[priv_alg].flags;
-        return 0;
+        const struct sptlrpc_ciph_type *ct;
+
+        ct = sptlrpc_get_ciph_type(ciph_alg);
+        if (ct)
+                return ct->sct_name;
+        else
+                return "unknown";
 }
-EXPORT_SYMBOL(sptlrpc_bulk_priv_alg2flags);
+EXPORT_SYMBOL(sptlrpc_get_ciph_name);
diff --git a/lustre/ptlrpc/sec_config.c b/lustre/ptlrpc/sec_config.c
index 3534ba2250cf7f0a1dadfa65a8cb2af37d28ac7e..97aaa2a566eb7858efaebf498b29e67f001c5d0e 100644
--- a/lustre/ptlrpc/sec_config.c
+++ b/lustre/ptlrpc/sec_config.c
@@ -91,8 +91,8 @@ typedef enum {
 static void get_default_flavor(struct sptlrpc_flavor *sf)
 {
         sf->sf_rpc = SPTLRPC_FLVR_NULL;
-        sf->sf_bulk_priv = BULK_PRIV_ALG_NULL;
-        sf->sf_bulk_csum = BULK_CSUM_ALG_NULL;
+        sf->sf_bulk_ciph = BULK_CIPH_ALG_NULL;
+        sf->sf_bulk_hash = BULK_HASH_ALG_NULL;
         sf->sf_flags = 0;
 }
 
@@ -104,35 +104,50 @@ static void get_flavor_by_rpc(struct sptlrpc_rule *rule, __u16 rpc_flavor)
 
         switch (rpc_flavor) {
         case SPTLRPC_FLVR_NULL:
+                break;
         case SPTLRPC_FLVR_PLAIN:
         case SPTLRPC_FLVR_KRB5N:
         case SPTLRPC_FLVR_KRB5A:
+                rule->sr_flvr.sf_bulk_hash = BULK_HASH_ALG_ADLER32;
                 break;
         case SPTLRPC_FLVR_KRB5P:
-                rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_ARC4;
+                rule->sr_flvr.sf_bulk_ciph = BULK_CIPH_ALG_AES128;
                 /* fall through */
         case SPTLRPC_FLVR_KRB5I:
-                rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_SHA1;
+                rule->sr_flvr.sf_bulk_hash = BULK_HASH_ALG_SHA1;
                 break;
         default:
                 LBUG();
         }
 }
 
-static void get_flavor_by_bulk(struct sptlrpc_rule *rule, bulk_type_t bulk_type)
+static void get_flavor_by_bulk(struct sptlrpc_rule *rule,
+                               __u16 rpc_flavor, bulk_type_t bulk_type)
 {
         switch (bulk_type) {
         case BULK_TYPE_N:
-                rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_NULL;
-                rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_NULL;
+                rule->sr_flvr.sf_bulk_hash = BULK_HASH_ALG_NULL;
+                rule->sr_flvr.sf_bulk_ciph = BULK_CIPH_ALG_NULL;
                 break;
         case BULK_TYPE_I:
-                rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_SHA1;
-                rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_NULL;
+                switch (rpc_flavor) {
+                case SPTLRPC_FLVR_PLAIN:
+                case SPTLRPC_FLVR_KRB5N:
+                case SPTLRPC_FLVR_KRB5A:
+                        rule->sr_flvr.sf_bulk_hash = BULK_HASH_ALG_ADLER32;
+                        break;
+                case SPTLRPC_FLVR_KRB5I:
+                case SPTLRPC_FLVR_KRB5P:
+                        rule->sr_flvr.sf_bulk_hash = BULK_HASH_ALG_SHA1;
+                        break;
+                default:
+                        LBUG();
+                }
+                rule->sr_flvr.sf_bulk_ciph = BULK_CIPH_ALG_NULL;
                 break;
         case BULK_TYPE_P:
-                rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_SHA1;
-                rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_ARC4;
+                rule->sr_flvr.sf_bulk_hash = BULK_HASH_ALG_SHA1;
+                rule->sr_flvr.sf_bulk_ciph = BULK_CIPH_ALG_AES128;
                 break;
         default:
                 LBUG();
@@ -207,8 +222,8 @@ static int parse_flavor(char *str, struct sptlrpc_rule *rule)
 
         /* verify bulk section */
         if (strcmp(bulk, "bulkn") == 0) {
-                rule->sr_flvr.sf_bulk_csum = BULK_CSUM_ALG_NULL;
-                rule->sr_flvr.sf_bulk_priv = BULK_PRIV_ALG_NULL;
+                rule->sr_flvr.sf_bulk_hash = BULK_HASH_ALG_NULL;
+                rule->sr_flvr.sf_bulk_ciph = BULK_CIPH_ALG_NULL;
                 bulk_type = BULK_TYPE_N;
         } else if (strcmp(bulk, "bulki") == 0)
                 bulk_type = BULK_TYPE_I;
@@ -225,7 +240,7 @@ static int parse_flavor(char *str, struct sptlrpc_rule *rule)
         if (__flavors[i] == SPTLRPC_FLVR_PLAIN && bulk_type == BULK_TYPE_P)
                 GOTO(invalid, -EINVAL);
 
-        get_flavor_by_bulk(rule, bulk_type);
+        get_flavor_by_bulk(rule, __flavors[i], bulk_type);
 
         if (alg == NULL)
                 goto out;
@@ -236,24 +251,24 @@ static int parse_flavor(char *str, struct sptlrpc_rule *rule)
                 *enc++ = '\0';
 
         /* checksum algorithm */
-        for (i = 0; i < BULK_CSUM_ALG_MAX; i++) {
-                if (strcmp(alg, sptlrpc_bulk_csum_alg2name(i)) == 0) {
-                        rule->sr_flvr.sf_bulk_csum = i;
+        for (i = 0; i < BULK_HASH_ALG_MAX; i++) {
+                if (strcmp(alg, sptlrpc_get_hash_name(i)) == 0) {
+                        rule->sr_flvr.sf_bulk_hash = i;
                         break;
                 }
         }
-        if (i >= BULK_CSUM_ALG_MAX)
+        if (i >= BULK_HASH_ALG_MAX)
                 GOTO(invalid, -EINVAL);
 
         /* privacy algorithm */
         if (enc) {
-                for (i = 0; i < BULK_PRIV_ALG_MAX; i++) {
-                        if (strcmp(enc, sptlrpc_bulk_priv_alg2name(i)) == 0) {
-                                rule->sr_flvr.sf_bulk_priv = i;
+                for (i = 0; i < BULK_CIPH_ALG_MAX; i++) {
+                        if (strcmp(enc, sptlrpc_get_ciph_name(i)) == 0) {
+                                rule->sr_flvr.sf_bulk_ciph = i;
                                 break;
                         }
                 }
-                if (i >= BULK_PRIV_ALG_MAX)
+                if (i >= BULK_CIPH_ALG_MAX)
                         GOTO(invalid, -EINVAL);
         }
 
@@ -261,17 +276,17 @@ static int parse_flavor(char *str, struct sptlrpc_rule *rule)
          * bulk combination sanity checks
          */
         if (bulk_type == BULK_TYPE_P &&
-            rule->sr_flvr.sf_bulk_priv == BULK_PRIV_ALG_NULL)
+            rule->sr_flvr.sf_bulk_ciph == BULK_CIPH_ALG_NULL)
                 GOTO(invalid, -EINVAL);
 
         if (bulk_type == BULK_TYPE_I &&
-            (rule->sr_flvr.sf_bulk_csum == BULK_CSUM_ALG_NULL ||
-             rule->sr_flvr.sf_bulk_priv != BULK_PRIV_ALG_NULL))
+            (rule->sr_flvr.sf_bulk_hash == BULK_HASH_ALG_NULL ||
+             rule->sr_flvr.sf_bulk_ciph != BULK_CIPH_ALG_NULL))
                 GOTO(invalid, -EINVAL);
 
         if (bulk_type == BULK_TYPE_N &&
-            (rule->sr_flvr.sf_bulk_csum != BULK_CSUM_ALG_NULL ||
-             rule->sr_flvr.sf_bulk_priv != BULK_PRIV_ALG_NULL))
+            (rule->sr_flvr.sf_bulk_hash != BULK_HASH_ALG_NULL ||
+             rule->sr_flvr.sf_bulk_ciph != BULK_CIPH_ALG_NULL))
                 GOTO(invalid, -EINVAL);
 
 out:
diff --git a/lustre/ptlrpc/sec_lproc.c b/lustre/ptlrpc/sec_lproc.c
index 115eae6c75f0814e0a9922f01736dd29f6c0a212..69da081b10d54834a752b13853d84f0ea8422c4b 100644
--- a/lustre/ptlrpc/sec_lproc.c
+++ b/lustre/ptlrpc/sec_lproc.c
@@ -88,8 +88,8 @@ static int sptlrpc_info_lprocfs_seq_show(struct seq_file *seq, void *v)
         seq_printf(seq, "rpc flavor:    %s\n",
                    sptlrpc_rpcflavor2name(sec->ps_flvr.sf_rpc));
         seq_printf(seq, "bulk flavor:   %s/%s\n",
-                   sptlrpc_bulk_csum_alg2name(sec->ps_flvr.sf_bulk_csum),
-                   sptlrpc_bulk_priv_alg2name(sec->ps_flvr.sf_bulk_priv));
+                   sptlrpc_get_hash_name(sec->ps_flvr.sf_bulk_hash),
+                   sptlrpc_get_ciph_name(sec->ps_flvr.sf_bulk_ciph));
         seq_printf(seq, "flags:         %s\n", flags_str);
         seq_printf(seq, "id:            %d\n", sec->ps_id);
         seq_printf(seq, "refcount:      %d\n", atomic_read(&sec->ps_refcount));
diff --git a/lustre/ptlrpc/sec_null.c b/lustre/ptlrpc/sec_null.c
index dddf7e2bd8f5fcf142d9d31d99fc8445718e42dd..1e693da3ad9f9750b5edefa0a43d4309e773c973 100644
--- a/lustre/ptlrpc/sec_null.c
+++ b/lustre/ptlrpc/sec_null.c
@@ -112,10 +112,10 @@ struct ptlrpc_sec *null_create_sec(struct obd_import *imp,
 {
         LASSERT(RPC_FLVR_POLICY(sf->sf_rpc) == SPTLRPC_POLICY_NULL);
 
-        if (sf->sf_bulk_priv != BULK_PRIV_ALG_NULL ||
-            sf->sf_bulk_csum != BULK_CSUM_ALG_NULL) {
+        if (sf->sf_bulk_ciph != BULK_CIPH_ALG_NULL ||
+            sf->sf_bulk_hash != BULK_HASH_ALG_NULL) {
                 CERROR("null sec don't support bulk algorithm: %u/%u\n",
-                       sf->sf_bulk_priv, sf->sf_bulk_csum);
+                       sf->sf_bulk_ciph, sf->sf_bulk_hash);
                 return NULL;
         }
 
@@ -374,8 +374,8 @@ void null_init_internal(void)
         null_sec.ps_id = -1;
         null_sec.ps_import = NULL;
         null_sec.ps_flvr.sf_rpc = SPTLRPC_FLVR_NULL;
-        null_sec.ps_flvr.sf_bulk_priv = BULK_PRIV_ALG_NULL;
-        null_sec.ps_flvr.sf_bulk_csum = BULK_CSUM_ALG_NULL;
+        null_sec.ps_flvr.sf_bulk_ciph = BULK_CIPH_ALG_NULL;
+        null_sec.ps_flvr.sf_bulk_hash = BULK_HASH_ALG_NULL;
         null_sec.ps_flvr.sf_flags = 0;
         null_sec.ps_part = LUSTRE_SP_ANY;
         null_sec.ps_dying = 0;
diff --git a/lustre/ptlrpc/sec_plain.c b/lustre/ptlrpc/sec_plain.c
index 5c75f66e1e6fde9f3ab40396020830544ac448e4..53e5fe7003c80ace1eae42834b57aa272cc0aa90 100644
--- a/lustre/ptlrpc/sec_plain.c
+++ b/lustre/ptlrpc/sec_plain.c
@@ -130,7 +130,6 @@ static
 int plain_ctx_verify(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req)
 {
         struct lustre_msg *msg = req->rq_repbuf;
-        __u16              wflvr;
         ENTRY;
 
         if (msg->lm_bufcount != PLAIN_PACK_SEGMENTS) {
@@ -138,16 +137,15 @@ int plain_ctx_verify(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req)
                 RETURN(-EPROTO);
         }
 
-        wflvr = WIRE_FLVR_RPC(msg->lm_secflvr);
-
         /* expect no user desc in reply */
-        if (PLAIN_WFLVR_HAS_USER(wflvr)) {
+        if (PLAIN_WFLVR_HAS_USER(msg->lm_secflvr)) {
                 CERROR("Unexpected udesc flag in reply\n");
                 RETURN(-EPROTO);
         }
 
         /* whether we sent with bulk or not, we expect the same in reply */
-        if (!equi(req->rq_pack_bulk == 1, PLAIN_WFLVR_HAS_BULK(wflvr))) {
+        if (!equi(req->rq_pack_bulk == 1,
+                  PLAIN_WFLVR_HAS_BULK(msg->lm_secflvr))) {
                 CERROR("%s bulk checksum in reply\n",
                        req->rq_pack_bulk ? "Missing" : "Unexpected");
                 RETURN(-EPROTO);
@@ -173,7 +171,7 @@ int plain_cli_wrap_bulk(struct ptlrpc_cli_ctx *ctx,
         LASSERT(req->rq_reqbuf->lm_bufcount == PLAIN_PACK_SEGMENTS);
 
         return bulk_csum_cli_request(desc, req->rq_bulk_read,
-                                     req->rq_flvr.sf_bulk_csum,
+                                     req->rq_flvr.sf_bulk_hash,
                                      req->rq_reqbuf,
                                      PLAIN_PACK_BULK_OFF);
 }
@@ -272,9 +270,9 @@ struct ptlrpc_sec *plain_create_sec(struct obd_import *imp,
 
         LASSERT(RPC_FLVR_POLICY(sf->sf_rpc) == SPTLRPC_POLICY_PLAIN);
 
-        if (sf->sf_bulk_priv != BULK_PRIV_ALG_NULL) {
-                CERROR("plain policy don't support bulk encryption: %u\n",
-                       sf->sf_bulk_priv);
+        if (sf->sf_bulk_ciph != BULK_CIPH_ALG_NULL) {
+                CERROR("plain policy don't support bulk cipher: %u\n",
+                       sf->sf_bulk_ciph);
                 RETURN(NULL);
         }
 
@@ -389,7 +387,7 @@ int plain_alloc_reqbuf(struct ptlrpc_sec *sec,
                 LASSERT(req->rq_bulk_read || req->rq_bulk_write);
 
                 buflens[PLAIN_PACK_BULK_OFF] = bulk_sec_desc_size(
-                                                req->rq_flvr.sf_bulk_csum, 1,
+                                                req->rq_flvr.sf_bulk_hash, 1,
                                                 req->rq_bulk_read);
         }
 
@@ -449,7 +447,7 @@ int plain_alloc_repbuf(struct ptlrpc_sec *sec,
                 LASSERT(req->rq_bulk_read || req->rq_bulk_write);
 
                 buflens[PLAIN_PACK_BULK_OFF] = bulk_sec_desc_size(
-                                                req->rq_flvr.sf_bulk_csum, 0,
+                                                req->rq_flvr.sf_bulk_hash, 0,
                                                 req->rq_bulk_read);
         }
 
@@ -609,7 +607,7 @@ int plain_alloc_rs(struct ptlrpc_request *req, int msgsize)
                 LASSERT(bsd);
 
                 buflens[PLAIN_PACK_BULK_OFF] = bulk_sec_desc_size(
-                                                        bsd->bsd_csum_alg, 0,
+                                                        bsd->bsd_hash_alg, 0,
                                                         req->rq_bulk_read);
         }
         rs_size += lustre_msg_size_v2(PLAIN_PACK_SEGMENTS, buflens);
diff --git a/lustre/ptlrpc/service.c b/lustre/ptlrpc/service.c
index 64bfdcfa2edc8eb0cfa4a77fe8bd39ab9db66976..5949cc40ab16d1ca4820cc381b13978f1fc49c32 100644
--- a/lustre/ptlrpc/service.c
+++ b/lustre/ptlrpc/service.c
@@ -597,7 +597,7 @@ ptlrpc_server_handle_request(struct ptlrpc_service *svc,
                 break;
         case SECSVC_COMPLETE:
                 target_send_reply(request, 0, OBD_FAIL_MDS_ALL_REPLY_NET);
-                goto put_conn;
+                goto out_stat;
         case SECSVC_DROP:
                 goto out_req;
         default:
@@ -726,7 +726,7 @@ put_conn:
 
         lu_context_exit(&request->rq_session);
         lu_context_fini(&request->rq_session);
-
+out_stat:
         reply = request->rq_reply_state && request->rq_repmsg;  /* bug 11169 */
 
         do_gettimeofday(&work_end);
diff --git a/lustre/tests/conf-sanity.sh b/lustre/tests/conf-sanity.sh
index e05409144ff236e3267deccbc6c47aa28e6e61e1..469ce7557d2af15d4eb218312de2a2d1ba59c68e 100644
--- a/lustre/tests/conf-sanity.sh
+++ b/lustre/tests/conf-sanity.sh
@@ -788,6 +788,14 @@ test_22() {
 
 	echo Client mount with a running ost
 	start_ost
+	if $GSS; then
+		# if gss enabled, wait full time to let connection from
+		# mds to ost be established, due to the mismatch between
+		# initial connect timeout and gss context negotiation timeout.
+		# This perhaps could be remove after AT landed.
+		echo "sleep $((TIMEOUT + TIMEOUT + TIMEOUT))s"
+		sleep $((TIMEOUT + TIMEOUT + TIMEOUT))
+	fi
 	mount_client $MOUNT
 	check_mount || return 41
 	pass
diff --git a/lustre/tests/sanity-gss.sh b/lustre/tests/sanity-gss.sh
index 56370ce968e0e2779ba5ee313a1b80521b337a47..f4346df5461b2b7245bada695a5d1f9e6f390fb1 100644
--- a/lustre/tests/sanity-gss.sh
+++ b/lustre/tests/sanity-gss.sh
@@ -12,8 +12,6 @@ ONLY=${ONLY:-"$*"}
 ALWAYS_EXCEPT=${ALWAYS_EXCEPT:-"$SANITY_GSS_EXCEPT"}
 # UPDATE THE COMMENT ABOVE WITH BUG NUMBERS WHEN CHANGING ALWAYS_EXCEPT!
 
-[ "$SLOW" = "no" ] && EXCEPT_SLOW="100 101"
-
 # Tests that fail on uml
 CPU=`awk '/model/ {print $4}' /proc/cpuinfo`
 [ "$CPU" = "UML" ] && EXCEPT="$EXCEPT"
@@ -36,17 +34,10 @@ LUSTRE=${LUSTRE:-`dirname $0`/..}
 init_test_env $@
 . ${CONFIG:=$LUSTRE/tests/cfg/$NAME.sh}
 
-if [ $UID -ne 0 ]; then
-    echo "Warning: running as non-root uid $UID"
-    RUNAS_ID="$UID"
-    RUNAS=""
-else
-    RUNAS_ID=${RUNAS_ID:-500}
-    RUNAS=${RUNAS:-"runas -u $RUNAS_ID"}
+[ "$SLOW" = "no" ] && EXCEPT_SLOW="100 101"
 
-    # $RUNAS_ID may get set incorrectly somewhere else
-    [ $RUNAS_ID -eq 0 ] && error "\$RUNAS_ID set to 0, but \$UID is also 0!"
-fi
+# $RUNAS_ID may get set incorrectly somewhere else
+[ $UID -eq 0 -a $RUNAS_ID -eq 0 ] && error "\$RUNAS_ID set to 0, but \$UID is also 0!"
 
 # remove $SEC, we'd like to control everything by ourselves
 unset SEC
@@ -72,24 +63,28 @@ PROC_CLI="srpc.info"
 GSS=true
 GSS_KRB5=true
 
+prepare_krb5_creds() {
+    echo prepare krb5 cred
+    rm -f $KRB5_CRED_SAVE
+    echo RUNAS=$RUNAS
+    $RUNAS krb5_login.sh || exit 1
+    [ -f $KRB5_CRED ] || exit 2
+    echo CRED=$KRB5_CRED
+    cp $KRB5_CRED $KRB5_CRED_SAVE
+}
+
+prepare_krb5_creds
+
 # we want double mount
 MOUNT_2=${MOUNT_2:-"yes"}
 cleanup_and_setup_lustre
 
-rm -rf $DIR/${TESTSUITE}/[df][0-9]*
 rm -rf $DIR/[df][0-9]*
 
 check_runas_id $RUNAS_ID $RUNAS
 
 build_test_filter
 
-prepare_krb5_creds() {
-    rm -f $KRB5_CRED_SAVE
-    $RUNAS krb5_login.sh || exit 1
-    [ -f $KRB5_CRED ] || exit 2
-    cp $KRB5_CRED $KRB5_CRED_SAVE
-}
-
 combination()
 {
     local M=$1
@@ -150,10 +145,38 @@ set_rule()
 
 count_flvr()
 {
-    output=$1
-    flavor=$2
+    local output=$1
+    local flavor=$2
+    local count=0
+
+    rpc_flvr=`echo $flavor | awk -F - '{ print $1 }'`
+    bulkspec=`echo $flavor | awk -F - '{ print $2 }'`
+
+    count=`echo "$output" | grep "rpc flavor" | grep $rpc_flvr | wc -l`
+
+    if [ "x$bulkspec" != "x" ]; then
+        algs=`echo $bulkspec | awk -F : '{ print $2 }'`
+
+        if [ "x$algs" != "x" ]; then
+            bulk_count=`echo "$output" | grep "bulk flavor" | grep $algs | wc -l`
+        else
+            bulk=`echo $bulkspec | awk -F : '{ print $1 }'`
+            if [ $bulk == "bulkn" ]; then
+                bulk_count=`echo "$output" | grep "bulk flavor" \
+                            | grep "null/null" | wc -l`
+            elif [ $bulk == "bulki" ]; then
+                bulk_count=`echo "$output" | grep "bulk flavor" \
+                            | grep "/null" | grep -v "null/" | wc -l`
+            else
+                bulk_count=`echo "$output" | grep "bulk flavor" \
+                            | grep -v "/null" | grep -v "null/" | wc -l`
+            fi
+        fi
+
+        [ $bulk_count -lt $count ] && count=$bulk_count
+    fi
 
-    echo "$output" | grep rpc | grep $flavor | wc -l
+    echo $count
 }
 
 flvr_cnt_cli2mdt()
@@ -382,7 +405,6 @@ check_multiple_gss_daemons() {
     fi
 }
 
-prepare_krb5_creds
 calc_connection_cnt
 umask 077
 
@@ -607,6 +629,98 @@ test_7() {
 }
 run_test 7 "exercise enlarge_reqbuf()"
 
+test_8() {
+    local sample=$TMP/sanity-gss-8
+    local tdir=$MOUNT/dir8
+    local iosize="256K"
+    local hash_algs="adler32 crc32 md5 sha1 sha256 sha384 sha512 wp256 wp384 wp512"
+
+    # create sample file with aligned size for direct i/o
+    dd if=/dev/zero of=$sample bs=$iosize count=1 || error
+    dd conv=notrunc if=/etc/termcap of=$sample bs=$iosize count=1 || error
+
+    rm -rf $tdir
+    mkdir $tdir || error "create dir $tdir"
+
+    restore_to_default_flavor
+
+    for alg in $hash_algs; do
+        echo "Testing $alg..."
+        flavor=krb5i-bulki:$alg/null
+        set_rule $FSNAME any cli2ost $flavor
+        wait_flavor cli2ost $flavor $cnt_cli2ost
+
+        dd if=$sample of=$tdir/$alg oflag=direct,dsync bs=$iosize || error "$alg write"
+        diff $sample $tdir/$alg || error "$alg read"
+    done
+
+    rm -rf $tdir
+    rm -f $sample
+}
+run_test 8 "verify bulk hash algorithms works"
+
+test_9() {
+    local s1=$TMP/sanity-gss-9.1
+    local s2=$TMP/sanity-gss-9.2
+    local s3=$TMP/sanity-gss-9.3
+    local s4=$TMP/sanity-gss-9.4
+    local tdir=$MOUNT/dir9
+    local s1_size=4194304   # n * pagesize (4M)
+    local s2_size=512       # n * blksize
+    local s3_size=111       # n * blksize + m
+    local s4_size=5         # m
+    local cipher_algs="arc4 aes128 aes192 aes256 cast128 cast256 twofish128 twofish256"
+
+    # create sample files for each situation
+    rm -f $s1 $s2 $s2 $s4
+    dd if=/dev/urandom of=$s1 bs=1M count=4 || error
+    dd if=/dev/urandom of=$s2 bs=$s2_size count=1 || error
+    dd if=/dev/urandom of=$s3 bs=$s3_size count=1 || error
+    dd if=/dev/urandom of=$s4 bs=$s4_size count=1 || error
+
+    rm -rf $tdir
+    mkdir $tdir || error "create dir $tdir"
+
+    restore_to_default_flavor
+
+    #
+    # different bulk data alignment will lead to different behavior of
+    # the implementation: (n > 0; 0 < m < encryption_block_size)
+    #  - full page i/o
+    #  - partial page, size = n * encryption_block_size
+    #  - partial page, size = n * encryption_block_size + m
+    #  - partial page, size = m
+    #
+    for alg in $cipher_algs; do
+        echo "Testing $alg..."
+        flavor=krb5p-bulkp:sha1/$alg
+        set_rule $FSNAME any cli2ost $flavor
+        wait_flavor cli2ost $flavor $cnt_cli2ost
+
+        # sync write
+        dd if=$s1 of=$tdir/$alg.1 oflag=dsync bs=1M || error "write $alg.1"
+        dd if=$s2 of=$tdir/$alg.2 oflag=dsync || error "write $alg.2"
+        dd if=$s3 of=$tdir/$alg.3 oflag=dsync || error "write $alg.3"
+        dd if=$s4 of=$tdir/$alg.4 oflag=dsync || error "write $alg.4"
+
+        # remount client
+        umount_client $MOUNT
+        umount_client $MOUNT2
+        mount_client $MOUNT
+        mount_client $MOUNT2
+
+        # read & compare
+        diff $tdir/$alg.1 $s1 || error "read $alg.1"
+        diff $tdir/$alg.2 $s2 || error "read $alg.2"
+        diff $tdir/$alg.3 $s3 || error "read $alg.3"
+        diff $tdir/$alg.4 $s4 || error "read $alg.4"
+    done
+
+    rm -rf $tdir
+    rm -f $sample
+}
+run_test 9 "bulk data alignment test under encryption mode"
+
 test_90() {
     if [ "$SLOW" = "no" ]; then
         total=10
@@ -614,6 +728,10 @@ test_90() {
         total=60
     fi
 
+    restore_to_default_flavor
+    set_rule $FSNAME any any krb5p
+    wait_flavor all2all krb5p $cnt_all2all
+
     start_dbench
 
     for ((n=0;n<$total;n++)); do
diff --git a/lustre/tests/sanity.sh b/lustre/tests/sanity.sh
index c15eddb8b9316912331454496bb50b79ac0a1397..84294d03c805a84a156067236948abdad3368608 100644
--- a/lustre/tests/sanity.sh
+++ b/lustre/tests/sanity.sh
@@ -75,13 +75,6 @@ init_test_env $@
 
 [ "$SLOW" = "no" ] && EXCEPT_SLOW="24o 27m 36f 36g 51b 51c 60c 63 64b 68 71 73 77f 78 101 103 115 120g 124b"
 
-if $GSS_KRB5; then
-    $RUNAS krb5_login.sh || exit 1
-    $RUNAS -u $(($RUNAS_ID + 1)) krb5_login.sh || exit 1
-fi
-
-[ "$SLOW" = "no" ] && EXCEPT_SLOW="24o 27m 36f 36g 51b 51c 60c 63 64b 68 71 73 77f 78 101 103 115 120g 124b"
-
 SANITYLOG=${TESTSUITELOG:-$TMP/$(basename $0 .sh).log}
 FAIL_ON_ERROR=false
 
@@ -134,6 +127,7 @@ rm -rf $DIR/[Rdfs][0-9]*
 [ $UID -eq 0 -a $RUNAS_ID -eq 0 ] && error "\$RUNAS_ID set to 0, but \$UID is also 0!"
 
 check_runas_id $RUNAS_ID $RUNAS
+check_runas_id $(($RUNAS_ID + 1)) "$RUNAS -u $(($RUNAS_ID + 1))"
 
 build_test_filter
 
@@ -2981,7 +2975,6 @@ run_test 68 "support swapping to Lustre ========================"
 test_69() {
 	[ $(grep -c obdfilter $LPROC/devices) -eq 0 ] && \
 		skip "skipping test for remote OST" && return
-	$GSS && skip "gss with bulk security will triger oops. re-enable this after b10091 get fixed" && return
 
 	f="$DIR/$tfile"
 	touch $f
diff --git a/lustre/tests/sanityN.sh b/lustre/tests/sanityN.sh
index 0f6b129aecb0aefdb8a02283027d5db953ecaa99..9a55449ec9b5aa578f50d83369eda21294b026cb 100644
--- a/lustre/tests/sanityN.sh
+++ b/lustre/tests/sanityN.sh
@@ -51,10 +51,6 @@ init_test_env $@
 SANITYLOG=${TESTSUITELOG:-$TMP/$(basename $0 .sh).log}
 FAIL_ON_ERROR=false
 
-if $GSS_KRB5; then
-    $RUNAS krb5_login.sh || exit 1
-fi
-
 SETUP=${SETUP:-:}
 TRACE=${TRACE:-""}
 
diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh
index 7ecd12b4cabca51828ae10b827861c75ece64a55..d059bcbd7549561e4525d9fddfd3fb0f6fe40ade 100644
--- a/lustre/tests/test-framework.sh
+++ b/lustre/tests/test-framework.sh
@@ -920,6 +920,10 @@ mount_client() {
     grep " $1 " /proc/mounts || zconf_mount $HOSTNAME $*
 }
 
+umount_client() {
+    grep " $1 " /proc/mounts && zconf_umount `hostname` $*
+}
+
 # return value:
 # 0: success, the old identity set already.
 # 1: success, the old identity does not set.
@@ -975,7 +979,17 @@ setupall() {
     if [ "$MOUNT_2" ]; then
 	mount_client $MOUNT2
     fi
-    sleep 5
+
+    # by remounting mdt before ost, initial connect from mdt to ost might
+    # timeout because ost is not ready yet. wait some time to its fully
+    # recovery. initial obd_connect timeout is 5s; in GSS case it's preceeded
+    # by a context negotiation rpc with $TIMEOUT.
+    # FIXME better by monitoring import status.
+    if $GSS; then
+        sleep $((TIMEOUT + 5))
+    else
+        sleep 5
+    fi
 }
 
 mounted_lustre_filesystems() {
@@ -1523,6 +1537,12 @@ check_runas_id() {
     local myRUNAS_ID=$1
     shift
     local myRUNAS=$@
+
+    if $GSS_KRB5; then
+        $myRUNAS krb5_login.sh || \
+            error "Failed to refresh Kerberos V5 TGT for UID $myRUNAS_ID."
+    fi
+
     mkdir $DIR/d0_runas_test
     chmod 0755 $DIR
     chown $myRUNAS_ID:$myRUNAS_ID $DIR/d0_runas_test