@@ -348,7 +348,12 @@ struct gss_svc_seq_data {
* the parent.
* 2) assertions: set on a child rsc cache entry to hold the
* RPCSEC_GSS_CREATE data to assert.
- *
+ * 3) net: set on the parent rsc cache entry and is required for the
+ * lookup associated child rsc cache entries upon parent destruction.
+ * 4) ch_lock: used by the parent; protects the children list
+ * 5) children: used by the parent rsc cache entry to hold a list of
+ * associated child rsc cache entries and used upon parent destruction
+ * to lookup child rsc cache entries so as to destroy the children.
*/
struct rsc {
struct cache_head h;
@@ -358,11 +363,20 @@ struct rsc {
struct gss_svc_seq_data seqdata;
struct gss_ctx *mechctx;
struct gss3_svc_assert *assertions;
+ struct net *net;
+ spinlock_t ch_lock; /* for children */
+ struct list_head children;
+};
+
+struct rsc_child_entry {
+ struct list_head ce_list;
+ struct xdr_netobj ce_chandle;
};
static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct rsc *old);
static struct rsc *rsc_lookup(struct cache_detail *cd, struct rsc *item);
static void gss3_free_svc_assert(struct gss3_svc_assert *g3a);
+static void gss3_free_rsc_children(struct rsc *rsci);
static void rsc_free(struct rsc *rsci)
{
@@ -373,6 +387,8 @@ static void rsc_free(struct rsc *rsci)
free_svc_cred(&rsci->cred);
if (rsci->assertions)
gss3_free_svc_assert(rsci->assertions);
+ if (!list_empty(&rsci->children))
+ gss3_free_rsc_children(rsci);
}
static void rsc_put(struct kref *ref)
@@ -399,6 +415,14 @@ static void rsc_put(struct kref *ref)
}
static void
+init_rsc(struct rsc *rsci)
+{
+ memset(rsci, 0, sizeof(struct rsc));
+ spin_lock_init(&rsci->ch_lock);
+ INIT_LIST_HEAD(&rsci->children);
+}
+
+static void
rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
{
struct rsc *new = container_of(cnew, struct rsc, h);
@@ -417,6 +441,9 @@ static void rsc_put(struct kref *ref)
new->mechctx = NULL;
init_svc_cred(&new->cred);
new->assertions = NULL;
+ new->net = NULL;
+ spin_lock_init(&new->ch_lock);
+ INIT_LIST_HEAD(&new->children);
}
static void
@@ -437,6 +464,16 @@ static void rsc_put(struct kref *ref)
new->assertions = tmp->assertions;
tmp->assertions = NULL;
init_svc_cred(&tmp->cred);
+ new->net = tmp->net;
+ tmp->net = NULL;
+ spin_lock_init(&new->ch_lock);
+ INIT_LIST_HEAD(&new->children);
+ spin_lock(&tmp->ch_lock);
+ if (!list_empty(&tmp->children)) {
+ list_move(&tmp->children, &new->children);
+ INIT_LIST_HEAD(&tmp->children);
+ }
+ spin_unlock(&tmp->ch_lock);
}
static struct cache_head *
@@ -461,7 +498,7 @@ static int rsc_parse(struct cache_detail *cd,
int status = -EINVAL;
struct gss_api_mech *gm = NULL;
- memset(&rsci, 0, sizeof(rsci));
+ init_rsc(&rsci);
/* context handle */
len = qword_get(&mesg, buf, mlen);
if (len < 0) goto out;
@@ -613,7 +650,7 @@ static struct rsc *rsc_update(struct cache_detail *cd, struct rsc *new, struct r
struct rsc rsci;
struct rsc *found;
- memset(&rsci, 0, sizeof(rsci));
+ init_rsc(&rsci);
if (dup_to_netobj(&rsci.handle, handle->data, handle->len))
return NULL;
found = rsc_lookup(cd, &rsci);
@@ -1285,7 +1322,7 @@ static int gss_proxy_save_rsc(struct cache_detail *cd,
time_t expiry;
int status = -EINVAL;
- memset(&rsci, 0, sizeof(rsci));
+ init_rsc(&rsci);
/* context handle */
status = -ENOMEM;
/* the handle needs to be just a unique id,
@@ -1544,6 +1581,101 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
kfree(g3a);
}
+static void
+gss3_free_child(struct rsc_child_entry *cep, struct sunrpc_net *sn)
+{
+ struct rsc *child;
+
+ child = gss_svc_searchbyctx(sn->rsc_cache, &cep->ce_chandle);
+ if (!child) {
+ pr_warn("RPC %s child in children list not found\n",
+ __func__);
+ goto out_free;
+ }
+ /* balance gss_svc_searchbyctx cache_get */
+ cache_put(&child->h, sn->rsc_cache);
+ /* reap the child */
+ sunrpc_cache_unhash(sn->rsc_cache, &child->h);
+out_free:
+ kfree(cep);
+}
+
+static void gss3_free_rsc_children(struct rsc *parent_rsc)
+{
+ struct rsc_child_entry *cep, *tmp;
+ LIST_HEAD(free);
+ struct sunrpc_net *sn = net_generic(parent_rsc->net, sunrpc_net_id);
+
+ spin_lock(&parent_rsc->ch_lock);
+ list_for_each_entry_safe(cep, tmp, &parent_rsc->children, ce_list) {
+ list_move(&cep->ce_list, &free);
+ /* balance cache_get in gss3_add_child_rsc */
+ cache_put(&parent_rsc->h, sn->rsc_cache);
+ }
+ spin_unlock(&parent_rsc->ch_lock);
+
+ list_for_each_entry_safe(cep, tmp, &free, ce_list) {
+ list_del(&cep->ce_list);
+ gss3_free_child(cep, sn);
+ }
+}
+
+static void
+gss3_free_child_entry(struct rsc *p_rsci, struct xdr_netobj *chandle)
+{
+ struct sunrpc_net *sn = net_generic(p_rsci->net, sunrpc_net_id);
+ struct rsc_child_entry *cep, *tmp;
+
+ spin_lock(&p_rsci->ch_lock);
+ list_for_each_entry_safe(cep, tmp, &p_rsci->children, ce_list) {
+ if (netobj_equal(chandle, &cep->ce_chandle)) {
+ list_del(&cep->ce_list);
+ spin_unlock(&p_rsci->ch_lock);
+ /* balance cache_get in gss3_add_child_rsc */
+ cache_put(&p_rsci->h, sn->rsc_cache);
+ kfree(cep);
+ return;
+ }
+ }
+ spin_unlock(&p_rsci->ch_lock);
+}
+
+/**
+ * After a gss3 child rsc is created, add it's context handle to the
+ * children list of the parent rsc.
+ * Required: gss_svc_searchbyctx has already been called on parent_rsc.
+ */
+static int
+gss3_add_child_rsc(struct cache_detail *cd, struct rsc *parent_rsc,
+ struct xdr_netobj *chandle)
+{
+ struct rsc_child_entry *cep;
+ int status = -ENOMEM;
+
+ cep = kmalloc(sizeof(*cep), GFP_KERNEL);
+ if (!cep)
+ goto out;
+
+ /* child handle */
+ if (dup_netobj(&cep->ce_chandle, chandle))
+ goto out_free;
+
+ parent_rsc->net = cd->net;
+ INIT_LIST_HEAD(&cep->ce_list);
+ spin_lock(&parent_rsc->ch_lock);
+ list_add(&cep->ce_list, &parent_rsc->children);
+ spin_unlock(&parent_rsc->ch_lock);
+ /* balanced by cache_put in free children routines */
+ cache_get(&parent_rsc->h);
+
+ status = 0;
+out:
+ return status;
+out_free:
+ kfree(cep);
+ goto out;
+}
+
/**
* gss3_save_child_rsc()
* Create a child handle, set the parent handle, assertions, and add to
@@ -1565,8 +1697,7 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
long dummy;
long long ctxh;
- memset(&child, 0, sizeof(child));
-
+ init_rsc(&child);
/* context handle */
ctxh = atomic64_inc_return(&ctxhctr);
@@ -1673,8 +1804,10 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
struct sunrpc_net *sn)
{
struct kvec *resv = &rqstp->rq_res.head[0];
+ struct gss_svc_data *svcdata = rqstp->rq_auth_data;
struct gss3_svc_assert *g3a;
struct gss3_label *glp;
+ struct rsc *p_rsci = svcdata->rsci;
u64 c_handle;
struct xdr_netobj child_handle;
int enc_len, assert_len, ret = 0;
@@ -1689,6 +1822,22 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
child_handle.data = (u8 *)&c_handle;
child_handle.len = sizeof(c_handle);
+ ret = gss3_add_child_rsc(sn->rsc_cache, p_rsci, &child_handle);
+ if (ret < 0) {
+ struct rsc *child;
+
+ pr_warn("%s failed to add child rsc to parent\n", __func__);
+ /* delete child */
+ child = gss_svc_searchbyctx(sn->rsc_cache, &child_handle);
+ if (child) {
+ /* balance gss_svc_searchbyctx cache_get */
+ cache_put(&child->h, sn->rsc_cache);
+ /* reap the child */
+ sunrpc_cache_unhash(sn->rsc_cache, &child->h);
+ }
+ goto auth_err;
+ }
+
/* calculate the assert length. Support one assert per request */
switch (g3a->sa_assert.au_type) {
case GSS3_LABEL:
@@ -1762,8 +1911,7 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
dprintk("RPC: svcauth_gss: argv->iov_len = %zd\n",
argv->iov_len);
- *authp = rpc_autherr_badcred;
- if (!svcdata)
+ *authp = rpc_autherr_badcred; if (!svcdata)
svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);
if (!svcdata)
goto auth_err;
@@ -1823,6 +1971,7 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
if (rsci->parent_handle.len != 0) { /* GSSv3 child handle */
rsci_ch = rsci;
+ /* take reference on child rsc entry */
rsci = gss_svc_searchbyctx(sn->rsc_cache,
&rsci_ch->parent_handle);
if (!rsci) {
@@ -1854,6 +2003,15 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
case RPC_GSS_PROC_DESTROY:
if (gss_write_verf(rqstp, rsci->mechctx, gc, gc->gc_seq))
goto auth_err;
+
+ /* Destroying child, remove from parent list */
+ if (rsci_ch)
+ gss3_free_child_entry(rsci, &rsci_ch->handle);
+
+ /* Destroying parent. Remove all children from list */
+ else if (!list_empty(&rsci->children))
+ gss3_free_rsc_children(rsci);
+
/* Delete the entry from the cache_list and call cache_put */
sunrpc_cache_unhash(sn->rsc_cache, &rsci->h);
if (resv->iov_len + 4 > PAGE_SIZE)
@@ -1934,10 +2092,14 @@ static void gss3_free_svc_assert(struct gss3_svc_assert *g3a)
drop:
ret = SVC_CLOSE;
out:
+ if (rsci_ch) {
+ if (rsci)
+ cache_put(&rsci_ch->h, sn->rsc_cache);
+ else
+ WARN_ON_ONCE(!rsci);
+ }
if (rsci)
cache_put(&rsci->h, sn->rsc_cache);
- if (rsci_ch)
- cache_put(&rsci_ch->h, sn->rsc_cache);
return ret;
}