From: David van Moolenbroek Date: Fri, 30 Sep 2016 21:55:33 +0000 (+0000) Subject: RMIB: add indirection support for sparse subtrees X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/zlib_tech.html?a=commitdiff_plain;h=0f03189a6a8bafe2a370ea990cf2cbb19da81b70;p=minix.git RMIB: add indirection support for sparse subtrees Normally, each RMIB subtree consists of an array of nodes, indexed by node identifier. In a sparsely filled subtree, most of the array is empty and just wasting memory. In that case, it may be beneficial to have a level of indirection, with an intermediate array containing pairs of node IDs and pointers to the actual nodes. This patch adds support for such indirection. For the use cases that inspired this patch, net.inet and net.inet6, the indirection shaves off a little under 16KB of memory from the TCP/IP service. Change-Id: Ic68ca3fee1a0f2032f77eef6df42728f9b9400e8 --- diff --git a/minix/include/minix/rmib.h b/minix/include/minix/rmib.h index a7a59125d..35ea2c079 100644 --- a/minix/include/minix/rmib.h +++ b/minix/include/minix/rmib.h @@ -45,6 +45,22 @@ struct rmib_newp; typedef ssize_t (*rmib_func_ptr)(struct rmib_call *, struct rmib_node *, struct rmib_oldp *, struct rmib_newp *); +/* + * Indirect node, used for sparse nodes. Sparse nodes are node-type nodes with + * the CTLFLAG_SPARSE flag set. A sparse node points not to an array of child + * nodes (using rnode_cptr), but to a array of {id,child pointer} elements + * (using rnode_icptr). At the cost of O(n) lookups, sparse nodes save memory. + * Currently for presentation reasons only, indirect lists must be sorted + * ascending by node identifiers. They may also not have ID duplicates, may not + * have NULL node pointers, and may not point to nodes with zero flags fields. + */ +#define CTLFLAG_SPARSE CTLFLAG_ROOT /* overloaded NetBSD flag */ + +struct rmib_indir { + unsigned int rindir_id; /* node identifier */ + struct rmib_node *rindir_node; /* pointer to actual node */ +}; + /* * The central structure for remote MIB nodes. This is essentially a somewhat * cut-down version of the node structure used within the MIB service. See the @@ -64,6 +80,7 @@ struct rmib_node { union pxfer_rnode_ptr_u { void *rpu_data; /* struct or string data pointer */ struct rmib_node *rpu_cptr; /* child node array */ + struct rmib_indir *rpu_icptr; /* indirect child node array */ } rnode_ptr_u; rmib_func_ptr rnode_func; /* handler function */ const char *rnode_name; /* node name string */ @@ -75,6 +92,7 @@ struct rmib_node { #define rnode_clen rnode_val_u.rvu_clen #define rnode_data rnode_ptr_u.rpu_data #define rnode_cptr rnode_ptr_u.rpu_cptr +#define rnode_icptr rnode_ptr_u.rpu_icptr /* Various macros to initialize nodes at compile time. */ #define RMIB_NODE(f,t,n,d) { \ @@ -85,6 +103,14 @@ struct rmib_node { .rnode_name = n, \ .rnode_desc = d \ } +#define RMIB_SNODE(f,t,n,d) { \ + .rnode_flags = CTLTYPE_NODE | CTLFLAG_READONLY | \ + CTLFLAG_PERMANENT | CTLFLAG_SPARSE | f, \ + .rnode_size = 0, \ + .rnode_icptr = t, \ + .rnode_name = n, \ + .rnode_desc = d \ +} #define RMIB_FUNC(f,s,fp,n,d) { \ .rnode_flags = CTLFLAG_PERMANENT | f, \ .rnode_size = s, \ diff --git a/minix/lib/libsys/rmib.c b/minix/lib/libsys/rmib.c index ac581b981..e4b686389 100644 --- a/minix/lib/libsys/rmib.c +++ b/minix/lib/libsys/rmib.c @@ -5,6 +5,8 @@ * though the copy here operates on slightly different data structures in order * to keep the implementation more lightweight. For clarification on many * aspects of the source code here, see the source code of the MIB service. + * One unique feature here is support for sparse nodes, which is needed for + * net.inet/inet6 as those are using subtrees with protocol-based identifiers. * * There is no way for this module to get to know about MIB service deaths * without possibly interfering with the main code of the service this module @@ -204,10 +206,12 @@ rmib_copyout_node(struct rmib_call * call, struct rmib_oldp * oldp, memset(&scn, 0, sizeof(scn)); /* - * The RMIB implementation does not overload flags, so it also need not + * We use CTLFLAG_SPARSE internally only. NetBSD uses these flags for + * different purposes. Either way, do not expose it to userland. * hide any of them from the user. */ - scn.sysctl_flags = SYSCTL_VERSION | rnode->rnode_flags; + scn.sysctl_flags = SYSCTL_VERSION | + (rnode->rnode_flags & ~CTLFLAG_SPARSE); scn.sysctl_num = id; strlcpy(scn.sysctl_name, rnode->rnode_name, sizeof(scn.sysctl_name)); scn.sysctl_ver = call->call_rootver; @@ -266,7 +270,7 @@ rmib_query(struct rmib_call * call, struct rmib_node * rparent, { struct sysctlnode scn; struct rmib_node *rnode; - unsigned int id; + unsigned int i, id; ssize_t r, off; /* If the user passed in version numbers, check them. */ @@ -290,11 +294,17 @@ rmib_query(struct rmib_call * call, struct rmib_node * rparent, /* Enumerate the child nodes of the given parent node. */ off = 0; - for (id = 0; id < rparent->rnode_size; id++) { - rnode = &rparent->rnode_cptr[id]; + for (i = 0; i < rparent->rnode_size; i++) { + if (rparent->rnode_flags & CTLFLAG_SPARSE) { + id = rparent->rnode_icptr[i].rindir_id; + rnode = rparent->rnode_icptr[i].rindir_node; + } else { + id = i; + rnode = &rparent->rnode_cptr[i]; - if (rnode->rnode_flags == 0) - continue; + if (rnode->rnode_flags == 0) + continue; + } if ((r = rmib_copyout_node(call, oldp, off, id, rnode)) < 0) return r; @@ -369,6 +379,34 @@ rmib_copyout_desc(struct rmib_call * call, struct rmib_oldp * oldp, return roundup2(size, sizeof(int32_t)); } +/* + * Look up a child node given a parent node and a child node identifier. + * Return a pointer to the child node if found, or NULL otherwise. The lookup + * procedure differs based on whether the parent node is sparse or not. + */ +static struct rmib_node * +rmib_lookup(struct rmib_node * rparent, unsigned int id) +{ + struct rmib_node *rnode; + struct rmib_indir *rindir; + unsigned int i; + + if (rparent->rnode_flags & CTLFLAG_SPARSE) { + rindir = rparent->rnode_icptr; + for (i = 0; i < rparent->rnode_size; i++, rindir++) + if (rindir->rindir_id == id) + return rindir->rindir_node; + } else { + if (id >= rparent->rnode_size) + return NULL; + rnode = &rparent->rnode_cptr[id]; + if (rnode->rnode_flags != 0) + return rnode; + } + + return NULL; +} + /* * Retrieve node descriptions in bulk, or retrieve a particular node's * description. @@ -379,7 +417,7 @@ rmib_describe(struct rmib_call * call, struct rmib_node * rparent, { struct sysctlnode scn; struct rmib_node *rnode; - unsigned int id; + unsigned int i, id; ssize_t r, off; if (newp != NULL) { @@ -390,10 +428,7 @@ rmib_describe(struct rmib_call * call, struct rmib_node * rparent, return EINVAL; /* Locate the child node. */ - if ((unsigned int)scn.sysctl_num >= rparent->rnode_size) - return ENOENT; - rnode = &rparent->rnode_cptr[scn.sysctl_num]; - if (rnode->rnode_flags == 0) + if ((rnode = rmib_lookup(rparent, scn.sysctl_num)) == NULL) return ENOENT; /* Descriptions of private nodes are considered private too. */ @@ -419,11 +454,17 @@ rmib_describe(struct rmib_call * call, struct rmib_node * rparent, /* Describe the child nodes of the given parent node. */ off = 0; - for (id = 0; id < rparent->rnode_size; id++) { - rnode = &rparent->rnode_cptr[id]; + for (i = 0; i < rparent->rnode_size; i++) { + if (rparent->rnode_flags & CTLFLAG_SPARSE) { + id = rparent->rnode_icptr[i].rindir_id; + rnode = rparent->rnode_icptr[i].rindir_node; + } else { + id = i; + rnode = &rparent->rnode_cptr[i]; - if (rnode->rnode_flags == 0) - continue; + if (rnode->rnode_flags == 0) + continue; + } if ((r = rmib_copyout_desc(call, oldp, off, id, rnode)) < 0) return r; @@ -732,10 +773,7 @@ rmib_call(const message * m_in) } /* Locate the child node. */ - if ((unsigned int)id >= rparent->rnode_size) - return ENOENT; - rnode = &rparent->rnode_cptr[id]; - if (rnode->rnode_flags == 0) + if ((rnode = rmib_lookup(rparent, id)) == NULL) return ENOENT; /* Check if access is permitted at this level. */ @@ -790,21 +828,29 @@ rmib_call(const message * m_in) * assigning the proper child length value to each of them. */ static void -rmib_init(struct rmib_node * rnode) +rmib_init(struct rmib_node * rparent) { - struct rmib_node *rchild; - unsigned int id; + struct rmib_node *rnode; + unsigned int i; + + for (i = 0; i < rparent->rnode_size; i++) { + if (rparent->rnode_flags & CTLFLAG_SPARSE) { + /* Indirect lists must be sorted ascending by ID. */ + assert(i == 0 || rparent->rnode_icptr[i].rindir_id > + rparent->rnode_icptr[i - 1].rindir_id); - rchild = rnode->rnode_cptr; + rnode = rparent->rnode_icptr[i].rindir_node; + } else { + rnode = &rparent->rnode_cptr[i]; - for (id = 0; id < rnode->rnode_size; id++, rchild++) { - if (rchild->rnode_flags == 0) - continue; + if (rnode->rnode_flags == 0) + continue; + } - rnode->rnode_clen++; + rparent->rnode_clen++; - if (SYSCTL_TYPE(rchild->rnode_flags) == CTLTYPE_NODE) - rmib_init(rchild); /* recurse */ + if (SYSCTL_TYPE(rnode->rnode_flags) == CTLTYPE_NODE) + rmib_init(rnode); /* recurse */ } } @@ -824,7 +870,7 @@ rmib_send_reg(int id) m.m_type = MIB_REGISTER; m.m_lsys_mib_register.root_id = id; m.m_lsys_mib_register.flags = SYSCTL_VERSION | - rnodes[id].rno_node->rnode_flags; + (rnodes[id].rno_node->rnode_flags & ~CTLFLAG_SPARSE); m.m_lsys_mib_register.csize = rnodes[id].rno_node->rnode_size; m.m_lsys_mib_register.clen = rnodes[id].rno_node->rnode_clen; m.m_lsys_mib_register.miblen = rnodes[id].rno_namelen;