greybus: loopback: drop bus aggregate calculation
At some point we had a statement in a Jira work item to pull out 'bus level' data from greybus and to have messages to different interfaces be synchronized with respect to each other. Synchronizing threads with respect to each other is slow and it turns out we can get the same 'bus level' stastics by making the user-space test application smarter. That's great news for the in-kernel code since it means we can cut out a whole lot of code to-do with calculating 'bus level' aggregate data and we can stop forcing threads to hit a rendezvous before sending out another loopback operation. So this patch drops bus level aggregates in favour of doing that in user-space. It subtracts a lot of code and cycles that in practice nobody cares about anyway. Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Родитель
9673dcebe4
Коммит
8e1d6c336d
|
@ -36,29 +36,11 @@ struct gb_loopback_stats {
|
|||
|
||||
struct gb_loopback_device {
|
||||
struct dentry *root;
|
||||
struct dentry *file;
|
||||
u32 count;
|
||||
size_t size_max;
|
||||
|
||||
struct kfifo kfifo;
|
||||
struct mutex mutex;
|
||||
struct list_head list;
|
||||
wait_queue_head_t wq;
|
||||
|
||||
int type;
|
||||
u32 mask;
|
||||
u32 size;
|
||||
u32 iteration_max;
|
||||
u32 iteration_count;
|
||||
size_t size_max;
|
||||
int ms_wait;
|
||||
u32 error;
|
||||
|
||||
/* Overall stats */
|
||||
struct gb_loopback_stats latency;
|
||||
struct gb_loopback_stats throughput;
|
||||
struct gb_loopback_stats requests_per_second;
|
||||
struct gb_loopback_stats apbridge_unipro_latency;
|
||||
struct gb_loopback_stats gpbridge_firmware_latency;
|
||||
};
|
||||
|
||||
static struct gb_loopback_device gb_dev;
|
||||
|
@ -72,6 +54,7 @@ struct gb_loopback {
|
|||
struct mutex mutex;
|
||||
struct task_struct *task;
|
||||
struct list_head entry;
|
||||
wait_queue_head_t wq;
|
||||
|
||||
/* Per connection stats */
|
||||
struct gb_loopback_stats latency;
|
||||
|
@ -80,10 +63,15 @@ struct gb_loopback {
|
|||
struct gb_loopback_stats apbridge_unipro_latency;
|
||||
struct gb_loopback_stats gpbridge_firmware_latency;
|
||||
|
||||
u32 lbid;
|
||||
int type;
|
||||
u32 mask;
|
||||
u32 size;
|
||||
u32 iteration_max;
|
||||
u32 iteration_count;
|
||||
u64 elapsed_nsecs;
|
||||
int ms_wait;
|
||||
u32 error;
|
||||
u32 lbid;
|
||||
u64 elapsed_nsecs;
|
||||
u32 apbridge_latency_ts;
|
||||
u32 gpbridge_latency_ts;
|
||||
};
|
||||
|
@ -99,42 +87,34 @@ module_param(kfifo_depth, uint, 0444);
|
|||
#define GB_LOOPBACK_MS_WAIT_MAX 1000
|
||||
|
||||
/* interface sysfs attributes */
|
||||
#define gb_loopback_ro_attr(field, pfx, conn) \
|
||||
static ssize_t field##_##pfx##_show(struct device *dev, \
|
||||
#define gb_loopback_ro_attr(field) \
|
||||
static ssize_t field##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct gb_bundle *bundle; \
|
||||
struct gb_loopback *gb; \
|
||||
if (conn) { \
|
||||
bundle = to_gb_bundle(dev); \
|
||||
gb = bundle->private; \
|
||||
return sprintf(buf, "%u\n", gb->field); \
|
||||
} else { \
|
||||
return sprintf(buf, "%u\n", gb_dev.field); \
|
||||
} \
|
||||
bundle = to_gb_bundle(dev); \
|
||||
gb = bundle->private; \
|
||||
return sprintf(buf, "%u\n", gb->field); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(field##_##pfx)
|
||||
static DEVICE_ATTR_RO(field)
|
||||
|
||||
#define gb_loopback_ro_stats_attr(name, field, type, pfx, conn) \
|
||||
static ssize_t name##_##field##_##pfx##_show(struct device *dev, \
|
||||
#define gb_loopback_ro_stats_attr(name, field, type) \
|
||||
static ssize_t name##_##field##_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct gb_bundle *bundle; \
|
||||
struct gb_loopback *gb; \
|
||||
if (conn) { \
|
||||
bundle = to_gb_bundle(dev); \
|
||||
gb = bundle->private; \
|
||||
return sprintf(buf, "%"#type"\n", gb->name.field); \
|
||||
} else { \
|
||||
return sprintf(buf, "%"#type"\n", gb_dev.name.field); \
|
||||
} \
|
||||
bundle = to_gb_bundle(dev); \
|
||||
gb = bundle->private; \
|
||||
return sprintf(buf, "%"#type"\n", gb->name.field); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name##_##field##_##pfx)
|
||||
static DEVICE_ATTR_RO(name##_##field)
|
||||
|
||||
#define gb_loopback_ro_avg_attr(name, pfx, conn) \
|
||||
static ssize_t name##_avg_##pfx##_show(struct device *dev, \
|
||||
#define gb_loopback_ro_avg_attr(name) \
|
||||
static ssize_t name##_avg_show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
|
@ -143,24 +123,20 @@ static ssize_t name##_avg_##pfx##_show(struct device *dev, \
|
|||
struct gb_loopback *gb; \
|
||||
u64 avg; \
|
||||
u32 count, rem; \
|
||||
if (conn) { \
|
||||
bundle = to_gb_bundle(dev); \
|
||||
gb = bundle->private; \
|
||||
stats = &gb->name; \
|
||||
} else { \
|
||||
stats = &gb_dev.name; \
|
||||
} \
|
||||
bundle = to_gb_bundle(dev); \
|
||||
gb = bundle->private; \
|
||||
stats = &gb->name; \
|
||||
count = stats->count ? stats->count : 1; \
|
||||
avg = stats->sum + count / 2; /* round closest */ \
|
||||
rem = do_div(avg, count); \
|
||||
return sprintf(buf, "%llu.%06u\n", avg, 1000000 * rem / count); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name##_avg_##pfx)
|
||||
static DEVICE_ATTR_RO(name##_avg)
|
||||
|
||||
#define gb_loopback_stats_attrs(field, pfx, conn) \
|
||||
gb_loopback_ro_stats_attr(field, min, u, pfx, conn); \
|
||||
gb_loopback_ro_stats_attr(field, max, u, pfx, conn); \
|
||||
gb_loopback_ro_avg_attr(field, pfx, conn)
|
||||
#define gb_loopback_stats_attrs(field) \
|
||||
gb_loopback_ro_stats_attr(field, min, u); \
|
||||
gb_loopback_ro_stats_attr(field, max, u); \
|
||||
gb_loopback_ro_avg_attr(field)
|
||||
|
||||
#define gb_loopback_attr(field, type) \
|
||||
static ssize_t field##_show(struct device *dev, \
|
||||
|
@ -178,13 +154,14 @@ static ssize_t field##_store(struct device *dev, \
|
|||
{ \
|
||||
int ret; \
|
||||
struct gb_bundle *bundle = to_gb_bundle(dev); \
|
||||
mutex_lock(&gb_dev.mutex); \
|
||||
struct gb_loopback *gb = bundle->private; \
|
||||
mutex_lock(&gb->mutex); \
|
||||
ret = sscanf(buf, "%"#type, &gb->field); \
|
||||
if (ret != 1) \
|
||||
len = -EINVAL; \
|
||||
else \
|
||||
gb_loopback_check_attr(&gb_dev, bundle); \
|
||||
mutex_unlock(&gb_dev.mutex); \
|
||||
gb_loopback_check_attr(gb, bundle); \
|
||||
mutex_unlock(&gb->mutex); \
|
||||
return len; \
|
||||
} \
|
||||
static DEVICE_ATTR_RW(field)
|
||||
|
@ -194,7 +171,9 @@ static ssize_t field##_show(struct device *dev, \
|
|||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, "%u\n", gb_dev.field); \
|
||||
struct gb_bundle *bundle = to_gb_bundle(dev); \
|
||||
struct gb_loopback *gb = bundle->private; \
|
||||
return sprintf(buf, "%u\n", gb->field); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(field)
|
||||
|
||||
|
@ -203,7 +182,9 @@ static ssize_t field##_show(struct device *dev, \
|
|||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, "%"#type"\n", gb_dev.field); \
|
||||
struct gb_bundle *bundle = to_gb_bundle(dev); \
|
||||
struct gb_loopback *gb = bundle->private; \
|
||||
return sprintf(buf, "%"#type"\n", gb->field); \
|
||||
} \
|
||||
static ssize_t field##_store(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
|
@ -212,76 +193,63 @@ static ssize_t field##_store(struct device *dev, \
|
|||
{ \
|
||||
int ret; \
|
||||
struct gb_bundle *bundle = to_gb_bundle(dev); \
|
||||
mutex_lock(&gb_dev.mutex); \
|
||||
ret = sscanf(buf, "%"#type, &gb_dev.field); \
|
||||
struct gb_loopback *gb = bundle->private; \
|
||||
mutex_lock(&gb->mutex); \
|
||||
ret = sscanf(buf, "%"#type, &gb->field); \
|
||||
if (ret != 1) \
|
||||
len = -EINVAL; \
|
||||
else \
|
||||
gb_loopback_check_attr(&gb_dev, bundle); \
|
||||
mutex_unlock(&gb_dev.mutex); \
|
||||
gb_loopback_check_attr(gb, bundle); \
|
||||
mutex_unlock(&gb->mutex); \
|
||||
return len; \
|
||||
} \
|
||||
static DEVICE_ATTR_RW(field)
|
||||
|
||||
static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev);
|
||||
static void gb_loopback_check_attr(struct gb_loopback_device *gb_dev,
|
||||
static void gb_loopback_reset_stats(struct gb_loopback *gb);
|
||||
static void gb_loopback_check_attr(struct gb_loopback *gb,
|
||||
struct gb_bundle *bundle)
|
||||
{
|
||||
struct gb_loopback *gb;
|
||||
if (gb->ms_wait > GB_LOOPBACK_MS_WAIT_MAX)
|
||||
gb->ms_wait = GB_LOOPBACK_MS_WAIT_MAX;
|
||||
if (gb->size > gb_dev.size_max)
|
||||
gb->size = gb_dev.size_max;
|
||||
gb->iteration_count = 0;
|
||||
gb->error = 0;
|
||||
|
||||
if (gb_dev->ms_wait > GB_LOOPBACK_MS_WAIT_MAX)
|
||||
gb_dev->ms_wait = GB_LOOPBACK_MS_WAIT_MAX;
|
||||
if (gb_dev->size > gb_dev->size_max)
|
||||
gb_dev->size = gb_dev->size_max;
|
||||
gb_dev->iteration_count = 0;
|
||||
gb_dev->error = 0;
|
||||
|
||||
list_for_each_entry(gb, &gb_dev->list, entry) {
|
||||
mutex_lock(&gb->mutex);
|
||||
gb->iteration_count = 0;
|
||||
gb->error = 0;
|
||||
if (kfifo_depth < gb_dev->iteration_max) {
|
||||
dev_warn(&bundle->dev,
|
||||
"cannot log bytes %u kfifo_depth %u\n",
|
||||
gb_dev->iteration_max, kfifo_depth);
|
||||
}
|
||||
kfifo_reset_out(&gb->kfifo_lat);
|
||||
kfifo_reset_out(&gb->kfifo_ts);
|
||||
mutex_unlock(&gb->mutex);
|
||||
if (kfifo_depth < gb->iteration_max) {
|
||||
dev_warn(&bundle->dev,
|
||||
"cannot log bytes %u kfifo_depth %u\n",
|
||||
gb->iteration_max, kfifo_depth);
|
||||
}
|
||||
kfifo_reset_out(&gb->kfifo_lat);
|
||||
kfifo_reset_out(&gb->kfifo_ts);
|
||||
|
||||
switch (gb_dev->type) {
|
||||
switch (gb->type) {
|
||||
case GB_LOOPBACK_TYPE_PING:
|
||||
case GB_LOOPBACK_TYPE_TRANSFER:
|
||||
case GB_LOOPBACK_TYPE_SINK:
|
||||
kfifo_reset_out(&gb_dev->kfifo);
|
||||
gb_loopback_reset_stats(gb_dev);
|
||||
wake_up(&gb_dev->wq);
|
||||
gb_loopback_reset_stats(gb);
|
||||
wake_up(&gb->wq);
|
||||
break;
|
||||
default:
|
||||
gb_dev->type = 0;
|
||||
gb->type = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Time to send and receive one message */
|
||||
gb_loopback_stats_attrs(latency, dev, false);
|
||||
gb_loopback_stats_attrs(latency, con, true);
|
||||
gb_loopback_stats_attrs(latency);
|
||||
/* Number of requests sent per second on this cport */
|
||||
gb_loopback_stats_attrs(requests_per_second, dev, false);
|
||||
gb_loopback_stats_attrs(requests_per_second, con, true);
|
||||
gb_loopback_stats_attrs(requests_per_second);
|
||||
/* Quantity of data sent and received on this cport */
|
||||
gb_loopback_stats_attrs(throughput, dev, false);
|
||||
gb_loopback_stats_attrs(throughput, con, true);
|
||||
gb_loopback_stats_attrs(throughput);
|
||||
/* Latency across the UniPro link from APBridge's perspective */
|
||||
gb_loopback_stats_attrs(apbridge_unipro_latency, dev, false);
|
||||
gb_loopback_stats_attrs(apbridge_unipro_latency, con, true);
|
||||
gb_loopback_stats_attrs(apbridge_unipro_latency);
|
||||
/* Firmware induced overhead in the GPBridge */
|
||||
gb_loopback_stats_attrs(gpbridge_firmware_latency, dev, false);
|
||||
gb_loopback_stats_attrs(gpbridge_firmware_latency, con, true);
|
||||
gb_loopback_stats_attrs(gpbridge_firmware_latency);
|
||||
|
||||
/* Number of errors encountered during loop */
|
||||
gb_loopback_ro_attr(error, dev, false);
|
||||
gb_loopback_ro_attr(error, con, true);
|
||||
gb_loopback_ro_attr(error);
|
||||
|
||||
/*
|
||||
* Type of loopback message to send based on protocol type definitions
|
||||
|
@ -303,53 +271,32 @@ gb_dev_loopback_ro_attr(iteration_count, false);
|
|||
/* A bit-mask of destination connecitons to include in the test run */
|
||||
gb_dev_loopback_rw_attr(mask, u);
|
||||
|
||||
static struct attribute *loopback_dev_attrs[] = {
|
||||
&dev_attr_latency_min_dev.attr,
|
||||
&dev_attr_latency_max_dev.attr,
|
||||
&dev_attr_latency_avg_dev.attr,
|
||||
&dev_attr_requests_per_second_min_dev.attr,
|
||||
&dev_attr_requests_per_second_max_dev.attr,
|
||||
&dev_attr_requests_per_second_avg_dev.attr,
|
||||
&dev_attr_throughput_min_dev.attr,
|
||||
&dev_attr_throughput_max_dev.attr,
|
||||
&dev_attr_throughput_avg_dev.attr,
|
||||
&dev_attr_apbridge_unipro_latency_min_dev.attr,
|
||||
&dev_attr_apbridge_unipro_latency_max_dev.attr,
|
||||
&dev_attr_apbridge_unipro_latency_avg_dev.attr,
|
||||
&dev_attr_gpbridge_firmware_latency_min_dev.attr,
|
||||
&dev_attr_gpbridge_firmware_latency_max_dev.attr,
|
||||
&dev_attr_gpbridge_firmware_latency_avg_dev.attr,
|
||||
static struct attribute *loopback_attrs[] = {
|
||||
&dev_attr_latency_min.attr,
|
||||
&dev_attr_latency_max.attr,
|
||||
&dev_attr_latency_avg.attr,
|
||||
&dev_attr_requests_per_second_min.attr,
|
||||
&dev_attr_requests_per_second_max.attr,
|
||||
&dev_attr_requests_per_second_avg.attr,
|
||||
&dev_attr_throughput_min.attr,
|
||||
&dev_attr_throughput_max.attr,
|
||||
&dev_attr_throughput_avg.attr,
|
||||
&dev_attr_apbridge_unipro_latency_min.attr,
|
||||
&dev_attr_apbridge_unipro_latency_max.attr,
|
||||
&dev_attr_apbridge_unipro_latency_avg.attr,
|
||||
&dev_attr_gpbridge_firmware_latency_min.attr,
|
||||
&dev_attr_gpbridge_firmware_latency_max.attr,
|
||||
&dev_attr_gpbridge_firmware_latency_avg.attr,
|
||||
&dev_attr_type.attr,
|
||||
&dev_attr_size.attr,
|
||||
&dev_attr_ms_wait.attr,
|
||||
&dev_attr_iteration_count.attr,
|
||||
&dev_attr_iteration_max.attr,
|
||||
&dev_attr_mask.attr,
|
||||
&dev_attr_error_dev.attr,
|
||||
&dev_attr_error.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(loopback_dev);
|
||||
|
||||
static struct attribute *loopback_con_attrs[] = {
|
||||
&dev_attr_latency_min_con.attr,
|
||||
&dev_attr_latency_max_con.attr,
|
||||
&dev_attr_latency_avg_con.attr,
|
||||
&dev_attr_requests_per_second_min_con.attr,
|
||||
&dev_attr_requests_per_second_max_con.attr,
|
||||
&dev_attr_requests_per_second_avg_con.attr,
|
||||
&dev_attr_throughput_min_con.attr,
|
||||
&dev_attr_throughput_max_con.attr,
|
||||
&dev_attr_throughput_avg_con.attr,
|
||||
&dev_attr_apbridge_unipro_latency_min_con.attr,
|
||||
&dev_attr_apbridge_unipro_latency_max_con.attr,
|
||||
&dev_attr_apbridge_unipro_latency_avg_con.attr,
|
||||
&dev_attr_gpbridge_firmware_latency_min_con.attr,
|
||||
&dev_attr_gpbridge_firmware_latency_max_con.attr,
|
||||
&dev_attr_gpbridge_firmware_latency_avg_con.attr,
|
||||
&dev_attr_error_con.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(loopback_con);
|
||||
ATTRIBUTE_GROUPS(loopback);
|
||||
|
||||
static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs)
|
||||
{
|
||||
|
@ -385,11 +332,6 @@ static void gb_loopback_push_latency_ts(struct gb_loopback *gb,
|
|||
kfifo_in(&gb->kfifo_ts, (unsigned char *)te, sizeof(*te));
|
||||
}
|
||||
|
||||
static int gb_loopback_active(struct gb_loopback *gb)
|
||||
{
|
||||
return (gb_dev.mask == 0 || (gb_dev.mask & gb->lbid));
|
||||
}
|
||||
|
||||
static int gb_loopback_operation_sync(struct gb_loopback *gb, int type,
|
||||
void *request, int request_size,
|
||||
void *response, int response_size)
|
||||
|
@ -550,37 +492,32 @@ static int gb_loopback_request_recv(u8 type, struct gb_operation *operation)
|
|||
}
|
||||
}
|
||||
|
||||
static void gb_loopback_reset_stats(struct gb_loopback_device *gb_dev)
|
||||
static void gb_loopback_reset_stats(struct gb_loopback *gb)
|
||||
{
|
||||
struct gb_loopback_stats reset = {
|
||||
.min = U32_MAX,
|
||||
};
|
||||
struct gb_loopback *gb;
|
||||
|
||||
/* Reset per-connection stats */
|
||||
list_for_each_entry(gb, &gb_dev->list, entry) {
|
||||
mutex_lock(&gb->mutex);
|
||||
memcpy(&gb->latency, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->throughput, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->requests_per_second, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->apbridge_unipro_latency, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->gpbridge_firmware_latency, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
mutex_unlock(&gb->mutex);
|
||||
}
|
||||
memcpy(&gb->latency, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->throughput, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->requests_per_second, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->apbridge_unipro_latency, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->gpbridge_firmware_latency, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
|
||||
/* Reset aggregate stats */
|
||||
memcpy(&gb_dev->latency, &reset, sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb_dev->throughput, &reset, sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb_dev->requests_per_second, &reset,
|
||||
memcpy(&gb->latency, &reset, sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->throughput, &reset, sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb->requests_per_second, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb_dev->apbridge_unipro_latency, &reset,
|
||||
memcpy(&gb->apbridge_unipro_latency, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
memcpy(&gb_dev->gpbridge_firmware_latency, &reset,
|
||||
memcpy(&gb->gpbridge_firmware_latency, &reset,
|
||||
sizeof(struct gb_loopback_stats));
|
||||
}
|
||||
|
||||
|
@ -599,7 +536,6 @@ static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency)
|
|||
u32 req = USEC_PER_SEC;
|
||||
|
||||
do_div(req, latency);
|
||||
gb_loopback_update_stats(&gb_dev.requests_per_second, req);
|
||||
gb_loopback_update_stats(&gb->requests_per_second, req);
|
||||
}
|
||||
|
||||
|
@ -608,17 +544,17 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency)
|
|||
u32 throughput;
|
||||
u32 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2;
|
||||
|
||||
switch (gb_dev.type) {
|
||||
switch (gb->type) {
|
||||
case GB_LOOPBACK_TYPE_PING:
|
||||
break;
|
||||
case GB_LOOPBACK_TYPE_SINK:
|
||||
aggregate_size += sizeof(struct gb_loopback_transfer_request) +
|
||||
gb_dev.size;
|
||||
gb->size;
|
||||
break;
|
||||
case GB_LOOPBACK_TYPE_TRANSFER:
|
||||
aggregate_size += sizeof(struct gb_loopback_transfer_request) +
|
||||
sizeof(struct gb_loopback_transfer_response) +
|
||||
gb_dev.size * 2;
|
||||
gb->size * 2;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
@ -628,65 +564,9 @@ static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency)
|
|||
throughput = USEC_PER_SEC;
|
||||
do_div(throughput, latency);
|
||||
throughput *= aggregate_size;
|
||||
gb_loopback_update_stats(&gb_dev.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 (!gb_loopback_active(gb))
|
||||
continue;
|
||||
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)
|
||||
{
|
||||
u32 lat;
|
||||
|
@ -695,7 +575,6 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb)
|
|||
lat = gb_loopback_nsec_to_usec_latency(gb->elapsed_nsecs);
|
||||
|
||||
/* Log latency stastic */
|
||||
gb_loopback_update_stats(&gb_dev.latency, lat);
|
||||
gb_loopback_update_stats(&gb->latency, lat);
|
||||
|
||||
/* Raw latency log on a per thread basis */
|
||||
|
@ -706,12 +585,8 @@ static void gb_loopback_calculate_stats(struct gb_loopback *gb)
|
|||
gb_loopback_requests_update(gb, lat);
|
||||
|
||||
/* Log the firmware supplied latency values */
|
||||
gb_loopback_update_stats(&gb_dev.apbridge_unipro_latency,
|
||||
gb->apbridge_latency_ts);
|
||||
gb_loopback_update_stats(&gb->apbridge_unipro_latency,
|
||||
gb->apbridge_latency_ts);
|
||||
gb_loopback_update_stats(&gb_dev.gpbridge_firmware_latency,
|
||||
gb->gpbridge_latency_ts);
|
||||
gb_loopback_update_stats(&gb->gpbridge_firmware_latency,
|
||||
gb->gpbridge_latency_ts);
|
||||
}
|
||||
|
@ -722,56 +597,29 @@ static int gb_loopback_fn(void *data)
|
|||
int ms_wait = 0;
|
||||
int type;
|
||||
u32 size;
|
||||
u32 low_count;
|
||||
struct gb_loopback *gb = data;
|
||||
struct gb_loopback *gb_list;
|
||||
|
||||
while (1) {
|
||||
if (!gb_dev.type)
|
||||
wait_event_interruptible(gb_dev.wq, gb_dev.type ||
|
||||
if (!gb->type)
|
||||
wait_event_interruptible(gb->wq, gb->type ||
|
||||
kthread_should_stop());
|
||||
if (kthread_should_stop())
|
||||
break;
|
||||
|
||||
mutex_lock(&gb_dev.mutex);
|
||||
if (!gb_loopback_active(gb)) {
|
||||
ms_wait = 100;
|
||||
goto unlock_continue;
|
||||
}
|
||||
if (gb_dev.iteration_max) {
|
||||
/* Determine overall lowest count */
|
||||
low_count = gb->iteration_count;
|
||||
list_for_each_entry(gb_list, &gb_dev.list, entry) {
|
||||
if (!gb_loopback_active(gb_list))
|
||||
continue;
|
||||
if (gb_list->iteration_count < low_count)
|
||||
low_count = gb_list->iteration_count;
|
||||
}
|
||||
/* All threads achieved at least low_count iterations */
|
||||
if (gb_dev.iteration_count < low_count) {
|
||||
gb_dev.iteration_count = low_count;
|
||||
sysfs_notify(&gb->connection->bundle->dev.kobj,
|
||||
NULL, "iteration_count");
|
||||
}
|
||||
/* Optionally terminate */
|
||||
if (gb_dev.iteration_count == gb_dev.iteration_max) {
|
||||
gb_loopback_calculate_aggregate_stats();
|
||||
gb_dev.type = 0;
|
||||
goto unlock_continue;
|
||||
}
|
||||
}
|
||||
size = gb_dev.size;
|
||||
ms_wait = gb_dev.ms_wait;
|
||||
type = gb_dev.type;
|
||||
mutex_unlock(&gb_dev.mutex);
|
||||
|
||||
mutex_lock(&gb->mutex);
|
||||
if (gb->iteration_count >= gb_dev.iteration_max) {
|
||||
/* If this thread finished before siblings then sleep */
|
||||
ms_wait = 1;
|
||||
|
||||
sysfs_notify(&gb->connection->bundle->dev.kobj,
|
||||
NULL, "iteration_count");
|
||||
|
||||
/* Optionally terminate */
|
||||
if (gb->iteration_count == gb->iteration_max) {
|
||||
gb->type = 0;
|
||||
mutex_unlock(&gb->mutex);
|
||||
goto sleep;
|
||||
continue;
|
||||
}
|
||||
size = gb->size;
|
||||
ms_wait = gb->ms_wait;
|
||||
type = gb->type;
|
||||
mutex_unlock(&gb->mutex);
|
||||
|
||||
/* Else operations to perform */
|
||||
|
@ -782,20 +630,12 @@ static int gb_loopback_fn(void *data)
|
|||
else if (type == GB_LOOPBACK_TYPE_SINK)
|
||||
error = gb_loopback_sink(gb, size);
|
||||
|
||||
mutex_lock(&gb_dev.mutex);
|
||||
mutex_lock(&gb->mutex);
|
||||
|
||||
if (error) {
|
||||
gb_dev.error++;
|
||||
if (error)
|
||||
gb->error++;
|
||||
}
|
||||
|
||||
gb_loopback_calculate_stats(gb);
|
||||
gb->iteration_count++;
|
||||
|
||||
mutex_unlock(&gb->mutex);
|
||||
unlock_continue:
|
||||
mutex_unlock(&gb_dev.mutex);
|
||||
sleep:
|
||||
if (ms_wait)
|
||||
msleep(ms_wait);
|
||||
}
|
||||
|
@ -846,27 +686,6 @@ static const struct file_operations gb_loopback_debugfs_latency_ops = {
|
|||
.release = single_release,
|
||||
};
|
||||
|
||||
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 gb_loopback_bus_id_compare(void *priv, struct list_head *lha,
|
||||
struct list_head *lhb)
|
||||
{
|
||||
|
@ -912,31 +731,22 @@ static int gb_loopback_connection_init(struct gb_connection *connection)
|
|||
struct gb_loopback *gb;
|
||||
int retval;
|
||||
char name[DEBUGFS_NAMELEN];
|
||||
struct kobject *kobj = &connection->bundle->dev.kobj;
|
||||
|
||||
gb = kzalloc(sizeof(*gb), GFP_KERNEL);
|
||||
if (!gb)
|
||||
return -ENOMEM;
|
||||
gb_loopback_reset_stats(&gb_dev);
|
||||
|
||||
/* If this is the first connection - create a per-bus entry */
|
||||
init_waitqueue_head(&gb->wq);
|
||||
gb_loopback_reset_stats(gb);
|
||||
|
||||
mutex_lock(&gb_dev.mutex);
|
||||
if (!gb_dev.count) {
|
||||
snprintf(name, sizeof(name), "raw_latency_%d",
|
||||
connection->bundle->intf->hd->bus_id);
|
||||
gb_dev.file = debugfs_create_file(name, S_IFREG | S_IRUGO,
|
||||
gb_dev.root, &gb_dev,
|
||||
&gb_loopback_debugfs_dev_latency_ops);
|
||||
retval = sysfs_create_groups(kobj, loopback_dev_groups);
|
||||
if (retval)
|
||||
goto out_sysfs;
|
||||
|
||||
/* Calculate maximum payload */
|
||||
gb_dev.size_max = gb_operation_get_payload_size_max(connection);
|
||||
if (gb_dev.size_max <=
|
||||
sizeof(struct gb_loopback_transfer_request)) {
|
||||
retval = -EINVAL;
|
||||
goto out_sysfs_dev;
|
||||
goto out_sysfs;
|
||||
}
|
||||
gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request);
|
||||
}
|
||||
|
@ -949,9 +759,9 @@ static int gb_loopback_connection_init(struct gb_connection *connection)
|
|||
gb->connection = connection;
|
||||
connection->bundle->private = gb;
|
||||
retval = sysfs_create_groups(&connection->bundle->dev.kobj,
|
||||
loopback_con_groups);
|
||||
loopback_groups);
|
||||
if (retval)
|
||||
goto out_sysfs_dev;
|
||||
goto out_sysfs;
|
||||
|
||||
/* Allocate kfifo */
|
||||
if (kfifo_alloc(&gb->kfifo_lat, kfifo_depth * sizeof(u32),
|
||||
|
@ -984,15 +794,10 @@ out_kfifo1:
|
|||
out_kfifo0:
|
||||
kfifo_free(&gb->kfifo_lat);
|
||||
out_sysfs_conn:
|
||||
sysfs_remove_groups(&connection->bundle->dev.kobj, loopback_con_groups);
|
||||
out_sysfs_dev:
|
||||
if (!gb_dev.count) {
|
||||
sysfs_remove_groups(kobj, loopback_dev_groups);
|
||||
debugfs_remove(gb_dev.file);
|
||||
}
|
||||
sysfs_remove_groups(&connection->bundle->dev.kobj, loopback_groups);
|
||||
out_sysfs:
|
||||
debugfs_remove(gb->file);
|
||||
connection->bundle->private = NULL;
|
||||
out_sysfs:
|
||||
mutex_unlock(&gb_dev.mutex);
|
||||
kfree(gb);
|
||||
|
||||
|
@ -1002,7 +807,6 @@ out_sysfs:
|
|||
static void gb_loopback_connection_exit(struct gb_connection *connection)
|
||||
{
|
||||
struct gb_loopback *gb = connection->bundle->private;
|
||||
struct kobject *kobj = &connection->bundle->dev.kobj;
|
||||
|
||||
if (!IS_ERR_OR_NULL(gb->task))
|
||||
kthread_stop(gb->task);
|
||||
|
@ -1014,12 +818,8 @@ static void gb_loopback_connection_exit(struct gb_connection *connection)
|
|||
kfifo_free(&gb->kfifo_ts);
|
||||
gb_connection_latency_tag_disable(connection);
|
||||
gb_dev.count--;
|
||||
if (!gb_dev.count) {
|
||||
sysfs_remove_groups(kobj, loopback_dev_groups);
|
||||
debugfs_remove(gb_dev.file);
|
||||
}
|
||||
sysfs_remove_groups(&connection->bundle->dev.kobj,
|
||||
loopback_con_groups);
|
||||
loopback_groups);
|
||||
debugfs_remove(gb->file);
|
||||
list_del(&gb->entry);
|
||||
mutex_unlock(&gb_dev.mutex);
|
||||
|
@ -1040,21 +840,14 @@ static int loopback_init(void)
|
|||
{
|
||||
int retval;
|
||||
|
||||
init_waitqueue_head(&gb_dev.wq);
|
||||
INIT_LIST_HEAD(&gb_dev.list);
|
||||
mutex_init(&gb_dev.mutex);
|
||||
gb_dev.root = debugfs_create_dir("gb_loopback", NULL);
|
||||
|
||||
if (kfifo_alloc(&gb_dev.kfifo, kfifo_depth * sizeof(u32), GFP_KERNEL)) {
|
||||
retval = -ENOMEM;
|
||||
goto error_debugfs;
|
||||
}
|
||||
|
||||
retval = gb_protocol_register(&loopback_protocol);
|
||||
if (!retval)
|
||||
return retval;
|
||||
|
||||
error_debugfs:
|
||||
debugfs_remove_recursive(gb_dev.root);
|
||||
return retval;
|
||||
}
|
||||
|
@ -1063,7 +856,6 @@ module_init(loopback_init);
|
|||
static void __exit loopback_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(gb_dev.root);
|
||||
kfifo_free(&gb_dev.kfifo);
|
||||
gb_protocol_deregister(&loopback_protocol);
|
||||
}
|
||||
module_exit(loopback_exit);
|
||||
|
|
Загрузка…
Ссылка в новой задаче