Plan 9 from Bell Labs’s /usr/web/sources/contrib/blstuart/θfs/hash.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 * Copyright (c) 2013, Coraid, Inc.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * 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.
 *     * Neither the name of Coraid 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 CORAID 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 <u.h>
#include <libc.h>
#include <thread.h>
#include <fcall.h>
#include <9p.h>
#include "dat.h"

static uvlong np2q, nq2m;
static int maxp2q, maxq2m;
static int p2qcoll, q2mcoll;

/* FNV hash */
static ulong
pathhash(char *path)
{
	uchar *p;
	ulong h;

	h = 2166136261UL;
	for(p = (uchar *)path; *p; ++p)
		h = (h ^ *p) * 16777619;
	return h % super.nht;
}

static ulong
qidhash(uvlong qpath)
{
	return qpath % super.nht;
}

static uvlong
qoffset(ulong bucket)
{
	return BlkSize * (super.nhashblk + 1) + bucket * sizeof(uvlong);
}

static PQMap *
nextpq(PQMap *pq)
{
	uchar *p;

	p = (uchar *)pq;
	p += pq->plen + offsetof(PQMap, pname[0]);
	return (PQMap *)p;
}

uvlong
p2q(int fd, char *path, int create)
{
	PQMap *pq, *pend;
	uchar *p;
	uvlong *uvp;
	uvlong hlist, next, qpath;
	ulong bucket;
	int plen, nsearch, n;

	++np2q;
	plen = strlen(path);
	bucket = pathhash(path);
	if(fd == -1)
		n = cread(&hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong));
	else
		n = spread(fd, &hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong));
	if(n < 0)
		sysfatal("cread failure: %r");
	if(hlist == 0) {
		if(create) {
			hlist = allocblock();
			if(hlist == 0)
				return 0;
			p = cbclean(hlist);
			pq = (PQMap *)p;
			qpath = super.qgen++ | ((uvlong)TFile << 60);
			pq->qpath = qpath;
			pq->plen = plen;
			memmove(pq->pname, path, plen);
			cbwrite(hlist);
			brelease(hlist);
			uvp = cbread(bucket / NPerBlk + 1);
			uvp[bucket % NPerBlk] = hlist;
			cbwrite(bucket / NPerBlk + 1);
			brelease(bucket / NPerBlk + 1);
			return qpath;
		}
		return 0;
	}
	nsearch = 1;
	p = nil;	/* make the compiler happy */
	if(fd != -1)
		p = θmalloc(BlkSize);
	while(hlist) {
		if(fd == -1) {
			p = cbread(hlist);
			if(p == nil) {
				fprint(2, "cbread failed on block %ulld\n", hlist);
				return 0;
			}
		}
		else
			spread(fd, p, BlkSize, hlist * BlkSize);
		pend = (PQMap *)(p + BlkSize);
		--pend;
		for(pq = (PQMap *)p; pq < pend && pq->qpath != 0; pq = nextpq(pq)) {
			if(plen == pq->plen && memcmp(path, pq->pname, plen) == 0)
				goto found;
			++nsearch;
		}
		next = *((uvlong *)(p + BlkSize - sizeof(uvlong)));
		if(next == 0 && create)
			goto addone;
		if(fd == -1)
			brelease(hlist);
		hlist = next;
	}
	if(fd != -1)
		free(p);
	return 0;
found:
	if(nsearch > maxp2q)
		maxp2q = nsearch;
	next = pq->qpath;
	if(fd == -1)
		brelease(hlist);
	else
		free(p);
	if(create)
		return 0;
	return next;
addone:
	for(pq = (PQMap *)p; pq < pend && pq->qpath != 0; pq = nextpq(pq)) ;
	if(pq != (PQMap *)p)
		++p2qcoll;
	if(pq >= pend) {
fprint(2, "HUH?");
		next = allocblock();
		if(next == 0)
			return 0;
		*((uvlong *)(p + BlkSize - sizeof(uvlong))) = next;
		cbwrite(hlist);
		brelease(hlist);
		hlist = next;
		p = cbclean(hlist);
		pq = (PQMap *)p;
	}
	qpath = super.qgen++ | ((uvlong)TFile << 60);
	pq->qpath = qpath;
	pq->plen = plen;
	memmove(pq->pname, path, plen);
	if(hlist != 0) {		/* shouldn't be possible, but just to be safe */
		cbwrite(hlist);
		brelease(hlist);
	}
	return qpath;
}

void
setqhash(uvlong qpath, uvlong midx)
{
	ulong bucket;

	bucket = qidhash(qpath);
	cwrite(&midx, sizeof(uvlong), qoffset(bucket));
}

uvlong
q2m(int fd, uvlong qpath, int create)
{
	uvlong val;
	uvlong first, meta;
	ulong bucket;
	int nsearch, n;

	if(qpath == 0)
		return 0;
	++nq2m;
	bucket = qidhash(qpath);
	if(fd == -1)
		n = cread(&first, sizeof(uvlong), qoffset(bucket));
	else
		n= spread(fd, &first, sizeof(uvlong), qoffset(bucket));
	if(n < 0)
		sysfatal("cread failure: %r");
	if(first == 0) {
		if(create) {
			meta = setmetaint(0, "qhnext", nil, 0);
			//setqhash(qpath, meta);
			return meta;
		}
		return 0;
	}
	nsearch = 1;
	for(meta = first; meta; ) {
		if(getmetaint(fd, meta, "qpath", &val) != MTnone && val == qpath)
			break;
		if(getmetaint(fd, meta, "qhnext", &meta) == MTnone)
			meta = 0;
		++nsearch;
	}
	if(meta == 0) {
		if(create) {
			meta = setmetaint(0, "qhnext", nil, first);
			//setqhash(qpath, meta);
		}
	}
	else
		if(nsearch > maxq2m)
			maxq2m = nsearch;
	return meta;
}

void
rehashone(uvlong qpath, char *from, char *to)
{
	PQMap *pq, *rend;
	uchar *p;
	uvlong *uvp;
	uvlong hlist, next;
	ulong bucket;
	int plen;

	rmp(from);
	plen = strlen(to);
	bucket = pathhash(to);
	if(cread(&hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong)) < 0)
		sysfatal("cread failure: %r");
	if(hlist == 0) {
		hlist = allocblock();
		if(hlist == 0)
			return;
		p = cbclean(hlist);
		pq = (PQMap *)p;
		pq->qpath = qpath;
		pq->plen = plen;
		memmove(pq->pname, to, plen);
		cbwrite(hlist);
		brelease(hlist);
		uvp = cbread(bucket / NPerBlk + 1);
		uvp[bucket % NPerBlk] = hlist;
		cbwrite(bucket / NPerBlk + 1);
		brelease(bucket / NPerBlk + 1);
		return;
	}
	while(hlist) {
		p = cbread(hlist);
		rend = (PQMap *)(p + BlkSize);
		--rend;
		for(pq = (PQMap *)p; pq < rend; pq = nextpq(pq))
			if(plen == pq->plen && memcmp(to, pq->pname, plen) == 0)
				goto found;
		next = *((uvlong *)(p + BlkSize - sizeof(uvlong)));
		if(next == 0)
			goto addone;
		brelease(hlist);
		hlist = next;
	}
	return;
found:
	fprint(2, "Impossible! Repath destination exists\n");
	brelease(hlist);
	return;
addone:
	for(pq = (PQMap *)p; pq < rend && pq->qpath != 0; pq = nextpq(pq)) ;
	if(pq >= rend) {
		next = allocblock();
		*((uvlong *)(p + BlkSize - sizeof(uvlong))) = next;
		cbwrite(hlist);
		brelease(hlist);
		if(next == 0)
			return;
		hlist = next;
		p = cbclean(hlist);
		pq = (PQMap *)p;
	}
	pq->qpath = qpath;
	pq->plen = plen;
	memmove(pq->pname, to, plen);
	cbwrite(hlist);
	brelease(hlist);
}

void
rehashpath(uvlong qpath, char *from, char *to)
{
	char *f, *t, *name;
	uvlong cqid, meta;

	meta = q2m(-1, qpath, 0);
	if(meta != 0 && getmetaint(-1, meta, "child", &cqid) != MTnone) {
		while(cqid != 0) {
			meta = q2m(-1, cqid, 0);
			if(meta == 0)
				break;
			name = getmetastr(-1, meta, "name");
			f = smprint("%s/%s", from, name);
			t = smprint("%s/%s", to, name);
			free(name);
			rehashpath(cqid, f, t);
			free(f);
			free(t);
			if(getmetaint(-1, meta, "sib", &cqid) == MTnone)
				break;
		}
	}
	rehashone(qpath, from, to);
}

static PQMap *
rmpath(PQMap *full, PQMap *victim)
{
	PQMap *next, *last;
	PQMap *rend;
	int plen;

	rend = (PQMap *)((char *)full + BlkSize - sizeof(uvlong));
	for(last = victim; last < rend - 1 && last->plen > 0; last = nextpq(last)) ;
	/*
	 * last now points to the start of the first empty path/qid map slot
	 */
	plen = victim->plen + offsetof(PQMap, pname[0]);
	next = nextpq(victim);
	memmove(victim, next, (char *)rend - (char *)next);
	/*
	 * now last has moved up by plen bytes
	 */
	last = (PQMap *)((char *)last - plen);
	memset(last, 0, (char *)rend - (char *)last);
	return last;
}

void
rmp(char *path)
{
	PQMap *pq, *rend, *last;
	uchar *p;
	uvlong hlist, next;
	ulong bucket;
	int plen;

	plen = strlen(path);
	if(plen == 0)
		return;
	bucket = pathhash(path);
	if(cread(&hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong)) < 0)
		sysfatal("cread failure: %r");
	while(hlist) {
		p = cbread(hlist);
		rend = (PQMap *)(p + BlkSize);
		--rend;
		for(pq = (PQMap *)p; pq < rend; pq = nextpq(pq))
			if(plen == pq->plen && memcmp(path, pq->pname, plen) == 0)
				goto found;
		next = *((uvlong *)(p + BlkSize - sizeof(uvlong)));
		brelease(hlist);
		hlist = next;
	}
	return;
found:
	while(hlist) {
		last = rmpath((PQMap *)p, pq);
		next = *((uvlong *)(p + BlkSize - sizeof(uvlong)));
		if(next != 0) {
			p = cbread(next);
			pq = (PQMap *)p;
			if(pq->plen == 0 || pq->plen > (char *)rend - (char *)last) {
				brelease(next);
				next = 0;
			}
			else
				memmove(last, pq, pq->plen + offsetof(PQMap, pname[0]));
		}
		cbwrite(hlist);
		brelease(hlist);
		hlist = next;
	}
}

void
rmq(uvlong qpath, uvlong victim)
{
	uvlong prev, meta, next;
	ulong bucket;

	if(qpath == 0)
		return;
	bucket = qidhash(qpath);
	if(cread(&meta, sizeof(uvlong), qoffset(bucket)) < 0)
		sysfatal("cread failure: %r");
	if(meta == victim) {
		if(getmetaint(-1, meta, "qhnext", &next) == MTnone)
			next = 0;
		if(cwrite(&next, sizeof(uvlong), qoffset(bucket)) < 0)
			sysfatal("cwrite failure: %r");
		return;
	}
	for(prev = meta; prev; ) {
		if(getmetaint(-1, prev, "qhnext", &meta) == MTnone)
			meta = 0;
		if(meta == victim) {
			if(getmetaint(-1, victim, "qhnext", &next) == MTnone)
				next = 0;
			setmetaint(prev, "qhnext", nil, next);
			return;
		}
		prev = meta;		
	}
}

static char hstatbuf[1024];

char *
prhstat(void)
{
	char *p, *e;

	p = hstatbuf;
	e = p + nelem(hstatbuf);
	p = seprint(p, e, "Hash stats:\n");
	p = seprint(p, e, "np2q: %ulld\n", np2q);
	p = seprint(p, e, "p2qcoll: %ud\n", p2qcoll);
	p = seprint(p, e, "maxp2q: %ud\n", maxp2q);
	p = seprint(p, e, "nq2m: %ulld\n", nq2m);
	p = seprint(p, e, "q2mcoll: %ud\n", q2mcoll);
	seprint(p, e, "maxq2m: %ud\n", maxq2m);
	return hstatbuf;
}

void
showphash(int fd, char *path)
{
	uvlong hlist;
	ulong bucket;

	bucket = pathhash(path);
	cread(&hlist, sizeof(uvlong), BlkSize + bucket * sizeof(uvlong));
	fprint(fd, "%s: bucket:%uld hlist:%ulld\n", path, bucket, hlist);
}

void
fixpaths(int fd)
{
	PQMap *pq, *pend;
	uvlong *hb;
	uchar *p;
	char *path;
	uvlong hlist, next;
	ulong bucket;
	int i, j;

	fprint(fd, "Checking for dangling path names\n");
	for(bucket = 0, i = 0; i < super.nhashblk; ++i) {
		hb = cbread(i + 1);
		for(j = 0; j < BlkSize / sizeof(uvlong) && bucket < super.nht; ++j, ++bucket) {
			if(bucket % 100000 == 0)
				fprint(fd, ".");
restart:
			hlist = hb[j];
			while(hlist) {
				p = cbread(hlist);
				if(p == nil) {
					fprint(fd, "hlist block read failure in fixpaths\n");
					return;
				}
				pend = (PQMap *)(p + BlkSize);
				--pend;
				for(pq = (PQMap *)p; pq < pend && pq->qpath != 0; pq = nextpq(pq)) {
					if(q2m(-1, pq->qpath, 0) == 0) {
						path = θmalloc(pq->plen + 1);
						memmove(path, pq->pname, pq->plen);
						fprint(fd, "removing dangling path %s\n", path);
						rmp(path);
						free(path);
						brelease(hlist);
						goto restart;
					}
				}
				next = *((uvlong *)(p + BlkSize - sizeof(uvlong)));
				brelease(hlist);
				hlist = next;
			}
		}
		brelease(i + 1);
	}
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.