* [9front] [PATCH] limits: added devlimit, process and memory limits
@ 2024-02-24 21:39 kitzman
0 siblings, 0 replies; only message in thread
From: kitzman @ 2024-02-24 21:39 UTC (permalink / raw)
To: 9front
Hello,
I wrote a patch which provides process and memory limits, and devlimit,
a device to manage the limit groups, patch which I want to share
with others.
I'd be thankful for a review. I don't expect it to be merged with the
front branch.
There are some (known) bugs described in the manual page.
Kind regards,
kitzman
---
diff 79a7b4ae59c2e0352b354cf719bc7ef4055f83ae c7a44c1564d2fab0b82b744927ed0f2be468fa1b
--- /dev/null
+++ b/sys/man/3/limit
@@ -1,0 +1,136 @@
+.TH LIMIT 3
+.SH NAME
+limit \- system limit interface
+.SH SYNOPSIS
+.nf
+.B bind #Λ /mnt/limit
+
+.B /mnt/limit/ctl
+.B /mnt/limit/clone
+.B /mnt/limit/switch
+.B /mnt/limit/status
+.BI /mnt/limit/groups/ \&...
+.fi
+.SH DESCRIPTION
+The
+.B #Λ
+device provides the interface to the system's limit mechanism.
+Each process has a limit group assigned. Groups are created as
+children of other groups, are represented by an id, and follow the
+restrictions imposed by their parents. A group is deleted the
+moment there are no more processes or segments referring to it.
+The group is
+.IR owned
+by the uname and gid of the creator process, and the attributes
+can be changed. The system's group, with id 1, imposes no
+restrictions and does not increase counters.
+.PP
+The root directory contains the
+.BR ctl ,
+.BR clone ,
+.BR switch ,
+.BR status
+files, and a directory named
+.BR groups .
+The current process' limit group is always presented in the root directory,
+and other groups, in the
+.B groups
+subdirectory, where they have the same files, except for the
+.B groups .
+.PP
+The
+.B ctl
+file controls the limit group's attributes, namely, the label, and the limits.
+Reading the file returns the limit group's id. The following commands are
+supported:
+.RS
+.TF "\fLmproc number \fR"
+.PD
+.
+.TP
+.BI "label " label
+Set the group's label; the labels need not be unique.
+.TP
+.BI "mlim " number
+Set the maximum amount of groups that can be
+created from this group.
+.TP
+.BI "mproc " number
+Set the maximum amount of processes that can be
+forked inside this group.
+.TP
+.BI "mpage " number
+Set the maximum total amount of pages that segments
+inside the limit group can have.
+.PD
+.RE
+.PP
+Reading the
+.B status
+file shows the group's attributes: the label (or an empty line
+if not set), the current limit restrictions on the left, and the counts,
+on the right.
+.PP
+To create a new group, the
+.B clone
+file should be opened. Reading from the fid returns the newly created
+group's id. This creates a new reference, and when the fid is clunked,
+the reference is destroyed. A reference is also created for the parent limit group.
+Thus, the opener's process limit group is updated.
+.PP
+To switch to an existing limit group, the
+.B switch
+should be opened.
+.PP
+The following limits exist and are imposed: limit limits, which represent the
+amount of maximum amount of limits which can be \"forked\" from this group,
+and process limits, which represent the maximum number of processes which can
+exist in the group. The kernel already imposes limits on the number of files
+which can be opened, so implementing that should be redundant.
+.SH EXAMPLES
+To create a new limit group and restrict the amount of sublimits and processes:
+.IP
+.EX
+% <>[10] /mnt/limit/clone
+% echo label mygroup >/mnt/limit/ctl
+% echo mlim 1 >/mnt/limit/ctl
+% echo mproc 20 >/mnt/limit/ctl
+.EE
+.PP
+Snippet to attach to an existing limit group, looking for a specific label:
+.IP
+.EX
+#!/bin/rc
+
+slabel=$1
+shift
+prog=$*
+
+bind '#Λ' /mnt/limit
+
+for (lgrpstat in `{walk -f /mnt/limit/groups | grep 'status$'}) {
+ lgrpdir=`{basename -d $lgrpstat}
+ lgrplabel=`{cat $lgrpstat | sed 1q}
+ if(~ $lgrplabel $slabel) {
+ <>[10] $lgrpdir/switch
+ exec $prog
+ }
+}
+
+echo lgrp $slabel not found
+exit nolgrp
+.EE
+.SH SOURCE
+.B /sys/src/9/port/limit.c
+.br
+.B /sys/src/9/port/devlimit.c
+.SH BUGS
+.PP
+Not all processes use
+.B pexit ,
+probably the ones which are created during boot. This
+has to be checked.
+.PP
+Spawning a lot of processes inside a limit group which is not
+the root group can cause a kernel panic related
+to runlock. Needs to be investigated.
--- a/sys/src/9/arm64/mkfile
+++ b/sys/src/9/arm64/mkfile
@@ -20,6 +20,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
page.$O\
parse.$O\
--- a/sys/src/9/bcm/mkfile
+++ b/sys/src/9/bcm/mkfile
@@ -21,6 +21,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
page.$O\
parse.$O\
--- a/sys/src/9/bcm64/mkfile
+++ b/sys/src/9/bcm64/mkfile
@@ -19,6 +19,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
page.$O\
parse.$O\
--- a/sys/src/9/cycv/mkfile
+++ b/sys/src/9/cycv/mkfile
@@ -20,6 +20,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
rebootcmd.$O\
page.$O\
--- a/sys/src/9/imx8/mkfile
+++ b/sys/src/9/imx8/mkfile
@@ -20,6 +20,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
page.$O\
parse.$O\
--- a/sys/src/9/kw/mkfile
+++ b/sys/src/9/kw/mkfile
@@ -21,6 +21,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
rebootcmd.$O\
page.$O\
--- a/sys/src/9/mt7688/mkfile
+++ b/sys/src/9/mt7688/mkfile
@@ -24,6 +24,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
page.$O\
parse.$O\
--- a/sys/src/9/mtx/mkfile
+++ b/sys/src/9/mtx/mkfile
@@ -18,6 +18,7 @@
edf.$O\
fault.$O\
iomap.$O\
+ limit.$O\
log.$O\
mul64fract.$O\
rebootcmd.$O\
--- a/sys/src/9/omap/mkfile
+++ b/sys/src/9/omap/mkfile
@@ -22,6 +22,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
rebootcmd.$O\
page.$O\
--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -25,6 +25,7 @@
edf.$O\
fault.$O\
iomap.$O\
+ limit.$O\
memmap.$O\
page.$O\
parse.$O\
--- a/sys/src/9/pc64/mkfile
+++ b/sys/src/9/pc64/mkfile
@@ -23,6 +23,7 @@
edf.$O\
fault.$O\
iomap.$O\
+ limit.$O\
memmap.$O\
page.$O\
parse.$O\
--- /dev/null
+++ b/sys/src/9/port/devlimit.c
@@ -1,0 +1,415 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+extern ulong kerndate;
+
+extern Lgrp *lgrptab[LIMMAX];
+extern Lock lgrptablock;
+
+Lgrp* getlgrp(int);
+void switchlgrp(Lgrp*);
+
+/* filesystem */
+enum {
+ LimitQidPos = 8,
+ LimitQidMask = 0xff,
+ LabelSize = 64,
+ StatusSize = 256,
+};
+
+#define QID(q) ((int)(q & LimitQidMask))
+#define LGID(q) ((int)(q >> LimitQidPos))
+#define LGPATH(i, q) (((uvlong)i << LimitQidPos) + (uvlong)q)
+
+enum
+{
+ Qroot,
+ Qctl,
+ Qclone,
+ Qswitch,
+ Qstatus,
+ Qgroups,
+};
+
+static Dirtab limitdir[] =
+{
+ ".", {Qroot, 0, QTDIR}, 0, DMDIR|0550,
+ "ctl", {Qctl}, 0, 0640,
+ "clone", {Qclone}, 0, 0440,
+ "switch", {Qswitch}, 0, 0440,
+ "status", {Qstatus}, 0, 0440,
+ "groups", {Qgroups, 0, QTDIR}, 0, DMDIR|0550,
+};
+
+// ctl commands
+enum {
+ CMlabel,
+ CMsetmlim,
+ CMsetmproc,
+ CMsetmpage,
+};
+
+static
+Cmdtab limitcmd[] = {
+ CMlabel, "label", 2,
+ CMsetmlim, "mlim", 2,
+ CMsetmproc, "mproc", 2,
+ CMsetmpage, "mpage", 2,
+};
+
+void
+limdir(Chan *c, Qid qid, char *n, vlong length, char *user, char *group, long perm, Dir *db)
+{
+ db->name = n;
+ if(c->flag&CMSG)
+ qid.type |= QTMOUNT;
+ db->qid = qid;
+ db->type = devtab[c->type]->dc;
+ db->dev = c->dev;
+ db->mode = perm;
+ db->mode |= qid.type << 24;
+ db->atime = seconds();
+ db->mtime = kerndate;
+ db->length = length;
+ db->uid = user;
+ db->gid = group;
+ db->muid = user;
+}
+
+static int
+limgen(Chan *c, char *name, Dirtab* tab, int ntab, int s, Dir *dp)
+{
+ Lgrp *l;
+ Qid q;
+ long perm;
+
+ int lgid = LGID(c->qid.path);
+ int i;
+
+ if(lgid)
+ l = getlgrp(lgid);
+ else
+ l = up->lgrp;
+ if(l == nil)
+ return -1;
+
+ /* device root */
+ if(s == DEVDOTDOT){
+ rlock(l);
+ if(lgid == 0) {
+ c->qid.vers = 1;
+ limdir(c, c->qid, "#λ", 0, l->uid, l->gid, (long)tab[Qroot].perm, dp);
+ } else {
+ limdir(c, tab[Qgroups].qid, tab[Qgroups].name, 0, l->uid, l->gid, (long)tab[Qgroups].perm, dp);
+ }
+ runlock(l);
+ return 1;
+ }
+
+ /* tab is part of every gen due to (i) and (ii) */
+ if(QID(c->qid.path) == Qgroups) goto groupsgen; // or not?
+ if(name) {
+ if(lgid != 0 && strcmp(name, tab[Qgroups].name) == 0)
+ return 0;
+ if(strcmp(name, tab[QID(c->qid.path)].name) == 0)
+ return -1;
+ for(i = 0; i < ntab; i++) {
+ if(strcmp(name, tab[i].name) == 0) {
+ rlock(l);
+ perm = tab[i].perm;
+ if(i == Qctl && (lgid == 1 || (lgid == 0 && up->lgrp->lgid == 1)))
+ perm = 0440;
+ mkqid(&q, LGPATH(lgid, i), 0, tab[i].qid.type);
+ limdir(c, q, name, 0, l->uid, l->gid, perm, dp);
+ runlock(l);
+ return 1;
+ }
+ }
+ } else {
+ if(s < ntab) {
+ if(lgid != 0 && s == Qgroups)
+ return 0;
+ if(QID(c->qid.path) == s)
+ return 0;
+ rlock(l);
+ perm = tab[s].perm;
+ if(s == Qctl && (lgid == 1 || (lgid == 0 && up->lgrp->lgid == 1)))
+ perm = 0440;
+ mkqid(&q, LGPATH(lgid, s), 0, tab[s].qid.type);
+ limdir(c, q, tab[s].name, 0, l->uid, l->gid, perm, dp);
+ runlock(l);
+ return 1;
+ }
+ }
+
+ /* the lgrp dirs are only part of Qgroups (i) and the dirs themselves (ii) */
+groupsgen:
+ if(QID(c->qid.path) == Qgroups && s < ntab)
+ return 0;
+ if(QID(c->qid.path) != Qgroups || (QID(c->qid.path) == Qroot && lgid == 0))
+ return -1;
+ if(name) {
+ i = atoi(name);
+ if(i > LIMMAX || i < 1)
+ return -1;
+ if(l = getlgrp(i)) {
+ rlock(l);
+ mkqid(&q, LGPATH(i, Qroot), 0, QTDIR);
+ limdir(c, q, name, 0, l->uid, l->gid, (long)tab[Qroot].perm, dp);
+ runlock(l);
+ return 1;
+ }
+ } else {
+ i = s - ntab + 1;
+ if(i > LIMMAX || i < 1)
+ return -1;
+ if(l = getlgrp(i)) {
+ rlock(l);
+ name = malloc(NUMSIZE);
+ snprint(name, NUMSIZE, "%d", s - ntab + 1);
+ mkqid(&q, LGPATH(i, Qroot), 0, QTDIR);
+ limdir(c, q, name, 0, l->uid, l->gid, (long)tab[Qroot].perm, dp);
+ runlock(l);
+ return 1;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static Chan*
+limattach(char *spec)
+{
+ return devattach(L'Λ', spec);
+}
+
+static Walkqid*
+limwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, limitdir, nelem(limitdir), limgen);
+}
+
+static int
+limstat(Chan *c, uchar *db, int n)
+{
+ return devstat(c, db, n, limitdir, nelem(limitdir), limgen);
+}
+
+static Chan*
+limopen(Chan *c, int omode)
+{
+ Chan *co;
+ Lgrp *l = up->lgrp;
+ int lgid = LGID(c->qid.path);
+
+ if(lgid)
+ l = getlgrp(lgid);
+ if(l == nil)
+ error(Enonexist);
+
+ if(c->qid.type & QTDIR)
+ if(omode != OREAD)
+ error(Eperm);
+
+ co = devopen(c, omode, limitdir, nelem(limitdir), limgen);
+
+ switch(QID(c->qid.path)) {
+ case Qclone:
+ l = newlgrp(l);
+ switchlgrp(l);
+ break;
+ case Qswitch:
+ switchlgrp(l);
+ break;
+ }
+
+ return co;
+}
+
+static void
+limclose(Chan *c)
+{
+ Lgrp *l = up->lgrp;
+ int lgid = LGID(c->qid.path);
+
+ if(lgid)
+ l = getlgrp(lgid);
+ if(l == nil)
+ error(Enonexist);
+}
+
+static void
+limremove(Chan*)
+{
+ error(Eperm);
+}
+
+static long
+limread(Chan *c, void *va, long n, vlong off)
+{
+ Lgrp *l = up->lgrp;
+ char *buf;
+ long m;
+ int lgid = LGID(c->qid.path);
+
+ if(lgid)
+ l = getlgrp(lgid);
+ if(l == nil)
+ error(Enonexist);
+
+ switch(QID(c->qid.path)){
+ case Qroot:
+ return devdirread(c, va, n, limitdir, nelem(limitdir), limgen);
+ case Qctl:
+ rlock(l);
+ m = readnum((ulong) off, va, n, l->lgid, NUMSIZE);
+ runlock(l);
+ return m;
+ break;
+ case Qclone:
+ case Qswitch:
+ return readstr((ulong) off, va, n, "");
+ break;
+ case Qstatus:
+ buf = malloc(StatusSize);
+ rlock(l);
+ if(l->label) m = snprint(buf, LabelSize + 1, "%s\n", l->label);
+ else m = snprint(buf, LabelSize + 1, "\n");
+ snprint(buf, StatusSize, "%s%*lud %*lud\n", buf, NUMSIZE-1, l->mlim, NUMSIZE-1, l->clim);
+ snprint(buf, StatusSize, "%s%*lud %*lud\n", buf, NUMSIZE-1, l->mproc, NUMSIZE-1, l->cproc);
+ snprint(buf, StatusSize, "%s%*lud %*lud\n", buf, NUMSIZE-1, l->mpage, NUMSIZE-1, l->cpage);
+ runlock(l);
+ m = readstr((ulong) off, va, n, buf);
+ free(buf);
+ return m;
+ case Qgroups:
+ if(lgid != 0)
+ error(Eperm);
+ return devdirread(c, va, n, limitdir, nelem(limitdir), limgen);
+ default:
+ error(Eperm);
+ break;
+ }
+}
+
+static long
+limwrite(Chan *c, void *va, long n, vlong)
+{
+ Lgrp *l = up->lgrp;
+ Cmdbuf *cb;
+ Cmdtab *ct;
+ char *label, *newm;
+ long m;
+ int lgid = LGID(c->qid.path);
+
+ if(lgid)
+ l = getlgrp(lgid);
+ if(l == nil)
+ error(Enonexist);
+
+ switch(QID(c->qid.path)){
+ case Qctl:
+ cb = parsecmd(va, n);
+ if(waserror()) {
+ free(cb);
+ nexterror();
+ }
+ ct = lookupcmd(cb, limitcmd, nelem(limitcmd));
+ if(ct == nil)
+ error(Ebadctl);
+
+ switch(ct->index) {
+ case CMlabel:
+ label = cb->f[1];
+ if(strlen(label) > LabelSize - 1)
+ error(Eperm);
+ wlock(l);
+ kstrdup(&l->label, label);
+ wunlock(l);
+ break;
+ case CMsetmlim:
+ newm = cb->f[1];
+ m = atoi(newm);
+ if(!m)
+ error(Ebadctl);
+ wlock(l);
+ l->mlim = m;
+ wunlock(l);
+ break;
+ case CMsetmproc:
+ newm = cb->f[1];
+ m = atoi(newm);
+ if(!m)
+ error(Ebadctl);
+ wlock(l);
+ l->mproc = m;
+ wunlock(l);
+ break;
+ case CMsetmpage:
+ newm = cb->f[1];
+ m = atoi(newm);
+ if(!m)
+ error(Ebadctl);
+ wlock(l);
+ l->mpage = m;
+ wunlock(l);
+ break;
+ default:
+ error(Ebadctl);
+ break;
+ }
+ free(cb);
+ poperror();
+ break;
+ default:
+ error(Eperm);
+ break;
+ }
+
+ return n;
+}
+
+Dev limitdevtab = {
+ L'Λ',
+ "limit",
+
+ devreset,
+ devinit,
+ devshutdown,
+ limattach,
+ limwalk,
+ limstat,
+ limopen,
+ devcreate,
+ limclose,
+ limread,
+ devbread,
+ limwrite,
+ devbwrite,
+ limremove,
+ devwstat,
+};
+
+/* helper functions */
+Lgrp*
+getlgrp(int lgid)
+{
+ Lgrp *l;
+ lock(&lgrptablock);
+ l = lgrptab[lgid - 1];
+ unlock(&lgrptablock);
+ return l;
+}
+
+void
+switchlgrp(Lgrp *l)
+{
+ Lgrp *o = up->lgrp;
+ incref(l);
+ up->lgrp = l;
+ closelgrp(o);
+}
--- /dev/null
+++ b/sys/src/9/port/limit.c
@@ -1,0 +1,216 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+Lgrp *lgrptab[LIMMAX] = { nil };
+Lock lgrptablock;
+
+/* helper functions */
+void
+addchild(Lgrp *parent, Lgrp *new)
+{
+ wlock(parent);
+ incref(parent);
+ if(parent->submax == 0 || parent->subgrp == nil) {
+ parent->submax = LIMINISUB;
+ parent->subgrp = malloc(LIMINISUB);
+ }
+ if(parent->subcount + 1 > parent->submax) {
+ parent->subgrp = realloc(parent->subgrp, parent->submax * 2);
+ parent->submax *= 2;
+ }
+ parent->subgrp[parent->subcount] = new;
+ parent->subcount++;
+ wunlock(parent);
+}
+
+void
+removechild(Lgrp *old)
+{
+ Lgrp *parent;
+ int i;
+
+ if(old->parent) parent = old->parent; else return;
+ wlock(parent);
+ for(i = 0; i < parent->subcount; i++)
+ if(parent->subgrp[i] == old)
+ break;
+ if(i == parent->subcount) {
+ wunlock(parent);
+ return;
+ }
+
+ for(; i < parent->subcount - 1; i++)
+ parent->subgrp[i] = parent->subgrp[i + 1];
+ parent->subcount--;
+
+ wunlock(parent);
+ closelgrp(parent);
+}
+
+/* kernel functions */
+Lgrp*
+newlgrp(Lgrp *parent)
+{
+ int lgid;
+
+ lock(&lgrptablock);
+ for(lgid = 0; lgid < LIMMAX; lgid++)
+ if(lgrptab[lgid] == nil)
+ break;
+
+ if(waserror()) {
+ unlock(&lgrptablock);
+ nexterror();
+ }
+ if(lgid == LIMMAX)
+ error("system has reached the maximum amount of limits");
+ lgid++;
+
+ if(parent) inclimit(LTLIM, parent, 1);
+
+ Lgrp* l = malloc(sizeof(Lgrp));
+ l->lgid = lgid;
+ if(parent) addchild(parent, l);
+ if(parent) rlock(parent);
+ wlock(l);
+ lgrptab[lgid - 1] = l;
+ poperror();
+ unlock(&lgrptablock);
+ l->clim = 0;
+ l->cproc = 0;
+ l->cpage = 0;
+ l->mlim = 0;
+ l->mproc = 0;
+ l->mpage = 0;
+ l->subcount = 0;
+ l->submax = 0;
+ l->subgrp = nil;
+ if(parent) {
+ l->mlim = parent->mlim;
+ l->mproc = parent->mproc;
+ l->mpage = parent->mpage;
+ }
+ if(up->user) {
+ kstrdup(&l->uid, up->user);
+ kstrdup(&l->gid, up->user);
+ } else {
+ kstrdup(&l->uid, eve);
+ kstrdup(&l->gid, eve);
+ }
+ l->parent = parent;
+ l->subgrp = nil;
+ if(parent) runlock(parent);
+ wunlock(l);
+
+ return l;
+}
+
+void
+inclimit(int limit, Lgrp *l, int q)
+{
+ Lgrp *c;
+ ulong cval, mval;
+
+ for(c = l; c; c = c->parent) {
+ if(c->lgid == 1)
+ break;
+ rlock(c);
+ switch(limit) {
+ case LTLIM:
+ cval = c->clim;
+ mval = c->mlim;
+ break;
+ case LTPROC:
+ cval = c->cproc;
+ mval = c->mproc;
+ break;
+ case LTPAGE:
+ cval = c->cpage;
+ mval = c->mpage;
+ break;
+ default:
+ runlock(c);
+ error("unknown limit type");
+ }
+ if(mval && cval + q > mval) {
+ runlock(c);
+ error("limit reached");
+ }
+ runlock(c);
+ }
+ /* small amounts over the limit can't hurt */
+ for(c = l; c; c = c->parent) {
+ if(c->lgid == 1)
+ break;
+ wlock(c);
+ switch(limit) {
+ case LTLIM:
+ c->clim += q;
+ break;
+ case LTPROC:
+ c->cproc += q;
+ break;
+ case LTPAGE:
+ c->cpage += q;
+ break;
+ default:
+ wunlock(c);
+ error("unknown limit type");
+ }
+ wunlock(c);
+ }
+}
+
+void
+declimit(int limit, Lgrp *l, int q)
+{
+ Lgrp *c;
+
+ for(c = l; c; c = c->parent) {
+ if(c->lgid == 1)
+ break;
+ wlock(c);
+ switch(limit) {
+ case LTLIM:
+ c->clim -= q;
+ break;
+ case LTPROC:
+ c->cproc -= q;
+ break;
+ case LTPAGE:
+ c->cpage -= q;
+ break;
+ default:
+ wunlock(c);
+ error("unknown limit type");
+ }
+ wunlock(c);
+ }
+}
+
+void
+closelgrp(Lgrp* l)
+{
+ if(decref(l) == 0) {
+ wlock(l);
+ if(waserror()) {
+ wunlock(l);
+ nexterror();
+ }
+ if(l->parent) declimit(LTLIM, l->parent, 1);
+ removechild(l);
+ wunlock(l);
+ poperror();
+ lock(&lgrptablock);
+ lgrptab[l->lgid - 1] = nil;
+ unlock(&lgrptablock);
+ if(l->label) free(l->label);
+ free(l->uid);
+ free(l->gid);
+ free(l);
+ }
+}
--- a/sys/src/9/port/portdat.h
+++ b/sys/src/9/port/portdat.h
@@ -14,6 +14,7 @@
typedef struct Image Image;
typedef struct Log Log;
typedef struct Logflag Logflag;
+typedef struct Lgrp Lgrp;
typedef struct Mntcache Mntcache;
typedef struct Mount Mount;
typedef struct Mntrah Mntrah;
@@ -433,6 +434,7 @@
Pte *ssegmap[SSEGMAPSIZE];
Sema sema;
ulong mark; /* portcountrefs */
+ Lgrp *lgrp;
};
struct Segio
@@ -538,6 +540,31 @@
DELTAFD = 20 /* incremental increase in Fgrp.fd's */
};
+struct Lgrp
+{
+ Ref;
+ RWlock;
+ int lgid;
+ ulong clim, mlim;
+ ulong cproc, mproc;
+ ulong cpage, mpage;
+ char* label;
+ char* uid;
+ char* gid;
+ Lgrp* parent;
+ uint subcount, submax;
+ Lgrp** subgrp;
+};
+
+enum
+{
+ LIMMAX = 4096,
+ LIMINISUB = 8,
+ LTLIM = 0,
+ LTPROC,
+ LTPAGE,
+};
+
struct Palloc
{
Lock;
@@ -692,6 +719,7 @@
Egrp *egrp; /* Environment group */
Fgrp *fgrp; /* File descriptor group */
Rgrp *rgrp; /* Rendez group */
+ Lgrp *lgrp; /* Limit group */
Fgrp *closingfgrp; /* used during teardown */
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -34,6 +34,7 @@
void ccloseq(Chan*);
void closeegrp(Egrp*);
void closefgrp(Fgrp*);
+void closelgrp(Lgrp*);
void closepgrp(Pgrp*);
void closergrp(Rgrp*);
long clrfpintr(void);
@@ -55,6 +56,7 @@
void cupdate(Chan*, uchar*, int, vlong);
void cwrite(Chan*, uchar*, int, vlong);
uintptr dbgpc(Proc*);
+void declimit(int, Lgrp*, int);
long decref(Ref*);
int decrypt(void*, void*, int);
void delay(int);
@@ -141,6 +143,7 @@
void iunlock(Lock*);
ulong imagecached(void);
ulong imagereclaim(int);
+void inclimit(int, Lgrp*, int);
long incref(Ref*);
void init0(void);
void initseg(void);
@@ -210,6 +213,7 @@
int needpages(void*);
Chan* newchan(void);
int newfd(Chan*, int);
+Lgrp* newlgrp(Lgrp*);
Mhead* newmhead(Chan*);
Mount* newmount(Chan*, int, char*);
Page* newpage(int, Segment **, uintptr);
--- a/sys/src/9/port/proc.c
+++ b/sys/src/9/port/proc.c
@@ -1243,6 +1243,7 @@
Egrp *egrp;
Rgrp *rgrp;
Pgrp *pgrp;
+ Lgrp *lgrp;
Chan *dot;
void (*pt)(Proc*, int, vlong);
@@ -1262,6 +1263,8 @@
up->rgrp = nil;
pgrp = up->pgrp;
up->pgrp = nil;
+ lgrp = up->lgrp;
+ up->lgrp = nil;
dot = up->dot;
up->dot = nil;
qunlock(&up->debug);
@@ -1276,6 +1279,10 @@
cclose(dot);
if(pgrp != nil)
closepgrp(pgrp);
+ if(lgrp != nil) {
+ declimit(LTPROC, lgrp, 1);
+ closelgrp(lgrp);
+ }
if(up->parentpid == 0){
if(exitstr == nil)
--- a/sys/src/9/port/segment.c
+++ b/sys/src/9/port/segment.c
@@ -56,6 +56,10 @@
s = malloc(sizeof(Segment));
if(s == nil)
error(Enomem);
+ if(waserror()) {
+ if(s) free(s);
+ nexterror();
+ }
s->ref = 1;
s->type = type;
s->base = base;
@@ -70,13 +74,17 @@
return s;
}
+ if(up && up->lgrp) s->lgrp = up->lgrp;
+ if(s->lgrp != nil) {
+ inclimit(LTPAGE, s->lgrp, size);
+ incref(s->lgrp);
+ }
+
mapsize = ROUND(size, PTEPERTAB)/PTEPERTAB;
if(mapsize > nelem(s->ssegmap)){
s->map = malloc(mapsize*sizeof(Pte*));
- if(s->map == nil){
- free(s);
+ if(s->map == nil)
error(Enomem);
- }
s->mapsize = mapsize;
}
else{
@@ -84,6 +92,7 @@
s->mapsize = nelem(s->ssegmap);
}
+ poperror();
return s;
}
@@ -122,6 +131,11 @@
free(s->map);
}
+ if(s->lgrp != nil) {
+ declimit(LTPAGE, s->lgrp, s->size);
+ closelgrp(s->lgrp);
+ }
+
if(s->profile != nil)
free(s->profile);
@@ -409,13 +423,15 @@
return s->base;
qlock(s);
+ if(waserror()) {
+ qunlock(s);
+ nexterror();
+ }
/* We may start with the bss overlapping the data */
if(addr < s->base) {
- if(seg != BSEG || up->seg[DSEG] == nil || addr < up->seg[DSEG]->base) {
- qunlock(s);
+ if(seg != BSEG || up->seg[DSEG] == nil || addr < up->seg[DSEG]->base)
error(Enovmem);
- }
addr = s->base;
}
@@ -427,13 +443,13 @@
* to-be-freed address space may have been passed to the kernel
* already by another proc and is past the validaddr stage.
*/
- if(s->ref > 1){
- qunlock(s);
+ if(s->ref > 1)
error(Einuse);
- }
mfreeseg(s, newtop, (s->top-newtop)/BY2PG);
s->top = newtop;
s->size = newsize;
+ if(s->lgrp)
+ declimit(LTPAGE, s->lgrp, s->size - newsize);
qunlock(s);
flushmmu();
return 0;
@@ -443,33 +459,39 @@
ns = up->seg[i];
if(ns == nil || ns == s)
continue;
- if(newtop > ns->base && s->base < ns->top) {
- qunlock(s);
+ if(newtop > ns->base && s->base < ns->top)
error(Esoverlap);
- }
}
- if(newsize > (SEGMAPSIZE*PTEPERTAB)) {
- qunlock(s);
+ if(newsize > (SEGMAPSIZE*PTEPERTAB))
error(Enovmem);
- }
+
mapsize = ROUND(newsize, PTEPERTAB)/PTEPERTAB;
if(mapsize > s->mapsize){
map = malloc(mapsize*sizeof(Pte*));
- if(map == nil){
- qunlock(s);
+ if(map == nil)
error(Enomem);
+ if(waserror()) {
+ free(map);
+ nexterror();
}
+ if(s->lgrp)
+ inclimit(LTPAGE, s->lgrp, newsize - s->size);
memmove(map, s->map, s->mapsize*sizeof(Pte*));
if(s->map != s->ssegmap)
free(s->map);
s->map = map;
s->mapsize = mapsize;
+ poperror();
+ } else {
+ if(s->lgrp)
+ inclimit(LTPAGE, s->lgrp, newsize - s->size);
}
s->top = newtop;
s->size = newsize;
qunlock(s);
+ poperror();
return 0;
}
--- a/sys/src/9/port/sysproc.c
+++ b/sys/src/9/port/sysproc.c
@@ -96,6 +96,8 @@
if((p = newproc()) == nil)
error("no procs");
+ inclimit(LTPROC, up->lgrp, 1);
+
qlock(&up->debug);
qlock(&p->debug);
@@ -211,6 +213,10 @@
p->egrp = up->egrp;
incref(p->egrp);
}
+
+ /* Limit group */
+ p->lgrp = up->lgrp;
+ incref(p->lgrp);
procfork(p);
--- a/sys/src/9/port/userinit.c
+++ b/sys/src/9/port/userinit.c
@@ -33,6 +33,7 @@
up->egrp->ref = 1;
up->fgrp = dupfgrp(nil);
up->rgrp = newrgrp();
+ up->lgrp = newlgrp(nil);
/*
* These are o.k. because rootinit is null.
--- a/sys/src/9/ppc/mkfile
+++ b/sys/src/9/ppc/mkfile
@@ -19,6 +19,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
log.$O\
rebootcmd.$O\
page.$O\
--- a/sys/src/9/sgi/mkfile
+++ b/sys/src/9/sgi/mkfile
@@ -26,6 +26,7 @@
edf.$O\
fault.$O\
fptrap.$O\
+ limit.$O\
mul64fract.$O\
page.$O\
parse.$O\
--- a/sys/src/9/teg2/mkfile
+++ b/sys/src/9/teg2/mkfile
@@ -23,6 +23,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
rebootcmd.$O\
page.$O\
--- a/sys/src/9/xen/mkfile
+++ b/sys/src/9/xen/mkfile
@@ -23,6 +23,7 @@
edf.$O\
fault.$O\
iomap.$O\
+ limit.$O\
page.$O\
parse.$O\
pgrp.$O\
--- a/sys/src/9/zynq/mkfile
+++ b/sys/src/9/zynq/mkfile
@@ -20,6 +20,7 @@
dev.$O\
edf.$O\
fault.$O\
+ limit.$O\
mul64fract.$O\
rebootcmd.$O\
page.$O\
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2024-02-24 21:40 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-24 21:39 [9front] [PATCH] limits: added devlimit, process and memory limits kitzman
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).