From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <9front-bounces@9front.inri.net> X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-0.6 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 Received: from 9front.inri.net (9front.inri.net [168.235.81.73]) by inbox.vuxu.org (Postfix) with ESMTP id BFE402588A for ; Sat, 24 Feb 2024 22:40:33 +0100 (CET) Received: from layka.disroot.org ([178.21.23.139]) by 9front; Sat Feb 24 16:39:25 -0500 2024 Received: from localhost (localhost [127.0.0.1]) by disroot.org (Postfix) with ESMTP id 9799D44E3A for <9front@9front.org>; Sat, 24 Feb 2024 22:39:23 +0100 (CET) X-Virus-Scanned: SPAM Filter at disroot.org Received: from layka.disroot.org ([127.0.0.1]) by localhost (disroot.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Tgmwsvetk2l7 for <9front@9front.org>; Sat, 24 Feb 2024 22:39:21 +0100 (CET) Message-ID: <68B3F9B3F7B10654977EFAF8EAA6BC10@midgaard.lan> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=disroot.org; s=mail; t=1708810761; bh=svHJ2TtxF3PPgb6YP8s624/gPzgPKNCABNw1V5bynhs=; h=To:Subject:Date:From; b=WZzTrDHfx6dpH2lEDpNPf74RXn0uH4ghvKN8IKFtWFHgiYE6uwL+YMVxTF7fdJcTE FCnbcptCelfU+3/d0Iu/J2a8fanS81meXTDIWceLyD+QIxdcMkaeKE7EbonRgM0VNk xuGTD7kaXhwt3kWCjdCibNfNc8lPMI2XJLwsraXQRamdUhggdGPWhS4V4857y8O+PT vdsGclTy5bgGEreBlh+VLcTHfh0KFaFSALWPztQ96qixab/yuvmtoPpeKbfUJtIXv3 Mwil2WazOcXK/Shbb1ljE+CsaYVWbR2i0PogeBUBga4uOE3/ih2zPFJnNDMvq74dj2 ZQL2cJxRdvDMA== To: 9front@9front.org Date: Sat, 24 Feb 2024 22:39:15 +0100 From: kitzman@disroot.org MIME-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable List-ID: <9front.9front.org> List-Help: X-Glyph: ➈ X-Bullshit: object-oriented SSL CMS-aware method-scale realtime-java frontend Subject: [9front] [PATCH] limits: added devlimit, process and memory limits Reply-To: 9front@9front.org Precedence: bulk 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 c7a44c1564d2fab0b82b744927e= d0f2be468fa1b --- /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 #=CE=9B /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 #=CE=9B +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 directo= ry, +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 l= imits. +Reading the file returns the limit group's id. The following commands ar= e +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 l= imit 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 represen= t the +amount of maximum amount of limits which can be \"forked\" from this gro= up, +and process limits, which represent the maximum number of processes whic= h can +exist in the group. The kernel already imposes limits on the number of f= iles +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 pro= cesses: +.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 lab= el: +.IP +.EX +#!/bin/rc + +slabel=3D$1 +shift +prog=3D$* + +bind '#=CE=9B' /mnt/limit + +for (lgrpstat in `{walk -f /mnt/limit/groups | grep 'status$'}) { + lgrpdir=3D`{basename -d $lgrpstat} + lgrplabel=3D`{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 =3D 8, + LimitQidMask =3D 0xff, + LabelSize =3D 64, + StatusSize =3D 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[] =3D +{ + ".", {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[] =3D { + 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 =3D n; + if(c->flag&CMSG) + qid.type |=3D QTMOUNT; + db->qid =3D qid; + db->type =3D devtab[c->type]->dc; + db->dev =3D c->dev; + db->mode =3D perm; + db->mode |=3D qid.type << 24; + db->atime =3D seconds(); + db->mtime =3D kerndate; + db->length =3D length; + db->uid =3D user; + db->gid =3D group; + db->muid =3D user; +} + +static int +limgen(Chan *c, char *name, Dirtab* tab, int ntab, int s, Dir *dp) +{ + Lgrp *l; + Qid q; + long perm; + + int lgid =3D LGID(c->qid.path); + int i; + + if(lgid) + l =3D getlgrp(lgid); + else + l =3D up->lgrp; + if(l =3D=3D nil) + return -1; + + /* device root */ + if(s =3D=3D DEVDOTDOT){ + rlock(l); + if(lgid =3D=3D 0) { + c->qid.vers =3D 1; + limdir(c, c->qid, "#=CE=BB", 0, l->uid, l->gid, (long)tab[Qroot].perm= , dp); + } else { + limdir(c, tab[Qgroups].qid, tab[Qgroups].name, 0, l->uid, l->gid, (lo= ng)tab[Qgroups].perm, dp); + } + runlock(l); + return 1; + } + + /* tab is part of every gen due to (i) and (ii) */ + if(QID(c->qid.path) =3D=3D Qgroups) goto groupsgen; // or not? + if(name) { + if(lgid !=3D 0 && strcmp(name, tab[Qgroups].name) =3D=3D 0) + return 0; + if(strcmp(name, tab[QID(c->qid.path)].name) =3D=3D 0) + return -1; + for(i =3D 0; i < ntab; i++) { + if(strcmp(name, tab[i].name) =3D=3D 0) { + rlock(l); + perm =3D tab[i].perm; + if(i =3D=3D Qctl && (lgid =3D=3D 1 || (lgid =3D=3D 0 && up->lgrp->lg= id =3D=3D 1))) + perm =3D 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 !=3D 0 && s =3D=3D Qgroups) + return 0; + if(QID(c->qid.path) =3D=3D s) + return 0; + rlock(l); + perm =3D tab[s].perm; + if(s =3D=3D Qctl && (lgid =3D=3D 1 || (lgid =3D=3D 0 && up->lgrp->lgi= d =3D=3D 1))) + perm =3D 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) =3D=3D Qgroups && s < ntab) + return 0; + if(QID(c->qid.path) !=3D Qgroups || (QID(c->qid.path) =3D=3D Qroot && l= gid =3D=3D 0)) + return -1; + if(name) { + i =3D atoi(name); + if(i > LIMMAX || i < 1) + return -1; + if(l =3D 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 =3D s - ntab + 1; + if(i > LIMMAX || i < 1) + return -1; + if(l =3D getlgrp(i)) { + rlock(l); + name =3D 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'=CE=9B', 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 =3D up->lgrp; + int lgid =3D LGID(c->qid.path); + + if(lgid) + l =3D getlgrp(lgid); + if(l =3D=3D nil) + error(Enonexist); + + if(c->qid.type & QTDIR) + if(omode !=3D OREAD) + error(Eperm); + + co =3D devopen(c, omode, limitdir, nelem(limitdir), limgen); + + switch(QID(c->qid.path)) { + case Qclone: + l =3D newlgrp(l); + switchlgrp(l); + break; + case Qswitch: + switchlgrp(l); + break; + } + + return co; +} + +static void +limclose(Chan *c) +{ + Lgrp *l =3D up->lgrp; + int lgid =3D LGID(c->qid.path); + + if(lgid) + l =3D getlgrp(lgid); + if(l =3D=3D nil) + error(Enonexist); +} + +static void +limremove(Chan*) +{ + error(Eperm); +} + +static long +limread(Chan *c, void *va, long n, vlong off) +{ + Lgrp *l =3D up->lgrp; + char *buf; + long m; + int lgid =3D LGID(c->qid.path); + + if(lgid) + l =3D getlgrp(lgid); + if(l =3D=3D nil) + error(Enonexist); + + switch(QID(c->qid.path)){ + case Qroot: + return devdirread(c, va, n, limitdir, nelem(limitdir), limgen); + case Qctl: + rlock(l); + m =3D 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 =3D malloc(StatusSize); + rlock(l); + if(l->label) m =3D snprint(buf, LabelSize + 1, "%s\n", l->label); + else m =3D snprint(buf, LabelSize + 1, "\n"); + snprint(buf, StatusSize, "%s%*lud %*lud\n", buf, NUMSIZE-1, l->mlim, N= UMSIZE-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 =3D readstr((ulong) off, va, n, buf); + free(buf); + return m; + case Qgroups: + if(lgid !=3D 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 =3D up->lgrp; + Cmdbuf *cb; + Cmdtab *ct; + char *label, *newm; + long m; + int lgid =3D LGID(c->qid.path); + + if(lgid) + l =3D getlgrp(lgid); + if(l =3D=3D nil) + error(Enonexist); + + switch(QID(c->qid.path)){ + case Qctl: + cb =3D parsecmd(va, n); + if(waserror()) { + free(cb); + nexterror(); + } + ct =3D lookupcmd(cb, limitcmd, nelem(limitcmd)); + if(ct =3D=3D nil) + error(Ebadctl); + + switch(ct->index) { + case CMlabel: + label =3D cb->f[1]; + if(strlen(label) > LabelSize - 1) + error(Eperm); + wlock(l); + kstrdup(&l->label, label); + wunlock(l); + break; + case CMsetmlim: + newm =3D cb->f[1]; + m =3D atoi(newm); + if(!m) + error(Ebadctl); + wlock(l); + l->mlim =3D m; + wunlock(l); + break; + case CMsetmproc: + newm =3D cb->f[1]; + m =3D atoi(newm); + if(!m) + error(Ebadctl); + wlock(l); + l->mproc =3D m; + wunlock(l); + break; + case CMsetmpage: + newm =3D cb->f[1]; + m =3D atoi(newm); + if(!m) + error(Ebadctl); + wlock(l); + l->mpage =3D m; + wunlock(l); + break; + default: + error(Ebadctl); + break; + } + free(cb); + poperror(); + break; + default: + error(Eperm); + break; + } + + return n; +} + +Dev limitdevtab =3D { + L'=CE=9B', + "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 =3D lgrptab[lgid - 1]; + unlock(&lgrptablock); + return l; +} + +void +switchlgrp(Lgrp *l) +{ + Lgrp *o =3D up->lgrp; + incref(l); + up->lgrp =3D 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] =3D { nil }; +Lock lgrptablock; + +/* helper functions */ +void +addchild(Lgrp *parent, Lgrp *new) +{ + wlock(parent); + incref(parent); + if(parent->submax =3D=3D 0 || parent->subgrp =3D=3D nil) { + parent->submax =3D LIMINISUB; + parent->subgrp =3D malloc(LIMINISUB); + } + if(parent->subcount + 1 > parent->submax) { + parent->subgrp =3D realloc(parent->subgrp, parent->submax * 2); + parent->submax *=3D 2; + } + parent->subgrp[parent->subcount] =3D new; + parent->subcount++; + wunlock(parent); +} + +void +removechild(Lgrp *old) +{ + Lgrp *parent; + int i; + + if(old->parent) parent =3D old->parent; else return; + wlock(parent); + for(i =3D 0; i < parent->subcount; i++) + if(parent->subgrp[i] =3D=3D old) + break; + if(i =3D=3D parent->subcount) { + wunlock(parent); + return; + } + + for(; i < parent->subcount - 1; i++) + parent->subgrp[i] =3D parent->subgrp[i + 1]; + parent->subcount--; =09 + + wunlock(parent); + closelgrp(parent); +} + +/* kernel functions */ +Lgrp* +newlgrp(Lgrp *parent) +{ + int lgid; + + lock(&lgrptablock); + for(lgid =3D 0; lgid < LIMMAX; lgid++) + if(lgrptab[lgid] =3D=3D nil) + break; + + if(waserror()) { + unlock(&lgrptablock); + nexterror(); + } + if(lgid =3D=3D LIMMAX) + error("system has reached the maximum amount of limits"); + lgid++; + + if(parent) inclimit(LTLIM, parent, 1); + + Lgrp* l =3D malloc(sizeof(Lgrp)); + l->lgid =3D lgid; + if(parent) addchild(parent, l); + if(parent) rlock(parent); + wlock(l); + lgrptab[lgid - 1] =3D l; + poperror(); + unlock(&lgrptablock); + l->clim =3D 0; + l->cproc =3D 0; + l->cpage =3D 0; + l->mlim =3D 0; + l->mproc =3D 0; + l->mpage =3D 0; + l->subcount =3D 0; + l->submax =3D 0; + l->subgrp =3D nil; + if(parent) { + l->mlim =3D parent->mlim; + l->mproc =3D parent->mproc; + l->mpage =3D 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 =3D parent; + l->subgrp =3D nil; + if(parent) runlock(parent); + wunlock(l); + + return l; +} + +void +inclimit(int limit, Lgrp *l, int q) +{ + Lgrp *c; + ulong cval, mval; + + for(c =3D l; c; c =3D c->parent) { + if(c->lgid =3D=3D 1) + break; + rlock(c); + switch(limit) { + case LTLIM: + cval =3D c->clim; + mval =3D c->mlim; + break; + case LTPROC: + cval =3D c->cproc; + mval =3D c->mproc; + break; + case LTPAGE: + cval =3D c->cpage; + mval =3D 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 =3D l; c; c =3D c->parent) { + if(c->lgid =3D=3D 1) + break; + wlock(c); + switch(limit) { + case LTLIM: + c->clim +=3D q; + break; + case LTPROC: + c->cproc +=3D q; + break; + case LTPAGE: + c->cpage +=3D q; + break; + default: + wunlock(c); + error("unknown limit type"); + } + wunlock(c); + } +} + +void +declimit(int limit, Lgrp *l, int q) +{ + Lgrp *c; + + for(c =3D l; c; c =3D c->parent) { + if(c->lgid =3D=3D 1) + break; + wlock(c); + switch(limit) { + case LTLIM: + c->clim -=3D q; + break; + case LTPROC: + c->cproc -=3D q; + break; + case LTPAGE: + c->cpage -=3D q; + break; + default: + wunlock(c); + error("unknown limit type"); + } + wunlock(c); + } +} + +void +closelgrp(Lgrp* l) +{ + if(decref(l) =3D=3D 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] =3D 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; }; =20 struct Segio @@ -538,6 +540,31 @@ DELTAFD =3D 20 /* incremental increase in Fgrp.fd's */ }; =20 +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 =3D 4096, + LIMINISUB =3D 8, + LTLIM =3D 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 */ =20 Fgrp *closingfgrp; /* used during teardown */ =20 --- 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); =20 @@ -1262,6 +1263,8 @@ up->rgrp =3D nil; pgrp =3D up->pgrp; up->pgrp =3D nil; + lgrp =3D up->lgrp; + up->lgrp =3D nil; dot =3D up->dot; up->dot =3D nil; qunlock(&up->debug); @@ -1276,6 +1279,10 @@ cclose(dot); if(pgrp !=3D nil) closepgrp(pgrp); + if(lgrp !=3D nil) { + declimit(LTPROC, lgrp, 1); + closelgrp(lgrp); + } =20 if(up->parentpid =3D=3D 0){ if(exitstr =3D=3D nil) --- a/sys/src/9/port/segment.c +++ b/sys/src/9/port/segment.c @@ -56,6 +56,10 @@ s =3D malloc(sizeof(Segment)); if(s =3D=3D nil) error(Enomem); + if(waserror()) { + if(s) free(s); + nexterror(); + } s->ref =3D 1; s->type =3D type; s->base =3D base; @@ -70,13 +74,17 @@ return s; } =20 + if(up && up->lgrp) s->lgrp =3D up->lgrp; + if(s->lgrp !=3D nil) { + inclimit(LTPAGE, s->lgrp, size); + incref(s->lgrp); + } + mapsize =3D ROUND(size, PTEPERTAB)/PTEPERTAB; if(mapsize > nelem(s->ssegmap)){ s->map =3D malloc(mapsize*sizeof(Pte*)); - if(s->map =3D=3D nil){ - free(s); + if(s->map =3D=3D nil) error(Enomem); - } s->mapsize =3D mapsize; } else{ @@ -84,6 +92,7 @@ s->mapsize =3D nelem(s->ssegmap); } =20 + poperror(); return s; } =20 @@ -122,6 +131,11 @@ free(s->map); } =20 + if(s->lgrp !=3D nil) { + declimit(LTPAGE, s->lgrp, s->size); + closelgrp(s->lgrp); + } + if(s->profile !=3D nil) free(s->profile); =20 @@ -409,13 +423,15 @@ return s->base; =20 qlock(s); + if(waserror()) { + qunlock(s); + nexterror(); + } =20 /* We may start with the bss overlapping the data */ if(addr < s->base) { - if(seg !=3D BSEG || up->seg[DSEG] =3D=3D nil || addr < up->seg[DSEG]->= base) { - qunlock(s); + if(seg !=3D BSEG || up->seg[DSEG] =3D=3D nil || addr < up->seg[DSEG]->= base) error(Enovmem); - } addr =3D s->base; } =20 @@ -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 =3D newtop; s->size =3D newsize; + if(s->lgrp) + declimit(LTPAGE, s->lgrp, s->size - newsize); qunlock(s); flushmmu(); return 0; @@ -443,33 +459,39 @@ ns =3D up->seg[i]; if(ns =3D=3D nil || ns =3D=3D s) continue; - if(newtop > ns->base && s->base < ns->top) { - qunlock(s); + if(newtop > ns->base && s->base < ns->top) error(Esoverlap); - } } =20 - if(newsize > (SEGMAPSIZE*PTEPERTAB)) { - qunlock(s); + if(newsize > (SEGMAPSIZE*PTEPERTAB)) error(Enovmem); - } + mapsize =3D ROUND(newsize, PTEPERTAB)/PTEPERTAB; if(mapsize > s->mapsize){ map =3D malloc(mapsize*sizeof(Pte*)); - if(map =3D=3D nil){ - qunlock(s); + if(map =3D=3D 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 !=3D s->ssegmap) free(s->map); s->map =3D map; s->mapsize =3D mapsize; + poperror(); + } else { + if(s->lgrp) + inclimit(LTPAGE, s->lgrp, newsize - s->size); } =20 s->top =3D newtop; s->size =3D newsize; qunlock(s); + poperror(); return 0; } =20 --- a/sys/src/9/port/sysproc.c +++ b/sys/src/9/port/sysproc.c @@ -96,6 +96,8 @@ if((p =3D newproc()) =3D=3D nil) error("no procs"); =20 + inclimit(LTPROC, up->lgrp, 1); + qlock(&up->debug); qlock(&p->debug); =20 @@ -211,6 +213,10 @@ p->egrp =3D up->egrp; incref(p->egrp); } + + /* Limit group */ + p->lgrp =3D up->lgrp; + incref(p->lgrp); =20 procfork(p); =20 --- a/sys/src/9/port/userinit.c +++ b/sys/src/9/port/userinit.c @@ -33,6 +33,7 @@ up->egrp->ref =3D 1; up->fgrp =3D dupfgrp(nil); up->rgrp =3D newrgrp(); + up->lgrp =3D newlgrp(nil); =20 /* * 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\