#define PR_ENVP_SZ 512
typedef struct dtrace_psinfo {
+/* Orabug 18383027 - Remove the conditionals at the next major UEK release. */
+#ifndef __GENKSYMS__
+ atomic_t usage;
+#endif
union {
unsigned long argc;
struct dtrace_psinfo *next;
char psargs[PR_PSARGS_SZ];
} dtrace_psinfo_t;
-extern dtrace_psinfo_t *dtrace_psinfo_alloc(struct task_struct *);
-extern void dtrace_psinfo_free(dtrace_psinfo_t *);
+extern void dtrace_psinfo_alloc(struct task_struct *);
+extern void dtrace_psinfo_free(struct task_struct *);
+
+#define get_psinfo(tsk) \
+ do { \
+ if (likely((tsk)->dtrace_psinfo)) \
+ atomic_inc(&(tsk)->dtrace_psinfo->usage); \
+ } while (0)
+#define put_psinfo(tsk) \
+ do { \
+ if (likely((tsk)->dtrace_psinfo)) { \
+ if (atomic_dec_and_test(&(tsk)->dtrace_psinfo->usage))\
+ dtrace_psinfo_free(tsk); \
+ } \
+ } while (0)
#endif /* _LINUX_DTRACE_PSINFO_H_ */
/*
* Allocate a new dtrace_psinfo_t structure.
*/
-dtrace_psinfo_t *dtrace_psinfo_alloc(struct task_struct *task)
+void dtrace_psinfo_alloc(struct task_struct *tsk)
{
dtrace_psinfo_t *psinfo;
struct mm_struct *mm;
+ if (likely(tsk->dtrace_psinfo)) {
+ put_psinfo(tsk);
+ tsk->dtrace_psinfo = NULL; /* while we build one */
+ }
+
psinfo = kmem_cache_alloc(psinfo_cachep, GFP_KERNEL);
if (psinfo == NULL)
goto fail;
- mm = get_task_mm(task);
+ mm = get_task_mm(tsk);
if (mm) {
size_t len = mm->arg_end - mm->arg_start;
int i, envc = 0;
if (len >= PR_PSARGS_SZ)
len = PR_PSARGS_SZ - 1;
- i = access_process_vm(task, mm->arg_start, psinfo->psargs,
+ i = access_process_vm(tsk, mm->arg_start, psinfo->psargs,
len, 0);
while (i < PR_PSARGS_SZ)
psinfo->psargs[i++] = 0;
- mmput(mm);
-
for (i = 0; i < len; i++) {
if (psinfo->psargs[i] == '\0')
psinfo->psargs[i] = ' ';
if ((len = psinfo->argc) >= PR_ARGV_SZ)
len = PR_ARGV_SZ - 1;
- psinfo->argv = kmalloc((len + 1) * sizeof(char *), GFP_KERNEL);
+ psinfo->argv = kmalloc((len + 1) * sizeof(char *),
+ GFP_KERNEL);
if (psinfo->argv == NULL)
goto fail;
if ((len = envc) >= PR_ENVP_SZ)
len = PR_ENVP_SZ - 1;
- psinfo->envp = kmalloc((len + 1) * sizeof(char *), GFP_KERNEL);
+ psinfo->envp = kmalloc((len + 1) * sizeof(char *),
+ GFP_KERNEL);
if (psinfo->envp == NULL)
goto fail;
p += strnlen(p, MAX_ARG_STRLEN) + 1;
}
psinfo->envp[len] = NULL;
+
+ mmput(mm);
+ } else {
+ size_t len = min(TASK_COMM_LEN, PR_PSARGS_SZ);
+ int i;
+
+ /*
+ * We end up here for tasks that do not have managed memory at
+ * all, which generally means that this is a kernel thread.
+ * If it is not, this is still safe because we know that tasks
+ * always have the comm member populated with something (even
+ * if it would be an empty string).
+ */
+ memcpy(psinfo->psargs, tsk->comm, len);
+ for (i = len; i < PR_PSARGS_SZ; i++)
+ psinfo->psargs[i] = 0;
+
+ psinfo->argc = 0;
+ psinfo->argv = kmalloc(sizeof(char *), GFP_KERNEL);
+ psinfo->argv[0] = NULL;
+ psinfo->envp = kmalloc(sizeof(char *), GFP_KERNEL);
+ psinfo->envp[0] = NULL;
}
- return psinfo;
+ atomic_set(&psinfo->usage, 1);
+ tsk->dtrace_psinfo = psinfo; /* new one */
+
+ return;
fail:
- pr_warning("%s: cannot allocate DTrace psinfo structure\n", __func__);
+ if (mm)
+ mmput(mm);
+
if (psinfo) {
if (psinfo->argv)
kfree(psinfo->argv);
kmem_cache_free(psinfo_cachep, psinfo);
}
-
- return NULL;
}
static DEFINE_SPINLOCK(psinfo_lock);
/*
* Schedule a psinfo structure for free'ing.
*/
-void dtrace_psinfo_free(dtrace_psinfo_t *psinfo)
+void dtrace_psinfo_free(struct task_struct *tsk)
{
unsigned long flags;
+ dtrace_psinfo_t *psinfo = tsk->dtrace_psinfo;
/*
- * For most tasks, we never populate any DTrace psinfo.
+ * There are (very few) tasks without psinfo...
*/
- if (!psinfo)
+ if (unlikely(psinfo == NULL))
return;
+ tsk->dtrace_psinfo = NULL;
+
spin_lock_irqsave(&psinfo_lock, flags);
psinfo->next = psinfo_free_list;
psinfo_free_list = psinfo;