mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 19:21:31 -04:00
Merge branch 'for-2.6.37' of git://linux-nfs.org/~bfields/linux
* 'for-2.6.37' of git://linux-nfs.org/~bfields/linux: (99 commits) svcrpc: svc_tcp_sendto XPT_DEAD check is redundant svcrpc: no need for XPT_DEAD check in svc_xprt_enqueue svcrpc: assume svc_delete_xprt() called only once svcrpc: never clear XPT_BUSY on dead xprt nfsd4: fix connection allocation in sequence() nfsd4: only require krb5 principal for NFSv4.0 callbacks nfsd4: move minorversion to client nfsd4: delay session removal till free_client nfsd4: separate callback change and callback probe nfsd4: callback program number is per-session nfsd4: track backchannel connections nfsd4: confirm only on succesful create_session nfsd4: make backchannel sequence number per-session nfsd4: use client pointer to backchannel session nfsd4: move callback setup into session init code nfsd4: don't cache seq_misordered replies SUNRPC: Properly initialize sock_xprt.srcaddr in all cases SUNRPC: Use conventional switch statement when reclassifying sockets sunrpc/xprtrdma: clean up workqueue usage sunrpc: Turn list_for_each-s into the ..._entry-s ... Fix up trivial conflicts (two different deprecation notices added in separate branches) in Documentation/feature-removal-schedule.txt
This commit is contained in:
commit
4390110fef
61 changed files with 1535 additions and 1937 deletions
|
|
@ -36,22 +36,3 @@ config RPCSEC_GSS_KRB5
|
|||
Kerberos support should be installed.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config RPCSEC_GSS_SPKM3
|
||||
tristate "Secure RPC: SPKM3 mechanism (EXPERIMENTAL)"
|
||||
depends on SUNRPC && EXPERIMENTAL
|
||||
select SUNRPC_GSS
|
||||
select CRYPTO
|
||||
select CRYPTO_MD5
|
||||
select CRYPTO_DES
|
||||
select CRYPTO_CAST5
|
||||
select CRYPTO_CBC
|
||||
help
|
||||
Choose Y here to enable Secure RPC using the SPKM3 public key
|
||||
GSS-API mechanism (RFC 2025).
|
||||
|
||||
Secure RPC calls with SPKM3 require an auxiliary userspace
|
||||
daemon which may be found in the Linux nfs-utils package
|
||||
available from http://linux-nfs.org/.
|
||||
|
||||
If unsure, say N.
|
||||
|
|
|
|||
|
|
@ -658,7 +658,7 @@ out1:
|
|||
return err;
|
||||
}
|
||||
|
||||
void __exit rpcauth_remove_module(void)
|
||||
void rpcauth_remove_module(void)
|
||||
{
|
||||
rpc_destroy_authunix();
|
||||
rpc_destroy_generic_auth();
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ int __init rpc_init_generic_auth(void)
|
|||
return rpcauth_init_credcache(&generic_auth);
|
||||
}
|
||||
|
||||
void __exit rpc_destroy_generic_auth(void)
|
||||
void rpc_destroy_generic_auth(void)
|
||||
{
|
||||
rpcauth_destroy_credcache(&generic_auth);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,3 @@ obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
|
|||
|
||||
rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
|
||||
gss_krb5_seqnum.o gss_krb5_wrap.o gss_krb5_crypto.o gss_krb5_keys.o
|
||||
|
||||
obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o
|
||||
|
||||
rpcsec_gss_spkm3-objs := gss_spkm3_mech.o gss_spkm3_seal.o gss_spkm3_unseal.o \
|
||||
gss_spkm3_token.o
|
||||
|
|
|
|||
|
|
@ -1,247 +0,0 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_spkm3_mech.c
|
||||
*
|
||||
* Copyright (c) 2003 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Andy Adamson <andros@umich.edu>
|
||||
* J. Bruce Fields <bfields@umich.edu>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sunrpc/auth.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/sunrpc/svcauth_gss.h>
|
||||
#include <linux/sunrpc/gss_spkm3.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
static const void *
|
||||
simple_get_bytes(const void *p, const void *end, void *res, int len)
|
||||
{
|
||||
const void *q = (const void *)((const char *)p + len);
|
||||
if (unlikely(q > end || q < p))
|
||||
return ERR_PTR(-EFAULT);
|
||||
memcpy(res, p, len);
|
||||
return q;
|
||||
}
|
||||
|
||||
static const void *
|
||||
simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res)
|
||||
{
|
||||
const void *q;
|
||||
unsigned int len;
|
||||
p = simple_get_bytes(p, end, &len, sizeof(len));
|
||||
if (IS_ERR(p))
|
||||
return p;
|
||||
res->len = len;
|
||||
if (len == 0) {
|
||||
res->data = NULL;
|
||||
return p;
|
||||
}
|
||||
q = (const void *)((const char *)p + len);
|
||||
if (unlikely(q > end || q < p))
|
||||
return ERR_PTR(-EFAULT);
|
||||
res->data = kmemdup(p, len, GFP_NOFS);
|
||||
if (unlikely(res->data == NULL))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return q;
|
||||
}
|
||||
|
||||
static int
|
||||
gss_import_sec_context_spkm3(const void *p, size_t len,
|
||||
struct gss_ctx *ctx_id,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
const void *end = (const void *)((const char *)p + len);
|
||||
struct spkm3_ctx *ctx;
|
||||
int version;
|
||||
|
||||
if (!(ctx = kzalloc(sizeof(*ctx), gfp_mask)))
|
||||
goto out_err;
|
||||
|
||||
p = simple_get_bytes(p, end, &version, sizeof(version));
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_ctx;
|
||||
if (version != 1) {
|
||||
dprintk("RPC: unknown spkm3 token format: "
|
||||
"obsolete nfs-utils?\n");
|
||||
p = ERR_PTR(-EINVAL);
|
||||
goto out_err_free_ctx;
|
||||
}
|
||||
|
||||
p = simple_get_netobj(p, end, &ctx->ctx_id);
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_ctx;
|
||||
|
||||
p = simple_get_bytes(p, end, &ctx->endtime, sizeof(ctx->endtime));
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_ctx_id;
|
||||
|
||||
p = simple_get_netobj(p, end, &ctx->mech_used);
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_ctx_id;
|
||||
|
||||
p = simple_get_bytes(p, end, &ctx->ret_flags, sizeof(ctx->ret_flags));
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_mech;
|
||||
|
||||
p = simple_get_netobj(p, end, &ctx->conf_alg);
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_mech;
|
||||
|
||||
p = simple_get_netobj(p, end, &ctx->derived_conf_key);
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_conf_alg;
|
||||
|
||||
p = simple_get_netobj(p, end, &ctx->intg_alg);
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_conf_key;
|
||||
|
||||
p = simple_get_netobj(p, end, &ctx->derived_integ_key);
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_intg_alg;
|
||||
|
||||
if (p != end) {
|
||||
p = ERR_PTR(-EFAULT);
|
||||
goto out_err_free_intg_key;
|
||||
}
|
||||
|
||||
ctx_id->internal_ctx_id = ctx;
|
||||
|
||||
dprintk("RPC: Successfully imported new spkm context.\n");
|
||||
return 0;
|
||||
|
||||
out_err_free_intg_key:
|
||||
kfree(ctx->derived_integ_key.data);
|
||||
out_err_free_intg_alg:
|
||||
kfree(ctx->intg_alg.data);
|
||||
out_err_free_conf_key:
|
||||
kfree(ctx->derived_conf_key.data);
|
||||
out_err_free_conf_alg:
|
||||
kfree(ctx->conf_alg.data);
|
||||
out_err_free_mech:
|
||||
kfree(ctx->mech_used.data);
|
||||
out_err_free_ctx_id:
|
||||
kfree(ctx->ctx_id.data);
|
||||
out_err_free_ctx:
|
||||
kfree(ctx);
|
||||
out_err:
|
||||
return PTR_ERR(p);
|
||||
}
|
||||
|
||||
static void
|
||||
gss_delete_sec_context_spkm3(void *internal_ctx)
|
||||
{
|
||||
struct spkm3_ctx *sctx = internal_ctx;
|
||||
|
||||
kfree(sctx->derived_integ_key.data);
|
||||
kfree(sctx->intg_alg.data);
|
||||
kfree(sctx->derived_conf_key.data);
|
||||
kfree(sctx->conf_alg.data);
|
||||
kfree(sctx->mech_used.data);
|
||||
kfree(sctx->ctx_id.data);
|
||||
kfree(sctx);
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_verify_mic_spkm3(struct gss_ctx *ctx,
|
||||
struct xdr_buf *signbuf,
|
||||
struct xdr_netobj *checksum)
|
||||
{
|
||||
u32 maj_stat = 0;
|
||||
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
|
||||
|
||||
maj_stat = spkm3_read_token(sctx, checksum, signbuf, SPKM_MIC_TOK);
|
||||
|
||||
dprintk("RPC: gss_verify_mic_spkm3 returning %d\n", maj_stat);
|
||||
return maj_stat;
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_get_mic_spkm3(struct gss_ctx *ctx,
|
||||
struct xdr_buf *message_buffer,
|
||||
struct xdr_netobj *message_token)
|
||||
{
|
||||
u32 err = 0;
|
||||
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
|
||||
|
||||
err = spkm3_make_token(sctx, message_buffer,
|
||||
message_token, SPKM_MIC_TOK);
|
||||
dprintk("RPC: gss_get_mic_spkm3 returning %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct gss_api_ops gss_spkm3_ops = {
|
||||
.gss_import_sec_context = gss_import_sec_context_spkm3,
|
||||
.gss_get_mic = gss_get_mic_spkm3,
|
||||
.gss_verify_mic = gss_verify_mic_spkm3,
|
||||
.gss_delete_sec_context = gss_delete_sec_context_spkm3,
|
||||
};
|
||||
|
||||
static struct pf_desc gss_spkm3_pfs[] = {
|
||||
{RPC_AUTH_GSS_SPKM, RPC_GSS_SVC_NONE, "spkm3"},
|
||||
{RPC_AUTH_GSS_SPKMI, RPC_GSS_SVC_INTEGRITY, "spkm3i"},
|
||||
};
|
||||
|
||||
static struct gss_api_mech gss_spkm3_mech = {
|
||||
.gm_name = "spkm3",
|
||||
.gm_owner = THIS_MODULE,
|
||||
.gm_oid = {7, "\053\006\001\005\005\001\003"},
|
||||
.gm_ops = &gss_spkm3_ops,
|
||||
.gm_pf_num = ARRAY_SIZE(gss_spkm3_pfs),
|
||||
.gm_pfs = gss_spkm3_pfs,
|
||||
};
|
||||
|
||||
static int __init init_spkm3_module(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = gss_mech_register(&gss_spkm3_mech);
|
||||
if (status)
|
||||
printk("Failed to register spkm3 gss mechanism!\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
static void __exit cleanup_spkm3_module(void)
|
||||
{
|
||||
gss_mech_unregister(&gss_spkm3_mech);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(init_spkm3_module);
|
||||
module_exit(cleanup_spkm3_module);
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_spkm3_seal.c
|
||||
*
|
||||
* Copyright (c) 2003 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Andy Adamson <andros@umich.edu>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sunrpc/gss_spkm3.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
const struct xdr_netobj hmac_md5_oid = { 8, "\x2B\x06\x01\x05\x05\x08\x01\x01"};
|
||||
const struct xdr_netobj cast5_cbc_oid = {9, "\x2A\x86\x48\x86\xF6\x7D\x07\x42\x0A"};
|
||||
|
||||
/*
|
||||
* spkm3_make_token()
|
||||
*
|
||||
* Only SPKM_MIC_TOK with md5 intg-alg is supported
|
||||
*/
|
||||
|
||||
u32
|
||||
spkm3_make_token(struct spkm3_ctx *ctx,
|
||||
struct xdr_buf * text, struct xdr_netobj * token,
|
||||
int toktype)
|
||||
{
|
||||
s32 checksum_type;
|
||||
char tokhdrbuf[25];
|
||||
char cksumdata[16];
|
||||
struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata};
|
||||
struct xdr_netobj mic_hdr = {.len = 0, .data = tokhdrbuf};
|
||||
int tokenlen = 0;
|
||||
unsigned char *ptr;
|
||||
s32 now;
|
||||
int ctxelen = 0, ctxzbit = 0;
|
||||
int md5elen = 0, md5zbit = 0;
|
||||
|
||||
now = jiffies;
|
||||
|
||||
if (ctx->ctx_id.len != 16) {
|
||||
dprintk("RPC: spkm3_make_token BAD ctx_id.len %d\n",
|
||||
ctx->ctx_id.len);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) {
|
||||
dprintk("RPC: gss_spkm3_seal: unsupported I-ALG "
|
||||
"algorithm. only support hmac-md5 I-ALG.\n");
|
||||
goto out_err;
|
||||
} else
|
||||
checksum_type = CKSUMTYPE_HMAC_MD5;
|
||||
|
||||
if (!g_OID_equal(&ctx->conf_alg, &cast5_cbc_oid)) {
|
||||
dprintk("RPC: gss_spkm3_seal: unsupported C-ALG "
|
||||
"algorithm\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (toktype == SPKM_MIC_TOK) {
|
||||
/* Calculate checksum over the mic-header */
|
||||
asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit);
|
||||
spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data,
|
||||
ctxelen, ctxzbit);
|
||||
if (make_spkm3_checksum(checksum_type, &ctx->derived_integ_key,
|
||||
(char *)mic_hdr.data, mic_hdr.len,
|
||||
text, 0, &md5cksum))
|
||||
goto out_err;
|
||||
|
||||
asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit);
|
||||
tokenlen = 10 + ctxelen + 1 + md5elen + 1;
|
||||
|
||||
/* Create token header using generic routines */
|
||||
token->len = g_token_size(&ctx->mech_used, tokenlen + 2);
|
||||
|
||||
ptr = token->data;
|
||||
g_make_token_header(&ctx->mech_used, tokenlen + 2, &ptr);
|
||||
|
||||
spkm3_make_mic_token(&ptr, tokenlen, &mic_hdr, &md5cksum, md5elen, md5zbit);
|
||||
} else if (toktype == SPKM_WRAP_TOK) { /* Not Supported */
|
||||
dprintk("RPC: gss_spkm3_seal: SPKM_WRAP_TOK "
|
||||
"not supported\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* XXX need to implement sequence numbers, and ctx->expired */
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
out_err:
|
||||
token->data = NULL;
|
||||
token->len = 0;
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
static int
|
||||
spkm3_checksummer(struct scatterlist *sg, void *data)
|
||||
{
|
||||
struct hash_desc *desc = data;
|
||||
|
||||
return crypto_hash_update(desc, sg, sg->length);
|
||||
}
|
||||
|
||||
/* checksum the plaintext data and hdrlen bytes of the token header */
|
||||
s32
|
||||
make_spkm3_checksum(s32 cksumtype, struct xdr_netobj *key, char *header,
|
||||
unsigned int hdrlen, struct xdr_buf *body,
|
||||
unsigned int body_offset, struct xdr_netobj *cksum)
|
||||
{
|
||||
char *cksumname;
|
||||
struct hash_desc desc; /* XXX add to ctx? */
|
||||
struct scatterlist sg[1];
|
||||
int err;
|
||||
|
||||
switch (cksumtype) {
|
||||
case CKSUMTYPE_HMAC_MD5:
|
||||
cksumname = "hmac(md5)";
|
||||
break;
|
||||
default:
|
||||
dprintk("RPC: spkm3_make_checksum:"
|
||||
" unsupported checksum %d", cksumtype);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
if (key->data == NULL || key->len <= 0) return GSS_S_FAILURE;
|
||||
|
||||
desc.tfm = crypto_alloc_hash(cksumname, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(desc.tfm))
|
||||
return GSS_S_FAILURE;
|
||||
cksum->len = crypto_hash_digestsize(desc.tfm);
|
||||
desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
err = crypto_hash_setkey(desc.tfm, key->data, key->len);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = crypto_hash_init(&desc);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
sg_init_one(sg, header, hdrlen);
|
||||
crypto_hash_update(&desc, sg, sg->length);
|
||||
|
||||
xdr_process_buf(body, body_offset, body->len - body_offset,
|
||||
spkm3_checksummer, &desc);
|
||||
crypto_hash_final(&desc, cksum->data);
|
||||
|
||||
out:
|
||||
crypto_free_hash(desc.tfm);
|
||||
|
||||
return err ? GSS_S_FAILURE : 0;
|
||||
}
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_spkm3_token.c
|
||||
*
|
||||
* Copyright (c) 2003 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Andy Adamson <andros@umich.edu>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sunrpc/gss_spkm3.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
/*
|
||||
* asn1_bitstring_len()
|
||||
*
|
||||
* calculate the asn1 bitstring length of the xdr_netobject
|
||||
*/
|
||||
void
|
||||
asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits)
|
||||
{
|
||||
int i, zbit = 0,elen = in->len;
|
||||
char *ptr;
|
||||
|
||||
ptr = &in->data[in->len -1];
|
||||
|
||||
/* count trailing 0's */
|
||||
for(i = in->len; i > 0; i--) {
|
||||
if (*ptr == 0) {
|
||||
ptr--;
|
||||
elen--;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
/* count number of 0 bits in final octet */
|
||||
ptr = &in->data[elen - 1];
|
||||
for(i = 0; i < 8; i++) {
|
||||
short mask = 0x01;
|
||||
|
||||
if (!((mask << i) & *ptr))
|
||||
zbit++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
*enclen = elen;
|
||||
*zerobits = zbit;
|
||||
}
|
||||
|
||||
/*
|
||||
* decode_asn1_bitstring()
|
||||
*
|
||||
* decode a bitstring into a buffer of the expected length.
|
||||
* enclen = bit string length
|
||||
* explen = expected length (define in rfc)
|
||||
*/
|
||||
int
|
||||
decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen)
|
||||
{
|
||||
if (!(out->data = kzalloc(explen,GFP_NOFS)))
|
||||
return 0;
|
||||
out->len = explen;
|
||||
memcpy(out->data, in, enclen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* SPKMInnerContextToken choice SPKM_MIC asn1 token layout
|
||||
*
|
||||
* contextid is always 16 bytes plain data. max asn1 bitstring len = 17.
|
||||
*
|
||||
* tokenlen = pos[0] to end of token (max pos[45] with MD5 cksum)
|
||||
*
|
||||
* pos value
|
||||
* ----------
|
||||
* [0] a4 SPKM-MIC tag
|
||||
* [1] ?? innertoken length (max 44)
|
||||
*
|
||||
*
|
||||
* tok_hdr piece of checksum data starts here
|
||||
*
|
||||
* the maximum mic-header len = 9 + 17 = 26
|
||||
* mic-header
|
||||
* ----------
|
||||
* [2] 30 SEQUENCE tag
|
||||
* [3] ?? mic-header length: (max 23) = TokenID + ContextID
|
||||
*
|
||||
* TokenID - all fields constant and can be hardcoded
|
||||
* -------
|
||||
* [4] 02 Type 2
|
||||
* [5] 02 Length 2
|
||||
* [6][7] 01 01 TokenID (SPKM_MIC_TOK)
|
||||
*
|
||||
* ContextID - encoded length not constant, calculated
|
||||
* ---------
|
||||
* [8] 03 Type 3
|
||||
* [9] ?? encoded length
|
||||
* [10] ?? ctxzbit
|
||||
* [11] contextid
|
||||
*
|
||||
* mic_header piece of checksum data ends here.
|
||||
*
|
||||
* int-cksum - encoded length not constant, calculated
|
||||
* ---------
|
||||
* [??] 03 Type 3
|
||||
* [??] ?? encoded length
|
||||
* [??] ?? md5zbit
|
||||
* [??] int-cksum (NID_md5 = 16)
|
||||
*
|
||||
* maximum SPKM-MIC innercontext token length =
|
||||
* 10 + encoded contextid_size(17 max) + 2 + encoded
|
||||
* cksum_size (17 maxfor NID_md5) = 46
|
||||
*/
|
||||
|
||||
/*
|
||||
* spkm3_mic_header()
|
||||
*
|
||||
* Prepare the SPKM_MIC_TOK mic-header for check-sum calculation
|
||||
* elen: 16 byte context id asn1 bitstring encoded length
|
||||
*/
|
||||
void
|
||||
spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, unsigned char *ctxdata, int elen, int zbit)
|
||||
{
|
||||
char *hptr = *hdrbuf;
|
||||
char *top = *hdrbuf;
|
||||
|
||||
*(u8 *)hptr++ = 0x30;
|
||||
*(u8 *)hptr++ = elen + 7; /* on the wire header length */
|
||||
|
||||
/* tokenid */
|
||||
*(u8 *)hptr++ = 0x02;
|
||||
*(u8 *)hptr++ = 0x02;
|
||||
*(u8 *)hptr++ = 0x01;
|
||||
*(u8 *)hptr++ = 0x01;
|
||||
|
||||
/* coniextid */
|
||||
*(u8 *)hptr++ = 0x03;
|
||||
*(u8 *)hptr++ = elen + 1; /* add 1 to include zbit */
|
||||
*(u8 *)hptr++ = zbit;
|
||||
memcpy(hptr, ctxdata, elen);
|
||||
hptr += elen;
|
||||
*hdrlen = hptr - top;
|
||||
}
|
||||
|
||||
/*
|
||||
* spkm3_mic_innercontext_token()
|
||||
*
|
||||
* *tokp points to the beginning of the SPKM_MIC token described
|
||||
* in rfc 2025, section 3.2.1:
|
||||
*
|
||||
* toklen is the inner token length
|
||||
*/
|
||||
void
|
||||
spkm3_make_mic_token(unsigned char **tokp, int toklen, struct xdr_netobj *mic_hdr, struct xdr_netobj *md5cksum, int md5elen, int md5zbit)
|
||||
{
|
||||
unsigned char *ict = *tokp;
|
||||
|
||||
*(u8 *)ict++ = 0xa4;
|
||||
*(u8 *)ict++ = toklen;
|
||||
memcpy(ict, mic_hdr->data, mic_hdr->len);
|
||||
ict += mic_hdr->len;
|
||||
|
||||
*(u8 *)ict++ = 0x03;
|
||||
*(u8 *)ict++ = md5elen + 1; /* add 1 to include zbit */
|
||||
*(u8 *)ict++ = md5zbit;
|
||||
memcpy(ict, md5cksum->data, md5elen);
|
||||
}
|
||||
|
||||
u32
|
||||
spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen, unsigned char **cksum)
|
||||
{
|
||||
struct xdr_netobj spkm3_ctx_id = {.len =0, .data = NULL};
|
||||
unsigned char *ptr = *tokp;
|
||||
int ctxelen;
|
||||
u32 ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
/* spkm3 innercontext token preamble */
|
||||
if ((ptr[0] != 0xa4) || (ptr[2] != 0x30)) {
|
||||
dprintk("RPC: BAD SPKM ictoken preamble\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
*mic_hdrlen = ptr[3];
|
||||
|
||||
/* token type */
|
||||
if ((ptr[4] != 0x02) || (ptr[5] != 0x02)) {
|
||||
dprintk("RPC: BAD asn1 SPKM3 token type\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* only support SPKM_MIC_TOK */
|
||||
if((ptr[6] != 0x01) || (ptr[7] != 0x01)) {
|
||||
dprintk("RPC: ERROR unsupported SPKM3 token\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* contextid */
|
||||
if (ptr[8] != 0x03) {
|
||||
dprintk("RPC: BAD SPKM3 asn1 context-id type\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctxelen = ptr[9];
|
||||
if (ctxelen > 17) { /* length includes asn1 zbit octet */
|
||||
dprintk("RPC: BAD SPKM3 contextid len %d\n", ctxelen);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* ignore ptr[10] */
|
||||
|
||||
if(!decode_asn1_bitstring(&spkm3_ctx_id, &ptr[11], ctxelen - 1, 16))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* in the current implementation: the optional int-alg is not present
|
||||
* so the default int-alg (md5) is used the optional snd-seq field is
|
||||
* also not present
|
||||
*/
|
||||
|
||||
if (*mic_hdrlen != 6 + ctxelen) {
|
||||
dprintk("RPC: BAD SPKM_ MIC_TOK header len %d: we only "
|
||||
"support default int-alg (should be absent) "
|
||||
"and do not support snd-seq\n", *mic_hdrlen);
|
||||
goto out;
|
||||
}
|
||||
/* checksum */
|
||||
*cksum = (&ptr[10] + ctxelen); /* ctxelen includes ptr[10] */
|
||||
|
||||
ret = GSS_S_COMPLETE;
|
||||
out:
|
||||
kfree(spkm3_ctx_id.data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_spkm3_unseal.c
|
||||
*
|
||||
* Copyright (c) 2003 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Andy Adamson <andros@umich.edu>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sunrpc/gss_spkm3.h>
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
/*
|
||||
* spkm3_read_token()
|
||||
*
|
||||
* only SPKM_MIC_TOK with md5 intg-alg is supported
|
||||
*/
|
||||
u32
|
||||
spkm3_read_token(struct spkm3_ctx *ctx,
|
||||
struct xdr_netobj *read_token, /* checksum */
|
||||
struct xdr_buf *message_buffer, /* signbuf */
|
||||
int toktype)
|
||||
{
|
||||
s32 checksum_type;
|
||||
s32 code;
|
||||
struct xdr_netobj wire_cksum = {.len =0, .data = NULL};
|
||||
char cksumdata[16];
|
||||
struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata};
|
||||
unsigned char *ptr = (unsigned char *)read_token->data;
|
||||
unsigned char *cksum;
|
||||
int bodysize, md5elen;
|
||||
int mic_hdrlen;
|
||||
u32 ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used,
|
||||
&bodysize, &ptr, read_token->len))
|
||||
goto out;
|
||||
|
||||
/* decode the token */
|
||||
|
||||
if (toktype != SPKM_MIC_TOK) {
|
||||
dprintk("RPC: BAD SPKM3 token type: %d\n", toktype);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum)))
|
||||
goto out;
|
||||
|
||||
if (*cksum++ != 0x03) {
|
||||
dprintk("RPC: spkm3_read_token BAD checksum type\n");
|
||||
goto out;
|
||||
}
|
||||
md5elen = *cksum++;
|
||||
cksum++; /* move past the zbit */
|
||||
|
||||
if (!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16))
|
||||
goto out;
|
||||
|
||||
/* HARD CODED FOR MD5 */
|
||||
|
||||
/* compute the checksum of the message.
|
||||
* ptr + 2 = start of header piece of checksum
|
||||
* mic_hdrlen + 2 = length of header piece of checksum
|
||||
*/
|
||||
ret = GSS_S_DEFECTIVE_TOKEN;
|
||||
if (!g_OID_equal(&ctx->intg_alg, &hmac_md5_oid)) {
|
||||
dprintk("RPC: gss_spkm3_seal: unsupported I-ALG "
|
||||
"algorithm\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
checksum_type = CKSUMTYPE_HMAC_MD5;
|
||||
|
||||
code = make_spkm3_checksum(checksum_type,
|
||||
&ctx->derived_integ_key, ptr + 2, mic_hdrlen + 2,
|
||||
message_buffer, 0, &md5cksum);
|
||||
|
||||
if (code)
|
||||
goto out;
|
||||
|
||||
ret = GSS_S_BAD_SIG;
|
||||
code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len);
|
||||
if (code) {
|
||||
dprintk("RPC: bad MIC checksum\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
/* XXX: need to add expiration and sequencing */
|
||||
ret = GSS_S_COMPLETE;
|
||||
out:
|
||||
kfree(wire_cksum.data);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -964,7 +964,7 @@ svcauth_gss_set_client(struct svc_rqst *rqstp)
|
|||
if (rqstp->rq_gssclient == NULL)
|
||||
return SVC_DENIED;
|
||||
stat = svcauth_unix_set_client(rqstp);
|
||||
if (stat == SVC_DROP)
|
||||
if (stat == SVC_DROP || stat == SVC_CLOSE)
|
||||
return stat;
|
||||
return SVC_OK;
|
||||
}
|
||||
|
|
@ -1018,7 +1018,7 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
|
|||
return SVC_DENIED;
|
||||
memset(&rsikey, 0, sizeof(rsikey));
|
||||
if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx))
|
||||
return SVC_DROP;
|
||||
return SVC_CLOSE;
|
||||
*authp = rpc_autherr_badverf;
|
||||
if (svc_safe_getnetobj(argv, &tmpobj)) {
|
||||
kfree(rsikey.in_handle.data);
|
||||
|
|
@ -1026,38 +1026,35 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
|
|||
}
|
||||
if (dup_netobj(&rsikey.in_token, &tmpobj)) {
|
||||
kfree(rsikey.in_handle.data);
|
||||
return SVC_DROP;
|
||||
return SVC_CLOSE;
|
||||
}
|
||||
|
||||
/* Perform upcall, or find upcall result: */
|
||||
rsip = rsi_lookup(&rsikey);
|
||||
rsi_free(&rsikey);
|
||||
if (!rsip)
|
||||
return SVC_DROP;
|
||||
switch (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {
|
||||
case -EAGAIN:
|
||||
case -ETIMEDOUT:
|
||||
case -ENOENT:
|
||||
return SVC_CLOSE;
|
||||
if (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle) < 0)
|
||||
/* No upcall result: */
|
||||
return SVC_DROP;
|
||||
case 0:
|
||||
ret = SVC_DROP;
|
||||
/* Got an answer to the upcall; use it: */
|
||||
if (gss_write_init_verf(rqstp, rsip))
|
||||
goto out;
|
||||
if (resv->iov_len + 4 > PAGE_SIZE)
|
||||
goto out;
|
||||
svc_putnl(resv, RPC_SUCCESS);
|
||||
if (svc_safe_putnetobj(resv, &rsip->out_handle))
|
||||
goto out;
|
||||
if (resv->iov_len + 3 * 4 > PAGE_SIZE)
|
||||
goto out;
|
||||
svc_putnl(resv, rsip->major_status);
|
||||
svc_putnl(resv, rsip->minor_status);
|
||||
svc_putnl(resv, GSS_SEQ_WIN);
|
||||
if (svc_safe_putnetobj(resv, &rsip->out_token))
|
||||
goto out;
|
||||
}
|
||||
return SVC_CLOSE;
|
||||
|
||||
ret = SVC_CLOSE;
|
||||
/* Got an answer to the upcall; use it: */
|
||||
if (gss_write_init_verf(rqstp, rsip))
|
||||
goto out;
|
||||
if (resv->iov_len + 4 > PAGE_SIZE)
|
||||
goto out;
|
||||
svc_putnl(resv, RPC_SUCCESS);
|
||||
if (svc_safe_putnetobj(resv, &rsip->out_handle))
|
||||
goto out;
|
||||
if (resv->iov_len + 3 * 4 > PAGE_SIZE)
|
||||
goto out;
|
||||
svc_putnl(resv, rsip->major_status);
|
||||
svc_putnl(resv, rsip->minor_status);
|
||||
svc_putnl(resv, GSS_SEQ_WIN);
|
||||
if (svc_safe_putnetobj(resv, &rsip->out_token))
|
||||
goto out;
|
||||
|
||||
ret = SVC_COMPLETE;
|
||||
out:
|
||||
cache_put(&rsip->h, &rsi_cache);
|
||||
|
|
|
|||
|
|
@ -33,15 +33,16 @@
|
|||
#include <linux/sunrpc/cache.h>
|
||||
#include <linux/sunrpc/stats.h>
|
||||
#include <linux/sunrpc/rpc_pipe_fs.h>
|
||||
#include "netns.h"
|
||||
|
||||
#define RPCDBG_FACILITY RPCDBG_CACHE
|
||||
|
||||
static int cache_defer_req(struct cache_req *req, struct cache_head *item);
|
||||
static void cache_defer_req(struct cache_req *req, struct cache_head *item);
|
||||
static void cache_revisit_request(struct cache_head *item);
|
||||
|
||||
static void cache_init(struct cache_head *h)
|
||||
{
|
||||
time_t now = get_seconds();
|
||||
time_t now = seconds_since_boot();
|
||||
h->next = NULL;
|
||||
h->flags = 0;
|
||||
kref_init(&h->ref);
|
||||
|
|
@ -51,7 +52,7 @@ static void cache_init(struct cache_head *h)
|
|||
|
||||
static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
|
||||
{
|
||||
return (h->expiry_time < get_seconds()) ||
|
||||
return (h->expiry_time < seconds_since_boot()) ||
|
||||
(detail->flush_time > h->last_refresh);
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +127,7 @@ static void cache_dequeue(struct cache_detail *detail, struct cache_head *ch);
|
|||
static void cache_fresh_locked(struct cache_head *head, time_t expiry)
|
||||
{
|
||||
head->expiry_time = expiry;
|
||||
head->last_refresh = get_seconds();
|
||||
head->last_refresh = seconds_since_boot();
|
||||
set_bit(CACHE_VALID, &head->flags);
|
||||
}
|
||||
|
||||
|
|
@ -237,7 +238,7 @@ int cache_check(struct cache_detail *detail,
|
|||
|
||||
/* now see if we want to start an upcall */
|
||||
refresh_age = (h->expiry_time - h->last_refresh);
|
||||
age = get_seconds() - h->last_refresh;
|
||||
age = seconds_since_boot() - h->last_refresh;
|
||||
|
||||
if (rqstp == NULL) {
|
||||
if (rv == -EAGAIN)
|
||||
|
|
@ -252,7 +253,7 @@ int cache_check(struct cache_detail *detail,
|
|||
cache_revisit_request(h);
|
||||
if (rv == -EAGAIN) {
|
||||
set_bit(CACHE_NEGATIVE, &h->flags);
|
||||
cache_fresh_locked(h, get_seconds()+CACHE_NEW_EXPIRY);
|
||||
cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY);
|
||||
cache_fresh_unlocked(h, detail);
|
||||
rv = -ENOENT;
|
||||
}
|
||||
|
|
@ -267,7 +268,8 @@ int cache_check(struct cache_detail *detail,
|
|||
}
|
||||
|
||||
if (rv == -EAGAIN) {
|
||||
if (cache_defer_req(rqstp, h) < 0) {
|
||||
cache_defer_req(rqstp, h);
|
||||
if (!test_bit(CACHE_PENDING, &h->flags)) {
|
||||
/* Request is not deferred */
|
||||
rv = cache_is_valid(detail, h);
|
||||
if (rv == -EAGAIN)
|
||||
|
|
@ -387,11 +389,11 @@ static int cache_clean(void)
|
|||
return -1;
|
||||
}
|
||||
current_detail = list_entry(next, struct cache_detail, others);
|
||||
if (current_detail->nextcheck > get_seconds())
|
||||
if (current_detail->nextcheck > seconds_since_boot())
|
||||
current_index = current_detail->hash_size;
|
||||
else {
|
||||
current_index = 0;
|
||||
current_detail->nextcheck = get_seconds()+30*60;
|
||||
current_detail->nextcheck = seconds_since_boot()+30*60;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -476,7 +478,7 @@ EXPORT_SYMBOL_GPL(cache_flush);
|
|||
void cache_purge(struct cache_detail *detail)
|
||||
{
|
||||
detail->flush_time = LONG_MAX;
|
||||
detail->nextcheck = get_seconds();
|
||||
detail->nextcheck = seconds_since_boot();
|
||||
cache_flush();
|
||||
detail->flush_time = 1;
|
||||
}
|
||||
|
|
@ -505,81 +507,155 @@ EXPORT_SYMBOL_GPL(cache_purge);
|
|||
|
||||
static DEFINE_SPINLOCK(cache_defer_lock);
|
||||
static LIST_HEAD(cache_defer_list);
|
||||
static struct list_head cache_defer_hash[DFR_HASHSIZE];
|
||||
static struct hlist_head cache_defer_hash[DFR_HASHSIZE];
|
||||
static int cache_defer_cnt;
|
||||
|
||||
static int cache_defer_req(struct cache_req *req, struct cache_head *item)
|
||||
static void __unhash_deferred_req(struct cache_deferred_req *dreq)
|
||||
{
|
||||
hlist_del_init(&dreq->hash);
|
||||
if (!list_empty(&dreq->recent)) {
|
||||
list_del_init(&dreq->recent);
|
||||
cache_defer_cnt--;
|
||||
}
|
||||
}
|
||||
|
||||
static void __hash_deferred_req(struct cache_deferred_req *dreq, struct cache_head *item)
|
||||
{
|
||||
struct cache_deferred_req *dreq, *discard;
|
||||
int hash = DFR_HASH(item);
|
||||
|
||||
if (cache_defer_cnt >= DFR_MAX) {
|
||||
/* too much in the cache, randomly drop this one,
|
||||
* or continue and drop the oldest below
|
||||
*/
|
||||
if (net_random()&1)
|
||||
return -ENOMEM;
|
||||
}
|
||||
dreq = req->defer(req);
|
||||
if (dreq == NULL)
|
||||
return -ENOMEM;
|
||||
INIT_LIST_HEAD(&dreq->recent);
|
||||
hlist_add_head(&dreq->hash, &cache_defer_hash[hash]);
|
||||
}
|
||||
|
||||
static void setup_deferral(struct cache_deferred_req *dreq,
|
||||
struct cache_head *item,
|
||||
int count_me)
|
||||
{
|
||||
|
||||
dreq->item = item;
|
||||
|
||||
spin_lock(&cache_defer_lock);
|
||||
|
||||
list_add(&dreq->recent, &cache_defer_list);
|
||||
__hash_deferred_req(dreq, item);
|
||||
|
||||
if (cache_defer_hash[hash].next == NULL)
|
||||
INIT_LIST_HEAD(&cache_defer_hash[hash]);
|
||||
list_add(&dreq->hash, &cache_defer_hash[hash]);
|
||||
|
||||
/* it is in, now maybe clean up */
|
||||
discard = NULL;
|
||||
if (++cache_defer_cnt > DFR_MAX) {
|
||||
discard = list_entry(cache_defer_list.prev,
|
||||
struct cache_deferred_req, recent);
|
||||
list_del_init(&discard->recent);
|
||||
list_del_init(&discard->hash);
|
||||
cache_defer_cnt--;
|
||||
if (count_me) {
|
||||
cache_defer_cnt++;
|
||||
list_add(&dreq->recent, &cache_defer_list);
|
||||
}
|
||||
|
||||
spin_unlock(&cache_defer_lock);
|
||||
|
||||
if (discard)
|
||||
/* there was one too many */
|
||||
discard->revisit(discard, 1);
|
||||
}
|
||||
|
||||
if (!test_bit(CACHE_PENDING, &item->flags)) {
|
||||
/* must have just been validated... */
|
||||
cache_revisit_request(item);
|
||||
return -EAGAIN;
|
||||
struct thread_deferred_req {
|
||||
struct cache_deferred_req handle;
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
static void cache_restart_thread(struct cache_deferred_req *dreq, int too_many)
|
||||
{
|
||||
struct thread_deferred_req *dr =
|
||||
container_of(dreq, struct thread_deferred_req, handle);
|
||||
complete(&dr->completion);
|
||||
}
|
||||
|
||||
static void cache_wait_req(struct cache_req *req, struct cache_head *item)
|
||||
{
|
||||
struct thread_deferred_req sleeper;
|
||||
struct cache_deferred_req *dreq = &sleeper.handle;
|
||||
|
||||
sleeper.completion = COMPLETION_INITIALIZER_ONSTACK(sleeper.completion);
|
||||
dreq->revisit = cache_restart_thread;
|
||||
|
||||
setup_deferral(dreq, item, 0);
|
||||
|
||||
if (!test_bit(CACHE_PENDING, &item->flags) ||
|
||||
wait_for_completion_interruptible_timeout(
|
||||
&sleeper.completion, req->thread_wait) <= 0) {
|
||||
/* The completion wasn't completed, so we need
|
||||
* to clean up
|
||||
*/
|
||||
spin_lock(&cache_defer_lock);
|
||||
if (!hlist_unhashed(&sleeper.handle.hash)) {
|
||||
__unhash_deferred_req(&sleeper.handle);
|
||||
spin_unlock(&cache_defer_lock);
|
||||
} else {
|
||||
/* cache_revisit_request already removed
|
||||
* this from the hash table, but hasn't
|
||||
* called ->revisit yet. It will very soon
|
||||
* and we need to wait for it.
|
||||
*/
|
||||
spin_unlock(&cache_defer_lock);
|
||||
wait_for_completion(&sleeper.completion);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cache_limit_defers(void)
|
||||
{
|
||||
/* Make sure we haven't exceed the limit of allowed deferred
|
||||
* requests.
|
||||
*/
|
||||
struct cache_deferred_req *discard = NULL;
|
||||
|
||||
if (cache_defer_cnt <= DFR_MAX)
|
||||
return;
|
||||
|
||||
spin_lock(&cache_defer_lock);
|
||||
|
||||
/* Consider removing either the first or the last */
|
||||
if (cache_defer_cnt > DFR_MAX) {
|
||||
if (net_random() & 1)
|
||||
discard = list_entry(cache_defer_list.next,
|
||||
struct cache_deferred_req, recent);
|
||||
else
|
||||
discard = list_entry(cache_defer_list.prev,
|
||||
struct cache_deferred_req, recent);
|
||||
__unhash_deferred_req(discard);
|
||||
}
|
||||
spin_unlock(&cache_defer_lock);
|
||||
if (discard)
|
||||
discard->revisit(discard, 1);
|
||||
}
|
||||
|
||||
static void cache_defer_req(struct cache_req *req, struct cache_head *item)
|
||||
{
|
||||
struct cache_deferred_req *dreq;
|
||||
|
||||
if (req->thread_wait) {
|
||||
cache_wait_req(req, item);
|
||||
if (!test_bit(CACHE_PENDING, &item->flags))
|
||||
return;
|
||||
}
|
||||
dreq = req->defer(req);
|
||||
if (dreq == NULL)
|
||||
return;
|
||||
setup_deferral(dreq, item, 1);
|
||||
if (!test_bit(CACHE_PENDING, &item->flags))
|
||||
/* Bit could have been cleared before we managed to
|
||||
* set up the deferral, so need to revisit just in case
|
||||
*/
|
||||
cache_revisit_request(item);
|
||||
|
||||
cache_limit_defers();
|
||||
}
|
||||
|
||||
static void cache_revisit_request(struct cache_head *item)
|
||||
{
|
||||
struct cache_deferred_req *dreq;
|
||||
struct list_head pending;
|
||||
|
||||
struct list_head *lp;
|
||||
struct hlist_node *lp, *tmp;
|
||||
int hash = DFR_HASH(item);
|
||||
|
||||
INIT_LIST_HEAD(&pending);
|
||||
spin_lock(&cache_defer_lock);
|
||||
|
||||
lp = cache_defer_hash[hash].next;
|
||||
if (lp) {
|
||||
while (lp != &cache_defer_hash[hash]) {
|
||||
dreq = list_entry(lp, struct cache_deferred_req, hash);
|
||||
lp = lp->next;
|
||||
if (dreq->item == item) {
|
||||
list_del_init(&dreq->hash);
|
||||
list_move(&dreq->recent, &pending);
|
||||
cache_defer_cnt--;
|
||||
}
|
||||
hlist_for_each_entry_safe(dreq, lp, tmp, &cache_defer_hash[hash], hash)
|
||||
if (dreq->item == item) {
|
||||
__unhash_deferred_req(dreq);
|
||||
list_add(&dreq->recent, &pending);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&cache_defer_lock);
|
||||
|
||||
while (!list_empty(&pending)) {
|
||||
|
|
@ -600,9 +676,8 @@ void cache_clean_deferred(void *owner)
|
|||
|
||||
list_for_each_entry_safe(dreq, tmp, &cache_defer_list, recent) {
|
||||
if (dreq->owner == owner) {
|
||||
list_del_init(&dreq->hash);
|
||||
list_move(&dreq->recent, &pending);
|
||||
cache_defer_cnt--;
|
||||
__unhash_deferred_req(dreq);
|
||||
list_add(&dreq->recent, &pending);
|
||||
}
|
||||
}
|
||||
spin_unlock(&cache_defer_lock);
|
||||
|
|
@ -901,7 +976,7 @@ static int cache_release(struct inode *inode, struct file *filp,
|
|||
filp->private_data = NULL;
|
||||
kfree(rp);
|
||||
|
||||
cd->last_close = get_seconds();
|
||||
cd->last_close = seconds_since_boot();
|
||||
atomic_dec(&cd->readers);
|
||||
}
|
||||
module_put(cd->owner);
|
||||
|
|
@ -1014,6 +1089,23 @@ static void warn_no_listener(struct cache_detail *detail)
|
|||
}
|
||||
}
|
||||
|
||||
static bool cache_listeners_exist(struct cache_detail *detail)
|
||||
{
|
||||
if (atomic_read(&detail->readers))
|
||||
return true;
|
||||
if (detail->last_close == 0)
|
||||
/* This cache was never opened */
|
||||
return false;
|
||||
if (detail->last_close < seconds_since_boot() - 30)
|
||||
/*
|
||||
* We allow for the possibility that someone might
|
||||
* restart a userspace daemon without restarting the
|
||||
* server; but after 30 seconds, we give up.
|
||||
*/
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* register an upcall request to user-space and queue it up for read() by the
|
||||
* upcall daemon.
|
||||
|
|
@ -1032,10 +1124,9 @@ int sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h,
|
|||
char *bp;
|
||||
int len;
|
||||
|
||||
if (atomic_read(&detail->readers) == 0 &&
|
||||
detail->last_close < get_seconds() - 30) {
|
||||
warn_no_listener(detail);
|
||||
return -EINVAL;
|
||||
if (!cache_listeners_exist(detail)) {
|
||||
warn_no_listener(detail);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
||||
|
|
@ -1094,13 +1185,19 @@ int qword_get(char **bpp, char *dest, int bufsize)
|
|||
if (bp[0] == '\\' && bp[1] == 'x') {
|
||||
/* HEX STRING */
|
||||
bp += 2;
|
||||
while (isxdigit(bp[0]) && isxdigit(bp[1]) && len < bufsize) {
|
||||
int byte = isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
|
||||
bp++;
|
||||
byte <<= 4;
|
||||
byte |= isdigit(*bp) ? *bp-'0' : toupper(*bp)-'A'+10;
|
||||
*dest++ = byte;
|
||||
bp++;
|
||||
while (len < bufsize) {
|
||||
int h, l;
|
||||
|
||||
h = hex_to_bin(bp[0]);
|
||||
if (h < 0)
|
||||
break;
|
||||
|
||||
l = hex_to_bin(bp[1]);
|
||||
if (l < 0)
|
||||
break;
|
||||
|
||||
*dest++ = (h << 4) | l;
|
||||
bp += 2;
|
||||
len++;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -1218,7 +1315,8 @@ static int c_show(struct seq_file *m, void *p)
|
|||
|
||||
ifdebug(CACHE)
|
||||
seq_printf(m, "# expiry=%ld refcnt=%d flags=%lx\n",
|
||||
cp->expiry_time, atomic_read(&cp->ref.refcount), cp->flags);
|
||||
convert_to_wallclock(cp->expiry_time),
|
||||
atomic_read(&cp->ref.refcount), cp->flags);
|
||||
cache_get(cp);
|
||||
if (cache_check(cd, cp, NULL))
|
||||
/* cache_check does a cache_put on failure */
|
||||
|
|
@ -1284,7 +1382,7 @@ static ssize_t read_flush(struct file *file, char __user *buf,
|
|||
unsigned long p = *ppos;
|
||||
size_t len;
|
||||
|
||||
sprintf(tbuf, "%lu\n", cd->flush_time);
|
||||
sprintf(tbuf, "%lu\n", convert_to_wallclock(cd->flush_time));
|
||||
len = strlen(tbuf);
|
||||
if (p >= len)
|
||||
return 0;
|
||||
|
|
@ -1302,19 +1400,20 @@ static ssize_t write_flush(struct file *file, const char __user *buf,
|
|||
struct cache_detail *cd)
|
||||
{
|
||||
char tbuf[20];
|
||||
char *ep;
|
||||
long flushtime;
|
||||
char *bp, *ep;
|
||||
|
||||
if (*ppos || count > sizeof(tbuf)-1)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(tbuf, buf, count))
|
||||
return -EFAULT;
|
||||
tbuf[count] = 0;
|
||||
flushtime = simple_strtoul(tbuf, &ep, 0);
|
||||
simple_strtoul(tbuf, &ep, 0);
|
||||
if (*ep && *ep != '\n')
|
||||
return -EINVAL;
|
||||
|
||||
cd->flush_time = flushtime;
|
||||
cd->nextcheck = get_seconds();
|
||||
bp = tbuf;
|
||||
cd->flush_time = get_expiry(&bp);
|
||||
cd->nextcheck = seconds_since_boot();
|
||||
cache_flush();
|
||||
|
||||
*ppos += count;
|
||||
|
|
@ -1438,8 +1537,10 @@ static const struct file_operations cache_flush_operations_procfs = {
|
|||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static void remove_cache_proc_entries(struct cache_detail *cd)
|
||||
static void remove_cache_proc_entries(struct cache_detail *cd, struct net *net)
|
||||
{
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
if (cd->u.procfs.proc_ent == NULL)
|
||||
return;
|
||||
if (cd->u.procfs.flush_ent)
|
||||
|
|
@ -1449,15 +1550,18 @@ static void remove_cache_proc_entries(struct cache_detail *cd)
|
|||
if (cd->u.procfs.content_ent)
|
||||
remove_proc_entry("content", cd->u.procfs.proc_ent);
|
||||
cd->u.procfs.proc_ent = NULL;
|
||||
remove_proc_entry(cd->name, proc_net_rpc);
|
||||
sn = net_generic(net, sunrpc_net_id);
|
||||
remove_proc_entry(cd->name, sn->proc_net_rpc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static int create_cache_proc_entries(struct cache_detail *cd)
|
||||
static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
cd->u.procfs.proc_ent = proc_mkdir(cd->name, proc_net_rpc);
|
||||
sn = net_generic(net, sunrpc_net_id);
|
||||
cd->u.procfs.proc_ent = proc_mkdir(cd->name, sn->proc_net_rpc);
|
||||
if (cd->u.procfs.proc_ent == NULL)
|
||||
goto out_nomem;
|
||||
cd->u.procfs.channel_ent = NULL;
|
||||
|
|
@ -1488,11 +1592,11 @@ static int create_cache_proc_entries(struct cache_detail *cd)
|
|||
}
|
||||
return 0;
|
||||
out_nomem:
|
||||
remove_cache_proc_entries(cd);
|
||||
remove_cache_proc_entries(cd, net);
|
||||
return -ENOMEM;
|
||||
}
|
||||
#else /* CONFIG_PROC_FS */
|
||||
static int create_cache_proc_entries(struct cache_detail *cd)
|
||||
static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1503,22 +1607,32 @@ void __init cache_initialize(void)
|
|||
INIT_DELAYED_WORK_DEFERRABLE(&cache_cleaner, do_cache_clean);
|
||||
}
|
||||
|
||||
int cache_register(struct cache_detail *cd)
|
||||
int cache_register_net(struct cache_detail *cd, struct net *net)
|
||||
{
|
||||
int ret;
|
||||
|
||||
sunrpc_init_cache_detail(cd);
|
||||
ret = create_cache_proc_entries(cd);
|
||||
ret = create_cache_proc_entries(cd, net);
|
||||
if (ret)
|
||||
sunrpc_destroy_cache_detail(cd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cache_register(struct cache_detail *cd)
|
||||
{
|
||||
return cache_register_net(cd, &init_net);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cache_register);
|
||||
|
||||
void cache_unregister_net(struct cache_detail *cd, struct net *net)
|
||||
{
|
||||
remove_cache_proc_entries(cd, net);
|
||||
sunrpc_destroy_cache_detail(cd);
|
||||
}
|
||||
|
||||
void cache_unregister(struct cache_detail *cd)
|
||||
{
|
||||
remove_cache_proc_entries(cd);
|
||||
sunrpc_destroy_cache_detail(cd);
|
||||
cache_unregister_net(cd, &init_net);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cache_unregister);
|
||||
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
|
|||
struct rpc_xprt *xprt;
|
||||
struct rpc_clnt *clnt;
|
||||
struct xprt_create xprtargs = {
|
||||
.net = args->net,
|
||||
.ident = args->protocol,
|
||||
.srcaddr = args->saddress,
|
||||
.dstaddr = args->address,
|
||||
|
|
|
|||
19
net/sunrpc/netns.h
Normal file
19
net/sunrpc/netns.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef __SUNRPC_NETNS_H__
|
||||
#define __SUNRPC_NETNS_H__
|
||||
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/netns/generic.h>
|
||||
|
||||
struct cache_detail;
|
||||
|
||||
struct sunrpc_net {
|
||||
struct proc_dir_entry *proc_net_rpc;
|
||||
struct cache_detail *ip_map_cache;
|
||||
};
|
||||
|
||||
extern int sunrpc_net_id;
|
||||
|
||||
int ip_map_cache_create(struct net *);
|
||||
void ip_map_cache_destroy(struct net *);
|
||||
|
||||
#endif
|
||||
|
|
@ -177,6 +177,7 @@ static DEFINE_MUTEX(rpcb_create_local_mutex);
|
|||
static int rpcb_create_local(void)
|
||||
{
|
||||
struct rpc_create_args args = {
|
||||
.net = &init_net,
|
||||
.protocol = XPRT_TRANSPORT_TCP,
|
||||
.address = (struct sockaddr *)&rpcb_inaddr_loopback,
|
||||
.addrsize = sizeof(rpcb_inaddr_loopback),
|
||||
|
|
@ -229,6 +230,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
|
|||
size_t salen, int proto, u32 version)
|
||||
{
|
||||
struct rpc_create_args args = {
|
||||
.net = &init_net,
|
||||
.protocol = proto,
|
||||
.address = srvaddr,
|
||||
.addrsize = salen,
|
||||
|
|
@ -248,7 +250,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
|
|||
((struct sockaddr_in6 *)srvaddr)->sin6_port = htons(RPCBIND_PORT);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
return ERR_PTR(-EAFNOSUPPORT);
|
||||
}
|
||||
|
||||
return rpc_create(&args);
|
||||
|
|
|
|||
|
|
@ -22,12 +22,11 @@
|
|||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/svcsock.h>
|
||||
#include <linux/sunrpc/metrics.h>
|
||||
#include <net/net_namespace.h>
|
||||
|
||||
#include "netns.h"
|
||||
|
||||
#define RPCDBG_FACILITY RPCDBG_MISC
|
||||
|
||||
struct proc_dir_entry *proc_net_rpc = NULL;
|
||||
|
||||
/*
|
||||
* Get RPC client stats
|
||||
*/
|
||||
|
|
@ -218,10 +217,11 @@ EXPORT_SYMBOL_GPL(rpc_print_iostats);
|
|||
static inline struct proc_dir_entry *
|
||||
do_register(const char *name, void *data, const struct file_operations *fops)
|
||||
{
|
||||
rpc_proc_init();
|
||||
dprintk("RPC: registering /proc/net/rpc/%s\n", name);
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
return proc_create_data(name, 0, proc_net_rpc, fops, data);
|
||||
dprintk("RPC: registering /proc/net/rpc/%s\n", name);
|
||||
sn = net_generic(&init_net, sunrpc_net_id);
|
||||
return proc_create_data(name, 0, sn->proc_net_rpc, fops, data);
|
||||
}
|
||||
|
||||
struct proc_dir_entry *
|
||||
|
|
@ -234,7 +234,10 @@ EXPORT_SYMBOL_GPL(rpc_proc_register);
|
|||
void
|
||||
rpc_proc_unregister(const char *name)
|
||||
{
|
||||
remove_proc_entry(name, proc_net_rpc);
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
sn = net_generic(&init_net, sunrpc_net_id);
|
||||
remove_proc_entry(name, sn->proc_net_rpc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_proc_unregister);
|
||||
|
||||
|
|
@ -248,25 +251,29 @@ EXPORT_SYMBOL_GPL(svc_proc_register);
|
|||
void
|
||||
svc_proc_unregister(const char *name)
|
||||
{
|
||||
remove_proc_entry(name, proc_net_rpc);
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
sn = net_generic(&init_net, sunrpc_net_id);
|
||||
remove_proc_entry(name, sn->proc_net_rpc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_proc_unregister);
|
||||
|
||||
void
|
||||
rpc_proc_init(void)
|
||||
int rpc_proc_init(struct net *net)
|
||||
{
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
dprintk("RPC: registering /proc/net/rpc\n");
|
||||
if (!proc_net_rpc)
|
||||
proc_net_rpc = proc_mkdir("rpc", init_net.proc_net);
|
||||
sn = net_generic(net, sunrpc_net_id);
|
||||
sn->proc_net_rpc = proc_mkdir("rpc", net->proc_net);
|
||||
if (sn->proc_net_rpc == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
rpc_proc_exit(void)
|
||||
void rpc_proc_exit(struct net *net)
|
||||
{
|
||||
dprintk("RPC: unregistering /proc/net/rpc\n");
|
||||
if (proc_net_rpc) {
|
||||
proc_net_rpc = NULL;
|
||||
remove_proc_entry("rpc", init_net.proc_net);
|
||||
}
|
||||
remove_proc_entry("rpc", net->proc_net);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,44 @@
|
|||
#include <linux/sunrpc/rpc_pipe_fs.h>
|
||||
#include <linux/sunrpc/xprtsock.h>
|
||||
|
||||
extern struct cache_detail ip_map_cache, unix_gid_cache;
|
||||
#include "netns.h"
|
||||
|
||||
int sunrpc_net_id;
|
||||
|
||||
static __net_init int sunrpc_init_net(struct net *net)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rpc_proc_init(net);
|
||||
if (err)
|
||||
goto err_proc;
|
||||
|
||||
err = ip_map_cache_create(net);
|
||||
if (err)
|
||||
goto err_ipmap;
|
||||
|
||||
return 0;
|
||||
|
||||
err_ipmap:
|
||||
rpc_proc_exit(net);
|
||||
err_proc:
|
||||
return err;
|
||||
}
|
||||
|
||||
static __net_exit void sunrpc_exit_net(struct net *net)
|
||||
{
|
||||
ip_map_cache_destroy(net);
|
||||
rpc_proc_exit(net);
|
||||
}
|
||||
|
||||
static struct pernet_operations sunrpc_net_ops = {
|
||||
.init = sunrpc_init_net,
|
||||
.exit = sunrpc_exit_net,
|
||||
.id = &sunrpc_net_id,
|
||||
.size = sizeof(struct sunrpc_net),
|
||||
};
|
||||
|
||||
extern struct cache_detail unix_gid_cache;
|
||||
|
||||
extern void cleanup_rpcb_clnt(void);
|
||||
|
||||
|
|
@ -38,18 +75,22 @@ init_sunrpc(void)
|
|||
err = rpcauth_init_module();
|
||||
if (err)
|
||||
goto out3;
|
||||
|
||||
cache_initialize();
|
||||
|
||||
err = register_pernet_subsys(&sunrpc_net_ops);
|
||||
if (err)
|
||||
goto out4;
|
||||
#ifdef RPC_DEBUG
|
||||
rpc_register_sysctl();
|
||||
#endif
|
||||
#ifdef CONFIG_PROC_FS
|
||||
rpc_proc_init();
|
||||
#endif
|
||||
cache_initialize();
|
||||
cache_register(&ip_map_cache);
|
||||
cache_register(&unix_gid_cache);
|
||||
svc_init_xprt_sock(); /* svc sock transport */
|
||||
init_socket_xprt(); /* clnt sock transport */
|
||||
return 0;
|
||||
|
||||
out4:
|
||||
rpcauth_remove_module();
|
||||
out3:
|
||||
rpc_destroy_mempool();
|
||||
out2:
|
||||
|
|
@ -67,13 +108,10 @@ cleanup_sunrpc(void)
|
|||
svc_cleanup_xprt_sock();
|
||||
unregister_rpc_pipefs();
|
||||
rpc_destroy_mempool();
|
||||
cache_unregister(&ip_map_cache);
|
||||
cache_unregister(&unix_gid_cache);
|
||||
unregister_pernet_subsys(&sunrpc_net_ops);
|
||||
#ifdef RPC_DEBUG
|
||||
rpc_unregister_sysctl();
|
||||
#endif
|
||||
#ifdef CONFIG_PROC_FS
|
||||
rpc_proc_exit();
|
||||
#endif
|
||||
rcu_barrier(); /* Wait for completion of call_rcu()'s */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1055,6 +1055,9 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)
|
|||
goto err_bad;
|
||||
case SVC_DENIED:
|
||||
goto err_bad_auth;
|
||||
case SVC_CLOSE:
|
||||
if (test_bit(XPT_TEMP, &rqstp->rq_xprt->xpt_flags))
|
||||
svc_close_xprt(rqstp->rq_xprt);
|
||||
case SVC_DROP:
|
||||
goto dropit;
|
||||
case SVC_COMPLETE:
|
||||
|
|
|
|||
|
|
@ -100,16 +100,14 @@ EXPORT_SYMBOL_GPL(svc_unreg_xprt_class);
|
|||
*/
|
||||
int svc_print_xprts(char *buf, int maxlen)
|
||||
{
|
||||
struct list_head *le;
|
||||
struct svc_xprt_class *xcl;
|
||||
char tmpstr[80];
|
||||
int len = 0;
|
||||
buf[0] = '\0';
|
||||
|
||||
spin_lock(&svc_xprt_class_lock);
|
||||
list_for_each(le, &svc_xprt_class_list) {
|
||||
list_for_each_entry(xcl, &svc_xprt_class_list, xcl_list) {
|
||||
int slen;
|
||||
struct svc_xprt_class *xcl =
|
||||
list_entry(le, struct svc_xprt_class, xcl_list);
|
||||
|
||||
sprintf(tmpstr, "%s %d\n", xcl->xcl_name, xcl->xcl_max_payload);
|
||||
slen = strlen(tmpstr);
|
||||
|
|
@ -128,9 +126,9 @@ static void svc_xprt_free(struct kref *kref)
|
|||
struct svc_xprt *xprt =
|
||||
container_of(kref, struct svc_xprt, xpt_ref);
|
||||
struct module *owner = xprt->xpt_class->xcl_owner;
|
||||
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags) &&
|
||||
xprt->xpt_auth_cache != NULL)
|
||||
svcauth_unix_info_release(xprt->xpt_auth_cache);
|
||||
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags))
|
||||
svcauth_unix_info_release(xprt);
|
||||
put_net(xprt->xpt_net);
|
||||
xprt->xpt_ops->xpo_free(xprt);
|
||||
module_put(owner);
|
||||
}
|
||||
|
|
@ -156,15 +154,18 @@ void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt,
|
|||
INIT_LIST_HEAD(&xprt->xpt_list);
|
||||
INIT_LIST_HEAD(&xprt->xpt_ready);
|
||||
INIT_LIST_HEAD(&xprt->xpt_deferred);
|
||||
INIT_LIST_HEAD(&xprt->xpt_users);
|
||||
mutex_init(&xprt->xpt_mutex);
|
||||
spin_lock_init(&xprt->xpt_lock);
|
||||
set_bit(XPT_BUSY, &xprt->xpt_flags);
|
||||
rpc_init_wait_queue(&xprt->xpt_bc_pending, "xpt_bc_pending");
|
||||
xprt->xpt_net = get_net(&init_net);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_xprt_init);
|
||||
|
||||
static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
|
||||
struct svc_serv *serv,
|
||||
struct net *net,
|
||||
const int family,
|
||||
const unsigned short port,
|
||||
int flags)
|
||||
|
|
@ -199,12 +200,12 @@ static struct svc_xprt *__svc_xpo_create(struct svc_xprt_class *xcl,
|
|||
return ERR_PTR(-EAFNOSUPPORT);
|
||||
}
|
||||
|
||||
return xcl->xcl_ops->xpo_create(serv, sap, len, flags);
|
||||
return xcl->xcl_ops->xpo_create(serv, net, sap, len, flags);
|
||||
}
|
||||
|
||||
int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
|
||||
const int family, const unsigned short port,
|
||||
int flags)
|
||||
struct net *net, const int family,
|
||||
const unsigned short port, int flags)
|
||||
{
|
||||
struct svc_xprt_class *xcl;
|
||||
|
||||
|
|
@ -220,7 +221,7 @@ int svc_create_xprt(struct svc_serv *serv, const char *xprt_name,
|
|||
goto err;
|
||||
|
||||
spin_unlock(&svc_xprt_class_lock);
|
||||
newxprt = __svc_xpo_create(xcl, serv, family, port, flags);
|
||||
newxprt = __svc_xpo_create(xcl, serv, net, family, port, flags);
|
||||
if (IS_ERR(newxprt)) {
|
||||
module_put(xcl->xcl_owner);
|
||||
return PTR_ERR(newxprt);
|
||||
|
|
@ -329,12 +330,6 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)
|
|||
"svc_xprt_enqueue: "
|
||||
"threads and transports both waiting??\n");
|
||||
|
||||
if (test_bit(XPT_DEAD, &xprt->xpt_flags)) {
|
||||
/* Don't enqueue dead transports */
|
||||
dprintk("svc: transport %p is dead, not enqueued\n", xprt);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
pool->sp_stats.packets++;
|
||||
|
||||
/* Mark transport as busy. It will remain in this state until
|
||||
|
|
@ -651,6 +646,11 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
|
|||
if (signalled() || kthread_should_stop())
|
||||
return -EINTR;
|
||||
|
||||
/* Normally we will wait up to 5 seconds for any required
|
||||
* cache information to be provided.
|
||||
*/
|
||||
rqstp->rq_chandle.thread_wait = 5*HZ;
|
||||
|
||||
spin_lock_bh(&pool->sp_lock);
|
||||
xprt = svc_xprt_dequeue(pool);
|
||||
if (xprt) {
|
||||
|
|
@ -658,6 +658,12 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
|
|||
svc_xprt_get(xprt);
|
||||
rqstp->rq_reserved = serv->sv_max_mesg;
|
||||
atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);
|
||||
|
||||
/* As there is a shortage of threads and this request
|
||||
* had to be queued, don't allow the thread to wait so
|
||||
* long for cache updates.
|
||||
*/
|
||||
rqstp->rq_chandle.thread_wait = 1*HZ;
|
||||
} else {
|
||||
/* No data pending. Go to sleep */
|
||||
svc_thread_enqueue(pool, rqstp);
|
||||
|
|
@ -868,6 +874,19 @@ static void svc_age_temp_xprts(unsigned long closure)
|
|||
mod_timer(&serv->sv_temptimer, jiffies + svc_conn_age_period * HZ);
|
||||
}
|
||||
|
||||
static void call_xpt_users(struct svc_xprt *xprt)
|
||||
{
|
||||
struct svc_xpt_user *u;
|
||||
|
||||
spin_lock(&xprt->xpt_lock);
|
||||
while (!list_empty(&xprt->xpt_users)) {
|
||||
u = list_first_entry(&xprt->xpt_users, struct svc_xpt_user, list);
|
||||
list_del(&u->list);
|
||||
u->callback(u);
|
||||
}
|
||||
spin_unlock(&xprt->xpt_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a dead transport
|
||||
*/
|
||||
|
|
@ -878,7 +897,7 @@ void svc_delete_xprt(struct svc_xprt *xprt)
|
|||
|
||||
/* Only do this once */
|
||||
if (test_and_set_bit(XPT_DEAD, &xprt->xpt_flags))
|
||||
return;
|
||||
BUG();
|
||||
|
||||
dprintk("svc: svc_delete_xprt(%p)\n", xprt);
|
||||
xprt->xpt_ops->xpo_detach(xprt);
|
||||
|
|
@ -900,6 +919,7 @@ void svc_delete_xprt(struct svc_xprt *xprt)
|
|||
while ((dr = svc_deferred_dequeue(xprt)) != NULL)
|
||||
kfree(dr);
|
||||
|
||||
call_xpt_users(xprt);
|
||||
svc_xprt_put(xprt);
|
||||
}
|
||||
|
||||
|
|
@ -910,10 +930,7 @@ void svc_close_xprt(struct svc_xprt *xprt)
|
|||
/* someone else will have to effect the close */
|
||||
return;
|
||||
|
||||
svc_xprt_get(xprt);
|
||||
svc_delete_xprt(xprt);
|
||||
clear_bit(XPT_BUSY, &xprt->xpt_flags);
|
||||
svc_xprt_put(xprt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_close_xprt);
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
|
||||
#include "netns.h"
|
||||
|
||||
/*
|
||||
* AUTHUNIX and AUTHNULL credentials are both handled here.
|
||||
* AUTHNULL is treated just like AUTHUNIX except that the uid/gid
|
||||
|
|
@ -92,7 +94,6 @@ struct ip_map {
|
|||
struct unix_domain *m_client;
|
||||
int m_add_change;
|
||||
};
|
||||
static struct cache_head *ip_table[IP_HASHMAX];
|
||||
|
||||
static void ip_map_put(struct kref *kref)
|
||||
{
|
||||
|
|
@ -178,8 +179,8 @@ static int ip_map_upcall(struct cache_detail *cd, struct cache_head *h)
|
|||
return sunrpc_cache_pipe_upcall(cd, h, ip_map_request);
|
||||
}
|
||||
|
||||
static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr);
|
||||
static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry);
|
||||
static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class, struct in6_addr *addr);
|
||||
static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm, struct unix_domain *udom, time_t expiry);
|
||||
|
||||
static int ip_map_parse(struct cache_detail *cd,
|
||||
char *mesg, int mlen)
|
||||
|
|
@ -219,10 +220,9 @@ static int ip_map_parse(struct cache_detail *cd,
|
|||
switch (address.sa.sa_family) {
|
||||
case AF_INET:
|
||||
/* Form a mapped IPv4 address in sin6 */
|
||||
memset(&sin6, 0, sizeof(sin6));
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_addr.s6_addr32[2] = htonl(0xffff);
|
||||
sin6.sin6_addr.s6_addr32[3] = address.s4.sin_addr.s_addr;
|
||||
ipv6_addr_set_v4mapped(address.s4.sin_addr.s_addr,
|
||||
&sin6.sin6_addr);
|
||||
break;
|
||||
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
||||
case AF_INET6:
|
||||
|
|
@ -249,9 +249,9 @@ static int ip_map_parse(struct cache_detail *cd,
|
|||
dom = NULL;
|
||||
|
||||
/* IPv6 scope IDs are ignored for now */
|
||||
ipmp = ip_map_lookup(class, &sin6.sin6_addr);
|
||||
ipmp = __ip_map_lookup(cd, class, &sin6.sin6_addr);
|
||||
if (ipmp) {
|
||||
err = ip_map_update(ipmp,
|
||||
err = __ip_map_update(cd, ipmp,
|
||||
container_of(dom, struct unix_domain, h),
|
||||
expiry);
|
||||
} else
|
||||
|
|
@ -294,29 +294,15 @@ static int ip_map_show(struct seq_file *m,
|
|||
}
|
||||
|
||||
|
||||
struct cache_detail ip_map_cache = {
|
||||
.owner = THIS_MODULE,
|
||||
.hash_size = IP_HASHMAX,
|
||||
.hash_table = ip_table,
|
||||
.name = "auth.unix.ip",
|
||||
.cache_put = ip_map_put,
|
||||
.cache_upcall = ip_map_upcall,
|
||||
.cache_parse = ip_map_parse,
|
||||
.cache_show = ip_map_show,
|
||||
.match = ip_map_match,
|
||||
.init = ip_map_init,
|
||||
.update = update,
|
||||
.alloc = ip_map_alloc,
|
||||
};
|
||||
|
||||
static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr)
|
||||
static struct ip_map *__ip_map_lookup(struct cache_detail *cd, char *class,
|
||||
struct in6_addr *addr)
|
||||
{
|
||||
struct ip_map ip;
|
||||
struct cache_head *ch;
|
||||
|
||||
strcpy(ip.m_class, class);
|
||||
ipv6_addr_copy(&ip.m_addr, addr);
|
||||
ch = sunrpc_cache_lookup(&ip_map_cache, &ip.h,
|
||||
ch = sunrpc_cache_lookup(cd, &ip.h,
|
||||
hash_str(class, IP_HASHBITS) ^
|
||||
hash_ip6(*addr));
|
||||
|
||||
|
|
@ -326,7 +312,17 @@ static struct ip_map *ip_map_lookup(char *class, struct in6_addr *addr)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t expiry)
|
||||
static inline struct ip_map *ip_map_lookup(struct net *net, char *class,
|
||||
struct in6_addr *addr)
|
||||
{
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
sn = net_generic(net, sunrpc_net_id);
|
||||
return __ip_map_lookup(sn->ip_map_cache, class, addr);
|
||||
}
|
||||
|
||||
static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,
|
||||
struct unix_domain *udom, time_t expiry)
|
||||
{
|
||||
struct ip_map ip;
|
||||
struct cache_head *ch;
|
||||
|
|
@ -344,17 +340,25 @@ static int ip_map_update(struct ip_map *ipm, struct unix_domain *udom, time_t ex
|
|||
ip.m_add_change++;
|
||||
}
|
||||
ip.h.expiry_time = expiry;
|
||||
ch = sunrpc_cache_update(&ip_map_cache,
|
||||
&ip.h, &ipm->h,
|
||||
ch = sunrpc_cache_update(cd, &ip.h, &ipm->h,
|
||||
hash_str(ipm->m_class, IP_HASHBITS) ^
|
||||
hash_ip6(ipm->m_addr));
|
||||
if (!ch)
|
||||
return -ENOMEM;
|
||||
cache_put(ch, &ip_map_cache);
|
||||
cache_put(ch, cd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom)
|
||||
static inline int ip_map_update(struct net *net, struct ip_map *ipm,
|
||||
struct unix_domain *udom, time_t expiry)
|
||||
{
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
sn = net_generic(net, sunrpc_net_id);
|
||||
return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry);
|
||||
}
|
||||
|
||||
int auth_unix_add_addr(struct net *net, struct in6_addr *addr, struct auth_domain *dom)
|
||||
{
|
||||
struct unix_domain *udom;
|
||||
struct ip_map *ipmp;
|
||||
|
|
@ -362,10 +366,10 @@ int auth_unix_add_addr(struct in6_addr *addr, struct auth_domain *dom)
|
|||
if (dom->flavour != &svcauth_unix)
|
||||
return -EINVAL;
|
||||
udom = container_of(dom, struct unix_domain, h);
|
||||
ipmp = ip_map_lookup("nfsd", addr);
|
||||
ipmp = ip_map_lookup(net, "nfsd", addr);
|
||||
|
||||
if (ipmp)
|
||||
return ip_map_update(ipmp, udom, NEVER);
|
||||
return ip_map_update(net, ipmp, udom, NEVER);
|
||||
else
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -383,16 +387,18 @@ int auth_unix_forget_old(struct auth_domain *dom)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(auth_unix_forget_old);
|
||||
|
||||
struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
|
||||
struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr)
|
||||
{
|
||||
struct ip_map *ipm;
|
||||
struct auth_domain *rv;
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
ipm = ip_map_lookup("nfsd", addr);
|
||||
sn = net_generic(net, sunrpc_net_id);
|
||||
ipm = ip_map_lookup(net, "nfsd", addr);
|
||||
|
||||
if (!ipm)
|
||||
return NULL;
|
||||
if (cache_check(&ip_map_cache, &ipm->h, NULL))
|
||||
if (cache_check(sn->ip_map_cache, &ipm->h, NULL))
|
||||
return NULL;
|
||||
|
||||
if ((ipm->m_client->addr_changes - ipm->m_add_change) >0) {
|
||||
|
|
@ -403,22 +409,29 @@ struct auth_domain *auth_unix_lookup(struct in6_addr *addr)
|
|||
rv = &ipm->m_client->h;
|
||||
kref_get(&rv->ref);
|
||||
}
|
||||
cache_put(&ipm->h, &ip_map_cache);
|
||||
cache_put(&ipm->h, sn->ip_map_cache);
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(auth_unix_lookup);
|
||||
|
||||
void svcauth_unix_purge(void)
|
||||
{
|
||||
cache_purge(&ip_map_cache);
|
||||
struct net *net;
|
||||
|
||||
for_each_net(net) {
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
sn = net_generic(net, sunrpc_net_id);
|
||||
cache_purge(sn->ip_map_cache);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svcauth_unix_purge);
|
||||
|
||||
static inline struct ip_map *
|
||||
ip_map_cached_get(struct svc_rqst *rqstp)
|
||||
ip_map_cached_get(struct svc_xprt *xprt)
|
||||
{
|
||||
struct ip_map *ipm = NULL;
|
||||
struct svc_xprt *xprt = rqstp->rq_xprt;
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
|
||||
spin_lock(&xprt->xpt_lock);
|
||||
|
|
@ -430,9 +443,10 @@ ip_map_cached_get(struct svc_rqst *rqstp)
|
|||
* remembered, e.g. by a second mount from the
|
||||
* same IP address.
|
||||
*/
|
||||
sn = net_generic(xprt->xpt_net, sunrpc_net_id);
|
||||
xprt->xpt_auth_cache = NULL;
|
||||
spin_unlock(&xprt->xpt_lock);
|
||||
cache_put(&ipm->h, &ip_map_cache);
|
||||
cache_put(&ipm->h, sn->ip_map_cache);
|
||||
return NULL;
|
||||
}
|
||||
cache_get(&ipm->h);
|
||||
|
|
@ -443,10 +457,8 @@ ip_map_cached_get(struct svc_rqst *rqstp)
|
|||
}
|
||||
|
||||
static inline void
|
||||
ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm)
|
||||
ip_map_cached_put(struct svc_xprt *xprt, struct ip_map *ipm)
|
||||
{
|
||||
struct svc_xprt *xprt = rqstp->rq_xprt;
|
||||
|
||||
if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags)) {
|
||||
spin_lock(&xprt->xpt_lock);
|
||||
if (xprt->xpt_auth_cache == NULL) {
|
||||
|
|
@ -456,15 +468,26 @@ ip_map_cached_put(struct svc_rqst *rqstp, struct ip_map *ipm)
|
|||
}
|
||||
spin_unlock(&xprt->xpt_lock);
|
||||
}
|
||||
if (ipm)
|
||||
cache_put(&ipm->h, &ip_map_cache);
|
||||
if (ipm) {
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
sn = net_generic(xprt->xpt_net, sunrpc_net_id);
|
||||
cache_put(&ipm->h, sn->ip_map_cache);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
svcauth_unix_info_release(void *info)
|
||||
svcauth_unix_info_release(struct svc_xprt *xpt)
|
||||
{
|
||||
struct ip_map *ipm = info;
|
||||
cache_put(&ipm->h, &ip_map_cache);
|
||||
struct ip_map *ipm;
|
||||
|
||||
ipm = xpt->xpt_auth_cache;
|
||||
if (ipm != NULL) {
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
sn = net_generic(xpt->xpt_net, sunrpc_net_id);
|
||||
cache_put(&ipm->h, sn->ip_map_cache);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -674,6 +697,8 @@ static struct group_info *unix_gid_find(uid_t uid, struct svc_rqst *rqstp)
|
|||
switch (ret) {
|
||||
case -ENOENT:
|
||||
return ERR_PTR(-ENOENT);
|
||||
case -ETIMEDOUT:
|
||||
return ERR_PTR(-ESHUTDOWN);
|
||||
case 0:
|
||||
gi = get_group_info(ug->gi);
|
||||
cache_put(&ug->h, &unix_gid_cache);
|
||||
|
|
@ -691,6 +716,9 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
|
|||
struct ip_map *ipm;
|
||||
struct group_info *gi;
|
||||
struct svc_cred *cred = &rqstp->rq_cred;
|
||||
struct svc_xprt *xprt = rqstp->rq_xprt;
|
||||
struct net *net = xprt->xpt_net;
|
||||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
|
||||
switch (rqstp->rq_addr.ss_family) {
|
||||
case AF_INET:
|
||||
|
|
@ -709,26 +737,27 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
|
|||
if (rqstp->rq_proc == 0)
|
||||
return SVC_OK;
|
||||
|
||||
ipm = ip_map_cached_get(rqstp);
|
||||
ipm = ip_map_cached_get(xprt);
|
||||
if (ipm == NULL)
|
||||
ipm = ip_map_lookup(rqstp->rq_server->sv_program->pg_class,
|
||||
ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class,
|
||||
&sin6->sin6_addr);
|
||||
|
||||
if (ipm == NULL)
|
||||
return SVC_DENIED;
|
||||
|
||||
switch (cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
|
||||
switch (cache_check(sn->ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
|
||||
default:
|
||||
BUG();
|
||||
case -EAGAIN:
|
||||
case -ETIMEDOUT:
|
||||
return SVC_CLOSE;
|
||||
case -EAGAIN:
|
||||
return SVC_DROP;
|
||||
case -ENOENT:
|
||||
return SVC_DENIED;
|
||||
case 0:
|
||||
rqstp->rq_client = &ipm->m_client->h;
|
||||
kref_get(&rqstp->rq_client->ref);
|
||||
ip_map_cached_put(rqstp, ipm);
|
||||
ip_map_cached_put(xprt, ipm);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -736,6 +765,8 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
|
|||
switch (PTR_ERR(gi)) {
|
||||
case -EAGAIN:
|
||||
return SVC_DROP;
|
||||
case -ESHUTDOWN:
|
||||
return SVC_CLOSE;
|
||||
case -ENOENT:
|
||||
break;
|
||||
default:
|
||||
|
|
@ -776,7 +807,7 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)
|
|||
cred->cr_gid = (gid_t) -1;
|
||||
cred->cr_group_info = groups_alloc(0);
|
||||
if (cred->cr_group_info == NULL)
|
||||
return SVC_DROP; /* kmalloc failure - client must retry */
|
||||
return SVC_CLOSE; /* kmalloc failure - client must retry */
|
||||
|
||||
/* Put NULL verifier */
|
||||
svc_putnl(resv, RPC_AUTH_NULL);
|
||||
|
|
@ -840,7 +871,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)
|
|||
goto badcred;
|
||||
cred->cr_group_info = groups_alloc(slen);
|
||||
if (cred->cr_group_info == NULL)
|
||||
return SVC_DROP;
|
||||
return SVC_CLOSE;
|
||||
for (i = 0; i < slen; i++)
|
||||
GROUP_AT(cred->cr_group_info, i) = svc_getnl(argv);
|
||||
if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {
|
||||
|
|
@ -886,3 +917,56 @@ struct auth_ops svcauth_unix = {
|
|||
.set_client = svcauth_unix_set_client,
|
||||
};
|
||||
|
||||
int ip_map_cache_create(struct net *net)
|
||||
{
|
||||
int err = -ENOMEM;
|
||||
struct cache_detail *cd;
|
||||
struct cache_head **tbl;
|
||||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
|
||||
cd = kzalloc(sizeof(struct cache_detail), GFP_KERNEL);
|
||||
if (cd == NULL)
|
||||
goto err_cd;
|
||||
|
||||
tbl = kzalloc(IP_HASHMAX * sizeof(struct cache_head *), GFP_KERNEL);
|
||||
if (tbl == NULL)
|
||||
goto err_tbl;
|
||||
|
||||
cd->owner = THIS_MODULE,
|
||||
cd->hash_size = IP_HASHMAX,
|
||||
cd->hash_table = tbl,
|
||||
cd->name = "auth.unix.ip",
|
||||
cd->cache_put = ip_map_put,
|
||||
cd->cache_upcall = ip_map_upcall,
|
||||
cd->cache_parse = ip_map_parse,
|
||||
cd->cache_show = ip_map_show,
|
||||
cd->match = ip_map_match,
|
||||
cd->init = ip_map_init,
|
||||
cd->update = update,
|
||||
cd->alloc = ip_map_alloc,
|
||||
|
||||
err = cache_register_net(cd, net);
|
||||
if (err)
|
||||
goto err_reg;
|
||||
|
||||
sn->ip_map_cache = cd;
|
||||
return 0;
|
||||
|
||||
err_reg:
|
||||
kfree(tbl);
|
||||
err_tbl:
|
||||
kfree(cd);
|
||||
err_cd:
|
||||
return err;
|
||||
}
|
||||
|
||||
void ip_map_cache_destroy(struct net *net)
|
||||
{
|
||||
struct sunrpc_net *sn;
|
||||
|
||||
sn = net_generic(net, sunrpc_net_id);
|
||||
cache_purge(sn->ip_map_cache);
|
||||
cache_unregister_net(sn->ip_map_cache, net);
|
||||
kfree(sn->ip_map_cache->hash_table);
|
||||
kfree(sn->ip_map_cache);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ static void svc_tcp_sock_detach(struct svc_xprt *);
|
|||
static void svc_sock_free(struct svc_xprt *);
|
||||
|
||||
static struct svc_xprt *svc_create_socket(struct svc_serv *, int,
|
||||
struct sockaddr *, int, int);
|
||||
struct net *, struct sockaddr *,
|
||||
int, int);
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
static struct lock_class_key svc_key[2];
|
||||
static struct lock_class_key svc_slock_key[2];
|
||||
|
|
@ -657,10 +658,11 @@ static struct svc_xprt *svc_udp_accept(struct svc_xprt *xprt)
|
|||
}
|
||||
|
||||
static struct svc_xprt *svc_udp_create(struct svc_serv *serv,
|
||||
struct net *net,
|
||||
struct sockaddr *sa, int salen,
|
||||
int flags)
|
||||
{
|
||||
return svc_create_socket(serv, IPPROTO_UDP, sa, salen, flags);
|
||||
return svc_create_socket(serv, IPPROTO_UDP, net, sa, salen, flags);
|
||||
}
|
||||
|
||||
static struct svc_xprt_ops svc_udp_ops = {
|
||||
|
|
@ -1133,9 +1135,6 @@ static int svc_tcp_sendto(struct svc_rqst *rqstp)
|
|||
reclen = htonl(0x80000000|((xbufp->len ) - 4));
|
||||
memcpy(xbufp->head[0].iov_base, &reclen, 4);
|
||||
|
||||
if (test_bit(XPT_DEAD, &rqstp->rq_xprt->xpt_flags))
|
||||
return -ENOTCONN;
|
||||
|
||||
sent = svc_sendto(rqstp, &rqstp->rq_res);
|
||||
if (sent != xbufp->len) {
|
||||
printk(KERN_NOTICE
|
||||
|
|
@ -1178,10 +1177,11 @@ static int svc_tcp_has_wspace(struct svc_xprt *xprt)
|
|||
}
|
||||
|
||||
static struct svc_xprt *svc_tcp_create(struct svc_serv *serv,
|
||||
struct net *net,
|
||||
struct sockaddr *sa, int salen,
|
||||
int flags)
|
||||
{
|
||||
return svc_create_socket(serv, IPPROTO_TCP, sa, salen, flags);
|
||||
return svc_create_socket(serv, IPPROTO_TCP, net, sa, salen, flags);
|
||||
}
|
||||
|
||||
static struct svc_xprt_ops svc_tcp_ops = {
|
||||
|
|
@ -1258,19 +1258,13 @@ void svc_sock_update_bufs(struct svc_serv *serv)
|
|||
* The number of server threads has changed. Update
|
||||
* rcvbuf and sndbuf accordingly on all sockets
|
||||
*/
|
||||
struct list_head *le;
|
||||
struct svc_sock *svsk;
|
||||
|
||||
spin_lock_bh(&serv->sv_lock);
|
||||
list_for_each(le, &serv->sv_permsocks) {
|
||||
struct svc_sock *svsk =
|
||||
list_entry(le, struct svc_sock, sk_xprt.xpt_list);
|
||||
list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list)
|
||||
set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);
|
||||
}
|
||||
list_for_each(le, &serv->sv_tempsocks) {
|
||||
struct svc_sock *svsk =
|
||||
list_entry(le, struct svc_sock, sk_xprt.xpt_list);
|
||||
list_for_each_entry(svsk, &serv->sv_tempsocks, sk_xprt.xpt_list)
|
||||
set_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags);
|
||||
}
|
||||
spin_unlock_bh(&serv->sv_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(svc_sock_update_bufs);
|
||||
|
|
@ -1385,6 +1379,7 @@ EXPORT_SYMBOL_GPL(svc_addsock);
|
|||
*/
|
||||
static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
|
||||
int protocol,
|
||||
struct net *net,
|
||||
struct sockaddr *sin, int len,
|
||||
int flags)
|
||||
{
|
||||
|
|
@ -1421,7 +1416,7 @@ static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
|
|||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
error = sock_create_kern(family, type, protocol, &sock);
|
||||
error = __sock_create(net, family, type, protocol, &sock, 1);
|
||||
if (error < 0)
|
||||
return ERR_PTR(error);
|
||||
|
||||
|
|
|
|||
|
|
@ -199,8 +199,6 @@ int xprt_reserve_xprt(struct rpc_task *task)
|
|||
if (test_and_set_bit(XPRT_LOCKED, &xprt->state)) {
|
||||
if (task == xprt->snd_task)
|
||||
return 1;
|
||||
if (task == NULL)
|
||||
return 0;
|
||||
goto out_sleep;
|
||||
}
|
||||
xprt->snd_task = task;
|
||||
|
|
@ -757,13 +755,11 @@ static void xprt_connect_status(struct rpc_task *task)
|
|||
*/
|
||||
struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid)
|
||||
{
|
||||
struct list_head *pos;
|
||||
struct rpc_rqst *entry;
|
||||
|
||||
list_for_each(pos, &xprt->recv) {
|
||||
struct rpc_rqst *entry = list_entry(pos, struct rpc_rqst, rq_list);
|
||||
list_for_each_entry(entry, &xprt->recv, rq_list)
|
||||
if (entry->rq_xid == xid)
|
||||
return entry;
|
||||
}
|
||||
|
||||
dprintk("RPC: xprt_lookup_rqst did not find xid %08x\n",
|
||||
ntohl(xid));
|
||||
|
|
@ -962,6 +958,37 @@ static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req)
|
|||
spin_unlock(&xprt->reserve_lock);
|
||||
}
|
||||
|
||||
struct rpc_xprt *xprt_alloc(struct net *net, int size, int max_req)
|
||||
{
|
||||
struct rpc_xprt *xprt;
|
||||
|
||||
xprt = kzalloc(size, GFP_KERNEL);
|
||||
if (xprt == NULL)
|
||||
goto out;
|
||||
|
||||
xprt->max_reqs = max_req;
|
||||
xprt->slot = kcalloc(max_req, sizeof(struct rpc_rqst), GFP_KERNEL);
|
||||
if (xprt->slot == NULL)
|
||||
goto out_free;
|
||||
|
||||
xprt->xprt_net = get_net(net);
|
||||
return xprt;
|
||||
|
||||
out_free:
|
||||
kfree(xprt);
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xprt_alloc);
|
||||
|
||||
void xprt_free(struct rpc_xprt *xprt)
|
||||
{
|
||||
put_net(xprt->xprt_net);
|
||||
kfree(xprt->slot);
|
||||
kfree(xprt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xprt_free);
|
||||
|
||||
/**
|
||||
* xprt_reserve - allocate an RPC request slot
|
||||
* @task: RPC task requesting a slot allocation
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/sched.h>
|
||||
#include <linux/sunrpc/svc_rdma.h>
|
||||
|
|
@ -74,6 +75,8 @@ atomic_t rdma_stat_sq_prod;
|
|||
struct kmem_cache *svc_rdma_map_cachep;
|
||||
struct kmem_cache *svc_rdma_ctxt_cachep;
|
||||
|
||||
struct workqueue_struct *svc_rdma_wq;
|
||||
|
||||
/*
|
||||
* This function implements reading and resetting an atomic_t stat
|
||||
* variable through read/write to a proc file. Any write to the file
|
||||
|
|
@ -231,7 +234,7 @@ static ctl_table svcrdma_root_table[] = {
|
|||
void svc_rdma_cleanup(void)
|
||||
{
|
||||
dprintk("SVCRDMA Module Removed, deregister RPC RDMA transport\n");
|
||||
flush_scheduled_work();
|
||||
destroy_workqueue(svc_rdma_wq);
|
||||
if (svcrdma_table_header) {
|
||||
unregister_sysctl_table(svcrdma_table_header);
|
||||
svcrdma_table_header = NULL;
|
||||
|
|
@ -249,6 +252,11 @@ int svc_rdma_init(void)
|
|||
dprintk("\tsq_depth : %d\n",
|
||||
svcrdma_max_requests * RPCRDMA_SQ_DEPTH_MULT);
|
||||
dprintk("\tmax_inline : %d\n", svcrdma_max_req_size);
|
||||
|
||||
svc_rdma_wq = alloc_workqueue("svc_rdma", 0, 0);
|
||||
if (!svc_rdma_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!svcrdma_table_header)
|
||||
svcrdma_table_header =
|
||||
register_sysctl_table(svcrdma_root_table);
|
||||
|
|
@ -283,6 +291,7 @@ int svc_rdma_init(void)
|
|||
kmem_cache_destroy(svc_rdma_map_cachep);
|
||||
err0:
|
||||
unregister_sysctl_table(svcrdma_table_header);
|
||||
destroy_workqueue(svc_rdma_wq);
|
||||
return -ENOMEM;
|
||||
}
|
||||
MODULE_AUTHOR("Tom Tucker <[email protected]>");
|
||||
|
|
|
|||
|
|
@ -263,9 +263,9 @@ static int fast_reg_read_chunks(struct svcxprt_rdma *xprt,
|
|||
frmr->page_list_len = PAGE_ALIGN(byte_count) >> PAGE_SHIFT;
|
||||
for (page_no = 0; page_no < frmr->page_list_len; page_no++) {
|
||||
frmr->page_list->page_list[page_no] =
|
||||
ib_dma_map_single(xprt->sc_cm_id->device,
|
||||
page_address(rqstp->rq_arg.pages[page_no]),
|
||||
PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
ib_dma_map_page(xprt->sc_cm_id->device,
|
||||
rqstp->rq_arg.pages[page_no], 0,
|
||||
PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
|
||||
frmr->page_list->page_list[page_no]))
|
||||
goto fatal_err;
|
||||
|
|
@ -309,17 +309,21 @@ static int rdma_set_ctxt_sge(struct svcxprt_rdma *xprt,
|
|||
int count)
|
||||
{
|
||||
int i;
|
||||
unsigned long off;
|
||||
|
||||
ctxt->count = count;
|
||||
ctxt->direction = DMA_FROM_DEVICE;
|
||||
for (i = 0; i < count; i++) {
|
||||
ctxt->sge[i].length = 0; /* in case map fails */
|
||||
if (!frmr) {
|
||||
BUG_ON(0 == virt_to_page(vec[i].iov_base));
|
||||
off = (unsigned long)vec[i].iov_base & ~PAGE_MASK;
|
||||
ctxt->sge[i].addr =
|
||||
ib_dma_map_single(xprt->sc_cm_id->device,
|
||||
vec[i].iov_base,
|
||||
vec[i].iov_len,
|
||||
DMA_FROM_DEVICE);
|
||||
ib_dma_map_page(xprt->sc_cm_id->device,
|
||||
virt_to_page(vec[i].iov_base),
|
||||
off,
|
||||
vec[i].iov_len,
|
||||
DMA_FROM_DEVICE);
|
||||
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
|
||||
ctxt->sge[i].addr))
|
||||
return -EINVAL;
|
||||
|
|
@ -491,6 +495,7 @@ next_sge:
|
|||
printk(KERN_ERR "svcrdma: Error %d posting RDMA_READ\n",
|
||||
err);
|
||||
set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
|
||||
svc_rdma_unmap_dma(ctxt);
|
||||
svc_rdma_put_context(ctxt, 0);
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,8 +70,8 @@
|
|||
* on extra page for the RPCRMDA header.
|
||||
*/
|
||||
static int fast_reg_xdr(struct svcxprt_rdma *xprt,
|
||||
struct xdr_buf *xdr,
|
||||
struct svc_rdma_req_map *vec)
|
||||
struct xdr_buf *xdr,
|
||||
struct svc_rdma_req_map *vec)
|
||||
{
|
||||
int sge_no;
|
||||
u32 sge_bytes;
|
||||
|
|
@ -96,21 +96,25 @@ static int fast_reg_xdr(struct svcxprt_rdma *xprt,
|
|||
vec->count = 2;
|
||||
sge_no++;
|
||||
|
||||
/* Build the FRMR */
|
||||
/* Map the XDR head */
|
||||
frmr->kva = frva;
|
||||
frmr->direction = DMA_TO_DEVICE;
|
||||
frmr->access_flags = 0;
|
||||
frmr->map_len = PAGE_SIZE;
|
||||
frmr->page_list_len = 1;
|
||||
page_off = (unsigned long)xdr->head[0].iov_base & ~PAGE_MASK;
|
||||
frmr->page_list->page_list[page_no] =
|
||||
ib_dma_map_single(xprt->sc_cm_id->device,
|
||||
(void *)xdr->head[0].iov_base,
|
||||
PAGE_SIZE, DMA_TO_DEVICE);
|
||||
ib_dma_map_page(xprt->sc_cm_id->device,
|
||||
virt_to_page(xdr->head[0].iov_base),
|
||||
page_off,
|
||||
PAGE_SIZE - page_off,
|
||||
DMA_TO_DEVICE);
|
||||
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
|
||||
frmr->page_list->page_list[page_no]))
|
||||
goto fatal_err;
|
||||
atomic_inc(&xprt->sc_dma_used);
|
||||
|
||||
/* Map the XDR page list */
|
||||
page_off = xdr->page_base;
|
||||
page_bytes = xdr->page_len + page_off;
|
||||
if (!page_bytes)
|
||||
|
|
@ -128,9 +132,9 @@ static int fast_reg_xdr(struct svcxprt_rdma *xprt,
|
|||
page_bytes -= sge_bytes;
|
||||
|
||||
frmr->page_list->page_list[page_no] =
|
||||
ib_dma_map_single(xprt->sc_cm_id->device,
|
||||
page_address(page),
|
||||
PAGE_SIZE, DMA_TO_DEVICE);
|
||||
ib_dma_map_page(xprt->sc_cm_id->device,
|
||||
page, page_off,
|
||||
sge_bytes, DMA_TO_DEVICE);
|
||||
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
|
||||
frmr->page_list->page_list[page_no]))
|
||||
goto fatal_err;
|
||||
|
|
@ -166,8 +170,10 @@ static int fast_reg_xdr(struct svcxprt_rdma *xprt,
|
|||
vec->sge[sge_no].iov_base = frva + frmr->map_len + page_off;
|
||||
|
||||
frmr->page_list->page_list[page_no] =
|
||||
ib_dma_map_single(xprt->sc_cm_id->device, va, PAGE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
ib_dma_map_page(xprt->sc_cm_id->device, virt_to_page(va),
|
||||
page_off,
|
||||
PAGE_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
|
||||
frmr->page_list->page_list[page_no]))
|
||||
goto fatal_err;
|
||||
|
|
@ -245,6 +251,35 @@ static int map_xdr(struct svcxprt_rdma *xprt,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static dma_addr_t dma_map_xdr(struct svcxprt_rdma *xprt,
|
||||
struct xdr_buf *xdr,
|
||||
u32 xdr_off, size_t len, int dir)
|
||||
{
|
||||
struct page *page;
|
||||
dma_addr_t dma_addr;
|
||||
if (xdr_off < xdr->head[0].iov_len) {
|
||||
/* This offset is in the head */
|
||||
xdr_off += (unsigned long)xdr->head[0].iov_base & ~PAGE_MASK;
|
||||
page = virt_to_page(xdr->head[0].iov_base);
|
||||
} else {
|
||||
xdr_off -= xdr->head[0].iov_len;
|
||||
if (xdr_off < xdr->page_len) {
|
||||
/* This offset is in the page list */
|
||||
page = xdr->pages[xdr_off >> PAGE_SHIFT];
|
||||
xdr_off &= ~PAGE_MASK;
|
||||
} else {
|
||||
/* This offset is in the tail */
|
||||
xdr_off -= xdr->page_len;
|
||||
xdr_off += (unsigned long)
|
||||
xdr->tail[0].iov_base & ~PAGE_MASK;
|
||||
page = virt_to_page(xdr->tail[0].iov_base);
|
||||
}
|
||||
}
|
||||
dma_addr = ib_dma_map_page(xprt->sc_cm_id->device, page, xdr_off,
|
||||
min_t(size_t, PAGE_SIZE, len), dir);
|
||||
return dma_addr;
|
||||
}
|
||||
|
||||
/* Assumptions:
|
||||
* - We are using FRMR
|
||||
* - or -
|
||||
|
|
@ -293,10 +328,9 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
|
|||
sge[sge_no].length = sge_bytes;
|
||||
if (!vec->frmr) {
|
||||
sge[sge_no].addr =
|
||||
ib_dma_map_single(xprt->sc_cm_id->device,
|
||||
(void *)
|
||||
vec->sge[xdr_sge_no].iov_base + sge_off,
|
||||
sge_bytes, DMA_TO_DEVICE);
|
||||
dma_map_xdr(xprt, &rqstp->rq_res, xdr_off,
|
||||
sge_bytes, DMA_TO_DEVICE);
|
||||
xdr_off += sge_bytes;
|
||||
if (ib_dma_mapping_error(xprt->sc_cm_id->device,
|
||||
sge[sge_no].addr))
|
||||
goto err;
|
||||
|
|
@ -333,6 +367,8 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
|
|||
goto err;
|
||||
return 0;
|
||||
err:
|
||||
svc_rdma_unmap_dma(ctxt);
|
||||
svc_rdma_put_frmr(xprt, vec->frmr);
|
||||
svc_rdma_put_context(ctxt, 0);
|
||||
/* Fatal error, close transport */
|
||||
return -EIO;
|
||||
|
|
@ -494,7 +530,8 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt,
|
|||
* In all three cases, this function prepares the RPCRDMA header in
|
||||
* sge[0], the 'type' parameter indicates the type to place in the
|
||||
* RPCRDMA header, and the 'byte_count' field indicates how much of
|
||||
* the XDR to include in this RDMA_SEND.
|
||||
* the XDR to include in this RDMA_SEND. NB: The offset of the payload
|
||||
* to send is zero in the XDR.
|
||||
*/
|
||||
static int send_reply(struct svcxprt_rdma *rdma,
|
||||
struct svc_rqst *rqstp,
|
||||
|
|
@ -536,23 +573,24 @@ static int send_reply(struct svcxprt_rdma *rdma,
|
|||
ctxt->sge[0].lkey = rdma->sc_dma_lkey;
|
||||
ctxt->sge[0].length = svc_rdma_xdr_get_reply_hdr_len(rdma_resp);
|
||||
ctxt->sge[0].addr =
|
||||
ib_dma_map_single(rdma->sc_cm_id->device, page_address(page),
|
||||
ctxt->sge[0].length, DMA_TO_DEVICE);
|
||||
ib_dma_map_page(rdma->sc_cm_id->device, page, 0,
|
||||
ctxt->sge[0].length, DMA_TO_DEVICE);
|
||||
if (ib_dma_mapping_error(rdma->sc_cm_id->device, ctxt->sge[0].addr))
|
||||
goto err;
|
||||
atomic_inc(&rdma->sc_dma_used);
|
||||
|
||||
ctxt->direction = DMA_TO_DEVICE;
|
||||
|
||||
/* Determine how many of our SGE are to be transmitted */
|
||||
/* Map the payload indicated by 'byte_count' */
|
||||
for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) {
|
||||
int xdr_off = 0;
|
||||
sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count);
|
||||
byte_count -= sge_bytes;
|
||||
if (!vec->frmr) {
|
||||
ctxt->sge[sge_no].addr =
|
||||
ib_dma_map_single(rdma->sc_cm_id->device,
|
||||
vec->sge[sge_no].iov_base,
|
||||
sge_bytes, DMA_TO_DEVICE);
|
||||
dma_map_xdr(rdma, &rqstp->rq_res, xdr_off,
|
||||
sge_bytes, DMA_TO_DEVICE);
|
||||
xdr_off += sge_bytes;
|
||||
if (ib_dma_mapping_error(rdma->sc_cm_id->device,
|
||||
ctxt->sge[sge_no].addr))
|
||||
goto err;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/rdma_cm.h>
|
||||
#include <linux/sunrpc/svc_rdma.h>
|
||||
|
|
@ -52,6 +53,7 @@
|
|||
#define RPCDBG_FACILITY RPCDBG_SVCXPRT
|
||||
|
||||
static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
|
||||
struct net *net,
|
||||
struct sockaddr *sa, int salen,
|
||||
int flags);
|
||||
static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt);
|
||||
|
|
@ -89,6 +91,9 @@ struct svc_xprt_class svc_rdma_class = {
|
|||
/* WR context cache. Created in svc_rdma.c */
|
||||
extern struct kmem_cache *svc_rdma_ctxt_cachep;
|
||||
|
||||
/* Workqueue created in svc_rdma.c */
|
||||
extern struct workqueue_struct *svc_rdma_wq;
|
||||
|
||||
struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt)
|
||||
{
|
||||
struct svc_rdma_op_ctxt *ctxt;
|
||||
|
|
@ -120,7 +125,7 @@ void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
|
|||
*/
|
||||
if (ctxt->sge[i].lkey == xprt->sc_dma_lkey) {
|
||||
atomic_dec(&xprt->sc_dma_used);
|
||||
ib_dma_unmap_single(xprt->sc_cm_id->device,
|
||||
ib_dma_unmap_page(xprt->sc_cm_id->device,
|
||||
ctxt->sge[i].addr,
|
||||
ctxt->sge[i].length,
|
||||
ctxt->direction);
|
||||
|
|
@ -502,8 +507,8 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
|
|||
BUG_ON(sge_no >= xprt->sc_max_sge);
|
||||
page = svc_rdma_get_page();
|
||||
ctxt->pages[sge_no] = page;
|
||||
pa = ib_dma_map_single(xprt->sc_cm_id->device,
|
||||
page_address(page), PAGE_SIZE,
|
||||
pa = ib_dma_map_page(xprt->sc_cm_id->device,
|
||||
page, 0, PAGE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
if (ib_dma_mapping_error(xprt->sc_cm_id->device, pa))
|
||||
goto err_put_ctxt;
|
||||
|
|
@ -511,9 +516,9 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
|
|||
ctxt->sge[sge_no].addr = pa;
|
||||
ctxt->sge[sge_no].length = PAGE_SIZE;
|
||||
ctxt->sge[sge_no].lkey = xprt->sc_dma_lkey;
|
||||
ctxt->count = sge_no + 1;
|
||||
buflen += PAGE_SIZE;
|
||||
}
|
||||
ctxt->count = sge_no;
|
||||
recv_wr.next = NULL;
|
||||
recv_wr.sg_list = &ctxt->sge[0];
|
||||
recv_wr.num_sge = ctxt->count;
|
||||
|
|
@ -529,6 +534,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
|
|||
return ret;
|
||||
|
||||
err_put_ctxt:
|
||||
svc_rdma_unmap_dma(ctxt);
|
||||
svc_rdma_put_context(ctxt, 1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
@ -670,6 +676,7 @@ static int rdma_cma_handler(struct rdma_cm_id *cma_id,
|
|||
* Create a listening RDMA service endpoint.
|
||||
*/
|
||||
static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
|
||||
struct net *net,
|
||||
struct sockaddr *sa, int salen,
|
||||
int flags)
|
||||
{
|
||||
|
|
@ -798,8 +805,8 @@ static void frmr_unmap_dma(struct svcxprt_rdma *xprt,
|
|||
if (ib_dma_mapping_error(frmr->mr->device, addr))
|
||||
continue;
|
||||
atomic_dec(&xprt->sc_dma_used);
|
||||
ib_dma_unmap_single(frmr->mr->device, addr, PAGE_SIZE,
|
||||
frmr->direction);
|
||||
ib_dma_unmap_page(frmr->mr->device, addr, PAGE_SIZE,
|
||||
frmr->direction);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1184,7 +1191,7 @@ static void svc_rdma_free(struct svc_xprt *xprt)
|
|||
struct svcxprt_rdma *rdma =
|
||||
container_of(xprt, struct svcxprt_rdma, sc_xprt);
|
||||
INIT_WORK(&rdma->sc_work, __svc_rdma_free);
|
||||
schedule_work(&rdma->sc_work);
|
||||
queue_work(svc_rdma_wq, &rdma->sc_work);
|
||||
}
|
||||
|
||||
static int svc_rdma_has_wspace(struct svc_xprt *xprt)
|
||||
|
|
@ -1274,7 +1281,7 @@ int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr)
|
|||
atomic_read(&xprt->sc_sq_count) <
|
||||
xprt->sc_sq_depth);
|
||||
if (test_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags))
|
||||
return 0;
|
||||
return -ENOTCONN;
|
||||
continue;
|
||||
}
|
||||
/* Take a transport ref for each WR posted */
|
||||
|
|
@ -1306,7 +1313,6 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
|
|||
enum rpcrdma_errcode err)
|
||||
{
|
||||
struct ib_send_wr err_wr;
|
||||
struct ib_sge sge;
|
||||
struct page *p;
|
||||
struct svc_rdma_op_ctxt *ctxt;
|
||||
u32 *va;
|
||||
|
|
@ -1319,26 +1325,27 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
|
|||
/* XDR encode error */
|
||||
length = svc_rdma_xdr_encode_error(xprt, rmsgp, err, va);
|
||||
|
||||
ctxt = svc_rdma_get_context(xprt);
|
||||
ctxt->direction = DMA_FROM_DEVICE;
|
||||
ctxt->count = 1;
|
||||
ctxt->pages[0] = p;
|
||||
|
||||
/* Prepare SGE for local address */
|
||||
sge.addr = ib_dma_map_single(xprt->sc_cm_id->device,
|
||||
page_address(p), PAGE_SIZE, DMA_FROM_DEVICE);
|
||||
if (ib_dma_mapping_error(xprt->sc_cm_id->device, sge.addr)) {
|
||||
ctxt->sge[0].addr = ib_dma_map_page(xprt->sc_cm_id->device,
|
||||
p, 0, length, DMA_FROM_DEVICE);
|
||||
if (ib_dma_mapping_error(xprt->sc_cm_id->device, ctxt->sge[0].addr)) {
|
||||
put_page(p);
|
||||
return;
|
||||
}
|
||||
atomic_inc(&xprt->sc_dma_used);
|
||||
sge.lkey = xprt->sc_dma_lkey;
|
||||
sge.length = length;
|
||||
|
||||
ctxt = svc_rdma_get_context(xprt);
|
||||
ctxt->count = 1;
|
||||
ctxt->pages[0] = p;
|
||||
ctxt->sge[0].lkey = xprt->sc_dma_lkey;
|
||||
ctxt->sge[0].length = length;
|
||||
|
||||
/* Prepare SEND WR */
|
||||
memset(&err_wr, 0, sizeof err_wr);
|
||||
ctxt->wr_op = IB_WR_SEND;
|
||||
err_wr.wr_id = (unsigned long)ctxt;
|
||||
err_wr.sg_list = &sge;
|
||||
err_wr.sg_list = ctxt->sge;
|
||||
err_wr.num_sge = 1;
|
||||
err_wr.opcode = IB_WR_SEND;
|
||||
err_wr.send_flags = IB_SEND_SIGNALED;
|
||||
|
|
@ -1348,9 +1355,7 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
|
|||
if (ret) {
|
||||
dprintk("svcrdma: Error %d posting send for protocol error\n",
|
||||
ret);
|
||||
ib_dma_unmap_single(xprt->sc_cm_id->device,
|
||||
sge.addr, PAGE_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
svc_rdma_unmap_dma(ctxt);
|
||||
svc_rdma_put_context(ctxt, 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -237,8 +237,7 @@ xprt_rdma_destroy(struct rpc_xprt *xprt)
|
|||
|
||||
dprintk("RPC: %s: called\n", __func__);
|
||||
|
||||
cancel_delayed_work(&r_xprt->rdma_connect);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&r_xprt->rdma_connect);
|
||||
|
||||
xprt_clear_connected(xprt);
|
||||
|
||||
|
|
@ -251,9 +250,7 @@ xprt_rdma_destroy(struct rpc_xprt *xprt)
|
|||
|
||||
xprt_rdma_free_addresses(xprt);
|
||||
|
||||
kfree(xprt->slot);
|
||||
xprt->slot = NULL;
|
||||
kfree(xprt);
|
||||
xprt_free(xprt);
|
||||
|
||||
dprintk("RPC: %s: returning\n", __func__);
|
||||
|
||||
|
|
@ -285,23 +282,14 @@ xprt_setup_rdma(struct xprt_create *args)
|
|||
return ERR_PTR(-EBADF);
|
||||
}
|
||||
|
||||
xprt = kzalloc(sizeof(struct rpcrdma_xprt), GFP_KERNEL);
|
||||
xprt = xprt_alloc(args->net, sizeof(struct rpcrdma_xprt),
|
||||
xprt_rdma_slot_table_entries);
|
||||
if (xprt == NULL) {
|
||||
dprintk("RPC: %s: couldn't allocate rpcrdma_xprt\n",
|
||||
__func__);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
xprt->max_reqs = xprt_rdma_slot_table_entries;
|
||||
xprt->slot = kcalloc(xprt->max_reqs,
|
||||
sizeof(struct rpc_rqst), GFP_KERNEL);
|
||||
if (xprt->slot == NULL) {
|
||||
dprintk("RPC: %s: couldn't allocate %d slots\n",
|
||||
__func__, xprt->max_reqs);
|
||||
kfree(xprt);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
/* 60 second timeout, no retries */
|
||||
xprt->timeout = &xprt_rdma_default_timeout;
|
||||
xprt->bind_timeout = (60U * HZ);
|
||||
|
|
@ -410,8 +398,7 @@ out3:
|
|||
out2:
|
||||
rpcrdma_ia_close(&new_xprt->rx_ia);
|
||||
out1:
|
||||
kfree(xprt->slot);
|
||||
kfree(xprt);
|
||||
xprt_free(xprt);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
|
|
@ -460,7 +447,7 @@ xprt_rdma_connect(struct rpc_task *task)
|
|||
} else {
|
||||
schedule_delayed_work(&r_xprt->rdma_connect, 0);
|
||||
if (!RPC_IS_ASYNC(task))
|
||||
flush_scheduled_work();
|
||||
flush_delayed_work(&r_xprt->rdma_connect);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -774,8 +774,7 @@ static void xs_destroy(struct rpc_xprt *xprt)
|
|||
|
||||
xs_close(xprt);
|
||||
xs_free_peer_addresses(xprt);
|
||||
kfree(xprt->slot);
|
||||
kfree(xprt);
|
||||
xprt_free(xprt);
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
||||
|
|
@ -1516,7 +1515,7 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned short port)
|
|||
xs_update_peer_port(xprt);
|
||||
}
|
||||
|
||||
static unsigned short xs_get_srcport(struct sock_xprt *transport, struct socket *sock)
|
||||
static unsigned short xs_get_srcport(struct sock_xprt *transport)
|
||||
{
|
||||
unsigned short port = transport->srcport;
|
||||
|
||||
|
|
@ -1525,7 +1524,7 @@ static unsigned short xs_get_srcport(struct sock_xprt *transport, struct socket
|
|||
return port;
|
||||
}
|
||||
|
||||
static unsigned short xs_next_srcport(struct sock_xprt *transport, struct socket *sock, unsigned short port)
|
||||
static unsigned short xs_next_srcport(struct sock_xprt *transport, unsigned short port)
|
||||
{
|
||||
if (transport->srcport != 0)
|
||||
transport->srcport = 0;
|
||||
|
|
@ -1535,23 +1534,18 @@ static unsigned short xs_next_srcport(struct sock_xprt *transport, struct socket
|
|||
return xprt_max_resvport;
|
||||
return --port;
|
||||
}
|
||||
|
||||
static int xs_bind4(struct sock_xprt *transport, struct socket *sock)
|
||||
static int xs_bind(struct sock_xprt *transport, struct socket *sock)
|
||||
{
|
||||
struct sockaddr_in myaddr = {
|
||||
.sin_family = AF_INET,
|
||||
};
|
||||
struct sockaddr_in *sa;
|
||||
struct sockaddr_storage myaddr;
|
||||
int err, nloop = 0;
|
||||
unsigned short port = xs_get_srcport(transport, sock);
|
||||
unsigned short port = xs_get_srcport(transport);
|
||||
unsigned short last;
|
||||
|
||||
sa = (struct sockaddr_in *)&transport->srcaddr;
|
||||
myaddr.sin_addr = sa->sin_addr;
|
||||
memcpy(&myaddr, &transport->srcaddr, transport->xprt.addrlen);
|
||||
do {
|
||||
myaddr.sin_port = htons(port);
|
||||
err = kernel_bind(sock, (struct sockaddr *) &myaddr,
|
||||
sizeof(myaddr));
|
||||
rpc_set_port((struct sockaddr *)&myaddr, port);
|
||||
err = kernel_bind(sock, (struct sockaddr *)&myaddr,
|
||||
transport->xprt.addrlen);
|
||||
if (port == 0)
|
||||
break;
|
||||
if (err == 0) {
|
||||
|
|
@ -1559,47 +1553,22 @@ static int xs_bind4(struct sock_xprt *transport, struct socket *sock)
|
|||
break;
|
||||
}
|
||||
last = port;
|
||||
port = xs_next_srcport(transport, sock, port);
|
||||
port = xs_next_srcport(transport, port);
|
||||
if (port > last)
|
||||
nloop++;
|
||||
} while (err == -EADDRINUSE && nloop != 2);
|
||||
dprintk("RPC: %s %pI4:%u: %s (%d)\n",
|
||||
__func__, &myaddr.sin_addr,
|
||||
port, err ? "failed" : "ok", err);
|
||||
|
||||
if (myaddr.ss_family == AF_INET)
|
||||
dprintk("RPC: %s %pI4:%u: %s (%d)\n", __func__,
|
||||
&((struct sockaddr_in *)&myaddr)->sin_addr,
|
||||
port, err ? "failed" : "ok", err);
|
||||
else
|
||||
dprintk("RPC: %s %pI6:%u: %s (%d)\n", __func__,
|
||||
&((struct sockaddr_in6 *)&myaddr)->sin6_addr,
|
||||
port, err ? "failed" : "ok", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int xs_bind6(struct sock_xprt *transport, struct socket *sock)
|
||||
{
|
||||
struct sockaddr_in6 myaddr = {
|
||||
.sin6_family = AF_INET6,
|
||||
};
|
||||
struct sockaddr_in6 *sa;
|
||||
int err, nloop = 0;
|
||||
unsigned short port = xs_get_srcport(transport, sock);
|
||||
unsigned short last;
|
||||
|
||||
sa = (struct sockaddr_in6 *)&transport->srcaddr;
|
||||
myaddr.sin6_addr = sa->sin6_addr;
|
||||
do {
|
||||
myaddr.sin6_port = htons(port);
|
||||
err = kernel_bind(sock, (struct sockaddr *) &myaddr,
|
||||
sizeof(myaddr));
|
||||
if (port == 0)
|
||||
break;
|
||||
if (err == 0) {
|
||||
transport->srcport = port;
|
||||
break;
|
||||
}
|
||||
last = port;
|
||||
port = xs_next_srcport(transport, sock, port);
|
||||
if (port > last)
|
||||
nloop++;
|
||||
} while (err == -EADDRINUSE && nloop != 2);
|
||||
dprintk("RPC: xs_bind6 %pI6:%u: %s (%d)\n",
|
||||
&myaddr.sin6_addr, port, err ? "failed" : "ok", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
static struct lock_class_key xs_key[2];
|
||||
|
|
@ -1622,6 +1591,18 @@ static inline void xs_reclassify_socket6(struct socket *sock)
|
|||
sock_lock_init_class_and_name(sk, "slock-AF_INET6-RPC",
|
||||
&xs_slock_key[1], "sk_lock-AF_INET6-RPC", &xs_key[1]);
|
||||
}
|
||||
|
||||
static inline void xs_reclassify_socket(int family, struct socket *sock)
|
||||
{
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
xs_reclassify_socket4(sock);
|
||||
break;
|
||||
case AF_INET6:
|
||||
xs_reclassify_socket6(sock);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void xs_reclassify_socket4(struct socket *sock)
|
||||
{
|
||||
|
|
@ -1630,8 +1611,36 @@ static inline void xs_reclassify_socket4(struct socket *sock)
|
|||
static inline void xs_reclassify_socket6(struct socket *sock)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void xs_reclassify_socket(int family, struct socket *sock)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct socket *xs_create_sock(struct rpc_xprt *xprt,
|
||||
struct sock_xprt *transport, int family, int type, int protocol)
|
||||
{
|
||||
struct socket *sock;
|
||||
int err;
|
||||
|
||||
err = __sock_create(xprt->xprt_net, family, type, protocol, &sock, 1);
|
||||
if (err < 0) {
|
||||
dprintk("RPC: can't create %d transport socket (%d).\n",
|
||||
protocol, -err);
|
||||
goto out;
|
||||
}
|
||||
xs_reclassify_socket(family, sock);
|
||||
|
||||
if (xs_bind(transport, sock)) {
|
||||
sock_release(sock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return sock;
|
||||
out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
||||
{
|
||||
struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
|
||||
|
|
@ -1661,82 +1670,23 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
|||
xs_udp_do_set_buffer_size(xprt);
|
||||
}
|
||||
|
||||
/**
|
||||
* xs_udp_connect_worker4 - set up a UDP socket
|
||||
* @work: RPC transport to connect
|
||||
*
|
||||
* Invoked by a work queue tasklet.
|
||||
*/
|
||||
static void xs_udp_connect_worker4(struct work_struct *work)
|
||||
static void xs_udp_setup_socket(struct work_struct *work)
|
||||
{
|
||||
struct sock_xprt *transport =
|
||||
container_of(work, struct sock_xprt, connect_worker.work);
|
||||
struct rpc_xprt *xprt = &transport->xprt;
|
||||
struct socket *sock = transport->sock;
|
||||
int err, status = -EIO;
|
||||
int status = -EIO;
|
||||
|
||||
if (xprt->shutdown)
|
||||
goto out;
|
||||
|
||||
/* Start by resetting any existing state */
|
||||
xs_reset_transport(transport);
|
||||
|
||||
err = sock_create_kern(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &sock);
|
||||
if (err < 0) {
|
||||
dprintk("RPC: can't create UDP transport socket (%d).\n", -err);
|
||||
sock = xs_create_sock(xprt, transport,
|
||||
xs_addr(xprt)->sa_family, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (IS_ERR(sock))
|
||||
goto out;
|
||||
}
|
||||
xs_reclassify_socket4(sock);
|
||||
|
||||
if (xs_bind4(transport, sock)) {
|
||||
sock_release(sock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dprintk("RPC: worker connecting xprt %p via %s to "
|
||||
"%s (port %s)\n", xprt,
|
||||
xprt->address_strings[RPC_DISPLAY_PROTO],
|
||||
xprt->address_strings[RPC_DISPLAY_ADDR],
|
||||
xprt->address_strings[RPC_DISPLAY_PORT]);
|
||||
|
||||
xs_udp_finish_connecting(xprt, sock);
|
||||
status = 0;
|
||||
out:
|
||||
xprt_clear_connecting(xprt);
|
||||
xprt_wake_pending_tasks(xprt, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* xs_udp_connect_worker6 - set up a UDP socket
|
||||
* @work: RPC transport to connect
|
||||
*
|
||||
* Invoked by a work queue tasklet.
|
||||
*/
|
||||
static void xs_udp_connect_worker6(struct work_struct *work)
|
||||
{
|
||||
struct sock_xprt *transport =
|
||||
container_of(work, struct sock_xprt, connect_worker.work);
|
||||
struct rpc_xprt *xprt = &transport->xprt;
|
||||
struct socket *sock = transport->sock;
|
||||
int err, status = -EIO;
|
||||
|
||||
if (xprt->shutdown)
|
||||
goto out;
|
||||
|
||||
/* Start by resetting any existing state */
|
||||
xs_reset_transport(transport);
|
||||
|
||||
err = sock_create_kern(PF_INET6, SOCK_DGRAM, IPPROTO_UDP, &sock);
|
||||
if (err < 0) {
|
||||
dprintk("RPC: can't create UDP transport socket (%d).\n", -err);
|
||||
goto out;
|
||||
}
|
||||
xs_reclassify_socket6(sock);
|
||||
|
||||
if (xs_bind6(transport, sock) < 0) {
|
||||
sock_release(sock);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dprintk("RPC: worker connecting xprt %p via %s to "
|
||||
"%s (port %s)\n", xprt,
|
||||
|
|
@ -1755,12 +1705,12 @@ out:
|
|||
* We need to preserve the port number so the reply cache on the server can
|
||||
* find our cached RPC replies when we get around to reconnecting.
|
||||
*/
|
||||
static void xs_abort_connection(struct rpc_xprt *xprt, struct sock_xprt *transport)
|
||||
static void xs_abort_connection(struct sock_xprt *transport)
|
||||
{
|
||||
int result;
|
||||
struct sockaddr any;
|
||||
|
||||
dprintk("RPC: disconnecting xprt %p to reuse port\n", xprt);
|
||||
dprintk("RPC: disconnecting xprt %p to reuse port\n", transport);
|
||||
|
||||
/*
|
||||
* Disconnect the transport socket by doing a connect operation
|
||||
|
|
@ -1770,13 +1720,13 @@ static void xs_abort_connection(struct rpc_xprt *xprt, struct sock_xprt *transpo
|
|||
any.sa_family = AF_UNSPEC;
|
||||
result = kernel_connect(transport->sock, &any, sizeof(any), 0);
|
||||
if (!result)
|
||||
xs_sock_mark_closed(xprt);
|
||||
xs_sock_mark_closed(&transport->xprt);
|
||||
else
|
||||
dprintk("RPC: AF_UNSPEC connect return code %d\n",
|
||||
result);
|
||||
}
|
||||
|
||||
static void xs_tcp_reuse_connection(struct rpc_xprt *xprt, struct sock_xprt *transport)
|
||||
static void xs_tcp_reuse_connection(struct sock_xprt *transport)
|
||||
{
|
||||
unsigned int state = transport->inet->sk_state;
|
||||
|
||||
|
|
@ -1799,7 +1749,7 @@ static void xs_tcp_reuse_connection(struct rpc_xprt *xprt, struct sock_xprt *tra
|
|||
"sk_shutdown set to %d\n",
|
||||
__func__, transport->inet->sk_shutdown);
|
||||
}
|
||||
xs_abort_connection(xprt, transport);
|
||||
xs_abort_connection(transport);
|
||||
}
|
||||
|
||||
static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
||||
|
|
@ -1852,12 +1802,12 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
|
|||
*
|
||||
* Invoked by a work queue tasklet.
|
||||
*/
|
||||
static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
|
||||
struct sock_xprt *transport,
|
||||
struct socket *(*create_sock)(struct rpc_xprt *,
|
||||
struct sock_xprt *))
|
||||
static void xs_tcp_setup_socket(struct work_struct *work)
|
||||
{
|
||||
struct sock_xprt *transport =
|
||||
container_of(work, struct sock_xprt, connect_worker.work);
|
||||
struct socket *sock = transport->sock;
|
||||
struct rpc_xprt *xprt = &transport->xprt;
|
||||
int status = -EIO;
|
||||
|
||||
if (xprt->shutdown)
|
||||
|
|
@ -1865,7 +1815,8 @@ static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
|
|||
|
||||
if (!sock) {
|
||||
clear_bit(XPRT_CONNECTION_ABORT, &xprt->state);
|
||||
sock = create_sock(xprt, transport);
|
||||
sock = xs_create_sock(xprt, transport,
|
||||
xs_addr(xprt)->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (IS_ERR(sock)) {
|
||||
status = PTR_ERR(sock);
|
||||
goto out;
|
||||
|
|
@ -1876,7 +1827,7 @@ static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
|
|||
abort_and_exit = test_and_clear_bit(XPRT_CONNECTION_ABORT,
|
||||
&xprt->state);
|
||||
/* "close" the socket, preserving the local port */
|
||||
xs_tcp_reuse_connection(xprt, transport);
|
||||
xs_tcp_reuse_connection(transport);
|
||||
|
||||
if (abort_and_exit)
|
||||
goto out_eagain;
|
||||
|
|
@ -1925,84 +1876,6 @@ out:
|
|||
xprt_wake_pending_tasks(xprt, status);
|
||||
}
|
||||
|
||||
static struct socket *xs_create_tcp_sock4(struct rpc_xprt *xprt,
|
||||
struct sock_xprt *transport)
|
||||
{
|
||||
struct socket *sock;
|
||||
int err;
|
||||
|
||||
/* start from scratch */
|
||||
err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
|
||||
if (err < 0) {
|
||||
dprintk("RPC: can't create TCP transport socket (%d).\n",
|
||||
-err);
|
||||
goto out_err;
|
||||
}
|
||||
xs_reclassify_socket4(sock);
|
||||
|
||||
if (xs_bind4(transport, sock) < 0) {
|
||||
sock_release(sock);
|
||||
goto out_err;
|
||||
}
|
||||
return sock;
|
||||
out_err:
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* xs_tcp_connect_worker4 - connect a TCP socket to a remote endpoint
|
||||
* @work: RPC transport to connect
|
||||
*
|
||||
* Invoked by a work queue tasklet.
|
||||
*/
|
||||
static void xs_tcp_connect_worker4(struct work_struct *work)
|
||||
{
|
||||
struct sock_xprt *transport =
|
||||
container_of(work, struct sock_xprt, connect_worker.work);
|
||||
struct rpc_xprt *xprt = &transport->xprt;
|
||||
|
||||
xs_tcp_setup_socket(xprt, transport, xs_create_tcp_sock4);
|
||||
}
|
||||
|
||||
static struct socket *xs_create_tcp_sock6(struct rpc_xprt *xprt,
|
||||
struct sock_xprt *transport)
|
||||
{
|
||||
struct socket *sock;
|
||||
int err;
|
||||
|
||||
/* start from scratch */
|
||||
err = sock_create_kern(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &sock);
|
||||
if (err < 0) {
|
||||
dprintk("RPC: can't create TCP transport socket (%d).\n",
|
||||
-err);
|
||||
goto out_err;
|
||||
}
|
||||
xs_reclassify_socket6(sock);
|
||||
|
||||
if (xs_bind6(transport, sock) < 0) {
|
||||
sock_release(sock);
|
||||
goto out_err;
|
||||
}
|
||||
return sock;
|
||||
out_err:
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* xs_tcp_connect_worker6 - connect a TCP socket to a remote endpoint
|
||||
* @work: RPC transport to connect
|
||||
*
|
||||
* Invoked by a work queue tasklet.
|
||||
*/
|
||||
static void xs_tcp_connect_worker6(struct work_struct *work)
|
||||
{
|
||||
struct sock_xprt *transport =
|
||||
container_of(work, struct sock_xprt, connect_worker.work);
|
||||
struct rpc_xprt *xprt = &transport->xprt;
|
||||
|
||||
xs_tcp_setup_socket(xprt, transport, xs_create_tcp_sock6);
|
||||
}
|
||||
|
||||
/**
|
||||
* xs_connect - connect a socket to a remote endpoint
|
||||
* @task: address of RPC task that manages state of connect request
|
||||
|
|
@ -2262,6 +2135,31 @@ static struct rpc_xprt_ops bc_tcp_ops = {
|
|||
.print_stats = xs_tcp_print_stats,
|
||||
};
|
||||
|
||||
static int xs_init_anyaddr(const int family, struct sockaddr *sap)
|
||||
{
|
||||
static const struct sockaddr_in sin = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_addr.s_addr = htonl(INADDR_ANY),
|
||||
};
|
||||
static const struct sockaddr_in6 sin6 = {
|
||||
.sin6_family = AF_INET6,
|
||||
.sin6_addr = IN6ADDR_ANY_INIT,
|
||||
};
|
||||
|
||||
switch (family) {
|
||||
case AF_INET:
|
||||
memcpy(sap, &sin, sizeof(sin));
|
||||
break;
|
||||
case AF_INET6:
|
||||
memcpy(sap, &sin6, sizeof(sin6));
|
||||
break;
|
||||
default:
|
||||
dprintk("RPC: %s: Bad address family\n", __func__);
|
||||
return -EAFNOSUPPORT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
|
||||
unsigned int slot_table_size)
|
||||
{
|
||||
|
|
@ -2273,27 +2171,25 @@ static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
|
|||
return ERR_PTR(-EBADF);
|
||||
}
|
||||
|
||||
new = kzalloc(sizeof(*new), GFP_KERNEL);
|
||||
if (new == NULL) {
|
||||
xprt = xprt_alloc(args->net, sizeof(*new), slot_table_size);
|
||||
if (xprt == NULL) {
|
||||
dprintk("RPC: xs_setup_xprt: couldn't allocate "
|
||||
"rpc_xprt\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
xprt = &new->xprt;
|
||||
|
||||
xprt->max_reqs = slot_table_size;
|
||||
xprt->slot = kcalloc(xprt->max_reqs, sizeof(struct rpc_rqst), GFP_KERNEL);
|
||||
if (xprt->slot == NULL) {
|
||||
kfree(xprt);
|
||||
dprintk("RPC: xs_setup_xprt: couldn't allocate slot "
|
||||
"table\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
new = container_of(xprt, struct sock_xprt, xprt);
|
||||
memcpy(&xprt->addr, args->dstaddr, args->addrlen);
|
||||
xprt->addrlen = args->addrlen;
|
||||
if (args->srcaddr)
|
||||
memcpy(&new->srcaddr, args->srcaddr, args->addrlen);
|
||||
else {
|
||||
int err;
|
||||
err = xs_init_anyaddr(args->dstaddr->sa_family,
|
||||
(struct sockaddr *)&new->srcaddr);
|
||||
if (err != 0)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return xprt;
|
||||
}
|
||||
|
|
@ -2341,7 +2237,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
|
|||
xprt_set_bound(xprt);
|
||||
|
||||
INIT_DELAYED_WORK(&transport->connect_worker,
|
||||
xs_udp_connect_worker4);
|
||||
xs_udp_setup_socket);
|
||||
xs_format_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP);
|
||||
break;
|
||||
case AF_INET6:
|
||||
|
|
@ -2349,7 +2245,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
|
|||
xprt_set_bound(xprt);
|
||||
|
||||
INIT_DELAYED_WORK(&transport->connect_worker,
|
||||
xs_udp_connect_worker6);
|
||||
xs_udp_setup_socket);
|
||||
xs_format_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP6);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -2371,8 +2267,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
|
|||
return xprt;
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
out_err:
|
||||
kfree(xprt->slot);
|
||||
kfree(xprt);
|
||||
xprt_free(xprt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -2416,7 +2311,7 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
|
|||
xprt_set_bound(xprt);
|
||||
|
||||
INIT_DELAYED_WORK(&transport->connect_worker,
|
||||
xs_tcp_connect_worker4);
|
||||
xs_tcp_setup_socket);
|
||||
xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP);
|
||||
break;
|
||||
case AF_INET6:
|
||||
|
|
@ -2424,7 +2319,7 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
|
|||
xprt_set_bound(xprt);
|
||||
|
||||
INIT_DELAYED_WORK(&transport->connect_worker,
|
||||
xs_tcp_connect_worker6);
|
||||
xs_tcp_setup_socket);
|
||||
xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP6);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -2447,8 +2342,7 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
|
|||
return xprt;
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
out_err:
|
||||
kfree(xprt->slot);
|
||||
kfree(xprt);
|
||||
xprt_free(xprt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -2507,15 +2401,10 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
|
|||
goto out_err;
|
||||
}
|
||||
|
||||
if (xprt_bound(xprt))
|
||||
dprintk("RPC: set up xprt to %s (port %s) via %s\n",
|
||||
xprt->address_strings[RPC_DISPLAY_ADDR],
|
||||
xprt->address_strings[RPC_DISPLAY_PORT],
|
||||
xprt->address_strings[RPC_DISPLAY_PROTO]);
|
||||
else
|
||||
dprintk("RPC: set up xprt to %s (autobind) via %s\n",
|
||||
xprt->address_strings[RPC_DISPLAY_ADDR],
|
||||
xprt->address_strings[RPC_DISPLAY_PROTO]);
|
||||
dprintk("RPC: set up xprt to %s (port %s) via %s\n",
|
||||
xprt->address_strings[RPC_DISPLAY_ADDR],
|
||||
xprt->address_strings[RPC_DISPLAY_PORT],
|
||||
xprt->address_strings[RPC_DISPLAY_PROTO]);
|
||||
|
||||
/*
|
||||
* Since we don't want connections for the backchannel, we set
|
||||
|
|
@ -2528,8 +2417,7 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
|
|||
return xprt;
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
out_err:
|
||||
kfree(xprt->slot);
|
||||
kfree(xprt);
|
||||
xprt_free(xprt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue