@@ -154,6 +154,7 @@ static void persistent_event_close(int cpu, struct pevent *pevent)
}
static int pevent_sysfs_register(struct pevent *event);
+static void pevent_sysfs_unregister(struct pevent *event);
static int __maybe_unused
persistent_open(char *name, struct perf_event_attr *attr, int nr_pages)
@@ -204,6 +205,7 @@ fail:
__func__, ret);
out:
if (atomic_dec_and_test(&pevent->refcount)) {
+ pevent_sysfs_unregister(pevent);
if (pevent->id)
put_event_id(pevent->id);
kfree(pevent->name);
@@ -273,13 +275,12 @@ static struct attribute_group persistent_format_group = {
.attrs = persistent_format_attrs,
};
-#define MAX_EVENTS 16
-
-static struct attribute *pevents_attr[MAX_EVENTS + 1] = { };
+static struct mutex sysfs_lock;
+static int sysfs_nr_entries;
static struct attribute_group pevents_group = {
.name = "events",
- .attrs = pevents_attr,
+ .attrs = NULL, /* dynamically allocated */
};
static const struct attribute_group *persistent_attr_groups[] = {
@@ -288,6 +289,7 @@ static const struct attribute_group *persistent_attr_groups[] = {
NULL,
};
#define EVENTS_GROUP_PTR (&persistent_attr_groups[1])
+#define EVENTS_ATTRS_PTR (&pevents_group.attrs)
static ssize_t pevent_sysfs_show(struct device *dev,
struct device_attribute *__attr, char *page)
@@ -304,7 +306,9 @@ static int pevent_sysfs_register(struct pevent *pevent)
struct attribute *attr = &sysfs->attr.attr;
struct device *dev = persistent_pmu.dev;
const struct attribute_group **group = EVENTS_GROUP_PTR;
- int idx;
+ struct attribute ***attrs_ptr = EVENTS_ATTRS_PTR;
+ struct attribute **attrs;
+ int ret = 0;
sysfs->id = pevent->id;
sysfs->attr = (struct device_attribute)
@@ -312,21 +316,99 @@ static int pevent_sysfs_register(struct pevent *pevent)
attr->name = pevent->name;
sysfs_attr_init(attr);
- /* add sysfs attr to events: */
- for (idx = 0; idx < MAX_EVENTS; idx++) {
- if (!cmpxchg(pevents_attr + idx, NULL, attr))
- break;
+ mutex_lock(&sysfs_lock);
+
+ /*
+ * Keep old list if no new one is available. Need this for
+ * device_remove_attrs() if unregistering pmu.
+ */
+ attrs = __krealloc(*attrs_ptr, (sysfs_nr_entries + 2) * sizeof(*attrs),
+ GFP_KERNEL);
+
+ if (!attrs) {
+ ret = -ENOMEM;
+ goto unlock;
}
- if (idx >= MAX_EVENTS)
- return -ENOSPC;
- if (!idx)
+ attrs[sysfs_nr_entries++] = attr;
+ attrs[sysfs_nr_entries] = NULL;
+
+ if (!*group)
*group = &pevents_group;
+
+ if (!dev)
+ goto out; /* sysfs not yet initialized */
+
+ if (sysfs_nr_entries == 1)
+ ret = sysfs_create_group(&dev->kobj, *group);
+ else
+ ret = sysfs_add_file_to_group(&dev->kobj, attr, (*group)->name);
+
+ if (ret) {
+ /* roll back */
+ sysfs_nr_entries--;
+ if (!sysfs_nr_entries)
+ *group = NULL;
+ if (*attrs_ptr != attrs)
+ kfree(attrs);
+ else
+ attrs[sysfs_nr_entries] = NULL;
+ goto unlock;
+ }
+out:
+ if (*attrs_ptr != attrs) {
+ kfree(*attrs_ptr);
+ *attrs_ptr = attrs;
+ }
+unlock:
+ mutex_unlock(&sysfs_lock);
+
+ return ret;
+}
+
+static void pevent_sysfs_unregister(struct pevent *pevent)
+{
+ struct attribute *attr = &pevent->sysfs.attr.attr;
+ struct device *dev = persistent_pmu.dev;
+ const struct attribute_group **group = EVENTS_GROUP_PTR;
+ struct attribute ***attrs_ptr = EVENTS_ATTRS_PTR;
+ struct attribute **attrs, **dest;
+
+ mutex_lock(&sysfs_lock);
+
+ for (dest = *attrs_ptr; *dest; dest++) {
+ if (*dest == attr)
+ break;
+ }
+
+ if (!*dest)
+ goto unlock;
+
+ sysfs_nr_entries--;
+
+ *dest = (*attrs_ptr)[sysfs_nr_entries];
+ (*attrs_ptr)[sysfs_nr_entries] = NULL;
+
if (!dev)
- return 0; /* sysfs not yet initialized */
- if (idx)
- return sysfs_add_file_to_group(&dev->kobj, attr, (*group)->name);
- return sysfs_create_group(&persistent_pmu.dev->kobj, *group);
+ goto out; /* sysfs not yet initialized */
+
+ if (!sysfs_nr_entries)
+ sysfs_remove_group(&dev->kobj, *group);
+ else
+ sysfs_remove_file_from_group(&dev->kobj, attr, (*group)->name);
+out:
+ if (!sysfs_nr_entries)
+ *group = NULL;
+
+ attrs = __krealloc(*attrs_ptr, (sysfs_nr_entries + 1) * sizeof(*attrs),
+ GFP_KERNEL);
+
+ if (!attrs && *attrs_ptr != attrs) {
+ kfree(*attrs_ptr);
+ *attrs_ptr = attrs;
+ }
+unlock:
+ mutex_unlock(&sysfs_lock);
}
static int persistent_pmu_init(struct perf_event *event)
@@ -349,6 +431,7 @@ void __init perf_register_persistent(void)
idr_init(&event_idr);
mutex_init(&event_lock);
+ mutex_init(&sysfs_lock);
perf_pmu_register(&persistent_pmu, "persistent", PERF_TYPE_PERSISTENT);
for_each_possible_cpu(cpu) {