ref: 9b14a2f3b443a8bd615551cb2320829d134650bd
parent: ce768a70c40c96c6e6dbd1018306a122a4eeec43
author: kvik <kvik@a-b.xyz>
date: Sat May 29 08:11:49 EDT 2021
Fix merge
--- /dev/null
+++ b/qmap.c
@@ -1,0 +1,155 @@
+/*
+ * Encode (Dir.type, Dir.dev, Dir.Qid.path) into Qid.path.
+ * We do this by making an educated guess on how the input
+ * bits are going to be laid out in the common case which
+ * allows a direct encoding scheme to be used for most
+ * files; otherwise we must resort to storing a full map
+ * entry into a table.
+ *
+ * The bits of output Qid.path are assigned as follows:
+ *
+ * For directly encoded paths the bit 63 is set to zero,
+ * otherwise it is set to one. This splits the path space
+ * in half.
+ *
+ * For table-mapped paths the rest of the bits are simply
+ * counting from 0 to 2^63-1. There is no relation between
+ * input and output bits; the entire input tuple and the
+ * output Qid.path are stored into a map.
+ *
+ * The direct encoding scheme works by assuming several
+ * properties on the structure of input tuple fields.
+ * First, it is evident that the Plan 9 kernel uses a very
+ * small number of device types (Dir.type), the most common
+ * of which is devmnt (M). Furthermore, the file servers
+ * mounted through this type are the ones serving the most
+ * files and are most likely to be served by unionfs.
+ * Therefore we dedicate the entire space to this type
+ * of device.
+ * Secondly, the device instance (Dir.dev) is always a
+ * simple counter value, as are the Qid.paths of most
+ * file servers. This lets us simply drop some number
+ * of upper bits from each to make them fit into our space.
+ *
+ * We do this as follows:
+ * - 32-bit Dir.dev is reduced to its lower 28 bits.
+ * - 64-bit Dir.qid.path is reduced to its lower 35 bits.
+ *
+ * If type isn't M, or if the reduction of dev or path
+ * results in a loss of information, that is, if some of
+ * the cut bits are set; then the tuple must be table-mapped.
+ *
+ * The amount of bits allocated to each encoded field
+ * was determined by assuming that it is not very common for
+ * most file servers to host many billions of files, while a
+ * busy server will easily make many millions of mounts during
+ * its uptime.
+ * Making the kernel reuse device instance numbers could
+ * be done to help the matter significantly, letting us use
+ * almost the entire space just for paths.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "unionfs.h"
+
+#define mask(v) ((1ull << v) - 1)
+
+enum {
+ Qmapsize = 1024,
+
+ MapBit = 63,
+ DevBits = 28,
+ PathBits = 35,
+};
+
+typedef struct Qmap Qmap;
+struct Qmap {
+ ushort type;
+ uint dev;
+ uvlong path;
+ uvlong qpath;
+ Qmap *next;
+};
+
+RWLock qidmaplock;
+Qmap *qidmap[Qmapsize];
+
+uvlong
+qhash(Dir *d)
+{
+ uvlong h;
+ usize i;
+ uchar *key;
+
+ h = 14695981039346656037ULL;
+ key = (uchar*)&d->type;
+ for(i = 0; i < sizeof(d->type); i++){
+ h ^= key[i];
+ h *= 1099511628211ULL;
+ }
+ key = (uchar*)&d->dev;
+ for(i = 0; i < sizeof(d->dev); i++){
+ h ^= key[i];
+ h *= 1099511628211ULL;
+ }
+ key = (uchar*)&d->qid.path;
+ for(i = 0; i < sizeof(d->qid.path); i++){
+ h ^= key[i];
+ h *= 1099511628211ULL;
+ }
+ return h;
+}
+
+Qid
+qencode(Dir *d)
+{
+ ushort type;
+ uint dev;
+ uvlong path;
+ Qid qid;
+
+ qid.vers = d->qid.vers;
+ qid.type = d->qid.type;
+ type = d->type;
+ dev = d->dev;
+ path = d->qid.path;
+ if(type == 'M'
+ && (dev & ~mask(DevBits)) == 0
+ && (path & ~mask(PathBits)) == 0){
+ qid.path = 0
+ | (0ull << MapBit)
+ | ((uvlong)dev) << (63 - DevBits)
+ | path;
+ return qid;
+ }
+
+ static uvlong qpath = 0;
+ Qmap *q;
+ uvlong h;
+
+ h = qhash(d) % Qmapsize;
+ rlock(&qidmaplock);
+ for(q = qidmap[h]; q != nil; q = q->next){
+ if(q->type == type && q->dev == dev && q->path == path){
+ qid.path = q->qpath;
+ runlock(&qidmaplock);
+ return qid;
+ }
+ }
+ runlock(&qidmaplock);
+ q = emalloc(sizeof(Qmap));
+ q->type = type;
+ q->dev = dev;
+ q->path = path;
+ wlock(&qidmaplock);
+ q->qpath = 0
+ | (1ull << MapBit)
+ | qpath++;
+ q->next = qidmap[h];
+ qidmap[h] = q;
+ qid.path = q->qpath;
+ wunlock(&qidmaplock);
+ return qid;
+}