Advanced Namespace Tools blog 15 February 2017

Implementing /srv Namespaces Part One

The initial work is adding the data structures that will be used, and writing the non-devsrv code. Some of this may need to be changed as the "stub" code becomes active. For a first pass, what we want to do is create data structures and interfaces that are unused and leave the current behavior intact. At the present moment, I have succeeded in hacking in code which mostly parallels that used by rfork(RFCENVG).

The definition of Struct Srv was moved from devsrv.c to portdat.h, and a new struct Sgrp was added to portdat.h:

struct Sgrp
{
	Ref;
	Srv	*srvgrp;
};

A pointer to an Sgrp was added to the Proc struct in portdat.h:

Sgrp	*sgrp;		/* Srv group */

A definition of an RFCSRVG flag was added to the rfork flags enum in portdat.h, and will need to be added to /sys/include/libc.h:

RFCSRVG		= (1<<9),

In proc.c, the initial sgrp pointer is set to nil in newproc():

p->sgrp = nil;

In proc.c, the sgrp is cleaned up during pexit():

sgrp = up->sgrp;
up->sgrp = nil;
[...]
if(sgrp != nil)
	closesgrp(sgrp);

In sysproc.c, setting up the process sgrp is handled according to the flags to rfork:

/* The first RFPROC == 0 branch is rfork without making a new proc */
if(flag&RFPROC == 0){
[ handling of other flags omitted ]
	if(flag & RFCSRVG) {
		osg = up->sgrp;
		up->sgrp = smalloc(sizeof(Sgrp));
		up->sgrp->ref = 1;
		closesgrp(osg);
	}
	return 0;
}
/* Srv group - in the branch where we have a new proc */
if(flag & RFCSRVG) {
	p->sgrp = smalloc(sizeof(Sgrp));
	p->sgrp->ref = 1;
} else {
	if(up->sgrp == nil){
		up->sgrp = smalloc(sizeof(Sgrp));
		up->sgrp->ref = 1;
	}
	p->sgrp = up->sgrp;
	incref(p->sgrp);
}

In devsrv.c, a do-nothing closesgrp() function was added:

void
closesgrp(Sgrp *sg)
{
	return;
}

Testing the First Changes

Testing was simple enough - build a new kernel, boot it, and see if the world explodes. On the first attempt, the world did explode, because I hadn't written the "else" branch correctly in sysproc.c. I added some debugging prints along with fixing it, and the next boot produced normal behavior, apart from the spam of kernel prints I had added. I saved a log of the prints (though the kernel's message buffer was too small to contain all of them) and commented them out. I did notice that it seemed that occasionally, the "if(sgrp != nil) closesgrp(sgrp)" test wasn't being passed. The intention is that every proc will have a valid sgrp, so this may be a bug I will have to come back to. Here's a sample of some very junky output:

about to set up proc sgrp no RFCSRVG flagsetting sgrp = up->sgrp in pexitabout to call closegrp in pexitclosesgrp
returned from closegrp in pexitsetting p->sgrp = nil
about to set up proc sgrp no RFCSRVG flagsetting sgrp = up->sgrp in pexitabout to call closegrp in pexitclosesgrp
returned from closegrp in pexitsetting sgrp = up->sgrp in pexitabout to call closegrp in pexitclosesgrp
returned from closegrp in pexitsetting p->sgrp = nil
about to set up proc sgrp no RFCSRVG flagsetting p->sgrp = nil
about to set up proc sgrp no RFCSRVG flagsetting p->sgrp = nil
about to set up proc sgrp no RFCSRVG flagsetting sgrp = up->sgrp in pexitabout to call closegrp in pexitreturned from closegrp in pexitsetting sgrp = up->sgrp in pexitabout to call closegrp in pexitreturned from closegrp in pexitsetting sgrp = up->sgrp in pexitabout to call closegrp in pexitreturned from closegrp in pexitsetting sgrp = up->sgrp in pexitabout to call closegrp in pexitreturned from closegrp in pexitsetting p->sgrp = nil
about to set up proc sgrp no RFCSRVG flagsetting sgrp = up->sgrp in pexitabout to call closegrp in pexitclosesgrp
returned from closegrp in pexitsetting p->sgrp = nil

I would have expected that "closegrp" followed by a newline would always be printed after "abouttocall closegrp in pexit" but as can be seen from the above, this is not always the case. There may be a valid reason for this, but I want to check to make sure there is no way a process can end up without a pointer to an sgrp.

The Next Step

To progress towards the full implementation, it is time to start making changes to the actual devsrv.c code. In particular, the first thing to do is to make everything actually use the sgrp pointer to a srv. Right now, devsrv uses a single linked list of Srv structures with "srv" acting as a pointer to the most recently added. I will be attempting to replicate the current behavior, but with the reference to the linked list coming through up->sgrp->srvgrp.

After some additional work with debugging prints to verify my understanding of what was going on, I got sucked into an interesting issue: I created a function srvprocset() to verify what was going on with the data structures and make sure that up->sgrp and up->sgrp->srvgrp were behaving sensibly. Things seemed ok, except for mysterious appearances of nil during srvclose().

if(up->sgrp->srvgrp == nil){
	if(srv == nil)
		print(" default srv == nil ");
	print("proc %s %ld srvgrp == nil\n", up->args, up->pid);
	up->sgrp->srvgrp = srv;
}
print(" proc %s %ld srvgrp == %p ", up->args, up->pid, up->sgrp);

It turns out that srvclose() was being called during the "cleanup" portion of process exit, after the pointers were being set to nil. I'm not sure yet if this will prove to be an issue that needs a workaround, or if it is just an artifact of default cleanup routines. Further testing is required.