summaryrefslogtreecommitdiffstats
path: root/drivers/misc/hmc6352.c
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /drivers/misc/hmc6352.c
downloadlinux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz
linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since it can be declared outside of the ftrace.c code, it calls ftrace_ops_init() on the ops in every global function. The issue is that if two tasks call it on the same ops at the same time it is possible to have the initialization of one corrupt the initialization of the other call. Create a ops_mutex to use to synchronize every initialization of the ftrace_ops. The mutex is taken within checking the ftrace_ops flag that states it was initializied but the flag is checked again after the mutex has been taken. Checking first outside the mutex allows it to shortcut having to take the mutex. But then the check needs to be done again after the mute is taken in case of races. Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock") Reported-by: sashiko-bot@kernel.org Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/ Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/misc/hmc6352.c')
-rw-r--r--drivers/misc/hmc6352.c143
1 files changed, 143 insertions, 0 deletions
diff --git a/drivers/misc/hmc6352.c b/drivers/misc/hmc6352.c
new file mode 100644
index 000000000..275dc054c
--- /dev/null
+++ b/drivers/misc/hmc6352.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hmc6352.c - Honeywell Compass Driver
+ *
+ * Copyright (C) 2009 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/sysfs.h>
+#include <linux/nospec.h>
+
+static DEFINE_MUTEX(compass_mutex);
+
+static int compass_command(struct i2c_client *c, u8 cmd)
+{
+ int ret = i2c_master_send(c, &cmd, 1);
+ if (ret < 0)
+ dev_warn(&c->dev, "command '%c' failed.\n", cmd);
+ return ret;
+}
+
+static int compass_store(struct device *dev, const char *buf, size_t count,
+ const char *map)
+{
+ struct i2c_client *c = to_i2c_client(dev);
+ int ret;
+ unsigned long val;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+ if (val >= strlen(map))
+ return -EINVAL;
+ val = array_index_nospec(val, strlen(map));
+ mutex_lock(&compass_mutex);
+ ret = compass_command(c, map[val]);
+ mutex_unlock(&compass_mutex);
+ if (ret < 0)
+ return ret;
+ return count;
+}
+
+static ssize_t compass_calibration_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return compass_store(dev, buf, count, "EC");
+}
+
+static ssize_t compass_power_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ return compass_store(dev, buf, count, "SW");
+}
+
+static ssize_t compass_heading_data_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char i2c_data[2];
+ int ret;
+
+ mutex_lock(&compass_mutex);
+ ret = compass_command(client, 'A');
+ if (ret != 1) {
+ mutex_unlock(&compass_mutex);
+ return ret;
+ }
+ msleep(10); /* sending 'A' cmd we need to wait for 7-10 millisecs */
+ ret = i2c_master_recv(client, i2c_data, 2);
+ mutex_unlock(&compass_mutex);
+ if (ret < 0) {
+ dev_warn(dev, "i2c read data cmd failed\n");
+ return ret;
+ }
+ ret = (i2c_data[0] << 8) | i2c_data[1];
+ return sprintf(buf, "%d.%d\n", ret/10, ret%10);
+}
+
+
+static DEVICE_ATTR(heading0_input, S_IRUGO, compass_heading_data_show, NULL);
+static DEVICE_ATTR(calibration, S_IWUSR, NULL, compass_calibration_store);
+static DEVICE_ATTR(power_state, S_IWUSR, NULL, compass_power_mode_store);
+
+static struct attribute *mid_att_compass[] = {
+ &dev_attr_heading0_input.attr,
+ &dev_attr_calibration.attr,
+ &dev_attr_power_state.attr,
+ NULL
+};
+
+static const struct attribute_group m_compass_gr = {
+ .name = "hmc6352",
+ .attrs = mid_att_compass
+};
+
+static int hmc6352_probe(struct i2c_client *client)
+{
+ int res;
+
+ res = sysfs_create_group(&client->dev.kobj, &m_compass_gr);
+ if (res) {
+ dev_err(&client->dev, "device_create_file failed\n");
+ return res;
+ }
+ dev_info(&client->dev, "%s HMC6352 compass chip found\n",
+ client->name);
+ return 0;
+}
+
+static void hmc6352_remove(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &m_compass_gr);
+}
+
+static const struct i2c_device_id hmc6352_id[] = {
+ { .name = "hmc6352" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, hmc6352_id);
+
+static struct i2c_driver hmc6352_driver = {
+ .driver = {
+ .name = "hmc6352",
+ },
+ .probe = hmc6352_probe,
+ .remove = hmc6352_remove,
+ .id_table = hmc6352_id,
+};
+
+module_i2c_driver(hmc6352_driver);
+
+MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com");
+MODULE_DESCRIPTION("hmc6352 Compass Driver");
+MODULE_LICENSE("GPL v2");