diff options
Diffstat (limited to 'drivers/hwtracing/stm/policy.c')
-rw-r--r-- | drivers/hwtracing/stm/policy.c | 145 |
1 files changed, 135 insertions, 10 deletions
diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index a505f055f464..8bc90432ae69 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -33,8 +33,18 @@ struct stp_policy_node { unsigned int last_master; unsigned int first_channel; unsigned int last_channel; + /* this is the one that's exposed to the attributes */ + unsigned char priv[0]; }; +void *stp_policy_node_priv(struct stp_policy_node *pn) +{ + if (!pn) + return NULL; + + return pn->priv; +} + static struct configfs_subsystem stp_policy_subsys; void stp_policy_node_get_ranges(struct stp_policy_node *policy_node, @@ -68,6 +78,14 @@ to_stp_policy_node(struct config_item *item) NULL; } +void *to_pdrv_policy_node(struct config_item *item) +{ + struct stp_policy_node *node = to_stp_policy_node(item); + + return stp_policy_node_priv(node); +} +EXPORT_SYMBOL_GPL(to_pdrv_policy_node); + static ssize_t stp_policy_node_masters_show(struct config_item *item, char *page) { @@ -163,7 +181,9 @@ unlock: static void stp_policy_node_release(struct config_item *item) { - kfree(to_stp_policy_node(item)); + struct stp_policy_node *node = to_stp_policy_node(item); + + kfree(node); } static struct configfs_item_operations stp_policy_node_item_ops = { @@ -182,10 +202,61 @@ static struct configfs_attribute *stp_policy_node_attrs[] = { static const struct config_item_type stp_policy_type; static const struct config_item_type stp_policy_node_type; +/* lifted from arch/x86/events/core.c */ +static struct configfs_attribute **merge_attr(struct configfs_attribute **a, struct configfs_attribute **b) +{ + struct configfs_attribute **new; + int j, i; + + for (j = 0; a[j]; j++) + ; + for (i = 0; b[i]; i++) + j++; + j++; + + new = kmalloc_array(j, sizeof(struct configfs_attribute *), + GFP_KERNEL); + if (!new) + return NULL; + + j = 0; + for (i = 0; a[i]; i++) + new[j++] = a[i]; + for (i = 0; b[i]; i++) + new[j++] = b[i]; + new[j] = NULL; + + return new; +} + +const struct config_item_type * +get_policy_node_type(struct configfs_attribute **attrs) +{ + struct config_item_type *type; + struct configfs_attribute **merged; + + type = kmemdup(&stp_policy_node_type, sizeof(stp_policy_node_type), + GFP_KERNEL); + if (!type) + return NULL; + + merged = merge_attr(stp_policy_node_attrs, attrs); + if (!merged) { + kfree(type); + return NULL; + } + + type->ct_attrs = merged; + + return type; +} + static struct config_group * stp_policy_node_make(struct config_group *group, const char *name) { + const struct config_item_type *type = &stp_policy_node_type; struct stp_policy_node *policy_node, *parent_node; + const struct stm_protocol_driver *pdrv; struct stp_policy *policy; if (group->cg_item.ci_type == &stp_policy_type) { @@ -199,12 +270,20 @@ stp_policy_node_make(struct config_group *group, const char *name) if (!policy->stm) return ERR_PTR(-ENODEV); - policy_node = kzalloc(sizeof(struct stp_policy_node), GFP_KERNEL); + pdrv = policy->stm->pdrv; + policy_node = + kzalloc(offsetof(struct stp_policy_node, priv[pdrv->priv_sz]), + GFP_KERNEL); if (!policy_node) return ERR_PTR(-ENOMEM); - config_group_init_type_name(&policy_node->group, name, - &stp_policy_node_type); + if (pdrv->policy_node_init) + pdrv->policy_node_init((void *)policy_node->priv); + + if (policy->stm->pdrv_node_type) + type = policy->stm->pdrv_node_type; + + config_group_init_type_name(&policy_node->group, name, type); policy_node->policy = policy; @@ -254,8 +333,25 @@ static ssize_t stp_policy_device_show(struct config_item *item, CONFIGFS_ATTR_RO(stp_policy_, device); +static ssize_t stp_policy_protocol_show(struct config_item *item, + char *page) +{ + struct stp_policy *policy = to_stp_policy(item); + ssize_t count; + + count = sprintf(page, "%s\n", + (policy && policy->stm) ? + policy->stm->pdrv->name : + "<none>"); + + return count; +} + +CONFIGFS_ATTR_RO(stp_policy_, protocol); + static struct configfs_attribute *stp_policy_attrs[] = { &stp_policy_attr_device, + &stp_policy_attr_protocol, NULL, }; @@ -276,6 +372,7 @@ void stp_policy_unbind(struct stp_policy *policy) stm->policy = NULL; policy->stm = NULL; + stm_put_protocol(stm->pdrv); stm_put_device(stm); } @@ -313,9 +410,12 @@ static const struct config_item_type stp_policy_type = { static struct config_group * stp_policy_make(struct config_group *group, const char *name) { + const struct config_item_type *pdrv_node_type; + const struct stm_protocol_driver *pdrv; + char *devname, *proto, *p; struct config_group *ret; struct stm_device *stm; - char *devname, *p; + int err; devname = kasprintf(GFP_KERNEL, "%s", name); if (!devname) @@ -326,6 +426,7 @@ stp_policy_make(struct config_group *group, const char *name) * <device_name> is the name of an existing stm device; may * contain dots; * <policy_name> is an arbitrary string; may not contain dots + * <device_name>:<protocol_name>.<policy_name> */ p = strrchr(devname, '.'); if (!p) { @@ -335,11 +436,29 @@ stp_policy_make(struct config_group *group, const char *name) *p = '\0'; + /* + * look for ":<protocol_name>": + * + no protocol suffix: fall back to whatever is available; + * + unknown protocol: fail the whole thing + */ + proto = strrchr(devname, ':'); + if (proto) + *proto++ = '\0'; + stm = stm_find_device(devname); + if (!stm) { + kfree(devname); + return ERR_PTR(-ENODEV); + } + + err = stm_lookup_protocol(proto, &pdrv, &pdrv_node_type); kfree(devname); - if (!stm) + /* We don't have any protocol drivers yet */ + if (err != -ENOENT) { + stm_put_device(stm); return ERR_PTR(-ENODEV); + } mutex_lock(&stm->policy_mutex); if (stm->policy) { @@ -349,21 +468,27 @@ stp_policy_make(struct config_group *group, const char *name) stm->policy = kzalloc(sizeof(*stm->policy), GFP_KERNEL); if (!stm->policy) { - ret = ERR_PTR(-ENOMEM); - goto unlock_policy; + mutex_unlock(&stm->policy_mutex); + stm_put_protocol(pdrv); + stm_put_device(stm); + return ERR_PTR(-ENOMEM); } config_group_init_type_name(&stm->policy->group, name, &stp_policy_type); - stm->policy->stm = stm; + stm->pdrv = pdrv; + stm->pdrv_node_type = pdrv_node_type; + stm->policy->stm = stm; ret = &stm->policy->group; unlock_policy: mutex_unlock(&stm->policy_mutex); - if (IS_ERR(ret)) + if (IS_ERR(ret)) { + stm_put_protocol(stm->pdrv); stm_put_device(stm); + } return ret; } |