Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 19 May 2009 11:01:11 -0400 (EDT)
From:      Rick Macklem <rmacklem@uoguelph.ca>
To:        freebsd-arch@freebsd.org
Subject:   nfs server resource exhaustion (before it's too late)
Message-ID:  <Pine.GSO.4.63.0905191058380.13432@muncher.cs.uoguelph.ca>

next in thread | raw e-mail | index | archive | help
In the experimental nfs server (sys/fs/nfsserver), there is a function that,
when it returns non-zero, causes the server to reply NFSERR_DELAY to the
client so that it will try the RPC again a little later. (Or, for NFSv2 over
UDP, which doesn't have NFSERR_DELAY, it simply drops the request and assumes
the client will timeout and try it again.) This is intended to avoid the
situation where the server cannot m_get/m_getcl/malloc part way through
processing a request, due to resource exhaustion. (The malloc case isn't
as critical, since I have high water marks set to limit the # of allocations
for the various NFSv4 state related structures that are malloc'd.)

At this point the function is just a stub:
int
nfsrv_mallocmget_limit(void)
{

 	return (0);
}

I just took a quick look (I don't know anything about UMA, except that it
seems to be used by m_get and m_getcl) and this was what I could think of
for doing the above on FreeBSD8. (It wasn't obvious to me if there
was a limit set for the various zones used by malloc(), so I didn't
include them.

int
nfsrv_mallocmget_limit(void)
{
 	u_int32_t pages, maxpages;

 	uma_zone_get_pagecnts(zone_clust, &pages, &maxpages);
 	if (maxpages != 0 && (pages * 12 / 10) > maxpages)
 		return (1);
 	return (0);
}

At this point, the only function I could see that would return the above
information is sysctl_vm_zone_stats() and it looks like overkill. Also,
the function needs to be relatively low overhead, since it is called for
every nfs rpc the server gets so I thought this might be ok?
/* added to sys/vm/uma_core.c */
void
uma_zone_get_pagecnts(uma_zone_t zone, u_int32_t *pages, u_int32_t *maxpages)
{
 	uma_keg_t keg;

 	ZONE_LOCK(zone);
 	keg = zone_first_keg(zone);
 	*pages = keg->uk_pages;
 	*maxpages = keg->uk_maxpages;
 	ZONE_UNLOCK(zone);
}

Does this look reasonable or can anyone suggest a better alternative?

Thanks in advance for any suggestions, rick




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.GSO.4.63.0905191058380.13432>