Whamcloud - gitweb
LU-10698 obdclass: allow specifying complex jobids 91/31691/7
authorAndreas Dilger <andreas.dilger@intel.com>
Tue, 20 Mar 2018 09:45:36 +0000 (03:45 -0600)
committerOleg Drokin <oleg.drokin@intel.com>
Mon, 9 Apr 2018 19:46:03 +0000 (19:46 +0000)
Allow specifying a format string for the jobid_name variable to create
a jobid for processes on the client.  The jobid_name is used when
jobid_var=nodelocal, if jobid_name contains "%j", or as a fallback if
getting the specified jobid_var from the environment fails.

The jobid_node string allows the following escape sequences:

    %e = executable name
    %g = group ID
    %h = hostname (system utsname)
    %j = jobid from jobid_var environment variable
    %p = process ID
    %u = user ID

Any unknown escape sequences are dropped. Other arbitrary characters
pass through unmodified, up to the maximum jobid string size of 32,
though whitespace within the jobid is not copied.

This allows, for example, specifying an arbitrary prefix, such as the
cluster name, in addition to the traditional "procname.uid" format,
to distinguish between jobs running on clients in different clusters:

    lctl set_param jobid_var=nodelocal jobid_name=cluster2.%e.%u
or
    lctl set_param jobid_var=SLURM_JOB_ID jobid_name=cluster2.%j.%e

To use an environment-specified JobID, if available, but fall back to
a static string for all processes that do not have a valid JobID:

    lctl set_param jobid_var=SLURM_JOB_ID jobid_name=unknown

Implementation notes:

The LUSTRE_JOBID_SIZE includes a trailing NUL, so don't use
"LUSTRE_JOBID_SIZE + 1" anywhere, as that is misleading.

Rename the "obd_jobid_node" variable to "obd_jobid_name" to match
the /proc "jobid_name" parameter name to avoid confusion.

Rename "struct jobid_to_pid_map" to "jobid_pid_map" since this is
not actually mapping from a jobid *to* a PID, but the reverse.
Save jobid length, and reorder fields to avoid holes in structure.

Consolidate PID->jobid cache handling in jobid_get_from_cache(),
which only does environment lookups and caches the results.
The fallback to using obd_jobid_name is handled by the caller.

Rename check_job_name() to jobid_name_is_valid(), since that makes
it clear to the reader a "true" return is a valid name.

In jobid_cache_init() there is no benefit for locking the jobid_hash
creation, since the spinlock is just initialized in this function,
so multiple callers of this function would already be broken.

Pass the buffer size from the callers (who know the buffer size) to
lustre_get_jobid() instead of assuming it is LUSTRE_JOBID_SIZE.

Signed-off-by: Andreas Dilger <andreas.dilger@intel.com>
Change-Id: Iad350e87b446c7d2356718cf2e5f9563e63ebbe5
Reviewed-on: https://review.whamcloud.com/31691
Tested-by: Jenkins
Reviewed-by: Jinshan Xiong <jinshan.xiong@gmail.com>
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: Ben Evans <bevans@cray.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
12 files changed:
libcfs/libcfs/linux/linux-curproc.c
lustre/include/obd_class.h
lustre/include/uapi/linux/lustre/lustre_idl.h
lustre/llite/llite_internal.h
lustre/llite/llite_lib.c
lustre/llite/vvp_io.c
lustre/llite/vvp_object.c
lustre/obdclass/jobid.c
lustre/obdclass/linux/linux-module.c
lustre/obdclass/lprocfs_jobstats.c
lustre/ptlrpc/pack_generic.c
lustre/tests/sanity.sh

index 92c48d9..fee47a2 100644 (file)
@@ -262,9 +262,14 @@ int cfs_get_environ(const char *key, char *value, int *val_len)
                            !memcmp(entry, key, key_len)) {
                                entry += key_len + 1;
                                entry_len -= key_len + 1;
-                               /* The 'value' buffer passed in is too small.*/
-                               if (entry_len >= *val_len)
+
+                               /* The 'value' buffer passed in is too small.
+                                * Copy what fits, but return -EOVERFLOW. */
+                               if (entry_len >= *val_len) {
+                                       memcpy(value, entry, *val_len);
+                                       value[*val_len - 1] = 0;
                                        GOTO(out, rc = -EOVERFLOW);
+                               }
 
                                memcpy(value, entry, entry_len);
                                *val_len = entry_len;
index 25bf2d6..f9c02dd 100644 (file)
@@ -54,7 +54,7 @@ extern rwlock_t obd_dev_lock;
 extern struct obd_device *class_conn2obd(struct lustre_handle *);
 extern struct obd_device *class_exp2obd(struct obd_export *);
 extern int class_handle_ioctl(unsigned int cmd, unsigned long arg);
-int lustre_get_jobid(char *jobid);
+int lustre_get_jobid(char *jobid, size_t len);
 void lustre_jobid_clear(const char *jobid);
 void jobid_cache_fini(void);
 int jobid_cache_init(void);
@@ -1889,7 +1889,7 @@ int class_del_uuid (const char *uuid);
 int class_check_uuid(struct obd_uuid *uuid, __u64 nid);
 
 /* class_obd.c */
-extern char obd_jobid_node[];
+extern char obd_jobid_name[];
 
 /* prng.c */
 #define ll_generate_random_uuid(uuid_out) cfs_get_random_bytes(uuid_out, sizeof(class_uuid_t))
index 1ec7963..c4c2f53 100644 (file)
@@ -671,7 +671,7 @@ struct ptlrpc_body_v3 {
        __u64 pb_padding64_0;
        __u64 pb_padding64_1;
        __u64 pb_padding64_2;
-       char  pb_jobid[LUSTRE_JOBID_SIZE]; /* req: ASCII MPI jobid from env */
+       char  pb_jobid[LUSTRE_JOBID_SIZE]; /* req: ASCII jobid from env + NUL */
 };
 #define ptlrpc_body     ptlrpc_body_v3
 
index ea739d5..b885014 100644 (file)
@@ -178,8 +178,8 @@ struct ll_inode_info {
 
                /* for non-directory */
                struct {
-                       struct mutex                    lli_size_mutex;
-                       char                           *lli_symlink_name;
+                       struct mutex            lli_size_mutex;
+                       char                   *lli_symlink_name;
                        /*
                         * struct rw_semaphore {
                         *    signed long       count;     // align d.d_def_acl
@@ -187,23 +187,23 @@ struct ll_inode_info {
                         *    struct list_head wait_list;
                         * }
                         */
-                       struct rw_semaphore             lli_trunc_sem;
-                       struct range_lock_tree          lli_write_tree;
+                       struct rw_semaphore     lli_trunc_sem;
+                       struct range_lock_tree  lli_write_tree;
 
-                       struct rw_semaphore             lli_glimpse_sem;
-                       ktime_t                         lli_glimpse_time;
-                       struct list_head                lli_agl_list;
-                       __u64                           lli_agl_index;
+                       struct rw_semaphore     lli_glimpse_sem;
+                       ktime_t                 lli_glimpse_time;
+                       struct list_head        lli_agl_list;
+                       __u64                   lli_agl_index;
 
                        /* for writepage() only to communicate to fsync */
-                       int                             lli_async_rc;
+                       int                     lli_async_rc;
 
                        /*
-                        * whenever a process try to read/write the file, the
+                        * Whenever a process try to read/write the file, the
                         * jobid of the process will be saved here, and it'll
                         * be packed into the write PRC when flush later.
                         *
-                        * so the read/write statistics for jobid will not be
+                        * So the read/write statistics for jobid will not be
                         * accurate if the file is shared by different jobs.
                         */
                        char                    lli_jobid[LUSTRE_JOBID_SIZE];
index 3534e6d..ccd8863 100644 (file)
@@ -955,7 +955,7 @@ void ll_lli_init(struct ll_inode_info *lli)
                lli->lli_async_rc = 0;
        }
        mutex_init(&lli->lli_layout_mutex);
-       memset(lli->lli_jobid, 0, LUSTRE_JOBID_SIZE);
+       memset(lli->lli_jobid, 0, sizeof(lli->lli_jobid));
 }
 
 #ifndef HAVE_SUPER_SETUP_BDI_NAME
index 045f4dd..793ec00 100644 (file)
@@ -1472,7 +1472,7 @@ int vvp_io_init(const struct lu_env *env, struct cl_object *obj,
                 * it's not accurate if the file is shared by different
                 * jobs.
                 */
-               lustre_get_jobid(lli->lli_jobid);
+               lustre_get_jobid(lli->lli_jobid, sizeof(lli->lli_jobid));
        } else if (io->ci_type == CIT_SETATTR) {
                if (!cl_io_is_trunc(io))
                        io->ci_lockreq = CILR_MANDATORY;
index 1b3fd92..f4c695c 100644 (file)
@@ -219,7 +219,8 @@ static void vvp_req_attr_set(const struct lu_env *env, struct cl_object *obj,
        obdo_set_parent_fid(oa, &ll_i2info(inode)->lli_fid);
        if (OBD_FAIL_CHECK(OBD_FAIL_LFSCK_INVALID_PFID))
                oa->o_parent_oid++;
-       memcpy(attr->cra_jobid, ll_i2info(inode)->lli_jobid, LUSTRE_JOBID_SIZE);
+       memcpy(attr->cra_jobid, ll_i2info(inode)->lli_jobid,
+              sizeof(attr->cra_jobid));
 }
 
 static const struct cl_object_operations vvp_ops = {
index 5b772e3..842def4 100644 (file)
@@ -36,6 +36,7 @@
 #ifdef HAVE_UIDGID_HEADER
 #include <linux/uidgid.h>
 #endif
+#include <linux/utsname.h>
 
 #include <libcfs/linux/linux-misc.h>
 #include <obd_support.h>
@@ -50,36 +51,39 @@ spinlock_t jobid_hash_lock;
 #define DELETE_INTERVAL 300
 
 char obd_jobid_var[JOBSTATS_JOBID_VAR_MAX_LEN + 1] = JOBSTATS_DISABLE;
-char obd_jobid_node[LUSTRE_JOBID_SIZE + 1];
+char obd_jobid_name[LUSTRE_JOBID_SIZE] = "%e.%u";
 
 /**
- * Structure to store a single jobID/PID mapping
+ * Structure to store a single PID->JobID mapping
  */
-struct jobid_to_pid_map {
+struct jobid_pid_map {
        struct hlist_node       jp_hash;
        time64_t                jp_time;
-       atomic_t                jp_refcount;
        spinlock_t              jp_lock; /* protects jp_jobid */
-       char                    jp_jobid[LUSTRE_JOBID_SIZE + 1];
+       char                    jp_jobid[LUSTRE_JOBID_SIZE];
+       unsigned int            jp_joblen;
+       atomic_t                jp_refcount;
        pid_t                   jp_pid;
 };
 
-/* Get jobid of current process by reading the environment variable
+/*
+ * Get jobid of current process by reading the environment variable
  * stored in between the "env_start" & "env_end" of task struct.
  *
  * If some job scheduler doesn't store jobid in the "env_start/end",
  * then an upcall could be issued here to get the jobid by utilizing
  * the userspace tools/API. Then, the jobid must be cached.
  */
-int get_jobid_from_environ(char *jobid_var, char *jobid, int jobid_len)
+int jobid_get_from_environ(char *jobid_var, char *jobid, int *jobid_len)
 {
+       static bool printed;
        int rc;
 
-       rc = cfs_get_environ(jobid_var, jobid, &jobid_len);
+       rc = cfs_get_environ(jobid_var, jobid, jobid_len);
        if (!rc)
                goto out;
 
-       if (rc == -EOVERFLOW) {
+       if (unlikely(rc == -EOVERFLOW && !printed)) {
                /* For the PBS_JOBID and LOADL_STEP_ID keys (which are
                 * variable length strings instead of just numbers), it
                 * might make sense to keep the unique parts for JobID,
@@ -87,17 +91,16 @@ int get_jobid_from_environ(char *jobid_var, char *jobid, int jobid_len)
                 * larger temp buffer for cfs_get_environ(), then
                 * truncating the string at some separator to fit into
                 * the specified jobid_len.  Fix later if needed. */
-               static bool printed;
-               if (unlikely(!printed)) {
-                       LCONSOLE_ERROR_MSG(0x16b, "%s value too large "
-                                          "for JobID buffer (%d)\n",
-                                          obd_jobid_var, jobid_len);
-                       printed = true;
-               }
-       } else {
+               LCONSOLE_ERROR_MSG(0x16b,
+                                  "jobid: '%s' value too large (%d)\n",
+                                  obd_jobid_var, *jobid_len);
+               printed = true;
+               rc = 0;
+       }
+       if (rc) {
                CDEBUG((rc == -ENOENT || rc == -EINVAL ||
                        rc == -EDEADLK) ? D_INFO : D_ERROR,
-                      "Get jobid for (%s) failed: rc = %d\n",
+                      "jobid: get '%s' failed: rc = %d\n",
                       obd_jobid_var, rc);
        }
 
@@ -118,7 +121,7 @@ out:
 static int jobid_should_free_item(void *obj, void *data)
 {
        char *jobid = data;
-       struct jobid_to_pid_map *pidmap = obj;
+       struct jobid_pid_map *pidmap = obj;
        int rc = 0;
 
        if (obj == NULL)
@@ -139,19 +142,22 @@ static int jobid_should_free_item(void *obj, void *data)
 }
 
 /*
- * check_job_name
+ * jobid_name_is_valid
  *
  * Checks if the jobid is a Lustre process
  *
  * Returns true if jobid is valid
  * Returns false if jobid looks like it's a Lustre process
  */
-static bool check_job_name(char *jobid)
+static bool jobid_name_is_valid(char *jobid)
 {
-       const char *const lustre_reserved[] = {"ll_ping", "ptlrpc",
-                                               "ldlm", "ll_sa", NULL};
+       const char *const lustre_reserved[] = { "ll_ping", "ptlrpc",
+                                               "ldlm", "ll_sa", NULL };
        int i;
 
+       if (jobid[0] == '\0')
+               return false;
+
        for (i = 0; lustre_reserved[i] != NULL; i++) {
                if (strncmp(jobid, lustre_reserved[i],
                            strlen(lustre_reserved[i])) == 0)
@@ -161,27 +167,44 @@ static bool check_job_name(char *jobid)
 }
 
 /*
- * get_jobid
- *
- * Returns the jobid for the current pid.
+ * jobid_get_from_cache()
  *
- * If no jobid is found in the table, the jobid is calculated based on
- * the value of jobid_var, using procname_uid as the default.
+ * Returns contents of jobid_var from process environment for current PID.
+ * This will be cached for some time to avoid overhead scanning environment.
  *
  * Return: -ENOMEM if allocating a new pidmap fails
- *         0 for success
+ *         -ENOENT if no entry could be found
+ *         +ve string length for success (something was returned in jobid)
  */
-int get_jobid(char *jobid)
+static int jobid_get_from_cache(char *jobid, size_t joblen)
 {
+       static time64_t last_expire;
+       bool expire_cache = false;
        pid_t pid = current_pid();
-       struct jobid_to_pid_map *pidmap = NULL;
-       struct jobid_to_pid_map *pidmap2;
-       char tmp_jobid[LUSTRE_JOBID_SIZE + 1];
+       struct jobid_pid_map *pidmap = NULL;
+       time64_t now = ktime_get_real_seconds();
        int rc = 0;
        ENTRY;
 
+       LASSERT(jobid_hash != NULL);
+
+       /* scan hash periodically to remove old PID entries from cache */
+       spin_lock(&jobid_hash_lock);
+       if (unlikely(last_expire + DELETE_INTERVAL <= now)) {
+               expire_cache = true;
+               last_expire = now;
+       }
+       spin_unlock(&jobid_hash_lock);
+
+       if (expire_cache)
+               cfs_hash_cond_del(jobid_hash, jobid_should_free_item,
+                                 "intentionally_bad_jobid");
+
+       /* first try to find PID in the hash and use that value */
        pidmap = cfs_hash_lookup(jobid_hash, &pid);
        if (pidmap == NULL) {
+               struct jobid_pid_map *pidmap2;
+
                OBD_ALLOC_PTR(pidmap);
                if (pidmap == NULL)
                        GOTO(out, rc = -ENOMEM);
@@ -195,13 +218,13 @@ int get_jobid(char *jobid)
                /*
                 * Add the newly created map to the hash, on key collision we
                 * lost a racing addition and must destroy our newly allocated
-                * map.  The object which exists in the hash will be
-                * returned.
+                * map.  The object which exists in the hash will be returned.
                 */
                pidmap2 = cfs_hash_findadd_unique(jobid_hash, &pid,
                                                  &pidmap->jp_hash);
                if (unlikely(pidmap != pidmap2)) {
-                       CDEBUG(D_INFO, "Duplicate jobid found\n");
+                       CDEBUG(D_INFO, "jobid: duplicate found for PID=%u\n",
+                              pid);
                        OBD_FREE_PTR(pidmap);
                        pidmap = pidmap2;
                } else {
@@ -209,56 +232,136 @@ int get_jobid(char *jobid)
                }
        }
 
+       /*
+        * If pidmap is old (this is always true for new entries) refresh it.
+        * If obd_jobid_var is not found, cache empty entry and try again
+        * later, to avoid repeat lookups for PID if obd_jobid_var missing.
+        */
        spin_lock(&pidmap->jp_lock);
-       if ((ktime_get_real_seconds() - pidmap->jp_time >= RESCAN_INTERVAL) ||
-           pidmap->jp_jobid[0] == '\0') {
-               /* mark the pidmap as being up to date, if we fail to find
-                * a good jobid, revert to the old time and try again later
-                * prevent a race with deletion */
+       if (pidmap->jp_time + RESCAN_INTERVAL <= now) {
+               char env_jobid[LUSTRE_JOBID_SIZE] = "";
+               int env_len = sizeof(env_jobid);
 
-               time64_t tmp_time = pidmap->jp_time;
-               pidmap->jp_time = ktime_get_real_seconds();
+               pidmap->jp_time = now;
 
                spin_unlock(&pidmap->jp_lock);
-               if (strcmp(obd_jobid_var, JOBSTATS_PROCNAME_UID) == 0) {
-                       rc = 1;
-               } else {
-                       memset(tmp_jobid, '\0', LUSTRE_JOBID_SIZE + 1);
-                       rc = get_jobid_from_environ(obd_jobid_var,
-                                                   tmp_jobid,
-                                                   LUSTRE_JOBID_SIZE + 1);
-               }
+               rc = jobid_get_from_environ(obd_jobid_var, env_jobid, &env_len);
 
-               /* Use process name + fsuid as jobid default, or when
-                * specified by "jobname_uid" */
-               if (rc) {
-                       snprintf(tmp_jobid, LUSTRE_JOBID_SIZE, "%s.%u",
-                                current_comm(),
-                                from_kuid(&init_user_ns, current_fsuid()));
+               CDEBUG(D_INFO, "jobid: PID mapping established: %d->%s\n",
+                      pidmap->jp_pid, env_jobid);
+               spin_lock(&pidmap->jp_lock);
+               if (!rc) {
+                       pidmap->jp_joblen = env_len;
+                       strlcpy(pidmap->jp_jobid, env_jobid,
+                               sizeof(pidmap->jp_jobid));
                        rc = 0;
+               } else if (rc == -ENOENT) {
+                       /* It might have been deleted, clear out old entry */
+                       pidmap->jp_joblen = 0;
+                       pidmap->jp_jobid[0] = '\0';
                }
-
-               CDEBUG(D_INFO, "Jobid to pid mapping established: %d->%s\n",
-                      pidmap->jp_pid, tmp_jobid);
-
-               spin_lock(&pidmap->jp_lock);
-               if (check_job_name(tmp_jobid))
-                       strncpy(pidmap->jp_jobid, tmp_jobid,
-                               LUSTRE_JOBID_SIZE);
-               else
-                       pidmap->jp_time = tmp_time;
        }
 
-       if (strlen(pidmap->jp_jobid) != 0)
-               strncpy(jobid, pidmap->jp_jobid, LUSTRE_JOBID_SIZE);
-
+       /*
+        * Regardless of how pidmap was found, if it contains a valid entry
+        * use that for now.  If there was a technical error (e.g. -ENOMEM)
+        * use the old cached value until it can be looked up again properly.
+        * If a cached missing entry was found, return -ENOENT.
+        */
+       if (pidmap->jp_joblen) {
+               strlcpy(jobid, pidmap->jp_jobid, joblen);
+               joblen = pidmap->jp_joblen;
+               rc = 0;
+       } else if (!rc) {
+               rc = -ENOENT;
+       }
        spin_unlock(&pidmap->jp_lock);
 
        cfs_hash_put(jobid_hash, &pidmap->jp_hash);
 
        EXIT;
 out:
-       return rc;
+       return rc < 0 ? rc : joblen;
+}
+
+/*
+ * jobid_interpret_string()
+ *
+ * Interpret the jobfmt string to expand specified fields, like coredumps do:
+ *   %e = executable
+ *   %g = gid
+ *   %h = hostname
+ *   %j = jobid from environment
+ *   %p = pid
+ *   %u = uid
+ *
+ * Unknown escape strings are dropped.  Other characters are copied through,
+ * excluding whitespace (to avoid making jobid parsing difficult).
+ *
+ * Return: -EOVERFLOW if the expanded string does not fit within @joblen
+ *         0 for success
+ */
+static int jobid_interpret_string(const char *jobfmt, char *jobid,
+                                 ssize_t joblen)
+{
+       char c;
+
+       while ((c = *jobfmt++) && joblen > 1) {
+               char f;
+               int l;
+
+               if (isspace(c)) /* Don't allow embedded spaces */
+                       continue;
+
+               if (c != '%') {
+                       *jobid = c;
+                       joblen--;
+                       jobid++;
+                       continue;
+               }
+
+               switch ((f = *jobfmt++)) {
+               case 'e': /* executable name */
+                       l = snprintf(jobid, joblen, "%s", current_comm());
+                       break;
+               case 'g': /* group ID */
+                       l = snprintf(jobid, joblen, "%u",
+                                    from_kgid(&init_user_ns, current_fsgid()));
+                       break;
+               case 'h': /* hostname */
+                       l = snprintf(jobid, joblen, "%s",
+                                    init_utsname()->nodename);
+                       break;
+               case 'j': /* jobid stored in process environment */
+                       l = jobid_get_from_cache(jobid, joblen);
+                       if (l < 0)
+                               l = 0;
+                       break;
+               case 'p': /* process ID */
+                       l = snprintf(jobid, joblen, "%u", current_pid());
+                       break;
+               case 'u': /* user ID */
+                       l = snprintf(jobid, joblen, "%u",
+                                    from_kuid(&init_user_ns, current_fsuid()));
+                       break;
+               case '\0': /* '%' at end of format string */
+                       l = 0;
+                       goto out;
+               default: /* drop unknown %x format strings */
+                       l = 0;
+                       break;
+               }
+               jobid += l;
+               joblen -= l;
+       }
+       /*
+        * This points at the end of the buffer, so long as jobid is always
+        * incremented the same amount as joblen is decremented.
+        */
+out:
+       jobid[joblen - 1] = '\0';
+
+       return joblen < 0 ? -EOVERFLOW : 0;
 }
 
 /*
@@ -271,30 +374,16 @@ out:
 int jobid_cache_init(void)
 {
        int rc = 0;
-       struct cfs_hash *tmp_jobid_hash;
        ENTRY;
 
-       spin_lock_init(&jobid_hash_lock);
-
-       tmp_jobid_hash = cfs_hash_create("JOBID_HASH",
-                                        HASH_JOBID_CUR_BITS,
-                                        HASH_JOBID_MAX_BITS,
-                                        HASH_JOBID_BKT_BITS, 0,
-                                        CFS_HASH_MIN_THETA,
-                                        CFS_HASH_MAX_THETA,
-                                        &jobid_hash_ops,
-                                        CFS_HASH_DEFAULT);
-
-       spin_lock(&jobid_hash_lock);
-       if (jobid_hash == NULL) {
-               jobid_hash = tmp_jobid_hash;
-               spin_unlock(&jobid_hash_lock);
-       } else {
-               spin_unlock(&jobid_hash_lock);
-               if (tmp_jobid_hash != NULL)
-                       cfs_hash_putref(tmp_jobid_hash);
-       }
+       if (jobid_hash)
+               return 0;
 
+       spin_lock_init(&jobid_hash_lock);
+       jobid_hash = cfs_hash_create("JOBID_HASH", HASH_JOBID_CUR_BITS,
+                                    HASH_JOBID_MAX_BITS, HASH_JOBID_BKT_BITS,
+                                    0, CFS_HASH_MIN_THETA, CFS_HASH_MAX_THETA,
+                                    &jobid_hash_ops, CFS_HASH_DEFAULT);
        if (!jobid_hash)
                rc = -ENOMEM;
 
@@ -332,9 +421,9 @@ static unsigned jobid_hashfn(struct cfs_hash *hs, const void *key,
 
 static void *jobid_key(struct hlist_node *hnode)
 {
-       struct jobid_to_pid_map *pidmap;
+       struct jobid_pid_map *pidmap;
 
-       pidmap = hlist_entry(hnode, struct jobid_to_pid_map, jp_hash);
+       pidmap = hlist_entry(hnode, struct jobid_pid_map, jp_hash);
        return &pidmap->jp_pid;
 }
 
@@ -352,26 +441,26 @@ static int jobid_keycmp(const void *key, struct hlist_node *hnode)
 
 static void *jobid_object(struct hlist_node *hnode)
 {
-       return hlist_entry(hnode, struct jobid_to_pid_map, jp_hash);
+       return hlist_entry(hnode, struct jobid_pid_map, jp_hash);
 }
 
 static void jobid_get(struct cfs_hash *hs, struct hlist_node *hnode)
 {
-       struct jobid_to_pid_map *pidmap;
+       struct jobid_pid_map *pidmap;
 
-       pidmap = hlist_entry(hnode, struct jobid_to_pid_map, jp_hash);
+       pidmap = hlist_entry(hnode, struct jobid_pid_map, jp_hash);
 
        atomic_inc(&pidmap->jp_refcount);
 }
 
 static void jobid_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
 {
-       struct jobid_to_pid_map *pidmap;
+       struct jobid_pid_map *pidmap;
 
        if (hnode == NULL)
                return;
 
-       pidmap = hlist_entry(hnode, struct jobid_to_pid_map, jp_hash);
+       pidmap = hlist_entry(hnode, struct jobid_pid_map, jp_hash);
        LASSERT(atomic_read(&pidmap->jp_refcount) > 0);
        if (atomic_dec_and_test(&pidmap->jp_refcount)) {
                CDEBUG(D_INFO, "Freeing: %d->%s\n",
@@ -391,46 +480,55 @@ static struct cfs_hash_ops jobid_hash_ops = {
        .hs_put_locked  = jobid_put_locked,
 };
 
-/*
- * Return the jobid:
+/**
+ * Generate the job identifier string for this process for tracking purposes.
+ *
+ * Fill in @jobid string based on the value of obd_jobid_var:
+ * JOBSTATS_DISABLE:      none
+ * JOBSTATS_NODELOCAL:    content of obd_jobid_node (jobid_interpret_string())
+ * JOBSTATS_PROCNAME_UID: process name/UID
+ * anything else:         look up obd_jobid_var in the processes environment
  *
- * Based on the value of obd_jobid_var
- * JOBSTATS_DISABLE:  none
- * JOBSTATS_NODELOCAL:  Contents of obd_jobid_name
- * JOBSTATS_PROCNAME_UID:  Process name/UID
- * anything else:  Look up the value in the processes environment
- * default: JOBSTATS_PROCNAME_UID
+ * Return -ve error number, 0 on success.
  */
-
-int lustre_get_jobid(char *jobid)
+int lustre_get_jobid(char *jobid, size_t joblen)
 {
        int rc = 0;
-       int clear = 0;
-       static time64_t last_delete;
        ENTRY;
 
-       LASSERT(jobid_hash != NULL);
-
-       spin_lock(&jobid_hash_lock);
-       if (last_delete + DELETE_INTERVAL <= ktime_get_real_seconds()) {
-               clear = 1;
-               last_delete = ktime_get_real_seconds();
+       if (unlikely(joblen < 2)) {
+               if (joblen == 1)
+                       jobid[0] = '\0';
+               RETURN(-EINVAL);
        }
-       spin_unlock(&jobid_hash_lock);
-
-       if (clear)
-               cfs_hash_cond_del(jobid_hash, jobid_should_free_item,
-                                 "intentionally_bad_jobid");
 
-       if (strcmp(obd_jobid_var, JOBSTATS_DISABLE) == 0)
+       if (strcmp(obd_jobid_var, JOBSTATS_DISABLE) == 0) {
                /* Jobstats isn't enabled */
-               memset(jobid, 0, LUSTRE_JOBID_SIZE);
-       else if (strcmp(obd_jobid_var, JOBSTATS_NODELOCAL) == 0)
+               memset(jobid, 0, joblen);
+       } else if (strcmp(obd_jobid_var, JOBSTATS_NODELOCAL) == 0) {
                /* Whole node dedicated to single job */
-               memcpy(jobid, obd_jobid_node, LUSTRE_JOBID_SIZE);
-       else
-               /* Get jobid from hash table */
-               rc = get_jobid(jobid);
+               rc = jobid_interpret_string(obd_jobid_name, jobid, joblen);
+       } else if (strcmp(obd_jobid_var, JOBSTATS_PROCNAME_UID) == 0) {
+               rc = jobid_interpret_string("%e.%u", jobid, joblen);
+       } else if (jobid_name_is_valid(current_comm())) {
+               /*
+                * obd_jobid_var holds the jobid environment variable name.
+                * Skip initial check if obd_jobid_name already uses "%j",
+                * otherwise try just "%j" first, then fall back to whatever
+                * is in obd_jobid_name if obd_jobid_var is not found.
+                */
+               rc = -EAGAIN;
+               if (!strnstr(obd_jobid_name, "%j", joblen))
+                       rc = jobid_get_from_cache(jobid, joblen);
+
+               /* fall back to jobid_node if jobid_var not in environment */
+               if (rc < 0) {
+                       int rc2 = jobid_interpret_string(obd_jobid_name,
+                                                        jobid, joblen);
+                       if (!rc2)
+                               rc = 0;
+               }
+       }
 
        RETURN(rc);
 }
@@ -439,20 +537,22 @@ EXPORT_SYMBOL(lustre_get_jobid);
 /*
  * lustre_jobid_clear
  *
- * uses value pushed in via jobid_name
+ * Search cache for JobID given by @find_jobid.
  * If any entries in the hash table match the value, they are removed
  */
-void lustre_jobid_clear(const char *data)
+void lustre_jobid_clear(const char *find_jobid)
 {
-       char jobid[LUSTRE_JOBID_SIZE + 1];
+       char jobid[LUSTRE_JOBID_SIZE];
+       char *end;
 
        if (jobid_hash == NULL)
                return;
 
-       strncpy(jobid, data, LUSTRE_JOBID_SIZE);
+       strlcpy(jobid, find_jobid, sizeof(jobid));
        /* trim \n off the end of the incoming jobid */
-       if (jobid[strlen(jobid) - 1] == '\n')
-               jobid[strlen(jobid) - 1] = '\0';
+       end = strchr(jobid, '\n');
+       if (end && *end == '\n')
+               *end = '\0';
 
        CDEBUG(D_INFO, "Clearing Jobid: %s\n", jobid);
        cfs_hash_cond_del(jobid_hash, jobid_should_free_item, jobid);
index 4b2d257..464d288 100644 (file)
@@ -481,8 +481,8 @@ static ssize_t jobid_name_show(struct kobject *kobj, struct attribute *attr,
 {
        int rc = 0;
 
-       if (strlen(obd_jobid_node))
-               rc = snprintf(buf, PAGE_SIZE, "%s\n", obd_jobid_node);
+       if (strlen(obd_jobid_name))
+               rc = snprintf(buf, PAGE_SIZE, "%s\n", obd_jobid_name);
        return rc;
 }
 
@@ -492,22 +492,23 @@ static ssize_t jobid_name_store(struct kobject *kobj, struct attribute *attr,
        if (!count || count > LUSTRE_JOBID_SIZE)
                return -EINVAL;
 
-       if (strcmp(obd_jobid_var, JOBSTATS_NODELOCAL) != 0) {
+       if (strcmp(obd_jobid_var, JOBSTATS_NODELOCAL) != 0 &&
+           !strchr(buffer, '%')) {
                lustre_jobid_clear(buffer);
                return count;
        }
 
        /* clear previous value */
-       memset(obd_jobid_node, 0, LUSTRE_JOBID_SIZE);
+       memset(obd_jobid_name, 0, LUSTRE_JOBID_SIZE);
 
-       memcpy(obd_jobid_node, buffer, count);
+       memcpy(obd_jobid_name, buffer, count);
 
        /* Trim the trailing '\n' if any */
-       if (obd_jobid_node[count - 1] == '\n') {
+       if (obd_jobid_name[count - 1] == '\n') {
                /* Don't echo just a newline */
                if (count == 1)
                        return -EINVAL;
-               obd_jobid_node[count - 1] = 0;
+               obd_jobid_name[count - 1] = 0;
        }
 
        return count;
index 518d9c3..d6d599f 100644 (file)
@@ -65,7 +65,7 @@ struct job_stat {
        struct hlist_node       js_hash;        /* hash struct for this jobid */
        struct list_head        js_list;        /* on ojs_list, with ojs_lock */
        atomic_t                js_refcount;    /* num users of this struct */
-       char                    js_jobid[LUSTRE_JOBID_SIZE]; /* job name */
+       char                    js_jobid[LUSTRE_JOBID_SIZE]; /* job name + NUL*/
        time64_t                js_timestamp;   /* seconds of most recent stat*/
        struct lprocfs_stats    *js_stats;      /* per-job statistics */
        struct obd_job_stats    *js_jobstats;   /* for accessing ojs_lock */
@@ -252,7 +252,7 @@ static struct job_stat *job_alloc(char *jobid, struct obd_job_stats *jobs)
 
        jobs->ojs_cntr_init_fn(job->js_stats);
 
-       memcpy(job->js_jobid, jobid, LUSTRE_JOBID_SIZE);
+       memcpy(job->js_jobid, jobid, sizeof(job->js_jobid));
        job->js_timestamp = ktime_get_real_seconds();
        job->js_jobstats = jobs;
        INIT_HLIST_NODE(&job->js_hash);
index d09ea4e..a66b6d4 100644 (file)
@@ -1513,9 +1513,9 @@ void lustre_msg_set_jobid(struct lustre_msg *msg, char *jobid)
                LASSERTF(pb, "invalid msg %p: no ptlrpc body!\n", msg);
 
                if (jobid != NULL)
-                       memcpy(pb->pb_jobid, jobid, LUSTRE_JOBID_SIZE);
+                       memcpy(pb->pb_jobid, jobid, sizeof(pb->pb_jobid));
                else if (pb->pb_jobid[0] == '\0')
-                       lustre_get_jobid(pb->pb_jobid);
+                       lustre_get_jobid(pb->pb_jobid, sizeof(pb->pb_jobid));
                return;
        }
        default:
index 79ba19c..c42f169 100755 (executable)
@@ -13225,6 +13225,7 @@ else
                JOBENV=FAKE_JOBID
        fi
 fi
+LUSTRE_JOBID_SIZE=31 # plus NUL terminator
 
 verify_jobstats() {
        local cmd=($1)
@@ -13243,16 +13244,16 @@ verify_jobstats() {
        [ "$JOBENV" = "FAKE_JOBID" ] &&
                FAKE_JOBID=id.$testnum.$(basename ${cmd[0]}).$RANDOM
 
-       JOBVAL=${!JOBENV}
+       JOBVAL=${!JOBENV:0:$LUSTRE_JOBID_SIZE}
 
        [ "$JOBENV" = "nodelocal" ] && {
-               FAKE_JOBID=id.$testnum.$(basename ${cmd[0]}).$RANDOM
+               FAKE_JOBID=id.$testnum.%e.$RANDOM
                $LCTL set_param jobid_name=$FAKE_JOBID
-               JOBVAL=$FAKE_JOBID
+               JOBVAL=${FAKE_JOBID/\%e/$(basename ${cmd[0]})}
        }
 
        log "Test: ${cmd[*]}"
-       log "Using JobID environment variable $JOBENV=$JOBVAL"
+       log "Using JobID environment $($LCTL get_param -n jobid_var)=$JOBVAL"
 
        if [ $JOBENV = "FAKE_JOBID" ]; then
                FAKE_JOBID=$JOBVAL ${cmd[*]}
@@ -13263,8 +13264,10 @@ verify_jobstats() {
        # all files are created on OST0000
        for facet in $facets; do
                local stats="*.$(convert_facet2label $facet).job_stats"
+
+               # strip out libtool wrappers for in-tree executables
                if [ $(do_facet $facet lctl get_param $stats |
-                      grep -c $JOBVAL) -ne 1 ]; then
+                      sed -e 's/\.lt-/./' | grep -c $JOBVAL) -ne 1 ]; then
                        do_facet $facet lctl get_param $stats
                        error "No jobstats for $JOBVAL found on $facet::$stats"
                fi
@@ -13292,11 +13295,9 @@ test_205() { # Job stats
        [[ $JOBID_VAR = disable ]] && skip "jobstats is disabled" && return
 
        local old_jobenv=$($LCTL get_param -n jobid_var)
-       if [ $old_jobenv != $JOBENV ]; then
-               jobstats_set $JOBENV
-               stack_trap "do_facet mgs \
-                       $LCTL conf_param $FSNAME.sys.jobid_var=$old_jobenv" EXIT
-       fi
+       [ $old_jobenv != $JOBENV ] && jobstats_set $JOBENV
+       stack_trap "do_facet mgs \
+               $LCTL conf_param $FSNAME.sys.jobid_var=$old_jobenv" EXIT
 
        changelog_register
 
@@ -13373,6 +13374,12 @@ test_205() { # Job stats
                [ $jobids -eq 0 ] ||
                        error "Unexpected jobids when jobid_var=$JOBENV"
        fi
+
+       lctl set_param jobid_var=USER jobid_name="S.%j.%e.%u.%h.E"
+       JOBENV="JOBCOMPLEX"
+       JOBCOMPLEX="S.$USER.touch.$(id -u).$(hostname).E"
+
+       verify_jobstats "touch $DIR/$tfile" $SINGLEMDS
 }
 run_test 205 "Verify job stats"