Re: On the road to Xen : LTTng and relay CPU hotplug support

Tom Zanussi <[email protected]>
Newsgroups gmane.linux.kernel.tracing
Message-ID <[email protected]>
Mathieu Desnoyers wrote:
> Hi,
> 
> In my current work of porting LTTng to the Xen hypervisor, I notice that their
> project use the Linux CPU hotplug feature extensively in the domain-0 and
> domain-Us. This brings me to a preliminary adaptation phase of the LTTng kernel
> tracer : adding CPU hotplug support.
> 
> The basic idea is that we have (at least) to create a new buffer for every
> channel when a CPU is "UP_PREPARE". In order to do that, Relay must be
> modified so the buffer creation function is less coupled with the channel
> creation and that this function is exported as part of the API.
> (I just implemented this modification) We can also destroy the buffer when the
> CPU goes down (see below).
> 
> Relay cannot, by itself, deal with the CPU hotplug events because it does not
> have a list of the allocated channels. This is why I choose to put the hotplug

Hi,

I think it would be useful to add a channel list to relay and do some of 
this there.  Here's a first cut patch that does it - see if it makes 
sense for this.  It adds new buffers when cpus come online, but only 
flushes them when cpus go offline ,i.e. doesn't destroy the offline buffers.

> 
> What I could do is to implement (A), keeping in mind that the proper way to do
> it would be (B), the longer term target therefore being (B). In the mindset of
> doing incremental changes that "works", I think (A) would be a good start.
> 
> Having your insight on this would be appreciated,
> 

I think this approach makes sense - since all the buffers will be 
destroyed anyway when the channel is destroyed, you could probably avoid 
the complications of plan B unless you find you really need to.

Tom


diff --git a/include/linux/relay.h b/include/linux/relay.h
index 24accb4..6969fd2 100644
--- a/include/linux/relay.h
+++ b/include/linux/relay.h
@@ -24,7 +24,7 @@
  /*
   * Tracks changes to rchan/rchan_buf structs
   */
-#define RELAYFS_CHANNEL_VERSION        6
+#define RELAYFS_CHANNEL_VERSION        7

  /*
   * Per-cpu relay channel buffer
@@ -64,6 +64,9 @@ struct rchan
      void *private_data;        /* for user-defined data */
      size_t last_toobig;        /* tried to log event > subbuf size */
      struct rchan_buf *buf[NR_CPUS]; /* per-cpu channel buffers */
+    struct list_head list;        /* for channel list */
+    struct dentry *parent;        /* parent dentry passed to open */
+    char base_filename[NAME_MAX];    /* saved base filename */
  };

  /*
diff --git a/kernel/relay.c b/kernel/relay.c
index f04bbdb..5e03f0f 100644
--- a/kernel/relay.c
+++ b/kernel/relay.c
@@ -18,6 +18,11 @@
  #include <linux/relay.h>
  #include <linux/vmalloc.h>
  #include <linux/mm.h>
+#include <linux/cpu.h>
+
+/* list of open channels, for cpu hotplug */
+static DEFINE_MUTEX(relay_channels_mutex);
+static LIST_HEAD(relay_channels);

  /*
   * close() vm_op implementation for relay file mapping.
@@ -169,6 +174,9 @@ free_buf:
  void relay_destroy_channel(struct kref *kref)
  {
      struct rchan *chan = container_of(kref, struct rchan, kref);
+    mutex_lock(&relay_channels_mutex);
+    list_del(&chan->list);
+    mutex_unlock(&relay_channels_mutex);
      kfree(chan);
  }

@@ -366,9 +374,9 @@ void relay_reset(struct rchan *chan)
      if (!chan)
          return;

-    for (i = 0; i < NR_CPUS; i++) {
+    for_each_possible_cpu(i) {
          if (!chan->buf[i] || chan->buf[i] == prev)
-            break;
+            continue;
          __relay_reset(chan->buf[i], 0);
          prev = chan->buf[i];
      }
@@ -446,6 +454,67 @@ static inline void setup_callbacks(struc
      chan->cb = cb;
  }

+static int relay_add_hotcpu(int hotcpu)
+{
+    struct rchan *chan;
+    int err = 0, is_global = 0;
+    char *tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL);
+    if (!tmpname)
+        return -ENOMEM;
+
+    mutex_lock(&relay_channels_mutex);
+    list_for_each_entry(chan, &relay_channels, list) {
+        if (chan->buf[hotcpu])
+            continue;
+        sprintf(tmpname, "%s%d", chan->base_filename, hotcpu);
+        chan->buf[hotcpu] = relay_open_buf(chan, tmpname, chan->parent,
+                           &is_global);
+        if (!chan->buf[hotcpu]) {
+            err = -ENOMEM;
+            break;
+        }
+        chan->buf[hotcpu]->cpu = hotcpu;
+    }
+    mutex_unlock(&relay_channels_mutex);
+
+    kfree(tmpname);
+
+    return err;
+}
+
+static void relay_flush_hotcpu(int hotcpu)
+{
+    struct rchan *chan;
+
+    mutex_lock(&relay_channels_mutex);
+    list_for_each_entry(chan, &relay_channels, list) {
+        if (chan->buf[hotcpu])
+            continue;
+        relay_switch_subbuf(chan->buf[hotcpu], 0);
+    }
+    mutex_unlock(&relay_channels_mutex);
+}
+
+static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb,
+                       unsigned long action,
+                       void *hcpu)
+{
+    unsigned int hotcpu = (unsigned long)hcpu;
+
+    switch(action) {
+    case CPU_UP_PREPARE:
+        if (relay_add_hotcpu(hotcpu)) {
+            printk(KERN_ERR "relay_add_hotcpu: cpu %d buffer creation 
failed\n", hotcpu);
+            return NOTIFY_BAD;
+        }
+        break;
+    case CPU_DEAD:
+        relay_flush_hotcpu(hotcpu);
+        break;
+    }
+    return NOTIFY_OK;
+}
+
  /**
   *    relay_open - create a new relay channel
   *    @base_filename: base name of files to create
@@ -486,6 +555,8 @@ struct rchan *relay_open(const char *bas
      chan->n_subbufs = n_subbufs;
      chan->subbuf_size = subbuf_size;
      chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs);
+    chan->parent = parent;
+    strncpy(chan->base_filename, base_filename, NAME_MAX + 1);
      setup_callbacks(chan, cb);
      kref_init(&chan->kref);

@@ -503,13 +574,16 @@ struct rchan *relay_open(const char *bas
          chan->buf[i]->cpu = i;
      }

+    mutex_lock(&relay_channels_mutex);
+    list_add(&chan->list, &relay_channels);
+    mutex_unlock(&relay_channels_mutex);
      kfree(tmpname);
      return chan;

  free_bufs:
-    for (i = 0; i < NR_CPUS; i++) {
+    for_each_possible_cpu(i) {
          if (!chan->buf[i])
-            break;
+            continue;
          relay_close_buf(chan->buf[i]);
          if (is_global)
              break;
@@ -622,9 +696,9 @@ void relay_close(struct rchan *chan)
      if (!chan)
          return;

-    for (i = 0; i < NR_CPUS; i++) {
+    for_each_possible_cpu(i) {
          if (!chan->buf[i] || chan->buf[i] == prev)
-            break;
+            continue;
          relay_close_buf(chan->buf[i]);
          prev = chan->buf[i];
      }
@@ -652,9 +726,9 @@ void relay_flush(struct rchan *chan)
      if (!chan)
          return;

-    for (i = 0; i < NR_CPUS; i++) {
+    for_each_possible_cpu(i) {
          if (!chan->buf[i] || chan->buf[i] == prev)
-            break;
+            continue;
          relay_switch_subbuf(chan->buf[i], 0);
          prev = chan->buf[i];
      }
@@ -1021,3 +1095,12 @@ struct file_operations relay_file_operat
      .sendfile       = relay_file_sendfile,
  };
  EXPORT_SYMBOL_GPL(relay_file_operations);
+
+static __init int relay_init(void)
+{
+    hotcpu_notifier(relay_hotcpu_callback, 0);
+
+    return 0;
+}
+
+module_init(relay_init);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.