Re: [PATCH Version 3] SVCAUTH update the rsc cache on RPC_GSS_PROC_DESTROY

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, Dec 20, 2016 at 4:06 PM, NeilBrown <neilb@xxxxxxxx> wrote:
> On Wed, Dec 21 2016, Andy Adamson wrote:
>
>> On Mon, Dec 19, 2016 at 11:03 PM, NeilBrown <neilb@xxxxxxxx> wrote:
>>> On Tue, Dec 20 2016, andros@xxxxxxxxxx wrote:
>>>
>>>> From: Andy Adamson <andros@xxxxxxxxxx>
>>>>
>>>> The rsc cache code operates in a read_lock/write_lock environment.
>>>> Changes to a cache entry should use the provided rsc_update
>>>> routine which takes the write_lock.
>>>>
>>>> The current code sets the expiry_time and the CACHE_NEGATIVE flag
>>>> without taking the write_lock as it does not call rsc_update.
>>>> Without this patch, while cache_clean sees the entries to be
>>>> removed, it does not remove the rsc_entries. This is because
>>>> rsc_update sets other fields in the entry to properly trigger
>>>> cache_clean.
>>>>
>>>> Cache_clean takes the write_lock to remove expired or invalid
>>>> entries from the cache_list and calls cache_put on the entry.
>>>>
>>>> Looking at sunrpc_cache_update, what we want is to invalidate the
>>>> cache entry, so that it is direclty replaced which means that
>>>> update_rsc is called. We pass in a new zero'ed rsc cache entry
>>>> to rsc_update with an expiry_time set to 0 along with the
>>>> invalidatedcache entry to be destroyed.
>>>>
>>>> The cache_put at the end of svcauth_gss_accept processing drops
>>>> the reference count to 1 which allows the cache_put called by
>>>> cache_clean to result in a call to rsc_put and rsc_free
>>>> to reap the entry after it has been removed from the cache_list
>>>> under the write_lock.
>>>>
>>>> Signed-off-by: Andy Adamson <andros@xxxxxxxxxx>
>>>> ---
>>>>  net/sunrpc/auth_gss/svcauth_gss.c | 18 +++++++++++++++---
>>>>  1 file changed, 15 insertions(+), 3 deletions(-)
>>>>
>>>> diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
>>>> index 45662d7..b8093da 100644
>>>> --- a/net/sunrpc/auth_gss/svcauth_gss.c
>>>> +++ b/net/sunrpc/auth_gss/svcauth_gss.c
>>>> @@ -1409,7 +1409,9 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
>>>>       u32             crlen;
>>>>       struct gss_svc_data *svcdata = rqstp->rq_auth_data;
>>>>       struct rpc_gss_wire_cred *gc;
>>>> -     struct rsc      *rsci = NULL;
>>>> +     struct rsc      *rsci = NULL, new = {
>>>> +             .mechctx = 0,
>>>> +     };
>>>>       __be32          *rpcstart;
>>>>       __be32          *reject_stat = resv->iov_base + resv->iov_len;
>>>>       int             ret;
>>>> @@ -1489,10 +1491,20 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
>>>>       case RPC_GSS_PROC_DESTROY:
>>>>               if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))
>>>>                       goto auth_err;
>>>> -             rsci->h.expiry_time = get_seconds();
>>>> -             set_bit(CACHE_NEGATIVE, &rsci->h.flags);
>>>> +
>>>> +             /** Invalidate the cache entry so sunrpc_update_cache
>>>> +              * direclty updates rsci. new->h.expiry_time is zero,
>>>> +              * so rsci->h.expiry_time will be set to zero and
>>>> +              * cache_clean will properly remove rsci.
>>>> +              */
>>>> +             clear_bit(CACHE_VALID, &rsci->h.flags);
>>>
>>> I think this is poor form.  It might work in this particular case, but
>>> in general, clearing CACHE_VALID is the wrong thing to do.
>>
>> Why? That is exactly what an RPC_GSS_PROC_DESTROY message means - the
>> GSS context is invalid and needs be immediately destroyed.
>
> But that isn't what CACHE_VALID means.
> If a cache item doesn't have CACHE_VALID set, then it represents
> a question that hasn't been answered.
> When it is answered, CACHE_VALID is set, and either CACHE_NEGATIVE is
> set, or the 'content' fields are filled in.
> In general, the .cache_put doesn't free the 'content' fields unless
> CACHE_VALID is set, and CACHE_NEGATIVE is clear.  Consequently setting
> or clearing these fields needs to be paired with setting or freeing the
> content.
> auth_gss doesn't make that distinction and sets or frees everything
> together so just clearing the flag would probably work.  On other
> caches it wouldn't, so I'd rather not see it done at all.
>
>>
>> What other mechanism does your cache design provide for this very
>> common AUTH_GSS case?
>
> There is none.  No other cache requires entries to be individually
> deleted.
> That is why I suggested that we could add sunrpc_cache_unhash().
>
>>
>>>
>>> What is the goal here?  Do we want to make sure future lookups for this
>>> entry find a negative entry, or would we be happy for them to fail?
>>
>> No future lookups. No future use. Destroyed. Immediately, and all
>> resources freed.
>
> In that case, sunrpc_cache_unhash() sounds like something we should do.
>
> void sunrpc_cache_unhash(struct cache_detail *cd, struct cache_head *h)
> {
>         write_lock(&detail->hash_lock);
>         if (!hlist_unhashed(&h->cache_list)) {
>                 hlist_del_init(&h->cache_list);
>                 cd->entries--;
>                 write_unlock(&detail->hash_lock);
>                 cache_put(h, cd);
>         } else
>                 write_unlock(&detail->hash_lock);
> }
>
> Can you confirm that this does what you need?

Sure. I'll use it and let you know.

-->Andy

>
> Thanks,
> NeilBrown
>
>>
>> Clearing the CACHE_VALID flag and not setting the CACHE_NEGATIVE flag
>> on the does this by updating the entry with a new entry that has a
>> zero expiry_time.
>>
>> sunrpc_cache_update (called by rsc_update)
>>        /* The 'old' entry is to be replaced by 'new'.
>>          * If 'old' is not VALID, we update it directly,
>>          * otherwise we need to replace it
>>          */
>>
>>         struct cache_head *tmp;
>>
>>         if (!test_bit(CACHE_VALID, &old->flags)) {
>>                 write_lock(&detail->hash_lock);
>>                 if (!test_bit(CACHE_VALID, &old->flags)) {
>>                         if (test_bit(CACHE_NEGATIVE, &new->flags))
>>                                 set_bit(CACHE_NEGATIVE, &old->flags);
>>                         else
>>                                 detail->update(old, new);
>> <<<<<<<<<<<<<<<<<<<< goal is to hit this.
>>                         cache_fresh_locked(old, new->expiry_time, detail);
>>                         write_unlock(&detail->hash_lock);
>>                         cache_fresh_unlocked(old, detail);
>>                         return old;
>>
>>                 }
>>                 write_unlock(&detail->hash_lock);
>>         }
>>
>> This also leaves the refcount alone so that at the end of
>> svcauth_gss_processing, the refcount is 1 and cache_clean can properly
>> reap the entry.
>>
>> --Andy
>>
>>>
>>> If they should find a negative entry, then just do the update without
>>> messing with the CACHE_VALID flag.  It will require an allocation, but
>>> that shouldn't be a big cost.
>>>
>>> If you are happy for them to find nothing, the we should write a
>>> sunrpc_cache_unhash() or similar which unlinks an entry from the cache
>>> and decrements its refcount.
>>>
>>> NeilBrown
>>>
>>>
>>>> +             rsci = rsc_update(sn->rsc_cache, &new, rsci);
>>>> +             if (!rsci)
>>>> +                     goto drop;
>>>> +
>>>>               if (resv->iov_len + 4 > PAGE_SIZE)
>>>>                       goto drop;
>>>> +
>>>>               svc_putnl(resv, RPC_SUCCESS);
>>>>               goto complete;
>>>>       case RPC_GSS_PROC_DATA:
>>>> --
>>>> 1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Filesystem Development]     [Linux USB Development]     [Linux Media Development]     [Video for Linux]     [Linux NILFS]     [Linux Audio Users]     [Yosemite Info]     [Linux SCSI]

  Powered by Linux