Commit 4b0ea00c authored by Bryan O'Donoghue's avatar Bryan O'Donoghue Committed by Greg Kroah-Hartman

greybus: loopback: graph round-trip time for all threads

This patch adds the ability to time the delta between all threads like this

t1 = timestmap();
thread1:gb_operation_sync();
thread2:gb_operation_sync();
t2 = timestamp();

In order to enable that behaviour without forcing an undesirable
checkpointing scheme this patch introduces a kfifo for each thread to store
the raw timestamps and calculate a time difference.
Signed-off-by: default avatarBryan O'Donoghue <bryan.odonoghue@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 7c985351
...@@ -36,6 +36,7 @@ struct gb_loopback_device { ...@@ -36,6 +36,7 @@ struct gb_loopback_device {
struct dentry *root; struct dentry *root;
u32 count; u32 count;
struct kfifo kfifo;
struct mutex mutex; struct mutex mutex;
struct list_head list; struct list_head list;
wait_queue_head_t wq; wait_queue_head_t wq;
...@@ -64,7 +65,8 @@ struct gb_loopback { ...@@ -64,7 +65,8 @@ struct gb_loopback {
struct gb_connection *connection; struct gb_connection *connection;
struct dentry *file; struct dentry *file;
struct kfifo kfifo; struct kfifo kfifo_lat;
struct kfifo kfifo_ts;
struct mutex mutex; struct mutex mutex;
struct task_struct *task; struct task_struct *task;
struct list_head entry; struct list_head entry;
...@@ -75,6 +77,7 @@ struct gb_loopback { ...@@ -75,6 +77,7 @@ struct gb_loopback {
struct gb_loopback_stats throughput; struct gb_loopback_stats throughput;
struct gb_loopback_stats requests_per_second; struct gb_loopback_stats requests_per_second;
u32 lbid;
u32 iteration_count; u32 iteration_count;
u64 elapsed_nsecs; u64 elapsed_nsecs;
u64 elapsed_nsecs_gb; u64 elapsed_nsecs_gb;
...@@ -217,7 +220,8 @@ static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, ...@@ -217,7 +220,8 @@ static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev,
"cannot log bytes %u kfifo_depth %u\n", "cannot log bytes %u kfifo_depth %u\n",
gb_dev->iteration_max, kfifo_depth); gb_dev->iteration_max, kfifo_depth);
} }
kfifo_reset_out(&gb->kfifo); kfifo_reset_out(&gb->kfifo_lat);
kfifo_reset_out(&gb->kfifo_ts);
mutex_unlock(&gb->mutex); mutex_unlock(&gb->mutex);
} }
...@@ -225,6 +229,7 @@ static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev, ...@@ -225,6 +229,7 @@ static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev,
case GB_LOOPBACK_TYPE_PING: case GB_LOOPBACK_TYPE_PING:
case GB_LOOPBACK_TYPE_TRANSFER: case GB_LOOPBACK_TYPE_TRANSFER:
case GB_LOOPBACK_TYPE_SINK: case GB_LOOPBACK_TYPE_SINK:
kfifo_reset_out(&gb_dev->kfifo);
gb_loopback_reset_stats(gb_dev); gb_loopback_reset_stats(gb_dev);
wake_up(&gb_dev->wq); wake_up(&gb_dev->wq);
break; break;
...@@ -310,6 +315,13 @@ static u64 gb_loopback_calc_latency(struct timeval *ts, struct timeval *te) ...@@ -310,6 +315,13 @@ static u64 gb_loopback_calc_latency(struct timeval *ts, struct timeval *te)
return __gb_loopback_calc_latency(t1, t2); return __gb_loopback_calc_latency(t1, t2);
} }
static void gb_loopback_push_latency_ts(struct gb_loopback *gb,
struct timeval *ts, struct timeval *te)
{
kfifo_in(&gb->kfifo_ts, (unsigned char *)ts, sizeof(*ts));
kfifo_in(&gb->kfifo_ts, (unsigned char *)te, sizeof(*te));
}
static int gb_loopback_sink(struct gb_loopback *gb, u32 len) static int gb_loopback_sink(struct gb_loopback *gb, u32 len)
{ {
struct timeval ts, te; struct timeval ts, te;
...@@ -329,6 +341,7 @@ static int gb_loopback_sink(struct gb_loopback *gb, u32 len) ...@@ -329,6 +341,7 @@ static int gb_loopback_sink(struct gb_loopback *gb, u32 len)
do_gettimeofday(&te); do_gettimeofday(&te);
/* Calculate the total time the message took */ /* Calculate the total time the message took */
gb_loopback_push_latency_ts(gb, &ts, &te);
gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te);
/* Calculate non-greybus related component of the latency */ /* Calculate non-greybus related component of the latency */
...@@ -368,6 +381,7 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len) ...@@ -368,6 +381,7 @@ static int gb_loopback_transfer(struct gb_loopback *gb, u32 len)
do_gettimeofday(&te); do_gettimeofday(&te);
/* Calculate the total time the message took */ /* Calculate the total time the message took */
gb_loopback_push_latency_ts(gb, &ts, &te);
gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te);
/* Calculate non-greybus related component of the latency */ /* Calculate non-greybus related component of the latency */
...@@ -401,6 +415,7 @@ static int gb_loopback_ping(struct gb_loopback *gb) ...@@ -401,6 +415,7 @@ static int gb_loopback_ping(struct gb_loopback *gb)
do_gettimeofday(&te); do_gettimeofday(&te);
/* Calculate the total time the message took */ /* Calculate the total time the message took */
gb_loopback_push_latency_ts(gb, &ts, &te);
gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te);
/* Calculate non-greybus related component of the latency */ /* Calculate non-greybus related component of the latency */
...@@ -541,6 +556,59 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) ...@@ -541,6 +556,59 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency)
gb_loopback_update_stats(&gb->throughput, throughput); gb_loopback_update_stats(&gb->throughput, throughput);
} }
static int gb_loopback_calculate_aggregate_stats(void)
{
struct gb_loopback *gb;
struct timeval ts;
struct timeval te;
u64 t1, t2;
u64 ts_min;
u64 te_max;
u64 elapsed_nsecs;
u32 lat;
int i, latched;
int rollover = 0;
for (i = 0; i < gb_dev.iteration_max; i++) {
latched = 0;
ts_min = 0;
te_max = 0;
list_for_each_entry(gb, &gb_dev.list, entry) {
if (kfifo_out(&gb->kfifo_ts, &ts, sizeof(ts)) < sizeof(ts))
goto error;
if (kfifo_out(&gb->kfifo_ts, &te, sizeof(te)) < sizeof(te))
goto error;
t1 = timeval_to_ns(&ts);
t2 = timeval_to_ns(&te);
/* minimum timestamp is always what we want */
if (latched == 0 || t1 < ts_min)
ts_min = t1;
/* maximum timestamp needs to handle rollover */
if (t2 > t1) {
if (latched == 0 || t2 > te_max)
te_max = t2;
} else {
if (latched == 0 || rollover == 0)
te_max = t2;
if (rollover == 1 && t2 > te_max)
te_max = t2;
rollover = 1;
}
latched = 1;
}
/* Calculate the aggregate timestamp */
elapsed_nsecs = __gb_loopback_calc_latency(ts_min, te_max);
lat = gb_loopback_nsec_to_usec_latency(elapsed_nsecs);
kfifo_in(&gb_dev.kfifo, (unsigned char *)&lat, sizeof(lat));
}
return 0;
error:
kfifo_reset_out(&gb_dev.kfifo);
return -ENOMEM;
}
static void gb_loopback_calculate_stats(struct gb_loopback *gb) static void gb_loopback_calculate_stats(struct gb_loopback *gb)
{ {
u32 lat; u32 lat;
...@@ -554,7 +622,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb) ...@@ -554,7 +622,7 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb)
gb_loopback_update_stats(&gb->latency, lat); gb_loopback_update_stats(&gb->latency, lat);
/* Raw latency log on a per thread basis */ /* Raw latency log on a per thread basis */
kfifo_in(&gb->kfifo, (unsigned char *)&lat, sizeof(lat)); kfifo_in(&gb->kfifo_lat, (unsigned char *)&lat, sizeof(lat));
/* Log throughput and requests using latency as benchmark */ /* Log throughput and requests using latency as benchmark */
gb_loopback_throughput_update(gb, lat); gb_loopback_throughput_update(gb, lat);
...@@ -600,6 +668,7 @@ static int gb_loopback_fn(void *data) ...@@ -600,6 +668,7 @@ static int gb_loopback_fn(void *data)
} }
/* Optionally terminate */ /* Optionally terminate */
if (gb_dev.iteration_count == gb_dev.iteration_max) { if (gb_dev.iteration_count == gb_dev.iteration_max) {
gb_loopback_calculate_aggregate_stats();
gb_dev.type = 0; gb_dev.type = 0;
mutex_unlock(&gb_dev.mutex); mutex_unlock(&gb_dev.mutex);
continue; continue;
...@@ -645,28 +714,37 @@ static int gb_loopback_fn(void *data) ...@@ -645,28 +714,37 @@ static int gb_loopback_fn(void *data)
return 0; return 0;
} }
static int gb_loopback_dbgfs_latency_show(struct seq_file *s, void *unused) static int gb_loopback_dbgfs_latency_show_common(struct seq_file *s,
struct kfifo *kfifo,
struct mutex *mutex)
{ {
struct gb_loopback *gb = s->private;
u32 latency; u32 latency;
int retval; int retval;
if (kfifo_len(&gb->kfifo) == 0) { if (kfifo_len(kfifo) == 0) {
retval = -EAGAIN; retval = -EAGAIN;
goto done; goto done;
} }
mutex_lock(&gb->mutex); mutex_lock(mutex);
retval = kfifo_out(&gb->kfifo, &latency, sizeof(latency)); retval = kfifo_out(kfifo, &latency, sizeof(latency));
if (retval > 0) { if (retval > 0) {
seq_printf(s, "%u", latency); seq_printf(s, "%u", latency);
retval = 0; retval = 0;
} }
mutex_unlock(&gb->mutex); mutex_unlock(mutex);
done: done:
return retval; return retval;
} }
static int gb_loopback_dbgfs_latency_show(struct seq_file *s, void *unused)
{
struct gb_loopback *gb = s->private;
return gb_loopback_dbgfs_latency_show_common(s, &gb->kfifo_lat,
&gb->mutex);
}
static int gb_loopback_latency_open(struct inode *inode, struct file *file) static int gb_loopback_latency_open(struct inode *inode, struct file *file)
{ {
return single_open(file, gb_loopback_dbgfs_latency_show, return single_open(file, gb_loopback_dbgfs_latency_show,
...@@ -717,18 +795,24 @@ static int gb_loopback_connection_init(struct gb_connection *connection) ...@@ -717,18 +795,24 @@ static int gb_loopback_connection_init(struct gb_connection *connection)
mutex_unlock(&gb_dev.mutex); mutex_unlock(&gb_dev.mutex);
/* Allocate kfifo */ /* Allocate kfifo */
if (kfifo_alloc(&gb->kfifo, kfifo_depth * sizeof(u32), if (kfifo_alloc(&gb->kfifo_lat, kfifo_depth * sizeof(u32),
GFP_KERNEL)) { GFP_KERNEL)) {
retval = -ENOMEM; retval = -ENOMEM;
goto out_sysfs; goto out_sysfs;
} }
if (kfifo_alloc(&gb->kfifo_ts, kfifo_depth * sizeof(struct timeval) * 2,
GFP_KERNEL)) {
retval = -ENOMEM;
goto out_kfifo0;
}
/* Fork worker thread */ /* Fork worker thread */
mutex_init(&gb->mutex); mutex_init(&gb->mutex);
gb->lbid = 1 << gb_dev.count;
gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback");
if (IS_ERR(gb->task)) { if (IS_ERR(gb->task)) {
retval = PTR_ERR(gb->task); retval = PTR_ERR(gb->task);
goto out_kfifo; goto out_kfifo1;
} }
mutex_lock(&gb_dev.mutex); mutex_lock(&gb_dev.mutex);
...@@ -737,8 +821,10 @@ static int gb_loopback_connection_init(struct gb_connection *connection) ...@@ -737,8 +821,10 @@ static int gb_loopback_connection_init(struct gb_connection *connection)
gb_dev.count++; gb_dev.count++;
return 0; return 0;
out_kfifo: out_kfifo1:
kfifo_free(&gb->kfifo); kfifo_free(&gb->kfifo_ts);
out_kfifo0:
kfifo_free(&gb->kfifo_lat);
out_sysfs: out_sysfs:
sysfs_remove_groups(&connection->dev.kobj, loopback_groups); sysfs_remove_groups(&connection->dev.kobj, loopback_groups);
out_debugfs: out_debugfs:
...@@ -758,7 +844,8 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) ...@@ -758,7 +844,8 @@ static void gb_loopback_connection_exit(struct gb_connection *connection)
if (!IS_ERR_OR_NULL(gb->task)) if (!IS_ERR_OR_NULL(gb->task))
kthread_stop(gb->task); kthread_stop(gb->task);
kfifo_free(&gb->kfifo); kfifo_free(&gb->kfifo_lat);
kfifo_free(&gb->kfifo_ts);
sysfs_remove_groups(&connection->dev.kobj, loopback_groups); sysfs_remove_groups(&connection->dev.kobj, loopback_groups);
debugfs_remove(gb->file); debugfs_remove(gb->file);
kfree(gb); kfree(gb);
...@@ -774,20 +861,58 @@ static struct gb_protocol loopback_protocol = { ...@@ -774,20 +861,58 @@ static struct gb_protocol loopback_protocol = {
.request_recv = gb_loopback_request_recv, .request_recv = gb_loopback_request_recv,
}; };
static int gb_loopback_dbgfs_dev_latency_show(struct seq_file *s, void *unused)
{
struct gb_loopback_device *gb_dev = s->private;
return gb_loopback_dbgfs_latency_show_common(s, &gb_dev->kfifo,
&gb_dev->mutex);
}
static int gb_loopback_dev_latency_open(struct inode *inode, struct file *file)
{
return single_open(file, gb_loopback_dbgfs_dev_latency_show,
inode->i_private);
}
static const struct file_operations gb_loopback_debugfs_dev_latency_ops = {
.open = gb_loopback_dev_latency_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int loopback_init(void) static int loopback_init(void)
{ {
int retval;
init_waitqueue_head(&gb_dev.wq); init_waitqueue_head(&gb_dev.wq);
INIT_LIST_HEAD(&gb_dev.list); INIT_LIST_HEAD(&gb_dev.list);
mutex_init(&gb_dev.mutex); mutex_init(&gb_dev.mutex);
gb_dev.root = debugfs_create_dir("gb_loopback", NULL); gb_dev.root = debugfs_create_dir("gb_loopback", NULL);
return gb_protocol_register(&loopback_protocol); if (kfifo_alloc(&gb_dev.kfifo, kfifo_depth * sizeof(u32), GFP_KERNEL)) {
retval = -ENOMEM;
goto error_debugfs;
}
debugfs_create_file("aggregate_latency", S_IFREG | S_IRUGO,
gb_dev.root, &gb_dev,
&gb_loopback_debugfs_dev_latency_ops);
retval = gb_protocol_register(&loopback_protocol);
if (!retval)
return retval;
error_debugfs:
debugfs_remove_recursive(gb_dev.root);
return retval;
} }
module_init(loopback_init); module_init(loopback_init);
static void __exit loopback_exit(void) static void __exit loopback_exit(void)
{ {
debugfs_remove_recursive(gb_dev.root); debugfs_remove_recursive(gb_dev.root);
kfifo_free(&gb_dev.kfifo);
gb_protocol_deregister(&loopback_protocol); gb_protocol_deregister(&loopback_protocol);
} }
module_exit(loopback_exit); module_exit(loopback_exit);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment