Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys Create kernel thread and let it to issue the write reque...



details:   https://anonhg.NetBSD.org/src/rev/f745941ec941
branches:  trunk
changeset: 570083:f745941ec941
user:      enami <enami%NetBSD.org@localhost>
date:      Wed Sep 22 22:15:03 2004 +0000

description:
Create kernel thread and let it to issue the write request.  We can't
do this from trace target process since we can't sleep at certain
trace point (otherwise system may hang).  Address PR#23155.

diffstat:

 sys/kern/kern_ktrace.c |  947 +++++++++++++++++++++++++++++++++++-------------
 sys/sys/ktrace.h       |   24 +-
 2 files changed, 691 insertions(+), 280 deletions(-)

diffs (truncated from 1279 to 300 lines):

diff -r 04d4e9b65cab -r f745941ec941 sys/kern/kern_ktrace.c
--- a/sys/kern/kern_ktrace.c    Wed Sep 22 21:24:07 2004 +0000
+++ b/sys/kern/kern_ktrace.c    Wed Sep 22 22:15:03 2004 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: kern_ktrace.c,v 1.92 2004/09/04 07:09:35 skrll Exp $   */
+/*     $NetBSD: kern_ktrace.c,v 1.93 2004/09/22 22:15:03 enami Exp $   */
 
 /*
  * Copyright (c) 1989, 1993
@@ -32,7 +32,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_ktrace.c,v 1.92 2004/09/04 07:09:35 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_ktrace.c,v 1.93 2004/09/22 22:15:03 enami Exp $");
 
 #include "opt_ktrace.h"
 #include "opt_compat_mach.h"
@@ -43,11 +43,14 @@
 #include <sys/file.h>
 #include <sys/namei.h>
 #include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
 #include <sys/ktrace.h>
 #include <sys/malloc.h>
 #include <sys/syslog.h>
 #include <sys/filedesc.h>
 #include <sys/ioctl.h>
+#include <sys/callout.h>
 
 #include <sys/mount.h>
 #include <sys/sa.h>
@@ -55,14 +58,285 @@
 
 #ifdef KTRACE
 
-void   ktrinitheader(struct ktr_header *, struct proc *, int);
-int    ktrwrite(struct proc *, struct ktr_header *);
-int    ktrace_common(struct proc *, int, int, int, struct file *);
-int    ktrops(struct proc *, struct proc *, int, int, struct file *);
-int    ktrsetchildren(struct proc *, struct proc *, int, int,
-           struct file *);
-int    ktrcanset(struct proc *, struct proc *);
-int    ktrsamefile(struct file *, struct file *);
+/*
+ * XXX:
+ *     - need better error reporting?
+ *     - p->p_tracep access lock.  lock p_lock, lock ktd if !NULL, inc ref.
+ *     - userland utility to sort ktrace.out by timestamp.
+ *     - keep minimum information in ktrace_entry when rest of alloc failed.
+ *     - enlarge ktrace_entry so that small entry won't require additional
+ *       alloc?
+ *     - per trace control of configurable parameters.
+ */
+
+struct ktrace_entry {
+       TAILQ_ENTRY(ktrace_entry) kte_list;
+       struct ktr_header kte_kth;
+       void *kte_buf;                  /* ktr_buf */
+};
+
+struct ktr_desc {
+       TAILQ_ENTRY(ktr_desc) ktd_list;
+       int ktd_flags;
+#define        KTDF_WAIT               0x0001
+#define        KTDF_DONE               0x0002
+#define        KTDF_BLOCKING           0x0004
+#define        KTDF_INTERACTIVE        0x0008
+       int ktd_error;
+#define        KTDE_ENOMEM             0x0001
+#define        KTDE_ENOSPC             0x0002
+       int ktd_errcnt;
+       int ktd_ref;                    /* # of reference */
+       int ktd_qcount;                 /* # of entry in the queue */
+
+       /*
+        * Params to control behaviour.
+        */
+       int ktd_delayqcnt;              /* # of entry allowed to delay */
+       int ktd_wakedelay;              /* delay of wakeup in *tick* */
+       int ktd_intrwakdl;              /* ditto, but when interactive */
+
+       struct file *ktd_fp;            /* trace output file */
+       struct proc *ktd_proc;          /* our kernel thread */
+       TAILQ_HEAD(, ktrace_entry) ktd_queue;
+       struct callout ktd_wakch;       /* delayed wakeup */
+       struct simplelock ktd_slock;
+};
+
+static void    ktrinitheader(struct ktr_header *, struct proc *, int);
+static void    ktrwrite(struct ktr_desc *, struct ktrace_entry *);
+static int     ktrace_common(struct proc *, int, int, int, struct file *);
+static int     ktrops(struct proc *, struct proc *, int, int,
+                   struct ktr_desc *);
+static int     ktrsetchildren(struct proc *, struct proc *, int, int,
+                   struct ktr_desc *);
+static int     ktrcanset(struct proc *, struct proc *);
+static int     ktrsamefile(struct file *, struct file *);
+
+static struct ktr_desc *
+               ktd_lookup(struct file *);
+static void    ktdrel(struct ktr_desc *);
+static void    ktdref(struct ktr_desc *);
+static void    ktraddentry(struct proc *, struct ktrace_entry *, int);
+/* Flags for ktraddentry (3rd arg) */
+#define        KTA_NOWAIT              0x0000
+#define        KTA_WAITOK              0x0001
+#define        KTA_LARGE               0x0002
+static void    ktefree(struct ktrace_entry *);
+static void    ktd_logerrl(struct ktr_desc *, int);
+static void    ktd_logerr(struct proc *, int);
+static void    ktrace_thread(void *);
+
+/*
+ * Default vaules.
+ */
+#define        KTD_MAXENTRY            1000    /* XXX: tune */
+#define        KTD_TIMEOUT             5       /* XXX: tune */
+#define        KTD_DELAYQCNT           100     /* XXX: tune */
+#define        KTD_WAKEDELAY           5000    /* XXX: tune */
+#define        KTD_INTRWAKDL           100     /* XXX: tune */
+
+/*
+ * Patchable variables.
+ */
+int ktd_maxentry = KTD_MAXENTRY;       /* max # of entry in the queue */
+int ktd_timeout = KTD_TIMEOUT;         /* timeout in seconds */
+int ktd_delayqcnt = KTD_DELAYQCNT;     /* # of entry allowed to delay */
+int ktd_wakedelay = KTD_WAKEDELAY;     /* delay of wakeup in *ms* */
+int ktd_intrwakdl = KTD_INTRWAKDL;     /* ditto, but when interactive */
+
+static struct simplelock ktdq_slock = SIMPLELOCK_INITIALIZER;
+static TAILQ_HEAD(, ktr_desc) ktdq = TAILQ_HEAD_INITIALIZER(ktdq);
+
+MALLOC_DEFINE(M_KTRACE, "ktrace", "ktrace data buffer");
+POOL_INIT(kte_pool, sizeof(struct ktrace_entry), 0, 0, 0,
+    "ktepl", &pool_allocator_nointr);
+
+static __inline void
+ktd_wakeup(struct ktr_desc *ktd)
+{
+
+       callout_stop(&ktd->ktd_wakch);
+       wakeup(ktd);
+}
+
+static void
+ktd_logerrl(struct ktr_desc *ktd, int error)
+{
+
+       ktd->ktd_error |= error;
+       ktd->ktd_errcnt++;
+}
+
+static void
+ktd_logerr(struct proc *p, int error)
+{
+       struct ktr_desc *ktd = p->p_tracep;
+
+       if (ktd == NULL)
+               return;
+
+       simple_lock(&ktd->ktd_slock);
+       ktd_logerrl(ktd, error);
+       simple_unlock(&ktd->ktd_slock);
+}
+
+/*
+ * Release a reference.  Called with ktd_slock held.
+ */
+void
+ktdrel(struct ktr_desc *ktd)
+{
+
+       KDASSERT(ktd->ktd_ref != 0);
+       KASSERT(ktd->ktd_ref > 0);
+       if (--ktd->ktd_ref <= 0) {
+               ktd->ktd_flags |= KTDF_DONE;
+               wakeup(ktd);
+       }
+       simple_unlock(&ktd->ktd_slock);
+}
+
+void
+ktdref(struct ktr_desc *ktd)
+{
+
+       simple_lock(&ktd->ktd_slock);
+       ktd->ktd_ref++;
+       simple_unlock(&ktd->ktd_slock);
+}
+
+struct ktr_desc *
+ktd_lookup(struct file *fp)
+{
+       struct ktr_desc *ktd;
+
+       simple_lock(&ktdq_slock);
+       for (ktd = TAILQ_FIRST(&ktdq); ktd != NULL;
+           ktd = TAILQ_NEXT(ktd, ktd_list)) {
+               simple_lock(&ktd->ktd_slock);
+               if (ktrsamefile(ktd->ktd_fp, fp)) {
+                       ktd->ktd_ref++;
+                       simple_unlock(&ktd->ktd_slock);
+                       break;
+               }
+               simple_unlock(&ktd->ktd_slock);
+       }
+       simple_unlock(&ktdq_slock);
+       return (ktd);
+}
+
+void
+ktraddentry(struct proc *p, struct ktrace_entry *kte, int flags)
+{
+       struct ktr_desc *ktd;
+#ifdef DEBUG
+       struct timeval t;
+       int s;
+#endif
+
+       if (p->p_traceflag & KTRFAC_TRC_EMUL) {
+               /* Add emulation trace before first entry for this process */
+               p->p_traceflag &= ~KTRFAC_TRC_EMUL;
+               ktremul(p);
+       }
+
+       /*
+        * Tracing may be canceled while we were sleeping waiting for
+        * memory.
+        */
+       ktd = p->p_tracep;
+       if (ktd == NULL)
+               goto freekte;
+
+       /*
+        * Bump reference count so that the object will remain while
+        * we are here.  Note that the trace is controlled by other
+        * process.
+        */
+       ktdref(ktd);
+
+       simple_lock(&ktd->ktd_slock);
+       if (ktd->ktd_flags & KTDF_DONE)
+               goto relktd;
+
+       if (ktd->ktd_qcount > ktd_maxentry) {
+               ktd_logerrl(ktd, KTDE_ENOSPC);
+               goto relktd;
+       }
+       TAILQ_INSERT_TAIL(&ktd->ktd_queue, kte, kte_list);
+       ktd->ktd_qcount++;
+       if (ktd->ktd_flags & KTDF_BLOCKING)
+               goto skip_sync;
+
+       if (flags & KTA_WAITOK &&
+           (/* flags & KTA_LARGE */0 || ktd->ktd_flags & KTDF_WAIT ||
+           ktd->ktd_qcount > ktd_maxentry >> 1))
+               /*
+                * Sync with writer thread since we're requesting rather
+                * big one or many requests are pending.
+                */
+               do {
+                       ktd->ktd_flags |= KTDF_WAIT;
+                       ktd_wakeup(ktd);
+#ifdef DEBUG
+                       s = splclock();
+                       t = mono_time;
+                       splx(s);
+#endif
+                       if (ltsleep(&ktd->ktd_flags, PWAIT, "ktrsync",
+                           ktd_timeout * hz, &ktd->ktd_slock) != 0) {
+                               ktd->ktd_flags |= KTDF_BLOCKING;
+                               /*
+                                * Maybe the writer thread is blocking
+                                * completely for some reason, but
+                                * don't stop target process forever.
+                                */
+                               log(LOG_NOTICE, "ktrace timeout\n");
+                               break;
+                       }
+#ifdef DEBUG
+                       s = splclock();
+                       timersub(&mono_time, &t, &t);
+                       splx(s);
+                       if (t.tv_sec > 0)
+                               log(LOG_NOTICE,
+                                   "ktrace long wait: %ld.%06ld\n",
+                                   t.tv_sec, t.tv_usec);
+#endif
+               } while (p->p_tracep == ktd &&
+                   (ktd->ktd_flags & (KTDF_WAIT | KTDF_DONE)) == KTDF_WAIT);
+       else {
+               /* Schedule delayed wakeup */
+               if (ktd->ktd_qcount > ktd->ktd_delayqcnt)
+                       ktd_wakeup(ktd);        /* Wakeup now */
+               else if (!callout_pending(&ktd->ktd_wakch))
+                       callout_reset(&ktd->ktd_wakch,
+                           ktd->ktd_flags & KTDF_INTERACTIVE ?



Home | Main Index | Thread Index | Old Index