On Tue, 30 Jun 2015 at 09:28 -0000, Alexis Huxley wrote:

> Hi, is there any news regarding cgroup-based enforcement of h_vmem?

We also wanted this.  Our primary use case was users needing to mmap()
large files.

> I saw that Mark Dixon posted patches against 8.1.1 (details at
> http://gridengine.org/pipermail/users/2012-September/004699.html),
> but these are not in 8.1.8 nor the 20150129 snapshot.

There was also a followup email with updated patches:

    http://arc.liv.ac.uk/pipermail/sge-discuss/2012-December/000343.html

> We would like to use exactly this facility, but on 8.1.8.
>
> Before I try to port Mark's patches, I thought it made sense to ask
> here in case "native" support is just around the corner, or somebody
> else has already done this porting, or somebody has written their
> own patches. Thanks!

We have applied parts of Mark's patch set to our 8.1.8 system.  The
consolidated patches we use are attached.  I don't believe we did
anything significant except update them for 8.1.8 sources.

We have been running with these patches for a few months now.

I think there might be some interactions with cgroup based cpuset/core
binding support which will eventually need addressing.  We haven't
looked at that yet.

Stuart Barkley
-- 
I've never been lost; I was once bewildered for three days, but never lost!
                                        --  Daniel Boone
diff -ruN sge-8.1.8-orig/doc/man/man5/sge_conf.5 
sge-8.1.8/doc/man/man5/sge_conf.5
--- sge-8.1.8-orig/doc/man/man5/sge_conf.5      2014-10-23 18:03:54.000000000 
-0400
+++ sge-8.1.8/doc/man/man5/sge_conf.5   2015-03-31 14:18:00.010993411 -0400
@@ -1559,6 +1559,11 @@
 .I sources/experimental/qidle
 in the source repository, but it may be better to check the
 the screensaver state.
+.IP \fICGROUP_MEMORY\fP
+If this parameter is set to a directory, then xxQS_NAMExx will create a
+cgroup in that location for each job instance running on the execd. It
+will use this to control the amount of memory (both rss and vmem) used
+by jobs.
 .IP \fIUSE_CGROUPS\fP
 [Linux only.]  Use cgroups/cpusets for resource management if the
 system supports them and the necessary directories exist in the
diff -ruN sge-8.1.8-orig/source/daemons/common/Makefile 
sge-8.1.8/source/daemons/common/Makefile
--- sge-8.1.8-orig/source/daemons/common/Makefile       2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/common/Makefile    2015-03-31 14:18:00.035178000 
-0400
@@ -48,6 +48,7 @@
                     sge_processes_irix.c \
                     pdc.c \
                     procfs.c \
+                    cgroup.c \
                     binding_support.c
 
 DAEMONS_COMMON_TEST_OBJS = \
@@ -148,6 +149,9 @@
 procfs.o:      $(DAEMONS_COMMON_DIR)/procfs.c
                $(CC) $(CFLAGS) $(MEMCFLAG) -c $(DAEMONS_COMMON_DIR)/procfs.c
 
+cgroup.o:      $(DAEMONS_COMMON_DIR)/cgroup.c
+               $(CC) $(CFLAGS) $(MEMCFLAG) -c $(DAEMONS_COMMON_DIR)/cgroup.c
+
 binding_support.o:     $(DAEMONS_COMMON_DIR)/binding_support.c
                $(CC) $(CFLAGS) $(MEMCFLAG) -c 
$(DAEMONS_COMMON_DIR)/binding_support.c
 
diff -ruN sge-8.1.8-orig/source/daemons/common/cgroup.c 
sge-8.1.8/source/daemons/common/cgroup.c
--- sge-8.1.8-orig/source/daemons/common/cgroup.c       1969-12-31 
19:00:00.000000000 -0500
+++ sge-8.1.8/source/daemons/common/cgroup.c    2015-03-31 14:18:00.012710650 
-0400
@@ -0,0 +1,342 @@
+/* cgroup.c -- generic cgroup routines
+   Copyright (C) 2012 Mark Dixon, University of Leeds <m.c.dixon at 
leeds.ac.uk>
+
+   This file is free software: you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 3 of
+   the License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this file.  If not, a copy can be downloaded
+   from http://www.gnu.org/licenses/lgpl.html. 
+*/
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "uti/sge_rmon.h"
+#include "uti/sge_log.h"
+#include "uti/sge_unistd.h"
+#include "uti/sge_stdio.h"
+#include "uti/sge_dstring.h"
+#include "uti/sge_parse_num_par.h"
+
+#include "sgeobj/sge_conf.h"
+
+#include "basis_types.h"
+#include "cgroup.h"
+
+bool set_cgroup_value(const dstring *cgroup, const char *attrib, sge_rlim_t 
value);
+
+/****** cgroup/create_cgroup_memory() ****************************************
+*
+*  NAME
+*     create_cgroup_memory -- create a memory cgroup on current host
+*
+*  SYNOPSIS
+*     int create_cgroup_memory(u_long32 job_id, u_long32 ja_task_id,
+*                        const char *queue, sge_rlim_t rss, sge_rlim_t vmem)
+*
+*  FUNCTION
+*     Creates a cgroup for the specified job on the current execution host.
+*
+*     It *should* create the cgroup only for the required queue instance,
+*     permitting jobs to straddle multiple queues on the same host. This is not
+*     done as I couldn't figure out an easy way to get the queue names in the
+*     PDC. Perhaps the PDC should glob for all cgroups that match the job.
+*
+*  INPUTS
+*     job_id     - job id
+*     ja_task_id - task id
+*     queue      - name of queue (currently unused)
+*     rss        - RAM required ("memory.limit_in_bytes")
+*     vmem       - RAM/Swap required ("memory.memsw.limit_in_bytes")
+*
+*  RESULT
+*     0 on success
+*     -2 if there is a problem with the host
+*     -3 if there is a problem with the job
+******************************************************************************/
+
+int create_cgroup_memory(u_long32 job_id, u_long32 ja_task_id,
+const char *queue, sge_rlim_t rss, sge_rlim_t vmem) {
+DENTER(TOP_LAYER, "create_cgroup_memory");
+
+DPRINTF(("create cgroup: job_id = "sge_u32" ja_task_id = "sge_u32" rss = 
"sge_u64" vmem = "sge_u64"\n", job_id, ja_task_id, rss, vmem));
+
+   /* Sanity-check arguments */
+
+if (rss == 0 && vmem < 0) {
+      /* Not being asked to do anything */
+DRETURN(0);
+} else if (rss == 0 && vmem > 0) {
+      /* kernel doesn't allow vmem to be set unless rss is set */
+INFO((SGE_EVENT, "cgroup memory: vmem set but rss isn't"));
+DRETURN(-3);
+}
+
+   /* Get name of cgroup to create */
+dstring cgroup = get_cgroup_memory_name(job_id, ja_task_id);
+
+   /* Create memory cgroup */
+if (sge_mkdir(sge_dstring_get_string(&cgroup), 0755, false, false) != 0) {
+INFO((SGE_EVENT, "cgroup memory: failed to create cgroup."));
+DRETURN(-2);
+}
+
+   /* Assign resources */
+
+   int retval = 0;
+
+   /* If this goes wrong, probably a cgroups/host problem */
+   if (retval == 0 && rss > 0 && ! set_cgroup_value(&cgroup, 
"memory.limit_in_bytes", rss)) {
+      INFO((SGE_EVENT, "cgroup memory: Couldn't set vmem limit."));
+      retval = -2;
+   }
+
+   /* If this goes wrong, could be because rss < vmem */
+   if (retval == 0 && vmem > 0 && ! set_cgroup_value(&cgroup, 
"memory.memsw.limit_in_bytes", vmem)) {
+      INFO((SGE_EVENT, "cgroup memory: Couldn't set memsw limit."));
+      retval = -3;
+   }
+
+   /* Tidy cgroup away if we've hit an error condition */
+   if (retval != 0) {
+      remove_cgroup(sge_dstring_get_string(&cgroup));
+   }
+
+   sge_dstring_free(&cgroup);
+
+   DRETURN(retval);
+}
+
+/****** cgroup/get_cgroup_memory_name() 
****************************************
+*
+*  NAME
+*     cgroup/get_cgroup_memory_name -- generate a name for a memory cgroup
+*
+*  SYNOPSIS
+*     dstring get_cgroup_memory_name(u_long32 job_id, u_long32 ja_task_id)
+*
+*  FUNCTION
+*     Creates a unique name for a memory cgroup for an exec host to use.
+*     It *should* insert the queue name (see create_cgroup_memory discussion)
+*     but doesn't.
+*
+*  INPUTS
+*     job_id     - job id
+*     ja_task_id - task id
+*
+*  RESULT
+*     dstring containing name on success (CALLER MUST FREE RETURNED dstring)
+*     NULL if memory cgroup isn't activiated in config
+******************************************************************************/
+
+
+dstring get_cgroup_memory_name(u_long32 job_id, u_long32 ja_task_id) {
+   dstring name = DSTRING_INIT;
+
+   char *cgroup_memory = mconf_get_cgroup_memory();
+   if (cgroup_memory != NULL) {
+      sge_dstring_sprintf(&name, "%s/"sge_u32"."sge_u32, cgroup_memory, 
job_id, ja_task_id);
+      sge_free(&cgroup_memory);
+   }
+
+   return name;
+}
+
+/****** cgroup/set_cgroup_value() ****************************************
+*
+*  NAME
+*     cgroup/set_cgroup_value -- set a cgroup attribute
+*
+*  SYNOPSIS
+*     bool set_cgroup_value(const dstring *cgroup, const char *attrib,
+*                           sge_rlim_t value)
+*
+*  FUNCTION
+*     Modify a cgroup attribute.
+*
+*  INPUTS
+*     cgroup - filename of cgroup
+*     attrib - attribute to modify
+*     value  - value to set attribute to
+*
+*  RESULT
+*     true on success / false on failure
+******************************************************************************/
+
+bool set_cgroup_value(const dstring *cgroup, const char *attrib, sge_rlim_t 
value) {
+   bool retval = false;
+
+   DENTER(TOP_LAYER, "set_cgroup_value");
+
+   if (value == 0) {
+      return false;
+   }
+
+   dstring file = DSTRING_INIT;
+   sge_dstring_copy_dstring(&file, cgroup);
+   sge_dstring_append(&file, "/");
+   sge_dstring_append(&file, attrib);
+
+   FILE *fp;
+   FOPEN(fp, sge_dstring_get_string(&file), "w");
+   FPRINTF((fp, sge_u64, value));
+   FCLOSE(fp);
+
+   retval = true;
+
+FOPEN_ERROR:
+FPRINTF_ERROR:
+FCLOSE_ERROR:
+   sge_dstring_free(&file);
+
+   DRETURN(retval);
+}
+
+/****** cgroup/get_cgroup_double_value() ************************************
+*
+*  NAME
+*     cgroup/get_cgroup_double_value -- return a cgroup attribute
+*
+*  SYNOPSIS
+*     bool get_cgroup_double_value(const dstring *cgroup, const char *attrib,
+*                                  double *value)
+*
+*  FUNCTION
+*     Return a cgroup attribute, where the value is expected to be compatible
+*     with type double.
+*
+*  INPUTS
+*     cgroup - filename of cgroup
+*     attrib - attribute to return
+*     value  - returned value
+*
+*  RESULT
+*     true on success / false on failure
+******************************************************************************/
+
+bool get_cgroup_double_value(const dstring *cgroup, const char *attrib, double 
*value) {
+  bool retval = false;
+
+   DENTER(TOP_LAYER, "get_cgroup_ulong64_value");
+
+   dstring file = DSTRING_INIT;
+   sge_dstring_copy_dstring(&file, cgroup);
+   sge_dstring_append(&file, "/");
+   sge_dstring_append(&file, attrib);
+
+   FILE *fp;
+   FOPEN(fp, sge_dstring_get_string(&file), "r");
+   FSCANF((fp, "%le", value));
+   FCLOSE(fp);
+
+   retval = true;
+
+FOPEN_ERROR:
+FSCANF_ERROR:
+FCLOSE_ERROR:
+   sge_dstring_free(&file);
+
+   DRETURN(retval);
+}
+
+/****** cgroup/join_cgroup() ************************************************
+*
+*  NAME
+*     cgroup/join_cgroup -- add current process to cgroup
+*
+*  SYNOPSIS
+*     bool join_cgroup(char *cgroup)
+*
+*  FUNCTION
+*     Add the this process to the specified cgroup.
+*
+*  INPUTS
+*     cgroup - filename of cgroup
+*
+*  RESULT
+*     true on success / false on failure
+******************************************************************************/
+
+bool join_cgroup(char *cgroup) {
+   bool retval = false;
+
+   dstring tasks = DSTRING_INIT;
+   sge_dstring_copy_string(&tasks, cgroup);
+   sge_dstring_append(&tasks, "/tasks");
+
+   /* Seem to need euid == uid, otherwise don't have permission */
+   /* to move ourselves into a new cgroup */
+   uid_t uid  = getuid();
+   uid_t euid = geteuid();
+   if (uid != euid) {
+      sge_seteuid(uid);
+   }
+
+   /* Add current PID to cgroup's tasks */
+   FILE *fp;
+   FOPEN(fp, sge_dstring_get_string(&tasks), "w");
+   FPRINTF((fp, uid_t_fmt, getpid()));
+   FCLOSE(fp);
+
+   retval = true;
+
+FOPEN_ERROR:
+FPRINTF_ERROR:
+FCLOSE_ERROR:
+
+   /* Return to original euid. */
+   if (euid != geteuid()) {
+      sge_seteuid(euid);
+   }
+
+   sge_dstring_free(&tasks);
+
+   return retval;
+}
+
+/****** cgroup/remove_cgroup() ***********************************************
+*
+*  NAME
+*     cgroup/remove_cgroup -- delete the specified cgroup
+*
+*  SYNOPSIS
+*     bool remove_cgroup(const char *cgroup)
+*
+*  FUNCTION
+*     Removes the specified cgroup.
+*
+*  INPUTS
+*     cgroup - filename of cgroup
+*
+*  RESULT
+*     true on success / false on failure
+******************************************************************************/
+
+bool remove_cgroup(const char *cgroup) {
+   bool retval = true;
+
+   DENTER(TOP_LAYER, "remove_cgroup");
+
+   if (cgroup != NULL && strncmp(cgroup, "NONE", strlen(cgroup)) != 0) {
+      /*
+       * Don't use sge_rmdir - it halts early because it cannot get rid
+       * of "files" like memory.memsw.limit_in_bytes, etc.
+       */
+      if (rmdir(cgroup)) {
+         DPRINTF(("could not rmdir cgroup %s\n", cgroup));
+         retval = false;
+      } else {
+         DPRINTF(("removed cgroup %s\n", cgroup));
+      }
+   }
+
+   DRETURN(retval);
+}
diff -ruN sge-8.1.8-orig/source/daemons/common/cgroup.h 
sge-8.1.8/source/daemons/common/cgroup.h
--- sge-8.1.8-orig/source/daemons/common/cgroup.h       1969-12-31 
19:00:00.000000000 -0500
+++ sge-8.1.8/source/daemons/common/cgroup.h    2015-03-31 14:18:00.026568000 
-0400
@@ -0,0 +1,29 @@
+/* cgroup.h -- generic cgroup routines
+   Copyright (C) 2012 Mark Dixon, University of Leeds <m.c.dixon at 
leeds.ac.uk>
+
+   This file is free software: you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 3 of
+   the License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this file.  If not, a copy can be downloaded
+   from http://www.gnu.org/licenses/lgpl.html. 
+*/
+
+
+#include "uti/sge_dstring.h"
+#include "uti/sge_parse_num_par.h"
+
+/* creates cgroup container for job */
+int create_cgroup_memory(u_long32 job_id, u_long32 ja_task_id, const char 
*queue, sge_rlim_t rss, sge_rlim_t vmem);
+bool join_cgroup(char *cgroup);
+dstring get_cgroup_memory_name(u_long32 job_id, u_long32 ja_task_id);
+bool remove_cgroup(const char *cgroup);
+
+bool get_cgroup_double_value(const dstring *cgroup, const char *attrib, double 
*value);
diff -ruN sge-8.1.8-orig/source/daemons/execd/Makefile 
sge-8.1.8/source/daemons/execd/Makefile
--- sge-8.1.8-orig/source/daemons/execd/Makefile        2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/execd/Makefile     2015-03-31 14:18:00.054507000 
-0400
@@ -69,7 +69,8 @@
               sig_handlers.c \
               startprog.c \
               shutdown.c  \
-              sge_mt_init.c
+              sge_mt_init.c \
+             cgroup.c
 
 EXECD_OBJS    = $(EXECD_SRC:.c=.o)
 
diff -ruN sge-8.1.8-orig/source/daemons/execd/exec_job.c 
sge-8.1.8/source/daemons/execd/exec_job.c
--- sge-8.1.8-orig/source/daemons/execd/exec_job.c      2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/execd/exec_job.c   2015-03-31 14:18:00.044165321 
-0400
@@ -106,6 +106,7 @@
 #include "msg_common.h"
 #include "msg_execd.h"
 #include "msg_daemons_common.h"
+#include "cgroup.h"
 #include "binding_support.h"
 
 #if defined(SOLARIS)
@@ -321,7 +322,7 @@
    bool bInputFileStaging, bOutputFileStaging, bErrorFileStaging;
    
    const char* processor_binding_strategy = NULL;
-   
+   char *cgroup_memory;   
    /* env var reflecting SGE_BINDING if set */
    char* sge_binding_environment = NULL;
    /* string reflecting the logical socket,core pairs if "pe" is set */
@@ -482,6 +483,47 @@
          INFO((SGE_EVENT, "appended socket,core list to hostfile %s", 
rankfileinput));
       }
 
+
+      /***************** cgroup part 
******************************************/
+      cgroup_memory = mconf_get_cgroup_memory();
+      if (cgroup_memory) {
+         sge_free(&cgroup_memory);
+
+         sge_rlim_t cgroup_rss  = 0;
+         sge_rlim_t cgroup_vmem = 0;
+
+         if (strncmp(lGetString(master_q, QU_h_vmem), "INFINITY", 8) != 0) {
+            /* TODO: no error checking. Supposed to use        */
+            /* extended_parse_ulong_val but it's only 32-bit.  */
+            const char *vmem = lGetString(master_q, QU_h_vmem);
+            sge_parse_num_val(&cgroup_rss,  NULL, vmem, vmem, NULL, 0);
+            sge_parse_num_val(&cgroup_vmem, NULL, vmem, vmem, NULL, 0);
+         }
+
+         if (strncmp(lGetString(master_q, QU_h_rss), "INFINITY", 8) != 0) {
+            /* TODO: no error checking. Supposed to use 
extended_parse_ulong_val */
+            /* but it's only 32-bit.                                           
  */
+            const char *vmem = lGetString(master_q, QU_h_rss);
+            sge_parse_num_val(&cgroup_rss, NULL, vmem, vmem, NULL, 0);
+         }
+
+         /* Scale memory based on number of slots */
+         cgroup_rss  *= (sge_rlim_t) lGetUlong(master_q, QU_job_slots);
+         cgroup_vmem *= (sge_rlim_t) lGetUlong(master_q, QU_job_slots);
+
+         int ret = create_cgroup_memory(job_id, ja_task_id,
+                                        lGetString(master_q, QU_qname),
+                                        cgroup_rss, cgroup_vmem);
+
+         if (ret == 0) {
+            INFO((SGE_EVENT, "created memory cgroup"));
+         } else {
+            snprintf(err_str, err_length, SFNMAX, MSG_CGROUP_CREATEFAIL);
+            DRETURN(ret);
+         }
+      }
+
+
       /***************** write out sge host file 
******************************/
       /* JG: TODO: create function write_pe_hostfile() */
       /* JG: TODO (254) use function sge_get_active_job.... */
@@ -1165,6 +1207,9 @@
    fprintf(fp, "h_fsize=%s\n", lGetString(master_q, QU_h_fsize));
    fprintf(fp, "s_fsize=%s\n", lGetString(master_q, QU_s_fsize));
 
+   fprintf(fp, "h_as=%s\n", lGetString(master_q, QU_h_as));
+   fprintf(fp, "s_as=%s\n", lGetString(master_q, QU_s_as));
+
    {
       char *s;
 
@@ -1335,6 +1380,16 @@
       fprintf(fp, "binding=%s\n", processor_binding_strategy);
       
    }
+     {
+      /* memory cgroup information */
+      dstring cgroup = get_cgroup_memory_name(job_id, ja_task_id);
+      if (sge_dstring_get_string(&cgroup) == NULL) {
+         fprintf(fp, "cgroup_memory=NONE\n");
+      } else {
+         fprintf(fp, "cgroup_memory=%s\n", sge_dstring_get_string(&cgroup));
+      }
+      sge_dstring_free(&cgroup);
+   }
    if(petep != NULL) {
       fprintf(fp, "job_name=%s\n", lGetString(petep, PET_name));
    } else {
diff -ruN sge-8.1.8-orig/source/daemons/execd/msg_execd.h 
sge-8.1.8/source/daemons/execd/msg_execd.h
--- sge-8.1.8-orig/source/daemons/execd/msg_execd.h     2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/execd/msg_execd.h  2015-03-31 14:18:00.061899516 
-0400
@@ -288,5 +288,10 @@
 #define MSG_EXECD_DISABLEDELEAYDJOBREPORTING  _MESSAGE(29191,    _("Delayed 
job reporting period finished"))
 #define MSG_EXECD_ENABLEDELEAYDREPORTINGFORJOB_U _MESSAGE(29192, _("Enable 
delayed job reporting for job "sge_U32CFormat""))
 
+/*
+** Misc (waiting for renumbering)
+*/
+#define MSG_CGROUP_CREATEFAIL         _MESSAGE(29193, _("cannot create 
cgroup"))
+
 #endif /* __MSG_EXECD_H */
 
diff -ruN sge-8.1.8-orig/source/daemons/execd/ptf.c 
sge-8.1.8/source/daemons/execd/ptf.c
--- sge-8.1.8-orig/source/daemons/execd/ptf.c   2014-10-23 18:03:56.000000000 
-0400
+++ sge-8.1.8/source/daemons/execd/ptf.c        2015-03-31 14:18:00.068324069 
-0400
@@ -100,6 +100,7 @@
 #include "sgedefs.h"
 #include "exec_ifm.h"
 #include "pdc.h"
+#include "cgroup.h"
 
 /*
  *
@@ -908,14 +909,49 @@
                           jobs->jd_rwtime_c + jobs->jd_rwtime_a);
             }
 
+            double vmem  = 0;
+            double himem = 0;
+            dstring cgroup_memory = get_cgroup_memory_name(lGetUlong(job, 
JL_job_ID), lGetUlong(osjob, JO_ja_task_ID));
+            if (sge_dstring_get_string(&cgroup_memory) != NULL) {
+               /* Retrieve usage from cgroups */
+
+               /* TODO: really ought to check get_cgroup_double_value */
+               /*       return codes                                  */
+
+               double failcnt  = 0;
+               double vmem_lim = 0;
+
+               /* Retrieve vmem cgroup value */
+               get_cgroup_double_value(&cgroup_memory, 
"memory.memsw.usage_in_bytes", &vmem);
+
+               /* Retrieve himem cgroup value */
+               get_cgroup_double_value(&cgroup_memory, 
"memory.memsw.max_usage_in_bytes", &himem);
+
+               /* If failcnt > 0, we have hit the limit so fabricate an entry 
*/
+               /* that's too big */
+               get_cgroup_double_value(&cgroup_memory, "memory.memsw.failcnt", 
&failcnt);
+               if (failcnt > 0) {
+                  get_cgroup_double_value(&cgroup_memory, 
"memory.memsw.limit_in_bytes", &vmem_lim);
+                  vmem  = vmem_lim +1;
+                  himem = vmem_lim +1;
+               }
+            } else {
+               /* Retrieve usage from ptf */
+
+               vmem  = jobs->jd_vmem;
+               himem = jobs->jd_himem;
+            }
+            sge_dstring_free(&cgroup_memory);
+
+
             /* set vmem usage */
             if ((usage = lGetElemStr(usage_list, UA_name, USAGE_ATTR_VMEM))) {
-               lSetDouble(usage, UA_value, jobs->jd_vmem);
+               lSetDouble(usage, UA_value, vmem);
             }
 
             /* set himem usage */
             if ((usage = lGetElemStr(usage_list, UA_name, 
USAGE_ATTR_MAXVMEM))) {
-               lSetDouble(usage, UA_value, jobs->jd_himem);
+               lSetDouble(usage, UA_value, himem);
             }
 
             /* build new pid list */
@@ -958,7 +994,7 @@
                      tmp_jobs->jd_utime_c + tmp_jobs->jd_utime_a +
                      tmp_jobs->jd_stime_c + tmp_jobs->jd_stime_a,
                      tmp_jobs->jd_mem, tmp_jobs->jd_chars,
-                     (double) tmp_jobs->jd_vmem, (double) tmp_jobs->jd_himem));
+                    vmem, himem));
          } else {
             /* 
              * NOTE: Under what conditions would DC have a job
diff -ruN sge-8.1.8-orig/source/daemons/execd/reaper_execd.c 
sge-8.1.8/source/daemons/execd/reaper_execd.c
--- sge-8.1.8-orig/source/daemons/execd/reaper_execd.c  2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/execd/reaper_execd.c       2015-03-31 
14:18:00.076627680 -0400
@@ -101,6 +101,7 @@
 #include "msg_common.h"
 #include "msg_daemons_common.h"
 #include "msg_execd.h"
+#include "cgroup.h"
 #include "binding_support.h"
 
 #ifdef COMPILE_DC
@@ -488,6 +489,11 @@
    
    clean_up_binding(binding);
 
+    /*
+     * cgroups
+     */
+   remove_cgroup(get_conf_val("cgroup_memory"));
+
    /*
     * look for exit status of shepherd This is the last file the shepherd
     * creates. So if we can find this shepherd terminated normal.
diff -ruN 
sge-8.1.8-orig/source/daemons/qmaster/sge_advance_reservation_qmaster.c 
sge-8.1.8/source/daemons/qmaster/sge_advance_reservation_qmaster.c
--- sge-8.1.8-orig/source/daemons/qmaster/sge_advance_reservation_qmaster.c     
2014-10-23 18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/qmaster/sge_advance_reservation_qmaster.c  
2015-03-31 14:18:00.087717616 -0400
@@ -1632,12 +1632,15 @@
                                 QU_h_rss,
                                 QU_s_vmem,
                                 QU_h_vmem,
+                                QU_s_as,
+                                QU_h_as,
                                 NoName };
     static char *value = "INFINITY";
     static int attr[] = {
       QU_s_cpu, QU_h_cpu, QU_s_fsize, QU_h_fsize, QU_s_data,
       QU_h_data, QU_s_stack, QU_h_stack, QU_s_core, QU_h_core,
-      QU_s_rss, QU_h_rss, QU_s_vmem, QU_h_vmem, NoName
+      QU_s_rss, QU_h_rss, QU_s_vmem, QU_h_vmem, QU_s_as, QU_h_as,
+      NoName
     };
 
    lDescr *rdp = NULL;
diff -ruN sge-8.1.8-orig/source/daemons/qmaster/sge_give_jobs.c 
sge-8.1.8/source/daemons/qmaster/sge_give_jobs.c
--- sge-8.1.8-orig/source/daemons/qmaster/sge_give_jobs.c       2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/qmaster/sge_give_jobs.c    2015-03-31 
14:18:00.096173568 -0400
@@ -180,6 +180,8 @@
                              QU_h_rss,
                              QU_s_vmem,
                              QU_h_vmem,
+                             QU_s_as,
+                             QU_h_as,
                              NoName };
 
 
@@ -334,6 +336,8 @@
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_h_fsize, 
"h_fsize");
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_s_vmem,  
"s_vmem");
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_h_vmem,  
"h_vmem");
+      reduce_queue_limit(master_centry_list, qep, tmpjep, QU_s_as,    "s_as");
+      reduce_queue_limit(master_centry_list, qep, tmpjep, QU_h_as,    "h_as");
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_s_rt,    "s_rt");
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_h_rt,    "h_rt");
 
@@ -603,6 +607,8 @@
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_h_fsize, 
"h_fsize");
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_s_vmem,  
"s_vmem");
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_h_vmem,  
"h_vmem");
+      reduce_queue_limit(master_centry_list, qep, tmpjep, QU_s_as,    "s_as");
+      reduce_queue_limit(master_centry_list, qep, tmpjep, QU_h_as,    "h_as");
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_s_rt,    "s_rt");
       reduce_queue_limit(master_centry_list, qep, tmpjep, QU_h_rt,    "h_rt");
 
diff -ruN sge-8.1.8-orig/source/daemons/qmaster/sge_qinstance_qmaster.c 
sge-8.1.8/source/daemons/qmaster/sge_qinstance_qmaster.c
--- sge-8.1.8-orig/source/daemons/qmaster/sge_qinstance_qmaster.c       
2014-10-23 18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/qmaster/sge_qinstance_qmaster.c    2015-03-31 
14:18:00.102632016 -0400
@@ -205,6 +205,8 @@
          case CQ_h_rss:
          case CQ_s_vmem:
          case CQ_h_vmem:
+         case CQ_s_as:
+         case CQ_h_as:
             {
                const char *old_value = lGetString(this_elem, attribute_name);
                const char *new_value = NULL;
diff -ruN sge-8.1.8-orig/source/daemons/qmaster/sge_sched_prepare_data.c 
sge-8.1.8/source/daemons/qmaster/sge_sched_prepare_data.c
--- sge-8.1.8-orig/source/daemons/qmaster/sge_sched_prepare_data.c      
2014-10-23 18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/qmaster/sge_sched_prepare_data.c   2015-03-31 
14:18:00.109767580 -0400
@@ -93,6 +93,7 @@
    CQ_h_rt,
    CQ_h_stack,
    CQ_h_vmem,
+   CQ_h_as,
    CQ_min_cpu_interval,
    CQ_rerun,
    CQ_s_core,
@@ -103,6 +104,7 @@
    CQ_s_rss,
    CQ_s_stack,
    CQ_s_vmem,
+   CQ_s_as,
    NoName
 };
 
@@ -149,6 +151,8 @@
    QU_h_rss,
    QU_s_vmem,
    QU_h_vmem,
+   QU_s_as,
+   QU_h_as,
    QU_min_cpu_interval,
 
    QU_suspended_on_subordinate,
diff -ruN sge-8.1.8-orig/source/daemons/shepherd/Makefile 
sge-8.1.8/source/daemons/shepherd/Makefile
--- sge-8.1.8-orig/source/daemons/shepherd/Makefile     2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/shepherd/Makefile  2015-03-31 14:18:00.115930000 
-0400
@@ -45,7 +45,8 @@
            sge_pset.c \
            sge_fileio.c \
            sge_shepherd_ijs.c \
-           shepherd_binding.c 
+           shepherd_binding.c \
+          shepherd_cgroup.c
 
 SHEPHERD_ADD_SRC = \
            config_file.c \
@@ -59,6 +60,7 @@
            sge_mt_init.c \
            sge_processes_irix.c \
            sge_spool.c \
+           cgroup.c \
            binding_support.c
 
 SHEPHERD_OBJS = $(SHEPHERD_SRC:.c=.o) sge_ijs_comm.o sge_ijs_threads.o
@@ -133,3 +135,6 @@
 shepherd_binding.o: $(SHEPHERD_DIR)/shepherd_binding.c
                $(CC) -I$(SHEPHERD_DIR) $(CFLAGS) -c 
$(SHEPHERD_DIR)/shepherd_binding.c
 
+shepherd_cgroup.o: $(SHEPHERD_DIR)/shepherd_cgroup.c
+               $(CC) -I$(SHEPHERD_DIR) $(CFLAGS) -c 
$(SHEPHERD_DIR)/shepherd_cgroup.c
+
diff -ruN sge-8.1.8-orig/source/daemons/shepherd/setrlimits.c 
sge-8.1.8/source/daemons/shepherd/setrlimits.c
--- sge-8.1.8-orig/source/daemons/shepherd/setrlimits.c 2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/shepherd/setrlimits.c      2015-03-31 
14:18:00.122140158 -0400
@@ -131,6 +131,9 @@
    sge_rlim_t s_vmem, s_vmem_is_consumable_job;
    sge_rlim_t h_vmem, h_vmem_is_consumable_job;
 
+   sge_rlim_t s_as;
+   sge_rlim_t h_as;
+
    sge_rlim_t s_fsize;
    sge_rlim_t h_fsize;
 
@@ -199,6 +202,9 @@
    PARSE_IT(&s_rss, "s_rss");
    PARSE_IT(&h_rss, "h_rss");
 
+   PARSE_IT(&s_as,  "s_as");
+   PARSE_IT(&h_as,  "h_as");
+
    PARSE_IT(&s_fsize, "s_fsize");
    PARSE_IT(&h_fsize, "h_fsize");
 
@@ -223,6 +229,8 @@
    h_data = RL_MAX(h_data, LIMIT_VMEM_MIN);
    s_rss = RL_MAX(s_rss, LIMIT_VMEM_MIN);
    h_rss = RL_MAX(h_rss, LIMIT_VMEM_MIN);
+   s_as = RL_MAX(s_as, LIMIT_VMEM_MIN);
+   h_as = RL_MAX(h_as, LIMIT_VMEM_MIN);
    s_stack = RL_MAX(s_stack, LIMIT_STACK_MIN);
    h_stack = RL_MAX(h_stack, LIMIT_STACK_MIN);
    s_cpu = RL_MAX(s_cpu, LIMIT_CPU_MIN);
@@ -266,6 +274,9 @@
    if (s_vmem > h_vmem) {
       s_vmem = h_vmem;
    }    
+   if (s_as > h_as) {
+      s_as = h_as;
+   }
    /*s_data = RL_MIN(s_data, s_vmem);
    s_stack = RL_MIN(s_stack, s_vmem);
    h_data = RL_MIN(h_data, h_vmem);
@@ -403,12 +414,24 @@
 #  endif
 
 #  if defined(RLIMIT_VMEM)
-   rlp.rlim_cur = s_vmem;
-   rlp.rlim_max = h_vmem;
+   char *cgroup_memory = get_conf_val("cgroup_memory");
+   if (strncmp(cgroup_memory, "NONE", strlen(cgroup_memory)) == 0) {
+      rlp.rlim_cur = s_vmem;
+      rlp.rlim_max = h_vmem;
+   } else {
+      rlp.rlim_cur = s_as;
+      rlp.rlim_max = h_as;
+   }
    pushlimit(RLIMIT_VMEM, &rlp, trace_rlimit);
 #  elif defined(RLIMIT_AS)
-   rlp.rlim_cur = s_vmem;
-   rlp.rlim_max = h_vmem;
+   char *cgroup_memory = get_conf_val("cgroup_memory");
+   if (strncmp(cgroup_memory, "NONE", strlen(cgroup_memory)) == 0) {
+      rlp.rlim_cur = s_vmem;
+      rlp.rlim_max = h_vmem;
+   } else {
+      rlp.rlim_cur = s_as;
+      rlp.rlim_max = h_as;
+   }
    pushlimit(RLIMIT_AS, &rlp, trace_rlimit);
 #  endif
 
diff -ruN sge-8.1.8-orig/source/daemons/shepherd/shepherd.c 
sge-8.1.8/source/daemons/shepherd/shepherd.c
--- sge-8.1.8-orig/source/daemons/shepherd/shepherd.c   2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/shepherd/shepherd.c        2015-03-31 
14:18:00.129195713 -0400
@@ -50,6 +50,7 @@
 #include "uti/sge_binding_hlp.h"
 #include "uti/sge_string.h"
 #include "shepherd_binding.h"
+#include "shepherd_cgroup.h"
 /* TODO: (SH) get_path.h is a header-file of execd. We have to do a CLEANUP 
here. */
 #include "get_path.h"
 
@@ -894,6 +895,13 @@
 
    cpusetting();
 
+       /*
+        * Deal with cgroups
+        */
+   if (! do_cgroups()) {
+      shepherd_error(1, "cgroup processing failed");
+   }
+
    /*
     * this blocks sge_shepherd until the first time the token is
     * successfully set, then we start the sge_coshepherd in background
diff -ruN sge-8.1.8-orig/source/daemons/shepherd/shepherd_cgroup.c 
sge-8.1.8/source/daemons/shepherd/shepherd_cgroup.c
--- sge-8.1.8-orig/source/daemons/shepherd/shepherd_cgroup.c    1969-12-31 
19:00:00.000000000 -0500
+++ sge-8.1.8/source/daemons/shepherd/shepherd_cgroup.c 2015-03-31 
14:18:00.140277000 -0400
@@ -0,0 +1,63 @@
+/* shepherd_cgroup.c -- cgroup routines for shepherd
+   Copyright (C) 2012 Mark Dixon, University of Leeds <m.c.dixon at 
leeds.ac.uk>
+
+   This file is free software: you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 3 of
+   the License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this file.  If not, a copy can be downloaded
+   from http://www.gnu.org/licenses/lgpl.html. 
+*/
+
+#include <string.h>
+
+#include "uti/config_file.h"
+#include "uti/sge_unistd.h"
+
+#include "err_trace.h"
+#include "cgroup.h"
+
+/****** shepherd_cgroup/do_cgroups() 
***********************************************
+*
+*  NAME
+*     shepherd_cgroup/do_cgroups -- join shepherd to relevant cgroups
+*
+*  SYNOPSIS
+*     bool do_cgroups(void)
+*
+*  FUNCTION
+*     Join the job's cgroups (if enabled)
+*
+*  INPUTS
+*
+*  RESULT
+*     true on success / false on failure
+******************************************************************************/
+bool do_cgroups(void) {
+   char *cgroup_memory = get_conf_val("cgroup_memory");
+
+   if (cgroup_memory == NULL) {
+      shepherd_trace("do_cgroups: \"cgroup_memory\" parameter not found in 
config file");
+      return false;
+   }
+
+   /* Early exit if we don't need to do anything */
+   if (strncmp(cgroup_memory, "NONE", strlen(cgroup_memory)) == 0) {
+      return true;
+   }
+
+   if (! join_cgroup(cgroup_memory)) {
+      shepherd_trace("do_cgroups: failed to join cgroup");
+      return false;
+   }
+
+   shepherd_trace("do_cgroups: joined memory cgroup %s", cgroup_memory);
+   return true;
+}
diff -ruN sge-8.1.8-orig/source/daemons/shepherd/shepherd_cgroup.h 
sge-8.1.8/source/daemons/shepherd/shepherd_cgroup.h
--- sge-8.1.8-orig/source/daemons/shepherd/shepherd_cgroup.h    1969-12-31 
19:00:00.000000000 -0500
+++ sge-8.1.8/source/daemons/shepherd/shepherd_cgroup.h 2015-03-31 
14:18:00.167191000 -0400
@@ -0,0 +1,19 @@
+/* shepherd_cgroup.h -- cgroup routines for shepherd
+   Copyright (C) 2012 Mark Dixon, University of Leeds <m.c.dixon at 
leeds.ac.uk>
+
+   This file is free software: you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public License
+   as published by the Free Software Foundation; either version 3 of
+   the License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this file.  If not, a copy can be downloaded
+   from http://www.gnu.org/licenses/lgpl.html. 
+*/
+
+bool do_cgroups(void);
diff -ruN sge-8.1.8-orig/source/libs/sgeobj/sge_centry.c 
sge-8.1.8/source/libs/sgeobj/sge_centry.c
--- sge-8.1.8-orig/source/libs/sgeobj/sge_centry.c      2014-10-23 
18:03:58.000000000 -0400
+++ sge-8.1.8/source/libs/sgeobj/sge_centry.c   2015-03-31 14:18:00.169898994 
-0400
@@ -101,7 +101,7 @@
    {"m_topology_inuse",0,0, 0, TYPE_STR}
 };
 
-const int max_queue_resources=24; /* specifies the number of elements in the 
queue_resource array */
+const int max_queue_resources=26; /* specifies the number of elements in the 
queue_resource array */
 const struct queue2cmplx queue_resource[] = {
    {"qname",            QU_qname,            0,                   0,           
 TYPE_STR },
    {"hostname",         QU_qhostname,        0,                   0,           
 TYPE_HOST},
@@ -126,6 +126,8 @@
    {"h_rss",            QU_h_rss,            CQ_h_rss,            AMEM_value,  
 TYPE_MEM }, /* value is SGE_STRING */
    {"s_vmem",           QU_s_vmem,           CQ_s_vmem,           AMEM_value,  
 TYPE_MEM }, /* value is SGE_STRING */
    {"h_vmem",           QU_h_vmem,           CQ_h_vmem,           AMEM_value,  
 TYPE_MEM }, /* value is SGE_STRING */
+   {"s_as",             QU_s_as,             CQ_s_as,             AMEM_value,  
 TYPE_MEM }, /* value is SGE_STRING */
+   {"h_as",             QU_h_as,             CQ_h_as,             AMEM_value,  
 TYPE_MEM }, /* value is SGE_STRING */
    {"min_cpu_interval", QU_min_cpu_interval, CQ_min_cpu_interval, 
AINTER_value, TYPE_TIM }  /* value is SGE_STRING */
 };
 
diff -ruN sge-8.1.8-orig/source/libs/sgeobj/sge_conf.c 
sge-8.1.8/source/libs/sgeobj/sge_conf.c
--- sge-8.1.8-orig/source/libs/sgeobj/sge_conf.c        2014-10-23 
18:03:58.000000000 -0400
+++ sge-8.1.8/source/libs/sgeobj/sge_conf.c     2015-03-31 14:18:00.176952557 
-0400
@@ -190,6 +190,7 @@
 static bool keep_active = false;
 static bool enable_windomacc = false;
 static bool enable_binding = false;
+static char* cgroup_memory = NULL;
 static bool enable_addgrp_kill = true;
 static u_long32 pdc_interval = 1;
 static char s_descriptors[100];
@@ -874,6 +875,7 @@
       keep_active = false;
       enable_windomacc = false;
       enable_binding = HAVE_HWLOC ? true : false;
+      sge_free(&cgroup_memory);
       enable_addgrp_kill = true;
       use_qsub_gid = false;
       prof_execd_thrd = false;
@@ -933,6 +935,10 @@
          if (parse_bool_param(s, "ENABLE_BINDING", &enable_binding)) {
             continue;
          }
+         if (!strncasecmp(s, "CGROUP_MEMORY", sizeof("CGROUP_MEMORY")-1)) {
+            cgroup_memory = sge_strdup(NULL, &(s[sizeof("CGROUP_MEMORY")]));
+            continue;
+         }
          if (parse_bool_param(s, "ENABLE_ADDGRP_KILL", &enable_addgrp_kill)) {
             continue;
          }
@@ -2098,6 +2104,20 @@
    DRETURN(ret);
 }
 
+/* returned pointer needs to be freed */
+char* mconf_get_cgroup_memory(void) {
+   char* ret = NULL;
+
+   DENTER(BASIS_LAYER, "mconf_get_cgroup_memory");
+   SGE_LOCK(LOCK_MASTER_CONF, LOCK_READ);
+
+   ret = sge_strdup(ret, cgroup_memory);
+
+   SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_READ);
+   DRETURN(ret);
+}
+
+
 bool mconf_get_enable_addgrp_kill(void) {
    bool ret;
 
diff -ruN sge-8.1.8-orig/source/libs/sgeobj/sge_conf.h 
sge-8.1.8/source/libs/sgeobj/sge_conf.h
--- sge-8.1.8-orig/source/libs/sgeobj/sge_conf.h        2014-10-23 
18:03:58.000000000 -0400
+++ sge-8.1.8/source/libs/sgeobj/sge_conf.h     2015-03-31 14:18:00.186024000 
-0400
@@ -117,6 +117,7 @@
 bool mconf_get_keep_active(void);
 bool mconf_get_enable_windomacc(void);
 bool mconf_get_enable_binding(void);
+char* mconf_get_cgroup_memory(void);
 bool mconf_get_simulate_execds(void);
 bool mconf_get_simulate_jobs(void);
 long mconf_get_ptf_max_priority(void);
diff -ruN sge-8.1.8-orig/source/libs/sgeobj/sge_cqueue.c 
sge-8.1.8/source/libs/sgeobj/sge_cqueue.c
--- sge-8.1.8-orig/source/libs/sgeobj/sge_cqueue.c      2014-10-23 
18:03:58.000000000 -0400
+++ sge-8.1.8/source/libs/sgeobj/sge_cqueue.c   2015-03-31 14:18:00.195685975 
-0400
@@ -113,7 +113,8 @@
    { CQ_h_rss,                   QU_h_rss,                  AMEM_href,     
AMEM_value,       NoName,     SGE_ATTR_H_RSS,             false,  false, 
cqueue_verify_memory_value},
    { CQ_s_vmem,                  QU_s_vmem,                 AMEM_href,     
AMEM_value,       NoName,     SGE_ATTR_S_VMEM,            false,  false, 
cqueue_verify_memory_value},
    { CQ_h_vmem,                  QU_h_vmem,                 AMEM_href,     
AMEM_value,       NoName,     SGE_ATTR_H_VMEM,            false,  false, 
cqueue_verify_memory_value},
-
+   { CQ_s_as,                    QU_s_as,                   AMEM_href,     
AMEM_value,       NoName,     SGE_ATTR_S_AS,              false,  false, 
cqueue_verify_memory_value},
+   { CQ_h_as,                    QU_h_as,                   AMEM_href,     
AMEM_value,       NoName,     SGE_ATTR_H_AS,              false,  false, 
cqueue_verify_memory_value},
    { CQ_s_rt,                    QU_s_rt,                   ATIME_href,    
ATIME_value,      NoName,     SGE_ATTR_S_RT,              false,  false, 
cqueue_verify_time_value},
    { CQ_h_rt,                    QU_h_rt,                   ATIME_href,    
ATIME_value,      NoName,     SGE_ATTR_H_RT,              false,  false, 
cqueue_verify_time_value},
    { CQ_s_cpu,                   QU_s_cpu,                  ATIME_href,    
ATIME_value,      NoName,     SGE_ATTR_S_CPU,             false,  false, 
cqueue_verify_time_value},
@@ -639,12 +640,14 @@
             "INFINITY", "INFINITY", "INFINITY", "INFINITY",
             "INFINITY", "INFINITY", "INFINITY", "INFINITY",
             "INFINITY", "INFINITY", "INFINITY", "INFINITY",
+            "INFINITY", "INFINITY",
             NULL
          }; 
          const int attr[] = {
             CQ_s_fsize, CQ_h_fsize, CQ_s_data, CQ_h_data,
             CQ_s_stack, CQ_h_stack, CQ_s_core, CQ_h_core,
             CQ_s_rss, CQ_h_rss, CQ_s_vmem, CQ_h_vmem,
+            CQ_s_as, CQ_h_as,
             NoName
          };
          int index = 0;
diff -ruN sge-8.1.8-orig/source/libs/sgeobj/sge_cqueue.h 
sge-8.1.8/source/libs/sgeobj/sge_cqueue.h
--- sge-8.1.8-orig/source/libs/sgeobj/sge_cqueue.h      2014-10-23 
18:03:58.000000000 -0400
+++ sge-8.1.8/source/libs/sgeobj/sge_cqueue.h   2015-03-31 14:18:00.207133157 
-0400
@@ -164,6 +164,8 @@
 #define SGE_ATTR_S_RSS                 "s_rss"
 #define SGE_ATTR_H_VMEM                "h_vmem"
 #define SGE_ATTR_S_VMEM                "s_vmem"
+#define SGE_ATTR_H_AS                  "h_as"
+#define SGE_ATTR_S_AS                  "s_as"
 
 #define SGE_ATTR_QTYPE                 "qtype"
 #define SGE_ATTR_SEQ_NO                "seq_no"
diff -ruN sge-8.1.8-orig/source/libs/sgeobj/sge_cqueue_CQ_L.h 
sge-8.1.8/source/libs/sgeobj/sge_cqueue_CQ_L.h
--- sge-8.1.8-orig/source/libs/sgeobj/sge_cqueue_CQ_L.h 2014-10-23 
18:03:58.000000000 -0400
+++ sge-8.1.8/source/libs/sgeobj/sge_cqueue_CQ_L.h      2015-03-31 
14:18:00.201336491 -0400
@@ -63,6 +63,8 @@
    CQ_h_rss,
    CQ_s_vmem,
    CQ_h_vmem,
+   CQ_s_as,
+   CQ_h_as,
 
    CQ_s_rt,
    CQ_h_rt,
@@ -132,6 +134,8 @@
    SGE_MAP_D(CQ_h_rss, AMEM_Type, CULL_SPOOL | CULL_JGDI_CONF, "@/", 
"INFINITY", "hostname", "hRss")  
    SGE_MAP_D(CQ_s_vmem, AMEM_Type, CULL_SPOOL | CULL_JGDI_CONF, "@/", 
"INFINITY", "hostname", "sVmem") 
    SGE_MAP_D(CQ_h_vmem, AMEM_Type, CULL_SPOOL | CULL_JGDI_CONF, "@/", 
"INFINITY", "hostname", "hVmem")
+   SGE_MAP_D(CQ_s_as, AMEM_Type, CULL_SPOOL | CULL_JGDI_CONF, "@/", 
"INFINITY", "hostname", "sAs") 
+   SGE_MAP_D(CQ_h_as, AMEM_Type, CULL_SPOOL | CULL_JGDI_CONF, "@/", 
"INFINITY", "hostname", "hAs")
 
    SGE_MAP_D(CQ_s_rt, ATIME_Type, CULL_SPOOL | CULL_JGDI_CONF, "@/", 
"INFINITY", "hostname", "sRt")
    SGE_MAP_D(CQ_h_rt, ATIME_Type, CULL_SPOOL | CULL_JGDI_CONF, "@/", 
"INFINITY", "hostname", "hRt")
@@ -199,6 +203,8 @@
    NAME("CQ_h_rss")
    NAME("CQ_s_vmem")
    NAME("CQ_h_vmem")
+   NAME("CQ_s_as")
+   NAME("CQ_h_as")
 
    NAME("CQ_s_rt")
    NAME("CQ_h_rt")
diff -ruN sge-8.1.8-orig/source/libs/sgeobj/sge_qinstance_QU_L.h 
sge-8.1.8/source/libs/sgeobj/sge_qinstance_QU_L.h
--- sge-8.1.8-orig/source/libs/sgeobj/sge_qinstance_QU_L.h      2014-10-23 
18:03:58.000000000 -0400
+++ sge-8.1.8/source/libs/sgeobj/sge_qinstance_QU_L.h   2015-03-31 
14:18:00.213553637 -0400
@@ -86,6 +86,8 @@
    QU_h_rss,
    QU_s_vmem,
    QU_h_vmem,
+   QU_s_as,
+   QU_h_as,
 
    QU_suspend_interval,
    QU_min_cpu_interval,
@@ -178,6 +180,8 @@
    SGE_STRING(QU_h_rss, CULL_CONFIGURE | CULL_JGDI_RO)
    SGE_STRING(QU_s_vmem, CULL_CONFIGURE | CULL_JGDI_RO)
    SGE_STRING(QU_h_vmem, CULL_CONFIGURE | CULL_JGDI_RO)
+   SGE_STRING(QU_s_as, CULL_CONFIGURE | CULL_JGDI_RO)
+   SGE_STRING(QU_h_as, CULL_CONFIGURE | CULL_JGDI_RO)
 
    SGE_STRING(QU_suspend_interval, CULL_CONFIGURE | CULL_JGDI_RO)
    SGE_STRING(QU_min_cpu_interval, CULL_CONFIGURE | CULL_JGDI_RO)
@@ -265,6 +269,8 @@
    NAME("QU_h_rss")
    NAME("QU_s_vmem")
    NAME("QU_h_vmem")
+   NAME("QU_s_as")
+   NAME("QU_h_as")
 
    NAME("QU_suspend_interval")
    NAME("QU_min_cpu_interval")
diff -ruN sge-8.1.8-orig/source/libs/spool/flatfile/sge_flatfile_obj.c 
sge-8.1.8/source/libs/spool/flatfile/sge_flatfile_obj.c
--- sge-8.1.8-orig/source/libs/spool/flatfile/sge_flatfile_obj.c        
2014-10-23 18:03:58.000000000 -0400
+++ sge-8.1.8/source/libs/spool/flatfile/sge_flatfile_obj.c     2015-03-31 
14:18:00.219086079 -0400
@@ -482,6 +482,8 @@
    {  CQ_s_rss,                  21, "s_rss",              AMEM_sub_fields,    
 &qconf_sub_name_value_comma_braced_sfi, read_CQ_mem_attr_list,     
write_CQ_mem_attr_list},
    {  CQ_h_rss,                  21, "h_rss",              AMEM_sub_fields,    
 &qconf_sub_name_value_comma_braced_sfi, read_CQ_mem_attr_list,     
write_CQ_mem_attr_list},
    {  CQ_s_vmem,                 21, "s_vmem",             AMEM_sub_fields,    
 &qconf_sub_name_value_comma_braced_sfi, read_CQ_mem_attr_list,     
write_CQ_mem_attr_list},
+   {  CQ_s_as,                   21, "s_as",               AMEM_sub_fields,    
 &qconf_sub_name_value_comma_braced_sfi, read_CQ_mem_attr_list,     
write_CQ_mem_attr_list},
+   {  CQ_h_as,                   21, "h_as",               AMEM_sub_fields,    
 &qconf_sub_name_value_comma_braced_sfi, read_CQ_mem_attr_list,     
write_CQ_mem_attr_list},
    {  CQ_h_vmem,                 21, "h_vmem",             AMEM_sub_fields,    
 &qconf_sub_name_value_comma_braced_sfi, read_CQ_mem_attr_list,     
write_CQ_mem_attr_list},
    {  NoName,                    21, NULL,                 NULL,               
 NULL,                                   NULL,                      NULL}
 };
@@ -967,8 +969,9 @@
 
 spooling_field *sge_build_QU_field_list(bool to_stdout, bool to_file)
 {
-   /* There are 52 possible QU_Type fields. */
-   spooling_field *fields = (spooling_field 
*)malloc(sizeof(spooling_field)*52);
+   /* There are 54 possible QU_Type fields. */
+   spooling_field *fields = (spooling_field 
*)malloc(sizeof(spooling_field)*54);
+
    int count = 0;
    
    create_spooling_field (&fields[count++], QU_qname, 21, "qname", NULL,
@@ -1082,6 +1085,10 @@
                              NULL, NULL, NULL);
       create_spooling_field (&fields[count++], QU_h_vmem, 21, "h_vmem", NULL,
                              NULL, NULL, NULL);
+      create_spooling_field (&fields[count++], QU_s_as, 21, "s_as", NULL,
+                             NULL, NULL, NULL);
+      create_spooling_field (&fields[count++], QU_h_as, 21, "h_as", NULL,
+                             NULL, NULL, NULL);
    } else if (to_file) {
       /*
        * Spool only non-CQ attributes
diff -ruN sge-8.1.8-orig/source/libs/uti/sge_stdio.h 
sge-8.1.8/source/libs/uti/sge_stdio.h
--- sge-8.1.8-orig/source/libs/uti/sge_stdio.h  2014-10-23 18:03:59.000000000 
-0400
+++ sge-8.1.8/source/libs/uti/sge_stdio.h       2015-03-31 14:18:00.228596000 
-0400
@@ -49,6 +49,33 @@
       goto FOPEN_ERROR; \
    }
 
+/****** uti/stdio/FSCANF() ***************************************************
+ * *  NAME
+ * *     FSCANF() -- fscanf() macro 
+ * *
+ * *  SYNOPSIS
+ * *     #define FSCANF(arguments)
+ * *     void fscanf(FILE *stream, const char *format, ...)
+ * *
+ * *  FUNCTION
+ * *     This FSCANF macro has to be used similar to the fscanf 
+ * *     function. It is not necessary to check the return value. 
+ * *     In case of an error the macro will jump to a defined label.
+ * *     The label name is 'FSCANF_ERROR'.
+ * *
+ * *  INPUTS
+ * *     FILE *stream       - output stream
+ * *     const char *format - format string
+ * *     ...
+ * *
+ * *  NOTES
+ * *     Don't forget to define the 'FSCANF_ERROR'-label
+ * 
******************************************************************************/
+#define FSCANF(x) \
+   if (fscanf x < 0) { \
+      goto FSCANF_ERROR; \
+   }
+
 /****** uti/stdio/FPRINTF() ***************************************************
 *  NAME
 *     FPRINTF() -- fprintf() macro 
diff -ruN sge-8.1.8-cgroups/doc/man/man5/sge_conf.5 
sge-8.1.8/doc/man/man5/sge_conf.5
--- sge-8.1.8-cgroups/doc/man/man5/sge_conf.5   2015-03-31 14:18:00.010993411 
-0400
+++ sge-8.1.8/doc/man/man5/sge_conf.5   2015-03-31 14:22:51.305493493 -0400
@@ -1559,6 +1559,10 @@
 .I sources/experimental/qidle
 in the source repository, but it may be better to check the
 the screensaver state.
+.IP \fIENABLE_USEDMEMORY\fP
+If this parameter is set to true, then xxQS_NAMExx will use measure
+virtual memory usage (vmem) as the sum of physical memory and swap
+occupied by jobs.
 .IP \fICGROUP_MEMORY\fP
 If this parameter is set to a directory, then xxQS_NAMExx will create a
 cgroup in that location for each job instance running on the execd. It
diff -ruN sge-8.1.8-cgroups/source/daemons/common/procfs.c 
sge-8.1.8/source/daemons/common/procfs.c
--- sge-8.1.8-cgroups/source/daemons/common/procfs.c    2014-10-23 
18:03:56.000000000 -0400
+++ sge-8.1.8/source/daemons/common/procfs.c    2015-03-31 14:22:51.308308744 
-0400
@@ -80,6 +80,7 @@
 #include "cull/cull.h"
 
 #include "basis_types.h"
+#include "sge_conf.h"
 #include "sgedefs.h"
 #include "exec_ifm.h"
 #include "pdc.h"
@@ -103,6 +104,8 @@
                               unsigned long *stime, unsigned long *vsize);
 #endif
 
+static bool enable_usedmemory = false;
+
 /*-----------------------------------------------------------------------*/
 static gid_t *list = 0;
 #if defined(LINUX) || defined(ALPHA) || defined(SOLARIS)
@@ -336,6 +339,8 @@
 
 int pt_open(void)
 {
+ enable_usedmemory = mconf_get_enable_usedmemory();
+ 
    cwd = opendir(PROC_DIR);
    return !cwd;
 }
@@ -399,6 +404,54 @@
    if (!(linux_read_status(pidname, time_stamp, job_list,
                            &pid, &utime, &stime, &vsize) != 0))
       goto return0;
+
+         /*
+          * Obtain accurate memory usage statistics, if requested
+          * (instead of virtual memory).
+          */
+         if (enable_usedmemory) {
+            vsize = 0;
+            unsigned long value = 0;
+            int found_swap = 0;
+
+            FILE *fp;
+            char membuf[BIGLINE];
+            char key[10]; /* Size must match width in sscanf */
+
+            snprintf(membuf, sizeof(membuf), PROC_DIR "/%s/status", 
dent->d_name);
+            if ((fp = fopen(membuf, "r"))) {
+               while(fgets(membuf, sizeof(membuf), fp)) {
+                  if (sscanf(membuf, "%10s %lu", key, &value) == 2) {
+                     if (strncmp(membuf, "VmRSS:", 6) == 0) {
+                        vsize += value * 1024;
+                     } else if(strncmp(membuf, "VmSwap:", 7) == 0) {
+                        vsize += value * 1024;
+                        found_swap++;
+                     }
+                  }
+               }
+               fclose(fp);
+            }
+
+            /* Older Linux kernels keep swap usage elsewhere,  */
+            /* where values from each map need to be added up. */
+            if (found_swap == 0) {
+               snprintf(membuf, sizeof(membuf), PROC_DIR "/%s/smaps", 
dent->d_name);
+               if ((fp = fopen(membuf, "r"))) {
+                  while(fgets(membuf, sizeof(membuf), fp)) {
+                     if (sscanf(membuf, "%10s %lu", key, &value) == 2) {
+                        if (strncmp(membuf, "Swap:", 5) == 0) {
+                           vsize += value * 1024;
+                           found_swap++;
+                        }
+                     }
+                  }
+                  fclose(fp);
+               }
+            }
+         }
+
+
    if (pr == NULL) {
       pr = lCreateElem(PRO_Type);
       lSetPosUlong(pr, pos_pid, pid);
diff -ruN sge-8.1.8-cgroups/source/libs/sgeobj/sge_conf.c 
sge-8.1.8/source/libs/sgeobj/sge_conf.c
--- sge-8.1.8-cgroups/source/libs/sgeobj/sge_conf.c     2015-03-31 
14:18:00.176952557 -0400
+++ sge-8.1.8/source/libs/sgeobj/sge_conf.c     2015-03-31 14:22:51.322200878 
-0400
@@ -190,6 +190,7 @@
 static bool keep_active = false;
 static bool enable_windomacc = false;
 static bool enable_binding = false;
+static bool enable_usedmemory = false;
 static char* cgroup_memory = NULL;
 static bool enable_addgrp_kill = true;
 static u_long32 pdc_interval = 1;
@@ -875,6 +876,7 @@
       keep_active = false;
       enable_windomacc = false;
       enable_binding = HAVE_HWLOC ? true : false;
+      enable_usedmemory = false;
       sge_free(&cgroup_memory);
       enable_addgrp_kill = true;
       use_qsub_gid = false;
@@ -935,6 +937,9 @@
          if (parse_bool_param(s, "ENABLE_BINDING", &enable_binding)) {
             continue;
          }
+         if (parse_bool_param(s, "ENABLE_USEDMEMORY", &enable_usedmemory)) {
+            continue;
+         }
          if (!strncasecmp(s, "CGROUP_MEMORY", sizeof("CGROUP_MEMORY")-1)) {
             cgroup_memory = sge_strdup(NULL, &(s[sizeof("CGROUP_MEMORY")]));
             continue;
@@ -2104,6 +2109,18 @@
    DRETURN(ret);
 }
 
+bool mconf_get_enable_usedmemory(void) {
+   bool ret;
+
+   DENTER(BASIS_LAYER, "mconf_get_enable_usedmemory");
+   SGE_LOCK(LOCK_MASTER_CONF, LOCK_READ);
+
+   ret = enable_usedmemory;
+
+   SGE_UNLOCK(LOCK_MASTER_CONF, LOCK_READ);
+   DRETURN(ret);
+}
+
 /* returned pointer needs to be freed */
 char* mconf_get_cgroup_memory(void) {
    char* ret = NULL;
diff -ruN sge-8.1.8-cgroups/source/libs/sgeobj/sge_conf.h 
sge-8.1.8/source/libs/sgeobj/sge_conf.h
--- sge-8.1.8-cgroups/source/libs/sgeobj/sge_conf.h     2015-03-31 
14:18:00.186024000 -0400
+++ sge-8.1.8/source/libs/sgeobj/sge_conf.h     2015-03-31 14:22:51.332964000 
-0400
@@ -117,6 +117,7 @@
 bool mconf_get_keep_active(void);
 bool mconf_get_enable_windomacc(void);
 bool mconf_get_enable_binding(void);
+bool mconf_get_enable_usedmemory(void);
 char* mconf_get_cgroup_memory(void);
 bool mconf_get_simulate_execds(void);
 bool mconf_get_simulate_jobs(void);
_______________________________________________
SGE-discuss mailing list
[email protected]
https://arc.liv.ac.uk/mailman/listinfo/sge-discuss

Reply via email to