namecache: limit the number of delegations

to prevent delegations from claiming all available slots in the attribute cache, nfs41_name_cache_insert() now returns ERROR_TOO_MANY_OPEN_FILES when the number of delegated entries reaches 50% of the cache capacity.  see comment 'delegations and cache feedback' in name_cache.c for details

when nfs41_open() sees this error from nfs41_name_cache_insert(), it calls new function nfs41_client_delegation_return_lru() to return the least-recently-used delegation and, if successful, loops back to nfs41_name_cache_insert().  nfs41_client_delegation_return_lru() returns NFS4ERR_BADHANDLE if all delegations are currently in use (associated with an existing open), in which case nfs41_open() returns the newly-granted delegation

Signed-off-by: Casey Bodley <cbodley@citi.umich.edu>
This commit is contained in:
Casey Bodley 2011-08-19 12:04:30 -04:00 committed by unknown
parent a0d4403a99
commit 7b07dcebb8
4 changed files with 145 additions and 27 deletions

View file

@ -53,8 +53,38 @@ enum {
* lookups over the wire. a name cache entry is negative when its attributes
* pointer is NULL. negative entries are created by three functions:
* nfs41_name_cache_remove(), _insert() when called with NULL for the fh and
* attributes, and _rename() for the source entry
* attributes, and _rename() for the source entry */
/* delegations and cache feedback
*
* delegations provide a guarantee that no links or attributes will change
* without notice. the name cache takes advantage of this by preventing
* delegated entries from being removed on NAME_CACHE_EXPIRATION, though
* they're still removed when a parent is invalidated. the attribute cache
* holds an extra reference on delegated entries to prevent their removal
* entirely, until the delegation is returned.
* this extra reference presents a problem when the number of delegations
* approaches the maximum number of attribute cache entries. when there are
* not enough available entries to store the parent directories, every lookup
* results in a name cache miss, and cache performance degrades significantly.
* the solution is to provide feedback via nfs41_name_cache_insert() when
* delegations reach a certain percent of the cache capacity. the error code
* ERROR_TOO_MANY_OPEN_FILES, chosen arbitrarily for this case, instructs the
* caller to return an outstanding delegation before caching a new one.
*/
static __inline bool_t is_delegation(
IN enum open_delegation_type4 type)
{
return type == OPEN_DELEGATE_READ || type == OPEN_DELEGATE_WRITE;
}
static __inline bool_t deleg_entry_limit(
IN uint32_t delegations,
IN uint32_t max_entries)
{
/* limit the number of delegations to 50% of the cache capacity */
return delegations >= max_entries / 2;
}
/* attribute cache */
@ -283,7 +313,7 @@ static void attr_cache_update(
}
}
if (delegation == OPEN_DELEGATE_READ || delegation == OPEN_DELEGATE_WRITE)
if (is_delegation(delegation))
entry->delegated = TRUE;
}
@ -335,6 +365,7 @@ struct nfs41_name_cache {
struct list_entry exp_entries; /* list of entries by expiry */
uint32_t expiration;
uint32_t entries;
uint32_t delegations;
uint32_t max_entries;
SRWLOCK lock;
};
@ -468,8 +499,10 @@ static int name_cache_entry_update(
attr_cache_update(entry->attributes, info, delegation);
/* hold a reference as long as we have the delegation */
if (delegation == OPEN_DELEGATE_READ || delegation == OPEN_DELEGATE_WRITE)
if (is_delegation(delegation)) {
attr_cache_entry_ref(&cache->attributes, entry->attributes);
cache->delegations++;
}
/* keep the entry from expiring */
if (entry->attributes->delegated)
@ -865,6 +898,13 @@ int nfs41_name_cache_insert(
goto out_unlock;
}
/* limit the number of delegations to prevent attr cache starvation */
if (is_delegation(delegation) && deleg_entry_limit(
cache->delegations, cache->max_entries)) {
status = ERROR_TOO_MANY_OPEN_FILES;
goto out_unlock;
}
/* an empty path or component implies the root entry */
if (path == NULL || name == NULL || name->len == 0) {
/* create the root entry if it doesn't exist */
@ -906,13 +946,15 @@ out_unlock:
return status;
out_err_deleg:
if (delegation == OPEN_DELEGATE_READ || delegation == OPEN_DELEGATE_WRITE) {
if (is_delegation(delegation)) {
/* we still need a reference to the attributes for the delegation */
struct attr_cache_entry *attributes;
status = attr_cache_find_or_create(&cache->attributes,
info->fileid, &attributes);
if (status == NO_ERROR)
attr_cache_update(attributes, info, delegation);
else
status = ERROR_TOO_MANY_OPEN_FILES;
}
goto out_unlock;
}
@ -958,6 +1000,7 @@ int nfs41_name_cache_delegreturn(
/* release the reference from name_cache_entry_update() */
attributes->delegated = FALSE;
attr_cache_entry_deref(&cache->attributes, attributes);
cache->delegations--;
status = NO_ERROR;
out_unlock: