Whamcloud - gitweb
LU-14681 obclass: fix typo of comment on job ID
[fs/lustre-release.git] / lustre / obdclass / jobid.c
index 58f03b3..1093f78 100644 (file)
@@ -33,9 +33,7 @@
 
 #define DEBUG_SUBSYSTEM S_RPC
 #include <linux/user_namespace.h>
-#ifdef HAVE_UIDGID_HEADER
 #include <linux/uidgid.h>
-#endif
 #include <linux/utsname.h>
 
 #include <libcfs/libcfs.h>
@@ -211,6 +209,172 @@ static void jobid_prune_expedite(void)
        }
 }
 
+static int cfs_access_process_vm(struct task_struct *tsk,
+                                struct mm_struct *mm,
+                                unsigned long addr,
+                                void *buf, int len, int write)
+{
+       /* Just copied from kernel for the kernels which doesn't
+        * have access_process_vm() exported
+        */
+       struct vm_area_struct *vma;
+       struct page *page;
+       void *old_buf = buf;
+
+       /* Avoid deadlocks on mmap_sem if called from sys_mmap_pgoff(),
+        * which is already holding mmap_sem for writes.  If some other
+        * thread gets the write lock in the meantime, this thread will
+        * block, but at least it won't deadlock on itself.  LU-1735
+        */
+       if (!mmap_read_trylock(mm))
+               return -EDEADLK;
+
+       /* ignore errors, just check how much was successfully transferred */
+       while (len) {
+               int bytes, rc, offset;
+               void *maddr;
+
+#if defined(HAVE_GET_USER_PAGES_GUP_FLAGS)
+               rc = get_user_pages(addr, 1, write ? FOLL_WRITE : 0, &page,
+                                   &vma);
+#elif defined(HAVE_GET_USER_PAGES_6ARG)
+               rc = get_user_pages(addr, 1, write, 1, &page, &vma);
+#else
+               rc = get_user_pages(tsk, mm, addr, 1, write, 1, &page, &vma);
+#endif
+               if (rc <= 0)
+                       break;
+
+               bytes = len;
+               offset = addr & (PAGE_SIZE-1);
+               if (bytes > PAGE_SIZE-offset)
+                       bytes = PAGE_SIZE-offset;
+
+               maddr = kmap(page);
+               if (write) {
+                       copy_to_user_page(vma, page, addr,
+                                         maddr + offset, buf, bytes);
+                       set_page_dirty_lock(page);
+               } else {
+                       copy_from_user_page(vma, page, addr,
+                                           buf, maddr + offset, bytes);
+               }
+               kunmap(page);
+               put_page(page);
+               len -= bytes;
+               buf += bytes;
+               addr += bytes;
+       }
+       mmap_read_unlock(mm);
+
+       return buf - old_buf;
+}
+
+/* Read the environment variable of current process specified by @key. */
+static int cfs_get_environ(const char *key, char *value, int *val_len)
+{
+       struct mm_struct *mm;
+       char *buffer;
+       int buf_len = PAGE_SIZE;
+       int key_len = strlen(key);
+       unsigned long addr;
+       int rc;
+       bool skip = false;
+
+       ENTRY;
+       buffer = kmalloc(buf_len, GFP_USER);
+       if (!buffer)
+               RETURN(-ENOMEM);
+
+       mm = get_task_mm(current);
+       if (!mm) {
+               kfree(buffer);
+               RETURN(-EINVAL);
+       }
+
+       addr = mm->env_start;
+       while (addr < mm->env_end) {
+               int this_len, retval, scan_len;
+               char *env_start, *env_end;
+
+               memset(buffer, 0, buf_len);
+
+               this_len = min_t(int, mm->env_end - addr, buf_len);
+               retval = cfs_access_process_vm(current, mm, addr, buffer,
+                                              this_len, 0);
+               if (retval < 0)
+                       GOTO(out, rc = retval);
+               else if (retval != this_len)
+                       break;
+
+               addr += retval;
+
+               /* Parse the buffer to find out the specified key/value pair.
+                * The "key=value" entries are separated by '\0'.
+                */
+               env_start = buffer;
+               scan_len = this_len;
+               while (scan_len) {
+                       char *entry;
+                       int entry_len;
+
+                       env_end = memscan(env_start, '\0', scan_len);
+                       LASSERT(env_end >= env_start &&
+                               env_end <= env_start + scan_len);
+
+                       /* The last entry of this buffer cross the buffer
+                        * boundary, reread it in next cycle.
+                        */
+                       if (unlikely(env_end - env_start == scan_len)) {
+                               /* Just skip the entry larger than page size,
+                                * it can't be jobID env variable.
+                                */
+                               if (unlikely(scan_len == this_len))
+                                       skip = true;
+                               else
+                                       addr -= scan_len;
+                               break;
+                       } else if (unlikely(skip)) {
+                               skip = false;
+                               goto skip;
+                       }
+                       entry = env_start;
+                       entry_len = env_end - env_start;
+                       CDEBUG(D_INFO, "key: %s, entry: %s\n", key, entry);
+
+                       /* Key length + length of '=' */
+                       if (entry_len > key_len + 1 &&
+                           entry[key_len] == '='  &&
+                           !memcmp(entry, key, key_len)) {
+                               entry += key_len + 1;
+                               entry_len -= key_len + 1;
+
+                               /* 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;
+                               GOTO(out, rc = 0);
+                       }
+skip:
+                       scan_len -= (env_end - env_start + 1);
+                       env_start = env_end + 1;
+               }
+       }
+       GOTO(out, rc = -ENOENT);
+
+out:
+       mmput(mm);
+       kfree((void *)buffer);
+       return rc;
+}
+
 /*
  * Get jobid of current process by reading the environment variable
  * stored in between the "env_start" & "env_end" of task struct.
@@ -221,14 +385,13 @@ static void jobid_prune_expedite(void)
  */
 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);
        if (!rc)
                goto out;
 
-       if (unlikely(rc == -EOVERFLOW && !printed)) {
+       if (rc == -EOVERFLOW) {
                /* 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,
@@ -236,16 +399,23 @@ int jobid_get_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. */
-               LCONSOLE_WARN("jobid: '%s' value too large (%d)\n",
-                             obd_jobid_var, *jobid_len);
-               printed = true;
+               static ktime_t printed;
+
+               if (unlikely(ktime_to_ns(printed) == 0 ||
+                            ktime_after(ktime_get(),
+                                        ktime_add_ns(printed,
+                                            3600ULL * 24 * NSEC_PER_SEC)))) {
+                       LCONSOLE_WARN("jobid: '%s' value too large (%d)\n",
+                                     obd_jobid_var, *jobid_len);
+                       printed = ktime_get();
+               }
+
                rc = 0;
-       }
-       if (rc) {
-               CDEBUG((rc == -ENOENT || rc == -EINVAL ||
-                       rc == -EDEADLK) ? D_INFO : D_ERROR,
-                      "jobid: get '%s' failed: rc = %d\n",
-                      obd_jobid_var, rc);
+       } else {
+               CDEBUG_LIMIT((rc == -ENOENT || rc == -EINVAL ||
+                             rc == -EDEADLK) ? D_INFO : D_ERROR,
+                            "jobid: get '%s' failed: rc = %d\n",
+                            obd_jobid_var, rc);
        }
 
 out:
@@ -330,7 +500,7 @@ 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();
+       pid_t pid = current->pid;
        struct jobid_pid_map *pidmap = NULL;
        time64_t now = ktime_get_real_seconds();
        int rc = 0;
@@ -460,6 +630,7 @@ out:
  *   %e = executable
  *   %g = gid
  *   %h = hostname
+ *   %H = short hostname
  *   %j = jobid from environment
  *   %p = pid
  *   %u = uid
@@ -476,7 +647,7 @@ static int jobid_interpret_string(const char *jobfmt, char *jobid,
        char c;
 
        while ((c = *jobfmt++) && joblen > 1) {
-               char f;
+               char f, *p;
                int l;
 
                if (isspace(c)) /* Don't allow embedded spaces */
@@ -486,12 +657,13 @@ static int jobid_interpret_string(const char *jobfmt, char *jobid,
                        *jobid = c;
                        joblen--;
                        jobid++;
+                       *jobid = '\0';
                        continue;
                }
 
                switch ((f = *jobfmt++)) {
                case 'e': /* executable name */
-                       l = snprintf(jobid, joblen, "%s", current_comm());
+                       l = snprintf(jobid, joblen, "%s", current->comm);
                        break;
                case 'g': /* group ID */
                        l = snprintf(jobid, joblen, "%u",
@@ -501,13 +673,22 @@ static int jobid_interpret_string(const char *jobfmt, char *jobid,
                        l = snprintf(jobid, joblen, "%s",
                                     init_utsname()->nodename);
                        break;
+               case 'H': /* short hostname. Cut at first dot */
+                       l = snprintf(jobid, joblen, "%s",
+                                    init_utsname()->nodename);
+                       p = strnchr(jobid, joblen, '.');
+                       if (p) {
+                               *p = '\0';
+                               l = p - jobid;
+                       }
+                       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());
+                       l = snprintf(jobid, joblen, "%u", current->pid);
                        break;
                case 'u': /* user ID */
                        l = snprintf(jobid, joblen, "%u",
@@ -693,26 +874,19 @@ int lustre_get_jobid(char *jobid, size_t joblen)
                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 (strcmp(obd_jobid_var, JOBSTATS_SESSION) == 0) {
-               char *jid;
-
-               rcu_read_lock();
-               jid = jobid_current();
-               if (jid)
-                       strlcpy(jobid, jid, sizeof(jobid));
-               rcu_read_unlock();
-       } else if (jobid_name_is_valid(current_comm())) {
+       } else if (strcmp(obd_jobid_var, JOBSTATS_SESSION) == 0 ||
+                  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.
+                * per-process jobid wanted, either from environment or from
+                * per-session setting.
+                * If obd_jobid_name contains "%j" or if getting the per-process
+                * jobid directly fails, fall back to using obd_jobid_name.
                 */
                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 */
+               /* fall back to jobid_name if jobid_var not available */
                if (rc < 0) {
                        int rc2 = jobid_interpret_string(obd_jobid_name,
                                                         jobid, joblen);