Re: [PATCH] Relay CPU hotplug support

Tom Zanussi <[email protected]>
Newsgroups gmane.linux.kernel.tracing
Message-ID <[email protected]>
Mathieu Desnoyers writes:
 > Hi Tom,
 > 
 > Here is my version of the relay patch for CPU hotplug support. It uses a
 > channel list within relay, as you suggested. The patch is based on a 2.6.18
 > kernel.
 > 
 > Comments are welcome,
 > 
 > Mathieu
 > 

Hi Mathieu,

Comments below...

 > 
 > diff --git a/include/linux/relay.h b/include/linux/relay.h
 > index 24accb4..6190fa0 100644
 > --- a/include/linux/relay.h
 > +++ b/include/linux/relay.h
 > @@ -24,7 +24,7 @@ #define FIX_SIZE(x) ((((x) - 1) & PAGE_M
 >  /*
 >   * Tracks changes to rchan/rchan_buf structs
 >   */
 > -#define RELAYFS_CHANNEL_VERSION		6
 > +#define RELAYFS_CHANNEL_VERSION		7
 > 
 >  /*
 >   * Per-cpu relay channel buffer
 > @@ -41,6 +41,7 @@ struct rchan_buf
 >  	struct work_struct wake_readers; /* reader wake-up work struct */
 >  	struct dentry *dentry;		/* channel file dentry */
 >  	struct kref kref;		/* channel buffer refcount */
 > +	struct kref krefw;		/* channel buffer writers
refcount */

I still don't think this is needed (see below and previous reply)

 >  	struct page **page_array;	/* array of current buffer pages */
 >  	unsigned int page_count;	/* number of current buffer pages */
 >  	unsigned int finalized;		/* buffer has been finalized */
 > @@ -64,6 +65,10 @@ 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 */
 > +	int is_global;			/* One global buffer ? */
 > +	struct list_head list;		/* for channel list */
 > +	struct dentry *parent;		/* parent dentry passed to open */
 > +	char base_filename[NAME_MAX];	/* saved base filename */
 >  };
 > 
 >  /*
 > @@ -133,6 +138,8 @@ struct rchan_callbacks
 >  	 * cause relay_open() to create a single global buffer rather
 >  	 * than the default set of per-cpu buffers.
 >  	 *
 > +	 * buf->chan->buf[buf->cpu] is not set when this callback is called.
 > +	 *

If this is important, should we arrange to have it set when the
callback is called?

 >  	 * See Documentation/filesystems/relayfs.txt for more info.
 >  	 */
 >  	struct dentry *(*create_buf_file)(const char *filename,
 > @@ -162,7 +169,8 @@ struct rchan *relay_open(const char *bas
 >  			 struct dentry *parent,
 >  			 size_t subbuf_size,
 >  			 size_t n_subbufs,
 > -			 struct rchan_callbacks *cb);
 > +			 struct rchan_callbacks *cb,
 > +			 void *private_data);
 >  extern void relay_close(struct rchan *chan);
 >  extern void relay_flush(struct rchan *chan);
 >  extern void relay_subbufs_consumed(struct rchan *chan,
 > 
 > diff --git a/kernel/relay.c b/kernel/relay.c
 > index 33345e7..8dab79c 100644
 > --- a/kernel/relay.c
 > +++ b/kernel/relay.c
 > @@ -7,8 +7,14 @@
 >   * Copyright (C) 1999-2005 - Karim Yaghmour ([email protected])
 >   *
 >   * Moved to kernel/relay.c by Paul Mundt, 2006.
 > + * November 2006 - CPU hotplug support by Mathieu Desnoyers
 > + * 	([email protected])
 >   *
 >   * This file is released under the GPL.
 > + *
 > + * In case CPU_DEAD CPU hotplug is someday implemented, we protect against
 > + * buffer removal by locking cpu hotplug around each test on buf[cpu] being
 > + * NULL everywhere in the file.
 >   */
 >  #include <linux/errno.h>
 >  #include <linux/stddef.h>
 > @@ -18,6 +24,11 @@ #include <linux/string.h>
 >  #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.
 > @@ -189,6 +200,7 @@ void relay_destroy_buf(struct rchan_buf
 >  	}
 >  	kfree(buf->padding);
 >  	kfree(buf);
 > +	chan->buf[buf->cpu] = NULL;
 >  	kref_put(&chan->kref, relay_destroy_channel);
 >  }
 > 
 > @@ -327,6 +339,7 @@ static inline void __relay_reset(struct
 >  	if (init) {
 >  		init_waitqueue_head(&buf->read_wait);
 >  		kref_init(&buf->kref);
 > +		kref_init(&buf->krefw);
 >  		INIT_WORK(&buf->wake_readers, NULL, NULL);
 >  	} else {
 >  		cancel_delayed_work(&buf->wake_readers);
 > @@ -360,70 +373,104 @@ static inline void __relay_reset(struct
 >  void relay_reset(struct rchan *chan)
 >  {
 >  	unsigned int i;
 > -	struct rchan_buf *prev = NULL;
 > 
 >  	if (!chan)
 >  		return;
 > -
 > -	for (i = 0; i < NR_CPUS; i++) {
 > -		if (!chan->buf[i] || chan->buf[i] == prev)
 > -			break;
 > -		__relay_reset(chan->buf[i], 0);
 > -		prev = chan->buf[i];
 > +	lock_cpu_hotplug();
 > +	if (chan->is_global) {
 > +		if (chan->buf[0])
 > +			__relay_reset(chan->buf[0], 0);
 > +	} else {
 > +		for_each_online_cpu(i)
 > +			if (chan->buf[i])
 > +				__relay_reset(chan->buf[i], 0);
 >  	}
 > +	unlock_cpu_hotplug();
 >  }

If the is_global case isn't affected by cpu hotplug, no need to have
those cases inside lock_cpu_hotplug().

 >  EXPORT_SYMBOL_GPL(relay_reset);
 > 
 >  /**
 >   *	relay_open_buf - create a new relay channel buffer
 >   *
 > - *	Internal - used by relay_open().
 > + *	used by relay_open() and CPU hotplug.
 >   */
 > -static struct rchan_buf *relay_open_buf(struct rchan *chan,
 > -					const char *filename,
 > -					struct dentry *parent,
 > -					int *is_global)
 > +struct rchan_buf *relay_open_buf(struct rchan *chan, unsigned int cpu)
 >  {
 > -	struct rchan_buf *buf;
 > +	struct rchan_buf *buf = NULL;
 >  	struct dentry *dentry;
 > +	char *tmpname;
 > 
 > -	if (*is_global)
 > +	if (chan->is_global) {
 > +		kref_get(&chan->buf[0]->krefw);
 >  		return chan->buf[0];
 > +	}
 > + + tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL); + if (!tmpname) +
 > goto end; + sprintf(tmpname, "%s%d", chan->base_filename, cpu);
 > 
 >  	buf = relay_create_buf(chan);
 >  	if (!buf)
 > -		return NULL;
 > +		goto free_name;
 > +
 > +	buf->cpu = cpu;
 > +	__relay_reset(buf, 1);
 > 
 >  	/* Create file in fs */
 > -	dentry = chan->cb->create_buf_file(filename, parent, S_IRUSR,
 > -					   buf, is_global);
 > -	if (!dentry) {
 > -		relay_destroy_buf(buf);
 > -		return NULL;
 > +	dentry = chan->cb->create_buf_file(tmpname, chan->parent, S_IRUSR,
 > +					   buf, &chan->is_global);
 > +	if (!dentry)
 > +		goto free_buf;
 > +
 > +	if(chan->is_global) {
 > +		chan->buf[0] = buf;

If we added a foreach_possible_cpu() chan_buf[i] = buf here, we should
then be able to ignore hotcpu events altogether for global channels.

 > +		buf->cpu = 0;
 >  	}
 > 

Also, since we're now setting chan->buf[0] here, it might make sense
to do the same for the non-global case as well and change the return
value from struct rchan_buf * to an integer return code, since it no
longer needs to return a pointer to the buf.  If it helps, we might
also be able to move it to before create_buf_file() and have access to
buf->chan->buf[], but I haven't thought about whether it would break
anything.  On second thought, it's probably cleaner to leave it as it
is.

 >  	buf->dentry = dentry;
 > -	__relay_reset(buf, 1);
 > +	goto free_name;
 > 
 > +free_buf:
 > +	relay_destroy_buf(buf);
 > +free_name:
 > +	kfree(tmpname);
 > +end:
 >  	return buf;
 >  }
 > +EXPORT_SYMBOL_GPL(relay_open_buf);
 > 
 >  /**
 > - *	relay_close_buf - close a channel buffer
 > - *	@buf: channel buffer
 > + *	relay_close_write_buf - close write to a channel buffer
 > + *	@kref: buffer writers reference
 >   *
 >   *	Marks the buffer finalized and restores the default callbacks.
 >   *	The channel buffer and channel buffer data structure are then freed
 >   *	automatically when the last reference is given up.
 >   */
 > -static inline void relay_close_buf(struct rchan_buf *buf)
 > +static void relay_close_write_buf(struct kref *kref)
 >  {
 > +	struct rchan_buf *buf = container_of(kref, struct rchan_buf, krefw);
 > +
 >  	buf->finalized = 1;
 >  	cancel_delayed_work(&buf->wake_readers);
 >  	flush_scheduled_work();
 >  	kref_put(&buf->kref, relay_remove_buf);
 >  }
 > 
 > +/**
 > + *	relay_close_buf - close a channel buffer
 > + *	@buf: channel buffer
 > + *	
 > + *	Remove a writer reference.
 > + */
 > +
 > +void relay_close_buf(struct rchan_buf *buf)
 > +{
 > +	kref_put(&buf->krefw, relay_close_write_buf);
 > +}
 > +EXPORT_SYMBOL_GPL(relay_close_buf);
 > +
 >  static inline void setup_callbacks(struct rchan *chan,
 >  				   struct rchan_callbacks *cb)
 >  {
 > @@ -446,6 +493,45 @@ static inline void setup_callbacks(struc
 >  }
 > 
 >  /**
 > + *
 > + * 	relay_hotcpu_callback - CPU hotplug callback
 > + * 	@nb: notifier block
 > + * 	@action: hotplug action to take
 > + * 	@hcpu: CPU number
 > + *
 > + * 	Returns the success/failure of the operation. (NOTIFY_OK, NOTIFY_BAD)
 > + */
 > +static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb,
 > +				unsigned long action,
 > +				void *hcpu)
 > +{
 > +	unsigned int hotcpu = (unsigned long)hcpu;
 > +	struct rchan *chan;
 > +
 > +	switch(action) {
 > +	case CPU_UP_PREPARE:
 > +		mutex_lock(&relay_channels_mutex);
 > +		list_for_each_entry(chan, &relay_channels, list) {
 > +			chan->buf[hotcpu] = relay_open_buf(chan, hotcpu);
 > +			if(!chan->buf[hotcpu]) {
 > +				printk(KERN_ERR
 > +					"relay_hotcpu_callback: cpu %d buffer "
 > +					"creation failed\n", hotcpu);
 > +				mutex_unlock(&relay_channels_mutex);
 > +				return NOTIFY_BAD;
 > +			}
 > +		}
 > +		mutex_unlock(&relay_channels_mutex);
 > +		break;
 > +	case CPU_DEAD:
 > +		/* No need to flush the cpu : will be flushed upon
 > +		 * final relay_flush() call. */
 > +		break;
 > +	}
 > +	return NOTIFY_OK;
 > +}
 > +
 > +/**
 >   *	relay_open - create a new relay channel
 >   *	@base_filename: base name of files to create
 >   *	@parent: dentry of parent directory, NULL for root directory
 > @@ -464,13 +550,11 @@ struct rchan *relay_open(const char *bas
 >  			 struct dentry *parent,
 >  			 size_t subbuf_size,
 >  			 size_t n_subbufs,
 > -			 struct rchan_callbacks *cb)
 > +			 struct rchan_callbacks *cb,
 > +			 void *private_data)
 >  {
 >  	unsigned int i;
 >  	struct rchan *chan;
 > -	char *tmpname;
 > -	int is_global = 0;
 > -
 >  	if (!base_filename)
 >  		return NULL;
 > 
 > @@ -485,38 +569,40 @@ 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->is_global = 0;

This should already be 0 from the kcalloc.


Tom
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.