summaryrefslogtreecommitdiff
path: root/drivers/hwtracing/stm/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwtracing/stm/core.c')
-rw-r--r--drivers/hwtracing/stm/core.c292
1 files changed, 238 insertions, 54 deletions
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
index 10bcb5d73f90..93ce3aa740a9 100644
--- a/drivers/hwtracing/stm/core.c
+++ b/drivers/hwtracing/stm/core.c
@@ -293,15 +293,15 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width,
if (width > stm->data->sw_nchannels)
return -EINVAL;
- if (policy_node) {
- stp_policy_node_get_ranges(policy_node,
- &midx, &mend, &cidx, &cend);
- } else {
- midx = stm->data->sw_start;
- cidx = 0;
- mend = stm->data->sw_end;
- cend = stm->data->sw_nchannels - 1;
- }
+ /* We no longer accept policy_node==NULL here */
+ if (WARN_ON_ONCE(!policy_node))
+ return -EINVAL;
+
+ /*
+ * Also, the caller holds reference to policy_node, so it won't
+ * disappear on us.
+ */
+ stp_policy_node_get_ranges(policy_node, &midx, &mend, &cidx, &cend);
spin_lock(&stm->mc_lock);
spin_lock(&output->lock);
@@ -316,11 +316,26 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width,
output->master = midx;
output->channel = cidx;
output->nr_chans = width;
+ if (stm->pdrv->output_open) {
+ void *priv = stp_policy_node_priv(policy_node);
+
+ if (WARN_ON_ONCE(!priv))
+ goto unlock;
+
+ /* configfs subsys mutex is held by the caller */
+ ret = stm->pdrv->output_open(priv, output);
+ if (ret)
+ goto unlock;
+ }
+
stm_output_claim(stm, output);
dev_dbg(&stm->dev, "assigned %u:%u (+%u)\n", midx, cidx, width);
ret = 0;
unlock:
+ if (ret)
+ output->nr_chans = 0;
+
spin_unlock(&output->lock);
spin_unlock(&stm->mc_lock);
@@ -333,6 +348,8 @@ static void stm_output_free(struct stm_device *stm, struct stm_output *output)
spin_lock(&output->lock);
if (output->nr_chans)
stm_output_disclaim(stm, output);
+ if (stm->pdrv && stm->pdrv->output_close)
+ stm->pdrv->output_close(output);
spin_unlock(&output->lock);
spin_unlock(&stm->mc_lock);
}
@@ -349,6 +366,127 @@ static int major_match(struct device *dev, const void *data)
return MAJOR(dev->devt) == major;
}
+/*
+ * Framing protocol management
+ * Modules can implement STM protocol drivers and (un-)register them
+ * with the STM class framework.
+ */
+static struct list_head stm_pdrv_head;
+static struct mutex stm_pdrv_mutex;
+
+struct stm_pdrv_entry {
+ struct list_head entry;
+ const struct stm_protocol_driver *pdrv;
+ const struct config_item_type *node_type;
+};
+
+static const struct stm_pdrv_entry *
+__stm_lookup_protocol(const char *name)
+{
+ struct stm_pdrv_entry *pe;
+
+ /*
+ * If no name is given (NULL or ""), fall back to "p_basic".
+ */
+ if (!name || !*name)
+ name = "p_basic";
+
+ list_for_each_entry(pe, &stm_pdrv_head, entry) {
+ if (!strcmp(name, pe->pdrv->name))
+ return pe;
+ }
+
+ return NULL;
+}
+
+int stm_register_protocol(const struct stm_protocol_driver *pdrv)
+{
+ struct stm_pdrv_entry *pe = NULL;
+ int ret = -ENOMEM;
+
+ mutex_lock(&stm_pdrv_mutex);
+
+ if (__stm_lookup_protocol(pdrv->name)) {
+ ret = -EEXIST;
+ goto unlock;
+ }
+
+ pe = kzalloc(sizeof(*pe), GFP_KERNEL);
+ if (!pe)
+ goto unlock;
+
+ if (pdrv->policy_attr) {
+ pe->node_type = get_policy_node_type(pdrv->policy_attr);
+ if (!pe->node_type)
+ goto unlock;
+ }
+
+ list_add_tail(&pe->entry, &stm_pdrv_head);
+ pe->pdrv = pdrv;
+
+ ret = 0;
+unlock:
+ mutex_unlock(&stm_pdrv_mutex);
+
+ if (ret)
+ kfree(pe);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(stm_register_protocol);
+
+void stm_unregister_protocol(const struct stm_protocol_driver *pdrv)
+{
+ struct stm_pdrv_entry *pe, *iter;
+
+ mutex_lock(&stm_pdrv_mutex);
+
+ list_for_each_entry_safe(pe, iter, &stm_pdrv_head, entry) {
+ if (pe->pdrv == pdrv) {
+ list_del(&pe->entry);
+
+ if (pe->node_type) {
+ kfree(pe->node_type->ct_attrs);
+ kfree(pe->node_type);
+ }
+ kfree(pe);
+ break;
+ }
+ }
+
+ mutex_unlock(&stm_pdrv_mutex);
+}
+EXPORT_SYMBOL_GPL(stm_unregister_protocol);
+
+static bool stm_get_protocol(const struct stm_protocol_driver *pdrv)
+{
+ return try_module_get(pdrv->owner);
+}
+
+void stm_put_protocol(const struct stm_protocol_driver *pdrv)
+{
+ module_put(pdrv->owner);
+}
+
+int stm_lookup_protocol(const char *name,
+ const struct stm_protocol_driver **pdrv,
+ const struct config_item_type **node_type)
+{
+ const struct stm_pdrv_entry *pe;
+
+ mutex_lock(&stm_pdrv_mutex);
+
+ pe = __stm_lookup_protocol(name);
+ if (pe && pe->pdrv && stm_get_protocol(pe->pdrv)) {
+ *pdrv = pe->pdrv;
+ *node_type = pe->node_type;
+ }
+
+ mutex_unlock(&stm_pdrv_mutex);
+
+ return pe ? 0 : -ENOENT;
+}
+
static int stm_char_open(struct inode *inode, struct file *file)
{
struct stm_file *stmf;
@@ -405,42 +543,81 @@ static int stm_char_release(struct inode *inode, struct file *file)
return 0;
}
-static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width)
+static int
+stm_assign_first_policy(struct stm_device *stm, struct stm_output *output,
+ char **ids, unsigned int width)
{
- struct stm_device *stm = stmf->stm;
- int ret;
+ struct stp_policy_node *pn;
+ int err, n;
- stmf->policy_node = stp_policy_node_lookup(stm, id);
+ /*
+ * On success, stp_policy_node_lookup() will return holding the
+ * configfs subsystem mutex, which is then released in
+ * stp_policy_node_put(). This allows the pdrv->output_open() in
+ * stm_output_assign() to serialize against the attribute accessors.
+ */
+ for (n = 0, pn = NULL; ids[n] && !pn; n++)
+ pn = stp_policy_node_lookup(stm, ids[n]);
- ret = stm_output_assign(stm, width, stmf->policy_node, &stmf->output);
+ if (!pn)
+ return -EINVAL;
- if (stmf->policy_node)
- stp_policy_node_put(stmf->policy_node);
+ err = stm_output_assign(stm, width, pn, output);
- return ret;
+ stp_policy_node_put(pn);
+
+ return err;
}
-static ssize_t notrace stm_write(struct stm_data *data, unsigned int master,
- unsigned int channel, const char *buf, size_t count)
+/**
+ * stm_data_write() - send the given payload as data packets
+ * @data: stm driver's data
+ * @m: STP master
+ * @c: STP channel
+ * @ts_first: timestamp the first packet
+ * @buf: data payload buffer
+ * @count: data payload size
+ */
+ssize_t notrace stm_data_write(struct stm_data *data, unsigned int m,
+ unsigned int c, bool ts_first, const void *buf,
+ size_t count)
{
- unsigned int flags = STP_PACKET_TIMESTAMPED;
- const unsigned char *p = buf, nil = 0;
- size_t pos;
+ unsigned int flags = ts_first ? STP_PACKET_TIMESTAMPED : 0;
ssize_t sz;
+ size_t pos;
- for (pos = 0, p = buf; count > pos; pos += sz, p += sz) {
+ for (pos = 0, sz = 0; pos < count; pos += sz) {
sz = min_t(unsigned int, count - pos, 8);
- sz = data->packet(data, master, channel, STP_PACKET_DATA, flags,
- sz, p);
- flags = 0;
-
- if (sz < 0)
+ sz = data->packet(data, m, c, STP_PACKET_DATA, flags, sz,
+ &((u8 *)buf)[pos]);
+ if (sz <= 0)
break;
+
+ if (ts_first) {
+ flags = 0;
+ ts_first = false;
+ }
}
- data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil);
+ return sz < 0 ? sz : pos;
+}
+EXPORT_SYMBOL_GPL(stm_data_write);
+
+static ssize_t notrace
+stm_write(struct stm_device *stm, struct stm_output *output,
+ unsigned int chan, const char *buf, size_t count)
+{
+ int err;
+
+ /* stm->pdrv is serialized against policy_mutex */
+ if (!stm->pdrv)
+ return -ENODEV;
+
+ err = stm->pdrv->write(stm->data, output, chan, buf, count);
+ if (err < 0)
+ return err;
- return pos;
+ return err;
}
static ssize_t stm_char_write(struct file *file, const char __user *buf,
@@ -455,16 +632,21 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf,
count = PAGE_SIZE - 1;
/*
- * if no m/c have been assigned to this writer up to this
- * point, use "default" policy entry
+ * If no m/c have been assigned to this writer up to this
+ * point, try to use the task name and "default" policy entries.
*/
if (!stmf->output.nr_chans) {
- err = stm_file_assign(stmf, "default", 1);
+ char comm[sizeof(current->comm)];
+ char *ids[] = { comm, "default", NULL };
+
+ get_task_comm(comm, current);
+
+ err = stm_assign_first_policy(stmf->stm, &stmf->output, ids, 1);
/*
* EBUSY means that somebody else just assigned this
* output, which is just fine for write()
*/
- if (err && err != -EBUSY)
+ if (err)
return err;
}
@@ -480,8 +662,7 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf,
pm_runtime_get_sync(&stm->dev);
- count = stm_write(stm->data, stmf->output.master, stmf->output.channel,
- kbuf, count);
+ count = stm_write(stm, &stmf->output, 0, kbuf, count);
pm_runtime_mark_last_busy(&stm->dev);
pm_runtime_put_autosuspend(&stm->dev);
@@ -550,6 +731,7 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg)
{
struct stm_device *stm = stmf->stm;
struct stp_policy_id *id;
+ char *ids[] = { NULL, NULL };
int ret = -EINVAL;
u32 size;
@@ -582,7 +764,9 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg)
id->width > PAGE_SIZE / stm->data->sw_mmiosz)
goto err_free;
- ret = stm_file_assign(stmf, id->id, id->width);
+ ids[0] = id->id;
+ ret = stm_assign_first_policy(stmf->stm, &stmf->output, ids,
+ id->width);
if (ret)
goto err_free;
@@ -818,8 +1002,8 @@ EXPORT_SYMBOL_GPL(stm_unregister_device);
static int stm_source_link_add(struct stm_source_device *src,
struct stm_device *stm)
{
- char *id;
- int err;
+ char *ids[] = { NULL, "default", NULL };
+ int err = -ENOMEM;
mutex_lock(&stm->link_mutex);
spin_lock(&stm->link_lock);
@@ -833,19 +1017,13 @@ static int stm_source_link_add(struct stm_source_device *src,
spin_unlock(&stm->link_lock);
mutex_unlock(&stm->link_mutex);
- id = kstrdup(src->data->name, GFP_KERNEL);
- if (id) {
- src->policy_node =
- stp_policy_node_lookup(stm, id);
-
- kfree(id);
- }
-
- err = stm_output_assign(stm, src->data->nr_chans,
- src->policy_node, &src->output);
+ ids[0] = kstrdup(src->data->name, GFP_KERNEL);
+ if (!ids[0])
+ goto fail_detach;
- if (src->policy_node)
- stp_policy_node_put(src->policy_node);
+ err = stm_assign_first_policy(stm, &src->output, ids,
+ src->data->nr_chans);
+ kfree(ids[0]);
if (err)
goto fail_detach;
@@ -1134,9 +1312,7 @@ int notrace stm_source_write(struct stm_source_data *data,
stm = srcu_dereference(src->link, &stm_source_srcu);
if (stm)
- count = stm_write(stm->data, src->output.master,
- src->output.channel + chan,
- buf, count);
+ count = stm_write(stm, &src->output, chan, buf, count);
else
count = -ENODEV;
@@ -1163,7 +1339,15 @@ static int __init stm_core_init(void)
goto err_src;
init_srcu_struct(&stm_source_srcu);
+ INIT_LIST_HEAD(&stm_pdrv_head);
+ mutex_init(&stm_pdrv_mutex);
+ /*
+ * So as to not confuse existing users with a requirement
+ * to load yet another module, do it here.
+ */
+ if (IS_ENABLED(CONFIG_STM_PROTO_BASIC))
+ (void)request_module_nowait("stm_p_basic");
stm_core_up++;
return 0;