Linux Headquarters
[ Register ]
[ About us ] [ Home Page ]

[ Kernel ] [ Documentation ] [ Links ] [ Books ]

Kernel v2.6.24-rc5 /kernel/marker.c

Filename:/kernel/marker.c
Lines Added:518
Lines Deleted:0
Also changed in: (Previous) 2.6.24-rc4  2.6.24-rc3  2.6.24-rc2-git6  2.6.24-rc2-git5  2.6.24-rc2  2.6.24-rc1 
(Following) 2.6.24-rc6  2.6.24-rc7  2.6.24-rc8  2.6.24  2.6.25-rc1-git3  2.6.25-rc1-git4 

Location
[  2.6.24-rc5
  [  kernel
     o  marker.c

Patch

diff --git a/kernel/marker.c b/kernel/marker.c
new file mode 100644
index 0000000..5323cfa
--- /dev/null
+++ b/kernel/marker.c
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2007 Mathieu Desnoyers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/jhash.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/marker.h>
+#include <linux/err.h>
+
+extern struct marker __start___markers[];
+extern struct marker __stop___markers[];
+
+/*
+ * markers_mutex nests inside module_mutex. Markers mutex protects the builtin
+ * and module markers, the hash table and deferred_sync.
+ */
+static DEFINE_MUTEX(markers_mutex);
+
+/*
+ * Marker deferred synchronization.
+ * Upon marker probe_unregister, we delay call to synchronize_sched() to
+ * accelerate mass unregistration (only when there is no more reference to a
+ * given module do we call synchronize_sched()). However, we need to make sure
+ * every critical region has ended before we re-arm a marker that has been
+ * unregistered and then registered back with a different probe data.
+ */
+static int deferred_sync;
+
+/*
+ * Marker hash table, containing the active markers.
+ * Protected by module_mutex.
+ */
+#define MARKER_HASH_BITS 6
+#define MARKER_TABLE_SIZE (1 << MARKER_HASH_BITS)
+
+struct marker_entry {
+   struct hlist_node hlist;
+   char *format;
+   marker_probe_func *probe;
+   void *private;
+   int refcount;   /* Number of times armed. 0 if disarmed. */
+   char name[0];   /* Contains name'\0'format'\0' */
+};
+
+static struct hlist_head marker_table[MARKER_TABLE_SIZE];
+
+/**
+ * __mark_empty_function - Empty probe callback
+ * @mdata: pointer of type const struct marker
+ * @fmt: format string
+ * @...: variable argument list
+ *
+ * Empty callback provided as a probe to the markers. By providing this to a
+ * disabled marker, we make sure the  execution flow is always valid even
+ * though the function pointer change and the marker enabling are two distinct
+ * operations that modifies the execution flow of preemptible code.
+ */
+void __mark_empty_function(const struct marker *mdata, void *private,
+   const char *fmt, ...)
+{
+}
+EXPORT_SYMBOL_GPL(__mark_empty_function);
+
+/*
+ * Get marker if the marker is present in the marker hash table.
+ * Must be called with markers_mutex held.
+ * Returns NULL if not present.
+ */
+static struct marker_entry *get_marker(const char *name)
+{
+   struct hlist_head *head;
+   struct hlist_node *node;
+   struct marker_entry *e;
+   u32 hash = jhash(name, strlen(name), 0);
+
+   head = &marker_table[hash & ((1 << MARKER_HASH_BITS)-1)];
+   hlist_for_each_entry(e, node, head, hlist) {
+      if (!strcmp(name, e->name))
+         return e;
+   }
+   return NULL;
+}
+
+/*
+ * Add the marker to the marker hash table. Must be called with markers_mutex
+ * held.
+ */
+static int add_marker(const char *name, const char *format,
+   marker_probe_func *probe, void *private)
+{
+   struct hlist_head *head;
+   struct hlist_node *node;
+   struct marker_entry *e;
+   size_t name_len = strlen(name) + 1;
+   size_t format_len = 0;
+   u32 hash = jhash(name, name_len-1, 0);
+
+   if (format)
+      format_len = strlen(format) + 1;
+   head = &marker_table[hash & ((1 << MARKER_HASH_BITS)-1)];
+   hlist_for_each_entry(e, node, head, hlist) {
+      if (!strcmp(name, e->name)) {
+         printk(KERN_NOTICE
+            "Marker %s busy, probe %p already installed\n",
+            name, e->probe);
+         return -EBUSY;   /* Already there */
+      }
+   }
+   /*
+    * Using kmalloc here to allocate a variable length element. Could
+    * cause some memory fragmentation if overused.
+    */
+   e = kmalloc(sizeof(struct marker_entry) + name_len + format_len,
+         GFP_KERNEL);
+   if (!e)
+      return -ENOMEM;
+   memcpy(&e->name[0], name, name_len);
+   if (format) {
+      e->format = &e->name[name_len];
+      memcpy(e->format, format, format_len);
+      trace_mark(core_marker_format, "name %s format %s",
+            e->name, e->format);
+   } else
+      e->format = NULL;
+   e->probe = probe;
+   e->private = private;
+   e->refcount = 0;
+   hlist_add_head(&e->hlist, head);
+   return 0;
+}
+
+/*
+ * Remove the marker from the marker hash table. Must be called with mutex_lock
+ * held.
+ */
+static void *remove_marker(const char *name)
+{
+   struct hlist_head *head;
+   struct hlist_node *node;
+   struct marker_entry *e;
+   int found = 0;
+   size_t len = strlen(name) + 1;
+   void *private = NULL;
+   u32 hash = jhash(name, len-1, 0);
+
+   head = &marker_table[hash & ((1 << MARKER_HASH_BITS)-1)];
+   hlist_for_each_entry(e, node, head, hlist) {
+      if (!strcmp(name, e->name)) {
+         found = 1;
+         break;
+      }
+   }
+   if (found) {
+      private = e->private;
+      hlist_del(&e->hlist);
+      kfree(e);
+   }
+   return private;
+}
+
+/*
+ * Set the mark_entry format to the format found in the element.
+ */
+static int marker_set_format(struct marker_entry **entry, const char *format)
+{
+   struct marker_entry *e;
+   size_t name_len = strlen((*entry)->name) + 1;
+   size_t format_len = strlen(format) + 1;
+
+   e = kmalloc(sizeof(struct marker_entry) + name_len + format_len,
+         GFP_KERNEL);
+   if (!e)
+      return -ENOMEM;
+   memcpy(&e->name[0], (*entry)->name, name_len);
+   e->format = &e->name[name_len];
+   memcpy(e->format, format, format_len);
+   e->probe = (*entry)->probe;
+   e->private = (*entry)->private;
+   e->refcount = (*entry)->refcount;
+   hlist_add_before(&e->hlist, &(*entry)->hlist);
+   hlist_del(&(*entry)->hlist);
+   kfree(*entry);
+   *entry = e;
+   trace_mark(core_marker_format, "name %s format %s",
+         e->name, e->format);
+   return 0;
+}
+
+/*
+ * Sets the probe callback corresponding to one marker.
+ */
+static int set_marker(struct marker_entry **entry, struct marker *elem)
+{
+   int ret;
+   WARN_ON(strcmp((*entry)->name, elem->name) != 0);
+
+   if ((*entry)->format) {
+      if (strcmp((*entry)->format, elem->format) != 0) {
+         printk(KERN_NOTICE
+            "Format mismatch for probe %s "
+            "(%s), marker (%s)\n",
+            (*entry)->name,
+            (*entry)->format,
+            elem->format);
+         return -EPERM;
+      }
+   } else {
+      ret = marker_set_format(entry, elem->format);
+      if (ret)
+         return ret;
+   }
+   elem->call = (*entry)->probe;
+   elem->private = (*entry)->private;
+   elem->state = 1;
+   return 0;
+}
+
+/*
+ * Disable a marker and its probe callback.
+ * Note: only after a synchronize_sched() issued after setting elem->call to the
+ * empty function insures that the original callback is not used anymore. This
+ * insured by preemption disabling around the call site.
+ */
+static void disable_marker(struct marker *elem)
+{
+   elem->state = 0;
+   elem->call = __mark_empty_function;
+   /*
+    * Leave the private data and id there, because removal is racy and
+    * should be done only after a synchronize_sched(). These are never used
+    * until the next initialization anyway.
+    */
+}
+
+/**
+ * marker_update_probe_range - Update a probe range
+ * @begin: beginning of the range
+ * @end: end of the range
+ * @probe_module: module address of the probe being updated
+ * @refcount: number of references left to the given probe_module (out)
+ *
+ * Updates the probe callback corresponding to a range of markers.
+ */
+void marker_update_probe_range(struct marker *begin,
+   struct marker *end, struct module *probe_module,
+   int *refcount)
+{
+   struct marker *iter;
+   struct marker_entry *mark_entry;
+
+   mutex_lock(&markers_mutex);
+   for (iter = begin; iter < end; iter++) {
+      mark_entry = get_marker(iter->name);
+      if (mark_entry && mark_entry->refcount) {
+         set_marker(&mark_entry, iter);
+         /*
+          * ignore error, continue
+          */
+         if (probe_module)
+            if (probe_module ==
+         __module_text_address((unsigned long)mark_entry->probe))
+               (*refcount)++;
+      } else {
+         disable_marker(iter);
+      }
+   }
+   mutex_unlock(&markers_mutex);
+}
+
+/*
+ * Update probes, removing the faulty probes.
+ * Issues a synchronize_sched() when no reference to the module passed
+ * as parameter is found in the probes so the probe module can be
+ * safely unloaded from now on.
+ */
+static void marker_update_probes(struct module *probe_module)
+{
+   int refcount = 0;
+
+   /* Core kernel markers */
+   marker_update_probe_range(__start___markers,
+         __stop___markers, probe_module, &refcount);
+   /* Markers in modules. */
+   module_update_markers(probe_module, &refcount);
+   if (probe_module && refcount == 0) {
+      synchronize_sched();
+      deferred_sync = 0;
+   }
+}
+
+/**
+ * marker_probe_register -  Connect a probe to a marker
+ * @name: marker name
+ * @format: format string
+ * @probe: probe handler
+ * @private: probe private data
+ *
+ * private data must be a valid allocated memory address, or NULL.
+ * Returns 0 if ok, error value on error.
+ */
+int marker_probe_register(const char *name, const char *format,
+         marker_probe_func *probe, void *private)
+{
+   struct marker_entry *entry;
+   int ret = 0;
+
+   mutex_lock(&markers_mutex);
+   entry = get_marker(name);
+   if (entry && entry->refcount) {
+      ret = -EBUSY;
+      goto end;
+   }
+   if (deferred_sync) {
+      synchronize_sched();
+      deferred_sync = 0;
+   }
+   ret = add_marker(name, format, probe, private);
+   if (ret)
+      goto end;
+   mutex_unlock(&markers_mutex);
+   marker_update_probes(NULL);
+   return ret;
+end:
+   mutex_unlock(&markers_mutex);
+   return ret;
+}
+EXPORT_SYMBOL_GPL(marker_probe_register);
+
+/**
+ * marker_probe_unregister -  Disconnect a probe from a marker
+ * @name: marker name
+ *
+ * Returns the private data given to marker_probe_register, or an ERR_PTR().
+ */
+void *marker_probe_unregister(const char *name)
+{
+   struct module *probe_module;
+   struct marker_entry *entry;
+   void *private;
+
+   mutex_lock(&markers_mutex);
+   entry = get_marker(name);
+   if (!entry) {
+      private = ERR_PTR(-ENOENT);
+      goto end;
+   }
+   entry->refcount = 0;
+   /* In what module is the probe handler ? */
+   probe_module = __module_text_address((unsigned long)entry->probe);
+   private = remove_marker(name);
+   deferred_sync = 1;
+   mutex_unlock(&markers_mutex);
+   marker_update_probes(probe_module);
+   return private;
+end:
+   mutex_unlock(&markers_mutex);
+   return private;
+}
+EXPORT_SYMBOL_GPL(marker_probe_unregister);
+
+/**
+ * marker_probe_unregister_private_data -  Disconnect a probe from a marker
+ * @private: probe private data
+ *
+ * Unregister a marker by providing the registered private data.
+ * Returns the private data given to marker_probe_register, or an ERR_PTR().
+ */
+void *marker_probe_unregister_private_data(void *private)
+{
+   struct module *probe_module;
+   struct hlist_head *head;
+   struct hlist_node *node;
+   struct marker_entry *entry;
+   int found = 0;
+   unsigned int i;
+
+   mutex_lock(&markers_mutex);
+   for (i = 0; i < MARKER_TABLE_SIZE; i++) {
+      head = &marker_table[i];
+      hlist_for_each_entry(entry, node, head, hlist) {
+         if (entry->private == private) {
+            found = 1;
+            goto iter_end;
+         }
+      }
+   }
+iter_end:
+   if (!found) {
+      private = ERR_PTR(-ENOENT);
+      goto end;
+   }
+   entry->refcount = 0;
+   /* In what module is the probe handler ? */
+   probe_module = __module_text_address((unsigned long)entry->probe);
+   private = remove_marker(entry->name);
+   deferred_sync = 1;
+   mutex_unlock(&markers_mutex);
+   marker_update_probes(probe_module);
+   return private;
+end:
+   mutex_unlock(&markers_mutex);
+   return private;
+}
+EXPORT_SYMBOL_GPL(marker_probe_unregister_private_data);
+
+/**
+ * marker_arm - Arm a marker
+ * @name: marker name
+ *
+ * Activate a marker. It keeps a reference count of the number of
+ * arming/disarming done.
+ * Returns 0 if ok, error value on error.
+ */
+int marker_arm(const char *name)
+{
+   struct marker_entry *entry;
+   int ret = 0;
+
+   mutex_lock(&markers_mutex);
+   entry = get_marker(name);
+   if (!entry) {
+      ret = -ENOENT;
+      goto end;
+   }
+   /*
+    * Only need to update probes when refcount passes from 0 to 1.
+    */
+   if (entry->refcount++)
+      goto end;
+end:
+   mutex_unlock(&markers_mutex);
+   marker_update_probes(NULL);
+   return ret;
+}
+EXPORT_SYMBOL_GPL(marker_arm);
+
+/**
+ * marker_disarm - Disarm a marker
+ * @name: marker name
+ *
+ * Disarm a marker. It keeps a reference count of the number of arming/disarming
+ * done.
+ * Returns 0 if ok, error value on error.
+ */
+int marker_disarm(const char *name)
+{
+   struct marker_entry *entry;
+   int ret = 0;
+
+   mutex_lock(&markers_mutex);
+   entry = get_marker(name);
+   if (!entry) {
+      ret = -ENOENT;
+      goto end;
+   }
+   /*
+    * Only permit decrement refcount if higher than 0.
+    * Do probe update only on 1 -> 0 transition.
+    */
+   if (entry->refcount) {
+      if (--entry->refcount)
+         goto end;
+   } else {
+      ret = -EPERM;
+      goto end;
+   }
+end:
+   mutex_unlock(&markers_mutex);
+   marker_update_probes(NULL);
+   return ret;
+}
+EXPORT_SYMBOL_GPL(marker_disarm);
+
+/**
+ * marker_get_private_data - Get a marker's probe private data
+ * @name: marker name
+ *
+ * Returns the private data pointer, or an ERR_PTR.
+ * The private data pointer should _only_ be dereferenced if the caller is the
+ * owner of the data, or its content could vanish. This is mostly used to
+ * confirm that a caller is the owner of a registered probe.
+ */
+void *marker_get_private_data(const char *name)
+{
+   struct hlist_head *head;
+   struct hlist_node *node;
+   struct marker_entry *e;
+   size_t name_len = strlen(name) + 1;
+   u32 hash = jhash(name, name_len-1, 0);
+   int found = 0;
+
+   head = &marker_table[hash & ((1 << MARKER_HASH_BITS)-1)];
+   hlist_for_each_entry(e, node, head, hlist) {
+      if (!strcmp(name, e->name)) {
+         found = 1;
+         return e->private;
+      }
+   }
+   return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL_GPL(marker_get_private_data);


Comments: webmaster (at) linuxhq.com.
Advertising: banners (at) linuxhq.com.
Compilation ©1998-2008 Linux Headquarters, Inc.