From b8a5593858bb3d7100b3d94483299bff54e6efbe Mon Sep 17 00:00:00 2001 From: Rom Lemarchand Date: Thu, 7 Apr 2016 07:19:34 -0700 Subject: [PATCH 001/656] android: base-cfg: enable CONFIG_QUOTA Bug: 28032718 Change-Id: I7cb6b641f72085e69b90dca11d2ea68adcd02390 (cherry picked from commit e1b53a388e9cfcf870520a6899a37456cf1ae2c6) --- android/configs/android-base.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index fa53af0c37ad..3f82e9339dad 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -138,6 +138,7 @@ CONFIG_PPP_BSDCOMP=y CONFIG_PPP_DEFLATE=y CONFIG_PPP_MPPE=y CONFIG_PREEMPT=y +CONFIG_QUOTA=y CONFIG_RESOURCE_COUNTERS=y CONFIG_RTC_CLASS=y CONFIG_RT_GROUP_SCHED=y From c2e10e5cb24622a42dba65c52e1a4793c9287966 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Tue, 5 Apr 2016 13:06:27 -0700 Subject: [PATCH 002/656] BACKPORT: selinux: restrict kernel module loading Backport notes: Backport uses kernel_module_from_file not kernel_read_file hook. kernel_read_file replaced kernel_module_from_file in the 4.6 kernel. There are no inode_security_() helper functions (also introduced in 4.6) so the inode lookup is done using the file_inode() helper which is standard for kernel version < 4.6. (Cherry picked from commit 61d612ea731e57dc510472fb746b55cdc017f371) Utilize existing kernel_read_file hook on kernel module load. Add module_load permission to the system class. Enforces restrictions on kernel module origin when calling the finit_module syscall. The hook checks that source type has permission module_load for the target type. Example for finit_module: allow foo bar_file:system module_load; Similarly restrictions are enforced on kernel module loading when calling the init_module syscall. The hook checks that source type has permission module_load with itself as the target object because the kernel module is sourced from the calling process. Example for init_module: allow foo foo:system module_load; Bug: 27824855 Change-Id: I64bf3bd1ab2dc735321160642dc6bbfa996f8068 Signed-off-by: Jeff Vander Stoep Signed-off-by: Paul Moore --- security/selinux/hooks.c | 33 +++++++++++++++++++++++++++++ security/selinux/include/classmap.h | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7c22a15c7e4b..78d06ff4eeb7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3660,6 +3660,38 @@ static int selinux_kernel_module_request(char *kmod_name) SYSTEM__MODULE_REQUEST, &ad); } +static int selinux_kernel_module_from_file(struct file *file) +{ + struct common_audit_data ad; + struct inode_security_struct *isec; + struct file_security_struct *fsec; + struct inode *inode; + u32 sid = current_sid(); + int rc; + + /* init_module */ + if (file == NULL) + return avc_has_perm(sid, sid, SECCLASS_SYSTEM, + SYSTEM__MODULE_LOAD, NULL); + + /* finit_module */ + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = file->f_path; + + inode = file_inode(file); + isec = inode->i_security; + fsec = file->f_security; + + if (sid != fsec->sid) { + rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad); + if (rc) + return rc; + } + + return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM, + SYSTEM__MODULE_LOAD, &ad); +} + static int selinux_task_setpgid(struct task_struct *p, pid_t pgid) { return current_has_perm(p, PROCESS__SETPGID); @@ -5950,6 +5982,7 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as), LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as), LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request), + LSM_HOOK_INIT(kernel_module_from_file, selinux_kernel_module_from_file), LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid), LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid), LSM_HOOK_INIT(task_getsid, selinux_task_getsid), diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 5a4eef59aeff..b393d29ae857 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -32,7 +32,7 @@ struct security_class_mapping secclass_map[] = { "setsockcreate", NULL } }, { "system", { "ipc_info", "syslog_read", "syslog_mod", - "syslog_console", "module_request", NULL } }, + "syslog_console", "module_request", "module_load", NULL } }, { "capability", { "chown", "dac_override", "dac_read_search", "fowner", "fsetid", "kill", "setgid", "setuid", "setpcap", From 56fc8bbed1d9d612b146b81a18582ad09c3b1cab Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 12 Apr 2016 01:19:24 +0530 Subject: [PATCH 003/656] ANDROID: base-cfg: enable CONFIG_IP_NF_NAT IP_NF_TARGET_{MASQUERADE,NETMAP,REDIRECT} configs, already enabled in android-base.cfg for tethering, are of no use if CONFIG_IP_NF_NAT is not enabled. Don't rely on platform config for that and enable CONFIG_IP_NF_NAT in android-base.cfg as well. Change-Id: Ic72bcebbd925b142b09539466bf963188c83108a Signed-off-by: Amit Pundir --- android/configs/android-base.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index 3f82e9339dad..a8b529cae6eb 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -57,6 +57,7 @@ CONFIG_IP_NF_MANGLE=y CONFIG_IP_NF_MATCH_AH=y CONFIG_IP_NF_MATCH_ECN=y CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_NAT=y CONFIG_IP_NF_RAW=y CONFIG_IP_NF_SECURITY=y CONFIG_IP_NF_TARGET_MASQUERADE=y From d18fffdddbf0a395440cd8e8e2578d322c8f0dbd Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 19 Apr 2016 12:37:31 -0700 Subject: [PATCH 004/656] Revert "drivers: switch: remove S_IWUSR from dev_attr" This reverts commit dc66dee02dcd6ea774e3ed4ae32e88b0f3b4bee7. --- drivers/switch/switch_class.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c index e373b625806e..e05fc2591147 100644 --- a/drivers/switch/switch_class.c +++ b/drivers/switch/switch_class.c @@ -54,8 +54,8 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%s\n", sdev->name); } -static DEVICE_ATTR(state, S_IRUGO, state_show, NULL); -static DEVICE_ATTR(name, S_IRUGO, name_show, NULL); +static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL); +static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL); void switch_set_state(struct switch_dev *sdev, int state) { From c631c9800f20373ddafff77ce19844e78d2d2d9d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 19 Apr 2016 12:37:47 -0700 Subject: [PATCH 005/656] Revert "switch: switch class and GPIO drivers." Drivers should use extcon moving forward. Documentation/extcon/porting-android-switch-class describes how to port existing switch class drivers to extcon. This reverts commit e4b8e66e0ae2e78e913d7b86f2507fdb0aa731b4. Change-Id: I5b622c7ab4c0cb9670f8903f259a99888f503c1a --- drivers/Kconfig | 2 - drivers/Makefile | 1 - drivers/switch/Kconfig | 15 --- drivers/switch/Makefile | 4 - drivers/switch/switch_class.c | 174 ---------------------------------- drivers/switch/switch_gpio.c | 172 --------------------------------- include/linux/switch.h | 53 ----------- 7 files changed, 421 deletions(-) delete mode 100644 drivers/switch/Kconfig delete mode 100644 drivers/switch/Makefile delete mode 100644 drivers/switch/switch_class.c delete mode 100644 drivers/switch/switch_gpio.c delete mode 100644 include/linux/switch.h diff --git a/drivers/Kconfig b/drivers/Kconfig index 3c363e559864..d2ac339de85f 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -104,8 +104,6 @@ source "drivers/memstick/Kconfig" source "drivers/leds/Kconfig" -source "drivers/switch/Kconfig" - source "drivers/accessibility/Kconfig" source "drivers/infiniband/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 8acfb530993d..795d0ca714bf 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -122,7 +122,6 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle/ obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-y += leds/ -obj-$(CONFIG_SWITCH) += switch/ obj-$(CONFIG_INFINIBAND) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ diff --git a/drivers/switch/Kconfig b/drivers/switch/Kconfig deleted file mode 100644 index 19404b6f7778..000000000000 --- a/drivers/switch/Kconfig +++ /dev/null @@ -1,15 +0,0 @@ -menuconfig SWITCH - tristate "Switch class support" - help - Say Y here to enable switch class support. This allows - monitoring switches by userspace via sysfs and uevent. - -if SWITCH - -config SWITCH_GPIO - tristate "GPIO Swith support" - depends on GPIOLIB - help - Say Y here to enable GPIO based switch support. - -endif # SWITCH diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile deleted file mode 100644 index f7606ed4a719..000000000000 --- a/drivers/switch/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# Switch Class Driver -obj-$(CONFIG_SWITCH) += switch_class.o -obj-$(CONFIG_SWITCH_GPIO) += switch_gpio.o - diff --git a/drivers/switch/switch_class.c b/drivers/switch/switch_class.c deleted file mode 100644 index e05fc2591147..000000000000 --- a/drivers/switch/switch_class.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * drivers/switch/switch_class.c - * - * Copyright (C) 2008 Google, Inc. - * Author: Mike Lockwood - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * -*/ - -#include -#include -#include -#include -#include -#include -#include - -struct class *switch_class; -static atomic_t device_count; - -static ssize_t state_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct switch_dev *sdev = (struct switch_dev *) - dev_get_drvdata(dev); - - if (sdev->print_state) { - int ret = sdev->print_state(sdev, buf); - if (ret >= 0) - return ret; - } - return sprintf(buf, "%d\n", sdev->state); -} - -static ssize_t name_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct switch_dev *sdev = (struct switch_dev *) - dev_get_drvdata(dev); - - if (sdev->print_name) { - int ret = sdev->print_name(sdev, buf); - if (ret >= 0) - return ret; - } - return sprintf(buf, "%s\n", sdev->name); -} - -static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, state_show, NULL); -static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, name_show, NULL); - -void switch_set_state(struct switch_dev *sdev, int state) -{ - char name_buf[120]; - char state_buf[120]; - char *prop_buf; - char *envp[3]; - int env_offset = 0; - int length; - - if (sdev->state != state) { - sdev->state = state; - - prop_buf = (char *)get_zeroed_page(GFP_KERNEL); - if (prop_buf) { - length = name_show(sdev->dev, NULL, prop_buf); - if (length > 0) { - if (prop_buf[length - 1] == '\n') - prop_buf[length - 1] = 0; - snprintf(name_buf, sizeof(name_buf), - "SWITCH_NAME=%s", prop_buf); - envp[env_offset++] = name_buf; - } - length = state_show(sdev->dev, NULL, prop_buf); - if (length > 0) { - if (prop_buf[length - 1] == '\n') - prop_buf[length - 1] = 0; - snprintf(state_buf, sizeof(state_buf), - "SWITCH_STATE=%s", prop_buf); - envp[env_offset++] = state_buf; - } - envp[env_offset] = NULL; - kobject_uevent_env(&sdev->dev->kobj, KOBJ_CHANGE, envp); - free_page((unsigned long)prop_buf); - } else { - printk(KERN_ERR "out of memory in switch_set_state\n"); - kobject_uevent(&sdev->dev->kobj, KOBJ_CHANGE); - } - } -} -EXPORT_SYMBOL_GPL(switch_set_state); - -static int create_switch_class(void) -{ - if (!switch_class) { - switch_class = class_create(THIS_MODULE, "switch"); - if (IS_ERR(switch_class)) - return PTR_ERR(switch_class); - atomic_set(&device_count, 0); - } - - return 0; -} - -int switch_dev_register(struct switch_dev *sdev) -{ - int ret; - - if (!switch_class) { - ret = create_switch_class(); - if (ret < 0) - return ret; - } - - sdev->index = atomic_inc_return(&device_count); - sdev->dev = device_create(switch_class, NULL, - MKDEV(0, sdev->index), NULL, sdev->name); - if (IS_ERR(sdev->dev)) - return PTR_ERR(sdev->dev); - - ret = device_create_file(sdev->dev, &dev_attr_state); - if (ret < 0) - goto err_create_file_1; - ret = device_create_file(sdev->dev, &dev_attr_name); - if (ret < 0) - goto err_create_file_2; - - dev_set_drvdata(sdev->dev, sdev); - sdev->state = 0; - return 0; - -err_create_file_2: - device_remove_file(sdev->dev, &dev_attr_state); -err_create_file_1: - device_destroy(switch_class, MKDEV(0, sdev->index)); - printk(KERN_ERR "switch: Failed to register driver %s\n", sdev->name); - - return ret; -} -EXPORT_SYMBOL_GPL(switch_dev_register); - -void switch_dev_unregister(struct switch_dev *sdev) -{ - device_remove_file(sdev->dev, &dev_attr_name); - device_remove_file(sdev->dev, &dev_attr_state); - device_destroy(switch_class, MKDEV(0, sdev->index)); - dev_set_drvdata(sdev->dev, NULL); -} -EXPORT_SYMBOL_GPL(switch_dev_unregister); - -static int __init switch_class_init(void) -{ - return create_switch_class(); -} - -static void __exit switch_class_exit(void) -{ - class_destroy(switch_class); -} - -module_init(switch_class_init); -module_exit(switch_class_exit); - -MODULE_AUTHOR("Mike Lockwood "); -MODULE_DESCRIPTION("Switch class driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/switch/switch_gpio.c b/drivers/switch/switch_gpio.c deleted file mode 100644 index 621d62d20c99..000000000000 --- a/drivers/switch/switch_gpio.c +++ /dev/null @@ -1,172 +0,0 @@ -/* - * drivers/switch/switch_gpio.c - * - * Copyright (C) 2008 Google, Inc. - * Author: Mike Lockwood - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct gpio_switch_data { - struct switch_dev sdev; - unsigned gpio; - const char *name_on; - const char *name_off; - const char *state_on; - const char *state_off; - int irq; - struct work_struct work; -}; - -static void gpio_switch_work(struct work_struct *work) -{ - int state; - struct gpio_switch_data *data = - container_of(work, struct gpio_switch_data, work); - - state = gpio_get_value(data->gpio); - switch_set_state(&data->sdev, state); -} - -static irqreturn_t gpio_irq_handler(int irq, void *dev_id) -{ - struct gpio_switch_data *switch_data = - (struct gpio_switch_data *)dev_id; - - schedule_work(&switch_data->work); - return IRQ_HANDLED; -} - -static ssize_t switch_gpio_print_state(struct switch_dev *sdev, char *buf) -{ - struct gpio_switch_data *switch_data = - container_of(sdev, struct gpio_switch_data, sdev); - const char *state; - if (switch_get_state(sdev)) - state = switch_data->state_on; - else - state = switch_data->state_off; - - if (state) - return sprintf(buf, "%s\n", state); - return -1; -} - -static int gpio_switch_probe(struct platform_device *pdev) -{ - struct gpio_switch_platform_data *pdata = pdev->dev.platform_data; - struct gpio_switch_data *switch_data; - int ret = 0; - - if (!pdata) - return -EBUSY; - - switch_data = kzalloc(sizeof(struct gpio_switch_data), GFP_KERNEL); - if (!switch_data) - return -ENOMEM; - - switch_data->sdev.name = pdata->name; - switch_data->gpio = pdata->gpio; - switch_data->name_on = pdata->name_on; - switch_data->name_off = pdata->name_off; - switch_data->state_on = pdata->state_on; - switch_data->state_off = pdata->state_off; - switch_data->sdev.print_state = switch_gpio_print_state; - - ret = switch_dev_register(&switch_data->sdev); - if (ret < 0) - goto err_switch_dev_register; - - ret = gpio_request(switch_data->gpio, pdev->name); - if (ret < 0) - goto err_request_gpio; - - ret = gpio_direction_input(switch_data->gpio); - if (ret < 0) - goto err_set_gpio_input; - - INIT_WORK(&switch_data->work, gpio_switch_work); - - switch_data->irq = gpio_to_irq(switch_data->gpio); - if (switch_data->irq < 0) { - ret = switch_data->irq; - goto err_detect_irq_num_failed; - } - - ret = request_irq(switch_data->irq, gpio_irq_handler, - IRQF_TRIGGER_LOW, pdev->name, switch_data); - if (ret < 0) - goto err_request_irq; - - /* Perform initial detection */ - gpio_switch_work(&switch_data->work); - - return 0; - -err_request_irq: -err_detect_irq_num_failed: -err_set_gpio_input: - gpio_free(switch_data->gpio); -err_request_gpio: - switch_dev_unregister(&switch_data->sdev); -err_switch_dev_register: - kfree(switch_data); - - return ret; -} - -static int gpio_switch_remove(struct platform_device *pdev) -{ - struct gpio_switch_data *switch_data = platform_get_drvdata(pdev); - - cancel_work_sync(&switch_data->work); - gpio_free(switch_data->gpio); - switch_dev_unregister(&switch_data->sdev); - kfree(switch_data); - - return 0; -} - -static struct platform_driver gpio_switch_driver = { - .probe = gpio_switch_probe, - .remove = gpio_switch_remove, - .driver = { - .name = "switch-gpio", - .owner = THIS_MODULE, - }, -}; - -static int __init gpio_switch_init(void) -{ - return platform_driver_register(&gpio_switch_driver); -} - -static void __exit gpio_switch_exit(void) -{ - platform_driver_unregister(&gpio_switch_driver); -} - -module_init(gpio_switch_init); -module_exit(gpio_switch_exit); - -MODULE_AUTHOR("Mike Lockwood "); -MODULE_DESCRIPTION("GPIO Switch driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/switch.h b/include/linux/switch.h deleted file mode 100644 index 3e4c748e343a..000000000000 --- a/include/linux/switch.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Switch class driver - * - * Copyright (C) 2008 Google, Inc. - * Author: Mike Lockwood - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * -*/ - -#ifndef __LINUX_SWITCH_H__ -#define __LINUX_SWITCH_H__ - -struct switch_dev { - const char *name; - struct device *dev; - int index; - int state; - - ssize_t (*print_name)(struct switch_dev *sdev, char *buf); - ssize_t (*print_state)(struct switch_dev *sdev, char *buf); -}; - -struct gpio_switch_platform_data { - const char *name; - unsigned gpio; - - /* if NULL, switch_dev.name will be printed */ - const char *name_on; - const char *name_off; - /* if NULL, "0" or "1" will be printed */ - const char *state_on; - const char *state_off; -}; - -extern int switch_dev_register(struct switch_dev *sdev); -extern void switch_dev_unregister(struct switch_dev *sdev); - -static inline int switch_get_state(struct switch_dev *sdev) -{ - return sdev->state; -} - -extern void switch_set_state(struct switch_dev *sdev, int state); - -#endif /* __LINUX_SWITCH_H__ */ From e193d9de7c58bd7a1fdc102fe06b0e5c802ac57b Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 19 Apr 2016 12:44:42 -0700 Subject: [PATCH 006/656] android: base-cfg: remove CONFIG_SWITCH Change-Id: I3fd1aa7a54fe3a8d3ad5537cbc61386e52f41ea0 Signed-off-by: Dmitry Shmidt --- android/configs/android-base.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/android/configs/android-base.cfg b/android/configs/android-base.cfg index a8b529cae6eb..304f1d4fd7c4 100644 --- a/android/configs/android-base.cfg +++ b/android/configs/android-base.cfg @@ -148,7 +148,6 @@ CONFIG_SECURITY_NETWORK=y CONFIG_SECURITY_SELINUX=y CONFIG_SETEND_EMULATION=y CONFIG_STAGING=y -CONFIG_SWITCH=y CONFIG_SWP_EMULATION=y CONFIG_SYNC=y CONFIG_TUN=y From 7f3c0b7b8a48d16707bc9d332b2dda175312ce50 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 21 Apr 2016 15:43:29 -0700 Subject: [PATCH 007/656] Revert "tcp: Fix IPV6 module build errors" This reverts commit 3823c8136f2170b3ac5e6a5f8b857746a786e845. --- net/ipv4/tcp.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 7c0465202cc5..e23f2c529eca 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3264,7 +3264,7 @@ static int tcp_is_local(struct net *net, __be32 addr) { return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK); } -#if defined(CONFIG_IPV6) +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) static int tcp_is_local6(struct net *net, struct in6_addr *addr) { struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0); return rt6 && rt6->dst.dev && (rt6->dst.dev->flags & IFF_LOOPBACK); @@ -3332,7 +3332,7 @@ restart: continue; } -#if defined(CONFIG_IPV6) +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) if (family == AF_INET6) { struct in6_addr *s6; if (!inet->pinet6) @@ -3369,4 +3369,3 @@ restart: return 0; } -EXPORT_SYMBOL_GPL(tcp_nuke_addr); From 99bbdddee077677da9acc8dd340ab206781f8614 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 21 Apr 2016 15:43:58 -0700 Subject: [PATCH 008/656] Revert "Don't kill IPv4 sockets when killing IPv6 sockets was requested." This reverts commit 8bf4413b4f54e24120b90ecbfee426beeddc3ff0. --- net/ipv4/tcp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index e23f2c529eca..edd1312ba73d 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3335,8 +3335,6 @@ restart: #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) if (family == AF_INET6) { struct in6_addr *s6; - if (!inet->pinet6) - continue; s6 = &sk->sk_v6_rcv_saddr; if (ipv6_addr_type(s6) == IPV6_ADDR_MAPPED) From cf84d75f72ae87d15b959967c9a0e1b74f686a6f Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 21 Apr 2016 15:44:11 -0700 Subject: [PATCH 009/656] Revert "net: fix crash in tcp_nuke_addr()" This reverts commit 08f7c4280cd5efe9e274240c42177f459431bac2. --- net/ipv4/tcp.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index edd1312ba73d..6c8a77eb6e57 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3305,19 +3305,8 @@ restart: sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { struct inet_sock *inet = inet_sk(sk); - if (sk->sk_state == TCP_TIME_WAIT) { - /* - * Sockets that are in TIME_WAIT state are - * instances of lightweight inet_timewait_sock, - * we should simply skip them (or we'll try to - * access non-existing fields and crash). - */ - continue; - } - if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) continue; - if (sock_flag(sk, SOCK_DEAD)) continue; From d8c5b6f8507d171050d318f3126cf2c43cee1dd5 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 21 Apr 2016 15:44:25 -0700 Subject: [PATCH 010/656] Revert "net: fix iterating over hashtable in tcp_nuke_addr()" This reverts commit 4747299b2c8e8778927b3df0501023d76fe4f2d5. --- net/ipv4/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 6c8a77eb6e57..0d01284271b4 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3295,7 +3295,7 @@ int tcp_nuke_addr(struct net *net, struct sockaddr *addr) return -EAFNOSUPPORT; } - for (bucket = 0; bucket <= tcp_hashinfo.ehash_mask; bucket++) { + for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) { struct hlist_nulls_node *node; struct sock *sk; spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); From 3094efd84ca7c548cdba67fa6d995c3bc52238e7 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Thu, 21 Apr 2016 15:47:01 -0700 Subject: [PATCH 011/656] Revert "net: socket ioctl to reset connections matching local address" Use SOCK_DESTROY from now instead of SIOCKILLADDR This reverts commit 38f0ec724f5306c81130ca9343c856aa37a76d54. Change-Id: I2dcd833b66c88a48de8978dce9d72ab78f9af549 --- include/net/tcp.h | 2 - include/uapi/linux/sockios.h | 1 - net/ipv4/af_inet.c | 1 - net/ipv4/devinet.c | 8 +-- net/ipv4/tcp.c | 105 ----------------------------------- net/ipv6/af_inet6.c | 17 ------ 6 files changed, 1 insertion(+), 133 deletions(-) diff --git a/include/net/tcp.h b/include/net/tcp.h index 5f4d135a00cc..b36cebad6b2f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1681,8 +1681,6 @@ static inline bool tcp_stream_memory_free(const struct sock *sk) return notsent_bytes < tcp_notsent_lowat(tp); } -extern int tcp_nuke_addr(struct net *net, struct sockaddr *addr); - #ifdef CONFIG_PROC_FS int tcp4_proc_init(void); void tcp4_proc_exit(void); diff --git a/include/uapi/linux/sockios.h b/include/uapi/linux/sockios.h index 623e9aab645e..e888b1aed69f 100644 --- a/include/uapi/linux/sockios.h +++ b/include/uapi/linux/sockios.h @@ -65,7 +65,6 @@ #define SIOCDIFADDR 0x8936 /* delete PA address */ #define SIOCSIFHWBROADCAST 0x8937 /* set hardware broadcast addr */ #define SIOCGIFCOUNT 0x8938 /* get number of devices */ -#define SIOCKILLADDR 0x8939 /* kill sockets with this local addr */ #define SIOCGIFBR 0x8940 /* Bridging support */ #define SIOCSIFBR 0x8941 /* Set bridging options */ diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 671eb0092915..eb12bd0ff9d3 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -886,7 +886,6 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) case SIOCSIFPFLAGS: case SIOCGIFPFLAGS: case SIOCSIFFLAGS: - case SIOCKILLADDR: err = devinet_ioctl(net, cmd, (void __user *)arg); break; default: diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 926169c94a0b..0212591b0077 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -59,7 +59,6 @@ #include #include -#include #include #include #include @@ -969,7 +968,6 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) case SIOCSIFBRDADDR: /* Set the broadcast address */ case SIOCSIFDSTADDR: /* Set the destination address */ case SIOCSIFNETMASK: /* Set the netmask for the interface */ - case SIOCKILLADDR: /* Nuke all sockets on this address */ ret = -EPERM; if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) goto out; @@ -1021,8 +1019,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) } ret = -EADDRNOTAVAIL; - if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS - && cmd != SIOCKILLADDR) + if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) goto done; switch (cmd) { @@ -1149,9 +1146,6 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) inet_insert_ifa(ifa); } break; - case SIOCKILLADDR: /* Nuke all connections on this address */ - ret = tcp_nuke_addr(net, (struct sockaddr *) sin); - break; } done: rtnl_unlock(); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0d01284271b4..cf2e13db0619 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -276,9 +276,6 @@ #include #include #include -#include -#include -#include #include #include @@ -3254,105 +3251,3 @@ void __init tcp_init(void) BUG_ON(tcp_register_congestion_control(&tcp_reno) != 0); tcp_tasklet_init(); } - -static int tcp_is_local(struct net *net, __be32 addr) { - struct rtable *rt; - struct flowi4 fl4 = { .daddr = addr }; - rt = ip_route_output_key(net, &fl4); - if (IS_ERR_OR_NULL(rt)) - return 0; - return rt->dst.dev && (rt->dst.dev->flags & IFF_LOOPBACK); -} - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static int tcp_is_local6(struct net *net, struct in6_addr *addr) { - struct rt6_info *rt6 = rt6_lookup(net, addr, addr, 0, 0); - return rt6 && rt6->dst.dev && (rt6->dst.dev->flags & IFF_LOOPBACK); -} -#endif - -/* - * tcp_nuke_addr - destroy all sockets on the given local address - * if local address is the unspecified address (0.0.0.0 or ::), destroy all - * sockets with local addresses that are not configured. - */ -int tcp_nuke_addr(struct net *net, struct sockaddr *addr) -{ - int family = addr->sa_family; - unsigned int bucket; - - struct in_addr *in; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - struct in6_addr *in6 = NULL; -#endif - if (family == AF_INET) { - in = &((struct sockaddr_in *)addr)->sin_addr; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - } else if (family == AF_INET6) { - in6 = &((struct sockaddr_in6 *)addr)->sin6_addr; -#endif - } else { - return -EAFNOSUPPORT; - } - - for (bucket = 0; bucket < tcp_hashinfo.ehash_mask; bucket++) { - struct hlist_nulls_node *node; - struct sock *sk; - spinlock_t *lock = inet_ehash_lockp(&tcp_hashinfo, bucket); - -restart: - spin_lock_bh(lock); - sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) { - struct inet_sock *inet = inet_sk(sk); - - if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT) - continue; - if (sock_flag(sk, SOCK_DEAD)) - continue; - - if (family == AF_INET) { - __be32 s4 = inet->inet_rcv_saddr; - if (s4 == LOOPBACK4_IPV6) - continue; - - if (in->s_addr != s4 && - !(in->s_addr == INADDR_ANY && - !tcp_is_local(net, s4))) - continue; - } - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - if (family == AF_INET6) { - struct in6_addr *s6; - - s6 = &sk->sk_v6_rcv_saddr; - if (ipv6_addr_type(s6) == IPV6_ADDR_MAPPED) - continue; - - if (!ipv6_addr_equal(in6, s6) && - !(ipv6_addr_equal(in6, &in6addr_any) && - !tcp_is_local6(net, s6))) - continue; - } -#endif - - sock_hold(sk); - spin_unlock_bh(lock); - - local_bh_disable(); - bh_lock_sock(sk); - sk->sk_err = ETIMEDOUT; - sk->sk_error_report(sk); - - tcp_done(sk); - bh_unlock_sock(sk); - local_bh_enable(); - sock_put(sk); - - goto restart; - } - spin_unlock_bh(lock); - } - - return 0; -} diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 99fccad391e0..d9b25bd17bf1 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -495,21 +495,6 @@ int inet6_getname(struct socket *sock, struct sockaddr *uaddr, } EXPORT_SYMBOL(inet6_getname); -int inet6_killaddr_ioctl(struct net *net, void __user *arg) { - struct in6_ifreq ireq; - struct sockaddr_in6 sin6; - - if (!capable(CAP_NET_ADMIN)) - return -EACCES; - - if (copy_from_user(&ireq, arg, sizeof(struct in6_ifreq))) - return -EFAULT; - - sin6.sin6_family = AF_INET6; - sin6.sin6_addr = ireq.ifr6_addr; - return tcp_nuke_addr(net, (struct sockaddr *) &sin6); -} - int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; @@ -533,8 +518,6 @@ int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return addrconf_del_ifaddr(net, (void __user *) arg); case SIOCSIFDSTADDR: return addrconf_set_dstaddr(net, (void __user *) arg); - case SIOCKILLADDR: - return inet6_killaddr_ioctl(net, (void __user *) arg); default: if (!sk->sk_prot->ioctl) return -ENOIOCTLCMD; From 985a3401366362eb0a76b2818e5df205c16da2ee Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 25 Apr 2016 16:58:20 +0530 Subject: [PATCH 012/656] Revert "misc: uid_stat: Include linux/atomic.h instead of asm/atomic.h" This reverts commit 8d3a6c1538fb021448c4f6381f6191061f947ba1. This series of patches revert AOSP UID_STAT and NET_ACTIVITY_STATS drivers. I could not find any meaningful usage of these interfaces in AOSP master. UID_STAT driver expose "/proc/uid_stat/*" interfaces but it is only used in AOSP master as in what appears be an out of date bandwidth test in frameworks/base and in somewhat recent battery utils test in external/chromium-trace project. NET_ACTIVITY_STATS driver expose "/proc/net/stat/activity" interface but I can not track its usage anywhere in AOSP at all. Signed-off-by: Amit Pundir --- drivers/misc/uid_stat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c index 185c69c9738a..8b8c9a22360b 100644 --- a/drivers/misc/uid_stat.c +++ b/drivers/misc/uid_stat.c @@ -13,7 +13,7 @@ * */ -#include +#include #include #include From 5cbf5fb1475adfe150d662f203a5e7d06aaaae63 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 25 Apr 2016 17:00:08 +0530 Subject: [PATCH 013/656] Revert "misc seq_printf fixes for 4.4" This reverts commit 5c7566a29bff14166d952f2ea525d5231546f821. This patch revert some changes in net/netfilter/xt_qtaguid.c as well. I'll submit another patch to restore those changes. Signed-off-by: Amit Pundir --- drivers/misc/uid_stat.c | 3 +-- net/activity_stats.c | 7 ++++--- net/netfilter/xt_qtaguid.c | 5 +++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c index 8b8c9a22360b..4766c1f83b94 100644 --- a/drivers/misc/uid_stat.c +++ b/drivers/misc/uid_stat.c @@ -55,8 +55,7 @@ static int uid_stat_atomic_int_show(struct seq_file *m, void *v) atomic_t *counter = m->private; bytes = (unsigned int) (atomic_read(counter) + INT_MIN); - seq_printf(m, "%u\n", bytes); - return seq_has_overflowed(m) ? -ENOSPC : 0; + return seq_printf(m, "%u\n", bytes); } static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file) diff --git a/net/activity_stats.c b/net/activity_stats.c index 3bf92d80b8b9..4609ce2043eb 100644 --- a/net/activity_stats.c +++ b/net/activity_stats.c @@ -63,13 +63,14 @@ void activity_stats_update(void) static int activity_stats_show(struct seq_file *m, void *v) { int i; + int ret; seq_printf(m, "Min Bucket(sec) Count\n"); for (i = 0; i < BUCKET_MAX; i++) { - seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]); - if (seq_has_overflowed(m)) - return -ENOSPC; + ret = seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]); + if (ret) + return ret; } return 0; diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index e1442bfb668d..04bb081adde8 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2543,6 +2543,7 @@ static void pp_stats_header(struct seq_file *m) static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, int cnt_set) { + int ret; struct data_counters *cnts; tag_t tag = ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); @@ -2561,7 +2562,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, } ppi->item_index++; cnts = &ts_entry->counters; - seq_printf(m, "%d %s 0x%llx %u %u " + ret = seq_printf(m, "%d %s 0x%llx %u %u " "%llu %llu " "%llu %llu " "%llu %llu " @@ -2591,7 +2592,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - return seq_has_overflowed(m) ? -ENOSPC : 1; + return ret ?: 1; } static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry) From d9e6431c6c494ffb4b61c1a9f447f952127d02fd Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 25 Apr 2016 17:00:31 +0530 Subject: [PATCH 014/656] Revert "misc: uidstat: Remove use of obsolete create_proc_read_entry api" This reverts commit fccab646d33381af63e4f4c0d4f309a1d2b4b0c3. Signed-off-by: Amit Pundir --- drivers/misc/uid_stat.c | 50 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c index 4766c1f83b94..509822c81e97 100644 --- a/drivers/misc/uid_stat.c +++ b/drivers/misc/uid_stat.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -49,26 +48,41 @@ static struct uid_stat *find_uid_stat(uid_t uid) { return NULL; } -static int uid_stat_atomic_int_show(struct seq_file *m, void *v) +static int tcp_snd_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) { + int len; unsigned int bytes; - atomic_t *counter = m->private; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; - bytes = (unsigned int) (atomic_read(counter) + INT_MIN); - return seq_printf(m, "%u\n", bytes); + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; } -static int uid_stat_read_atomic_int_open(struct inode *inode, struct file *file) +static int tcp_rcv_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) { - return single_open(file, uid_stat_atomic_int_show, PDE_DATA(inode)); -} + int len; + unsigned int bytes; + char *p = page; + struct uid_stat *uid_entry = (struct uid_stat *) data; + if (!data) + return 0; -static const struct file_operations uid_stat_read_atomic_int_fops = { - .open = uid_stat_read_atomic_int_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; + bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN); + p += sprintf(p, "%u\n", bytes); + len = (p - page) - off; + *eof = (len <= count) ? 1 : 0; + *start = page + off; + return len; +} /* Create a new entry for tracking the specified uid. */ static struct uid_stat *create_stat(uid_t uid) { @@ -95,11 +109,11 @@ static void create_stat_proc(struct uid_stat *new_uid) entry = proc_mkdir(uid_s, parent); /* Keep reference to uid_stat so we know what uid to read stats from. */ - proc_create_data("tcp_snd", S_IRUGO, entry, - &uid_stat_read_atomic_int_fops, &new_uid->tcp_snd); + create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc, + (void *) new_uid); - proc_create_data("tcp_rcv", S_IRUGO, entry, - &uid_stat_read_atomic_int_fops, &new_uid->tcp_rcv); + create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, + (void *) new_uid); } static struct uid_stat *find_or_create_uid_stat(uid_t uid) From e9edcb004651ba55399b3c019b9f08aecb476e0d Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 25 Apr 2016 17:00:43 +0530 Subject: [PATCH 015/656] Revert "misc: uidstat: avoid create_stat() race and blockage." This reverts commit f7a812174033fe620509e6e8ca7022abd924b1c4. Signed-off-by: Amit Pundir --- drivers/misc/uid_stat.c | 52 +++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c index 509822c81e97..2141124a6c12 100644 --- a/drivers/misc/uid_stat.c +++ b/drivers/misc/uid_stat.c @@ -38,13 +38,17 @@ struct uid_stat { }; static struct uid_stat *find_uid_stat(uid_t uid) { + unsigned long flags; struct uid_stat *entry; + spin_lock_irqsave(&uid_lock, flags); list_for_each_entry(entry, &uid_list, link) { if (entry->uid == uid) { + spin_unlock_irqrestore(&uid_lock, flags); return entry; } } + spin_unlock_irqrestore(&uid_lock, flags); return NULL; } @@ -86,10 +90,13 @@ static int tcp_rcv_read_proc(char *page, char **start, off_t off, /* Create a new entry for tracking the specified uid. */ static struct uid_stat *create_stat(uid_t uid) { + unsigned long flags; + char uid_s[32]; struct uid_stat *new_uid; + struct proc_dir_entry *entry; + /* Create the uid stat struct and append it to the list. */ - new_uid = kmalloc(sizeof(struct uid_stat), GFP_ATOMIC); - if (!new_uid) + if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL) return NULL; new_uid->uid = uid; @@ -97,15 +104,11 @@ static struct uid_stat *create_stat(uid_t uid) { atomic_set(&new_uid->tcp_rcv, INT_MIN); atomic_set(&new_uid->tcp_snd, INT_MIN); + spin_lock_irqsave(&uid_lock, flags); list_add_tail(&new_uid->link, &uid_list); - return new_uid; -} + spin_unlock_irqrestore(&uid_lock, flags); -static void create_stat_proc(struct uid_stat *new_uid) -{ - char uid_s[32]; - struct proc_dir_entry *entry; - sprintf(uid_s, "%d", new_uid->uid); + sprintf(uid_s, "%d", uid); entry = proc_mkdir(uid_s, parent); /* Keep reference to uid_stat so we know what uid to read stats from. */ @@ -114,31 +117,17 @@ static void create_stat_proc(struct uid_stat *new_uid) create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, (void *) new_uid); -} -static struct uid_stat *find_or_create_uid_stat(uid_t uid) -{ - struct uid_stat *entry; - unsigned long flags; - spin_lock_irqsave(&uid_lock, flags); - entry = find_uid_stat(uid); - if (entry) { - spin_unlock_irqrestore(&uid_lock, flags); - return entry; - } - entry = create_stat(uid); - spin_unlock_irqrestore(&uid_lock, flags); - if (entry) - create_stat_proc(entry); - return entry; + return new_uid; } int uid_stat_tcp_snd(uid_t uid, int size) { struct uid_stat *entry; activity_stats_update(); - entry = find_or_create_uid_stat(uid); - if (!entry) - return -1; + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } atomic_add(size, &entry->tcp_snd); return 0; } @@ -146,9 +135,10 @@ int uid_stat_tcp_snd(uid_t uid, int size) { int uid_stat_tcp_rcv(uid_t uid, int size) { struct uid_stat *entry; activity_stats_update(); - entry = find_or_create_uid_stat(uid); - if (!entry) - return -1; + if ((entry = find_uid_stat(uid)) == NULL && + ((entry = create_stat(uid)) == NULL)) { + return -1; + } atomic_add(size, &entry->tcp_rcv); return 0; } From 87b4d82801832426d42df4982e7af11473c5e1f2 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 25 Apr 2016 17:00:57 +0530 Subject: [PATCH 016/656] Revert "net: activity_stats: Stop using obsolete create_proc_read_entry api" This reverts commit 7c121720fa14889d59e933aad0a8b9ce948a39ae. Signed-off-by: Amit Pundir --- net/activity_stats.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/net/activity_stats.c b/net/activity_stats.c index 4609ce2043eb..8a3e93470069 100644 --- a/net/activity_stats.c +++ b/net/activity_stats.c @@ -15,7 +15,6 @@ */ #include -#include #include #include @@ -60,20 +59,29 @@ void activity_stats_update(void) spin_unlock_irqrestore(&activity_lock, flags); } -static int activity_stats_show(struct seq_file *m, void *v) +static int activity_stats_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) { int i; - int ret; + int len; + char *p = page; - seq_printf(m, "Min Bucket(sec) Count\n"); + /* Only print if offset is 0, or we have enough buffer space */ + if (off || count < (30 * BUCKET_MAX + 22)) + return -ENOMEM; + + len = snprintf(p, count, "Min Bucket(sec) Count\n"); + count -= len; + p += len; for (i = 0; i < BUCKET_MAX; i++) { - ret = seq_printf(m, "%15d %lu\n", 1 << i, activity_stats[i]); - if (ret) - return ret; + len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]); + count -= len; + p += len; } + *eof = 1; - return 0; + return p - page; } static int activity_stats_notifier(struct notifier_block *nb, @@ -92,26 +100,14 @@ static int activity_stats_notifier(struct notifier_block *nb, return 0; } -static int activity_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, activity_stats_show, PDE_DATA(inode)); -} - -static const struct file_operations activity_stats_fops = { - .open = activity_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - static struct notifier_block activity_stats_notifier_block = { .notifier_call = activity_stats_notifier, }; static int __init activity_stats_init(void) { - proc_create("activity", S_IRUGO, - init_net.proc_net_stat, &activity_stats_fops); + create_proc_read_entry("activity", S_IRUGO, + init_net.proc_net_stat, activity_stats_read_proc, NULL); return register_pm_notifier(&activity_stats_notifier_block); } From 358ebabf995e4a9bddfcd8eb6d888f1ba2b1c227 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 25 Apr 2016 17:01:08 +0530 Subject: [PATCH 017/656] Revert "net: activity_stats: Add statistics for network transmission activity" This reverts commit afedd7beba14385fd797166751fde39e0f52cf72. Signed-off-by: Amit Pundir --- drivers/misc/uid_stat.c | 3 - include/net/activity_stats.h | 25 -------- net/Kconfig | 8 --- net/Makefile | 1 - net/activity_stats.c | 115 ----------------------------------- 5 files changed, 152 deletions(-) delete mode 100644 include/net/activity_stats.h delete mode 100644 net/activity_stats.c diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c index 2141124a6c12..e6760b56083c 100644 --- a/drivers/misc/uid_stat.c +++ b/drivers/misc/uid_stat.c @@ -24,7 +24,6 @@ #include #include #include -#include static DEFINE_SPINLOCK(uid_lock); static LIST_HEAD(uid_list); @@ -123,7 +122,6 @@ static struct uid_stat *create_stat(uid_t uid) { int uid_stat_tcp_snd(uid_t uid, int size) { struct uid_stat *entry; - activity_stats_update(); if ((entry = find_uid_stat(uid)) == NULL && ((entry = create_stat(uid)) == NULL)) { return -1; @@ -134,7 +132,6 @@ int uid_stat_tcp_snd(uid_t uid, int size) { int uid_stat_tcp_rcv(uid_t uid, int size) { struct uid_stat *entry; - activity_stats_update(); if ((entry = find_uid_stat(uid)) == NULL && ((entry = create_stat(uid)) == NULL)) { return -1; diff --git a/include/net/activity_stats.h b/include/net/activity_stats.h deleted file mode 100644 index 10e4c1506eeb..000000000000 --- a/include/net/activity_stats.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2010 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Author: Mike Chan (mike@android.com) - */ - -#ifndef __activity_stats_h -#define __activity_stats_h - -#ifdef CONFIG_NET_ACTIVITY_STATS -void activity_stats_update(void); -#else -#define activity_stats_update(void) {} -#endif - -#endif /* _NET_ACTIVITY_STATS_H */ diff --git a/net/Kconfig b/net/Kconfig index 043fe1dc0860..ce9585cf343a 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -92,14 +92,6 @@ config ANDROID_PARANOID_NETWORK help none -config NET_ACTIVITY_STATS - bool "Network activity statistics tracking" - default y - help - Network activity statistics are useful for tracking wireless - modem activity on 2G, 3G, 4G wireless networks. Counts number of - transmissions and groups them in specified time buckets. - config NETWORK_SECMARK bool "Security Marking" help diff --git a/net/Makefile b/net/Makefile index eeb9d5db454f..a5d04098dfce 100644 --- a/net/Makefile +++ b/net/Makefile @@ -77,4 +77,3 @@ endif ifneq ($(CONFIG_NET_L3_MASTER_DEV),) obj-y += l3mdev/ endif -obj-$(CONFIG_NET_ACTIVITY_STATS) += activity_stats.o diff --git a/net/activity_stats.c b/net/activity_stats.c deleted file mode 100644 index 8a3e93470069..000000000000 --- a/net/activity_stats.c +++ /dev/null @@ -1,115 +0,0 @@ -/* net/activity_stats.c - * - * Copyright (C) 2010 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Author: Mike Chan (mike@android.com) - */ - -#include -#include -#include - -/* - * Track transmission rates in buckets (power of 2). - * 1,2,4,8...512 seconds. - * - * Buckets represent the count of network transmissions at least - * N seconds apart, where N is 1 << bucket index. - */ -#define BUCKET_MAX 10 - -/* Track network activity frequency */ -static unsigned long activity_stats[BUCKET_MAX]; -static ktime_t last_transmit; -static ktime_t suspend_time; -static DEFINE_SPINLOCK(activity_lock); - -void activity_stats_update(void) -{ - int i; - unsigned long flags; - ktime_t now; - s64 delta; - - spin_lock_irqsave(&activity_lock, flags); - now = ktime_get(); - delta = ktime_to_ns(ktime_sub(now, last_transmit)); - - for (i = BUCKET_MAX - 1; i >= 0; i--) { - /* - * Check if the time delta between network activity is within the - * minimum bucket range. - */ - if (delta < (1000000000ULL << i)) - continue; - - activity_stats[i]++; - last_transmit = now; - break; - } - spin_unlock_irqrestore(&activity_lock, flags); -} - -static int activity_stats_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int i; - int len; - char *p = page; - - /* Only print if offset is 0, or we have enough buffer space */ - if (off || count < (30 * BUCKET_MAX + 22)) - return -ENOMEM; - - len = snprintf(p, count, "Min Bucket(sec) Count\n"); - count -= len; - p += len; - - for (i = 0; i < BUCKET_MAX; i++) { - len = snprintf(p, count, "%15d %lu\n", 1 << i, activity_stats[i]); - count -= len; - p += len; - } - *eof = 1; - - return p - page; -} - -static int activity_stats_notifier(struct notifier_block *nb, - unsigned long event, void *dummy) -{ - switch (event) { - case PM_SUSPEND_PREPARE: - suspend_time = ktime_get_real(); - break; - - case PM_POST_SUSPEND: - suspend_time = ktime_sub(ktime_get_real(), suspend_time); - last_transmit = ktime_sub(last_transmit, suspend_time); - } - - return 0; -} - -static struct notifier_block activity_stats_notifier_block = { - .notifier_call = activity_stats_notifier, -}; - -static int __init activity_stats_init(void) -{ - create_proc_read_entry("activity", S_IRUGO, - init_net.proc_net_stat, activity_stats_read_proc, NULL); - return register_pm_notifier(&activity_stats_notifier_block); -} - -subsys_initcall(activity_stats_init); - From 58025a0a1b0a001af20ea3f601fb9978e9a4e57e Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 25 Apr 2016 17:08:15 +0530 Subject: [PATCH 018/656] Revert "misc: uidstat: Adding uid stat driver to collect network statistics." This reverts commit 6b6d5fbf9ae567aefb58099a30bbb6d25fa8925b. Signed-off-by: Amit Pundir --- drivers/misc/Kconfig | 4 - drivers/misc/Makefile | 1 - drivers/misc/uid_stat.c | 153 --------------------------------------- include/linux/uid_stat.h | 29 -------- net/ipv4/tcp.c | 14 ---- 5 files changed, 201 deletions(-) delete mode 100644 drivers/misc/uid_stat.c delete mode 100644 include/linux/uid_stat.h diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2dcafd0ba17f..ca7463544c72 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -412,10 +412,6 @@ config TI_DAC7512 This driver can also be built as a module. If so, the module will be called ti_dac7512. -config UID_STAT - bool "UID based statistics tracking exported to /proc/uid_stat" - default n - config VMWARE_BALLOON tristate "VMware Balloon Driver" depends on VMWARE_VMCI && X86 && HYPERVISOR_GUEST diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 24483a6caa6b..e5142b836aee 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -35,7 +35,6 @@ obj-$(CONFIG_ISL29020) += isl29020.o obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o obj-$(CONFIG_DS1682) += ds1682.o obj-$(CONFIG_TI_DAC7512) += ti_dac7512.o -obj-$(CONFIG_UID_STAT) += uid_stat.o obj-$(CONFIG_C2PORT) += c2port/ obj-$(CONFIG_HMC6352) += hmc6352.o obj-y += eeprom/ diff --git a/drivers/misc/uid_stat.c b/drivers/misc/uid_stat.c deleted file mode 100644 index e6760b56083c..000000000000 --- a/drivers/misc/uid_stat.c +++ /dev/null @@ -1,153 +0,0 @@ -/* drivers/misc/uid_stat.c - * - * Copyright (C) 2008 - 2009 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static DEFINE_SPINLOCK(uid_lock); -static LIST_HEAD(uid_list); -static struct proc_dir_entry *parent; - -struct uid_stat { - struct list_head link; - uid_t uid; - atomic_t tcp_rcv; - atomic_t tcp_snd; -}; - -static struct uid_stat *find_uid_stat(uid_t uid) { - unsigned long flags; - struct uid_stat *entry; - - spin_lock_irqsave(&uid_lock, flags); - list_for_each_entry(entry, &uid_list, link) { - if (entry->uid == uid) { - spin_unlock_irqrestore(&uid_lock, flags); - return entry; - } - } - spin_unlock_irqrestore(&uid_lock, flags); - return NULL; -} - -static int tcp_snd_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len; - unsigned int bytes; - char *p = page; - struct uid_stat *uid_entry = (struct uid_stat *) data; - if (!data) - return 0; - - bytes = (unsigned int) (atomic_read(&uid_entry->tcp_snd) + INT_MIN); - p += sprintf(p, "%u\n", bytes); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; -} - -static int tcp_rcv_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len; - unsigned int bytes; - char *p = page; - struct uid_stat *uid_entry = (struct uid_stat *) data; - if (!data) - return 0; - - bytes = (unsigned int) (atomic_read(&uid_entry->tcp_rcv) + INT_MIN); - p += sprintf(p, "%u\n", bytes); - len = (p - page) - off; - *eof = (len <= count) ? 1 : 0; - *start = page + off; - return len; -} - -/* Create a new entry for tracking the specified uid. */ -static struct uid_stat *create_stat(uid_t uid) { - unsigned long flags; - char uid_s[32]; - struct uid_stat *new_uid; - struct proc_dir_entry *entry; - - /* Create the uid stat struct and append it to the list. */ - if ((new_uid = kmalloc(sizeof(struct uid_stat), GFP_KERNEL)) == NULL) - return NULL; - - new_uid->uid = uid; - /* Counters start at INT_MIN, so we can track 4GB of network traffic. */ - atomic_set(&new_uid->tcp_rcv, INT_MIN); - atomic_set(&new_uid->tcp_snd, INT_MIN); - - spin_lock_irqsave(&uid_lock, flags); - list_add_tail(&new_uid->link, &uid_list); - spin_unlock_irqrestore(&uid_lock, flags); - - sprintf(uid_s, "%d", uid); - entry = proc_mkdir(uid_s, parent); - - /* Keep reference to uid_stat so we know what uid to read stats from. */ - create_proc_read_entry("tcp_snd", S_IRUGO, entry , tcp_snd_read_proc, - (void *) new_uid); - - create_proc_read_entry("tcp_rcv", S_IRUGO, entry, tcp_rcv_read_proc, - (void *) new_uid); - - return new_uid; -} - -int uid_stat_tcp_snd(uid_t uid, int size) { - struct uid_stat *entry; - if ((entry = find_uid_stat(uid)) == NULL && - ((entry = create_stat(uid)) == NULL)) { - return -1; - } - atomic_add(size, &entry->tcp_snd); - return 0; -} - -int uid_stat_tcp_rcv(uid_t uid, int size) { - struct uid_stat *entry; - if ((entry = find_uid_stat(uid)) == NULL && - ((entry = create_stat(uid)) == NULL)) { - return -1; - } - atomic_add(size, &entry->tcp_rcv); - return 0; -} - -static int __init uid_stat_init(void) -{ - parent = proc_mkdir("uid_stat", NULL); - if (!parent) { - pr_err("uid_stat: failed to create proc entry\n"); - return -1; - } - return 0; -} - -__initcall(uid_stat_init); diff --git a/include/linux/uid_stat.h b/include/linux/uid_stat.h deleted file mode 100644 index 6bd6c4e52d17..000000000000 --- a/include/linux/uid_stat.h +++ /dev/null @@ -1,29 +0,0 @@ -/* include/linux/uid_stat.h - * - * Copyright (C) 2008-2009 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef __uid_stat_h -#define __uid_stat_h - -/* Contains definitions for resource tracking per uid. */ - -#ifdef CONFIG_UID_STAT -int uid_stat_tcp_snd(uid_t uid, int size); -int uid_stat_tcp_rcv(uid_t uid, int size); -#else -#define uid_stat_tcp_snd(uid, size) do {} while (0); -#define uid_stat_tcp_rcv(uid, size) do {} while (0); -#endif - -#endif /* _LINUX_UID_STAT_H */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index cf2e13db0619..6ecfc9de599c 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -269,7 +269,6 @@ #include #include #include -#include #include #include @@ -1285,10 +1284,6 @@ out: tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); out_nopush: release_sock(sk); - - if (copied + copied_syn) - uid_stat_tcp_snd(from_kuid(&init_user_ns, current_uid()), - copied + copied_syn); return copied + copied_syn; do_fault: @@ -1563,8 +1558,6 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc, if (copied > 0) { tcp_recv_skb(sk, seq, &offset); tcp_cleanup_rbuf(sk, copied); - uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), - copied); } return copied; } @@ -1898,10 +1891,6 @@ skip_copy: tcp_cleanup_rbuf(sk, copied); release_sock(sk); - - if (copied > 0) - uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), - copied); return copied; out: @@ -1910,9 +1899,6 @@ out: recv_urg: err = tcp_recv_urg(sk, msg, len, flags); - if (err > 0) - uid_stat_tcp_rcv(from_kuid(&init_user_ns, current_uid()), - err); goto out; recv_sndq: From 6fb5902fa958c09ad440cadcdaa4915c650c3803 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Thu, 1 Oct 2015 10:44:36 +0530 Subject: [PATCH 019/656] netfilter: xt_qtaguid: seq_printf fixes Update seq_printf() usage in xt_qtaguid to align with changes from mainline commit 6798a8caaf64 "fs/seq_file: convert int seq_vprint/seq_printf/etc... returns to void". Signed-off-by: Amit Pundir --- net/netfilter/xt_qtaguid.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 04bb081adde8..e1442bfb668d 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -2543,7 +2543,6 @@ static void pp_stats_header(struct seq_file *m) static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, int cnt_set) { - int ret; struct data_counters *cnts; tag_t tag = ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); @@ -2562,7 +2561,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, } ppi->item_index++; cnts = &ts_entry->counters; - ret = seq_printf(m, "%d %s 0x%llx %u %u " + seq_printf(m, "%d %s 0x%llx %u %u " "%llu %llu " "%llu %llu " "%llu %llu " @@ -2592,7 +2591,7 @@ static int pp_stats_line(struct seq_file *m, struct tag_stat *ts_entry, cnts->bpc[cnt_set][IFS_TX][IFS_UDP].packets, cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].bytes, cnts->bpc[cnt_set][IFS_TX][IFS_PROTO_OTHER].packets); - return ret ?: 1; + return seq_has_overflowed(m) ? -ENOSPC : 1; } static bool pp_sets(struct seq_file *m, struct tag_stat *ts_entry) From aaed164e81bcc055eee6e18a4450310bfa620fba Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 25 Apr 2016 23:55:44 +0530 Subject: [PATCH 020/656] android: recommended.cfg: remove CONFIG_UID_STAT Remove UID Stat driver. Change-Id: Ifc9d2c6fe27900f30e6407398f5b24222518bffc Signed-off-by: Amit Pundir --- android/configs/android-recommended.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/android/configs/android-recommended.cfg b/android/configs/android-recommended.cfg index e4c8aaade197..35936afdcae4 100644 --- a/android/configs/android-recommended.cfg +++ b/android/configs/android-recommended.cfg @@ -119,7 +119,6 @@ CONFIG_TIMER_STATS=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y CONFIG_UHID=y -CONFIG_UID_STAT=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_EHCI_HCD=y CONFIG_USB_HIDDEV=y From 6fa18ffc7122663277db9311c4e679f632d2b78d Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 22 Apr 2016 00:00:14 -0700 Subject: [PATCH 021/656] vfs: change d_canonical_path to take two paths bug: 23904372 Change-Id: I4a686d64b6de37decf60019be1718e1d820193e6 Signed-off-by: Daniel Rosenberg --- fs/notify/inotify/inotify_user.c | 2 +- fs/sdcardfs/dentry.c | 6 +++++- include/linux/dcache.h | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index f72f3b25b3f2..e2893f17dde2 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -746,7 +746,7 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname, /* support stacked filesystems */ if(path.dentry && path.dentry->d_op) { if (path.dentry->d_op->d_canonical_path) { - path.dentry->d_op->d_canonical_path(path.dentry, &alteredpath); + path.dentry->d_op->d_canonical_path(&path, &alteredpath); canonical_path = &alteredpath; path_put(&path); } diff --git a/fs/sdcardfs/dentry.c b/fs/sdcardfs/dentry.c index ba165ef11e27..971928ab6c21 100644 --- a/fs/sdcardfs/dentry.c +++ b/fs/sdcardfs/dentry.c @@ -172,11 +172,15 @@ static int sdcardfs_cmp_ci(const struct dentry *parent, return 1; } +static void sdcardfs_canonical_path(const struct path *path, struct path *actual_path) { + sdcardfs_get_real_lower(path->dentry, actual_path); +} + const struct dentry_operations sdcardfs_ci_dops = { .d_revalidate = sdcardfs_d_revalidate, .d_release = sdcardfs_d_release, .d_hash = sdcardfs_hash_ci, .d_compare = sdcardfs_cmp_ci, - .d_canonical_path = sdcardfs_get_real_lower, + .d_canonical_path = sdcardfs_canonical_path, }; diff --git a/include/linux/dcache.h b/include/linux/dcache.h index e4221f7c5b53..bdbb2d490d2d 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h @@ -161,7 +161,7 @@ struct dentry_operations { struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(struct dentry *, bool); struct inode *(*d_select_inode)(struct dentry *, unsigned); - void (*d_canonical_path)(const struct dentry *, struct path *); + void (*d_canonical_path)(const struct path *, struct path *); struct dentry *(*d_real)(struct dentry *, struct inode *); } ____cacheline_aligned; From 28813390bf99b818801105328fbc2da466a9974d Mon Sep 17 00:00:00 2001 From: Daniel Rosenberg Date: Fri, 22 Apr 2016 00:00:48 -0700 Subject: [PATCH 022/656] fuse: Add support for d_canonical_path Allows FUSE to report to inotify that it is acting as a layered filesystem. The userspace component returns a string representing the location of the underlying file. If the string cannot be resolved into a path, the top level path is returned instead. bug: 23904372 Change-Id: Iabdca0bbedfbff59e9c820c58636a68ef9683d9f Signed-off-by: Daniel Rosenberg --- fs/fuse/dev.c | 5 +++++ fs/fuse/dir.c | 45 +++++++++++++++++++++++++++++++++++++++ fs/fuse/fuse_i.h | 3 +++ include/uapi/linux/fuse.h | 1 + 4 files changed, 54 insertions(+) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 175fcdeabe4c..8932c06e40c1 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -1935,6 +1936,10 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, cs->move_pages = 0; err = copy_out_args(cs, &req->out, nbytes); + if (req->in.h.opcode == FUSE_CANONICAL_PATH) { + req->out.h.error = kern_path((char *)req->out.args[0].value, 0, + req->canonical_path); + } fuse_copy_finish(cs); spin_lock(&fpq->lock); diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 5e2e08712d3b..24df0a5df675 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -267,6 +267,50 @@ invalid: goto out; } +/* + * Get the canonical path. Since we must translate to a path, this must be done + * in the context of the userspace daemon, however, the userspace daemon cannot + * look up paths on its own. Instead, we handle the lookup as a special case + * inside of the write request. + */ +static void fuse_dentry_canonical_path(const struct path *path, struct path *canonical_path) { + struct inode *inode = path->dentry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req; + int err; + char *path_name; + + req = fuse_get_req(fc, 1); + err = PTR_ERR(req); + if (IS_ERR(req)) + goto default_path; + + path_name = (char*)__get_free_page(GFP_KERNEL); + if (!path_name) { + fuse_put_request(fc, req); + goto default_path; + } + + req->in.h.opcode = FUSE_CANONICAL_PATH; + req->in.h.nodeid = get_node_id(inode); + req->in.numargs = 0; + req->out.numargs = 1; + req->out.args[0].size = PATH_MAX; + req->out.args[0].value = path_name; + req->canonical_path = canonical_path; + req->out.argvar = 1; + fuse_request_send(fc, req); + err = req->out.h.error; + fuse_put_request(fc, req); + free_page((unsigned long)path_name); + if (!err) + return; +default_path: + canonical_path->dentry = path->dentry; + canonical_path->mnt = path->mnt; + path_get(canonical_path); +} + static int invalid_nodeid(u64 nodeid) { return !nodeid || nodeid == FUSE_ROOT_ID; @@ -274,6 +318,7 @@ static int invalid_nodeid(u64 nodeid) const struct dentry_operations fuse_dentry_operations = { .d_revalidate = fuse_dentry_revalidate, + .d_canonical_path = fuse_dentry_canonical_path, }; int fuse_valid_type(int m) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 604cd42dafef..644687ae04bd 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -372,6 +372,9 @@ struct fuse_req { /** Inode used in the request or NULL */ struct inode *inode; + /** Path used for completing d_canonical_path */ + struct path *canonical_path; + /** AIO control block */ struct fuse_io_priv *io; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index c9aca042e61d..8485dd4300b8 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -358,6 +358,7 @@ enum fuse_opcode { FUSE_FALLOCATE = 43, FUSE_READDIRPLUS = 44, FUSE_RENAME2 = 45, + FUSE_CANONICAL_PATH= 2016, /* CUSE specific operations */ CUSE_INIT = 4096, From e7d7e51782dc6837d958d730848f5c5dceaee71d Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 26 Apr 2016 15:51:06 +0530 Subject: [PATCH 023/656] Revert "SELinux: build fix for 4.1" This reverts commit 43e1b4f528e1654fadd1097f7cc5c50be6e45b77. This patch is part of code which is already upstreamed in v4.4. Commits 5c73fceb8c70 (SELinux: Enable setting security contexts on rootfs inodes.), 12f348b9dcf6 (SELinux: rename SE_SBLABELSUPP to SBLABEL_MNT), and b43e725d8d38 (SELinux: use a helper function to determine seclabel). for reference. Signed-off-by: Amit Pundir --- security/selinux/hooks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 78d06ff4eeb7..d4d12949de09 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -469,7 +469,7 @@ static int sb_finish_set_opts(struct super_block *sb) * setting SELinux context on in-core inodes. */ if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) - sbsec->flags |= SBLABEL_MNT; + sbsec->flags |= SE_SBLABELSUPP; /* Initialize the root inode. */ rc = inode_doinit_with_dentry(root_inode, root); From ab378c0ed26df68765c2930fd9ab3f3935424b53 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 26 Apr 2016 15:51:20 +0530 Subject: [PATCH 024/656] Revert "SELinux: Enable setting security contexts on rootfs inodes." This reverts commit 78d36d2111cd4ca722a602846f7db8f54a0b074c. Drop this duplicate patch. This patch is already upstreamed in v4.4. Commits 5c73fceb8c70 (SELinux: Enable setting security contexts on rootfs inodes.), 12f348b9dcf6 (SELinux: rename SE_SBLABELSUPP to SBLABEL_MNT), and b43e725d8d38 (SELinux: use a helper function to determine seclabel), for reference. Signed-off-by: Amit Pundir --- security/selinux/hooks.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d4d12949de09..9b0586d0f3c4 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -464,13 +464,6 @@ static int sb_finish_set_opts(struct super_block *sb) if (selinux_is_sblabel_mnt(sb)) sbsec->flags |= SBLABEL_MNT; - /* - * Special handling for rootfs. Is genfs but supports - * setting SELinux context on in-core inodes. - */ - if (strncmp(sb->s_type->name, "rootfs", sizeof("rootfs")) == 0) - sbsec->flags |= SE_SBLABELSUPP; - /* Initialize the root inode. */ rc = inode_doinit_with_dentry(root_inode, root); From 52a20402ae1d5d81ddccd2be2a9f62283cc256e7 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Mon, 25 Apr 2016 14:28:30 -0700 Subject: [PATCH 025/656] Revert "mm: vmscan: Add a debug file for shrinkers" Kernel panic when type "cat /sys/kernel/debug/shrinker" Unable to handle kernel paging request at virtual address 0af37d40 pgd = d4dec000 [0af37d40] *pgd=00000000 Internal error: Oops: 5 [#1] PREEMPT SMP ARM [] (_raw_spin_lock) from [] (list_lru_count_one+0x14/0x28) [] (list_lru_count_one) from [] (super_cache_count+0x40/0xa0) [] (super_cache_count) from [] (debug_shrinker_show+0x50/0x90) [] (debug_shrinker_show) from [] (seq_read+0x1ec/0x48c) [] (seq_read) from [] (__vfs_read+0x20/0xd0) [] (__vfs_read) from [] (vfs_read+0x7c/0x104) [] (vfs_read) from [] (SyS_read+0x44/0x9c) [] (SyS_read) from [] (ret_fast_syscall+0x0/0x3c) Code: e1a04000 e3a00001 ebd66b39 f594f000 (e1943f9f) ---[ end trace 60c74014a63a9688 ]--- Kernel panic - not syncing: Fatal exception shrink_control.nid is used but not initialzed, same for shrink_control.memcg. This reverts commit b0e7a582b2264cdf75874dcd8df915b6b4427755. Change-Id: I108de88fa4baaef99a53c4e4c6a1d8c4b4804157 Reported-by: Xiaowen Liu Signed-off-by: Dmitry Shmidt --- mm/vmscan.c | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 2e6547ec1b09..0c114e2b01d3 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -46,7 +46,6 @@ #include #include #include -#include #include #include @@ -221,39 +220,6 @@ static unsigned long get_lru_size(struct lruvec *lruvec, enum lru_list lru) return zone_page_state(lruvec_zone(lruvec), NR_LRU_BASE + lru); } -struct dentry *debug_file; - -static int debug_shrinker_show(struct seq_file *s, void *unused) -{ - struct shrinker *shrinker; - struct shrink_control sc; - - sc.gfp_mask = -1; - sc.nr_to_scan = 0; - - down_read(&shrinker_rwsem); - list_for_each_entry(shrinker, &shrinker_list, list) { - int num_objs; - - num_objs = shrinker->count_objects(shrinker, &sc); - seq_printf(s, "%pf %d\n", shrinker->scan_objects, num_objs); - } - up_read(&shrinker_rwsem); - return 0; -} - -static int debug_shrinker_open(struct inode *inode, struct file *file) -{ - return single_open(file, debug_shrinker_show, inode->i_private); -} - -static const struct file_operations debug_shrinker_fops = { - .open = debug_shrinker_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - /* * Add a shrinker callback to be called from the vm. */ @@ -283,15 +249,6 @@ int register_shrinker(struct shrinker *shrinker) } EXPORT_SYMBOL(register_shrinker); -static int __init add_shrinker_debug(void) -{ - debugfs_create_file("shrinker", 0644, NULL, NULL, - &debug_shrinker_fops); - return 0; -} - -late_initcall(add_shrinker_debug); - /* * Remove one */ From 202d12a1f733a252ee76f4cc497c9ce86270ebb2 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Fri, 22 Apr 2016 17:12:57 -0700 Subject: [PATCH 026/656] xt_qtaguid: Fix panic caused by synack processing In upstream commit ca6fb06518836ef9b65dc0aac02ff97704d52a05 (tcp: attach SYNACK messages to request sockets instead of listener) http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=ca6fb0651883 The building of synack messages was changed, which made it so the skb->sk points to a casted request_sock. This is problematic, as there is no sk_socket in a request_sock. So when the qtaguid_mt function tries to access the sk->sk_socket, it accesses uninitialized memory. After looking at how other netfilter implementations handle this, I realized there was a skb_to_full_sk() helper added, which the xt_qtaguid code isn't yet using. This patch adds its use, and resovles panics seen when accessing uninitialzed memory when processing synack packets. Reported-by: YongQin Liu Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index e1442bfb668d..822dc3c3bce1 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1689,7 +1689,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) /* default: Fall through and do UID releated work */ } - sk = skb->sk; + sk = skb_to_full_sk(skb); /* * When in TCP_TIME_WAIT the sk is not a "struct sock" but * "struct inet_timewait_sock" which is missing fields. From 11f34dc252b9dc8f899b0624ef2f192a9f05d254 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 26 Apr 2016 15:14:35 +0530 Subject: [PATCH 027/656] Revert "HID: steelseries: validate output report details" This reverts commit 90037b2720acffa6da2269a10ecf24ec2dace89b. Remove duplicate code. This patch is already upstreamed in v4.4, commit 41df7f6d4372 (HID: steelseries: validate output report details). Signed-off-by: Amit Pundir --- drivers/hid/hid-steelseries.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/hid/hid-steelseries.c b/drivers/hid/hid-steelseries.c index 93ddc1c65b4c..3edd4ac36494 100644 --- a/drivers/hid/hid-steelseries.c +++ b/drivers/hid/hid-steelseries.c @@ -253,11 +253,6 @@ static int steelseries_srws1_probe(struct hid_device *hdev, goto err_free; } - if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 16)) { - ret = -ENODEV; - goto err_free; - } - ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); From f3126efd240499b6313ad3cd9ed6ed25ee97eb89 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 26 Apr 2016 14:47:53 +0530 Subject: [PATCH 028/656] Revert "hid-multitouch: Filter collections by application usage." This reverts commit 0840b80cb9626906b57df54e7229db60f9aea4f2. This patch is already upstreamed in v4.4, commit 658d4aed59b3 (HID: hid-multitouch: Filter collections by application usage.), and further fixed/cleaned up afterwards in commits c2ef8f21ea8f (HID: multitouch: add support for trackpads), 76f5902aebda (HID: hid-multitouch: Simplify setup and frame synchronization) et al. By having this duplicate patch in AOSP we are doing redundant checks for Touchscreen and Touchpad devices. Signed-off-by: Amit Pundir --- drivers/hid/hid-multitouch.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 7ecd96bdf834..c5ec4f915594 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -450,16 +450,6 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) td->buttons_count++; - /* Only map fields from TouchScreen or TouchPad collections. - * We need to ignore fields that belong to other collections - * such as Mouse that might have the same GenericDesktop usages. */ - if (field->application == HID_DG_TOUCHSCREEN) - set_bit(INPUT_PROP_DIRECT, hi->input->propbit); - else if (field->application == HID_DG_TOUCHPAD) - set_bit(INPUT_PROP_POINTER, hi->input->propbit); - else - return 0; - if (usage->usage_index) prev_usage = &field->usage[usage->usage_index - 1]; From 284430f2784e49c5c6d59f8b58c47220602069f8 Mon Sep 17 00:00:00 2001 From: Yongqin Liu Date: Thu, 28 Apr 2016 13:53:36 +0800 Subject: [PATCH 029/656] quick selinux support for tracefs Here is just the quick fix for tracefs with selinux. just add tracefs to the list of whitelisted filesystem types in selinux_is_sblabel_mnt(), but the right fix would be to generalize this logic as described in the last item on the todo list, https://bitbucket.org/seandroid/wiki/wiki/ToDo Change-Id: I2aa803ccffbcd2802a7287514da7648e6b364157 Signed-off-by: Yongqin Liu --- security/selinux/hooks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9b0586d0f3c4..94a0bfc748d1 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -420,6 +420,7 @@ static int selinux_is_sblabel_mnt(struct super_block *sb) !strcmp(sb->s_type->name, "sysfs") || !strcmp(sb->s_type->name, "pstore") || !strcmp(sb->s_type->name, "debugfs") || + !strcmp(sb->s_type->name, "tracefs") || !strcmp(sb->s_type->name, "rootfs"); } From dab07c53da6e724035684eb63a27d478e49c58c5 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 2 May 2016 15:32:15 +0530 Subject: [PATCH 030/656] Revert "mmc: Add status IRQ and status callback function to mmc platform data" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 91fa97e1e5c001d52f6c993d37be08d1e84f47b7. This patch is no longer valid. There are no users for this status irq and callback in android-4.x. The Qcom platform (mach-msm/qsd8x50, HTC Dream..) and SDCC controller (msm_sdcc) using this status IRQ and callback are dropped from mainline sometime back. 27842bb18b00 (mmc: Remove msm_sdcc driver) c0c89fafa289 (ARM: Remove mach-msm and associated ARM architecture code) Change-Id: Ia38e42a06dc184395f79c1ec1d306bf9775704d5 Signed-off-by: Amit Pundir --- include/linux/amba/mmci.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 8bfd21c13d59..eff56cb0016a 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -40,10 +40,7 @@ struct mmci_platform_data { int gpio_wp; int gpio_cd; bool cd_invert; - unsigned int status_irq; struct embedded_sdio_data *embedded_sdio; - int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); - }; #endif From d4c7c8eafe99e465e06b81a9ee0458bff5f65b1d Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Wed, 23 Mar 2016 13:18:03 -0700 Subject: [PATCH 031/656] usb: dual-role: make stub functions inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_DUAL_ROLE_USB_INTF is disabled but the exported functions are referenced, the build will result in warnings such as: In file included from include/linux/usb/class-dual-role.h:112:13: warning: ‘dual_role_instance_changed’ defined but not used [-Wunused-function] These stub functions should be static inline. Change-Id: I5a9ef58dca32306fac5a4c7f28cdaa36fa8ae078 Signed-off-by: Jack Pham (cherry picked from commit 2d152dbb0743526b21d6bbefe097f874c027f860) (cherry picked from commit 8ad66cafaa10e6ba94ff79a8dbc2cc437c6bfe93) --- include/linux/usb/class-dual-role.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/linux/usb/class-dual-role.h b/include/linux/usb/class-dual-role.h index af42ed34944a..c6df2238012e 100644 --- a/include/linux/usb/class-dual-role.h +++ b/include/linux/usb/class-dual-role.h @@ -109,18 +109,19 @@ extern int dual_role_property_is_writeable(struct dual_role_phy_instance enum dual_role_property prop); extern void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role); #else /* CONFIG_DUAL_ROLE_USB_INTF */ -static void dual_role_instance_changed(struct dual_role_phy_instance +static inline void dual_role_instance_changed(struct dual_role_phy_instance *dual_role){} -static struct dual_role_phy_instance *__must_check +static inline struct dual_role_phy_instance *__must_check devm_dual_role_instance_register(struct device *parent, const struct dual_role_phy_desc *desc) { return ERR_PTR(-ENOSYS); } -static void devm_dual_role_instance_unregister(struct device *dev, +static inline void devm_dual_role_instance_unregister(struct device *dev, struct dual_role_phy_instance *dual_role){} -static void *dual_role_get_drvdata(struct dual_role_phy_instance *dual_role) +static inline void *dual_role_get_drvdata(struct dual_role_phy_instance + *dual_role) { return ERR_PTR(-ENOSYS); } From 761edb8bf7a03a72ae12adbf1ab64bde92f2cb8c Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Wed, 4 May 2016 11:05:15 +0530 Subject: [PATCH 032/656] Revert "ARM: Add 'card_present' state to mmc_platfrom_data" This reverts commit 541632275e983573b8250fcd4402f772d7bd1e6f. mmc_platform_data (or arch/arm/include/asm/mach/mmc.h in general) has no active user in android-4.x kernels. Also all the necessary bits are already moved to include/linux/amba/mmci.h. 6ef297f86b62 (ARM: 5720/1: Move MMCI header to amba include dir) Change-Id: Iff384eb527327bf88543408e0257241c1fd99a43 Signed-off-by: Amit Pundir --- arch/arm/include/asm/mach/mmc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h index bca864ac945f..f8d391ad9203 100644 --- a/arch/arm/include/asm/mach/mmc.h +++ b/arch/arm/include/asm/mach/mmc.h @@ -18,7 +18,6 @@ struct embedded_sdio_data { struct mmc_platform_data { unsigned int ocr_mask; /* available voltages */ int built_in; /* built-in device flag */ - int card_present; /* card detect state */ u32 (*translate_vdd)(struct device *, unsigned int); unsigned int (*status)(struct device *); struct embedded_sdio_data *embedded_sdio; From 96515037c151b35a09a10544bd82099fcab8af9d Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Wed, 4 May 2016 11:14:16 +0530 Subject: [PATCH 033/656] Revert "Recreate asm/mach/mmc.h include file" This reverts commit 5b42ae3edab6c39c1337d36881d29350bb36dcff. This recereated arch/arm/include/asm/mach/mmc.h include file has no active user in android-4.x kernels. Also all the necessary bits are already moved to include/linux/amba/mmci.h. 6ef297f86b62 (ARM: 5720/1: Move MMCI header to amba include dir) Change-Id: Ibf258b355d17f54f49b777a8f6e0089e9b59a3a5 Signed-off-by: Amit Pundir --- arch/arm/include/asm/mach/mmc.h | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 arch/arm/include/asm/mach/mmc.h diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h deleted file mode 100644 index f8d391ad9203..000000000000 --- a/arch/arm/include/asm/mach/mmc.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * arch/arm/include/asm/mach/mmc.h - */ -#ifndef ASMARM_MACH_MMC_H -#define ASMARM_MACH_MMC_H - -#include -#include -#include - -struct embedded_sdio_data { - struct sdio_cis cis; - struct sdio_cccr cccr; - struct sdio_embedded_func *funcs; - int num_funcs; -}; - -struct mmc_platform_data { - unsigned int ocr_mask; /* available voltages */ - int built_in; /* built-in device flag */ - u32 (*translate_vdd)(struct device *, unsigned int); - unsigned int (*status)(struct device *); - struct embedded_sdio_data *embedded_sdio; - int (*register_status_notify)(void (*callback)(int card_present, void *dev_id), void *dev_id); -}; - -#endif From ad8a69c91954d48d3eaa0784f2f3bc9204dc10b3 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 4 May 2016 13:51:38 -0700 Subject: [PATCH 034/656] fiq_debugger: Add option to apply uart overlay by FIQ_DEBUGGER_UART_OVERLAY fiq_debugger is taking over uart, so it is necessary to disable original uart in DT file. It can be done manually or by overlay. Change-Id: I9f50ec15b0e22e602d73b9f745fc8666f8925d09 Signed-off-by: Dmitry Shmidt --- drivers/staging/android/fiq_debugger/Kconfig | 9 ++++++ .../android/fiq_debugger/fiq_debugger.c | 30 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/drivers/staging/android/fiq_debugger/Kconfig b/drivers/staging/android/fiq_debugger/Kconfig index 56f7f999377e..60fc224d4efc 100644 --- a/drivers/staging/android/fiq_debugger/Kconfig +++ b/drivers/staging/android/fiq_debugger/Kconfig @@ -42,6 +42,15 @@ config FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE If enabled, this puts the fiq debugger into console mode by default. Otherwise, the fiq debugger will start out in debug mode. +config FIQ_DEBUGGER_UART_OVERLAY + bool "Install uart DT overlay" + depends on FIQ_DEBUGGER + select OF_OVERLAY + default n + help + If enabled, fiq debugger is calling fiq_debugger_uart_overlay() + that will apply overlay uart_overlay@0 to disable proper uart. + config FIQ_WATCHDOG bool select FIQ_DEBUGGER diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index 7f056831dbff..0c558331a3d2 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -39,6 +39,10 @@ #include #endif +#ifdef CONFIG_FIQ_DEBUGGER_UART_OVERLAY +#include +#endif + #include #include "fiq_debugger.h" @@ -1201,10 +1205,36 @@ static struct platform_driver fiq_debugger_driver = { }, }; +#if defined(CONFIG_FIQ_DEBUGGER_UART_OVERLAY) +int fiq_debugger_uart_overlay(void) +{ + struct device_node *onp = of_find_node_by_path("/uart_overlay@0"); + int ret; + + if (!onp) { + pr_err("serial_debugger: uart overlay not found\n"); + return -ENODEV; + } + + ret = of_overlay_create(onp); + if (ret < 0) { + pr_err("serial_debugger: fail to create overlay: %d\n", ret); + of_node_put(onp); + return ret; + } + + pr_info("serial_debugger: uart overlay applied\n"); + return 0; +} +#endif + static int __init fiq_debugger_init(void) { #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) fiq_debugger_tty_init(); +#endif +#if defined(CONFIG_FIQ_DEBUGGER_UART_OVERLAY) + fiq_debugger_uart_overlay(); #endif return platform_driver_register(&fiq_debugger_driver); } From e40360e32afc3f3efb62bd249e5f3d36b01e85e3 Mon Sep 17 00:00:00 2001 From: Lianwei Wang Date: Fri, 6 May 2016 00:17:57 -0700 Subject: [PATCH 035/656] Revert "drivers: power: Add watchdog timer to catch drivers which lockup during suspend." This reverts commit ad86cc8ad63229eeeba0628e99f2f59df55a25fd. Commit 70fea60d888d ("PM / Sleep: Detect device suspend/resume lockup...") added a suspend/resume watchdog timer to catch the lockup. Let's revert the duplicate one. Change-Id: Ic72a87432e27844155467817600adc6cf0c2209c Signed-off-by: Lianwei Wang Signed-off-by: Amit Pundir --- drivers/base/power/main.c | 43 --------------------------------------- 1 file changed, 43 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 6ed8b9326629..2f7a6e1b568c 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -62,12 +62,6 @@ struct suspend_stats suspend_stats; static DEFINE_MUTEX(dpm_list_mtx); static pm_message_t pm_transition; -static void dpm_drv_timeout(unsigned long data); -struct dpm_drv_wd_data { - struct device *dev; - struct task_struct *tsk; -}; - static int async_error; static char *pm_verb(int event) @@ -838,30 +832,6 @@ static void async_resume(void *data, async_cookie_t cookie) put_device(dev); } -/** - * dpm_drv_timeout - Driver suspend / resume watchdog handler - * @data: struct device which timed out - * - * Called when a driver has timed out suspending or resuming. - * There's not much we can do here to recover so - * BUG() out for a crash-dump - * - */ -static void dpm_drv_timeout(unsigned long data) -{ - struct dpm_drv_wd_data *wd_data = (void *)data; - struct device *dev = wd_data->dev; - struct task_struct *tsk = wd_data->tsk; - - printk(KERN_EMERG "**** DPM device timeout: %s (%s)\n", dev_name(dev), - (dev->driver ? dev->driver->name : "no driver")); - - printk(KERN_EMERG "dpm suspend stack:\n"); - show_stack(tsk, NULL); - - BUG(); -} - /** * dpm_resume - Execute "resume" callbacks for non-sysdev devices. * @state: PM transition of the system being carried out. @@ -1380,8 +1350,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) pm_callback_t callback = NULL; char *info = NULL; int error = 0; - struct timer_list timer; - struct dpm_drv_wd_data data; char suspend_abort[MAX_SUSPEND_ABORT_LEN]; DECLARE_DPM_WATCHDOG_ON_STACK(wd); @@ -1412,14 +1380,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) if (dev->power.syscore) goto Complete; - - data.dev = dev; - data.tsk = current; - init_timer_on_stack(&timer); - timer.expires = jiffies + HZ * 12; - timer.function = dpm_drv_timeout; - timer.data = (unsigned long)&data; - add_timer(&timer); if (dev->power.direct_complete) { if (pm_runtime_status_suspended(dev)) { @@ -1500,9 +1460,6 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) device_unlock(dev); dpm_watchdog_clear(&wd); - del_timer_sync(&timer); - destroy_timer_on_stack(&timer); - Complete: complete_all(&dev->power.completion); if (error) From be1bb4a76a5a55d354823fa7c6aeae63ac69e245 Mon Sep 17 00:00:00 2001 From: Jimmy Perchet Date: Mon, 9 May 2016 10:32:04 -0700 Subject: [PATCH 036/656] FROMLIST: wlcore: Disable filtering in AP role When you configure (set it up) a STA interface, the driver install a multicast filter. This is normal behavior, when one application subscribe to multicast address the filter is updated. When Access Point interface is configured, there is no filter installation and the "filter update" path is disabled in the driver. The problem happens when you switch an interface from STA type to AP type. The filter is installed but there are no means to update it. Change-Id: Ied22323af831575303abd548574918baa9852dd0 Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/ti/wlcore/init.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index e92f2639af2c..9fd3c6af0a61 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -549,6 +549,11 @@ static int wl12xx_init_ap_role(struct wl1271 *wl, struct wl12xx_vif *wlvif) { int ret; + /* Disable filtering */ + ret = wl1271_acx_group_address_tbl(wl, wlvif, false, NULL, 0); + if (ret < 0) + return ret; + ret = wl1271_acx_ap_max_tx_retry(wl, wlvif); if (ret < 0) return ret; From 1a6976beade0acd4f56514dac72891cb23bafec3 Mon Sep 17 00:00:00 2001 From: Janis Danisevskis Date: Thu, 14 Apr 2016 13:57:03 +0100 Subject: [PATCH 037/656] UPSTREAM: procfs: fixes pthread cross-thread naming if !PR_DUMPABLE The PR_DUMPABLE flag causes the pid related paths of the proc file system to be owned by ROOT. The implementation of pthread_set/getname_np however needs access to /proc//task//comm. If PR_DUMPABLE is false this implementation is locked out. This patch installs a special permission function for the file "comm" that grants read and write access to all threads of the same group regardless of the ownership of the inode. For all other threads the function falls back to the generic inode permission check. Signed-off-by: Janis Danisevskis --- fs/proc/base.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index 23d60b1995fb..2df808a81035 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3078,6 +3078,44 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) return 0; } +/* + * proc_tid_comm_permission is a special permission function exclusively + * used for the node /proc//task//comm. + * It bypasses generic permission checks in the case where a task of the same + * task group attempts to access the node. + * The rational behind this is that glibc and bionic access this node for + * cross thread naming (pthread_set/getname_np(!self)). However, if + * PR_SET_DUMPABLE gets set to 0 this node among others becomes uid=0 gid=0, + * which locks out the cross thread naming implementation. + * This function makes sure that the node is always accessible for members of + * same thread group. + */ +static int proc_tid_comm_permission(struct inode *inode, int mask) +{ + bool is_same_tgroup; + struct task_struct *task; + + task = get_proc_task(inode); + if (!task) + return -ESRCH; + is_same_tgroup = same_thread_group(current, task); + put_task_struct(task); + + if (likely(is_same_tgroup && !(mask & MAY_EXEC))) { + /* This file (/proc//task//comm) can always be + * read or written by the members of the corresponding + * thread group. + */ + return 0; + } + + return generic_permission(inode, mask); +} + +static const struct inode_operations proc_tid_comm_inode_operations = { + .permission = proc_tid_comm_permission, +}; + /* * Tasks */ @@ -3096,7 +3134,9 @@ static const struct pid_entry tid_base_stuff[] = { #ifdef CONFIG_SCHED_DEBUG REG("sched", S_IRUGO|S_IWUSR, proc_pid_sched_operations), #endif - REG("comm", S_IRUGO|S_IWUSR, proc_pid_set_comm_operations), + NOD("comm", S_IFREG|S_IRUGO|S_IWUSR, + &proc_tid_comm_inode_operations, + &proc_pid_set_comm_operations, {}), #ifdef CONFIG_HAVE_ARCH_TRACEHOOK ONE("syscall", S_IRUSR, proc_pid_syscall), #endif From f73ca028a0a4cc307597efc6ef2c910dc2d20639 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 11 May 2016 11:01:02 -0700 Subject: [PATCH 038/656] fiq_debugger: Add fiq_debugger.disable option This change allows to use same kernel image with different console options for uart and fiq_debugger. If fiq_debugger.disable will be set to 1/y/Y, fiq_debugger will not be initialized. Change-Id: I71fda54f5f863d13b1437b1f909e52dd375d002d Signed-off-by: Dmitry Shmidt --- drivers/staging/android/fiq_debugger/fiq_debugger.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/android/fiq_debugger/fiq_debugger.c b/drivers/staging/android/fiq_debugger/fiq_debugger.c index 0c558331a3d2..b132cff14f01 100644 --- a/drivers/staging/android/fiq_debugger/fiq_debugger.c +++ b/drivers/staging/android/fiq_debugger/fiq_debugger.c @@ -123,11 +123,13 @@ static bool initial_console_enable; #endif static bool fiq_kgdb_enable; +static bool fiq_debugger_disable; module_param_named(no_sleep, initial_no_sleep, bool, 0644); module_param_named(debug_enable, initial_debug_enable, bool, 0644); module_param_named(console_enable, initial_console_enable, bool, 0644); module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644); +module_param_named(disable, fiq_debugger_disable, bool, 0644); #ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON static inline @@ -1230,6 +1232,10 @@ int fiq_debugger_uart_overlay(void) static int __init fiq_debugger_init(void) { + if (fiq_debugger_disable) { + pr_err("serial_debugger: disabled\n"); + return -ENODEV; + } #if defined(CONFIG_FIQ_DEBUGGER_CONSOLE) fiq_debugger_tty_init(); #endif From 4158b3431f473aad101da1100a9b241ff8b3cc74 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 12 May 2016 11:17:52 -0700 Subject: [PATCH 039/656] xt_qtaguid: Fix panic caused by processing non-full socket. In an issue very similar to 4e461c777e3 (xt_qtaguid: Fix panic caused by synack processing), we were seeing panics on occasion in testing. In this case, it was the same issue, but caused by a different call path, as the sk being returned from qtaguid_find_sk() was not a full socket. Resulting in the sk->sk_socket deref to fail. This patch adds an extra check to ensure the sk being retuned is a full socket, and if not it returns NULL. Reported-by: Milosz Wasilewski Signed-off-by: John Stultz --- net/netfilter/xt_qtaguid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index 822dc3c3bce1..e2e7d54f9bb1 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -1606,7 +1606,7 @@ static struct sock *qtaguid_find_sk(const struct sk_buff *skb, * When in TCP_TIME_WAIT the sk is not a "struct sock" but * "struct inet_timewait_sock" which is missing fields. */ - if (sk->sk_state == TCP_TIME_WAIT) { + if (!sk_fullsock(sk) || sk->sk_state == TCP_TIME_WAIT) { sock_gen_put(sk); sk = NULL; } From b42fb9a7101001d14f996fe2e671f3e7eddded3b Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 16 May 2016 13:17:28 +0530 Subject: [PATCH 040/656] Revert "cpufreq: interactive: build fixes for 4.4" This reverts commit bc68f6c4efbd4ddbb15817203f18b7941d9ffd52. This build fix broke the Interactive Gov at runtime with duplicate sysfs entry warnings at boot time. We no longer need to this create/destroy cpufreq sysfs entry at run time on need basis thanks to upstream commit 8eec1020f0c0 (cpufreq: create cpu/cpufreq at boot time) which creates it at boot time. Hence drop this build fix. Signed-off-by: Amit Pundir --- drivers/cpufreq/cpufreq_interactive.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index bd83be39f17b..7f846b06e024 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -116,28 +116,6 @@ struct cpufreq_interactive_tunables { bool io_is_busy; }; -/* - * HACK: FIXME: Bring back cpufreq_{get,put}_global_kobject() - * definition removed by upstream commit 8eec1020f0c0 "cpufreq: - * create cpu/cpufreq at boot time" to fix build failures. - */ -static int cpufreq_global_kobject_usage; - -int cpufreq_get_global_kobject(void) -{ - if (!cpufreq_global_kobject_usage++) - return kobject_add(cpufreq_global_kobject, - &cpu_subsys.dev_root->kobj, "%s", "cpufreq"); - - return 0; -} - -void cpufreq_put_global_kobject(void) -{ - if (!--cpufreq_global_kobject_usage) - kobject_del(cpufreq_global_kobject); -} - /* For cases where we have single governor instance for system */ static struct cpufreq_interactive_tunables *common_tunables; From de5723c8a64e7f7a69662b75d4a789a6e5307dd4 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Mon, 16 May 2016 13:25:35 +0530 Subject: [PATCH 041/656] cpufreq: interactive: drop cpufreq_{get,put}_global_kobject func calls Upstream commit 8eec1020f0c0 (cpufreq: create cpu/cpufreq at boot time) make sure that cpufreq sysfs entry get created at boot time, and there is no need to create/destroy it on need basis anymore. So drop deprecated cpufreq_{get,put}_global_kobject function calls which otherwise result in following compilation errors: drivers/cpufreq/cpufreq_interactive.c: In function 'cpufreq_governor_interactive': drivers/cpufreq/cpufreq_interactive.c:1187:4: error: implicit declaration of function 'cpufreq_get_global_kobject' [-Werror=implicit-function-declaration] WARN_ON(cpufreq_get_global_kobject()); ^ drivers/cpufreq/cpufreq_interactive.c:1197:5: error: implicit declaration of function 'cpufreq_put_global_kobject'[-Werror=implicit-function-declaration] cpufreq_put_global_kobject(); ^ Signed-off-by: Amit Pundir --- drivers/cpufreq/cpufreq_interactive.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c index 7f846b06e024..f2929e628820 100644 --- a/drivers/cpufreq/cpufreq_interactive.c +++ b/drivers/cpufreq/cpufreq_interactive.c @@ -1184,7 +1184,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, policy->governor_data = tunables; if (!have_governor_per_policy()) { common_tunables = tunables; - WARN_ON(cpufreq_get_global_kobject()); } rc = sysfs_create_group(get_governor_parent_kobj(policy), @@ -1194,7 +1193,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, policy->governor_data = NULL; if (!have_governor_per_policy()) { common_tunables = NULL; - cpufreq_put_global_kobject(); } return rc; } @@ -1218,9 +1216,6 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy, sysfs_remove_group(get_governor_parent_kobj(policy), get_sysfs_attr()); - if (!have_governor_per_policy()) - cpufreq_put_global_kobject(); - kfree(tunables); common_tunables = NULL; } From aacf3ae2a6caae8dfc7d4a41a9f537674613831c Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 17 May 2016 16:06:17 +0530 Subject: [PATCH 042/656] Revert "drivers: power: use 'current' instead of 'get_current()'" This reverts commit e1b5d103894d097fb630aebc3c1fdaf257f7c9bb. This patch fixed the aosp commit ad86cc8ad632 (drivers: power: Add watchdog timer to catch drivers which lockup during suspend.), which we dropped in Change Id Ic72a87432e27844155467817600adc6cf0c2209c, so we no longer need this fix. A part of this patch is already reverted in above mentioned Change Id. Signed-off-by: Amit Pundir --- drivers/base/power/main.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 2f7a6e1b568c..5ea529165362 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -35,8 +35,6 @@ #include #include -#include - #include "../base.h" #include "power.h" From 63a2b92307eb8fff4451ae58ca937dd6aa899469 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Mon, 4 Apr 2016 14:15:23 -0400 Subject: [PATCH 043/656] =?UTF-8?q?UPSTREAM:=20mac80211:=20fix=20"warning:?= =?UTF-8?q?=20=E2=80=98target=5Fmetric=E2=80=99=20may=20be=20used=20uninit?= =?UTF-8?q?ialized"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (This cherry-picks b4201cc4fc6e1c57d6d306b1f787865043d60129 upstream) This fixes: net/mac80211/mesh_hwmp.c:603:26: warning: ‘target_metric’ may be used uninitialized in this function target_metric is only consumed when reply = true so no bug exists here, but not all versions of gcc realize it. Initialize to 0 to remove the warning. Change-Id: I13923fda9d314f48196c29e4354133dfe01f5abd Signed-off-by: Jeff Mahoney Signed-off-by: Johannes Berg [jstultz: Cherry-picked to android-4.4] Signed-off-by: John Stultz --- net/mac80211/mesh_hwmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c index c6be0b4f4058..b6dc2d7cd650 100644 --- a/net/mac80211/mesh_hwmp.c +++ b/net/mac80211/mesh_hwmp.c @@ -530,7 +530,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata, const u8 *target_addr, *orig_addr; const u8 *da; u8 target_flags, ttl, flags; - u32 orig_sn, target_sn, lifetime, target_metric; + u32 orig_sn, target_sn, lifetime, target_metric = 0; bool reply = false; bool forward = true; bool root_is_gate; From e1ce8c2a299d41c4c96be785c50427635e0a9c7f Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Fri, 25 Mar 2016 10:02:41 -0400 Subject: [PATCH 044/656] Btrfs: don't use src fd for printk commit c79b4713304f812d3d6c95826fc3e5fc2c0b0c14 upstream. The fd we pass in may not be on a btrfs file system, so don't try to do BTRFS_I() on it. Thanks, Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba Cc: Jeff Mahoney Signed-off-by: Greg Kroah-Hartman --- fs/btrfs/ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f07d01bc4875..bfcd87ee8ff5 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1648,7 +1648,7 @@ static noinline int btrfs_ioctl_snap_create_transid(struct file *file, src_inode = file_inode(src.file); if (src_inode->i_sb != file_inode(file)->i_sb) { - btrfs_info(BTRFS_I(src_inode)->root->fs_info, + btrfs_info(BTRFS_I(file_inode(file))->root->fs_info, "Snapshot src from another FS"); ret = -EXDEV; } else if (!inode_owner_or_capable(src_inode)) { From a7a9e0efc81b7b914bc6818678af05b8d5c616be Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 10 May 2016 16:18:32 +0300 Subject: [PATCH 045/656] perf/x86/intel/pt: Generate PMI in the STOP region as well commit ab92b232ae05c382c3df0e3d6a5c6d16b639ac8c upstream. Currently, the PT driver always sets the PMI bit one region (page) before the STOP region so that we can wake up the consumer before we run out of room in the buffer and have to disable the event. However, we also need an interrupt in the last output region, so that we actually get to disable the event (if no more room from new data is available at that point), otherwise hardware just quietly refuses to start, but the event is scheduled in and we end up losing trace data till the event gets removed. For a cpu-wide event it is even worse since there may not be any re-scheduling at all and no chance for the ring buffer code to notice that its buffer is filled up and the event needs to be disabled (so that the consumer can re-enable it when it finishes reading the data out). In other words, all the trace data will be lost after the buffer gets filled up. This patch makes PT also generate a PMI when the last output region is full. Reported-by: Markus Metzger Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1462886313-13660-2-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/cpu/perf_event_intel_pt.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/x86/kernel/cpu/perf_event_intel_pt.c b/arch/x86/kernel/cpu/perf_event_intel_pt.c index 868e1194337f..49e35d003b74 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_pt.c +++ b/arch/x86/kernel/cpu/perf_event_intel_pt.c @@ -694,6 +694,7 @@ static int pt_buffer_reset_markers(struct pt_buffer *buf, /* clear STOP and INT from current entry */ buf->topa_index[buf->stop_pos]->stop = 0; + buf->topa_index[buf->stop_pos]->intr = 0; buf->topa_index[buf->intr_pos]->intr = 0; /* how many pages till the STOP marker */ @@ -718,6 +719,7 @@ static int pt_buffer_reset_markers(struct pt_buffer *buf, buf->intr_pos = idx; buf->topa_index[buf->stop_pos]->stop = 1; + buf->topa_index[buf->stop_pos]->intr = 1; buf->topa_index[buf->intr_pos]->intr = 1; return 0; From c5174678e2e5bd691da58a116fbc123c6fbc7b2c Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Tue, 26 Apr 2016 11:36:53 +0200 Subject: [PATCH 046/656] perf/core: Fix perf_event_open() vs. execve() race commit 79c9ce57eb2d5f1497546a3946b4ae21b6fdc438 upstream. Jann reported that the ptrace_may_access() check in find_lively_task_by_vpid() is racy against exec(). Specifically: perf_event_open() execve() ptrace_may_access() commit_creds() ... if (get_dumpable() != SUID_DUMP_USER) perf_event_exit_task(); perf_install_in_context() would result in installing a counter across the creds boundary. Fix this by wrapping lots of perf_event_open() in cred_guard_mutex. This should be fine as perf_event_exit_task() is already called with cred_guard_mutex held, so all perf locks already nest inside it. Reported-by: Jann Horn Signed-off-by: Peter Zijlstra (Intel) Cc: Alexander Shishkin Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Signed-off-by: Ingo Molnar Signed-off-by: He Kuang Signed-off-by: Greg Kroah-Hartman --- kernel/events/core.c | 52 ++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 1e889a078dbc..95e47d2f2c67 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -946,6 +946,7 @@ static void put_ctx(struct perf_event_context *ctx) * function. * * Lock order: + * cred_guard_mutex * task_struct::perf_event_mutex * perf_event_context::mutex * perf_event_context::lock @@ -3418,7 +3419,6 @@ static struct task_struct * find_lively_task_by_vpid(pid_t vpid) { struct task_struct *task; - int err; rcu_read_lock(); if (!vpid) @@ -3432,16 +3432,7 @@ find_lively_task_by_vpid(pid_t vpid) if (!task) return ERR_PTR(-ESRCH); - /* Reuse ptrace permission checks for now. */ - err = -EACCES; - if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) - goto errout; - return task; -errout: - put_task_struct(task); - return ERR_PTR(err); - } /* @@ -8328,6 +8319,24 @@ SYSCALL_DEFINE5(perf_event_open, get_online_cpus(); + if (task) { + err = mutex_lock_interruptible(&task->signal->cred_guard_mutex); + if (err) + goto err_cpus; + + /* + * Reuse ptrace permission checks for now. + * + * We must hold cred_guard_mutex across this and any potential + * perf_install_in_context() call for this new event to + * serialize against exec() altering our credentials (and the + * perf_event_exit_task() that could imply). + */ + err = -EACCES; + if (!ptrace_may_access(task, PTRACE_MODE_READ_REALCREDS)) + goto err_cred; + } + if (flags & PERF_FLAG_PID_CGROUP) cgroup_fd = pid; @@ -8335,7 +8344,7 @@ SYSCALL_DEFINE5(perf_event_open, NULL, NULL, cgroup_fd); if (IS_ERR(event)) { err = PTR_ERR(event); - goto err_cpus; + goto err_cred; } if (is_sampling_event(event)) { @@ -8394,11 +8403,6 @@ SYSCALL_DEFINE5(perf_event_open, goto err_context; } - if (task) { - put_task_struct(task); - task = NULL; - } - /* * Look up the group leader (we will attach this event to it): */ @@ -8486,6 +8490,11 @@ SYSCALL_DEFINE5(perf_event_open, WARN_ON_ONCE(ctx->parent_ctx); + /* + * This is the point on no return; we cannot fail hereafter. This is + * where we start modifying current state. + */ + if (move_group) { /* * See perf_event_ctx_lock() for comments on the details @@ -8555,6 +8564,11 @@ SYSCALL_DEFINE5(perf_event_open, mutex_unlock(&gctx->mutex); mutex_unlock(&ctx->mutex); + if (task) { + mutex_unlock(&task->signal->cred_guard_mutex); + put_task_struct(task); + } + put_online_cpus(); event->owner = current; @@ -8589,6 +8603,9 @@ err_alloc: */ if (!event_file) free_event(event); +err_cred: + if (task) + mutex_unlock(&task->signal->cred_guard_mutex); err_cpus: put_online_cpus(); err_task: @@ -8868,6 +8885,9 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn) /* * When a child task exits, feed back event values to parent events. + * + * Can be called with cred_guard_mutex held when called from + * install_exec_creds(). */ void perf_event_exit_task(struct task_struct *child) { From eef1195bb671cd15d30df683bd0aca373943719e Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 18 Nov 2015 17:38:49 -0300 Subject: [PATCH 047/656] perf test: Fix build of BPF and LLVM on older glibc libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 916d4092a1d2d7bb50630497be71ee4c4c2807fa upstream. $ rpm -q glibc glibc-2.12-1.166.el6_7.1.x86_64 CC /tmp/build/perf/tests/llvm.o cc1: warnings being treated as errors tests/llvm.c: In function ‘test_llvm__fetch_bpf_obj’: tests/llvm.c:53: error: declaration of ‘index’ shadows a global declaration /usr/include/string.h:489: error: shadowed declaration is here CC /tmp/build/perf/tests/bpf.o cc1: warnings being treated as errors tests/bpf.c: In function ‘__test__bpf’: tests/bpf.c:149: error: declaration of ‘index’ shadows a global declaration /usr/include/string.h:489: error: shadowed declaration is here Cc: He Kuang Cc: Jiri Olsa Cc: Namhyung Kim Cc: pi3orama@163.com Cc: Wang Nan Cc: Zefan Li Fixes: b31de018a628 ("perf test: Enhance the LLVM test: update basic BPF test program") Fixes: ba1fae431e74 ("perf test: Add 'perf test BPF'") Link: http://lkml.kernel.org/n/tip-akpo4r750oya2phxoh9e3447@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo Cc: Nikolay Borisov Signed-off-by: Greg Kroah-Hartman --- tools/perf/tests/bpf.c | 14 +++++++------- tools/perf/tests/llvm.c | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c index ec16f7812c8b..6ebfdee3e2c6 100644 --- a/tools/perf/tests/bpf.c +++ b/tools/perf/tests/bpf.c @@ -146,7 +146,7 @@ prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name) return obj; } -static int __test__bpf(int index) +static int __test__bpf(int idx) { int ret; void *obj_buf; @@ -154,27 +154,27 @@ static int __test__bpf(int index) struct bpf_object *obj; ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, - bpf_testcase_table[index].prog_id, + bpf_testcase_table[idx].prog_id, true); if (ret != TEST_OK || !obj_buf || !obj_buf_sz) { pr_debug("Unable to get BPF object, %s\n", - bpf_testcase_table[index].msg_compile_fail); - if (index == 0) + bpf_testcase_table[idx].msg_compile_fail); + if (idx == 0) return TEST_SKIP; else return TEST_FAIL; } obj = prepare_bpf(obj_buf, obj_buf_sz, - bpf_testcase_table[index].name); + bpf_testcase_table[idx].name); if (!obj) { ret = TEST_FAIL; goto out; } ret = do_test(obj, - bpf_testcase_table[index].target_func, - bpf_testcase_table[index].expect_result); + bpf_testcase_table[idx].target_func, + bpf_testcase_table[idx].expect_result); out: bpf__clear(); return ret; diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c index bc4cf507cde5..366e38ba8b49 100644 --- a/tools/perf/tests/llvm.c +++ b/tools/perf/tests/llvm.c @@ -50,7 +50,7 @@ static struct { int test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz, - enum test_llvm__testcase index, + enum test_llvm__testcase idx, bool force) { const char *source; @@ -59,11 +59,11 @@ test_llvm__fetch_bpf_obj(void **p_obj_buf, char *tmpl_new = NULL, *clang_opt_new = NULL; int err, old_verbose, ret = TEST_FAIL; - if (index >= __LLVM_TESTCASE_MAX) + if (idx >= __LLVM_TESTCASE_MAX) return TEST_FAIL; - source = bpf_source_table[index].source; - desc = bpf_source_table[index].desc; + source = bpf_source_table[idx].source; + desc = bpf_source_table[idx].desc; perf_config(perf_config_cb, NULL); From fa5613b1f39ec020671705453eeceaaf57459e49 Mon Sep 17 00:00:00 2001 From: Eryu Guan Date: Sun, 21 Feb 2016 18:38:44 -0500 Subject: [PATCH 048/656] ext4: iterate over buffer heads correctly in move_extent_per_page() commit 6ffe77bad545f4a7c8edd2a4ee797ccfcd894ab4 upstream. In commit bcff24887d00 ("ext4: don't read blocks from disk after extents being swapped") bh is not updated correctly in the for loop and wrong data has been written to disk. generic/324 catches this on sub-page block size ext4. Fixes: bcff24887d00 ("ext4: don't read blocks from disk after extentsbeing swapped") Signed-off-by: Eryu Guan Signed-off-by: Theodore Ts'o Signed-off-by: Greg Kroah-Hartman --- fs/ext4/move_extent.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 9bdbf98240a0..796ff0eafd3c 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c @@ -390,6 +390,7 @@ data_copy: *err = ext4_get_block(orig_inode, orig_blk_offset + i, bh, 0); if (*err < 0) break; + bh = bh->b_this_page; } if (!*err) *err = block_commit_write(pagep[0], from, from + replaced_size); From f07f749170d30f6159fd29b3853380769097b37b Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Thu, 5 May 2016 10:43:59 +0100 Subject: [PATCH 049/656] arm64: Fix typo in the pmdp_huge_get_and_clear() definition commit 911f56eeb87ee378f5e215469268a7a2f68a5a8a upstream. With hardware AF/DBM support, pmd modifications (transparent huge pages) should be performed atomically using load/store exclusive. The initial patches defined the get-and-clear function and __HAVE_ARCH_* macro without the "huge" word, leaving the pmdp_huge_get_and_clear() to the default, non-atomic implementation. Fixes: 2f4b829c625e ("arm64: Add support for hardware updates of the access and dirty pte bits") Reviewed-by: Will Deacon Signed-off-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/pgtable.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index c63868ae9a4a..ed288a4513f9 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -590,9 +590,9 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm, } #ifdef CONFIG_TRANSPARENT_HUGEPAGE -#define __HAVE_ARCH_PMDP_GET_AND_CLEAR -static inline pmd_t pmdp_get_and_clear(struct mm_struct *mm, - unsigned long address, pmd_t *pmdp) +#define __HAVE_ARCH_PMDP_HUGE_GET_AND_CLEAR +static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm, + unsigned long address, pmd_t *pmdp) { return pte_pmd(ptep_get_and_clear(mm, address, (pte_t *)pmdp)); } From 737b0679fd93f737f066ed53a86dc9ed0098d40a Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Thu, 5 May 2016 10:44:02 +0100 Subject: [PATCH 050/656] arm64: Ensure pmd_present() returns false after pmd_mknotpresent() commit 5bb1cc0ff9a6b68871970737e6c4c16919928d8b upstream. Currently, pmd_present() only checks for a non-zero value, returning true even after pmd_mknotpresent() (which only clears the type bits). This patch converts pmd_present() to using pte_present(), similar to the other pmd_*() checks. As a side effect, it will return true for PROT_NONE mappings, though they are not yet used by the kernel with transparent huge pages. For consistency, also change pmd_mknotpresent() to only clear the PMD_SECT_VALID bit, even though the PMD_TABLE_BIT is already 0 for block mappings (no functional change). The unused PMD_SECT_PROT_NONE definition is removed as transparent huge pages use the pte page prot values. Fixes: 9c7e535fcc17 ("arm64: mm: Route pmd thp functions through pte equivalents") Reviewed-by: Will Deacon Signed-off-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/pgtable-hwdef.h | 1 - arch/arm64/include/asm/pgtable.h | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h index d6739e836f7b..b9da9545b442 100644 --- a/arch/arm64/include/asm/pgtable-hwdef.h +++ b/arch/arm64/include/asm/pgtable-hwdef.h @@ -117,7 +117,6 @@ * Section */ #define PMD_SECT_VALID (_AT(pmdval_t, 1) << 0) -#define PMD_SECT_PROT_NONE (_AT(pmdval_t, 1) << 58) #define PMD_SECT_USER (_AT(pmdval_t, 1) << 6) /* AP[1] */ #define PMD_SECT_RDONLY (_AT(pmdval_t, 1) << 7) /* AP[2] */ #define PMD_SECT_S (_AT(pmdval_t, 3) << 8) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index ed288a4513f9..5b7d276d4c3d 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -347,6 +347,7 @@ void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address, #endif /* CONFIG_HAVE_RCU_TABLE_FREE */ #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ +#define pmd_present(pmd) pte_present(pmd_pte(pmd)) #define pmd_dirty(pmd) pte_dirty(pmd_pte(pmd)) #define pmd_young(pmd) pte_young(pmd_pte(pmd)) #define pmd_wrprotect(pmd) pte_pmd(pte_wrprotect(pmd_pte(pmd))) @@ -355,7 +356,7 @@ void pmdp_splitting_flush(struct vm_area_struct *vma, unsigned long address, #define pmd_mkwrite(pmd) pte_pmd(pte_mkwrite(pmd_pte(pmd))) #define pmd_mkdirty(pmd) pte_pmd(pte_mkdirty(pmd_pte(pmd))) #define pmd_mkyoung(pmd) pte_pmd(pte_mkyoung(pmd_pte(pmd))) -#define pmd_mknotpresent(pmd) (__pmd(pmd_val(pmd) & ~PMD_TYPE_MASK)) +#define pmd_mknotpresent(pmd) (__pmd(pmd_val(pmd) & ~PMD_SECT_VALID)) #define __HAVE_ARCH_PMD_WRITE #define pmd_write(pmd) pte_write(pmd_pte(pmd)) @@ -394,7 +395,6 @@ extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, unsigned long size, pgprot_t vma_prot); #define pmd_none(pmd) (!pmd_val(pmd)) -#define pmd_present(pmd) (pmd_val(pmd)) #define pmd_bad(pmd) (!(pmd_val(pmd) & 2)) From 098942bcf4b1d0573119150be8f68459d36ebde8 Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Wed, 13 Apr 2016 16:01:22 +0100 Subject: [PATCH 051/656] arm64: Implement ptep_set_access_flags() for hardware AF/DBM commit 66dbd6e61a526ae7d11a208238ae2c17e5cacb6b upstream. When hardware updates of the access and dirty states are enabled, the default ptep_set_access_flags() implementation based on calling set_pte_at() directly is potentially racy. This triggers the "racy dirty state clearing" warning in set_pte_at() because an existing writable PTE is overridden with a clean entry. There are two main scenarios for this situation: 1. The CPU getting an access fault does not support hardware updates of the access/dirty flags. However, a different agent in the system (e.g. SMMU) can do this, therefore overriding a writable entry with a clean one could potentially lose the automatically updated dirty status 2. A more complex situation is possible when all CPUs support hardware AF/DBM: a) Initial state: shareable + writable vma and pte_none(pte) b) Read fault taken by two threads of the same process on different CPUs c) CPU0 takes the mmap_sem and proceeds to handling the fault. It eventually reaches do_set_pte() which sets a writable + clean pte. CPU0 releases the mmap_sem d) CPU1 acquires the mmap_sem and proceeds to handle_pte_fault(). The pte entry it reads is present, writable and clean and it continues to pte_mkyoung() e) CPU1 calls ptep_set_access_flags() If between (d) and (e) the hardware (another CPU) updates the dirty state (clears PTE_RDONLY), CPU1 will override the PTR_RDONLY bit marking the entry clean again. This patch implements an arm64-specific ptep_set_access_flags() function to perform an atomic update of the PTE flags. Fixes: 2f4b829c625e ("arm64: Add support for hardware updates of the access and dirty pte bits") Signed-off-by: Catalin Marinas Reported-by: Ming Lei Tested-by: Julien Grall Cc: Will Deacon [will: reworded comment] Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/pgtable.h | 5 ++++ arch/arm64/mm/fault.c | 50 ++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 5b7d276d4c3d..bb29b0f56cbc 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -538,6 +538,11 @@ static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot) } #ifdef CONFIG_ARM64_HW_AFDBM +#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS +extern int ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep, + pte_t entry, int dirty); + /* * Atomic pte/pmd modifications. */ diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c index 92ddac1e8ca2..40f5522245a2 100644 --- a/arch/arm64/mm/fault.c +++ b/arch/arm64/mm/fault.c @@ -81,6 +81,56 @@ void show_pte(struct mm_struct *mm, unsigned long addr) printk("\n"); } +#ifdef CONFIG_ARM64_HW_AFDBM +/* + * This function sets the access flags (dirty, accessed), as well as write + * permission, and only to a more permissive setting. + * + * It needs to cope with hardware update of the accessed/dirty state by other + * agents in the system and can safely skip the __sync_icache_dcache() call as, + * like set_pte_at(), the PTE is never changed from no-exec to exec here. + * + * Returns whether or not the PTE actually changed. + */ +int ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep, + pte_t entry, int dirty) +{ + pteval_t old_pteval; + unsigned int tmp; + + if (pte_same(*ptep, entry)) + return 0; + + /* only preserve the access flags and write permission */ + pte_val(entry) &= PTE_AF | PTE_WRITE | PTE_DIRTY; + + /* + * PTE_RDONLY is cleared by default in the asm below, so set it in + * back if necessary (read-only or clean PTE). + */ + if (!pte_write(entry) || !dirty) + pte_val(entry) |= PTE_RDONLY; + + /* + * Setting the flags must be done atomically to avoid racing with the + * hardware update of the access/dirty state. + */ + asm volatile("// ptep_set_access_flags\n" + " prfm pstl1strm, %2\n" + "1: ldxr %0, %2\n" + " and %0, %0, %3 // clear PTE_RDONLY\n" + " orr %0, %0, %4 // set flags\n" + " stxr %w1, %0, %2\n" + " cbnz %w1, 1b\n" + : "=&r" (old_pteval), "=&r" (tmp), "+Q" (pte_val(*ptep)) + : "L" (~PTE_RDONLY), "r" (pte_val(entry))); + + flush_tlb_fix_spurious_fault(vma, address); + return 1; +} +#endif + /* * The kernel tried to access some page that wasn't present. */ From 7e1c1db08e0d4c110a253939df0bd5a0c4ddbc1f Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Thu, 5 May 2016 10:44:00 +0100 Subject: [PATCH 052/656] arm64: Implement pmdp_set_access_flags() for hardware AF/DBM commit 282aa7051b0169991b34716f0f22d9c2f59c46c4 upstream. The update to the accessed or dirty states for block mappings must be done atomically on hardware with support for automatic AF/DBM. The ptep_set_access_flags() function has been fixed as part of commit 66dbd6e61a52 ("arm64: Implement ptep_set_access_flags() for hardware AF/DBM"). This patch brings pmdp_set_access_flags() in line with the pte counterpart. Fixes: 2f4b829c625e ("arm64: Add support for hardware updates of the access and dirty pte bits") Reviewed-by: Will Deacon Signed-off-by: Catalin Marinas Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/include/asm/pgtable.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index bb29b0f56cbc..67c2ad6d33b7 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -543,6 +543,16 @@ extern int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address, pte_t *ptep, pte_t entry, int dirty); +#ifdef CONFIG_TRANSPARENT_HUGEPAGE +#define __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS +static inline int pmdp_set_access_flags(struct vm_area_struct *vma, + unsigned long address, pmd_t *pmdp, + pmd_t entry, int dirty) +{ + return ptep_set_access_flags(vma, address, (pte_t *)pmdp, pmd_pte(entry), dirty); +} +#endif + /* * Atomic pte/pmd modifications. */ From 44f47d94e8424c20b8f6b38bc46514bd3428b048 Mon Sep 17 00:00:00 2001 From: Julien Grall Date: Tue, 10 May 2016 15:40:31 +0100 Subject: [PATCH 053/656] arm64: cpuinfo: Missing NULL terminator in compat_hwcap_str commit f228b494e56d949be8d8ea09d4f973d1979201bf upstream. The loop that browses the array compat_hwcap_str will stop when a NULL is encountered, however NULL is missing at the end of array. This will lead to overrun until a NULL is found somewhere in the following memory. In reality, this works out because the compat_hwcap2_str array tends to follow immediately in memory, and that *is* terminated correctly. Furthermore, the unsigned int compat_elf_hwcap is checked before printing each capability, so we end up doing the right thing because the size of the two arrays is less than 32. Still, this is an obvious mistake and should be fixed. Note for backporting: commit 12d11817eaafa414 ("arm64: Move /proc/cpuinfo handling code") moved this code in v4.4. Prior to that commit, the same change should be made in arch/arm64/kernel/setup.c. Fixes: 44b82b7700d0 "arm64: Fix up /proc/cpuinfo" Signed-off-by: Julien Grall Signed-off-by: Will Deacon Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kernel/cpuinfo.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index 212ae6361d8b..a5f234039616 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -85,7 +85,8 @@ static const char *const compat_hwcap_str[] = { "idivt", "vfpd32", "lpae", - "evtstrm" + "evtstrm", + NULL }; static const char *const compat_hwcap2_str[] = { From ab85830541e8cae251ab3f87409bd13a82554374 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Thu, 28 Apr 2016 16:16:31 +0100 Subject: [PATCH 054/656] arm/arm64: KVM: Enforce Break-Before-Make on Stage-2 page tables commit d4b9e0790aa764c0b01e18d4e8d33e93ba36d51f upstream. The ARM architecture mandates that when changing a page table entry from a valid entry to another valid entry, an invalid entry is first written, TLB invalidated, and only then the new entry being written. The current code doesn't respect this, directly writing the new entry and only then invalidating TLBs. Let's fix it up. Reported-by: Christoffer Dall Signed-off-by: Marc Zyngier Signed-off-by: Christoffer Dall Signed-off-by: Greg Kroah-Hartman --- arch/arm/kvm/mmu.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 61d96a645ff3..12d727fae0a7 100644 --- a/arch/arm/kvm/mmu.c +++ b/arch/arm/kvm/mmu.c @@ -886,11 +886,14 @@ static int stage2_set_pmd_huge(struct kvm *kvm, struct kvm_mmu_memory_cache VM_BUG_ON(pmd_present(*pmd) && pmd_pfn(*pmd) != pmd_pfn(*new_pmd)); old_pmd = *pmd; - kvm_set_pmd(pmd, *new_pmd); - if (pmd_present(old_pmd)) + if (pmd_present(old_pmd)) { + pmd_clear(pmd); kvm_tlb_flush_vmid_ipa(kvm, addr); - else + } else { get_page(virt_to_page(pmd)); + } + + kvm_set_pmd(pmd, *new_pmd); return 0; } @@ -939,12 +942,14 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache, /* Create 2nd stage page table mapping - Level 3 */ old_pte = *pte; - kvm_set_pte(pte, *new_pte); - if (pte_present(old_pte)) + if (pte_present(old_pte)) { + kvm_set_pte(pte, __pte(0)); kvm_tlb_flush_vmid_ipa(kvm, addr); - else + } else { get_page(virt_to_page(pte)); + } + kvm_set_pte(pte, *new_pte); return 0; } From 6ff3df201009776bd6d4316eb76078a977deb566 Mon Sep 17 00:00:00 2001 From: Matt Evans Date: Mon, 16 May 2016 13:54:56 +0100 Subject: [PATCH 055/656] kvm: arm64: Fix EC field in inject_abt64 commit e4fe9e7dc3828bf6a5714eb3c55aef6260d823a2 upstream. The EC field of the constructed ESR is conditionally modified by ORing in ESR_ELx_EC_DABT_LOW for a data abort. However, ESR_ELx_EC_SHIFT is missing from this condition. Signed-off-by: Matt Evans Acked-by: Marc Zyngier Signed-off-by: Christoffer Dall Signed-off-by: Greg Kroah-Hartman --- arch/arm64/kvm/inject_fault.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index 648112e90ed5..3972e65fbd5a 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -130,7 +130,7 @@ static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr esr |= (ESR_ELx_EC_IABT_CUR << ESR_ELx_EC_SHIFT); if (!is_iabt) - esr |= ESR_ELx_EC_DABT_LOW; + esr |= ESR_ELx_EC_DABT_LOW << ESR_ELx_EC_SHIFT; vcpu_sys_reg(vcpu, ESR_EL1) = esr | ESR_ELx_FSC_EXTABT; } From b7d7ba3115bfff550958703edd4bdbd3691aea12 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 12 May 2016 21:20:36 -0500 Subject: [PATCH 056/656] remove directory incorrectly tries to set delete on close on non-empty directories commit 897fba1172d637d344f009d700f7eb8a1fa262f1 upstream. Wrong return code was being returned on SMB3 rmdir of non-empty directory. For SMB3 (unlike for cifs), we attempt to delete a directory by set of delete on close flag on the open. Windows clients set this flag via a set info (SET_FILE_DISPOSITION to set this flag) which properly checks if the directory is empty. With this patch on smb3 mounts we correctly return "DIRECTORY NOT EMPTY" on attempts to remove a non-empty directory. Signed-off-by: Steve French Acked-by: Sachin Prabhu Signed-off-by: Greg Kroah-Hartman --- fs/cifs/smb2glob.h | 1 + fs/cifs/smb2inode.c | 8 ++++++-- fs/cifs/smb2pdu.c | 16 ++++++++++++++++ fs/cifs/smb2proto.h | 2 ++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h index bc0bb9c34f72..0ffa18094335 100644 --- a/fs/cifs/smb2glob.h +++ b/fs/cifs/smb2glob.h @@ -44,6 +44,7 @@ #define SMB2_OP_DELETE 7 #define SMB2_OP_HARDLINK 8 #define SMB2_OP_SET_EOF 9 +#define SMB2_OP_RMDIR 10 /* Used when constructing chained read requests. */ #define CHAINED_REQUEST 1 diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 899bbc86f73e..4f0231e685a9 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -80,6 +80,10 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, * SMB2_open() call. */ break; + case SMB2_OP_RMDIR: + tmprc = SMB2_rmdir(xid, tcon, fid.persistent_fid, + fid.volatile_fid); + break; case SMB2_OP_RENAME: tmprc = SMB2_rename(xid, tcon, fid.persistent_fid, fid.volatile_fid, (__le16 *)data); @@ -191,8 +195,8 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, struct cifs_sb_info *cifs_sb) { return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, - CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE, - NULL, SMB2_OP_DELETE); + CREATE_NOT_FILE, + NULL, SMB2_OP_RMDIR); } int diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 373b5cd1c913..82c5f57382b2 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -2576,6 +2576,22 @@ SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, return rc; } +int +SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid) +{ + __u8 delete_pending = 1; + void *data; + unsigned int size; + + data = &delete_pending; + size = 1; /* sizeof __u8 */ + + return send_set_info(xid, tcon, persistent_fid, volatile_fid, + current->tgid, FILE_DISPOSITION_INFORMATION, 1, &data, + &size); +} + int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, __le16 *target_file) diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index 79dc650c18b2..9bc59f9c12fb 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -140,6 +140,8 @@ extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, __le16 *target_file); +extern int SMB2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, + u64 persistent_fid, u64 volatile_fid); extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid, u64 volatile_fid, __le16 *target_file); From 9ad66e1474a345fe2df897351a82640c39431f4b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 3 May 2016 10:52:30 +0200 Subject: [PATCH 057/656] fs/cifs: correctly to anonymous authentication via NTLMSSP commit cfda35d98298131bf38fbad3ce4cd5ecb3cf18db upstream. See [MS-NLMP] 3.2.5.1.2 Server Receives an AUTHENTICATE_MESSAGE from the Client: ... Set NullSession to FALSE If (AUTHENTICATE_MESSAGE.UserNameLen == 0 AND AUTHENTICATE_MESSAGE.NtChallengeResponse.Length == 0 AND (AUTHENTICATE_MESSAGE.LmChallengeResponse == Z(1) OR AUTHENTICATE_MESSAGE.LmChallengeResponse.Length == 0)) -- Special case: client requested anonymous authentication Set NullSession to TRUE ... Only server which map unknown users to guest will allow access using a non-null NTChallengeResponse. For Samba it's the "map to guest = bad user" option. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11913 Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/sess.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 59727e32ed0f..09b0201d15d0 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -400,19 +400,27 @@ int build_ntlmssp_auth_blob(unsigned char *pbuffer, sec_blob->LmChallengeResponse.MaximumLength = 0; sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); - rc = setup_ntlmv2_rsp(ses, nls_cp); - if (rc) { - cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); - goto setup_ntlmv2_ret; - } - memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - ses->auth_key.len - CIFS_SESS_KEY_SIZE); - tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; + if (ses->user_name != NULL) { + rc = setup_ntlmv2_rsp(ses, nls_cp); + if (rc) { + cifs_dbg(VFS, "Error %d during NTLMSSP authentication\n", rc); + goto setup_ntlmv2_ret; + } + memcpy(tmp, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + ses->auth_key.len - CIFS_SESS_KEY_SIZE); + tmp += ses->auth_key.len - CIFS_SESS_KEY_SIZE; - sec_blob->NtChallengeResponse.Length = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); - sec_blob->NtChallengeResponse.MaximumLength = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + sec_blob->NtChallengeResponse.Length = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + sec_blob->NtChallengeResponse.MaximumLength = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + } else { + /* + * don't send an NT Response for anonymous access + */ + sec_blob->NtChallengeResponse.Length = 0; + sec_blob->NtChallengeResponse.MaximumLength = 0; + } if (ses->domainName == NULL) { sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); From 4dc809685c3b30c20f46886f54e4f96772328f3f Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 3 May 2016 10:52:30 +0200 Subject: [PATCH 058/656] fs/cifs: correctly to anonymous authentication for the LANMAN authentication commit fa8f3a354bb775ec586e4475bcb07f7dece97e0c upstream. Only server which map unknown users to guest will allow access using a non-null LMChallengeResponse. For Samba it's the "map to guest = bad user" option. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11913 Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/sess.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 09b0201d15d0..b9e2cc1437ef 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -678,20 +678,24 @@ sess_auth_lanman(struct sess_data *sess_data) pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE; - /* no capabilities flags in old lanman negotiation */ - pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE); + if (ses->user_name != NULL) { + /* no capabilities flags in old lanman negotiation */ + pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE); - /* Calculate hash with password and copy into bcc_ptr. - * Encryption Key (stored as in cryptkey) gets used if the - * security mode bit in Negottiate Protocol response states - * to use challenge/response method (i.e. Password bit is 1). - */ - rc = calc_lanman_hash(ses->password, ses->server->cryptkey, - ses->server->sec_mode & SECMODE_PW_ENCRYPT ? - true : false, lnm_session_key); + /* Calculate hash with password and copy into bcc_ptr. + * Encryption Key (stored as in cryptkey) gets used if the + * security mode bit in Negottiate Protocol response states + * to use challenge/response method (i.e. Password bit is 1). + */ + rc = calc_lanman_hash(ses->password, ses->server->cryptkey, + ses->server->sec_mode & SECMODE_PW_ENCRYPT ? + true : false, lnm_session_key); - memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); - bcc_ptr += CIFS_AUTH_RESP_SIZE; + memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); + bcc_ptr += CIFS_AUTH_RESP_SIZE; + } else { + pSMB->old_req.PasswordLength = 0; + } /* * can not sign if LANMAN negotiated so no need From 0e5e5bfd9be9d4835c46eb9c3a5188b61ee82240 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 3 May 2016 10:52:30 +0200 Subject: [PATCH 059/656] fs/cifs: correctly to anonymous authentication for the NTLM(v1) authentication commit 777f69b8d26bf35ade4a76b08f203c11e048365d upstream. Only server which map unknown users to guest will allow access using a non-null NTChallengeResponse. For Samba it's the "map to guest = bad user" option. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11913 Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/sess.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index b9e2cc1437ef..5d5d11ea0251 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -781,27 +781,32 @@ sess_auth_ntlm(struct sess_data *sess_data) capabilities = cifs_ssetup_hdr(ses, pSMB); pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); - pSMB->req_no_secext.CaseInsensitivePasswordLength = - cpu_to_le16(CIFS_AUTH_RESP_SIZE); - pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(CIFS_AUTH_RESP_SIZE); + if (ses->user_name != NULL) { + pSMB->req_no_secext.CaseInsensitivePasswordLength = + cpu_to_le16(CIFS_AUTH_RESP_SIZE); + pSMB->req_no_secext.CaseSensitivePasswordLength = + cpu_to_le16(CIFS_AUTH_RESP_SIZE); - /* calculate ntlm response and session key */ - rc = setup_ntlm_response(ses, sess_data->nls_cp); - if (rc) { - cifs_dbg(VFS, "Error %d during NTLM authentication\n", - rc); - goto out; + /* calculate ntlm response and session key */ + rc = setup_ntlm_response(ses, sess_data->nls_cp); + if (rc) { + cifs_dbg(VFS, "Error %d during NTLM authentication\n", + rc); + goto out; + } + + /* copy ntlm response */ + memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + CIFS_AUTH_RESP_SIZE); + bcc_ptr += CIFS_AUTH_RESP_SIZE; + memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + CIFS_AUTH_RESP_SIZE); + bcc_ptr += CIFS_AUTH_RESP_SIZE; + } else { + pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; + pSMB->req_no_secext.CaseSensitivePasswordLength = 0; } - /* copy ntlm response */ - memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - CIFS_AUTH_RESP_SIZE); - bcc_ptr += CIFS_AUTH_RESP_SIZE; - memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - CIFS_AUTH_RESP_SIZE); - bcc_ptr += CIFS_AUTH_RESP_SIZE; - if (ses->capabilities & CAP_UNICODE) { /* unicode strings must be word aligned */ if (sess_data->iov[0].iov_len % 2) { From 6b83512b378928327114656589a678357c4fb88a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 3 May 2016 10:52:30 +0200 Subject: [PATCH 060/656] fs/cifs: correctly to anonymous authentication for the NTLM(v2) authentication commit 1a967d6c9b39c226be1b45f13acd4d8a5ab3dc44 upstream. Only server which map unknown users to guest will allow access using a non-null NTLMv2_Response. For Samba it's the "map to guest = bad user" option. BUG: https://bugzilla.samba.org/show_bug.cgi?id=11913 Signed-off-by: Stefan Metzmacher Signed-off-by: Steve French Signed-off-by: Greg Kroah-Hartman --- fs/cifs/sess.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 5d5d11ea0251..af0ec2d5ad0e 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -895,23 +895,27 @@ sess_auth_ntlmv2(struct sess_data *sess_data) /* LM2 password would be here if we supported it */ pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; - /* calculate nlmv2 response and session key */ - rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp); - if (rc) { - cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc); - goto out; + if (ses->user_name != NULL) { + /* calculate nlmv2 response and session key */ + rc = setup_ntlmv2_rsp(ses, sess_data->nls_cp); + if (rc) { + cifs_dbg(VFS, "Error %d during NTLMv2 authentication\n", rc); + goto out; + } + + memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, + ses->auth_key.len - CIFS_SESS_KEY_SIZE); + bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; + + /* set case sensitive password length after tilen may get + * assigned, tilen is 0 otherwise. + */ + pSMB->req_no_secext.CaseSensitivePasswordLength = + cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); + } else { + pSMB->req_no_secext.CaseSensitivePasswordLength = 0; } - memcpy(bcc_ptr, ses->auth_key.response + CIFS_SESS_KEY_SIZE, - ses->auth_key.len - CIFS_SESS_KEY_SIZE); - bcc_ptr += ses->auth_key.len - CIFS_SESS_KEY_SIZE; - - /* set case sensitive password length after tilen may get - * assigned, tilen is 0 otherwise. - */ - pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(ses->auth_key.len - CIFS_SESS_KEY_SIZE); - if (ses->capabilities & CAP_UNICODE) { if (sess_data->iov[0].iov_len % 2) { *bcc_ptr = 0; From 0e4d7a015eccd90432aeb306b8158903a35505f5 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Mon, 16 May 2016 20:36:15 -0700 Subject: [PATCH 061/656] asix: Fix offset calculation in asix_rx_fixup() causing slow transmissions commit cd9e2e5d3ff148be9ea210f622ce3e8e8292fcd6 upstream. In testing with HiKey, we found that since commit 3f30b158eba5 ("asix: On RX avoid creating bad Ethernet frames"), we're seeing lots of noise during network transfers: [ 239.027993] asix 1-1.1:1.0 eth0: asix_rx_fixup() Data Header synchronisation was lost, remaining 988 [ 239.037310] asix 1-1.1:1.0 eth0: asix_rx_fixup() Bad Header Length 0x54ebb5ec, offset 4 [ 239.045519] asix 1-1.1:1.0 eth0: asix_rx_fixup() Bad Header Length 0xcdffe7a2, offset 4 [ 239.275044] asix 1-1.1:1.0 eth0: asix_rx_fixup() Data Header synchronisation was lost, remaining 988 [ 239.284355] asix 1-1.1:1.0 eth0: asix_rx_fixup() Bad Header Length 0x1d36f59d, offset 4 [ 239.292541] asix 1-1.1:1.0 eth0: asix_rx_fixup() Bad Header Length 0xaef3c1e9, offset 4 [ 239.518996] asix 1-1.1:1.0 eth0: asix_rx_fixup() Data Header synchronisation was lost, remaining 988 [ 239.528300] asix 1-1.1:1.0 eth0: asix_rx_fixup() Bad Header Length 0x2881912, offset 4 [ 239.536413] asix 1-1.1:1.0 eth0: asix_rx_fixup() Bad Header Length 0x5638f7e2, offset 4 And network throughput ends up being pretty bursty and slow with a overall throughput of at best ~30kB/s (where as previously we got 1.1MB/s with the slower USB1.1 "full speed" host). We found the issue also was reproducible on a x86_64 system, using a "high-speed" USB2.0 port but the throughput did not measurably drop (possibly due to the scp transfer being cpu bound on my slow test hardware). After lots of debugging, I found the check added in the problematic commit seems to be calculating the offset incorrectly. In the normal case, in the main loop of the function, we do: (where offset is zero, or set to "offset += (copy_length + 1) & 0xfffe" in the previous loop) rx->header = get_unaligned_le32(skb->data + offset); offset += sizeof(u32); But the problematic patch calculates: offset = ((rx->remaining + 1) & 0xfffe) + sizeof(u32); rx->header = get_unaligned_le32(skb->data + offset); Adding some debug logic to check those offset calculation used to find rx->header, the one in problematic code is always too large by sizeof(u32). Thus, this patch removes the incorrect " + sizeof(u32)" addition in the problematic calculation, and resolves the issue. Cc: Dean Jenkins Cc: "David B. Robins" Cc: Mark Craske Cc: Emil Goode Cc: "David S. Miller" Cc: YongQin Liu Cc: Guodong Xu Cc: Ivan Vecera Cc: linux-usb@vger.kernel.org Cc: netdev@vger.kernel.org Reported-by: Yongqin Liu Signed-off-by: John Stultz Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman --- drivers/net/usb/asix_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c index bd9acff1eb7b..7fbd8f044207 100644 --- a/drivers/net/usb/asix_common.c +++ b/drivers/net/usb/asix_common.c @@ -66,7 +66,7 @@ int asix_rx_fixup_internal(struct usbnet *dev, struct sk_buff *skb, * buffer. */ if (rx->remaining && (rx->remaining + sizeof(u32) <= skb->len)) { - offset = ((rx->remaining + 1) & 0xfffe) + sizeof(u32); + offset = ((rx->remaining + 1) & 0xfffe); rx->header = get_unaligned_le32(skb->data + offset); offset = 0; From dfb71aefc99d84267e5ac848d8765307ac5765f8 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Thu, 12 May 2016 11:01:24 -0400 Subject: [PATCH 062/656] ring-buffer: Use long for nr_pages to avoid overflow failures commit 9b94a8fba501f38368aef6ac1b30e7335252a220 upstream. The size variable to change the ring buffer in ftrace is a long. The nr_pages used to update the ring buffer based on the size is int. On 64 bit machines this can cause an overflow problem. For example, the following will cause the ring buffer to crash: # cd /sys/kernel/debug/tracing # echo 10 > buffer_size_kb # echo 8556384240 > buffer_size_kb Then you get the warning of: WARNING: CPU: 1 PID: 318 at kernel/trace/ring_buffer.c:1527 rb_update_pages+0x22f/0x260 Which is: RB_WARN_ON(cpu_buffer, nr_removed); Note each ring buffer page holds 4080 bytes. This is because: 1) 10 causes the ring buffer to have 3 pages. (10kb requires 3 * 4080 pages to hold) 2) (2^31 / 2^10 + 1) * 4080 = 8556384240 The value written into buffer_size_kb is shifted by 10 and then passed to ring_buffer_resize(). 8556384240 * 2^10 = 8761737461760 3) The size passed to ring_buffer_resize() is then divided by BUF_PAGE_SIZE which is 4080. 8761737461760 / 4080 = 2147484672 4) nr_pages is subtracted from the current nr_pages (3) and we get: 2147484669. This value is saved in a signed integer nr_pages_to_update 5) 2147484669 is greater than 2^31 but smaller than 2^32, a signed int turns into the value of -2147482627 6) As the value is a negative number, in update_pages_handler() it is negated and passed to rb_remove_pages() and 2147482627 pages will be removed, which is much larger than 3 and it causes the warning because not all the pages asked to be removed were removed. Link: https://bugzilla.kernel.org/show_bug.cgi?id=118001 Fixes: 7a8e76a3829f1 ("tracing: unified trace buffer") Reported-by: Hao Qin Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ring_buffer.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 9c6045a27ba3..16c67e2b2cb3 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -437,7 +437,7 @@ struct ring_buffer_per_cpu { raw_spinlock_t reader_lock; /* serialize readers */ arch_spinlock_t lock; struct lock_class_key lock_key; - unsigned int nr_pages; + unsigned long nr_pages; unsigned int current_context; struct list_head *pages; struct buffer_page *head_page; /* read from head */ @@ -458,7 +458,7 @@ struct ring_buffer_per_cpu { u64 write_stamp; u64 read_stamp; /* ring buffer pages to update, > 0 to add, < 0 to remove */ - int nr_pages_to_update; + long nr_pages_to_update; struct list_head new_pages; /* new pages to add */ struct work_struct update_pages_work; struct completion update_done; @@ -1137,10 +1137,10 @@ static int rb_check_pages(struct ring_buffer_per_cpu *cpu_buffer) return 0; } -static int __rb_allocate_pages(int nr_pages, struct list_head *pages, int cpu) +static int __rb_allocate_pages(long nr_pages, struct list_head *pages, int cpu) { - int i; struct buffer_page *bpage, *tmp; + long i; for (i = 0; i < nr_pages; i++) { struct page *page; @@ -1177,7 +1177,7 @@ free_pages: } static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, - unsigned nr_pages) + unsigned long nr_pages) { LIST_HEAD(pages); @@ -1202,7 +1202,7 @@ static int rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, } static struct ring_buffer_per_cpu * -rb_allocate_cpu_buffer(struct ring_buffer *buffer, int nr_pages, int cpu) +rb_allocate_cpu_buffer(struct ring_buffer *buffer, long nr_pages, int cpu) { struct ring_buffer_per_cpu *cpu_buffer; struct buffer_page *bpage; @@ -1302,8 +1302,9 @@ struct ring_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *key) { struct ring_buffer *buffer; + long nr_pages; int bsize; - int cpu, nr_pages; + int cpu; /* keep it in its own cache line */ buffer = kzalloc(ALIGN(sizeof(*buffer), cache_line_size()), @@ -1429,12 +1430,12 @@ static inline unsigned long rb_page_write(struct buffer_page *bpage) } static int -rb_remove_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned int nr_pages) +rb_remove_pages(struct ring_buffer_per_cpu *cpu_buffer, unsigned long nr_pages) { struct list_head *tail_page, *to_remove, *next_page; struct buffer_page *to_remove_page, *tmp_iter_page; struct buffer_page *last_page, *first_page; - unsigned int nr_removed; + unsigned long nr_removed; unsigned long head_bit; int page_entries; @@ -1651,7 +1652,7 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size, int cpu_id) { struct ring_buffer_per_cpu *cpu_buffer; - unsigned nr_pages; + unsigned long nr_pages; int cpu, err = 0; /* @@ -4645,8 +4646,9 @@ static int rb_cpu_notify(struct notifier_block *self, struct ring_buffer *buffer = container_of(self, struct ring_buffer, cpu_notify); long cpu = (long)hcpu; - int cpu_i, nr_pages_same; - unsigned int nr_pages; + long nr_pages_same; + int cpu_i; + unsigned long nr_pages; switch (action) { case CPU_UP_PREPARE: From f199023137853eb1d46e8b7ebc274b29ba99028c Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Red Hat)" Date: Fri, 13 May 2016 09:34:12 -0400 Subject: [PATCH 063/656] ring-buffer: Prevent overflow of size in ring_buffer_resize() commit 59643d1535eb220668692a5359de22545af579f6 upstream. If the size passed to ring_buffer_resize() is greater than MAX_LONG - BUF_PAGE_SIZE then the DIV_ROUND_UP() will return zero. Here's the details: # echo 18014398509481980 > /sys/kernel/debug/tracing/buffer_size_kb tracing_entries_write() processes this and converts kb to bytes. 18014398509481980 << 10 = 18446744073709547520 and this is passed to ring_buffer_resize() as unsigned long size. size = DIV_ROUND_UP(size, BUF_PAGE_SIZE); Where DIV_ROUND_UP(a, b) is (a + b - 1)/b BUF_PAGE_SIZE is 4080 and here 18446744073709547520 + 4080 - 1 = 18446744073709551599 where 18446744073709551599 is still smaller than 2^64 2^64 - 18446744073709551599 = 17 But now 18446744073709551599 / 4080 = 4521260802379792 and size = size * 4080 = 18446744073709551360 This is checked to make sure its still greater than 2 * 4080, which it is. Then we convert to the number of buffer pages needed. nr_page = DIV_ROUND_UP(size, BUF_PAGE_SIZE) but this time size is 18446744073709551360 and 2^64 - (18446744073709551360 + 4080 - 1) = -3823 Thus it overflows and the resulting number is less than 4080, which makes 3823 / 4080 = 0 an nr_pages is set to this. As we already checked against the minimum that nr_pages may be, this causes the logic to fail as well, and we crash the kernel. There's no reason to have the two DIV_ROUND_UP() (that's just result of historical code changes), clean up the code and fix this bug. Fixes: 83f40318dab00 ("ring-buffer: Make removal of ring buffer pages atomic") Signed-off-by: Steven Rostedt Signed-off-by: Greg Kroah-Hartman --- kernel/trace/ring_buffer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 16c67e2b2cb3..acbb0e73d3a2 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -1666,14 +1666,13 @@ int ring_buffer_resize(struct ring_buffer *buffer, unsigned long size, !cpumask_test_cpu(cpu_id, buffer->cpumask)) return size; - size = DIV_ROUND_UP(size, BUF_PAGE_SIZE); - size *= BUF_PAGE_SIZE; + nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE); /* we need a minimum of two pages */ - if (size < BUF_PAGE_SIZE * 2) - size = BUF_PAGE_SIZE * 2; + if (nr_pages < 2) + nr_pages = 2; - nr_pages = DIV_ROUND_UP(size, BUF_PAGE_SIZE); + size = nr_pages * BUF_PAGE_SIZE; /* * Don't succeed if resizing is disabled, as a reader might be From fd97b4fbaaf2fa1776de2cd73d9ec9d3ce82e68c Mon Sep 17 00:00:00 2001 From: Catalin Vasile Date: Fri, 6 May 2016 16:18:53 +0300 Subject: [PATCH 064/656] crypto: caam - fix caam_jr_alloc() ret code commit e930c765ca5c6b039cd22ebfb4504ea7b5dab43d upstream. caam_jr_alloc() used to return NULL if a JR device could not be allocated for a session. In turn, every user of this function used IS_ERR() function to verify if anything went wrong, which does NOT look for NULL values. This made the kernel crash if the sanity check failed, because the driver continued to think it had allocated a valid JR dev instance to the session and at some point it tries to do a caam_jr_free() on a NULL JR dev pointer. This patch is a fix for this issue. Signed-off-by: Catalin Vasile Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/caam/jr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index f7e0d8d4c3da..8f50a02ff68d 100644 --- a/drivers/crypto/caam/jr.c +++ b/drivers/crypto/caam/jr.c @@ -248,7 +248,7 @@ static void caam_jr_dequeue(unsigned long devarg) struct device *caam_jr_alloc(void) { struct caam_drv_private_jr *jrpriv, *min_jrpriv = NULL; - struct device *dev = NULL; + struct device *dev = ERR_PTR(-ENODEV); int min_tfm_cnt = INT_MAX; int tfm_cnt; From efc1e73ce8f21cb6b039cedced25580f2de901a5 Mon Sep 17 00:00:00 2001 From: Horia Geant? Date: Thu, 21 Apr 2016 19:24:55 +0300 Subject: [PATCH 065/656] crypto: talitos - fix ahash algorithms registration commit 3639ca840df953f9af6f15fc8a6bf77f19075ab1 upstream. Provide hardware state import/export functionality, as mandated by commit 8996eafdcbad ("crypto: ahash - ensure statesize is non-zero") Reported-by: Jonas Eymann Signed-off-by: Horia Geant? Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/talitos.c | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index a04fea4d0063..9a8a18aafd5c 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -835,6 +835,16 @@ struct talitos_ahash_req_ctx { struct scatterlist *psrc; }; +struct talitos_export_state { + u32 hw_context[TALITOS_MDEU_MAX_CONTEXT_SIZE / sizeof(u32)]; + u8 buf[HASH_MAX_BLOCK_SIZE]; + unsigned int swinit; + unsigned int first; + unsigned int last; + unsigned int to_hash_later; + unsigned int nbuf; +}; + static int aead_setkey(struct crypto_aead *authenc, const u8 *key, unsigned int keylen) { @@ -1954,6 +1964,46 @@ static int ahash_digest(struct ahash_request *areq) return ahash_process_req(areq, areq->nbytes); } +static int ahash_export(struct ahash_request *areq, void *out) +{ + struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); + struct talitos_export_state *export = out; + + memcpy(export->hw_context, req_ctx->hw_context, + req_ctx->hw_context_size); + memcpy(export->buf, req_ctx->buf, req_ctx->nbuf); + export->swinit = req_ctx->swinit; + export->first = req_ctx->first; + export->last = req_ctx->last; + export->to_hash_later = req_ctx->to_hash_later; + export->nbuf = req_ctx->nbuf; + + return 0; +} + +static int ahash_import(struct ahash_request *areq, const void *in) +{ + struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq); + const struct talitos_export_state *export = in; + + memset(req_ctx, 0, sizeof(*req_ctx)); + req_ctx->hw_context_size = + (crypto_ahash_digestsize(tfm) <= SHA256_DIGEST_SIZE) + ? TALITOS_MDEU_CONTEXT_SIZE_MD5_SHA1_SHA256 + : TALITOS_MDEU_CONTEXT_SIZE_SHA384_SHA512; + memcpy(req_ctx->hw_context, export->hw_context, + req_ctx->hw_context_size); + memcpy(req_ctx->buf, export->buf, export->nbuf); + req_ctx->swinit = export->swinit; + req_ctx->first = export->first; + req_ctx->last = export->last; + req_ctx->to_hash_later = export->to_hash_later; + req_ctx->nbuf = export->nbuf; + + return 0; +} + struct keyhash_result { struct completion completion; int err; @@ -2348,6 +2398,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = MD5_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "md5", .cra_driver_name = "md5-talitos", @@ -2363,6 +2414,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA1_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "sha1", .cra_driver_name = "sha1-talitos", @@ -2378,6 +2430,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "sha224", .cra_driver_name = "sha224-talitos", @@ -2393,6 +2446,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "sha256", .cra_driver_name = "sha256-talitos", @@ -2408,6 +2462,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA384_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "sha384", .cra_driver_name = "sha384-talitos", @@ -2423,6 +2478,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA512_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "sha512", .cra_driver_name = "sha512-talitos", @@ -2438,6 +2494,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = MD5_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "hmac(md5)", .cra_driver_name = "hmac-md5-talitos", @@ -2453,6 +2510,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA1_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "hmac(sha1)", .cra_driver_name = "hmac-sha1-talitos", @@ -2468,6 +2526,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA224_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "hmac(sha224)", .cra_driver_name = "hmac-sha224-talitos", @@ -2483,6 +2542,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA256_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "hmac(sha256)", .cra_driver_name = "hmac-sha256-talitos", @@ -2498,6 +2558,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA384_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "hmac(sha384)", .cra_driver_name = "hmac-sha384-talitos", @@ -2513,6 +2574,7 @@ static struct talitos_alg_template driver_algs[] = { { .type = CRYPTO_ALG_TYPE_AHASH, .alg.hash = { .halg.digestsize = SHA512_DIGEST_SIZE, + .halg.statesize = sizeof(struct talitos_export_state), .halg.base = { .cra_name = "hmac(sha512)", .cra_driver_name = "hmac-sha512-talitos", @@ -2704,6 +2766,8 @@ static struct talitos_crypto_alg *talitos_alg_alloc(struct device *dev, t_alg->algt.alg.hash.finup = ahash_finup; t_alg->algt.alg.hash.digest = ahash_digest; t_alg->algt.alg.hash.setkey = ahash_setkey; + t_alg->algt.alg.hash.import = ahash_import; + t_alg->algt.alg.hash.export = ahash_export; if (!(priv->features & TALITOS_FTR_HMAC_OK) && !strncmp(alg->cra_name, "hmac", 4)) { From a80e1fbf11b6564a9f762841b5f497c0a72720d3 Mon Sep 17 00:00:00 2001 From: Corentin LABBE Date: Wed, 23 Mar 2016 16:11:24 +0100 Subject: [PATCH 066/656] crypto: sun4i-ss - Replace spinlock_bh by spin_lock_irq{save|restore} commit bdb6cf9f6fe6d9af905ea34b7c4bb78ea601329e upstream. The current sun4i-ss driver could generate data corruption when ciphering/deciphering. It occurs randomly on end of handled data. No root cause have been found and the only way to remove it is to replace all spin_lock_bh by their irq counterparts. Fixes: 6298e948215f ("crypto: sunxi-ss - Add Allwinner Security System crypto accelerator") Signed-off-by: LABBE Corentin Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/crypto/sunxi-ss/sun4i-ss-cipher.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c b/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c index a19ee127edca..e72fea737a0d 100644 --- a/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c +++ b/drivers/crypto/sunxi-ss/sun4i-ss-cipher.c @@ -35,6 +35,7 @@ static int sun4i_ss_opti_poll(struct ablkcipher_request *areq) unsigned int todo; struct sg_mapping_iter mi, mo; unsigned int oi, oo; /* offset for in and out */ + unsigned long flags; if (areq->nbytes == 0) return 0; @@ -49,7 +50,7 @@ static int sun4i_ss_opti_poll(struct ablkcipher_request *areq) return -EINVAL; } - spin_lock_bh(&ss->slock); + spin_lock_irqsave(&ss->slock, flags); for (i = 0; i < op->keylen; i += 4) writel(*(op->key + i / 4), ss->base + SS_KEY0 + i); @@ -117,7 +118,7 @@ release_ss: sg_miter_stop(&mi); sg_miter_stop(&mo); writel(0, ss->base + SS_CTL); - spin_unlock_bh(&ss->slock); + spin_unlock_irqrestore(&ss->slock, flags); return err; } @@ -149,6 +150,7 @@ static int sun4i_ss_cipher_poll(struct ablkcipher_request *areq) unsigned int ob = 0; /* offset in buf */ unsigned int obo = 0; /* offset in bufo*/ unsigned int obl = 0; /* length of data in bufo */ + unsigned long flags; if (areq->nbytes == 0) return 0; @@ -181,7 +183,7 @@ static int sun4i_ss_cipher_poll(struct ablkcipher_request *areq) if (no_chunk == 1) return sun4i_ss_opti_poll(areq); - spin_lock_bh(&ss->slock); + spin_lock_irqsave(&ss->slock, flags); for (i = 0; i < op->keylen; i += 4) writel(*(op->key + i / 4), ss->base + SS_KEY0 + i); @@ -308,7 +310,7 @@ release_ss: sg_miter_stop(&mi); sg_miter_stop(&mo); writel(0, ss->base + SS_CTL); - spin_unlock_bh(&ss->slock); + spin_unlock_irqrestore(&ss->slock, flags); return err; } From aef531699fe1f26dd0d3e8fc9555500a517276b9 Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Tue, 3 May 2016 15:24:11 -0500 Subject: [PATCH 067/656] clk: qcom: msm8916: Fix crypto clock flags commit 2a0974aa1a0b40a92387ea03dbfeacfbc9ba182c upstream. This patch adds the CLK_SET_RATE_PARENT flag for the crypto core and ahb blocks. Without this flag, clk_set_rate can fail for certain frequency requests. Signed-off-by: Andy Gross Fixes: 3966fab8b6ab ("clk: qcom: Add MSM8916 Global Clock Controller support") Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/clk/qcom/gcc-msm8916.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index d0a0313d6bef..2e7f03d50f4e 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -2346,6 +2346,7 @@ static struct clk_branch gcc_crypto_ahb_clk = { "pcnoc_bfdcd_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, @@ -2381,6 +2382,7 @@ static struct clk_branch gcc_crypto_clk = { "crypto_clk_src", }, .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, }, }, From 1df73f1884599d40c76e111e99e9511d943fa4c4 Mon Sep 17 00:00:00 2001 From: Vik Heyndrickx Date: Thu, 28 Apr 2016 20:46:28 +0200 Subject: [PATCH 068/656] sched/loadavg: Fix loadavg artifacts on fully idle and on fully loaded systems commit 20878232c52329f92423d27a60e48b6a6389e0dd upstream. Systems show a minimal load average of 0.00, 0.01, 0.05 even when they have no load at all. Uptime and /proc/loadavg on all systems with kernels released during the last five years up until kernel version 4.6-rc5, show a 5- and 15-minute minimum loadavg of 0.01 and 0.05 respectively. This should be 0.00 on idle systems, but the way the kernel calculates this value prevents it from getting lower than the mentioned values. Likewise but not as obviously noticeable, a fully loaded system with no processes waiting, shows a maximum 1/5/15 loadavg of 1.00, 0.99, 0.95 (multiplied by number of cores). Once the (old) load becomes 93 or higher, it mathematically can never get lower than 93, even when the active (load) remains 0 forever. This results in the strange 0.00, 0.01, 0.05 uptime values on idle systems. Note: 93/2048 = 0.0454..., which rounds up to 0.05. It is not correct to add a 0.5 rounding (=1024/2048) here, since the result from this function is fed back into the next iteration again, so the result of that +0.5 rounding value then gets multiplied by (2048-2037), and then rounded again, so there is a virtual "ghost" load created, next to the old and active load terms. By changing the way the internally kept value is rounded, that internal value equivalent now can reach 0.00 on idle, and 1.00 on full load. Upon increasing load, the internally kept load value is rounded up, when the load is decreasing, the load value is rounded down. The modified code was tested on nohz=off and nohz kernels. It was tested on vanilla kernel 4.6-rc5 and on centos 7.1 kernel 3.10.0-327. It was tested on single, dual, and octal cores system. It was tested on virtual hosts and bare hardware. No unwanted effects have been observed, and the problems that the patch intended to fix were indeed gone. Tested-by: Damien Wyart Signed-off-by: Vik Heyndrickx Signed-off-by: Peter Zijlstra (Intel) Cc: Doug Smythies Cc: Linus Torvalds Cc: Mike Galbraith Cc: Peter Zijlstra Cc: Thomas Gleixner Fixes: 0f004f5a696a ("sched: Cure more NO_HZ load average woes") Link: http://lkml.kernel.org/r/e8d32bff-d544-7748-72b5-3c86cc71f09f@veribox.net Signed-off-by: Ingo Molnar Signed-off-by: Greg Kroah-Hartman --- kernel/sched/loadavg.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/kernel/sched/loadavg.c b/kernel/sched/loadavg.c index ef7159012cf3..b0b93fd33af9 100644 --- a/kernel/sched/loadavg.c +++ b/kernel/sched/loadavg.c @@ -99,10 +99,13 @@ long calc_load_fold_active(struct rq *this_rq) static unsigned long calc_load(unsigned long load, unsigned long exp, unsigned long active) { - load *= exp; - load += active * (FIXED_1 - exp); - load += 1UL << (FSHIFT - 1); - return load >> FSHIFT; + unsigned long newload; + + newload = load * exp + active * (FIXED_1 - exp); + if (active >= load) + newload += FIXED_1-1; + + return newload / FIXED_1; } #ifdef CONFIG_NO_HZ_COMMON From c5215990d6ce401cefca648bff578a0a33667f75 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Mon, 9 May 2016 11:28:37 +0300 Subject: [PATCH 069/656] mfd: omap-usb-tll: Fix scheduling while atomic BUG commit b49b927f16acee626c56a1af4ab4cb062f75b5df upstream. We shouldn't be calling clk_prepare_enable()/clk_prepare_disable() in an atomic context. Fixes the following issue: [ 5.830970] ehci-omap: OMAP-EHCI Host Controller driver [ 5.830974] driver_register 'ehci-omap' [ 5.895849] driver_register 'wl1271_sdio' [ 5.896870] BUG: scheduling while atomic: udevd/994/0x00000002 [ 5.896876] 4 locks held by udevd/994: [ 5.896904] #0: (&dev->mutex){......}, at: [] __driver_attach+0x60/0xac [ 5.896923] #1: (&dev->mutex){......}, at: [] __driver_attach+0x70/0xac [ 5.896946] #2: (tll_lock){+.+...}, at: [] omap_tll_enable+0x2c/0xd0 [ 5.896966] #3: (prepare_lock){+.+...}, at: [] clk_prepare_lock+0x48/0xe0 [ 5.897042] Modules linked in: wlcore_sdio(+) ehci_omap(+) dwc3_omap snd_soc_ts3a225e leds_is31fl319x bq27xxx_battery_i2c tsc2007 bq27xxx_battery bq2429x_charger ina2xx tca8418_keypad as5013 leds_tca6507 twl6040_vibra gpio_twl6040 bmp085_i2c(+) palmas_gpadc usb3503 palmas_pwrbutton bmg160_i2c(+) bmp085 bma150(+) bmg160_core bmp280 input_polldev snd_soc_omap_mcbsp snd_soc_omap_mcpdm snd_soc_omap snd_pcm_dmaengine [ 5.897048] Preemption disabled at:[< (null)>] (null) [ 5.897051] [ 5.897059] CPU: 0 PID: 994 Comm: udevd Not tainted 4.6.0-rc5-letux+ #233 [ 5.897062] Hardware name: Generic OMAP5 (Flattened Device Tree) [ 5.897076] [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [ 5.897087] [] (show_stack) from [] (dump_stack+0x88/0xc0) [ 5.897099] [] (dump_stack) from [] (__schedule_bug+0xac/0xd0) [ 5.897111] [] (__schedule_bug) from [] (__schedule+0x88/0x7e4) [ 5.897120] [] (__schedule) from [] (schedule+0x9c/0xc0) [ 5.897129] [] (schedule) from [] (schedule_preempt_disabled+0x14/0x20) [ 5.897140] [] (schedule_preempt_disabled) from [] (mutex_lock_nested+0x258/0x43c) [ 5.897150] [] (mutex_lock_nested) from [] (clk_prepare_lock+0x48/0xe0) [ 5.897160] [] (clk_prepare_lock) from [] (clk_prepare+0x10/0x28) [ 5.897169] [] (clk_prepare) from [] (omap_tll_enable+0x64/0xd0) [ 5.897180] [] (omap_tll_enable) from [] (usbhs_runtime_resume+0x18/0x17c) [ 5.897192] [] (usbhs_runtime_resume) from [] (pm_generic_runtime_resume+0x2c/0x40) [ 5.897202] [] (pm_generic_runtime_resume) from [] (__rpm_callback+0x38/0x68) [ 5.897210] [] (__rpm_callback) from [] (rpm_callback+0x70/0x88) [ 5.897218] [] (rpm_callback) from [] (rpm_resume+0x4ec/0x7ec) [ 5.897227] [] (rpm_resume) from [] (__pm_runtime_resume+0x4c/0x64) [ 5.897236] [] (__pm_runtime_resume) from [] (driver_probe_device+0x30/0x70) [ 5.897246] [] (driver_probe_device) from [] (__driver_attach+0x88/0xac) [ 5.897256] [] (__driver_attach) from [] (bus_for_each_dev+0x50/0x84) [ 5.897267] [] (bus_for_each_dev) from [] (bus_add_driver+0xcc/0x1e4) [ 5.897276] [] (bus_add_driver) from [] (driver_register+0xac/0xf4) [ 5.897286] [] (driver_register) from [] (do_one_initcall+0x100/0x1b8) [ 5.897296] [] (do_one_initcall) from [] (do_init_module+0x58/0x1c0) [ 5.897304] [] (do_init_module) from [] (SyS_finit_module+0x88/0x90) [ 5.897313] [] (SyS_finit_module) from [] (ret_fast_syscall+0x0/0x1c) [ 5.912697] ------------[ cut here ]------------ [ 5.912711] WARNING: CPU: 0 PID: 994 at kernel/sched/core.c:2996 _raw_spin_unlock+0x28/0x58 [ 5.912717] DEBUG_LOCKS_WARN_ON(val > preempt_count()) Reported-by: H. Nikolaus Schaller Tested-by: H. Nikolaus Schaller Signed-off-by: Roger Quadros Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/omap-usb-tll.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/mfd/omap-usb-tll.c b/drivers/mfd/omap-usb-tll.c index b7b3e8ee64f2..c30290f33430 100644 --- a/drivers/mfd/omap-usb-tll.c +++ b/drivers/mfd/omap-usb-tll.c @@ -269,6 +269,8 @@ static int usbtll_omap_probe(struct platform_device *pdev) if (IS_ERR(tll->ch_clk[i])) dev_dbg(dev, "can't get clock : %s\n", clkname); + else + clk_prepare(tll->ch_clk[i]); } pm_runtime_put_sync(dev); @@ -301,9 +303,12 @@ static int usbtll_omap_remove(struct platform_device *pdev) tll_dev = NULL; spin_unlock(&tll_lock); - for (i = 0; i < tll->nch; i++) - if (!IS_ERR(tll->ch_clk[i])) + for (i = 0; i < tll->nch; i++) { + if (!IS_ERR(tll->ch_clk[i])) { + clk_unprepare(tll->ch_clk[i]); clk_put(tll->ch_clk[i]); + } + } pm_runtime_disable(&pdev->dev); return 0; @@ -420,7 +425,7 @@ int omap_tll_enable(struct usbhs_omap_platform_data *pdata) if (IS_ERR(tll->ch_clk[i])) continue; - r = clk_prepare_enable(tll->ch_clk[i]); + r = clk_enable(tll->ch_clk[i]); if (r) { dev_err(tll_dev, "Error enabling ch %d clock: %d\n", i, r); @@ -448,7 +453,7 @@ int omap_tll_disable(struct usbhs_omap_platform_data *pdata) for (i = 0; i < tll->nch; i++) { if (omap_usb_mode_needs_tll(pdata->port_mode[i])) { if (!IS_ERR(tll->ch_clk[i])) - clk_disable_unprepare(tll->ch_clk[i]); + clk_disable(tll->ch_clk[i]); } } From 02c23447d5e30642ddcbaec014dedcf62f6e2d92 Mon Sep 17 00:00:00 2001 From: Manfred Schlaegl Date: Fri, 27 May 2016 16:36:36 -0700 Subject: [PATCH 070/656] Input: pwm-beeper - fix - scheduling while atomic commit f49cf3b8b4c841457244c461c66186a719e13bcc upstream. Pwm config may sleep so defer it using a worker. On a Freescale i.MX53 based board we ran into "BUG: scheduling while atomic" because input_inject_event locks interrupts, but imx_pwm_config_v2 sleeps. Tested on Freescale i.MX53 SoC with 4.6.0. Signed-off-by: Manfred Schlaegl Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/pwm-beeper.c | 69 +++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index f2261ab54701..18663d4edae5 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -20,21 +20,40 @@ #include #include #include +#include struct pwm_beeper { struct input_dev *input; struct pwm_device *pwm; + struct work_struct work; unsigned long period; }; #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) +static void __pwm_beeper_set(struct pwm_beeper *beeper) +{ + unsigned long period = beeper->period; + + if (period) { + pwm_config(beeper->pwm, period / 2, period); + pwm_enable(beeper->pwm); + } else + pwm_disable(beeper->pwm); +} + +static void pwm_beeper_work(struct work_struct *work) +{ + struct pwm_beeper *beeper = + container_of(work, struct pwm_beeper, work); + + __pwm_beeper_set(beeper); +} + static int pwm_beeper_event(struct input_dev *input, unsigned int type, unsigned int code, int value) { - int ret = 0; struct pwm_beeper *beeper = input_get_drvdata(input); - unsigned long period; if (type != EV_SND || value < 0) return -EINVAL; @@ -49,22 +68,31 @@ static int pwm_beeper_event(struct input_dev *input, return -EINVAL; } - if (value == 0) { - pwm_disable(beeper->pwm); - } else { - period = HZ_TO_NANOSECONDS(value); - ret = pwm_config(beeper->pwm, period / 2, period); - if (ret) - return ret; - ret = pwm_enable(beeper->pwm); - if (ret) - return ret; - beeper->period = period; - } + if (value == 0) + beeper->period = 0; + else + beeper->period = HZ_TO_NANOSECONDS(value); + + schedule_work(&beeper->work); return 0; } +static void pwm_beeper_stop(struct pwm_beeper *beeper) +{ + cancel_work_sync(&beeper->work); + + if (beeper->period) + pwm_disable(beeper->pwm); +} + +static void pwm_beeper_close(struct input_dev *input) +{ + struct pwm_beeper *beeper = input_get_drvdata(input); + + pwm_beeper_stop(beeper); +} + static int pwm_beeper_probe(struct platform_device *pdev) { unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev); @@ -87,6 +115,8 @@ static int pwm_beeper_probe(struct platform_device *pdev) goto err_free; } + INIT_WORK(&beeper->work, pwm_beeper_work); + beeper->input = input_allocate_device(); if (!beeper->input) { dev_err(&pdev->dev, "Failed to allocate input device\n"); @@ -106,6 +136,7 @@ static int pwm_beeper_probe(struct platform_device *pdev) beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); beeper->input->event = pwm_beeper_event; + beeper->input->close = pwm_beeper_close; input_set_drvdata(beeper->input, beeper); @@ -135,7 +166,6 @@ static int pwm_beeper_remove(struct platform_device *pdev) input_unregister_device(beeper->input); - pwm_disable(beeper->pwm); pwm_free(beeper->pwm); kfree(beeper); @@ -147,8 +177,7 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); - if (beeper->period) - pwm_disable(beeper->pwm); + pwm_beeper_stop(beeper); return 0; } @@ -157,10 +186,8 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); - if (beeper->period) { - pwm_config(beeper->pwm, beeper->period / 2, beeper->period); - pwm_enable(beeper->pwm); - } + if (beeper->period) + __pwm_beeper_set(beeper); return 0; } From 3607d54e5aa9e72390ca9963b01e964d6425e148 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 26 Apr 2016 12:00:00 +0100 Subject: [PATCH 071/656] irqchip/gic: Ensure ordering between read of INTACK and shared data commit f86c4fbd930ff6fecf3d8a1c313182bd0f49f496 upstream. When an IPI is generated by a CPU, the pattern looks roughly like: smp_wmb(); On the receiving CPU we rely on the fact that, once we've taken the interrupt, then the freshly written shared data must be visible to us. Put another way, the CPU isn't going to speculate taking an interrupt. Unfortunately, this assumption turns out to be broken. Consider that CPUx wants to send an IPI to CPUy, which will cause CPUy to read some shared_data. Before CPUx has done anything, a random peripheral raises an IRQ to the GIC and the IRQ line on CPUy is raised. CPUy then takes the IRQ and starts executing the entry code, heading towards gic_handle_irq. Furthermore, let's assume that a bunch of the previous interrupts handled by CPUy were SGIs, so the branch predictor kicks in and speculates that irqnr will be <16 and we're likely to head into handle_IPI. The prefetcher then grabs a speculative copy of shared_data which contains a stale value. Meanwhile, CPUx gets round to updating shared_data and asking the GIC to send an SGI to CPUy. Internally, the GIC decides that the SGI is more important than the peripheral interrupt (which hasn't yet been ACKed) but doesn't need to do anything to CPUy, because the IRQ line is already raised. CPUy then reads the ACK register on the GIC, sees the SGI value which confirms the branch prediction and we end up with a stale shared_data value. This patch fixes the problem by adding an smp_rmb() to the IPI entry code in gic_handle_irq. As it turns out, the combination of a control dependency and an ISB instruction from the EOI in the GICv3 driver is enough to provide the ordering we need, so we add a comment there justifying the absence of an explicit smp_rmb(). Signed-off-by: Will Deacon Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-gic-v3.c | 7 +++++++ drivers/irqchip/irq-gic.c | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index d7be6ddc34f6..e35ab799be12 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -361,6 +361,13 @@ static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs if (static_key_true(&supports_deactivate)) gic_write_dir(irqnr); #ifdef CONFIG_SMP + /* + * Unlike GICv2, we don't need an smp_rmb() here. + * The control dependency from gic_read_iar to + * the ISB in gic_write_eoir is enough to ensure + * that any shared data read by handle_IPI will + * be read after the ACK. + */ handle_IPI(irqnr, regs); #else WARN_ONCE(true, "Unexpected SGI received!\n"); diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index abf2ffaed392..cebd8efe651a 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -347,6 +347,14 @@ static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) if (static_key_true(&supports_deactivate)) writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE); #ifdef CONFIG_SMP + /* + * Ensure any shared data written by the CPU sending + * the IPI is read after we've read the ACK register + * on the GIC. + * + * Pairs with the write barrier in gic_raise_softirq + */ + smp_rmb(); handle_IPI(irqnr, regs); #endif continue; From 9b68f12b57e5241f26c2924637aaae740cc1b276 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 6 May 2016 19:41:56 +0100 Subject: [PATCH 072/656] irqchip/gic-v3: Configure all interrupts as non-secure Group-1 commit 7c9b973061b03af62734f613f6abec46c0dd4a88 upstream. The GICv3 driver wrongly assumes that it runs on the non-secure side of a secure-enabled system, while it could be on a system with a single security state, or a GICv3 with GICD_CTLR.DS set. Either way, it is important to configure this properly, or interrupts will simply not be delivered on this HW. Reported-by: Peter Maydell Tested-by: Peter Maydell Signed-off-by: Marc Zyngier Signed-off-by: Greg Kroah-Hartman --- drivers/irqchip/irq-gic-v3.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index e35ab799be12..2fc499a2207e 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -387,6 +387,15 @@ static void __init gic_dist_init(void) writel_relaxed(0, base + GICD_CTLR); gic_dist_wait_for_rwp(); + /* + * Configure SPIs as non-secure Group-1. This will only matter + * if the GIC only has a single security state. This will not + * do the right thing if the kernel is running in secure mode, + * but that's not the intended use case anyway. + */ + for (i = 32; i < gic_data.irq_nr; i += 32) + writel_relaxed(~0, base + GICD_IGROUPR + i / 8); + gic_dist_config(base, gic_data.irq_nr, gic_dist_wait_for_rwp); /* Enable distributor with ARE, Group1 */ @@ -501,6 +510,9 @@ static void gic_cpu_init(void) rbase = gic_data_rdist_sgi_base(); + /* Configure SGIs/PPIs as non-secure Group-1 */ + writel_relaxed(~0, rbase + GICR_IGROUPR0); + gic_cpu_config(rbase, gic_redist_wait_for_rwp); /* Give LPIs a spin */ From 24bf50bc89088686a40cb4ed7f21bd0d08ce6b0b Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Mon, 21 Mar 2016 20:18:21 +0100 Subject: [PATCH 073/656] can: fix handling of unmodifiable configuration options commit bb208f144cf3f59d8f89a09a80efd04389718907 upstream. As described in 'can: m_can: tag current CAN FD controllers as non-ISO' (6cfda7fbebe) it is possible to define fixed configuration options by setting the according bit in 'ctrlmode' and clear it in 'ctrlmode_supported'. This leads to the incovenience that the fixed configuration bits can not be passed by netlink even when they have the correct values (e.g. non-ISO, FD). This patch fixes that issue and not only allows fixed set bit values to be set again but now requires(!) to provide these fixed values at configuration time. A valid CAN FD configuration consists of a nominal/arbitration bittiming, a data bittiming and a control mode with CAN_CTRLMODE_FD set - which is now enforced by a new can_validate() function. This fix additionally removed the inconsistency that was prohibiting the support of 'CANFD-only' controller drivers, like the RCar CAN FD. For this reason a new helper can_set_static_ctrlmode() has been introduced to provide a proper interface to handle static enabled CAN controller options. Reported-by: Ramesh Shanmugasundaram Signed-off-by: Oliver Hartkopp Reviewed-by: Ramesh Shanmugasundaram Signed-off-by: Marc Kleine-Budde Signed-off-by: Greg Kroah-Hartman --- drivers/net/can/dev.c | 56 ++++++++++++++++++++++++++++++++--- drivers/net/can/m_can/m_can.c | 2 +- include/linux/can/dev.h | 22 ++++++++++++-- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 141c2a42d7ed..910c12e2638e 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -696,11 +696,17 @@ int can_change_mtu(struct net_device *dev, int new_mtu) /* allow change of MTU according to the CANFD ability of the device */ switch (new_mtu) { case CAN_MTU: + /* 'CANFD-only' controllers can not switch to CAN_MTU */ + if (priv->ctrlmode_static & CAN_CTRLMODE_FD) + return -EINVAL; + priv->ctrlmode &= ~CAN_CTRLMODE_FD; break; case CANFD_MTU: - if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD)) + /* check for potential CANFD ability */ + if (!(priv->ctrlmode_supported & CAN_CTRLMODE_FD) && + !(priv->ctrlmode_static & CAN_CTRLMODE_FD)) return -EINVAL; priv->ctrlmode |= CAN_CTRLMODE_FD; @@ -782,6 +788,35 @@ static const struct nla_policy can_policy[IFLA_CAN_MAX + 1] = { = { .len = sizeof(struct can_bittiming_const) }, }; +static int can_validate(struct nlattr *tb[], struct nlattr *data[]) +{ + bool is_can_fd = false; + + /* Make sure that valid CAN FD configurations always consist of + * - nominal/arbitration bittiming + * - data bittiming + * - control mode with CAN_CTRLMODE_FD set + */ + + if (data[IFLA_CAN_CTRLMODE]) { + struct can_ctrlmode *cm = nla_data(data[IFLA_CAN_CTRLMODE]); + + is_can_fd = cm->flags & cm->mask & CAN_CTRLMODE_FD; + } + + if (is_can_fd) { + if (!data[IFLA_CAN_BITTIMING] || !data[IFLA_CAN_DATA_BITTIMING]) + return -EOPNOTSUPP; + } + + if (data[IFLA_CAN_DATA_BITTIMING]) { + if (!is_can_fd || !data[IFLA_CAN_BITTIMING]) + return -EOPNOTSUPP; + } + + return 0; +} + static int can_changelink(struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { @@ -813,19 +848,31 @@ static int can_changelink(struct net_device *dev, if (data[IFLA_CAN_CTRLMODE]) { struct can_ctrlmode *cm; + u32 ctrlstatic; + u32 maskedflags; /* Do not allow changing controller mode while running */ if (dev->flags & IFF_UP) return -EBUSY; cm = nla_data(data[IFLA_CAN_CTRLMODE]); + ctrlstatic = priv->ctrlmode_static; + maskedflags = cm->flags & cm->mask; - /* check whether changed bits are allowed to be modified */ - if (cm->mask & ~priv->ctrlmode_supported) + /* check whether provided bits are allowed to be passed */ + if (cm->mask & ~(priv->ctrlmode_supported | ctrlstatic)) + return -EOPNOTSUPP; + + /* do not check for static fd-non-iso if 'fd' is disabled */ + if (!(maskedflags & CAN_CTRLMODE_FD)) + ctrlstatic &= ~CAN_CTRLMODE_FD_NON_ISO; + + /* make sure static options are provided by configuration */ + if ((maskedflags & ctrlstatic) != ctrlstatic) return -EOPNOTSUPP; /* clear bits to be modified and copy the flag values */ priv->ctrlmode &= ~cm->mask; - priv->ctrlmode |= (cm->flags & cm->mask); + priv->ctrlmode |= maskedflags; /* CAN_CTRLMODE_FD can only be set when driver supports FD */ if (priv->ctrlmode & CAN_CTRLMODE_FD) @@ -966,6 +1013,7 @@ static struct rtnl_link_ops can_link_ops __read_mostly = { .maxtype = IFLA_CAN_MAX, .policy = can_policy, .setup = can_setup, + .validate = can_validate, .newlink = can_newlink, .changelink = can_changelink, .get_size = can_get_size, diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index 39cf911f7a1e..195f15edb32e 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -955,7 +955,7 @@ static struct net_device *alloc_m_can_dev(void) priv->can.do_get_berr_counter = m_can_get_berr_counter; /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */ - priv->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO; + can_set_static_ctrlmode(dev, CAN_CTRLMODE_FD_NON_ISO); /* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 735f9f8c4e43..5261751f6bd4 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -40,8 +40,11 @@ struct can_priv { struct can_clock clock; enum can_state state; - u32 ctrlmode; - u32 ctrlmode_supported; + + /* CAN controller features - see include/uapi/linux/can/netlink.h */ + u32 ctrlmode; /* current options setting */ + u32 ctrlmode_supported; /* options that can be modified by netlink */ + u32 ctrlmode_static; /* static enabled options for driver/hardware */ int restart_ms; struct timer_list restart_timer; @@ -108,6 +111,21 @@ static inline bool can_is_canfd_skb(const struct sk_buff *skb) return skb->len == CANFD_MTU; } +/* helper to define static CAN controller features at device creation time */ +static inline void can_set_static_ctrlmode(struct net_device *dev, + u32 static_mode) +{ + struct can_priv *priv = netdev_priv(dev); + + /* alloc_candev() succeeded => netdev_priv() is valid at this point */ + priv->ctrlmode = static_mode; + priv->ctrlmode_static = static_mode; + + /* override MTU which was set by default in can_setup()? */ + if (static_mode & CAN_CTRLMODE_FD) + dev->mtu = CANFD_MTU; +} + /* get data length from can_dlc with sanitized can_dlc */ u8 can_dlc2len(u8 can_dlc); From 98b0125d76fba0325acc790c2126d326f3fd0180 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 5 May 2016 08:12:28 +0300 Subject: [PATCH 074/656] mmc: mmc: Fix partition switch timeout for some eMMCs commit 1c447116d017a98c90f8f71c8c5a611e0aa42178 upstream. Some eMMCs set the partition switch timeout too low. Now typically eMMCs are considered a critical component (e.g. because they store the root file system) and consequently are expected to be reliable. Thus we can neglect the use case where eMMCs can't switch reliably and we might want a lower timeout to facilitate speedy recovery. Although we could employ a quirk for the cards that are affected (if we could identify them all), as described above, there is little benefit to having a low timeout, so instead simply set a minimum timeout. The minimum is set to 300ms somewhat arbitrarily - the examples that have been seen had a timeout of 10ms but were sometimes taking 60-70ms. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/core/mmc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 3d5087b03999..78187699467a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -333,6 +333,9 @@ static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd) } } +/* Minimum partition switch timeout in milliseconds */ +#define MMC_MIN_PART_SWITCH_TIME 300 + /* * Decode extended CSD. */ @@ -397,6 +400,10 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) /* EXT_CSD value is in units of 10ms, but we store in ms */ card->ext_csd.part_time = 10 * ext_csd[EXT_CSD_PART_SWITCH_TIME]; + /* Some eMMC set the value too low so set a minimum */ + if (card->ext_csd.part_time && + card->ext_csd.part_time < MMC_MIN_PART_SWITCH_TIME) + card->ext_csd.part_time = MMC_MIN_PART_SWITCH_TIME; /* Sleep / awake timeout in 100ns units */ if (sa_shift > 0 && sa_shift <= 0x17) From bb2b58c7d372e161736e2b444f9010532f9145ef Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 20 May 2016 10:33:48 +0300 Subject: [PATCH 075/656] mmc: sdhci-acpi: Remove MMC_CAP_BUS_WIDTH_TEST for Intel controllers commit 265984b36ce82fec67957d452dd2b22e010611e4 upstream. The CMD19/CMD14 bus width test has been found to be unreliable in some cases. It is not essential, so simply remove it. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-acpi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index a5cda926d38e..8aea3fa6938b 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -233,7 +233,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { .chip = &sdhci_acpi_chip_int, .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | - MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY, + MMC_CAP_WAIT_WHILE_BUSY, .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, @@ -248,7 +248,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD | - MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY, + MMC_CAP_WAIT_WHILE_BUSY, .flags = SDHCI_ACPI_RUNTIME_PM, .pm_caps = MMC_PM_KEEP_POWER, .probe_slot = sdhci_acpi_sdio_probe_slot, @@ -260,7 +260,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON | SDHCI_QUIRK2_STOP_WITH_TC, - .caps = MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY, + .caps = MMC_CAP_WAIT_WHILE_BUSY, .probe_slot = sdhci_acpi_sd_probe_slot, }; From 419b1d21b3f498de333daf4c21c28454d444589e Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Tue, 3 May 2016 16:48:20 +0800 Subject: [PATCH 076/656] ACPI / osi: Fix an issue that acpi_osi=!* cannot disable ACPICA internal strings commit 30c9bb0d7603e7b3f4d6a0ea231e1cddae020c32 upstream. The order of the _OSI related functionalities is as follows: acpi_blacklisted() acpi_dmi_osi_linux() acpi_osi_setup() acpi_osi_setup() acpi_update_interfaces() if "!*" <<<<<<<<<<<<<<<<<<<<<<<< parse_args() __setup("acpi_osi=") acpi_osi_setup_linux() acpi_update_interfaces() if "!*" <<<<<<<<<<<<<<<<<<<<<<<< acpi_early_init() acpi_initialize_subsystem() acpi_ut_initialize_interfaces() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ acpi_bus_init() acpi_os_initialize1() acpi_install_interface_handler(acpi_osi_handler) acpi_osi_setup_late() acpi_update_interfaces() for "!" >>>>>>>>>>>>>>>>>>>>>>>> acpi_osi_handler() Since acpi_osi_setup_linux() can override acpi_dmi_osi_linux(), the command line setting can override the DMI detection. That's why acpi_blacklisted() is put before __setup("acpi_osi="). Then we can notice the following wrong invocation order. There are acpi_update_interfaces() (marked by <<<<) calls invoked before acpi_ut_initialize_interfaces() (marked by ^^^^). This makes it impossible to use acpi_osi=!* correctly from OSI DMI table or from the command line. The use of acpi_osi=!* is meant to disable both ACPICA (acpi_gbl_supported_interfaces) and Linux specific strings (osi_setup_entries) while the ACPICA part should have stopped working because of the order issue. This patch fixes this issue by moving acpi_update_interfaces() to where it is invoked for acpi_osi=! (marked by >>>>) as this is ensured to be invoked after acpi_ut_initialize_interfaces() (marked by ^^^^). Linux specific strings are still handled in the original place in order to make the following command line working: acpi_osi=!* acpi_osi="Module Device". Note that since acpi_osi=!* is meant to further disable linux specific string comparing to the acpi_osi=!, there is no such use case in our bug fixing work and hence there is no one using acpi_osi=!* either from the command line or from the DMI quirks, this issue is just a theoretical issue. Fixes: 741d81280ad2 (ACPI: Add facility to remove all _OSI strings) Tested-by: Lukas Wunner Tested-by: Chen Yu Signed-off-by: Lv Zheng Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/osl.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 32d684af0ec7..a000ecb995e6 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -135,7 +135,7 @@ static struct osi_linux { unsigned int enable:1; unsigned int dmi:1; unsigned int cmdline:1; - unsigned int default_disabling:1; + u8 default_disabling; } osi_linux = {0, 0, 0, 0}; static u32 acpi_osi_handler(acpi_string interface, u32 supported) @@ -1444,10 +1444,13 @@ void __init acpi_osi_setup(char *str) if (*str == '!') { str++; if (*str == '\0') { - osi_linux.default_disabling = 1; + /* Do not override acpi_osi=!* */ + if (!osi_linux.default_disabling) + osi_linux.default_disabling = + ACPI_DISABLE_ALL_VENDOR_STRINGS; return; } else if (*str == '*') { - acpi_update_interfaces(ACPI_DISABLE_ALL_STRINGS); + osi_linux.default_disabling = ACPI_DISABLE_ALL_STRINGS; for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { osi = &osi_setup_entries[i]; osi->enable = false; @@ -1520,10 +1523,13 @@ static void __init acpi_osi_setup_late(void) acpi_status status; if (osi_linux.default_disabling) { - status = acpi_update_interfaces(ACPI_DISABLE_ALL_VENDOR_STRINGS); + status = acpi_update_interfaces(osi_linux.default_disabling); if (ACPI_SUCCESS(status)) - printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors\n"); + printk(KERN_INFO PREFIX "Disabled all _OSI OS vendors%s\n", + osi_linux.default_disabling == + ACPI_DISABLE_ALL_STRINGS ? + " and feature groups" : ""); } for (i = 0; i < OSI_STRING_ENTRIES_MAX; i++) { From 96cd084c819cdab8478942ba057b63f8bb633639 Mon Sep 17 00:00:00 2001 From: Gabriele Mazzotta Date: Tue, 24 May 2016 22:53:08 +0200 Subject: [PATCH 077/656] dell-rbtn: Ignore ACPI notifications if device is suspended MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ff8651237f39cea60dc89b2d9f25d9ede3fc82c0 upstream. Some BIOSes unconditionally send an ACPI notification to RBTN when the system is resuming from suspend. This makes dell-rbtn send an input event to userspace as if a function key was pressed. Prevent this by ignoring all the notifications received while the device is suspended. Link: https://bugzilla.kernel.org/show_bug.cgi?id=106031 Signed-off-by: Gabriele Mazzotta Tested-by: Alex Hung Reviewed-by: Pali Rohár Signed-off-by: Darren Hart Signed-off-by: Greg Kroah-Hartman --- drivers/platform/x86/dell-rbtn.c | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/platform/x86/dell-rbtn.c b/drivers/platform/x86/dell-rbtn.c index cd410e392550..d33e9ad3218f 100644 --- a/drivers/platform/x86/dell-rbtn.c +++ b/drivers/platform/x86/dell-rbtn.c @@ -28,6 +28,7 @@ struct rbtn_data { enum rbtn_type type; struct rfkill *rfkill; struct input_dev *input_dev; + bool suspended; }; @@ -220,9 +221,55 @@ static const struct acpi_device_id rbtn_ids[] = { { "", 0 }, }; +#ifdef CONFIG_PM_SLEEP +static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context) +{ + struct rbtn_data *rbtn_data = context; + + rbtn_data->suspended = false; +} + +static int rbtn_suspend(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct rbtn_data *rbtn_data = acpi_driver_data(device); + + rbtn_data->suspended = true; + + return 0; +} + +static int rbtn_resume(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct rbtn_data *rbtn_data = acpi_driver_data(device); + acpi_status status; + + /* + * Upon resume, some BIOSes send an ACPI notification thet triggers + * an unwanted input event. In order to ignore it, we use a flag + * that we set at suspend and clear once we have received the extra + * ACPI notification. Since ACPI notifications are delivered + * asynchronously to drivers, we clear the flag from the workqueue + * used to deliver the notifications. This should be enough + * to have the flag cleared only after we received the extra + * notification, if any. + */ + status = acpi_os_execute(OSL_NOTIFY_HANDLER, + rbtn_clear_suspended_flag, rbtn_data); + if (ACPI_FAILURE(status)) + rbtn_clear_suspended_flag(rbtn_data); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume); + static struct acpi_driver rbtn_driver = { .name = "dell-rbtn", .ids = rbtn_ids, + .drv.pm = &rbtn_pm_ops, .ops = { .add = rbtn_add, .remove = rbtn_remove, @@ -384,6 +431,15 @@ static void rbtn_notify(struct acpi_device *device, u32 event) { struct rbtn_data *rbtn_data = device->driver_data; + /* + * Some BIOSes send a notification at resume. + * Ignore it to prevent unwanted input events. + */ + if (rbtn_data->suspended) { + dev_dbg(&device->dev, "ACPI notification ignored\n"); + return; + } + if (event != 0x80) { dev_info(&device->dev, "Received unknown event (0x%x)\n", event); From 32971328e591632a077c3683b9c36c187deadd0e Mon Sep 17 00:00:00 2001 From: Matt Gumbel Date: Fri, 20 May 2016 10:33:46 +0300 Subject: [PATCH 078/656] mmc: longer timeout for long read time quirk commit 32ecd320db39bcb007679ed42f283740641b81ea upstream. 008GE0 Toshiba mmc in some Intel Baytrail tablets responds to MMC_SEND_EXT_CSD in 450-600ms. This patch will... () Increase the long read time quirk timeout from 300ms to 600ms. Original author of that quirk says 300ms was only a guess and that the number may need to be raised in the future. () Add this specific MMC to the quirk Signed-off-by: Matt Gumbel Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/card/block.c | 5 +++-- drivers/mmc/core/core.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 553113eb1bdb..c641c202fe7e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2514,11 +2514,12 @@ static const struct mmc_fixup blk_fixups[] = MMC_QUIRK_BLK_NO_CMD23), /* - * Some Micron MMC cards needs longer data read timeout than - * indicated in CSD. + * Some MMC cards need longer data read timeout than indicated in CSD. */ MMC_FIXUP(CID_NAME_ANY, CID_MANFID_MICRON, 0x200, add_quirk_mmc, MMC_QUIRK_LONG_READ_TIME), + MMC_FIXUP("008GE0", CID_MANFID_TOSHIBA, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_LONG_READ_TIME), /* * On these Samsung MoviNAND parts, performing secure erase or diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 5ae89e48fd85..5f7d10ba498a 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -874,11 +874,11 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) /* * Some cards require longer data read timeout than indicated in CSD. * Address this by setting the read timeout to a "reasonably high" - * value. For the cards tested, 300ms has proven enough. If necessary, + * value. For the cards tested, 600ms has proven enough. If necessary, * this value can be increased if other problematic cards require this. */ if (mmc_card_long_read_time(card) && data->flags & MMC_DATA_READ) { - data->timeout_ns = 300000000; + data->timeout_ns = 600000000; data->timeout_clks = 0; } From 7973b064f956440e419c877bf0e225df4c7dde91 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 20 May 2016 10:33:47 +0300 Subject: [PATCH 079/656] mmc: sdhci-pci: Remove MMC_CAP_BUS_WIDTH_TEST for Intel controllers commit 822969369482166050c5b2f7013501505e025c39 upstream. The CMD19/CMD14 bus width test has been found to be unreliable in some cases. It is not essential, so simply remove it. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/sdhci-pci-core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 610154836d79..5ebe6eb6b89e 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -361,7 +361,6 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR | - MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY; slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; slot->hw_reset = sdhci_pci_int_hw_reset; @@ -377,15 +376,13 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_NONREMOVABLE | - MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_WAIT_WHILE_BUSY; return 0; } static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) { - slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST | - MMC_CAP_WAIT_WHILE_BUSY; + slot->host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; slot->cd_con_id = NULL; slot->cd_idx = 0; slot->cd_override_level = true; From 1af4f24cb6708615f5a0eb30f2fcfcdcc0f1766e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Mar 2016 11:05:18 +0100 Subject: [PATCH 080/656] Bluetooth: vhci: fix open_timeout vs. hdev race commit 373a32c848ae3a1c03618517cce85f9211a6facf upstream. Both vhci_get_user and vhci_release race with open_timeout work. They both contain cancel_delayed_work_sync, but do not test whether the work actually created hdev or not. Since the work can be in progress and _sync will wait for finishing it, we can have data->hdev allocated when cancel_delayed_work_sync returns. But the call sites do 'if (data->hdev)' *before* cancel_delayed_work_sync. As a result: * vhci_get_user allocates a second hdev and puts it into data->hdev. The former is leaked. * vhci_release does not release data->hdev properly as it thinks there is none. Fix both cases by moving the actual test *after* the call to cancel_delayed_work_sync. This can be hit by this program: #include #include #include #include #include #include #include #include int main(int argc, char **argv) { int fd; srand(time(NULL)); while (1) { const int delta = (rand() % 200 - 100) * 100; fd = open("/dev/vhci", O_RDWR); if (fd < 0) err(1, "open"); usleep(1000000 + delta); close(fd); } return 0; } And the result is: BUG: KASAN: use-after-free in skb_queue_tail+0x13e/0x150 at addr ffff88006b0c1228 Read of size 8 by task kworker/u13:1/32068 ============================================================================= BUG kmalloc-192 (Tainted: G E ): kasan: bad access detected ----------------------------------------------------------------------------- Disabling lock debugging due to kernel taint INFO: Allocated in vhci_open+0x50/0x330 [hci_vhci] age=260 cpu=3 pid=32040 ... kmem_cache_alloc_trace+0x150/0x190 vhci_open+0x50/0x330 [hci_vhci] misc_open+0x35b/0x4e0 chrdev_open+0x23b/0x510 ... INFO: Freed in vhci_release+0xa4/0xd0 [hci_vhci] age=9 cpu=2 pid=32040 ... __slab_free+0x204/0x310 vhci_release+0xa4/0xd0 [hci_vhci] ... INFO: Slab 0xffffea0001ac3000 objects=16 used=13 fp=0xffff88006b0c1e00 flags=0x5fffff80004080 INFO: Object 0xffff88006b0c1200 @offset=4608 fp=0xffff88006b0c0600 Bytes b4 ffff88006b0c11f0: 09 df 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................ Object ffff88006b0c1200: 00 06 0c 6b 00 88 ff ff 00 00 00 00 00 00 00 00 ...k............ Object ffff88006b0c1210: 10 12 0c 6b 00 88 ff ff 10 12 0c 6b 00 88 ff ff ...k.......k.... Object ffff88006b0c1220: c0 46 c2 6b 00 88 ff ff c0 46 c2 6b 00 88 ff ff .F.k.....F.k.... Object ffff88006b0c1230: 01 00 00 00 01 00 00 00 e0 ff ff ff 0f 00 00 00 ................ Object ffff88006b0c1240: 40 12 0c 6b 00 88 ff ff 40 12 0c 6b 00 88 ff ff @..k....@..k.... Object ffff88006b0c1250: 50 0d 6e a0 ff ff ff ff 00 02 00 00 00 00 ad de P.n............. Object ffff88006b0c1260: 00 00 00 00 00 00 00 00 ab 62 02 00 01 00 00 00 .........b...... Object ffff88006b0c1270: 90 b9 19 81 ff ff ff ff 38 12 0c 6b 00 88 ff ff ........8..k.... Object ffff88006b0c1280: 03 00 20 00 ff ff ff ff ff ff ff ff 00 00 00 00 .. ............. Object ffff88006b0c1290: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ Object ffff88006b0c12a0: 00 00 00 00 00 00 00 00 00 80 cd 3d 00 88 ff ff ...........=.... Object ffff88006b0c12b0: 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 . .............. Redzone ffff88006b0c12c0: bb bb bb bb bb bb bb bb ........ Padding ffff88006b0c13f8: 00 00 00 00 00 00 00 00 ........ CPU: 3 PID: 32068 Comm: kworker/u13:1 Tainted: G B E 4.4.6-0-default #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.8.1-0-g4adadbd-20151112_172657-sheep25 04/01/2014 Workqueue: hci0 hci_cmd_work [bluetooth] 00000000ffffffff ffffffff81926cfa ffff88006be37c68 ffff88006bc27180 ffff88006b0c1200 ffff88006b0c1234 ffffffff81577993 ffffffff82489320 ffff88006bc24240 0000000000000046 ffff88006a100000 000000026e51eb80 Call Trace: ... [] ? skb_queue_tail+0x13e/0x150 [] ? vhci_send_frame+0xac/0x100 [hci_vhci] [] ? hci_send_frame+0x188/0x320 [bluetooth] [] ? hci_cmd_work+0x115/0x310 [bluetooth] [] ? process_one_work+0x815/0x1340 [] ? worker_thread+0xe5/0x11f0 [] ? process_one_work+0x1340/0x1340 [] ? kthread+0x1c8/0x230 ... Memory state around the buggy address: ffff88006b0c1100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc ffff88006b0c1180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >ffff88006b0c1200: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff88006b0c1280: fb fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc ffff88006b0c1300: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc Fixes: 23424c0d31 (Bluetooth: Add support creating virtual AMP controllers) Signed-off-by: Jiri Slaby Signed-off-by: Marcel Holtmann Cc: Dmitry Vyukov Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_vhci.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index ed888e302bc3..b6fb499a4531 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -189,13 +189,13 @@ static inline ssize_t vhci_get_user(struct vhci_data *data, break; case HCI_VENDOR_PKT: + cancel_delayed_work_sync(&data->open_timeout); + if (data->hdev) { kfree_skb(skb); return -EBADFD; } - cancel_delayed_work_sync(&data->open_timeout); - opcode = *((__u8 *) skb->data); skb_pull(skb, 1); @@ -333,10 +333,12 @@ static int vhci_open(struct inode *inode, struct file *file) static int vhci_release(struct inode *inode, struct file *file) { struct vhci_data *data = file->private_data; - struct hci_dev *hdev = data->hdev; + struct hci_dev *hdev; cancel_delayed_work_sync(&data->open_timeout); + hdev = data->hdev; + if (hdev) { hci_unregister_dev(hdev); hci_free_dev(hdev); From 3295bfd3f1f39f959dba17c1637e2741f7b3c4a3 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sat, 19 Mar 2016 11:49:43 +0100 Subject: [PATCH 081/656] Bluetooth: vhci: purge unhandled skbs commit 13407376b255325fa817798800117a839f3aa055 upstream. The write handler allocates skbs and queues them into data->readq. Read side should read them, if there is any. If there is none, skbs should be dropped by hdev->flush. But this happens only if the device is HCI_UP, i.e. hdev->power_on work was triggered already. When it was not, skbs stay allocated in the queue when /dev/vhci is closed. So purge the queue in ->release. Program to reproduce: #include #include #include #include #include #include #include int main() { char buf[] = { 0xff, 0 }; struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf), }; int fd; while (1) { fd = open("/dev/vhci", O_RDWR); if (fd < 0) err(1, "open"); usleep(50); if (writev(fd, &iov, 1) < 0) err(1, "writev"); usleep(50); close(fd); } return 0; } Result: kmemleak: 4609 new suspected memory leaks unreferenced object 0xffff88059f4d5440 (size 232): comm "vhci", pid 1084, jiffies 4294912542 (age 37569.296s) hex dump (first 32 bytes): 20 f0 23 87 05 88 ff ff 20 f0 23 87 05 88 ff ff .#..... .#..... 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: ... [] __alloc_skb+0x0/0x5a0 [] vhci_create_device+0x5c/0x580 [hci_vhci] [] vhci_write+0x306/0x4c8 [hci_vhci] Fixes: 23424c0d31 (Bluetooth: Add support creating virtual AMP controllers) Signed-off-by: Jiri Slaby Signed-off-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_vhci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index b6fb499a4531..4987f8b648f6 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -344,6 +344,7 @@ static int vhci_release(struct inode *inode, struct file *file) hci_free_dev(hdev); } + skb_queue_purge(&data->readq); file->private_data = NULL; kfree(data); From 2ceff6c403894da9de23a52c20bfcd81d8659a50 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 14 Apr 2016 17:32:19 +0200 Subject: [PATCH 082/656] Bluetooth: vhci: Fix race at creating hci device commit c7c999cb18da88a881e10e07f0724ad0bfaff770 upstream. hci_vhci driver creates a hci device object dynamically upon each HCI_VENDOR_PKT write. Although it checks the already created object and returns an error, it's still racy and may build multiple hci_dev objects concurrently when parallel writes are performed, as the device tracks only a single hci_dev object. This patch introduces a mutex to protect against the concurrent device creations. Signed-off-by: Takashi Iwai Signed-off-by: Marcel Holtmann Signed-off-by: Greg Kroah-Hartman --- drivers/bluetooth/hci_vhci.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 4987f8b648f6..597b2d16b775 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -50,6 +50,7 @@ struct vhci_data { wait_queue_head_t read_wait; struct sk_buff_head readq; + struct mutex open_mutex; struct delayed_work open_timeout; }; @@ -87,12 +88,15 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) return 0; } -static int vhci_create_device(struct vhci_data *data, __u8 opcode) +static int __vhci_create_device(struct vhci_data *data, __u8 opcode) { struct hci_dev *hdev; struct sk_buff *skb; __u8 dev_type; + if (data->hdev) + return -EBADFD; + /* bits 0-1 are dev_type (BR/EDR or AMP) */ dev_type = opcode & 0x03; @@ -151,6 +155,17 @@ static int vhci_create_device(struct vhci_data *data, __u8 opcode) return 0; } +static int vhci_create_device(struct vhci_data *data, __u8 opcode) +{ + int err; + + mutex_lock(&data->open_mutex); + err = __vhci_create_device(data, opcode); + mutex_unlock(&data->open_mutex); + + return err; +} + static inline ssize_t vhci_get_user(struct vhci_data *data, struct iov_iter *from) { @@ -191,11 +206,6 @@ static inline ssize_t vhci_get_user(struct vhci_data *data, case HCI_VENDOR_PKT: cancel_delayed_work_sync(&data->open_timeout); - if (data->hdev) { - kfree_skb(skb); - return -EBADFD; - } - opcode = *((__u8 *) skb->data); skb_pull(skb, 1); @@ -320,6 +330,7 @@ static int vhci_open(struct inode *inode, struct file *file) skb_queue_head_init(&data->readq); init_waitqueue_head(&data->read_wait); + mutex_init(&data->open_mutex); INIT_DELAYED_WORK(&data->open_timeout, vhci_open_timeout); file->private_data = data; From 2826506a7fc8a165292b109c5ad4456d60b1e580 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 17 Apr 2016 12:16:03 -0400 Subject: [PATCH 083/656] mei: fix NULL dereferencing during FW initiated disconnection commit 6a8d648c8d1824117a9e9edb948ed1611fb013c0 upstream. In the case when disconnection is initiated from the FW the driver is flushing items from the write control list while iterating over it: mei_irq_write_handler() list_for_each_entry_safe(ctrl_wr_list) <-- outer loop mei_cl_irq_disconnect_rsp() mei_cl_set_disconnected() mei_io_list_flush(ctrl_wr_list) <-- destorying list We move the list flushing to the completion routine. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 4 ++++ drivers/misc/mei/hbm.c | 3 +-- drivers/misc/mei/interrupt.c | 5 +---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index a6c87c713193..958af84884b5 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -1734,6 +1734,10 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) if (waitqueue_active(&cl->wait)) wake_up(&cl->wait); + break; + case MEI_FOP_DISCONNECT_RSP: + mei_io_cb_free(cb); + mei_cl_set_disconnected(cl); break; default: BUG_ON(0); diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index e7b7aad0999b..fd8a9f057ea6 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -873,8 +873,7 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT_RSP, NULL); if (!cb) return -ENOMEM; - cl_dbg(dev, cl, "add disconnect response as first\n"); - list_add(&cb->list, &dev->ctrl_wr_list.list); + list_add_tail(&cb->list, &dev->ctrl_wr_list.list); } return 0; } diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 64b568a0268d..44471edb62f7 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -184,10 +184,7 @@ static int mei_cl_irq_disconnect_rsp(struct mei_cl *cl, struct mei_cl_cb *cb, return -EMSGSIZE; ret = mei_hbm_cl_disconnect_rsp(dev, cl); - mei_cl_set_disconnected(cl); - mei_io_cb_free(cb); - mei_me_cl_put(cl->me_cl); - cl->me_cl = NULL; + list_move_tail(&cb->list, &cmpl_list->list); return ret; } From 2e6440e92503f08fe016f187a6534099e16ca4e9 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Sun, 17 Apr 2016 12:16:04 -0400 Subject: [PATCH 084/656] mei: amthif: discard not read messages commit 9d04ee11db7bf0d848266cbfd7db336097a0e239 upstream. When a message is received and amthif client is not in reading state the message is ignored and left dangling in the queue. This may happen after one of the amthif host connections is closed w/o completing the reading. Another client will pick up a wrong message on next read attempt which will lead to link reset. To prevent this the driver has to properly discard the message when amthif client is not in reading state. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/amthif.c | 4 +++- drivers/misc/mei/interrupt.c | 1 - drivers/misc/mei/mei_dev.h | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/misc/mei/amthif.c b/drivers/misc/mei/amthif.c index cd0403f09267..e79c0371ee6f 100644 --- a/drivers/misc/mei/amthif.c +++ b/drivers/misc/mei/amthif.c @@ -417,8 +417,10 @@ int mei_amthif_irq_read_msg(struct mei_cl *cl, dev = cl->dev; - if (dev->iamthif_state != MEI_IAMTHIF_READING) + if (dev->iamthif_state != MEI_IAMTHIF_READING) { + mei_irq_discard_msg(dev, mei_hdr); return 0; + } ret = mei_cl_irq_read_msg(cl, mei_hdr, cmpl_list); if (ret) diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 44471edb62f7..d1df797c7568 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -76,7 +76,6 @@ static inline int mei_cl_hbm_equal(struct mei_cl *cl, * @dev: mei device * @hdr: message header */ -static inline void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr) { /* diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 4250555d5e72..1b06e2fd6858 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -782,6 +782,8 @@ bool mei_hbuf_acquire(struct mei_device *dev); bool mei_write_is_idle(struct mei_device *dev); +void mei_irq_discard_msg(struct mei_device *dev, struct mei_msg_hdr *hdr); + #if IS_ENABLED(CONFIG_DEBUG_FS) int mei_dbgfs_register(struct mei_device *dev, const char *name); void mei_dbgfs_deregister(struct mei_device *dev); From 40f9ca60c564ab71221f5b7c7da58e476cef39d5 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 3 May 2016 18:54:21 -0400 Subject: [PATCH 085/656] mei: bus: call mei_cl_read_start under device lock commit bc46b45a421a64a0895dd41a34d3d2086e1ac7f6 upstream. Ensure that mei_cl_read_start is called under the device lock also in the bus layer. The function updates global ctrl_wr_list which should be locked. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/bus.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 1a173d0af694..a77643954523 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -222,17 +222,23 @@ EXPORT_SYMBOL_GPL(mei_cldev_recv); static void mei_cl_bus_event_work(struct work_struct *work) { struct mei_cl_device *cldev; + struct mei_device *bus; cldev = container_of(work, struct mei_cl_device, event_work); + bus = cldev->bus; + if (cldev->event_cb) cldev->event_cb(cldev, cldev->events, cldev->event_context); cldev->events = 0; /* Prepare for the next read */ - if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) + if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { + mutex_lock(&bus->device_lock); mei_cl_read_start(cldev->cl, 0, NULL); + mutex_unlock(&bus->device_lock); + } } /** @@ -296,6 +302,7 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev, unsigned long events_mask, mei_cldev_event_cb_t event_cb, void *context) { + struct mei_device *bus = cldev->bus; int ret; if (cldev->event_cb) @@ -308,15 +315,17 @@ int mei_cldev_register_event_cb(struct mei_cl_device *cldev, INIT_WORK(&cldev->event_work, mei_cl_bus_event_work); if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { + mutex_lock(&bus->device_lock); ret = mei_cl_read_start(cldev->cl, 0, NULL); + mutex_unlock(&bus->device_lock); if (ret && ret != -EBUSY) return ret; } if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) { - mutex_lock(&cldev->cl->dev->device_lock); + mutex_lock(&bus->device_lock); ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0); - mutex_unlock(&cldev->cl->dev->device_lock); + mutex_unlock(&bus->device_lock); if (ret) return ret; } From c0b572be507563e379a8c102b1bba57d18db26ef Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 8 May 2016 20:08:01 +0200 Subject: [PATCH 086/656] USB: serial: mxuport: fix use-after-free in probe error path commit 9e45284984096314994777f27e1446dfbfd2f0d7 upstream. The interface read and event URBs are submitted in attach, but were never explicitly unlinked by the driver. Instead the URBs would have been killed by usb-serial core on disconnect. In case of a late probe error (e.g. due to failed minor allocation), disconnect is never called and we could end up with active URBs for an unbound interface. This in turn could lead to deallocated memory being dereferenced in the completion callbacks. Fixes: ee467a1f2066 ("USB: serial: add Moxa UPORT 12XX/14XX/16XX driver") Signed-off-by: Johan Hovold Acked-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/mxuport.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index 31a8b47f1ac6..c6596cbcc4b6 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -1259,6 +1259,15 @@ static int mxuport_attach(struct usb_serial *serial) return 0; } +static void mxuport_release(struct usb_serial *serial) +{ + struct usb_serial_port *port0 = serial->port[0]; + struct usb_serial_port *port1 = serial->port[1]; + + usb_serial_generic_close(port1); + usb_serial_generic_close(port0); +} + static int mxuport_open(struct tty_struct *tty, struct usb_serial_port *port) { struct mxuport_port *mxport = usb_get_serial_port_data(port); @@ -1361,6 +1370,7 @@ static struct usb_serial_driver mxuport_device = { .probe = mxuport_probe, .port_probe = mxuport_port_probe, .attach = mxuport_attach, + .release = mxuport_release, .calc_num_ports = mxuport_calc_num_ports, .open = mxuport_open, .close = mxuport_close, From 00efa6c22dd1fbbdaf1239c95ee615180512a736 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 8 May 2016 20:07:58 +0200 Subject: [PATCH 087/656] USB: serial: keyspan: fix use-after-free in probe error path commit 35be1a71d70775e7bd7e45fa6d2897342ff4c9d2 upstream. The interface instat and indat URBs were submitted in attach, but never unlinked in release before deallocating the corresponding transfer buffers. In the case of a late probe error (e.g. due to failed minor allocation), disconnect would not have been called before release, causing the buffers to be freed while the URBs are still in use. We'd also end up with active URBs for an unbound interface. Fixes: f9c99bb8b3a1 ("USB: usb-serial: replace shutdown with disconnect, release") Signed-off-by: Johan Hovold Acked-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/keyspan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index e07b15ed5814..7faa901ee47f 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -2376,6 +2376,10 @@ static void keyspan_release(struct usb_serial *serial) s_priv = usb_get_serial_data(serial); + /* Make sure to unlink the URBs submitted in attach. */ + usb_kill_urb(s_priv->instat_urb); + usb_kill_urb(s_priv->indat_urb); + usb_free_urb(s_priv->instat_urb); usb_free_urb(s_priv->indat_urb); usb_free_urb(s_priv->glocont_urb); From 68f03961991a002e6143b9d771792d42fe8a26d2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 8 May 2016 20:08:02 +0200 Subject: [PATCH 088/656] USB: serial: quatech2: fix use-after-free in probe error path commit 028c49f5e02a257c94129cd815f7c8485f51d4ef upstream. The interface read URB is submitted in attach, but was only unlinked by the driver at disconnect. In case of a late probe error (e.g. due to failed minor allocation), disconnect is never called and we would end up with active URBs for an unbound interface. This in turn could lead to deallocated memory being dereferenced in the completion callback. Fixes: f7a33e608d9a ("USB: serial: add quatech2 usb to serial driver") Signed-off-by: Johan Hovold Acked-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/quatech2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index 504f5bff79c0..b18974cbd995 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -141,6 +141,7 @@ static void qt2_release(struct usb_serial *serial) serial_priv = usb_get_serial_data(serial); + usb_kill_urb(serial_priv->read_urb); usb_free_urb(serial_priv->read_urb); kfree(serial_priv->read_buffer); kfree(serial_priv); From d6f695703c8decb0ef9740de50829961ecdcd639 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 8 May 2016 20:07:56 +0200 Subject: [PATCH 089/656] USB: serial: io_edgeport: fix memory leaks in attach error path commit c5c0c55598cefc826d6cfb0a417eeaee3631715c upstream. Private data, URBs and buffers allocated for Epic devices during attach were never released on errors (e.g. missing endpoints). Fixes: 6e8cf7751f9f ("USB: add EPIC support to the io_edgeport driver") Signed-off-by: Johan Hovold Acked-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_edgeport.c | 39 +++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index c0866971db2b..1106e7d9ced0 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2856,14 +2856,16 @@ static int edge_startup(struct usb_serial *serial) /* not set up yet, so do it now */ edge_serial->interrupt_read_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!edge_serial->interrupt_read_urb) - return -ENOMEM; + if (!edge_serial->interrupt_read_urb) { + response = -ENOMEM; + break; + } edge_serial->interrupt_in_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!edge_serial->interrupt_in_buffer) { - usb_free_urb(edge_serial->interrupt_read_urb); - return -ENOMEM; + response = -ENOMEM; + break; } edge_serial->interrupt_in_endpoint = endpoint->bEndpointAddress; @@ -2891,14 +2893,16 @@ static int edge_startup(struct usb_serial *serial) /* not set up yet, so do it now */ edge_serial->read_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!edge_serial->read_urb) - return -ENOMEM; + if (!edge_serial->read_urb) { + response = -ENOMEM; + break; + } edge_serial->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!edge_serial->bulk_in_buffer) { - usb_free_urb(edge_serial->read_urb); - return -ENOMEM; + response = -ENOMEM; + break; } edge_serial->bulk_in_endpoint = endpoint->bEndpointAddress; @@ -2924,9 +2928,22 @@ static int edge_startup(struct usb_serial *serial) } } - if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) { - dev_err(ddev, "Error - the proper endpoints were not found!\n"); - return -ENODEV; + if (response || !interrupt_in_found || !bulk_in_found || + !bulk_out_found) { + if (!response) { + dev_err(ddev, "expected endpoints not found\n"); + response = -ENODEV; + } + + usb_free_urb(edge_serial->interrupt_read_urb); + kfree(edge_serial->interrupt_in_buffer); + + usb_free_urb(edge_serial->read_urb); + kfree(edge_serial->bulk_in_buffer); + + kfree(edge_serial); + + return response; } /* start interrupt read for this edgeport this interrupt will From 7cb5461cf860cccf2ec36f821ee36b8baed9515b Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 8 May 2016 20:07:57 +0200 Subject: [PATCH 090/656] USB: serial: io_edgeport: fix memory leaks in probe error path commit c8d62957d450cc1a22ce3242908709fe367ddc8e upstream. URBs and buffers allocated in attach for Epic devices would never be deallocated in case of a later probe error (e.g. failure to allocate minor numbers) as disconnect is then never called. Fix by moving deallocation to release and making sure that the URBs are first unlinked. Fixes: f9c99bb8b3a1 ("USB: usb-serial: replace shutdown with disconnect, release") Signed-off-by: Johan Hovold Acked-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/io_edgeport.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 1106e7d9ced0..1947ea0e0988 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -2966,16 +2966,9 @@ static void edge_disconnect(struct usb_serial *serial) { struct edgeport_serial *edge_serial = usb_get_serial_data(serial); - /* stop reads and writes on all ports */ - /* free up our endpoint stuff */ if (edge_serial->is_epic) { usb_kill_urb(edge_serial->interrupt_read_urb); - usb_free_urb(edge_serial->interrupt_read_urb); - kfree(edge_serial->interrupt_in_buffer); - usb_kill_urb(edge_serial->read_urb); - usb_free_urb(edge_serial->read_urb); - kfree(edge_serial->bulk_in_buffer); } } @@ -2988,6 +2981,16 @@ static void edge_release(struct usb_serial *serial) { struct edgeport_serial *edge_serial = usb_get_serial_data(serial); + if (edge_serial->is_epic) { + usb_kill_urb(edge_serial->interrupt_read_urb); + usb_free_urb(edge_serial->interrupt_read_urb); + kfree(edge_serial->interrupt_in_buffer); + + usb_kill_urb(edge_serial->read_urb); + usb_free_urb(edge_serial->read_urb); + kfree(edge_serial->bulk_in_buffer); + } + kfree(edge_serial); } From 50e765292be2584a24c8e06740091d345c8be567 Mon Sep 17 00:00:00 2001 From: Schemmel Hans-Christoph Date: Fri, 29 Apr 2016 08:51:06 +0000 Subject: [PATCH 091/656] USB: serial: option: add support for Cinterion PH8 and AHxx commit 444f94e9e625f6ec6bbe2cb232a6451c637f35a3 upstream. Added support for Gemalto's Cinterion PH8 and AHxx products with 2 RmNet Interfaces and products with 1 RmNet + 1 USB Audio interface. In addition some minor renaming and formatting. Signed-off-by: Hans-Christoph Schemmel [johan: sort current entries and trim trailing whitespace ] Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index c6f497f16526..ab542d3ee2b1 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -375,18 +375,22 @@ static void option_instat_callback(struct urb *urb); #define HAIER_PRODUCT_CE81B 0x10f8 #define HAIER_PRODUCT_CE100 0x2009 -/* Cinterion (formerly Siemens) products */ -#define SIEMENS_VENDOR_ID 0x0681 -#define CINTERION_VENDOR_ID 0x1e2d +/* Gemalto's Cinterion products (formerly Siemens) */ +#define SIEMENS_VENDOR_ID 0x0681 +#define CINTERION_VENDOR_ID 0x1e2d +#define CINTERION_PRODUCT_HC25_MDMNET 0x0040 #define CINTERION_PRODUCT_HC25_MDM 0x0047 -#define CINTERION_PRODUCT_HC25_MDMNET 0x0040 +#define CINTERION_PRODUCT_HC28_MDMNET 0x004A /* same for HC28J */ #define CINTERION_PRODUCT_HC28_MDM 0x004C -#define CINTERION_PRODUCT_HC28_MDMNET 0x004A /* same for HC28J */ #define CINTERION_PRODUCT_EU3_E 0x0051 #define CINTERION_PRODUCT_EU3_P 0x0052 #define CINTERION_PRODUCT_PH8 0x0053 #define CINTERION_PRODUCT_AHXX 0x0055 #define CINTERION_PRODUCT_PLXX 0x0060 +#define CINTERION_PRODUCT_PH8_2RMNET 0x0082 +#define CINTERION_PRODUCT_PH8_AUDIO 0x0083 +#define CINTERION_PRODUCT_AHXX_2RMNET 0x0084 +#define CINTERION_PRODUCT_AHXX_AUDIO 0x0085 /* Olivetti products */ #define OLIVETTI_VENDOR_ID 0x0b3c @@ -633,6 +637,10 @@ static const struct option_blacklist_info telit_le922_blacklist_usbcfg3 = { .reserved = BIT(1) | BIT(2) | BIT(3), }; +static const struct option_blacklist_info cinterion_rmnet2_blacklist = { + .reserved = BIT(4) | BIT(5), +}; + static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -1712,7 +1720,13 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX, 0xff) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PLXX), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, - { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, + { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_2RMNET, 0xff), + .driver_info = (kernel_ulong_t)&cinterion_rmnet2_blacklist }, + { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_PH8_AUDIO, 0xff), + .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_2RMNET, 0xff) }, + { USB_DEVICE_INTERFACE_CLASS(CINTERION_VENDOR_ID, CINTERION_PRODUCT_AHXX_AUDIO, 0xff) }, + { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDM) }, { USB_DEVICE(CINTERION_VENDOR_ID, CINTERION_PRODUCT_HC28_MDMNET) }, { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDM) }, { USB_DEVICE(SIEMENS_VENDOR_ID, CINTERION_PRODUCT_HC25_MDMNET) }, From 9ec187e54b8630fe7235bbd66882aeb116b08d23 Mon Sep 17 00:00:00 2001 From: lei liu Date: Tue, 3 May 2016 14:44:19 -0700 Subject: [PATCH 092/656] USB: serial: option: add more ZTE device ids commit f0d09463c59c2d764a6c6d492cbe6d2c77f27153 upstream. More ZTE device ids. Signed-off-by: lei liu [properly sort them - gregkh] Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 75 ++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index ab542d3ee2b1..6ac8630162ca 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1610,7 +1610,79 @@ static const struct usb_device_id option_ids[] = { .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0178, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf3_blacklist }, - { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff42, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff43, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff44, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff45, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff46, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff47, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff48, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff49, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4a, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4b, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4c, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4d, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4e, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff4f, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff50, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff51, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff52, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff53, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff54, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff55, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff56, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff57, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff58, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff59, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5a, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5b, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5c, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5d, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5e, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff5f, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff60, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff61, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff62, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff63, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff64, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff65, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff66, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff67, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff68, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff69, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6a, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6b, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6c, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6d, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6e, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff6f, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff70, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff71, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff72, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff73, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff74, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff75, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff76, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff77, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff78, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff79, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7a, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7b, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7c, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7d, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7e, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff7f, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff80, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff81, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff82, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff83, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff84, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff85, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff86, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff87, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff88, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff89, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8a, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8b, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8c, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff8d, 0xff, 0xff, 0xff) }, @@ -1621,6 +1693,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff92, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffec, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffee, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xfff6, 0xff, 0xff, 0xff) }, From 92f54c192b643da15504bc022dd6f17c2f930266 Mon Sep 17 00:00:00 2001 From: Lei Liu Date: Wed, 4 May 2016 16:34:22 +0800 Subject: [PATCH 093/656] USB: serial: option: add even more ZTE device ids commit 74d2a91aec97ab832790c9398d320413ad185321 upstream. Add even more ZTE device ids. Signed-off-by: lei liu [johan: rebase and replace commit message ] Signed-off-by: Johan Hovold Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 6ac8630162ca..d96d423d00e6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1693,6 +1693,60 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff92, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff93, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff94, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xff9f, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa0, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa1, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa2, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa3, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa4, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa5, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa6, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa7, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa8, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffa9, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaa, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffab, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffac, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffae, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffaf, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb0, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb1, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb2, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb3, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb4, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb5, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb6, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb7, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb8, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffb9, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffba, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbb, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbc, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbd, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbe, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffbf, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc0, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc1, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc2, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc3, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc4, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc5, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc6, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc7, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc8, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffc9, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffca, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcb, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcc, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcd, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffce, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffcf, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd0, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd1, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd2, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd3, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd4, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffd5, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffe9, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffec, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0xffee, 0xff, 0xff, 0xff) }, From ed97f0d96dfad18989b922c4c4095e9d06230bf6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 30 Mar 2016 13:49:14 +0200 Subject: [PATCH 094/656] usb: gadget: f_fs: Fix EFAULT generation for async read operations commit 332a5b446b7916d272c2a659a3b20909ce34d2c1 upstream. In the current implementation functionfs generates a EFAULT for async read operations if the read buffer size is larger than the URB data size. Since a application does not necessarily know how much data the host side is going to send it typically supplies a buffer larger than the actual data, which will then result in a EFAULT error. This behaviour was introduced while refactoring the code to use iov_iter interface in commit c993c39b8639 ("gadget/function/f_fs.c: use put iov_iter into io_data"). The original code took the minimum over the URB size and the user buffer size and then attempted to copy that many bytes using copy_to_user(). If copy_to_user() could not copy all data a EFAULT error was generated. Restore the original behaviour by only generating a EFAULT error when the number of bytes copied is not the size of the URB and the target buffer has not been fully filled. Commit 342f39a6c8d3 ("usb: gadget: f_fs: fix check in read operation") already fixed the same problem for the synchronous read path. Fixes: c993c39b8639 ("gadget/function/f_fs.c: use put iov_iter into io_data") Acked-by: Michal Nazarewicz Signed-off-by: Lars-Peter Clausen Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_fs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 79d895c2dd71..97ef75af9632 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -651,7 +651,7 @@ static void ffs_user_copy_worker(struct work_struct *work) if (io_data->read && ret > 0) { use_mm(io_data->mm); ret = copy_to_iter(io_data->buf, ret, &io_data->data); - if (iov_iter_count(&io_data->data)) + if (ret != io_data->req->actual && iov_iter_count(&io_data->data)) ret = -EFAULT; unuse_mm(io_data->mm); } From 51c312792d3bb676bb1d4faa5cd0dd56d7ea2e60 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Fri, 8 Apr 2016 10:24:11 +0200 Subject: [PATCH 095/656] usb: f_mass_storage: test whether thread is running before starting another MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f78bbcae86e676fad9e6c6bb6cd9d9868ba23696 upstream. When binding the function to usb_configuration, check whether the thread is running before starting another one. Without that, when function instance is added to multiple configurations, fsg_bing starts multiple threads with all but the latest one being forgotten by the driver. This leads to obvious thread leaks, possible lockups when trying to halt the machine and possible more issues. This fixes issues with legacy/multi¹ gadget as well as configfs gadgets when mass_storage function is added to multiple configurations. This change also simplifies API since the legacy gadgets no longer need to worry about starting the thread by themselves (which was where bug in legacy/multi was in the first place). N.B., this patch doesn’t address adding single mass_storage function instance to a single configuration twice. Thankfully, there’s no legitimate reason for such setup plus, if I’m not mistaken, configfs gadget doesn’t even allow it to be expressed. ¹ I have no example failure though. Conclusion that legacy/multi has a bug is based purely on me reading the code. Acked-by: Alan Stern Signed-off-by: Michal Nazarewicz Tested-by: Ivaylo Dimitrov Cc: Alan Stern Signed-off-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_mass_storage.c | 36 ++++++++------------ drivers/usb/gadget/function/f_mass_storage.h | 2 -- drivers/usb/gadget/legacy/acm_ms.c | 4 --- drivers/usb/gadget/legacy/mass_storage.c | 4 --- drivers/usb/gadget/legacy/multi.c | 12 ------- drivers/usb/gadget/legacy/nokia.c | 7 ---- 6 files changed, 15 insertions(+), 50 deletions(-) diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 223ccf89d226..a4f664062e0c 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2977,25 +2977,6 @@ void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, } EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string); -int fsg_common_run_thread(struct fsg_common *common) -{ - common->state = FSG_STATE_IDLE; - /* Tell the thread to start working */ - common->thread_task = - kthread_create(fsg_main_thread, common, "file-storage"); - if (IS_ERR(common->thread_task)) { - common->state = FSG_STATE_TERMINATED; - return PTR_ERR(common->thread_task); - } - - DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task)); - - wake_up_process(common->thread_task); - - return 0; -} -EXPORT_SYMBOL_GPL(fsg_common_run_thread); - static void fsg_common_release(struct kref *ref) { struct fsg_common *common = container_of(ref, struct fsg_common, ref); @@ -3005,6 +2986,7 @@ static void fsg_common_release(struct kref *ref) if (common->state != FSG_STATE_TERMINATED) { raise_exception(common, FSG_STATE_EXIT); wait_for_completion(&common->thread_notifier); + common->thread_task = NULL; } for (i = 0; i < ARRAY_SIZE(common->luns); ++i) { @@ -3050,9 +3032,21 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) if (ret) return ret; fsg_common_set_inquiry_string(fsg->common, NULL, NULL); - ret = fsg_common_run_thread(fsg->common); - if (ret) + } + + if (!common->thread_task) { + common->state = FSG_STATE_IDLE; + common->thread_task = + kthread_create(fsg_main_thread, common, "file-storage"); + if (IS_ERR(common->thread_task)) { + int ret = PTR_ERR(common->thread_task); + common->thread_task = NULL; + common->state = FSG_STATE_TERMINATED; return ret; + } + DBG(common, "I/O thread pid: %d\n", + task_pid_nr(common->thread_task)); + wake_up_process(common->thread_task); } fsg->gadget = gadget; diff --git a/drivers/usb/gadget/function/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h index 445df6775609..b6a9918eaefb 100644 --- a/drivers/usb/gadget/function/f_mass_storage.h +++ b/drivers/usb/gadget/function/f_mass_storage.h @@ -153,8 +153,6 @@ int fsg_common_create_luns(struct fsg_common *common, struct fsg_config *cfg); void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, const char *pn); -int fsg_common_run_thread(struct fsg_common *common); - void fsg_config_from_params(struct fsg_config *cfg, const struct fsg_module_parameters *params, unsigned int fsg_num_buffers); diff --git a/drivers/usb/gadget/legacy/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c index 4b158e2d1e57..64b2cbb0bc6b 100644 --- a/drivers/usb/gadget/legacy/acm_ms.c +++ b/drivers/usb/gadget/legacy/acm_ms.c @@ -133,10 +133,6 @@ static int acm_ms_do_config(struct usb_configuration *c) if (status < 0) goto put_msg; - status = fsg_common_run_thread(opts->common); - if (status) - goto remove_acm; - status = usb_add_function(c, f_msg); if (status) goto remove_acm; diff --git a/drivers/usb/gadget/legacy/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c index bda3c519110f..99aa22c81770 100644 --- a/drivers/usb/gadget/legacy/mass_storage.c +++ b/drivers/usb/gadget/legacy/mass_storage.c @@ -132,10 +132,6 @@ static int msg_do_config(struct usb_configuration *c) if (IS_ERR(f_msg)) return PTR_ERR(f_msg); - ret = fsg_common_run_thread(opts->common); - if (ret) - goto put_func; - ret = usb_add_function(c, f_msg); if (ret) goto put_func; diff --git a/drivers/usb/gadget/legacy/multi.c b/drivers/usb/gadget/legacy/multi.c index 4fe794ddcd49..09c7c28f32f7 100644 --- a/drivers/usb/gadget/legacy/multi.c +++ b/drivers/usb/gadget/legacy/multi.c @@ -137,7 +137,6 @@ static struct usb_function *f_msg_rndis; static int rndis_do_config(struct usb_configuration *c) { - struct fsg_opts *fsg_opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -169,11 +168,6 @@ static int rndis_do_config(struct usb_configuration *c) goto err_fsg; } - fsg_opts = fsg_opts_from_func_inst(fi_msg); - ret = fsg_common_run_thread(fsg_opts->common); - if (ret) - goto err_run; - ret = usb_add_function(c, f_msg_rndis); if (ret) goto err_run; @@ -225,7 +219,6 @@ static struct usb_function *f_msg_multi; static int cdc_do_config(struct usb_configuration *c) { - struct fsg_opts *fsg_opts; int ret; if (gadget_is_otg(c->cdev->gadget)) { @@ -258,11 +251,6 @@ static int cdc_do_config(struct usb_configuration *c) goto err_fsg; } - fsg_opts = fsg_opts_from_func_inst(fi_msg); - ret = fsg_common_run_thread(fsg_opts->common); - if (ret) - goto err_run; - ret = usb_add_function(c, f_msg_multi); if (ret) goto err_run; diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c index 8b3f6fb1825d..05d3f79e768d 100644 --- a/drivers/usb/gadget/legacy/nokia.c +++ b/drivers/usb/gadget/legacy/nokia.c @@ -152,7 +152,6 @@ static int nokia_bind_config(struct usb_configuration *c) struct usb_function *f_ecm; struct usb_function *f_obex2 = NULL; struct usb_function *f_msg; - struct fsg_opts *fsg_opts; int status = 0; int obex1_stat = -1; int obex2_stat = -1; @@ -222,12 +221,6 @@ static int nokia_bind_config(struct usb_configuration *c) goto err_ecm; } - fsg_opts = fsg_opts_from_func_inst(fi_msg); - - status = fsg_common_run_thread(fsg_opts->common); - if (status) - goto err_msg; - status = usb_add_function(c, f_msg); if (status) goto err_msg; From e3a037a5b851f808883b7bc6c192195635dcb758 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Mon, 2 May 2016 11:39:03 +0300 Subject: [PATCH 096/656] usb: misc: usbtest: fix pattern tests for scatterlists. commit cdc77c82a8286b1181b81b6e5ef60c8e83ded7bc upstream. The current implemenentation restart the sent pattern for each entry in the sg list. The receiving end expects a continuous pattern, and test will fail unless scatterilst entries happen to be aligned with the pattern Fix this by calculating the pattern byte based on total sent size instead of just the current sg entry. Signed-off-by: Mathias Nyman Fixes: 8b5249019352 ("[PATCH] USB: usbtest: scatterlist OUT data pattern testing") Acked-by: Felipe Balbi Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usbtest.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 637f3f7cfce8..1a812eafe670 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -505,6 +505,7 @@ static struct scatterlist * alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe) { struct scatterlist *sg; + unsigned int n_size = 0; unsigned i; unsigned size = max; unsigned maxpacket = @@ -537,7 +538,8 @@ alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe) break; case 1: for (j = 0; j < size; j++) - *buf++ = (u8) ((j % maxpacket) % 63); + *buf++ = (u8) (((j + n_size) % maxpacket) % 63); + n_size += size; break; } From 15e67f90026240145fc9ffa84ede62ea35de2971 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Fri, 29 Apr 2016 15:25:17 -0400 Subject: [PATCH 097/656] USB: leave LPM alone if possible when binding/unbinding interface drivers commit 6fb650d43da3e7054984dc548eaa88765a94d49f upstream. When a USB driver is bound to an interface (either through probing or by claiming it) or is unbound from an interface, the USB core always disables Link Power Management during the transition and then re-enables it afterward. The reason is because the driver might want to prevent hub-initiated link power transitions, in which case the HCD would have to recalculate the various LPM parameters. This recalculation takes place when LPM is re-enabled and the new parameters are sent to the device and its parent hub. However, if the driver does not want to prevent hub-initiated link power transitions then none of this work is necessary. The parameters don't need to be recalculated, and LPM doesn't need to be disabled and re-enabled. It turns out that disabling and enabling LPM can be time-consuming, enough so that it interferes with user programs that want to claim and release interfaces rapidly via usbfs. Since the usbfs kernel driver doesn't set the disable_hub_initiated_lpm flag, we can speed things up and get the user programs to work by leaving LPM alone whenever the flag isn't set. And while we're improving the way disable_hub_initiated_lpm gets used, let's also fix its kerneldoc. Signed-off-by: Alan Stern Tested-by: Matthew Giassa CC: Mathias Nyman Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 40 ++++++++++++++++++++++----------------- include/linux/usb.h | 2 +- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2057d91d8336..dadd1e8dfe09 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -284,7 +284,7 @@ static int usb_probe_interface(struct device *dev) struct usb_device *udev = interface_to_usbdev(intf); const struct usb_device_id *id; int error = -ENODEV; - int lpm_disable_error; + int lpm_disable_error = -ENODEV; dev_dbg(dev, "%s\n", __func__); @@ -336,12 +336,14 @@ static int usb_probe_interface(struct device *dev) * setting during probe, that should also be fine. usb_set_interface() * will attempt to disable LPM, and fail if it can't disable it. */ - lpm_disable_error = usb_unlocked_disable_lpm(udev); - if (lpm_disable_error && driver->disable_hub_initiated_lpm) { - dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.", - __func__, driver->name); - error = lpm_disable_error; - goto err; + if (driver->disable_hub_initiated_lpm) { + lpm_disable_error = usb_unlocked_disable_lpm(udev); + if (lpm_disable_error) { + dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.", + __func__, driver->name); + error = lpm_disable_error; + goto err; + } } /* Carry out a deferred switch to altsetting 0 */ @@ -391,7 +393,8 @@ static int usb_unbind_interface(struct device *dev) struct usb_interface *intf = to_usb_interface(dev); struct usb_host_endpoint *ep, **eps = NULL; struct usb_device *udev; - int i, j, error, r, lpm_disable_error; + int i, j, error, r; + int lpm_disable_error = -ENODEV; intf->condition = USB_INTERFACE_UNBINDING; @@ -399,12 +402,13 @@ static int usb_unbind_interface(struct device *dev) udev = interface_to_usbdev(intf); error = usb_autoresume_device(udev); - /* Hub-initiated LPM policy may change, so attempt to disable LPM until + /* If hub-initiated LPM policy may change, attempt to disable LPM until * the driver is unbound. If LPM isn't disabled, that's fine because it * wouldn't be enabled unless all the bound interfaces supported * hub-initiated LPM. */ - lpm_disable_error = usb_unlocked_disable_lpm(udev); + if (driver->disable_hub_initiated_lpm) + lpm_disable_error = usb_unlocked_disable_lpm(udev); /* * Terminate all URBs for this interface unless the driver @@ -505,7 +509,7 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct device *dev; struct usb_device *udev; int retval = 0; - int lpm_disable_error; + int lpm_disable_error = -ENODEV; if (!iface) return -ENODEV; @@ -526,12 +530,14 @@ int usb_driver_claim_interface(struct usb_driver *driver, iface->condition = USB_INTERFACE_BOUND; - /* Disable LPM until this driver is bound. */ - lpm_disable_error = usb_unlocked_disable_lpm(udev); - if (lpm_disable_error && driver->disable_hub_initiated_lpm) { - dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.", - __func__, driver->name); - return -ENOMEM; + /* See the comment about disabling LPM in usb_probe_interface(). */ + if (driver->disable_hub_initiated_lpm) { + lpm_disable_error = usb_unlocked_disable_lpm(udev); + if (lpm_disable_error) { + dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.", + __func__, driver->name); + return -ENOMEM; + } } /* Claimed interfaces are initially inactive (suspended) and diff --git a/include/linux/usb.h b/include/linux/usb.h index b79925dd2b41..12891ffd4bf0 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1068,7 +1068,7 @@ struct usbdrv_wrap { * for interfaces bound to this driver. * @soft_unbind: if set to 1, the USB core will not kill URBs and disable * endpoints before calling the driver's disconnect method. - * @disable_hub_initiated_lpm: if set to 0, the USB core will not allow hubs + * @disable_hub_initiated_lpm: if set to 1, the USB core will not allow hubs * to initiate lower power link state transitions when an idle timeout * occurs. Device-initiated USB 3.0 link PM will still be allowed. * From 53c24ba2dd1da897950baef016f2f2d08d95cbf7 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 18 Apr 2016 16:53:38 +0900 Subject: [PATCH 098/656] usb: gadget: udc: core: Fix argument of dev_err() in usb_gadget_map_request() commit 5096c4d3bfa75bdd23c78f799aabd08598afb48f upstream. The argument of dev_err() in usb_gadget_map_request() should be dev instead of &gadget->dev. Fixes: 7ace8fc ("usb: gadget: udc: core: Fix argument of dma_map_single for IOMMU") Signed-off-by: Yoshihiro Shimoda Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/udc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index f660afba715d..89f7cd66f5e6 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -71,7 +71,7 @@ int usb_gadget_map_request(struct usb_gadget *gadget, mapped = dma_map_sg(dev, req->sg, req->num_sgs, is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); if (mapped == 0) { - dev_err(&gadget->dev, "failed to map SGs\n"); + dev_err(dev, "failed to map SGs\n"); return -EFAULT; } From 023bea74522670cdedcc54dea5bec1b64e3f81e3 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 8 Apr 2016 10:14:58 -0700 Subject: [PATCH 099/656] staging: comedi: das1800: fix possible NULL dereference commit d375278d666760e195693b57415ba0a125cadd55 upstream. DMA is optional with this driver. If it was not enabled the devpriv->dma pointer will be NULL. Fix the possible NULL pointer dereference when trying to disable the DMA channels in das1800_ai_cancel() and tidy up the comments to fix the checkpatch.pl issues: WARNING: line over 80 characters It's probably harmless in das1800_ai_setup_dma() because the 'desc' pointer will not be used if DMA is disabled but fix it there also. Fixes: 99dfc3357e98 ("staging: comedi: das1800: remove depends on ISA_DMA_API limitation") Signed-off-by: H Hartley Sweeten Reviewed-by: Ian Abbott Signed-off-by: Greg Kroah-Hartman --- drivers/staging/comedi/drivers/das1800.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/staging/comedi/drivers/das1800.c b/drivers/staging/comedi/drivers/das1800.c index 940781183fac..3be10963f98b 100644 --- a/drivers/staging/comedi/drivers/das1800.c +++ b/drivers/staging/comedi/drivers/das1800.c @@ -567,14 +567,17 @@ static int das1800_cancel(struct comedi_device *dev, struct comedi_subdevice *s) struct comedi_isadma_desc *desc; int i; - outb(0x0, dev->iobase + DAS1800_STATUS); /* disable conversions */ - outb(0x0, dev->iobase + DAS1800_CONTROL_B); /* disable interrupts and dma */ - outb(0x0, dev->iobase + DAS1800_CONTROL_A); /* disable and clear fifo and stop triggering */ + /* disable and stop conversions */ + outb(0x0, dev->iobase + DAS1800_STATUS); + outb(0x0, dev->iobase + DAS1800_CONTROL_B); + outb(0x0, dev->iobase + DAS1800_CONTROL_A); - for (i = 0; i < 2; i++) { - desc = &dma->desc[i]; - if (desc->chan) - comedi_isadma_disable(desc->chan); + if (dma) { + for (i = 0; i < 2; i++) { + desc = &dma->desc[i]; + if (desc->chan) + comedi_isadma_disable(desc->chan); + } } return 0; @@ -934,13 +937,14 @@ static void das1800_ai_setup_dma(struct comedi_device *dev, { struct das1800_private *devpriv = dev->private; struct comedi_isadma *dma = devpriv->dma; - struct comedi_isadma_desc *desc = &dma->desc[0]; + struct comedi_isadma_desc *desc; unsigned int bytes; if ((devpriv->irq_dma_bits & DMA_ENABLED) == 0) return; dma->cur_dma = 0; + desc = &dma->desc[0]; /* determine a dma transfer size to fill buffer in 0.3 sec */ bytes = das1800_ai_transfer_size(dev, s, desc->maxsize, 300000000); From 1716643bc4725bb92a1f527f759a93e37a2bc1f0 Mon Sep 17 00:00:00 2001 From: Andy Honig Date: Tue, 17 May 2016 17:41:47 +0200 Subject: [PATCH 100/656] KVM: MTRR: remove MSR 0x2f8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 9842df62004f366b9fed2423e24df10542ee0dc5 upstream. MSR 0x2f8 accessed the 124th Variable Range MTRR ever since MTRR support was introduced by 9ba075a664df ("KVM: MTRR support"). 0x2f8 became harmful when 910a6aae4e2e ("KVM: MTRR: exactly define the size of variable MTRRs") shrinked the array of VR MTRRs from 256 to 8, which made access to index 124 out of bounds. The surrounding code only WARNs in this situation, thus the guest gained a limited read/write access to struct kvm_arch_vcpu. 0x2f8 is not a valid VR MTRR MSR, because KVM has/advertises only 16 VR MTRR MSRs, 0x200-0x20f. Every VR MTRR is set up using two MSRs, 0x2f8 was treated as a PHYSBASE and 0x2f9 would be its PHYSMASK, but 0x2f9 was not implemented in KVM, therefore 0x2f8 could never do anything useful and getting rid of it is safe. This fixes CVE-2016-3713. Fixes: 910a6aae4e2e ("KVM: MTRR: exactly define the size of variable MTRRs") Reported-by: David Matlack Signed-off-by: Andy Honig Signed-off-by: Radim Krčmář Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/mtrr.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/x86/kvm/mtrr.c b/arch/x86/kvm/mtrr.c index 3f8c732117ec..c146f3c262c3 100644 --- a/arch/x86/kvm/mtrr.c +++ b/arch/x86/kvm/mtrr.c @@ -44,8 +44,6 @@ static bool msr_mtrr_valid(unsigned msr) case MSR_MTRRdefType: case MSR_IA32_CR_PAT: return true; - case 0x2f8: - return true; } return false; } From 4a0041b6382601e5cc2427b4a586a65361db9722 Mon Sep 17 00:00:00 2001 From: Bruce Rogers Date: Thu, 28 Apr 2016 14:49:21 -0600 Subject: [PATCH 101/656] KVM: x86: fix ordering of cr0 initialization code in vmx_cpu_reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit f24632475d4ffed5626abbfab7ef30a128dd1474 upstream. Commit d28bc9dd25ce reversed the order of two lines which initialize cr0, allowing the current (old) cr0 value to mess up vcpu initialization. This was observed in the checks for cr0 X86_CR0_WP bit in the context of kvm_mmu_reset_context(). Besides, setting vcpu->arch.cr0 after vmx_set_cr0() is completely redundant. Change the order back to ensure proper vcpu initialization. The combination of booting with ovmf firmware when guest vcpus > 1 and kvm's ept=N option being set results in a VM-entry failure. This patch fixes that. Fixes: d28bc9dd25ce ("KVM: x86: INIT and reset sequences are different") Signed-off-by: Bruce Rogers Signed-off-by: Radim Krčmář Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/vmx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index f34ab71dfd57..f314e9b9660b 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -4954,8 +4954,8 @@ static void vmx_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid); cr0 = X86_CR0_NW | X86_CR0_CD | X86_CR0_ET; - vmx_set_cr0(vcpu, cr0); /* enter rmode */ vmx->vcpu.arch.cr0 = cr0; + vmx_set_cr0(vcpu, cr0); /* enter rmode */ vmx_set_cr4(vcpu, 0); vmx_set_efer(vcpu, 0); vmx_fpu_activate(vcpu); From 9e01c02f98f98c03cba7e147577364b667d03808 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 22 Apr 2016 10:38:45 +0100 Subject: [PATCH 102/656] MIPS: KVM: Fix timer IRQ race when freezing timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 4355c44f063d3de4f072d796604c7f4ba4085cc3 upstream. There's a particularly narrow and subtle race condition when the software emulated guest timer is frozen which can allow a guest timer interrupt to be missed. This happens due to the hrtimer expiry being inexact, so very occasionally the freeze time will be after the moment when the emulated CP0_Count transitions to the same value as CP0_Compare (so an IRQ should be generated), but before the moment when the hrtimer is due to expire (so no IRQ is generated). The IRQ won't be generated when the timer is resumed either, since the resume CP0_Count will already match CP0_Compare. With VZ guests in particular this is far more likely to happen, since the soft timer may be frozen frequently in order to restore the timer state to the hardware guest timer. This happens after 5-10 hours of guest soak testing, resulting in an overflow in guest kernel timekeeping calculations, hanging the guest. A more focussed test case to intentionally hit the race (with the help of a new hypcall to cause the timer state to migrated between hardware & software) hits the condition fairly reliably within around 30 seconds. Instead of relying purely on the inexact hrtimer expiry to determine whether an IRQ should be generated, read the guest CP0_Compare and directly check whether the freeze time is before or after it. Only if CP0_Count is on or after CP0_Compare do we check the hrtimer expiry to determine whether the last IRQ has already been generated (which will have pushed back the expiry by one timer period). Fixes: e30492bbe95a ("MIPS: KVM: Rewrite count/compare timer emulation") Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: "Radim Krčmář" Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: kvm@vger.kernel.org Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/mips/kvm/emulate.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c index 41b1b090f56f..eaf77b5479ce 100644 --- a/arch/mips/kvm/emulate.c +++ b/arch/mips/kvm/emulate.c @@ -302,12 +302,31 @@ static inline ktime_t kvm_mips_count_time(struct kvm_vcpu *vcpu) */ static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now) { - ktime_t expires; + struct mips_coproc *cop0 = vcpu->arch.cop0; + ktime_t expires, threshold; + uint32_t count, compare; int running; - /* Is the hrtimer pending? */ + /* Calculate the biased and scaled guest CP0_Count */ + count = vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now); + compare = kvm_read_c0_guest_compare(cop0); + + /* + * Find whether CP0_Count has reached the closest timer interrupt. If + * not, we shouldn't inject it. + */ + if ((int32_t)(count - compare) < 0) + return count; + + /* + * The CP0_Count we're going to return has already reached the closest + * timer interrupt. Quickly check if it really is a new interrupt by + * looking at whether the interval until the hrtimer expiry time is + * less than 1/4 of the timer period. + */ expires = hrtimer_get_expires(&vcpu->arch.comparecount_timer); - if (ktime_compare(now, expires) >= 0) { + threshold = ktime_add_ns(now, vcpu->arch.count_period / 4); + if (ktime_before(expires, threshold)) { /* * Cancel it while we handle it so there's no chance of * interference with the timeout handler. @@ -329,8 +348,7 @@ static uint32_t kvm_mips_read_count_running(struct kvm_vcpu *vcpu, ktime_t now) } } - /* Return the biased and scaled guest CP0_Count */ - return vcpu->arch.count_bias + kvm_mips_ktime_to_count(vcpu, now); + return count; } /** From a6fa60f5614352eb53fc57b23f1984fed4e71eac Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 22 Apr 2016 10:38:46 +0100 Subject: [PATCH 103/656] MIPS: KVM: Fix timer IRQ race when writing CP0_Compare MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit b45bacd2d048f405c7760e5cc9b60dd67708734f upstream. Writing CP0_Compare clears the timer interrupt pending bit (CP0_Cause.TI), but this wasn't being done atomically. If a timer interrupt raced with the write of the guest CP0_Compare, the timer interrupt could end up being pending even though the new CP0_Compare is nowhere near CP0_Count. We were already updating the hrtimer expiry with kvm_mips_update_hrtimer(), which used both kvm_mips_freeze_hrtimer() and kvm_mips_resume_hrtimer(). Close the race window by expanding out kvm_mips_update_hrtimer(), and clearing CP0_Cause.TI and setting CP0_Compare between the freeze and resume. Since the pending timer interrupt should not be cleared when CP0_Compare is written via the KVM user API, an ack argument is added to distinguish the source of the write. Fixes: e30492bbe95a ("MIPS: KVM: Rewrite count/compare timer emulation") Signed-off-by: James Hogan Cc: Paolo Bonzini Cc: "Radim Krčmář" Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Cc: kvm@vger.kernel.org Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/kvm_host.h | 2 +- arch/mips/kvm/emulate.c | 61 ++++++++++++++------------------ arch/mips/kvm/trap_emul.c | 2 +- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/arch/mips/include/asm/kvm_host.h b/arch/mips/include/asm/kvm_host.h index 6ded8d347af9..4e956b3e16f5 100644 --- a/arch/mips/include/asm/kvm_host.h +++ b/arch/mips/include/asm/kvm_host.h @@ -784,7 +784,7 @@ extern enum emulation_result kvm_mips_complete_mmio_load(struct kvm_vcpu *vcpu, uint32_t kvm_mips_read_count(struct kvm_vcpu *vcpu); void kvm_mips_write_count(struct kvm_vcpu *vcpu, uint32_t count); -void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare); +void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare, bool ack); void kvm_mips_init_count(struct kvm_vcpu *vcpu); int kvm_mips_set_count_ctl(struct kvm_vcpu *vcpu, s64 count_ctl); int kvm_mips_set_count_resume(struct kvm_vcpu *vcpu, s64 count_resume); diff --git a/arch/mips/kvm/emulate.c b/arch/mips/kvm/emulate.c index eaf77b5479ce..dc10c77b7500 100644 --- a/arch/mips/kvm/emulate.c +++ b/arch/mips/kvm/emulate.c @@ -437,32 +437,6 @@ static void kvm_mips_resume_hrtimer(struct kvm_vcpu *vcpu, hrtimer_start(&vcpu->arch.comparecount_timer, expire, HRTIMER_MODE_ABS); } -/** - * kvm_mips_update_hrtimer() - Update next expiry time of hrtimer. - * @vcpu: Virtual CPU. - * - * Recalculates and updates the expiry time of the hrtimer. This can be used - * after timer parameters have been altered which do not depend on the time that - * the change occurs (in those cases kvm_mips_freeze_hrtimer() and - * kvm_mips_resume_hrtimer() are used directly). - * - * It is guaranteed that no timer interrupts will be lost in the process. - * - * Assumes !kvm_mips_count_disabled(@vcpu) (guest CP0_Count timer is running). - */ -static void kvm_mips_update_hrtimer(struct kvm_vcpu *vcpu) -{ - ktime_t now; - uint32_t count; - - /* - * freeze_hrtimer takes care of a timer interrupts <= count, and - * resume_hrtimer the hrtimer takes care of a timer interrupts > count. - */ - now = kvm_mips_freeze_hrtimer(vcpu, &count); - kvm_mips_resume_hrtimer(vcpu, now, count); -} - /** * kvm_mips_write_count() - Modify the count and update timer. * @vcpu: Virtual CPU. @@ -558,23 +532,42 @@ int kvm_mips_set_count_hz(struct kvm_vcpu *vcpu, s64 count_hz) * kvm_mips_write_compare() - Modify compare and update timer. * @vcpu: Virtual CPU. * @compare: New CP0_Compare value. + * @ack: Whether to acknowledge timer interrupt. * * Update CP0_Compare to a new value and update the timeout. + * If @ack, atomically acknowledge any pending timer interrupt, otherwise ensure + * any pending timer interrupt is preserved. */ -void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare) +void kvm_mips_write_compare(struct kvm_vcpu *vcpu, uint32_t compare, bool ack) { struct mips_coproc *cop0 = vcpu->arch.cop0; + int dc; + u32 old_compare = kvm_read_c0_guest_compare(cop0); + ktime_t now; + uint32_t count; /* if unchanged, must just be an ack */ - if (kvm_read_c0_guest_compare(cop0) == compare) + if (old_compare == compare) { + if (!ack) + return; + kvm_mips_callbacks->dequeue_timer_int(vcpu); + kvm_write_c0_guest_compare(cop0, compare); return; + } + + /* freeze_hrtimer() takes care of timer interrupts <= count */ + dc = kvm_mips_count_disabled(vcpu); + if (!dc) + now = kvm_mips_freeze_hrtimer(vcpu, &count); + + if (ack) + kvm_mips_callbacks->dequeue_timer_int(vcpu); - /* Update compare */ kvm_write_c0_guest_compare(cop0, compare); - /* Update timeout if count enabled */ - if (!kvm_mips_count_disabled(vcpu)) - kvm_mips_update_hrtimer(vcpu); + /* resume_hrtimer() takes care of timer interrupts > count */ + if (!dc) + kvm_mips_resume_hrtimer(vcpu, now, count); } /** @@ -1113,9 +1106,9 @@ enum emulation_result kvm_mips_emulate_CP0(uint32_t inst, uint32_t *opc, /* If we are writing to COMPARE */ /* Clear pending timer interrupt, if any */ - kvm_mips_callbacks->dequeue_timer_int(vcpu); kvm_mips_write_compare(vcpu, - vcpu->arch.gprs[rt]); + vcpu->arch.gprs[rt], + true); } else if ((rd == MIPS_CP0_STATUS) && (sel == 0)) { unsigned int old_val, val, change; diff --git a/arch/mips/kvm/trap_emul.c b/arch/mips/kvm/trap_emul.c index d836ed5b0bc7..307cc4c98bdd 100644 --- a/arch/mips/kvm/trap_emul.c +++ b/arch/mips/kvm/trap_emul.c @@ -547,7 +547,7 @@ static int kvm_trap_emul_set_one_reg(struct kvm_vcpu *vcpu, kvm_mips_write_count(vcpu, v); break; case KVM_REG_MIPS_CP0_COMPARE: - kvm_mips_write_compare(vcpu, v); + kvm_mips_write_compare(vcpu, v, false); break; case KVM_REG_MIPS_CP0_CAUSE: /* From 039f0813ad4ed196883d87a6643041ae413dfde0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 21 Mar 2016 12:33:00 +0100 Subject: [PATCH 104/656] KVM: x86: mask CPUID(0xD,0x1).EAX against host value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 316314cae15fb0e3869b76b468f59a0c83ac3d4e upstream. This ensures that the guest doesn't see XSAVE extensions (e.g. xgetbv1 or xsavec) that the host lacks. Cc: stable@vger.kernel.org Reviewed-by: Radim Krčmář [4.5 does have CPUID_D_1_EAX, but earlier kernels don't, so use the numeric value. This is consistent with other occurrences of cpuid_mask in arch/x86/kvm/cpuid.c - Paolo] Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- arch/x86/kvm/cpuid.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 6525e926f566..2e1fd586b895 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -509,6 +509,7 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, do_cpuid_1_ent(&entry[i], function, idx); if (idx == 1) { entry[i].eax &= kvm_supported_word10_x86_features; + cpuid_mask(&entry[i].eax, 10); entry[i].ebx = 0; if (entry[i].eax & (F(XSAVES)|F(XSAVEC))) entry[i].ebx = From 3bf7d0328640474509d6345f9c3356e08bebf585 Mon Sep 17 00:00:00 2001 From: Stefano Stabellini Date: Wed, 20 Apr 2016 14:15:01 +0100 Subject: [PATCH 105/656] xen/x86: actually allocate legacy interrupts on PV guests commit 702f926067d2a4b28c10a3c41a1172dd62d9e735 upstream. b4ff8389ed14 is incomplete: relies on nr_legacy_irqs() to get the number of legacy interrupts when actually nr_legacy_irqs() returns 0 after probe_8259A(). Use NR_IRQS_LEGACY instead. Signed-off-by: Stefano Stabellini Signed-off-by: Greg Kroah-Hartman --- arch/x86/pci/xen.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c index ff31ab464213..c6d6efed392a 100644 --- a/arch/x86/pci/xen.c +++ b/arch/x86/pci/xen.c @@ -488,8 +488,11 @@ int __init pci_xen_initial_domain(void) #endif __acpi_register_gsi = acpi_register_gsi_xen; __acpi_unregister_gsi = NULL; - /* Pre-allocate legacy irqs */ - for (irq = 0; irq < nr_legacy_irqs(); irq++) { + /* + * Pre-allocate the legacy IRQs. Use NR_LEGACY_IRQS here + * because we don't have a PIC and thus nr_legacy_irqs() is zero. + */ + for (irq = 0; irq < NR_IRQS_LEGACY; irq++) { int trigger, polarity; if (acpi_get_override_irq(irq, &trigger, &polarity) == -1) From fce893a5e3f578f9f26a2718156057c84b397f12 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 3 May 2016 17:05:54 +0200 Subject: [PATCH 106/656] tty: vt, return error when con_startup fails commit 6798df4c5fe0a7e6d2065cf79649a794e5ba7114 upstream. When csw->con_startup() fails in do_register_con_driver, we return no error (i.e. 0). This was changed back in 2006 by commit 3e795de763. Before that we used to return -ENODEV. So fix the return value to be -ENODEV in that case again. Fixes: 3e795de763 ("VT binding: Add binding/unbinding support for the VT console") Signed-off-by: Jiri Slaby Reported-by: "Dan Carpenter" Signed-off-by: Greg Kroah-Hartman --- drivers/tty/vt/vt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 4462d167900c..cf20282f79f0 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3583,9 +3583,10 @@ static int do_register_con_driver(const struct consw *csw, int first, int last) goto err; desc = csw->con_startup(); - - if (!desc) + if (!desc) { + retval = -ENODEV; goto err; + } retval = -EINVAL; From c9715888bf1039d0009070792347a82ed665202e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 22 Mar 2016 18:09:51 +0100 Subject: [PATCH 107/656] TTY: n_gsm, fix false positive WARN_ON commit d175feca89a1c162f60f4e3560ca7bc9437c65eb upstream. Dmitry reported, that the current cleanup code in n_gsm can trigger a warning: WARNING: CPU: 2 PID: 24238 at drivers/tty/n_gsm.c:2048 gsm_cleanup_mux+0x166/0x6b0() ... Call Trace: ... [] warn_slowpath_null+0x29/0x30 kernel/panic.c:490 [] gsm_cleanup_mux+0x166/0x6b0 drivers/tty/n_gsm.c:2048 [] gsmld_open+0x5b7/0x7a0 drivers/tty/n_gsm.c:2386 [] tty_ldisc_open.isra.2+0x78/0xd0 drivers/tty/tty_ldisc.c:447 [] tty_set_ldisc+0x1ca/0xa70 drivers/tty/tty_ldisc.c:567 [< inline >] tiocsetd drivers/tty/tty_io.c:2650 [] tty_ioctl+0xb2a/0x2140 drivers/tty/tty_io.c:2883 ... But this is a legal path when open fails to find a space in the gsm_mux array and tries to clean up. So make it a standard test instead of a warning. Reported-by: "Dmitry Vyukov" Cc: Alan Cox Link: http://lkml.kernel.org/r/CACT4Y+bHQbAB68VFi7Romcs-Z9ZW3kQRvcq+BvHH1oa5NcAdLA@mail.gmail.com Fixes: 5a640967 ("tty/n_gsm.c: fix a memory leak in gsmld_open()") Signed-off-by: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index c3fe026d3168..9aff37186246 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2045,7 +2045,9 @@ static void gsm_cleanup_mux(struct gsm_mux *gsm) } } spin_unlock(&gsm_mux_lock); - WARN_ON(i == MAX_MUX); + /* open failed before registering => nothing to do */ + if (i == MAX_MUX) + return; /* In theory disconnecting DLCI 0 is sufficient but for some modems this is apparently not the case. */ From eb578848032f6abc2d1f78c5e5c72be5e235bffd Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Tue, 12 Apr 2016 14:51:40 +0200 Subject: [PATCH 108/656] tty/serial: atmel: fix hardware handshake selection commit 5be605ac9af979265d7b64c160ad9928088a78be upstream. Commit 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management when hardware handshake is enabled") actually allowed to enable hardware handshaking. Before, the CRTSCTS flags was silently ignored. As the DMA controller can't drive RTS (as explain in the commit message). Ensure that hardware flow control stays disabled when DMA is used and FIFOs are not available. Signed-off-by: Alexandre Belloni Acked-by: Nicolas Ferre Fixes: 1cf6e8fc8341 ("tty/serial: at91: fix RTS line management when hardware handshake is enabled") Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/atmel_serial.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 94294558943c..7bbadd176c74 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -277,6 +277,13 @@ static bool atmel_use_dma_rx(struct uart_port *port) return atmel_port->use_dma_rx; } +static bool atmel_use_fifo(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); + + return atmel_port->fifo_size; +} + static unsigned int atmel_get_lines_status(struct uart_port *port) { struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); @@ -2169,7 +2176,12 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, mode |= ATMEL_US_USMODE_RS485; } else if (termios->c_cflag & CRTSCTS) { /* RS232 with hardware handshake (RTS/CTS) */ - mode |= ATMEL_US_USMODE_HWHS; + if (atmel_use_dma_rx(port) && !atmel_use_fifo(port)) { + dev_info(port->dev, "not enabling hardware flow control because DMA is used"); + termios->c_cflag &= ~CRTSCTS; + } else { + mode |= ATMEL_US_USMODE_HWHS; + } } else { /* RS232 without hadware handshake */ mode |= ATMEL_US_USMODE_NORMAL; From 71378785b6bbeea4098c0dfaca0571b06297224f Mon Sep 17 00:00:00 2001 From: Brian Bloniarz Date: Sun, 6 Mar 2016 13:16:30 -0800 Subject: [PATCH 109/656] Fix OpenSSH pty regression on close commit 0f40fbbcc34e093255a2b2d70b6b0fb48c3f39aa upstream. OpenSSH expects the (non-blocking) read() of pty master to return EAGAIN only if it has received all of the slave-side output after it has received SIGCHLD. This used to work on pre-3.12 kernels. This fix effectively forces non-blocking read() and poll() to block for parallel i/o to complete for all ttys. It also unwinds these changes: 1) f8747d4a466ab2cafe56112c51b3379f9fdb7a12 tty: Fix pty master read() after slave closes 2) 52bce7f8d4fc633c9a9d0646eef58ba6ae9a3b73 pty, n_tty: Simplify input processing on final close 3) 1a48632ffed61352a7810ce089dc5a8bcd505a60 pty: Fix input race when closing Inspired by analysis and patch from Marc Aurele La France Reported-by: Volth Reported-by: Marc Aurele La France BugLink: https://bugzilla.mindrot.org/show_bug.cgi?id=52 BugLink: https://bugzilla.mindrot.org/show_bug.cgi?id=2492 Signed-off-by: Brian Bloniarz Reviewed-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- Documentation/serial/tty.txt | 3 -- drivers/tty/n_hdlc.c | 4 +- drivers/tty/n_tty.c | 72 +++++++++++++++++------------------- drivers/tty/pty.c | 4 +- drivers/tty/tty_buffer.c | 34 +++-------------- include/linux/tty.h | 2 +- 6 files changed, 44 insertions(+), 75 deletions(-) diff --git a/Documentation/serial/tty.txt b/Documentation/serial/tty.txt index bc3842dc323a..e2dea3dc4307 100644 --- a/Documentation/serial/tty.txt +++ b/Documentation/serial/tty.txt @@ -213,9 +213,6 @@ TTY_IO_ERROR If set, causes all subsequent userspace read/write TTY_OTHER_CLOSED Device is a pty and the other side has closed. -TTY_OTHER_DONE Device is a pty and the other side has closed and - all pending input processing has been completed. - TTY_NO_WRITE_SPLIT Prevent driver from splitting up writes into smaller chunks. diff --git a/drivers/tty/n_hdlc.c b/drivers/tty/n_hdlc.c index bbc4ce66c2c1..644ddb841d9f 100644 --- a/drivers/tty/n_hdlc.c +++ b/drivers/tty/n_hdlc.c @@ -600,7 +600,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, add_wait_queue(&tty->read_wait, &wait); for (;;) { - if (test_bit(TTY_OTHER_DONE, &tty->flags)) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { ret = -EIO; break; } @@ -828,7 +828,7 @@ static unsigned int n_hdlc_tty_poll(struct tty_struct *tty, struct file *filp, /* set bits for operations that won't block */ if (n_hdlc->rx_buf_list.head) mask |= POLLIN | POLLRDNORM; /* readable */ - if (test_bit(TTY_OTHER_DONE, &tty->flags)) + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) mask |= POLLHUP; if (tty_hung_up_p(filp)) mask |= POLLHUP; diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cf000b331eed..84e71bd19082 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1955,18 +1955,6 @@ static inline int input_available_p(struct tty_struct *tty, int poll) return ldata->commit_head - ldata->read_tail >= amt; } -static inline int check_other_done(struct tty_struct *tty) -{ - int done = test_bit(TTY_OTHER_DONE, &tty->flags); - if (done) { - /* paired with cmpxchg() in check_other_closed(); ensures - * read buffer head index is not stale - */ - smp_mb__after_atomic(); - } - return done; -} - /** * copy_from_read_buf - copy read data directly * @tty: terminal device @@ -2171,7 +2159,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, struct n_tty_data *ldata = tty->disc_data; unsigned char __user *b = buf; DEFINE_WAIT_FUNC(wait, woken_wake_function); - int c, done; + int c; int minimum, time; ssize_t retval = 0; long timeout; @@ -2239,32 +2227,35 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, ((minimum - (b - buf)) >= 1)) ldata->minimum_to_wake = (minimum - (b - buf)); - done = check_other_done(tty); - if (!input_available_p(tty, 0)) { - if (done) { - retval = -EIO; - break; - } - if (tty_hung_up_p(file)) - break; - if (!timeout) - break; - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } up_read(&tty->termios_rwsem); - - timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, - timeout); - + tty_buffer_flush_work(tty->port); down_read(&tty->termios_rwsem); - continue; + if (!input_available_p(tty, 0)) { + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) { + retval = -EIO; + break; + } + if (tty_hung_up_p(file)) + break; + if (!timeout) + break; + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + up_read(&tty->termios_rwsem); + + timeout = wait_woken(&wait, TASK_INTERRUPTIBLE, + timeout); + + down_read(&tty->termios_rwsem); + continue; + } } if (ldata->icanon && !L_EXTPROC(tty)) { @@ -2446,12 +2437,17 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_wait(file, &tty->read_wait, wait); poll_wait(file, &tty->write_wait, wait); - if (check_other_done(tty)) - mask |= POLLHUP; if (input_available_p(tty, 1)) mask |= POLLIN | POLLRDNORM; + else { + tty_buffer_flush_work(tty->port); + if (input_available_p(tty, 1)) + mask |= POLLIN | POLLRDNORM; + } if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + mask |= POLLHUP; if (tty_hung_up_p(file)) mask |= POLLHUP; if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 78e983677339..7865228f664f 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -59,7 +59,7 @@ static void pty_close(struct tty_struct *tty, struct file *filp) if (!tty->link) return; set_bit(TTY_OTHER_CLOSED, &tty->link->flags); - tty_flip_buffer_push(tty->link->port); + wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->write_wait); if (tty->driver->subtype == PTY_TYPE_MASTER) { set_bit(TTY_OTHER_CLOSED, &tty->flags); @@ -247,9 +247,7 @@ static int pty_open(struct tty_struct *tty, struct file *filp) goto out; clear_bit(TTY_IO_ERROR, &tty->flags); - /* TTY_OTHER_CLOSED must be cleared before TTY_OTHER_DONE */ clear_bit(TTY_OTHER_CLOSED, &tty->link->flags); - clear_bit(TTY_OTHER_DONE, &tty->link->flags); set_bit(TTY_THROTTLED, &tty->flags); return 0; diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 3cd31e0d4bd9..fb31eecb708d 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -37,29 +37,6 @@ #define TTY_BUFFER_PAGE (((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF) -/* - * If all tty flip buffers have been processed by flush_to_ldisc() or - * dropped by tty_buffer_flush(), check if the linked pty has been closed. - * If so, wake the reader/poll to process - */ -static inline void check_other_closed(struct tty_struct *tty) -{ - unsigned long flags, old; - - /* transition from TTY_OTHER_CLOSED => TTY_OTHER_DONE must be atomic */ - for (flags = ACCESS_ONCE(tty->flags); - test_bit(TTY_OTHER_CLOSED, &flags); - ) { - old = flags; - __set_bit(TTY_OTHER_DONE, &flags); - flags = cmpxchg(&tty->flags, old, flags); - if (old == flags) { - wake_up_interruptible(&tty->read_wait); - break; - } - } -} - /** * tty_buffer_lock_exclusive - gain exclusive access to buffer * tty_buffer_unlock_exclusive - release exclusive access @@ -254,8 +231,6 @@ void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld) if (ld && ld->ops->flush_buffer) ld->ops->flush_buffer(tty); - check_other_closed(tty); - atomic_dec(&buf->priority); mutex_unlock(&buf->lock); } @@ -505,10 +480,8 @@ static void flush_to_ldisc(struct work_struct *work) */ count = smp_load_acquire(&head->commit) - head->read; if (!count) { - if (next == NULL) { - check_other_closed(tty); + if (next == NULL) break; - } buf->head = next; tty_buffer_free(port, head); continue; @@ -597,3 +570,8 @@ bool tty_buffer_cancel_work(struct tty_port *port) { return cancel_work_sync(&port->buf.work); } + +void tty_buffer_flush_work(struct tty_port *port) +{ + flush_work(&port->buf.work); +} diff --git a/include/linux/tty.h b/include/linux/tty.h index 3bf03b6b52e9..83b264c52898 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -338,7 +338,6 @@ struct tty_file_private { #define TTY_EXCLUSIVE 3 /* Exclusive open mode */ #define TTY_DEBUG 4 /* Debugging */ #define TTY_DO_WRITE_WAKEUP 5 /* Call write_wakeup after queuing new */ -#define TTY_OTHER_DONE 6 /* Closed pty has completed input processing */ #define TTY_LDISC_OPEN 11 /* Line discipline is open */ #define TTY_PTY_LOCK 16 /* pty private */ #define TTY_NO_WRITE_SPLIT 17 /* Preserve write boundaries to driver */ @@ -469,6 +468,7 @@ extern void tty_buffer_init(struct tty_port *port); extern void tty_buffer_set_lock_subclass(struct tty_port *port); extern bool tty_buffer_restart_work(struct tty_port *port); extern bool tty_buffer_cancel_work(struct tty_port *port); +extern void tty_buffer_flush_work(struct tty_port *port); extern speed_t tty_termios_baud_rate(struct ktermios *termios); extern speed_t tty_termios_input_baud_rate(struct ktermios *termios); extern void tty_termios_encode_baud_rate(struct ktermios *termios, From 1401ebda89ebf3156984c175209f630e0844f6ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller?= Date: Wed, 27 Apr 2016 11:58:32 +0200 Subject: [PATCH 110/656] serial: 8250_pci: fix divide error bug if baud rate is 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 6f210c18c1c0f016772c8cd51ae12a02bfb9e7ef upstream. Since commit 21947ba654a6 ("serial: 8250_pci: replace switch-case by formula"), the 8250 driver crashes in the byt_set_termios() function with a divide error. This is caused by the fact that a baud rate of 0 (B0) is not handled properly. Fix it by falling back to B9600 in this case. Signed-off-by: David Müller Fixes: 21947ba654a6 ("serial: 8250_pci: replace switch-case by formula") Suggested-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 7cd6f9a90542..c1d4a8fa9be8 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1401,6 +1401,9 @@ byt_set_termios(struct uart_port *p, struct ktermios *termios, unsigned long m, n; u32 reg; + /* Gracefully handle the B0 case: fall back to B9600 */ + fuart = fuart ? fuart : 9600 * 16; + /* Get Fuart closer to Fref */ fuart *= rounddown_pow_of_two(fref / fuart); From 3c5dafe43d1e36b70606d3baef8d7f24c0883343 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Apr 2016 17:35:09 +0300 Subject: [PATCH 111/656] serial: 8250_mid: use proper bar for DNV platform commit 107e15fc1f8d6ef69eac5f175971252f76e82f0d upstream. Unlike Intel Medfield and Tangier platforms DNV uses PCI BAR0 for IO compatible resources and BAR1 for MMIO. We need latter in a way to support DMA. Introduce an additional field in the internal structure and pass PCI BAR based on device ID. Reported-by: "Lai, Poey Seng" Fixes: 6ede6dcd87aa ("serial: 8250_mid: add support for DMA engine handling from UART MMIO") Reviewed-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 88531a36b69c..7aa6ef81ca93 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -14,6 +14,7 @@ #include #include +#include #include "8250.h" @@ -31,6 +32,7 @@ struct mid8250; struct mid8250_board { + unsigned int flags; unsigned long freq; unsigned int base_baud; int (*setup)(struct mid8250 *, struct uart_port *p); @@ -106,12 +108,13 @@ static int dnv_setup(struct mid8250 *mid, struct uart_port *p) { struct hsu_dma_chip *chip = &mid->dma_chip; struct pci_dev *pdev = to_pci_dev(p->dev); + unsigned int bar = FL_GET_BASE(mid->board->flags); int ret; chip->dev = &pdev->dev; chip->irq = pdev->irq; chip->regs = p->membase; - chip->length = pci_resource_len(pdev, 0); + chip->length = pci_resource_len(pdev, bar); chip->offset = DNV_DMA_CHAN_OFFSET; /* Falling back to PIO mode if DMA probing fails */ @@ -217,6 +220,7 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct uart_8250_port uart; struct mid8250 *mid; + unsigned int bar; int ret; ret = pcim_enable_device(pdev); @@ -230,6 +234,7 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; mid->board = (struct mid8250_board *)id->driver_data; + bar = FL_GET_BASE(mid->board->flags); memset(&uart, 0, sizeof(struct uart_8250_port)); @@ -242,8 +247,8 @@ static int mid8250_probe(struct pci_dev *pdev, const struct pci_device_id *id) uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE; uart.port.set_termios = mid8250_set_termios; - uart.port.mapbase = pci_resource_start(pdev, 0); - uart.port.membase = pcim_iomap(pdev, 0, 0); + uart.port.mapbase = pci_resource_start(pdev, bar); + uart.port.membase = pcim_iomap(pdev, bar, 0); if (!uart.port.membase) return -ENOMEM; @@ -282,18 +287,21 @@ static void mid8250_remove(struct pci_dev *pdev) } static const struct mid8250_board pnw_board = { + .flags = FL_BASE0, .freq = 50000000, .base_baud = 115200, .setup = pnw_setup, }; static const struct mid8250_board tng_board = { + .flags = FL_BASE0, .freq = 38400000, .base_baud = 1843200, .setup = tng_setup, }; static const struct mid8250_board dnv_board = { + .flags = FL_BASE1, .freq = 133333333, .base_baud = 115200, .setup = dnv_setup, From 430b4aab73830577eb22aa434b35947f87e7ea4b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Apr 2016 17:35:10 +0300 Subject: [PATCH 112/656] serial: 8250_mid: recognize interrupt source in handler commit c42850f1ae7e70056f852e67bb9dddf927853b47 upstream. There is a special register that shows interrupt status by source. In particular case the source can be a combination of DMA Tx, DMA Rx, and UART. Read the register and call the handlers only for sources that request an interrupt. Fixes: 6ede6dcd87aa ("serial: 8250_mid: add support for DMA engine handling from UART MMIO") Reviewed-by: Heikki Krogerus Signed-off-by: Andy Shevchenko Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/8250_mid.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/tty/serial/8250/8250_mid.c b/drivers/tty/serial/8250/8250_mid.c index 7aa6ef81ca93..ed489880e62b 100644 --- a/drivers/tty/serial/8250/8250_mid.c +++ b/drivers/tty/serial/8250/8250_mid.c @@ -25,6 +25,7 @@ #define PCI_DEVICE_ID_INTEL_DNV_UART 0x19d8 /* Intel MID Specific registers */ +#define INTEL_MID_UART_DNV_FISR 0x08 #define INTEL_MID_UART_PS 0x30 #define INTEL_MID_UART_MUL 0x34 #define INTEL_MID_UART_DIV 0x38 @@ -90,16 +91,16 @@ static int tng_setup(struct mid8250 *mid, struct uart_port *p) static int dnv_handle_irq(struct uart_port *p) { struct mid8250 *mid = p->private_data; - int ret; + unsigned int fisr = serial_port_in(p, INTEL_MID_UART_DNV_FISR); + int ret = IRQ_NONE; - ret = hsu_dma_irq(&mid->dma_chip, 0); - ret |= hsu_dma_irq(&mid->dma_chip, 1); - - /* For now, letting the HW generate separate interrupt for the UART */ - if (ret) - return ret; - - return serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); + if (fisr & BIT(2)) + ret |= hsu_dma_irq(&mid->dma_chip, 1); + if (fisr & BIT(1)) + ret |= hsu_dma_irq(&mid->dma_chip, 0); + if (fisr & BIT(0)) + ret |= serial8250_handle_irq(p, serial_port_in(p, UART_IIR)); + return ret; } #define DNV_DMA_CHAN_OFFSET 0x80 From df8ad62006f452ee4902e216e955f1a142cf144f Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 21 Apr 2016 18:58:31 +0900 Subject: [PATCH 113/656] serial: samsung: Reorder the sequence of clock control when call s3c24xx_serial_set_termios() commit b8995f527aac143e83d3900ff39357651ea4e0f6 upstream. This patch fixes the broken serial log when changing the clock source of uart device. Before disabling the original clock source, this patch enables the new clock source to protect the clock off state for a split second. Signed-off-by: Chanwoo Choi Reviewed-by: Marek Szyprowski Signed-off-by: Greg Kroah-Hartman Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Krzysztof Kozlowski --- drivers/tty/serial/samsung.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index d72cd736bdc6..8320173af846 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1263,6 +1263,8 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, /* check to see if we need to change clock source */ if (ourport->baudclk != clk) { + clk_prepare_enable(clk); + s3c24xx_serial_setsource(port, clk_sel); if (!IS_ERR(ourport->baudclk)) { @@ -1270,8 +1272,6 @@ static void s3c24xx_serial_set_termios(struct uart_port *port, ourport->baudclk = ERR_PTR(-EINVAL); } - clk_prepare_enable(clk); - ourport->baudclk = clk; ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0; } From 035688290a740745adf9daff65bceac8b70e8732 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 20 May 2016 18:04:36 +0200 Subject: [PATCH 114/656] locking,qspinlock: Fix spin_is_locked() and spin_unlock_wait() commit 54cf809b9512be95f53ed4a5e3b631d1ac42f0fa upstream. Similar to commits: 51d7d5205d33 ("powerpc: Add smp_mb() to arch_spin_is_locked()") d86b8da04dfa ("arm64: spinlock: serialise spin_unlock_wait against concurrent lockers") qspinlock suffers from the fact that the _Q_LOCKED_VAL store is unordered inside the ACQUIRE of the lock. And while this is not a problem for the regular mutual exclusive critical section usage of spinlocks, it breaks creative locking like: spin_lock(A) spin_lock(B) spin_unlock_wait(B) if (!spin_is_locked(A)) do_something() do_something() In that both CPUs can end up running do_something at the same time, because our _Q_LOCKED_VAL store can drop past the spin_unlock_wait() spin_is_locked() loads (even on x86!!). To avoid making the normal case slower, add smp_mb()s to the less used spin_unlock_wait() / spin_is_locked() side of things to avoid this problem. Reported-and-tested-by: Davidlohr Bueso Reported-by: Giovanni Gherdovich Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/asm-generic/qspinlock.h | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/include/asm-generic/qspinlock.h b/include/asm-generic/qspinlock.h index e2aadbc7151f..7d633f19e38a 100644 --- a/include/asm-generic/qspinlock.h +++ b/include/asm-generic/qspinlock.h @@ -27,7 +27,30 @@ */ static __always_inline int queued_spin_is_locked(struct qspinlock *lock) { - return atomic_read(&lock->val); + /* + * queued_spin_lock_slowpath() can ACQUIRE the lock before + * issuing the unordered store that sets _Q_LOCKED_VAL. + * + * See both smp_cond_acquire() sites for more detail. + * + * This however means that in code like: + * + * spin_lock(A) spin_lock(B) + * spin_unlock_wait(B) spin_is_locked(A) + * do_something() do_something() + * + * Both CPUs can end up running do_something() because the store + * setting _Q_LOCKED_VAL will pass through the loads in + * spin_unlock_wait() and/or spin_is_locked(). + * + * Avoid this by issuing a full memory barrier between the spin_lock() + * and the loads in spin_unlock_wait() and spin_is_locked(). + * + * Note that regular mutual exclusion doesn't care about this + * delayed store. + */ + smp_mb(); + return atomic_read(&lock->val) & _Q_LOCKED_MASK; } /** @@ -107,6 +130,8 @@ static __always_inline void queued_spin_unlock(struct qspinlock *lock) */ static inline void queued_spin_unlock_wait(struct qspinlock *lock) { + /* See queued_spin_is_locked() */ + smp_mb(); while (atomic_read(&lock->val) & _Q_LOCKED_MASK) cpu_relax(); } From 86dbfda83215e340445a0ae44e24c84031e79f1c Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Mon, 29 Feb 2016 11:39:18 +0000 Subject: [PATCH 115/656] clk: bcm2835: add locking to pll*_on/off methods commit ec36a5c6682fdd5328abf15c3c67281bed0241d7 upstream. Add missing locking to: * bcm2835_pll_divider_on * bcm2835_pll_divider_off to protect the read modify write cycle for the register access protecting both cm_reg and a2w_reg registers. Fixes: 41691b8862e2 ("clk: bcm2835: Add support for programming the audio domain clocks") Signed-off-by: Martin Sperl Signed-off-by: Eric Anholt Reviewed-by: Eric Anholt Signed-off-by: Greg Kroah-Hartman --- drivers/clk/bcm/clk-bcm2835.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 4f9830c1b121..185a4d859638 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -1068,10 +1068,12 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw) struct bcm2835_cprman *cprman = divider->cprman; const struct bcm2835_pll_divider_data *data = divider->data; + spin_lock(&cprman->regs_lock); cprman_write(cprman, data->cm_reg, (cprman_read(cprman, data->cm_reg) & ~data->load_mask) | data->hold_mask); cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE); + spin_unlock(&cprman->regs_lock); } static int bcm2835_pll_divider_on(struct clk_hw *hw) @@ -1080,12 +1082,14 @@ static int bcm2835_pll_divider_on(struct clk_hw *hw) struct bcm2835_cprman *cprman = divider->cprman; const struct bcm2835_pll_divider_data *data = divider->data; + spin_lock(&cprman->regs_lock); cprman_write(cprman, data->a2w_reg, cprman_read(cprman, data->a2w_reg) & ~A2W_PLL_CHANNEL_DISABLE); cprman_write(cprman, data->cm_reg, cprman_read(cprman, data->cm_reg) & ~data->hold_mask); + spin_unlock(&cprman->regs_lock); return 0; } From c69d01bd58505b65a5903854ac7372f84ffc8627 Mon Sep 17 00:00:00 2001 From: Andreas Werner Date: Tue, 3 May 2016 12:42:00 +0200 Subject: [PATCH 116/656] mcb: Fixed bar number assignment for the gdd commit f75564d343010b025301d9548f2304f48eb25f01 upstream. The bar number is found in reg2 within the gdd. Therefore we need to change the assigment from reg1 to reg2 which is the correct location. Signed-off-by: Andreas Werner Fixes: '3764e82e5' drivers: Introduce MEN Chameleon Bus Signed-off-by: Johannes Thumshirn Signed-off-by: Greg Kroah-Hartman --- drivers/mcb/mcb-parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mcb/mcb-parse.c b/drivers/mcb/mcb-parse.c index 004926955263..b0155b05cddb 100644 --- a/drivers/mcb/mcb-parse.c +++ b/drivers/mcb/mcb-parse.c @@ -57,7 +57,7 @@ static int chameleon_parse_gdd(struct mcb_bus *bus, mdev->id = GDD_DEV(reg1); mdev->rev = GDD_REV(reg1); mdev->var = GDD_VAR(reg1); - mdev->bar = GDD_BAR(reg1); + mdev->bar = GDD_BAR(reg2); mdev->group = GDD_GRP(reg2); mdev->inst = GDD_INS(reg2); From 5058aa1ed3ed889620dc61b5427f815108916abb Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Wed, 4 May 2016 15:50:18 +0800 Subject: [PATCH 117/656] ALSA: hda/realtek - New codecs support for ALC234/ALC274/ALC294 commit dcd4f0db6141d6bf2cb897309d5d6f53d1b1696f upstream. Support new codecs for ALC234/ALC274/ALC294. This three codecs was the same IC. But bonding is not the same. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4918ffa5ba68..002f153bc659 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -342,6 +342,11 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0293: alc_update_coef_idx(codec, 0xa, 1<<13, 0); break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + alc_update_coef_idx(codec, 0x10, 1<<15, 0); + break; case 0x10ec0662: if ((coef & 0x00f0) == 0x0030) alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */ @@ -2647,6 +2652,7 @@ enum { ALC269_TYPE_ALC255, ALC269_TYPE_ALC256, ALC269_TYPE_ALC225, + ALC269_TYPE_ALC294, }; /* @@ -2677,6 +2683,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec) case ALC269_TYPE_ALC255: case ALC269_TYPE_ALC256: case ALC269_TYPE_ALC225: + case ALC269_TYPE_ALC294: ssids = alc269_ssids; break; default: @@ -6028,6 +6035,11 @@ static int patch_alc269(struct hda_codec *codec) case 0x10ec0225: spec->codec_variant = ALC269_TYPE_ALC225; break; + case 0x10ec0234: + case 0x10ec0274: + case 0x10ec0294: + spec->codec_variant = ALC269_TYPE_ALC294; + break; } if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) { @@ -6942,6 +6954,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269), HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269), HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0234, "ALC234", patch_alc269), HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269), HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269), HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269), @@ -6952,6 +6965,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269), HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269), HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662), + HDA_CODEC_ENTRY(0x10ec0274, "ALC274", patch_alc269), HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269), HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269), HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269), @@ -6964,6 +6978,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269), HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269), HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269), HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), From 1979d0bf95d15487f1ea4f3a50fa7e242896b35b Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Fri, 20 May 2016 15:47:23 +0800 Subject: [PATCH 118/656] ALSA: hda - Fix headphone noise on Dell XPS 13 9360 commit 423cd785619ac6778252fbdb916505aa1c153959 upstream. The headphone has noise when playing sound or switching microphone sources. It uses the same codec on XPS 13 9350, but with different subsystem ID. Applying the fixup can solve the issue. Also, changing the model name to better differentiate models. v2: Reorder by device ID. Signed-off-by: Kai-Heng Feng Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 002f153bc659..10ca90058bfa 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5466,8 +5466,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK), - SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE), + SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE), SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE), + SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), From b217d532e65b30ea1aaf8165809bdf457b8a6e65 Mon Sep 17 00:00:00 2001 From: Kailang Yang Date: Tue, 24 May 2016 16:46:07 +0800 Subject: [PATCH 119/656] ALSA: hda/realtek - Add support for ALC295/ALC3254 commit 7d727869c7b86da0874436ac5675dcdadaf3a0a1 upstream. Add support for ALC295/ALC3254. They are simply compatible with ALC225 chip. Signed-off-by: Kailang Yang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 10ca90058bfa..a7f7df138191 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -335,6 +335,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) case 0x10ec0283: case 0x10ec0286: case 0x10ec0288: + case 0x10ec0295: case 0x10ec0298: alc_update_coef_idx(codec, 0x10, 1<<9, 0); break; @@ -907,6 +908,7 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = { { 0x10ec0298, 0x1028, 0, "ALC3266" }, { 0x10ec0256, 0x1028, 0, "ALC3246" }, { 0x10ec0225, 0x1028, 0, "ALC3253" }, + { 0x10ec0295, 0x1028, 0, "ALC3254" }, { 0x10ec0670, 0x1025, 0, "ALC669X" }, { 0x10ec0676, 0x1025, 0, "ALC679X" }, { 0x10ec0282, 0x1043, 0, "ALC3229" }, @@ -3697,6 +3699,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) alc_process_coef_fw(codec, coef0668); break; case 0x10ec0225: + case 0x10ec0295: alc_process_coef_fw(codec, coef0225); break; } @@ -3797,6 +3800,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; case 0x10ec0225: + case 0x10ec0295: alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10); snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); alc_process_coef_fw(codec, coef0225); @@ -3854,6 +3858,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) switch (codec->core.vendor_id) { case 0x10ec0225: + case 0x10ec0295: alc_process_coef_fw(codec, coef0225); break; case 0x10ec0255: @@ -3957,6 +3962,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) alc_process_coef_fw(codec, coef0688); break; case 0x10ec0225: + case 0x10ec0295: alc_process_coef_fw(codec, coef0225); break; } @@ -4038,6 +4044,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) alc_process_coef_fw(codec, coef0688); break; case 0x10ec0225: + case 0x10ec0295: alc_process_coef_fw(codec, coef0225); break; } @@ -4121,6 +4128,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) is_ctia = (val & 0x1c02) == 0x1c02; break; case 0x10ec0225: + case 0x10ec0295: alc_process_coef_fw(codec, coef0225); msleep(800); val = alc_read_coef_idx(codec, 0x46); @@ -6034,6 +6042,7 @@ static int patch_alc269(struct hda_codec *codec) alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/ break; case 0x10ec0225: + case 0x10ec0295: spec->codec_variant = ALC269_TYPE_ALC225; break; case 0x10ec0234: @@ -6980,6 +6989,7 @@ static const struct hda_device_id snd_hda_id_realtek[] = { HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269), HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269), HDA_CODEC_ENTRY(0x10ec0294, "ALC294", patch_alc269), + HDA_CODEC_ENTRY(0x10ec0295, "ALC295", patch_alc269), HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269), HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861), HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd), From 23bc22aaa4fbcf0a244a1f024e0236dedeb12668 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Wed, 25 May 2016 12:12:32 +0800 Subject: [PATCH 120/656] ALSA: hda - Fix headset mic detection problem for one Dell machine commit 86c72d1ce91d804e4fa8d90b316a89597dd220f1 upstream. Add the pin configuration value of this machine into the pin_quirk table to make DELL1_MIC_NO_PRESENCE apply to this machine. Signed-off-by: Hui Wang Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- sound/pci/hda/patch_realtek.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a7f7df138191..d53c25e7a1c1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5719,6 +5719,9 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE, {0x14, 0x90170110}, {0x21, 0x02211020}), + SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, + {0x14, 0x90170130}, + {0x21, 0x02211040}), SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE, {0x12, 0x90a60140}, {0x14, 0x90170110}, From a03870181d89d5f02e70711139c0cbbaaa308d0d Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Tue, 12 Apr 2016 14:39:18 -0700 Subject: [PATCH 121/656] IB/srp: Fix a debug kernel crash commit 54f5c9c52d69afa55abf2b034df8d45f588466c3 upstream. Avoid that the following BUG() is triggered against a debug kernel: kernel BUG at include/linux/scatterlist.h:92! RIP: 0010:[] [] srp_map_idb+0x199/0x1a0 [ib_srp] Call Trace: [] srp_map_data+0x84a/0x890 [ib_srp] [] srp_queuecommand+0x1e4/0x610 [ib_srp] [] scsi_dispatch_cmd+0x9e/0x180 [] scsi_request_fn+0x477/0x610 [] __blk_run_queue+0x2e/0x40 [] blk_delay_work+0x20/0x30 [] process_one_work+0x197/0x480 [] worker_thread+0x49/0x490 [] kthread+0xea/0x100 [] ret_from_fork+0x22/0x40 Fixes: f7f7aab1a5c0 ("IB/srp: Convert to new registration API") Signed-off-by: Bart Van Assche Cc: Sagi Grimberg Cc: Christoph Hellwig Reviewed-by: Max Gurtovoy Reviewed-by: Sagi Grimberg Signed-off-by: Doug Ledford Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/ulp/srp/ib_srp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c index 3db9a659719b..5f0f4fc58f43 100644 --- a/drivers/infiniband/ulp/srp/ib_srp.c +++ b/drivers/infiniband/ulp/srp/ib_srp.c @@ -1519,7 +1519,7 @@ static int srp_map_idb(struct srp_rdma_ch *ch, struct srp_request *req, if (dev->use_fast_reg) { state.sg = idb_sg; - sg_set_buf(idb_sg, req->indirect_desc, idb_len); + sg_init_one(idb_sg, req->indirect_desc, idb_len); idb_sg->dma_address = req->indirect_dma_addr; /* hack! */ #ifdef CONFIG_NEED_SG_DMA_LENGTH idb_sg->dma_length = idb_sg->length; /* hack^2 */ From b20909f10797b2fcbfed55dca4dbd131fff9a94d Mon Sep 17 00:00:00 2001 From: Andreas Noever Date: Sun, 10 Apr 2016 12:48:27 +0200 Subject: [PATCH 122/656] thunderbolt: Fix double free of drom buffer commit 2ffa9a5d76a75abbc1f95c17959fced666095bdd upstream. If tb_drom_read() fails, sw->drom is freed but not set to NULL. sw->drom is then freed again in the error path of tb_switch_alloc(). The bug can be triggered by unplugging a thunderbolt device shortly after it is detected by the thunderbolt driver. Clear sw->drom if tb_drom_read() fails. [bhelgaas: add Fixes:, stable versions of interest] Fixes: 343fcb8c70d7 ("thunderbolt: Fix nontrivial endpoint devices.") Signed-off-by: Andreas Noever Signed-off-by: Bjorn Helgaas CC: Lukas Wunner Signed-off-by: Greg Kroah-Hartman --- drivers/thunderbolt/eeprom.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 0dde34e3a7c5..545c60c826a1 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -444,6 +444,7 @@ int tb_drom_read(struct tb_switch *sw) return tb_drom_parse_entries(sw); err: kfree(sw->drom); + sw->drom = NULL; return -EIO; } From e044b25aa519a8a997f5669ff11e96dc2fe9ac42 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 8 Feb 2016 18:43:50 +0000 Subject: [PATCH 123/656] SIGNAL: Move generic copy_siginfo() to signal.h commit ca9eb49aa9562eaadf3cea071ec7018ad6800425 upstream. The generic copy_siginfo() is currently defined in asm-generic/siginfo.h, after including uapi/asm-generic/siginfo.h which defines the generic struct siginfo. However this makes it awkward for an architecture to use it if it has to define its own struct siginfo (e.g. MIPS and potentially IA64), since it means that asm-generic/siginfo.h can only be included after defining the arch-specific siginfo, which may be problematic if the arch-specific definition needs definitions from uapi/asm-generic/siginfo.h. It is possible to work around this by first including uapi/asm-generic/siginfo.h to get the constants before defining the arch-specific siginfo, and include asm-generic/siginfo.h after. However uapi headers can't be included by other uapi headers, so that first include has to be in an ifdef __kernel__, with the non __kernel__ case including the non-UAPI header instead. Instead of that mess, move the generic copy_siginfo() definition into linux/signal.h, which allows an arch-specific uapi/asm/siginfo.h to include asm-generic/siginfo.h and define the arch-specific siginfo, and for the generic copy_siginfo() to see that arch-specific definition. Signed-off-by: James Hogan Cc: Arnd Bergmann Cc: Ralf Baechle Cc: Petr Malat Cc: Tony Luck Cc: Fenghua Yu Cc: Christopher Ferris Cc: linux-arch@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: linux-ia64@vger.kernel.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/12478/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- include/asm-generic/siginfo.h | 15 --------------- include/linux/signal.h | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/include/asm-generic/siginfo.h b/include/asm-generic/siginfo.h index 3d1a3af5cf59..a2508a8f9a9c 100644 --- a/include/asm-generic/siginfo.h +++ b/include/asm-generic/siginfo.h @@ -17,21 +17,6 @@ struct siginfo; void do_schedule_next_timer(struct siginfo *info); -#ifndef HAVE_ARCH_COPY_SIGINFO - -#include - -static inline void copy_siginfo(struct siginfo *to, struct siginfo *from) -{ - if (from->si_code < 0) - memcpy(to, from, sizeof(*to)); - else - /* _sigchld is currently the largest know union member */ - memcpy(to, from, __ARCH_SI_PREAMBLE_SIZE + sizeof(from->_sifields._sigchld)); -} - -#endif - extern int copy_siginfo_to_user(struct siginfo __user *to, const struct siginfo *from); #endif diff --git a/include/linux/signal.h b/include/linux/signal.h index 92557bbce7e7..d80259afb9e5 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -28,6 +28,21 @@ struct sigpending { sigset_t signal; }; +#ifndef HAVE_ARCH_COPY_SIGINFO + +#include + +static inline void copy_siginfo(struct siginfo *to, struct siginfo *from) +{ + if (from->si_code < 0) + memcpy(to, from, sizeof(*to)); + else + /* _sigchld is currently the largest know union member */ + memcpy(to, from, __ARCH_SI_PREAMBLE_SIZE + sizeof(from->_sifields._sigchld)); +} + +#endif + /* * Define some primitives to manipulate sigset_t. */ From 1ba1f094568b3366c7fa8fd8e0655274badca220 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Tue, 26 Apr 2016 16:39:48 +0200 Subject: [PATCH 124/656] UBI: Fix static volume checks when Fastmap is used commit 1900149c835ab5b48bea31a823ea5e5a401fb560 upstream. Ezequiel reported that he's facing UBI going into read-only mode after power cut. It turned out that this behavior happens only when updating a static volume is interrupted and Fastmap is used. A possible trace can look like: ubi0 warning: ubi_io_read_vid_hdr [ubi]: no VID header found at PEB 2323, only 0xFF bytes ubi0 warning: ubi_eba_read_leb [ubi]: switch to read-only mode CPU: 0 PID: 833 Comm: ubiupdatevol Not tainted 4.6.0-rc2-ARCH #4 Hardware name: SAMSUNG ELECTRONICS CO., LTD. 300E4C/300E5C/300E7C/NP300E5C-AD8AR, BIOS P04RAP 10/15/2012 0000000000000286 00000000eba949bd ffff8800c45a7b38 ffffffff8140d841 ffff8801964be000 ffff88018eaa4800 ffff8800c45a7bb8 ffffffffa003abf6 ffffffff850e2ac0 8000000000000163 ffff8801850e2ac0 ffff8801850e2ac0 Call Trace: [] dump_stack+0x63/0x82 [] ubi_eba_read_leb+0x486/0x4a0 [ubi] [] ubi_check_volume+0x83/0xf0 [ubi] [] ubi_open_volume+0x177/0x350 [ubi] [] vol_cdev_open+0x58/0xb0 [ubi] [] chrdev_open+0xae/0x1d0 [] do_dentry_open+0x1ff/0x300 [] ? cdev_put+0x30/0x30 [] vfs_open+0x56/0x60 [] path_openat+0x4f4/0x1190 [] do_filp_open+0x91/0x100 [] ? __alloc_fd+0xc7/0x190 [] do_sys_open+0x13f/0x210 [] SyS_open+0x1e/0x20 [] entry_SYSCALL_64_fastpath+0x1a/0xa4 UBI checks static volumes for data consistency and reads the whole volume upon first open. If the volume is found erroneous users of UBI cannot read from it, but another volume update is possible to fix it. The check is performed by running ubi_eba_read_leb() on every allocated LEB of the volume. For static volumes ubi_eba_read_leb() computes the checksum of all data stored in a LEB. To verify the computed checksum it has to read the LEB's volume header which stores the original checksum. If the volume header is not found UBI treats this as fatal internal error and switches to RO mode. If the UBI device was attached via a full scan the assumption is correct, the volume header has to be present as it had to be there while scanning to get known as mapped. If the attach operation happened via Fastmap the assumption is no longer correct. When attaching via Fastmap UBI learns the mapping table from Fastmap's snapshot of the system state and not via a full scan. It can happen that a LEB got unmapped after a Fastmap was written to the flash. Then UBI can learn the LEB still as mapped and accessing it returns only 0xFF bytes. As UBI is not a FTL it is allowed to have mappings to empty PEBs, it assumes that the layer above takes care of LEB accounting and referencing. UBIFS does so using the LEB property tree (LPT). For static volumes UBI blindly assumes that all LEBs are present and therefore special actions have to be taken. The described situation can happen when updating a static volume is interrupted, either by a user or a power cut. The volume update code first unmaps all LEBs of a volume and then writes LEB by LEB. If the sequence of operations is interrupted UBI detects this either by the absence of LEBs, no volume header present at scan time, or corrupted payload, detected via checksum. In the Fastmap case the former method won't trigger as no scan happened and UBI automatically thinks all LEBs are present. Only by reading data from a LEB it detects that the volume header is missing and incorrectly treats this as fatal error. To deal with the situation ubi_eba_read_leb() from now on checks whether we attached via Fastmap and handles the absence of a volume header like a data corruption error. This way interrupted static volume updates will correctly get detected also when Fastmap is used. Reported-by: Ezequiel Garcia Tested-by: Ezequiel Garcia Signed-off-by: Richard Weinberger Signed-off-by: Greg Kroah-Hartman --- drivers/mtd/ubi/eba.c | 21 +++++++++++++++++++-- drivers/mtd/ubi/fastmap.c | 1 + drivers/mtd/ubi/ubi.h | 2 ++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 5b9834cf2820..96fddb016bf1 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -426,8 +426,25 @@ retry: pnum, vol_id, lnum); err = -EBADMSG; } else { - err = -EINVAL; - ubi_ro_mode(ubi); + /* + * Ending up here in the non-Fastmap case + * is a clear bug as the VID header had to + * be present at scan time to have it referenced. + * With fastmap the story is more complicated. + * Fastmap has the mapping info without the need + * of a full scan. So the LEB could have been + * unmapped, Fastmap cannot know this and keeps + * the LEB referenced. + * This is valid and works as the layer above UBI + * has to do bookkeeping about used/referenced + * LEBs in any case. + */ + if (ubi->fast_attach) { + err = -EBADMSG; + } else { + err = -EINVAL; + ubi_ro_mode(ubi); + } } } goto out_free; diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index 263b439e21a8..990898b9dc72 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -1058,6 +1058,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, ubi_msg(ubi, "fastmap WL pool size: %d", ubi->fm_wl_pool.max_size); ubi->fm_disabled = 0; + ubi->fast_attach = 1; ubi_free_vid_hdr(ubi, vh); kfree(ech); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 2974b67f6c6c..de1ea2e4c37d 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -462,6 +462,7 @@ struct ubi_debug_info { * @fm_eba_sem: allows ubi_update_fastmap() to block EBA table changes * @fm_work: fastmap work queue * @fm_work_scheduled: non-zero if fastmap work was scheduled + * @fast_attach: non-zero if UBI was attached by fastmap * * @used: RB-tree of used physical eraseblocks * @erroneous: RB-tree of erroneous used physical eraseblocks @@ -570,6 +571,7 @@ struct ubi_device { size_t fm_size; struct work_struct fm_work; int fm_work_scheduled; + int fast_attach; /* Wear-leveling sub-system's stuff */ struct rb_root used; From 5cb3ec3d605ba80d0f33968d54bf85875d427161 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 24 May 2016 22:47:00 +0200 Subject: [PATCH 125/656] hpfs: fix remount failure when there are no options changed commit 44d51706b4685f965cd32acde3fe0fcc1e6198e8 upstream. Commit ce657611baf9 ("hpfs: kstrdup() out of memory handling") checks if the kstrdup function returns NULL due to out-of-memory condition. However, if we are remounting a filesystem with no change to filesystem-specific options, the parameter data is NULL. In this case, kstrdup returns NULL (because it was passed NULL parameter), although no out of memory condition exists. The mount syscall then fails with ENOMEM. This patch fixes the bug. We fail with ENOMEM only if data is non-NULL. The patch also changes the call to replace_mount_options - if we didn't pass any filesystem-specific options, we don't call replace_mount_options (thus we don't erase existing reported options). Fixes: ce657611baf9 ("hpfs: kstrdup() out of memory handling") Signed-off-by: Mikulas Patocka Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/hpfs/super.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index a561591896bd..76c4697ecefd 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -455,7 +455,7 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data) struct hpfs_sb_info *sbi = hpfs_sb(s); char *new_opts = kstrdup(data, GFP_KERNEL); - if (!new_opts) + if (data && !new_opts) return -ENOMEM; sync_filesystem(s); @@ -493,7 +493,8 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data) if (!(*flags & MS_RDONLY)) mark_dirty(s, 1); - replace_mount_options(s, new_opts); + if (new_opts) + replace_mount_options(s, new_opts); hpfs_unlock(s); return 0; From 7e920411dd449298d76e1aef64a2ca4ef6e2655d Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 24 May 2016 22:49:18 +0200 Subject: [PATCH 126/656] hpfs: implement the show_options method commit 037369b872940cd923835a0a589763180c4a36bc upstream. The HPFS filesystem used generic_show_options to produce string that is displayed in /proc/mounts. However, there is a problem that the options may disappear after remount. If we mount the filesystem with option1 and then remount it with option2, /proc/mounts should show both option1 and option2, however it only shows option2 because the whole option string is replaced with replace_mount_options in hpfs_remount_fs. To fix this bug, implement the hpfs_show_options function that prints options that are currently selected. Signed-off-by: Mikulas Patocka Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/hpfs/super.c | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/fs/hpfs/super.c b/fs/hpfs/super.c index 76c4697ecefd..3713fd52b44b 100644 --- a/fs/hpfs/super.c +++ b/fs/hpfs/super.c @@ -15,6 +15,7 @@ #include #include #include +#include /* Mark the filesystem dirty, so that chkdsk checks it when os/2 booted */ @@ -453,10 +454,6 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data) int lowercase, eas, chk, errs, chkdsk, timeshift; int o; struct hpfs_sb_info *sbi = hpfs_sb(s); - char *new_opts = kstrdup(data, GFP_KERNEL); - - if (data && !new_opts) - return -ENOMEM; sync_filesystem(s); @@ -493,18 +490,44 @@ static int hpfs_remount_fs(struct super_block *s, int *flags, char *data) if (!(*flags & MS_RDONLY)) mark_dirty(s, 1); - if (new_opts) - replace_mount_options(s, new_opts); - hpfs_unlock(s); return 0; out_err: hpfs_unlock(s); - kfree(new_opts); return -EINVAL; } +static int hpfs_show_options(struct seq_file *seq, struct dentry *root) +{ + struct hpfs_sb_info *sbi = hpfs_sb(root->d_sb); + + seq_printf(seq, ",uid=%u", from_kuid_munged(&init_user_ns, sbi->sb_uid)); + seq_printf(seq, ",gid=%u", from_kgid_munged(&init_user_ns, sbi->sb_gid)); + seq_printf(seq, ",umask=%03o", (~sbi->sb_mode & 0777)); + if (sbi->sb_lowercase) + seq_printf(seq, ",case=lower"); + if (!sbi->sb_chk) + seq_printf(seq, ",check=none"); + if (sbi->sb_chk == 2) + seq_printf(seq, ",check=strict"); + if (!sbi->sb_err) + seq_printf(seq, ",errors=continue"); + if (sbi->sb_err == 2) + seq_printf(seq, ",errors=panic"); + if (!sbi->sb_chkdsk) + seq_printf(seq, ",chkdsk=no"); + if (sbi->sb_chkdsk == 2) + seq_printf(seq, ",chkdsk=always"); + if (!sbi->sb_eas) + seq_printf(seq, ",eas=no"); + if (sbi->sb_eas == 1) + seq_printf(seq, ",eas=ro"); + if (sbi->sb_timeshift) + seq_printf(seq, ",timeshift=%d", sbi->sb_timeshift); + return 0; +} + /* Super operations */ static const struct super_operations hpfs_sops = @@ -515,7 +538,7 @@ static const struct super_operations hpfs_sops = .put_super = hpfs_put_super, .statfs = hpfs_statfs, .remount_fs = hpfs_remount_fs, - .show_options = generic_show_options, + .show_options = hpfs_show_options, }; static int hpfs_fill_super(struct super_block *s, void *options, int silent) @@ -538,8 +561,6 @@ static int hpfs_fill_super(struct super_block *s, void *options, int silent) int o; - save_mount_options(s, options); - sbi = kzalloc(sizeof(*sbi), GFP_KERNEL); if (!sbi) { return -ENOMEM; From 210588c0344e7daf1351aa3c68b0cd9141ab80dc Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Tue, 5 Apr 2016 11:50:44 +0200 Subject: [PATCH 127/656] scsi: Add intermediate STARGET_REMOVE state to scsi_target_state commit f05795d3d771f30a7bdc3a138bf714b06d42aa95 upstream. Add intermediate STARGET_REMOVE state to scsi_target_state to avoid running into the BUG_ON() in scsi_target_reap(). The STARGET_REMOVE state is only valid in the path from scsi_remove_target() to scsi_target_destroy() indicating this target is going to be removed. This re-fixes the problem introduced in commits bc3f02a795d3 ("[SCSI] scsi_remove_target: fix softlockup regression on hot remove") and 40998193560d ("scsi: restart list search after unlock in scsi_remove_target") in a more comprehensive way. [mkp: Included James' fix for scsi_target_destroy()] Signed-off-by: Johannes Thumshirn Fixes: 40998193560dab6c3ce8d25f4fa58a23e252ef38 Reported-by: Sergey Senozhatsky Tested-by: Sergey Senozhatsky Reviewed-by: Ewan D. Milne Reviewed-by: Hannes Reinecke Reviewed-by: James Bottomley Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_scan.c | 1 + drivers/scsi/scsi_sysfs.c | 2 ++ include/scsi/scsi_device.h | 1 + 3 files changed, 4 insertions(+) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 054923e3393c..f0cfaacbfabd 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -314,6 +314,7 @@ static void scsi_target_destroy(struct scsi_target *starget) struct Scsi_Host *shost = dev_to_shost(dev->parent); unsigned long flags; + BUG_ON(starget->state == STARGET_DEL); starget->state = STARGET_DEL; transport_destroy_device(dev); spin_lock_irqsave(shost->host_lock, flags); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index c8115b4fe474..9185cfc323bc 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1199,11 +1199,13 @@ restart: spin_lock_irqsave(shost->host_lock, flags); list_for_each_entry(starget, &shost->__targets, siblings) { if (starget->state == STARGET_DEL || + starget->state == STARGET_REMOVE || starget == last_target) continue; if (starget->dev.parent == dev || &starget->dev == dev) { kref_get(&starget->reap_ref); last_target = starget; + starget->state = STARGET_REMOVE; spin_unlock_irqrestore(shost->host_lock, flags); __scsi_remove_target(starget); scsi_target_reap(starget); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index fe89d7cd67b9..4f6ba34cdee6 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -239,6 +239,7 @@ scmd_printk(const char *, const struct scsi_cmnd *, const char *, ...); enum scsi_target_state { STARGET_CREATED = 1, STARGET_RUNNING, + STARGET_REMOVE, STARGET_DEL, }; From 9acf5535346cb06f349ffa98ebb2408d2aa9fbca Mon Sep 17 00:00:00 2001 From: Johannes Thumshirn Date: Tue, 5 Apr 2016 11:50:45 +0200 Subject: [PATCH 128/656] Revert "scsi: fix soft lockup in scsi_remove_target() on module removal" commit 305c2e71b3d733ec065cb716c76af7d554bd5571 upstream. Now that we've done a more comprehensive fix with the intermediate target state we can remove the previous hack introduced with commit 90a88d6ef88e ("scsi: fix soft lockup in scsi_remove_target() on module removal"). Signed-off-by: Johannes Thumshirn Reviewed-by: Ewan D. Milne Reviewed-by: Hannes Reinecke Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_sysfs.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 9185cfc323bc..f7ae898833dd 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -1192,19 +1192,17 @@ static void __scsi_remove_target(struct scsi_target *starget) void scsi_remove_target(struct device *dev) { struct Scsi_Host *shost = dev_to_shost(dev->parent); - struct scsi_target *starget, *last_target = NULL; + struct scsi_target *starget; unsigned long flags; restart: spin_lock_irqsave(shost->host_lock, flags); list_for_each_entry(starget, &shost->__targets, siblings) { if (starget->state == STARGET_DEL || - starget->state == STARGET_REMOVE || - starget == last_target) + starget->state == STARGET_REMOVE) continue; if (starget->dev.parent == dev || &starget->dev == dev) { kref_get(&starget->reap_ref); - last_target = starget; starget->state = STARGET_REMOVE; spin_unlock_irqrestore(shost->host_lock, flags); __scsi_remove_target(starget); From e576ffd986dcd97efbd9a09361e8746d7f288946 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 10 May 2016 23:30:01 +0200 Subject: [PATCH 129/656] kbuild: move -Wunused-const-variable to W=1 warning level commit c9c6837d39311b0cc14cdbe7c18e815ab44aefb1 upstream. gcc-6 started warning by default about variables that are not used anywhere and that are marked 'const', generating many false positives in an allmodconfig build, e.g.: arch/arm/mach-davinci/board-da830-evm.c:282:20: warning: 'da830_evm_emif25_pins' defined but not used [-Wunused-const-variable=] arch/arm/plat-omap/dmtimer.c:958:34: warning: 'omap_timer_match' defined but not used [-Wunused-const-variable=] drivers/bluetooth/hci_bcm.c:625:39: warning: 'acpi_bcm_default_gpios' defined but not used [-Wunused-const-variable=] drivers/char/hw_random/omap-rng.c:92:18: warning: 'reg_map_omap4' defined but not used [-Wunused-const-variable=] drivers/devfreq/exynos/exynos5_bus.c:381:32: warning: 'exynos5_busfreq_int_pm' defined but not used [-Wunused-const-variable=] drivers/dma/mv_xor.c:1139:34: warning: 'mv_xor_dt_ids' defined but not used [-Wunused-const-variable=] This is similar to the existing -Wunused-but-set-variable warning that was added in an earlier release and that we disable by default now and only enable when W=1 is set, so it makes sense to do the same here. Once we have eliminated the majority of the warnings for both, we can put them back into the default list. We probably want this in backport kernels as well, to allow building them with gcc-6 without introducing extra warnings. Signed-off-by: Arnd Bergmann Acked-by: Olof Johansson Acked-by: Lee Jones Signed-off-by: Michal Marek Signed-off-by: Greg Kroah-Hartman --- Makefile | 5 +++-- scripts/Makefile.extrawarn | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index aad86274b61b..54cb8ea4bb50 100644 --- a/Makefile +++ b/Makefile @@ -682,9 +682,10 @@ KBUILD_CFLAGS += $(call cc-option, -mno-global-merge,) KBUILD_CFLAGS += $(call cc-option, -fcatch-undefined-behavior) else -# This warning generated too much noise in a regular build. -# Use make W=1 to enable this warning (see scripts/Makefile.build) +# These warnings generated too much noise in a regular build. +# Use make W=1 to enable them (see scripts/Makefile.build) KBUILD_CFLAGS += $(call cc-disable-warning, unused-but-set-variable) +KBUILD_CFLAGS += $(call cc-disable-warning, unused-const-variable) endif ifdef CONFIG_FRAME_POINTER diff --git a/scripts/Makefile.extrawarn b/scripts/Makefile.extrawarn index 4efedcbe4165..da3386a9d244 100644 --- a/scripts/Makefile.extrawarn +++ b/scripts/Makefile.extrawarn @@ -24,6 +24,7 @@ warning-1 += $(call cc-option, -Wmissing-prototypes) warning-1 += -Wold-style-definition warning-1 += $(call cc-option, -Wmissing-include-dirs) warning-1 += $(call cc-option, -Wunused-but-set-variable) +warning-1 += $(call cc-option, -Wunused-const-variable) warning-1 += $(call cc-disable-warning, missing-field-initializers) warning-2 := -Waggregate-return From 351d2d4d3108a9a0f0e112dabbc36c2b1446e4f8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 1 Jun 2016 12:16:06 -0700 Subject: [PATCH 130/656] Linux 4.4.12 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 54cb8ea4bb50..a1fbd691a36e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 4 PATCHLEVEL = 4 -SUBLEVEL = 11 +SUBLEVEL = 12 EXTRAVERSION = NAME = Blurry Fish Butt From 52814abf26659b3f3178410fcb0e2222152b4f8a Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 19 Jan 2016 17:14:29 +0200 Subject: [PATCH 131/656] perf: Synchronously free aux pages in case of allocation failure We are currently using asynchronous deallocation in the error path in AUX mmap code, which is unnecessary and also presents a problem for users that wish to probe for the biggest possible buffer size they can get: they'll get -EINVAL on all subsequent attemts to allocate a smaller buffer before the asynchronous deallocation callback frees up the pages from the previous unsuccessful attempt. Currently, gdb does that for allocating AUX buffers for Intel PT traces. More specifically, overwrite mode of AUX pmus that don't support hardware sg (some implementations of Intel PT, for instance) is limited to only one contiguous high order allocation for its buffer and there is no way of knowing its size without trying. This patch changes error path freeing to be synchronous as there won't be any contenders for the AUX pages at that point. Reported-by: Markus Metzger Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: David Ahern Cc: Jiri Olsa Cc: Linus Torvalds Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1453216469-9509-1-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 45c815f06b80031659c63d7b93e580015d6024dd) --- kernel/events/ring_buffer.c | 40 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index adfdc0536117..1faad2cfdb9e 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -459,6 +459,25 @@ static void rb_free_aux_page(struct ring_buffer *rb, int idx) __free_page(page); } +static void __rb_free_aux(struct ring_buffer *rb) +{ + int pg; + + if (rb->aux_priv) { + rb->free_aux(rb->aux_priv); + rb->free_aux = NULL; + rb->aux_priv = NULL; + } + + if (rb->aux_nr_pages) { + for (pg = 0; pg < rb->aux_nr_pages; pg++) + rb_free_aux_page(rb, pg); + + kfree(rb->aux_pages); + rb->aux_nr_pages = 0; + } +} + int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event, pgoff_t pgoff, int nr_pages, long watermark, int flags) { @@ -547,30 +566,11 @@ out: if (!ret) rb->aux_pgoff = pgoff; else - rb_free_aux(rb); + __rb_free_aux(rb); return ret; } -static void __rb_free_aux(struct ring_buffer *rb) -{ - int pg; - - if (rb->aux_priv) { - rb->free_aux(rb->aux_priv); - rb->free_aux = NULL; - rb->aux_priv = NULL; - } - - if (rb->aux_nr_pages) { - for (pg = 0; pg < rb->aux_nr_pages; pg++) - rb_free_aux_page(rb, pg); - - kfree(rb->aux_pages); - rb->aux_nr_pages = 0; - } -} - void rb_free_aux(struct ring_buffer *rb) { if (atomic_dec_and_test(&rb->aux_refcount)) From 33bb0224c0a4f7e518dc7462ff7440fbc35ded67 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 22 Dec 2015 17:25:15 +0200 Subject: [PATCH 132/656] stm class: Hide STM-specific options if STM is disabled If STM=n, it doesn't make sense to ask about STM_DUMMY and STM_SOURCE_CONSOLE support, which are not even built when enabled anyway. Hence hide these options if STM=n. Reported-by: Linus Torvalds Signed-off-by: Geert Uytterhoeven Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4a2e2b19f96acfc037a9773c7729d133ce1e7e3b) --- drivers/hwtracing/stm/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 83e9f591a54b..4c13762f2b4d 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -8,6 +8,8 @@ config STM Say Y here to enable System Trace Module device support. +if STM + config STM_DUMMY tristate "Dummy STM driver" help @@ -24,3 +26,5 @@ config STM_SOURCE_CONSOLE If you want to send kernel console messages over STM devices, say Y. + +endif From 51d81f32321e52023849e73a7ea777f89757e6f3 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 22 Dec 2015 17:25:17 +0200 Subject: [PATCH 133/656] stm class: Select CONFIG_SRCU The newly added STM code uses SRCU, but does not ensure that this code is part of the kernel: drivers/built-in.o: In function `stm_source_link_show': include/linux/srcu.h:221: undefined reference to `__srcu_read_lock' include/linux/srcu.h:238: undefined reference to `__srcu_read_unlock' drivers/built-in.o: In function `stm_source_link_drop': include/linux/srcu.h:221: undefined reference to `__srcu_read_lock' include/linux/srcu.h:238: undefined reference to `__srcu_read_unlock' This adds a Kconfig 'select' statement like all the other SRCU using drivers have. Signed-off-by: Arnd Bergmann Fixes: 7bd1d4093c2f ("stm class: Introduce an abstraction for System Trace Module devices") Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 042d4460b5b4379a12f375045ff9065cf6758735) --- drivers/hwtracing/stm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index 4c13762f2b4d..e0ac75395526 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -1,6 +1,7 @@ config STM tristate "System Trace Module devices" select CONFIGFS_FS + select SRCU help A System Trace Module (STM) is a device exporting data in System Trace Protocol (STP) format as defined by MIPI STP standards. From df313b9ca8b14575387fbe53e5609caacde3e243 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:18 +0200 Subject: [PATCH 134/656] stm class: Fix locking in unbinding policy path Right now, if stm device removal has to unbind from a policy (that is, an stm device that has STP policy, gets removed), it will trigger a nested lock on the stm device's policy mutex. This patch fixes the problem by moving the locking from the policy unbinding to policy removal (configfs path), where it's actually needed; the other caller of the policy unbinding function already takes the mutex around the call. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4c127fd16e6b33ecb7badc091480c84ea9aebeb6) --- drivers/hwtracing/stm/policy.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 11ab6d01adf6..94d3abfb737a 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -272,13 +272,17 @@ void stp_policy_unbind(struct stp_policy *policy) { struct stm_device *stm = policy->stm; + /* + * stp_policy_release() will not call here if the policy is already + * unbound; other users should not either, as no link exists between + * this policy and anything else in that case + */ if (WARN_ON_ONCE(!policy->stm)) return; - mutex_lock(&stm->policy_mutex); - stm->policy = NULL; - mutex_unlock(&stm->policy_mutex); + lockdep_assert_held(&stm->policy_mutex); + stm->policy = NULL; policy->stm = NULL; stm_put_device(stm); @@ -287,8 +291,16 @@ void stp_policy_unbind(struct stp_policy *policy) static void stp_policy_release(struct config_item *item) { struct stp_policy *policy = to_stp_policy(item); + struct stm_device *stm = policy->stm; + /* a policy *can* be unbound and still exist in configfs tree */ + if (!stm) + return; + + mutex_lock(&stm->policy_mutex); stp_policy_unbind(policy); + mutex_unlock(&stm->policy_mutex); + kfree(policy); } From 0f049c5ab8a7bc839647702fd29c63177f9e7c6b Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:19 +0200 Subject: [PATCH 135/656] stm class: Fix link list locking Currently, the list of stm_sources linked to an stm device is protected by a spinlock, which also means that sources' .unlink() method is called under this spinlock. However, this method may (and does) sleep, which means trouble. This patch slightly reworks locking around stm::link_list so that bits that might_sleep() are called with a mutex held instead. Modification of this list requires both mutex and spinlock to be held, while looking at the list can be done under either mutex or spinlock. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c74f7e8281add80bdfa0ad2998b8df287b13df73) --- drivers/hwtracing/stm/core.c | 38 +++++++++++++++++++++++++++--------- drivers/hwtracing/stm/stm.h | 1 + 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index b6445d9e5453..ddcb606acea6 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -641,6 +641,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, if (err) goto err_device; + mutex_init(&stm->link_mutex); spin_lock_init(&stm->link_lock); INIT_LIST_HEAD(&stm->link_list); @@ -671,11 +672,11 @@ void stm_unregister_device(struct stm_data *stm_data) struct stm_source_device *src, *iter; int i; - spin_lock(&stm->link_lock); + mutex_lock(&stm->link_mutex); list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { __stm_source_link_drop(src, stm); } - spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); synchronize_srcu(&stm_source_srcu); @@ -694,6 +695,17 @@ void stm_unregister_device(struct stm_data *stm_data) } EXPORT_SYMBOL_GPL(stm_unregister_device); +/* + * stm::link_list access serialization uses a spinlock and a mutex; holding + * either of them guarantees that the list is stable; modification requires + * holding both of them. + * + * Lock ordering is as follows: + * stm::link_mutex + * stm::link_lock + * src::link_lock + */ + /** * stm_source_link_add() - connect an stm_source device to an stm device * @src: stm_source device @@ -710,6 +722,7 @@ static int stm_source_link_add(struct stm_source_device *src, char *id; int err; + mutex_lock(&stm->link_mutex); spin_lock(&stm->link_lock); spin_lock(&src->link_lock); @@ -719,6 +732,7 @@ static int stm_source_link_add(struct stm_source_device *src, spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); id = kstrdup(src->data->name, GFP_KERNEL); if (id) { @@ -756,6 +770,7 @@ fail_free_output: stm_put_device(stm); fail_detach: + mutex_lock(&stm->link_mutex); spin_lock(&stm->link_lock); spin_lock(&src->link_lock); @@ -764,6 +779,7 @@ fail_detach: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); return err; } @@ -776,13 +792,20 @@ fail_detach: * If @stm is @src::link, disconnect them from one another and put the * reference on the @stm device. * - * Caller must hold stm::link_lock. + * Caller must hold stm::link_mutex. */ static void __stm_source_link_drop(struct stm_source_device *src, struct stm_device *stm) { struct stm_device *link; + lockdep_assert_held(&stm->link_mutex); + + if (src->data->unlink) + src->data->unlink(src->data); + + /* for stm::link_list modification, we hold both mutex and spinlock */ + spin_lock(&stm->link_lock); spin_lock(&src->link_lock); link = srcu_dereference_check(src->link, &stm_source_srcu, 1); if (WARN_ON_ONCE(link != stm)) { @@ -791,13 +814,13 @@ static void __stm_source_link_drop(struct stm_source_device *src, } stm_output_free(link, &src->output); - /* caller must hold stm::link_lock */ list_del_init(&src->link_entry); /* matches stm_find_device() from stm_source_link_store() */ stm_put_device(link); rcu_assign_pointer(src->link, NULL); spin_unlock(&src->link_lock); + spin_unlock(&stm->link_lock); } /** @@ -819,12 +842,9 @@ static void stm_source_link_drop(struct stm_source_device *src) stm = srcu_dereference(src->link, &stm_source_srcu); if (stm) { - if (src->data->unlink) - src->data->unlink(src->data); - - spin_lock(&stm->link_lock); + mutex_lock(&stm->link_mutex); __stm_source_link_drop(src, stm); - spin_unlock(&stm->link_lock); + mutex_unlock(&stm->link_mutex); } srcu_read_unlock(&stm_source_srcu, idx); diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 95ece0292c99..97ee02241440 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -45,6 +45,7 @@ struct stm_device { int major; unsigned int sw_nmasters; struct stm_data *data; + struct mutex link_mutex; spinlock_t link_lock; struct list_head link_list; /* master allocation */ From 1caf1d3016fcf51a9cb67ac0b750ac90cbc32bbe Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 22 Dec 2015 17:25:21 +0200 Subject: [PATCH 136/656] stm class: Prevent user-controllable allocations Currently, the character device write method allocates a temporary buffer for user's data, but the user's data size is not sanitized and can cause arbitrarily large allocations via kzalloc() or an integer overflow that will then result in overwriting kernel memory. This patch trims the input buffer size to avoid these issues. Reported-by: Sasha Levin Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f08b18266c7116e2ec6885dd53a928f580060a71) --- drivers/hwtracing/stm/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index ddcb606acea6..76697ad59f2d 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -406,6 +406,9 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, char *kbuf; int err; + if (count + 1 > PAGE_SIZE) + count = PAGE_SIZE - 1; + /* * if no m/c have been assigned to this writer up to this * point, use "default" policy entry From a22a7d13aa3875912aa74cc0ba1624fa5fb9990f Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:01 +0200 Subject: [PATCH 137/656] stm class: Use driver's packet callback return value STM drivers provide a callback to generate/send individual STP packets; it also tells the stm core how many bytes of payload it has consumed. However, we would also need to use the negative space of this return value to communicate errors that occur during the packet generation, in which case the stm core will have to take appropriate action. For now, we need to account for the possibility that the stm driver may not support certain combinations of packet type/flags, in which case it is expected to signal an error. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f8560a9bc76b2cd5c06fa412cb7b5481d70fcf34) --- drivers/hwtracing/stm/core.c | 19 ++++++++++++------- include/linux/stm.h | 7 +++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 76697ad59f2d..1f2b7b47692d 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -380,8 +380,8 @@ static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width) return ret; } -static void stm_write(struct stm_data *data, unsigned int master, - unsigned int channel, const char *buf, size_t count) +static ssize_t stm_write(struct stm_data *data, unsigned int master, + unsigned int channel, const char *buf, size_t count) { unsigned int flags = STP_PACKET_TIMESTAMPED; const unsigned char *p = buf, nil = 0; @@ -393,9 +393,14 @@ static void stm_write(struct stm_data *data, unsigned int master, sz = data->packet(data, master, channel, STP_PACKET_DATA, flags, sz, p); flags = 0; + + if (sz < 0) + break; } data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil); + + return pos; } static ssize_t stm_char_write(struct file *file, const char __user *buf, @@ -433,8 +438,8 @@ static ssize_t stm_char_write(struct file *file, const char __user *buf, return -EFAULT; } - stm_write(stm->data, stmf->output.master, stmf->output.channel, kbuf, - count); + count = stm_write(stm->data, stmf->output.master, stmf->output.channel, + kbuf, count); kfree(kbuf); @@ -996,9 +1001,9 @@ int stm_source_write(struct stm_source_data *data, unsigned int chan, stm = srcu_dereference(src->link, &stm_source_srcu); if (stm) - stm_write(stm->data, src->output.master, - src->output.channel + chan, - buf, count); + count = stm_write(stm->data, src->output.master, + src->output.channel + chan, + buf, count); else count = -ENODEV; diff --git a/include/linux/stm.h b/include/linux/stm.h index 9d0083d364e6..ab8ceca4f570 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -67,6 +67,13 @@ struct stm_device; * description. That is, the lowest master that can be allocated to software * writers is @sw_start and data from this writer will appear is @sw_start * master in the STP stream. + * + * The @packet callback should adhere to the following rules: + * 1) it must return the number of bytes it consumed from the payload; + * 2) therefore, if it sent a packet that does not have payload (like FLAG), + * it must return zero; + * 3) if it does not support the requested packet type/flag combination, + * it must return -ENOTSUPP. */ struct stm_data { const char *name; From 548e3d400155fc6a3f473063368c7028a25d631b Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:02 +0200 Subject: [PATCH 138/656] stm class: Support devices with multiple instances By convention, the name of the stm policy directory in configfs consists of the device name to which it applies and the actual policy name, separated by a dot. Now, some devices already have dots in their names that separate name of the actual device from its instance identifier. Such devices will result in two (or more, who can tell) dots in the policy directory name. Existing policy code, however, will treat the first dot as the one that separates device name from policy name, therefore failing the above case. This patch makes the last dot in the directory name be the separator, thus prohibiting dots from being used in policy names. Suggested-by: Chunyan Zhang Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 59be422e4ce10e3d49d4c9407a80fab8a9b7bc84) --- drivers/hwtracing/stm/policy.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 94d3abfb737a..1db189657b2b 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -332,10 +332,11 @@ stp_policies_make(struct config_group *group, const char *name) /* * node must look like ., where - * is the name of an existing stm device and - * is an arbitrary string + * is the name of an existing stm device; may + * contain dots; + * is an arbitrary string; may not contain dots */ - p = strchr(devname, '.'); + p = strrchr(devname, '.'); if (!p) { kfree(devname); return ERR_PTR(-EINVAL); From bff93fcc5f62c9318652892c4310eaf551e5c2fa Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:03 +0200 Subject: [PATCH 139/656] stm class: dummy_stm: Create multiple devices STM framework should be able to handle multiple STM devices at a time, each one with its own master allocation policy. This patch changes dummy_stm driver to create multiple STM sinks to help testing the framework. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit bcfdf8afdebe63a2217fa632ae94f8aeecf9126f) --- drivers/hwtracing/stm/dummy_stm.c | 57 ++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index 3709bef0b21f..4ff5961cd36f 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -40,22 +40,61 @@ dummy_stm_packet(struct stm_data *stm_data, unsigned int master, return size; } -static struct stm_data dummy_stm = { - .name = "dummy_stm", - .sw_start = 0x0000, - .sw_end = 0xffff, - .sw_nchannels = 0xffff, - .packet = dummy_stm_packet, -}; +#define DUMMY_STM_MAX 32 + +static struct stm_data dummy_stm[DUMMY_STM_MAX]; + +static int nr_dummies = 4; + +module_param(nr_dummies, int, 0600); + +static unsigned int dummy_stm_nr; static int dummy_stm_init(void) { - return stm_register_device(NULL, &dummy_stm, THIS_MODULE); + int i, ret = -ENOMEM, __nr_dummies = ACCESS_ONCE(nr_dummies); + + if (__nr_dummies < 0 || __nr_dummies > DUMMY_STM_MAX) + return -EINVAL; + + for (i = 0; i < __nr_dummies; i++) { + dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i); + if (!dummy_stm[i].name) + goto fail_unregister; + + dummy_stm[i].sw_start = 0x0000; + dummy_stm[i].sw_end = 0xffff; + dummy_stm[i].sw_nchannels = 0xffff; + dummy_stm[i].packet = dummy_stm_packet; + + ret = stm_register_device(NULL, &dummy_stm[i], THIS_MODULE); + if (ret) + goto fail_free; + } + + dummy_stm_nr = __nr_dummies; + + return 0; + +fail_unregister: + for (i--; i >= 0; i--) { + stm_unregister_device(&dummy_stm[i]); +fail_free: + kfree(dummy_stm[i].name); + } + + return ret; + } static void dummy_stm_exit(void) { - stm_unregister_device(&dummy_stm); + int i; + + for (i = 0; i < dummy_stm_nr; i++) { + stm_unregister_device(&dummy_stm[i]); + kfree(dummy_stm[i].name); + } } module_init(dummy_stm_init); From 8ffc98078a5ea1acb750ff53c7a935613e201e72 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:04 +0200 Subject: [PATCH 140/656] stm class: Add heartbeat stm source device Heartbeat stm source may have multiple instances (for connecting to different stm devices). Each instance will send a periodic test message over its stm device when it is linked. This can be used for testing stm class framework, stm device drivers or as a heartbeat over the stm link. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 1192918530381b5cfc0e5da51233fa94f783b221) --- drivers/hwtracing/stm/Kconfig | 11 +++ drivers/hwtracing/stm/Makefile | 2 + drivers/hwtracing/stm/heartbeat.c | 130 ++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 drivers/hwtracing/stm/heartbeat.c diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig index e0ac75395526..847a39b35307 100644 --- a/drivers/hwtracing/stm/Kconfig +++ b/drivers/hwtracing/stm/Kconfig @@ -28,4 +28,15 @@ config STM_SOURCE_CONSOLE If you want to send kernel console messages over STM devices, say Y. +config STM_SOURCE_HEARTBEAT + tristate "Heartbeat over STM devices" + help + This is a kernel space trace source that sends periodic + heartbeat messages to trace hosts over STM devices. It is + also useful for testing stm class drivers and the stm class + framework itself. + + If you want to send heartbeat messages over STM devices, + say Y. + endif diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile index f9312c38dd7a..a9ce3d487e57 100644 --- a/drivers/hwtracing/stm/Makefile +++ b/drivers/hwtracing/stm/Makefile @@ -5,5 +5,7 @@ stm_core-y := core.o policy.o obj-$(CONFIG_STM_DUMMY) += dummy_stm.o obj-$(CONFIG_STM_SOURCE_CONSOLE) += stm_console.o +obj-$(CONFIG_STM_SOURCE_HEARTBEAT) += stm_heartbeat.o stm_console-y := console.o +stm_heartbeat-y := heartbeat.o diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c new file mode 100644 index 000000000000..0133571b506f --- /dev/null +++ b/drivers/hwtracing/stm/heartbeat.c @@ -0,0 +1,130 @@ +/* + * Simple heartbeat STM source driver + * Copyright (c) 2016, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * Heartbeat STM source will send repetitive messages over STM devices to a + * trace host. + */ + +#include +#include +#include +#include +#include + +#define STM_HEARTBEAT_MAX 32 + +static int nr_devs = 4; +static int interval_ms = 10; + +module_param(nr_devs, int, 0600); +module_param(interval_ms, int, 0600); + +static struct stm_heartbeat { + struct stm_source_data data; + struct hrtimer hrtimer; + unsigned int active; +} stm_heartbeat[STM_HEARTBEAT_MAX]; + +static unsigned int nr_instances; + +static const char str[] = "heartbeat stm source driver is here to serve you"; + +static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr) +{ + struct stm_heartbeat *heartbeat = container_of(hr, struct stm_heartbeat, + hrtimer); + + stm_source_write(&heartbeat->data, 0, str, sizeof str); + if (heartbeat->active) + hrtimer_forward_now(hr, ms_to_ktime(interval_ms)); + + return heartbeat->active ? HRTIMER_RESTART : HRTIMER_NORESTART; +} + +static int stm_heartbeat_link(struct stm_source_data *data) +{ + struct stm_heartbeat *heartbeat = + container_of(data, struct stm_heartbeat, data); + + heartbeat->active = 1; + hrtimer_start(&heartbeat->hrtimer, ms_to_ktime(interval_ms), + HRTIMER_MODE_ABS); + + return 0; +} + +static void stm_heartbeat_unlink(struct stm_source_data *data) +{ + struct stm_heartbeat *heartbeat = + container_of(data, struct stm_heartbeat, data); + + heartbeat->active = 0; + hrtimer_cancel(&heartbeat->hrtimer); +} + +static int stm_heartbeat_init(void) +{ + int i, ret = -ENOMEM, __nr_instances = ACCESS_ONCE(nr_devs); + + if (__nr_instances < 0 || __nr_instances > STM_HEARTBEAT_MAX) + return -EINVAL; + + for (i = 0; i < __nr_instances; i++) { + stm_heartbeat[i].data.name = + kasprintf(GFP_KERNEL, "heartbeat.%d", i); + if (!stm_heartbeat[i].data.name) + goto fail_unregister; + + stm_heartbeat[i].data.nr_chans = 1; + stm_heartbeat[i].data.link = stm_heartbeat_link; + stm_heartbeat[i].data.unlink = stm_heartbeat_unlink; + hrtimer_init(&stm_heartbeat[i].hrtimer, CLOCK_MONOTONIC, + HRTIMER_MODE_ABS); + stm_heartbeat[i].hrtimer.function = + stm_heartbeat_hrtimer_handler; + + ret = stm_source_register_device(NULL, &stm_heartbeat[i].data); + if (ret) + goto fail_free; + } + + nr_instances = __nr_instances; + + return 0; + +fail_unregister: + for (i--; i >= 0; i--) { + stm_source_unregister_device(&stm_heartbeat[i].data); +fail_free: + kfree(stm_heartbeat[i].data.name); + } + + return ret; +} + +static void stm_heartbeat_exit(void) +{ + int i; + + for (i = 0; i < nr_instances; i++) { + stm_source_unregister_device(&stm_heartbeat[i].data); + kfree(stm_heartbeat[i].data.name); + } +} + +module_init(stm_heartbeat_init); +module_exit(stm_heartbeat_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("stm_heartbeat driver"); +MODULE_AUTHOR("Alexander Shishkin "); From 1f6037f5220b05dc3dda8d917d9c57fc29efa0ea Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:05 +0200 Subject: [PATCH 141/656] stm class: Fix unlocking braino in the error path If an illegal attempt is made to unlink stm source device from an stm device, the stm device's link spinlock mistakenly remains locked. While this really shouldn't happen (there's a warning in place), the locking should remain in order so that we can still recover from this situation if it indeed does happen. This patch unifies the unlocking in the exit path of __stm_source_link_drop() to fix this. Reported-by: Laurent Fert Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 1810f2c44817c74ca3d05d1e3981e3a2e2ceb6f5) --- drivers/hwtracing/stm/core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 1f2b7b47692d..7aaed081fbf9 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -816,10 +816,8 @@ static void __stm_source_link_drop(struct stm_source_device *src, spin_lock(&stm->link_lock); spin_lock(&src->link_lock); link = srcu_dereference_check(src->link, &stm_source_srcu, 1); - if (WARN_ON_ONCE(link != stm)) { - spin_unlock(&src->link_lock); - return; - } + if (WARN_ON_ONCE(link != stm)) + goto unlock; stm_output_free(link, &src->output); list_del_init(&src->link_entry); @@ -827,6 +825,7 @@ static void __stm_source_link_drop(struct stm_source_device *src, stm_put_device(link); rcu_assign_pointer(src->link, NULL); +unlock: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); } From dd5219ad9e3d488a42c341c10a5ee060d0a322dd Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:06 +0200 Subject: [PATCH 142/656] stm class: Guard output assignment against concurrency It is possible to concurrently assign the same output (a character device writer or an stm_source device) to different stm devices, which sets off a strategically placed warning in stm_output_assign(). To avoid this, use a spinlock to serialize (un)assignments between outputs and stm devices. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit cde4ad8368840e414ecf67db258fe1dabaa5fd2e) --- drivers/hwtracing/stm/core.c | 17 +++++++++++++++++ drivers/hwtracing/stm/stm.h | 1 + 2 files changed, 18 insertions(+) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 7aaed081fbf9..41fee197ed31 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -185,6 +185,9 @@ static void stm_output_claim(struct stm_device *stm, struct stm_output *output) { struct stp_master *master = stm_master(stm, output->master); + lockdep_assert_held(&stm->mc_lock); + lockdep_assert_held(&output->lock); + if (WARN_ON_ONCE(master->nr_free < output->nr_chans)) return; @@ -199,6 +202,9 @@ stm_output_disclaim(struct stm_device *stm, struct stm_output *output) { struct stp_master *master = stm_master(stm, output->master); + lockdep_assert_held(&stm->mc_lock); + lockdep_assert_held(&output->lock); + bitmap_release_region(&master->chan_map[0], output->channel, ilog2(output->nr_chans)); @@ -288,6 +294,7 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, } spin_lock(&stm->mc_lock); + spin_lock(&output->lock); /* output is already assigned -- shouldn't happen */ if (WARN_ON_ONCE(output->nr_chans)) goto unlock; @@ -304,6 +311,7 @@ static int stm_output_assign(struct stm_device *stm, unsigned int width, ret = 0; unlock: + spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); return ret; @@ -312,11 +320,18 @@ unlock: static void stm_output_free(struct stm_device *stm, struct stm_output *output) { spin_lock(&stm->mc_lock); + spin_lock(&output->lock); if (output->nr_chans) stm_output_disclaim(stm, output); + spin_unlock(&output->lock); spin_unlock(&stm->mc_lock); } +static void stm_output_init(struct stm_output *output) +{ + spin_lock_init(&output->lock); +} + static int major_match(struct device *dev, const void *data) { unsigned int major = *(unsigned int *)data; @@ -339,6 +354,7 @@ static int stm_char_open(struct inode *inode, struct file *file) if (!stmf) return -ENOMEM; + stm_output_init(&stmf->output); stmf->stm = to_stm_device(dev); if (!try_module_get(stmf->stm->owner)) @@ -952,6 +968,7 @@ int stm_source_register_device(struct device *parent, if (err) goto err; + stm_output_init(&src->output); spin_lock_init(&src->link_lock); INIT_LIST_HEAD(&src->link_entry); src->data = data; diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h index 97ee02241440..4e8c6926260f 100644 --- a/drivers/hwtracing/stm/stm.h +++ b/drivers/hwtracing/stm/stm.h @@ -57,6 +57,7 @@ struct stm_device { container_of((_d), struct stm_device, dev) struct stm_output { + spinlock_t lock; unsigned int master; unsigned int channel; unsigned int nr_chans; From 984a45ed12b5c04728f85e9c258ba1ae9ebac2d3 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:07 +0200 Subject: [PATCH 143/656] stm class: Fix unbalanced module/device refcounting STM code takes references to the stm device and its module for the duration of the character device's existence or the stm_source link. Dropping these references is not well balanced everywhere, which may lead to leaks. This patch balances the acquisition and releasing of these two references and annotates each site so that it's easier to verify correctness by reading the code. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f7c81c7176c72c7899390754b4b038a64b296e4d) --- drivers/hwtracing/stm/core.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 41fee197ed31..31a03e2fae29 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -113,6 +113,7 @@ struct stm_device *stm_find_device(const char *buf) stm = to_stm_device(dev); if (!try_module_get(stm->owner)) { + /* matches class_find_device() above */ put_device(dev); return NULL; } @@ -125,7 +126,7 @@ struct stm_device *stm_find_device(const char *buf) * @stm: stm device, previously acquired by stm_find_device() * * This drops the module reference and device reference taken by - * stm_find_device(). + * stm_find_device() or stm_char_open(). */ void stm_put_device(struct stm_device *stm) { @@ -365,6 +366,8 @@ static int stm_char_open(struct inode *inode, struct file *file) return nonseekable_open(inode, file); err_free: + /* matches class_find_device() above */ + put_device(dev); kfree(stmf); return err; @@ -375,6 +378,11 @@ static int stm_char_release(struct inode *inode, struct file *file) struct stm_file *stmf = file->private_data; stm_output_free(stmf->stm, &stmf->output); + + /* + * matches the stm_char_open()'s + * class_find_device() + try_module_get() + */ stm_put_device(stmf->stm); kfree(stmf); @@ -539,10 +547,8 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) ret = stm->data->link(stm->data, stmf->output.master, stmf->output.channel); - if (ret) { + if (ret) stm_output_free(stmf->stm, &stmf->output); - stm_put_device(stmf->stm); - } err_free: kfree(id); @@ -679,6 +685,7 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, return 0; err_device: + /* matches device_initialize() above */ put_device(&stm->dev); err_free: kfree(stm); @@ -791,7 +798,6 @@ static int stm_source_link_add(struct stm_source_device *src, fail_free_output: stm_output_free(stm, &src->output); - stm_put_device(stm); fail_detach: mutex_lock(&stm->link_mutex); @@ -905,8 +911,10 @@ static ssize_t stm_source_link_store(struct device *dev, return -EINVAL; err = stm_source_link_add(src, link); - if (err) + if (err) { + /* matches the stm_find_device() above */ stm_put_device(link); + } return err ? : count; } From fffb71e99d58c8d8a1481821a9d39d90b004dc38 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:08 +0200 Subject: [PATCH 144/656] stm class: Fix a race in unlinking There is a window in stm_source_link_drop(), during which the source's link may change before locks are acquired. When this happens, it throws a warning, since this is not an expected scenario. This patch handles the race in such a way that if the link appears to have changed by the time we took the locks, it will release them and repeat the whole unlinking procedure from the beginning, unless the other contender beat us to it. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b4ca34aaf78ed0cdfc15956d377064104257a437) --- drivers/hwtracing/stm/core.c | 54 ++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 31a03e2fae29..e072652d9646 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -694,18 +694,26 @@ err_free: } EXPORT_SYMBOL_GPL(stm_register_device); -static void __stm_source_link_drop(struct stm_source_device *src, - struct stm_device *stm); +static int __stm_source_link_drop(struct stm_source_device *src, + struct stm_device *stm); void stm_unregister_device(struct stm_data *stm_data) { struct stm_device *stm = stm_data->stm; struct stm_source_device *src, *iter; - int i; + int i, ret; mutex_lock(&stm->link_mutex); list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) { - __stm_source_link_drop(src, stm); + ret = __stm_source_link_drop(src, stm); + /* + * src <-> stm link must not change under the same + * stm::link_mutex, so complain loudly if it has; + * also in this situation ret!=0 means this src is + * not connected to this stm and it should be otherwise + * safe to proceed with the tear-down of stm. + */ + WARN_ON_ONCE(ret); } mutex_unlock(&stm->link_mutex); @@ -824,22 +832,28 @@ fail_detach: * * Caller must hold stm::link_mutex. */ -static void __stm_source_link_drop(struct stm_source_device *src, - struct stm_device *stm) +static int __stm_source_link_drop(struct stm_source_device *src, + struct stm_device *stm) { struct stm_device *link; + int ret = 0; lockdep_assert_held(&stm->link_mutex); - if (src->data->unlink) - src->data->unlink(src->data); - /* for stm::link_list modification, we hold both mutex and spinlock */ spin_lock(&stm->link_lock); spin_lock(&src->link_lock); link = srcu_dereference_check(src->link, &stm_source_srcu, 1); - if (WARN_ON_ONCE(link != stm)) + + /* + * The linked device may have changed since we last looked, because + * we weren't holding the src::link_lock back then; if this is the + * case, tell the caller to retry. + */ + if (link != stm) { + ret = -EAGAIN; goto unlock; + } stm_output_free(link, &src->output); list_del_init(&src->link_entry); @@ -850,6 +864,11 @@ static void __stm_source_link_drop(struct stm_source_device *src, unlock: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); + + if (!ret && src->data->unlink) + src->data->unlink(src->data); + + return ret; } /** @@ -865,18 +884,29 @@ unlock: static void stm_source_link_drop(struct stm_source_device *src) { struct stm_device *stm; - int idx; + int idx, ret; +retry: idx = srcu_read_lock(&stm_source_srcu); + /* + * The stm device will be valid for the duration of this + * read section, but the link may change before we grab + * the src::link_lock in __stm_source_link_drop(). + */ stm = srcu_dereference(src->link, &stm_source_srcu); + ret = 0; if (stm) { mutex_lock(&stm->link_mutex); - __stm_source_link_drop(src, stm); + ret = __stm_source_link_drop(src, stm); mutex_unlock(&stm->link_mutex); } srcu_read_unlock(&stm_source_srcu, idx); + + /* if it did change, retry */ + if (ret == -EAGAIN) + goto retry; } static ssize_t stm_source_link_show(struct device *dev, From d3b187f9c7bc7f9f73e4064041890fd71de9f5b3 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:09 +0200 Subject: [PATCH 145/656] stm class: Plug stm device's unlink callback STM device's unlink callback is never actually called from anywhere in the stm class code. This patch adds calls to stm driver's unlink method after the unlinking has succeeded. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit cc8424074e51355e0c6ba717d8edc50d408f2802) --- drivers/hwtracing/stm/core.c | 23 +++++++++++++++++++---- include/linux/stm.h | 3 +++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index e072652d9646..0dd8f4c996f1 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -376,14 +376,19 @@ err_free: static int stm_char_release(struct inode *inode, struct file *file) { struct stm_file *stmf = file->private_data; + struct stm_device *stm = stmf->stm; - stm_output_free(stmf->stm, &stmf->output); + if (stm->data->unlink) + stm->data->unlink(stm->data, stmf->output.master, + stmf->output.channel); + + stm_output_free(stm, &stmf->output); /* * matches the stm_char_open()'s * class_find_device() + try_module_get() */ - stm_put_device(stmf->stm); + stm_put_device(stm); kfree(stmf); return 0; @@ -865,8 +870,18 @@ unlock: spin_unlock(&src->link_lock); spin_unlock(&stm->link_lock); - if (!ret && src->data->unlink) - src->data->unlink(src->data); + /* + * Call the unlink callbacks for both source and stm, when we know + * that we have actually performed the unlinking. + */ + if (!ret) { + if (src->data->unlink) + src->data->unlink(src->data); + + if (stm->data->unlink) + stm->data->unlink(stm->data, src->output.master, + src->output.channel); + } return ret; } diff --git a/include/linux/stm.h b/include/linux/stm.h index ab8ceca4f570..1a79ed8e43da 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -74,6 +74,9 @@ struct stm_device; * it must return zero; * 3) if it does not support the requested packet type/flag combination, * it must return -ENOTSUPP. + * + * The @unlink callback is called when there are no more active writers so + * that the master/channel can be quiesced. */ struct stm_data { const char *name; From a2fc3f7248b75b8cdbf54590e41874aebd19303d Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 15 Feb 2016 19:12:10 +0200 Subject: [PATCH 146/656] stm class: dummy_stm: Add link callback for fault injection STM device's link callback has the power to abort master/channel assignment by returning a negative error code. Use this in dummy stm device to optionally abort assigning certain channel IDs. This is useful as fault injection into the stm class core, for testing purposes. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit adcde635f5d6b77e9f11087af09c6f62da2bf108) --- drivers/hwtracing/stm/dummy_stm.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index 4ff5961cd36f..310adf57e7a1 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -50,6 +50,19 @@ module_param(nr_dummies, int, 0600); static unsigned int dummy_stm_nr; +static unsigned int fail_mode; + +module_param(fail_mode, int, 0600); + +static int dummy_stm_link(struct stm_data *data, unsigned int master, + unsigned int channel) +{ + if (fail_mode && (channel & fail_mode)) + return -EINVAL; + + return 0; +} + static int dummy_stm_init(void) { int i, ret = -ENOMEM, __nr_dummies = ACCESS_ONCE(nr_dummies); @@ -66,6 +79,7 @@ static int dummy_stm_init(void) dummy_stm[i].sw_end = 0xffff; dummy_stm[i].sw_nchannels = 0xffff; dummy_stm[i].packet = dummy_stm_packet; + dummy_stm[i].link = dummy_stm_link; ret = stm_register_device(NULL, &dummy_stm[i], THIS_MODULE); if (ret) From 072536f0702fc1587c0e0b80377a9b9ca58fda22 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 2 Mar 2016 13:24:14 +0200 Subject: [PATCH 147/656] perf/core: Fix perf_sched_count derailment The error path in perf_event_open() is such that asking for a sampling event on a PMU that doesn't generate interrupts will end up in dropping the perf_sched_count even though it hasn't been incremented for this event yet. Given a sufficient amount of these calls, we'll end up disabling scheduler's jump label even though we'd still have active events in the system, thereby facilitating the arrival of the infernal regions upon us. I'm fixing this by moving account_event() inside perf_event_alloc(). Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1456917854-29427-1-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 927a5570855836e5d5859a80ce7e91e963545e8f) --- kernel/events/core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index cfc227ccfceb..c22f4579a2e7 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7979,6 +7979,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, } } + /* symmetric to unaccount_event() in _free_event() */ + account_event(event); + return event; err_per_task: @@ -8342,8 +8345,6 @@ SYSCALL_DEFINE5(perf_event_open, } } - account_event(event); - /* * Special case software events and allow them to be part of * any hardware group. @@ -8626,8 +8627,6 @@ perf_event_create_kernel_counter(struct perf_event_attr *attr, int cpu, /* Mark owner so we could distinguish it from user events. */ event->owner = EVENT_OWNER_KERNEL; - account_event(event); - ctx = find_get_context(event->pmu, task, event); if (IS_ERR(ctx)) { err = PTR_ERR(ctx); From 75663c46e8076b468ceb1b4d17358cdb65d4ba77 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Mon, 21 Mar 2016 10:02:42 +0200 Subject: [PATCH 148/656] perf/core: Don't leak event in the syscall error path In the error path, event_file not being NULL is used to determine whether the event itself still needs to be free'd, so fix it up to avoid leaking. Reported-by: Leon Yu Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Fixes: 130056275ade ("perf: Do not double free") Link: http://lkml.kernel.org/r/87twk06yxp.fsf@ashishki-desk.ger.corp.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 201c2f85bd0bc13b712d9c0b3d11251b182e06ae) --- kernel/events/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index c22f4579a2e7..b296032a2c4e 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -8457,6 +8457,7 @@ SYSCALL_DEFINE5(perf_event_open, f_flags); if (IS_ERR(event_file)) { err = PTR_ERR(event_file); + event_file = NULL; goto err_context; } From e05ed3268032a6c0dc677558fb82afb046dd638f Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 10 May 2016 16:18:33 +0300 Subject: [PATCH 149/656] perf/core: Disable the event on a truncated AUX record When the PMU driver reports a truncated AUX record, it effectively means that there is no more usable room in the event's AUX buffer (even though there may still be some room, so that perf_aux_output_begin() doesn't take action). At this point the consumer still has to be woken up and the event has to be disabled, otherwise the event will just keep spinning between perf_aux_output_begin() and perf_aux_output_end() until its context gets unscheduled. Again, for cpu-wide events this means never, so once in this condition, they will be forever losing data. Fix this by disabling the event and waking up the consumer in case of a truncated AUX record. Reported-by: Markus Metzger Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Borislav Petkov Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1462886313-13660-3-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 9f448cd3cbcec8995935e60b27802ae56aac8cc0) --- kernel/events/ring_buffer.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 1faad2cfdb9e..287201a5d12f 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -347,6 +347,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, bool truncated) { struct ring_buffer *rb = handle->rb; + bool wakeup = truncated; unsigned long aux_head; u64 flags = 0; @@ -375,9 +376,16 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, aux_head = rb->user_page->aux_head = local_read(&rb->aux_head); if (aux_head - local_read(&rb->aux_wakeup) >= rb->aux_watermark) { - perf_output_wakeup(handle); + wakeup = true; local_add(rb->aux_watermark, &rb->aux_wakeup); } + + if (wakeup) { + if (truncated) + handle->event->pending_disable = 1; + perf_output_wakeup(handle); + } + handle->event = NULL; local_set(&rb->aux_nest, 0); From b746b9cf2f52aa7a572b8be2b1718966b65f14a1 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 17 Dec 2015 08:47:02 -0700 Subject: [PATCH 150/656] coresight: checking for NULL string in coresight_name_match() Connection child names associated to ports can sometimes be NULL, which is the case when booting a system on QEMU or when the Coresight power domain isn't switched on. This patch is adding a check to make sure a NULL string isn't fed to strcmp(), something that avoid crashing the system. Cc: # v3.18+ Reported-by: Tyler Baker Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit fadf3a44e974b030e7145218ad1ab25e3ef91738) --- drivers/hwtracing/coresight/coresight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index e25492137d8b..93738dfbf631 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -548,7 +548,7 @@ static int coresight_name_match(struct device *dev, void *data) to_match = data; i_csdev = to_coresight_device(dev); - if (!strcmp(to_match, dev_name(&i_csdev->dev))) + if (to_match && !strcmp(to_match, dev_name(&i_csdev->dev))) return 1; return 0; From 4b5c6a09cdf7db8c7a30f6b69dc4284bbdd6ed53 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Thu, 17 Dec 2015 08:47:02 -0700 Subject: [PATCH 151/656] coresight: Fix a typo in Kconfig Signed-off-by: Andrew F. Davis Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b9884d3b79f60846cdf04be262d13bca8996f99a) --- drivers/hwtracing/coresight/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index 6c8921140f02..c85935f3525a 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -8,7 +8,7 @@ menuconfig CORESIGHT This framework provides a kernel interface for the CoreSight debug and trace drivers to register themselves with. It's intended to build a topological view of the CoreSight components based on a DT - specification and configure the right serie of components when a + specification and configure the right series of components when a trace source gets enabled. if CORESIGHT From 0d03bf0e9b50a6310cc0f92c08930111605d8540 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:55 -0700 Subject: [PATCH 152/656] coresight: fixing indentation problem Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ff8742279ff163e36ee50bb4e75090af1d7d6e3b) --- drivers/hwtracing/coresight/coresight.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 93738dfbf631..4236a02cdab2 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -536,7 +536,7 @@ static void coresight_fixup_orphan_conns(struct coresight_device *csdev) * are hooked-up with each newly added component. */ bus_for_each_dev(&coresight_bustype, NULL, - csdev, coresight_orphan_match); + csdev, coresight_orphan_match); } From 3e42ab0f61d184a7e6ffa063535afb31538b1595 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:56 -0700 Subject: [PATCH 153/656] coresight: fixing lockdep error On some platform the following lockdep error occurs when doing simple manipulations: [ 23.197021] [ 23.198608] ====================================================== [ 23.205078] [ INFO: possible circular locking dependency detected ] [ 23.211639] 4.4.0-rc8-00025-gbbf360b #172 Not tainted [ 23.216918] ------------------------------------------------------- [ 23.223480] sh/858 is trying to acquire lock: [ 23.228057] (coresight_mutex){+.+.+.}, at: [] coresight_enable+0x1c/0x1b4 [ 23.236206] [ 23.236206] but task is already holding lock: [ 23.242309] (s_active#52){++++.+}, at: [] kernfs_fop_write+0x5c/0x1c0 [ 23.250122] [ 23.250122] which lock already depends on the new lock. [ 23.250122] [ 23.258697] [ 23.258697] the existing dependency chain (in reverse order) is: [ 23.266510] -> #1 (s_active#52){++++.+}: [ 23.270843] [] __kernfs_remove+0x294/0x35c [ 23.276672] [] kernfs_remove_by_name_ns+0x44/0x8c [ 23.283172] [] remove_files+0x3c/0x84 [ 23.288543] [] sysfs_remove_group+0x48/0x9c [ 23.294494] [] sysfs_remove_groups+0x2c/0x3c [ 23.300506] [] device_remove_attrs+0x5c/0x74 [ 23.306549] [] device_del+0x110/0x218 [ 23.311950] [] device_unregister+0x2c/0x6c [ 23.317779] [] coresight_unregister+0x30/0x40 [ 23.323883] [] etm_probe+0x228/0x2e8 [ 23.329193] [] amba_probe+0xe4/0x160 [ 23.334503] [] driver_probe_device+0x23c/0x480 [ 23.340728] [] __driver_attach+0x9c/0xa0 [ 23.346374] [] bus_for_each_dev+0x70/0xa4 [ 23.352142] [] driver_attach+0x24/0x28 [ 23.357604] [] bus_add_driver+0x1e0/0x278 [ 23.363372] [] driver_register+0x80/0x100 [ 23.369110] [] amba_driver_register+0x58/0x5c [ 23.375244] [] etm_driver_init+0x18/0x1c [ 23.380889] [] do_one_initcall+0xc4/0x20c [ 23.386657] [] kernel_init_freeable+0x160/0x208 [ 23.392974] [] kernel_init+0x18/0xf0 [ 23.398254] [] ret_from_fork+0x14/0x24 [ 23.403747] -> #0 (coresight_mutex){+.+.+.}: [ 23.408447] [] lock_acquire+0xe4/0x210 [ 23.413909] [] mutex_lock_nested+0x74/0x450 [ 23.419860] [] coresight_enable+0x1c/0x1b4 [ 23.425689] [] enable_source_store+0x58/0x68 [ 23.431732] [] dev_attr_store+0x20/0x2c [ 23.437286] [] sysfs_kf_write+0x50/0x54 [ 23.442871] [] kernfs_fop_write+0xc4/0x1c0 [ 23.448699] [] __vfs_write+0x34/0xe4 [ 23.454040] [] vfs_write+0x98/0x174 [ 23.459228] [] SyS_write+0x4c/0xa8 [ 23.464355] [] ret_fast_syscall+0x0/0x1c [ 23.470031] [ 23.470031] other info that might help us debug this: [ 23.470031] [ 23.478393] Possible unsafe locking scenario: [ 23.478393] [ 23.484619] CPU0 CPU1 [ 23.489349] ---- ---- [ 23.494079] lock(s_active#52); [ 23.497497] lock(coresight_mutex); [ 23.503906] lock(s_active#52); [ 23.509918] lock(coresight_mutex); [ 23.513702] [ 23.513702] *** DEADLOCK *** [ 23.513702] [ 23.519897] 3 locks held by sh/858: [ 23.523529] #0: (sb_writers#7){.+.+.+}, at: [] __sb_start_write+0xa8/0xd4 [ 23.531799] #1: (&of->mutex){+.+...}, at: [] kernfs_fop_write+0x54/0x1c0 [ 23.539916] #2: (s_active#52){++++.+}, at: [] kernfs_fop_write+0x5c/0x1c0 [ 23.548156] [ 23.548156] stack backtrace: [ 23.552734] CPU: 0 PID: 858 Comm: sh Not tainted 4.4.0-rc8-00025-gbbf360b #172 [ 23.560302] Hardware name: Generic OMAP4 (Flattened Device Tree) [ 23.566589] Backtrace: [ 23.569152] [] (dump_backtrace) from [] (show_stack+0x18/0x1c) [ 23.577087] r7:ed4b8570 r6:c0936400 r5:c07ae71c r4:00000000 [ 23.583038] [] (show_stack) from [] (dump_stack+0x98/0xc0) [ 23.590606] [] (dump_stack) from [] (print_circular_bug+0x21c/0x33c) [ 23.599090] r5:c0939d60 r4:c0936400 [ 23.602874] [] (print_circular_bug) from [] (__lock_acquire+0x1c98/0x1d88) [ 23.611877] r10:00000003 r9:c0fd7a5c r8:ed4b8550 r7:ed4b8570 r6:ed4b8000 r5:c0ff69e4 [ 23.620117] r4:c0936400 r3:ed4b8550 [ 23.623901] [] (__lock_acquire) from [] (lock_acquire+0xe4/0x210) [ 23.632080] r10:00000000 r9:00000000 r8:60000013 r7:c07cb7b4 r6:00000001 r5:00000000 [ 23.640350] r4:00000000 [ 23.643005] [] (lock_acquire) from [] (mutex_lock_nested+0x74/0x450) [ 23.651458] r10:ecc0bf80 r9:edbe7dcc r8:ed4b8000 r7:c0fd7a5c r6:c0415d40 r5:00000000 [ 23.659729] r4:c07cb780 [ 23.662384] [] (mutex_lock_nested) from [] (coresight_enable+0x1c/0x1b4) [ 23.671234] r10:ecc0bf80 r9:edbe7dcc r8:ed733c00 r7:00000000 r6:ed733c00 r5:00000002 [ 23.679473] r4:ed762140 [ 23.682128] [] (coresight_enable) from [] (enable_source_store+0x58/0x68) [ 23.691070] r7:00000000 r6:ed733c00 r5:00000002 r4:ed762160 [ 23.697052] [] (enable_source_store) from [] (dev_attr_store+0x20/0x2c) [ 23.705780] r5:edbe7dc0 r4:c0415fd8 [ 23.709533] [] (dev_attr_store) from [] (sysfs_kf_write+0x50/0x54) [ 23.717834] r5:edbe7dc0 r4:c030b338 [ 23.721618] [] (sysfs_kf_write) from [] (kernfs_fop_write+0xc4/0x1c0) [ 23.730163] r7:00000000 r6:00000000 r5:00000002 r4:edbe7dc0 [ 23.736145] [] (kernfs_fop_write) from [] (__vfs_write+0x34/0xe4) [ 23.744323] r10:00000000 r9:ecc0a000 r8:c0010964 r7:ecc0bf80 r6:00000002 r5:c01d4ae4 [ 23.752593] r4:ee385a40 [ 23.755249] [] (__vfs_write) from [] (vfs_write+0x98/0x174) [ 23.762908] r9:ecc0a000 r8:c0010964 r7:ecc0bf80 r6:000ab0d8 r5:00000002 r4:ee385a40 [ 23.771057] [] (vfs_write) from [] (SyS_write+0x4c/0xa8) [ 23.778442] r8:c0010964 r7:00000002 r6:000ab0d8 r5:ee385a40 r4:ee385a40 [ 23.785522] [] (SyS_write) from [] (ret_fast_syscall+0x0/0x1c) [ 23.793457] r7:00000004 r6:00000001 r5:000ab0d8 r4:00000002 [ 23.799652] coresight-etb10 54162000.etb: ETB enabled [ 23.805084] coresight-funnel 54164000.funnel: FUNNEL inport 0 enabled [ 23.811859] coresight-replicator 44000000.ocp:replicator: REPLICATOR enabled [ 23.819335] coresight-funnel 54158000.funnel: FUNNEL inport 0 enabled [ 23.826110] coresight-etm3x 5414c000.ptm: ETM tracing enabled The locking in coresight_unregister() is not required as the only customers of the function are drivers themselves when an initialisation failure has been encoutered. Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a9ddc71f5840c2711e530f2e055b278f79948b29) --- drivers/hwtracing/coresight/coresight.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 4236a02cdab2..41b42018b660 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -713,12 +713,8 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { - mutex_lock(&coresight_mutex); - kfree(csdev->conns); device_unregister(&csdev->dev); - - mutex_unlock(&coresight_mutex); } EXPORT_SYMBOL_GPL(coresight_unregister); From 27aa9ceebe6a14dcc961126bd2a3f462dfba256b Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:57 -0700 Subject: [PATCH 154/656] coresight: coresight_unregister() function cleanup In its current form the code never frees csdev->refcnt allocated in coresight_register(). There is also a problem with csdev->conns that is freed before device_unregister() rather than in the device release function. This patch addresses both issues by moving kfree(csdev->conns) to coresight_device_release() and freeing csdev->refcnt, also in the same function. Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit fae54158792aec705620bdc3938d342879204f0c) --- drivers/hwtracing/coresight/coresight.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 41b42018b660..8872db4410eb 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -481,6 +481,8 @@ static void coresight_device_release(struct device *dev) { struct coresight_device *csdev = to_coresight_device(dev); + kfree(csdev->conns); + kfree(csdev->refcnt); kfree(csdev); } @@ -713,7 +715,6 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { - kfree(csdev->conns); device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); From f03467661ca82e16b05e786c893bb4cb89951ca3 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:58 -0700 Subject: [PATCH 155/656] coresight: release reference taken by 'bus_find_device()' The reference count taken by function bus_find_device() needs to be released if a child device is found, something this patch is adding. Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f2dfab3568fc32afeac8b698481e80e7ab2dc658) --- drivers/hwtracing/coresight/coresight.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 8872db4410eb..a35ca54a76c9 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -570,6 +570,8 @@ static void coresight_fixup_device_conns(struct coresight_device *csdev) if (dev) { conn->child_dev = to_coresight_device(dev); + /* and put reference from 'bus_find_device()' */ + put_device(dev); } else { csdev->orphan = true; conn->child_dev = NULL; From c7d05101bca353aef2e8d16605eca13737fa9b61 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:13:59 -0700 Subject: [PATCH 156/656] coresight: remove csdev's link from topology In function 'coresight_unregister()', all references to the csdev that is being taken away need to be removed from the topology. Otherwise building the next coresight path from source to sink may use memory that has been released. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ad725aee070caf8fa93d84d6fb78321f9642db18) --- drivers/hwtracing/coresight/coresight.c | 46 +++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index a35ca54a76c9..7e6e9ff27dd1 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -579,6 +579,50 @@ static void coresight_fixup_device_conns(struct coresight_device *csdev) } } +static int coresight_remove_match(struct device *dev, void *data) +{ + int i; + struct coresight_device *csdev, *iterator; + struct coresight_connection *conn; + + csdev = data; + iterator = to_coresight_device(dev); + + /* No need to check oneself */ + if (csdev == iterator) + return 0; + + /* + * Circle throuch all the connection of that component. If we find + * a connection whose name matches @csdev, remove it. + */ + for (i = 0; i < iterator->nr_outport; i++) { + conn = &iterator->conns[i]; + + if (conn->child_dev == NULL) + continue; + + if (!strcmp(dev_name(&csdev->dev), conn->child_name)) { + iterator->orphan = true; + conn->child_dev = NULL; + /* No need to continue */ + break; + } + } + + /* + * Returning '0' ensures that all known component on the + * bus will be checked. + */ + return 0; +} + +static void coresight_remove_conns(struct coresight_device *csdev) +{ + bus_for_each_dev(&coresight_bustype, NULL, + csdev, coresight_remove_match); +} + /** * coresight_timeout - loop until a bit has changed to a specific state. * @addr: base address of the area of interest. @@ -717,6 +761,8 @@ EXPORT_SYMBOL_GPL(coresight_register); void coresight_unregister(struct coresight_device *csdev) { + /* Remove references of that device in the topology */ + coresight_remove_conns(csdev); device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); From b24ef072a99aeb7aeab9ced2a207c6a4af364d52 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:14:00 -0700 Subject: [PATCH 157/656] coresight: removing bind/unbind options from sysfs The coresight drivers have absolutely no control over bind and unbind operations triggered from sysfs. The operations simply can't be cancelled or denied event when one or several tracing sessions are under way. Since the memory associated to individual device is invariably freed, the end result is a kernel crash when the path from source to sink is travelled again as demonstrated here[1]. One solution could be to keep track of all the path (i.e tracing session) that get created and iterate through the elements of those path looking for the coresight device that is being removed. This proposition doesn't scale well since there is no upper bound on the amount of concurrent trace session that can be created. With the above in mind, this patch prevent devices from being unbounded from their driver by using the driver->suppress_bind_attr option. That way trace sessions can be managed without fearing to loose devices. Since device can't be removed anymore the xyz_remove() functions found in each driver is also removed. [1]. http://www.spinics.net/lists/arm-kernel/msg474952.html Reported-by: Rabin Vincent Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b15f0fb657e040401d875d11ae13b269af8a16e0) --- drivers/hwtracing/coresight/coresight-etb10.c | 11 +---------- drivers/hwtracing/coresight/coresight-etm3x.c | 13 +------------ drivers/hwtracing/coresight/coresight-etm4x.c | 13 +------------ drivers/hwtracing/coresight/coresight-funnel.c | 10 +--------- .../coresight/coresight-replicator-qcom.c | 11 +---------- .../hwtracing/coresight/coresight-replicator.c | 16 +--------------- drivers/hwtracing/coresight/coresight-tmc.c | 15 +-------------- drivers/hwtracing/coresight/coresight-tpiu.c | 10 +--------- 8 files changed, 8 insertions(+), 91 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 77d0f9c1118d..92969dae739d 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -489,15 +489,6 @@ err_misc_register: return ret; } -static int etb_remove(struct amba_device *adev) -{ - struct etb_drvdata *drvdata = amba_get_drvdata(adev); - - misc_deregister(&drvdata->miscdev); - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int etb_runtime_suspend(struct device *dev) { @@ -537,10 +528,10 @@ static struct amba_driver etb_driver = { .name = "coresight-etb10", .owner = THIS_MODULE, .pm = &etb_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = etb_probe, - .remove = etb_remove, .id_table = etb_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index d630b7ece735..5981fcc69960 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -1877,17 +1877,6 @@ err_arch_supported: return ret; } -static int etm_remove(struct amba_device *adev) -{ - struct etm_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - if (--etm_count == 0) - unregister_hotcpu_notifier(&etm_cpu_notifier); - - return 0; -} - #ifdef CONFIG_PM static int etm_runtime_suspend(struct device *dev) { @@ -1948,9 +1937,9 @@ static struct amba_driver etm_driver = { .name = "coresight-etm3x", .owner = THIS_MODULE, .pm = &etm_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = etm_probe, - .remove = etm_remove, .id_table = etm_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index a6707642bb23..167004f9c42b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -2684,17 +2684,6 @@ err_coresight_register: return ret; } -static int etm4_remove(struct amba_device *adev) -{ - struct etmv4_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - if (--etm4_count == 0) - unregister_hotcpu_notifier(&etm4_cpu_notifier); - - return 0; -} - static struct amba_id etm4_ids[] = { { /* ETM 4.0 - Qualcomm */ .id = 0x0003b95d, @@ -2712,9 +2701,9 @@ static struct amba_id etm4_ids[] = { static struct amba_driver etm4x_driver = { .drv = { .name = "coresight-etm4x", + .suppress_bind_attrs = true, }, .probe = etm4_probe, - .remove = etm4_remove, .id_table = etm4_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 2e36bde7fcb4..25e8ea140a09 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -226,14 +226,6 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -static int funnel_remove(struct amba_device *adev) -{ - struct funnel_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int funnel_runtime_suspend(struct device *dev) { @@ -273,9 +265,9 @@ static struct amba_driver funnel_driver = { .name = "coresight-funnel", .owner = THIS_MODULE, .pm = &funnel_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = funnel_probe, - .remove = funnel_remove, .id_table = funnel_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 584059e9e866..444815179460 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -156,15 +156,6 @@ static int replicator_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -static int replicator_remove(struct amba_device *adev) -{ - struct replicator_state *drvdata = amba_get_drvdata(adev); - - pm_runtime_disable(&adev->dev); - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int replicator_runtime_suspend(struct device *dev) { @@ -206,9 +197,9 @@ static struct amba_driver replicator_driver = { .drv = { .name = "coresight-replicator-qcom", .pm = &replicator_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = replicator_probe, - .remove = replicator_remove, .id_table = replicator_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 963ac197c253..b77d700a3f0e 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -127,20 +127,6 @@ out_disable_pm: return ret; } -static int replicator_remove(struct platform_device *pdev) -{ - struct replicator_drvdata *drvdata = platform_get_drvdata(pdev); - - coresight_unregister(drvdata->csdev); - pm_runtime_get_sync(&pdev->dev); - if (!IS_ERR(drvdata->atclk)) - clk_disable_unprepare(drvdata->atclk); - pm_runtime_put_noidle(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - return 0; -} - #ifdef CONFIG_PM static int replicator_runtime_suspend(struct device *dev) { @@ -175,11 +161,11 @@ static const struct of_device_id replicator_match[] = { static struct platform_driver replicator_driver = { .probe = replicator_probe, - .remove = replicator_remove, .driver = { .name = "coresight-replicator", .of_match_table = replicator_match, .pm = &replicator_dev_pm_ops, + .suppress_bind_attrs = true, }, }; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index a57c7ec1661f..570d96815f78 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -770,19 +770,6 @@ err_devm_kzalloc: return ret; } -static int tmc_remove(struct amba_device *adev) -{ - struct tmc_drvdata *drvdata = amba_get_drvdata(adev); - - misc_deregister(&drvdata->miscdev); - coresight_unregister(drvdata->csdev); - if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) - dma_free_coherent(drvdata->dev, drvdata->size, - &drvdata->paddr, GFP_KERNEL); - - return 0; -} - static struct amba_id tmc_ids[] = { { .id = 0x0003b961, @@ -795,9 +782,9 @@ static struct amba_driver tmc_driver = { .drv = { .name = "coresight-tmc", .owner = THIS_MODULE, + .suppress_bind_attrs = true, }, .probe = tmc_probe, - .remove = tmc_remove, .id_table = tmc_ids, }; diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 7214efd10db5..ca50e73d9df7 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -172,14 +172,6 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) return 0; } -static int tpiu_remove(struct amba_device *adev) -{ - struct tpiu_drvdata *drvdata = amba_get_drvdata(adev); - - coresight_unregister(drvdata->csdev); - return 0; -} - #ifdef CONFIG_PM static int tpiu_runtime_suspend(struct device *dev) { @@ -223,9 +215,9 @@ static struct amba_driver tpiu_driver = { .name = "coresight-tpiu", .owner = THIS_MODULE, .pm = &tpiu_dev_pm_ops, + .suppress_bind_attrs = true, }, .probe = tpiu_probe, - .remove = tpiu_remove, .id_table = tpiu_ids, }; From 8a403a74c16625044c3bc83ae76791e562952727 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 2 Feb 2016 14:14:01 -0700 Subject: [PATCH 158/656] coresight: implementing 'cpu_id()' API Other than plainly parsing the device tree there is no way to know which CPU a tracer is affined to. As such adding an interface to lookup the CPU field enclosed in the etm_drvdata structure that was initialised at boot time. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 52210c8745e418f82f3f0aeeee01d7bc4858812a) --- drivers/hwtracing/coresight/coresight-etm3x.c | 8 ++++++++ drivers/hwtracing/coresight/coresight-etm4x.c | 8 ++++++++ include/linux/coresight.h | 3 +++ 3 files changed, 19 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 5981fcc69960..aae80e14508d 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -315,6 +315,13 @@ static void etm_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } +static int etm_cpu_id(struct coresight_device *csdev) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return drvdata->cpu; +} + static int etm_trace_id(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -421,6 +428,7 @@ static void etm_disable(struct coresight_device *csdev) } static const struct coresight_ops_source etm_source_ops = { + .cpu_id = etm_cpu_id, .trace_id = etm_trace_id, .enable = etm_enable, .disable = etm_disable, diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 167004f9c42b..b6ae9cb6ff57 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -63,6 +63,13 @@ static bool etm4_arch_supported(u8 arch) return true; } +static int etm4_cpu_id(struct coresight_device *csdev) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return drvdata->cpu; +} + static int etm4_trace_id(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -262,6 +269,7 @@ static void etm4_disable(struct coresight_device *csdev) } static const struct coresight_ops_source etm4_source_ops = { + .cpu_id = etm4_cpu_id, .trace_id = etm4_trace_id, .enable = etm4_enable, .disable = etm4_disable, diff --git a/include/linux/coresight.h b/include/linux/coresight.h index a7cabfa23b55..bf62b265bf52 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -205,12 +205,15 @@ struct coresight_ops_link { /** * struct coresight_ops_source - basic operations for a source * Operations available for sources. + * @cpu_id: returns the value of the CPU number this component + * is associated to. * @trace_id: returns the value of the component's trace ID as known to the HW. * @enable: enables tracing for a source. * @disable: disables tracing for a source. */ struct coresight_ops_source { + int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); int (*enable)(struct coresight_device *csdev); void (*disable)(struct coresight_device *csdev); From fea6fe15e3ac5fae3f220c17f4f56601bbcf4d02 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 17 Feb 2016 17:51:43 -0700 Subject: [PATCH 159/656] coresight: "DEVICE_ATTR_RO" should defined as static. "DEVICE_ATTR_RO(name)" should be defined as static. And there is an unnecessary space at the front of the code. The sparse tool output logs as the following: coresight-etm4x.c:2224:1: warning: symbol 'dev_attr_trcoslsr' was not declared. Should it be static? coresight-etm4x.c:2225:1: warning: symbol 'dev_attr_trcpdcr' was not declared. Should it be static? coresight-etm4x.c:2226:1: warning: symbol 'dev_attr_trcpdsr' was not declared. Should it be static? And the smatch tool output logs as the following: of_coresight.c:89 of_coresight_alloc_memory() warn: inconsistent indenting Signed-off-by: Eric Long Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit bf16e5b8cdeabc1fe6565af0be475bb2084dc388) --- drivers/hwtracing/coresight/coresight-etm4x.c | 2 +- drivers/hwtracing/coresight/of_coresight.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index b6ae9cb6ff57..d6a92c6d3a66 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -2227,7 +2227,7 @@ static ssize_t name##_show(struct device *_dev, \ return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ readl_relaxed(drvdata->base + offset)); \ } \ -DEVICE_ATTR_RO(name) +static DEVICE_ATTR_RO(name) coresight_simple_func(trcoslsr, TRCOSLSR); coresight_simple_func(trcpdcr, TRCPDCR); diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index b0973617826f..3cc57c1e3b5d 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -86,7 +86,7 @@ static int of_coresight_alloc_memory(struct device *dev, return -ENOMEM; /* Children connected to this component via @outports */ - pdata->child_names = devm_kzalloc(dev, pdata->nr_outport * + pdata->child_names = devm_kzalloc(dev, pdata->nr_outport * sizeof(*pdata->child_names), GFP_KERNEL); if (!pdata->child_names) From e1e8321c38b7e47ca9b8095b584dc89bef46504c Mon Sep 17 00:00:00 2001 From: Eric Long Date: Wed, 17 Feb 2016 17:51:44 -0700 Subject: [PATCH 160/656] coresight: etm4x: Check every parameter used by dma_xx_coherent. The dma_alloc_coherent return an "void *" not an "void __iomen *". It uses the wrong parameters when calls dma_free_coherent function. The sparse tool output logs as the following: coresight-tmc.c:199:23: expected void * coresight-tmc.c:199:23: got void [noderef] *vaddr coresight-tmc.c:336:30: warning: incorrect type in assignment (different address spaces) coresight-tmc.c:336:30: expected char *buf coresight-tmc.c:336:30: got void [noderef] * coresight-tmc.c:769:50: warning: incorrect type in argument 4 (different base types) coresight-tmc.c:769:50: expected unsigned long long [unsigned] [usertype] dma_handle coresight-tmc.c:769:50: got restricted gfp_t Signed-off-by: Eric Long Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 61390593f72377c3a8f41ef998462e2d3985adac) --- drivers/hwtracing/coresight/coresight-tmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 570d96815f78..c4fa70ed14ce 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -124,7 +124,7 @@ struct tmc_drvdata { bool reading; char *buf; dma_addr_t paddr; - void __iomem *vaddr; + void *vaddr; u32 size; bool enable; enum tmc_config_type config_type; @@ -766,7 +766,7 @@ err_misc_register: err_devm_kzalloc: if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) dma_free_coherent(dev, drvdata->size, - &drvdata->paddr, GFP_KERNEL); + drvdata->vaddr, drvdata->paddr); return ret; } From fde79d4de7443a7e3cca1dd05d4a789ebc490a53 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:45 -0700 Subject: [PATCH 161/656] coresight: associating path with session rather than tracer When using the Coresight framework from the sysFS interface a tracer is always handling a single session and as such, a path can be associated with a tracer. But when supporting multiple session per tracer there is no guarantee that sessions will always have the same path from source to sink. This patch is removing the automatic association between path and tracers. The building of a path and enablement of the components in the path are decoupled, allowing for the association of a path with a session rather than a tracer. To keep backward functionality with the current sysFS access methods a per-cpu place holder is used to keep a handle on the path built when tracers are enabled. Lastly APIs to build paths and enable tracers are made public so that other subsystem can interact with the Coresight framework. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b3e94405941e6916d5e365454d74560c2bea47ca) --- drivers/hwtracing/coresight/coresight-priv.h | 5 + drivers/hwtracing/coresight/coresight.c | 310 +++++++++++++------ include/linux/coresight.h | 2 - 3 files changed, 213 insertions(+), 104 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 62fcd98cc7cf..7b193a34d709 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -52,6 +52,11 @@ static inline void CS_UNLOCK(void __iomem *addr) } while (0); } +void coresight_disable_path(struct list_head *path); +int coresight_enable_path(struct list_head *path); +struct list_head *coresight_build_path(struct coresight_device *csdev); +void coresight_release_path(struct list_head *path); + #ifdef CONFIG_CORESIGHT_SOURCE_ETM3X extern int etm_readl_cp14(u32 off, unsigned int *val); extern int etm_writel_cp14(u32 off, u32 val); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 7e6e9ff27dd1..f26589effb70 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -29,6 +29,22 @@ static DEFINE_MUTEX(coresight_mutex); +/** + * struct coresight_node - elements of a path, from source to sink + * @csdev: Address of an element. + * @link: hook to the list. + */ +struct coresight_node { + struct coresight_device *csdev; + struct list_head link; +}; + +/* + * When operating Coresight drivers from the sysFS interface, only a single + * path can exist from a tracer (associated to a CPU) to a sink. + */ +static DEFINE_PER_CPU(struct list_head *, sysfs_path); + static int coresight_id_match(struct device *dev, void *data) { int trace_id, i_trace_id; @@ -68,15 +84,12 @@ static int coresight_source_is_unique(struct coresight_device *csdev) csdev, coresight_id_match); } -static int coresight_find_link_inport(struct coresight_device *csdev) +static int coresight_find_link_inport(struct coresight_device *csdev, + struct coresight_device *parent) { int i; - struct coresight_device *parent; struct coresight_connection *conn; - parent = container_of(csdev->path_link.next, - struct coresight_device, path_link); - for (i = 0; i < parent->nr_outport; i++) { conn = &parent->conns[i]; if (conn->child_dev == csdev) @@ -89,15 +102,12 @@ static int coresight_find_link_inport(struct coresight_device *csdev) return 0; } -static int coresight_find_link_outport(struct coresight_device *csdev) +static int coresight_find_link_outport(struct coresight_device *csdev, + struct coresight_device *child) { int i; - struct coresight_device *child; struct coresight_connection *conn; - child = container_of(csdev->path_link.prev, - struct coresight_device, path_link); - for (i = 0; i < csdev->nr_outport; i++) { conn = &csdev->conns[i]; if (conn->child_dev == child) @@ -138,14 +148,19 @@ static void coresight_disable_sink(struct coresight_device *csdev) } } -static int coresight_enable_link(struct coresight_device *csdev) +static int coresight_enable_link(struct coresight_device *csdev, + struct coresight_device *parent, + struct coresight_device *child) { int ret; int link_subtype; int refport, inport, outport; - inport = coresight_find_link_inport(csdev); - outport = coresight_find_link_outport(csdev); + if (!parent || !child) + return -EINVAL; + + inport = coresight_find_link_inport(csdev, parent); + outport = coresight_find_link_outport(csdev, child); link_subtype = csdev->subtype.link_subtype; if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) @@ -168,14 +183,19 @@ static int coresight_enable_link(struct coresight_device *csdev) return 0; } -static void coresight_disable_link(struct coresight_device *csdev) +static void coresight_disable_link(struct coresight_device *csdev, + struct coresight_device *parent, + struct coresight_device *child) { int i, nr_conns; int link_subtype; int refport, inport, outport; - inport = coresight_find_link_inport(csdev); - outport = coresight_find_link_outport(csdev); + if (!parent || !child) + return; + + inport = coresight_find_link_inport(csdev, parent); + outport = coresight_find_link_outport(csdev, child); link_subtype = csdev->subtype.link_subtype; if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG) { @@ -235,109 +255,167 @@ static void coresight_disable_source(struct coresight_device *csdev) } } -static int coresight_enable_path(struct list_head *path) +void coresight_disable_path(struct list_head *path) { + struct coresight_node *nd; + struct coresight_device *csdev, *parent, *child; + + list_for_each_entry(nd, path, link) { + csdev = nd->csdev; + + switch (csdev->type) { + case CORESIGHT_DEV_TYPE_SINK: + case CORESIGHT_DEV_TYPE_LINKSINK: + coresight_disable_sink(csdev); + break; + case CORESIGHT_DEV_TYPE_SOURCE: + /* sources are disabled from either sysFS or Perf */ + break; + case CORESIGHT_DEV_TYPE_LINK: + parent = list_prev_entry(nd, link)->csdev; + child = list_next_entry(nd, link)->csdev; + coresight_disable_link(csdev, parent, child); + break; + default: + break; + } + } +} + +int coresight_enable_path(struct list_head *path) +{ + int ret = 0; - struct coresight_device *cd; + struct coresight_node *nd; + struct coresight_device *csdev, *parent, *child; - /* - * At this point we have a full @path, from source to sink. The - * sink is the first entry and the source the last one. Go through - * all the components and enable them one by one. - */ - list_for_each_entry(cd, path, path_link) { - if (cd == list_first_entry(path, struct coresight_device, - path_link)) { - ret = coresight_enable_sink(cd); - } else if (list_is_last(&cd->path_link, path)) { - /* - * Don't enable the source just yet - this needs to - * happen at the very end when all links and sink - * along the path have been configured properly. - */ - ; - } else { - ret = coresight_enable_link(cd); - } - if (ret) + list_for_each_entry_reverse(nd, path, link) { + csdev = nd->csdev; + + switch (csdev->type) { + case CORESIGHT_DEV_TYPE_SINK: + case CORESIGHT_DEV_TYPE_LINKSINK: + ret = coresight_enable_sink(csdev); + if (ret) + goto err; + break; + case CORESIGHT_DEV_TYPE_SOURCE: + /* sources are enabled from either sysFS or Perf */ + break; + case CORESIGHT_DEV_TYPE_LINK: + parent = list_prev_entry(nd, link)->csdev; + child = list_next_entry(nd, link)->csdev; + ret = coresight_enable_link(csdev, parent, child); + if (ret) + goto err; + break; + default: goto err; - } - - return 0; -err: - list_for_each_entry_continue_reverse(cd, path, path_link) { - if (cd == list_first_entry(path, struct coresight_device, - path_link)) { - coresight_disable_sink(cd); - } else if (list_is_last(&cd->path_link, path)) { - ; - } else { - coresight_disable_link(cd); } } +out: return ret; +err: + coresight_disable_path(path); + goto out; } -static int coresight_disable_path(struct list_head *path) +/** + * _coresight_build_path - recursively build a path from a @csdev to a sink. + * @csdev: The device to start from. + * @path: The list to add devices to. + * + * The tree of Coresight device is traversed until an activated sink is + * found. From there the sink is added to the list along with all the + * devices that led to that point - the end result is a list from source + * to sink. In that list the source is the first device and the sink the + * last one. + */ +static int _coresight_build_path(struct coresight_device *csdev, + struct list_head *path) { - struct coresight_device *cd; - - list_for_each_entry_reverse(cd, path, path_link) { - if (cd == list_first_entry(path, struct coresight_device, - path_link)) { - coresight_disable_sink(cd); - } else if (list_is_last(&cd->path_link, path)) { - /* - * The source has already been stopped, no need - * to do it again here. - */ - ; - } else { - coresight_disable_link(cd); - } - } - - return 0; -} - -static int coresight_build_paths(struct coresight_device *csdev, - struct list_head *path, - bool enable) -{ - int i, ret = -EINVAL; + int i; + bool found = false; + struct coresight_node *node; struct coresight_connection *conn; - list_add(&csdev->path_link, path); - + /* An activated sink has been found. Enqueue the element */ if ((csdev->type == CORESIGHT_DEV_TYPE_SINK || - csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && - csdev->activated) { - if (enable) - ret = coresight_enable_path(path); - else - ret = coresight_disable_path(path); - } else { - for (i = 0; i < csdev->nr_outport; i++) { - conn = &csdev->conns[i]; - if (coresight_build_paths(conn->child_dev, - path, enable) == 0) - ret = 0; + csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) && csdev->activated) + goto out; + + /* Not a sink - recursively explore each port found on this element */ + for (i = 0; i < csdev->nr_outport; i++) { + conn = &csdev->conns[i]; + if (_coresight_build_path(conn->child_dev, path) == 0) { + found = true; + break; } } - if (list_first_entry(path, struct coresight_device, path_link) != csdev) - dev_err(&csdev->dev, "wrong device in %s\n", __func__); + if (!found) + return -ENODEV; - list_del(&csdev->path_link); +out: + /* + * A path from this element to a sink has been found. The elements + * leading to the sink are already enqueued, all that is left to do + * is add a node for this element. + */ + node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL); + if (!node) + return -ENOMEM; - return ret; + node->csdev = csdev; + list_add(&node->link, path); + + return 0; +} + +struct list_head *coresight_build_path(struct coresight_device *csdev) +{ + struct list_head *path; + + path = kzalloc(sizeof(struct list_head), GFP_KERNEL); + if (!path) + return NULL; + + INIT_LIST_HEAD(path); + + if (_coresight_build_path(csdev, path)) { + kfree(path); + path = NULL; + } + + return path; +} + +/** + * coresight_release_path - release a previously built path. + * @path: the path to release. + * + * Go through all the elements of a path and 1) removed it from the list and + * 2) free the memory allocated for each node. + */ +void coresight_release_path(struct list_head *path) +{ + struct coresight_node *nd, *next; + + list_for_each_entry_safe(nd, next, path, link) { + list_del(&nd->link); + kfree(nd); + } + + kfree(path); + path = NULL; } int coresight_enable(struct coresight_device *csdev) { int ret = 0; - LIST_HEAD(path); + int cpu; + struct list_head *path; mutex_lock(&coresight_mutex); if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { @@ -348,22 +426,47 @@ int coresight_enable(struct coresight_device *csdev) if (csdev->enable) goto out; - if (coresight_build_paths(csdev, &path, true)) { - dev_err(&csdev->dev, "building path(s) failed\n"); + path = coresight_build_path(csdev); + if (!path) { + pr_err("building path(s) failed\n"); goto out; } - if (coresight_enable_source(csdev)) - dev_err(&csdev->dev, "source enable failed\n"); + ret = coresight_enable_path(path); + if (ret) + goto err_path; + + ret = coresight_enable_source(csdev); + if (ret) + goto err_source; + + /* + * When working from sysFS it is important to keep track + * of the paths that were created so that they can be + * undone in 'coresight_disable()'. Since there can only + * be a single session per tracer (when working from sysFS) + * a per-cpu variable will do just fine. + */ + cpu = source_ops(csdev)->cpu_id(csdev); + per_cpu(sysfs_path, cpu) = path; + out: mutex_unlock(&coresight_mutex); return ret; + +err_source: + coresight_disable_path(path); + +err_path: + coresight_release_path(path); + goto out; } EXPORT_SYMBOL_GPL(coresight_enable); void coresight_disable(struct coresight_device *csdev) { - LIST_HEAD(path); + int cpu; + struct list_head *path; mutex_lock(&coresight_mutex); if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { @@ -373,9 +476,12 @@ void coresight_disable(struct coresight_device *csdev) if (!csdev->enable) goto out; + cpu = source_ops(csdev)->cpu_id(csdev); + path = per_cpu(sysfs_path, cpu); coresight_disable_source(csdev); - if (coresight_build_paths(csdev, &path, false)) - dev_err(&csdev->dev, "releasing path(s) failed\n"); + coresight_disable_path(path); + coresight_release_path(path); + per_cpu(sysfs_path, cpu) = NULL; out: mutex_unlock(&coresight_mutex); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index bf62b265bf52..851ecb22397e 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -152,7 +152,6 @@ struct coresight_connection { by @coresight_ops. * @dev: The device entity associated to this component. * @refcnt: keep track of what is in use. - * @path_link: link of current component into the path being enabled. * @orphan: true if the component has connections that haven't been linked. * @enable: 'true' if component is currently part of an active path. * @activated: 'true' only if a _sink_ has been activated. A sink can be @@ -168,7 +167,6 @@ struct coresight_device { const struct coresight_ops *ops; struct device dev; atomic_t *refcnt; - struct list_head path_link; bool orphan; bool enable; /* true only if configured as part of a path */ bool activated; /* true only if a sink is part of a path */ From f72b3bc46dab124c25d84575d3addab8ef7c68cd Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:46 -0700 Subject: [PATCH 162/656] coresight: add API to get sink from path Add an API allowing external code to quickly get a handle on the sink within a path. The sink is always last, but adding an API allows to keep the path's node structure private and remove redundant checks. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b6404e21f023e4aa208a0ba03d55a9c8a57cb940) --- drivers/hwtracing/coresight/coresight-priv.h | 1 + drivers/hwtracing/coresight/coresight.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 7b193a34d709..14f245a2018d 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -54,6 +54,7 @@ static inline void CS_UNLOCK(void __iomem *addr) void coresight_disable_path(struct list_head *path); int coresight_enable_path(struct list_head *path); +struct coresight_device *coresight_get_sink(struct list_head *path); struct list_head *coresight_build_path(struct coresight_device *csdev); void coresight_release_path(struct list_head *path); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index f26589effb70..2ed48545df26 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -321,6 +321,21 @@ err: goto out; } +struct coresight_device *coresight_get_sink(struct list_head *path) +{ + struct coresight_device *csdev; + + if (!path) + return NULL; + + csdev = list_last_entry(path, struct coresight_node, link)->csdev; + if (csdev->type != CORESIGHT_DEV_TYPE_SINK && + csdev->type != CORESIGHT_DEV_TYPE_LINKSINK) + return NULL; + + return csdev; +} + /** * _coresight_build_path - recursively build a path from a @csdev to a sink. * @csdev: The device to start from. From f7e289557fa79b9aa25a3e57717f9538b5a88098 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:47 -0700 Subject: [PATCH 163/656] coresight: moving PM runtime operations to core framework Moving PM runtime operations in Coresight devices enable() and disable() API to the framework core when a path is setup. That way the runtime core doesn't have to be involved everytime a path is enabled. It also avoids calling runtime PM operations in IRQ context. Signed-off-by: Mathieu Poirier Reviewed-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 5da5325fa85658ee793792b5285dd5fdb76ccfb7) --- drivers/hwtracing/coresight/coresight-etb10.c | 4 ---- drivers/hwtracing/coresight/coresight-etm3x.c | 3 --- drivers/hwtracing/coresight/coresight-etm4x.c | 6 ------ drivers/hwtracing/coresight/coresight-funnel.c | 2 -- drivers/hwtracing/coresight/coresight-replicator-qcom.c | 4 ---- drivers/hwtracing/coresight/coresight-replicator.c | 2 -- drivers/hwtracing/coresight/coresight-tmc.c | 5 ----- drivers/hwtracing/coresight/coresight-tpiu.c | 2 -- drivers/hwtracing/coresight/coresight.c | 9 ++++++++- 9 files changed, 8 insertions(+), 29 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 92969dae739d..917562ecf82a 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -137,8 +137,6 @@ static int etb_enable(struct coresight_device *csdev) struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); unsigned long flags; - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); etb_enable_hw(drvdata); drvdata->enable = true; @@ -247,8 +245,6 @@ static void etb_disable(struct coresight_device *csdev) drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "ETB disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index aae80e14508d..fe6791e0c66c 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -349,7 +349,6 @@ static int etm_enable(struct coresight_device *csdev) struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; - pm_runtime_get_sync(csdev->dev.parent); spin_lock(&drvdata->spinlock); /* @@ -373,7 +372,6 @@ static int etm_enable(struct coresight_device *csdev) return 0; err: spin_unlock(&drvdata->spinlock); - pm_runtime_put(csdev->dev.parent); return ret; } @@ -422,7 +420,6 @@ static void etm_disable(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); put_online_cpus(); - pm_runtime_put(csdev->dev.parent); dev_info(drvdata->dev, "ETM tracing disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index d6a92c6d3a66..c2f518fbc9a8 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -79,7 +79,6 @@ static int etm4_trace_id(struct coresight_device *csdev) if (!drvdata->enable) return drvdata->trcid; - pm_runtime_get_sync(drvdata->dev); spin_lock_irqsave(&drvdata->spinlock, flags); CS_UNLOCK(drvdata->base); @@ -88,7 +87,6 @@ static int etm4_trace_id(struct coresight_device *csdev) CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); return trace_id; } @@ -194,7 +192,6 @@ static int etm4_enable(struct coresight_device *csdev) struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; - pm_runtime_get_sync(drvdata->dev); spin_lock(&drvdata->spinlock); /* @@ -214,7 +211,6 @@ static int etm4_enable(struct coresight_device *csdev) return 0; err: spin_unlock(&drvdata->spinlock); - pm_runtime_put(drvdata->dev); return ret; } @@ -263,8 +259,6 @@ static void etm4_disable(struct coresight_device *csdev) spin_unlock(&drvdata->spinlock); put_online_cpus(); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "ETM tracing disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 25e8ea140a09..fb2c679fbc44 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -69,7 +69,6 @@ static int funnel_enable(struct coresight_device *csdev, int inport, { struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(drvdata->dev); funnel_enable_hw(drvdata, inport); dev_info(drvdata->dev, "FUNNEL inport %d enabled\n", inport); @@ -95,7 +94,6 @@ static void funnel_disable(struct coresight_device *csdev, int inport, struct funnel_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); funnel_disable_hw(drvdata, inport); - pm_runtime_put(drvdata->dev); dev_info(drvdata->dev, "FUNNEL inport %d disabled\n", inport); } diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 444815179460..286f90b50989 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -48,8 +48,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport, { struct replicator_state *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(drvdata->dev); - CS_UNLOCK(drvdata->base); /* @@ -86,8 +84,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport, CS_LOCK(drvdata->base); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "REPLICATOR disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index b77d700a3f0e..0ce98903992c 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -41,7 +41,6 @@ static int replicator_enable(struct coresight_device *csdev, int inport, { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(drvdata->dev); dev_info(drvdata->dev, "REPLICATOR enabled\n"); return 0; } @@ -51,7 +50,6 @@ static void replicator_disable(struct coresight_device *csdev, int inport, { struct replicator_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_put(drvdata->dev); dev_info(drvdata->dev, "REPLICATOR disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index c4fa70ed14ce..b526396d80b6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -242,12 +242,9 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) { unsigned long flags; - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); return -EBUSY; } @@ -381,8 +378,6 @@ out: drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - dev_info(drvdata->dev, "TMC disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index ca50e73d9df7..0d8fce651ff7 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -74,7 +74,6 @@ static int tpiu_enable(struct coresight_device *csdev) { struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - pm_runtime_get_sync(csdev->dev.parent); tpiu_enable_hw(drvdata); dev_info(drvdata->dev, "TPIU enabled\n"); @@ -98,7 +97,6 @@ static void tpiu_disable(struct coresight_device *csdev) struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); tpiu_disable_hw(drvdata); - pm_runtime_put(csdev->dev.parent); dev_info(drvdata->dev, "TPIU disabled\n"); } diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2ed48545df26..6b44928c1076 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "coresight-priv.h" @@ -376,7 +377,8 @@ out: /* * A path from this element to a sink has been found. The elements * leading to the sink are already enqueued, all that is left to do - * is add a node for this element. + * is tell the PM runtime core we need this element and add a node + * for it. */ node = kzalloc(sizeof(struct coresight_node), GFP_KERNEL); if (!node) @@ -384,6 +386,7 @@ out: node->csdev = csdev; list_add(&node->link, path); + pm_runtime_get_sync(csdev->dev.parent); return 0; } @@ -415,9 +418,13 @@ struct list_head *coresight_build_path(struct coresight_device *csdev) */ void coresight_release_path(struct list_head *path) { + struct coresight_device *csdev; struct coresight_node *nd, *next; list_for_each_entry_safe(nd, next, path, link) { + csdev = nd->csdev; + + pm_runtime_put_sync(csdev->dev.parent); list_del(&nd->link); kfree(nd); } From 46f118189e6870463bbd5bac9f818dcad64922de Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:48 -0700 Subject: [PATCH 164/656] coresight: etm3x: moving etm_readl/writel to header file Moving functions etm_readl/writel to file "coresight-etm.h" so that the main ETM3x driver can be split in more than one file. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c1f8e57c9e6692f6e8c6c1f9eab7a46264ac4245) --- drivers/hwtracing/coresight/coresight-etm.h | 29 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm3x.c | 29 ------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index b4481eb29304..34f7db881fa7 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -251,4 +251,33 @@ enum etm_addr_type { ETM_ADDR_TYPE_START, ETM_ADDR_TYPE_STOP, }; + +static inline void etm_writel(struct etm_drvdata *drvdata, + u32 val, u32 off) +{ + if (drvdata->use_cp14) { + if (etm_writel_cp14(off, val)) { + dev_err(drvdata->dev, + "invalid CP14 access to ETM reg: %#x", off); + } + } else { + writel_relaxed(val, drvdata->base + off); + } +} + +static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) +{ + u32 val; + + if (drvdata->use_cp14) { + if (etm_readl_cp14(off, &val)) { + dev_err(drvdata->dev, + "invalid CP14 access to ETM reg: %#x", off); + } + } else { + val = readl_relaxed(drvdata->base + off); + } + + return val; +} #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index fe6791e0c66c..d53440e9af6f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -42,35 +42,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); static int etm_count; static struct etm_drvdata *etmdrvdata[NR_CPUS]; -static inline void etm_writel(struct etm_drvdata *drvdata, - u32 val, u32 off) -{ - if (drvdata->use_cp14) { - if (etm_writel_cp14(off, val)) { - dev_err(drvdata->dev, - "invalid CP14 access to ETM reg: %#x", off); - } - } else { - writel_relaxed(val, drvdata->base + off); - } -} - -static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) -{ - u32 val; - - if (drvdata->use_cp14) { - if (etm_readl_cp14(off, &val)) { - dev_err(drvdata->dev, - "invalid CP14 access to ETM reg: %#x", off); - } - } else { - val = readl_relaxed(drvdata->base + off); - } - - return val; -} - /* * Memory mapped writes to clear os lock are not supported on some processors * and OS lock must be unlocked before any memory mapped access on such From dd1e17528c41f41b06287bf7f2b47a078628b87a Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:49 -0700 Subject: [PATCH 165/656] coresight: etm3x: moving sysFS entries to dedicated file SysFS entries are big enough to justify their own file. As such moving all sysFS related declarations to a dedicated location. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c04148e708c0d8d7eabc447946b712a66b468b47) --- drivers/hwtracing/coresight/Makefile | 3 +- drivers/hwtracing/coresight/coresight-etm.h | 4 + .../coresight/coresight-etm3x-sysfs.c | 1218 ++++++++++++++++ drivers/hwtracing/coresight/coresight-etm3x.c | 1234 +---------------- 4 files changed, 1241 insertions(+), 1218 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm3x-sysfs.c diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 99f8e5f6256e..233d66cf22d3 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ coresight-replicator.o -obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o +obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ + coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 34f7db881fa7..9a30aa392ed9 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -280,4 +280,8 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) return val; } + +extern const struct attribute_group *coresight_etm_groups[]; +int etm_get_trace_id(struct etm_drvdata *drvdata); +void etm_set_default(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c new file mode 100644 index 000000000000..f409f5a88e95 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -0,0 +1,1218 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include "coresight-etm.h" + +static ssize_t nr_addr_cmp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_addr_cmp; + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_addr_cmp); + +static ssize_t nr_cntr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_cntr; + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_cntr); + +static ssize_t nr_ctxid_cmp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_ctxid_cmp; + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_ctxid_cmp); + +static ssize_t etmsr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long flags, val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + pm_runtime_get_sync(drvdata->dev); + spin_lock_irqsave(&drvdata->spinlock, flags); + CS_UNLOCK(drvdata->base); + + val = etm_readl(drvdata, ETMSR); + + CS_LOCK(drvdata->base); + spin_unlock_irqrestore(&drvdata->spinlock, flags); + pm_runtime_put(drvdata->dev); + + return sprintf(buf, "%#lx\n", val); +} +static DEVICE_ATTR_RO(etmsr); + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int i, ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val) { + spin_lock(&drvdata->spinlock); + drvdata->mode = ETM_MODE_EXCLUDE; + drvdata->ctrl = 0x0; + drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; + drvdata->startstop_ctrl = 0x0; + drvdata->addr_idx = 0x0; + for (i = 0; i < drvdata->nr_addr_cmp; i++) { + drvdata->addr_val[i] = 0x0; + drvdata->addr_acctype[i] = 0x0; + drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + } + drvdata->cntr_idx = 0x0; + + etm_set_default(drvdata); + spin_unlock(&drvdata->spinlock); + } + + return size; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->mode; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->mode = val & ETM_MODE_ALL; + + if (drvdata->mode & ETM_MODE_EXCLUDE) + drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; + else + drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; + + if (drvdata->mode & ETM_MODE_CYCACC) + drvdata->ctrl |= ETMCR_CYC_ACC; + else + drvdata->ctrl &= ~ETMCR_CYC_ACC; + + if (drvdata->mode & ETM_MODE_STALL) { + if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { + dev_warn(drvdata->dev, "stall mode not supported\n"); + ret = -EINVAL; + goto err_unlock; + } + drvdata->ctrl |= ETMCR_STALL_MODE; + } else + drvdata->ctrl &= ~ETMCR_STALL_MODE; + + if (drvdata->mode & ETM_MODE_TIMESTAMP) { + if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { + dev_warn(drvdata->dev, "timestamp not supported\n"); + ret = -EINVAL; + goto err_unlock; + } + drvdata->ctrl |= ETMCR_TIMESTAMP_EN; + } else + drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; + + if (drvdata->mode & ETM_MODE_CTXID) + drvdata->ctrl |= ETMCR_CTXID_SIZE; + else + drvdata->ctrl &= ~ETMCR_CTXID_SIZE; + spin_unlock(&drvdata->spinlock); + + return size; + +err_unlock: + spin_unlock(&drvdata->spinlock); + return ret; +} +static DEVICE_ATTR_RW(mode); + +static ssize_t trigger_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->trigger_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t trigger_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->trigger_event = val & ETM_EVENT_MASK; + + return size; +} +static DEVICE_ATTR_RW(trigger_event); + +static ssize_t enable_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->enable_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t enable_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->enable_event = val & ETM_EVENT_MASK; + + return size; +} +static DEVICE_ATTR_RW(enable_event); + +static ssize_t fifofull_level_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->fifofull_level; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t fifofull_level_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->fifofull_level = val; + + return size; +} +static DEVICE_ATTR_RW(fifofull_level); + +static ssize_t addr_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->addr_idx; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val >= drvdata->nr_addr_cmp) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->addr_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_idx); + +static ssize_t addr_single_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + + val = drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_single_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + + drvdata->addr_val[idx] = val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_single); + +static ssize_t addr_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val1, val2; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val1 = drvdata->addr_val[idx]; + val2 = drvdata->addr_val[idx + 1]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx %#lx\n", val1, val2); +} + +static ssize_t addr_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val1, val2; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + /* Lower address comparator cannot have a higher address value */ + if (val1 > val2) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = val1; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + drvdata->addr_val[idx + 1] = val2; + drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + drvdata->enable_ctrl1 |= (1 << (idx/2)); + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_range); + +static ssize_t addr_start_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_start_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; + drvdata->startstop_ctrl |= (1 << idx); + drvdata->enable_ctrl1 |= BIT(25); + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_start); + +static ssize_t addr_stop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 idx; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_stop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; + drvdata->startstop_ctrl |= (1 << (idx + 16)); + drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_stop); + +static ssize_t addr_acctype_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->addr_acctype[drvdata->addr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t addr_acctype_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->addr_acctype[drvdata->addr_idx] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(addr_acctype); + +static ssize_t cntr_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cntr_idx; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val >= drvdata->nr_cntr) + return -EINVAL; + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->cntr_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_idx); + +static ssize_t cntr_rld_val_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->cntr_rld_val[drvdata->cntr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_rld_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_rld_val[drvdata->cntr_idx] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_rld_val); + +static ssize_t cntr_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->cntr_event[drvdata->cntr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_event); + +static ssize_t cntr_rld_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->cntr_rld_event[drvdata->cntr_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t cntr_rld_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_rld_event); + +static ssize_t cntr_val_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, ret = 0; + u32 val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (!drvdata->enable) { + spin_lock(&drvdata->spinlock); + for (i = 0; i < drvdata->nr_cntr; i++) + ret += sprintf(buf, "counter %d: %x\n", + i, drvdata->cntr_val[i]); + spin_unlock(&drvdata->spinlock); + return ret; + } + + for (i = 0; i < drvdata->nr_cntr; i++) { + val = etm_readl(drvdata, ETMCNTVRn(i)); + ret += sprintf(buf, "counter %d: %x\n", i, val); + } + + return ret; +} + +static ssize_t cntr_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->cntr_val[drvdata->cntr_idx] = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(cntr_val); + +static ssize_t seq_12_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_12_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_12_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_12_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_12_event); + +static ssize_t seq_21_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_21_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_21_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_21_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_21_event); + +static ssize_t seq_23_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_23_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_23_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_23_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_23_event); + +static ssize_t seq_31_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_31_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_31_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_31_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_31_event); + +static ssize_t seq_32_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_32_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_32_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_32_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_32_event); + +static ssize_t seq_13_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_13_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_13_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->seq_13_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_13_event); + +static ssize_t seq_curr_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val, flags; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (!drvdata->enable) { + val = drvdata->seq_curr_state; + goto out; + } + + pm_runtime_get_sync(drvdata->dev); + spin_lock_irqsave(&drvdata->spinlock, flags); + + CS_UNLOCK(drvdata->base); + val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); + CS_LOCK(drvdata->base); + + spin_unlock_irqrestore(&drvdata->spinlock, flags); + pm_runtime_put(drvdata->dev); +out: + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t seq_curr_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val > ETM_SEQ_STATE_MAX_VAL) + return -EINVAL; + + drvdata->seq_curr_state = val; + + return size; +} +static DEVICE_ATTR_RW(seq_curr_state); + +static ssize_t ctxid_idx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ctxid_idx; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t ctxid_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + if (val >= drvdata->nr_ctxid_cmp) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->ctxid_idx = val; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(ctxid_idx); + +static ssize_t ctxid_pid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; + spin_unlock(&drvdata->spinlock); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t ctxid_pid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long vpid, pid; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &vpid); + if (ret) + return ret; + + pid = coresight_vpid_to_pid(vpid); + + spin_lock(&drvdata->spinlock); + drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; + drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(ctxid_pid); + +static ssize_t ctxid_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ctxid_mask; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t ctxid_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->ctxid_mask = val; + return size; +} +static DEVICE_ATTR_RW(ctxid_mask); + +static ssize_t sync_freq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->sync_freq; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t sync_freq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->sync_freq = val & ETM_SYNC_MASK; + return size; +} +static DEVICE_ATTR_RW(sync_freq); + +static ssize_t timestamp_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->timestamp_event; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t timestamp_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->timestamp_event = val & ETM_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(timestamp_event); + +static ssize_t cpu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cpu; + return scnprintf(buf, PAGE_SIZE, "%d\n", val); + +} +static DEVICE_ATTR_RO(cpu); + +static ssize_t traceid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = etm_get_trace_id(drvdata); + + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t traceid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + drvdata->traceid = val & ETM_TRACEID_MASK; + return size; +} +static DEVICE_ATTR_RW(traceid); + +static struct attribute *coresight_etm_attrs[] = { + &dev_attr_nr_addr_cmp.attr, + &dev_attr_nr_cntr.attr, + &dev_attr_nr_ctxid_cmp.attr, + &dev_attr_etmsr.attr, + &dev_attr_reset.attr, + &dev_attr_mode.attr, + &dev_attr_trigger_event.attr, + &dev_attr_enable_event.attr, + &dev_attr_fifofull_level.attr, + &dev_attr_addr_idx.attr, + &dev_attr_addr_single.attr, + &dev_attr_addr_range.attr, + &dev_attr_addr_start.attr, + &dev_attr_addr_stop.attr, + &dev_attr_addr_acctype.attr, + &dev_attr_cntr_idx.attr, + &dev_attr_cntr_rld_val.attr, + &dev_attr_cntr_event.attr, + &dev_attr_cntr_rld_event.attr, + &dev_attr_cntr_val.attr, + &dev_attr_seq_12_event.attr, + &dev_attr_seq_21_event.attr, + &dev_attr_seq_23_event.attr, + &dev_attr_seq_31_event.attr, + &dev_attr_seq_32_event.attr, + &dev_attr_seq_13_event.attr, + &dev_attr_seq_curr_state.attr, + &dev_attr_ctxid_idx.attr, + &dev_attr_ctxid_pid.attr, + &dev_attr_ctxid_mask.attr, + &dev_attr_sync_freq.attr, + &dev_attr_timestamp_event.attr, + &dev_attr_traceid.attr, + &dev_attr_cpu.attr, + NULL, +}; + +#define coresight_simple_func(name, offset) \ +static ssize_t name##_show(struct device *_dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ + readl_relaxed(drvdata->base + offset)); \ +} \ +DEVICE_ATTR_RO(name) + +coresight_simple_func(etmccr, ETMCCR); +coresight_simple_func(etmccer, ETMCCER); +coresight_simple_func(etmscr, ETMSCR); +coresight_simple_func(etmidr, ETMIDR); +coresight_simple_func(etmcr, ETMCR); +coresight_simple_func(etmtraceidr, ETMTRACEIDR); +coresight_simple_func(etmteevr, ETMTEEVR); +coresight_simple_func(etmtssvr, ETMTSSCR); +coresight_simple_func(etmtecr1, ETMTECR1); +coresight_simple_func(etmtecr2, ETMTECR2); + +static struct attribute *coresight_etm_mgmt_attrs[] = { + &dev_attr_etmccr.attr, + &dev_attr_etmccer.attr, + &dev_attr_etmscr.attr, + &dev_attr_etmidr.attr, + &dev_attr_etmcr.attr, + &dev_attr_etmtraceidr.attr, + &dev_attr_etmteevr.attr, + &dev_attr_etmtssvr.attr, + &dev_attr_etmtecr1.attr, + &dev_attr_etmtecr2.attr, + NULL, +}; + +static const struct attribute_group coresight_etm_group = { + .attrs = coresight_etm_attrs, +}; + +static const struct attribute_group coresight_etm_mgmt_group = { + .attrs = coresight_etm_mgmt_attrs, + .name = "mgmt", +}; + +const struct attribute_group *coresight_etm_groups[] = { + &coresight_etm_group, + &coresight_etm_mgmt_group, + NULL, +}; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index d53440e9af6f..6ea35b350958 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -186,7 +186,7 @@ static void etm_clr_prog(struct etm_drvdata *drvdata) } } -static void etm_set_default(struct etm_drvdata *drvdata) +void etm_set_default(struct etm_drvdata *drvdata) { int i; @@ -293,15 +293,18 @@ static int etm_cpu_id(struct coresight_device *csdev) return drvdata->cpu; } -static int etm_trace_id(struct coresight_device *csdev) +int etm_get_trace_id(struct etm_drvdata *drvdata) { - struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); unsigned long flags; int trace_id = -1; + if (!drvdata) + goto out; + if (!drvdata->enable) return drvdata->traceid; - pm_runtime_get_sync(csdev->dev.parent); + + pm_runtime_get_sync(drvdata->dev); spin_lock_irqsave(&drvdata->spinlock, flags); @@ -310,9 +313,18 @@ static int etm_trace_id(struct coresight_device *csdev) CS_LOCK(drvdata->base); spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(csdev->dev.parent); + pm_runtime_put(drvdata->dev); +out: return trace_id; + +} + +static int etm_trace_id(struct coresight_device *csdev) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return etm_get_trace_id(drvdata); } static int etm_enable(struct coresight_device *csdev) @@ -406,1218 +418,6 @@ static const struct coresight_ops etm_cs_ops = { .source_ops = &etm_source_ops, }; -static ssize_t nr_addr_cmp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_addr_cmp; - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_addr_cmp); - -static ssize_t nr_cntr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_cntr; - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_cntr); - -static ssize_t nr_ctxid_cmp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_ctxid_cmp; - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_ctxid_cmp); - -static ssize_t etmsr_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long flags, val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); - - val = etm_readl(drvdata, ETMSR); - - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - - return sprintf(buf, "%#lx\n", val); -} -static DEVICE_ATTR_RO(etmsr); - -static ssize_t reset_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int i, ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val) { - spin_lock(&drvdata->spinlock); - drvdata->mode = ETM_MODE_EXCLUDE; - drvdata->ctrl = 0x0; - drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; - drvdata->startstop_ctrl = 0x0; - drvdata->addr_idx = 0x0; - for (i = 0; i < drvdata->nr_addr_cmp; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acctype[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; - } - drvdata->cntr_idx = 0x0; - - etm_set_default(drvdata); - spin_unlock(&drvdata->spinlock); - } - - return size; -} -static DEVICE_ATTR_WO(reset); - -static ssize_t mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->mode; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETM_MODE_ALL; - - if (drvdata->mode & ETM_MODE_EXCLUDE) - drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; - else - drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; - - if (drvdata->mode & ETM_MODE_CYCACC) - drvdata->ctrl |= ETMCR_CYC_ACC; - else - drvdata->ctrl &= ~ETMCR_CYC_ACC; - - if (drvdata->mode & ETM_MODE_STALL) { - if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { - dev_warn(drvdata->dev, "stall mode not supported\n"); - ret = -EINVAL; - goto err_unlock; - } - drvdata->ctrl |= ETMCR_STALL_MODE; - } else - drvdata->ctrl &= ~ETMCR_STALL_MODE; - - if (drvdata->mode & ETM_MODE_TIMESTAMP) { - if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { - dev_warn(drvdata->dev, "timestamp not supported\n"); - ret = -EINVAL; - goto err_unlock; - } - drvdata->ctrl |= ETMCR_TIMESTAMP_EN; - } else - drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; - - if (drvdata->mode & ETM_MODE_CTXID) - drvdata->ctrl |= ETMCR_CTXID_SIZE; - else - drvdata->ctrl &= ~ETMCR_CTXID_SIZE; - spin_unlock(&drvdata->spinlock); - - return size; - -err_unlock: - spin_unlock(&drvdata->spinlock); - return ret; -} -static DEVICE_ATTR_RW(mode); - -static ssize_t trigger_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->trigger_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t trigger_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->trigger_event = val & ETM_EVENT_MASK; - - return size; -} -static DEVICE_ATTR_RW(trigger_event); - -static ssize_t enable_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->enable_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t enable_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->enable_event = val & ETM_EVENT_MASK; - - return size; -} -static DEVICE_ATTR_RW(enable_event); - -static ssize_t fifofull_level_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->fifofull_level; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t fifofull_level_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->fifofull_level = val; - - return size; -} -static DEVICE_ATTR_RW(fifofull_level); - -static ssize_t addr_idx_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->addr_idx; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val >= drvdata->nr_addr_cmp) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_idx); - -static ssize_t addr_single_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - - val = drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_single_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_single); - -static ssize_t addr_range_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val1, val2; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val1 = drvdata->addr_val[idx]; - val2 = drvdata->addr_val[idx + 1]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx %#lx\n", val1, val2); -} - -static ssize_t addr_range_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val1, val2; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) - return -EINVAL; - /* Lower address comparator cannot have a higher address value */ - if (val1 > val2) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; - drvdata->enable_ctrl1 |= (1 << (idx/2)); - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_range); - -static ssize_t addr_start_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_start_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->startstop_ctrl |= (1 << idx); - drvdata->enable_ctrl1 |= BIT(25); - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_start); - -static ssize_t addr_stop_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - u8 idx; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_stop_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->startstop_ctrl |= (1 << (idx + 16)); - drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_stop); - -static ssize_t addr_acctype_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->addr_acctype[drvdata->addr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t addr_acctype_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->addr_acctype[drvdata->addr_idx] = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(addr_acctype); - -static ssize_t cntr_idx_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cntr_idx; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val >= drvdata->nr_cntr) - return -EINVAL; - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_idx); - -static ssize_t cntr_rld_val_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_val[drvdata->cntr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_rld_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_val[drvdata->cntr_idx] = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_rld_val); - -static ssize_t cntr_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->cntr_event[drvdata->cntr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_event); - -static ssize_t cntr_rld_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_event[drvdata->cntr_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t cntr_rld_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_rld_event); - -static ssize_t cntr_val_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int i, ret = 0; - u32 val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (!drvdata->enable) { - spin_lock(&drvdata->spinlock); - for (i = 0; i < drvdata->nr_cntr; i++) - ret += sprintf(buf, "counter %d: %x\n", - i, drvdata->cntr_val[i]); - spin_unlock(&drvdata->spinlock); - return ret; - } - - for (i = 0; i < drvdata->nr_cntr; i++) { - val = etm_readl(drvdata, ETMCNTVRn(i)); - ret += sprintf(buf, "counter %d: %x\n", i, val); - } - - return ret; -} - -static ssize_t cntr_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - spin_lock(&drvdata->spinlock); - drvdata->cntr_val[drvdata->cntr_idx] = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(cntr_val); - -static ssize_t seq_12_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_12_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_12_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_12_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_12_event); - -static ssize_t seq_21_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_21_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_21_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_21_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_21_event); - -static ssize_t seq_23_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_23_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_23_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_23_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_23_event); - -static ssize_t seq_31_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_31_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_31_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_31_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_31_event); - -static ssize_t seq_32_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_32_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_32_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_32_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_32_event); - -static ssize_t seq_13_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_13_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_13_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->seq_13_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_13_event); - -static ssize_t seq_curr_state_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val, flags; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (!drvdata->enable) { - val = drvdata->seq_curr_state; - goto out; - } - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - - CS_UNLOCK(drvdata->base); - val = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); - CS_LOCK(drvdata->base); - - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); -out: - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t seq_curr_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val > ETM_SEQ_STATE_MAX_VAL) - return -EINVAL; - - drvdata->seq_curr_state = val; - - return size; -} -static DEVICE_ATTR_RW(seq_curr_state); - -static ssize_t ctxid_idx_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ctxid_idx; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t ctxid_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - if (val >= drvdata->nr_ctxid_cmp) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(ctxid_idx); - -static ssize_t ctxid_pid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; - spin_unlock(&drvdata->spinlock); - - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t ctxid_pid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long vpid, pid; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &vpid); - if (ret) - return ret; - - pid = coresight_vpid_to_pid(vpid); - - spin_lock(&drvdata->spinlock); - drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; - drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; - spin_unlock(&drvdata->spinlock); - - return size; -} -static DEVICE_ATTR_RW(ctxid_pid); - -static ssize_t ctxid_mask_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ctxid_mask; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t ctxid_mask_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->ctxid_mask = val; - return size; -} -static DEVICE_ATTR_RW(ctxid_mask); - -static ssize_t sync_freq_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->sync_freq; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t sync_freq_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->sync_freq = val & ETM_SYNC_MASK; - return size; -} -static DEVICE_ATTR_RW(sync_freq); - -static ssize_t timestamp_event_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->timestamp_event; - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t timestamp_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->timestamp_event = val & ETM_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(timestamp_event); - -static ssize_t cpu_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cpu; - return scnprintf(buf, PAGE_SIZE, "%d\n", val); - -} -static DEVICE_ATTR_RO(cpu); - -static ssize_t traceid_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val, flags; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (!drvdata->enable) { - val = drvdata->traceid; - goto out; - } - - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); - - val = (etm_readl(drvdata, ETMTRACEIDR) & ETM_TRACEID_MASK); - - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); -out: - return sprintf(buf, "%#lx\n", val); -} - -static ssize_t traceid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int ret; - unsigned long val; - struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - - ret = kstrtoul(buf, 16, &val); - if (ret) - return ret; - - drvdata->traceid = val & ETM_TRACEID_MASK; - return size; -} -static DEVICE_ATTR_RW(traceid); - -static struct attribute *coresight_etm_attrs[] = { - &dev_attr_nr_addr_cmp.attr, - &dev_attr_nr_cntr.attr, - &dev_attr_nr_ctxid_cmp.attr, - &dev_attr_etmsr.attr, - &dev_attr_reset.attr, - &dev_attr_mode.attr, - &dev_attr_trigger_event.attr, - &dev_attr_enable_event.attr, - &dev_attr_fifofull_level.attr, - &dev_attr_addr_idx.attr, - &dev_attr_addr_single.attr, - &dev_attr_addr_range.attr, - &dev_attr_addr_start.attr, - &dev_attr_addr_stop.attr, - &dev_attr_addr_acctype.attr, - &dev_attr_cntr_idx.attr, - &dev_attr_cntr_rld_val.attr, - &dev_attr_cntr_event.attr, - &dev_attr_cntr_rld_event.attr, - &dev_attr_cntr_val.attr, - &dev_attr_seq_12_event.attr, - &dev_attr_seq_21_event.attr, - &dev_attr_seq_23_event.attr, - &dev_attr_seq_31_event.attr, - &dev_attr_seq_32_event.attr, - &dev_attr_seq_13_event.attr, - &dev_attr_seq_curr_state.attr, - &dev_attr_ctxid_idx.attr, - &dev_attr_ctxid_pid.attr, - &dev_attr_ctxid_mask.attr, - &dev_attr_sync_freq.attr, - &dev_attr_timestamp_event.attr, - &dev_attr_traceid.attr, - &dev_attr_cpu.attr, - NULL, -}; - -#define coresight_simple_func(name, offset) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ -} \ -DEVICE_ATTR_RO(name) - -coresight_simple_func(etmccr, ETMCCR); -coresight_simple_func(etmccer, ETMCCER); -coresight_simple_func(etmscr, ETMSCR); -coresight_simple_func(etmidr, ETMIDR); -coresight_simple_func(etmcr, ETMCR); -coresight_simple_func(etmtraceidr, ETMTRACEIDR); -coresight_simple_func(etmteevr, ETMTEEVR); -coresight_simple_func(etmtssvr, ETMTSSCR); -coresight_simple_func(etmtecr1, ETMTECR1); -coresight_simple_func(etmtecr2, ETMTECR2); - -static struct attribute *coresight_etm_mgmt_attrs[] = { - &dev_attr_etmccr.attr, - &dev_attr_etmccer.attr, - &dev_attr_etmscr.attr, - &dev_attr_etmidr.attr, - &dev_attr_etmcr.attr, - &dev_attr_etmtraceidr.attr, - &dev_attr_etmteevr.attr, - &dev_attr_etmtssvr.attr, - &dev_attr_etmtecr1.attr, - &dev_attr_etmtecr2.attr, - NULL, -}; - -static const struct attribute_group coresight_etm_group = { - .attrs = coresight_etm_attrs, -}; - - -static const struct attribute_group coresight_etm_mgmt_group = { - .attrs = coresight_etm_mgmt_attrs, - .name = "mgmt", -}; - -static const struct attribute_group *coresight_etm_groups[] = { - &coresight_etm_group, - &coresight_etm_mgmt_group, - NULL, -}; - static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { From fdd3e24c6b7cbea0737008ffb846c76d8b479d97 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:50 -0700 Subject: [PATCH 166/656] coresight: etm3x: unlocking tracers in default arch init Calling function 'smp_call_function_single()' to unlock a tracer and calling it again right after to perform the default initialisation doesn't make sense. Moving 'etm_os_unlock()' just before making the default initialisation results in the same outcome while saving one call to 'smp_call_function_single()'. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ae69a1da399fccaed406932f5cbee55a6f9d4425) --- drivers/hwtracing/coresight/coresight-etm3x.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 6ea35b350958..f2c74bb56993 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -47,11 +47,11 @@ static struct etm_drvdata *etmdrvdata[NR_CPUS]; * and OS lock must be unlocked before any memory mapped access on such * processors, otherwise memory mapped reads/writes will be invalid. */ -static void etm_os_unlock(void *info) +static void etm_os_unlock(struct etm_drvdata *drvdata) { - struct etm_drvdata *drvdata = (struct etm_drvdata *)info; /* Writing any value to ETMOSLAR unlocks the trace registers */ etm_writel(drvdata, 0x0, ETMOSLAR); + drvdata->os_unlock = true; isb(); } @@ -483,6 +483,9 @@ static void etm_init_arch_data(void *info) u32 etmccr; struct etm_drvdata *drvdata = info; + /* Make sure all registers are accessible */ + etm_os_unlock(drvdata); + CS_UNLOCK(drvdata->base); /* First dummy read */ @@ -607,9 +610,6 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) get_online_cpus(); etmdrvdata[drvdata->cpu] = drvdata; - if (!smp_call_function_single(drvdata->cpu, etm_os_unlock, drvdata, 1)) - drvdata->os_unlock = true; - if (smp_call_function_single(drvdata->cpu, etm_init_arch_data, drvdata, 1)) dev_err(dev, "ETM arch init failed\n"); From e66f4306639b71295995f91374b233efb01227db Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:51 -0700 Subject: [PATCH 167/656] coresight: etm3x: splitting struct etm_drvdata Splitting "etm_drvdata" in two sections, one for the HW specific data and another for user configuration. That way it is easier to manipulate and zero out the configuration data when more than one concurrent tracing session configuration is active. Also taking care of up-lifting all the code affected by this new arrangement. No loss or gain of functionality (other than what is mentioned above) is introduced by this patch. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 1925a470ce69cdfa2b82ac1565d58dfd39cd877d) --- drivers/hwtracing/coresight/coresight-etm.h | 102 +++--- .../coresight/coresight-etm3x-sysfs.c | 290 ++++++++++-------- drivers/hwtracing/coresight/coresight-etm3x.c | 151 ++++----- 3 files changed, 304 insertions(+), 239 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 9a30aa392ed9..371fb7d2e829 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -136,29 +136,9 @@ #define ETM_DEFAULT_EVENT_VAL (ETM_HARD_WIRE_RES_A | \ ETM_ADD_COMP_0 | \ ETM_EVENT_NOT_A) + /** - * struct etm_drvdata - specifics associated to an ETM component - * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. - * @atclk: optional clock for the core parts of the ETM. - * @csdev: component vitals needed by the framework. - * @spinlock: only one at a time pls. - * @cpu: the cpu this component is affined to. - * @port_size: port size as reported by ETMCR bit 4-6 and 21. - * @arch: ETM/PTM version number. - * @use_cpu14: true if management registers need to be accessed via CP14. - * @enable: is this ETM/PTM currently tracing. - * @sticky_enable: true if ETM base configuration has been done. - * @boot_enable:true if we should start tracing at boot time. - * @os_unlock: true if access to management registers is allowed. - * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR. - * @nr_cntr: Number of counters as found in ETMCCR bit 13-15. - * @nr_ext_inp: Number of external input as found in ETMCCR bit 17-19. - * @nr_ext_out: Number of external output as found in ETMCCR bit 20-22. - * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25. - * @etmccr: value of register ETMCCR. - * @etmccer: value of register ETMCCER. - * @traceid: value of the current ID for this component. + * struct etm_config - configuration information related to an ETM * @mode: controls various modes supported by this ETM/PTM. * @ctrl: used in conjunction with @mode. * @trigger_event: setting for register ETMTRIGGER. @@ -189,30 +169,9 @@ * @ctxid_mask: mask applicable to all the context IDs. * @sync_freq: Synchronisation frequency. * @timestamp_event: Defines an event that requests the insertion - of a timestamp into the trace stream. + * of a timestamp into the trace stream. */ -struct etm_drvdata { - void __iomem *base; - struct device *dev; - struct clk *atclk; - struct coresight_device *csdev; - spinlock_t spinlock; - int cpu; - int port_size; - u8 arch; - bool use_cp14; - bool enable; - bool sticky_enable; - bool boot_enable; - bool os_unlock; - u8 nr_addr_cmp; - u8 nr_cntr; - u8 nr_ext_inp; - u8 nr_ext_out; - u8 nr_ctxid_cmp; - u32 etmccr; - u32 etmccer; - u32 traceid; +struct etm_config { u32 mode; u32 ctrl; u32 trigger_event; @@ -244,6 +203,56 @@ struct etm_drvdata { u32 timestamp_event; }; +/** + * struct etm_drvdata - specifics associated to an ETM component + * @base: memory mapped base address for this component. + * @dev: the device entity associated to this component. + * @atclk: optional clock for the core parts of the ETM. + * @csdev: component vitals needed by the framework. + * @spinlock: only one at a time pls. + * @cpu: the cpu this component is affined to. + * @port_size: port size as reported by ETMCR bit 4-6 and 21. + * @arch: ETM/PTM version number. + * @use_cpu14: true if management registers need to be accessed via CP14. + * @enable: is this ETM/PTM currently tracing. + * @sticky_enable: true if ETM base configuration has been done. + * @boot_enable:true if we should start tracing at boot time. + * @os_unlock: true if access to management registers is allowed. + * @nr_addr_cmp:Number of pairs of address comparators as found in ETMCCR. + * @nr_cntr: Number of counters as found in ETMCCR bit 13-15. + * @nr_ext_inp: Number of external input as found in ETMCCR bit 17-19. + * @nr_ext_out: Number of external output as found in ETMCCR bit 20-22. + * @nr_ctxid_cmp: Number of contextID comparators as found in ETMCCR bit 24-25. + * @etmccr: value of register ETMCCR. + * @etmccer: value of register ETMCCER. + * @traceid: value of the current ID for this component. + * @config: structure holding configuration parameters. + */ +struct etm_drvdata { + void __iomem *base; + struct device *dev; + struct clk *atclk; + struct coresight_device *csdev; + spinlock_t spinlock; + int cpu; + int port_size; + u8 arch; + bool use_cp14; + bool enable; + bool sticky_enable; + bool boot_enable; + bool os_unlock; + u8 nr_addr_cmp; + u8 nr_cntr; + u8 nr_ext_inp; + u8 nr_ext_out; + u8 nr_ctxid_cmp; + u32 etmccr; + u32 etmccer; + u32 traceid; + struct etm_config config; +}; + enum etm_addr_type { ETM_ADDR_TYPE_NONE, ETM_ADDR_TYPE_SINGLE, @@ -283,5 +292,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) extern const struct attribute_group *coresight_etm_groups[]; int etm_get_trace_id(struct etm_drvdata *drvdata); -void etm_set_default(struct etm_drvdata *drvdata); +void etm_set_default(struct etm_config *config); +struct etm_config *get_etm_config(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index f409f5a88e95..456df2378a6f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -78,6 +78,7 @@ static ssize_t reset_store(struct device *dev, int i, ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -85,19 +86,14 @@ static ssize_t reset_store(struct device *dev, if (val) { spin_lock(&drvdata->spinlock); - drvdata->mode = ETM_MODE_EXCLUDE; - drvdata->ctrl = 0x0; - drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; - drvdata->startstop_ctrl = 0x0; - drvdata->addr_idx = 0x0; + memset(config, 0, sizeof(struct etm_config)); + config->mode = ETM_MODE_EXCLUDE; + config->trigger_event = ETM_DEFAULT_EVENT_VAL; for (i = 0; i < drvdata->nr_addr_cmp; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acctype[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + config->addr_type[i] = ETM_ADDR_TYPE_NONE; } - drvdata->cntr_idx = 0x0; - etm_set_default(drvdata); + etm_set_default(config); spin_unlock(&drvdata->spinlock); } @@ -110,8 +106,9 @@ static ssize_t mode_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->mode; + val = config->mode; return sprintf(buf, "%#lx\n", val); } @@ -122,48 +119,49 @@ static ssize_t mode_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETM_MODE_ALL; + config->mode = val & ETM_MODE_ALL; - if (drvdata->mode & ETM_MODE_EXCLUDE) - drvdata->enable_ctrl1 |= ETMTECR1_INC_EXC; + if (config->mode & ETM_MODE_EXCLUDE) + config->enable_ctrl1 |= ETMTECR1_INC_EXC; else - drvdata->enable_ctrl1 &= ~ETMTECR1_INC_EXC; + config->enable_ctrl1 &= ~ETMTECR1_INC_EXC; - if (drvdata->mode & ETM_MODE_CYCACC) - drvdata->ctrl |= ETMCR_CYC_ACC; + if (config->mode & ETM_MODE_CYCACC) + config->ctrl |= ETMCR_CYC_ACC; else - drvdata->ctrl &= ~ETMCR_CYC_ACC; + config->ctrl &= ~ETMCR_CYC_ACC; - if (drvdata->mode & ETM_MODE_STALL) { + if (config->mode & ETM_MODE_STALL) { if (!(drvdata->etmccr & ETMCCR_FIFOFULL)) { dev_warn(drvdata->dev, "stall mode not supported\n"); ret = -EINVAL; goto err_unlock; } - drvdata->ctrl |= ETMCR_STALL_MODE; + config->ctrl |= ETMCR_STALL_MODE; } else - drvdata->ctrl &= ~ETMCR_STALL_MODE; + config->ctrl &= ~ETMCR_STALL_MODE; - if (drvdata->mode & ETM_MODE_TIMESTAMP) { + if (config->mode & ETM_MODE_TIMESTAMP) { if (!(drvdata->etmccer & ETMCCER_TIMESTAMP)) { dev_warn(drvdata->dev, "timestamp not supported\n"); ret = -EINVAL; goto err_unlock; } - drvdata->ctrl |= ETMCR_TIMESTAMP_EN; + config->ctrl |= ETMCR_TIMESTAMP_EN; } else - drvdata->ctrl &= ~ETMCR_TIMESTAMP_EN; + config->ctrl &= ~ETMCR_TIMESTAMP_EN; - if (drvdata->mode & ETM_MODE_CTXID) - drvdata->ctrl |= ETMCR_CTXID_SIZE; + if (config->mode & ETM_MODE_CTXID) + config->ctrl |= ETMCR_CTXID_SIZE; else - drvdata->ctrl &= ~ETMCR_CTXID_SIZE; + config->ctrl &= ~ETMCR_CTXID_SIZE; spin_unlock(&drvdata->spinlock); return size; @@ -179,8 +177,9 @@ static ssize_t trigger_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->trigger_event; + val = config->trigger_event; return sprintf(buf, "%#lx\n", val); } @@ -191,12 +190,13 @@ static ssize_t trigger_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->trigger_event = val & ETM_EVENT_MASK; + config->trigger_event = val & ETM_EVENT_MASK; return size; } @@ -207,8 +207,9 @@ static ssize_t enable_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->enable_event; + val = config->enable_event; return sprintf(buf, "%#lx\n", val); } @@ -219,12 +220,13 @@ static ssize_t enable_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->enable_event = val & ETM_EVENT_MASK; + config->enable_event = val & ETM_EVENT_MASK; return size; } @@ -235,8 +237,9 @@ static ssize_t fifofull_level_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->fifofull_level; + val = config->fifofull_level; return sprintf(buf, "%#lx\n", val); } @@ -247,12 +250,13 @@ static ssize_t fifofull_level_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->fifofull_level = val; + config->fifofull_level = val; return size; } @@ -263,8 +267,9 @@ static ssize_t addr_idx_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->addr_idx; + val = config->addr_idx; return sprintf(buf, "%#lx\n", val); } @@ -275,6 +280,7 @@ static ssize_t addr_idx_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -288,7 +294,7 @@ static ssize_t addr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; + config->addr_idx = val; spin_unlock(&drvdata->spinlock); return size; @@ -301,16 +307,17 @@ static ssize_t addr_single_show(struct device *dev, u8 idx; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - val = drvdata->addr_val[idx]; + val = config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -324,21 +331,22 @@ static ssize_t addr_single_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + config->addr_val[idx] = val; + config->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; spin_unlock(&drvdata->spinlock); return size; @@ -351,23 +359,24 @@ static ssize_t addr_range_show(struct device *dev, u8 idx; unsigned long val1, val2; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val1 = drvdata->addr_val[idx]; - val2 = drvdata->addr_val[idx + 1]; + val1 = config->addr_val[idx]; + val2 = config->addr_val[idx + 1]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx %#lx\n", val1, val2); @@ -380,6 +389,7 @@ static ssize_t addr_range_store(struct device *dev, u8 idx; unsigned long val1, val2; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) return -EINVAL; @@ -388,24 +398,24 @@ static ssize_t addr_range_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; - drvdata->enable_ctrl1 |= (1 << (idx/2)); + config->addr_val[idx] = val1; + config->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + config->addr_val[idx + 1] = val2; + config->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + config->enable_ctrl1 |= (1 << (idx/2)); spin_unlock(&drvdata->spinlock); return size; @@ -418,16 +428,17 @@ static ssize_t addr_start_show(struct device *dev, u8 idx; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = drvdata->addr_val[idx]; + val = config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -441,23 +452,24 @@ static ssize_t addr_start_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->startstop_ctrl |= (1 << idx); - drvdata->enable_ctrl1 |= BIT(25); + config->addr_val[idx] = val; + config->addr_type[idx] = ETM_ADDR_TYPE_START; + config->startstop_ctrl |= (1 << idx); + config->enable_ctrl1 |= BIT(25); spin_unlock(&drvdata->spinlock); return size; @@ -470,16 +482,17 @@ static ssize_t addr_stop_show(struct device *dev, u8 idx; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = drvdata->addr_val[idx]; + val = config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -493,23 +506,24 @@ static ssize_t addr_stop_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->startstop_ctrl |= (1 << (idx + 16)); - drvdata->enable_ctrl1 |= ETMTECR1_START_STOP; + config->addr_val[idx] = val; + config->addr_type[idx] = ETM_ADDR_TYPE_STOP; + config->startstop_ctrl |= (1 << (idx + 16)); + config->enable_ctrl1 |= ETMTECR1_START_STOP; spin_unlock(&drvdata->spinlock); return size; @@ -521,9 +535,10 @@ static ssize_t addr_acctype_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->addr_acctype[drvdata->addr_idx]; + val = config->addr_acctype[config->addr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -536,13 +551,14 @@ static ssize_t addr_acctype_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->addr_acctype[drvdata->addr_idx] = val; + config->addr_acctype[config->addr_idx] = val; spin_unlock(&drvdata->spinlock); return size; @@ -554,8 +570,9 @@ static ssize_t cntr_idx_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->cntr_idx; + val = config->cntr_idx; return sprintf(buf, "%#lx\n", val); } @@ -566,6 +583,7 @@ static ssize_t cntr_idx_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -578,7 +596,7 @@ static ssize_t cntr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; + config->cntr_idx = val; spin_unlock(&drvdata->spinlock); return size; @@ -590,9 +608,10 @@ static ssize_t cntr_rld_val_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_val[drvdata->cntr_idx]; + val = config->cntr_rld_val[config->cntr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -605,13 +624,14 @@ static ssize_t cntr_rld_val_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_val[drvdata->cntr_idx] = val; + config->cntr_rld_val[config->cntr_idx] = val; spin_unlock(&drvdata->spinlock); return size; @@ -623,9 +643,10 @@ static ssize_t cntr_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->cntr_event[drvdata->cntr_idx]; + val = config->cntr_event[config->cntr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -638,13 +659,14 @@ static ssize_t cntr_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + config->cntr_event[config->cntr_idx] = val & ETM_EVENT_MASK; spin_unlock(&drvdata->spinlock); return size; @@ -656,9 +678,10 @@ static ssize_t cntr_rld_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->cntr_rld_event[drvdata->cntr_idx]; + val = config->cntr_rld_event[config->cntr_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -671,13 +694,14 @@ static ssize_t cntr_rld_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_rld_event[drvdata->cntr_idx] = val & ETM_EVENT_MASK; + config->cntr_rld_event[config->cntr_idx] = val & ETM_EVENT_MASK; spin_unlock(&drvdata->spinlock); return size; @@ -690,12 +714,13 @@ static ssize_t cntr_val_show(struct device *dev, int i, ret = 0; u32 val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; if (!drvdata->enable) { spin_lock(&drvdata->spinlock); for (i = 0; i < drvdata->nr_cntr; i++) ret += sprintf(buf, "counter %d: %x\n", - i, drvdata->cntr_val[i]); + i, config->cntr_val[i]); spin_unlock(&drvdata->spinlock); return ret; } @@ -715,13 +740,14 @@ static ssize_t cntr_val_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; spin_lock(&drvdata->spinlock); - drvdata->cntr_val[drvdata->cntr_idx] = val; + config->cntr_val[config->cntr_idx] = val; spin_unlock(&drvdata->spinlock); return size; @@ -733,8 +759,9 @@ static ssize_t seq_12_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_12_event; + val = config->seq_12_event; return sprintf(buf, "%#lx\n", val); } @@ -745,12 +772,13 @@ static ssize_t seq_12_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_12_event = val & ETM_EVENT_MASK; + config->seq_12_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_12_event); @@ -760,8 +788,9 @@ static ssize_t seq_21_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_21_event; + val = config->seq_21_event; return sprintf(buf, "%#lx\n", val); } @@ -772,12 +801,13 @@ static ssize_t seq_21_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_21_event = val & ETM_EVENT_MASK; + config->seq_21_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_21_event); @@ -787,8 +817,9 @@ static ssize_t seq_23_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_23_event; + val = config->seq_23_event; return sprintf(buf, "%#lx\n", val); } @@ -799,12 +830,13 @@ static ssize_t seq_23_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_23_event = val & ETM_EVENT_MASK; + config->seq_23_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_23_event); @@ -814,8 +846,9 @@ static ssize_t seq_31_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_31_event; + val = config->seq_31_event; return sprintf(buf, "%#lx\n", val); } @@ -826,12 +859,13 @@ static ssize_t seq_31_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_31_event = val & ETM_EVENT_MASK; + config->seq_31_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_31_event); @@ -841,8 +875,9 @@ static ssize_t seq_32_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_32_event; + val = config->seq_32_event; return sprintf(buf, "%#lx\n", val); } @@ -853,12 +888,13 @@ static ssize_t seq_32_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_32_event = val & ETM_EVENT_MASK; + config->seq_32_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_32_event); @@ -868,8 +904,9 @@ static ssize_t seq_13_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->seq_13_event; + val = config->seq_13_event; return sprintf(buf, "%#lx\n", val); } @@ -880,12 +917,13 @@ static ssize_t seq_13_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->seq_13_event = val & ETM_EVENT_MASK; + config->seq_13_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_13_event); @@ -895,9 +933,10 @@ static ssize_t seq_curr_state_show(struct device *dev, { unsigned long val, flags; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; if (!drvdata->enable) { - val = drvdata->seq_curr_state; + val = config->seq_curr_state; goto out; } @@ -921,6 +960,7 @@ static ssize_t seq_curr_state_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -929,7 +969,7 @@ static ssize_t seq_curr_state_store(struct device *dev, if (val > ETM_SEQ_STATE_MAX_VAL) return -EINVAL; - drvdata->seq_curr_state = val; + config->seq_curr_state = val; return size; } @@ -940,8 +980,9 @@ static ssize_t ctxid_idx_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->ctxid_idx; + val = config->ctxid_idx; return sprintf(buf, "%#lx\n", val); } @@ -952,6 +993,7 @@ static ssize_t ctxid_idx_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) @@ -965,7 +1007,7 @@ static ssize_t ctxid_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; + config->ctxid_idx = val; spin_unlock(&drvdata->spinlock); return size; @@ -977,9 +1019,10 @@ static ssize_t ctxid_pid_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; + val = config->ctxid_vpid[config->ctxid_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); @@ -992,6 +1035,7 @@ static ssize_t ctxid_pid_store(struct device *dev, int ret; unsigned long vpid, pid; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &vpid); if (ret) @@ -1000,8 +1044,8 @@ static ssize_t ctxid_pid_store(struct device *dev, pid = coresight_vpid_to_pid(vpid); spin_lock(&drvdata->spinlock); - drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; - drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; + config->ctxid_pid[config->ctxid_idx] = pid; + config->ctxid_vpid[config->ctxid_idx] = vpid; spin_unlock(&drvdata->spinlock); return size; @@ -1013,8 +1057,9 @@ static ssize_t ctxid_mask_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->ctxid_mask; + val = config->ctxid_mask; return sprintf(buf, "%#lx\n", val); } @@ -1025,12 +1070,13 @@ static ssize_t ctxid_mask_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->ctxid_mask = val; + config->ctxid_mask = val; return size; } static DEVICE_ATTR_RW(ctxid_mask); @@ -1040,8 +1086,9 @@ static ssize_t sync_freq_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->sync_freq; + val = config->sync_freq; return sprintf(buf, "%#lx\n", val); } @@ -1052,12 +1099,13 @@ static ssize_t sync_freq_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->sync_freq = val & ETM_SYNC_MASK; + config->sync_freq = val & ETM_SYNC_MASK; return size; } static DEVICE_ATTR_RW(sync_freq); @@ -1067,8 +1115,9 @@ static ssize_t timestamp_event_show(struct device *dev, { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; - val = drvdata->timestamp_event; + val = config->timestamp_event; return sprintf(buf, "%#lx\n", val); } @@ -1079,12 +1128,13 @@ static ssize_t timestamp_event_store(struct device *dev, int ret; unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etm_config *config = &drvdata->config; ret = kstrtoul(buf, 16, &val); if (ret) return ret; - drvdata->timestamp_event = val & ETM_EVENT_MASK; + config->timestamp_event = val & ETM_EVENT_MASK; return size; } static DEVICE_ATTR_RW(timestamp_event); diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index f2c74bb56993..1952dc31fee4 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -41,6 +41,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); /* The number of ETM/PTM currently registered */ static int etm_count; static struct etm_drvdata *etmdrvdata[NR_CPUS]; +static void etm_init_default_data(struct etm_config *config); /* * Memory mapped writes to clear os lock are not supported on some processors @@ -186,36 +187,39 @@ static void etm_clr_prog(struct etm_drvdata *drvdata) } } -void etm_set_default(struct etm_drvdata *drvdata) +void etm_set_default(struct etm_config *config) { int i; - drvdata->trigger_event = ETM_DEFAULT_EVENT_VAL; - drvdata->enable_event = ETM_HARD_WIRE_RES_A; + if (WARN_ON_ONCE(!config)) + return; - drvdata->seq_12_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_21_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_23_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_31_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_32_event = ETM_DEFAULT_EVENT_VAL; - drvdata->seq_13_event = ETM_DEFAULT_EVENT_VAL; - drvdata->timestamp_event = ETM_DEFAULT_EVENT_VAL; + config->trigger_event = ETM_DEFAULT_EVENT_VAL; + config->enable_event = ETM_HARD_WIRE_RES_A; - for (i = 0; i < drvdata->nr_cntr; i++) { - drvdata->cntr_rld_val[i] = 0x0; - drvdata->cntr_event[i] = ETM_DEFAULT_EVENT_VAL; - drvdata->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL; - drvdata->cntr_val[i] = 0x0; + config->seq_12_event = ETM_DEFAULT_EVENT_VAL; + config->seq_21_event = ETM_DEFAULT_EVENT_VAL; + config->seq_23_event = ETM_DEFAULT_EVENT_VAL; + config->seq_31_event = ETM_DEFAULT_EVENT_VAL; + config->seq_32_event = ETM_DEFAULT_EVENT_VAL; + config->seq_13_event = ETM_DEFAULT_EVENT_VAL; + config->timestamp_event = ETM_DEFAULT_EVENT_VAL; + + for (i = 0; i < ETM_MAX_CNTR; i++) { + config->cntr_rld_val[i] = 0x0; + config->cntr_event[i] = ETM_DEFAULT_EVENT_VAL; + config->cntr_rld_event[i] = ETM_DEFAULT_EVENT_VAL; + config->cntr_val[i] = 0x0; } - drvdata->seq_curr_state = 0x0; - drvdata->ctxid_idx = 0x0; - for (i = 0; i < drvdata->nr_ctxid_cmp; i++) { - drvdata->ctxid_pid[i] = 0x0; - drvdata->ctxid_vpid[i] = 0x0; + config->seq_curr_state = 0x0; + config->ctxid_idx = 0x0; + for (i = 0; i < ETM_MAX_CTXID_CMP; i++) { + config->ctxid_pid[i] = 0x0; + config->ctxid_vpid[i] = 0x0; } - drvdata->ctxid_mask = 0x0; + config->ctxid_mask = 0x0; } static void etm_enable_hw(void *info) @@ -223,6 +227,7 @@ static void etm_enable_hw(void *info) int i; u32 etmcr; struct etm_drvdata *drvdata = info; + struct etm_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); @@ -238,39 +243,39 @@ static void etm_enable_hw(void *info) etmcr = etm_readl(drvdata, ETMCR); etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG); etmcr |= drvdata->port_size; - etm_writel(drvdata, drvdata->ctrl | etmcr, ETMCR); - etm_writel(drvdata, drvdata->trigger_event, ETMTRIGGER); - etm_writel(drvdata, drvdata->startstop_ctrl, ETMTSSCR); - etm_writel(drvdata, drvdata->enable_event, ETMTEEVR); - etm_writel(drvdata, drvdata->enable_ctrl1, ETMTECR1); - etm_writel(drvdata, drvdata->fifofull_level, ETMFFLR); + etm_writel(drvdata, config->ctrl | etmcr, ETMCR); + etm_writel(drvdata, config->trigger_event, ETMTRIGGER); + etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR); + etm_writel(drvdata, config->enable_event, ETMTEEVR); + etm_writel(drvdata, config->enable_ctrl1, ETMTECR1); + etm_writel(drvdata, config->fifofull_level, ETMFFLR); for (i = 0; i < drvdata->nr_addr_cmp; i++) { - etm_writel(drvdata, drvdata->addr_val[i], ETMACVRn(i)); - etm_writel(drvdata, drvdata->addr_acctype[i], ETMACTRn(i)); + etm_writel(drvdata, config->addr_val[i], ETMACVRn(i)); + etm_writel(drvdata, config->addr_acctype[i], ETMACTRn(i)); } for (i = 0; i < drvdata->nr_cntr; i++) { - etm_writel(drvdata, drvdata->cntr_rld_val[i], ETMCNTRLDVRn(i)); - etm_writel(drvdata, drvdata->cntr_event[i], ETMCNTENRn(i)); - etm_writel(drvdata, drvdata->cntr_rld_event[i], + etm_writel(drvdata, config->cntr_rld_val[i], ETMCNTRLDVRn(i)); + etm_writel(drvdata, config->cntr_event[i], ETMCNTENRn(i)); + etm_writel(drvdata, config->cntr_rld_event[i], ETMCNTRLDEVRn(i)); - etm_writel(drvdata, drvdata->cntr_val[i], ETMCNTVRn(i)); + etm_writel(drvdata, config->cntr_val[i], ETMCNTVRn(i)); } - etm_writel(drvdata, drvdata->seq_12_event, ETMSQ12EVR); - etm_writel(drvdata, drvdata->seq_21_event, ETMSQ21EVR); - etm_writel(drvdata, drvdata->seq_23_event, ETMSQ23EVR); - etm_writel(drvdata, drvdata->seq_31_event, ETMSQ31EVR); - etm_writel(drvdata, drvdata->seq_32_event, ETMSQ32EVR); - etm_writel(drvdata, drvdata->seq_13_event, ETMSQ13EVR); - etm_writel(drvdata, drvdata->seq_curr_state, ETMSQR); + etm_writel(drvdata, config->seq_12_event, ETMSQ12EVR); + etm_writel(drvdata, config->seq_21_event, ETMSQ21EVR); + etm_writel(drvdata, config->seq_23_event, ETMSQ23EVR); + etm_writel(drvdata, config->seq_31_event, ETMSQ31EVR); + etm_writel(drvdata, config->seq_32_event, ETMSQ32EVR); + etm_writel(drvdata, config->seq_13_event, ETMSQ13EVR); + etm_writel(drvdata, config->seq_curr_state, ETMSQR); for (i = 0; i < drvdata->nr_ext_out; i++) etm_writel(drvdata, ETM_DEFAULT_EVENT_VAL, ETMEXTOUTEVRn(i)); for (i = 0; i < drvdata->nr_ctxid_cmp; i++) - etm_writel(drvdata, drvdata->ctxid_pid[i], ETMCIDCVRn(i)); - etm_writel(drvdata, drvdata->ctxid_mask, ETMCIDCMR); - etm_writel(drvdata, drvdata->sync_freq, ETMSYNCFR); + etm_writel(drvdata, config->ctxid_pid[i], ETMCIDCVRn(i)); + etm_writel(drvdata, config->ctxid_mask, ETMCIDCMR); + etm_writel(drvdata, config->sync_freq, ETMSYNCFR); /* No external input selected */ etm_writel(drvdata, 0x0, ETMEXTINSELR); - etm_writel(drvdata, drvdata->timestamp_event, ETMTSEVR); + etm_writel(drvdata, config->timestamp_event, ETMTSEVR); /* No auxiliary control selected */ etm_writel(drvdata, 0x0, ETMAUXCR); etm_writel(drvdata, drvdata->traceid, ETMTRACEIDR); @@ -278,7 +283,7 @@ static void etm_enable_hw(void *info) etm_writel(drvdata, 0x0, ETMVMIDCVR); /* Ensures trace output is enabled from this ETM */ - etm_writel(drvdata, drvdata->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); + etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); etm_clr_prog(drvdata); CS_LOCK(drvdata->base); @@ -362,6 +367,7 @@ static void etm_disable_hw(void *info) { int i; struct etm_drvdata *drvdata = info; + struct etm_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); etm_set_prog(drvdata); @@ -370,10 +376,10 @@ static void etm_disable_hw(void *info) etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR); /* Read back sequencer and counters for post trace analysis */ - drvdata->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); + config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); for (i = 0; i < drvdata->nr_cntr; i++) - drvdata->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i)); + config->cntr_val[i] = etm_readl(drvdata, ETMCNTVRn(i)); etm_set_pwrdwn(drvdata); CS_LOCK(drvdata->base); @@ -522,14 +528,8 @@ static void etm_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm_init_default_data(struct etm_drvdata *drvdata) +static void etm_init_default_data(struct etm_config *config) { - /* - * A trace ID of value 0 is invalid, so let's start at some - * random value that fits in 7 bits and will be just as good. - */ - static int etm3x_traceid = 0x10; - u32 flags = (1 << 0 | /* instruction execute*/ 3 << 3 | /* ARM instruction */ 0 << 5 | /* No data value comparison */ @@ -537,25 +537,28 @@ static void etm_init_default_data(struct etm_drvdata *drvdata) 0 << 8 | /* Ignore context ID */ 0 << 10); /* Security ignored */ - /* - * Initial configuration only - guarantees sources handled by - * this driver have a unique ID at startup time but not between - * all other types of sources. For that we lean on the core - * framework. - */ - drvdata->traceid = etm3x_traceid++; - drvdata->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); - drvdata->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; - if (drvdata->nr_addr_cmp >= 2) { - drvdata->addr_val[0] = (u32) _stext; - drvdata->addr_val[1] = (u32) _etext; - drvdata->addr_acctype[0] = flags; - drvdata->addr_acctype[1] = flags; - drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE; - } + if (WARN_ON_ONCE(!config)) + return; - etm_set_default(drvdata); + config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); + config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; + config->addr_val[0] = (u32) _stext; + config->addr_val[1] = (u32) _etext; + config->addr_acctype[0] = flags; + config->addr_acctype[1] = flags; + config->addr_type[0] = ETM_ADDR_TYPE_RANGE; + config->addr_type[1] = ETM_ADDR_TYPE_RANGE; + + etm_set_default(config); +} + +static void etm_init_trace_id(struct etm_drvdata *drvdata) +{ + /* + * A trace ID of value 0 is invalid, so let's start at some + * random value that fits in 7 bits and go from there. + */ + drvdata->traceid = 0x10 + drvdata->cpu; } static int etm_probe(struct amba_device *adev, const struct amba_id *id) @@ -623,7 +626,9 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) ret = -EINVAL; goto err_arch_supported; } - etm_init_default_data(drvdata); + + etm_init_trace_id(drvdata); + etm_init_default_data(&drvdata->config); desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; From 877324a46672c3bf72a423fcd9b257642f29a50e Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:52 -0700 Subject: [PATCH 168/656] coresight: etm3x: adding operation mode for etm_enable() Adding a new mode to source API enable() in order to distinguish where the request comes from. That way it is possible to perform different operations based on where the request was issued from. The ETM4x driver is also modified to keep in sync with the new interface. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 22fd532eaa0c24d86e23d8e9e3b7feac4a8cac80) --- drivers/hwtracing/coresight/coresight-etm.h | 5 +- .../coresight/coresight-etm3x-sysfs.c | 4 +- drivers/hwtracing/coresight/coresight-etm3x.c | 68 ++++++++++++++++--- drivers/hwtracing/coresight/coresight-etm4x.c | 2 +- drivers/hwtracing/coresight/coresight-priv.h | 6 ++ drivers/hwtracing/coresight/coresight.c | 6 +- include/linux/coresight.h | 2 +- 7 files changed, 76 insertions(+), 17 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 371fb7d2e829..5b29d5540fe5 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -13,6 +13,7 @@ #ifndef _CORESIGHT_CORESIGHT_ETM_H #define _CORESIGHT_CORESIGHT_ETM_H +#include #include #include "coresight-priv.h" @@ -214,7 +215,7 @@ struct etm_config { * @port_size: port size as reported by ETMCR bit 4-6 and 21. * @arch: ETM/PTM version number. * @use_cpu14: true if management registers need to be accessed via CP14. - * @enable: is this ETM/PTM currently tracing. + * @mode: this tracer's mode, i.e sysFS, Perf or disabled. * @sticky_enable: true if ETM base configuration has been done. * @boot_enable:true if we should start tracing at boot time. * @os_unlock: true if access to management registers is allowed. @@ -238,7 +239,7 @@ struct etm_drvdata { int port_size; u8 arch; bool use_cp14; - bool enable; + local_t mode; bool sticky_enable; bool boot_enable; bool os_unlock; diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 456df2378a6f..387c79fd9d5e 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -716,7 +716,7 @@ static ssize_t cntr_val_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!drvdata->enable) { + if (!local_read(&drvdata->mode)) { spin_lock(&drvdata->spinlock); for (i = 0; i < drvdata->nr_cntr; i++) ret += sprintf(buf, "counter %d: %x\n", @@ -935,7 +935,7 @@ static ssize_t seq_curr_state_show(struct device *dev, struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); struct etm_config *config = &drvdata->config; - if (!drvdata->enable) { + if (!local_read(&drvdata->mode)) { val = config->seq_curr_state; goto out; } diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 1952dc31fee4..e16501b41ed8 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -306,7 +306,7 @@ int etm_get_trace_id(struct etm_drvdata *drvdata) if (!drvdata) goto out; - if (!drvdata->enable) + if (!local_read(&drvdata->mode)) return drvdata->traceid; pm_runtime_get_sync(drvdata->dev); @@ -332,7 +332,7 @@ static int etm_trace_id(struct coresight_device *csdev) return etm_get_trace_id(drvdata); } -static int etm_enable(struct coresight_device *csdev) +static int etm_enable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; @@ -351,18 +351,44 @@ static int etm_enable(struct coresight_device *csdev) goto err; } - drvdata->enable = true; drvdata->sticky_enable = true; - spin_unlock(&drvdata->spinlock); dev_info(drvdata->dev, "ETM tracing enabled\n"); return 0; + err: spin_unlock(&drvdata->spinlock); return ret; } +static int etm_enable(struct coresight_device *csdev, u32 mode) +{ + int ret; + u32 val; + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + + /* Someone is already using the tracer */ + if (val) + return -EBUSY; + + switch (mode) { + case CS_MODE_SYSFS: + ret = etm_enable_sysfs(csdev); + break; + default: + ret = -EINVAL; + } + + /* The tracer didn't start */ + if (ret) + local_set(&drvdata->mode, CS_MODE_DISABLED); + + return ret; +} + static void etm_disable_hw(void *info) { int i; @@ -387,7 +413,7 @@ static void etm_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } -static void etm_disable(struct coresight_device *csdev) +static void etm_disable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -405,7 +431,6 @@ static void etm_disable(struct coresight_device *csdev) * ensures that register writes occur when cpu is powered. */ smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1); - drvdata->enable = false; spin_unlock(&drvdata->spinlock); put_online_cpus(); @@ -413,6 +438,33 @@ static void etm_disable(struct coresight_device *csdev) dev_info(drvdata->dev, "ETM tracing disabled\n"); } +static void etm_disable(struct coresight_device *csdev) +{ + u32 mode; + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* + * For as long as the tracer isn't disabled another entity can't + * change its status. As such we can read the status here without + * fearing it will change under us. + */ + mode = local_read(&drvdata->mode); + + switch (mode) { + case CS_MODE_DISABLED: + break; + case CS_MODE_SYSFS: + etm_disable_sysfs(csdev); + break; + default: + WARN_ON_ONCE(mode); + return; + } + + if (mode) + local_set(&drvdata->mode, CS_MODE_DISABLED); +} + static const struct coresight_ops_source etm_source_ops = { .cpu_id = etm_cpu_id, .trace_id = etm_trace_id, @@ -440,7 +492,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, etmdrvdata[cpu]->os_unlock = true; } - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm_enable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; @@ -453,7 +505,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action, case CPU_DYING: spin_lock(&etmdrvdata[cpu]->spinlock); - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm_disable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index c2f518fbc9a8..0026092fec7f 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -187,7 +187,7 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } -static int etm4_enable(struct coresight_device *csdev) +static int etm4_enable(struct coresight_device *csdev, u32 mode) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 14f245a2018d..ed116b303e87 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -34,6 +34,12 @@ #define TIMEOUT_US 100 #define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) +enum cs_mode { + CS_MODE_DISABLED, + CS_MODE_SYSFS, + CS_MODE_PERF, +}; + static inline void CS_LOCK(void __iomem *addr) { do { diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 6b44928c1076..b20afb709141 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -222,7 +222,7 @@ static void coresight_disable_link(struct coresight_device *csdev, csdev->enable = false; } -static int coresight_enable_source(struct coresight_device *csdev) +static int coresight_enable_source(struct coresight_device *csdev, u32 mode) { int ret; @@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev) if (!csdev->enable) { if (source_ops(csdev)->enable) { - ret = source_ops(csdev)->enable(csdev); + ret = source_ops(csdev)->enable(csdev, mode); if (ret) return ret; } @@ -458,7 +458,7 @@ int coresight_enable(struct coresight_device *csdev) if (ret) goto err_path; - ret = coresight_enable_source(csdev); + ret = coresight_enable_source(csdev, CS_MODE_SYSFS); if (ret) goto err_source; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 851ecb22397e..61dfb8d511ea 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -213,7 +213,7 @@ struct coresight_ops_link { struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); - int (*enable)(struct coresight_device *csdev); + int (*enable)(struct coresight_device *csdev, u32 mode); void (*disable)(struct coresight_device *csdev); }; From 9e977106258d578d7b9d29251084a43e025a914d Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:53 -0700 Subject: [PATCH 169/656] coresight: etm3x: set progbit to stop trace collection There is no need to use the event enable's "always false" event to stop trace collection. For that purpose setting the programming bit (ETMCR:10) is enough. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 47cd066cd00a65902ee3bd57da5bd395cb83aff9) --- drivers/hwtracing/coresight/coresight-etm3x.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index e16501b41ed8..447459969cb5 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -398,9 +398,6 @@ static void etm_disable_hw(void *info) CS_UNLOCK(drvdata->base); etm_set_prog(drvdata); - /* Program trace enable to low by using always false event */ - etm_writel(drvdata, ETM_HARD_WIRE_RES_A | ETM_EVENT_NOT_A, ETMTEEVR); - /* Read back sequencer and counters for post trace analysis */ config->seq_curr_state = (etm_readl(drvdata, ETMSQR) & ETM_SQR_MASK); From 9226d25094f893acde0eb8e04ce1564d83a3ccff Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:54 -0700 Subject: [PATCH 170/656] coresight: etm3x: changing default trace configuration Changing default configuration to include the entire address range rather than just the kernel. That way traces are more inclusive and it is easier to narrow down if needed. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit e19217299caf1a54c55081ab6339b3baccec63b0) --- drivers/hwtracing/coresight/coresight-etm.h | 2 ++ drivers/hwtracing/coresight/coresight-etm3x.c | 29 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 5b29d5540fe5..44585d4adb26 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -146,6 +146,7 @@ * @startstop_ctrl: setting for register ETMTSSCR. * @enable_event: setting for register ETMTEEVR. * @enable_ctrl1: setting for register ETMTECR1. + * @enable_ctrl2: setting for register ETMTECR2. * @fifofull_level: setting for register ETMFFLR. * @addr_idx: index for the address comparator selection. * @addr_val: value for address comparator register. @@ -179,6 +180,7 @@ struct etm_config { u32 startstop_ctrl; u32 enable_event; u32 enable_ctrl1; + u32 enable_ctrl2; u32 fifofull_level; u8 addr_idx; u32 addr_val[ETM_MAX_ADDR_CMP]; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 447459969cb5..92139674bea4 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -579,26 +579,23 @@ static void etm_init_arch_data(void *info) static void etm_init_default_data(struct etm_config *config) { - u32 flags = (1 << 0 | /* instruction execute*/ - 3 << 3 | /* ARM instruction */ - 0 << 5 | /* No data value comparison */ - 0 << 7 | /* No exact mach */ - 0 << 8 | /* Ignore context ID */ - 0 << 10); /* Security ignored */ - if (WARN_ON_ONCE(!config)) return; - config->ctrl = (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN); - config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; - config->addr_val[0] = (u32) _stext; - config->addr_val[1] = (u32) _etext; - config->addr_acctype[0] = flags; - config->addr_acctype[1] = flags; - config->addr_type[0] = ETM_ADDR_TYPE_RANGE; - config->addr_type[1] = ETM_ADDR_TYPE_RANGE; - etm_set_default(config); + + /* + * Taken verbatim from the TRM: + * + * To trace all memory: + * set bit [24] in register 0x009, the ETMTECR1, to 1 + * set all other bits in register 0x009, the ETMTECR1, to 0 + * set all bits in register 0x007, the ETMTECR2, to 0 + * set register 0x008, the ETMTEEVR, to 0x6F (TRUE). + */ + config->enable_ctrl1 = BIT(24); + config->enable_ctrl2 = 0x0; + config->enable_event = ETM_HARD_WIRE_RES_A; } static void etm_init_trace_id(struct etm_drvdata *drvdata) From 003099a9ab219bb985eabfdb64bb29510ff5c0fd Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:55 -0700 Subject: [PATCH 171/656] coresight: etm3x: consolidating initial config There is really no point in having two functions to take care of doing the initial tracer configuration. As such moving everything to 'etm_set_default()'. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c528a25ac7c4dacba9e4d98d5f06846939c5966f) --- drivers/hwtracing/coresight/coresight-etm3x.c | 37 +++++++------------ 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 92139674bea4..34a69583ccbc 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -41,7 +41,6 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); /* The number of ETM/PTM currently registered */ static int etm_count; static struct etm_drvdata *etmdrvdata[NR_CPUS]; -static void etm_init_default_data(struct etm_config *config); /* * Memory mapped writes to clear os lock are not supported on some processors @@ -194,6 +193,19 @@ void etm_set_default(struct etm_config *config) if (WARN_ON_ONCE(!config)) return; + /* + * Taken verbatim from the TRM: + * + * To trace all memory: + * set bit [24] in register 0x009, the ETMTECR1, to 1 + * set all other bits in register 0x009, the ETMTECR1, to 0 + * set all bits in register 0x007, the ETMTECR2, to 0 + * set register 0x008, the ETMTEEVR, to 0x6F (TRUE). + */ + config->enable_ctrl1 = BIT(24); + config->enable_ctrl2 = 0x0; + config->enable_event = ETM_HARD_WIRE_RES_A; + config->trigger_event = ETM_DEFAULT_EVENT_VAL; config->enable_event = ETM_HARD_WIRE_RES_A; @@ -577,27 +589,6 @@ static void etm_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm_init_default_data(struct etm_config *config) -{ - if (WARN_ON_ONCE(!config)) - return; - - etm_set_default(config); - - /* - * Taken verbatim from the TRM: - * - * To trace all memory: - * set bit [24] in register 0x009, the ETMTECR1, to 1 - * set all other bits in register 0x009, the ETMTECR1, to 0 - * set all bits in register 0x007, the ETMTECR2, to 0 - * set register 0x008, the ETMTEEVR, to 0x6F (TRUE). - */ - config->enable_ctrl1 = BIT(24); - config->enable_ctrl2 = 0x0; - config->enable_event = ETM_HARD_WIRE_RES_A; -} - static void etm_init_trace_id(struct etm_drvdata *drvdata) { /* @@ -674,7 +665,7 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) } etm_init_trace_id(drvdata); - etm_init_default_data(&drvdata->config); + etm_set_default(&drvdata->config); desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; From 749ef4caf345a43c284712b3e4507af76aa7b503 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:56 -0700 Subject: [PATCH 172/656] coresight: etm3x: implementing user/kernel mode tracing Adding new mode to limit tracing to kernel or user space. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 2127154d115d4fe8f18300e5ef6f566581359d56) --- drivers/hwtracing/coresight/coresight-etm.h | 6 +- .../coresight/coresight-etm3x-sysfs.c | 4 ++ drivers/hwtracing/coresight/coresight-etm3x.c | 63 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-priv.h | 3 + 4 files changed, 75 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 44585d4adb26..51597cb2c08a 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -110,7 +110,10 @@ #define ETM_MODE_STALL BIT(2) #define ETM_MODE_TIMESTAMP BIT(3) #define ETM_MODE_CTXID BIT(4) -#define ETM_MODE_ALL 0x1f +#define ETM_MODE_ALL (ETM_MODE_EXCLUDE | ETM_MODE_CYCACC | \ + ETM_MODE_STALL | ETM_MODE_TIMESTAMP | \ + ETM_MODE_CTXID | ETM_MODE_EXCL_KERN | \ + ETM_MODE_EXCL_USER) #define ETM_SQR_MASK 0x3 #define ETM_TRACEID_MASK 0x3f @@ -296,5 +299,6 @@ static inline unsigned int etm_readl(struct etm_drvdata *drvdata, u32 off) extern const struct attribute_group *coresight_etm_groups[]; int etm_get_trace_id(struct etm_drvdata *drvdata); void etm_set_default(struct etm_config *config); +void etm_config_trace_mode(struct etm_config *config); struct etm_config *get_etm_config(struct etm_drvdata *drvdata); #endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index 387c79fd9d5e..cbb4046c1070 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -162,6 +162,10 @@ static ssize_t mode_store(struct device *dev, config->ctrl |= ETMCR_CTXID_SIZE; else config->ctrl &= ~ETMCR_CTXID_SIZE; + + if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) + etm_config_trace_mode(config); + spin_unlock(&drvdata->spinlock); return size; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 34a69583ccbc..c82d545e68ef 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -234,6 +234,69 @@ void etm_set_default(struct etm_config *config) config->ctxid_mask = 0x0; } +void etm_config_trace_mode(struct etm_config *config) +{ + u32 flags, mode; + + mode = config->mode; + + mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER); + + /* excluding kernel AND user space doesn't make sense */ + if (mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) + return; + + /* nothing to do if neither flags are set */ + if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER)) + return; + + flags = (1 << 0 | /* instruction execute */ + 3 << 3 | /* ARM instruction */ + 0 << 5 | /* No data value comparison */ + 0 << 7 | /* No exact mach */ + 0 << 8); /* Ignore context ID */ + + /* No need to worry about single address comparators. */ + config->enable_ctrl2 = 0x0; + + /* Bit 0 is address range comparator 1 */ + config->enable_ctrl1 = ETMTECR1_ADDR_COMP_1; + + /* + * On ETMv3.5: + * ETMACTRn[13,11] == Non-secure state comparison control + * ETMACTRn[12,10] == Secure state comparison control + * + * b00 == Match in all modes in this state + * b01 == Do not match in any more in this state + * b10 == Match in all modes excepts user mode in this state + * b11 == Match only in user mode in this state + */ + + /* Tracing in secure mode is not supported at this time */ + flags |= (0 << 12 | 1 << 10); + + if (mode & ETM_MODE_EXCL_USER) { + /* exclude user, match all modes except user mode */ + flags |= (1 << 13 | 0 << 11); + } else { + /* exclude kernel, match only in user mode */ + flags |= (1 << 13 | 1 << 11); + } + + /* + * The ETMEEVR register is already set to "hard wire A". As such + * all there is to do is setup an address comparator that spans + * the entire address range and configure the state and mode bits. + */ + config->addr_val[0] = (u32) 0x0; + config->addr_val[1] = (u32) ~0x0; + config->addr_acctype[0] = flags; + config->addr_acctype[1] = flags; + config->addr_type[0] = ETM_ADDR_TYPE_RANGE; + config->addr_type[1] = ETM_ADDR_TYPE_RANGE; +} + static void etm_enable_hw(void *info) { int i; diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index ed116b303e87..932f34a84d96 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -34,6 +34,9 @@ #define TIMEOUT_US 100 #define BMVAL(val, lsb, msb) ((val & GENMASK(msb, lsb)) >> lsb) +#define ETM_MODE_EXCL_KERN BIT(30) +#define ETM_MODE_EXCL_USER BIT(31) + enum cs_mode { CS_MODE_DISABLED, CS_MODE_SYSFS, From 2d25f4c5e5709286d85887bec2d5528dd2cb6eb3 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:57 -0700 Subject: [PATCH 173/656] coresight: etm3x: implementing perf_enable/disable() API That way traces can be enabled and disabled automatically from the Perf subystem using the PMU abstraction. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 882d5e112491c875ab7c8c336b8beaeec54d0509) --- drivers/hwtracing/coresight/Kconfig | 1 + drivers/hwtracing/coresight/coresight-etm3x.c | 95 ++++++++++++++++++- drivers/hwtracing/coresight/coresight-etm4x.c | 4 +- drivers/hwtracing/coresight/coresight.c | 2 +- include/linux/coresight.h | 6 +- 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index c85935f3525a..db0541031c72 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -4,6 +4,7 @@ menuconfig CORESIGHT bool "CoreSight Tracing Support" select ARM_AMBA + select PERF_EVENTS help This framework provides a kernel interface for the CoreSight debug and trace drivers to register themselves with. It's intended to build diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index c82d545e68ef..a9b820ec16aa 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "coresight-etm.h" @@ -297,6 +298,47 @@ void etm_config_trace_mode(struct etm_config *config) config->addr_type[1] = ETM_ADDR_TYPE_RANGE; } +#define ETM3X_SUPPORTED_OPTIONS (ETMCR_CYC_ACC | ETMCR_TIMESTAMP_EN) + +static int etm_parse_event_config(struct etm_drvdata *drvdata, + struct perf_event_attr *attr) +{ + struct etm_config *config = &drvdata->config; + + if (!attr) + return -EINVAL; + + /* Clear configuration from previous run */ + memset(config, 0, sizeof(struct etm_config)); + + if (attr->exclude_kernel) + config->mode = ETM_MODE_EXCL_KERN; + + if (attr->exclude_user) + config->mode = ETM_MODE_EXCL_USER; + + /* Always start from the default config */ + etm_set_default(config); + + /* + * By default the tracers are configured to trace the whole address + * range. Narrow the field only if requested by user space. + */ + if (config->mode) + etm_config_trace_mode(config); + + /* + * At this time only cycle accurate and timestamp options are + * available. + */ + if (attr->config & ~ETM3X_SUPPORTED_OPTIONS) + return -EINVAL; + + config->ctrl = attr->config; + + return 0; +} + static void etm_enable_hw(void *info) { int i; @@ -316,8 +358,10 @@ static void etm_enable_hw(void *info) etm_set_prog(drvdata); etmcr = etm_readl(drvdata, ETMCR); - etmcr &= (ETMCR_PWD_DWN | ETMCR_ETM_PRG); + /* Clear setting from a previous run if need be */ + etmcr &= ~ETM3X_SUPPORTED_OPTIONS; etmcr |= drvdata->port_size; + etmcr |= ETMCR_ETM_EN; etm_writel(drvdata, config->ctrl | etmcr, ETMCR); etm_writel(drvdata, config->trigger_event, ETMTRIGGER); etm_writel(drvdata, config->startstop_ctrl, ETMTSSCR); @@ -357,9 +401,6 @@ static void etm_enable_hw(void *info) /* No VMID comparator value selected */ etm_writel(drvdata, 0x0, ETMVMIDCVR); - /* Ensures trace output is enabled from this ETM */ - etm_writel(drvdata, config->ctrl | ETMCR_ETM_EN | etmcr, ETMCR); - etm_clr_prog(drvdata); CS_LOCK(drvdata->base); @@ -407,6 +448,22 @@ static int etm_trace_id(struct coresight_device *csdev) return etm_get_trace_id(drvdata); } +static int etm_enable_perf(struct coresight_device *csdev, + struct perf_event_attr *attr) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return -EINVAL; + + /* Configure the tracer based on the session's specifics */ + etm_parse_event_config(drvdata, attr); + /* And enable it */ + etm_enable_hw(drvdata); + + return 0; +} + static int etm_enable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -437,7 +494,8 @@ err: return ret; } -static int etm_enable(struct coresight_device *csdev, u32 mode) +static int etm_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) { int ret; u32 val; @@ -453,6 +511,9 @@ static int etm_enable(struct coresight_device *csdev, u32 mode) case CS_MODE_SYSFS: ret = etm_enable_sysfs(csdev); break; + case CS_MODE_PERF: + ret = etm_enable_perf(csdev, attr); + break; default: ret = -EINVAL; } @@ -485,6 +546,27 @@ static void etm_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } +static void etm_disable_perf(struct coresight_device *csdev) +{ + struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return; + + CS_UNLOCK(drvdata->base); + + /* Setting the prog bit disables tracing immediately */ + etm_set_prog(drvdata); + + /* + * There is no way to know when the tracer will be used again so + * power down the tracer. + */ + etm_set_pwrdwn(drvdata); + + CS_LOCK(drvdata->base); +} + static void etm_disable_sysfs(struct coresight_device *csdev) { struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -528,6 +610,9 @@ static void etm_disable(struct coresight_device *csdev) case CS_MODE_SYSFS: etm_disable_sysfs(csdev); break; + case CS_MODE_PERF: + etm_disable_perf(csdev); + break; default: WARN_ON_ONCE(mode); return; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 0026092fec7f..d0169ba7fbf2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "coresight-etm4x.h" @@ -187,7 +188,8 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } -static int etm4_enable(struct coresight_device *csdev, u32 mode) +static int etm4_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index b20afb709141..95cccb179763 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -234,7 +234,7 @@ static int coresight_enable_source(struct coresight_device *csdev, u32 mode) if (!csdev->enable) { if (source_ops(csdev)->enable) { - ret = source_ops(csdev)->enable(csdev, mode); + ret = source_ops(csdev)->enable(csdev, NULL, mode); if (ret) return ret; } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 61dfb8d511ea..6801dd64ee5d 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -14,6 +14,7 @@ #define _LINUX_CORESIGHT_H #include +#include #include /* Peripheral id registers (0xFD0-0xFEC) */ @@ -206,14 +207,15 @@ struct coresight_ops_link { * @cpu_id: returns the value of the CPU number this component * is associated to. * @trace_id: returns the value of the component's trace ID as known - to the HW. + * to the HW. * @enable: enables tracing for a source. * @disable: disables tracing for a source. */ struct coresight_ops_source { int (*cpu_id)(struct coresight_device *csdev); int (*trace_id)(struct coresight_device *csdev); - int (*enable)(struct coresight_device *csdev, u32 mode); + int (*enable)(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode); void (*disable)(struct coresight_device *csdev); }; From 1aac114220bb712bb693f93a636be4bb9b27876e Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:58 -0700 Subject: [PATCH 174/656] coresight: etb10: moving to local atomic operations Moving to use local atomic operations to take advantage of the lockless implementation, something that will come handy when the ETB is accessed from the Perf subsystem. Also changing the name of the variable to something more meaningful. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 27b10da8fff27d74b755707e61637f6ab488c617) --- drivers/hwtracing/coresight/coresight-etb10.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 917562ecf82a..162c9ccc8c33 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -10,6 +10,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -71,7 +72,7 @@ * @csdev: component vitals needed by the framework. * @miscdev: specifics to handle "/dev/xyz.etb" entry. * @spinlock: only one at a time pls. - * @in_use: synchronise user space access to etb buffer. + * @reading: synchronise user space access to etb buffer. * @buf: area of memory where ETB buffer content gets sent. * @buffer_depth: size of @buf. * @enable: this ETB is being used. @@ -84,7 +85,7 @@ struct etb_drvdata { struct coresight_device *csdev; struct miscdevice miscdev; spinlock_t spinlock; - atomic_t in_use; + local_t reading; u8 *buf; u32 buffer_depth; bool enable; @@ -277,7 +278,7 @@ static int etb_open(struct inode *inode, struct file *file) struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - if (atomic_cmpxchg(&drvdata->in_use, 0, 1)) + if (local_cmpxchg(&drvdata->reading, 0, 1)) return -EBUSY; dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); @@ -313,7 +314,7 @@ static int etb_release(struct inode *inode, struct file *file) { struct etb_drvdata *drvdata = container_of(file->private_data, struct etb_drvdata, miscdev); - atomic_set(&drvdata->in_use, 0); + local_set(&drvdata->reading, 0); dev_dbg(drvdata->dev, "%s: released\n", __func__); return 0; From 5e1df2fa7a6dd8452f56e17c0db9b1214fd8590f Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:51:59 -0700 Subject: [PATCH 175/656] coresight: etb10: adding operation mode for sink->enable() Adding an operation mode to the sink->enable() API in order to prevent simultaneous access from different callers. TPIU and TMC won't be supplemented with the AUX area API immediately and as such ignore the new mode. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit e827d4550aa3225b8965ce4c266208cfe0297509) --- drivers/hwtracing/coresight/coresight-etb10.c | 30 ++++++++++++++----- drivers/hwtracing/coresight/coresight-priv.h | 2 +- drivers/hwtracing/coresight/coresight-tmc.c | 2 +- drivers/hwtracing/coresight/coresight-tpiu.c | 2 +- drivers/hwtracing/coresight/coresight.c | 10 +++---- include/linux/coresight.h | 2 +- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 162c9ccc8c33..79099f95ba3f 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -73,9 +73,9 @@ * @miscdev: specifics to handle "/dev/xyz.etb" entry. * @spinlock: only one at a time pls. * @reading: synchronise user space access to etb buffer. + * @mode: this ETB is being used. * @buf: area of memory where ETB buffer content gets sent. * @buffer_depth: size of @buf. - * @enable: this ETB is being used. * @trigger_cntr: amount of words to store after a trigger. */ struct etb_drvdata { @@ -86,9 +86,9 @@ struct etb_drvdata { struct miscdevice miscdev; spinlock_t spinlock; local_t reading; + local_t mode; u8 *buf; u32 buffer_depth; - bool enable; u32 trigger_cntr; }; @@ -133,16 +133,31 @@ static void etb_enable_hw(struct etb_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int etb_enable(struct coresight_device *csdev) +static int etb_enable(struct coresight_device *csdev, u32 mode) { - struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + u32 val; unsigned long flags; + struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + val = local_cmpxchg(&drvdata->mode, + CS_MODE_DISABLED, mode); + /* + * When accessing from Perf, a HW buffer can be handled + * by a single trace entity. In sysFS mode many tracers + * can be logging to the same HW buffer. + */ + if (val == CS_MODE_PERF) + return -EBUSY; + + /* Nothing to do, the tracer is already enabled. */ + if (val == CS_MODE_SYSFS) + goto out; spin_lock_irqsave(&drvdata->spinlock, flags); etb_enable_hw(drvdata); - drvdata->enable = true; spin_unlock_irqrestore(&drvdata->spinlock, flags); +out: dev_info(drvdata->dev, "ETB enabled\n"); return 0; } @@ -243,9 +258,10 @@ static void etb_disable(struct coresight_device *csdev) spin_lock_irqsave(&drvdata->spinlock, flags); etb_disable_hw(drvdata); etb_dump_hw(drvdata); - drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); + local_set(&drvdata->mode, CS_MODE_DISABLED); + dev_info(drvdata->dev, "ETB disabled\n"); } @@ -263,7 +279,7 @@ static void etb_dump(struct etb_drvdata *drvdata) unsigned long flags; spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->enable) { + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { etb_disable_hw(drvdata); etb_dump_hw(drvdata); etb_enable_hw(drvdata); diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 932f34a84d96..333eddaed339 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -62,7 +62,7 @@ static inline void CS_UNLOCK(void __iomem *addr) } void coresight_disable_path(struct list_head *path); -int coresight_enable_path(struct list_head *path); +int coresight_enable_path(struct list_head *path, u32 mode); struct coresight_device *coresight_get_sink(struct list_head *path); struct list_head *coresight_build_path(struct coresight_device *csdev); void coresight_release_path(struct list_head *path); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index b526396d80b6..ac91b0b40ec2 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -265,7 +265,7 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) return 0; } -static int tmc_enable_sink(struct coresight_device *csdev) +static int tmc_enable_sink(struct coresight_device *csdev, u32 mode) { struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 0d8fce651ff7..71582f6dfd4c 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -70,7 +70,7 @@ static void tpiu_enable_hw(struct tpiu_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int tpiu_enable(struct coresight_device *csdev) +static int tpiu_enable(struct coresight_device *csdev, u32 mode) { struct tpiu_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 95cccb179763..6ec2b66af9ee 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -121,13 +121,13 @@ static int coresight_find_link_outport(struct coresight_device *csdev, return 0; } -static int coresight_enable_sink(struct coresight_device *csdev) +static int coresight_enable_sink(struct coresight_device *csdev, u32 mode) { int ret; if (!csdev->enable) { if (sink_ops(csdev)->enable) { - ret = sink_ops(csdev)->enable(csdev); + ret = sink_ops(csdev)->enable(csdev, mode); if (ret) return ret; } @@ -283,7 +283,7 @@ void coresight_disable_path(struct list_head *path) } } -int coresight_enable_path(struct list_head *path) +int coresight_enable_path(struct list_head *path, u32 mode) { int ret = 0; @@ -296,7 +296,7 @@ int coresight_enable_path(struct list_head *path) switch (csdev->type) { case CORESIGHT_DEV_TYPE_SINK: case CORESIGHT_DEV_TYPE_LINKSINK: - ret = coresight_enable_sink(csdev); + ret = coresight_enable_sink(csdev, mode); if (ret) goto err; break; @@ -454,7 +454,7 @@ int coresight_enable(struct coresight_device *csdev) goto out; } - ret = coresight_enable_path(path); + ret = coresight_enable_path(path, CS_MODE_SYSFS); if (ret) goto err_path; diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 6801dd64ee5d..9fa92dcdd2ea 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -186,7 +186,7 @@ struct coresight_device { * @disable: disables the sink. */ struct coresight_ops_sink { - int (*enable)(struct coresight_device *csdev); + int (*enable)(struct coresight_device *csdev, u32 mode); void (*disable)(struct coresight_device *csdev); }; From b18cfc50970dfa87b6f90b544f8b2be520825aa6 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:52:00 -0700 Subject: [PATCH 176/656] coresight: etb10: implementing AUX API Adding an ETB10 specific AUX area operations to be used by the perf framework when events are initialised. Part of this operation involves modeling the mmap'ed area based on the specific ways a sink buffer gathers information. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 2997aa4063d97fdb39450c6078bd81a7b0504f22) --- drivers/hwtracing/coresight/coresight-etb10.c | 234 ++++++++++++++++++ include/linux/coresight.h | 21 +- 2 files changed, 253 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 79099f95ba3f..a2eb6bdeaafa 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -28,6 +28,11 @@ #include #include #include +#include +#include +#include + +#include #include "coresight-priv.h" @@ -64,6 +69,26 @@ #define ETB_FFSR_BIT 1 #define ETB_FRAME_SIZE_WORDS 4 +/** + * struct cs_buffer - keep track of a recording session' specifics + * @cur: index of the current buffer + * @nr_pages: max number of pages granted to us + * @offset: offset within the current buffer + * @data_size: how much we collected in this run + * @lost: other than zero if we had a HW buffer wrap around + * @snapshot: is this run in snapshot mode + * @data_pages: a handle the ring buffer + */ +struct cs_buffers { + unsigned int cur; + unsigned int nr_pages; + unsigned long offset; + local_t data_size; + local_t lost; + bool snapshot; + void **data_pages; +}; + /** * struct etb_drvdata - specifics associated to an ETB component * @base: memory mapped base address for this component. @@ -265,9 +290,218 @@ static void etb_disable(struct coresight_device *csdev) dev_info(drvdata->dev, "ETB disabled\n"); } +static void *etb_alloc_buffer(struct coresight_device *csdev, int cpu, + void **pages, int nr_pages, bool overwrite) +{ + int node; + struct cs_buffers *buf; + + if (cpu == -1) + cpu = smp_processor_id(); + node = cpu_to_node(cpu); + + buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); + if (!buf) + return NULL; + + buf->snapshot = overwrite; + buf->nr_pages = nr_pages; + buf->data_pages = pages; + + return buf; +} + +static void etb_free_buffer(void *config) +{ + struct cs_buffers *buf = config; + + kfree(buf); +} + +static int etb_set_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int ret = 0; + unsigned long head; + struct cs_buffers *buf = sink_config; + + /* wrap head around to the amount of space we have */ + head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); + + /* find the page to write to */ + buf->cur = head / PAGE_SIZE; + + /* and offset within that page */ + buf->offset = head % PAGE_SIZE; + + local_set(&buf->data_size, 0); + + return ret; +} + +static unsigned long etb_reset_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config, bool *lost) +{ + unsigned long size = 0; + struct cs_buffers *buf = sink_config; + + if (buf) { + /* + * In snapshot mode ->data_size holds the new address of the + * ring buffer's head. The size itself is the whole address + * range since we want the latest information. + */ + if (buf->snapshot) + handle->head = local_xchg(&buf->data_size, + buf->nr_pages << PAGE_SHIFT); + + /* + * Tell the tracer PMU how much we got in this run and if + * something went wrong along the way. Nobody else can use + * this cs_buffers instance until we are done. As such + * resetting parameters here and squaring off with the ring + * buffer API in the tracer PMU is fine. + */ + *lost = !!local_xchg(&buf->lost, 0); + size = local_xchg(&buf->data_size, 0); + } + + return size; +} + +static void etb_update_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int i, cur; + u8 *buf_ptr; + u32 read_ptr, write_ptr, capacity; + u32 status, read_data, to_read; + unsigned long offset; + struct cs_buffers *buf = sink_config; + struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (!buf) + return; + + capacity = drvdata->buffer_depth * ETB_FRAME_SIZE_WORDS; + + CS_UNLOCK(drvdata->base); + etb_disable_hw(drvdata); + + /* unit is in words, not bytes */ + read_ptr = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); + write_ptr = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER); + + /* + * Entries should be aligned to the frame size. If they are not + * go back to the last alignement point to give decoding tools a + * chance to fix things. + */ + if (write_ptr % ETB_FRAME_SIZE_WORDS) { + dev_err(drvdata->dev, + "write_ptr: %lu not aligned to formatter frame size\n", + (unsigned long)write_ptr); + + write_ptr &= ~(ETB_FRAME_SIZE_WORDS - 1); + local_inc(&buf->lost); + } + + /* + * Get a hold of the status register and see if a wrap around + * has occurred. If so adjust things accordingly. Otherwise + * start at the beginning and go until the write pointer has + * been reached. + */ + status = readl_relaxed(drvdata->base + ETB_STATUS_REG); + if (status & ETB_STATUS_RAM_FULL) { + local_inc(&buf->lost); + to_read = capacity; + read_ptr = write_ptr; + } else { + to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->buffer_depth); + to_read *= ETB_FRAME_SIZE_WORDS; + } + + /* + * Make sure we don't overwrite data that hasn't been consumed yet. + * It is entirely possible that the HW buffer has more data than the + * ring buffer can currently handle. If so adjust the start address + * to take only the last traces. + * + * In snapshot mode we are looking to get the latest traces only and as + * such, we don't care about not overwriting data that hasn't been + * processed by user space. + */ + if (!buf->snapshot && to_read > handle->size) { + u32 mask = ~(ETB_FRAME_SIZE_WORDS - 1); + + /* The new read pointer must be frame size aligned */ + to_read -= handle->size & mask; + /* + * Move the RAM read pointer up, keeping in mind that + * everything is in frame size units. + */ + read_ptr = (write_ptr + drvdata->buffer_depth) - + to_read / ETB_FRAME_SIZE_WORDS; + /* Wrap around if need be*/ + read_ptr &= ~(drvdata->buffer_depth - 1); + /* let the decoder know we've skipped ahead */ + local_inc(&buf->lost); + } + + /* finally tell HW where we want to start reading from */ + writel_relaxed(read_ptr, drvdata->base + ETB_RAM_READ_POINTER); + + cur = buf->cur; + offset = buf->offset; + for (i = 0; i < to_read; i += 4) { + buf_ptr = buf->data_pages[cur] + offset; + read_data = readl_relaxed(drvdata->base + + ETB_RAM_READ_DATA_REG); + *buf_ptr++ = read_data >> 0; + *buf_ptr++ = read_data >> 8; + *buf_ptr++ = read_data >> 16; + *buf_ptr++ = read_data >> 24; + + offset += 4; + if (offset >= PAGE_SIZE) { + offset = 0; + cur++; + /* wrap around at the end of the buffer */ + cur &= buf->nr_pages - 1; + } + } + + /* reset ETB buffer for next run */ + writel_relaxed(0x0, drvdata->base + ETB_RAM_READ_POINTER); + writel_relaxed(0x0, drvdata->base + ETB_RAM_WRITE_POINTER); + + /* + * In snapshot mode all we have to do is communicate to + * perf_aux_output_end() the address of the current head. In full + * trace mode the same function expects a size to move rb->aux_head + * forward. + */ + if (buf->snapshot) + local_set(&buf->data_size, (cur * PAGE_SIZE) + offset); + else + local_add(to_read, &buf->data_size); + + etb_enable_hw(drvdata); + CS_LOCK(drvdata->base); +} + static const struct coresight_ops_sink etb_sink_ops = { .enable = etb_enable, .disable = etb_disable, + .alloc_buffer = etb_alloc_buffer, + .free_buffer = etb_free_buffer, + .set_buffer = etb_set_buffer, + .reset_buffer = etb_reset_buffer, + .update_buffer = etb_update_buffer, }; static const struct coresight_ops etb_cs_ops = { diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 9fa92dcdd2ea..385d62e64abb 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -182,12 +182,29 @@ struct coresight_device { /** * struct coresight_ops_sink - basic operations for a sink * Operations available for sinks - * @enable: enables the sink. - * @disable: disables the sink. + * @enable: enables the sink. + * @disable: disables the sink. + * @alloc_buffer: initialises perf's ring buffer for trace collection. + * @free_buffer: release memory allocated in @get_config. + * @set_buffer: initialises buffer mechanic before a trace session. + * @reset_buffer: finalises buffer mechanic after a trace session. + * @update_buffer: update buffer pointers after a trace session. */ struct coresight_ops_sink { int (*enable)(struct coresight_device *csdev, u32 mode); void (*disable)(struct coresight_device *csdev); + void *(*alloc_buffer)(struct coresight_device *csdev, int cpu, + void **pages, int nr_pages, bool overwrite); + void (*free_buffer)(void *config); + int (*set_buffer)(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config); + unsigned long (*reset_buffer)(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config, bool *lost); + void (*update_buffer)(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config); }; /** From fbeb6125a14d179f9431524d8502af63ef7afe10 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:52:01 -0700 Subject: [PATCH 177/656] coresight: etm-perf: new PMU driver for ETM tracers Perf is a well known and used tool for performance monitoring and much more. A such it is an ideal candidate for integration with coresight based HW tracing. This patch introduces a PMU that represent a coresight tracer to the Perf core. Cc: Alexander Shishkin Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 0bcbf2e30ff2271b54f54c8697a185f7d86ec6e4) --- drivers/hwtracing/coresight/Makefile | 3 +- .../hwtracing/coresight/coresight-etm-perf.c | 393 ++++++++++++++++++ .../hwtracing/coresight/coresight-etm-perf.h | 32 ++ drivers/hwtracing/coresight/coresight-etm3x.c | 7 + include/linux/coresight-pmu.h | 27 ++ 5 files changed, 461 insertions(+), 1 deletion(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.c create mode 100644 drivers/hwtracing/coresight/coresight-etm-perf.h create mode 100644 include/linux/coresight-pmu.h diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 233d66cf22d3..cf8c6d689747 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ coresight-replicator.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ - coresight-etm3x-sysfs.o + coresight-etm3x-sysfs.o \ + coresight-etm-perf.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c new file mode 100644 index 000000000000..36153a77e982 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -0,0 +1,393 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coresight-priv.h" + +static struct pmu etm_pmu; +static bool etm_perf_up; + +/** + * struct etm_event_data - Coresight specifics associated to an event + * @work: Handle to free allocated memory outside IRQ context. + * @mask: Hold the CPU(s) this event was set for. + * @snk_config: The sink configuration. + * @path: An array of path, each slot for one CPU. + */ +struct etm_event_data { + struct work_struct work; + cpumask_t mask; + void *snk_config; + struct list_head **path; +}; + +static DEFINE_PER_CPU(struct perf_output_handle, ctx_handle); +static DEFINE_PER_CPU(struct coresight_device *, csdev_src); + +/* ETMv3.5/PTM's ETMCR is 'config' */ +PMU_FORMAT_ATTR(cycacc, "config:" __stringify(ETM_OPT_CYCACC)); +PMU_FORMAT_ATTR(timestamp, "config:" __stringify(ETM_OPT_TS)); + +static struct attribute *etm_config_formats_attr[] = { + &format_attr_cycacc.attr, + &format_attr_timestamp.attr, + NULL, +}; + +static struct attribute_group etm_pmu_format_group = { + .name = "format", + .attrs = etm_config_formats_attr, +}; + +static const struct attribute_group *etm_pmu_attr_groups[] = { + &etm_pmu_format_group, + NULL, +}; + +static void etm_event_read(struct perf_event *event) {} + +static int etm_event_init(struct perf_event *event) +{ + if (event->attr.type != etm_pmu.type) + return -ENOENT; + + return 0; +} + +static void free_event_data(struct work_struct *work) +{ + int cpu; + cpumask_t *mask; + struct etm_event_data *event_data; + struct coresight_device *sink; + + event_data = container_of(work, struct etm_event_data, work); + mask = &event_data->mask; + /* + * First deal with the sink configuration. See comment in + * etm_setup_aux() about why we take the first available path. + */ + if (event_data->snk_config) { + cpu = cpumask_first(mask); + sink = coresight_get_sink(event_data->path[cpu]); + if (sink_ops(sink)->free_buffer) + sink_ops(sink)->free_buffer(event_data->snk_config); + } + + for_each_cpu(cpu, mask) { + if (event_data->path[cpu]) + coresight_release_path(event_data->path[cpu]); + } + + kfree(event_data->path); + kfree(event_data); +} + +static void *alloc_event_data(int cpu) +{ + int size; + cpumask_t *mask; + struct etm_event_data *event_data; + + /* First get memory for the session's data */ + event_data = kzalloc(sizeof(struct etm_event_data), GFP_KERNEL); + if (!event_data) + return NULL; + + /* Make sure nothing disappears under us */ + get_online_cpus(); + size = num_online_cpus(); + + mask = &event_data->mask; + if (cpu != -1) + cpumask_set_cpu(cpu, mask); + else + cpumask_copy(mask, cpu_online_mask); + put_online_cpus(); + + /* + * Each CPU has a single path between source and destination. As such + * allocate an array using CPU numbers as indexes. That way a path + * for any CPU can easily be accessed at any given time. We proceed + * the same way for sessions involving a single CPU. The cost of + * unused memory when dealing with single CPU trace scenarios is small + * compared to the cost of searching through an optimized array. + */ + event_data->path = kcalloc(size, + sizeof(struct list_head *), GFP_KERNEL); + if (!event_data->path) { + kfree(event_data); + return NULL; + } + + return event_data; +} + +static void etm_free_aux(void *data) +{ + struct etm_event_data *event_data = data; + + schedule_work(&event_data->work); +} + +static void *etm_setup_aux(int event_cpu, void **pages, + int nr_pages, bool overwrite) +{ + int cpu; + cpumask_t *mask; + struct coresight_device *sink; + struct etm_event_data *event_data = NULL; + + event_data = alloc_event_data(event_cpu); + if (!event_data) + return NULL; + + INIT_WORK(&event_data->work, free_event_data); + + mask = &event_data->mask; + + /* Setup the path for each CPU in a trace session */ + for_each_cpu(cpu, mask) { + struct coresight_device *csdev; + + csdev = per_cpu(csdev_src, cpu); + if (!csdev) + goto err; + + /* + * Building a path doesn't enable it, it simply builds a + * list of devices from source to sink that can be + * referenced later when the path is actually needed. + */ + event_data->path[cpu] = coresight_build_path(csdev); + if (!event_data->path[cpu]) + goto err; + } + + /* + * In theory nothing prevent tracers in a trace session from being + * associated with different sinks, nor having a sink per tracer. But + * until we have HW with this kind of topology and a way to convey + * sink assignement from the perf cmd line we need to assume tracers + * in a trace session are using the same sink. Therefore pick the sink + * found at the end of the first available path. + */ + cpu = cpumask_first(mask); + /* Grab the sink at the end of the path */ + sink = coresight_get_sink(event_data->path[cpu]); + if (!sink) + goto err; + + if (!sink_ops(sink)->alloc_buffer) + goto err; + + /* Get the AUX specific data from the sink buffer */ + event_data->snk_config = + sink_ops(sink)->alloc_buffer(sink, cpu, pages, + nr_pages, overwrite); + if (!event_data->snk_config) + goto err; + +out: + return event_data; + +err: + etm_free_aux(event_data); + event_data = NULL; + goto out; +} + +static void etm_event_start(struct perf_event *event, int flags) +{ + int cpu = smp_processor_id(); + struct etm_event_data *event_data; + struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle); + struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); + + if (!csdev) + goto fail; + + /* + * Deal with the ring buffer API and get a handle on the + * session's information. + */ + event_data = perf_aux_output_begin(handle, event); + if (!event_data) + goto fail; + + /* We need a sink, no need to continue without one */ + sink = coresight_get_sink(event_data->path[cpu]); + if (WARN_ON_ONCE(!sink || !sink_ops(sink)->set_buffer)) + goto fail_end_stop; + + /* Configure the sink */ + if (sink_ops(sink)->set_buffer(sink, handle, + event_data->snk_config)) + goto fail_end_stop; + + /* Nothing will happen without a path */ + if (coresight_enable_path(event_data->path[cpu], CS_MODE_PERF)) + goto fail_end_stop; + + /* Tell the perf core the event is alive */ + event->hw.state = 0; + + /* Finally enable the tracer */ + if (source_ops(csdev)->enable(csdev, &event->attr, CS_MODE_PERF)) + goto fail_end_stop; + +out: + return; + +fail_end_stop: + perf_aux_output_end(handle, 0, true); +fail: + event->hw.state = PERF_HES_STOPPED; + goto out; +} + +static void etm_event_stop(struct perf_event *event, int mode) +{ + bool lost; + int cpu = smp_processor_id(); + unsigned long size; + struct coresight_device *sink, *csdev = per_cpu(csdev_src, cpu); + struct perf_output_handle *handle = this_cpu_ptr(&ctx_handle); + struct etm_event_data *event_data = perf_get_aux(handle); + + if (event->hw.state == PERF_HES_STOPPED) + return; + + if (!csdev) + return; + + sink = coresight_get_sink(event_data->path[cpu]); + if (!sink) + return; + + /* stop tracer */ + source_ops(csdev)->disable(csdev); + + /* tell the core */ + event->hw.state = PERF_HES_STOPPED; + + if (mode & PERF_EF_UPDATE) { + if (WARN_ON_ONCE(handle->event != event)) + return; + + /* update trace information */ + if (!sink_ops(sink)->update_buffer) + return; + + sink_ops(sink)->update_buffer(sink, handle, + event_data->snk_config); + + if (!sink_ops(sink)->reset_buffer) + return; + + size = sink_ops(sink)->reset_buffer(sink, handle, + event_data->snk_config, + &lost); + + perf_aux_output_end(handle, size, lost); + } + + /* Disabling the path make its elements available to other sessions */ + coresight_disable_path(event_data->path[cpu]); +} + +static int etm_event_add(struct perf_event *event, int mode) +{ + int ret = 0; + struct hw_perf_event *hwc = &event->hw; + + if (mode & PERF_EF_START) { + etm_event_start(event, 0); + if (hwc->state & PERF_HES_STOPPED) + ret = -EINVAL; + } else { + hwc->state = PERF_HES_STOPPED; + } + + return ret; +} + +static void etm_event_del(struct perf_event *event, int mode) +{ + etm_event_stop(event, PERF_EF_UPDATE); +} + +int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ + char entry[sizeof("cpu9999999")]; + int ret = 0, cpu = source_ops(csdev)->cpu_id(csdev); + struct device *pmu_dev = etm_pmu.dev; + struct device *cs_dev = &csdev->dev; + + sprintf(entry, "cpu%d", cpu); + + if (!etm_perf_up) + return -EPROBE_DEFER; + + if (link) { + ret = sysfs_create_link(&pmu_dev->kobj, &cs_dev->kobj, entry); + if (ret) + return ret; + per_cpu(csdev_src, cpu) = csdev; + } else { + sysfs_remove_link(&pmu_dev->kobj, entry); + per_cpu(csdev_src, cpu) = NULL; + } + + return 0; +} + +static int __init etm_perf_init(void) +{ + int ret; + + etm_pmu.capabilities = PERF_PMU_CAP_EXCLUSIVE; + + etm_pmu.attr_groups = etm_pmu_attr_groups; + etm_pmu.task_ctx_nr = perf_sw_context; + etm_pmu.read = etm_event_read; + etm_pmu.event_init = etm_event_init; + etm_pmu.setup_aux = etm_setup_aux; + etm_pmu.free_aux = etm_free_aux; + etm_pmu.start = etm_event_start; + etm_pmu.stop = etm_event_stop; + etm_pmu.add = etm_event_add; + etm_pmu.del = etm_event_del; + + ret = perf_pmu_register(&etm_pmu, CORESIGHT_ETM_PMU_NAME, -1); + if (ret == 0) + etm_perf_up = true; + + return ret; +} +module_init(etm_perf_init); diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.h b/drivers/hwtracing/coresight/coresight-etm-perf.h new file mode 100644 index 000000000000..87f5a134eb6f --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm-perf.h @@ -0,0 +1,32 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _CORESIGHT_ETM_PERF_H +#define _CORESIGHT_ETM_PERF_H + +struct coresight_device; + +#ifdef CONFIG_CORESIGHT +int etm_perf_symlink(struct coresight_device *csdev, bool link); + +#else +static inline int etm_perf_symlink(struct coresight_device *csdev, bool link) +{ return -EINVAL; } + +#endif /* CONFIG_CORESIGHT */ + +#endif diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index a9b820ec16aa..77b37413803f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -35,6 +35,7 @@ #include #include "coresight-etm.h" +#include "coresight-etm-perf.h" static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -827,6 +828,12 @@ static int etm_probe(struct amba_device *adev, const struct amba_id *id) goto err_arch_supported; } + ret = etm_perf_symlink(drvdata->csdev, true); + if (ret) { + coresight_unregister(drvdata->csdev); + goto err_arch_supported; + } + pm_runtime_put(&adev->dev); dev_info(dev, "%s initialized\n", (char *)id->data); diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h new file mode 100644 index 000000000000..6c5386b23b10 --- /dev/null +++ b/include/linux/coresight-pmu.h @@ -0,0 +1,27 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _LINUX_CORESIGHT_PMU_H +#define _LINUX_CORESIGHT_PMU_H + +#define CORESIGHT_ETM_PMU_NAME "cs_etm" + +/* ETMv3.5/PTM's ETMCR config bit */ +#define ETM_OPT_CYCACC 12 +#define ETM_OPT_TS 28 + +#endif From c8ec21bd630e8a56fa811c6542c466b1bdc565ac Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Wed, 17 Feb 2016 17:52:02 -0700 Subject: [PATCH 178/656] coresight: introducing a global trace ID function TraceID values have to be unique for all tracers and consistent between drivers and user space. As such introducing a central function to be used whenever a traceID value is required. The patch also account for data traceIDs, which are usually I(N) + 1. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 17534ceb835a1a96eb921a2a80df168723d6570a) --- drivers/hwtracing/coresight/coresight-etm3x.c | 7 ++----- include/linux/coresight-pmu.h | 12 ++++++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 77b37413803f..0ba1a3981373 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -740,11 +741,7 @@ static void etm_init_arch_data(void *info) static void etm_init_trace_id(struct etm_drvdata *drvdata) { - /* - * A trace ID of value 0 is invalid, so let's start at some - * random value that fits in 7 bits and go from there. - */ - drvdata->traceid = 0x10 + drvdata->cpu; + drvdata->traceid = coresight_get_trace_id(drvdata->cpu); } static int etm_probe(struct amba_device *adev, const struct amba_id *id) diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h index 6c5386b23b10..7d410260661b 100644 --- a/include/linux/coresight-pmu.h +++ b/include/linux/coresight-pmu.h @@ -19,9 +19,21 @@ #define _LINUX_CORESIGHT_PMU_H #define CORESIGHT_ETM_PMU_NAME "cs_etm" +#define CORESIGHT_ETM_PMU_SEED 0x10 /* ETMv3.5/PTM's ETMCR config bit */ #define ETM_OPT_CYCACC 12 #define ETM_OPT_TS 28 +static inline int coresight_get_trace_id(int cpu) +{ + /* + * A trace ID of value 0 is invalid, so let's start at some + * random value that fits in 7 bits and go from there. Since + * the common convention is to have data trace IDs be I(N) + 1, + * set instruction trace IDs as a function of the CPU number. + */ + return (CORESIGHT_ETM_PMU_SEED + (cpu * 2)); +} + #endif From a70af6f83fda06ff263fb38e79081cc86e3e8fff Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 17 Feb 2016 17:52:03 -0700 Subject: [PATCH 179/656] drivers/hwtracing: make coresight-* explicitly non-modular None of the Kconfig currently controlling compilation of any of the files here are tristate, meaning that none of it currently is being built as a module by anyone. We need not be concerned about .remove functions and blocking the unbind sysfs operations, since that was already done in a recent commit. Lets remove any remaining modular references, so that when reading the drivers there is no doubt they are builtin-only. All drivers get mostly the same changes, so they are handled in batch. Changes are (1) convert to builtin_amba_driver, (2) delete module.h include where unused, and (3) relocate the description into the comments so we don't need MODULE_DESCRIPTION and associated tags. The etm3x and etm4x use module_param_named, and have been adjusted to just include moduleparam.h for that purpose. In commit f309d4443130bf814e991f836e919dca22df37ae ("platform_device: better support builtin boilerplate avoidance") we introduced the builtin_driver macro. Here we use that support and extend it to amba driver registration, so where a driver is clearly non-modular and builtin-only, we can update with the simple mapping of module_amba_driver(...) ---> builtin_amba_driver(...) Since module_amba_driver() uses the same init level priority as builtin_amba_driver() the init ordering remains unchanged with this commit. Cc: Mathieu Poirier Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Paul Gortmaker Signed-off-by: Mathieu Poirier Acked-by: Russell King Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 941943cf519f7cacbbcecee5c4ef4b77b466bd5c) --- drivers/hwtracing/coresight/coresight-etb10.c | 9 +++------ drivers/hwtracing/coresight/coresight-etm3x.c | 14 ++++++++------ drivers/hwtracing/coresight/coresight-etm4x.c | 4 +--- drivers/hwtracing/coresight/coresight-funnel.c | 9 +++------ .../coresight/coresight-replicator-qcom.c | 4 +--- drivers/hwtracing/coresight/coresight-replicator.c | 7 ++----- drivers/hwtracing/coresight/coresight-tmc.c | 9 +++------ drivers/hwtracing/coresight/coresight-tpiu.c | 9 +++------ drivers/hwtracing/coresight/coresight.c | 3 --- drivers/hwtracing/coresight/of_coresight.c | 1 - include/linux/amba/bus.h | 9 +++++++++ 11 files changed, 33 insertions(+), 45 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index a2eb6bdeaafa..acbce79934d6 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Embedded Trace Buffer driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -12,7 +14,6 @@ #include #include -#include #include #include #include @@ -781,8 +782,4 @@ static struct amba_driver etb_driver = { .probe = etb_probe, .id_table = etb_ids, }; - -module_amba_driver(etb_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Embedded Trace Buffer driver"); +builtin_amba_driver(etb_driver); diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 0ba1a3981373..d83ab82672e4 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Program Flow Trace driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,7 @@ */ #include -#include +#include #include #include #include @@ -38,6 +40,10 @@ #include "coresight-etm.h" #include "coresight-etm-perf.h" +/* + * Not really modular but using module_param is the easiest way to + * remain consistent with existing use cases for now. + */ static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -912,8 +918,4 @@ static struct amba_driver etm_driver = { .probe = etm_probe, .id_table = etm_ids, }; - -module_amba_driver(etm_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Program Flow Trace driver"); +builtin_amba_driver(etm_driver); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index d0169ba7fbf2..1c59bd36834c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -2710,5 +2709,4 @@ static struct amba_driver etm4x_driver = { .probe = etm4_probe, .id_table = etm4_ids, }; - -module_amba_driver(etm4x_driver); +builtin_amba_driver(etm4x_driver); diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index fb2c679fbc44..0600ca30649d 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Funnel driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -268,8 +269,4 @@ static struct amba_driver funnel_driver = { .probe = funnel_probe, .id_table = funnel_ids, }; - -module_amba_driver(funnel_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Funnel driver"); +builtin_amba_driver(funnel_driver); diff --git a/drivers/hwtracing/coresight/coresight-replicator-qcom.c b/drivers/hwtracing/coresight/coresight-replicator-qcom.c index 286f90b50989..700f710e4bfa 100644 --- a/drivers/hwtracing/coresight/coresight-replicator-qcom.c +++ b/drivers/hwtracing/coresight/coresight-replicator-qcom.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -198,5 +197,4 @@ static struct amba_driver replicator_driver = { .probe = replicator_probe, .id_table = replicator_ids, }; - -module_amba_driver(replicator_driver); +builtin_amba_driver(replicator_driver); diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 0ce98903992c..4299c0569340 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Replicator driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -166,8 +167,4 @@ static struct platform_driver replicator_driver = { .suppress_bind_attrs = true, }, }; - builtin_platform_driver(replicator_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Replicator driver"); diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index ac91b0b40ec2..1be191f5d39c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -1,4 +1,6 @@ /* Copyright (c) 2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Trace Memory Controller driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -782,8 +783,4 @@ static struct amba_driver tmc_driver = { .probe = tmc_probe, .id_table = tmc_ids, }; - -module_amba_driver(tmc_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Trace Memory Controller driver"); +builtin_amba_driver(tmc_driver); diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 71582f6dfd4c..8fb09d9237ab 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -1,4 +1,6 @@ /* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved. + * + * Description: CoreSight Trace Port Interface Unit driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,7 +13,6 @@ */ #include -#include #include #include #include @@ -218,8 +219,4 @@ static struct amba_driver tpiu_driver = { .probe = tpiu_probe, .id_table = tpiu_ids, }; - -module_amba_driver(tpiu_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("CoreSight Trace Port Interface Unit driver"); +builtin_amba_driver(tpiu_driver); diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 6ec2b66af9ee..2ea5961092c1 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -11,7 +11,6 @@ */ #include -#include #include #include #include @@ -894,5 +893,3 @@ void coresight_unregister(struct coresight_device *csdev) device_unregister(&csdev->dev); } EXPORT_SYMBOL_GPL(coresight_unregister); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwtracing/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c index 3cc57c1e3b5d..b68da1888fd5 100644 --- a/drivers/hwtracing/coresight/of_coresight.c +++ b/drivers/hwtracing/coresight/of_coresight.c @@ -10,7 +10,6 @@ * GNU General Public License for more details. */ -#include #include #include #include diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h index 9006c4e75cf7..3d8dcdd1aeae 100644 --- a/include/linux/amba/bus.h +++ b/include/linux/amba/bus.h @@ -163,4 +163,13 @@ struct amba_device name##_device = { \ #define module_amba_driver(__amba_drv) \ module_driver(__amba_drv, amba_driver_register, amba_driver_unregister) +/* + * builtin_amba_driver() - Helper macro for drivers that don't do anything + * special in driver initcall. This eliminates a lot of boilerplate. Each + * driver may only use this macro once, and calling it replaces the instance + * device_initcall(). + */ +#define builtin_amba_driver(__amba_drv) \ + builtin_driver(__amba_drv, amba_driver_register) + #endif From 409ce7ce900fdeef6181cd64b4566829c5ae23a4 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Sat, 27 Feb 2016 15:21:47 -0500 Subject: [PATCH 180/656] drivers/hwtracing: make coresight-etm-perf.c explicitly non-modular In commit 941943cf519f7cacbbcecee5c4ef4b77b466bd5c ("drivers/hwtracing: make coresight-* explicitly non-modular") we removed all uses of modular functions/macros in favour of their built-in equivlents in this subsystem. However that commit and commit 0bcbf2e30ff2271b54f54c8697a185f7d86ec6e4 ("coresight: etm-perf: new PMU driver for ETM tracers") were in flight at the same time, and hence one new non-modular user of module_init crept back in. Fix it up like we did all the others. Since module_init translates to device_initcall in the non-modular case, the init ordering remains unchanged with this commit. Cc: Alexander Shishkin Signed-off-by: Paul Gortmaker Acked-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ca48fa22c3ed3b7b062bc6fa7b72493c00571e33) --- drivers/hwtracing/coresight/coresight-etm-perf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm-perf.c b/drivers/hwtracing/coresight/coresight-etm-perf.c index 36153a77e982..755125f7917f 100644 --- a/drivers/hwtracing/coresight/coresight-etm-perf.c +++ b/drivers/hwtracing/coresight/coresight-etm-perf.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -390,4 +390,4 @@ static int __init etm_perf_init(void) return ret; } -module_init(etm_perf_init); +device_initcall(etm_perf_init); From b102ff2ac3a5d80863065a9c7749f9646c5ab414 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 26 Jan 2016 15:51:46 -0300 Subject: [PATCH 181/656] perf cpumap: Auto initialize cpu__max_{node,cpu} Since it was always checking if the initialization was done, use that branch to do the initialization if not done already. With this we reduce the number of exported globals from these files. Cc: Adrian Hunter Cc: Borislav Petkov Cc: David Ahern Cc: Frederic Weisbecker Cc: Jiri Olsa Cc: Kan Liang Cc: Mathieu Poirier Cc: Namhyung Kim Cc: Stephane Eranian Cc: Wang Nan Link: http://lkml.kernel.org/r/20160125212955.GG22501@kernel.org Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit 5ac76283b32b116c58e362e99542182ddcfc8262) --- tools/perf/util/cpumap.c | 30 ++++++++++++++++++++++++++++++ tools/perf/util/cpumap.h | 32 +++----------------------------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c index 10af1e7524fb..6523e1a8eea5 100644 --- a/tools/perf/util/cpumap.c +++ b/tools/perf/util/cpumap.c @@ -7,6 +7,10 @@ #include #include "asm/bug.h" +static int max_cpu_num; +static int max_node_num; +static int *cpunode_map; + static struct cpu_map *cpu_map__default_new(void) { struct cpu_map *cpus; @@ -435,6 +439,32 @@ out: pr_err("Failed to read max nodes, using default of %d\n", max_node_num); } +int cpu__max_node(void) +{ + if (unlikely(!max_node_num)) + set_max_node_num(); + + return max_node_num; +} + +int cpu__max_cpu(void) +{ + if (unlikely(!max_cpu_num)) + set_max_cpu_num(); + + return max_cpu_num; +} + +int cpu__get_node(int cpu) +{ + if (unlikely(cpunode_map == NULL)) { + pr_debug("cpu_map not initialized\n"); + return -1; + } + + return cpunode_map[cpu]; +} + static int init_cpunode_map(void) { int i; diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h index 85f7772457fa..d6184ba929b6 100644 --- a/tools/perf/util/cpumap.h +++ b/tools/perf/util/cpumap.h @@ -56,37 +56,11 @@ static inline bool cpu_map__empty(const struct cpu_map *map) return map ? map->map[0] == -1 : true; } -int max_cpu_num; -int max_node_num; -int *cpunode_map; - int cpu__setup_cpunode_map(void); -static inline int cpu__max_node(void) -{ - if (unlikely(!max_node_num)) - pr_debug("cpu_map not initialized\n"); - - return max_node_num; -} - -static inline int cpu__max_cpu(void) -{ - if (unlikely(!max_cpu_num)) - pr_debug("cpu_map not initialized\n"); - - return max_cpu_num; -} - -static inline int cpu__get_node(int cpu) -{ - if (unlikely(cpunode_map == NULL)) { - pr_debug("cpu_map not initialized\n"); - return -1; - } - - return cpunode_map[cpu]; -} +int cpu__max_node(void); +int cpu__max_cpu(void); +int cpu__get_node(int cpu); int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res, int (*f)(struct cpu_map *map, int cpu, void *data), From e8224c4aa1b5cb27809762f59c78034ddda5e23b Mon Sep 17 00:00:00 2001 From: Jiri Olsa Date: Thu, 3 Dec 2015 10:06:41 +0100 Subject: [PATCH 182/656] perf evsel: Introduce disable() method Adding perf_evsel__disable function to have complement for perf_evsel__enable function. Both will be used in following patch to factor perf_evlist__(enable|disable). Signed-off-by: Jiri Olsa Cc: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1449133606-14429-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit e98a4cbb01e0ba1110eba5166a425b3eab9b2244) --- tools/perf/util/evsel.c | 10 ++++++++++ tools/perf/util/evsel.h | 1 + 2 files changed, 11 insertions(+) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 397fb4ed3c97..1eb4d02e3968 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -988,6 +988,16 @@ int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads) 0); } +int perf_evsel__disable(struct perf_evsel *evsel) +{ + int nthreads = thread_map__nr(evsel->threads); + int ncpus = cpu_map__nr(evsel->cpus); + + return perf_evsel__run_ioctl(evsel, ncpus, nthreads, + PERF_EVENT_IOC_DISABLE, + 0); +} + int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads) { if (ncpus == 0 || nthreads == 0) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 0e49bd742c63..eaa4c733c976 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -228,6 +228,7 @@ int perf_evsel__append_filter(struct perf_evsel *evsel, int perf_evsel__apply_filter(struct perf_evsel *evsel, int ncpus, int nthreads, const char *filter); int perf_evsel__enable(struct perf_evsel *evsel, int ncpus, int nthreads); +int perf_evsel__disable(struct perf_evsel *evsel); int perf_evsel__open_per_cpu(struct perf_evsel *evsel, struct cpu_map *cpus); From 14091e83ba1e7dbfaa347b406af4d5fb9f0f81b5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 Jan 2016 10:13:59 +0100 Subject: [PATCH 183/656] perf evlist: Make perf_evlist__open() open evsels with their cpus and threads (like perf record does) 'perf record' uses perf_evsel__open() to open events and passes the evsel->cpus and evsel->threads. Many tests and some tools instead use perf_evlist__open() which passes instead evlist->cpus and evlist->threads. Make perf_evlist__open() follow the 'perf record' behaviour so that a consistent approach is taken. Signed-off-by: Adrian Hunter Cc: David Ahern Cc: Namhyung Kim Cc: Noel Grandin Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/1452158050-28061-3-git-send-email-jolsa@kernel.org Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit 23df7f798435796aff07d641456326b81cb34a77) --- tools/perf/util/evlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index d1392194a9a9..5da08efda32f 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1486,7 +1486,7 @@ int perf_evlist__open(struct perf_evlist *evlist) perf_evlist__update_id_pos(evlist); evlist__for_each(evlist, evsel) { - err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); + err = perf_evsel__open(evsel, evsel->cpus, evsel->threads); if (err < 0) goto out_err; } From 33dbaa134e0b715b07ba25a8f46027cb2a3a7d96 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 26 Jan 2016 14:05:20 +0200 Subject: [PATCH 184/656] perf tools: tracepoint_error() can receive e=NULL, robustify it Fixes segmentation fault using, for instance: (gdb) run record -I -e intel_pt/tsc=1,noretcomp=1/u /bin/ls Starting program: /home/acme/bin/perf record -I -e intel_pt/tsc=1,noretcomp=1/u /bin/ls Missing separate debuginfos, use: dnf debuginfo-install glibc-2.22-7.fc23.x86_64 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". Program received signal SIGSEGV, Segmentation fault. 0 x00000000004b9ea5 in tracepoint_error (e=0x0, err=13, sys=0x19b1370 "sched", name=0x19a5d00 "sched_switch") at util/parse-events.c:410 (gdb) bt #0 0x00000000004b9ea5 in tracepoint_error (e=0x0, err=13, sys=0x19b1370 "sched", name=0x19a5d00 "sched_switch") at util/parse-events.c:410 #1 0x00000000004b9fc5 in add_tracepoint (list=0x19a5d20, idx=0x7fffffffb8c0, sys_name=0x19b1370 "sched", evt_name=0x19a5d00 "sched_switch", err=0x0, head_config=0x0) at util/parse-events.c:433 #2 0x00000000004ba334 in add_tracepoint_event (list=0x19a5d20, idx=0x7fffffffb8c0, sys_name=0x19b1370 "sched", evt_name=0x19a5d00 "sched_switch", err=0x0, head_config=0x0) at util/parse-events.c:498 #3 0x00000000004bb699 in parse_events_add_tracepoint (list=0x19a5d20, idx=0x7fffffffb8c0, sys=0x19b1370 "sched", event=0x19a5d00 "sched_switch", err=0x0, head_config=0x0) at util/parse-events.c:936 #4 0x00000000004f6eda in parse_events_parse (_data=0x7fffffffb8b0, scanner=0x19a49d0) at util/parse-events.y:391 #5 0x00000000004bc8e5 in parse_events__scanner (str=0x663ff2 "sched:sched_switch", data=0x7fffffffb8b0, start_token=258) at util/parse-events.c:1361 #6 0x00000000004bca57 in parse_events (evlist=0x19a5220, str=0x663ff2 "sched:sched_switch", err=0x0) at util/parse-events.c:1401 #7 0x0000000000518d5f in perf_evlist__can_select_event (evlist=0x19a3b90, str=0x663ff2 "sched:sched_switch") at util/record.c:253 #8 0x0000000000553c42 in intel_pt_track_switches (evlist=0x19a3b90) at arch/x86/util/intel-pt.c:364 #9 0x00000000005549d1 in intel_pt_recording_options (itr=0x19a2c40, evlist=0x19a3b90, opts=0x8edf68 ) at arch/x86/util/intel-pt.c:664 #10 0x000000000051e076 in auxtrace_record__options (itr=0x19a2c40, evlist=0x19a3b90, opts=0x8edf68 ) at util/auxtrace.c:539 #11 0x0000000000433368 in cmd_record (argc=1, argv=0x7fffffffde60, prefix=0x0) at builtin-record.c:1264 #12 0x000000000049bec2 in run_builtin (p=0x8fa2a8 , argc=5, argv=0x7fffffffde60) at perf.c:390 #13 0x000000000049c12a in handle_internal_command (argc=5, argv=0x7fffffffde60) at perf.c:451 #14 0x000000000049c278 in run_argv (argcp=0x7fffffffdcbc, argv=0x7fffffffdcb0) at perf.c:495 #15 0x000000000049c60a in main (argc=5, argv=0x7fffffffde60) at perf.c:618 (gdb) Intel PT attempts to find the sched:sched_switch tracepoint but that seg faults if tracefs is not readable, because the error reporting structure is null, as errors are not reported when automatically adding tracepoints. Fix by checking before using. Committer note: This doesn't take place in a kernel that supports perf_event_attr.context_switch, that is the default way that will be used for tracking context switches, only in older kernels, like 4.2, in a machine with Intel PT (e.g. Broadwell) for non-priviledged users. Further info from a similar patch by Wang: The error is in tracepoint_error: it assumes the 'e' parameter is valid. However, there are many situation a parse_event() can be called without parse_events_error. See result of $ grep 'parse_events(.*NULL)' ./tools/perf/ -r' Signed-off-by: Adrian Hunter Tested-by: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Josh Poimboeuf Cc: Tong Zhang Cc: Wang Nan Cc: stable@vger.kernel.org # v4.4+ Fixes: 196581717d85 ("perf tools: Enhance parsing events tracepoint error output") Link: http://lkml.kernel.org/r/1453809921-24596-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit ec183d22cc284a7a1e17f0341219d8ec8ca070cc) --- tools/perf/util/parse-events.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6fc8cd753e1a..b48e87693aa5 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -399,6 +399,9 @@ static void tracepoint_error(struct parse_events_error *e, int err, { char help[BUFSIZ]; + if (!e) + return; + /* * We get error directly from syscall errno ( > 0), * or from encoded pointer's error ( < 0). From b020f5b0ad1d29376a56ba181a6c4839a5a448a1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 7 Mar 2016 16:44:38 -0300 Subject: [PATCH 185/656] perf inject: Hit all DSOs for AUX data in JIT and other cases Currently, when injecting build ids, if there is AUX data then 'perf inject' hits all DSOs because it is not known which DSOs the trace data would hit. That needs to be done for JIT injection also, and in fact there is no reason to distinguish what kind of injection is being done. That is, any time there is AUX data and the HEADER_BUID_ID feature flag is set, and the AUX data is not being processed, then hit all DSOs. This patch does that. Signed-off-by: Adrian Hunter Signed-off-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1457005856-6143-2-git-send-email-adrian.hunter@intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 640dad47988ec4b734d71934be103bb6e931279f) --- tools/perf/builtin-inject.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 99d127fe9c35..ac369c494036 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -626,12 +626,16 @@ static int __cmd_inject(struct perf_inject *inject) ret = perf_session__process_events(session); if (!file_out->is_pipe) { - if (inject->build_ids) { + if (inject->build_ids) perf_header__set_feat(&session->header, HEADER_BUILD_ID); - if (inject->have_auxtrace) - dsos__hit_all(session); - } + /* + * Keep all buildids when there is unprocessed AUX data because + * it is not known which ones the AUX trace hits. + */ + if (perf_header__has_feat(&session->header, HEADER_BUILD_ID) && + inject->have_auxtrace && !inject->itrace_synth_opts.set) + dsos__hit_all(session); /* * The AUX areas have been removed and replaced with * synthesized hardware events, so clear the feature flag and From 279b3b64ec953fc6eb20900134f163e831d1eb13 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 7 Mar 2016 16:44:39 -0300 Subject: [PATCH 186/656] perf session: Simplify tool stubs Some of the stubs are identical so just have one function for them. Signed-off-by: Adrian Hunter Signed-off-by: Arnaldo Carvalho de Melo Cc: Alexander Shishkin Cc: David Ahern Cc: Jiri Olsa Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Link: http://lkml.kernel.org/r/1457005856-6143-3-git-send-email-adrian.hunter@intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 5fb0ac16c5091f48eecf1a77e461f6957a463d61) --- tools/perf/util/session.c | 40 +++++++-------------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index c35ffdd360fe..d8522b84987e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -224,14 +224,6 @@ static int process_event_stub(struct perf_tool *tool __maybe_unused, return 0; } -static int process_build_id_stub(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_session *session __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - static int process_finished_round_stub(struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct ordered_events *oe __maybe_unused) @@ -244,23 +236,6 @@ static int process_finished_round(struct perf_tool *tool, union perf_event *event, struct ordered_events *oe); -static int process_id_index_stub(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_session *perf_session - __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_event_auxtrace_info_stub(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_session *session __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - static int skipn(int fd, off_t n) { char buf[4096]; @@ -287,10 +262,9 @@ static s64 process_event_auxtrace_stub(struct perf_tool *tool __maybe_unused, return event->auxtrace.size; } -static -int process_event_auxtrace_error_stub(struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_session *session __maybe_unused) +static int process_event_op2_stub(struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_session *session __maybe_unused) { dump_printf(": unhandled!\n"); return 0; @@ -331,7 +305,7 @@ void perf_tool__fill_defaults(struct perf_tool *tool) if (tool->tracing_data == NULL) tool->tracing_data = process_event_synth_tracing_data_stub; if (tool->build_id == NULL) - tool->build_id = process_build_id_stub; + tool->build_id = process_event_op2_stub; if (tool->finished_round == NULL) { if (tool->ordered_events) tool->finished_round = process_finished_round; @@ -339,13 +313,13 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->finished_round = process_finished_round_stub; } if (tool->id_index == NULL) - tool->id_index = process_id_index_stub; + tool->id_index = process_event_op2_stub; if (tool->auxtrace_info == NULL) - tool->auxtrace_info = process_event_auxtrace_info_stub; + tool->auxtrace_info = process_event_op2_stub; if (tool->auxtrace == NULL) tool->auxtrace = process_event_auxtrace_stub; if (tool->auxtrace_error == NULL) - tool->auxtrace_error = process_event_auxtrace_error_stub; + tool->auxtrace_error = process_event_op2_stub; } static void swap_sample_id_all(union perf_event *event, void *data) From 49d2bcdaf87549681057bcab36fce0ac47584a5d Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Thu, 14 Jan 2016 14:46:15 -0700 Subject: [PATCH 187/656] perf auxtrace: Add perf_evlist pointer to *info_priv_size() On some architecture the size of the private header may be dependent on the number of tracers used in the session. As such adding a "struct perf_evlist *" parameter, which should contain all the required information. Also adjusting the existing client of the interface to take the new parameter into account. Signed-off-by: Mathieu Poirier Acked-by: Adrian Hunter Cc: Al Grant Cc: Chunyan Zhang Cc: linux-arm-kernel@lists.infradead.org Cc: linux-doc@vger.kernel.org Cc: Mike Leach Cc: Peter Zijlstra Cc: Rabin Vincent Cc: Tor Jeremiassen Link: http://lkml.kernel.org/r/1452807977-8069-22-git-send-email-mathieu.poirier@linaro.org Signed-off-by: Arnaldo Carvalho de Melo (cherry picked from commit 14a05e13a044c1cd6aaa3eb1a5fcdad7b4f6c990) --- tools/perf/arch/x86/util/intel-bts.c | 4 +++- tools/perf/arch/x86/util/intel-pt.c | 4 +++- tools/perf/util/auxtrace.c | 7 ++++--- tools/perf/util/auxtrace.h | 6 ++++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c index 9b94ce520917..4685a40777cc 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c @@ -60,7 +60,9 @@ struct branch { u64 misc; }; -static size_t intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused) +static size_t +intel_bts_info_priv_size(struct auxtrace_record *itr __maybe_unused, + struct perf_evlist *evlist __maybe_unused) { return INTEL_BTS_AUXTRACE_PRIV_SIZE; } diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index b02af064f0f9..e5c1f2e21f87 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -273,7 +273,9 @@ intel_pt_pmu_default_config(struct perf_pmu *intel_pt_pmu) return attr; } -static size_t intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused) +static size_t +intel_pt_info_priv_size(struct auxtrace_record *itr __maybe_unused, + struct perf_evlist *evlist __maybe_unused) { return INTEL_PT_AUXTRACE_PRIV_SIZE; } diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index 7f10430af39c..cc1c9ce5cc56 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -478,10 +478,11 @@ void auxtrace_heap__pop(struct auxtrace_heap *heap) heap_array[last].ordinal); } -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr) +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr, + struct perf_evlist *evlist) { if (itr) - return itr->info_priv_size(itr); + return itr->info_priv_size(itr, evlist); return 0; } @@ -852,7 +853,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, int err; pr_debug2("Synthesizing auxtrace information\n"); - priv_size = auxtrace_record__info_priv_size(itr); + priv_size = auxtrace_record__info_priv_size(itr, session->evlist); ev = zalloc(sizeof(struct auxtrace_info_event) + priv_size); if (!ev) return -ENOMEM; diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index b86f90db1352..e5a8e2d4f2af 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -293,7 +293,8 @@ struct auxtrace_record { int (*recording_options)(struct auxtrace_record *itr, struct perf_evlist *evlist, struct record_opts *opts); - size_t (*info_priv_size)(struct auxtrace_record *itr); + size_t (*info_priv_size)(struct auxtrace_record *itr, + struct perf_evlist *evlist); int (*info_fill)(struct auxtrace_record *itr, struct perf_session *session, struct auxtrace_info_event *auxtrace_info, @@ -429,7 +430,8 @@ int auxtrace_parse_snapshot_options(struct auxtrace_record *itr, int auxtrace_record__options(struct auxtrace_record *itr, struct perf_evlist *evlist, struct record_opts *opts); -size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr); +size_t auxtrace_record__info_priv_size(struct auxtrace_record *itr, + struct perf_evlist *evlist); int auxtrace_record__info_fill(struct auxtrace_record *itr, struct perf_session *session, struct auxtrace_info_event *auxtrace_info, From 5025483e77155a5aa0b62728ebc9ec45355807aa Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 15:42:45 +0200 Subject: [PATCH 188/656] perf/ring_buffer: Refuse to begin AUX transaction after rb->aux_mmap_count drops When ring buffer's AUX area is unmapped and rb->aux_mmap_count drops to zero, new AUX transactions into this buffer can still be started, even though the buffer in en route to deallocation. This patch adds a check to perf_aux_output_begin() for rb->aux_mmap_count being zero, in which case there is no point starting new transactions, in other words, the ring buffers that pass a certain point in perf_mmap_close will not have their events sending new data, which clears path for freeing those buffers' pages right there and then, provided that no active transactions are holding the AUX reference. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1457098969-21595-2-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit dcb10a967ce82d5ad20570693091139ae716ff76) --- kernel/events/ring_buffer.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index 287201a5d12f..c5d0b63a148f 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -287,6 +287,13 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, if (!rb_has_aux(rb) || !atomic_inc_not_zero(&rb->aux_refcount)) goto err; + /* + * If rb::aux_mmap_count is zero (and rb_has_aux() above went through), + * the aux buffer is in perf_mmap_close(), about to get freed. + */ + if (!atomic_read(&rb->aux_mmap_count)) + goto err; + /* * Nesting is not supported for AUX area, make sure nested * writers are caught early From c5611c7b5fc21996fc8d81e0d6cbcd75ced77bbb Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Wed, 2 Dec 2015 18:41:11 +0200 Subject: [PATCH 189/656] perf/core: Free AUX pages in unmap path Now that we can ensure that when ring buffer's AUX area is on the way to getting unmapped new transactions won't start, we only need to stop all events that can potentially be writing aux data to our ring buffer. Having done that, we can safely free the AUX pages and corresponding PMU data, as this time it is guaranteed to be the last aux reference holder. This partially reverts: 57ffc5ca679 ("perf: Fix AUX buffer refcounting") ... which was made to defer deallocation that was otherwise possible from an NMI context. Now it is no longer the case; the last call to rb_free_aux() that drops the last AUX reference has to happen in perf_mmap_close() on that AUX area. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/87d1qtz23d.fsf@ashishki-desk.ger.corp.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit 95ff4ca26c492fc1ed7751f5dd7ab7674b54f4e0) --- kernel/events/core.c | 120 +++++++++++++++++++++++++++++++++++- kernel/events/internal.h | 1 - kernel/events/ring_buffer.c | 37 ++++------- 3 files changed, 129 insertions(+), 29 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index b296032a2c4e..cda4b292135a 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1884,8 +1884,13 @@ event_sched_in(struct perf_event *event, if (event->state <= PERF_EVENT_STATE_OFF) return 0; - event->state = PERF_EVENT_STATE_ACTIVE; - event->oncpu = smp_processor_id(); + WRITE_ONCE(event->oncpu, smp_processor_id()); + /* + * Order event::oncpu write to happen before the ACTIVE state + * is visible. + */ + smp_wmb(); + WRITE_ONCE(event->state, PERF_EVENT_STATE_ACTIVE); /* * Unthrottle events, since we scheduled we might have missed several @@ -2366,6 +2371,29 @@ void perf_event_enable(struct perf_event *event) } EXPORT_SYMBOL_GPL(perf_event_enable); +static int __perf_event_stop(void *info) +{ + struct perf_event *event = info; + + /* for AUX events, our job is done if the event is already inactive */ + if (READ_ONCE(event->state) != PERF_EVENT_STATE_ACTIVE) + return 0; + + /* matches smp_wmb() in event_sched_in() */ + smp_rmb(); + + /* + * There is a window with interrupts enabled before we get here, + * so we need to check again lest we try to stop another CPU's event. + */ + if (READ_ONCE(event->oncpu) != smp_processor_id()) + return -EAGAIN; + + event->pmu->stop(event, PERF_EF_UPDATE); + + return 0; +} + static int _perf_event_refresh(struct perf_event *event, int refresh) { /* @@ -4625,6 +4653,8 @@ static void perf_mmap_open(struct vm_area_struct *vma) event->pmu->event_mapped(event); } +static void perf_pmu_output_stop(struct perf_event *event); + /* * A buffer can be mmap()ed multiple times; either directly through the same * event, or through other events by use of perf_event_set_output(). @@ -4652,10 +4682,22 @@ static void perf_mmap_close(struct vm_area_struct *vma) */ if (rb_has_aux(rb) && vma->vm_pgoff == rb->aux_pgoff && atomic_dec_and_mutex_lock(&rb->aux_mmap_count, &event->mmap_mutex)) { + /* + * Stop all AUX events that are writing to this buffer, + * so that we can free its AUX pages and corresponding PMU + * data. Note that after rb::aux_mmap_count dropped to zero, + * they won't start any more (see perf_aux_output_begin()). + */ + perf_pmu_output_stop(event); + + /* now it's safe to free the pages */ atomic_long_sub(rb->aux_nr_pages, &mmap_user->locked_vm); vma->vm_mm->pinned_vm -= rb->aux_mmap_locked; + /* this has to be the last one */ rb_free_aux(rb); + WARN_ON_ONCE(atomic_read(&rb->aux_refcount)); + mutex_unlock(&event->mmap_mutex); } @@ -5726,6 +5768,80 @@ next: rcu_read_unlock(); } +struct remote_output { + struct ring_buffer *rb; + int err; +}; + +static void __perf_event_output_stop(struct perf_event *event, void *data) +{ + struct perf_event *parent = event->parent; + struct remote_output *ro = data; + struct ring_buffer *rb = ro->rb; + + if (!has_aux(event)) + return; + + if (!parent) + parent = event; + + /* + * In case of inheritance, it will be the parent that links to the + * ring-buffer, but it will be the child that's actually using it: + */ + if (rcu_dereference(parent->rb) == rb) + ro->err = __perf_event_stop(event); +} + +static int __perf_pmu_output_stop(void *info) +{ + struct perf_event *event = info; + struct pmu *pmu = event->pmu; + struct perf_cpu_context *cpuctx = get_cpu_ptr(pmu->pmu_cpu_context); + struct remote_output ro = { + .rb = event->rb, + }; + + rcu_read_lock(); + perf_event_aux_ctx(&cpuctx->ctx, __perf_event_output_stop, &ro); + if (cpuctx->task_ctx) + perf_event_aux_ctx(cpuctx->task_ctx, __perf_event_output_stop, + &ro); + rcu_read_unlock(); + + return ro.err; +} + +static void perf_pmu_output_stop(struct perf_event *event) +{ + struct perf_event *iter; + int err, cpu; + +restart: + rcu_read_lock(); + list_for_each_entry_rcu(iter, &event->rb->event_list, rb_entry) { + /* + * For per-CPU events, we need to make sure that neither they + * nor their children are running; for cpu==-1 events it's + * sufficient to stop the event itself if it's active, since + * it can't have children. + */ + cpu = iter->cpu; + if (cpu == -1) + cpu = READ_ONCE(iter->oncpu); + + if (cpu == -1) + continue; + + err = cpu_function_call(cpu, __perf_pmu_output_stop, event); + if (err == -EAGAIN) { + rcu_read_unlock(); + goto restart; + } + } + rcu_read_unlock(); +} + /* * task tracking -- fork/exit * diff --git a/kernel/events/internal.h b/kernel/events/internal.h index 2bbad9c1274c..2b229fdcfc09 100644 --- a/kernel/events/internal.h +++ b/kernel/events/internal.h @@ -11,7 +11,6 @@ struct ring_buffer { atomic_t refcount; struct rcu_head rcu_head; - struct irq_work irq_work; #ifdef CONFIG_PERF_USE_VMALLOC struct work_struct work; int page_order; /* allocation order */ diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index c5d0b63a148f..dcd21cae982c 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -221,8 +221,6 @@ void perf_output_end(struct perf_output_handle *handle) rcu_read_unlock(); } -static void rb_irq_work(struct irq_work *work); - static void ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) { @@ -243,16 +241,6 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) INIT_LIST_HEAD(&rb->event_list); spin_lock_init(&rb->event_lock); - init_irq_work(&rb->irq_work, rb_irq_work); -} - -static void ring_buffer_put_async(struct ring_buffer *rb) -{ - if (!atomic_dec_and_test(&rb->refcount)) - return; - - rb->rcu_head.next = (void *)rb; - irq_work_queue(&rb->irq_work); } /* @@ -292,7 +280,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, * the aux buffer is in perf_mmap_close(), about to get freed. */ if (!atomic_read(&rb->aux_mmap_count)) - goto err; + goto err_put; /* * Nesting is not supported for AUX area, make sure nested @@ -338,7 +326,7 @@ err_put: rb_free_aux(rb); err: - ring_buffer_put_async(rb); + ring_buffer_put(rb); handle->event = NULL; return NULL; @@ -397,7 +385,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, local_set(&rb->aux_nest, 0); rb_free_aux(rb); - ring_buffer_put_async(rb); + ring_buffer_put(rb); } /* @@ -478,6 +466,14 @@ static void __rb_free_aux(struct ring_buffer *rb) { int pg; + /* + * Should never happen, the last reference should be dropped from + * perf_mmap_close() path, which first stops aux transactions (which + * in turn are the atomic holders of aux_refcount) and then does the + * last rb_free_aux(). + */ + WARN_ON_ONCE(in_atomic()); + if (rb->aux_priv) { rb->free_aux(rb->aux_priv); rb->free_aux = NULL; @@ -589,18 +585,7 @@ out: void rb_free_aux(struct ring_buffer *rb) { if (atomic_dec_and_test(&rb->aux_refcount)) - irq_work_queue(&rb->irq_work); -} - -static void rb_irq_work(struct irq_work *work) -{ - struct ring_buffer *rb = container_of(work, struct ring_buffer, irq_work); - - if (!atomic_read(&rb->aux_refcount)) __rb_free_aux(rb); - - if (rb->rcu_head.next == (void *)rb) - call_rcu(&rb->rcu_head, rb_free_rcu); } #ifndef CONFIG_PERF_USE_VMALLOC From 84b84017f4369341bdf54054f86947043fa534ef Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 15:42:47 +0200 Subject: [PATCH 190/656] perf/ring_buffer: Document AUX API usage In order to ensure safe AUX buffer management, we rely on the assumption that pmu::stop() stops its ongoing AUX transaction and not just the hw. This patch documents this requirement for the perf_aux_output_{begin,end}() APIs. Signed-off-by: Alexander Shishkin Signed-off-by: Peter Zijlstra (Intel) Cc: Arnaldo Carvalho de Melo Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Linus Torvalds Cc: Mathieu Poirier Cc: Peter Zijlstra Cc: Stephane Eranian Cc: Thomas Gleixner Cc: Vince Weaver Cc: vince@deater.net Link: http://lkml.kernel.org/r/1457098969-21595-4-git-send-email-alexander.shishkin@linux.intel.com Signed-off-by: Ingo Molnar (cherry picked from commit af5bb4ed1254a378b6028c09e58bdcc1cd9bf5b3) --- kernel/events/ring_buffer.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index dcd21cae982c..084be7d41bcf 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -252,6 +252,10 @@ ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) * The ordering is similar to that of perf_output_{begin,end}, with * the exception of (B), which should be taken care of by the pmu * driver, since ordering rules will differ depending on hardware. + * + * Call this from pmu::start(); see the comment in perf_aux_output_end() + * about its use in pmu callbacks. Both can also be called from the PMI + * handler if needed. */ void *perf_aux_output_begin(struct perf_output_handle *handle, struct perf_event *event) @@ -323,6 +327,7 @@ void *perf_aux_output_begin(struct perf_output_handle *handle, return handle->rb->aux_priv; err_put: + /* can't be last */ rb_free_aux(rb); err: @@ -337,6 +342,10 @@ err: * aux_head and posting a PERF_RECORD_AUX into the perf buffer. It is the * pmu driver's responsibility to observe ordering rules of the hardware, * so that all the data is externally visible before this is called. + * + * Note: this has to be called from pmu::stop() callback, as the assumption + * of the AUX buffer management code is that after pmu::stop(), the AUX + * transaction must be stopped and therefore drop the AUX reference count. */ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, bool truncated) @@ -384,6 +393,7 @@ void perf_aux_output_end(struct perf_output_handle *handle, unsigned long size, handle->event = NULL; local_set(&rb->aux_nest, 0); + /* can't be last */ rb_free_aux(rb); ring_buffer_put(rb); } From de7b545d3abec0b103ec1e7df1b58b7edeecb873 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Thu, 31 Mar 2016 16:30:15 +0300 Subject: [PATCH 191/656] MAINTAINERS: Add a git tree for the stm class So that people know where their patches go. Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit e787bc463cc1fe3f51b0cd7bf540236318f69cf1) --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 233f83464814..6b4f6ac66326 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9356,6 +9356,7 @@ F: drivers/mmc/host/dw_mmc* SYSTEM TRACE MODULE CLASS M: Alexander Shishkin S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/ash/stm.git F: Documentation/trace/stm.txt F: drivers/hwtracing/stm/ F: include/linux/stm.h From 5b5ff82fe22e0417001852701b25185e5ac610c6 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:22:33 +0200 Subject: [PATCH 192/656] stm class: dummy_stm: Make nr_dummies parameter read-only Changing nr_dummies after the module has been loaded doesn't actually change anything, so just make it read-only. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit 118b4515aa6916ee7751f29c8b2a3af95abc9783) --- drivers/hwtracing/stm/dummy_stm.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/dummy_stm.c b/drivers/hwtracing/stm/dummy_stm.c index 310adf57e7a1..a86612d989f9 100644 --- a/drivers/hwtracing/stm/dummy_stm.c +++ b/drivers/hwtracing/stm/dummy_stm.c @@ -46,9 +46,7 @@ static struct stm_data dummy_stm[DUMMY_STM_MAX]; static int nr_dummies = 4; -module_param(nr_dummies, int, 0600); - -static unsigned int dummy_stm_nr; +module_param(nr_dummies, int, 0400); static unsigned int fail_mode; @@ -65,12 +63,12 @@ static int dummy_stm_link(struct stm_data *data, unsigned int master, static int dummy_stm_init(void) { - int i, ret = -ENOMEM, __nr_dummies = ACCESS_ONCE(nr_dummies); + int i, ret = -ENOMEM; - if (__nr_dummies < 0 || __nr_dummies > DUMMY_STM_MAX) + if (nr_dummies < 0 || nr_dummies > DUMMY_STM_MAX) return -EINVAL; - for (i = 0; i < __nr_dummies; i++) { + for (i = 0; i < nr_dummies; i++) { dummy_stm[i].name = kasprintf(GFP_KERNEL, "dummy_stm.%d", i); if (!dummy_stm[i].name) goto fail_unregister; @@ -86,8 +84,6 @@ static int dummy_stm_init(void) goto fail_free; } - dummy_stm_nr = __nr_dummies; - return 0; fail_unregister: @@ -105,7 +101,7 @@ static void dummy_stm_exit(void) { int i; - for (i = 0; i < dummy_stm_nr; i++) { + for (i = 0; i < nr_dummies; i++) { stm_unregister_device(&dummy_stm[i]); kfree(dummy_stm[i].name); } From 450abebc37405e8a6b0167a7aa60922315e76b4c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:22:33 +0200 Subject: [PATCH 193/656] stm class: stm_heartbeat: Make nr_devs parameter read-only Changing nr_devs after the module has been loaded doesn't actually change anything, so just make it read-only. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit c8be4899449c0b27bc5daf71742cd601b2b3b4e3) --- drivers/hwtracing/stm/heartbeat.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/hwtracing/stm/heartbeat.c b/drivers/hwtracing/stm/heartbeat.c index 0133571b506f..3da7b673aab2 100644 --- a/drivers/hwtracing/stm/heartbeat.c +++ b/drivers/hwtracing/stm/heartbeat.c @@ -26,7 +26,7 @@ static int nr_devs = 4; static int interval_ms = 10; -module_param(nr_devs, int, 0600); +module_param(nr_devs, int, 0400); module_param(interval_ms, int, 0600); static struct stm_heartbeat { @@ -35,8 +35,6 @@ static struct stm_heartbeat { unsigned int active; } stm_heartbeat[STM_HEARTBEAT_MAX]; -static unsigned int nr_instances; - static const char str[] = "heartbeat stm source driver is here to serve you"; static enum hrtimer_restart stm_heartbeat_hrtimer_handler(struct hrtimer *hr) @@ -74,12 +72,12 @@ static void stm_heartbeat_unlink(struct stm_source_data *data) static int stm_heartbeat_init(void) { - int i, ret = -ENOMEM, __nr_instances = ACCESS_ONCE(nr_devs); + int i, ret = -ENOMEM; - if (__nr_instances < 0 || __nr_instances > STM_HEARTBEAT_MAX) + if (nr_devs < 0 || nr_devs > STM_HEARTBEAT_MAX) return -EINVAL; - for (i = 0; i < __nr_instances; i++) { + for (i = 0; i < nr_devs; i++) { stm_heartbeat[i].data.name = kasprintf(GFP_KERNEL, "heartbeat.%d", i); if (!stm_heartbeat[i].data.name) @@ -98,8 +96,6 @@ static int stm_heartbeat_init(void) goto fail_free; } - nr_instances = __nr_instances; - return 0; fail_unregister: @@ -116,7 +112,7 @@ static void stm_heartbeat_exit(void) { int i; - for (i = 0; i < nr_instances; i++) { + for (i = 0; i < nr_devs; i++) { stm_source_unregister_device(&stm_heartbeat[i].data); kfree(stm_heartbeat[i].data.name); } From 5a4e9c9c9c37e7a907eccc9e112e9ecd1970b862 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:30:24 +0200 Subject: [PATCH 194/656] stm class: Remove a pointless line No point in explicitly setting something to zero right after we explicitly checked that it is zero. Fix this. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit 8fa11d1c1322f3de40a0e3f3f3e57436a204fcc4) --- drivers/hwtracing/stm/core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 0dd8f4c996f1..a6c6b2d6a67b 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -546,8 +546,6 @@ static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg) if (ret) goto err_free; - ret = 0; - if (stm->data->link) ret = stm->data->link(stm->data, stmf->output.master, stmf->output.channel); From 7c678b8584505e7005b99b4f09cca4496812d30c Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:36:10 +0200 Subject: [PATCH 195/656] stm class: Do not leak the chrdev in error path Currently, the error path of stm_register_device() forgets to unregister the chrdev. Fix this. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit cbe4a61d1ddc4790d950ca8c33ef79ee68ef5e2b) --- drivers/hwtracing/stm/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index a6c6b2d6a67b..0ffccfdd1017 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -688,6 +688,8 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, return 0; err_device: + unregister_chrdev(stm->major, stm_data->name); + /* matches device_initialize() above */ put_device(&stm->dev); err_free: From 134bbd594d1c21ffa97ab49d70816536369f1fb1 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:48:14 +0200 Subject: [PATCH 196/656] stm class: Fix stm device initialization order Currently, stm_register_device() makes the device visible and then proceeds to initializing spinlocks and other properties, which leaves a window when the device can already be opened but is not yet fully operational. Fix this by reversing the initialization order. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit 389b6699a2aa0b457aa69986e9ddf39f3b4030fd) --- drivers/hwtracing/stm/core.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index 0ffccfdd1017..d21eb53ba199 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -666,6 +666,18 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, stm->dev.parent = parent; stm->dev.release = stm_device_release; + mutex_init(&stm->link_mutex); + spin_lock_init(&stm->link_lock); + INIT_LIST_HEAD(&stm->link_list); + + /* initialize the object before it is accessible via sysfs */ + spin_lock_init(&stm->mc_lock); + mutex_init(&stm->policy_mutex); + stm->sw_nmasters = nmasters; + stm->owner = owner; + stm->data = stm_data; + stm_data->stm = stm; + err = kobject_set_name(&stm->dev.kobj, "%s", stm_data->name); if (err) goto err_device; @@ -674,17 +686,6 @@ int stm_register_device(struct device *parent, struct stm_data *stm_data, if (err) goto err_device; - mutex_init(&stm->link_mutex); - spin_lock_init(&stm->link_lock); - INIT_LIST_HEAD(&stm->link_list); - - spin_lock_init(&stm->mc_lock); - mutex_init(&stm->policy_mutex); - stm->sw_nmasters = nmasters; - stm->owner = owner; - stm->data = stm_data; - stm_data->stm = stm; - return 0; err_device: From 8ebbc16a38eea98885d097b14551dcc378ba0ba5 Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Fri, 4 Mar 2016 16:55:12 +0200 Subject: [PATCH 197/656] stm class: Remove unnecessary pointer increment Readability: a postfix increment is used on a pointer which is not used anywhere afterwards, which may send the reader looking through the function one extra time. Drop the unnecessary increment. Reported-by: Alan Cox Signed-off-by: Alexander Shishkin Reviewed-by: Laurent Fert (cherry picked from commit fb0801904bbbc7b109d4009520c7fa34bcfb7450) --- drivers/hwtracing/stm/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c index 1db189657b2b..1c061cb9bff0 100644 --- a/drivers/hwtracing/stm/policy.c +++ b/drivers/hwtracing/stm/policy.c @@ -342,7 +342,7 @@ stp_policies_make(struct config_group *group, const char *name) return ERR_PTR(-EINVAL); } - *p++ = '\0'; + *p = '\0'; stm = stm_find_device(devname); kfree(devname); From 1525472db066e6d9a56b8b0f6ef040b43bdc3cbe Mon Sep 17 00:00:00 2001 From: Alexander Shishkin Date: Tue, 3 May 2016 11:33:37 -0600 Subject: [PATCH 198/656] stm class: Support devices that override software assigned masters Some STM devices adjust software assigned master numbers depending on the trace source and its runtime state and whatnot. This patch adds a sysfs attribute to inform the trace-side software that master numbers assigned to software sources will not match those in the STP stream, so that, for example, master/channel allocation policy can be adjusted accordingly. Signed-off-by: Alexander Shishkin Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 8e996a2874bbbed30e8dfe881453825fc6b7654e) --- Documentation/ABI/testing/sysfs-class-stm | 10 ++++++++++ drivers/hwtracing/stm/core.c | 15 +++++++++++++++ include/linux/stm.h | 3 +++ 3 files changed, 28 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-stm b/Documentation/ABI/testing/sysfs-class-stm index c9aa4f3fc9a7..77ed3da0f68e 100644 --- a/Documentation/ABI/testing/sysfs-class-stm +++ b/Documentation/ABI/testing/sysfs-class-stm @@ -12,3 +12,13 @@ KernelVersion: 4.3 Contact: Alexander Shishkin Description: Shows the number of channels per master on this STM device. + +What: /sys/class/stm//hw_override +Date: March 2016 +KernelVersion: 4.7 +Contact: Alexander Shishkin +Description: + Reads as 0 if master numbers in the STP stream produced by + this stm device will match the master numbers assigned by + the software or 1 if the stm hardware overrides software + assigned masters. diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c index d21eb53ba199..02095410cb33 100644 --- a/drivers/hwtracing/stm/core.c +++ b/drivers/hwtracing/stm/core.c @@ -67,9 +67,24 @@ static ssize_t channels_show(struct device *dev, static DEVICE_ATTR_RO(channels); +static ssize_t hw_override_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct stm_device *stm = to_stm_device(dev); + int ret; + + ret = sprintf(buf, "%u\n", stm->data->hw_override); + + return ret; +} + +static DEVICE_ATTR_RO(hw_override); + static struct attribute *stm_attrs[] = { &dev_attr_masters.attr, &dev_attr_channels.attr, + &dev_attr_hw_override.attr, NULL, }; diff --git a/include/linux/stm.h b/include/linux/stm.h index 1a79ed8e43da..8369d8a8cabd 100644 --- a/include/linux/stm.h +++ b/include/linux/stm.h @@ -50,6 +50,8 @@ struct stm_device; * @sw_end: last STP master available to software * @sw_nchannels: number of STP channels per master * @sw_mmiosz: size of one channel's IO space, for mmap, optional + * @hw_override: masters in the STP stream will not match the ones + * assigned by software, but are up to the STM hardware * @packet: callback that sends an STP packet * @mmio_addr: mmap callback, optional * @link: called when a new stm_source gets linked to us, optional @@ -85,6 +87,7 @@ struct stm_data { unsigned int sw_end; unsigned int sw_nchannels; unsigned int sw_mmiosz; + unsigned int hw_override; ssize_t (*packet)(struct stm_data *, unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, From f069ef6e0dace0ad0b6f5975672cd5b750628eba Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:42 -0600 Subject: [PATCH 199/656] coresight: etm4x: moving sysFS entries to a dedicated file As with the etm3x driver, sysFS entries are big enough to justify their own file. As such moving all sysFS related declarations to a dedicated location. No gain/loss of functionality is incurred from this patch. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a77de2637c9eb4794c6234b40cee2a243c548875) --- drivers/hwtracing/coresight/Makefile | 3 +- .../coresight/coresight-etm4x-sysfs.c | 2048 +++++++++++++++++ drivers/hwtracing/coresight/coresight-etm4x.c | 2029 ---------------- drivers/hwtracing/coresight/coresight-etm4x.h | 2 + 4 files changed, 2052 insertions(+), 2030 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-etm4x-sysfs.c diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index cf8c6d689747..637ca978cd05 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -11,5 +11,6 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ coresight-etm3x-sysfs.o \ coresight-etm-perf.o -obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o +obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ + coresight-etm4x-sysfs.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c new file mode 100644 index 000000000000..5db0de7d4e0e --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -0,0 +1,2048 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include "coresight-etm4x.h" + +static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) +{ + u8 idx = drvdata->addr_idx; + + /* + * TRCACATRn.TYPE bit[1:0]: type of comparison + * the trace unit performs + */ + if (BMVAL(drvdata->addr_acc[idx], 0, 1) == ETM_INSTR_ADDR) { + if (idx % 2 != 0) + return -EINVAL; + + /* + * We are performing instruction address comparison. Set the + * relevant bit of ViewInst Include/Exclude Control register + * for corresponding address comparator pair. + */ + if (drvdata->addr_type[idx] != ETM_ADDR_TYPE_RANGE || + drvdata->addr_type[idx + 1] != ETM_ADDR_TYPE_RANGE) + return -EINVAL; + + if (exclude == true) { + /* + * Set exclude bit and unset the include bit + * corresponding to comparator pair + */ + drvdata->viiectlr |= BIT(idx / 2 + 16); + drvdata->viiectlr &= ~BIT(idx / 2); + } else { + /* + * Set include bit and unset exclude bit + * corresponding to comparator pair + */ + drvdata->viiectlr |= BIT(idx / 2); + drvdata->viiectlr &= ~BIT(idx / 2 + 16); + } + } + return 0; +} + +static ssize_t nr_pe_cmp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_pe_cmp; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_pe_cmp); + +static ssize_t nr_addr_cmp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_addr_cmp; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_addr_cmp); + +static ssize_t nr_cntr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_cntr; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_cntr); + +static ssize_t nr_ext_inp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_ext_inp; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_ext_inp); + +static ssize_t numcidc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->numcidc; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(numcidc); + +static ssize_t numvmidc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->numvmidc; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(numvmidc); + +static ssize_t nrseqstate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nrseqstate; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nrseqstate); + +static ssize_t nr_resource_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_resource; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_resource); + +static ssize_t nr_ss_cmp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->nr_ss_cmp; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} +static DEVICE_ATTR_RO(nr_ss_cmp); + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int i; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + if (val) + drvdata->mode = 0x0; + + /* Disable data tracing: do not trace load and store data transfers */ + drvdata->mode &= ~(ETM_MODE_LOAD | ETM_MODE_STORE); + drvdata->cfg &= ~(BIT(1) | BIT(2)); + + /* Disable data value and data address tracing */ + drvdata->mode &= ~(ETM_MODE_DATA_TRACE_ADDR | + ETM_MODE_DATA_TRACE_VAL); + drvdata->cfg &= ~(BIT(16) | BIT(17)); + + /* Disable all events tracing */ + drvdata->eventctrl0 = 0x0; + drvdata->eventctrl1 = 0x0; + + /* Disable timestamp event */ + drvdata->ts_ctrl = 0x0; + + /* Disable stalling */ + drvdata->stall_ctrl = 0x0; + + /* Reset trace synchronization period to 2^8 = 256 bytes*/ + if (drvdata->syncpr == false) + drvdata->syncfreq = 0x8; + + /* + * Enable ViewInst to trace everything with start-stop logic in + * started state. ARM recommends start-stop logic is set before + * each trace run. + */ + drvdata->vinst_ctrl |= BIT(0); + if (drvdata->nr_addr_cmp == true) { + drvdata->mode |= ETM_MODE_VIEWINST_STARTSTOP; + /* SSSTATUS, bit[9] */ + drvdata->vinst_ctrl |= BIT(9); + } + + /* No address range filtering for ViewInst */ + drvdata->viiectlr = 0x0; + + /* No start-stop filtering for ViewInst */ + drvdata->vissctlr = 0x0; + + /* Disable seq events */ + for (i = 0; i < drvdata->nrseqstate-1; i++) + drvdata->seq_ctrl[i] = 0x0; + drvdata->seq_rst = 0x0; + drvdata->seq_state = 0x0; + + /* Disable external input events */ + drvdata->ext_inp = 0x0; + + drvdata->cntr_idx = 0x0; + for (i = 0; i < drvdata->nr_cntr; i++) { + drvdata->cntrldvr[i] = 0x0; + drvdata->cntr_ctrl[i] = 0x0; + drvdata->cntr_val[i] = 0x0; + } + + drvdata->res_idx = 0x0; + for (i = 0; i < drvdata->nr_resource; i++) + drvdata->res_ctrl[i] = 0x0; + + for (i = 0; i < drvdata->nr_ss_cmp; i++) { + drvdata->ss_ctrl[i] = 0x0; + drvdata->ss_pe_cmp[i] = 0x0; + } + + drvdata->addr_idx = 0x0; + for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { + drvdata->addr_val[i] = 0x0; + drvdata->addr_acc[i] = 0x0; + drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + } + + drvdata->ctxid_idx = 0x0; + for (i = 0; i < drvdata->numcidc; i++) { + drvdata->ctxid_pid[i] = 0x0; + drvdata->ctxid_vpid[i] = 0x0; + } + + drvdata->ctxid_mask0 = 0x0; + drvdata->ctxid_mask1 = 0x0; + + drvdata->vmid_idx = 0x0; + for (i = 0; i < drvdata->numvmidc; i++) + drvdata->vmid_val[i] = 0x0; + drvdata->vmid_mask0 = 0x0; + drvdata->vmid_mask1 = 0x0; + + drvdata->trcid = drvdata->cpu + 1; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_WO(reset); + +static ssize_t mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->mode; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val, mode; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->mode = val & ETMv4_MODE_ALL; + + if (drvdata->mode & ETM_MODE_EXCLUDE) + etm4_set_mode_exclude(drvdata, true); + else + etm4_set_mode_exclude(drvdata, false); + + if (drvdata->instrp0 == true) { + /* start by clearing instruction P0 field */ + drvdata->cfg &= ~(BIT(1) | BIT(2)); + if (drvdata->mode & ETM_MODE_LOAD) + /* 0b01 Trace load instructions as P0 instructions */ + drvdata->cfg |= BIT(1); + if (drvdata->mode & ETM_MODE_STORE) + /* 0b10 Trace store instructions as P0 instructions */ + drvdata->cfg |= BIT(2); + if (drvdata->mode & ETM_MODE_LOAD_STORE) + /* + * 0b11 Trace load and store instructions + * as P0 instructions + */ + drvdata->cfg |= BIT(1) | BIT(2); + } + + /* bit[3], Branch broadcast mode */ + if ((drvdata->mode & ETM_MODE_BB) && (drvdata->trcbb == true)) + drvdata->cfg |= BIT(3); + else + drvdata->cfg &= ~BIT(3); + + /* bit[4], Cycle counting instruction trace bit */ + if ((drvdata->mode & ETMv4_MODE_CYCACC) && + (drvdata->trccci == true)) + drvdata->cfg |= BIT(4); + else + drvdata->cfg &= ~BIT(4); + + /* bit[6], Context ID tracing bit */ + if ((drvdata->mode & ETMv4_MODE_CTXID) && (drvdata->ctxid_size)) + drvdata->cfg |= BIT(6); + else + drvdata->cfg &= ~BIT(6); + + if ((drvdata->mode & ETM_MODE_VMID) && (drvdata->vmid_size)) + drvdata->cfg |= BIT(7); + else + drvdata->cfg &= ~BIT(7); + + /* bits[10:8], Conditional instruction tracing bit */ + mode = ETM_MODE_COND(drvdata->mode); + if (drvdata->trccond == true) { + drvdata->cfg &= ~(BIT(8) | BIT(9) | BIT(10)); + drvdata->cfg |= mode << 8; + } + + /* bit[11], Global timestamp tracing bit */ + if ((drvdata->mode & ETMv4_MODE_TIMESTAMP) && (drvdata->ts_size)) + drvdata->cfg |= BIT(11); + else + drvdata->cfg &= ~BIT(11); + + /* bit[12], Return stack enable bit */ + if ((drvdata->mode & ETM_MODE_RETURNSTACK) && + (drvdata->retstack == true)) + drvdata->cfg |= BIT(12); + else + drvdata->cfg &= ~BIT(12); + + /* bits[14:13], Q element enable field */ + mode = ETM_MODE_QELEM(drvdata->mode); + /* start by clearing QE bits */ + drvdata->cfg &= ~(BIT(13) | BIT(14)); + /* if supported, Q elements with instruction counts are enabled */ + if ((mode & BIT(0)) && (drvdata->q_support & BIT(0))) + drvdata->cfg |= BIT(13); + /* + * if supported, Q elements with and without instruction + * counts are enabled + */ + if ((mode & BIT(1)) && (drvdata->q_support & BIT(1))) + drvdata->cfg |= BIT(14); + + /* bit[11], AMBA Trace Bus (ATB) trigger enable bit */ + if ((drvdata->mode & ETM_MODE_ATB_TRIGGER) && + (drvdata->atbtrig == true)) + drvdata->eventctrl1 |= BIT(11); + else + drvdata->eventctrl1 &= ~BIT(11); + + /* bit[12], Low-power state behavior override bit */ + if ((drvdata->mode & ETM_MODE_LPOVERRIDE) && + (drvdata->lpoverride == true)) + drvdata->eventctrl1 |= BIT(12); + else + drvdata->eventctrl1 &= ~BIT(12); + + /* bit[8], Instruction stall bit */ + if (drvdata->mode & ETM_MODE_ISTALL_EN) + drvdata->stall_ctrl |= BIT(8); + else + drvdata->stall_ctrl &= ~BIT(8); + + /* bit[10], Prioritize instruction trace bit */ + if (drvdata->mode & ETM_MODE_INSTPRIO) + drvdata->stall_ctrl |= BIT(10); + else + drvdata->stall_ctrl &= ~BIT(10); + + /* bit[13], Trace overflow prevention bit */ + if ((drvdata->mode & ETM_MODE_NOOVERFLOW) && + (drvdata->nooverflow == true)) + drvdata->stall_ctrl |= BIT(13); + else + drvdata->stall_ctrl &= ~BIT(13); + + /* bit[9] Start/stop logic control bit */ + if (drvdata->mode & ETM_MODE_VIEWINST_STARTSTOP) + drvdata->vinst_ctrl |= BIT(9); + else + drvdata->vinst_ctrl &= ~BIT(9); + + /* bit[10], Whether a trace unit must trace a Reset exception */ + if (drvdata->mode & ETM_MODE_TRACE_RESET) + drvdata->vinst_ctrl |= BIT(10); + else + drvdata->vinst_ctrl &= ~BIT(10); + + /* bit[11], Whether a trace unit must trace a system error exception */ + if ((drvdata->mode & ETM_MODE_TRACE_ERR) && + (drvdata->trc_error == true)) + drvdata->vinst_ctrl |= BIT(11); + else + drvdata->vinst_ctrl &= ~BIT(11); + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(mode); + +static ssize_t pe_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->pe_sel; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t pe_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + if (val > drvdata->nr_pe) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + + drvdata->pe_sel = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(pe); + +static ssize_t event_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->eventctrl0; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + switch (drvdata->nr_event) { + case 0x0: + /* EVENT0, bits[7:0] */ + drvdata->eventctrl0 = val & 0xFF; + break; + case 0x1: + /* EVENT1, bits[15:8] */ + drvdata->eventctrl0 = val & 0xFFFF; + break; + case 0x2: + /* EVENT2, bits[23:16] */ + drvdata->eventctrl0 = val & 0xFFFFFF; + break; + case 0x3: + /* EVENT3, bits[31:24] */ + drvdata->eventctrl0 = val; + break; + default: + break; + } + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(event); + +static ssize_t event_instren_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = BMVAL(drvdata->eventctrl1, 0, 3); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t event_instren_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* start by clearing all instruction event enable bits */ + drvdata->eventctrl1 &= ~(BIT(0) | BIT(1) | BIT(2) | BIT(3)); + switch (drvdata->nr_event) { + case 0x0: + /* generate Event element for event 1 */ + drvdata->eventctrl1 |= val & BIT(1); + break; + case 0x1: + /* generate Event element for event 1 and 2 */ + drvdata->eventctrl1 |= val & (BIT(0) | BIT(1)); + break; + case 0x2: + /* generate Event element for event 1, 2 and 3 */ + drvdata->eventctrl1 |= val & (BIT(0) | BIT(1) | BIT(2)); + break; + case 0x3: + /* generate Event element for all 4 events */ + drvdata->eventctrl1 |= val & 0xF; + break; + default: + break; + } + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(event_instren); + +static ssize_t event_ts_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ts_ctrl; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t event_ts_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (!drvdata->ts_size) + return -EINVAL; + + drvdata->ts_ctrl = val & ETMv4_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(event_ts); + +static ssize_t syncfreq_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->syncfreq; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t syncfreq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (drvdata->syncpr == true) + return -EINVAL; + + drvdata->syncfreq = val & ETMv4_SYNC_MASK; + return size; +} +static DEVICE_ATTR_RW(syncfreq); + +static ssize_t cyc_threshold_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ccctlr; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cyc_threshold_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val < drvdata->ccitmin) + return -EINVAL; + + drvdata->ccctlr = val & ETM_CYC_THRESHOLD_MASK; + return size; +} +static DEVICE_ATTR_RW(cyc_threshold); + +static ssize_t bb_ctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->bb_ctrl; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t bb_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (drvdata->trcbb == false) + return -EINVAL; + if (!drvdata->nr_addr_cmp) + return -EINVAL; + /* + * Bit[7:0] selects which address range comparator is used for + * branch broadcast control. + */ + if (BMVAL(val, 0, 7) > drvdata->nr_addr_cmp) + return -EINVAL; + + drvdata->bb_ctrl = val; + return size; +} +static DEVICE_ATTR_RW(bb_ctrl); + +static ssize_t event_vinst_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->vinst_ctrl & ETMv4_EVENT_MASK; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t event_vinst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + val &= ETMv4_EVENT_MASK; + drvdata->vinst_ctrl &= ~ETMv4_EVENT_MASK; + drvdata->vinst_ctrl |= val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(event_vinst); + +static ssize_t s_exlevel_vinst_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = BMVAL(drvdata->vinst_ctrl, 16, 19); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t s_exlevel_vinst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* clear all EXLEVEL_S bits (bit[18] is never implemented) */ + drvdata->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19)); + /* enable instruction tracing for corresponding exception level */ + val &= drvdata->s_ex_level; + drvdata->vinst_ctrl |= (val << 16); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(s_exlevel_vinst); + +static ssize_t ns_exlevel_vinst_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + /* EXLEVEL_NS, bits[23:20] */ + val = BMVAL(drvdata->vinst_ctrl, 20, 23); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t ns_exlevel_vinst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* clear EXLEVEL_NS bits (bit[23] is never implemented */ + drvdata->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22)); + /* enable instruction tracing for corresponding exception level */ + val &= drvdata->ns_ex_level; + drvdata->vinst_ctrl |= (val << 20); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(ns_exlevel_vinst); + +static ssize_t addr_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->addr_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->nr_addr_cmp * 2) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->addr_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_idx); + +static ssize_t addr_instdatatype_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len; + u8 val, idx; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + val = BMVAL(drvdata->addr_acc[idx], 0, 1); + len = scnprintf(buf, PAGE_SIZE, "%s\n", + val == ETM_INSTR_ADDR ? "instr" : + (val == ETM_DATA_LOAD_ADDR ? "data_load" : + (val == ETM_DATA_STORE_ADDR ? "data_store" : + "data_load_store"))); + spin_unlock(&drvdata->spinlock); + return len; +} + +static ssize_t addr_instdatatype_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + char str[20] = ""; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (strlen(buf) >= 20) + return -EINVAL; + if (sscanf(buf, "%s", str) != 1) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!strcmp(str, "instr")) + /* TYPE, bits[1:0] */ + drvdata->addr_acc[idx] &= ~(BIT(0) | BIT(1)); + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_instdatatype); + +static ssize_t addr_single_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + idx = drvdata->addr_idx; + spin_lock(&drvdata->spinlock); + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + val = (unsigned long)drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_single_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = (u64)val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_single); + +static ssize_t addr_range_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val1, val2; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val1 = (unsigned long)drvdata->addr_val[idx]; + val2 = (unsigned long)drvdata->addr_val[idx + 1]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); +} + +static ssize_t addr_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val1, val2; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + /* lower address comparator cannot have a higher address value */ + if (val1 > val2) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (idx % 2 != 0) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = (u64)val1; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + drvdata->addr_val[idx + 1] = (u64)val2; + drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + /* + * Program include or exclude control bits for vinst or vdata + * whenever we change addr comparators to ETM_ADDR_TYPE_RANGE + */ + if (drvdata->mode & ETM_MODE_EXCLUDE) + etm4_set_mode_exclude(drvdata, true); + else + etm4_set_mode_exclude(drvdata, false); + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_range); + +static ssize_t addr_start_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = (unsigned long)drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_start_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!drvdata->nr_addr_cmp) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = (u64)val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; + drvdata->vissctlr |= BIT(idx); + /* SSSTATUS, bit[9] - turn on start/stop logic */ + drvdata->vinst_ctrl |= BIT(9); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_start); + +static ssize_t addr_stop_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + val = (unsigned long)drvdata->addr_val[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_stop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!drvdata->nr_addr_cmp) { + spin_unlock(&drvdata->spinlock); + return -EINVAL; + } + if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || + drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + spin_unlock(&drvdata->spinlock); + return -EPERM; + } + + drvdata->addr_val[idx] = (u64)val; + drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; + drvdata->vissctlr |= BIT(idx + 16); + /* SSSTATUS, bit[9] - turn on start/stop logic */ + drvdata->vinst_ctrl |= BIT(9); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_stop); + +static ssize_t addr_ctxtype_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len; + u8 idx, val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + /* CONTEXTTYPE, bits[3:2] */ + val = BMVAL(drvdata->addr_acc[idx], 2, 3); + len = scnprintf(buf, PAGE_SIZE, "%s\n", val == ETM_CTX_NONE ? "none" : + (val == ETM_CTX_CTXID ? "ctxid" : + (val == ETM_CTX_VMID ? "vmid" : "all"))); + spin_unlock(&drvdata->spinlock); + return len; +} + +static ssize_t addr_ctxtype_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + char str[10] = ""; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (strlen(buf) >= 10) + return -EINVAL; + if (sscanf(buf, "%s", str) != 1) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + if (!strcmp(str, "none")) + /* start by clearing context type bits */ + drvdata->addr_acc[idx] &= ~(BIT(2) | BIT(3)); + else if (!strcmp(str, "ctxid")) { + /* 0b01 The trace unit performs a Context ID */ + if (drvdata->numcidc) { + drvdata->addr_acc[idx] |= BIT(2); + drvdata->addr_acc[idx] &= ~BIT(3); + } + } else if (!strcmp(str, "vmid")) { + /* 0b10 The trace unit performs a VMID */ + if (drvdata->numvmidc) { + drvdata->addr_acc[idx] &= ~BIT(2); + drvdata->addr_acc[idx] |= BIT(3); + } + } else if (!strcmp(str, "all")) { + /* + * 0b11 The trace unit performs a Context ID + * comparison and a VMID + */ + if (drvdata->numcidc) + drvdata->addr_acc[idx] |= BIT(2); + if (drvdata->numvmidc) + drvdata->addr_acc[idx] |= BIT(3); + } + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_ctxtype); + +static ssize_t addr_context_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + /* context ID comparator bits[6:4] */ + val = BMVAL(drvdata->addr_acc[idx], 4, 6); + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t addr_context_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if ((drvdata->numcidc <= 1) && (drvdata->numvmidc <= 1)) + return -EINVAL; + if (val >= (drvdata->numcidc >= drvdata->numvmidc ? + drvdata->numcidc : drvdata->numvmidc)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->addr_idx; + /* clear context ID comparator bits[6:4] */ + drvdata->addr_acc[idx] &= ~(BIT(4) | BIT(5) | BIT(6)); + drvdata->addr_acc[idx] |= (val << 4); + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(addr_context); + +static ssize_t seq_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t seq_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->nrseqstate - 1) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->seq_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(seq_idx); + +static ssize_t seq_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_state; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t seq_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->nrseqstate) + return -EINVAL; + + drvdata->seq_state = val; + return size; +} +static DEVICE_ATTR_RW(seq_state); + +static ssize_t seq_event_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->seq_idx; + val = drvdata->seq_ctrl[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t seq_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->seq_idx; + /* RST, bits[7:0] */ + drvdata->seq_ctrl[idx] = val & 0xFF; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(seq_event); + +static ssize_t seq_reset_event_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->seq_rst; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t seq_reset_event_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (!(drvdata->nrseqstate)) + return -EINVAL; + + drvdata->seq_rst = val & ETMv4_EVENT_MASK; + return size; +} +static DEVICE_ATTR_RW(seq_reset_event); + +static ssize_t cntr_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cntr_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cntr_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->nr_cntr) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->cntr_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(cntr_idx); + +static ssize_t cntrldvr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + val = drvdata->cntrldvr[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cntrldvr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val > ETM_CNTR_MAX_VAL) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + drvdata->cntrldvr[idx] = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(cntrldvr); + +static ssize_t cntr_val_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + val = drvdata->cntr_val[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cntr_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val > ETM_CNTR_MAX_VAL) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + drvdata->cntr_val[idx] = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(cntr_val); + +static ssize_t cntr_ctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + val = drvdata->cntr_ctrl[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t cntr_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->cntr_idx; + drvdata->cntr_ctrl[idx] = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(cntr_ctrl); + +static ssize_t res_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->res_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t res_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + /* Resource selector pair 0 is always implemented and reserved */ + if ((val == 0) || (val >= drvdata->nr_resource)) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->res_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(res_idx); + +static ssize_t res_ctrl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->res_idx; + val = drvdata->res_ctrl[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t res_ctrl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + idx = drvdata->res_idx; + /* For odd idx pair inversal bit is RES0 */ + if (idx % 2 != 0) + /* PAIRINV, bit[21] */ + val &= ~BIT(21); + drvdata->res_ctrl[idx] = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(res_ctrl); + +static ssize_t ctxid_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->ctxid_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t ctxid_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->numcidc) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->ctxid_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(ctxid_idx); + +static ssize_t ctxid_pid_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 idx; + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + idx = drvdata->ctxid_idx; + val = (unsigned long)drvdata->ctxid_vpid[idx]; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t ctxid_pid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 idx; + unsigned long vpid, pid; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + /* + * only implemented when ctxid tracing is enabled, i.e. at least one + * ctxid comparator is implemented and ctxid is greater than 0 bits + * in length + */ + if (!drvdata->ctxid_size || !drvdata->numcidc) + return -EINVAL; + if (kstrtoul(buf, 16, &vpid)) + return -EINVAL; + + pid = coresight_vpid_to_pid(vpid); + + spin_lock(&drvdata->spinlock); + idx = drvdata->ctxid_idx; + drvdata->ctxid_pid[idx] = (u64)pid; + drvdata->ctxid_vpid[idx] = (u64)vpid; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(ctxid_pid); + +static ssize_t ctxid_masks_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val1, val2; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val1 = drvdata->ctxid_mask0; + val2 = drvdata->ctxid_mask1; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); +} + +static ssize_t ctxid_masks_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 i, j, maskbyte; + unsigned long val1, val2, mask; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + /* + * only implemented when ctxid tracing is enabled, i.e. at least one + * ctxid comparator is implemented and ctxid is greater than 0 bits + * in length + */ + if (!drvdata->ctxid_size || !drvdata->numcidc) + return -EINVAL; + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + /* + * each byte[0..3] controls mask value applied to ctxid + * comparator[0..3] + */ + switch (drvdata->numcidc) { + case 0x1: + /* COMP0, bits[7:0] */ + drvdata->ctxid_mask0 = val1 & 0xFF; + break; + case 0x2: + /* COMP1, bits[15:8] */ + drvdata->ctxid_mask0 = val1 & 0xFFFF; + break; + case 0x3: + /* COMP2, bits[23:16] */ + drvdata->ctxid_mask0 = val1 & 0xFFFFFF; + break; + case 0x4: + /* COMP3, bits[31:24] */ + drvdata->ctxid_mask0 = val1; + break; + case 0x5: + /* COMP4, bits[7:0] */ + drvdata->ctxid_mask0 = val1; + drvdata->ctxid_mask1 = val2 & 0xFF; + break; + case 0x6: + /* COMP5, bits[15:8] */ + drvdata->ctxid_mask0 = val1; + drvdata->ctxid_mask1 = val2 & 0xFFFF; + break; + case 0x7: + /* COMP6, bits[23:16] */ + drvdata->ctxid_mask0 = val1; + drvdata->ctxid_mask1 = val2 & 0xFFFFFF; + break; + case 0x8: + /* COMP7, bits[31:24] */ + drvdata->ctxid_mask0 = val1; + drvdata->ctxid_mask1 = val2; + break; + default: + break; + } + /* + * If software sets a mask bit to 1, it must program relevant byte + * of ctxid comparator value 0x0, otherwise behavior is unpredictable. + * For example, if bit[3] of ctxid_mask0 is 1, we must clear bits[31:24] + * of ctxid comparator0 value (corresponding to byte 0) register. + */ + mask = drvdata->ctxid_mask0; + for (i = 0; i < drvdata->numcidc; i++) { + /* mask value of corresponding ctxid comparator */ + maskbyte = mask & ETMv4_EVENT_MASK; + /* + * each bit corresponds to a byte of respective ctxid comparator + * value register + */ + for (j = 0; j < 8; j++) { + if (maskbyte & 1) + drvdata->ctxid_pid[i] &= ~(0xFF << (j * 8)); + maskbyte >>= 1; + } + /* Select the next ctxid comparator mask value */ + if (i == 3) + /* ctxid comparators[4-7] */ + mask = drvdata->ctxid_mask1; + else + mask >>= 0x8; + } + + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(ctxid_masks); + +static ssize_t vmid_idx_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->vmid_idx; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t vmid_idx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + if (val >= drvdata->numvmidc) + return -EINVAL; + + /* + * Use spinlock to ensure index doesn't change while it gets + * dereferenced multiple times within a spinlock block elsewhere. + */ + spin_lock(&drvdata->spinlock); + drvdata->vmid_idx = val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(vmid_idx); + +static ssize_t vmid_val_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = (unsigned long)drvdata->vmid_val[drvdata->vmid_idx]; + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t vmid_val_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + /* + * only implemented when vmid tracing is enabled, i.e. at least one + * vmid comparator is implemented and at least 8 bit vmid size + */ + if (!drvdata->vmid_size || !drvdata->numvmidc) + return -EINVAL; + if (kstrtoul(buf, 16, &val)) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + drvdata->vmid_val[drvdata->vmid_idx] = (u64)val; + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(vmid_val); + +static ssize_t vmid_masks_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val1, val2; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + spin_lock(&drvdata->spinlock); + val1 = drvdata->vmid_mask0; + val2 = drvdata->vmid_mask1; + spin_unlock(&drvdata->spinlock); + return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); +} + +static ssize_t vmid_masks_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 i, j, maskbyte; + unsigned long val1, val2, mask; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + /* + * only implemented when vmid tracing is enabled, i.e. at least one + * vmid comparator is implemented and at least 8 bit vmid size + */ + if (!drvdata->vmid_size || !drvdata->numvmidc) + return -EINVAL; + if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) + return -EINVAL; + + spin_lock(&drvdata->spinlock); + + /* + * each byte[0..3] controls mask value applied to vmid + * comparator[0..3] + */ + switch (drvdata->numvmidc) { + case 0x1: + /* COMP0, bits[7:0] */ + drvdata->vmid_mask0 = val1 & 0xFF; + break; + case 0x2: + /* COMP1, bits[15:8] */ + drvdata->vmid_mask0 = val1 & 0xFFFF; + break; + case 0x3: + /* COMP2, bits[23:16] */ + drvdata->vmid_mask0 = val1 & 0xFFFFFF; + break; + case 0x4: + /* COMP3, bits[31:24] */ + drvdata->vmid_mask0 = val1; + break; + case 0x5: + /* COMP4, bits[7:0] */ + drvdata->vmid_mask0 = val1; + drvdata->vmid_mask1 = val2 & 0xFF; + break; + case 0x6: + /* COMP5, bits[15:8] */ + drvdata->vmid_mask0 = val1; + drvdata->vmid_mask1 = val2 & 0xFFFF; + break; + case 0x7: + /* COMP6, bits[23:16] */ + drvdata->vmid_mask0 = val1; + drvdata->vmid_mask1 = val2 & 0xFFFFFF; + break; + case 0x8: + /* COMP7, bits[31:24] */ + drvdata->vmid_mask0 = val1; + drvdata->vmid_mask1 = val2; + break; + default: + break; + } + + /* + * If software sets a mask bit to 1, it must program relevant byte + * of vmid comparator value 0x0, otherwise behavior is unpredictable. + * For example, if bit[3] of vmid_mask0 is 1, we must clear bits[31:24] + * of vmid comparator0 value (corresponding to byte 0) register. + */ + mask = drvdata->vmid_mask0; + for (i = 0; i < drvdata->numvmidc; i++) { + /* mask value of corresponding vmid comparator */ + maskbyte = mask & ETMv4_EVENT_MASK; + /* + * each bit corresponds to a byte of respective vmid comparator + * value register + */ + for (j = 0; j < 8; j++) { + if (maskbyte & 1) + drvdata->vmid_val[i] &= ~(0xFF << (j * 8)); + maskbyte >>= 1; + } + /* Select the next vmid comparator mask value */ + if (i == 3) + /* vmid comparators[4-7] */ + mask = drvdata->vmid_mask1; + else + mask >>= 0x8; + } + spin_unlock(&drvdata->spinlock); + return size; +} +static DEVICE_ATTR_RW(vmid_masks); + +static ssize_t cpu_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->cpu; + return scnprintf(buf, PAGE_SIZE, "%d\n", val); + +} +static DEVICE_ATTR_RO(cpu); + +static struct attribute *coresight_etmv4_attrs[] = { + &dev_attr_nr_pe_cmp.attr, + &dev_attr_nr_addr_cmp.attr, + &dev_attr_nr_cntr.attr, + &dev_attr_nr_ext_inp.attr, + &dev_attr_numcidc.attr, + &dev_attr_numvmidc.attr, + &dev_attr_nrseqstate.attr, + &dev_attr_nr_resource.attr, + &dev_attr_nr_ss_cmp.attr, + &dev_attr_reset.attr, + &dev_attr_mode.attr, + &dev_attr_pe.attr, + &dev_attr_event.attr, + &dev_attr_event_instren.attr, + &dev_attr_event_ts.attr, + &dev_attr_syncfreq.attr, + &dev_attr_cyc_threshold.attr, + &dev_attr_bb_ctrl.attr, + &dev_attr_event_vinst.attr, + &dev_attr_s_exlevel_vinst.attr, + &dev_attr_ns_exlevel_vinst.attr, + &dev_attr_addr_idx.attr, + &dev_attr_addr_instdatatype.attr, + &dev_attr_addr_single.attr, + &dev_attr_addr_range.attr, + &dev_attr_addr_start.attr, + &dev_attr_addr_stop.attr, + &dev_attr_addr_ctxtype.attr, + &dev_attr_addr_context.attr, + &dev_attr_seq_idx.attr, + &dev_attr_seq_state.attr, + &dev_attr_seq_event.attr, + &dev_attr_seq_reset_event.attr, + &dev_attr_cntr_idx.attr, + &dev_attr_cntrldvr.attr, + &dev_attr_cntr_val.attr, + &dev_attr_cntr_ctrl.attr, + &dev_attr_res_idx.attr, + &dev_attr_res_ctrl.attr, + &dev_attr_ctxid_idx.attr, + &dev_attr_ctxid_pid.attr, + &dev_attr_ctxid_masks.attr, + &dev_attr_vmid_idx.attr, + &dev_attr_vmid_val.attr, + &dev_attr_vmid_masks.attr, + &dev_attr_cpu.attr, + NULL, +}; + +#define coresight_simple_func(name, offset) \ +static ssize_t name##_show(struct device *_dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct etmv4_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ + readl_relaxed(drvdata->base + offset)); \ +} \ +DEVICE_ATTR_RO(name) + +coresight_simple_func(trcoslsr, TRCOSLSR); +coresight_simple_func(trcpdcr, TRCPDCR); +coresight_simple_func(trcpdsr, TRCPDSR); +coresight_simple_func(trclsr, TRCLSR); +coresight_simple_func(trcauthstatus, TRCAUTHSTATUS); +coresight_simple_func(trcdevid, TRCDEVID); +coresight_simple_func(trcdevtype, TRCDEVTYPE); +coresight_simple_func(trcpidr0, TRCPIDR0); +coresight_simple_func(trcpidr1, TRCPIDR1); +coresight_simple_func(trcpidr2, TRCPIDR2); +coresight_simple_func(trcpidr3, TRCPIDR3); + +static struct attribute *coresight_etmv4_mgmt_attrs[] = { + &dev_attr_trcoslsr.attr, + &dev_attr_trcpdcr.attr, + &dev_attr_trcpdsr.attr, + &dev_attr_trclsr.attr, + &dev_attr_trcauthstatus.attr, + &dev_attr_trcdevid.attr, + &dev_attr_trcdevtype.attr, + &dev_attr_trcpidr0.attr, + &dev_attr_trcpidr1.attr, + &dev_attr_trcpidr2.attr, + &dev_attr_trcpidr3.attr, + NULL, +}; + +coresight_simple_func(trcidr0, TRCIDR0); +coresight_simple_func(trcidr1, TRCIDR1); +coresight_simple_func(trcidr2, TRCIDR2); +coresight_simple_func(trcidr3, TRCIDR3); +coresight_simple_func(trcidr4, TRCIDR4); +coresight_simple_func(trcidr5, TRCIDR5); +/* trcidr[6,7] are reserved */ +coresight_simple_func(trcidr8, TRCIDR8); +coresight_simple_func(trcidr9, TRCIDR9); +coresight_simple_func(trcidr10, TRCIDR10); +coresight_simple_func(trcidr11, TRCIDR11); +coresight_simple_func(trcidr12, TRCIDR12); +coresight_simple_func(trcidr13, TRCIDR13); + +static struct attribute *coresight_etmv4_trcidr_attrs[] = { + &dev_attr_trcidr0.attr, + &dev_attr_trcidr1.attr, + &dev_attr_trcidr2.attr, + &dev_attr_trcidr3.attr, + &dev_attr_trcidr4.attr, + &dev_attr_trcidr5.attr, + /* trcidr[6,7] are reserved */ + &dev_attr_trcidr8.attr, + &dev_attr_trcidr9.attr, + &dev_attr_trcidr10.attr, + &dev_attr_trcidr11.attr, + &dev_attr_trcidr12.attr, + &dev_attr_trcidr13.attr, + NULL, +}; + +static const struct attribute_group coresight_etmv4_group = { + .attrs = coresight_etmv4_attrs, +}; + +static const struct attribute_group coresight_etmv4_mgmt_group = { + .attrs = coresight_etmv4_mgmt_attrs, + .name = "mgmt", +}; + +static const struct attribute_group coresight_etmv4_trcidr_group = { + .attrs = coresight_etmv4_trcidr_attrs, + .name = "trcidr", +}; + +const struct attribute_group *coresight_etmv4_groups[] = { + &coresight_etmv4_group, + &coresight_etmv4_mgmt_group, + &coresight_etmv4_trcidr_group, + NULL, +}; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 1c59bd36834c..5cb919118d70 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -274,2035 +274,6 @@ static const struct coresight_ops etm4_cs_ops = { .source_ops = &etm4_source_ops, }; -static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) -{ - u8 idx = drvdata->addr_idx; - - /* - * TRCACATRn.TYPE bit[1:0]: type of comparison - * the trace unit performs - */ - if (BMVAL(drvdata->addr_acc[idx], 0, 1) == ETM_INSTR_ADDR) { - if (idx % 2 != 0) - return -EINVAL; - - /* - * We are performing instruction address comparison. Set the - * relevant bit of ViewInst Include/Exclude Control register - * for corresponding address comparator pair. - */ - if (drvdata->addr_type[idx] != ETM_ADDR_TYPE_RANGE || - drvdata->addr_type[idx + 1] != ETM_ADDR_TYPE_RANGE) - return -EINVAL; - - if (exclude == true) { - /* - * Set exclude bit and unset the include bit - * corresponding to comparator pair - */ - drvdata->viiectlr |= BIT(idx / 2 + 16); - drvdata->viiectlr &= ~BIT(idx / 2); - } else { - /* - * Set include bit and unset exclude bit - * corresponding to comparator pair - */ - drvdata->viiectlr |= BIT(idx / 2); - drvdata->viiectlr &= ~BIT(idx / 2 + 16); - } - } - return 0; -} - -static ssize_t nr_pe_cmp_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_pe_cmp; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_pe_cmp); - -static ssize_t nr_addr_cmp_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_addr_cmp; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_addr_cmp); - -static ssize_t nr_cntr_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_cntr; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_cntr); - -static ssize_t nr_ext_inp_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_ext_inp; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_ext_inp); - -static ssize_t numcidc_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->numcidc; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(numcidc); - -static ssize_t numvmidc_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->numvmidc; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(numvmidc); - -static ssize_t nrseqstate_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nrseqstate; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nrseqstate); - -static ssize_t nr_resource_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_resource; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_resource); - -static ssize_t nr_ss_cmp_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->nr_ss_cmp; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} -static DEVICE_ATTR_RO(nr_ss_cmp); - -static ssize_t reset_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - int i; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - if (val) - drvdata->mode = 0x0; - - /* Disable data tracing: do not trace load and store data transfers */ - drvdata->mode &= ~(ETM_MODE_LOAD | ETM_MODE_STORE); - drvdata->cfg &= ~(BIT(1) | BIT(2)); - - /* Disable data value and data address tracing */ - drvdata->mode &= ~(ETM_MODE_DATA_TRACE_ADDR | - ETM_MODE_DATA_TRACE_VAL); - drvdata->cfg &= ~(BIT(16) | BIT(17)); - - /* Disable all events tracing */ - drvdata->eventctrl0 = 0x0; - drvdata->eventctrl1 = 0x0; - - /* Disable timestamp event */ - drvdata->ts_ctrl = 0x0; - - /* Disable stalling */ - drvdata->stall_ctrl = 0x0; - - /* Reset trace synchronization period to 2^8 = 256 bytes*/ - if (drvdata->syncpr == false) - drvdata->syncfreq = 0x8; - - /* - * Enable ViewInst to trace everything with start-stop logic in - * started state. ARM recommends start-stop logic is set before - * each trace run. - */ - drvdata->vinst_ctrl |= BIT(0); - if (drvdata->nr_addr_cmp == true) { - drvdata->mode |= ETM_MODE_VIEWINST_STARTSTOP; - /* SSSTATUS, bit[9] */ - drvdata->vinst_ctrl |= BIT(9); - } - - /* No address range filtering for ViewInst */ - drvdata->viiectlr = 0x0; - - /* No start-stop filtering for ViewInst */ - drvdata->vissctlr = 0x0; - - /* Disable seq events */ - for (i = 0; i < drvdata->nrseqstate-1; i++) - drvdata->seq_ctrl[i] = 0x0; - drvdata->seq_rst = 0x0; - drvdata->seq_state = 0x0; - - /* Disable external input events */ - drvdata->ext_inp = 0x0; - - drvdata->cntr_idx = 0x0; - for (i = 0; i < drvdata->nr_cntr; i++) { - drvdata->cntrldvr[i] = 0x0; - drvdata->cntr_ctrl[i] = 0x0; - drvdata->cntr_val[i] = 0x0; - } - - /* Resource selector pair 0 is always implemented and reserved */ - drvdata->res_idx = 0x2; - for (i = 2; i < drvdata->nr_resource * 2; i++) - drvdata->res_ctrl[i] = 0x0; - - for (i = 0; i < drvdata->nr_ss_cmp; i++) { - drvdata->ss_ctrl[i] = 0x0; - drvdata->ss_pe_cmp[i] = 0x0; - } - - drvdata->addr_idx = 0x0; - for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acc[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; - } - - drvdata->ctxid_idx = 0x0; - for (i = 0; i < drvdata->numcidc; i++) { - drvdata->ctxid_pid[i] = 0x0; - drvdata->ctxid_vpid[i] = 0x0; - } - - drvdata->ctxid_mask0 = 0x0; - drvdata->ctxid_mask1 = 0x0; - - drvdata->vmid_idx = 0x0; - for (i = 0; i < drvdata->numvmidc; i++) - drvdata->vmid_val[i] = 0x0; - drvdata->vmid_mask0 = 0x0; - drvdata->vmid_mask1 = 0x0; - - drvdata->trcid = drvdata->cpu + 1; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_WO(reset); - -static ssize_t mode_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->mode; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val, mode; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETMv4_MODE_ALL; - - if (drvdata->mode & ETM_MODE_EXCLUDE) - etm4_set_mode_exclude(drvdata, true); - else - etm4_set_mode_exclude(drvdata, false); - - if (drvdata->instrp0 == true) { - /* start by clearing instruction P0 field */ - drvdata->cfg &= ~(BIT(1) | BIT(2)); - if (drvdata->mode & ETM_MODE_LOAD) - /* 0b01 Trace load instructions as P0 instructions */ - drvdata->cfg |= BIT(1); - if (drvdata->mode & ETM_MODE_STORE) - /* 0b10 Trace store instructions as P0 instructions */ - drvdata->cfg |= BIT(2); - if (drvdata->mode & ETM_MODE_LOAD_STORE) - /* - * 0b11 Trace load and store instructions - * as P0 instructions - */ - drvdata->cfg |= BIT(1) | BIT(2); - } - - /* bit[3], Branch broadcast mode */ - if ((drvdata->mode & ETM_MODE_BB) && (drvdata->trcbb == true)) - drvdata->cfg |= BIT(3); - else - drvdata->cfg &= ~BIT(3); - - /* bit[4], Cycle counting instruction trace bit */ - if ((drvdata->mode & ETMv4_MODE_CYCACC) && - (drvdata->trccci == true)) - drvdata->cfg |= BIT(4); - else - drvdata->cfg &= ~BIT(4); - - /* bit[6], Context ID tracing bit */ - if ((drvdata->mode & ETMv4_MODE_CTXID) && (drvdata->ctxid_size)) - drvdata->cfg |= BIT(6); - else - drvdata->cfg &= ~BIT(6); - - if ((drvdata->mode & ETM_MODE_VMID) && (drvdata->vmid_size)) - drvdata->cfg |= BIT(7); - else - drvdata->cfg &= ~BIT(7); - - /* bits[10:8], Conditional instruction tracing bit */ - mode = ETM_MODE_COND(drvdata->mode); - if (drvdata->trccond == true) { - drvdata->cfg &= ~(BIT(8) | BIT(9) | BIT(10)); - drvdata->cfg |= mode << 8; - } - - /* bit[11], Global timestamp tracing bit */ - if ((drvdata->mode & ETMv4_MODE_TIMESTAMP) && (drvdata->ts_size)) - drvdata->cfg |= BIT(11); - else - drvdata->cfg &= ~BIT(11); - - /* bit[12], Return stack enable bit */ - if ((drvdata->mode & ETM_MODE_RETURNSTACK) && - (drvdata->retstack == true)) - drvdata->cfg |= BIT(12); - else - drvdata->cfg &= ~BIT(12); - - /* bits[14:13], Q element enable field */ - mode = ETM_MODE_QELEM(drvdata->mode); - /* start by clearing QE bits */ - drvdata->cfg &= ~(BIT(13) | BIT(14)); - /* if supported, Q elements with instruction counts are enabled */ - if ((mode & BIT(0)) && (drvdata->q_support & BIT(0))) - drvdata->cfg |= BIT(13); - /* - * if supported, Q elements with and without instruction - * counts are enabled - */ - if ((mode & BIT(1)) && (drvdata->q_support & BIT(1))) - drvdata->cfg |= BIT(14); - - /* bit[11], AMBA Trace Bus (ATB) trigger enable bit */ - if ((drvdata->mode & ETM_MODE_ATB_TRIGGER) && - (drvdata->atbtrig == true)) - drvdata->eventctrl1 |= BIT(11); - else - drvdata->eventctrl1 &= ~BIT(11); - - /* bit[12], Low-power state behavior override bit */ - if ((drvdata->mode & ETM_MODE_LPOVERRIDE) && - (drvdata->lpoverride == true)) - drvdata->eventctrl1 |= BIT(12); - else - drvdata->eventctrl1 &= ~BIT(12); - - /* bit[8], Instruction stall bit */ - if (drvdata->mode & ETM_MODE_ISTALL_EN) - drvdata->stall_ctrl |= BIT(8); - else - drvdata->stall_ctrl &= ~BIT(8); - - /* bit[10], Prioritize instruction trace bit */ - if (drvdata->mode & ETM_MODE_INSTPRIO) - drvdata->stall_ctrl |= BIT(10); - else - drvdata->stall_ctrl &= ~BIT(10); - - /* bit[13], Trace overflow prevention bit */ - if ((drvdata->mode & ETM_MODE_NOOVERFLOW) && - (drvdata->nooverflow == true)) - drvdata->stall_ctrl |= BIT(13); - else - drvdata->stall_ctrl &= ~BIT(13); - - /* bit[9] Start/stop logic control bit */ - if (drvdata->mode & ETM_MODE_VIEWINST_STARTSTOP) - drvdata->vinst_ctrl |= BIT(9); - else - drvdata->vinst_ctrl &= ~BIT(9); - - /* bit[10], Whether a trace unit must trace a Reset exception */ - if (drvdata->mode & ETM_MODE_TRACE_RESET) - drvdata->vinst_ctrl |= BIT(10); - else - drvdata->vinst_ctrl &= ~BIT(10); - - /* bit[11], Whether a trace unit must trace a system error exception */ - if ((drvdata->mode & ETM_MODE_TRACE_ERR) && - (drvdata->trc_error == true)) - drvdata->vinst_ctrl |= BIT(11); - else - drvdata->vinst_ctrl &= ~BIT(11); - - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(mode); - -static ssize_t pe_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->pe_sel; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t pe_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - if (val > drvdata->nr_pe) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - - drvdata->pe_sel = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(pe); - -static ssize_t event_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->eventctrl0; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - switch (drvdata->nr_event) { - case 0x0: - /* EVENT0, bits[7:0] */ - drvdata->eventctrl0 = val & 0xFF; - break; - case 0x1: - /* EVENT1, bits[15:8] */ - drvdata->eventctrl0 = val & 0xFFFF; - break; - case 0x2: - /* EVENT2, bits[23:16] */ - drvdata->eventctrl0 = val & 0xFFFFFF; - break; - case 0x3: - /* EVENT3, bits[31:24] */ - drvdata->eventctrl0 = val; - break; - default: - break; - } - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(event); - -static ssize_t event_instren_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = BMVAL(drvdata->eventctrl1, 0, 3); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t event_instren_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - /* start by clearing all instruction event enable bits */ - drvdata->eventctrl1 &= ~(BIT(0) | BIT(1) | BIT(2) | BIT(3)); - switch (drvdata->nr_event) { - case 0x0: - /* generate Event element for event 1 */ - drvdata->eventctrl1 |= val & BIT(1); - break; - case 0x1: - /* generate Event element for event 1 and 2 */ - drvdata->eventctrl1 |= val & (BIT(0) | BIT(1)); - break; - case 0x2: - /* generate Event element for event 1, 2 and 3 */ - drvdata->eventctrl1 |= val & (BIT(0) | BIT(1) | BIT(2)); - break; - case 0x3: - /* generate Event element for all 4 events */ - drvdata->eventctrl1 |= val & 0xF; - break; - default: - break; - } - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(event_instren); - -static ssize_t event_ts_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ts_ctrl; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t event_ts_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (!drvdata->ts_size) - return -EINVAL; - - drvdata->ts_ctrl = val & ETMv4_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(event_ts); - -static ssize_t syncfreq_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->syncfreq; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t syncfreq_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (drvdata->syncpr == true) - return -EINVAL; - - drvdata->syncfreq = val & ETMv4_SYNC_MASK; - return size; -} -static DEVICE_ATTR_RW(syncfreq); - -static ssize_t cyc_threshold_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ccctlr; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cyc_threshold_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val < drvdata->ccitmin) - return -EINVAL; - - drvdata->ccctlr = val & ETM_CYC_THRESHOLD_MASK; - return size; -} -static DEVICE_ATTR_RW(cyc_threshold); - -static ssize_t bb_ctrl_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->bb_ctrl; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t bb_ctrl_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (drvdata->trcbb == false) - return -EINVAL; - if (!drvdata->nr_addr_cmp) - return -EINVAL; - /* - * Bit[7:0] selects which address range comparator is used for - * branch broadcast control. - */ - if (BMVAL(val, 0, 7) > drvdata->nr_addr_cmp) - return -EINVAL; - - drvdata->bb_ctrl = val; - return size; -} -static DEVICE_ATTR_RW(bb_ctrl); - -static ssize_t event_vinst_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->vinst_ctrl & ETMv4_EVENT_MASK; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t event_vinst_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - val &= ETMv4_EVENT_MASK; - drvdata->vinst_ctrl &= ~ETMv4_EVENT_MASK; - drvdata->vinst_ctrl |= val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(event_vinst); - -static ssize_t s_exlevel_vinst_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = BMVAL(drvdata->vinst_ctrl, 16, 19); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t s_exlevel_vinst_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - /* clear all EXLEVEL_S bits (bit[18] is never implemented) */ - drvdata->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19)); - /* enable instruction tracing for corresponding exception level */ - val &= drvdata->s_ex_level; - drvdata->vinst_ctrl |= (val << 16); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(s_exlevel_vinst); - -static ssize_t ns_exlevel_vinst_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - /* EXLEVEL_NS, bits[23:20] */ - val = BMVAL(drvdata->vinst_ctrl, 20, 23); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t ns_exlevel_vinst_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - /* clear EXLEVEL_NS bits (bit[23] is never implemented */ - drvdata->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22)); - /* enable instruction tracing for corresponding exception level */ - val &= drvdata->ns_ex_level; - drvdata->vinst_ctrl |= (val << 20); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(ns_exlevel_vinst); - -static ssize_t addr_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->addr_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->nr_addr_cmp * 2) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_idx); - -static ssize_t addr_instdatatype_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - ssize_t len; - u8 val, idx; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - val = BMVAL(drvdata->addr_acc[idx], 0, 1); - len = scnprintf(buf, PAGE_SIZE, "%s\n", - val == ETM_INSTR_ADDR ? "instr" : - (val == ETM_DATA_LOAD_ADDR ? "data_load" : - (val == ETM_DATA_STORE_ADDR ? "data_store" : - "data_load_store"))); - spin_unlock(&drvdata->spinlock); - return len; -} - -static ssize_t addr_instdatatype_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - char str[20] = ""; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (strlen(buf) >= 20) - return -EINVAL; - if (sscanf(buf, "%s", str) != 1) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!strcmp(str, "instr")) - /* TYPE, bits[1:0] */ - drvdata->addr_acc[idx] &= ~(BIT(0) | BIT(1)); - - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_instdatatype); - -static ssize_t addr_single_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - idx = drvdata->addr_idx; - spin_lock(&drvdata->spinlock); - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - val = (unsigned long)drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_single_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_single); - -static ssize_t addr_range_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val1, val2; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val1 = (unsigned long)drvdata->addr_val[idx]; - val2 = (unsigned long)drvdata->addr_val[idx + 1]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); -} - -static ssize_t addr_range_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val1, val2; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) - return -EINVAL; - /* lower address comparator cannot have a higher address value */ - if (val1 > val2) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (idx % 2 != 0) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = (u64)val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = (u64)val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; - /* - * Program include or exclude control bits for vinst or vdata - * whenever we change addr comparators to ETM_ADDR_TYPE_RANGE - */ - if (drvdata->mode & ETM_MODE_EXCLUDE) - etm4_set_mode_exclude(drvdata, true); - else - etm4_set_mode_exclude(drvdata, false); - - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_range); - -static ssize_t addr_start_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = (unsigned long)drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_start_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!drvdata->nr_addr_cmp) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->vissctlr |= BIT(idx); - /* SSSTATUS, bit[9] - turn on start/stop logic */ - drvdata->vinst_ctrl |= BIT(9); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_start); - -static ssize_t addr_stop_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - val = (unsigned long)drvdata->addr_val[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_stop_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!drvdata->nr_addr_cmp) { - spin_unlock(&drvdata->spinlock); - return -EINVAL; - } - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { - spin_unlock(&drvdata->spinlock); - return -EPERM; - } - - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->vissctlr |= BIT(idx + 16); - /* SSSTATUS, bit[9] - turn on start/stop logic */ - drvdata->vinst_ctrl |= BIT(9); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_stop); - -static ssize_t addr_ctxtype_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - ssize_t len; - u8 idx, val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - /* CONTEXTTYPE, bits[3:2] */ - val = BMVAL(drvdata->addr_acc[idx], 2, 3); - len = scnprintf(buf, PAGE_SIZE, "%s\n", val == ETM_CTX_NONE ? "none" : - (val == ETM_CTX_CTXID ? "ctxid" : - (val == ETM_CTX_VMID ? "vmid" : "all"))); - spin_unlock(&drvdata->spinlock); - return len; -} - -static ssize_t addr_ctxtype_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - char str[10] = ""; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (strlen(buf) >= 10) - return -EINVAL; - if (sscanf(buf, "%s", str) != 1) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!strcmp(str, "none")) - /* start by clearing context type bits */ - drvdata->addr_acc[idx] &= ~(BIT(2) | BIT(3)); - else if (!strcmp(str, "ctxid")) { - /* 0b01 The trace unit performs a Context ID */ - if (drvdata->numcidc) { - drvdata->addr_acc[idx] |= BIT(2); - drvdata->addr_acc[idx] &= ~BIT(3); - } - } else if (!strcmp(str, "vmid")) { - /* 0b10 The trace unit performs a VMID */ - if (drvdata->numvmidc) { - drvdata->addr_acc[idx] &= ~BIT(2); - drvdata->addr_acc[idx] |= BIT(3); - } - } else if (!strcmp(str, "all")) { - /* - * 0b11 The trace unit performs a Context ID - * comparison and a VMID - */ - if (drvdata->numcidc) - drvdata->addr_acc[idx] |= BIT(2); - if (drvdata->numvmidc) - drvdata->addr_acc[idx] |= BIT(3); - } - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_ctxtype); - -static ssize_t addr_context_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - /* context ID comparator bits[6:4] */ - val = BMVAL(drvdata->addr_acc[idx], 4, 6); - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t addr_context_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if ((drvdata->numcidc <= 1) && (drvdata->numvmidc <= 1)) - return -EINVAL; - if (val >= (drvdata->numcidc >= drvdata->numvmidc ? - drvdata->numcidc : drvdata->numvmidc)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - /* clear context ID comparator bits[6:4] */ - drvdata->addr_acc[idx] &= ~(BIT(4) | BIT(5) | BIT(6)); - drvdata->addr_acc[idx] |= (val << 4); - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(addr_context); - -static ssize_t seq_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t seq_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->nrseqstate - 1) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->seq_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(seq_idx); - -static ssize_t seq_state_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_state; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t seq_state_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->nrseqstate) - return -EINVAL; - - drvdata->seq_state = val; - return size; -} -static DEVICE_ATTR_RW(seq_state); - -static ssize_t seq_event_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->seq_idx; - val = drvdata->seq_ctrl[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t seq_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->seq_idx; - /* RST, bits[7:0] */ - drvdata->seq_ctrl[idx] = val & 0xFF; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(seq_event); - -static ssize_t seq_reset_event_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->seq_rst; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t seq_reset_event_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (!(drvdata->nrseqstate)) - return -EINVAL; - - drvdata->seq_rst = val & ETMv4_EVENT_MASK; - return size; -} -static DEVICE_ATTR_RW(seq_reset_event); - -static ssize_t cntr_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cntr_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cntr_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->nr_cntr) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(cntr_idx); - -static ssize_t cntrldvr_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntrldvr[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cntrldvr_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val > ETM_CNTR_MAX_VAL) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntrldvr[idx] = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(cntrldvr); - -static ssize_t cntr_val_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntr_val[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cntr_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val > ETM_CNTR_MAX_VAL) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntr_val[idx] = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(cntr_val); - -static ssize_t cntr_ctrl_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntr_ctrl[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t cntr_ctrl_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntr_ctrl[idx] = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(cntr_ctrl); - -static ssize_t res_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->res_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t res_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - /* Resource selector pair 0 is always implemented and reserved */ - if (val < 2 || val >= drvdata->nr_resource * 2) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->res_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(res_idx); - -static ssize_t res_ctrl_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->res_idx; - val = drvdata->res_ctrl[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t res_ctrl_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - idx = drvdata->res_idx; - /* For odd idx pair inversal bit is RES0 */ - if (idx % 2 != 0) - /* PAIRINV, bit[21] */ - val &= ~BIT(21); - drvdata->res_ctrl[idx] = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(res_ctrl); - -static ssize_t ctxid_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->ctxid_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t ctxid_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->numcidc) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(ctxid_idx); - -static ssize_t ctxid_pid_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - u8 idx; - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - idx = drvdata->ctxid_idx; - val = (unsigned long)drvdata->ctxid_vpid[idx]; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t ctxid_pid_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 idx; - unsigned long vpid, pid; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - /* - * only implemented when ctxid tracing is enabled, i.e. at least one - * ctxid comparator is implemented and ctxid is greater than 0 bits - * in length - */ - if (!drvdata->ctxid_size || !drvdata->numcidc) - return -EINVAL; - if (kstrtoul(buf, 16, &vpid)) - return -EINVAL; - - pid = coresight_vpid_to_pid(vpid); - - spin_lock(&drvdata->spinlock); - idx = drvdata->ctxid_idx; - drvdata->ctxid_pid[idx] = (u64)pid; - drvdata->ctxid_vpid[idx] = (u64)vpid; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(ctxid_pid); - -static ssize_t ctxid_masks_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val1, val2; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val1 = drvdata->ctxid_mask0; - val2 = drvdata->ctxid_mask1; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); -} - -static ssize_t ctxid_masks_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 i, j, maskbyte; - unsigned long val1, val2, mask; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - /* - * only implemented when ctxid tracing is enabled, i.e. at least one - * ctxid comparator is implemented and ctxid is greater than 0 bits - * in length - */ - if (!drvdata->ctxid_size || !drvdata->numcidc) - return -EINVAL; - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - /* - * each byte[0..3] controls mask value applied to ctxid - * comparator[0..3] - */ - switch (drvdata->numcidc) { - case 0x1: - /* COMP0, bits[7:0] */ - drvdata->ctxid_mask0 = val1 & 0xFF; - break; - case 0x2: - /* COMP1, bits[15:8] */ - drvdata->ctxid_mask0 = val1 & 0xFFFF; - break; - case 0x3: - /* COMP2, bits[23:16] */ - drvdata->ctxid_mask0 = val1 & 0xFFFFFF; - break; - case 0x4: - /* COMP3, bits[31:24] */ - drvdata->ctxid_mask0 = val1; - break; - case 0x5: - /* COMP4, bits[7:0] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFF; - break; - case 0x6: - /* COMP5, bits[15:8] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFFFF; - break; - case 0x7: - /* COMP6, bits[23:16] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFFFFFF; - break; - case 0x8: - /* COMP7, bits[31:24] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2; - break; - default: - break; - } - /* - * If software sets a mask bit to 1, it must program relevant byte - * of ctxid comparator value 0x0, otherwise behavior is unpredictable. - * For example, if bit[3] of ctxid_mask0 is 1, we must clear bits[31:24] - * of ctxid comparator0 value (corresponding to byte 0) register. - */ - mask = drvdata->ctxid_mask0; - for (i = 0; i < drvdata->numcidc; i++) { - /* mask value of corresponding ctxid comparator */ - maskbyte = mask & ETMv4_EVENT_MASK; - /* - * each bit corresponds to a byte of respective ctxid comparator - * value register - */ - for (j = 0; j < 8; j++) { - if (maskbyte & 1) - drvdata->ctxid_pid[i] &= ~(0xFF << (j * 8)); - maskbyte >>= 1; - } - /* Select the next ctxid comparator mask value */ - if (i == 3) - /* ctxid comparators[4-7] */ - mask = drvdata->ctxid_mask1; - else - mask >>= 0x8; - } - - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(ctxid_masks); - -static ssize_t vmid_idx_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->vmid_idx; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t vmid_idx_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - if (val >= drvdata->numvmidc) - return -EINVAL; - - /* - * Use spinlock to ensure index doesn't change while it gets - * dereferenced multiple times within a spinlock block elsewhere. - */ - spin_lock(&drvdata->spinlock); - drvdata->vmid_idx = val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(vmid_idx); - -static ssize_t vmid_val_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = (unsigned long)drvdata->vmid_val[drvdata->vmid_idx]; - return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); -} - -static ssize_t vmid_val_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - unsigned long val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - /* - * only implemented when vmid tracing is enabled, i.e. at least one - * vmid comparator is implemented and at least 8 bit vmid size - */ - if (!drvdata->vmid_size || !drvdata->numvmidc) - return -EINVAL; - if (kstrtoul(buf, 16, &val)) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - drvdata->vmid_val[drvdata->vmid_idx] = (u64)val; - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(vmid_val); - -static ssize_t vmid_masks_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long val1, val2; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - spin_lock(&drvdata->spinlock); - val1 = drvdata->vmid_mask0; - val2 = drvdata->vmid_mask1; - spin_unlock(&drvdata->spinlock); - return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); -} - -static ssize_t vmid_masks_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - u8 i, j, maskbyte; - unsigned long val1, val2, mask; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - /* - * only implemented when vmid tracing is enabled, i.e. at least one - * vmid comparator is implemented and at least 8 bit vmid size - */ - if (!drvdata->vmid_size || !drvdata->numvmidc) - return -EINVAL; - if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) - return -EINVAL; - - spin_lock(&drvdata->spinlock); - - /* - * each byte[0..3] controls mask value applied to vmid - * comparator[0..3] - */ - switch (drvdata->numvmidc) { - case 0x1: - /* COMP0, bits[7:0] */ - drvdata->vmid_mask0 = val1 & 0xFF; - break; - case 0x2: - /* COMP1, bits[15:8] */ - drvdata->vmid_mask0 = val1 & 0xFFFF; - break; - case 0x3: - /* COMP2, bits[23:16] */ - drvdata->vmid_mask0 = val1 & 0xFFFFFF; - break; - case 0x4: - /* COMP3, bits[31:24] */ - drvdata->vmid_mask0 = val1; - break; - case 0x5: - /* COMP4, bits[7:0] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFF; - break; - case 0x6: - /* COMP5, bits[15:8] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFFFF; - break; - case 0x7: - /* COMP6, bits[23:16] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFFFFFF; - break; - case 0x8: - /* COMP7, bits[31:24] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2; - break; - default: - break; - } - - /* - * If software sets a mask bit to 1, it must program relevant byte - * of vmid comparator value 0x0, otherwise behavior is unpredictable. - * For example, if bit[3] of vmid_mask0 is 1, we must clear bits[31:24] - * of vmid comparator0 value (corresponding to byte 0) register. - */ - mask = drvdata->vmid_mask0; - for (i = 0; i < drvdata->numvmidc; i++) { - /* mask value of corresponding vmid comparator */ - maskbyte = mask & ETMv4_EVENT_MASK; - /* - * each bit corresponds to a byte of respective vmid comparator - * value register - */ - for (j = 0; j < 8; j++) { - if (maskbyte & 1) - drvdata->vmid_val[i] &= ~(0xFF << (j * 8)); - maskbyte >>= 1; - } - /* Select the next vmid comparator mask value */ - if (i == 3) - /* vmid comparators[4-7] */ - mask = drvdata->vmid_mask1; - else - mask >>= 0x8; - } - spin_unlock(&drvdata->spinlock); - return size; -} -static DEVICE_ATTR_RW(vmid_masks); - -static ssize_t cpu_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - int val; - struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); - - val = drvdata->cpu; - return scnprintf(buf, PAGE_SIZE, "%d\n", val); - -} -static DEVICE_ATTR_RO(cpu); - -static struct attribute *coresight_etmv4_attrs[] = { - &dev_attr_nr_pe_cmp.attr, - &dev_attr_nr_addr_cmp.attr, - &dev_attr_nr_cntr.attr, - &dev_attr_nr_ext_inp.attr, - &dev_attr_numcidc.attr, - &dev_attr_numvmidc.attr, - &dev_attr_nrseqstate.attr, - &dev_attr_nr_resource.attr, - &dev_attr_nr_ss_cmp.attr, - &dev_attr_reset.attr, - &dev_attr_mode.attr, - &dev_attr_pe.attr, - &dev_attr_event.attr, - &dev_attr_event_instren.attr, - &dev_attr_event_ts.attr, - &dev_attr_syncfreq.attr, - &dev_attr_cyc_threshold.attr, - &dev_attr_bb_ctrl.attr, - &dev_attr_event_vinst.attr, - &dev_attr_s_exlevel_vinst.attr, - &dev_attr_ns_exlevel_vinst.attr, - &dev_attr_addr_idx.attr, - &dev_attr_addr_instdatatype.attr, - &dev_attr_addr_single.attr, - &dev_attr_addr_range.attr, - &dev_attr_addr_start.attr, - &dev_attr_addr_stop.attr, - &dev_attr_addr_ctxtype.attr, - &dev_attr_addr_context.attr, - &dev_attr_seq_idx.attr, - &dev_attr_seq_state.attr, - &dev_attr_seq_event.attr, - &dev_attr_seq_reset_event.attr, - &dev_attr_cntr_idx.attr, - &dev_attr_cntrldvr.attr, - &dev_attr_cntr_val.attr, - &dev_attr_cntr_ctrl.attr, - &dev_attr_res_idx.attr, - &dev_attr_res_ctrl.attr, - &dev_attr_ctxid_idx.attr, - &dev_attr_ctxid_pid.attr, - &dev_attr_ctxid_masks.attr, - &dev_attr_vmid_idx.attr, - &dev_attr_vmid_val.attr, - &dev_attr_vmid_masks.attr, - &dev_attr_cpu.attr, - NULL, -}; - -#define coresight_simple_func(name, offset) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct etmv4_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ -} \ -static DEVICE_ATTR_RO(name) - -coresight_simple_func(trcoslsr, TRCOSLSR); -coresight_simple_func(trcpdcr, TRCPDCR); -coresight_simple_func(trcpdsr, TRCPDSR); -coresight_simple_func(trclsr, TRCLSR); -coresight_simple_func(trcauthstatus, TRCAUTHSTATUS); -coresight_simple_func(trcdevid, TRCDEVID); -coresight_simple_func(trcdevtype, TRCDEVTYPE); -coresight_simple_func(trcpidr0, TRCPIDR0); -coresight_simple_func(trcpidr1, TRCPIDR1); -coresight_simple_func(trcpidr2, TRCPIDR2); -coresight_simple_func(trcpidr3, TRCPIDR3); - -static struct attribute *coresight_etmv4_mgmt_attrs[] = { - &dev_attr_trcoslsr.attr, - &dev_attr_trcpdcr.attr, - &dev_attr_trcpdsr.attr, - &dev_attr_trclsr.attr, - &dev_attr_trcauthstatus.attr, - &dev_attr_trcdevid.attr, - &dev_attr_trcdevtype.attr, - &dev_attr_trcpidr0.attr, - &dev_attr_trcpidr1.attr, - &dev_attr_trcpidr2.attr, - &dev_attr_trcpidr3.attr, - NULL, -}; - -coresight_simple_func(trcidr0, TRCIDR0); -coresight_simple_func(trcidr1, TRCIDR1); -coresight_simple_func(trcidr2, TRCIDR2); -coresight_simple_func(trcidr3, TRCIDR3); -coresight_simple_func(trcidr4, TRCIDR4); -coresight_simple_func(trcidr5, TRCIDR5); -/* trcidr[6,7] are reserved */ -coresight_simple_func(trcidr8, TRCIDR8); -coresight_simple_func(trcidr9, TRCIDR9); -coresight_simple_func(trcidr10, TRCIDR10); -coresight_simple_func(trcidr11, TRCIDR11); -coresight_simple_func(trcidr12, TRCIDR12); -coresight_simple_func(trcidr13, TRCIDR13); - -static struct attribute *coresight_etmv4_trcidr_attrs[] = { - &dev_attr_trcidr0.attr, - &dev_attr_trcidr1.attr, - &dev_attr_trcidr2.attr, - &dev_attr_trcidr3.attr, - &dev_attr_trcidr4.attr, - &dev_attr_trcidr5.attr, - /* trcidr[6,7] are reserved */ - &dev_attr_trcidr8.attr, - &dev_attr_trcidr9.attr, - &dev_attr_trcidr10.attr, - &dev_attr_trcidr11.attr, - &dev_attr_trcidr12.attr, - &dev_attr_trcidr13.attr, - NULL, -}; - -static const struct attribute_group coresight_etmv4_group = { - .attrs = coresight_etmv4_attrs, -}; - -static const struct attribute_group coresight_etmv4_mgmt_group = { - .attrs = coresight_etmv4_mgmt_attrs, - .name = "mgmt", -}; - -static const struct attribute_group coresight_etmv4_trcidr_group = { - .attrs = coresight_etmv4_trcidr_attrs, - .name = "trcidr", -}; - -static const struct attribute_group *coresight_etmv4_groups[] = { - &coresight_etmv4_group, - &coresight_etmv4_mgmt_group, - &coresight_etmv4_trcidr_group, - NULL, -}; - static void etm4_init_arch_data(void *info) { u32 etmidr0; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index c34100205ca9..e6ff9aae2a7e 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -391,4 +391,6 @@ enum etm_addr_type { ETM_ADDR_TYPE_START, ETM_ADDR_TYPE_STOP, }; + +extern const struct attribute_group *coresight_etmv4_groups[]; #endif From 2b888e472c42df49c54da0ef0ec2a3bd4e158c9b Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:43 -0600 Subject: [PATCH 200/656] coresight: etm4x: adding config and traceid registers Adding new sysFS management interface to query the configuration and the traceid registers. Both are required to convey information to the perf cmd line tools when using ETMv4 tracers as PMU. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 7c38aa4b03b3fc6ce17e5a00327f8c0be18daf8a) --- .../ABI/testing/sysfs-bus-coresight-devices-etm4x | 13 +++++++++++++ drivers/hwtracing/coresight/coresight-etm4x-sysfs.c | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x index 2355ed8ae31f..36258bc1b473 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x @@ -359,6 +359,19 @@ Contact: Mathieu Poirier Description: (R) Print the content of the Peripheral ID3 Register (0xFEC). The value is taken directly from the HW. +What: /sys/bus/coresight/devices/.etm/mgmt/trcconfig +Date: February 2016 +KernelVersion: 4.07 +Contact: Mathieu Poirier +Description: (R) Print the content of the trace configuration register + (0x010) as currently set by SW. + +What: /sys/bus/coresight/devices/.etm/mgmt/trctraceid +Date: February 2016 +KernelVersion: 4.07 +Contact: Mathieu Poirier +Description: (R) Print the content of the trace ID register (0x040). + What: /sys/bus/coresight/devices/.etm/trcidr/trcidr0 Date: April 2015 KernelVersion: 4.01 diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 5db0de7d4e0e..39a8b077e0c2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -1972,6 +1972,8 @@ coresight_simple_func(trcoslsr, TRCOSLSR); coresight_simple_func(trcpdcr, TRCPDCR); coresight_simple_func(trcpdsr, TRCPDSR); coresight_simple_func(trclsr, TRCLSR); +coresight_simple_func(trcconfig, TRCCONFIGR); +coresight_simple_func(trctraceid, TRCTRACEIDR); coresight_simple_func(trcauthstatus, TRCAUTHSTATUS); coresight_simple_func(trcdevid, TRCDEVID); coresight_simple_func(trcdevtype, TRCDEVTYPE); @@ -1985,6 +1987,8 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = { &dev_attr_trcpdcr.attr, &dev_attr_trcpdsr.attr, &dev_attr_trclsr.attr, + &dev_attr_trcconfig.attr, + &dev_attr_trctraceid.attr, &dev_attr_trcauthstatus.attr, &dev_attr_trcdevid.attr, &dev_attr_trcdevtype.attr, From 640cc6adfefeeea9a2f66d16bff1bb2f547acbc6 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:44 -0600 Subject: [PATCH 201/656] coresight: etm4x: splitting struct etmv4_drvdata Similar to what was done on etm3x, splitting driver structure etmv4_drvdata in two. One half is concerned with the HW characteristics that are generally static in nature. The other half deals with user configuration and will change from one trace session to another. No gain/loss of functionality is incurred from this patch. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 54ff892b76c68ea3fa0ba53a0cdc4508b35aee6f) --- .../coresight/coresight-etm4x-sysfs.c | 642 ++++++++++-------- drivers/hwtracing/coresight/coresight-etm4x.c | 134 ++-- drivers/hwtracing/coresight/coresight-etm4x.h | 202 +++--- 3 files changed, 533 insertions(+), 445 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 39a8b077e0c2..a996db7ef2fc 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -21,13 +21,16 @@ static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) { - u8 idx = drvdata->addr_idx; + u8 idx; + struct etmv4_config *config = &drvdata->config; + + idx = config->addr_idx; /* * TRCACATRn.TYPE bit[1:0]: type of comparison * the trace unit performs */ - if (BMVAL(drvdata->addr_acc[idx], 0, 1) == ETM_INSTR_ADDR) { + if (BMVAL(config->addr_acc[idx], 0, 1) == ETM_INSTR_ADDR) { if (idx % 2 != 0) return -EINVAL; @@ -36,8 +39,8 @@ static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) * relevant bit of ViewInst Include/Exclude Control register * for corresponding address comparator pair. */ - if (drvdata->addr_type[idx] != ETM_ADDR_TYPE_RANGE || - drvdata->addr_type[idx + 1] != ETM_ADDR_TYPE_RANGE) + if (config->addr_type[idx] != ETM_ADDR_TYPE_RANGE || + config->addr_type[idx + 1] != ETM_ADDR_TYPE_RANGE) return -EINVAL; if (exclude == true) { @@ -45,15 +48,15 @@ static int etm4_set_mode_exclude(struct etmv4_drvdata *drvdata, bool exclude) * Set exclude bit and unset the include bit * corresponding to comparator pair */ - drvdata->viiectlr |= BIT(idx / 2 + 16); - drvdata->viiectlr &= ~BIT(idx / 2); + config->viiectlr |= BIT(idx / 2 + 16); + config->viiectlr &= ~BIT(idx / 2); } else { /* * Set include bit and unset exclude bit * corresponding to comparator pair */ - drvdata->viiectlr |= BIT(idx / 2); - drvdata->viiectlr &= ~BIT(idx / 2 + 16); + config->viiectlr |= BIT(idx / 2); + config->viiectlr &= ~BIT(idx / 2 + 16); } } return 0; @@ -174,104 +177,107 @@ static ssize_t reset_store(struct device *dev, int i; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); if (val) - drvdata->mode = 0x0; + config->mode = 0x0; /* Disable data tracing: do not trace load and store data transfers */ - drvdata->mode &= ~(ETM_MODE_LOAD | ETM_MODE_STORE); - drvdata->cfg &= ~(BIT(1) | BIT(2)); + config->mode &= ~(ETM_MODE_LOAD | ETM_MODE_STORE); + config->cfg &= ~(BIT(1) | BIT(2)); /* Disable data value and data address tracing */ - drvdata->mode &= ~(ETM_MODE_DATA_TRACE_ADDR | + config->mode &= ~(ETM_MODE_DATA_TRACE_ADDR | ETM_MODE_DATA_TRACE_VAL); - drvdata->cfg &= ~(BIT(16) | BIT(17)); + config->cfg &= ~(BIT(16) | BIT(17)); /* Disable all events tracing */ - drvdata->eventctrl0 = 0x0; - drvdata->eventctrl1 = 0x0; + config->eventctrl0 = 0x0; + config->eventctrl1 = 0x0; /* Disable timestamp event */ - drvdata->ts_ctrl = 0x0; + config->ts_ctrl = 0x0; /* Disable stalling */ - drvdata->stall_ctrl = 0x0; + config->stall_ctrl = 0x0; /* Reset trace synchronization period to 2^8 = 256 bytes*/ if (drvdata->syncpr == false) - drvdata->syncfreq = 0x8; + config->syncfreq = 0x8; /* * Enable ViewInst to trace everything with start-stop logic in * started state. ARM recommends start-stop logic is set before * each trace run. */ - drvdata->vinst_ctrl |= BIT(0); + config->vinst_ctrl |= BIT(0); if (drvdata->nr_addr_cmp == true) { - drvdata->mode |= ETM_MODE_VIEWINST_STARTSTOP; + config->mode |= ETM_MODE_VIEWINST_STARTSTOP; /* SSSTATUS, bit[9] */ - drvdata->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); } /* No address range filtering for ViewInst */ - drvdata->viiectlr = 0x0; + config->viiectlr = 0x0; /* No start-stop filtering for ViewInst */ - drvdata->vissctlr = 0x0; + config->vissctlr = 0x0; /* Disable seq events */ for (i = 0; i < drvdata->nrseqstate-1; i++) - drvdata->seq_ctrl[i] = 0x0; - drvdata->seq_rst = 0x0; - drvdata->seq_state = 0x0; + config->seq_ctrl[i] = 0x0; + config->seq_rst = 0x0; + config->seq_state = 0x0; /* Disable external input events */ - drvdata->ext_inp = 0x0; + config->ext_inp = 0x0; - drvdata->cntr_idx = 0x0; + config->cntr_idx = 0x0; for (i = 0; i < drvdata->nr_cntr; i++) { - drvdata->cntrldvr[i] = 0x0; - drvdata->cntr_ctrl[i] = 0x0; - drvdata->cntr_val[i] = 0x0; + config->cntrldvr[i] = 0x0; + config->cntr_ctrl[i] = 0x0; + config->cntr_val[i] = 0x0; } - drvdata->res_idx = 0x0; + config->res_idx = 0x0; for (i = 0; i < drvdata->nr_resource; i++) - drvdata->res_ctrl[i] = 0x0; + config->res_ctrl[i] = 0x0; for (i = 0; i < drvdata->nr_ss_cmp; i++) { - drvdata->ss_ctrl[i] = 0x0; - drvdata->ss_pe_cmp[i] = 0x0; + config->ss_ctrl[i] = 0x0; + config->ss_pe_cmp[i] = 0x0; } - drvdata->addr_idx = 0x0; + config->addr_idx = 0x0; for (i = 0; i < drvdata->nr_addr_cmp * 2; i++) { - drvdata->addr_val[i] = 0x0; - drvdata->addr_acc[i] = 0x0; - drvdata->addr_type[i] = ETM_ADDR_TYPE_NONE; + config->addr_val[i] = 0x0; + config->addr_acc[i] = 0x0; + config->addr_type[i] = ETM_ADDR_TYPE_NONE; } - drvdata->ctxid_idx = 0x0; + config->ctxid_idx = 0x0; for (i = 0; i < drvdata->numcidc; i++) { - drvdata->ctxid_pid[i] = 0x0; - drvdata->ctxid_vpid[i] = 0x0; + config->ctxid_pid[i] = 0x0; + config->ctxid_vpid[i] = 0x0; } - drvdata->ctxid_mask0 = 0x0; - drvdata->ctxid_mask1 = 0x0; + config->ctxid_mask0 = 0x0; + config->ctxid_mask1 = 0x0; - drvdata->vmid_idx = 0x0; + config->vmid_idx = 0x0; for (i = 0; i < drvdata->numvmidc; i++) - drvdata->vmid_val[i] = 0x0; - drvdata->vmid_mask0 = 0x0; - drvdata->vmid_mask1 = 0x0; + config->vmid_val[i] = 0x0; + config->vmid_mask0 = 0x0; + config->vmid_mask1 = 0x0; drvdata->trcid = drvdata->cpu + 1; + spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_WO(reset); @@ -282,8 +288,9 @@ static ssize_t mode_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->mode; + val = config->mode; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -293,146 +300,148 @@ static ssize_t mode_store(struct device *dev, { unsigned long val, mode; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - drvdata->mode = val & ETMv4_MODE_ALL; + config->mode = val & ETMv4_MODE_ALL; - if (drvdata->mode & ETM_MODE_EXCLUDE) + if (config->mode & ETM_MODE_EXCLUDE) etm4_set_mode_exclude(drvdata, true); else etm4_set_mode_exclude(drvdata, false); if (drvdata->instrp0 == true) { /* start by clearing instruction P0 field */ - drvdata->cfg &= ~(BIT(1) | BIT(2)); - if (drvdata->mode & ETM_MODE_LOAD) + config->cfg &= ~(BIT(1) | BIT(2)); + if (config->mode & ETM_MODE_LOAD) /* 0b01 Trace load instructions as P0 instructions */ - drvdata->cfg |= BIT(1); - if (drvdata->mode & ETM_MODE_STORE) + config->cfg |= BIT(1); + if (config->mode & ETM_MODE_STORE) /* 0b10 Trace store instructions as P0 instructions */ - drvdata->cfg |= BIT(2); - if (drvdata->mode & ETM_MODE_LOAD_STORE) + config->cfg |= BIT(2); + if (config->mode & ETM_MODE_LOAD_STORE) /* * 0b11 Trace load and store instructions * as P0 instructions */ - drvdata->cfg |= BIT(1) | BIT(2); + config->cfg |= BIT(1) | BIT(2); } /* bit[3], Branch broadcast mode */ - if ((drvdata->mode & ETM_MODE_BB) && (drvdata->trcbb == true)) - drvdata->cfg |= BIT(3); + if ((config->mode & ETM_MODE_BB) && (drvdata->trcbb == true)) + config->cfg |= BIT(3); else - drvdata->cfg &= ~BIT(3); + config->cfg &= ~BIT(3); /* bit[4], Cycle counting instruction trace bit */ - if ((drvdata->mode & ETMv4_MODE_CYCACC) && + if ((config->mode & ETMv4_MODE_CYCACC) && (drvdata->trccci == true)) - drvdata->cfg |= BIT(4); + config->cfg |= BIT(4); else - drvdata->cfg &= ~BIT(4); + config->cfg &= ~BIT(4); /* bit[6], Context ID tracing bit */ - if ((drvdata->mode & ETMv4_MODE_CTXID) && (drvdata->ctxid_size)) - drvdata->cfg |= BIT(6); + if ((config->mode & ETMv4_MODE_CTXID) && (drvdata->ctxid_size)) + config->cfg |= BIT(6); else - drvdata->cfg &= ~BIT(6); + config->cfg &= ~BIT(6); - if ((drvdata->mode & ETM_MODE_VMID) && (drvdata->vmid_size)) - drvdata->cfg |= BIT(7); + if ((config->mode & ETM_MODE_VMID) && (drvdata->vmid_size)) + config->cfg |= BIT(7); else - drvdata->cfg &= ~BIT(7); + config->cfg &= ~BIT(7); /* bits[10:8], Conditional instruction tracing bit */ - mode = ETM_MODE_COND(drvdata->mode); + mode = ETM_MODE_COND(config->mode); if (drvdata->trccond == true) { - drvdata->cfg &= ~(BIT(8) | BIT(9) | BIT(10)); - drvdata->cfg |= mode << 8; + config->cfg &= ~(BIT(8) | BIT(9) | BIT(10)); + config->cfg |= mode << 8; } /* bit[11], Global timestamp tracing bit */ - if ((drvdata->mode & ETMv4_MODE_TIMESTAMP) && (drvdata->ts_size)) - drvdata->cfg |= BIT(11); + if ((config->mode & ETMv4_MODE_TIMESTAMP) && (drvdata->ts_size)) + config->cfg |= BIT(11); else - drvdata->cfg &= ~BIT(11); + config->cfg &= ~BIT(11); /* bit[12], Return stack enable bit */ - if ((drvdata->mode & ETM_MODE_RETURNSTACK) && - (drvdata->retstack == true)) - drvdata->cfg |= BIT(12); + if ((config->mode & ETM_MODE_RETURNSTACK) && + (drvdata->retstack == true)) + config->cfg |= BIT(12); else - drvdata->cfg &= ~BIT(12); + config->cfg &= ~BIT(12); /* bits[14:13], Q element enable field */ - mode = ETM_MODE_QELEM(drvdata->mode); + mode = ETM_MODE_QELEM(config->mode); /* start by clearing QE bits */ - drvdata->cfg &= ~(BIT(13) | BIT(14)); + config->cfg &= ~(BIT(13) | BIT(14)); /* if supported, Q elements with instruction counts are enabled */ if ((mode & BIT(0)) && (drvdata->q_support & BIT(0))) - drvdata->cfg |= BIT(13); + config->cfg |= BIT(13); /* * if supported, Q elements with and without instruction * counts are enabled */ if ((mode & BIT(1)) && (drvdata->q_support & BIT(1))) - drvdata->cfg |= BIT(14); + config->cfg |= BIT(14); /* bit[11], AMBA Trace Bus (ATB) trigger enable bit */ - if ((drvdata->mode & ETM_MODE_ATB_TRIGGER) && + if ((config->mode & ETM_MODE_ATB_TRIGGER) && (drvdata->atbtrig == true)) - drvdata->eventctrl1 |= BIT(11); + config->eventctrl1 |= BIT(11); else - drvdata->eventctrl1 &= ~BIT(11); + config->eventctrl1 &= ~BIT(11); /* bit[12], Low-power state behavior override bit */ - if ((drvdata->mode & ETM_MODE_LPOVERRIDE) && + if ((config->mode & ETM_MODE_LPOVERRIDE) && (drvdata->lpoverride == true)) - drvdata->eventctrl1 |= BIT(12); + config->eventctrl1 |= BIT(12); else - drvdata->eventctrl1 &= ~BIT(12); + config->eventctrl1 &= ~BIT(12); /* bit[8], Instruction stall bit */ - if (drvdata->mode & ETM_MODE_ISTALL_EN) - drvdata->stall_ctrl |= BIT(8); + if (config->mode & ETM_MODE_ISTALL_EN) + config->stall_ctrl |= BIT(8); else - drvdata->stall_ctrl &= ~BIT(8); + config->stall_ctrl &= ~BIT(8); /* bit[10], Prioritize instruction trace bit */ - if (drvdata->mode & ETM_MODE_INSTPRIO) - drvdata->stall_ctrl |= BIT(10); + if (config->mode & ETM_MODE_INSTPRIO) + config->stall_ctrl |= BIT(10); else - drvdata->stall_ctrl &= ~BIT(10); + config->stall_ctrl &= ~BIT(10); /* bit[13], Trace overflow prevention bit */ - if ((drvdata->mode & ETM_MODE_NOOVERFLOW) && + if ((config->mode & ETM_MODE_NOOVERFLOW) && (drvdata->nooverflow == true)) - drvdata->stall_ctrl |= BIT(13); + config->stall_ctrl |= BIT(13); else - drvdata->stall_ctrl &= ~BIT(13); + config->stall_ctrl &= ~BIT(13); /* bit[9] Start/stop logic control bit */ - if (drvdata->mode & ETM_MODE_VIEWINST_STARTSTOP) - drvdata->vinst_ctrl |= BIT(9); + if (config->mode & ETM_MODE_VIEWINST_STARTSTOP) + config->vinst_ctrl |= BIT(9); else - drvdata->vinst_ctrl &= ~BIT(9); + config->vinst_ctrl &= ~BIT(9); /* bit[10], Whether a trace unit must trace a Reset exception */ - if (drvdata->mode & ETM_MODE_TRACE_RESET) - drvdata->vinst_ctrl |= BIT(10); + if (config->mode & ETM_MODE_TRACE_RESET) + config->vinst_ctrl |= BIT(10); else - drvdata->vinst_ctrl &= ~BIT(10); + config->vinst_ctrl &= ~BIT(10); /* bit[11], Whether a trace unit must trace a system error exception */ - if ((drvdata->mode & ETM_MODE_TRACE_ERR) && + if ((config->mode & ETM_MODE_TRACE_ERR) && (drvdata->trc_error == true)) - drvdata->vinst_ctrl |= BIT(11); + config->vinst_ctrl |= BIT(11); else - drvdata->vinst_ctrl &= ~BIT(11); + config->vinst_ctrl &= ~BIT(11); spin_unlock(&drvdata->spinlock); + return size; } static DEVICE_ATTR_RW(mode); @@ -443,8 +452,9 @@ static ssize_t pe_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->pe_sel; + val = config->pe_sel; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -454,6 +464,7 @@ static ssize_t pe_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -464,7 +475,7 @@ static ssize_t pe_store(struct device *dev, return -EINVAL; } - drvdata->pe_sel = val; + config->pe_sel = val; spin_unlock(&drvdata->spinlock); return size; } @@ -476,8 +487,9 @@ static ssize_t event_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->eventctrl0; + val = config->eventctrl0; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -487,6 +499,7 @@ static ssize_t event_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -495,19 +508,19 @@ static ssize_t event_store(struct device *dev, switch (drvdata->nr_event) { case 0x0: /* EVENT0, bits[7:0] */ - drvdata->eventctrl0 = val & 0xFF; + config->eventctrl0 = val & 0xFF; break; case 0x1: /* EVENT1, bits[15:8] */ - drvdata->eventctrl0 = val & 0xFFFF; + config->eventctrl0 = val & 0xFFFF; break; case 0x2: /* EVENT2, bits[23:16] */ - drvdata->eventctrl0 = val & 0xFFFFFF; + config->eventctrl0 = val & 0xFFFFFF; break; case 0x3: /* EVENT3, bits[31:24] */ - drvdata->eventctrl0 = val; + config->eventctrl0 = val; break; default: break; @@ -523,8 +536,9 @@ static ssize_t event_instren_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = BMVAL(drvdata->eventctrl1, 0, 3); + val = BMVAL(config->eventctrl1, 0, 3); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -534,29 +548,30 @@ static ssize_t event_instren_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); /* start by clearing all instruction event enable bits */ - drvdata->eventctrl1 &= ~(BIT(0) | BIT(1) | BIT(2) | BIT(3)); + config->eventctrl1 &= ~(BIT(0) | BIT(1) | BIT(2) | BIT(3)); switch (drvdata->nr_event) { case 0x0: /* generate Event element for event 1 */ - drvdata->eventctrl1 |= val & BIT(1); + config->eventctrl1 |= val & BIT(1); break; case 0x1: /* generate Event element for event 1 and 2 */ - drvdata->eventctrl1 |= val & (BIT(0) | BIT(1)); + config->eventctrl1 |= val & (BIT(0) | BIT(1)); break; case 0x2: /* generate Event element for event 1, 2 and 3 */ - drvdata->eventctrl1 |= val & (BIT(0) | BIT(1) | BIT(2)); + config->eventctrl1 |= val & (BIT(0) | BIT(1) | BIT(2)); break; case 0x3: /* generate Event element for all 4 events */ - drvdata->eventctrl1 |= val & 0xF; + config->eventctrl1 |= val & 0xF; break; default: break; @@ -572,8 +587,9 @@ static ssize_t event_ts_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->ts_ctrl; + val = config->ts_ctrl; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -583,13 +599,14 @@ static ssize_t event_ts_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (!drvdata->ts_size) return -EINVAL; - drvdata->ts_ctrl = val & ETMv4_EVENT_MASK; + config->ts_ctrl = val & ETMv4_EVENT_MASK; return size; } static DEVICE_ATTR_RW(event_ts); @@ -600,8 +617,9 @@ static ssize_t syncfreq_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->syncfreq; + val = config->syncfreq; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -611,13 +629,14 @@ static ssize_t syncfreq_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (drvdata->syncpr == true) return -EINVAL; - drvdata->syncfreq = val & ETMv4_SYNC_MASK; + config->syncfreq = val & ETMv4_SYNC_MASK; return size; } static DEVICE_ATTR_RW(syncfreq); @@ -628,8 +647,9 @@ static ssize_t cyc_threshold_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->ccctlr; + val = config->ccctlr; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -639,13 +659,14 @@ static ssize_t cyc_threshold_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (val < drvdata->ccitmin) return -EINVAL; - drvdata->ccctlr = val & ETM_CYC_THRESHOLD_MASK; + config->ccctlr = val & ETM_CYC_THRESHOLD_MASK; return size; } static DEVICE_ATTR_RW(cyc_threshold); @@ -656,8 +677,9 @@ static ssize_t bb_ctrl_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->bb_ctrl; + val = config->bb_ctrl; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -667,6 +689,7 @@ static ssize_t bb_ctrl_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -681,7 +704,7 @@ static ssize_t bb_ctrl_store(struct device *dev, if (BMVAL(val, 0, 7) > drvdata->nr_addr_cmp) return -EINVAL; - drvdata->bb_ctrl = val; + config->bb_ctrl = val; return size; } static DEVICE_ATTR_RW(bb_ctrl); @@ -692,8 +715,9 @@ static ssize_t event_vinst_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->vinst_ctrl & ETMv4_EVENT_MASK; + val = config->vinst_ctrl & ETMv4_EVENT_MASK; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -703,14 +727,15 @@ static ssize_t event_vinst_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); val &= ETMv4_EVENT_MASK; - drvdata->vinst_ctrl &= ~ETMv4_EVENT_MASK; - drvdata->vinst_ctrl |= val; + config->vinst_ctrl &= ~ETMv4_EVENT_MASK; + config->vinst_ctrl |= val; spin_unlock(&drvdata->spinlock); return size; } @@ -722,8 +747,9 @@ static ssize_t s_exlevel_vinst_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = BMVAL(drvdata->vinst_ctrl, 16, 19); + val = BMVAL(config->vinst_ctrl, 16, 19); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -733,16 +759,17 @@ static ssize_t s_exlevel_vinst_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); /* clear all EXLEVEL_S bits (bit[18] is never implemented) */ - drvdata->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19)); + config->vinst_ctrl &= ~(BIT(16) | BIT(17) | BIT(19)); /* enable instruction tracing for corresponding exception level */ val &= drvdata->s_ex_level; - drvdata->vinst_ctrl |= (val << 16); + config->vinst_ctrl |= (val << 16); spin_unlock(&drvdata->spinlock); return size; } @@ -754,9 +781,10 @@ static ssize_t ns_exlevel_vinst_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; /* EXLEVEL_NS, bits[23:20] */ - val = BMVAL(drvdata->vinst_ctrl, 20, 23); + val = BMVAL(config->vinst_ctrl, 20, 23); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -766,16 +794,17 @@ static ssize_t ns_exlevel_vinst_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); /* clear EXLEVEL_NS bits (bit[23] is never implemented */ - drvdata->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22)); + config->vinst_ctrl &= ~(BIT(20) | BIT(21) | BIT(22)); /* enable instruction tracing for corresponding exception level */ val &= drvdata->ns_ex_level; - drvdata->vinst_ctrl |= (val << 20); + config->vinst_ctrl |= (val << 20); spin_unlock(&drvdata->spinlock); return size; } @@ -787,8 +816,9 @@ static ssize_t addr_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->addr_idx; + val = config->addr_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -798,6 +828,7 @@ static ssize_t addr_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -809,7 +840,7 @@ static ssize_t addr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->addr_idx = val; + config->addr_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -822,10 +853,11 @@ static ssize_t addr_instdatatype_show(struct device *dev, ssize_t len; u8 val, idx; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - val = BMVAL(drvdata->addr_acc[idx], 0, 1); + idx = config->addr_idx; + val = BMVAL(config->addr_acc[idx], 0, 1); len = scnprintf(buf, PAGE_SIZE, "%s\n", val == ETM_INSTR_ADDR ? "instr" : (val == ETM_DATA_LOAD_ADDR ? "data_load" : @@ -842,6 +874,7 @@ static ssize_t addr_instdatatype_store(struct device *dev, u8 idx; char str[20] = ""; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (strlen(buf) >= 20) return -EINVAL; @@ -849,10 +882,10 @@ static ssize_t addr_instdatatype_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (!strcmp(str, "instr")) /* TYPE, bits[1:0] */ - drvdata->addr_acc[idx] &= ~(BIT(0) | BIT(1)); + config->addr_acc[idx] &= ~(BIT(0) | BIT(1)); spin_unlock(&drvdata->spinlock); return size; @@ -866,15 +899,16 @@ static ssize_t addr_single_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - idx = drvdata->addr_idx; + idx = config->addr_idx; spin_lock(&drvdata->spinlock); - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = (unsigned long)drvdata->addr_val[idx]; + val = (unsigned long)config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -886,20 +920,21 @@ static ssize_t addr_single_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { + idx = config->addr_idx; + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_SINGLE)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; + config->addr_val[idx] = (u64)val; + config->addr_type[idx] = ETM_ADDR_TYPE_SINGLE; spin_unlock(&drvdata->spinlock); return size; } @@ -912,23 +947,24 @@ static ssize_t addr_range_show(struct device *dev, u8 idx; unsigned long val1, val2; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val1 = (unsigned long)drvdata->addr_val[idx]; - val2 = (unsigned long)drvdata->addr_val[idx + 1]; + val1 = (unsigned long)config->addr_val[idx]; + val2 = (unsigned long)config->addr_val[idx + 1]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); } @@ -940,6 +976,7 @@ static ssize_t addr_range_store(struct device *dev, u8 idx; unsigned long val1, val2; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (sscanf(buf, "%lx %lx", &val1, &val2) != 2) return -EINVAL; @@ -948,29 +985,29 @@ static ssize_t addr_range_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (idx % 2 != 0) { spin_unlock(&drvdata->spinlock); return -EPERM; } - if (!((drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || - (drvdata->addr_type[idx] == ETM_ADDR_TYPE_RANGE && - drvdata->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { + if (!((config->addr_type[idx] == ETM_ADDR_TYPE_NONE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_NONE) || + (config->addr_type[idx] == ETM_ADDR_TYPE_RANGE && + config->addr_type[idx + 1] == ETM_ADDR_TYPE_RANGE))) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = (u64)val1; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_val[idx + 1] = (u64)val2; - drvdata->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; + config->addr_val[idx] = (u64)val1; + config->addr_type[idx] = ETM_ADDR_TYPE_RANGE; + config->addr_val[idx + 1] = (u64)val2; + config->addr_type[idx + 1] = ETM_ADDR_TYPE_RANGE; /* * Program include or exclude control bits for vinst or vdata * whenever we change addr comparators to ETM_ADDR_TYPE_RANGE */ - if (drvdata->mode & ETM_MODE_EXCLUDE) + if (config->mode & ETM_MODE_EXCLUDE) etm4_set_mode_exclude(drvdata, true); else etm4_set_mode_exclude(drvdata, false); @@ -987,17 +1024,18 @@ static ssize_t addr_start_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = (unsigned long)drvdata->addr_val[idx]; + val = (unsigned long)config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1009,27 +1047,28 @@ static ssize_t addr_start_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (!drvdata->nr_addr_cmp) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_START)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_START)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_START; - drvdata->vissctlr |= BIT(idx); + config->addr_val[idx] = (u64)val; + config->addr_type[idx] = ETM_ADDR_TYPE_START; + config->vissctlr |= BIT(idx); /* SSSTATUS, bit[9] - turn on start/stop logic */ - drvdata->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); spin_unlock(&drvdata->spinlock); return size; } @@ -1042,17 +1081,18 @@ static ssize_t addr_stop_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - val = (unsigned long)drvdata->addr_val[idx]; + val = (unsigned long)config->addr_val[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1064,27 +1104,28 @@ static ssize_t addr_stop_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (!drvdata->nr_addr_cmp) { spin_unlock(&drvdata->spinlock); return -EINVAL; } - if (!(drvdata->addr_type[idx] == ETM_ADDR_TYPE_NONE || - drvdata->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { + if (!(config->addr_type[idx] == ETM_ADDR_TYPE_NONE || + config->addr_type[idx] == ETM_ADDR_TYPE_STOP)) { spin_unlock(&drvdata->spinlock); return -EPERM; } - drvdata->addr_val[idx] = (u64)val; - drvdata->addr_type[idx] = ETM_ADDR_TYPE_STOP; - drvdata->vissctlr |= BIT(idx + 16); + config->addr_val[idx] = (u64)val; + config->addr_type[idx] = ETM_ADDR_TYPE_STOP; + config->vissctlr |= BIT(idx + 16); /* SSSTATUS, bit[9] - turn on start/stop logic */ - drvdata->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); spin_unlock(&drvdata->spinlock); return size; } @@ -1097,11 +1138,12 @@ static ssize_t addr_ctxtype_show(struct device *dev, ssize_t len; u8 idx, val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; /* CONTEXTTYPE, bits[3:2] */ - val = BMVAL(drvdata->addr_acc[idx], 2, 3); + val = BMVAL(config->addr_acc[idx], 2, 3); len = scnprintf(buf, PAGE_SIZE, "%s\n", val == ETM_CTX_NONE ? "none" : (val == ETM_CTX_CTXID ? "ctxid" : (val == ETM_CTX_VMID ? "vmid" : "all"))); @@ -1116,6 +1158,7 @@ static ssize_t addr_ctxtype_store(struct device *dev, u8 idx; char str[10] = ""; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (strlen(buf) >= 10) return -EINVAL; @@ -1123,21 +1166,21 @@ static ssize_t addr_ctxtype_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; if (!strcmp(str, "none")) /* start by clearing context type bits */ - drvdata->addr_acc[idx] &= ~(BIT(2) | BIT(3)); + config->addr_acc[idx] &= ~(BIT(2) | BIT(3)); else if (!strcmp(str, "ctxid")) { /* 0b01 The trace unit performs a Context ID */ if (drvdata->numcidc) { - drvdata->addr_acc[idx] |= BIT(2); - drvdata->addr_acc[idx] &= ~BIT(3); + config->addr_acc[idx] |= BIT(2); + config->addr_acc[idx] &= ~BIT(3); } } else if (!strcmp(str, "vmid")) { /* 0b10 The trace unit performs a VMID */ if (drvdata->numvmidc) { - drvdata->addr_acc[idx] &= ~BIT(2); - drvdata->addr_acc[idx] |= BIT(3); + config->addr_acc[idx] &= ~BIT(2); + config->addr_acc[idx] |= BIT(3); } } else if (!strcmp(str, "all")) { /* @@ -1145,9 +1188,9 @@ static ssize_t addr_ctxtype_store(struct device *dev, * comparison and a VMID */ if (drvdata->numcidc) - drvdata->addr_acc[idx] |= BIT(2); + config->addr_acc[idx] |= BIT(2); if (drvdata->numvmidc) - drvdata->addr_acc[idx] |= BIT(3); + config->addr_acc[idx] |= BIT(3); } spin_unlock(&drvdata->spinlock); return size; @@ -1161,11 +1204,12 @@ static ssize_t addr_context_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; /* context ID comparator bits[6:4] */ - val = BMVAL(drvdata->addr_acc[idx], 4, 6); + val = BMVAL(config->addr_acc[idx], 4, 6); spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1177,6 +1221,7 @@ static ssize_t addr_context_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1187,10 +1232,10 @@ static ssize_t addr_context_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->addr_idx; + idx = config->addr_idx; /* clear context ID comparator bits[6:4] */ - drvdata->addr_acc[idx] &= ~(BIT(4) | BIT(5) | BIT(6)); - drvdata->addr_acc[idx] |= (val << 4); + config->addr_acc[idx] &= ~(BIT(4) | BIT(5) | BIT(6)); + config->addr_acc[idx] |= (val << 4); spin_unlock(&drvdata->spinlock); return size; } @@ -1202,8 +1247,9 @@ static ssize_t seq_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->seq_idx; + val = config->seq_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1213,6 +1259,7 @@ static ssize_t seq_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1224,7 +1271,7 @@ static ssize_t seq_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->seq_idx = val; + config->seq_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1236,8 +1283,9 @@ static ssize_t seq_state_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->seq_state; + val = config->seq_state; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1247,13 +1295,14 @@ static ssize_t seq_state_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (val >= drvdata->nrseqstate) return -EINVAL; - drvdata->seq_state = val; + config->seq_state = val; return size; } static DEVICE_ATTR_RW(seq_state); @@ -1265,10 +1314,11 @@ static ssize_t seq_event_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->seq_idx; - val = drvdata->seq_ctrl[idx]; + idx = config->seq_idx; + val = config->seq_ctrl[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1280,14 +1330,15 @@ static ssize_t seq_event_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->seq_idx; + idx = config->seq_idx; /* RST, bits[7:0] */ - drvdata->seq_ctrl[idx] = val & 0xFF; + config->seq_ctrl[idx] = val & 0xFF; spin_unlock(&drvdata->spinlock); return size; } @@ -1299,8 +1350,9 @@ static ssize_t seq_reset_event_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->seq_rst; + val = config->seq_rst; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1310,13 +1362,14 @@ static ssize_t seq_reset_event_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; if (!(drvdata->nrseqstate)) return -EINVAL; - drvdata->seq_rst = val & ETMv4_EVENT_MASK; + config->seq_rst = val & ETMv4_EVENT_MASK; return size; } static DEVICE_ATTR_RW(seq_reset_event); @@ -1327,8 +1380,9 @@ static ssize_t cntr_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->cntr_idx; + val = config->cntr_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1338,6 +1392,7 @@ static ssize_t cntr_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1349,7 +1404,7 @@ static ssize_t cntr_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->cntr_idx = val; + config->cntr_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1362,10 +1417,11 @@ static ssize_t cntrldvr_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntrldvr[idx]; + idx = config->cntr_idx; + val = config->cntrldvr[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1377,6 +1433,7 @@ static ssize_t cntrldvr_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1384,8 +1441,8 @@ static ssize_t cntrldvr_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntrldvr[idx] = val; + idx = config->cntr_idx; + config->cntrldvr[idx] = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1398,10 +1455,11 @@ static ssize_t cntr_val_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntr_val[idx]; + idx = config->cntr_idx; + val = config->cntr_val[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1413,6 +1471,7 @@ static ssize_t cntr_val_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1420,8 +1479,8 @@ static ssize_t cntr_val_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntr_val[idx] = val; + idx = config->cntr_idx; + config->cntr_val[idx] = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1434,10 +1493,11 @@ static ssize_t cntr_ctrl_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - val = drvdata->cntr_ctrl[idx]; + idx = config->cntr_idx; + val = config->cntr_ctrl[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1449,13 +1509,14 @@ static ssize_t cntr_ctrl_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->cntr_idx; - drvdata->cntr_ctrl[idx] = val; + idx = config->cntr_idx; + config->cntr_ctrl[idx] = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1467,8 +1528,9 @@ static ssize_t res_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->res_idx; + val = config->res_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1478,6 +1540,7 @@ static ssize_t res_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1490,7 +1553,7 @@ static ssize_t res_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->res_idx = val; + config->res_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1503,10 +1566,11 @@ static ssize_t res_ctrl_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->res_idx; - val = drvdata->res_ctrl[idx]; + idx = config->res_idx; + val = config->res_ctrl[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1518,17 +1582,18 @@ static ssize_t res_ctrl_store(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; spin_lock(&drvdata->spinlock); - idx = drvdata->res_idx; + idx = config->res_idx; /* For odd idx pair inversal bit is RES0 */ if (idx % 2 != 0) /* PAIRINV, bit[21] */ val &= ~BIT(21); - drvdata->res_ctrl[idx] = val; + config->res_ctrl[idx] = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1540,8 +1605,9 @@ static ssize_t ctxid_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->ctxid_idx; + val = config->ctxid_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1551,6 +1617,7 @@ static ssize_t ctxid_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1562,7 +1629,7 @@ static ssize_t ctxid_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->ctxid_idx = val; + config->ctxid_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1575,10 +1642,11 @@ static ssize_t ctxid_pid_show(struct device *dev, u8 idx; unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - idx = drvdata->ctxid_idx; - val = (unsigned long)drvdata->ctxid_vpid[idx]; + idx = config->ctxid_idx; + val = (unsigned long)config->ctxid_vpid[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1590,6 +1658,7 @@ static ssize_t ctxid_pid_store(struct device *dev, u8 idx; unsigned long vpid, pid; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; /* * only implemented when ctxid tracing is enabled, i.e. at least one @@ -1604,9 +1673,9 @@ static ssize_t ctxid_pid_store(struct device *dev, pid = coresight_vpid_to_pid(vpid); spin_lock(&drvdata->spinlock); - idx = drvdata->ctxid_idx; - drvdata->ctxid_pid[idx] = (u64)pid; - drvdata->ctxid_vpid[idx] = (u64)vpid; + idx = config->ctxid_idx; + config->ctxid_pid[idx] = (u64)pid; + config->ctxid_vpid[idx] = (u64)vpid; spin_unlock(&drvdata->spinlock); return size; } @@ -1618,10 +1687,11 @@ static ssize_t ctxid_masks_show(struct device *dev, { unsigned long val1, val2; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val1 = drvdata->ctxid_mask0; - val2 = drvdata->ctxid_mask1; + val1 = config->ctxid_mask0; + val2 = config->ctxid_mask1; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); } @@ -1633,6 +1703,7 @@ static ssize_t ctxid_masks_store(struct device *dev, u8 i, j, maskbyte; unsigned long val1, val2, mask; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; /* * only implemented when ctxid tracing is enabled, i.e. at least one @@ -1652,39 +1723,39 @@ static ssize_t ctxid_masks_store(struct device *dev, switch (drvdata->numcidc) { case 0x1: /* COMP0, bits[7:0] */ - drvdata->ctxid_mask0 = val1 & 0xFF; + config->ctxid_mask0 = val1 & 0xFF; break; case 0x2: /* COMP1, bits[15:8] */ - drvdata->ctxid_mask0 = val1 & 0xFFFF; + config->ctxid_mask0 = val1 & 0xFFFF; break; case 0x3: /* COMP2, bits[23:16] */ - drvdata->ctxid_mask0 = val1 & 0xFFFFFF; + config->ctxid_mask0 = val1 & 0xFFFFFF; break; case 0x4: /* COMP3, bits[31:24] */ - drvdata->ctxid_mask0 = val1; + config->ctxid_mask0 = val1; break; case 0x5: /* COMP4, bits[7:0] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFF; + config->ctxid_mask0 = val1; + config->ctxid_mask1 = val2 & 0xFF; break; case 0x6: /* COMP5, bits[15:8] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFFFF; + config->ctxid_mask0 = val1; + config->ctxid_mask1 = val2 & 0xFFFF; break; case 0x7: /* COMP6, bits[23:16] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2 & 0xFFFFFF; + config->ctxid_mask0 = val1; + config->ctxid_mask1 = val2 & 0xFFFFFF; break; case 0x8: /* COMP7, bits[31:24] */ - drvdata->ctxid_mask0 = val1; - drvdata->ctxid_mask1 = val2; + config->ctxid_mask0 = val1; + config->ctxid_mask1 = val2; break; default: break; @@ -1695,7 +1766,7 @@ static ssize_t ctxid_masks_store(struct device *dev, * For example, if bit[3] of ctxid_mask0 is 1, we must clear bits[31:24] * of ctxid comparator0 value (corresponding to byte 0) register. */ - mask = drvdata->ctxid_mask0; + mask = config->ctxid_mask0; for (i = 0; i < drvdata->numcidc; i++) { /* mask value of corresponding ctxid comparator */ maskbyte = mask & ETMv4_EVENT_MASK; @@ -1705,13 +1776,13 @@ static ssize_t ctxid_masks_store(struct device *dev, */ for (j = 0; j < 8; j++) { if (maskbyte & 1) - drvdata->ctxid_pid[i] &= ~(0xFF << (j * 8)); + config->ctxid_pid[i] &= ~(0xFF << (j * 8)); maskbyte >>= 1; } /* Select the next ctxid comparator mask value */ if (i == 3) /* ctxid comparators[4-7] */ - mask = drvdata->ctxid_mask1; + mask = config->ctxid_mask1; else mask >>= 0x8; } @@ -1727,8 +1798,9 @@ static ssize_t vmid_idx_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = drvdata->vmid_idx; + val = config->vmid_idx; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1738,6 +1810,7 @@ static ssize_t vmid_idx_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; if (kstrtoul(buf, 16, &val)) return -EINVAL; @@ -1749,7 +1822,7 @@ static ssize_t vmid_idx_store(struct device *dev, * dereferenced multiple times within a spinlock block elsewhere. */ spin_lock(&drvdata->spinlock); - drvdata->vmid_idx = val; + config->vmid_idx = val; spin_unlock(&drvdata->spinlock); return size; } @@ -1761,8 +1834,9 @@ static ssize_t vmid_val_show(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; - val = (unsigned long)drvdata->vmid_val[drvdata->vmid_idx]; + val = (unsigned long)config->vmid_val[config->vmid_idx]; return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } @@ -1772,6 +1846,7 @@ static ssize_t vmid_val_store(struct device *dev, { unsigned long val; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; /* * only implemented when vmid tracing is enabled, i.e. at least one @@ -1783,7 +1858,7 @@ static ssize_t vmid_val_store(struct device *dev, return -EINVAL; spin_lock(&drvdata->spinlock); - drvdata->vmid_val[drvdata->vmid_idx] = (u64)val; + config->vmid_val[config->vmid_idx] = (u64)val; spin_unlock(&drvdata->spinlock); return size; } @@ -1794,10 +1869,11 @@ static ssize_t vmid_masks_show(struct device *dev, { unsigned long val1, val2; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; spin_lock(&drvdata->spinlock); - val1 = drvdata->vmid_mask0; - val2 = drvdata->vmid_mask1; + val1 = config->vmid_mask0; + val2 = config->vmid_mask1; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx %#lx\n", val1, val2); } @@ -1809,6 +1885,8 @@ static ssize_t vmid_masks_store(struct device *dev, u8 i, j, maskbyte; unsigned long val1, val2, mask; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); + struct etmv4_config *config = &drvdata->config; + /* * only implemented when vmid tracing is enabled, i.e. at least one * vmid comparator is implemented and at least 8 bit vmid size @@ -1827,39 +1905,39 @@ static ssize_t vmid_masks_store(struct device *dev, switch (drvdata->numvmidc) { case 0x1: /* COMP0, bits[7:0] */ - drvdata->vmid_mask0 = val1 & 0xFF; + config->vmid_mask0 = val1 & 0xFF; break; case 0x2: /* COMP1, bits[15:8] */ - drvdata->vmid_mask0 = val1 & 0xFFFF; + config->vmid_mask0 = val1 & 0xFFFF; break; case 0x3: /* COMP2, bits[23:16] */ - drvdata->vmid_mask0 = val1 & 0xFFFFFF; + config->vmid_mask0 = val1 & 0xFFFFFF; break; case 0x4: /* COMP3, bits[31:24] */ - drvdata->vmid_mask0 = val1; + config->vmid_mask0 = val1; break; case 0x5: /* COMP4, bits[7:0] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFF; + config->vmid_mask0 = val1; + config->vmid_mask1 = val2 & 0xFF; break; case 0x6: /* COMP5, bits[15:8] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFFFF; + config->vmid_mask0 = val1; + config->vmid_mask1 = val2 & 0xFFFF; break; case 0x7: /* COMP6, bits[23:16] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2 & 0xFFFFFF; + config->vmid_mask0 = val1; + config->vmid_mask1 = val2 & 0xFFFFFF; break; case 0x8: /* COMP7, bits[31:24] */ - drvdata->vmid_mask0 = val1; - drvdata->vmid_mask1 = val2; + config->vmid_mask0 = val1; + config->vmid_mask1 = val2; break; default: break; @@ -1871,7 +1949,7 @@ static ssize_t vmid_masks_store(struct device *dev, * For example, if bit[3] of vmid_mask0 is 1, we must clear bits[31:24] * of vmid comparator0 value (corresponding to byte 0) register. */ - mask = drvdata->vmid_mask0; + mask = config->vmid_mask0; for (i = 0; i < drvdata->numvmidc; i++) { /* mask value of corresponding vmid comparator */ maskbyte = mask & ETMv4_EVENT_MASK; @@ -1881,13 +1959,13 @@ static ssize_t vmid_masks_store(struct device *dev, */ for (j = 0; j < 8; j++) { if (maskbyte & 1) - drvdata->vmid_val[i] &= ~(0xFF << (j * 8)); + config->vmid_val[i] &= ~(0xFF << (j * 8)); maskbyte >>= 1; } /* Select the next vmid comparator mask value */ if (i == 3) /* vmid comparators[4-7] */ - mask = drvdata->vmid_mask1; + mask = config->vmid_mask1; else mask >>= 0x8; } diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 5cb919118d70..102e9aaf952b 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -95,6 +95,7 @@ static void etm4_enable_hw(void *info) { int i; struct etmv4_drvdata *drvdata = info; + struct etmv4_config *config = &drvdata->config; CS_UNLOCK(drvdata->base); @@ -109,69 +110,69 @@ static void etm4_enable_hw(void *info) "timeout observed when probing at offset %#x\n", TRCSTATR); - writel_relaxed(drvdata->pe_sel, drvdata->base + TRCPROCSELR); - writel_relaxed(drvdata->cfg, drvdata->base + TRCCONFIGR); + writel_relaxed(config->pe_sel, drvdata->base + TRCPROCSELR); + writel_relaxed(config->cfg, drvdata->base + TRCCONFIGR); /* nothing specific implemented */ writel_relaxed(0x0, drvdata->base + TRCAUXCTLR); - writel_relaxed(drvdata->eventctrl0, drvdata->base + TRCEVENTCTL0R); - writel_relaxed(drvdata->eventctrl1, drvdata->base + TRCEVENTCTL1R); - writel_relaxed(drvdata->stall_ctrl, drvdata->base + TRCSTALLCTLR); - writel_relaxed(drvdata->ts_ctrl, drvdata->base + TRCTSCTLR); - writel_relaxed(drvdata->syncfreq, drvdata->base + TRCSYNCPR); - writel_relaxed(drvdata->ccctlr, drvdata->base + TRCCCCTLR); - writel_relaxed(drvdata->bb_ctrl, drvdata->base + TRCBBCTLR); + writel_relaxed(config->eventctrl0, drvdata->base + TRCEVENTCTL0R); + writel_relaxed(config->eventctrl1, drvdata->base + TRCEVENTCTL1R); + writel_relaxed(config->stall_ctrl, drvdata->base + TRCSTALLCTLR); + writel_relaxed(config->ts_ctrl, drvdata->base + TRCTSCTLR); + writel_relaxed(config->syncfreq, drvdata->base + TRCSYNCPR); + writel_relaxed(config->ccctlr, drvdata->base + TRCCCCTLR); + writel_relaxed(config->bb_ctrl, drvdata->base + TRCBBCTLR); writel_relaxed(drvdata->trcid, drvdata->base + TRCTRACEIDR); - writel_relaxed(drvdata->vinst_ctrl, drvdata->base + TRCVICTLR); - writel_relaxed(drvdata->viiectlr, drvdata->base + TRCVIIECTLR); - writel_relaxed(drvdata->vissctlr, + writel_relaxed(config->vinst_ctrl, drvdata->base + TRCVICTLR); + writel_relaxed(config->viiectlr, drvdata->base + TRCVIIECTLR); + writel_relaxed(config->vissctlr, drvdata->base + TRCVISSCTLR); - writel_relaxed(drvdata->vipcssctlr, + writel_relaxed(config->vipcssctlr, drvdata->base + TRCVIPCSSCTLR); for (i = 0; i < drvdata->nrseqstate - 1; i++) - writel_relaxed(drvdata->seq_ctrl[i], + writel_relaxed(config->seq_ctrl[i], drvdata->base + TRCSEQEVRn(i)); - writel_relaxed(drvdata->seq_rst, drvdata->base + TRCSEQRSTEVR); - writel_relaxed(drvdata->seq_state, drvdata->base + TRCSEQSTR); - writel_relaxed(drvdata->ext_inp, drvdata->base + TRCEXTINSELR); + writel_relaxed(config->seq_rst, drvdata->base + TRCSEQRSTEVR); + writel_relaxed(config->seq_state, drvdata->base + TRCSEQSTR); + writel_relaxed(config->ext_inp, drvdata->base + TRCEXTINSELR); for (i = 0; i < drvdata->nr_cntr; i++) { - writel_relaxed(drvdata->cntrldvr[i], + writel_relaxed(config->cntrldvr[i], drvdata->base + TRCCNTRLDVRn(i)); - writel_relaxed(drvdata->cntr_ctrl[i], + writel_relaxed(config->cntr_ctrl[i], drvdata->base + TRCCNTCTLRn(i)); - writel_relaxed(drvdata->cntr_val[i], + writel_relaxed(config->cntr_val[i], drvdata->base + TRCCNTVRn(i)); } /* Resource selector pair 0 is always implemented and reserved */ - for (i = 2; i < drvdata->nr_resource * 2; i++) - writel_relaxed(drvdata->res_ctrl[i], + for (i = 0; i < drvdata->nr_resource * 2; i++) + writel_relaxed(config->res_ctrl[i], drvdata->base + TRCRSCTLRn(i)); for (i = 0; i < drvdata->nr_ss_cmp; i++) { - writel_relaxed(drvdata->ss_ctrl[i], + writel_relaxed(config->ss_ctrl[i], drvdata->base + TRCSSCCRn(i)); - writel_relaxed(drvdata->ss_status[i], + writel_relaxed(config->ss_status[i], drvdata->base + TRCSSCSRn(i)); - writel_relaxed(drvdata->ss_pe_cmp[i], + writel_relaxed(config->ss_pe_cmp[i], drvdata->base + TRCSSPCICRn(i)); } for (i = 0; i < drvdata->nr_addr_cmp; i++) { - writeq_relaxed(drvdata->addr_val[i], + writeq_relaxed(config->addr_val[i], drvdata->base + TRCACVRn(i)); - writeq_relaxed(drvdata->addr_acc[i], + writeq_relaxed(config->addr_acc[i], drvdata->base + TRCACATRn(i)); } for (i = 0; i < drvdata->numcidc; i++) - writeq_relaxed(drvdata->ctxid_pid[i], + writeq_relaxed(config->ctxid_pid[i], drvdata->base + TRCCIDCVRn(i)); - writel_relaxed(drvdata->ctxid_mask0, drvdata->base + TRCCIDCCTLR0); - writel_relaxed(drvdata->ctxid_mask1, drvdata->base + TRCCIDCCTLR1); + writel_relaxed(config->ctxid_mask0, drvdata->base + TRCCIDCCTLR0); + writel_relaxed(config->ctxid_mask1, drvdata->base + TRCCIDCCTLR1); for (i = 0; i < drvdata->numvmidc; i++) - writeq_relaxed(drvdata->vmid_val[i], + writeq_relaxed(config->vmid_val[i], drvdata->base + TRCVMIDCVRn(i)); - writel_relaxed(drvdata->vmid_mask0, drvdata->base + TRCVMIDCCTLR0); - writel_relaxed(drvdata->vmid_mask1, drvdata->base + TRCVMIDCCTLR1); + writel_relaxed(config->vmid_mask0, drvdata->base + TRCVMIDCCTLR0); + writel_relaxed(config->vmid_mask1, drvdata->base + TRCVMIDCCTLR1); /* Enable the trace unit */ writel_relaxed(1, drvdata->base + TRCPRGCTLR); @@ -438,83 +439,84 @@ static void etm4_init_arch_data(void *info) static void etm4_init_default_data(struct etmv4_drvdata *drvdata) { int i; + struct etmv4_config *config = &drvdata->config; - drvdata->pe_sel = 0x0; - drvdata->cfg = (ETMv4_MODE_CTXID | ETM_MODE_VMID | + config->pe_sel = 0x0; + config->cfg = (ETMv4_MODE_CTXID | ETM_MODE_VMID | ETMv4_MODE_TIMESTAMP | ETM_MODE_RETURNSTACK); /* disable all events tracing */ - drvdata->eventctrl0 = 0x0; - drvdata->eventctrl1 = 0x0; + config->eventctrl0 = 0x0; + config->eventctrl1 = 0x0; /* disable stalling */ - drvdata->stall_ctrl = 0x0; + config->stall_ctrl = 0x0; /* disable timestamp event */ - drvdata->ts_ctrl = 0x0; + config->ts_ctrl = 0x0; /* enable trace synchronization every 4096 bytes for trace */ if (drvdata->syncpr == false) - drvdata->syncfreq = 0xC; + config->syncfreq = 0xC; /* * enable viewInst to trace everything with start-stop logic in * started state */ - drvdata->vinst_ctrl |= BIT(0); + config->vinst_ctrl |= BIT(0); /* set initial state of start-stop logic */ if (drvdata->nr_addr_cmp) - drvdata->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); /* no address range filtering for ViewInst */ - drvdata->viiectlr = 0x0; + config->viiectlr = 0x0; /* no start-stop filtering for ViewInst */ - drvdata->vissctlr = 0x0; + config->vissctlr = 0x0; /* disable seq events */ for (i = 0; i < drvdata->nrseqstate-1; i++) - drvdata->seq_ctrl[i] = 0x0; - drvdata->seq_rst = 0x0; - drvdata->seq_state = 0x0; + config->seq_ctrl[i] = 0x0; + config->seq_rst = 0x0; + config->seq_state = 0x0; /* disable external input events */ - drvdata->ext_inp = 0x0; + config->ext_inp = 0x0; for (i = 0; i < drvdata->nr_cntr; i++) { - drvdata->cntrldvr[i] = 0x0; - drvdata->cntr_ctrl[i] = 0x0; - drvdata->cntr_val[i] = 0x0; + config->cntrldvr[i] = 0x0; + config->cntr_ctrl[i] = 0x0; + config->cntr_val[i] = 0x0; } /* Resource selector pair 0 is always implemented and reserved */ - drvdata->res_idx = 0x2; + config->res_idx = 0x2; for (i = 2; i < drvdata->nr_resource * 2; i++) - drvdata->res_ctrl[i] = 0x0; + config->res_ctrl[i] = 0x0; for (i = 0; i < drvdata->nr_ss_cmp; i++) { - drvdata->ss_ctrl[i] = 0x0; - drvdata->ss_pe_cmp[i] = 0x0; + config->ss_ctrl[i] = 0x0; + config->ss_pe_cmp[i] = 0x0; } if (drvdata->nr_addr_cmp >= 1) { - drvdata->addr_val[0] = (unsigned long)_stext; - drvdata->addr_val[1] = (unsigned long)_etext; - drvdata->addr_type[0] = ETM_ADDR_TYPE_RANGE; - drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE; + config->addr_val[0] = (unsigned long)_stext; + config->addr_val[1] = (unsigned long)_etext; + config->addr_type[0] = ETM_ADDR_TYPE_RANGE; + config->addr_type[1] = ETM_ADDR_TYPE_RANGE; } for (i = 0; i < drvdata->numcidc; i++) { - drvdata->ctxid_pid[i] = 0x0; - drvdata->ctxid_vpid[i] = 0x0; + config->ctxid_pid[i] = 0x0; + config->ctxid_vpid[i] = 0x0; } - drvdata->ctxid_mask0 = 0x0; - drvdata->ctxid_mask1 = 0x0; + config->ctxid_mask0 = 0x0; + config->ctxid_mask1 = 0x0; for (i = 0; i < drvdata->numvmidc; i++) - drvdata->vmid_val[i] = 0x0; - drvdata->vmid_mask0 = 0x0; - drvdata->vmid_mask1 = 0x0; + config->vmid_val[i] = 0x0; + config->vmid_mask0 = 0x0; + config->vmid_mask1 = 0x0; /* * A trace ID value of 0 is invalid, so let's start at some diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index e6ff9aae2a7e..709d96e63910 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -180,66 +180,19 @@ #define TRCSTATR_IDLE_BIT 0 /** - * struct etm4_drvdata - specifics associated to an ETM component - * @base: Memory mapped base address for this component. - * @dev: The device entity associated to this component. - * @csdev: Component vitals needed by the framework. - * @spinlock: Only one at a time pls. - * @cpu: The cpu this component is affined to. - * @arch: ETM version number. - * @enable: Is this ETM currently tracing. - * @sticky_enable: true if ETM base configuration has been done. - * @boot_enable:True if we should start tracing at boot time. - * @os_unlock: True if access to management registers is allowed. - * @nr_pe: The number of processing entity available for tracing. - * @nr_pe_cmp: The number of processing entity comparator inputs that are - * available for tracing. - * @nr_addr_cmp:Number of pairs of address comparators available - * as found in ETMIDR4 0-3. - * @nr_cntr: Number of counters as found in ETMIDR5 bit 28-30. - * @nr_ext_inp: Number of external input. - * @numcidc: Number of contextID comparators. - * @numvmidc: Number of VMID comparators. - * @nrseqstate: The number of sequencer states that are implemented. - * @nr_event: Indicates how many events the trace unit support. - * @nr_resource:The number of resource selection pairs available for tracing. - * @nr_ss_cmp: Number of single-shot comparator controls that are available. + * struct etmv4_config - configuration information related to an ETMv4 * @mode: Controls various modes supported by this ETM. - * @trcid: value of the current ID for this component. - * @trcid_size: Indicates the trace ID width. - * @instrp0: Tracing of load and store instructions - * as P0 elements is supported. - * @trccond: If the trace unit supports conditional - * instruction tracing. - * @retstack: Indicates if the implementation supports a return stack. - * @trc_error: Whether a trace unit can trace a system - * error exception. - * @atbtrig: If the implementation can support ATB triggers - * @lpoverride: If the implementation can support low-power state over. * @pe_sel: Controls which PE to trace. * @cfg: Controls the tracing options. * @eventctrl0: Controls the tracing of arbitrary events. * @eventctrl1: Controls the behavior of the events that @event_ctrl0 selects. * @stallctl: If functionality that prevents trace unit buffer overflows * is available. - * @sysstall: Does the system support stall control of the PE? - * @nooverflow: Indicate if overflow prevention is supported. - * @stall_ctrl: Enables trace unit functionality that prevents trace - * unit buffer overflows. - * @ts_size: Global timestamp size field. * @ts_ctrl: Controls the insertion of global timestamps in the * trace streams. - * @syncpr: Indicates if an implementation has a fixed - * synchronization period. * @syncfreq: Controls how often trace synchronization requests occur. - * @trccci: Indicates if the trace unit supports cycle counting - * for instruction. - * @ccsize: Indicates the size of the cycle counter in bits. - * @ccitmin: minimum value that can be programmed in * the TRCCCCTLR register. * @ccctlr: Sets the threshold value for cycle counting. - * @trcbb: Indicates if the trace unit supports branch broadcast tracing. - * @q_support: Q element support characteristics. * @vinst_ctrl: Controls instruction trace filtering. * @viiectlr: Set or read, the address range comparators. * @vissctlr: Set, or read, the single address comparators that control the @@ -264,73 +217,28 @@ * @addr_acc: Address comparator access type. * @addr_type: Current status of the comparator register. * @ctxid_idx: Context ID index selector. - * @ctxid_size: Size of the context ID field to consider. * @ctxid_pid: Value of the context ID comparator. * @ctxid_vpid: Virtual PID seen by users if PID namespace is enabled, otherwise * the same value of ctxid_pid. * @ctxid_mask0:Context ID comparator mask for comparator 0-3. * @ctxid_mask1:Context ID comparator mask for comparator 4-7. * @vmid_idx: VM ID index selector. - * @vmid_size: Size of the VM ID comparator to consider. * @vmid_val: Value of the VM ID comparator. * @vmid_mask0: VM ID comparator mask for comparator 0-3. * @vmid_mask1: VM ID comparator mask for comparator 4-7. - * @s_ex_level: In secure state, indicates whether instruction tracing is - * supported for the corresponding Exception level. - * @ns_ex_level:In non-secure state, indicates whether instruction tracing is - * supported for the corresponding Exception level. * @ext_inp: External input selection. */ -struct etmv4_drvdata { - void __iomem *base; - struct device *dev; - struct coresight_device *csdev; - spinlock_t spinlock; - int cpu; - u8 arch; - bool enable; - bool sticky_enable; - bool boot_enable; - bool os_unlock; - u8 nr_pe; - u8 nr_pe_cmp; - u8 nr_addr_cmp; - u8 nr_cntr; - u8 nr_ext_inp; - u8 numcidc; - u8 numvmidc; - u8 nrseqstate; - u8 nr_event; - u8 nr_resource; - u8 nr_ss_cmp; +struct etmv4_config { u32 mode; - u8 trcid; - u8 trcid_size; - bool instrp0; - bool trccond; - bool retstack; - bool trc_error; - bool atbtrig; - bool lpoverride; u32 pe_sel; u32 cfg; u32 eventctrl0; u32 eventctrl1; - bool stallctl; - bool sysstall; - bool nooverflow; u32 stall_ctrl; - u8 ts_size; u32 ts_ctrl; - bool syncpr; u32 syncfreq; - bool trccci; - u8 ccsize; - u8 ccitmin; u32 ccctlr; - bool trcbb; u32 bb_ctrl; - bool q_support; u32 vinst_ctrl; u32 viiectlr; u32 vissctlr; @@ -353,19 +261,119 @@ struct etmv4_drvdata { u64 addr_acc[ETM_MAX_SINGLE_ADDR_CMP]; u8 addr_type[ETM_MAX_SINGLE_ADDR_CMP]; u8 ctxid_idx; - u8 ctxid_size; u64 ctxid_pid[ETMv4_MAX_CTXID_CMP]; u64 ctxid_vpid[ETMv4_MAX_CTXID_CMP]; u32 ctxid_mask0; u32 ctxid_mask1; u8 vmid_idx; - u8 vmid_size; u64 vmid_val[ETM_MAX_VMID_CMP]; u32 vmid_mask0; u32 vmid_mask1; + u32 ext_inp; +}; + +/** + * struct etm4_drvdata - specifics associated to an ETM component + * @base: Memory mapped base address for this component. + * @dev: The device entity associated to this component. + * @csdev: Component vitals needed by the framework. + * @spinlock: Only one at a time pls. + * @cpu: The cpu this component is affined to. + * @arch: ETM version number. + * @nr_pe: The number of processing entity available for tracing. + * @nr_pe_cmp: The number of processing entity comparator inputs that are + * available for tracing. + * @nr_addr_cmp:Number of pairs of address comparators available + * as found in ETMIDR4 0-3. + * @nr_cntr: Number of counters as found in ETMIDR5 bit 28-30. + * @nr_ext_inp: Number of external input. + * @numcidc: Number of contextID comparators. + * @numvmidc: Number of VMID comparators. + * @nrseqstate: The number of sequencer states that are implemented. + * @nr_event: Indicates how many events the trace unit support. + * @nr_resource:The number of resource selection pairs available for tracing. + * @nr_ss_cmp: Number of single-shot comparator controls that are available. + * @trcid: value of the current ID for this component. + * @trcid_size: Indicates the trace ID width. + * @ts_size: Global timestamp size field. + * @ctxid_size: Size of the context ID field to consider. + * @vmid_size: Size of the VM ID comparator to consider. + * @ccsize: Indicates the size of the cycle counter in bits. + * @ccitmin: minimum value that can be programmed in + * @s_ex_level: In secure state, indicates whether instruction tracing is + * supported for the corresponding Exception level. + * @ns_ex_level:In non-secure state, indicates whether instruction tracing is + * supported for the corresponding Exception level. + * @enable: Is this ETM currently tracing. + * @sticky_enable: true if ETM base configuration has been done. + * @boot_enable:True if we should start tracing at boot time. + * @os_unlock: True if access to management registers is allowed. + * @instrp0: Tracing of load and store instructions + * as P0 elements is supported. + * @trcbb: Indicates if the trace unit supports branch broadcast tracing. + * @trccond: If the trace unit supports conditional + * instruction tracing. + * @retstack: Indicates if the implementation supports a return stack. + * @trccci: Indicates if the trace unit supports cycle counting + * for instruction. + * @q_support: Q element support characteristics. + * @trc_error: Whether a trace unit can trace a system + * error exception. + * @syncpr: Indicates if an implementation has a fixed + * synchronization period. + * @stall_ctrl: Enables trace unit functionality that prevents trace + * unit buffer overflows. + * @sysstall: Does the system support stall control of the PE? + * @nooverflow: Indicate if overflow prevention is supported. + * @atbtrig: If the implementation can support ATB triggers + * @lpoverride: If the implementation can support low-power state over. + * @config: structure holding configuration parameters. + */ +struct etmv4_drvdata { + void __iomem *base; + struct device *dev; + struct coresight_device *csdev; + spinlock_t spinlock; + int cpu; + u8 arch; + u8 nr_pe; + u8 nr_pe_cmp; + u8 nr_addr_cmp; + u8 nr_cntr; + u8 nr_ext_inp; + u8 numcidc; + u8 numvmidc; + u8 nrseqstate; + u8 nr_event; + u8 nr_resource; + u8 nr_ss_cmp; + u8 trcid; + u8 trcid_size; + u8 ts_size; + u8 ctxid_size; + u8 vmid_size; + u8 ccsize; + u8 ccitmin; u8 s_ex_level; u8 ns_ex_level; - u32 ext_inp; + bool enable; + bool sticky_enable; + bool boot_enable; + bool os_unlock; + bool instrp0; + bool trcbb; + bool trccond; + bool retstack; + bool trccci; + bool q_support; + bool trc_error; + bool syncpr; + bool stallctl; + bool sysstall; + bool nooverflow; + bool atbtrig; + bool lpoverride; + struct etmv4_config config; }; /* Address comparator access types */ From 687e02507830c20c5df9db16e1211194e0b0406d Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:45 -0600 Subject: [PATCH 202/656] coresight: etm4x: splitting etmv4 default configuration Splitting and updating the default initialisation for each etmv4 configuration so that it can be called at the beginning of each session rather than initialisation time only. Since the trace ID isn't expected to change with every session, moving it with the default tracer initialisation. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit fc208abef39279903887bea955139f64bf0bbb12) --- drivers/hwtracing/coresight/coresight-etm4x.c | 134 +++++++++--------- drivers/hwtracing/coresight/coresight-etm4x.h | 12 ++ 2 files changed, 76 insertions(+), 70 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 102e9aaf952b..2744413dedb2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -436,14 +437,20 @@ static void etm4_init_arch_data(void *info) CS_LOCK(drvdata->base); } -static void etm4_init_default_data(struct etmv4_drvdata *drvdata) +static void etm4_set_default(struct etmv4_config *config) { - int i; - struct etmv4_config *config = &drvdata->config; + if (WARN_ON_ONCE(!config)) + return; - config->pe_sel = 0x0; - config->cfg = (ETMv4_MODE_CTXID | ETM_MODE_VMID | - ETMv4_MODE_TIMESTAMP | ETM_MODE_RETURNSTACK); + /* + * Make default initialisation trace everything + * + * Select the "always true" resource selector on the + * "Enablign Event" line and configure address range comparator + * '0' to trace all the possible address range. From there + * configure the "include/exclude" engine to include address + * range comparator '0'. + */ /* disable all events tracing */ config->eventctrl0 = 0x0; @@ -452,78 +459,58 @@ static void etm4_init_default_data(struct etmv4_drvdata *drvdata) /* disable stalling */ config->stall_ctrl = 0x0; + /* enable trace synchronization every 4096 bytes, if available */ + config->syncfreq = 0xC; + /* disable timestamp event */ config->ts_ctrl = 0x0; - /* enable trace synchronization every 4096 bytes for trace */ - if (drvdata->syncpr == false) - config->syncfreq = 0xC; + /* TRCVICTLR::EVENT = 0x01, select the always on logic */ + config->vinst_ctrl |= BIT(0); /* - * enable viewInst to trace everything with start-stop logic in - * started state + * TRCVICTLR::SSSTATUS == 1, the start-stop logic is + * in the started state */ - config->vinst_ctrl |= BIT(0); - /* set initial state of start-stop logic */ - if (drvdata->nr_addr_cmp) - config->vinst_ctrl |= BIT(9); + config->vinst_ctrl |= BIT(9); + + /* + * Configure address range comparator '0' to encompass all + * possible addresses. + */ + + /* First half of default address comparator: start at address 0 */ + config->addr_val[ETM_DEFAULT_ADDR_COMP] = 0x0; + /* trace instruction addresses */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP] &= ~(BIT(0) | BIT(1)); + /* EXLEVEL_NS, bits[12:15], only trace application and kernel space */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= ETM_EXLEVEL_NS_HYP; + /* EXLEVEL_S, bits[11:8], don't trace anything in secure state */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP] |= (ETM_EXLEVEL_S_APP | + ETM_EXLEVEL_S_OS | + ETM_EXLEVEL_S_HYP); + config->addr_type[ETM_DEFAULT_ADDR_COMP] = ETM_ADDR_TYPE_RANGE; + + /* + * Second half of default address comparator: go all + * the way to the top. + */ + config->addr_val[ETM_DEFAULT_ADDR_COMP + 1] = ~0x0; + /* trace instruction addresses */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] &= ~(BIT(0) | BIT(1)); + /* Address comparator type must be equal for both halves */ + config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = + config->addr_acc[ETM_DEFAULT_ADDR_COMP]; + config->addr_type[ETM_DEFAULT_ADDR_COMP + 1] = ETM_ADDR_TYPE_RANGE; + + /* + * Configure the ViewInst function to filter on address range + * comparator '0'. + */ + config->viiectlr = BIT(0); - /* no address range filtering for ViewInst */ - config->viiectlr = 0x0; /* no start-stop filtering for ViewInst */ config->vissctlr = 0x0; - - /* disable seq events */ - for (i = 0; i < drvdata->nrseqstate-1; i++) - config->seq_ctrl[i] = 0x0; - config->seq_rst = 0x0; - config->seq_state = 0x0; - - /* disable external input events */ - config->ext_inp = 0x0; - - for (i = 0; i < drvdata->nr_cntr; i++) { - config->cntrldvr[i] = 0x0; - config->cntr_ctrl[i] = 0x0; - config->cntr_val[i] = 0x0; - } - - /* Resource selector pair 0 is always implemented and reserved */ - config->res_idx = 0x2; - for (i = 2; i < drvdata->nr_resource * 2; i++) - config->res_ctrl[i] = 0x0; - - for (i = 0; i < drvdata->nr_ss_cmp; i++) { - config->ss_ctrl[i] = 0x0; - config->ss_pe_cmp[i] = 0x0; - } - - if (drvdata->nr_addr_cmp >= 1) { - config->addr_val[0] = (unsigned long)_stext; - config->addr_val[1] = (unsigned long)_etext; - config->addr_type[0] = ETM_ADDR_TYPE_RANGE; - config->addr_type[1] = ETM_ADDR_TYPE_RANGE; - } - - for (i = 0; i < drvdata->numcidc; i++) { - config->ctxid_pid[i] = 0x0; - config->ctxid_vpid[i] = 0x0; - } - - config->ctxid_mask0 = 0x0; - config->ctxid_mask1 = 0x0; - - for (i = 0; i < drvdata->numvmidc; i++) - config->vmid_val[i] = 0x0; - config->vmid_mask0 = 0x0; - config->vmid_mask1 = 0x0; - - /* - * A trace ID value of 0 is invalid, so let's start at some - * random value that fits in 7 bits. ETMv3.x has 0x10 so let's - * start at 0x20. - */ - drvdata->trcid = 0x20 + drvdata->cpu; } static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action, @@ -568,6 +555,11 @@ static struct notifier_block etm4_cpu_notifier = { .notifier_call = etm4_cpu_callback, }; +static void etm4_init_trace_id(struct etmv4_drvdata *drvdata) +{ + drvdata->trcid = coresight_get_trace_id(drvdata->cpu); +} + static int etm4_probe(struct amba_device *adev, const struct amba_id *id) { int ret; @@ -627,7 +619,9 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) ret = -EINVAL; goto err_arch_supported; } - etm4_init_default_data(drvdata); + + etm4_init_trace_id(drvdata); + etm4_set_default(&drvdata->config); pm_runtime_put(&adev->dev); diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 709d96e63910..6ff499bfb2f2 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -178,6 +178,18 @@ #define ETMv4_MODE_ALL 0xFFFFFFF #define TRCSTATR_IDLE_BIT 0 +#define ETM_DEFAULT_ADDR_COMP 0 + +/* secure state access levels */ +#define ETM_EXLEVEL_S_APP BIT(8) +#define ETM_EXLEVEL_S_OS BIT(9) +#define ETM_EXLEVEL_S_NA BIT(10) +#define ETM_EXLEVEL_S_HYP BIT(11) +/* non-secure state access levels */ +#define ETM_EXLEVEL_NS_APP BIT(12) +#define ETM_EXLEVEL_NS_OS BIT(13) +#define ETM_EXLEVEL_NS_HYP BIT(14) +#define ETM_EXLEVEL_NS_NA BIT(15) /** * struct etmv4_config - configuration information related to an ETMv4 From 17b04a9f5548d04c62d7ffe3094e8d90d4cc9b9d Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:46 -0600 Subject: [PATCH 203/656] coresight: etm4x: unlocking tracers in default arch init As with the ETMv3.x driver, calling 'smp_call_function_single()' twice in a row is highly ineffective. As such moving function 'etm4_os_unlock()' before the default initialisation takes place, which results in the same outcome. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 66bbbb77540e846b9aac4c9467aca936128951bf) --- drivers/hwtracing/coresight/coresight-etm4x.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 2744413dedb2..92524fe1aa6c 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -44,12 +44,11 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); static int etm4_count; static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; -static void etm4_os_unlock(void *info) +static void etm4_os_unlock(struct etmv4_drvdata *drvdata) { - struct etmv4_drvdata *drvdata = (struct etmv4_drvdata *)info; - /* Writing any value to ETMOSLAR unlocks the trace registers */ writel_relaxed(0x0, drvdata->base + TRCOSLAR); + drvdata->os_unlock = true; isb(); } @@ -286,6 +285,9 @@ static void etm4_init_arch_data(void *info) u32 etmidr5; struct etmv4_drvdata *drvdata = info; + /* Make sure all registers are accessible */ + etm4_os_unlock(drvdata); + CS_UNLOCK(drvdata->base); /* find all capabilities of the tracing unit */ @@ -603,9 +605,6 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) get_online_cpus(); etmdrvdata[drvdata->cpu] = drvdata; - if (!smp_call_function_single(drvdata->cpu, etm4_os_unlock, drvdata, 1)) - drvdata->os_unlock = true; - if (smp_call_function_single(drvdata->cpu, etm4_init_arch_data, drvdata, 1)) dev_err(dev, "ETM arch init failed\n"); From 2c25a4e91650464a2bcbedc108654086e9904a68 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:47 -0600 Subject: [PATCH 204/656] coresight: etm4x: moving etm_drvdata::enable to atomic field Similarly to ETMv3, moving etmv4_drvdata::enable to an atomic type that gives the 'mode' of a tracer and prevents multiple, simultanious access by different subsystems. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit c38a9ec2b2c12c38abca0b7954ed793f26969835) --- drivers/hwtracing/coresight/coresight-etm4x.c | 69 ++++++++++++++++--- drivers/hwtracing/coresight/coresight-etm4x.h | 5 +- 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 92524fe1aa6c..9f51a8f47650 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "coresight-etm4x.h" @@ -76,7 +77,7 @@ static int etm4_trace_id(struct coresight_device *csdev) unsigned long flags; int trace_id = -1; - if (!drvdata->enable) + if (!local_read(&drvdata->mode)) return drvdata->trcid; spin_lock_irqsave(&drvdata->spinlock, flags); @@ -188,8 +189,7 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } -static int etm4_enable(struct coresight_device *csdev, - struct perf_event_attr *attr, u32 mode) +static int etm4_enable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); int ret; @@ -204,18 +204,46 @@ static int etm4_enable(struct coresight_device *csdev, etm4_enable_hw, drvdata, 1); if (ret) goto err; - drvdata->enable = true; - drvdata->sticky_enable = true; + drvdata->sticky_enable = true; spin_unlock(&drvdata->spinlock); dev_info(drvdata->dev, "ETM tracing enabled\n"); return 0; + err: spin_unlock(&drvdata->spinlock); return ret; } +static int etm4_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) +{ + int ret; + u32 val; + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + + /* Someone is already using the tracer */ + if (val) + return -EBUSY; + + switch (mode) { + case CS_MODE_SYSFS: + ret = etm4_enable_sysfs(csdev); + break; + default: + ret = -EINVAL; + } + + /* The tracer didn't start */ + if (ret) + local_set(&drvdata->mode, CS_MODE_DISABLED); + + return ret; +} + static void etm4_disable_hw(void *info) { u32 control; @@ -238,7 +266,7 @@ static void etm4_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } -static void etm4_disable(struct coresight_device *csdev) +static void etm4_disable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -256,7 +284,6 @@ static void etm4_disable(struct coresight_device *csdev) * ensures that register writes occur when cpu is powered. */ smp_call_function_single(drvdata->cpu, etm4_disable_hw, drvdata, 1); - drvdata->enable = false; spin_unlock(&drvdata->spinlock); put_online_cpus(); @@ -264,6 +291,30 @@ static void etm4_disable(struct coresight_device *csdev) dev_info(drvdata->dev, "ETM tracing disabled\n"); } +static void etm4_disable(struct coresight_device *csdev) +{ + u32 mode; + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* + * For as long as the tracer isn't disabled another entity can't + * change its status. As such we can read the status here without + * fearing it will change under us. + */ + mode = local_read(&drvdata->mode); + + switch (mode) { + case CS_MODE_DISABLED: + break; + case CS_MODE_SYSFS: + etm4_disable_sysfs(csdev); + break; + } + + if (mode) + local_set(&drvdata->mode, CS_MODE_DISABLED); +} + static const struct coresight_ops_source etm4_source_ops = { .cpu_id = etm4_cpu_id, .trace_id = etm4_trace_id, @@ -531,7 +582,7 @@ static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action, etmdrvdata[cpu]->os_unlock = true; } - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm4_enable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; @@ -544,7 +595,7 @@ static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action, case CPU_DYING: spin_lock(&etmdrvdata[cpu]->spinlock); - if (etmdrvdata[cpu]->enable) + if (local_read(&etmdrvdata[cpu]->mode)) etm4_disable_hw(etmdrvdata[cpu]); spin_unlock(&etmdrvdata[cpu]->spinlock); break; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index 6ff499bfb2f2..f7748ae63451 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -13,6 +13,7 @@ #ifndef _CORESIGHT_CORESIGHT_ETM_H #define _CORESIGHT_CORESIGHT_ETM_H +#include #include #include "coresight-priv.h" @@ -290,6 +291,7 @@ struct etmv4_config { * @dev: The device entity associated to this component. * @csdev: Component vitals needed by the framework. * @spinlock: Only one at a time pls. + * @mode: This tracer's mode, i.e sysFS, Perf or disabled. * @cpu: The cpu this component is affined to. * @arch: ETM version number. * @nr_pe: The number of processing entity available for tracing. @@ -316,7 +318,6 @@ struct etmv4_config { * supported for the corresponding Exception level. * @ns_ex_level:In non-secure state, indicates whether instruction tracing is * supported for the corresponding Exception level. - * @enable: Is this ETM currently tracing. * @sticky_enable: true if ETM base configuration has been done. * @boot_enable:True if we should start tracing at boot time. * @os_unlock: True if access to management registers is allowed. @@ -346,6 +347,7 @@ struct etmv4_drvdata { struct device *dev; struct coresight_device *csdev; spinlock_t spinlock; + local_t mode; int cpu; u8 arch; u8 nr_pe; @@ -368,7 +370,6 @@ struct etmv4_drvdata { u8 ccitmin; u8 s_ex_level; u8 ns_ex_level; - bool enable; bool sticky_enable; bool boot_enable; bool os_unlock; From 7d2266e97a1348a8781d0940e9d84549e1baa7d4 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:48 -0600 Subject: [PATCH 205/656] coresight: etm4x: implementing user/kernel mode tracing Adding new mode to limit tracing to kernel or user space. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4f6fce54528e0382281cf199635d098e4b108357) --- .../coresight/coresight-etm4x-sysfs.c | 3 ++ drivers/hwtracing/coresight/coresight-etm4x.c | 35 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-etm4x.h | 5 ++- 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index a996db7ef2fc..0e80ec668402 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -440,6 +440,9 @@ static ssize_t mode_store(struct device *dev, else config->vinst_ctrl &= ~BIT(11); + if (config->mode & (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)) + etm4_config_trace_mode(config); + spin_unlock(&drvdata->spinlock); return size; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 9f51a8f47650..88b8bc0f6549 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -566,6 +566,41 @@ static void etm4_set_default(struct etmv4_config *config) config->vissctlr = 0x0; } +void etm4_config_trace_mode(struct etmv4_config *config) +{ + u32 addr_acc, mode; + + mode = config->mode; + mode &= (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER); + + /* excluding kernel AND user space doesn't make sense */ + WARN_ON_ONCE(mode == (ETM_MODE_EXCL_KERN | ETM_MODE_EXCL_USER)); + + /* nothing to do if neither flags are set */ + if (!(mode & ETM_MODE_EXCL_KERN) && !(mode & ETM_MODE_EXCL_USER)) + return; + + addr_acc = config->addr_acc[ETM_DEFAULT_ADDR_COMP]; + /* clear default config */ + addr_acc &= ~(ETM_EXLEVEL_NS_APP | ETM_EXLEVEL_NS_OS); + + /* + * EXLEVEL_NS, bits[15:12] + * The Exception levels are: + * Bit[12] Exception level 0 - Application + * Bit[13] Exception level 1 - OS + * Bit[14] Exception level 2 - Hypervisor + * Bit[15] Never implemented + */ + if (mode & ETM_MODE_EXCL_KERN) + addr_acc |= ETM_EXLEVEL_NS_OS; + else + addr_acc |= ETM_EXLEVEL_NS_APP; + + config->addr_acc[ETM_DEFAULT_ADDR_COMP] = addr_acc; + config->addr_acc[ETM_DEFAULT_ADDR_COMP + 1] = addr_acc; +} + static int etm4_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) { diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index f7748ae63451..a291d4cc8bd8 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -176,7 +176,9 @@ #define ETM_MODE_TRACE_RESET BIT(25) #define ETM_MODE_TRACE_ERR BIT(26) #define ETM_MODE_VIEWINST_STARTSTOP BIT(27) -#define ETMv4_MODE_ALL 0xFFFFFFF +#define ETMv4_MODE_ALL (GENMASK(27, 0) | \ + ETM_MODE_EXCL_KERN | \ + ETM_MODE_EXCL_USER) #define TRCSTATR_IDLE_BIT 0 #define ETM_DEFAULT_ADDR_COMP 0 @@ -414,4 +416,5 @@ enum etm_addr_type { }; extern const struct attribute_group *coresight_etmv4_groups[]; +void etm4_config_trace_mode(struct etmv4_config *config); #endif From a77728f6795f441d79529d9faf285b127efe8d52 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:49 -0600 Subject: [PATCH 206/656] coresight: etm4x: implementing the perf PMU API Adding a set of API allowing the Perf core to treat ETMv4 tracers like other PMUs. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 37fbbdbde9ad3722a7a18beab936825a6ff322bf) --- drivers/hwtracing/coresight/Makefile | 5 +- drivers/hwtracing/coresight/coresight-etm4x.c | 85 +++++++++++++++++-- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 637ca978cd05..1d0e32c7dbe4 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -1,7 +1,7 @@ # # Makefile for CoreSight drivers. # -obj-$(CONFIG_CORESIGHT) += coresight.o +obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o obj-$(CONFIG_OF) += of_coresight.o obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o @@ -9,8 +9,7 @@ obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ coresight-replicator.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ - coresight-etm3x-sysfs.o \ - coresight-etm-perf.o + coresight-etm3x-sysfs.o obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ coresight-etm4x-sysfs.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 88b8bc0f6549..6396b28993da 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -31,12 +31,14 @@ #include #include #include +#include #include #include #include #include #include "coresight-etm4x.h" +#include "coresight-etm-perf.h" static int boot_enable; module_param_named(boot_enable, boot_enable, int, S_IRUGO); @@ -44,6 +46,7 @@ module_param_named(boot_enable, boot_enable, int, S_IRUGO); /* The number of ETMv4 currently registered */ static int etm4_count; static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; +static void etm4_set_default(struct etmv4_config *config); static void etm4_os_unlock(struct etmv4_drvdata *drvdata) { @@ -189,6 +192,58 @@ static void etm4_enable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d enable smp call done\n", drvdata->cpu); } +static int etm4_parse_event_config(struct etmv4_drvdata *drvdata, + struct perf_event_attr *attr) +{ + struct etmv4_config *config = &drvdata->config; + + if (!attr) + return -EINVAL; + + /* Clear configuration from previous run */ + memset(config, 0, sizeof(struct etmv4_config)); + + if (attr->exclude_kernel) + config->mode = ETM_MODE_EXCL_KERN; + + if (attr->exclude_user) + config->mode = ETM_MODE_EXCL_USER; + + /* Always start from the default config */ + etm4_set_default(config); + + /* + * By default the tracers are configured to trace the whole address + * range. Narrow the field only if requested by user space. + */ + if (config->mode) + etm4_config_trace_mode(config); + + /* Go from generic option to ETMv4 specifics */ + if (attr->config & BIT(ETM_OPT_CYCACC)) + config->cfg |= ETMv4_MODE_CYCACC; + if (attr->config & BIT(ETM_OPT_TS)) + config->cfg |= ETMv4_MODE_TIMESTAMP; + + return 0; +} + +static int etm4_enable_perf(struct coresight_device *csdev, + struct perf_event_attr *attr) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return -EINVAL; + + /* Configure the tracer based on the session's specifics */ + etm4_parse_event_config(drvdata, attr); + /* And enable it */ + etm4_enable_hw(drvdata); + + return 0; +} + static int etm4_enable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -233,6 +288,9 @@ static int etm4_enable(struct coresight_device *csdev, case CS_MODE_SYSFS: ret = etm4_enable_sysfs(csdev); break; + case CS_MODE_PERF: + ret = etm4_enable_perf(csdev, attr); + break; default: ret = -EINVAL; } @@ -266,6 +324,17 @@ static void etm4_disable_hw(void *info) dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu); } +static int etm4_disable_perf(struct coresight_device *csdev) +{ + struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (WARN_ON_ONCE(drvdata->cpu != smp_processor_id())) + return -EINVAL; + + etm4_disable_hw(drvdata); + return 0; +} + static void etm4_disable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -309,6 +378,9 @@ static void etm4_disable(struct coresight_device *csdev) case CS_MODE_SYSFS: etm4_disable_sysfs(csdev); break; + case CS_MODE_PERF: + etm4_disable_perf(csdev); + break; } if (mode) @@ -708,8 +780,6 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) etm4_init_trace_id(drvdata); etm4_set_default(&drvdata->config); - pm_runtime_put(&adev->dev); - desc->type = CORESIGHT_DEV_TYPE_SOURCE; desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC; desc->ops = &etm4_cs_ops; @@ -719,9 +789,16 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) drvdata->csdev = coresight_register(desc); if (IS_ERR(drvdata->csdev)) { ret = PTR_ERR(drvdata->csdev); - goto err_coresight_register; + goto err_arch_supported; } + ret = etm_perf_symlink(drvdata->csdev, true); + if (ret) { + coresight_unregister(drvdata->csdev); + goto err_arch_supported; + } + + pm_runtime_put(&adev->dev); dev_info(dev, "%s initialized\n", (char *)id->data); if (boot_enable) { @@ -732,8 +809,6 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id) return 0; err_arch_supported: - pm_runtime_put(&adev->dev); -err_coresight_register: if (--etm4_count == 0) unregister_hotcpu_notifier(&etm4_cpu_notifier); return ret; From 7e388295d098f3a1ca58dcb823929e33027510ae Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:50 -0600 Subject: [PATCH 207/656] coresight: moving coresight_simple_func() to header file Macro "coresight_simple_func()" can be used by several drivers. As such making the structure type generic and moving to a globally available header file. That way individual drivers can use the functionality by simply specifying the structure they need to work with. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 154f3520fe1cdef9009909dc62828eb2d7635631) --- .../coresight/coresight-etm3x-sysfs.c | 31 ++++------ .../coresight/coresight-etm4x-sysfs.c | 61 ++++++++----------- drivers/hwtracing/coresight/coresight-priv.h | 10 +++ 3 files changed, 49 insertions(+), 53 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c index cbb4046c1070..02d4b629891f 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm3x-sysfs.c @@ -1221,26 +1221,19 @@ static struct attribute *coresight_etm_attrs[] = { NULL, }; -#define coresight_simple_func(name, offset) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct etm_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ -} \ -DEVICE_ATTR_RO(name) +#define coresight_etm3x_simple_func(name, offset) \ + coresight_simple_func(struct etm_drvdata, name, offset) -coresight_simple_func(etmccr, ETMCCR); -coresight_simple_func(etmccer, ETMCCER); -coresight_simple_func(etmscr, ETMSCR); -coresight_simple_func(etmidr, ETMIDR); -coresight_simple_func(etmcr, ETMCR); -coresight_simple_func(etmtraceidr, ETMTRACEIDR); -coresight_simple_func(etmteevr, ETMTEEVR); -coresight_simple_func(etmtssvr, ETMTSSCR); -coresight_simple_func(etmtecr1, ETMTECR1); -coresight_simple_func(etmtecr2, ETMTECR2); +coresight_etm3x_simple_func(etmccr, ETMCCR); +coresight_etm3x_simple_func(etmccer, ETMCCER); +coresight_etm3x_simple_func(etmscr, ETMSCR); +coresight_etm3x_simple_func(etmidr, ETMIDR); +coresight_etm3x_simple_func(etmcr, ETMCR); +coresight_etm3x_simple_func(etmtraceidr, ETMTRACEIDR); +coresight_etm3x_simple_func(etmteevr, ETMTEEVR); +coresight_etm3x_simple_func(etmtssvr, ETMTSSCR); +coresight_etm3x_simple_func(etmtecr1, ETMTECR1); +coresight_etm3x_simple_func(etmtecr2, ETMTECR2); static struct attribute *coresight_etm_mgmt_attrs[] = { &dev_attr_etmccr.attr, diff --git a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c index 0e80ec668402..7c84308c5564 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-sysfs.c @@ -2039,29 +2039,22 @@ static struct attribute *coresight_etmv4_attrs[] = { NULL, }; -#define coresight_simple_func(name, offset) \ -static ssize_t name##_show(struct device *_dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct etmv4_drvdata *drvdata = dev_get_drvdata(_dev->parent); \ - return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ - readl_relaxed(drvdata->base + offset)); \ -} \ -DEVICE_ATTR_RO(name) +#define coresight_etm4x_simple_func(name, offset) \ + coresight_simple_func(struct etmv4_drvdata, name, offset) -coresight_simple_func(trcoslsr, TRCOSLSR); -coresight_simple_func(trcpdcr, TRCPDCR); -coresight_simple_func(trcpdsr, TRCPDSR); -coresight_simple_func(trclsr, TRCLSR); -coresight_simple_func(trcconfig, TRCCONFIGR); -coresight_simple_func(trctraceid, TRCTRACEIDR); -coresight_simple_func(trcauthstatus, TRCAUTHSTATUS); -coresight_simple_func(trcdevid, TRCDEVID); -coresight_simple_func(trcdevtype, TRCDEVTYPE); -coresight_simple_func(trcpidr0, TRCPIDR0); -coresight_simple_func(trcpidr1, TRCPIDR1); -coresight_simple_func(trcpidr2, TRCPIDR2); -coresight_simple_func(trcpidr3, TRCPIDR3); +coresight_etm4x_simple_func(trcoslsr, TRCOSLSR); +coresight_etm4x_simple_func(trcpdcr, TRCPDCR); +coresight_etm4x_simple_func(trcpdsr, TRCPDSR); +coresight_etm4x_simple_func(trclsr, TRCLSR); +coresight_etm4x_simple_func(trcconfig, TRCCONFIGR); +coresight_etm4x_simple_func(trctraceid, TRCTRACEIDR); +coresight_etm4x_simple_func(trcauthstatus, TRCAUTHSTATUS); +coresight_etm4x_simple_func(trcdevid, TRCDEVID); +coresight_etm4x_simple_func(trcdevtype, TRCDEVTYPE); +coresight_etm4x_simple_func(trcpidr0, TRCPIDR0); +coresight_etm4x_simple_func(trcpidr1, TRCPIDR1); +coresight_etm4x_simple_func(trcpidr2, TRCPIDR2); +coresight_etm4x_simple_func(trcpidr3, TRCPIDR3); static struct attribute *coresight_etmv4_mgmt_attrs[] = { &dev_attr_trcoslsr.attr, @@ -2080,19 +2073,19 @@ static struct attribute *coresight_etmv4_mgmt_attrs[] = { NULL, }; -coresight_simple_func(trcidr0, TRCIDR0); -coresight_simple_func(trcidr1, TRCIDR1); -coresight_simple_func(trcidr2, TRCIDR2); -coresight_simple_func(trcidr3, TRCIDR3); -coresight_simple_func(trcidr4, TRCIDR4); -coresight_simple_func(trcidr5, TRCIDR5); +coresight_etm4x_simple_func(trcidr0, TRCIDR0); +coresight_etm4x_simple_func(trcidr1, TRCIDR1); +coresight_etm4x_simple_func(trcidr2, TRCIDR2); +coresight_etm4x_simple_func(trcidr3, TRCIDR3); +coresight_etm4x_simple_func(trcidr4, TRCIDR4); +coresight_etm4x_simple_func(trcidr5, TRCIDR5); /* trcidr[6,7] are reserved */ -coresight_simple_func(trcidr8, TRCIDR8); -coresight_simple_func(trcidr9, TRCIDR9); -coresight_simple_func(trcidr10, TRCIDR10); -coresight_simple_func(trcidr11, TRCIDR11); -coresight_simple_func(trcidr12, TRCIDR12); -coresight_simple_func(trcidr13, TRCIDR13); +coresight_etm4x_simple_func(trcidr8, TRCIDR8); +coresight_etm4x_simple_func(trcidr9, TRCIDR9); +coresight_etm4x_simple_func(trcidr10, TRCIDR10); +coresight_etm4x_simple_func(trcidr11, TRCIDR11); +coresight_etm4x_simple_func(trcidr12, TRCIDR12); +coresight_etm4x_simple_func(trcidr13, TRCIDR13); static struct attribute *coresight_etmv4_trcidr_attrs[] = { &dev_attr_trcidr0.attr, diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 333eddaed339..3b5dd95a3588 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -37,6 +37,16 @@ #define ETM_MODE_EXCL_KERN BIT(30) #define ETM_MODE_EXCL_USER BIT(31) +#define coresight_simple_func(type, name, offset) \ +static ssize_t name##_show(struct device *_dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + type *drvdata = dev_get_drvdata(_dev->parent); \ + return scnprintf(buf, PAGE_SIZE, "0x%x\n", \ + readl_relaxed(drvdata->base + offset)); \ +} \ +static DEVICE_ATTR_RO(name) + enum cs_mode { CS_MODE_DISABLED, CS_MODE_SYSFS, From ae1e4d1141cea361cc963da04ce0e1505cd36309 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:51 -0600 Subject: [PATCH 208/656] coresight: etb10: splitting sysFS "status" entry The sysFS "status" entry conveys a wealth of information about the status of the HW but goes agains the sysFS rule of one topic per file. This patch rectify the situation by adding read-only entries for each of the field formaly displayed by "status". The ABI documentation is kept up to date. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ad352acbb9d606a5facff31fd96b05d0346726b1) --- .../testing/sysfs-bus-coresight-devices-etb10 | 69 +++++++++++++++-- drivers/hwtracing/coresight/coresight-etb10.c | 77 +++++++++---------- 2 files changed, 98 insertions(+), 48 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etb10 b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etb10 index 4b8d6ec92e2b..b5f526081711 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etb10 +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etb10 @@ -6,13 +6,6 @@ Description: (RW) Add/remove a sink from a trace path. There can be multiple source for a single sink. ex: echo 1 > /sys/bus/coresight/devices/20010000.etb/enable_sink -What: /sys/bus/coresight/devices/.etb/status -Date: November 2014 -KernelVersion: 3.19 -Contact: Mathieu Poirier -Description: (R) List various control and status registers. The specific - layout and content is driver specific. - What: /sys/bus/coresight/devices/.etb/trigger_cntr Date: November 2014 KernelVersion: 3.19 @@ -22,3 +15,65 @@ Description: (RW) Disables write access to the Trace RAM by stopping the following the trigger event. The number of 32-bit words written into the Trace RAM following the trigger event is equal to the value stored in this register+1 (from ARM ETB-TRM). + +What: /sys/bus/coresight/devices/.etb/mgmt/rdp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Defines the depth, in words, of the trace RAM in powers of + 2. The value is read directly from HW register RDP, 0x004. + +What: /sys/bus/coresight/devices/.etb/mgmt/sts +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB status register. The value + is read directly from HW register STS, 0x00C. + +What: /sys/bus/coresight/devices/.etb/mgmt/rrp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB RAM Read Pointer register + that is used to read entries from the Trace RAM over the APB + interface. The value is read directly from HW register RRP, + 0x014. + +What: /sys/bus/coresight/devices/.etb/mgmt/rwp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB RAM Write Pointer register + that is used to sets the write pointer to write entries from + the CoreSight bus into the Trace RAM. The value is read directly + from HW register RWP, 0x018. + +What: /sys/bus/coresight/devices/.etb/mgmt/trg +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Similar to "trigger_cntr" above except that this value is + read directly from HW register TRG, 0x01C. + +What: /sys/bus/coresight/devices/.etb/mgmt/ctl +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB Control register. The value + is read directly from HW register CTL, 0x020. + +What: /sys/bus/coresight/devices/.etb/mgmt/ffsr +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB Formatter and Flush Status + register. The value is read directly from HW register FFSR, + 0x300. + +What: /sys/bus/coresight/devices/.etb/mgmt/ffcr +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the ETB Formatter and Flush Control + register. The value is read directly from HW register FFCR, + 0x304. diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index acbce79934d6..8bf4429e1fb3 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -579,47 +579,29 @@ static const struct file_operations etb_fops = { .llseek = no_llseek, }; -static ssize_t status_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long flags; - u32 etb_rdr, etb_sr, etb_rrp, etb_rwp; - u32 etb_trg, etb_cr, etb_ffsr, etb_ffcr; - struct etb_drvdata *drvdata = dev_get_drvdata(dev->parent); +#define coresight_etb10_simple_func(name, offset) \ + coresight_simple_func(struct etb_drvdata, name, offset) - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); +coresight_etb10_simple_func(rdp, ETB_RAM_DEPTH_REG); +coresight_etb10_simple_func(sts, ETB_STATUS_REG); +coresight_etb10_simple_func(rrp, ETB_RAM_READ_POINTER); +coresight_etb10_simple_func(rwp, ETB_RAM_WRITE_POINTER); +coresight_etb10_simple_func(trg, ETB_TRG); +coresight_etb10_simple_func(ctl, ETB_CTL_REG); +coresight_etb10_simple_func(ffsr, ETB_FFSR); +coresight_etb10_simple_func(ffcr, ETB_FFCR); - etb_rdr = readl_relaxed(drvdata->base + ETB_RAM_DEPTH_REG); - etb_sr = readl_relaxed(drvdata->base + ETB_STATUS_REG); - etb_rrp = readl_relaxed(drvdata->base + ETB_RAM_READ_POINTER); - etb_rwp = readl_relaxed(drvdata->base + ETB_RAM_WRITE_POINTER); - etb_trg = readl_relaxed(drvdata->base + ETB_TRG); - etb_cr = readl_relaxed(drvdata->base + ETB_CTL_REG); - etb_ffsr = readl_relaxed(drvdata->base + ETB_FFSR); - etb_ffcr = readl_relaxed(drvdata->base + ETB_FFCR); - - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - - pm_runtime_put(drvdata->dev); - - return sprintf(buf, - "Depth:\t\t0x%x\n" - "Status:\t\t0x%x\n" - "RAM read ptr:\t0x%x\n" - "RAM wrt ptr:\t0x%x\n" - "Trigger cnt:\t0x%x\n" - "Control:\t0x%x\n" - "Flush status:\t0x%x\n" - "Flush ctrl:\t0x%x\n", - etb_rdr, etb_sr, etb_rrp, etb_rwp, - etb_trg, etb_cr, etb_ffsr, etb_ffcr); - - return -EINVAL; -} -static DEVICE_ATTR_RO(status); +static struct attribute *coresight_etb_mgmt_attrs[] = { + &dev_attr_rdp.attr, + &dev_attr_sts.attr, + &dev_attr_rrp.attr, + &dev_attr_rwp.attr, + &dev_attr_trg.attr, + &dev_attr_ctl.attr, + &dev_attr_ffsr.attr, + &dev_attr_ffcr.attr, + NULL, +}; static ssize_t trigger_cntr_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -649,10 +631,23 @@ static DEVICE_ATTR_RW(trigger_cntr); static struct attribute *coresight_etb_attrs[] = { &dev_attr_trigger_cntr.attr, - &dev_attr_status.attr, NULL, }; -ATTRIBUTE_GROUPS(coresight_etb); + +static const struct attribute_group coresight_etb_group = { + .attrs = coresight_etb_attrs, +}; + +static const struct attribute_group coresight_etb_mgmt_group = { + .attrs = coresight_etb_mgmt_attrs, + .name = "mgmt", +}; + +const struct attribute_group *coresight_etb_groups[] = { + &coresight_etb_group, + &coresight_etb_mgmt_group, + NULL, +}; static int etb_probe(struct amba_device *adev, const struct amba_id *id) { From 3f4440648d25bbf3d91b0353aad02e8535cf899e Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 5 Apr 2016 11:53:52 -0600 Subject: [PATCH 209/656] coresight: removing gratuitous boot time log messages Removing boot time log for drivers that don't report useful information other than they came up properly. The same information can be found in sysFS once the system has booted and as such doesn't provide any value in the boot log. Reported-by: Mark Brown Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ef0fd640e3312b8164ec43e1eff24769a7c08b7f) --- drivers/hwtracing/coresight/coresight-etb10.c | 1 - drivers/hwtracing/coresight/coresight-funnel.c | 1 - drivers/hwtracing/coresight/coresight-replicator.c | 1 - drivers/hwtracing/coresight/coresight-tmc.c | 1 - drivers/hwtracing/coresight/coresight-tpiu.c | 1 - 5 files changed, 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 8bf4429e1fb3..2461d5c8c157 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -724,7 +724,6 @@ static int etb_probe(struct amba_device *adev, const struct amba_id *id) if (ret) goto err_misc_register; - dev_info(dev, "ETB initialized\n"); return 0; err_misc_register: diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 0600ca30649d..05df789056cc 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -221,7 +221,6 @@ static int funnel_probe(struct amba_device *adev, const struct amba_id *id) if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); - dev_info(dev, "FUNNEL initialized\n"); return 0; } diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 4299c0569340..c6982e312e15 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -114,7 +114,6 @@ static int replicator_probe(struct platform_device *pdev) pm_runtime_put(&pdev->dev); - dev_info(dev, "REPLICATOR initialized\n"); return 0; out_disable_pm: diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 1be191f5d39c..0a4db1ac52ae 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -754,7 +754,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) if (ret) goto err_misc_register; - dev_info(dev, "TMC initialized\n"); return 0; err_misc_register: diff --git a/drivers/hwtracing/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c index 8fb09d9237ab..4e471e2e9d89 100644 --- a/drivers/hwtracing/coresight/coresight-tpiu.c +++ b/drivers/hwtracing/coresight/coresight-tpiu.c @@ -167,7 +167,6 @@ static int tpiu_probe(struct amba_device *adev, const struct amba_id *id) if (IS_ERR(drvdata->csdev)) return PTR_ERR(drvdata->csdev); - dev_info(dev, "TPIU initialized\n"); return 0; } From 0a1d16cf4f923e224b49f37855a94ee3ae97344d Mon Sep 17 00:00:00 2001 From: Li Pengcheng Date: Tue, 3 May 2016 11:33:35 -0600 Subject: [PATCH 210/656] coresight: no need to do the forced type conversion activated and enable are already unsigned type, no need to change them to unsigned. Signed-off-by: Li Pengcheng Signed-off-by: Li Zhong Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit e8dc27d0ee458f9622b50e2d9476719b3a0e686b) --- drivers/hwtracing/coresight/coresight.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 2ea5961092c1..945bc31f0ec5 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -514,7 +514,7 @@ static ssize_t enable_sink_show(struct device *dev, { struct coresight_device *csdev = to_coresight_device(dev); - return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->activated); + return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->activated); } static ssize_t enable_sink_store(struct device *dev, @@ -544,7 +544,7 @@ static ssize_t enable_source_show(struct device *dev, { struct coresight_device *csdev = to_coresight_device(dev); - return scnprintf(buf, PAGE_SIZE, "%u\n", (unsigned)csdev->enable); + return scnprintf(buf, PAGE_SIZE, "%u\n", csdev->enable); } static ssize_t enable_source_store(struct device *dev, From b1ffdd7ebb35dbd535cb37476b4819ab45c57e4e Mon Sep 17 00:00:00 2001 From: Li Pengcheng Date: Tue, 3 May 2016 11:33:36 -0600 Subject: [PATCH 211/656] coresight: etm4x: modify q_support type Because this operation exceed the range of boolean, so we should modify q_support to unit8 bit. drvdata->q_support = BMVAL(etmidr0, 15, 16) Signed-off-by: Li Pengcheng Signed-off-by: Li Zhong Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 6327a454a8ab0dcab24a647367d216c1b84020c6) --- drivers/hwtracing/coresight/coresight-etm4x.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index a291d4cc8bd8..5359c5197c1d 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -372,6 +372,7 @@ struct etmv4_drvdata { u8 ccitmin; u8 s_ex_level; u8 ns_ex_level; + u8 q_support; bool sticky_enable; bool boot_enable; bool os_unlock; @@ -380,7 +381,6 @@ struct etmv4_drvdata { bool trccond; bool retstack; bool trccci; - bool q_support; bool trc_error; bool syncpr; bool stallctl; From 27e5d1a7ac405242327797a69dfc82235d86d513 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:38 -0600 Subject: [PATCH 212/656] coresight: adding path for STM device >From a core framework point of view an STM device is a source that is treated the same way as any other tracers. Unlike tracers though STM devices are not associated with a CPU. As such it doesn't make sense to associate the path from an STM device to its sink with a per-cpu variable as it is done for tracers. This patch simply adds another global variable to keep STM paths and the processing in coresight_enable/disable() is updated to deal with STM devices properly. Signed-off-by: Mathieu Poirier Signed-off-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a685d68328f14579b2e68d6a3a2066089cffbf98) --- drivers/hwtracing/coresight/coresight.c | 106 ++++++++++++++++++------ 1 file changed, 82 insertions(+), 24 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 945bc31f0ec5..0995df8f85bc 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -43,7 +43,15 @@ struct coresight_node { * When operating Coresight drivers from the sysFS interface, only a single * path can exist from a tracer (associated to a CPU) to a sink. */ -static DEFINE_PER_CPU(struct list_head *, sysfs_path); +static DEFINE_PER_CPU(struct list_head *, tracer_path); + +/* + * As of this writing only a single STM can be found in CS topologies. Since + * there is no way to know if we'll ever see more and what kind of + * configuration they will enact, for the time being only define a single path + * for STM. + */ +static struct list_head *stm_path; static int coresight_id_match(struct device *dev, void *data) { @@ -432,18 +440,45 @@ void coresight_release_path(struct list_head *path) path = NULL; } +/** coresight_validate_source - make sure a source has the right credentials + * @csdev: the device structure for a source. + * @function: the function this was called from. + * + * Assumes the coresight_mutex is held. + */ +static int coresight_validate_source(struct coresight_device *csdev, + const char *function) +{ + u32 type, subtype; + + type = csdev->type; + subtype = csdev->subtype.source_subtype; + + if (type != CORESIGHT_DEV_TYPE_SOURCE) { + dev_err(&csdev->dev, "wrong device type in %s\n", function); + return -EINVAL; + } + + if (subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_PROC && + subtype != CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE) { + dev_err(&csdev->dev, "wrong device subtype in %s\n", function); + return -EINVAL; + } + + return 0; +} + int coresight_enable(struct coresight_device *csdev) { - int ret = 0; - int cpu; + int cpu, ret = 0; struct list_head *path; mutex_lock(&coresight_mutex); - if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { - ret = -EINVAL; - dev_err(&csdev->dev, "wrong device type in %s\n", __func__); + + ret = coresight_validate_source(csdev, __func__); + if (ret) goto out; - } + if (csdev->enable) goto out; @@ -461,15 +496,25 @@ int coresight_enable(struct coresight_device *csdev) if (ret) goto err_source; - /* - * When working from sysFS it is important to keep track - * of the paths that were created so that they can be - * undone in 'coresight_disable()'. Since there can only - * be a single session per tracer (when working from sysFS) - * a per-cpu variable will do just fine. - */ - cpu = source_ops(csdev)->cpu_id(csdev); - per_cpu(sysfs_path, cpu) = path; + switch (csdev->subtype.source_subtype) { + case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: + /* + * When working from sysFS it is important to keep track + * of the paths that were created so that they can be + * undone in 'coresight_disable()'. Since there can only + * be a single session per tracer (when working from sysFS) + * a per-cpu variable will do just fine. + */ + cpu = source_ops(csdev)->cpu_id(csdev); + per_cpu(tracer_path, cpu) = path; + break; + case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: + stm_path = path; + break; + default: + /* We can't be here */ + break; + } out: mutex_unlock(&coresight_mutex); @@ -486,23 +531,36 @@ EXPORT_SYMBOL_GPL(coresight_enable); void coresight_disable(struct coresight_device *csdev) { - int cpu; - struct list_head *path; + int cpu, ret; + struct list_head *path = NULL; mutex_lock(&coresight_mutex); - if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE) { - dev_err(&csdev->dev, "wrong device type in %s\n", __func__); + + ret = coresight_validate_source(csdev, __func__); + if (ret) goto out; - } + if (!csdev->enable) goto out; - cpu = source_ops(csdev)->cpu_id(csdev); - path = per_cpu(sysfs_path, cpu); + switch (csdev->subtype.source_subtype) { + case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC: + cpu = source_ops(csdev)->cpu_id(csdev); + path = per_cpu(tracer_path, cpu); + per_cpu(tracer_path, cpu) = NULL; + break; + case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE: + path = stm_path; + stm_path = NULL; + break; + default: + /* We can't be here */ + break; + } + coresight_disable_source(csdev); coresight_disable_path(path); coresight_release_path(path); - per_cpu(sysfs_path, cpu) = NULL; out: mutex_unlock(&coresight_mutex); From feff85e807c07a11794da2ad4d5a4d8a0368dbfd Mon Sep 17 00:00:00 2001 From: Pratik Patel Date: Tue, 3 May 2016 11:33:40 -0600 Subject: [PATCH 213/656] coresight: stm: adding driver for CoreSight STM component This driver adds support for the STM CoreSight IP block, allowing any system compoment (HW or SW) to log and aggregate messages via a single entity. The CoreSight STM exposes an application defined number of channels called stimulus port. Configuration is done using entries in sysfs and channels made available to userspace via configfs. Signed-off-by: Pratik Patel Signed-off-by: Mathieu Poirier Reviewed-by: Michael Williams Signed-off-by: Chunyan Zhang Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 237483aa5cf43105d148d3f03b29eed47c3e6cf9) --- .../testing/sysfs-bus-coresight-devices-stm | 53 + Documentation/trace/coresight.txt | 37 +- drivers/hwtracing/coresight/Kconfig | 11 + drivers/hwtracing/coresight/Makefile | 1 + drivers/hwtracing/coresight/coresight-stm.c | 920 ++++++++++++++++++ include/linux/coresight-stm.h | 6 + include/uapi/linux/coresight-stm.h | 21 + 7 files changed, 1047 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-coresight-devices-stm create mode 100644 drivers/hwtracing/coresight/coresight-stm.c create mode 100644 include/linux/coresight-stm.h create mode 100644 include/uapi/linux/coresight-stm.h diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-stm b/Documentation/ABI/testing/sysfs-bus-coresight-devices-stm new file mode 100644 index 000000000000..1dffabe7f48d --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-stm @@ -0,0 +1,53 @@ +What: /sys/bus/coresight/devices/.stm/enable_source +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Enable/disable tracing on this specific trace macrocell. + Enabling the trace macrocell implies it has been configured + properly and a sink has been identified for it. The path + of coresight components linking the source to the sink is + configured and managed automatically by the coresight framework. + +What: /sys/bus/coresight/devices/.stm/hwevent_enable +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Provides access to the HW event enable register, used in + conjunction with HW event bank select register. + +What: /sys/bus/coresight/devices/.stm/hwevent_select +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Gives access to the HW event block select register + (STMHEBSR) in order to configure up to 256 channels. Used in + conjunction with "hwevent_enable" register as described above. + +What: /sys/bus/coresight/devices/.stm/port_enable +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Provides access to the stimulus port enable register + (STMSPER). Used in conjunction with "port_select" described + below. + +What: /sys/bus/coresight/devices/.stm/port_select +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Used to determine which bank of stimulus port bit in + register STMSPER (see above) apply to. + +What: /sys/bus/coresight/devices/.stm/status +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) List various control and status registers. The specific + layout and content is driver specific. + +What: /sys/bus/coresight/devices/.stm/traceid +Date: April 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (RW) Holds the trace ID that will appear in the trace stream + coming from this trace entity. diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index 0a5c3290e732..a33c88cd5d1d 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -190,8 +190,8 @@ expected to be accessed and controlled using those entries. Last but not least, "struct module *owner" is expected to be set to reflect the information carried in "THIS_MODULE". -How to use ----------- +How to use the tracer modules +----------------------------- Before trace collection can start, a coresight sink needs to be identify. There is no limit on the amount of sinks (nor sources) that can be enabled at @@ -297,3 +297,36 @@ Info Tracing enabled Instruction 13570831 0x8026B584 E28DD00C false ADD sp,sp,#0xc Instruction 0 0x8026B588 E8BD8000 true LDM sp!,{pc} Timestamp Timestamp: 17107041535 + +How to use the STM module +------------------------- + +Using the System Trace Macrocell module is the same as the tracers - the only +difference is that clients are driving the trace capture rather +than the program flow through the code. + +As with any other CoreSight component, specifics about the STM tracer can be +found in sysfs with more information on each entry being found in [1]: + +root@genericarmv8:~# ls /sys/bus/coresight/devices/20100000.stm +enable_source hwevent_select port_enable subsystem uevent +hwevent_enable mgmt port_select traceid +root@genericarmv8:~# + +Like any other source a sink needs to be identified and the STM enabled before +being used: + +root@genericarmv8:~# echo 1 > /sys/bus/coresight/devices/20010000.etf/enable_sink +root@genericarmv8:~# echo 1 > /sys/bus/coresight/devices/20100000.stm/enable_source + +From there user space applications can request and use channels using the devfs +interface provided for that purpose by the generic STM API: + +root@genericarmv8:~# ls -l /dev/20100000.stm +crw------- 1 root root 10, 61 Jan 3 18:11 /dev/20100000.stm +root@genericarmv8:~# + +Details on how to use the generic STM API can be found here [2]. + +[1]. Documentation/ABI/testing/sysfs-bus-coresight-devices-stm +[2]. Documentation/trace/stm.txt diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig index db0541031c72..130cb2114059 100644 --- a/drivers/hwtracing/coresight/Kconfig +++ b/drivers/hwtracing/coresight/Kconfig @@ -78,4 +78,15 @@ config CORESIGHT_QCOM_REPLICATOR programmable ATB replicator sends the ATB trace stream from the ETB/ETF to the TPIUi and ETR. +config CORESIGHT_STM + bool "CoreSight System Trace Macrocell driver" + depends on (ARM && !(CPU_32v3 || CPU_32v4 || CPU_32v4T)) || ARM64 + select CORESIGHT_LINKS_AND_SINKS + select STM + help + This driver provides support for hardware assisted software + instrumentation based tracing. This is primarily used for + logging useful software events or data coming from various entities + in the system, possibly running different OSs + endif diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 1d0e32c7dbe4..c6f84b57f52a 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o coresight-etm-cp14.o \ obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \ coresight-etm4x-sysfs.o obj-$(CONFIG_CORESIGHT_QCOM_REPLICATOR) += coresight-replicator-qcom.o +obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c new file mode 100644 index 000000000000..73be58a11e4f --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -0,0 +1,920 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. + * + * Description: CoreSight System Trace Macrocell driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Initial implementation by Pratik Patel + * (C) 2014-2015 Pratik Patel + * + * Serious refactoring, code cleanup and upgrading to the Coresight upstream + * framework by Mathieu Poirier + * (C) 2015-2016 Mathieu Poirier + * + * Guaranteed timing and support for various packet type coming from the + * generic STM API by Chunyan Zhang + * (C) 2015-2016 Chunyan Zhang + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "coresight-priv.h" + +#define STMDMASTARTR 0xc04 +#define STMDMASTOPR 0xc08 +#define STMDMASTATR 0xc0c +#define STMDMACTLR 0xc10 +#define STMDMAIDR 0xcfc +#define STMHEER 0xd00 +#define STMHETER 0xd20 +#define STMHEBSR 0xd60 +#define STMHEMCR 0xd64 +#define STMHEMASTR 0xdf4 +#define STMHEFEAT1R 0xdf8 +#define STMHEIDR 0xdfc +#define STMSPER 0xe00 +#define STMSPTER 0xe20 +#define STMPRIVMASKR 0xe40 +#define STMSPSCR 0xe60 +#define STMSPMSCR 0xe64 +#define STMSPOVERRIDER 0xe68 +#define STMSPMOVERRIDER 0xe6c +#define STMSPTRIGCSR 0xe70 +#define STMTCSR 0xe80 +#define STMTSSTIMR 0xe84 +#define STMTSFREQR 0xe8c +#define STMSYNCR 0xe90 +#define STMAUXCR 0xe94 +#define STMSPFEAT1R 0xea0 +#define STMSPFEAT2R 0xea4 +#define STMSPFEAT3R 0xea8 +#define STMITTRIGGER 0xee8 +#define STMITATBDATA0 0xeec +#define STMITATBCTR2 0xef0 +#define STMITATBID 0xef4 +#define STMITATBCTR0 0xef8 + +#define STM_32_CHANNEL 32 +#define BYTES_PER_CHANNEL 256 +#define STM_TRACE_BUF_SIZE 4096 +#define STM_SW_MASTER_END 127 + +/* Register bit definition */ +#define STMTCSR_BUSY_BIT 23 +/* Reserve the first 10 channels for kernel usage */ +#define STM_CHANNEL_OFFSET 0 + +enum stm_pkt_type { + STM_PKT_TYPE_DATA = 0x98, + STM_PKT_TYPE_FLAG = 0xE8, + STM_PKT_TYPE_TRIG = 0xF8, +}; + +#define stm_channel_addr(drvdata, ch) (drvdata->chs.base + \ + (ch * BYTES_PER_CHANNEL)) +#define stm_channel_off(type, opts) (type & ~opts) + +static int boot_nr_channel; + +/* + * Not really modular but using module_param is the easiest way to + * remain consistent with existing use cases for now. + */ +module_param_named( + boot_nr_channel, boot_nr_channel, int, S_IRUGO +); + +/** + * struct channel_space - central management entity for extended ports + * @base: memory mapped base address where channels start. + * @guaraneed: is the channel delivery guaranteed. + */ +struct channel_space { + void __iomem *base; + unsigned long *guaranteed; +}; + +/** + * struct stm_drvdata - specifics associated to an STM component + * @base: memory mapped base address for this component. + * @dev: the device entity associated to this component. + * @atclk: optional clock for the core parts of the STM. + * @csdev: component vitals needed by the framework. + * @spinlock: only one at a time pls. + * @chs: the channels accociated to this STM. + * @stm: structure associated to the generic STM interface. + * @mode: this tracer's mode, i.e sysFS, or disabled. + * @traceid: value of the current ID for this component. + * @write_bytes: Maximus bytes this STM can write at a time. + * @stmsper: settings for register STMSPER. + * @stmspscr: settings for register STMSPSCR. + * @numsp: the total number of stimulus port support by this STM. + * @stmheer: settings for register STMHEER. + * @stmheter: settings for register STMHETER. + * @stmhebsr: settings for register STMHEBSR. + */ +struct stm_drvdata { + void __iomem *base; + struct device *dev; + struct clk *atclk; + struct coresight_device *csdev; + spinlock_t spinlock; + struct channel_space chs; + struct stm_data stm; + local_t mode; + u8 traceid; + u32 write_bytes; + u32 stmsper; + u32 stmspscr; + u32 numsp; + u32 stmheer; + u32 stmheter; + u32 stmhebsr; +}; + +static void stm_hwevent_enable_hw(struct stm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + writel_relaxed(drvdata->stmhebsr, drvdata->base + STMHEBSR); + writel_relaxed(drvdata->stmheter, drvdata->base + STMHETER); + writel_relaxed(drvdata->stmheer, drvdata->base + STMHEER); + writel_relaxed(0x01 | /* Enable HW event tracing */ + 0x04, /* Error detection on event tracing */ + drvdata->base + STMHEMCR); + + CS_LOCK(drvdata->base); +} + +static void stm_port_enable_hw(struct stm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + /* ATB trigger enable on direct writes to TRIG locations */ + writel_relaxed(0x10, + drvdata->base + STMSPTRIGCSR); + writel_relaxed(drvdata->stmspscr, drvdata->base + STMSPSCR); + writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER); + + CS_LOCK(drvdata->base); +} + +static void stm_enable_hw(struct stm_drvdata *drvdata) +{ + if (drvdata->stmheer) + stm_hwevent_enable_hw(drvdata); + + stm_port_enable_hw(drvdata); + + CS_UNLOCK(drvdata->base); + + /* 4096 byte between synchronisation packets */ + writel_relaxed(0xFFF, drvdata->base + STMSYNCR); + writel_relaxed((drvdata->traceid << 16 | /* trace id */ + 0x02 | /* timestamp enable */ + 0x01), /* global STM enable */ + drvdata->base + STMTCSR); + + CS_LOCK(drvdata->base); +} + +static int stm_enable(struct coresight_device *csdev, + struct perf_event_attr *attr, u32 mode) +{ + u32 val; + struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (mode != CS_MODE_SYSFS) + return -EINVAL; + + val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode); + + /* Someone is already using the tracer */ + if (val) + return -EBUSY; + + pm_runtime_get_sync(drvdata->dev); + + spin_lock(&drvdata->spinlock); + stm_enable_hw(drvdata); + spin_unlock(&drvdata->spinlock); + + dev_info(drvdata->dev, "STM tracing enabled\n"); + return 0; +} + +static void stm_hwevent_disable_hw(struct stm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + writel_relaxed(0x0, drvdata->base + STMHEMCR); + writel_relaxed(0x0, drvdata->base + STMHEER); + writel_relaxed(0x0, drvdata->base + STMHETER); + + CS_LOCK(drvdata->base); +} + +static void stm_port_disable_hw(struct stm_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + writel_relaxed(0x0, drvdata->base + STMSPER); + writel_relaxed(0x0, drvdata->base + STMSPTRIGCSR); + + CS_LOCK(drvdata->base); +} + +static void stm_disable_hw(struct stm_drvdata *drvdata) +{ + u32 val; + + CS_UNLOCK(drvdata->base); + + val = readl_relaxed(drvdata->base + STMTCSR); + val &= ~0x1; /* clear global STM enable [0] */ + writel_relaxed(val, drvdata->base + STMTCSR); + + CS_LOCK(drvdata->base); + + stm_port_disable_hw(drvdata); + if (drvdata->stmheer) + stm_hwevent_disable_hw(drvdata); +} + +static void stm_disable(struct coresight_device *csdev) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* + * For as long as the tracer isn't disabled another entity can't + * change its status. As such we can read the status here without + * fearing it will change under us. + */ + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { + spin_lock(&drvdata->spinlock); + stm_disable_hw(drvdata); + spin_unlock(&drvdata->spinlock); + + /* Wait until the engine has completely stopped */ + coresight_timeout(drvdata, STMTCSR, STMTCSR_BUSY_BIT, 0); + + pm_runtime_put(drvdata->dev); + + local_set(&drvdata->mode, CS_MODE_DISABLED); + dev_info(drvdata->dev, "STM tracing disabled\n"); + } +} + +static int stm_trace_id(struct coresight_device *csdev) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + return drvdata->traceid; +} + +static const struct coresight_ops_source stm_source_ops = { + .trace_id = stm_trace_id, + .enable = stm_enable, + .disable = stm_disable, +}; + +static const struct coresight_ops stm_cs_ops = { + .source_ops = &stm_source_ops, +}; + +static inline bool stm_addr_unaligned(const void *addr, u8 write_bytes) +{ + return ((unsigned long)addr & (write_bytes - 1)); +} + +static void stm_send(void *addr, const void *data, u32 size, u8 write_bytes) +{ + u8 paload[8]; + + if (stm_addr_unaligned(data, write_bytes)) { + memcpy(paload, data, size); + data = paload; + } + + /* now we are 64bit/32bit aligned */ + switch (size) { +#ifdef CONFIG_64BIT + case 8: + writeq_relaxed(*(u64 *)data, addr); + break; +#endif + case 4: + writel_relaxed(*(u32 *)data, addr); + break; + case 2: + writew_relaxed(*(u16 *)data, addr); + break; + case 1: + writeb_relaxed(*(u8 *)data, addr); + break; + default: + break; + } +} + +static int stm_generic_link(struct stm_data *stm_data, + unsigned int master, unsigned int channel) +{ + struct stm_drvdata *drvdata = container_of(stm_data, + struct stm_drvdata, stm); + if (!drvdata || !drvdata->csdev) + return -EINVAL; + + return coresight_enable(drvdata->csdev); +} + +static void stm_generic_unlink(struct stm_data *stm_data, + unsigned int master, unsigned int channel) +{ + struct stm_drvdata *drvdata = container_of(stm_data, + struct stm_drvdata, stm); + if (!drvdata || !drvdata->csdev) + return; + + stm_disable(drvdata->csdev); +} + +static long stm_generic_set_options(struct stm_data *stm_data, + unsigned int master, + unsigned int channel, + unsigned int nr_chans, + unsigned long options) +{ + struct stm_drvdata *drvdata = container_of(stm_data, + struct stm_drvdata, stm); + if (!(drvdata && local_read(&drvdata->mode))) + return -EINVAL; + + if (channel >= drvdata->numsp) + return -EINVAL; + + switch (options) { + case STM_OPTION_GUARANTEED: + set_bit(channel, drvdata->chs.guaranteed); + break; + + case STM_OPTION_INVARIANT: + clear_bit(channel, drvdata->chs.guaranteed); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static ssize_t stm_generic_packet(struct stm_data *stm_data, + unsigned int master, + unsigned int channel, + unsigned int packet, + unsigned int flags, + unsigned int size, + const unsigned char *payload) +{ + unsigned long ch_addr; + struct stm_drvdata *drvdata = container_of(stm_data, + struct stm_drvdata, stm); + + if (!(drvdata && local_read(&drvdata->mode))) + return 0; + + if (channel >= drvdata->numsp) + return 0; + + ch_addr = (unsigned long)stm_channel_addr(drvdata, channel); + + flags = (flags == STP_PACKET_TIMESTAMPED) ? STM_FLAG_TIMESTAMPED : 0; + flags |= test_bit(channel, drvdata->chs.guaranteed) ? + STM_FLAG_GUARANTEED : 0; + + if (size > drvdata->write_bytes) + size = drvdata->write_bytes; + else + size = rounddown_pow_of_two(size); + + switch (packet) { + case STP_PACKET_FLAG: + ch_addr |= stm_channel_off(STM_PKT_TYPE_FLAG, flags); + + /* + * The generic STM core sets a size of '0' on flag packets. + * As such send a flag packet of size '1' and tell the + * core we did so. + */ + stm_send((void *)ch_addr, payload, 1, drvdata->write_bytes); + size = 1; + break; + + case STP_PACKET_DATA: + ch_addr |= stm_channel_off(STM_PKT_TYPE_DATA, flags); + stm_send((void *)ch_addr, payload, size, + drvdata->write_bytes); + break; + + default: + return -ENOTSUPP; + } + + return size; +} + +static ssize_t hwevent_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val = drvdata->stmheer; + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t hwevent_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + int ret = 0; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return -EINVAL; + + drvdata->stmheer = val; + /* HW event enable and trigger go hand in hand */ + drvdata->stmheter = val; + + return size; +} +static DEVICE_ATTR_RW(hwevent_enable); + +static ssize_t hwevent_select_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val = drvdata->stmhebsr; + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t hwevent_select_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + int ret = 0; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return -EINVAL; + + drvdata->stmhebsr = val; + + return size; +} +static DEVICE_ATTR_RW(hwevent_select); + +static ssize_t port_select_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if (!local_read(&drvdata->mode)) { + val = drvdata->stmspscr; + } else { + spin_lock(&drvdata->spinlock); + val = readl_relaxed(drvdata->base + STMSPSCR); + spin_unlock(&drvdata->spinlock); + } + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t port_select_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val, stmsper; + int ret = 0; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->stmspscr = val; + + if (local_read(&drvdata->mode)) { + CS_UNLOCK(drvdata->base); + /* Process as per ARM's TRM recommendation */ + stmsper = readl_relaxed(drvdata->base + STMSPER); + writel_relaxed(0x0, drvdata->base + STMSPER); + writel_relaxed(drvdata->stmspscr, drvdata->base + STMSPSCR); + writel_relaxed(stmsper, drvdata->base + STMSPER); + CS_LOCK(drvdata->base); + } + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(port_select); + +static ssize_t port_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + + if (!local_read(&drvdata->mode)) { + val = drvdata->stmsper; + } else { + spin_lock(&drvdata->spinlock); + val = readl_relaxed(drvdata->base + STMSPER); + spin_unlock(&drvdata->spinlock); + } + + return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); +} + +static ssize_t port_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + unsigned long val; + int ret = 0; + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + spin_lock(&drvdata->spinlock); + drvdata->stmsper = val; + + if (local_read(&drvdata->mode)) { + CS_UNLOCK(drvdata->base); + writel_relaxed(drvdata->stmsper, drvdata->base + STMSPER); + CS_LOCK(drvdata->base); + } + spin_unlock(&drvdata->spinlock); + + return size; +} +static DEVICE_ATTR_RW(port_enable); + +static ssize_t traceid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long val; + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + val = drvdata->traceid; + return sprintf(buf, "%#lx\n", val); +} + +static ssize_t traceid_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + int ret; + unsigned long val; + struct stm_drvdata *drvdata = dev_get_drvdata(dev->parent); + + ret = kstrtoul(buf, 16, &val); + if (ret) + return ret; + + /* traceid field is 7bit wide on STM32 */ + drvdata->traceid = val & 0x7f; + return size; +} +static DEVICE_ATTR_RW(traceid); + +#define coresight_stm_simple_func(name, offset) \ + coresight_simple_func(struct stm_drvdata, name, offset) + +coresight_stm_simple_func(tcsr, STMTCSR); +coresight_stm_simple_func(tsfreqr, STMTSFREQR); +coresight_stm_simple_func(syncr, STMSYNCR); +coresight_stm_simple_func(sper, STMSPER); +coresight_stm_simple_func(spter, STMSPTER); +coresight_stm_simple_func(privmaskr, STMPRIVMASKR); +coresight_stm_simple_func(spscr, STMSPSCR); +coresight_stm_simple_func(spmscr, STMSPMSCR); +coresight_stm_simple_func(spfeat1r, STMSPFEAT1R); +coresight_stm_simple_func(spfeat2r, STMSPFEAT2R); +coresight_stm_simple_func(spfeat3r, STMSPFEAT3R); +coresight_stm_simple_func(devid, CORESIGHT_DEVID); + +static struct attribute *coresight_stm_attrs[] = { + &dev_attr_hwevent_enable.attr, + &dev_attr_hwevent_select.attr, + &dev_attr_port_enable.attr, + &dev_attr_port_select.attr, + &dev_attr_traceid.attr, + NULL, +}; + +static struct attribute *coresight_stm_mgmt_attrs[] = { + &dev_attr_tcsr.attr, + &dev_attr_tsfreqr.attr, + &dev_attr_syncr.attr, + &dev_attr_sper.attr, + &dev_attr_spter.attr, + &dev_attr_privmaskr.attr, + &dev_attr_spscr.attr, + &dev_attr_spmscr.attr, + &dev_attr_spfeat1r.attr, + &dev_attr_spfeat2r.attr, + &dev_attr_spfeat3r.attr, + &dev_attr_devid.attr, + NULL, +}; + +static const struct attribute_group coresight_stm_group = { + .attrs = coresight_stm_attrs, +}; + +static const struct attribute_group coresight_stm_mgmt_group = { + .attrs = coresight_stm_mgmt_attrs, + .name = "mgmt", +}; + +static const struct attribute_group *coresight_stm_groups[] = { + &coresight_stm_group, + &coresight_stm_mgmt_group, + NULL, +}; + +static int stm_get_resource_byname(struct device_node *np, + char *ch_base, struct resource *res) +{ + const char *name = NULL; + int index = 0, found = 0; + + while (!of_property_read_string_index(np, "reg-names", index, &name)) { + if (strcmp(ch_base, name)) { + index++; + continue; + } + + /* We have a match and @index is where it's at */ + found = 1; + break; + } + + if (!found) + return -EINVAL; + + return of_address_to_resource(np, index, res); +} + +static u32 stm_fundamental_data_size(struct stm_drvdata *drvdata) +{ + u32 stmspfeat2r; + + if (!IS_ENABLED(CONFIG_64BIT)) + return 4; + + stmspfeat2r = readl_relaxed(drvdata->base + STMSPFEAT2R); + + /* + * bit[15:12] represents the fundamental data size + * 0 - 32-bit data + * 1 - 64-bit data + */ + return BMVAL(stmspfeat2r, 12, 15) ? 8 : 4; +} + +static u32 stm_num_stimulus_port(struct stm_drvdata *drvdata) +{ + u32 numsp; + + numsp = readl_relaxed(drvdata->base + CORESIGHT_DEVID); + /* + * NUMPS in STMDEVID is 17 bit long and if equal to 0x0, + * 32 stimulus ports are supported. + */ + numsp &= 0x1ffff; + if (!numsp) + numsp = STM_32_CHANNEL; + return numsp; +} + +static void stm_init_default_data(struct stm_drvdata *drvdata) +{ + /* Don't use port selection */ + drvdata->stmspscr = 0x0; + /* + * Enable all channel regardless of their number. When port + * selection isn't used (see above) STMSPER applies to all + * 32 channel group available, hence setting all 32 bits to 1 + */ + drvdata->stmsper = ~0x0; + + /* + * The trace ID value for *ETM* tracers start at CPU_ID * 2 + 0x10 and + * anything equal to or higher than 0x70 is reserved. Since 0x00 is + * also reserved the STM trace ID needs to be higher than 0x00 and + * lowner than 0x10. + */ + drvdata->traceid = 0x1; + + /* Set invariant transaction timing on all channels */ + bitmap_clear(drvdata->chs.guaranteed, 0, drvdata->numsp); +} + +static void stm_init_generic_data(struct stm_drvdata *drvdata) +{ + drvdata->stm.name = dev_name(drvdata->dev); + + /* + * MasterIDs are assigned at HW design phase. As such the core is + * using a single master for interaction with this device. + */ + drvdata->stm.sw_start = 1; + drvdata->stm.sw_end = 1; + drvdata->stm.hw_override = true; + drvdata->stm.sw_nchannels = drvdata->numsp; + drvdata->stm.packet = stm_generic_packet; + drvdata->stm.link = stm_generic_link; + drvdata->stm.unlink = stm_generic_unlink; + drvdata->stm.set_options = stm_generic_set_options; +} + +static int stm_probe(struct amba_device *adev, const struct amba_id *id) +{ + int ret; + void __iomem *base; + unsigned long *guaranteed; + struct device *dev = &adev->dev; + struct coresight_platform_data *pdata = NULL; + struct stm_drvdata *drvdata; + struct resource *res = &adev->res; + struct resource ch_res; + size_t res_size, bitmap_size; + struct coresight_desc *desc; + struct device_node *np = adev->dev.of_node; + + if (np) { + pdata = of_get_coresight_platform_data(dev, np); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + adev->dev.platform_data = pdata; + } + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &adev->dev; + drvdata->atclk = devm_clk_get(&adev->dev, "atclk"); /* optional */ + if (!IS_ERR(drvdata->atclk)) { + ret = clk_prepare_enable(drvdata->atclk); + if (ret) + return ret; + } + dev_set_drvdata(dev, drvdata); + + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + drvdata->base = base; + + ret = stm_get_resource_byname(np, "stm-stimulus-base", &ch_res); + if (ret) + return ret; + + base = devm_ioremap_resource(dev, &ch_res); + if (IS_ERR(base)) + return PTR_ERR(base); + drvdata->chs.base = base; + + drvdata->write_bytes = stm_fundamental_data_size(drvdata); + + if (boot_nr_channel) { + drvdata->numsp = boot_nr_channel; + res_size = min((resource_size_t)(boot_nr_channel * + BYTES_PER_CHANNEL), resource_size(res)); + } else { + drvdata->numsp = stm_num_stimulus_port(drvdata); + res_size = min((resource_size_t)(drvdata->numsp * + BYTES_PER_CHANNEL), resource_size(res)); + } + bitmap_size = BITS_TO_LONGS(drvdata->numsp) * sizeof(long); + + guaranteed = devm_kzalloc(dev, bitmap_size, GFP_KERNEL); + if (!guaranteed) + return -ENOMEM; + drvdata->chs.guaranteed = guaranteed; + + spin_lock_init(&drvdata->spinlock); + + stm_init_default_data(drvdata); + stm_init_generic_data(drvdata); + + if (stm_register_device(dev, &drvdata->stm, THIS_MODULE)) { + dev_info(dev, + "stm_register_device failed, probing deffered\n"); + return -EPROBE_DEFER; + } + + desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); + if (!desc) { + ret = -ENOMEM; + goto stm_unregister; + } + + desc->type = CORESIGHT_DEV_TYPE_SOURCE; + desc->subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE; + desc->ops = &stm_cs_ops; + desc->pdata = pdata; + desc->dev = dev; + desc->groups = coresight_stm_groups; + drvdata->csdev = coresight_register(desc); + if (IS_ERR(drvdata->csdev)) { + ret = PTR_ERR(drvdata->csdev); + goto stm_unregister; + } + + pm_runtime_put(&adev->dev); + + dev_info(dev, "%s initialized\n", (char *)id->data); + return 0; + +stm_unregister: + stm_unregister_device(&drvdata->stm); + return ret; +} + +#ifdef CONFIG_PM +static int stm_runtime_suspend(struct device *dev) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev); + + if (drvdata && !IS_ERR(drvdata->atclk)) + clk_disable_unprepare(drvdata->atclk); + + return 0; +} + +static int stm_runtime_resume(struct device *dev) +{ + struct stm_drvdata *drvdata = dev_get_drvdata(dev); + + if (drvdata && !IS_ERR(drvdata->atclk)) + clk_prepare_enable(drvdata->atclk); + + return 0; +} +#endif + +static const struct dev_pm_ops stm_dev_pm_ops = { + SET_RUNTIME_PM_OPS(stm_runtime_suspend, stm_runtime_resume, NULL) +}; + +static struct amba_id stm_ids[] = { + { + .id = 0x0003b962, + .mask = 0x0003ffff, + .data = "STM32", + }, + { 0, 0}, +}; + +static struct amba_driver stm_driver = { + .drv = { + .name = "coresight-stm", + .owner = THIS_MODULE, + .pm = &stm_dev_pm_ops, + .suppress_bind_attrs = true, + }, + .probe = stm_probe, + .id_table = stm_ids, +}; + +builtin_amba_driver(stm_driver); diff --git a/include/linux/coresight-stm.h b/include/linux/coresight-stm.h new file mode 100644 index 000000000000..a978bb85599a --- /dev/null +++ b/include/linux/coresight-stm.h @@ -0,0 +1,6 @@ +#ifndef __LINUX_CORESIGHT_STM_H_ +#define __LINUX_CORESIGHT_STM_H_ + +#include + +#endif diff --git a/include/uapi/linux/coresight-stm.h b/include/uapi/linux/coresight-stm.h new file mode 100644 index 000000000000..7e4272cf1fb2 --- /dev/null +++ b/include/uapi/linux/coresight-stm.h @@ -0,0 +1,21 @@ +#ifndef __UAPI_CORESIGHT_STM_H_ +#define __UAPI_CORESIGHT_STM_H_ + +#define STM_FLAG_TIMESTAMPED BIT(3) +#define STM_FLAG_GUARANTEED BIT(7) + +/* + * The CoreSight STM supports guaranteed and invariant timing + * transactions. Guaranteed transactions are guaranteed to be + * traced, this might involve stalling the bus or system to + * ensure the transaction is accepted by the STM. While invariant + * timing transactions are not guaranteed to be traced, they + * will take an invariant amount of time regardless of the + * state of the STM. + */ +enum { + STM_OPTION_GUARANTEED = 0, + STM_OPTION_INVARIANT, +}; + +#endif From 143e92c2248c24712e453aef2a43fbe164d37d37 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:41 -0600 Subject: [PATCH 214/656] coresight: etb10: fixing the right amount of words to read This patch rectifies the amount of words to read when the internal buffer is deemed bigger than the amount of space available in the perf ring buffer. The amount to read is set to the amount of space in the perf ring buffer rather than being subtracted by it. Reported-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b5af0a26da84b75376706a92c7a58036a0bf3541) --- drivers/hwtracing/coresight/coresight-etb10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 2461d5c8c157..b0d402dbfeae 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -440,7 +440,7 @@ static void etb_update_buffer(struct coresight_device *csdev, u32 mask = ~(ETB_FRAME_SIZE_WORDS - 1); /* The new read pointer must be frame size aligned */ - to_read -= handle->size & mask; + to_read = handle->size & mask; /* * Move the RAM read pointer up, keeping in mind that * everything is in frame size units. From 42d5ddbbf0f8cfadadf6cb41bfa8bcf1b8c421b1 Mon Sep 17 00:00:00 2001 From: Li Pengcheng Date: Tue, 3 May 2016 11:33:42 -0600 Subject: [PATCH 215/656] coresight: etm4x: add tracer ID for A72 Maia processor. This patch adds a cellID for the ETMv4 tracer found on HiSillicon's A72 Maia processor. Signed-off-by: Li Pengcheng Signed-off-by: Li Zhong Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 960e30959988e0f37e0eeb22b6cdb65b94d5d2e7) --- drivers/hwtracing/coresight/coresight-etm4x.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 6396b28993da..462f0dc15757 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -825,6 +825,11 @@ static struct amba_id etm4_ids[] = { .mask = 0x000fffff, .data = "ETM 4.0", }, + { /* ETM 4.0 - A72, Maia, HiSilicon */ + .id = 0x000bb95a, + .mask = 0x000fffff, + .data = "ETM 4.0", + }, { 0, 0}, }; From d8400899814b77b374e69be43f766266caa73c74 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:43 -0600 Subject: [PATCH 216/656] coresight: tmc: adding sysFS management entries Adding management registers that convey implementation specific characteristics. Those are useful for trace configuration and collection along with general trouble shooting. Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 7d83d17795efce95def54a13ccd6c3f80de6e8f0) --- .../testing/sysfs-bus-coresight-devices-tmc | 77 +++++++++++++ drivers/hwtracing/coresight/coresight-tmc.c | 103 +++++++----------- 2 files changed, 118 insertions(+), 62 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc index f38cded5fa22..4fe677ed1305 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-tmc @@ -6,3 +6,80 @@ Description: (RW) Disables write access to the Trace RAM by stopping the formatter after a defined number of words have been stored following the trigger event. Additional interface for this driver are expected to be added as it matures. + +What: /sys/bus/coresight/devices/.tmc/mgmt/rsz +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Defines the size, in 32-bit words, of the local RAM buffer. + The value is read directly from HW register RSZ, 0x004. + +What: /sys/bus/coresight/devices/.tmc/mgmt/sts +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC status register. The value + is read directly from HW register STS, 0x00C. + +What: /sys/bus/coresight/devices/.tmc/mgmt/rrp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC RAM Read Pointer register + that is used to read entries from the Trace RAM over the APB + interface. The value is read directly from HW register RRP, + 0x014. + +What: /sys/bus/coresight/devices/.tmc/mgmt/rwp +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC RAM Write Pointer register + that is used to sets the write pointer to write entries from + the CoreSight bus into the Trace RAM. The value is read directly + from HW register RWP, 0x018. + +What: /sys/bus/coresight/devices/.tmc/mgmt/trg +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Similar to "trigger_cntr" above except that this value is + read directly from HW register TRG, 0x01C. + +What: /sys/bus/coresight/devices/.tmc/mgmt/ctl +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC Control register. The value + is read directly from HW register CTL, 0x020. + +What: /sys/bus/coresight/devices/.tmc/mgmt/ffsr +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC Formatter and Flush Status + register. The value is read directly from HW register FFSR, + 0x300. + +What: /sys/bus/coresight/devices/.tmc/mgmt/ffcr +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC Formatter and Flush Control + register. The value is read directly from HW register FFCR, + 0x304. + +What: /sys/bus/coresight/devices/.tmc/mgmt/mode +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Shows the value held by the TMC Mode register, which + indicate the mode the device has been configured to enact. The + The value is read directly from the MODE register, 0x028. + +What: /sys/bus/coresight/devices/.tmc/mgmt/devid +Date: March 2016 +KernelVersion: 4.7 +Contact: Mathieu Poirier +Description: (R) Indicates the capabilities of the Coresight TMC. + The value is read directly from the DEVID register, 0xFC8, diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 0a4db1ac52ae..2b42ecbd8831 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -556,56 +556,38 @@ static const struct file_operations tmc_fops = { .llseek = no_llseek, }; -static ssize_t status_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - unsigned long flags; - u32 tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg; - u32 tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr; - u32 devid; - struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); +#define coresight_tmc_simple_func(name, offset) \ + coresight_simple_func(struct tmc_drvdata, name, offset) - pm_runtime_get_sync(drvdata->dev); - spin_lock_irqsave(&drvdata->spinlock, flags); - CS_UNLOCK(drvdata->base); +coresight_tmc_simple_func(rsz, TMC_RSZ); +coresight_tmc_simple_func(sts, TMC_STS); +coresight_tmc_simple_func(rrp, TMC_RRP); +coresight_tmc_simple_func(rwp, TMC_RWP); +coresight_tmc_simple_func(trg, TMC_TRG); +coresight_tmc_simple_func(ctl, TMC_CTL); +coresight_tmc_simple_func(ffsr, TMC_FFSR); +coresight_tmc_simple_func(ffcr, TMC_FFCR); +coresight_tmc_simple_func(mode, TMC_MODE); +coresight_tmc_simple_func(pscr, TMC_PSCR); +coresight_tmc_simple_func(devid, CORESIGHT_DEVID); - tmc_rsz = readl_relaxed(drvdata->base + TMC_RSZ); - tmc_sts = readl_relaxed(drvdata->base + TMC_STS); - tmc_rrp = readl_relaxed(drvdata->base + TMC_RRP); - tmc_rwp = readl_relaxed(drvdata->base + TMC_RWP); - tmc_trg = readl_relaxed(drvdata->base + TMC_TRG); - tmc_ctl = readl_relaxed(drvdata->base + TMC_CTL); - tmc_ffsr = readl_relaxed(drvdata->base + TMC_FFSR); - tmc_ffcr = readl_relaxed(drvdata->base + TMC_FFCR); - tmc_mode = readl_relaxed(drvdata->base + TMC_MODE); - tmc_pscr = readl_relaxed(drvdata->base + TMC_PSCR); - devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); +static struct attribute *coresight_tmc_mgmt_attrs[] = { + &dev_attr_rsz.attr, + &dev_attr_sts.attr, + &dev_attr_rrp.attr, + &dev_attr_rwp.attr, + &dev_attr_trg.attr, + &dev_attr_ctl.attr, + &dev_attr_ffsr.attr, + &dev_attr_ffcr.attr, + &dev_attr_mode.attr, + &dev_attr_pscr.attr, + &dev_attr_devid.attr, + NULL, +}; - CS_LOCK(drvdata->base); - spin_unlock_irqrestore(&drvdata->spinlock, flags); - pm_runtime_put(drvdata->dev); - - return sprintf(buf, - "Depth:\t\t0x%x\n" - "Status:\t\t0x%x\n" - "RAM read ptr:\t0x%x\n" - "RAM wrt ptr:\t0x%x\n" - "Trigger cnt:\t0x%x\n" - "Control:\t0x%x\n" - "Flush status:\t0x%x\n" - "Flush ctrl:\t0x%x\n" - "Mode:\t\t0x%x\n" - "PSRC:\t\t0x%x\n" - "DEVID:\t\t0x%x\n", - tmc_rsz, tmc_sts, tmc_rrp, tmc_rwp, tmc_trg, - tmc_ctl, tmc_ffsr, tmc_ffcr, tmc_mode, tmc_pscr, devid); - - return -EINVAL; -} -static DEVICE_ATTR_RO(status); - -static ssize_t trigger_cntr_show(struct device *dev, - struct device_attribute *attr, char *buf) +ssize_t trigger_cntr_show(struct device *dev, + struct device_attribute *attr, char *buf) { struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent); unsigned long val = drvdata->trigger_cntr; @@ -630,26 +612,25 @@ static ssize_t trigger_cntr_store(struct device *dev, } static DEVICE_ATTR_RW(trigger_cntr); -static struct attribute *coresight_etb_attrs[] = { +static struct attribute *coresight_tmc_attrs[] = { &dev_attr_trigger_cntr.attr, - &dev_attr_status.attr, NULL, }; -ATTRIBUTE_GROUPS(coresight_etb); -static struct attribute *coresight_etr_attrs[] = { - &dev_attr_trigger_cntr.attr, - &dev_attr_status.attr, - NULL, +static const struct attribute_group coresight_tmc_group = { + .attrs = coresight_tmc_attrs, }; -ATTRIBUTE_GROUPS(coresight_etr); -static struct attribute *coresight_etf_attrs[] = { - &dev_attr_trigger_cntr.attr, - &dev_attr_status.attr, +static const struct attribute_group coresight_tmc_mgmt_group = { + .attrs = coresight_tmc_mgmt_attrs, + .name = "mgmt", +}; + +const struct attribute_group *coresight_tmc_groups[] = { + &coresight_tmc_group, + &coresight_tmc_mgmt_group, NULL, }; -ATTRIBUTE_GROUPS(coresight_etf); static int tmc_probe(struct amba_device *adev, const struct amba_id *id) { @@ -725,20 +706,18 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) desc->pdata = pdata; desc->dev = dev; desc->subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER; + desc->groups = coresight_tmc_groups; if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { desc->type = CORESIGHT_DEV_TYPE_SINK; desc->ops = &tmc_etb_cs_ops; - desc->groups = coresight_etb_groups; } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { desc->type = CORESIGHT_DEV_TYPE_SINK; desc->ops = &tmc_etr_cs_ops; - desc->groups = coresight_etr_groups; } else { desc->type = CORESIGHT_DEV_TYPE_LINKSINK; desc->subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO; desc->ops = &tmc_etf_cs_ops; - desc->groups = coresight_etf_groups; } drvdata->csdev = coresight_register(desc); From 43dc4e87f80f767136de5a6d8f5649f6195b9faa Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:44 -0600 Subject: [PATCH 217/656] coresight: tmc: modifying naming convention According to the TMC architectural state machine, the 'stopped' state is reached when bit 2 (TMCReady) of the TMC Status register turns to '1'. The code is correct but the naming convention isn't. The 'Triggered' bit occupies position '1' of the TMC Status register and has nothing to do with the indication of the TMC entering the stopped state. As such renaming function "tmc_wait_for_triggered()" and changing the #define to reflect what the code is really doing. This patch has no effect other than clarifying the semantic. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 580ff804ecaf5bc59835fec26e17325bcd53fc91) --- drivers/hwtracing/coresight/coresight-tmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 2b42ecbd8831..3f646e29a99b 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -77,7 +77,7 @@ #define TMC_FFCR_TRIGON_TRIGIN BIT(8) #define TMC_FFCR_STOP_ON_FLUSH BIT(12) -#define TMC_STS_TRIGGERED_BIT 2 +#define TMC_STS_TMCREADY_BIT 2 #define TMC_FFCR_FLUSHMAN_BIT 6 enum tmc_config_type { @@ -132,11 +132,11 @@ struct tmc_drvdata { u32 trigger_cntr; }; -static void tmc_wait_for_ready(struct tmc_drvdata *drvdata) +static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { /* Ensure formatter, unformatter and hardware fifo are empty */ if (coresight_timeout(drvdata->base, - TMC_STS, TMC_STS_TRIGGERED_BIT, 1)) { + TMC_STS, TMC_STS_TMCREADY_BIT, 1)) { dev_err(drvdata->dev, "timeout observed when probing at offset %#x\n", TMC_STS); @@ -160,7 +160,7 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata) TMC_FFCR); } - tmc_wait_for_ready(drvdata); + tmc_wait_for_tmcready(drvdata); } static void tmc_enable_hw(struct tmc_drvdata *drvdata) From 66c6e1177cfae0532c63b465bea65afaef289531 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:45 -0600 Subject: [PATCH 218/656] coresight: tmc: waiting for TMCReady bit before programming According to the TRM before programming the TMC in circular buffer mode (and that for any configuration, ETB, ETR, ETF), the TMCReady bit in the status register has to be set. This patch adds a check to make sure the state machine is in a state where it can be configured, and complains otherwise. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 358f42184e97f9a216b927a5a744597b98e0eee1) --- drivers/hwtracing/coresight/coresight-tmc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 3f646e29a99b..66fa7736d12f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -180,6 +180,9 @@ static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) CS_UNLOCK(drvdata->base); + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | @@ -201,6 +204,9 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) CS_UNLOCK(drvdata->base); + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ); writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); @@ -230,6 +236,9 @@ static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE); writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI, drvdata->base + TMC_FFCR); From 8356894677ad1f9900a037346514866c35cc4bd3 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:46 -0600 Subject: [PATCH 219/656] coresight: tmc: re-implementing tmc_read_prepare/unprepare() functions In their current implementation the tmc_read_prepare/unprepare() are a lump of if/else that is difficult to read. This patch is alleviating that by using a switch statement. The latter also allows for a better control on the error path. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b1789b793eb4627928f55a6acea8da7c25e5c6b4) --- drivers/hwtracing/coresight/coresight-tmc.c | 56 +++++++++++++-------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 66fa7736d12f..d211aeec49f8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -431,7 +431,7 @@ static const struct coresight_ops tmc_etf_cs_ops = { static int tmc_read_prepare(struct tmc_drvdata *drvdata) { - int ret; + int ret = 0; unsigned long flags; enum tmc_mode mode; @@ -439,25 +439,31 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata) if (!drvdata->enable) goto out; - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { + switch (drvdata->config_type) { + case TMC_CONFIG_TYPE_ETB: tmc_etb_disable_hw(drvdata); - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - tmc_etr_disable_hw(drvdata); - } else { + break; + case TMC_CONFIG_TYPE_ETF: + /* There is no point in reading a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); - if (mode == TMC_MODE_CIRCULAR_BUFFER) { - tmc_etb_disable_hw(drvdata); - } else { - ret = -ENODEV; + if (mode != TMC_MODE_CIRCULAR_BUFFER) { + ret = -EINVAL; goto err; } + + tmc_etb_disable_hw(drvdata); + break; + case TMC_CONFIG_TYPE_ETR: + tmc_etr_disable_hw(drvdata); + break; + default: + ret = -EINVAL; + goto err; } + out: drvdata->reading = true; - spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC read start\n"); - return 0; err: spin_unlock_irqrestore(&drvdata->spinlock, flags); return ret; @@ -472,20 +478,30 @@ static void tmc_read_unprepare(struct tmc_drvdata *drvdata) if (!drvdata->enable) goto out; - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { + switch (drvdata->config_type) { + case TMC_CONFIG_TYPE_ETB: tmc_etb_enable_hw(drvdata); - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - tmc_etr_enable_hw(drvdata); - } else { + break; + case TMC_CONFIG_TYPE_ETF: + /* Make sure we don't re-enable a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); - if (mode == TMC_MODE_CIRCULAR_BUFFER) - tmc_etb_enable_hw(drvdata); + if (mode != TMC_MODE_CIRCULAR_BUFFER) + goto err; + + tmc_etb_enable_hw(drvdata); + break; + case TMC_CONFIG_TYPE_ETR: + tmc_etr_disable_hw(drvdata); + break; + default: + goto err; } + out: drvdata->reading = false; - spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC read end\n"); +err: + spin_unlock_irqrestore(&drvdata->spinlock, flags); } static int tmc_open(struct inode *inode, struct file *file) From 00bc807d39000d6ff6efabcdc3728c87075c52f8 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:47 -0600 Subject: [PATCH 220/656] coresight: tmc: clearly define number of transfers per burst This patch makes the name of the define reflect the amount of data tranfers per burst, in this case 16. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit ebba56e7b2bd2c9c2bbe02fad8808feef18e1519) --- drivers/hwtracing/coresight/coresight-tmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index d211aeec49f8..8751d53fa078 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -67,7 +67,7 @@ #define TMC_AXICTL_PROT_CTL_B0 BIT(0) #define TMC_AXICTL_PROT_CTL_B1 BIT(1) #define TMC_AXICTL_SCT_GAT_MODE BIT(7) -#define TMC_AXICTL_WR_BURST_LEN 0xF00 +#define TMC_AXICTL_WR_BURST_16 0xF00 /* TMC_FFCR - 0x304 */ #define TMC_FFCR_EN_FMT BIT(0) #define TMC_FFCR_EN_TI BIT(1) @@ -211,7 +211,7 @@ static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); axictl = readl_relaxed(drvdata->base + TMC_AXICTL); - axictl |= TMC_AXICTL_WR_BURST_LEN; + axictl |= TMC_AXICTL_WR_BURST_16; writel_relaxed(axictl, drvdata->base + TMC_AXICTL); axictl &= ~TMC_AXICTL_SCT_GAT_MODE; writel_relaxed(axictl, drvdata->base + TMC_AXICTL); From c3e22ca4e145c472635ae17ecf7b60d531d98924 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:48 -0600 Subject: [PATCH 221/656] coresight: tmc: introducing new header file The amount of #define, enumeration and structure definition is big enough to justify moving them to a new header file. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4c324b5f0e8a692c8d077da9d18533820c2ab636) --- drivers/hwtracing/coresight/coresight-tmc.c | 102 +--------------- drivers/hwtracing/coresight/coresight-tmc.h | 122 ++++++++++++++++++++ 2 files changed, 123 insertions(+), 101 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-tmc.h diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 8751d53fa078..f9a6624ab531 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -30,107 +30,7 @@ #include #include "coresight-priv.h" - -#define TMC_RSZ 0x004 -#define TMC_STS 0x00c -#define TMC_RRD 0x010 -#define TMC_RRP 0x014 -#define TMC_RWP 0x018 -#define TMC_TRG 0x01c -#define TMC_CTL 0x020 -#define TMC_RWD 0x024 -#define TMC_MODE 0x028 -#define TMC_LBUFLEVEL 0x02c -#define TMC_CBUFLEVEL 0x030 -#define TMC_BUFWM 0x034 -#define TMC_RRPHI 0x038 -#define TMC_RWPHI 0x03c -#define TMC_AXICTL 0x110 -#define TMC_DBALO 0x118 -#define TMC_DBAHI 0x11c -#define TMC_FFSR 0x300 -#define TMC_FFCR 0x304 -#define TMC_PSCR 0x308 -#define TMC_ITMISCOP0 0xee0 -#define TMC_ITTRFLIN 0xee8 -#define TMC_ITATBDATA0 0xeec -#define TMC_ITATBCTR2 0xef0 -#define TMC_ITATBCTR1 0xef4 -#define TMC_ITATBCTR0 0xef8 - -/* register description */ -/* TMC_CTL - 0x020 */ -#define TMC_CTL_CAPT_EN BIT(0) -/* TMC_STS - 0x00C */ -#define TMC_STS_TRIGGERED BIT(1) -/* TMC_AXICTL - 0x110 */ -#define TMC_AXICTL_PROT_CTL_B0 BIT(0) -#define TMC_AXICTL_PROT_CTL_B1 BIT(1) -#define TMC_AXICTL_SCT_GAT_MODE BIT(7) -#define TMC_AXICTL_WR_BURST_16 0xF00 -/* TMC_FFCR - 0x304 */ -#define TMC_FFCR_EN_FMT BIT(0) -#define TMC_FFCR_EN_TI BIT(1) -#define TMC_FFCR_FON_FLIN BIT(4) -#define TMC_FFCR_FON_TRIG_EVT BIT(5) -#define TMC_FFCR_FLUSHMAN BIT(6) -#define TMC_FFCR_TRIGON_TRIGIN BIT(8) -#define TMC_FFCR_STOP_ON_FLUSH BIT(12) - -#define TMC_STS_TMCREADY_BIT 2 -#define TMC_FFCR_FLUSHMAN_BIT 6 - -enum tmc_config_type { - TMC_CONFIG_TYPE_ETB, - TMC_CONFIG_TYPE_ETR, - TMC_CONFIG_TYPE_ETF, -}; - -enum tmc_mode { - TMC_MODE_CIRCULAR_BUFFER, - TMC_MODE_SOFTWARE_FIFO, - TMC_MODE_HARDWARE_FIFO, -}; - -enum tmc_mem_intf_width { - TMC_MEM_INTF_WIDTH_32BITS = 0x2, - TMC_MEM_INTF_WIDTH_64BITS = 0x3, - TMC_MEM_INTF_WIDTH_128BITS = 0x4, - TMC_MEM_INTF_WIDTH_256BITS = 0x5, -}; - -/** - * struct tmc_drvdata - specifics associated to an TMC component - * @base: memory mapped base address for this component. - * @dev: the device entity associated to this component. - * @csdev: component vitals needed by the framework. - * @miscdev: specifics to handle "/dev/xyz.tmc" entry. - * @spinlock: only one at a time pls. - * @read_count: manages preparation of buffer for reading. - * @buf: area of memory where trace data get sent. - * @paddr: DMA start location in RAM. - * @vaddr: virtual representation of @paddr. - * @size: @buf size. - * @enable: this TMC is being used. - * @config_type: TMC variant, must be of type @tmc_config_type. - * @trigger_cntr: amount of words to store after a trigger. - */ -struct tmc_drvdata { - void __iomem *base; - struct device *dev; - struct coresight_device *csdev; - struct miscdevice miscdev; - spinlock_t spinlock; - int read_count; - bool reading; - char *buf; - dma_addr_t paddr; - void *vaddr; - u32 size; - bool enable; - enum tmc_config_type config_type; - u32 trigger_cntr; -}; +#include "coresight-tmc.h" static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h new file mode 100644 index 000000000000..49718b4a9788 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -0,0 +1,122 @@ +/* + * Copyright(C) 2015 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef _CORESIGHT_TMC_H +#define _CORESIGHT_TMC_H + +#define TMC_RSZ 0x004 +#define TMC_STS 0x00c +#define TMC_RRD 0x010 +#define TMC_RRP 0x014 +#define TMC_RWP 0x018 +#define TMC_TRG 0x01c +#define TMC_CTL 0x020 +#define TMC_RWD 0x024 +#define TMC_MODE 0x028 +#define TMC_LBUFLEVEL 0x02c +#define TMC_CBUFLEVEL 0x030 +#define TMC_BUFWM 0x034 +#define TMC_RRPHI 0x038 +#define TMC_RWPHI 0x03c +#define TMC_AXICTL 0x110 +#define TMC_DBALO 0x118 +#define TMC_DBAHI 0x11c +#define TMC_FFSR 0x300 +#define TMC_FFCR 0x304 +#define TMC_PSCR 0x308 +#define TMC_ITMISCOP0 0xee0 +#define TMC_ITTRFLIN 0xee8 +#define TMC_ITATBDATA0 0xeec +#define TMC_ITATBCTR2 0xef0 +#define TMC_ITATBCTR1 0xef4 +#define TMC_ITATBCTR0 0xef8 + +/* register description */ +/* TMC_CTL - 0x020 */ +#define TMC_CTL_CAPT_EN BIT(0) +/* TMC_STS - 0x00C */ +#define TMC_STS_TRIGGERED BIT(1) +/* TMC_AXICTL - 0x110 */ +#define TMC_AXICTL_PROT_CTL_B0 BIT(0) +#define TMC_AXICTL_PROT_CTL_B1 BIT(1) +#define TMC_AXICTL_SCT_GAT_MODE BIT(7) +#define TMC_AXICTL_WR_BURST_16 0xF00 +/* TMC_FFCR - 0x304 */ +#define TMC_FFCR_EN_FMT BIT(0) +#define TMC_FFCR_EN_TI BIT(1) +#define TMC_FFCR_FON_FLIN BIT(4) +#define TMC_FFCR_FON_TRIG_EVT BIT(5) +#define TMC_FFCR_FLUSHMAN BIT(6) +#define TMC_FFCR_TRIGON_TRIGIN BIT(8) +#define TMC_FFCR_STOP_ON_FLUSH BIT(12) + +#define TMC_STS_TMCREADY_BIT 2 +#define TMC_FFCR_FLUSHMAN_BIT 6 + +enum tmc_config_type { + TMC_CONFIG_TYPE_ETB, + TMC_CONFIG_TYPE_ETR, + TMC_CONFIG_TYPE_ETF, +}; + +enum tmc_mode { + TMC_MODE_CIRCULAR_BUFFER, + TMC_MODE_SOFTWARE_FIFO, + TMC_MODE_HARDWARE_FIFO, +}; + +enum tmc_mem_intf_width { + TMC_MEM_INTF_WIDTH_32BITS = 0x2, + TMC_MEM_INTF_WIDTH_64BITS = 0x3, + TMC_MEM_INTF_WIDTH_128BITS = 0x4, + TMC_MEM_INTF_WIDTH_256BITS = 0x5, +}; + +/** + * struct tmc_drvdata - specifics associated to an TMC component + * @base: memory mapped base address for this component. + * @dev: the device entity associated to this component. + * @csdev: component vitals needed by the framework. + * @miscdev: specifics to handle "/dev/xyz.tmc" entry. + * @spinlock: only one at a time pls. + * @read_count: manages preparation of buffer for reading. + * @buf: area of memory where trace data get sent. + * @paddr: DMA start location in RAM. + * @vaddr: virtual representation of @paddr. + * @size: @buf size. + * @enable: this TMC is being used. + * @config_type: TMC variant, must be of type @tmc_config_type. + * @trigger_cntr: amount of words to store after a trigger. + */ +struct tmc_drvdata { + void __iomem *base; + struct device *dev; + struct coresight_device *csdev; + struct miscdevice miscdev; + spinlock_t spinlock; + int read_count; + bool reading; + char *buf; + dma_addr_t paddr; + void __iomem *vaddr; + u32 size; + bool enable; + enum tmc_config_type config_type; + u32 trigger_cntr; +}; + +#endif From 5364b7d8eb0e51193f7a6e030fe83481b575f277 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:49 -0600 Subject: [PATCH 222/656] coresight: tmc: cleaning up header file This patch first move the TMC_STS_TMCREADY_BIT and TMC_FFCR_FLUSHMAN_BIT defines to their respective section. It also removes TMC_FFCR_FLUSHMAN, since the same result can easily be obtained using the BIT() macro. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a8ab4268e0db93c564ee6ccb770bb3d53af24be9) --- drivers/hwtracing/coresight/coresight-tmc.c | 2 +- drivers/hwtracing/coresight/coresight-tmc.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index f9a6624ab531..07e2809d832b 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -50,7 +50,7 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata) ffcr = readl_relaxed(drvdata->base + TMC_FFCR); ffcr |= TMC_FFCR_STOP_ON_FLUSH; writel_relaxed(ffcr, drvdata->base + TMC_FFCR); - ffcr |= TMC_FFCR_FLUSHMAN; + ffcr |= BIT(TMC_FFCR_FLUSHMAN_BIT); writel_relaxed(ffcr, drvdata->base + TMC_FFCR); /* Ensure flush completes */ if (coresight_timeout(drvdata->base, diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 49718b4a9788..5a60830c8db5 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -49,6 +49,7 @@ /* TMC_CTL - 0x020 */ #define TMC_CTL_CAPT_EN BIT(0) /* TMC_STS - 0x00C */ +#define TMC_STS_TMCREADY_BIT 2 #define TMC_STS_TRIGGERED BIT(1) /* TMC_AXICTL - 0x110 */ #define TMC_AXICTL_PROT_CTL_B0 BIT(0) @@ -56,16 +57,14 @@ #define TMC_AXICTL_SCT_GAT_MODE BIT(7) #define TMC_AXICTL_WR_BURST_16 0xF00 /* TMC_FFCR - 0x304 */ +#define TMC_FFCR_FLUSHMAN_BIT 6 #define TMC_FFCR_EN_FMT BIT(0) #define TMC_FFCR_EN_TI BIT(1) #define TMC_FFCR_FON_FLIN BIT(4) #define TMC_FFCR_FON_TRIG_EVT BIT(5) -#define TMC_FFCR_FLUSHMAN BIT(6) #define TMC_FFCR_TRIGON_TRIGIN BIT(8) #define TMC_FFCR_STOP_ON_FLUSH BIT(12) -#define TMC_STS_TMCREADY_BIT 2 -#define TMC_FFCR_FLUSHMAN_BIT 6 enum tmc_config_type { TMC_CONFIG_TYPE_ETB, From de03aeaf645691669fbcda2ec364c143ab7d900b Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:50 -0600 Subject: [PATCH 223/656] coresight: tmc: splitting driver in ETB/ETF and ETR components The TMC block can operate in 3 modes (ETB, ETF and ETR) and accessed via two interfaces (sysFS and Perf). That makes 6 mode to cover, which is way too much coupling for a single file. This patch splits the original TMC driver in 2 halves, one for ETB/ETF and another one for ETR mode. A common core is kept for functionality common to all 3 modes. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 6c6ed1e244c0530fb76a8b52024f199f398ef100) --- drivers/hwtracing/coresight/Makefile | 4 +- .../hwtracing/coresight/coresight-tmc-etf.c | 204 ++++++++++++++ .../hwtracing/coresight/coresight-tmc-etr.c | 128 +++++++++ drivers/hwtracing/coresight/coresight-tmc.c | 264 +----------------- drivers/hwtracing/coresight/coresight-tmc.h | 18 ++ 5 files changed, 357 insertions(+), 261 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etf.c create mode 100644 drivers/hwtracing/coresight/coresight-tmc-etr.c diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index c6f84b57f52a..af480d9c1441 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -3,7 +3,9 @@ # obj-$(CONFIG_CORESIGHT) += coresight.o coresight-etm-perf.o obj-$(CONFIG_OF) += of_coresight.o -obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o +obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o \ + coresight-tmc-etf.o \ + coresight-tmc-etr.o obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \ diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c new file mode 100644 index 000000000000..467d19221f7b --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -0,0 +1,204 @@ +/* + * Copyright(C) 2016 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include "coresight-priv.h" +#include "coresight-tmc.h" + +void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) +{ + /* Zero out the memory to help with debug */ + memset(drvdata->buf, 0, drvdata->size); + + CS_UNLOCK(drvdata->base); + + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + + writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); + writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | + TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | + TMC_FFCR_TRIGON_TRIGIN, + drvdata->base + TMC_FFCR); + + writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); + tmc_enable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) +{ + enum tmc_mem_intf_width memwidth; + u8 memwords; + char *bufp; + u32 read_data; + int i; + + memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10); + if (memwidth == TMC_MEM_INTF_WIDTH_32BITS) + memwords = 1; + else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS) + memwords = 2; + else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS) + memwords = 4; + else + memwords = 8; + + bufp = drvdata->buf; + while (1) { + for (i = 0; i < memwords; i++) { + read_data = readl_relaxed(drvdata->base + TMC_RRD); + if (read_data == 0xFFFFFFFF) + return; + memcpy(bufp, &read_data, 4); + bufp += 4; + } + } +} + +void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + tmc_flush_and_stop(drvdata); + tmc_etb_dump_hw(drvdata); + tmc_disable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + + writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE); + writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI, + drvdata->base + TMC_FFCR); + writel_relaxed(0x0, drvdata->base + TMC_BUFWM); + tmc_enable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + tmc_flush_and_stop(drvdata); + tmc_disable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EBUSY; + } + + tmc_etb_enable_hw(drvdata); + drvdata->enable = true; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n"); + return 0; +} + +static void tmc_disable_etf_sink(struct coresight_device *csdev) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return; + } + + tmc_etb_disable_hw(drvdata); + drvdata->enable = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n"); +} + +static int tmc_enable_etf_link(struct coresight_device *csdev, + int inport, int outport) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EBUSY; + } + + tmc_etf_enable_hw(drvdata); + drvdata->enable = true; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETF enabled\n"); + return 0; +} + +static void tmc_disable_etf_link(struct coresight_device *csdev, + int inport, int outport) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return; + } + + tmc_etf_disable_hw(drvdata); + drvdata->enable = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC disabled\n"); +} + +static const struct coresight_ops_sink tmc_etf_sink_ops = { + .enable = tmc_enable_etf_sink, + .disable = tmc_disable_etf_sink, +}; + +static const struct coresight_ops_link tmc_etf_link_ops = { + .enable = tmc_enable_etf_link, + .disable = tmc_disable_etf_link, +}; + +const struct coresight_ops tmc_etb_cs_ops = { + .sink_ops = &tmc_etf_sink_ops, +}; + +const struct coresight_ops tmc_etf_cs_ops = { + .sink_ops = &tmc_etf_sink_ops, + .link_ops = &tmc_etf_link_ops, +}; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c new file mode 100644 index 000000000000..5d9333ec49ae --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -0,0 +1,128 @@ +/* + * Copyright(C) 2016 Linaro Limited. All rights reserved. + * Author: Mathieu Poirier + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include "coresight-priv.h" +#include "coresight-tmc.h" + +void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) +{ + u32 axictl; + + /* Zero out the memory to help with debug */ + memset(drvdata->vaddr, 0, drvdata->size); + + CS_UNLOCK(drvdata->base); + + /* Wait for TMCSReady bit to be set */ + tmc_wait_for_tmcready(drvdata); + + writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ); + writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); + + axictl = readl_relaxed(drvdata->base + TMC_AXICTL); + axictl |= TMC_AXICTL_WR_BURST_16; + writel_relaxed(axictl, drvdata->base + TMC_AXICTL); + axictl &= ~TMC_AXICTL_SCT_GAT_MODE; + writel_relaxed(axictl, drvdata->base + TMC_AXICTL); + axictl = (axictl & + ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) | + TMC_AXICTL_PROT_CTL_B1; + writel_relaxed(axictl, drvdata->base + TMC_AXICTL); + + writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO); + writel_relaxed(0x0, drvdata->base + TMC_DBAHI); + writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | + TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | + TMC_FFCR_TRIGON_TRIGIN, + drvdata->base + TMC_FFCR); + writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); + tmc_enable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) +{ + u32 rwp, val; + + rwp = readl_relaxed(drvdata->base + TMC_RWP); + val = readl_relaxed(drvdata->base + TMC_STS); + + /* How much memory do we still have */ + if (val & BIT(0)) + drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; + else + drvdata->buf = drvdata->vaddr; +} + +void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) +{ + CS_UNLOCK(drvdata->base); + + tmc_flush_and_stop(drvdata); + tmc_etr_dump_hw(drvdata); + tmc_disable_hw(drvdata); + + CS_LOCK(drvdata->base); +} + +static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EBUSY; + } + + tmc_etr_enable_hw(drvdata); + drvdata->enable = true; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETR enabled\n"); + return 0; +} + +static void tmc_disable_etr_sink(struct coresight_device *csdev) +{ + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return; + } + + tmc_etr_disable_hw(drvdata); + drvdata->enable = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + dev_info(drvdata->dev, "TMC-ETR disabled\n"); +} + +static const struct coresight_ops_sink tmc_etr_sink_ops = { + .enable = tmc_enable_etr_sink, + .disable = tmc_disable_etr_sink, +}; + +const struct coresight_ops tmc_etr_cs_ops = { + .sink_ops = &tmc_etr_sink_ops, +}; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 07e2809d832b..8d7f6d54c9b0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -32,7 +32,7 @@ #include "coresight-priv.h" #include "coresight-tmc.h" -static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) +void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) { /* Ensure formatter, unformatter and hardware fifo are empty */ if (coresight_timeout(drvdata->base, @@ -43,7 +43,7 @@ static void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata) } } -static void tmc_flush_and_stop(struct tmc_drvdata *drvdata) +void tmc_flush_and_stop(struct tmc_drvdata *drvdata) { u32 ffcr; @@ -63,272 +63,16 @@ static void tmc_flush_and_stop(struct tmc_drvdata *drvdata) tmc_wait_for_tmcready(drvdata); } -static void tmc_enable_hw(struct tmc_drvdata *drvdata) +void tmc_enable_hw(struct tmc_drvdata *drvdata) { writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL); } -static void tmc_disable_hw(struct tmc_drvdata *drvdata) +void tmc_disable_hw(struct tmc_drvdata *drvdata) { writel_relaxed(0x0, drvdata->base + TMC_CTL); } -static void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) -{ - /* Zero out the memory to help with debug */ - memset(drvdata->buf, 0, drvdata->size); - - CS_UNLOCK(drvdata->base); - - /* Wait for TMCSReady bit to be set */ - tmc_wait_for_tmcready(drvdata); - - writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); - writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | - TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | - TMC_FFCR_TRIGON_TRIGIN, - drvdata->base + TMC_FFCR); - - writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); - tmc_enable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_etr_enable_hw(struct tmc_drvdata *drvdata) -{ - u32 axictl; - - /* Zero out the memory to help with debug */ - memset(drvdata->vaddr, 0, drvdata->size); - - CS_UNLOCK(drvdata->base); - - /* Wait for TMCSReady bit to be set */ - tmc_wait_for_tmcready(drvdata); - - writel_relaxed(drvdata->size / 4, drvdata->base + TMC_RSZ); - writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); - - axictl = readl_relaxed(drvdata->base + TMC_AXICTL); - axictl |= TMC_AXICTL_WR_BURST_16; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - axictl &= ~TMC_AXICTL_SCT_GAT_MODE; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - axictl = (axictl & - ~(TMC_AXICTL_PROT_CTL_B0 | TMC_AXICTL_PROT_CTL_B1)) | - TMC_AXICTL_PROT_CTL_B1; - writel_relaxed(axictl, drvdata->base + TMC_AXICTL); - - writel_relaxed(drvdata->paddr, drvdata->base + TMC_DBALO); - writel_relaxed(0x0, drvdata->base + TMC_DBAHI); - writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI | - TMC_FFCR_FON_FLIN | TMC_FFCR_FON_TRIG_EVT | - TMC_FFCR_TRIGON_TRIGIN, - drvdata->base + TMC_FFCR); - writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); - tmc_enable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) -{ - CS_UNLOCK(drvdata->base); - - /* Wait for TMCSReady bit to be set */ - tmc_wait_for_tmcready(drvdata); - - writel_relaxed(TMC_MODE_HARDWARE_FIFO, drvdata->base + TMC_MODE); - writel_relaxed(TMC_FFCR_EN_FMT | TMC_FFCR_EN_TI, - drvdata->base + TMC_FFCR); - writel_relaxed(0x0, drvdata->base + TMC_BUFWM); - tmc_enable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) -{ - unsigned long flags; - - spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) { - spin_unlock_irqrestore(&drvdata->spinlock, flags); - return -EBUSY; - } - - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { - tmc_etb_enable_hw(drvdata); - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - tmc_etr_enable_hw(drvdata); - } else { - if (mode == TMC_MODE_CIRCULAR_BUFFER) - tmc_etb_enable_hw(drvdata); - else - tmc_etf_enable_hw(drvdata); - } - drvdata->enable = true; - spin_unlock_irqrestore(&drvdata->spinlock, flags); - - dev_info(drvdata->dev, "TMC enabled\n"); - return 0; -} - -static int tmc_enable_sink(struct coresight_device *csdev, u32 mode) -{ - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - return tmc_enable(drvdata, TMC_MODE_CIRCULAR_BUFFER); -} - -static int tmc_enable_link(struct coresight_device *csdev, int inport, - int outport) -{ - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - return tmc_enable(drvdata, TMC_MODE_HARDWARE_FIFO); -} - -static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) -{ - enum tmc_mem_intf_width memwidth; - u8 memwords; - char *bufp; - u32 read_data; - int i; - - memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10); - if (memwidth == TMC_MEM_INTF_WIDTH_32BITS) - memwords = 1; - else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS) - memwords = 2; - else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS) - memwords = 4; - else - memwords = 8; - - bufp = drvdata->buf; - while (1) { - for (i = 0; i < memwords; i++) { - read_data = readl_relaxed(drvdata->base + TMC_RRD); - if (read_data == 0xFFFFFFFF) - return; - memcpy(bufp, &read_data, 4); - bufp += 4; - } - } -} - -static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) -{ - CS_UNLOCK(drvdata->base); - - tmc_flush_and_stop(drvdata); - tmc_etb_dump_hw(drvdata); - tmc_disable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) -{ - u32 rwp, val; - - rwp = readl_relaxed(drvdata->base + TMC_RWP); - val = readl_relaxed(drvdata->base + TMC_STS); - - /* How much memory do we still have */ - if (val & BIT(0)) - drvdata->buf = drvdata->vaddr + rwp - drvdata->paddr; - else - drvdata->buf = drvdata->vaddr; -} - -static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) -{ - CS_UNLOCK(drvdata->base); - - tmc_flush_and_stop(drvdata); - tmc_etr_dump_hw(drvdata); - tmc_disable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) -{ - CS_UNLOCK(drvdata->base); - - tmc_flush_and_stop(drvdata); - tmc_disable_hw(drvdata); - - CS_LOCK(drvdata->base); -} - -static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode) -{ - unsigned long flags; - - spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) - goto out; - - if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { - tmc_etb_disable_hw(drvdata); - } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - tmc_etr_disable_hw(drvdata); - } else { - if (mode == TMC_MODE_CIRCULAR_BUFFER) - tmc_etb_disable_hw(drvdata); - else - tmc_etf_disable_hw(drvdata); - } -out: - drvdata->enable = false; - spin_unlock_irqrestore(&drvdata->spinlock, flags); - - dev_info(drvdata->dev, "TMC disabled\n"); -} - -static void tmc_disable_sink(struct coresight_device *csdev) -{ - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - tmc_disable(drvdata, TMC_MODE_CIRCULAR_BUFFER); -} - -static void tmc_disable_link(struct coresight_device *csdev, int inport, - int outport) -{ - struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); - - tmc_disable(drvdata, TMC_MODE_HARDWARE_FIFO); -} - -static const struct coresight_ops_sink tmc_sink_ops = { - .enable = tmc_enable_sink, - .disable = tmc_disable_sink, -}; - -static const struct coresight_ops_link tmc_link_ops = { - .enable = tmc_enable_link, - .disable = tmc_disable_link, -}; - -static const struct coresight_ops tmc_etb_cs_ops = { - .sink_ops = &tmc_sink_ops, -}; - -static const struct coresight_ops tmc_etr_cs_ops = { - .sink_ops = &tmc_sink_ops, -}; - -static const struct coresight_ops tmc_etf_cs_ops = { - .sink_ops = &tmc_sink_ops, - .link_ops = &tmc_link_ops, -}; - static int tmc_read_prepare(struct tmc_drvdata *drvdata) { int ret = 0; diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 5a60830c8db5..b3017e115c4d 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -18,6 +18,8 @@ #ifndef _CORESIGHT_TMC_H #define _CORESIGHT_TMC_H +#include + #define TMC_RSZ 0x004 #define TMC_STS 0x00c #define TMC_RRD 0x010 @@ -118,4 +120,20 @@ struct tmc_drvdata { u32 trigger_cntr; }; +/* Generic functions */ +void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata); +void tmc_flush_and_stop(struct tmc_drvdata *drvdata); +void tmc_enable_hw(struct tmc_drvdata *drvdata); +void tmc_disable_hw(struct tmc_drvdata *drvdata); + +/* ETB/ETF functions */ +void tmc_etb_enable_hw(struct tmc_drvdata *drvdata); +void tmc_etb_disable_hw(struct tmc_drvdata *drvdata); +extern const struct coresight_ops tmc_etb_cs_ops; +extern const struct coresight_ops tmc_etf_cs_ops; + +/* ETR functions */ +void tmc_etr_enable_hw(struct tmc_drvdata *drvdata); +void tmc_etr_disable_hw(struct tmc_drvdata *drvdata); +extern const struct coresight_ops tmc_etr_cs_ops; #endif From 5e0bd5f02c29e095fbd68b3732e6ee83f1c941ad Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:51 -0600 Subject: [PATCH 224/656] coresight: tmc: making prepare/unprepare functions generic Dealing with HW related matters in tmc_read_prepare/unprepare becomes convoluted when many cases need to be handled distinctively. As such moving processing related to HW setup to individual driver files and keep the core driver generic. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4525412a5046692abb7a0588589d8ed2c20585e0) --- .../hwtracing/coresight/coresight-tmc-etf.c | 62 ++++++++++++++++++- .../hwtracing/coresight/coresight-tmc-etr.c | 42 ++++++++++++- drivers/hwtracing/coresight/coresight-tmc.c | 55 ++++------------ drivers/hwtracing/coresight/coresight-tmc.h | 8 +-- 4 files changed, 117 insertions(+), 50 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 467d19221f7b..91e43572ce9f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -71,7 +71,7 @@ static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) } } -void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) +static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -202,3 +202,63 @@ const struct coresight_ops tmc_etf_cs_ops = { .sink_ops = &tmc_etf_sink_ops, .link_ops = &tmc_etf_link_ops, }; + +int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) +{ + enum tmc_mode mode; + int ret = 0; + unsigned long flags; + + /* config types are set a boot time and never change */ + if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB && + drvdata->config_type != TMC_CONFIG_TYPE_ETF)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* There is no point in reading a TMC in HW FIFO mode */ + mode = readl_relaxed(drvdata->base + TMC_MODE); + if (mode != TMC_MODE_CIRCULAR_BUFFER) { + ret = -EINVAL; + goto out; + } + + /* Disable the TMC if need be */ + if (drvdata->enable) + tmc_etb_disable_hw(drvdata); + + drvdata->reading = true; +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return ret; +} + +int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) +{ + enum tmc_mode mode; + unsigned long flags; + + /* config types are set a boot time and never change */ + if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB && + drvdata->config_type != TMC_CONFIG_TYPE_ETF)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* There is no point in reading a TMC in HW FIFO mode */ + mode = readl_relaxed(drvdata->base + TMC_MODE); + if (mode != TMC_MODE_CIRCULAR_BUFFER) { + spin_unlock_irqrestore(&drvdata->spinlock, flags); + return -EINVAL; + } + + /* Re-enable the TMC if need be */ + if (drvdata->enable) + tmc_etb_enable_hw(drvdata); + + drvdata->reading = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return 0; +} diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 5d9333ec49ae..3483d139a4ac 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -70,7 +70,7 @@ static void tmc_etr_dump_hw(struct tmc_drvdata *drvdata) drvdata->buf = drvdata->vaddr; } -void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) +static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) { CS_UNLOCK(drvdata->base); @@ -126,3 +126,43 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = { const struct coresight_ops tmc_etr_cs_ops = { .sink_ops = &tmc_etr_sink_ops, }; + +int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) +{ + unsigned long flags; + + /* config types are set a boot time and never change */ + if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* Disable the TMC if need be */ + if (drvdata->enable) + tmc_etr_disable_hw(drvdata); + + drvdata->reading = true; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return 0; +} + +int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) +{ + unsigned long flags; + + /* config types are set a boot time and never change */ + if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + + /* RE-enable the TMC if need be */ + if (drvdata->enable) + tmc_etr_enable_hw(drvdata); + + drvdata->reading = false; + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return 0; +} diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 8d7f6d54c9b0..63f8e55116a6 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -76,76 +76,43 @@ void tmc_disable_hw(struct tmc_drvdata *drvdata) static int tmc_read_prepare(struct tmc_drvdata *drvdata) { int ret = 0; - unsigned long flags; - enum tmc_mode mode; - - spin_lock_irqsave(&drvdata->spinlock, flags); - if (!drvdata->enable) - goto out; switch (drvdata->config_type) { case TMC_CONFIG_TYPE_ETB: - tmc_etb_disable_hw(drvdata); - break; case TMC_CONFIG_TYPE_ETF: - /* There is no point in reading a TMC in HW FIFO mode */ - mode = readl_relaxed(drvdata->base + TMC_MODE); - if (mode != TMC_MODE_CIRCULAR_BUFFER) { - ret = -EINVAL; - goto err; - } - - tmc_etb_disable_hw(drvdata); + ret = tmc_read_prepare_etb(drvdata); break; case TMC_CONFIG_TYPE_ETR: - tmc_etr_disable_hw(drvdata); + ret = tmc_read_prepare_etr(drvdata); break; default: ret = -EINVAL; - goto err; } -out: - drvdata->reading = true; - dev_info(drvdata->dev, "TMC read start\n"); -err: - spin_unlock_irqrestore(&drvdata->spinlock, flags); + if (!ret) + dev_info(drvdata->dev, "TMC read start\n"); + return ret; } static void tmc_read_unprepare(struct tmc_drvdata *drvdata) { - unsigned long flags; - enum tmc_mode mode; - - spin_lock_irqsave(&drvdata->spinlock, flags); - if (!drvdata->enable) - goto out; + int ret = 0; switch (drvdata->config_type) { case TMC_CONFIG_TYPE_ETB: - tmc_etb_enable_hw(drvdata); - break; case TMC_CONFIG_TYPE_ETF: - /* Make sure we don't re-enable a TMC in HW FIFO mode */ - mode = readl_relaxed(drvdata->base + TMC_MODE); - if (mode != TMC_MODE_CIRCULAR_BUFFER) - goto err; - - tmc_etb_enable_hw(drvdata); + ret = tmc_read_unprepare_etb(drvdata); break; case TMC_CONFIG_TYPE_ETR: - tmc_etr_disable_hw(drvdata); + ret = tmc_read_unprepare_etr(drvdata); break; default: - goto err; + ret = -EINVAL; } -out: - drvdata->reading = false; - dev_info(drvdata->dev, "TMC read end\n"); -err: - spin_unlock_irqrestore(&drvdata->spinlock, flags); + if (!ret) + dev_info(drvdata->dev, "TMC read end\n"); } static int tmc_open(struct inode *inode, struct file *file) diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index b3017e115c4d..df661903f83c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -127,13 +127,13 @@ void tmc_enable_hw(struct tmc_drvdata *drvdata); void tmc_disable_hw(struct tmc_drvdata *drvdata); /* ETB/ETF functions */ -void tmc_etb_enable_hw(struct tmc_drvdata *drvdata); -void tmc_etb_disable_hw(struct tmc_drvdata *drvdata); +int tmc_read_prepare_etb(struct tmc_drvdata *drvdata); +int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata); extern const struct coresight_ops tmc_etb_cs_ops; extern const struct coresight_ops tmc_etf_cs_ops; /* ETR functions */ -void tmc_etr_enable_hw(struct tmc_drvdata *drvdata); -void tmc_etr_disable_hw(struct tmc_drvdata *drvdata); +int tmc_read_prepare_etr(struct tmc_drvdata *drvdata); +int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata); extern const struct coresight_ops tmc_etr_cs_ops; #endif From 17c61c32ab5a2e0ff67e00e4d562ba0a97b999f2 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:52 -0600 Subject: [PATCH 225/656] coresight: tmc: allocating memory when needed In it's current form the TMC probe() function allocates trace buffer memory at boot time, event if coresight isn't used. This is highly inefficient since trace buffers can occupy a lot of memory that could be used otherwised. This patch allocates trace buffers on the fly, when the coresight subsystem is solicited. Allocated buffers are released when traces are read using the device descriptors under /dev. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit de5461970b3e9e19470b821f5feaa3235ceb35f5) --- .../hwtracing/coresight/coresight-tmc-etf.c | 90 ++++++++++++++++-- .../hwtracing/coresight/coresight-tmc-etr.c | 93 ++++++++++++++++++- drivers/hwtracing/coresight/coresight-tmc.c | 14 --- 3 files changed, 170 insertions(+), 27 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 91e43572ce9f..6eb1665cfc9c 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -16,14 +16,12 @@ */ #include +#include #include "coresight-priv.h" #include "coresight-tmc.h" void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) { - /* Zero out the memory to help with debug */ - memset(drvdata->buf, 0, drvdata->size); - CS_UNLOCK(drvdata->base); /* Wait for TMCSReady bit to be set */ @@ -110,21 +108,67 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) { + int ret = 0; + bool used = false; + char *buf = NULL; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + /* This shouldn't be happening */ + if (WARN_ON(mode != CS_MODE_SYSFS)) + return -EINVAL; + + /* + * If we don't have a buffer release the lock and allocate memory. + * Otherwise keep the lock and move along. + */ spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) { + if (!drvdata->buf) { spin_unlock_irqrestore(&drvdata->spinlock, flags); - return -EBUSY; + + /* Allocating the memory here while outside of the spinlock */ + buf = kzalloc(drvdata->size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* Let's try again */ + spin_lock_irqsave(&drvdata->spinlock, flags); + } + + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + + /* + * If drvdata::buf isn't NULL, memory was allocated for a previous + * trace run but wasn't read. If so simply zero-out the memory. + * Otherwise use the memory allocated above. + * + * The memory is freed when users read the buffer using the + * /dev/xyz.{etf|etb} interface. See tmc_read_unprepare_etf() for + * details. + */ + if (drvdata->buf) { + memset(drvdata->buf, 0, drvdata->size); + } else { + used = true; + drvdata->buf = buf; } tmc_etb_enable_hw(drvdata); drvdata->enable = true; +out: spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n"); - return 0; + /* Free memory outside the spinlock if need be */ + if (!used && buf) + kfree(buf); + + if (!ret) + dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n"); + + return ret; } static void tmc_disable_etf_sink(struct coresight_device *csdev) @@ -223,6 +267,12 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) goto out; } + /* If drvdata::buf is NULL the trace data has been read already */ + if (drvdata->buf == NULL) { + ret = -EINVAL; + goto out; + } + /* Disable the TMC if need be */ if (drvdata->enable) tmc_etb_disable_hw(drvdata); @@ -236,6 +286,7 @@ out: int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) { + char *buf = NULL; enum tmc_mode mode; unsigned long flags; @@ -254,11 +305,34 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) } /* Re-enable the TMC if need be */ - if (drvdata->enable) + if (drvdata->enable) { + /* + * The trace run will continue with the same allocated trace + * buffer. As such zero-out the buffer so that we don't end + * up with stale data. + * + * Since the tracer is still enabled drvdata::buf + * can't be NULL. + */ + memset(drvdata->buf, 0, drvdata->size); tmc_etb_enable_hw(drvdata); + } else { + /* + * The ETB/ETF is not tracing and the buffer was just read. + * As such prepare to free the trace buffer. + */ + buf = drvdata->buf; + drvdata->buf = NULL; + } drvdata->reading = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); + /* + * Free allocated memory outside of the spinlock. There is no need + * to assert the validity of 'buf' since calling kfree(NULL) is safe. + */ + kfree(buf); + return 0; } diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 3483d139a4ac..ac37bf904fb7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -16,6 +16,7 @@ */ #include +#include #include "coresight-priv.h" #include "coresight-tmc.h" @@ -83,21 +84,71 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) { + int ret = 0; + bool used = false; unsigned long flags; + void __iomem *vaddr = NULL; + dma_addr_t paddr; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + /* This shouldn't be happening */ + if (WARN_ON(mode != CS_MODE_SYSFS)) + return -EINVAL; + + /* + * If we don't have a buffer release the lock and allocate memory. + * Otherwise keep the lock and move along. + */ spin_lock_irqsave(&drvdata->spinlock, flags); - if (drvdata->reading) { + if (!drvdata->vaddr) { spin_unlock_irqrestore(&drvdata->spinlock, flags); - return -EBUSY; + + /* + * Contiguous memory can't be allocated while a spinlock is + * held. As such allocate memory here and free it if a buffer + * has already been allocated (from a previous session). + */ + vaddr = dma_alloc_coherent(drvdata->dev, drvdata->size, + &paddr, GFP_KERNEL); + if (!vaddr) + return -ENOMEM; + + /* Let's try again */ + spin_lock_irqsave(&drvdata->spinlock, flags); } + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + + /* + * If drvdata::buf == NULL, use the memory allocated above. + * Otherwise a buffer still exists from a previous session, so + * simply use that. + */ + if (drvdata->buf == NULL) { + used = true; + drvdata->vaddr = vaddr; + drvdata->paddr = paddr; + drvdata->buf = drvdata->vaddr; + } + + memset(drvdata->vaddr, 0, drvdata->size); + tmc_etr_enable_hw(drvdata); drvdata->enable = true; +out: spin_unlock_irqrestore(&drvdata->spinlock, flags); - dev_info(drvdata->dev, "TMC-ETR enabled\n"); - return 0; + /* Free memory outside the spinlock if need be */ + if (!used && vaddr) + dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr); + + if (!ret) + dev_info(drvdata->dev, "TMC-ETR enabled\n"); + + return ret; } static void tmc_disable_etr_sink(struct coresight_device *csdev) @@ -129,6 +180,7 @@ const struct coresight_ops tmc_etr_cs_ops = { int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) { + int ret = 0; unsigned long flags; /* config types are set a boot time and never change */ @@ -137,11 +189,18 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); + /* If drvdata::buf is NULL the trace data has been read already */ + if (drvdata->buf == NULL) { + ret = -EINVAL; + goto out; + } + /* Disable the TMC if need be */ if (drvdata->enable) tmc_etr_disable_hw(drvdata); drvdata->reading = true; +out: spin_unlock_irqrestore(&drvdata->spinlock, flags); return 0; @@ -150,6 +209,8 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) { unsigned long flags; + dma_addr_t paddr; + void __iomem *vaddr = NULL; /* config types are set a boot time and never change */ if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR)) @@ -158,11 +219,33 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); /* RE-enable the TMC if need be */ - if (drvdata->enable) + if (drvdata->enable) { + /* + * The trace run will continue with the same allocated trace + * buffer. As such zero-out the buffer so that we don't end + * up with stale data. + * + * Since the tracer is still enabled drvdata::buf + * can't be NULL. + */ + memset(drvdata->buf, 0, drvdata->size); tmc_etr_enable_hw(drvdata); + } else { + /* + * The ETR is not tracing and the buffer was just read. + * As such prepare to free the trace buffer. + */ + vaddr = drvdata->vaddr; + paddr = drvdata->paddr; + drvdata->buf = NULL; + } drvdata->reading = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); + /* Free allocated memory out side of the spinlock */ + if (vaddr) + dma_free_coherent(drvdata->dev, drvdata->size, vaddr, paddr); + return 0; } diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index 63f8e55116a6..e8e12a9b917a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -319,20 +319,6 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) pm_runtime_put(&adev->dev); - if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { - drvdata->vaddr = dma_alloc_coherent(dev, drvdata->size, - &drvdata->paddr, GFP_KERNEL); - if (!drvdata->vaddr) - return -ENOMEM; - - memset(drvdata->vaddr, 0, drvdata->size); - drvdata->buf = drvdata->vaddr; - } else { - drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL); - if (!drvdata->buf) - return -ENOMEM; - } - desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); if (!desc) { ret = -ENOMEM; From 09c5cf1702bf498bd5d30e94eda0d855dbb301df Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:53 -0600 Subject: [PATCH 226/656] coresight: tmc: getting rid of multiple read access Allowing multiple readers to access the trace data simultaniously via sysFS provides no shortage of opportunity for race condition, mandates two variable to be maintained (drvdata::read_count and drvdata::reading), makes the code complex and provide little advantages, if any. This patch streamlines the read process by restricting trace data access to a single user. That way drvdata::read_count can be eliminated and race conditions (along with faulty error handling) in function tmc_open() and tmc_release() eliminated. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f74debbea0885ebb65fb3fa4e598323f40b03f5f) --- .../hwtracing/coresight/coresight-tmc-etf.c | 5 ++++ .../hwtracing/coresight/coresight-tmc-etr.c | 4 ++++ drivers/hwtracing/coresight/coresight-tmc.c | 24 +++++++------------ drivers/hwtracing/coresight/coresight-tmc.h | 2 -- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 6eb1665cfc9c..60edf4d1968f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -260,6 +260,11 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } + /* There is no point in reading a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode != TMC_MODE_CIRCULAR_BUFFER) { diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index ac37bf904fb7..d6999b457fb8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -188,6 +188,10 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) return -EINVAL; spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + ret = -EBUSY; + goto out; + } /* If drvdata::buf is NULL the trace data has been read already */ if (drvdata->buf == NULL) { diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index e8e12a9b917a..ae7525a2b94a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -95,7 +95,7 @@ static int tmc_read_prepare(struct tmc_drvdata *drvdata) return ret; } -static void tmc_read_unprepare(struct tmc_drvdata *drvdata) +static int tmc_read_unprepare(struct tmc_drvdata *drvdata) { int ret = 0; @@ -113,21 +113,20 @@ static void tmc_read_unprepare(struct tmc_drvdata *drvdata) if (!ret) dev_info(drvdata->dev, "TMC read end\n"); + + return ret; } static int tmc_open(struct inode *inode, struct file *file) { + int ret; struct tmc_drvdata *drvdata = container_of(file->private_data, struct tmc_drvdata, miscdev); - int ret = 0; - - if (drvdata->read_count++) - goto out; ret = tmc_read_prepare(drvdata); if (ret) return ret; -out: + nonseekable_open(inode, file); dev_dbg(drvdata->dev, "%s: successfully opened\n", __func__); @@ -167,19 +166,14 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len, static int tmc_release(struct inode *inode, struct file *file) { + int ret; struct tmc_drvdata *drvdata = container_of(file->private_data, struct tmc_drvdata, miscdev); - if (--drvdata->read_count) { - if (drvdata->read_count < 0) { - dev_err(drvdata->dev, "mismatched close\n"); - drvdata->read_count = 0; - } - goto out; - } + ret = tmc_read_unprepare(drvdata); + if (ret) + return ret; - tmc_read_unprepare(drvdata); -out: dev_dbg(drvdata->dev, "%s: released\n", __func__); return 0; } diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index df661903f83c..592eb149fe3a 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -94,7 +94,6 @@ enum tmc_mem_intf_width { * @csdev: component vitals needed by the framework. * @miscdev: specifics to handle "/dev/xyz.tmc" entry. * @spinlock: only one at a time pls. - * @read_count: manages preparation of buffer for reading. * @buf: area of memory where trace data get sent. * @paddr: DMA start location in RAM. * @vaddr: virtual representation of @paddr. @@ -109,7 +108,6 @@ struct tmc_drvdata { struct coresight_device *csdev; struct miscdevice miscdev; spinlock_t spinlock; - int read_count; bool reading; char *buf; dma_addr_t paddr; From cc878b6f43fbac6be550554870f053d20a77da73 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:54 -0600 Subject: [PATCH 227/656] coresight: tmc: adding mode of operation for link/sinks Moving tmc_drvdata::enable to a local_t mode. That way the sink interface is aware of it's orgin and the foundation for mutual exclusion between the sysFS and Perf interface can be laid out. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit f2facc3366d77e78dbc8bf865f1e4a6227a7f0e5) --- .../hwtracing/coresight/coresight-tmc-etf.c | 27 ++++++++++++++----- .../hwtracing/coresight/coresight-tmc-etr.c | 23 ++++++++++++---- drivers/hwtracing/coresight/coresight-tmc.h | 4 +-- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 60edf4d1968f..bc0efc1e5b49 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -111,6 +111,7 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) int ret = 0; bool used = false; char *buf = NULL; + long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -140,6 +141,15 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) goto out; } + val = local_xchg(&drvdata->mode, mode); + /* + * In sysFS mode we can have multiple writers per sink. Since this + * sink is already enabled no memory is needed and the HW need not be + * touched. + */ + if (val == CS_MODE_SYSFS) + goto out; + /* * If drvdata::buf isn't NULL, memory was allocated for a previous * trace run but wasn't read. If so simply zero-out the memory. @@ -157,7 +167,6 @@ static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) } tmc_etb_enable_hw(drvdata); - drvdata->enable = true; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -173,6 +182,7 @@ out: static void tmc_disable_etf_sink(struct coresight_device *csdev) { + long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -182,8 +192,11 @@ static void tmc_disable_etf_sink(struct coresight_device *csdev) return; } - tmc_etb_disable_hw(drvdata); - drvdata->enable = false; + val = local_xchg(&drvdata->mode, CS_MODE_DISABLED); + /* Disable the TMC only if it needs to */ + if (val != CS_MODE_DISABLED) + tmc_etb_disable_hw(drvdata); + spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC-ETB/ETF disabled\n"); @@ -202,7 +215,7 @@ static int tmc_enable_etf_link(struct coresight_device *csdev, } tmc_etf_enable_hw(drvdata); - drvdata->enable = true; + local_set(&drvdata->mode, CS_MODE_SYSFS); spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC-ETF enabled\n"); @@ -222,7 +235,7 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, } tmc_etf_disable_hw(drvdata); - drvdata->enable = false; + local_set(&drvdata->mode, CS_MODE_DISABLED); spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC disabled\n"); @@ -279,7 +292,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (drvdata->enable) + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) tmc_etb_disable_hw(drvdata); drvdata->reading = true; @@ -310,7 +323,7 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) } /* Re-enable the TMC if need be */ - if (drvdata->enable) { + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { /* * The trace run will continue with the same allocated trace * buffer. As such zero-out the buffer so that we don't end diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index d6999b457fb8..0c107811a232 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -86,6 +86,7 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) { int ret = 0; bool used = false; + long val; unsigned long flags; void __iomem *vaddr = NULL; dma_addr_t paddr; @@ -122,6 +123,15 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) goto out; } + val = local_xchg(&drvdata->mode, mode); + /* + * In sysFS mode we can have multiple writers per sink. Since this + * sink is already enabled no memory is needed and the HW need not be + * touched. + */ + if (val == CS_MODE_SYSFS) + goto out; + /* * If drvdata::buf == NULL, use the memory allocated above. * Otherwise a buffer still exists from a previous session, so @@ -137,7 +147,6 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) memset(drvdata->vaddr, 0, drvdata->size); tmc_etr_enable_hw(drvdata); - drvdata->enable = true; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); @@ -153,6 +162,7 @@ out: static void tmc_disable_etr_sink(struct coresight_device *csdev) { + long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); @@ -162,8 +172,11 @@ static void tmc_disable_etr_sink(struct coresight_device *csdev) return; } - tmc_etr_disable_hw(drvdata); - drvdata->enable = false; + val = local_xchg(&drvdata->mode, CS_MODE_DISABLED); + /* Disable the TMC only if it needs to */ + if (val != CS_MODE_DISABLED) + tmc_etr_disable_hw(drvdata); + spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC-ETR disabled\n"); @@ -200,7 +213,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (drvdata->enable) + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) tmc_etr_disable_hw(drvdata); drvdata->reading = true; @@ -223,7 +236,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) spin_lock_irqsave(&drvdata->spinlock, flags); /* RE-enable the TMC if need be */ - if (drvdata->enable) { + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { /* * The trace run will continue with the same allocated trace * buffer. As such zero-out the buffer so that we don't end diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 592eb149fe3a..94bc034d3b98 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -98,7 +98,7 @@ enum tmc_mem_intf_width { * @paddr: DMA start location in RAM. * @vaddr: virtual representation of @paddr. * @size: @buf size. - * @enable: this TMC is being used. + * @mode: how this TMC is being used. * @config_type: TMC variant, must be of type @tmc_config_type. * @trigger_cntr: amount of words to store after a trigger. */ @@ -113,7 +113,7 @@ struct tmc_drvdata { dma_addr_t paddr; void __iomem *vaddr; u32 size; - bool enable; + local_t mode; enum tmc_config_type config_type; u32 trigger_cntr; }; From 29b1d2628eb749a6fe9675f2165b2a53721fde55 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:55 -0600 Subject: [PATCH 228/656] coresight: tmc: dump system memory content only when needed Calling tmc_etf/etr_dump_hw() is required only when operating from sysFS. When working from Perf, the system memory is harvested from the AUX trace API. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a40318fb01e98e72175bd9891208541148633d42) --- drivers/hwtracing/coresight/coresight-tmc-etf.c | 7 ++++++- drivers/hwtracing/coresight/coresight-tmc-etr.c | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index bc0efc1e5b49..b5e5e6ac67eb 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -74,7 +74,12 @@ static void tmc_etb_disable_hw(struct tmc_drvdata *drvdata) CS_UNLOCK(drvdata->base); tmc_flush_and_stop(drvdata); - tmc_etb_dump_hw(drvdata); + /* + * When operating in sysFS mode the content of the buffer needs to be + * read before the TMC is disabled. + */ + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + tmc_etb_dump_hw(drvdata); tmc_disable_hw(drvdata); CS_LOCK(drvdata->base); diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 0c107811a232..7208584d0da7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -76,7 +76,12 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) CS_UNLOCK(drvdata->base); tmc_flush_and_stop(drvdata); - tmc_etr_dump_hw(drvdata); + /* + * When operating in sysFS mode the content of the buffer needs to be + * read before the TMC is disabled. + */ + if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + tmc_etr_dump_hw(drvdata); tmc_disable_hw(drvdata); CS_LOCK(drvdata->base); From 6b21a3edf2e21fb3267efe6d1a67e68f8e73b14e Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:56 -0600 Subject: [PATCH 229/656] coresight: tmc: make sysFS and Perf mode mutually exclusive The sysFS and Perf access methods can't be allowed to interfere with one another. As such introducing guards to access functions that prevents moving forward if a TMC is already being used. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit b217601e9adce4d2dccc95a9e6814bdbf5a4a815) --- .../hwtracing/coresight/coresight-tmc-etf.c | 60 +++++++++++++++++- .../hwtracing/coresight/coresight-tmc-etr.c | 62 ++++++++++++++++++- 2 files changed, 117 insertions(+), 5 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index b5e5e6ac67eb..b11c52be54a9 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -111,7 +111,7 @@ static void tmc_etf_disable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode) { int ret = 0; bool used = false; @@ -185,6 +185,54 @@ out: return ret; } +static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode) +{ + int ret = 0; + long val; + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* This shouldn't be happening */ + if (WARN_ON(mode != CS_MODE_PERF)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + ret = -EINVAL; + goto out; + } + + val = local_xchg(&drvdata->mode, mode); + /* + * In Perf mode there can be only one writer per sink. There + * is also no need to continue if the ETB/ETR is already operated + * from sysFS. + */ + if (val != CS_MODE_DISABLED) { + ret = -EINVAL; + goto out; + } + + tmc_etb_enable_hw(drvdata); +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return ret; +} + +static int tmc_enable_etf_sink(struct coresight_device *csdev, u32 mode) +{ + switch (mode) { + case CS_MODE_SYSFS: + return tmc_enable_etf_sink_sysfs(csdev, mode); + case CS_MODE_PERF: + return tmc_enable_etf_sink_perf(csdev, mode); + } + + /* We shouldn't be here */ + return -EINVAL; +} + static void tmc_disable_etf_sink(struct coresight_device *csdev) { long val; @@ -267,6 +315,7 @@ const struct coresight_ops tmc_etf_cs_ops = { int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) { + long val; enum tmc_mode mode; int ret = 0; unsigned long flags; @@ -290,6 +339,13 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) goto out; } + val = local_read(&drvdata->mode); + /* Don't interfere if operated from Perf */ + if (val == CS_MODE_PERF) { + ret = -EINVAL; + goto out; + } + /* If drvdata::buf is NULL the trace data has been read already */ if (drvdata->buf == NULL) { ret = -EINVAL; @@ -297,7 +353,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + if (val == CS_MODE_SYSFS) tmc_etb_disable_hw(drvdata); drvdata->reading = true; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index 7208584d0da7..847d1b5f2c13 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -87,7 +87,7 @@ static void tmc_etr_disable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } -static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) +static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev, u32 mode) { int ret = 0; bool used = false; @@ -165,6 +165,54 @@ out: return ret; } +static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, u32 mode) +{ + int ret = 0; + long val; + unsigned long flags; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + /* This shouldn't be happening */ + if (WARN_ON(mode != CS_MODE_PERF)) + return -EINVAL; + + spin_lock_irqsave(&drvdata->spinlock, flags); + if (drvdata->reading) { + ret = -EINVAL; + goto out; + } + + val = local_xchg(&drvdata->mode, mode); + /* + * In Perf mode there can be only one writer per sink. There + * is also no need to continue if the ETR is already operated + * from sysFS. + */ + if (val != CS_MODE_DISABLED) { + ret = -EINVAL; + goto out; + } + + tmc_etr_enable_hw(drvdata); +out: + spin_unlock_irqrestore(&drvdata->spinlock, flags); + + return ret; +} + +static int tmc_enable_etr_sink(struct coresight_device *csdev, u32 mode) +{ + switch (mode) { + case CS_MODE_SYSFS: + return tmc_enable_etr_sink_sysfs(csdev, mode); + case CS_MODE_PERF: + return tmc_enable_etr_sink_perf(csdev, mode); + } + + /* We shouldn't be here */ + return -EINVAL; +} + static void tmc_disable_etr_sink(struct coresight_device *csdev) { long val; @@ -199,6 +247,7 @@ const struct coresight_ops tmc_etr_cs_ops = { int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) { int ret = 0; + long val; unsigned long flags; /* config types are set a boot time and never change */ @@ -211,6 +260,13 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) goto out; } + val = local_read(&drvdata->mode); + /* Don't interfere if operated from Perf */ + if (val == CS_MODE_PERF) { + ret = -EINVAL; + goto out; + } + /* If drvdata::buf is NULL the trace data has been read already */ if (drvdata->buf == NULL) { ret = -EINVAL; @@ -218,14 +274,14 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata) } /* Disable the TMC if need be */ - if (local_read(&drvdata->mode) == CS_MODE_SYSFS) + if (val == CS_MODE_SYSFS) tmc_etr_disable_hw(drvdata); drvdata->reading = true; out: spin_unlock_irqrestore(&drvdata->spinlock, flags); - return 0; + return ret; } int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata) From 807aa8f320d493a3eb62226d48f1dec6bccce703 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:57 -0600 Subject: [PATCH 230/656] coresight: tmc: keep track of memory width Accessing the HW configuration register each time the memory width is needed simply doesn't make sense. It is much more efficient to read the value once and keep a reference for later use. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 4f1ff3de925d741b0b77c59bc1387cb940ad7c73) --- .../hwtracing/coresight/coresight-tmc-etf.c | 14 +------- drivers/hwtracing/coresight/coresight-tmc.c | 34 +++++++++++++++++++ drivers/hwtracing/coresight/coresight-tmc.h | 10 +++--- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index b11c52be54a9..ba3384781f71 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -41,25 +41,13 @@ void tmc_etb_enable_hw(struct tmc_drvdata *drvdata) static void tmc_etb_dump_hw(struct tmc_drvdata *drvdata) { - enum tmc_mem_intf_width memwidth; - u8 memwords; char *bufp; u32 read_data; int i; - memwidth = BMVAL(readl_relaxed(drvdata->base + CORESIGHT_DEVID), 8, 10); - if (memwidth == TMC_MEM_INTF_WIDTH_32BITS) - memwords = 1; - else if (memwidth == TMC_MEM_INTF_WIDTH_64BITS) - memwords = 2; - else if (memwidth == TMC_MEM_INTF_WIDTH_128BITS) - memwords = 4; - else - memwords = 8; - bufp = drvdata->buf; while (1) { - for (i = 0; i < memwords; i++) { + for (i = 0; i < drvdata->memwidth; i++) { read_data = readl_relaxed(drvdata->base + TMC_RRD); if (read_data == 0xFFFFFFFF) return; diff --git a/drivers/hwtracing/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c index ae7525a2b94a..9e02ac963cd0 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.c +++ b/drivers/hwtracing/coresight/coresight-tmc.c @@ -186,6 +186,39 @@ static const struct file_operations tmc_fops = { .llseek = no_llseek, }; +static enum tmc_mem_intf_width tmc_get_memwidth(u32 devid) +{ + enum tmc_mem_intf_width memwidth; + + /* + * Excerpt from the TRM: + * + * DEVID::MEMWIDTH[10:8] + * 0x2 Memory interface databus is 32 bits wide. + * 0x3 Memory interface databus is 64 bits wide. + * 0x4 Memory interface databus is 128 bits wide. + * 0x5 Memory interface databus is 256 bits wide. + */ + switch (BMVAL(devid, 8, 10)) { + case 0x2: + memwidth = TMC_MEM_INTF_WIDTH_32BITS; + break; + case 0x3: + memwidth = TMC_MEM_INTF_WIDTH_64BITS; + break; + case 0x4: + memwidth = TMC_MEM_INTF_WIDTH_128BITS; + break; + case 0x5: + memwidth = TMC_MEM_INTF_WIDTH_256BITS; + break; + default: + memwidth = 0; + } + + return memwidth; +} + #define coresight_tmc_simple_func(name, offset) \ coresight_simple_func(struct tmc_drvdata, name, offset) @@ -299,6 +332,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID); drvdata->config_type = BMVAL(devid, 6, 7); + drvdata->memwidth = tmc_get_memwidth(devid); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (np) diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 94bc034d3b98..c5d06fd57fa8 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -81,10 +81,10 @@ enum tmc_mode { }; enum tmc_mem_intf_width { - TMC_MEM_INTF_WIDTH_32BITS = 0x2, - TMC_MEM_INTF_WIDTH_64BITS = 0x3, - TMC_MEM_INTF_WIDTH_128BITS = 0x4, - TMC_MEM_INTF_WIDTH_256BITS = 0x5, + TMC_MEM_INTF_WIDTH_32BITS = 1, + TMC_MEM_INTF_WIDTH_64BITS = 2, + TMC_MEM_INTF_WIDTH_128BITS = 4, + TMC_MEM_INTF_WIDTH_256BITS = 8, }; /** @@ -100,6 +100,7 @@ enum tmc_mem_intf_width { * @size: @buf size. * @mode: how this TMC is being used. * @config_type: TMC variant, must be of type @tmc_config_type. + * @memwidth: width of the memory interface databus, in bytes. * @trigger_cntr: amount of words to store after a trigger. */ struct tmc_drvdata { @@ -115,6 +116,7 @@ struct tmc_drvdata { u32 size; local_t mode; enum tmc_config_type config_type; + enum tmc_mem_intf_width memwidth; u32 trigger_cntr; }; From 57b6b76d6f9a17797302efff65720b64326ec402 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:58 -0600 Subject: [PATCH 231/656] coresight: moving struct cs_buffers to header file That way we can re-use the structure in other drivers. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit a02e81f7a32b49f3cb70c5ebd2eab5608a088514) --- drivers/hwtracing/coresight/coresight-etb10.c | 20 ------------------- drivers/hwtracing/coresight/coresight-priv.h | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index b0d402dbfeae..92f942321e1e 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -70,26 +70,6 @@ #define ETB_FFSR_BIT 1 #define ETB_FRAME_SIZE_WORDS 4 -/** - * struct cs_buffer - keep track of a recording session' specifics - * @cur: index of the current buffer - * @nr_pages: max number of pages granted to us - * @offset: offset within the current buffer - * @data_size: how much we collected in this run - * @lost: other than zero if we had a HW buffer wrap around - * @snapshot: is this run in snapshot mode - * @data_pages: a handle the ring buffer - */ -struct cs_buffers { - unsigned int cur; - unsigned int nr_pages; - unsigned long offset; - local_t data_size; - local_t lost; - bool snapshot; - void **data_pages; -}; - /** * struct etb_drvdata - specifics associated to an ETB component * @base: memory mapped base address for this component. diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index 3b5dd95a3588..ad975c58080d 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -53,6 +53,26 @@ enum cs_mode { CS_MODE_PERF, }; +/** + * struct cs_buffer - keep track of a recording session' specifics + * @cur: index of the current buffer + * @nr_pages: max number of pages granted to us + * @offset: offset within the current buffer + * @data_size: how much we collected in this run + * @lost: other than zero if we had a HW buffer wrap around + * @snapshot: is this run in snapshot mode + * @data_pages: a handle the ring buffer + */ +struct cs_buffers { + unsigned int cur; + unsigned int nr_pages; + unsigned long offset; + local_t data_size; + local_t lost; + bool snapshot; + void **data_pages; +}; + static inline void CS_LOCK(void __iomem *addr) { do { From 2686c0577df400e8e78cd97c7f3676d0700dde51 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:33:59 -0600 Subject: [PATCH 232/656] coresight: tmc: implementing TMC-ETF AUX space API This patch implement the AUX area interfaces required to use the TMC (configured as an ETF) from the Perf sub-system. The heuristic is heavily borrowed from the ETB10 implementation. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit 2e499bbc1a929ac87dcb9832d11000fc055f8bc6) --- .../hwtracing/coresight/coresight-tmc-etf.c | 199 ++++++++++++++++++ drivers/hwtracing/coresight/coresight-tmc.h | 1 + 2 files changed, 200 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index ba3384781f71..466af86fd76f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -15,7 +15,9 @@ * this program. If not, see . */ +#include #include +#include #include #include "coresight-priv.h" #include "coresight-tmc.h" @@ -282,9 +284,206 @@ static void tmc_disable_etf_link(struct coresight_device *csdev, dev_info(drvdata->dev, "TMC disabled\n"); } +static void *tmc_alloc_etf_buffer(struct coresight_device *csdev, int cpu, + void **pages, int nr_pages, bool overwrite) +{ + int node; + struct cs_buffers *buf; + + if (cpu == -1) + cpu = smp_processor_id(); + node = cpu_to_node(cpu); + + /* Allocate memory structure for interaction with Perf */ + buf = kzalloc_node(sizeof(struct cs_buffers), GFP_KERNEL, node); + if (!buf) + return NULL; + + buf->snapshot = overwrite; + buf->nr_pages = nr_pages; + buf->data_pages = pages; + + return buf; +} + +static void tmc_free_etf_buffer(void *config) +{ + struct cs_buffers *buf = config; + + kfree(buf); +} + +static int tmc_set_etf_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int ret = 0; + unsigned long head; + struct cs_buffers *buf = sink_config; + + /* wrap head around to the amount of space we have */ + head = handle->head & ((buf->nr_pages << PAGE_SHIFT) - 1); + + /* find the page to write to */ + buf->cur = head / PAGE_SIZE; + + /* and offset within that page */ + buf->offset = head % PAGE_SIZE; + + local_set(&buf->data_size, 0); + + return ret; +} + +static unsigned long tmc_reset_etf_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config, bool *lost) +{ + long size = 0; + struct cs_buffers *buf = sink_config; + + if (buf) { + /* + * In snapshot mode ->data_size holds the new address of the + * ring buffer's head. The size itself is the whole address + * range since we want the latest information. + */ + if (buf->snapshot) + handle->head = local_xchg(&buf->data_size, + buf->nr_pages << PAGE_SHIFT); + /* + * Tell the tracer PMU how much we got in this run and if + * something went wrong along the way. Nobody else can use + * this cs_buffers instance until we are done. As such + * resetting parameters here and squaring off with the ring + * buffer API in the tracer PMU is fine. + */ + *lost = !!local_xchg(&buf->lost, 0); + size = local_xchg(&buf->data_size, 0); + } + + return size; +} + +static void tmc_update_etf_buffer(struct coresight_device *csdev, + struct perf_output_handle *handle, + void *sink_config) +{ + int i, cur; + u32 *buf_ptr; + u32 read_ptr, write_ptr; + u32 status, to_read; + unsigned long offset; + struct cs_buffers *buf = sink_config; + struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); + + if (!buf) + return; + + /* This shouldn't happen */ + if (WARN_ON_ONCE(local_read(&drvdata->mode) != CS_MODE_PERF)) + return; + + CS_UNLOCK(drvdata->base); + + tmc_flush_and_stop(drvdata); + + read_ptr = readl_relaxed(drvdata->base + TMC_RRP); + write_ptr = readl_relaxed(drvdata->base + TMC_RWP); + + /* + * Get a hold of the status register and see if a wrap around + * has occurred. If so adjust things accordingly. + */ + status = readl_relaxed(drvdata->base + TMC_STS); + if (status & TMC_STS_FULL) { + local_inc(&buf->lost); + to_read = drvdata->size; + } else { + to_read = CIRC_CNT(write_ptr, read_ptr, drvdata->size); + } + + /* + * The TMC RAM buffer may be bigger than the space available in the + * perf ring buffer (handle->size). If so advance the RRP so that we + * get the latest trace data. + */ + if (to_read > handle->size) { + u32 mask = 0; + + /* + * The value written to RRP must be byte-address aligned to + * the width of the trace memory databus _and_ to a frame + * boundary (16 byte), whichever is the biggest. For example, + * for 32-bit, 64-bit and 128-bit wide trace memory, the four + * LSBs must be 0s. For 256-bit wide trace memory, the five + * LSBs must be 0s. + */ + switch (drvdata->memwidth) { + case TMC_MEM_INTF_WIDTH_32BITS: + case TMC_MEM_INTF_WIDTH_64BITS: + case TMC_MEM_INTF_WIDTH_128BITS: + mask = GENMASK(31, 5); + break; + case TMC_MEM_INTF_WIDTH_256BITS: + mask = GENMASK(31, 6); + break; + } + + /* + * Make sure the new size is aligned in accordance with the + * requirement explained above. + */ + to_read = handle->size & mask; + /* Move the RAM read pointer up */ + read_ptr = (write_ptr + drvdata->size) - to_read; + /* Make sure we are still within our limits */ + if (read_ptr > (drvdata->size - 1)) + read_ptr -= drvdata->size; + /* Tell the HW */ + writel_relaxed(read_ptr, drvdata->base + TMC_RRP); + local_inc(&buf->lost); + } + + cur = buf->cur; + offset = buf->offset; + + /* for every byte to read */ + for (i = 0; i < to_read; i += 4) { + buf_ptr = buf->data_pages[cur] + offset; + *buf_ptr = readl_relaxed(drvdata->base + TMC_RRD); + + offset += 4; + if (offset >= PAGE_SIZE) { + offset = 0; + cur++; + /* wrap around at the end of the buffer */ + cur &= buf->nr_pages - 1; + } + } + + /* + * In snapshot mode all we have to do is communicate to + * perf_aux_output_end() the address of the current head. In full + * trace mode the same function expects a size to move rb->aux_head + * forward. + */ + if (buf->snapshot) + local_set(&buf->data_size, (cur * PAGE_SIZE) + offset); + else + local_add(to_read, &buf->data_size); + + CS_LOCK(drvdata->base); +} + static const struct coresight_ops_sink tmc_etf_sink_ops = { .enable = tmc_enable_etf_sink, .disable = tmc_disable_etf_sink, + .alloc_buffer = tmc_alloc_etf_buffer, + .free_buffer = tmc_free_etf_buffer, + .set_buffer = tmc_set_etf_buffer, + .reset_buffer = tmc_reset_etf_buffer, + .update_buffer = tmc_update_etf_buffer, }; static const struct coresight_ops_link tmc_etf_link_ops = { diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index c5d06fd57fa8..5c5fe2ad2ca7 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -52,6 +52,7 @@ #define TMC_CTL_CAPT_EN BIT(0) /* TMC_STS - 0x00C */ #define TMC_STS_TMCREADY_BIT 2 +#define TMC_STS_FULL BIT(0) #define TMC_STS_TRIGGERED BIT(1) /* TMC_AXICTL - 0x110 */ #define TMC_AXICTL_PROT_CTL_B0 BIT(0) From 6d1fcd0f0e7b17b4c9e4eaf0a61072b467110692 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:34:00 -0600 Subject: [PATCH 233/656] coresight: configuring ETF in FIFO mode when acting as link When part of a path but not identified as a sink, the EFT has to be configured as a link and placed in HW FIFO mode. As such when enabling a path, call the right configuration function based on the role the ETF if playing in this trace run. Signed-off-by: Mathieu Poirier Reviewed-by: Suzuki K Poulose Signed-off-by: Greg Kroah-Hartman (cherry picked from commit dc2c4ef141c5c14cb8d968ba16c74b4f3c373e2c) --- drivers/hwtracing/coresight/coresight.c | 32 +++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c index 0995df8f85bc..5443d03a1eec 100644 --- a/drivers/hwtracing/coresight/coresight.c +++ b/drivers/hwtracing/coresight/coresight.c @@ -265,15 +265,27 @@ static void coresight_disable_source(struct coresight_device *csdev) void coresight_disable_path(struct list_head *path) { + u32 type; struct coresight_node *nd; struct coresight_device *csdev, *parent, *child; list_for_each_entry(nd, path, link) { csdev = nd->csdev; + type = csdev->type; - switch (csdev->type) { + /* + * ETF devices are tricky... They can be a link or a sink, + * depending on how they are configured. If an ETF has been + * "activated" it will be configured as a sink, otherwise + * go ahead with the link configuration. + */ + if (type == CORESIGHT_DEV_TYPE_LINKSINK) + type = (csdev == coresight_get_sink(path)) ? + CORESIGHT_DEV_TYPE_SINK : + CORESIGHT_DEV_TYPE_LINK; + + switch (type) { case CORESIGHT_DEV_TYPE_SINK: - case CORESIGHT_DEV_TYPE_LINKSINK: coresight_disable_sink(csdev); break; case CORESIGHT_DEV_TYPE_SOURCE: @@ -294,15 +306,27 @@ int coresight_enable_path(struct list_head *path, u32 mode) { int ret = 0; + u32 type; struct coresight_node *nd; struct coresight_device *csdev, *parent, *child; list_for_each_entry_reverse(nd, path, link) { csdev = nd->csdev; + type = csdev->type; - switch (csdev->type) { + /* + * ETF devices are tricky... They can be a link or a sink, + * depending on how they are configured. If an ETF has been + * "activated" it will be configured as a sink, otherwise + * go ahead with the link configuration. + */ + if (type == CORESIGHT_DEV_TYPE_LINKSINK) + type = (csdev == coresight_get_sink(path)) ? + CORESIGHT_DEV_TYPE_SINK : + CORESIGHT_DEV_TYPE_LINK; + + switch (type) { case CORESIGHT_DEV_TYPE_SINK: - case CORESIGHT_DEV_TYPE_LINKSINK: ret = coresight_enable_sink(csdev, mode); if (ret) goto err; From 4a192f60855a84820d361ec58eda0d043faab6f6 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Tue, 3 May 2016 11:34:01 -0600 Subject: [PATCH 234/656] coresight: etb10: adjust read pointer only when needed The read pointer (read_ptr) needs to be adjusted only if its value has gone beyond the length of the memory buffer. Reported-by: Suzuki K Poulose Signed-off-by: Mathieu Poirier Signed-off-by: Greg Kroah-Hartman (cherry picked from commit bedffda8cad46bedb6880bb98c23c51c715216c3) --- drivers/hwtracing/coresight/coresight-etb10.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c index 92f942321e1e..4d20b0be0c0b 100644 --- a/drivers/hwtracing/coresight/coresight-etb10.c +++ b/drivers/hwtracing/coresight/coresight-etb10.c @@ -428,7 +428,8 @@ static void etb_update_buffer(struct coresight_device *csdev, read_ptr = (write_ptr + drvdata->buffer_depth) - to_read / ETB_FRAME_SIZE_WORDS; /* Wrap around if need be*/ - read_ptr &= ~(drvdata->buffer_depth - 1); + if (read_ptr > (drvdata->buffer_depth - 1)) + read_ptr -= drvdata->buffer_depth; /* let the decoder know we've skipped ahead */ local_inc(&buf->lost); } From a08eb4c2f58a21135c28c55796a7bda426103ade Mon Sep 17 00:00:00 2001 From: Leonid Yegoshin Date: Tue, 28 Apr 2015 12:53:35 -0700 Subject: [PATCH 235/656] MIPS64: R6: R2 emulation bugfix commit 41fa29e4d8cf4150568a0fe9bb4d62229f9caed5 upstream. Error recovery pointers for fixups was improperly set as ".word" which is unsuitable for MIPS64. Replaced by STR(PTR) [ralf@linux-mips.org: Apply changes as requested in the review process.] Signed-off-by: Leonid Yegoshin Reviewed-by: James Hogan Reviewed-by: Markos Chandras Fixes: b0a668fb2038 ("MIPS: kernel: mips-r2-to-r6-emul: Add R2 emulator for MIPS R6") Cc: macro@linux-mips.org Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/9911/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/mips-r2-to-r6-emul.c | 105 +++++++++++++------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c index 1f5aac7f9ec3..4674a74a08b5 100644 --- a/arch/mips/kernel/mips-r2-to-r6-emul.c +++ b/arch/mips/kernel/mips-r2-to-r6-emul.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1251,10 +1252,10 @@ fpu_emul: " j 10b\n" " .previous\n" " .section __ex_table,\"a\"\n" - " .word 1b,8b\n" - " .word 2b,8b\n" - " .word 3b,8b\n" - " .word 4b,8b\n" + STR(PTR) " 1b,8b\n" + STR(PTR) " 2b,8b\n" + STR(PTR) " 3b,8b\n" + STR(PTR) " 4b,8b\n" " .previous\n" " .set pop\n" : "+&r"(rt), "=&r"(rs), @@ -1326,10 +1327,10 @@ fpu_emul: " j 10b\n" " .previous\n" " .section __ex_table,\"a\"\n" - " .word 1b,8b\n" - " .word 2b,8b\n" - " .word 3b,8b\n" - " .word 4b,8b\n" + STR(PTR) " 1b,8b\n" + STR(PTR) " 2b,8b\n" + STR(PTR) " 3b,8b\n" + STR(PTR) " 4b,8b\n" " .previous\n" " .set pop\n" : "+&r"(rt), "=&r"(rs), @@ -1397,10 +1398,10 @@ fpu_emul: " j 9b\n" " .previous\n" " .section __ex_table,\"a\"\n" - " .word 1b,8b\n" - " .word 2b,8b\n" - " .word 3b,8b\n" - " .word 4b,8b\n" + STR(PTR) " 1b,8b\n" + STR(PTR) " 2b,8b\n" + STR(PTR) " 3b,8b\n" + STR(PTR) " 4b,8b\n" " .previous\n" " .set pop\n" : "+&r"(rt), "=&r"(rs), @@ -1467,10 +1468,10 @@ fpu_emul: " j 9b\n" " .previous\n" " .section __ex_table,\"a\"\n" - " .word 1b,8b\n" - " .word 2b,8b\n" - " .word 3b,8b\n" - " .word 4b,8b\n" + STR(PTR) " 1b,8b\n" + STR(PTR) " 2b,8b\n" + STR(PTR) " 3b,8b\n" + STR(PTR) " 4b,8b\n" " .previous\n" " .set pop\n" : "+&r"(rt), "=&r"(rs), @@ -1582,14 +1583,14 @@ fpu_emul: " j 9b\n" " .previous\n" " .section __ex_table,\"a\"\n" - " .word 1b,8b\n" - " .word 2b,8b\n" - " .word 3b,8b\n" - " .word 4b,8b\n" - " .word 5b,8b\n" - " .word 6b,8b\n" - " .word 7b,8b\n" - " .word 0b,8b\n" + STR(PTR) " 1b,8b\n" + STR(PTR) " 2b,8b\n" + STR(PTR) " 3b,8b\n" + STR(PTR) " 4b,8b\n" + STR(PTR) " 5b,8b\n" + STR(PTR) " 6b,8b\n" + STR(PTR) " 7b,8b\n" + STR(PTR) " 0b,8b\n" " .previous\n" " .set pop\n" : "+&r"(rt), "=&r"(rs), @@ -1701,14 +1702,14 @@ fpu_emul: " j 9b\n" " .previous\n" " .section __ex_table,\"a\"\n" - " .word 1b,8b\n" - " .word 2b,8b\n" - " .word 3b,8b\n" - " .word 4b,8b\n" - " .word 5b,8b\n" - " .word 6b,8b\n" - " .word 7b,8b\n" - " .word 0b,8b\n" + STR(PTR) " 1b,8b\n" + STR(PTR) " 2b,8b\n" + STR(PTR) " 3b,8b\n" + STR(PTR) " 4b,8b\n" + STR(PTR) " 5b,8b\n" + STR(PTR) " 6b,8b\n" + STR(PTR) " 7b,8b\n" + STR(PTR) " 0b,8b\n" " .previous\n" " .set pop\n" : "+&r"(rt), "=&r"(rs), @@ -1820,14 +1821,14 @@ fpu_emul: " j 9b\n" " .previous\n" " .section __ex_table,\"a\"\n" - " .word 1b,8b\n" - " .word 2b,8b\n" - " .word 3b,8b\n" - " .word 4b,8b\n" - " .word 5b,8b\n" - " .word 6b,8b\n" - " .word 7b,8b\n" - " .word 0b,8b\n" + STR(PTR) " 1b,8b\n" + STR(PTR) " 2b,8b\n" + STR(PTR) " 3b,8b\n" + STR(PTR) " 4b,8b\n" + STR(PTR) " 5b,8b\n" + STR(PTR) " 6b,8b\n" + STR(PTR) " 7b,8b\n" + STR(PTR) " 0b,8b\n" " .previous\n" " .set pop\n" : "+&r"(rt), "=&r"(rs), @@ -1938,14 +1939,14 @@ fpu_emul: " j 9b\n" " .previous\n" " .section __ex_table,\"a\"\n" - " .word 1b,8b\n" - " .word 2b,8b\n" - " .word 3b,8b\n" - " .word 4b,8b\n" - " .word 5b,8b\n" - " .word 6b,8b\n" - " .word 7b,8b\n" - " .word 0b,8b\n" + STR(PTR) " 1b,8b\n" + STR(PTR) " 2b,8b\n" + STR(PTR) " 3b,8b\n" + STR(PTR) " 4b,8b\n" + STR(PTR) " 5b,8b\n" + STR(PTR) " 6b,8b\n" + STR(PTR) " 7b,8b\n" + STR(PTR) " 0b,8b\n" " .previous\n" " .set pop\n" : "+&r"(rt), "=&r"(rs), @@ -2000,7 +2001,7 @@ fpu_emul: "j 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" - ".word 1b, 3b\n" + STR(PTR) " 1b,3b\n" ".previous\n" : "=&r"(res), "+&r"(err) : "r"(vaddr), "i"(SIGSEGV) @@ -2058,7 +2059,7 @@ fpu_emul: "j 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" - ".word 1b, 3b\n" + STR(PTR) " 1b,3b\n" ".previous\n" : "+&r"(res), "+&r"(err) : "r"(vaddr), "i"(SIGSEGV)); @@ -2119,7 +2120,7 @@ fpu_emul: "j 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" - ".word 1b, 3b\n" + STR(PTR) " 1b,3b\n" ".previous\n" : "=&r"(res), "+&r"(err) : "r"(vaddr), "i"(SIGSEGV) @@ -2182,7 +2183,7 @@ fpu_emul: "j 2b\n" ".previous\n" ".section __ex_table,\"a\"\n" - ".word 1b, 3b\n" + STR(PTR) " 1b,3b\n" ".previous\n" : "+&r"(res), "+&r"(err) : "r"(vaddr), "i"(SIGSEGV)); From 982db881156d3e1c97f2a247c17f15e25d2afeef Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 21 Apr 2016 14:04:55 +0100 Subject: [PATCH 236/656] MIPS: math-emu: Fix jalr emulation when rd == $0 commit ab4a92e66741b35ca12f8497896bafbe579c28a1 upstream. When emulating a jalr instruction with rd == $0, the code in isBranchInstr was incorrectly writing to GPR $0 which should actually always remain zeroed. This would lead to any further instructions emulated which use $0 operating on a bogus value until the task is next context switched, at which point the value of $0 in the task context would be restored to the correct zero by a store in SAVE_SOME. Fix this by not writing to rd if it is $0. Fixes: 102cedc32a6e ("MIPS: microMIPS: Floating point support.") Signed-off-by: Paul Burton Cc: Maciej W. Rozycki Cc: James Hogan Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/13160/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/math-emu/cp1emu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index 32f0e19a0d7f..734a2c7665ec 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -445,9 +445,11 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn, case spec_op: switch (insn.r_format.func) { case jalr_op: - regs->regs[insn.r_format.rd] = - regs->cp0_epc + dec_insn.pc_inc + - dec_insn.next_pc_inc; + if (insn.r_format.rd != 0) { + regs->regs[insn.r_format.rd] = + regs->cp0_epc + dec_insn.pc_inc + + dec_insn.next_pc_inc; + } /* Fall through */ case jr_op: /* For R6, JR already emulated in jalr_op */ From 5fc0cab84d045bc5c1a3174e546fb3c8e529150a Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Tue, 17 May 2016 06:12:27 +0100 Subject: [PATCH 237/656] MIPS: MSA: Fix a link error on `_init_msa_upper' with older GCC commit e49d38488515057dba8f0c2ba4cfde5be4a7281f upstream. Fix a build regression from commit c9017757c532 ("MIPS: init upper 64b of vector registers when MSA is first used"): arch/mips/built-in.o: In function `enable_restore_fp_context': traps.c:(.text+0xbb90): undefined reference to `_init_msa_upper' traps.c:(.text+0xbb90): relocation truncated to fit: R_MIPS_26 against `_init_msa_upper' traps.c:(.text+0xbef0): undefined reference to `_init_msa_upper' traps.c:(.text+0xbef0): relocation truncated to fit: R_MIPS_26 against `_init_msa_upper' to !CONFIG_CPU_HAS_MSA configurations with older GCC versions, which are unable to figure out that calls to `_init_msa_upper' are indeed dead. Of the many ways to tackle this failure choose the approach we have already taken in `thread_msa_context_live'. [ralf@linux-mips.org: Drop patch segment to junk file.] Signed-off-by: Maciej W. Rozycki Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/13271/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/msa.h | 13 +++++++++++++ arch/mips/kernel/traps.c | 6 +++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/mips/include/asm/msa.h b/arch/mips/include/asm/msa.h index bbb85fe21642..6e4effa6f626 100644 --- a/arch/mips/include/asm/msa.h +++ b/arch/mips/include/asm/msa.h @@ -147,6 +147,19 @@ static inline void restore_msa(struct task_struct *t) _restore_msa(t); } +static inline void init_msa_upper(void) +{ + /* + * Check cpu_has_msa only if it's a constant. This will allow the + * compiler to optimise out code for CPUs without MSA without adding + * an extra redundant check for CPUs with MSA. + */ + if (__builtin_constant_p(cpu_has_msa) && !cpu_has_msa) + return; + + _init_msa_upper(); +} + #ifdef TOOLCHAIN_SUPPORTS_MSA #define __BUILD_MSA_CTL_REG(name, cs) \ diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index ca9a81007489..7322daa96bd1 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -1241,7 +1241,7 @@ static int enable_restore_fp_context(int msa) err = init_fpu(); if (msa && !err) { enable_msa(); - _init_msa_upper(); + init_msa_upper(); set_thread_flag(TIF_USEDMSA); set_thread_flag(TIF_MSA_CTX_LIVE); } @@ -1304,7 +1304,7 @@ static int enable_restore_fp_context(int msa) */ prior_msa = test_and_set_thread_flag(TIF_MSA_CTX_LIVE); if (!prior_msa && was_fpu_owner) { - _init_msa_upper(); + init_msa_upper(); goto out; } @@ -1321,7 +1321,7 @@ static int enable_restore_fp_context(int msa) * of each vector register such that it cannot see data left * behind by another task. */ - _init_msa_upper(); + init_msa_upper(); } else { /* We need to restore the vector context. */ restore_msa(current); From a8389fdf397f4c591372b13b9caa1eec4bb2b09c Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 4 Dec 2015 22:25:01 +0000 Subject: [PATCH 238/656] MIPS: Don't unwind to user mode with EVA commit a816b306c62195b7c43c92cb13330821a96bdc27 upstream. When unwinding through IRQs and exceptions, the unwinding only continues if the PC is a kernel text address, however since EVA it is possible for user and kernel address ranges to overlap, potentially allowing unwinding to continue to user mode if the user PC happens to be in the kernel text address range. Adjust the check to also ensure that the register state from before the exception is actually running in kernel mode, i.e. !user_mode(regs). I don't believe any harm can come of this problem, since the PC is only output, the stack pointer is checked to ensure it resides within the task's stack page before it is dereferenced in search of the return address, and the return address register is similarly only output (if the PC is in a leaf function or the beginning of a non-leaf function). However unwind_stack() is only meant for unwinding kernel code, so to be correct the unwind should stop there. Signed-off-by: James Hogan Reviewed-by: Leonid Yegoshin Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/11700/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/process.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index f2975d4d1e44..6b3ae73cda3f 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -457,7 +457,7 @@ unsigned long notrace unwind_stack_by_address(unsigned long stack_page, *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) { regs = (struct pt_regs *)*sp; pc = regs->cp0_epc; - if (__kernel_text_address(pc)) { + if (!user_mode(regs) && __kernel_text_address(pc)) { *sp = regs->regs[29]; *ra = regs->regs[31]; return pc; From fdb691ea15b53da6854b8820df96926eb199ad2c Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 4 Dec 2015 22:25:02 +0000 Subject: [PATCH 239/656] MIPS: Avoid using unwind_stack() with usermode commit 81a76d7119f63c359750e4adeff922a31ad1135f upstream. When showing backtraces in response to traps, for example crashes and address errors (usually unaligned accesses) when they are set in debugfs to be reported, unwind_stack will be used if the PC was in the kernel text address range. However since EVA it is possible for user and kernel address ranges to overlap, and even without EVA userland can still trigger an address error by jumping to a KSeg0 address. Adjust the check to also ensure that it was running in kernel mode. I don't believe any harm can come of this problem, since unwind_stack() is sufficiently defensive, however it is only meant for unwinding kernel code, so to be correct it should use the raw backtracing instead. Signed-off-by: James Hogan Reviewed-by: Leonid Yegoshin Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/11701/ Signed-off-by: Ralf Baechle (cherry picked from commit d2941a975ac745c607dfb590e92bb30bc352dad9) Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/traps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 7322daa96bd1..99a402231f4d 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c @@ -144,7 +144,7 @@ static void show_backtrace(struct task_struct *task, const struct pt_regs *regs) if (!task) task = current; - if (raw_show_trace || !__kernel_text_address(pc)) { + if (raw_show_trace || user_mode(regs) || !__kernel_text_address(pc)) { show_raw_backtrace(sp); return; } From df27ff8678252b751e6bc2ea8835e58eb43b8e30 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 8 Feb 2016 18:43:49 +0000 Subject: [PATCH 240/656] MIPS: Fix siginfo.h to use strict posix types commit 5daebc477da4dfeb31ae193d83084def58fd2697 upstream. Commit 85efde6f4e0d ("make exported headers use strict posix types") changed the asm-generic siginfo.h to use the __kernel_* types, and commit 3a471cbc081b ("remove __KERNEL_STRICT_NAMES") make the internal types accessible only to the kernel, but the MIPS implementation hasn't been updated to match. Switch to proper types now so that the exported asm/siginfo.h won't produce quite so many compiler errors when included alone by a user program. Signed-off-by: James Hogan Cc: Christopher Ferris Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/12477/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/uapi/asm/siginfo.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/mips/include/uapi/asm/siginfo.h b/arch/mips/include/uapi/asm/siginfo.h index 2cb7fdead570..03ec1090f781 100644 --- a/arch/mips/include/uapi/asm/siginfo.h +++ b/arch/mips/include/uapi/asm/siginfo.h @@ -42,13 +42,13 @@ typedef struct siginfo { /* kill() */ struct { - pid_t _pid; /* sender's pid */ + __kernel_pid_t _pid; /* sender's pid */ __ARCH_SI_UID_T _uid; /* sender's uid */ } _kill; /* POSIX.1b timers */ struct { - timer_t _tid; /* timer id */ + __kernel_timer_t _tid; /* timer id */ int _overrun; /* overrun count */ char _pad[sizeof( __ARCH_SI_UID_T) - sizeof(int)]; sigval_t _sigval; /* same as below */ @@ -57,26 +57,26 @@ typedef struct siginfo { /* POSIX.1b signals */ struct { - pid_t _pid; /* sender's pid */ + __kernel_pid_t _pid; /* sender's pid */ __ARCH_SI_UID_T _uid; /* sender's uid */ sigval_t _sigval; } _rt; /* SIGCHLD */ struct { - pid_t _pid; /* which child */ + __kernel_pid_t _pid; /* which child */ __ARCH_SI_UID_T _uid; /* sender's uid */ int _status; /* exit code */ - clock_t _utime; - clock_t _stime; + __kernel_clock_t _utime; + __kernel_clock_t _stime; } _sigchld; /* IRIX SIGCHLD */ struct { - pid_t _pid; /* which child */ - clock_t _utime; + __kernel_pid_t _pid; /* which child */ + __kernel_clock_t _utime; int _status; /* exit code */ - clock_t _stime; + __kernel_clock_t _stime; } _irix_sigchld; /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ From d59a1b85d6b7d16485f5362077e2ed9a5c9022cd Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 8 Feb 2016 18:43:51 +0000 Subject: [PATCH 241/656] MIPS: Fix uapi include in exported asm/siginfo.h commit 987e5b834467c9251ca584febda65ef8f66351a9 upstream. Since commit 8cb48fe169dd ("MIPS: Provide correct siginfo_t.si_stime"), MIPS' uapi/asm/siginfo.h has included uapi/asm-generic/siginfo.h directly before defining MIPS' struct siginfo, in order to get the necessary definitions needed for the siginfo struct without the generic copy_siginfo() hitting compiler errors due to struct siginfo not yet being defined. Now that the generic copy_siginfo() is moved out to linux/signal.h we can safely include asm-generic/siginfo.h before defining the MIPS specific struct siginfo, which avoids the uapi/ include as well as breakage due to generic copy_siginfo() being defined before struct siginfo. Reported-by: Christopher Ferris Fixes: 8cb48fe169dd ("MIPS: Provide correct siginfo_t.si_stime") Signed-off-by: James Hogan Cc: Petr Malat Cc: linux-mips@linux-mips.org Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/uapi/asm/siginfo.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/mips/include/uapi/asm/siginfo.h b/arch/mips/include/uapi/asm/siginfo.h index 03ec1090f781..e2b5337e840f 100644 --- a/arch/mips/include/uapi/asm/siginfo.h +++ b/arch/mips/include/uapi/asm/siginfo.h @@ -28,7 +28,7 @@ #define __ARCH_SIGSYS -#include +#include /* We can't use generic siginfo_t, because our si_code and si_errno are swapped */ typedef struct siginfo { @@ -118,6 +118,4 @@ typedef struct siginfo { #define SI_TIMER __SI_CODE(__SI_TIMER, -3) /* sent by timer expiration */ #define SI_MESGQ __SI_CODE(__SI_MESGQ, -4) /* sent by real time mesq state change */ -#include - #endif /* _UAPI_ASM_SIGINFO_H */ From 68d64569ff9568752eee68ee2fceaf68725834e9 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 1 Mar 2016 22:19:36 +0000 Subject: [PATCH 242/656] MIPS: Fix watchpoint restoration commit a7e89326b415b5d81c4b1016fd4a40db861eb58d upstream. Commit f51246efee2b ("MIPS: Get rid of finish_arch_switch().") moved the __restore_watch() call from finish_arch_switch() (i.e. after resume() returns) to before the resume() call in switch_to(). This results in watchpoints only being restored when a task is descheduled, preventing the watchpoints from being effective most of the time, except due to chance before the watchpoints are lazily removed. Fix the call sequence from switch_to() through to mips_install_watch_registers() to pass the task_struct pointer of the next task, instead of using current. This allows the watchpoints for the next (non-current) task to be restored without reintroducing finish_arch_switch(). Fixes: f51246efee2b ("MIPS: Get rid of finish_arch_switch().") Signed-off-by: James Hogan Cc: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/12726/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/switch_to.h | 2 +- arch/mips/include/asm/watch.h | 10 +++++----- arch/mips/kernel/pm.c | 2 +- arch/mips/kernel/watch.c | 5 ++--- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/arch/mips/include/asm/switch_to.h b/arch/mips/include/asm/switch_to.h index 28b5d84a5022..ebb5c0f2f90d 100644 --- a/arch/mips/include/asm/switch_to.h +++ b/arch/mips/include/asm/switch_to.h @@ -105,7 +105,7 @@ do { \ __clear_software_ll_bit(); \ if (cpu_has_userlocal) \ write_c0_userlocal(task_thread_info(next)->tp_value); \ - __restore_watch(); \ + __restore_watch(next); \ (last) = resume(prev, next, task_thread_info(next)); \ } while (0) diff --git a/arch/mips/include/asm/watch.h b/arch/mips/include/asm/watch.h index 20126ec79359..6ffe3eadf105 100644 --- a/arch/mips/include/asm/watch.h +++ b/arch/mips/include/asm/watch.h @@ -12,21 +12,21 @@ #include -void mips_install_watch_registers(void); +void mips_install_watch_registers(struct task_struct *t); void mips_read_watch_registers(void); void mips_clear_watch_registers(void); void mips_probe_watch_registers(struct cpuinfo_mips *c); #ifdef CONFIG_HARDWARE_WATCHPOINTS -#define __restore_watch() do { \ +#define __restore_watch(task) do { \ if (unlikely(test_bit(TIF_LOAD_WATCH, \ - ¤t_thread_info()->flags))) { \ - mips_install_watch_registers(); \ + &task_thread_info(task)->flags))) { \ + mips_install_watch_registers(task); \ } \ } while (0) #else -#define __restore_watch() do {} while (0) +#define __restore_watch(task) do {} while (0) #endif #endif /* _ASM_WATCH_H */ diff --git a/arch/mips/kernel/pm.c b/arch/mips/kernel/pm.c index fefdf39d3df3..dc814892133c 100644 --- a/arch/mips/kernel/pm.c +++ b/arch/mips/kernel/pm.c @@ -56,7 +56,7 @@ static void mips_cpu_restore(void) write_c0_userlocal(current_thread_info()->tp_value); /* Restore watch registers */ - __restore_watch(); + __restore_watch(current); } /** diff --git a/arch/mips/kernel/watch.c b/arch/mips/kernel/watch.c index 2a03abb5bd2c..9b78e375118e 100644 --- a/arch/mips/kernel/watch.c +++ b/arch/mips/kernel/watch.c @@ -15,10 +15,9 @@ * Install the watch registers for the current thread. A maximum of * four registers are installed although the machine may have more. */ -void mips_install_watch_registers(void) +void mips_install_watch_registers(struct task_struct *t) { - struct mips3264_watch_reg_state *watches = - ¤t->thread.watch.mips3264; + struct mips3264_watch_reg_state *watches = &t->thread.watch.mips3264; switch (current_cpu_data.watch_reg_use_cnt) { default: BUG(); From a3dc327c1f5ccbe64d998f5e44926c967afd8a30 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 1 Mar 2016 02:37:57 +0000 Subject: [PATCH 243/656] MIPS: Flush highmem pages in __flush_dcache_page commit 234859e49a15323cf1b2331bdde7f658c4cb45fb upstream. When flush_dcache_page is called on an executable page, that page is about to be provided to userland & we can presume that the icache contains no valid entries for its address range. However if the icache does not fill from the dcache then we cannot presume that the pages content has been written back as far as the memories that the dcache will fill from (ie. L2 or further out). This was being done for lowmem pages, but not for highmem which can lead to icache corruption. Fix this by mapping highmem pages & flushing their content from the dcache in __flush_dcache_page before providing the page to userland, just as is done for lowmem pages. Signed-off-by: Paul Burton Cc: Lars Persson Cc: Andrew Morton Cc: Kirill A. Shutemov Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/12720/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/mm/cache.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index aab218c36e0d..801016df8999 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -83,8 +84,6 @@ void __flush_dcache_page(struct page *page) struct address_space *mapping = page_mapping(page); unsigned long addr; - if (PageHighMem(page)) - return; if (mapping && !mapping_mapped(mapping)) { SetPageDcacheDirty(page); return; @@ -95,8 +94,15 @@ void __flush_dcache_page(struct page *page) * case is for exec env/arg pages and those are %99 certainly going to * get faulted into the tlb (and thus flushed) anyways. */ - addr = (unsigned long) page_address(page); + if (PageHighMem(page)) + addr = (unsigned long)kmap_atomic(page); + else + addr = (unsigned long)page_address(page); + flush_data_cache_page(addr); + + if (PageHighMem(page)) + __kunmap_atomic((void *)addr); } EXPORT_SYMBOL(__flush_dcache_page); From a8c09ec300b6bdfae69d03fd047848f8d34421e8 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 1 Mar 2016 02:37:58 +0000 Subject: [PATCH 244/656] MIPS: Handle highmem pages in __update_cache commit f4281bba818105c7c91799abe40bc05c0dbdaa25 upstream. The following patch will expose __update_cache to highmem pages. Handle them by mapping them in for the duration of the cache maintenance, just like in __flush_dcache_page. The code for that isn't shared because we need the page address in __update_cache so sharing became messy. Given that the entirity is an extra 5 lines, just duplicate it. Signed-off-by: Paul Burton Cc: Lars Persson Cc: Andrew Morton Cc: Jerome Marchand Cc: Kirill A. Shutemov Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/12721/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/mm/cache.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index 801016df8999..fe93a51c80ef 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -149,9 +149,17 @@ void __update_cache(struct vm_area_struct *vma, unsigned long address, return; page = pfn_to_page(pfn); if (page_mapping(page) && Page_dcache_dirty(page)) { - addr = (unsigned long) page_address(page); + if (PageHighMem(page)) + addr = (unsigned long)kmap_atomic(page); + else + addr = (unsigned long)page_address(page); + if (exec || pages_do_alias(addr, address & PAGE_MASK)) flush_data_cache_page(addr); + + if (PageHighMem(page)) + __kunmap_atomic((void *)addr); + ClearPageDcacheDirty(page); } } From 6a0538541547a6513126f947b4417ff6ee8a9316 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Tue, 1 Mar 2016 02:37:59 +0000 Subject: [PATCH 245/656] MIPS: Sync icache & dcache in set_pte_at commit 37d22a0d798b5c938b277d32cfd86dc231381342 upstream. It's possible for pages to become visible prior to update_mmu_cache running if a thread within the same address space preempts the current thread or runs simultaneously on another CPU. That is, the following scenario is possible: CPU0 CPU1 write to page flush_dcache_page flush_icache_page set_pte_at map page update_mmu_cache If CPU1 maps the page in between CPU0's set_pte_at, which marks it valid & visible, and update_mmu_cache where the dcache flush occurs then CPU1s icache will fill from stale data (unless it fills from the dcache, in which case all is good, but most MIPS CPUs don't have this property). Commit 4d46a67a3eb8 ("MIPS: Fix race condition in lazy cache flushing.") attempted to fix that by performing the dcache flush in flush_icache_page such that it occurs before the set_pte_at call makes the page visible. However it has the problem that not all code that writes to pages exposed to userland call flush_icache_page. There are many callers of set_pte_at under mm/ and only 2 of them do call flush_icache_page. Thus the race window between a page becoming visible & being coherent between the icache & dcache remains open in some cases. To illustrate some of the cases, a WARN was added to __update_cache with this patch applied that triggered in cases where a page about to be flushed from the dcache was not the last page provided to flush_icache_page. That is, backtraces were obtained for cases in which the race window is left open without this patch. The 2 standout examples follow. When forking a process: [ 15.271842] [<80417630>] __update_cache+0xcc/0x188 [ 15.277274] [<80530394>] copy_page_range+0x56c/0x6ac [ 15.282861] [<8042936c>] copy_process.part.54+0xd40/0x17ac [ 15.289028] [<80429f80>] do_fork+0xe4/0x420 [ 15.293747] [<80413808>] handle_sys+0x128/0x14c When exec'ing an ELF binary: [ 14.445964] [<80417630>] __update_cache+0xcc/0x188 [ 14.451369] [<80538d88>] move_page_tables+0x414/0x498 [ 14.457075] [<8055d848>] setup_arg_pages+0x220/0x318 [ 14.462685] [<805b0f38>] load_elf_binary+0x530/0x12a0 [ 14.468374] [<8055ec3c>] search_binary_handler+0xbc/0x214 [ 14.474444] [<8055f6c0>] do_execveat_common+0x43c/0x67c [ 14.480324] [<8055f938>] do_execve+0x38/0x44 [ 14.485137] [<80413808>] handle_sys+0x128/0x14c These code paths write into a page, call flush_dcache_page then call set_pte_at without flush_icache_page inbetween. The end result is that the icache can become corrupted & userland processes may execute unexpected or invalid code, typically resulting in a reserved instruction exception, a trap or a segfault. Fix this race condition fully by performing any cache maintenance required to keep the icache & dcache in sync in set_pte_at, before the page is made valid. This has the added bonus of ensuring the cache maintenance always happens in one location, rather than being duplicated in flush_icache_page & update_mmu_cache. It also matches the way other architectures solve the same problem (see arm, ia64 & powerpc). Signed-off-by: Paul Burton Reported-by: Ionela Voinescu Cc: Lars Persson Fixes: 4d46a67a3eb8 ("MIPS: Fix race condition in lazy cache flushing.") Cc: Steven J. Hill Cc: David Daney Cc: Huacai Chen Cc: Aneesh Kumar K.V Cc: Andrew Morton Cc: Jerome Marchand Cc: Kirill A. Shutemov Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/12722/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/cacheflush.h | 6 ------ arch/mips/include/asm/pgtable.h | 26 +++++++++++++++++++++----- arch/mips/mm/cache.c | 19 +++---------------- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/arch/mips/include/asm/cacheflush.h b/arch/mips/include/asm/cacheflush.h index 723229f4cf27..176de586a71a 100644 --- a/arch/mips/include/asm/cacheflush.h +++ b/arch/mips/include/asm/cacheflush.h @@ -51,7 +51,6 @@ extern void (*flush_cache_range)(struct vm_area_struct *vma, unsigned long start, unsigned long end); extern void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn); extern void __flush_dcache_page(struct page *page); -extern void __flush_icache_page(struct vm_area_struct *vma, struct page *page); #define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1 static inline void flush_dcache_page(struct page *page) @@ -77,11 +76,6 @@ static inline void flush_anon_page(struct vm_area_struct *vma, static inline void flush_icache_page(struct vm_area_struct *vma, struct page *page) { - if (!cpu_has_ic_fills_f_dc && (vma->vm_flags & VM_EXEC) && - Page_dcache_dirty(page)) { - __flush_icache_page(vma, page); - ClearPageDcacheDirty(page); - } } extern void (*flush_icache_range)(unsigned long start, unsigned long end); diff --git a/arch/mips/include/asm/pgtable.h b/arch/mips/include/asm/pgtable.h index 18826aa15a7c..4e68c644acc5 100644 --- a/arch/mips/include/asm/pgtable.h +++ b/arch/mips/include/asm/pgtable.h @@ -127,10 +127,14 @@ do { \ } \ } while(0) +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval); + #if defined(CONFIG_PHYS_ADDR_T_64BIT) && defined(CONFIG_CPU_MIPS32) #define pte_none(pte) (!(((pte).pte_high) & ~_PAGE_GLOBAL)) #define pte_present(pte) ((pte).pte_low & _PAGE_PRESENT) +#define pte_no_exec(pte) ((pte).pte_low & _PAGE_NO_EXEC) static inline void set_pte(pte_t *ptep, pte_t pte) { @@ -148,7 +152,6 @@ static inline void set_pte(pte_t *ptep, pte_t pte) buddy->pte_high |= _PAGE_GLOBAL; } } -#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval) static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { @@ -166,6 +169,7 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt #define pte_none(pte) (!(pte_val(pte) & ~_PAGE_GLOBAL)) #define pte_present(pte) (pte_val(pte) & _PAGE_PRESENT) +#define pte_no_exec(pte) (pte_val(pte) & _PAGE_NO_EXEC) /* * Certain architectures need to do special things when pte's @@ -218,7 +222,6 @@ static inline void set_pte(pte_t *ptep, pte_t pteval) } #endif } -#define set_pte_at(mm, addr, ptep, pteval) set_pte(ptep, pteval) static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) { @@ -234,6 +237,22 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt } #endif +static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval) +{ + extern void __update_cache(unsigned long address, pte_t pte); + + if (!pte_present(pteval)) + goto cache_sync_done; + + if (pte_present(*ptep) && (pte_pfn(*ptep) == pte_pfn(pteval))) + goto cache_sync_done; + + __update_cache(addr, pteval); +cache_sync_done: + set_pte(ptep, pteval); +} + /* * (pmds are folded into puds so this doesn't get actually called, * but the define is needed for a generic inline function.) @@ -430,15 +449,12 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot) extern void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t pte); -extern void __update_cache(struct vm_area_struct *vma, unsigned long address, - pte_t pte); static inline void update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep) { pte_t pte = *ptep; __update_tlb(vma, address, pte); - __update_cache(vma, address, pte); } static inline void update_mmu_cache_pmd(struct vm_area_struct *vma, diff --git a/arch/mips/mm/cache.c b/arch/mips/mm/cache.c index fe93a51c80ef..e87bccd6e0aa 100644 --- a/arch/mips/mm/cache.c +++ b/arch/mips/mm/cache.c @@ -125,30 +125,17 @@ void __flush_anon_page(struct page *page, unsigned long vmaddr) EXPORT_SYMBOL(__flush_anon_page); -void __flush_icache_page(struct vm_area_struct *vma, struct page *page) -{ - unsigned long addr; - - if (PageHighMem(page)) - return; - - addr = (unsigned long) page_address(page); - flush_data_cache_page(addr); -} -EXPORT_SYMBOL_GPL(__flush_icache_page); - -void __update_cache(struct vm_area_struct *vma, unsigned long address, - pte_t pte) +void __update_cache(unsigned long address, pte_t pte) { struct page *page; unsigned long pfn, addr; - int exec = (vma->vm_flags & VM_EXEC) && !cpu_has_ic_fills_f_dc; + int exec = !pte_no_exec(pte) && !cpu_has_ic_fills_f_dc; pfn = pte_pfn(pte); if (unlikely(!pfn_valid(pfn))) return; page = pfn_to_page(pfn); - if (page_mapping(page) && Page_dcache_dirty(page)) { + if (Page_dcache_dirty(page)) { if (PageHighMem(page)) addr = (unsigned long)kmap_atomic(page); else From 969f0f69e8d0c4b2706c9c31589c2ac39806dafd Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 24 Mar 2016 16:02:52 +0100 Subject: [PATCH 246/656] MIPS: ath79: make bootconsole wait for both THRE and TEMT commit f5b556c94c8490d42fea79d7b4ae0ecbc291e69d upstream. This makes the ath79 bootconsole behave the same way as the generic 8250 bootconsole. Also waiting for TEMT (transmit buffer is empty) instead of just THRE (transmit buffer is not full) ensures that all characters have been transmitted before the real serial driver starts reconfiguring the serial controller (which would sometimes result in garbage being transmitted.) This change does not cause a visible performance loss. In addition, this seems to fix a hang observed in certain configurations on many AR7xxx/AR9xxx SoCs during autoconfig of the real serial driver. A more complete follow-up patch will disable 8250 autoconfig for ath79 altogether (the serial controller is detected as a 16550A, which is not fully compatible with the ath79 serial, and the autoconfig may lead to undefined behavior on ath79.) Signed-off-by: Matthias Schiffer Signed-off-by: Greg Kroah-Hartman Signed-off-by: Greg Kroah-Hartman --- arch/mips/ath79/early_printk.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/mips/ath79/early_printk.c b/arch/mips/ath79/early_printk.c index b955fafc58ba..d1adc59af5bf 100644 --- a/arch/mips/ath79/early_printk.c +++ b/arch/mips/ath79/early_printk.c @@ -31,13 +31,15 @@ static inline void prom_putchar_wait(void __iomem *reg, u32 mask, u32 val) } while (1); } +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + static void prom_putchar_ar71xx(unsigned char ch) { void __iomem *base = (void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE)); - prom_putchar_wait(base + UART_LSR * 4, UART_LSR_THRE, UART_LSR_THRE); + prom_putchar_wait(base + UART_LSR * 4, BOTH_EMPTY, BOTH_EMPTY); __raw_writel(ch, base + UART_TX * 4); - prom_putchar_wait(base + UART_LSR * 4, UART_LSR_THRE, UART_LSR_THRE); + prom_putchar_wait(base + UART_LSR * 4, BOTH_EMPTY, BOTH_EMPTY); } static void prom_putchar_ar933x(unsigned char ch) From f2c41222fc75e84b2f0337b673b31ae7e1bd6fd7 Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 17 Mar 2016 20:37:10 +0800 Subject: [PATCH 247/656] MIPS: Reserve nosave data for hibernation commit a95d069204e178f18476f5499abab0d0d9cbc32c upstream. After commit 92923ca3aacef63c92d ("mm: meminit: only set page reserved in the memblock region"), the MIPS hibernation is broken. Because pages in nosave data section should be "reserved", but currently they aren't set to "reserved" at initialization. This patch makes hibernation work again. Signed-off-by: Huacai Chen Cc: Aurelien Jarno Cc: Steven J . Hill Cc: Fuxin Zhang Cc: Zhangjin Wu Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/12888/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/setup.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 66aac55df349..8acae316f26b 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -706,6 +706,9 @@ static void __init arch_mem_init(char **cmdline_p) for_each_memblock(reserved, reg) if (reg->size != 0) reserve_bootmem(reg->base, reg->size, BOOTMEM_DEFAULT); + + reserve_bootmem_region(__pa_symbol(&__nosave_begin), + __pa_symbol(&__nosave_end)); /* Reserve for hibernation */ } static void __init resource_init(void) From 1a01c8c418772bc87a182126e16eba8400eaf46a Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Thu, 17 Mar 2016 20:41:05 +0800 Subject: [PATCH 248/656] MIPS: Loongson-3: Reserve 32MB for RS780E integrated GPU commit 3484de7bcbed20ecbf2b8d80671619e7059e2dd7 upstream. Due to datasheet, reserving 0xff800000~0xffffffff (8MB below 4GB) is not enough for RS780E integrated GPU's TOM (top of memory) registers and MSI/MSI-x memory region, so we reserve 0xfe000000~0xffffffff (32MB below 4GB). Signed-off-by: Huacai Chen Cc: Aurelien Jarno Cc: Steven J . Hill Cc: Fuxin Zhang Cc: Zhangjin Wu Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/12889/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/loongson64/loongson-3/numa.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/mips/loongson64/loongson-3/numa.c b/arch/mips/loongson64/loongson-3/numa.c index 6f9e010cec4d..282c5a8c2fcd 100644 --- a/arch/mips/loongson64/loongson-3/numa.c +++ b/arch/mips/loongson64/loongson-3/numa.c @@ -213,10 +213,10 @@ static void __init node_mem_init(unsigned int node) BOOTMEM_DEFAULT); if (node == 0 && node_end_pfn(0) >= (0xffffffff >> PAGE_SHIFT)) { - /* Reserve 0xff800000~0xffffffff for RS780E integrated GPU */ + /* Reserve 0xfe000000~0xffffffff for RS780E integrated GPU */ reserve_bootmem_node(NODE_DATA(node), - (node_addrspace_offset | 0xff800000), - 8 << 20, BOOTMEM_DEFAULT); + (node_addrspace_offset | 0xfe000000), + 32 << 20, BOOTMEM_DEFAULT); } sparse_memory_present_with_active_regions(node); From 73410f3b164cd436aaaf6a1944aab4892d7b92f2 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Fri, 15 Apr 2016 10:07:23 +0100 Subject: [PATCH 249/656] MIPS: Use copy_s.fmt rather than copy_u.fmt commit 8a3c8b48aca8771bff3536e40aa26ffb311699d1 upstream. In revision 1.12 of the MSA specification, the copy_u.w instruction has been removed for MIPS32 & the copy_u.d instruction has been removed for MIPS64. Newer toolchains (eg. Codescape SDK essentials 2015.10) will complain about this like so: arch/mips/kernel/r4k_fpu.S:290: Error: opcode not supported on this processor: mips32r2 (mips32r2) `copy_u.w $1,$w26[3]' Since we always copy to the width of a GPR, simply use copy_s instead of copy_u to fix this. Signed-off-by: Paul Burton Signed-off-by: James Hogan Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/13061/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/asmmacro.h | 24 ++++++++++++------------ arch/mips/kernel/r4k_fpu.S | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/arch/mips/include/asm/asmmacro.h b/arch/mips/include/asm/asmmacro.h index 867f924b05c7..b99b38862fcb 100644 --- a/arch/mips/include/asm/asmmacro.h +++ b/arch/mips/include/asm/asmmacro.h @@ -298,21 +298,21 @@ .set pop .endm - .macro copy_u_w ws, n + .macro copy_s_w ws, n .set push .set mips32r2 .set fp=64 .set msa - copy_u.w $1, $w\ws[\n] + copy_s.w $1, $w\ws[\n] .set pop .endm - .macro copy_u_d ws, n + .macro copy_s_d ws, n .set push .set mips64r2 .set fp=64 .set msa - copy_u.d $1, $w\ws[\n] + copy_s.d $1, $w\ws[\n] .set pop .endm @@ -346,8 +346,8 @@ #define STH_MSA_INSN 0x5800081f #define STW_MSA_INSN 0x5800082f #define STD_MSA_INSN 0x5800083f -#define COPY_UW_MSA_INSN 0x58f00056 -#define COPY_UD_MSA_INSN 0x58f80056 +#define COPY_SW_MSA_INSN 0x58b00056 +#define COPY_SD_MSA_INSN 0x58b80056 #define INSERT_W_MSA_INSN 0x59300816 #define INSERT_D_MSA_INSN 0x59380816 #else @@ -361,8 +361,8 @@ #define STH_MSA_INSN 0x78000825 #define STW_MSA_INSN 0x78000826 #define STD_MSA_INSN 0x78000827 -#define COPY_UW_MSA_INSN 0x78f00059 -#define COPY_UD_MSA_INSN 0x78f80059 +#define COPY_SW_MSA_INSN 0x78b00059 +#define COPY_SD_MSA_INSN 0x78b80059 #define INSERT_W_MSA_INSN 0x79300819 #define INSERT_D_MSA_INSN 0x79380819 #endif @@ -461,21 +461,21 @@ .set pop .endm - .macro copy_u_w ws, n + .macro copy_s_w ws, n .set push .set noat SET_HARDFLOAT .insn - .word COPY_UW_MSA_INSN | (\n << 16) | (\ws << 11) + .word COPY_SW_MSA_INSN | (\n << 16) | (\ws << 11) .set pop .endm - .macro copy_u_d ws, n + .macro copy_s_d ws, n .set push .set noat SET_HARDFLOAT .insn - .word COPY_UD_MSA_INSN | (\n << 16) | (\ws << 11) + .word COPY_SD_MSA_INSN | (\n << 16) | (\ws << 11) .set pop .endm diff --git a/arch/mips/kernel/r4k_fpu.S b/arch/mips/kernel/r4k_fpu.S index f09546ee2cdc..bc74485ec805 100644 --- a/arch/mips/kernel/r4k_fpu.S +++ b/arch/mips/kernel/r4k_fpu.S @@ -244,17 +244,17 @@ LEAF(\name) .set push .set noat #ifdef CONFIG_64BIT - copy_u_d \wr, 1 + copy_s_d \wr, 1 EX sd $1, \off(\base) #elif defined(CONFIG_CPU_LITTLE_ENDIAN) - copy_u_w \wr, 2 + copy_s_w \wr, 2 EX sw $1, \off(\base) - copy_u_w \wr, 3 + copy_s_w \wr, 3 EX sw $1, (\off+4)(\base) #else /* CONFIG_CPU_BIG_ENDIAN */ - copy_u_w \wr, 2 + copy_s_w \wr, 2 EX sw $1, (\off+4)(\base) - copy_u_w \wr, 3 + copy_s_w \wr, 3 EX sw $1, \off(\base) #endif .set pop From 612d5094624df41eb4cd83a8594836836e1041cd Mon Sep 17 00:00:00 2001 From: James Hogan Date: Fri, 15 Apr 2016 10:07:24 +0100 Subject: [PATCH 250/656] MIPS: Fix MSA ld_*/st_* asm macros to use PTR_ADDU commit ea1688573426adc2587ed52d086b51c7c62eaca3 upstream. The MSA ld_*/st_* assembler macros for when the toolchain doesn't support MSA use addu to offset the base address. However it is a virtual memory pointer so fix it to use PTR_ADDU which expands to daddu for 64-bit kernels. Signed-off-by: James Hogan Cc: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/13062/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/include/asm/asmmacro.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/mips/include/asm/asmmacro.h b/arch/mips/include/asm/asmmacro.h index b99b38862fcb..e689b894353c 100644 --- a/arch/mips/include/asm/asmmacro.h +++ b/arch/mips/include/asm/asmmacro.h @@ -393,7 +393,7 @@ .set push .set noat SET_HARDFLOAT - addu $1, \base, \off + PTR_ADDU $1, \base, \off .word LDB_MSA_INSN | (\wd << 6) .set pop .endm @@ -402,7 +402,7 @@ .set push .set noat SET_HARDFLOAT - addu $1, \base, \off + PTR_ADDU $1, \base, \off .word LDH_MSA_INSN | (\wd << 6) .set pop .endm @@ -411,7 +411,7 @@ .set push .set noat SET_HARDFLOAT - addu $1, \base, \off + PTR_ADDU $1, \base, \off .word LDW_MSA_INSN | (\wd << 6) .set pop .endm @@ -420,7 +420,7 @@ .set push .set noat SET_HARDFLOAT - addu $1, \base, \off + PTR_ADDU $1, \base, \off .word LDD_MSA_INSN | (\wd << 6) .set pop .endm @@ -429,7 +429,7 @@ .set push .set noat SET_HARDFLOAT - addu $1, \base, \off + PTR_ADDU $1, \base, \off .word STB_MSA_INSN | (\wd << 6) .set pop .endm @@ -438,7 +438,7 @@ .set push .set noat SET_HARDFLOAT - addu $1, \base, \off + PTR_ADDU $1, \base, \off .word STH_MSA_INSN | (\wd << 6) .set pop .endm @@ -447,7 +447,7 @@ .set push .set noat SET_HARDFLOAT - addu $1, \base, \off + PTR_ADDU $1, \base, \off .word STW_MSA_INSN | (\wd << 6) .set pop .endm @@ -456,7 +456,7 @@ .set push .set noat SET_HARDFLOAT - addu $1, \base, \off + PTR_ADDU $1, \base, \off .word STD_MSA_INSN | (\wd << 6) .set pop .endm From 6dc9f27c09cf380f70505c08bcd805e4495ab4f8 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 21 Apr 2016 18:04:53 +0100 Subject: [PATCH 251/656] MIPS: Prevent "restoration" of MSA context in non-MSA kernels commit 6533af4d4831c421cd9aa4dce7cfc19a3514cc09 upstream. If a kernel doesn't support MSA context (ie. CONFIG_CPU_HAS_MSA=n) then it will only keep 64 bits per FP register in thread context, and the calls to set_fpr64 in restore_msa_extcontext will overrun the end of the FP register context into the FCSR & MSACSR values. GCC 6.x has become smart enough to detect this & complain like so: arch/mips/kernel/signal.c: In function 'protected_restore_fp_context': ./arch/mips/include/asm/processor.h:114:17: error: array subscript is above array bounds [-Werror=array-bounds] fpr->val##width[FPR_IDX(width, idx)] = val; \ ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~ ./arch/mips/include/asm/processor.h:118:1: note: in expansion of macro 'BUILD_FPR_ACCESS' BUILD_FPR_ACCESS(64) The only way to trigger this code to run would be for a program to set up an artificial extended MSA context structure following a sigframe & execute sigreturn. Whilst this doesn't allow a program to write to any state that it couldn't already, it makes little sense to allow this "restoration" of MSA context in a system that doesn't support MSA. Fix this by killing a program with SIGSYS if it tries something as crazy as "restoring" fake MSA context in this way, also fixing the build error & allowing for most of restore_msa_extcontext to be optimised out of kernels without support for MSA. Signed-off-by: Paul Burton Reported-by: Michal Toman Fixes: bf82cb30c7e5 ("MIPS: Save MSA extended context around signals") Tested-by: Aaro Koskinen Cc: James Hogan Cc: Michal Toman Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/13164/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/signal.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index bf792e2839a6..fc7c1f0b3d8d 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -195,6 +195,9 @@ static int restore_msa_extcontext(void __user *buf, unsigned int size) unsigned int csr; int i, err; + if (!config_enabled(CONFIG_CPU_HAS_MSA)) + return SIGSYS; + if (size != sizeof(*msa)) return -EINVAL; @@ -398,8 +401,8 @@ int protected_restore_fp_context(void __user *sc) } fp_done: - if (used & USED_EXTCONTEXT) - err |= restore_extcontext(sc_to_extcontext(sc)); + if (!err && (used & USED_EXTCONTEXT)) + err = restore_extcontext(sc_to_extcontext(sc)); return err ?: sig; } From 57a94749251c3a5ce07b237a16723be8ea01b1ac Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Thu, 21 Apr 2016 12:43:57 +0100 Subject: [PATCH 252/656] MIPS: Disable preemption during prctl(PR_SET_FP_MODE, ...) commit bd239f1e1429e7781096bf3884bdb1b2b1bb4f28 upstream. Whilst a PR_SET_FP_MODE prctl is performed there are decisions made based upon whether the task is executing on the current CPU. This may change if we're preempted, so disable preemption to avoid such changes for the lifetime of the mode switch. Signed-off-by: Paul Burton Fixes: 9791554b45a2 ("MIPS,prctl: add PR_[GS]ET_FP_MODE prctl options for MIPS") Reviewed-by: Maciej W. Rozycki Tested-by: Aurelien Jarno Cc: Adam Buchbinder Cc: James Hogan Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/13144/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/process.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index 6b3ae73cda3f..89847bee2b53 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -603,6 +603,9 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value) if (!(value & PR_FP_MODE_FR) && cpu_has_fpu && cpu_has_mips_r6) return -EOPNOTSUPP; + /* Proceed with the mode switch */ + preempt_disable(); + /* Save FP & vector context, then disable FPU & MSA */ if (task->signal == current->signal) lose_fpu(1); @@ -661,6 +664,7 @@ int mips_set_process_fp_mode(struct task_struct *task, unsigned int value) /* Allow threads to use FP again */ atomic_set(&task->mm->context.fp_mode_switching, 0); + preempt_enable(); return 0; } From e47a4d4f11421b5725cba669bfef200bdc49564d Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Thu, 12 May 2016 10:18:27 +0100 Subject: [PATCH 253/656] MIPS: ptrace: Fix FP context restoration FCSR regression commit 4249548454f7ba4581aeee26bd83f42b48a14d15 upstream. Fix a floating-point context restoration regression introduced with commit 9b26616c8d9d ("MIPS: Respect the ISA level in FCSR handling") that causes a Floating Point exception and consequently a kernel oops with hard float configurations when one or more FCSR Enable and their corresponding Cause bits are set both at a time via a ptrace(2) call. To do so reinstate Cause bit masking originally introduced with commit b1442d39fac2 ("MIPS: Prevent user from setting FCSR cause bits") to address this exact problem and then inadvertently removed from the PTRACE_SETFPREGS request with the commit referred above. Signed-off-by: Maciej W. Rozycki Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/13238/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/ptrace.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 4f0ac78d17f1..80581d0f3b7c 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -176,6 +176,7 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) } __get_user(value, data + 64); + value &= ~FPU_CSR_ALL_X; fcr31 = child->thread.fpu.fcr31; mask = boot_cpu_data.fpu_msk31; child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask); From 13defedff19cae72e2b88a120c03792b3610bb06 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Thu, 12 May 2016 10:19:08 +0100 Subject: [PATCH 254/656] MIPS: ptrace: Prevent writes to read-only FCSR bits commit abf378be49f38c4d3e23581d3df3fa9f1b1b11d2 upstream. Correct the cases missed with commit 9b26616c8d9d ("MIPS: Respect the ISA level in FCSR handling") and prevent writes to read-only FCSR bits there. This in particular applies to FP context initialisation where any IEEE 754-2008 bits preset by `mips_set_personality_nan' are cleared before the relevant ptrace(2) call takes effect and the PTRACE_POKEUSR request addressing FPC_CSR where no masking of read-only FCSR bits is done. Remove the FCSR clearing from FP context initialisation then and unify PTRACE_POKEUSR/FPC_CSR and PTRACE_SETFPREGS handling, by factoring out code from `ptrace_setfpregs' and calling it from both places. This mostly matters to soft float configurations where the emulator can be switched this way to a mode which should not be accessible and cannot be set with the CTC1 instruction. With hard float configurations any effect is transient anyway as read-only bits will retain their values at the time the FP context is restored. Signed-off-by: Maciej W. Rozycki Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/13239/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/ptrace.c | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 80581d0f3b7c..74d581569778 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -57,8 +57,7 @@ static void init_fp_ctx(struct task_struct *target) /* Begin with data registers set to all 1s... */ memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr)); - /* ...and FCSR zeroed */ - target->thread.fpu.fcr31 = 0; + /* FCSR has been preset by `mips_set_personality_nan'. */ /* * Record that the target has "used" math, such that the context @@ -79,6 +78,22 @@ void ptrace_disable(struct task_struct *child) clear_tsk_thread_flag(child, TIF_LOAD_WATCH); } +/* + * Poke at FCSR according to its mask. Don't set the cause bits as + * this is currently not handled correctly in FP context restoration + * and will cause an oops if a corresponding enable bit is set. + */ +static void ptrace_setfcr31(struct task_struct *child, u32 value) +{ + u32 fcr31; + u32 mask; + + value &= ~FPU_CSR_ALL_X; + fcr31 = child->thread.fpu.fcr31; + mask = boot_cpu_data.fpu_msk31; + child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask); +} + /* * Read a general register set. We always use the 64-bit format, even * for 32-bit kernels and for 32-bit processes on a 64-bit kernel. @@ -159,9 +174,7 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) { union fpureg *fregs; u64 fpr_val; - u32 fcr31; u32 value; - u32 mask; int i; if (!access_ok(VERIFY_READ, data, 33 * 8)) @@ -176,10 +189,7 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) } __get_user(value, data + 64); - value &= ~FPU_CSR_ALL_X; - fcr31 = child->thread.fpu.fcr31; - mask = boot_cpu_data.fpu_msk31; - child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask); + ptrace_setfcr31(child, value); /* FIR may not be written. */ @@ -809,7 +819,7 @@ long arch_ptrace(struct task_struct *child, long request, break; #endif case FPC_CSR: - child->thread.fpu.fcr31 = data & ~FPU_CSR_ALL_X; + ptrace_setfcr31(child, data); break; case DSP_BASE ... DSP_BASE + 5: { dspreg_t *dregs; From 1985bf8d711a562dd143be05f05707333fbf7c7b Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 24 May 2016 09:35:10 +0100 Subject: [PATCH 255/656] MIPS: Fix sigreturn via VDSO on microMIPS kernel commit 13eb192d10bcc9ac518d57356179071d603bcb4e upstream. In microMIPS kernels, handle_signal() sets the isa16 mode bit in the vdso address so that the sigreturn trampolines (which are offset from the VDSO) get executed as microMIPS. However commit ebb5e78cc634 ("MIPS: Initial implementation of a VDSO") changed the offsets to come from the VDSO image, which already have the isa16 mode bit set correctly since they're extracted from the VDSO shared library symbol table. Drop the isa16 mode bit handling from handle_signal() to fix sigreturn for cores which support both microMIPS and normal MIPS. This doesn't fix microMIPS only cores, since the VDSO is still built for normal MIPS, but thats a separate problem. Fixes: ebb5e78cc634 ("MIPS: Initial implementation of a VDSO") Signed-off-by: James Hogan Cc: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/13348/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/kernel/signal.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index fc7c1f0b3d8d..9e35b6b26aa8 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -770,15 +770,7 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) sigset_t *oldset = sigmask_to_save(); int ret; struct mips_abi *abi = current->thread.abi; -#ifdef CONFIG_CPU_MICROMIPS - void *vdso; - unsigned long tmp = (unsigned long)current->mm->context.vdso; - - set_isa16_mode(tmp); - vdso = (void *)tmp; -#else void *vdso = current->mm->context.vdso; -#endif if (regs->regs[0]) { switch(regs->regs[2]) { From bfcc040a885bc182ad950cc45b00d0e1367a7284 Mon Sep 17 00:00:00 2001 From: James Hogan Date: Tue, 24 May 2016 09:35:11 +0100 Subject: [PATCH 256/656] MIPS: Build microMIPS VDSO for microMIPS kernels commit bb93078e655be1e24d68f28f2756676e62c037ce upstream. MicroMIPS kernels may be expected to run on microMIPS only cores which don't support the normal MIPS instruction set, so be sure to pass the -mmicromips flag through to the VDSO cflags. Fixes: ebb5e78cc634 ("MIPS: Initial implementation of a VDSO") Signed-off-by: James Hogan Cc: Paul Burton Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/13349/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/vdso/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/vdso/Makefile b/arch/mips/vdso/Makefile index 14568900fc1d..bd3eba74b05c 100644 --- a/arch/mips/vdso/Makefile +++ b/arch/mips/vdso/Makefile @@ -5,6 +5,7 @@ obj-vdso-y := elf.o gettimeofday.o sigreturn.o ccflags-vdso := \ $(filter -I%,$(KBUILD_CFLAGS)) \ $(filter -E%,$(KBUILD_CFLAGS)) \ + $(filter -mmicromips,$(KBUILD_CFLAGS)) \ $(filter -march=%,$(KBUILD_CFLAGS)) cflags-vdso := $(ccflags-vdso) \ $(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \ From 7b74228bb0815412ed2735b379113ee386179766 Mon Sep 17 00:00:00 2001 From: Harvey Hunt Date: Wed, 25 May 2016 11:06:35 +0100 Subject: [PATCH 257/656] MIPS: lib: Mark intrinsics notrace commit aedcfbe06558a9f53002e82d5be64c6c94687726 upstream. On certain MIPS32 devices, the ftrace tracer "function_graph" uses __lshrdi3() during the capturing of trace data. ftrace then attempts to trace __lshrdi3() which leads to infinite recursion and a stack overflow. Fix this by marking __lshrdi3() as notrace. Mark the other compiler intrinsics as notrace in case the compiler decides to use them in the ftrace path. Signed-off-by: Harvey Hunt Cc: Cc: Patchwork: https://patchwork.linux-mips.org/patch/13354/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/lib/ashldi3.c | 2 +- arch/mips/lib/ashrdi3.c | 2 +- arch/mips/lib/bswapdi.c | 2 +- arch/mips/lib/bswapsi.c | 2 +- arch/mips/lib/cmpdi2.c | 2 +- arch/mips/lib/lshrdi3.c | 2 +- arch/mips/lib/ucmpdi2.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/mips/lib/ashldi3.c b/arch/mips/lib/ashldi3.c index beb80f316095..927dc94a030f 100644 --- a/arch/mips/lib/ashldi3.c +++ b/arch/mips/lib/ashldi3.c @@ -2,7 +2,7 @@ #include "libgcc.h" -long long __ashldi3(long long u, word_type b) +long long notrace __ashldi3(long long u, word_type b) { DWunion uu, w; word_type bm; diff --git a/arch/mips/lib/ashrdi3.c b/arch/mips/lib/ashrdi3.c index c884a912b660..9fdf1a598428 100644 --- a/arch/mips/lib/ashrdi3.c +++ b/arch/mips/lib/ashrdi3.c @@ -2,7 +2,7 @@ #include "libgcc.h" -long long __ashrdi3(long long u, word_type b) +long long notrace __ashrdi3(long long u, word_type b) { DWunion uu, w; word_type bm; diff --git a/arch/mips/lib/bswapdi.c b/arch/mips/lib/bswapdi.c index 77e5f9c1f005..e3e77aa52c95 100644 --- a/arch/mips/lib/bswapdi.c +++ b/arch/mips/lib/bswapdi.c @@ -1,6 +1,6 @@ #include -unsigned long long __bswapdi2(unsigned long long u) +unsigned long long notrace __bswapdi2(unsigned long long u) { return (((u) & 0xff00000000000000ull) >> 56) | (((u) & 0x00ff000000000000ull) >> 40) | diff --git a/arch/mips/lib/bswapsi.c b/arch/mips/lib/bswapsi.c index 2b302ff121d2..530a8afe6fda 100644 --- a/arch/mips/lib/bswapsi.c +++ b/arch/mips/lib/bswapsi.c @@ -1,6 +1,6 @@ #include -unsigned int __bswapsi2(unsigned int u) +unsigned int notrace __bswapsi2(unsigned int u) { return (((u) & 0xff000000) >> 24) | (((u) & 0x00ff0000) >> 8) | diff --git a/arch/mips/lib/cmpdi2.c b/arch/mips/lib/cmpdi2.c index 8c1306437ed1..06857da96993 100644 --- a/arch/mips/lib/cmpdi2.c +++ b/arch/mips/lib/cmpdi2.c @@ -2,7 +2,7 @@ #include "libgcc.h" -word_type __cmpdi2(long long a, long long b) +word_type notrace __cmpdi2(long long a, long long b) { const DWunion au = { .ll = a diff --git a/arch/mips/lib/lshrdi3.c b/arch/mips/lib/lshrdi3.c index dcf8d6810b7c..364547449c65 100644 --- a/arch/mips/lib/lshrdi3.c +++ b/arch/mips/lib/lshrdi3.c @@ -2,7 +2,7 @@ #include "libgcc.h" -long long __lshrdi3(long long u, word_type b) +long long notrace __lshrdi3(long long u, word_type b) { DWunion uu, w; word_type bm; diff --git a/arch/mips/lib/ucmpdi2.c b/arch/mips/lib/ucmpdi2.c index bb4cb2f828ea..bd599f58234c 100644 --- a/arch/mips/lib/ucmpdi2.c +++ b/arch/mips/lib/ucmpdi2.c @@ -2,7 +2,7 @@ #include "libgcc.h" -word_type __ucmpdi2(unsigned long long a, unsigned long long b) +word_type notrace __ucmpdi2(unsigned long long a, unsigned long long b) { const DWunion au = {.ll = a}; const DWunion bu = {.ll = b}; From 71f5e9b77853dc80ed1b1d5b8428f0e10b3ae1d3 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Thu, 26 May 2016 12:55:45 +0100 Subject: [PATCH 258/656] MIPS: VDSO: Build with `-fno-strict-aliasing' commit 94cc36b84acc29f543b48bc5ed786011b112a666 upstream. Avoid an aliasing issue causing a build error in VDSO: In file included from include/linux/srcu.h:34:0, from include/linux/notifier.h:15, from ./arch/mips/include/asm/uprobes.h:9, from include/linux/uprobes.h:61, from include/linux/mm_types.h:13, from ./arch/mips/include/asm/vdso.h:14, from arch/mips/vdso/vdso.h:27, from arch/mips/vdso/gettimeofday.c:11: include/linux/workqueue.h: In function 'work_static': include/linux/workqueue.h:186:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing] return *work_data_bits(work) & WORK_STRUCT_STATIC; ^ cc1: all warnings being treated as errors make[2]: *** [arch/mips/vdso/gettimeofday.o] Error 1 with a CONFIG_DEBUG_OBJECTS_WORK configuration and GCC 5.2.0. Include `-fno-strict-aliasing' along with compiler options used, as required for kernel code, fixing a problem present since the introduction of VDSO with commit ebb5e78cc634 ("MIPS: Initial implementation of a VDSO"). Thanks to Tejun for diagnosing this properly! Signed-off-by: Maciej W. Rozycki Reviewed-by: James Hogan Fixes: ebb5e78cc634 ("MIPS: Initial implementation of a VDSO") Cc: Tejun Heo Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/13357/ Signed-off-by: Ralf Baechle Signed-off-by: Greg Kroah-Hartman --- arch/mips/vdso/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/mips/vdso/Makefile b/arch/mips/vdso/Makefile index bd3eba74b05c..090393aa0f20 100644 --- a/arch/mips/vdso/Makefile +++ b/arch/mips/vdso/Makefile @@ -9,7 +9,8 @@ ccflags-vdso := \ $(filter -march=%,$(KBUILD_CFLAGS)) cflags-vdso := $(ccflags-vdso) \ $(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \ - -O2 -g -fPIC -fno-common -fno-builtin -G 0 -DDISABLE_BRANCH_PROFILING \ + -O2 -g -fPIC -fno-strict-aliasing -fno-common -fno-builtin -G 0 \ + -DDISABLE_BRANCH_PROFILING \ $(call cc-option, -fno-stack-protector) aflags-vdso := $(ccflags-vdso) \ $(filter -I%,$(KBUILD_CFLAGS)) \ From 91bb3cf4785d545074689f481ff1a0bd5322d45b Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Tue, 24 May 2016 22:48:33 +0200 Subject: [PATCH 259/656] affs: fix remount failure when there are no options changed commit 01d6e08711bf90bc4d7ead14a93a0cbd73b1896a upstream. Commit c8f33d0bec99 ("affs: kstrdup() memory handling") checks if the kstrdup function returns NULL due to out-of-memory condition. However, if we are remounting a filesystem with no change to filesystem-specific options, the parameter data is NULL. In this case, kstrdup returns NULL (because it was passed NULL parameter), although no out of memory condition exists. The mount syscall then fails with ENOMEM. This patch fixes the bug. We fail with ENOMEM only if data is non-NULL. The patch also changes the call to replace_mount_options - if we didn't pass any filesystem-specific options, we don't call replace_mount_options (thus we don't erase existing reported options). Fixes: c8f33d0bec99 ("affs: kstrdup() memory handling") Signed-off-by: Mikulas Patocka Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- fs/affs/super.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/affs/super.c b/fs/affs/super.c index 5b50c4ca43a7..f90c535703ce 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -528,7 +528,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) char *prefix = NULL; new_opts = kstrdup(data, GFP_KERNEL); - if (!new_opts) + if (data && !new_opts) return -ENOMEM; pr_debug("%s(flags=0x%x,opts=\"%s\")\n", __func__, *flags, data); @@ -546,7 +546,8 @@ affs_remount(struct super_block *sb, int *flags, char *data) } flush_delayed_work(&sbi->sb_work); - replace_mount_options(sb, new_opts); + if (new_opts) + replace_mount_options(sb, new_opts); sbi->s_flags = mount_flags; sbi->s_mode = mode; From 8f1b5ede037a9c704283e3ac31f161523885dacc Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 18 May 2016 18:30:39 +0100 Subject: [PATCH 260/656] ASoC: ak4642: Enable cache usage to fix crashes on resume commit d3030d11961a8c103cf07aed59905276ddfc06c2 upstream. The ak4642 driver is using a regmap cache sync to restore the configuration of the chip on resume but (as Peter observed) does not actually define a register cache which means that the resume is never going to work and we trigger asserts in regmap. Fix this by enabling caching. Reported-by: Geert Uytterhoeven Reported-by: Peter Ujfalusi Tested-by: Geert Uytterhoeven Signed-off-by: Mark Brown Signed-off-by: Greg Kroah-Hartman --- sound/soc/codecs/ak4642.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index cda27c22812a..eb8fe212e163 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -560,6 +560,7 @@ static const struct regmap_config ak4642_regmap = { .max_register = FIL1_3, .reg_defaults = ak4642_reg, .num_reg_defaults = NUM_AK4642_REG_DEFAULTS, + .cache_type = REGCACHE_RBTREE, }; static const struct regmap_config ak4643_regmap = { @@ -568,6 +569,7 @@ static const struct regmap_config ak4643_regmap = { .max_register = SPK_MS, .reg_defaults = ak4643_reg, .num_reg_defaults = ARRAY_SIZE(ak4643_reg), + .cache_type = REGCACHE_RBTREE, }; static const struct regmap_config ak4648_regmap = { @@ -576,6 +578,7 @@ static const struct regmap_config ak4648_regmap = { .max_register = EQ_FBEQE, .reg_defaults = ak4648_reg, .num_reg_defaults = ARRAY_SIZE(ak4648_reg), + .cache_type = REGCACHE_RBTREE, }; static const struct ak4642_drvdata ak4642_drvdata = { From 854d1c275baea33efabec4daf97f7d662cad6505 Mon Sep 17 00:00:00 2001 From: Ricky Liang Date: Fri, 20 May 2016 10:58:59 -0700 Subject: [PATCH 261/656] Input: uinput - handle compat ioctl for UI_SET_PHYS commit affa80bd97f7ca282d1faa91667b3ee9e4c590e6 upstream. When running a 32-bit userspace on a 64-bit kernel, the UI_SET_PHYS ioctl needs to be treated with special care, as it has the pointer size encoded in the command. Signed-off-by: Ricky Liang Signed-off-by: Dmitry Torokhov Signed-off-by: Greg Kroah-Hartman --- drivers/input/misc/uinput.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 5adbcedcb81c..2bb4c8633d3b 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -893,9 +893,15 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } #ifdef CONFIG_COMPAT + +#define UI_SET_PHYS_COMPAT _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t) + static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + if (cmd == UI_SET_PHYS_COMPAT) + cmd = UI_SET_PHYS; + return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); } #endif From 90756ca854623b5f2bbcd4447b420ba46ee9cda1 Mon Sep 17 00:00:00 2001 From: Imre Kaloz Date: Fri, 31 Jul 2015 20:42:00 +0200 Subject: [PATCH 262/656] ARM: mvebu: fix GPIO config on the Linksys boards commit 9800917cf92f5b5fe5cae706cb70db8d014f663c upstream. Some of the GPIO configs were wrong in the submitted DTS files, this patch fixes all affected boards. Signed-off-by: Imre Kaloz Signed-off-by: Greg Kroah-Hartman Signed-off-by: Gregory CLEMENT --- arch/arm/boot/dts/armada-385-linksys.dtsi | 6 +++--- arch/arm/boot/dts/armada-xp-linksys-mamba.dts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/arch/arm/boot/dts/armada-385-linksys.dtsi b/arch/arm/boot/dts/armada-385-linksys.dtsi index 85d2c377c332..8450944b28e6 100644 --- a/arch/arm/boot/dts/armada-385-linksys.dtsi +++ b/arch/arm/boot/dts/armada-385-linksys.dtsi @@ -245,7 +245,7 @@ button@2 { label = "Factory Reset Button"; linux,code = ; - gpios = <&gpio1 15 GPIO_ACTIVE_LOW>; + gpios = <&gpio0 29 GPIO_ACTIVE_LOW>; }; }; @@ -260,7 +260,7 @@ }; sata { - gpios = <&gpio1 22 GPIO_ACTIVE_HIGH>; + gpios = <&gpio1 22 GPIO_ACTIVE_LOW>; default-state = "off"; }; }; @@ -313,7 +313,7 @@ &pinctrl { keys_pin: keys-pin { - marvell,pins = "mpp24", "mpp47"; + marvell,pins = "mpp24", "mpp29"; marvell,function = "gpio"; }; diff --git a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts index b89e6cf1271a..7a461541ce50 100644 --- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts +++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts @@ -304,13 +304,13 @@ button@1 { label = "WPS"; linux,code = ; - gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>; + gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; }; button@2 { label = "Factory Reset Button"; linux,code = ; - gpios = <&gpio1 1 GPIO_ACTIVE_HIGH>; + gpios = <&gpio1 1 GPIO_ACTIVE_LOW>; }; }; From 5d03c6ea151f5a4f5c0189bceddc10badf4cb54d Mon Sep 17 00:00:00 2001 From: Florian Vallee Date: Tue, 19 Apr 2016 17:50:05 +0200 Subject: [PATCH 263/656] ARM: dts: at91: fix typo in sama5d2 PIN_PD24 description commit b1f3a3b03eb5f61b4051e2da9aa15653e705e111 upstream. Fix a typo on PIN_PD24 for UTXD2 and FLEXCOM4_IO3 which were wrongly linked to PIN_PD23). Signed-off-by: Florian Vallee Fixes: 7f16cb676c00 ("ARM: at91/dt: add sama5d2 pinmux") [nicolas.ferre@atmel.com: add commit message, changed subject] Signed-off-by: Nicolas Ferre Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/sama5d2-pinfunc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/sama5d2-pinfunc.h b/arch/arm/boot/dts/sama5d2-pinfunc.h index b0c912feaa2f..8a394f336003 100644 --- a/arch/arm/boot/dts/sama5d2-pinfunc.h +++ b/arch/arm/boot/dts/sama5d2-pinfunc.h @@ -837,8 +837,8 @@ #define PIN_PD23__ISC_FIELD PINMUX_PIN(PIN_PD23, 6, 4) #define PIN_PD24 120 #define PIN_PD24__GPIO PINMUX_PIN(PIN_PD24, 0, 0) -#define PIN_PD24__UTXD2 PINMUX_PIN(PIN_PD23, 1, 2) -#define PIN_PD24__FLEXCOM4_IO3 PINMUX_PIN(PIN_PD23, 3, 3) +#define PIN_PD24__UTXD2 PINMUX_PIN(PIN_PD24, 1, 2) +#define PIN_PD24__FLEXCOM4_IO3 PINMUX_PIN(PIN_PD24, 3, 3) #define PIN_PD25 121 #define PIN_PD25__GPIO PINMUX_PIN(PIN_PD25, 0, 0) #define PIN_PD25__SPI1_SPCK PINMUX_PIN(PIN_PD25, 1, 3) From 59bc5bedbd712003945e6d6bbef0aba5bb4603dc Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Fri, 6 May 2016 09:27:16 +0200 Subject: [PATCH 264/656] ARM: dts: exynos: Add interrupt line to MAX8997 PMIC on exynos4210-trats commit 330d12764e15f6e3e94ff34cda29db96d2589c24 upstream. MAX8997 PMIC requires interrupt and fails probing without it. Signed-off-by: Marek Szyprowski Fixes: d105f0b1215d ("ARM: dts: Add basic dts file for Samsung Trats board") [k.kozlowski: Write commit message, add CC-stable] Signed-off-by: Krzysztof Kozlowski Signed-off-by: Greg Kroah-Hartman --- arch/arm/boot/dts/exynos4210-trats.dts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts index a50be640f1b0..59411e447fa0 100644 --- a/arch/arm/boot/dts/exynos4210-trats.dts +++ b/arch/arm/boot/dts/exynos4210-trats.dts @@ -298,6 +298,8 @@ compatible = "maxim,max8997-pmic"; reg = <0x66>; + interrupt-parent = <&gpx0>; + interrupts = <7 0>; max8997,pmic-buck1-uses-gpio-dvs; max8997,pmic-buck2-uses-gpio-dvs; From 670664734da1f96f7d8525a91cd71a4c21db460f Mon Sep 17 00:00:00 2001 From: Alexander Kurz Date: Thu, 14 Apr 2016 23:30:49 +0200 Subject: [PATCH 265/656] ARM: dts: imx35: restore existing used clock enumeration commit 3397c2c45b1b6f54834dfeae30a73046f33ca943 upstream. A new element got inserted into enum mx35_clks with commit 3713e3f5e927 ("clk: imx35: define two clocks for rtc"). This insertion shifted most nummerical clock assignments to a new nummerical value which in turn rendered most hardcoded nummeric values in imx35.dtsi incorrect. Restore the existing order by moving the newly introduced clock to the end of the enum. Update the dts documentation accordingly. Signed-off-by: Alexander Kurz Fixes: 3713e3f5e927 ("clk: imx35: define two clocks for rtc") Signed-off-by: Shawn Guo Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/clock/imx35-clock.txt | 1 + drivers/clk/imx/clk-imx35.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/imx35-clock.txt b/Documentation/devicetree/bindings/clock/imx35-clock.txt index a70356452a82..f49783213c56 100644 --- a/Documentation/devicetree/bindings/clock/imx35-clock.txt +++ b/Documentation/devicetree/bindings/clock/imx35-clock.txt @@ -94,6 +94,7 @@ clocks and IDs. csi_sel 79 iim_gate 80 gpu2d_gate 81 + ckli_gate 82 Examples: diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c index a71d24cb4c06..b0978d3b83e2 100644 --- a/drivers/clk/imx/clk-imx35.c +++ b/drivers/clk/imx/clk-imx35.c @@ -66,7 +66,7 @@ static const char *std_sel[] = {"ppll", "arm"}; static const char *ipg_per_sel[] = {"ahb_per_div", "arm_per_div"}; enum mx35_clks { - ckih, ckil, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, ipg, + ckih, mpll, ppll, mpll_075, arm, hsp, hsp_div, hsp_sel, ahb, ipg, arm_per_div, ahb_per_div, ipg_per, uart_sel, uart_div, esdhc_sel, esdhc1_div, esdhc2_div, esdhc3_div, spdif_sel, spdif_div_pre, spdif_div_post, ssi_sel, ssi1_div_pre, ssi1_div_post, ssi2_div_pre, @@ -79,7 +79,7 @@ enum mx35_clks { rtc_gate, rtic_gate, scc_gate, sdma_gate, spba_gate, spdif_gate, ssi1_gate, ssi2_gate, uart1_gate, uart2_gate, uart3_gate, usbotg_gate, wdog_gate, max_gate, admux_gate, csi_gate, csi_div, csi_sel, iim_gate, - gpu2d_gate, clk_max + gpu2d_gate, ckil, clk_max }; static struct clk *clk[clk_max]; From cc2e71f0b16f309b11b0fcdfd934ea2e4d0db13b Mon Sep 17 00:00:00 2001 From: "Vittorio Gambaletta (VittGam)" Date: Mon, 11 Apr 2016 04:48:54 +0200 Subject: [PATCH 266/656] ath9k: Add a module parameter to invert LED polarity. commit cd84042ce9040ad038e958bc67a46fcfc015c736 upstream. The LED can be active high instead of active low on some hardware. Add the led_active_high module parameter. It defaults to -1 to obey platform data as before. Setting the parameter to 1 or 0 will force the LED respectively active high or active low. Cc: Cc: Cc: Signed-off-by: Vittorio Gambaletta Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/init.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 2e2b92ba96b8..1bdeacf7b257 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -49,6 +49,10 @@ int ath9k_led_blink; module_param_named(blink, ath9k_led_blink, int, 0444); MODULE_PARM_DESC(blink, "Enable LED blink on activity"); +static int ath9k_led_active_high = -1; +module_param_named(led_active_high, ath9k_led_active_high, int, 0444); +MODULE_PARM_DESC(led_active_high, "Invert LED polarity"); + static int ath9k_btcoex_enable; module_param_named(btcoex_enable, ath9k_btcoex_enable, int, 0444); MODULE_PARM_DESC(btcoex_enable, "Enable wifi-BT coexistence"); @@ -600,6 +604,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, if (ret) return ret; + if (ath9k_led_active_high != -1) + ah->config.led_active_high = ath9k_led_active_high == 1; + /* * Enable WLAN/BT RX Antenna diversity only when: * From 354c6bccfc258851f7a6c9287f4e98a832feef22 Mon Sep 17 00:00:00 2001 From: "Vittorio Gambaletta (VittGam)" Date: Mon, 11 Apr 2016 04:48:55 +0200 Subject: [PATCH 267/656] ath9k: Fix LED polarity for some Mini PCI AR9220 MB92 cards. commit 0f9edcdd88a993914fa1d1dc369b35dc503979db upstream. The Wistron DNMA-92 and Compex WLM200NX have inverted LED polarity (active high instead of active low). The same PCI Subsystem ID is used by both cards, which are based on the same Atheros MB92 design. Cc: Cc: Cc: Signed-off-by: Vittorio Gambaletta Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath9k/pci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index e6fef1be9977..7cdaf40c3057 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -28,6 +28,16 @@ static const struct pci_device_id ath_pci_id_table[] = { { PCI_VDEVICE(ATHEROS, 0x0024) }, /* PCI-E */ { PCI_VDEVICE(ATHEROS, 0x0027) }, /* PCI */ { PCI_VDEVICE(ATHEROS, 0x0029) }, /* PCI */ + +#ifdef CONFIG_ATH9K_PCOEM + /* Mini PCI AR9220 MB92 cards: Compex WLM200NX, Wistron DNMA-92 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0029, + PCI_VENDOR_ID_ATHEROS, + 0x2096), + .driver_data = ATH9K_PCI_LED_ACT_HI }, +#endif + { PCI_VDEVICE(ATHEROS, 0x002A) }, /* PCI-E */ #ifdef CONFIG_ATH9K_PCOEM From b89721d70e1cce49e4f503ad9df765a02fb60cd8 Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Fri, 11 Mar 2016 11:46:39 +0530 Subject: [PATCH 268/656] ath10k: fix debugfs pktlog_filter write commit 9ddc486aa09a3413a6c492fcf160ce61bfccb7b1 upstream. It is observed that, we are disabling the packet log if we write same value to the pktlog_filter for the second time. Always enable pktlogs on non zero filter. Fixes: 90174455ae05 ("ath10k: add support to configure pktlog filter") Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath10k/debug.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 6cc1aa3449c8..1a88a24ffeac 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1986,7 +1986,12 @@ static ssize_t ath10k_write_pktlog_filter(struct file *file, goto out; } - if (filter && (filter != ar->debug.pktlog_filter)) { + if (filter == ar->debug.pktlog_filter) { + ret = count; + goto out; + } + + if (filter) { ret = ath10k_wmi_pdev_pktlog_enable(ar, filter); if (ret) { ath10k_warn(ar, "failed to enable pktlog filter %x: %d\n", From 86834a9133bb79a5dc078539385984a5afae014f Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Wed, 2 Mar 2016 20:13:52 +0530 Subject: [PATCH 269/656] ath10k: fix firmware assert in monitor mode commit 8a75fc54745fd3ce9062ab1cc6429a9da9ac2a68 upstream. commit 166de3f1895d ("ath10k: remove supported chain mask") had revealed an issue on monitor mode. Configuring NSS upon monitor interface creation is causing target assert in all qca9888x and qca6174 firmware. Firmware assert issue can be reproduced by below sequence even after reverting commit 166de3f1895d ("ath10k: remove supported chain mask"). ip link set wlan0 down iw wlan0 set type monitor iw phy0 set antenna 7 ip link set wlan0 up This issue is originally reported on qca9888 with 10.1 firmware. Fixes: 5572a95b4b ("ath10k: apply chainmask settings to vdev on creation") Reported-by: Janusz Dziedzic Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath10k/mac.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 95a55405ebf0..a5ba9cfd88b2 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -4456,7 +4456,10 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, goto err_vdev_delete; } - if (ar->cfg_tx_chainmask) { + /* Configuring number of spatial stream for monitor interface is causing + * target assert in qca9888 and qca6174. + */ + if (ar->cfg_tx_chainmask && (vif->type != NL80211_IFTYPE_MONITOR)) { u16 nss = get_nss_from_chainmask(ar->cfg_tx_chainmask); vdev_param = ar->wmi.vdev_param->nss; From 117d1b454bf5b8e66bc4ebfc50437977a52b0ce5 Mon Sep 17 00:00:00 2001 From: Rajkumar Manoharan Date: Thu, 7 Apr 2016 12:11:54 +0530 Subject: [PATCH 270/656] ath10k: fix rx_channel during hw reconfigure commit 1ce8c1484e80010a6e4b9611c65668ff77556f45 upstream. Upon firmware assert, restart work will be triggered so that mac80211 will reconfigure the driver. An issue is reported that after restart work, survey dump data do not contain in-use (SURVEY_INFO_IN_USE) info for operating channel. During reconfigure, since mac80211 already has valid channel context for given radio, channel context iteration return num_chanctx > 0. Hence rx_channel is always NULL. Fix this by assigning channel context to rx_channel when driver restart is in progress. Signed-off-by: Rajkumar Manoharan Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath10k/mac.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index a5ba9cfd88b2..1e1bef349487 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -6419,7 +6419,13 @@ ath10k_mac_update_rx_channel(struct ath10k *ar, def = &vifs[0].new_ctx->def; ar->rx_channel = def->chan; - } else if (ctx && ath10k_mac_num_chanctxs(ar) == 0) { + } else if ((ctx && ath10k_mac_num_chanctxs(ar) == 0) || + (ctx && (ar->state == ATH10K_STATE_RESTARTED))) { + /* During driver restart due to firmware assert, since mac80211 + * already has valid channel context for given radio, channel + * context iteration return num_chanctx > 0. So fix rx_channel + * when restart is in progress. + */ ar->rx_channel = ctx->def.chan; } else { ar->rx_channel = NULL; From dcd9fc92f1453d1f61279f2352d093bd8ce324cd Mon Sep 17 00:00:00 2001 From: Anilkumar Kolli Date: Tue, 26 Apr 2016 21:35:48 +0530 Subject: [PATCH 271/656] ath10k: fix kernel panic, move arvifs list head init before htt init commit 4ad24a9d83bd4bf0a85e95bf144e18d3fda4fbf1 upstream. It is observed that while loading and unloading ath10k modules in an infinite loop, before ath10k_core_start() completion HTT rx frames are received, while processing these frames, dereferencing the arvifs list code is getting hit before initilizing the arvifs list, causing a kernel panic. This patch initilizes the arvifs list before initilizing htt. Fixes the below issue: [] (ath10k_htt_rx_pktlog_completion_handler+0x278/0xd08 [ath10k_core]) [] (ath10k_htt_rx_pktlog_completion_handler [ath10k_core]) [] (ath10k_htt_txrx_compl_task+0x5f4/0xeb0 [ath10k_core]) [] (ath10k_htt_txrx_compl_task [ath10k_core]) [] (tasklet_action+0x8c/0xec) [] (tasklet_action) [] (__do_softirq+0xf8/0x228) [] (__do_softirq) [] (run_ksoftirqd+0x30/0x90) Code: e5954ad8 e2899008 e1540009 0a00000d (e5943008) ---[ end trace 71de5c2e011dbf56 ]--- Kernel panic - not syncing: Fatal exception in interrupt Fixes: 500ff9f9389d ("ath10k: implement chanctx API") Signed-off-by: Anilkumar Kolli Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath10k/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 0947cc271e69..531de256d58d 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1681,6 +1681,10 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) goto err_hif_stop; } + ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1; + + INIT_LIST_HEAD(&ar->arvifs); + /* we don't care about HTT in UTF mode */ if (mode == ATH10K_FIRMWARE_MODE_NORMAL) { status = ath10k_htt_setup(&ar->htt); @@ -1694,10 +1698,6 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) if (status) goto err_hif_stop; - ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1; - - INIT_LIST_HEAD(&ar->arvifs); - return 0; err_hif_stop: From ab1619f4f94b908bfa33ef7eb505c50955741eb4 Mon Sep 17 00:00:00 2001 From: Joseph Salisbury Date: Mon, 14 Mar 2016 14:51:48 -0400 Subject: [PATCH 272/656] ath5k: Change led pin configuration for compaq c700 laptop commit 7b9bc799a445aea95f64f15e0083cb19b5789abe upstream. BugLink: http://bugs.launchpad.net/bugs/972604 Commit 09c9bae26b0d3c9472cb6ae45010460a2cee8b8d ("ath5k: add led pin configuration for compaq c700 laptop") added a pin configuration for the Compaq c700 laptop. However, the polarity of the led pin is reversed. It should be red for wifi off and blue for wifi on, but it is the opposite. This bug was reported in the following bug report: http://pad.lv/972604 Fixes: 09c9bae26b0d3c9472cb6ae45010460a2cee8b8d ("ath5k: add led pin configuration for compaq c700 laptop") Signed-off-by: Joseph Salisbury Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/ath/ath5k/led.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index 803030fd17d3..6a2a16856763 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -77,7 +77,7 @@ static const struct pci_device_id ath5k_led_devices[] = { /* HP Compaq CQ60-206US (ddreggors@jumptv.com) */ { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137a), ATH_LED(3, 1) }, /* HP Compaq C700 (nitrousnrg@gmail.com) */ - { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 1) }, + { ATH_SDEVICE(PCI_VENDOR_ID_HP, 0x0137b), ATH_LED(3, 0) }, /* LiteOn AR5BXB63 (magooz@salug.it) */ { ATH_SDEVICE(PCI_VENDOR_ID_ATHEROS, 0x3067), ATH_LED(3, 0) }, /* IBM-specific AR5212 (all others) */ From c56cebbe83dde1c93599eb29d85d5c889d7a00cf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Mar 2016 09:07:13 +0900 Subject: [PATCH 273/656] hwrng: exynos - Fix unbalanced PM runtime put on timeout error path commit f1925d78d7b710a1179828d53e918295f5f5d222 upstream. In case of timeout during read operation, the exit path lacked PM runtime put. This could lead to unbalanced runtime PM usage counter thus leaving the device in an active state. Fixes: d7fd6075a205 ("hwrng: exynos - Add timeout for waiting on init done") Signed-off-by: Krzysztof Kozlowski Signed-off-by: Herbert Xu Signed-off-by: Greg Kroah-Hartman --- drivers/char/hw_random/exynos-rng.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c index 30cf4623184f..aa30af5f0f2b 100644 --- a/drivers/char/hw_random/exynos-rng.c +++ b/drivers/char/hw_random/exynos-rng.c @@ -89,6 +89,7 @@ static int exynos_read(struct hwrng *rng, void *buf, struct exynos_rng, rng); u32 *data = buf; int retry = 100; + int ret = 4; pm_runtime_get_sync(exynos_rng->dev); @@ -97,17 +98,20 @@ static int exynos_read(struct hwrng *rng, void *buf, while (!(exynos_rng_readl(exynos_rng, EXYNOS_PRNG_STATUS_OFFSET) & PRNG_DONE) && --retry) cpu_relax(); - if (!retry) - return -ETIMEDOUT; + if (!retry) { + ret = -ETIMEDOUT; + goto out; + } exynos_rng_writel(exynos_rng, PRNG_DONE, EXYNOS_PRNG_STATUS_OFFSET); *data = exynos_rng_readl(exynos_rng, EXYNOS_PRNG_OUT1_OFFSET); +out: pm_runtime_mark_last_busy(exynos_rng->dev); pm_runtime_put_sync_autosuspend(exynos_rng->dev); - return 4; + return ret; } static int exynos_rng_probe(struct platform_device *pdev) From 5618e882939a08f5733743ec5c296fa5254dc110 Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 16 Mar 2016 13:33:34 -0500 Subject: [PATCH 274/656] rtlwifi: rtl8723be: Add antenna select module parameter commit c18d8f5095715c56bb3cd9cba64242542632054b upstream. A number of new laptops have been delivered with only a single antenna. In principle, this is OK; however, a problem arises when the on-board EEPROM is programmed to use the other antenna connection. The option of opening the computer and moving the connector is not always possible as it will void the warranty in some cases. In addition, this solution breaks the Windows driver when the box dual boots Linux and Windows. A fix involving a new module parameter has been developed. This commit adds the new parameter and implements the changes needed for the driver. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c | 5 +++++ drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c | 3 +++ drivers/net/wireless/realtek/rtlwifi/wifi.h | 3 +++ 3 files changed, 11 insertions(+) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c index c983d2fe147f..5a3df9198ddf 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/hw.c @@ -2684,6 +2684,7 @@ void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, bool auto_load_fail, u8 *hwinfo) { struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mod_params *mod_params = rtlpriv->cfg->mod_params; u8 value; u32 tmpu_32; @@ -2702,6 +2703,10 @@ void rtl8723be_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, rtlpriv->btcoexist.btc_info.ant_num = ANT_X2; } + /* override ant_num / ant_path */ + if (mod_params->ant_sel) + rtlpriv->btcoexist.btc_info.ant_num = + (mod_params->ant_sel == 1 ? ANT_X2 : ANT_X1); } void rtl8723be_bt_reg_init(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c index a78eaeda0008..2101793438ed 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8723be/sw.c @@ -273,6 +273,7 @@ static struct rtl_mod_params rtl8723be_mod_params = { .msi_support = false, .disable_watchdog = false, .debug = DBG_EMERG, + .ant_sel = 0, }; static struct rtl_hal_cfg rtl8723be_hal_cfg = { @@ -394,6 +395,7 @@ module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444); module_param_named(msi, rtl8723be_mod_params.msi_support, bool, 0444); module_param_named(disable_watchdog, rtl8723be_mod_params.disable_watchdog, bool, 0444); +module_param_named(ant_sel, rtl8723be_mod_params.ant_sel, int, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); @@ -402,6 +404,7 @@ MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n"); +MODULE_PARM_DESC(ant_sel, "Set to 1 or 2 to force antenna number (default 0)\n"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); diff --git a/drivers/net/wireless/realtek/rtlwifi/wifi.h b/drivers/net/wireless/realtek/rtlwifi/wifi.h index 4544752a2ba8..b6faf624480e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/wifi.h +++ b/drivers/net/wireless/realtek/rtlwifi/wifi.h @@ -2252,6 +2252,9 @@ struct rtl_mod_params { /* default 0: 1 means do not disable interrupts */ bool int_clear; + + /* select antenna */ + int ant_sel; }; struct rtl_hal_usbint_cfg { From ad4d53046e1d131de921176d49cf805744089a0c Mon Sep 17 00:00:00 2001 From: Larry Finger Date: Wed, 16 Mar 2016 13:33:35 -0500 Subject: [PATCH 275/656] rtlwifi: btcoexist: Implement antenna selection commit baa1702290953295e421f0f433e2b1ff4815827c upstream. The previous patch added an option to rtl8723be to manually select the antenna for those cases when only a single antenna is present, and the on-board EEPROM is incorrectly programmed. This patch implements the necessary changes in the Bluetooth coexistence driver. Signed-off-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- .../rtlwifi/btcoexist/halbtc8723b2ant.c | 9 +++++-- .../realtek/rtlwifi/btcoexist/halbtcoutsrc.c | 27 ++++++++++++++++++- .../realtek/rtlwifi/btcoexist/halbtcoutsrc.h | 2 +- .../realtek/rtlwifi/btcoexist/rtl_btc.c | 5 +++- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c index f2b9d11adc9e..e85f1652ce55 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtc8723b2ant.c @@ -1203,7 +1203,6 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, /* Force GNT_BT to low */ btcoexist->btc_write_1byte_bitmask(btcoexist, 0x765, 0x18, 0x0); - btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) { /* tell firmware "no antenna inverse" */ @@ -1211,19 +1210,25 @@ static void btc8723b2ant_set_ant_path(struct btc_coexist *btcoexist, h2c_parameter[1] = 1; /* ext switch type */ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); } else { /* tell firmware "antenna inverse" */ h2c_parameter[0] = 1; h2c_parameter[1] = 1; /* ext switch type */ btcoexist->btc_fill_h2c(btcoexist, 0x65, 2, h2c_parameter); + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); } } /* ext switch setting */ if (use_ext_switch) { /* fixed internal switch S1->WiFi, S0->BT */ - btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); + if (board_info->btdm_ant_pos == BTC_ANTENNA_AT_MAIN_PORT) + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x0); + else + btcoexist->btc_write_2byte(btcoexist, 0x948, 0x280); + switch (antpos_type) { case BTC_ANT_WIFI_AT_MAIN: /* ext switch main at wifi */ diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c index b2791c893417..babd1490f20c 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.c @@ -965,13 +965,38 @@ void exhalbtc_set_chip_type(u8 chip_type) } } -void exhalbtc_set_ant_num(u8 type, u8 ant_num) +void exhalbtc_set_ant_num(struct rtl_priv *rtlpriv, u8 type, u8 ant_num) { if (BT_COEX_ANT_TYPE_PG == type) { gl_bt_coexist.board_info.pg_ant_num = ant_num; gl_bt_coexist.board_info.btdm_ant_num = ant_num; + /* The antenna position: + * Main (default) or Aux for pgAntNum=2 && btdmAntNum =1. + * The antenna position should be determined by + * auto-detect mechanism. + * The following is assumed to main, + * and those must be modified + * if y auto-detect mechanism is ready + */ + if ((gl_bt_coexist.board_info.pg_ant_num == 2) && + (gl_bt_coexist.board_info.btdm_ant_num == 1)) + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_MAIN_PORT; + else + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_MAIN_PORT; } else if (BT_COEX_ANT_TYPE_ANTDIV == type) { gl_bt_coexist.board_info.btdm_ant_num = ant_num; + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_MAIN_PORT; + } else if (type == BT_COEX_ANT_TYPE_DETECTED) { + gl_bt_coexist.board_info.btdm_ant_num = ant_num; + if (rtlpriv->cfg->mod_params->ant_sel == 1) + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_AUX_PORT; + else + gl_bt_coexist.board_info.btdm_ant_pos = + BTC_ANTENNA_AT_MAIN_PORT; } } diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h index 0a903ea179ef..f41ca57dd8a7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/halbtcoutsrc.h @@ -535,7 +535,7 @@ void exhalbtc_set_bt_patch_version(u16 bt_hci_version, u16 bt_patch_version); void exhalbtc_update_min_bt_rssi(char bt_rssi); void exhalbtc_set_bt_exist(bool bt_exist); void exhalbtc_set_chip_type(u8 chip_type); -void exhalbtc_set_ant_num(u8 type, u8 ant_num); +void exhalbtc_set_ant_num(struct rtl_priv *rtlpriv, u8 type, u8 ant_num); void exhalbtc_display_bt_coex_info(struct btc_coexist *btcoexist); void exhalbtc_signal_compensation(struct btc_coexist *btcoexist, u8 *rssi_wifi, u8 *rssi_bt); diff --git a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c index b9b0cb7af8ea..d3fd9211b3a4 100644 --- a/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c +++ b/drivers/net/wireless/realtek/rtlwifi/btcoexist/rtl_btc.c @@ -72,7 +72,10 @@ void rtl_btc_init_hal_vars(struct rtl_priv *rtlpriv) __func__, bt_type); exhalbtc_set_chip_type(bt_type); - exhalbtc_set_ant_num(BT_COEX_ANT_TYPE_PG, ant_num); + if (rtlpriv->cfg->mod_params->ant_sel == 1) + exhalbtc_set_ant_num(rtlpriv, BT_COEX_ANT_TYPE_DETECTED, 1); + else + exhalbtc_set_ant_num(rtlpriv, BT_COEX_ANT_TYPE_PG, ant_num); } void rtl_btc_init_hw_config(struct rtl_priv *rtlpriv) From d896d805dbf5ebf6814c4e84d5321a8169304b19 Mon Sep 17 00:00:00 2001 From: wang yanqing Date: Tue, 3 May 2016 00:38:36 +0800 Subject: [PATCH 276/656] rtlwifi: Fix logic error in enter/exit power-save mode commit 873ffe154ae074c46ed2d72dbd9a2a99f06f55b4 upstream. In commit a269913c52ad ("rtlwifi: Rework rtl_lps_leave() and rtl_lps_enter() to use work queue"), the tests for enter/exit power-save mode were inverted. With this change applied, the wifi connection becomes much more stable. Fixes: a269913c52ad ("rtlwifi: Rework rtl_lps_leave() and rtl_lps_enter() to use work queue") Signed-off-by: Wang YanQing Acked-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtlwifi/base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index 0517a4f2d3f2..7a40d8dffa36 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -1660,9 +1660,9 @@ void rtl_watchdog_wq_callback(void *data) if (((rtlpriv->link_info.num_rx_inperiod + rtlpriv->link_info.num_tx_inperiod) > 8) || (rtlpriv->link_info.num_rx_inperiod > 2)) - rtl_lps_enter(hw); - else rtl_lps_leave(hw); + else + rtl_lps_enter(hw); } rtlpriv->link_info.num_rx_inperiod = 0; From 04a6bcca2f76f7aa5b8749644e3a2947a3cf040d Mon Sep 17 00:00:00 2001 From: wang yanqing Date: Sat, 7 May 2016 00:33:53 +0800 Subject: [PATCH 277/656] rtlwifi: pci: use dev_kfree_skb_irq instead of kfree_skb in rtl_pci_reset_trx_ring commit cf968937d27751296920e6b82ffa89735e3a0023 upstream. We can't use kfree_skb in irq disable context, because spin_lock_irqsave make sure we are always in irq disable context, use dev_kfree_skb_irq instead of kfree_skb is better than dev_kfree_skb_any. This patch fix below kernel warning: [ 7612.095528] ------------[ cut here ]------------ [ 7612.095546] WARNING: CPU: 3 PID: 4460 at kernel/softirq.c:150 __local_bh_enable_ip+0x58/0x80() [ 7612.095550] Modules linked in: rtl8723be x86_pkg_temp_thermal btcoexist rtl_pci rtlwifi rtl8723_common [ 7612.095567] CPU: 3 PID: 4460 Comm: ifconfig Tainted: G W 4.4.0+ #4 [ 7612.095570] Hardware name: LENOVO 20DFA04FCD/20DFA04FCD, BIOS J5ET48WW (1.19 ) 08/27/2015 [ 7612.095574] 00000000 00000000 da37fc70 c12ce7c5 00000000 da37fca0 c104cc59 c19d4454 [ 7612.095584] 00000003 0000116c c19d4784 00000096 c10508a8 c10508a8 00000200 c1b42400 [ 7612.095594] f29be780 da37fcb0 c104ccad 00000009 00000000 da37fcbc c10508a8 f21f08b8 [ 7612.095604] Call Trace: [ 7612.095614] [] dump_stack+0x41/0x5c [ 7612.095620] [] warn_slowpath_common+0x89/0xc0 [ 7612.095628] [] ? __local_bh_enable_ip+0x58/0x80 [ 7612.095634] [] ? __local_bh_enable_ip+0x58/0x80 [ 7612.095640] [] warn_slowpath_null+0x1d/0x20 [ 7612.095646] [] __local_bh_enable_ip+0x58/0x80 [ 7612.095653] [] destroy_conntrack+0x64/0xa0 [ 7612.095660] [] nf_conntrack_destroy+0xf/0x20 [ 7612.095665] [] skb_release_head_state+0x55/0xa0 [ 7612.095670] [] skb_release_all+0xb/0x20 [ 7612.095674] [] __kfree_skb+0xb/0x60 [ 7612.095679] [] kfree_skb+0x30/0x70 [ 7612.095686] [] ? rtl_pci_reset_trx_ring+0x22d/0x370 [rtl_pci] [ 7612.095692] [] rtl_pci_reset_trx_ring+0x22d/0x370 [rtl_pci] [ 7612.095698] [] rtl_pci_start+0x19/0x190 [rtl_pci] [ 7612.095705] [] rtl_op_start+0x56/0x90 [rtlwifi] [ 7612.095712] [] drv_start+0x36/0xc0 [ 7612.095717] [] ieee80211_do_open+0x2d3/0x890 [ 7612.095725] [] ? call_netdevice_notifiers_info+0x2e/0x60 [ 7612.095730] [] ieee80211_open+0x4d/0x50 [ 7612.095736] [] __dev_open+0xa3/0x130 [ 7612.095742] [] ? _raw_spin_unlock_bh+0x13/0x20 [ 7612.095748] [] __dev_change_flags+0x89/0x140 [ 7612.095753] [] ? selinux_capable+0xd/0x10 [ 7612.095759] [] dev_change_flags+0x29/0x60 [ 7612.095765] [] devinet_ioctl+0x553/0x670 [ 7612.095772] [] ? _copy_to_user+0x28/0x40 [ 7612.095777] [] inet_ioctl+0x85/0xb0 [ 7612.095783] [] sock_ioctl+0x67/0x260 [ 7612.095788] [] ? sock_fasync+0x80/0x80 [ 7612.095795] [] do_vfs_ioctl+0x6b/0x550 [ 7612.095800] [] ? selinux_file_ioctl+0x102/0x1e0 [ 7612.095807] [] ? timekeeping_suspend+0x294/0x320 [ 7612.095813] [] ? __hrtimer_run_queues+0x14a/0x210 [ 7612.095820] [] ? security_file_ioctl+0x34/0x50 [ 7612.095827] [] SyS_ioctl+0x70/0x80 [ 7612.095832] [] do_fast_syscall_32+0x84/0x120 [ 7612.095839] [] sysenter_past_esp+0x36/0x55 [ 7612.095844] ---[ end trace 97e9c637a20e8348 ]--- Signed-off-by: Wang YanQing Acked-by: Larry Finger Signed-off-by: Kalle Valo Signed-off-by: Greg Kroah-Hartman --- drivers/net/wireless/realtek/rtlwifi/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 7f471bff435c..5b4048041147 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -1573,7 +1573,7 @@ int rtl_pci_reset_trx_ring(struct ieee80211_hw *hw) true, HW_DESC_TXBUFF_ADDR), skb->len, PCI_DMA_TODEVICE); - kfree_skb(skb); + dev_kfree_skb_irq(skb); ring->idx = (ring->idx + 1) % ring->entries; } ring->idx = 0; From 9a5ca989b54975b40915bc6fa5c909cf11f90c62 Mon Sep 17 00:00:00 2001 From: Raghava Aditya Renukunta Date: Mon, 25 Apr 2016 23:31:26 -0700 Subject: [PATCH 278/656] aacraid: Relinquish CPU during timeout wait commit 07beca2be24cc710461c0b131832524c9ee08910 upstream. aac_fib_send has a special function case for initial commands during driver initialization using wait < 0(pseudo sync mode). In this case, the command does not sleep but rather spins checking for timeout.This loop is calls cpu_relax() in an attempt to allow other processes/threads to use the CPU, but this function does not relinquish the CPU and so the command will hog the processor. This was observed in a KDUMP "crashkernel" and that prevented the "command thread" (which is responsible for completing the command from being timed out) from starting because it could not get the CPU. Fixed by replacing "cpu_relax()" call with "schedule()" Signed-off-by: Raghava Aditya Renukunta Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/aacraid/commsup.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index 4cbf54928640..b324a2eb8626 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -611,10 +611,10 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size, } return -EFAULT; } - /* We used to udelay() here but that absorbed - * a CPU when a timeout occured. Not very - * useful. */ - cpu_relax(); + /* + * Allow other processes / CPUS to use core + */ + schedule(); } } else if (down_interruptible(&fibptr->event_wait)) { /* Do nothing ... satisfy From 0f0a9a1866754cc025030789bb4928d8b50cc5e5 Mon Sep 17 00:00:00 2001 From: Raghava Aditya Renukunta Date: Mon, 25 Apr 2016 23:31:57 -0700 Subject: [PATCH 279/656] aacraid: Fix for aac_command_thread hang commit fc4bf75ea300a5e62a2419f89dd0e22189dd7ab7 upstream. Typically under error conditions, it is possible for aac_command_thread() to miss the wakeup from kthread_stop() and go back to sleep, causing it to hang aac_shutdown. In the observed scenario, the adapter is not functioning correctly and so aac_fib_send() never completes (or time-outs depending on how it was called). Shortly after aac_command_thread() starts it performs aac_fib_send(SendHostTime) which hangs. When aac_probe_one /aac_get_adapter_info send time outs, kthread_stop is called which breaks the command thread out of it's hang. The code will still go back to sleep in schedule_timeout() without checking kthread_should_stop() so it causes aac_probe_one to hang until the schedule_timeout() which is 30 minutes. Fixed by: Adding another kthread_should_stop() before schedule_timeout() Signed-off-by: Raghava Aditya Renukunta Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/aacraid/commsup.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c index b324a2eb8626..8c758c36fc70 100644 --- a/drivers/scsi/aacraid/commsup.c +++ b/drivers/scsi/aacraid/commsup.c @@ -1970,6 +1970,10 @@ int aac_command_thread(void *data) if (difference <= 0) difference = 1; set_current_state(TASK_INTERRUPTIBLE); + + if (kthread_should_stop()) + break; + schedule_timeout(difference); if (kthread_should_stop()) From 9a11bd2d14ad54e92170cdc9c2e2f278305929c1 Mon Sep 17 00:00:00 2001 From: Raghava Aditya Renukunta Date: Mon, 25 Apr 2016 23:32:37 -0700 Subject: [PATCH 280/656] aacraid: Fix for KDUMP driver hang commit 78cbccd3bd683c295a44af8050797dc4a41376ff upstream. When KDUMP is triggered the driver first talks to the firmware in INTX mode, but the adapter firmware is still in MSIX mode. Therefore the first driver command hangs since the driver is waiting for an INTX response and firmware gives a MSIX response. If when the OS is installed on a RAID drive created by the adapter KDUMP will hang since the driver does not receive a response in sync mode. Fixed by: Change the firmware to INTX mode if it is in MSIX mode before sending the first sync command. Signed-off-by: Raghava Aditya Renukunta Reviewed-by: Johannes Thumshirn Signed-off-by: Martin K. Petersen Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/aacraid/aacraid.h | 1 + drivers/scsi/aacraid/comminit.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h index d044f3f273be..467773033a20 100644 --- a/drivers/scsi/aacraid/aacraid.h +++ b/drivers/scsi/aacraid/aacraid.h @@ -29,6 +29,7 @@ enum { #define AAC_INT_MODE_MSI (1<<1) #define AAC_INT_MODE_AIF (1<<2) #define AAC_INT_MODE_SYNC (1<<3) +#define AAC_INT_MODE_MSIX (1<<16) #define AAC_INT_ENABLE_TYPE1_INTX 0xfffffffb #define AAC_INT_ENABLE_TYPE1_MSIX 0xfffffffa diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c index 0e954e37f0b5..0d351cd3191b 100644 --- a/drivers/scsi/aacraid/comminit.c +++ b/drivers/scsi/aacraid/comminit.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,20 @@ struct aac_common aac_config = { .irq_mod = 1 }; +static inline int aac_is_msix_mode(struct aac_dev *dev) +{ + u32 status; + + status = src_readl(dev, MUnit.OMR); + return (status & AAC_INT_MODE_MSIX); +} + +static inline void aac_change_to_intx(struct aac_dev *dev) +{ + aac_src_access_devreg(dev, AAC_DISABLE_MSIX); + aac_src_access_devreg(dev, AAC_ENABLE_INTX); +} + static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long commsize, unsigned long commalign) { unsigned char *base; @@ -425,6 +440,15 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev) dev->comm_interface = AAC_COMM_PRODUCER; dev->raw_io_interface = dev->raw_io_64 = 0; + + /* + * Enable INTX mode, if not done already Enabled + */ + if (aac_is_msix_mode(dev)) { + aac_change_to_intx(dev); + dev_info(&dev->pdev->dev, "Changed firmware to INTX mode"); + } + if ((!aac_adapter_sync_cmd(dev, GET_ADAPTER_PROPERTIES, 0, 0, 0, 0, 0, 0, status+0, status+1, status+2, status+3, NULL)) && From b8562066d4819ee23d3f8da2fa5e9160bf854835 Mon Sep 17 00:00:00 2001 From: Akshay Bhat Date: Mon, 18 Apr 2016 15:47:53 -0400 Subject: [PATCH 281/656] hwmon: (ads7828) Enable internal reference commit 7a18afe8097731b8ffb6cb5b2b3b418ded77c105 upstream. On ads7828 the internal reference defaults to off upon power up. When using internal reference, it needs to be turned on and the voltage needs to settle before normal conversion cycle can be started. Hence perform a dummy read in the probe to enable the internal reference allowing the voltage to settle before performing a normal read. Without this fix, the first read from the ADC when using internal reference always returns incorrect data. Signed-off-by: Akshay Bhat Signed-off-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/ads7828.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 6c99ee7bafa3..ee396ff167d9 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -120,6 +120,7 @@ static int ads7828_probe(struct i2c_client *client, unsigned int vref_mv = ADS7828_INT_VREF_MV; bool diff_input = false; bool ext_vref = false; + unsigned int regval; data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL); if (!data) @@ -154,6 +155,15 @@ static int ads7828_probe(struct i2c_client *client, if (!diff_input) data->cmd_byte |= ADS7828_CMD_SD_SE; + /* + * Datasheet specifies internal reference voltage is disabled by + * default. The internal reference voltage needs to be enabled and + * voltage needs to settle before getting valid ADC data. So perform a + * dummy read to enable the internal reference voltage. + */ + if (!ext_vref) + regmap_read(data->regmap, data->cmd_byte, ®val); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, ads7828_groups); From d306f756f8c63ea820af01ecf27e089f3118a00a Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Mon, 18 Apr 2016 15:14:56 +0300 Subject: [PATCH 282/656] mfd: intel-lpss: Save register context on suspend commit 41a3da2b8e1639d983192e3650670df4ecc94cf7 upstream. All configurations are lost and the registers will have default values when the hardware is suspended and resumed, so saving the private register space context on suspend, and restoring it on resume. Fixes: 4b45efe85263 (mfd: Add support for Intel Sunrisepoint LPSS devices) Signed-off-by: Heikki Krogerus Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/intel-lpss.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c index 68aa31ae553a..88e80ec772f6 100644 --- a/drivers/mfd/intel-lpss.c +++ b/drivers/mfd/intel-lpss.c @@ -33,6 +33,7 @@ #define LPSS_DEV_SIZE 0x200 #define LPSS_PRIV_OFFSET 0x200 #define LPSS_PRIV_SIZE 0x100 +#define LPSS_PRIV_REG_COUNT (LPSS_PRIV_SIZE / 4) #define LPSS_IDMA64_OFFSET 0x800 #define LPSS_IDMA64_SIZE 0x800 @@ -75,6 +76,7 @@ struct intel_lpss { const struct mfd_cell *cell; struct device *dev; void __iomem *priv; + u32 priv_ctx[LPSS_PRIV_REG_COUNT]; int devid; u32 caps; u32 active_ltr; @@ -485,6 +487,16 @@ EXPORT_SYMBOL_GPL(intel_lpss_prepare); int intel_lpss_suspend(struct device *dev) { + struct intel_lpss *lpss = dev_get_drvdata(dev); + unsigned int i; + + /* Save device context */ + for (i = 0; i < LPSS_PRIV_REG_COUNT; i++) + lpss->priv_ctx[i] = readl(lpss->priv + i * 4); + + /* Put the device into reset state */ + writel(0, lpss->priv + LPSS_PRIV_RESETS); + return 0; } EXPORT_SYMBOL_GPL(intel_lpss_suspend); @@ -492,8 +504,13 @@ EXPORT_SYMBOL_GPL(intel_lpss_suspend); int intel_lpss_resume(struct device *dev) { struct intel_lpss *lpss = dev_get_drvdata(dev); + unsigned int i; - intel_lpss_init_dev(lpss); + intel_lpss_deassert_reset(lpss); + + /* Restore device context */ + for (i = 0; i < LPSS_PRIV_REG_COUNT; i++) + writel(lpss->priv_ctx[i], lpss->priv + i * 4); return 0; } From 4072a4bcd24f2b07eb1b6dfc2a2e7fac4c501114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 22 Apr 2016 22:38:55 +0300 Subject: [PATCH 283/656] mfd: intel_soc_pmic_core: Terminate panel control GPIO lookup table correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 36e6d081cfb2cb64e6d8e5121cffb1e62f894d36 upstream. GPIO lookup tables are supposed to be zero terminated. Let's do that and avoid accidentally walking off the end. Fixes: 61dd2ca2d44e ("mfd: intel_soc_pmic_core: Add lookup table for Panel Control as GPIO signal") Signed-off-by: Ville Syrjälä Acked-by: Linus Walleij Acked-by: Daniel Vetter Signed-off-by: Lee Jones Signed-off-by: Greg Kroah-Hartman --- drivers/mfd/intel_soc_pmic_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mfd/intel_soc_pmic_core.c b/drivers/mfd/intel_soc_pmic_core.c index d9e15cf7c6c8..12d6ebb4ae5d 100644 --- a/drivers/mfd/intel_soc_pmic_core.c +++ b/drivers/mfd/intel_soc_pmic_core.c @@ -35,6 +35,7 @@ static struct gpiod_lookup_table panel_gpio_table = { .table = { /* Panel EN/DISABLE */ GPIO_LOOKUP("gpio_crystalcove", 94, "panel", GPIO_ACTIVE_HIGH), + { }, }, }; From a1e15f5ae6d69e834b751b719d007b380f4a42fb Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 8 Apr 2016 13:10:23 +0200 Subject: [PATCH 284/656] PM / Runtime: Fix error path in pm_runtime_force_resume() commit 0ae3aeefabbeef26294e7a349b51f1c761d46c9f upstream. As pm_runtime_set_active() may fail because the device's parent isn't active, we can end up executing the ->runtime_resume() callback for the device when it isn't allowed. Fix this by invoking pm_runtime_set_active() before running the callback and let's also deal with the error code. Fixes: 37f204164dfb (PM: Add pm_runtime_suspend|resume_force functions) Signed-off-by: Ulf Hansson Reviewed-by: Linus Walleij Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/runtime.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index e1a10a03df8e..9796a1a15ef6 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -1468,11 +1468,16 @@ int pm_runtime_force_resume(struct device *dev) goto out; } - ret = callback(dev); + ret = pm_runtime_set_active(dev); if (ret) goto out; - pm_runtime_set_active(dev); + ret = callback(dev); + if (ret) { + pm_runtime_set_suspended(dev); + goto out; + } + pm_runtime_mark_last_busy(dev); out: pm_runtime_enable(dev); From 3b2321716df07e7a61fdc8a7e54d2a85c878d758 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Tue, 5 Apr 2016 14:05:38 -0500 Subject: [PATCH 285/656] cpuidle: Indicate when a device has been unregistered commit c998c07836f985b24361629dc98506ec7893e7a0 upstream. Currently the 'registered' member of the cpuidle_device struct is set to 1 during cpuidle_register_device. In this same function there are checks to see if the device is already registered to prevent duplicate calls to register the device, but this value is never set to 0 even on unregister of the device. Because of this, any attempt to call cpuidle_register_device after a call to cpuidle_unregister_device will fail which shouldn't be the case. To prevent this, set registered to 0 when the device is unregistered. Fixes: c878a52d3c7c (cpuidle: Check if device is already registered) Signed-off-by: Dave Gerlach Acked-by: Daniel Lezcano Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpuidle/cpuidle.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index 17a6dc0e2111..f0541efedb37 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -433,6 +433,8 @@ static void __cpuidle_unregister_device(struct cpuidle_device *dev) list_del(&dev->device_list); per_cpu(cpuidle_devices, dev->cpu) = NULL; module_put(drv->owner); + + dev->registered = 0; } static void __cpuidle_device_init(struct cpuidle_device *dev) From dd1917666c6d13f1bdb34ef4647205e732df06d1 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Tue, 17 May 2016 16:54:00 +0200 Subject: [PATCH 286/656] cpuidle: Fix cpuidle_state_is_coupled() argument in cpuidle_enter() commit e7387da52028b072489c45efeb7a916c0205ebd2 upstream. Commit 0b89e9aa2856 (cpuidle: delay enabling interrupts until all coupled CPUs leave idle) rightfully fixed a regression by letting the coupled idle state framework to handle local interrupt enabling when the CPU is exiting an idle state. The current code checks if the idle state is coupled and, if so, it will let the coupled code to enable interrupts. This way, it can decrement the ready-count before handling the interrupt. This mechanism prevents the other CPUs from waiting for a CPU which is handling interrupts. But the check is done against the state index returned by the back end driver's ->enter functions which could be different from the initial index passed as parameter to the cpuidle_enter_state() function. entered_state = target_state->enter(dev, drv, index); [ ... ] if (!cpuidle_state_is_coupled(drv, entered_state)) local_irq_enable(); [ ... ] If the 'index' is referring to a coupled idle state but the 'entered_state' is *not* coupled, then the interrupts are enabled again. All CPUs blocked on the sync barrier may busy loop longer if the CPU has interrupts to handle before decrementing the ready-count. That's consuming more energy than saving. Fixes: 0b89e9aa2856 (cpuidle: delay enabling interrupts until all coupled CPUs leave idle) Signed-off-by: Daniel Lezcano [ rjw: Subject & changelog ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/cpuidle/cpuidle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c index f0541efedb37..d40b2c077746 100644 --- a/drivers/cpuidle/cpuidle.c +++ b/drivers/cpuidle/cpuidle.c @@ -214,7 +214,7 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, tick_broadcast_exit(); } - if (!cpuidle_state_is_coupled(drv, entered_state)) + if (!cpuidle_state_is_coupled(drv, index)) local_irq_enable(); diff = ktime_to_us(ktime_sub(time_end, time_start)); From d5298429006ed65ad676f3b7ade0b52fd712dea7 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Wed, 13 Apr 2016 13:05:03 -0700 Subject: [PATCH 287/656] clk: bcm2835: Fix PLL poweron commit e708b383f4b94feca2e0d5d06e1cfc13cdfea100 upstream. In poweroff, we set the reset bit and the power down bit, but only managed to unset the reset bit for poweron. This meant that if HDMI did -EPROBE_DEFER after it had grabbed its clocks, we'd power down the PLLH (that had been on at boot time) and never recover. Signed-off-by: Eric Anholt Fixes: 41691b8862e2 ("clk: bcm2835: Add support for programming the audio domain clocks") Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/clk/bcm/clk-bcm2835.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 185a4d859638..c58663841aae 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -901,6 +901,10 @@ static int bcm2835_pll_on(struct clk_hw *hw) const struct bcm2835_pll_data *data = pll->data; ktime_t timeout; + cprman_write(cprman, data->a2w_ctrl_reg, + cprman_read(cprman, data->a2w_ctrl_reg) & + ~A2W_PLL_CTRL_PWRDN); + /* Take the PLL out of reset. */ cprman_write(cprman, data->cm_ctrl_reg, cprman_read(cprman, data->cm_ctrl_reg) & ~CM_PLL_ANARST); From 4ee87930e4e26a1f873086ab24d53b946787c63f Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Tue, 8 Mar 2016 01:41:29 +0200 Subject: [PATCH 288/656] clk: at91: fix check of clk_register() returned value commit cb0ceaf77d93964a0d00477c79f4499123f6159c upstream. The clk_register() function returns a valid pointer to struct clk or ERR_PTR() error code, this makes a check for returned NULL value useless and may lead to oops on error path. Signed-off-by: Vladimir Zapolskiy Acked-by: Alexandre Belloni Acked-by: Boris Brezillon Fixes: bcc5fd49a0fd ("clk: at91: add a driver for the h32mx clock") Signed-off-by: Stephen Boyd Signed-off-by: Greg Kroah-Hartman --- drivers/clk/at91/clk-h32mx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c index 61566bcefa53..a165230e7eda 100644 --- a/drivers/clk/at91/clk-h32mx.c +++ b/drivers/clk/at91/clk-h32mx.c @@ -116,7 +116,7 @@ void __init of_sama5d4_clk_h32mx_setup(struct device_node *np, h32mxclk->pmc = pmc; clk = clk_register(NULL, &h32mxclk->hw); - if (!clk) { + if (IS_ERR(clk)) { kfree(h32mxclk); return; } From 3232e7c4db4fd966b30ceec24ed9f37277a81c61 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Mon, 29 Feb 2016 11:39:17 +0000 Subject: [PATCH 289/656] clk: bcm2835: pll_off should only update CM_PLL_ANARST commit 6727f086cfe4ddcc651eb2bf4301abfcf619be06 upstream. bcm2835_pll_off is currently assigning CM_PLL_ANARST to the control register, which may lose the other bits that are currently set by the clock dividers. It also now locks during the read/modify/write cycle of both registers. Fixes: 41691b8862e2 ("clk: bcm2835: Add support for programming the audio domain clocks") Signed-off-by: Martin Sperl Signed-off-by: Eric Anholt Reviewed-by: Eric Anholt Signed-off-by: Greg Kroah-Hartman --- drivers/clk/bcm/clk-bcm2835.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index c58663841aae..fcb581a7ce88 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -890,8 +890,14 @@ static void bcm2835_pll_off(struct clk_hw *hw) struct bcm2835_cprman *cprman = pll->cprman; const struct bcm2835_pll_data *data = pll->data; - cprman_write(cprman, data->cm_ctrl_reg, CM_PLL_ANARST); - cprman_write(cprman, data->a2w_ctrl_reg, A2W_PLL_CTRL_PWRDN); + spin_lock(&cprman->regs_lock); + cprman_write(cprman, data->cm_ctrl_reg, + cprman_read(cprman, data->cm_ctrl_reg) | + CM_PLL_ANARST); + cprman_write(cprman, data->a2w_ctrl_reg, + cprman_read(cprman, data->a2w_ctrl_reg) | + A2W_PLL_CTRL_PWRDN); + spin_unlock(&cprman->regs_lock); } static int bcm2835_pll_on(struct clk_hw *hw) From b2cfbf421062a4f27801d6771dfd83c1cd894342 Mon Sep 17 00:00:00 2001 From: Martin Sperl Date: Mon, 29 Feb 2016 11:39:20 +0000 Subject: [PATCH 290/656] clk: bcm2835: divider value has to be 1 or more commit 997f16bd5d2e9b3456027f96fcadfe1e2bf12f4e upstream. Current clamping of a normal divider allows a value < 1 to be valid. A divider of < 1 would actually only be possible if we had a PLL... So this patch clamps the divider to 1. Fixes: 41691b8862e2 ("clk: bcm2835: Add support for programming the audio domain clocks") Signed-off-by: Martin Sperl Signed-off-by: Eric Anholt Reviewed-by: Eric Anholt Signed-off-by: Greg Kroah-Hartman --- drivers/clk/bcm/clk-bcm2835.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index fcb581a7ce88..6029313aa995 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -1181,8 +1181,9 @@ static u32 bcm2835_clock_choose_div(struct clk_hw *hw, div &= ~unused_frac_mask; } - /* Clamp to the limits. */ - div = max(div, unused_frac_mask + 1); + /* clamp to min divider of 1 */ + div = max_t(u32, div, 1 << CM_DIV_FRAC_BITS); + /* clamp to the highest possible fractional divider */ div = min_t(u32, div, GENMASK(data->int_bits + CM_DIV_FRAC_BITS - 1, CM_DIV_FRAC_BITS - data->frac_bits)); From f4b0dd357f31c3f794ebb171a4ed903c0eee4565 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Wed, 20 Apr 2016 11:24:17 +0930 Subject: [PATCH 291/656] pinctrl: exynos5440: Use off-stack memory for pinctrl_gpio_range commit 71324fdc72ef0163e57631aa814a9a81e9e4770b upstream. The range is registered into a linked list which can be referenced throughout the lifetime of the driver. Ensure the range's memory is useful for the same lifetime by adding it to the driver's private data structure. The bug was introduced in the driver's initial commit, which was present in v3.10. Fixes: f0b9a7e521fa ("pinctrl: exynos5440: add pinctrl driver for Samsung EXYNOS5440 SoC") Signed-off-by: Andrew Jeffery Acked-by: Tomasz Figa Reviewed-by: Krzysztof Kozlowski Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- drivers/pinctrl/samsung/pinctrl-exynos5440.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/pinctrl/samsung/pinctrl-exynos5440.c b/drivers/pinctrl/samsung/pinctrl-exynos5440.c index 82dc109f7ed4..3149a877c51f 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos5440.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos5440.c @@ -107,6 +107,7 @@ struct exynos5440_pmx_func { * @nr_groups: number of pin groups available. * @pmx_functions: list of pin functions parsed from device tree. * @nr_functions: number of pin functions available. + * @range: gpio range to register with pinctrl */ struct exynos5440_pinctrl_priv_data { void __iomem *reg_base; @@ -117,6 +118,7 @@ struct exynos5440_pinctrl_priv_data { unsigned int nr_groups; const struct exynos5440_pmx_func *pmx_functions; unsigned int nr_functions; + struct pinctrl_gpio_range range; }; /** @@ -742,7 +744,6 @@ static int exynos5440_pinctrl_register(struct platform_device *pdev, struct pinctrl_desc *ctrldesc; struct pinctrl_dev *pctl_dev; struct pinctrl_pin_desc *pindesc, *pdesc; - struct pinctrl_gpio_range grange; char *pin_names; int pin, ret; @@ -794,12 +795,12 @@ static int exynos5440_pinctrl_register(struct platform_device *pdev, return PTR_ERR(pctl_dev); } - grange.name = "exynos5440-pctrl-gpio-range"; - grange.id = 0; - grange.base = 0; - grange.npins = EXYNOS5440_MAX_PINS; - grange.gc = priv->gc; - pinctrl_add_gpio_range(pctl_dev, &grange); + priv->range.name = "exynos5440-pctrl-gpio-range"; + priv->range.id = 0; + priv->range.base = 0; + priv->range.npins = EXYNOS5440_MAX_PINS; + priv->range.gc = priv->gc; + pinctrl_add_gpio_range(pctl_dev, &priv->range); return 0; } From a87f69dceff5c93a7d8f70f2cb255e1fcbda83bb Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Wed, 11 May 2016 12:27:16 -0400 Subject: [PATCH 292/656] PCI: Disable all BAR sizing for devices with non-compliant BARs commit ad67b437f187ea818b2860524d10f878fadfdd99 upstream. b84106b4e229 ("PCI: Disable IO/MEM decoding for devices with non-compliant BARs") disabled BAR sizing for BARs 0-5 of devices that don't comply with the PCI spec. But it didn't do anything for expansion ROM BARs, so we still try to size them, resulting in warnings like this on Broadwell-EP: pci 0000:ff:12.0: BAR 6: failed to assign [mem size 0x00000001 pref] Move the non-compliant BAR check from __pci_read_base() up to pci_read_bases() so it applies to the expansion ROM BAR as well as to BARs 0-5. Note that direct callers of __pci_read_base(), like sriov_init(), will now bypass this check. We haven't had reports of devices with broken SR-IOV BARs yet. [bhelgaas: changelog] Fixes: b84106b4e229 ("PCI: Disable IO/MEM decoding for devices with non-compliant BARs") Signed-off-by: Prarit Bhargava Signed-off-by: Bjorn Helgaas CC: Thomas Gleixner CC: Ingo Molnar CC: "H. Peter Anvin" CC: Andi Kleen Signed-off-by: Greg Kroah-Hartman --- drivers/pci/probe.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 7aafb5fb9336..9757cf9037a2 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -179,9 +179,6 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, u16 orig_cmd; struct pci_bus_region region, inverted_region; - if (dev->non_compliant_bars) - return 0; - mask = type ? PCI_ROM_ADDRESS_MASK : ~0; /* No printks while decoding is disabled! */ @@ -322,6 +319,9 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) { unsigned int pos, reg; + if (dev->non_compliant_bars) + return; + for (pos = 0; pos < howmany; pos++) { struct resource *res = &dev->resource[pos]; reg = PCI_BASE_ADDRESS_0 + (pos << 2); From 08c6a55e79c3e83a69b7e59f55f2307708eb706e Mon Sep 17 00:00:00 2001 From: Tiffany Lin Date: Mon, 14 Mar 2016 08:16:14 -0300 Subject: [PATCH 293/656] media: v4l2-compat-ioctl32: fix missing reserved field copy in put_v4l2_create32 commit baf43c6eace43868e490f18560287fa3481b2159 upstream. In v4l2-compliance utility, test VIDIOC_CREATE_BUFS will check whether reserved filed of v4l2_create_buffers filled with zero Reserved field is filled with zero in v4l_create_bufs. This patch copy reserved field of v4l2_create_buffer from kernel space to user space Signed-off-by: Tiffany Lin Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Greg Kroah-Hartman --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index f38c076752ce..109f687d1cbd 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -280,7 +280,8 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) { if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) || - copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format))) + copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) || + copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved))) return -EFAULT; return __put_v4l2_format32(&kp->format, &up->format); } From 18875bf7728d25dec9bce7966c2fe4fefd5d00bc Mon Sep 17 00:00:00 2001 From: Stefan Bader Date: Fri, 20 May 2016 16:58:38 -0700 Subject: [PATCH 294/656] mm: use phys_addr_t for reserve_bootmem_region() arguments commit 4b50bcc7eda4d3cc9e3f2a0aa60e590fedf728c5 upstream. Since commit 92923ca3aace ("mm: meminit: only set page reserved in the memblock region") the reserved bit is set on reserved memblock regions. However start and end address are passed as unsigned long. This is only 32bit on i386, so it can end up marking the wrong pages reserved for ranges at 4GB and above. This was observed on a 32bit Xen dom0 which was booted with initial memory set to a value below 4G but allowing to balloon in memory (dom0_mem=1024M for example). This would define a reserved bootmem region for the additional memory (for example on a 8GB system there was a reverved region covering the 4GB-8GB range). But since the addresses were passed on as unsigned long, this was actually marking all pages from 0 to 4GB as reserved. Fixes: 92923ca3aacef63 ("mm: meminit: only set page reserved in the memblock region") Link: http://lkml.kernel.org/r/1463491221-10573-1-git-send-email-stefan.bader@canonical.com Signed-off-by: Stefan Bader Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- include/linux/mm.h | 2 +- mm/page_alloc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index fb8b20e5d021..f24df9c0b9df 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1696,7 +1696,7 @@ extern void free_highmem_page(struct page *page); extern void adjust_managed_page_count(struct page *page, long count); extern void mem_init_print_info(const char *str); -extern void reserve_bootmem_region(unsigned long start, unsigned long end); +extern void reserve_bootmem_region(phys_addr_t start, phys_addr_t end); /* Free the reserved page into the buddy system, so it gets managed. */ static inline void __free_reserved_page(struct page *page) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 6cf5cadeaef7..99c1738684ec 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -951,7 +951,7 @@ static inline void init_reserved_page(unsigned long pfn) * marks the pages PageReserved. The remaining valid pages are later * sent to the buddy page allocator. */ -void __meminit reserve_bootmem_region(unsigned long start, unsigned long end) +void __meminit reserve_bootmem_region(phys_addr_t start, phys_addr_t end) { unsigned long start_pfn = PFN_DOWN(start); unsigned long end_pfn = PFN_UP(end); From 0eea2e24fc600fa20d357e022ef29029140bb714 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 23 May 2016 16:23:50 -0700 Subject: [PATCH 295/656] wait/ptrace: assume __WALL if the child is traced commit bf959931ddb88c4e4366e96dd22e68fa0db9527c upstream. The following program (simplified version of generated by syzkaller) #include #include #include #include #include void *thread_func(void *arg) { ptrace(PTRACE_TRACEME, 0,0,0); return 0; } int main(void) { pthread_t thread; if (fork()) return 0; while (getppid() != 1) ; pthread_create(&thread, NULL, thread_func, NULL); pthread_join(thread, NULL); return 0; } creates an unreapable zombie if /sbin/init doesn't use __WALL. This is not a kernel bug, at least in a sense that everything works as expected: debugger should reap a traced sub-thread before it can reap the leader, but without __WALL/__WCLONE do_wait() ignores sub-threads. Unfortunately, it seems that /sbin/init in most (all?) distributions doesn't use it and we have to change the kernel to avoid the problem. Note also that most init's use sys_waitid() which doesn't allow __WALL, so the necessary user-space fix is not that trivial. This patch just adds the "ptrace" check into eligible_child(). To some degree this matches the "tsk->ptrace" in exit_notify(), ->exit_signal is mostly ignored when the tracee reports to debugger. Or WSTOPPED, the tracer doesn't need to set this flag to wait for the stopped tracee. This obviously means the user-visible change: __WCLONE and __WALL no longer have any meaning for debugger. And I can only hope that this won't break something, but at least strace/gdb won't suffer. We could make a more conservative change. Say, we can take __WCLONE into account, or !thread_group_leader(). But it would be nice to not complicate these historical/confusing checks. Signed-off-by: Oleg Nesterov Reported-by: Dmitry Vyukov Cc: Denys Vlasenko Cc: Jan Kratochvil Cc: "Michael Kerrisk (man-pages)" Cc: Pedro Alves Cc: Roland McGrath Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds Signed-off-by: Greg Kroah-Hartman --- kernel/exit.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/kernel/exit.c b/kernel/exit.c index 07110c6020a0..ffba5df4abd5 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -918,17 +918,28 @@ static int eligible_pid(struct wait_opts *wo, struct task_struct *p) task_pid_type(p, wo->wo_type) == wo->wo_pid; } -static int eligible_child(struct wait_opts *wo, struct task_struct *p) +static int +eligible_child(struct wait_opts *wo, bool ptrace, struct task_struct *p) { if (!eligible_pid(wo, p)) return 0; - /* Wait for all children (clone and not) if __WALL is set; - * otherwise, wait for clone children *only* if __WCLONE is - * set; otherwise, wait for non-clone children *only*. (Note: - * A "clone" child here is one that reports to its parent - * using a signal other than SIGCHLD.) */ - if (((p->exit_signal != SIGCHLD) ^ !!(wo->wo_flags & __WCLONE)) - && !(wo->wo_flags & __WALL)) + + /* + * Wait for all children (clone and not) if __WALL is set or + * if it is traced by us. + */ + if (ptrace || (wo->wo_flags & __WALL)) + return 1; + + /* + * Otherwise, wait for clone children *only* if __WCLONE is set; + * otherwise, wait for non-clone children *only*. + * + * Note: a "clone" child here is one that reports to its parent + * using a signal other than SIGCHLD, or a non-leader thread which + * we can only see if it is traced by us. + */ + if ((p->exit_signal != SIGCHLD) ^ !!(wo->wo_flags & __WCLONE)) return 0; return 1; @@ -1301,7 +1312,7 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace, if (unlikely(exit_state == EXIT_DEAD)) return 0; - ret = eligible_child(wo, p); + ret = eligible_child(wo, ptrace, p); if (!ret) return ret; From 5015641d2152f0fdb328d45d517db95be5485edc Mon Sep 17 00:00:00 2001 From: Zhao Qiang Date: Wed, 9 Mar 2016 09:48:11 +0800 Subject: [PATCH 296/656] QE-UART: add "fsl,t1040-ucc-uart" to of_device_id commit 11ca2b7ab432eb90906168c327733575e68d388f upstream. New bindings use "fsl,t1040-ucc-uart" as the compatible for qe-uart. So add it. Signed-off-by: Zhao Qiang Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/ucc_uart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c index 73190f5d2832..71d26c8e1b8f 100644 --- a/drivers/tty/serial/ucc_uart.c +++ b/drivers/tty/serial/ucc_uart.c @@ -1478,6 +1478,9 @@ static const struct of_device_id ucc_uart_match[] = { .type = "serial", .compatible = "ucc_uart", }, + { + .compatible = "fsl,t1040-ucc-uart", + }, {}, }; MODULE_DEVICE_TABLE(of, ucc_uart_match); From fa6d0ba12a8eb6a2e9a1646c5816da307c1f93a7 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 18 Jan 2016 16:36:09 +0100 Subject: [PATCH 297/656] pipe: limit the per-user amount of pages allocated in pipes commit 759c01142a5d0f364a462346168a56de28a80f52 upstream. On no-so-small systems, it is possible for a single process to cause an OOM condition by filling large pipes with data that are never read. A typical process filling 4000 pipes with 1 MB of data will use 4 GB of memory. On small systems it may be tricky to set the pipe max size to prevent this from happening. This patch makes it possible to enforce a per-user soft limit above which new pipes will be limited to a single page, effectively limiting them to 4 kB each, as well as a hard limit above which no new pipes may be created for this user. This has the effect of protecting the system against memory abuse without hurting other users, and still allowing pipes to work correctly though with less data at once. The limit are controlled by two new sysctls : pipe-user-pages-soft, and pipe-user-pages-hard. Both may be disabled by setting them to zero. The default soft limit allows the default number of FDs per process (1024) to create pipes of the default size (64kB), thus reaching a limit of 64MB before starting to create only smaller pipes. With 256 processes limited to 1024 FDs each, this results in 1024*64kB + (256*1024 - 1024) * 4kB = 1084 MB of memory allocated for a user. The hard limit is disabled by default to avoid breaking existing applications that make intensive use of pipes (eg: for splicing). Reported-by: socketpair@gmail.com Reported-by: Tetsuo Handa Mitigates: CVE-2013-4312 (Linux 2.0+) Suggested-by: Linus Torvalds Signed-off-by: Willy Tarreau Signed-off-by: Al Viro Cc: Moritz Muehlenhoff Signed-off-by: Greg Kroah-Hartman --- Documentation/sysctl/fs.txt | 23 ++++++++++++++++++ fs/pipe.c | 47 +++++++++++++++++++++++++++++++++++-- include/linux/pipe_fs_i.h | 4 ++++ include/linux/sched.h | 1 + kernel/sysctl.c | 14 +++++++++++ 5 files changed, 87 insertions(+), 2 deletions(-) diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt index 88152f214f48..302b5ed616a6 100644 --- a/Documentation/sysctl/fs.txt +++ b/Documentation/sysctl/fs.txt @@ -32,6 +32,8 @@ Currently, these files are in /proc/sys/fs: - nr_open - overflowuid - overflowgid +- pipe-user-pages-hard +- pipe-user-pages-soft - protected_hardlinks - protected_symlinks - suid_dumpable @@ -159,6 +161,27 @@ The default is 65534. ============================================================== +pipe-user-pages-hard: + +Maximum total number of pages a non-privileged user may allocate for pipes. +Once this limit is reached, no new pipes may be allocated until usage goes +below the limit again. When set to 0, no limit is applied, which is the default +setting. + +============================================================== + +pipe-user-pages-soft: + +Maximum total number of pages a non-privileged user may allocate for pipes +before the pipe size gets limited to a single page. Once this limit is reached, +new pipes will be limited to a single page in size for this user in order to +limit total memory usage, and trying to increase them using fcntl() will be +denied until usage goes below the limit again. The default value allows to +allocate up to 1024 pipes at their default size. When set to 0, no limit is +applied. + +============================================================== + protected_hardlinks: A long-standing class of security issues is the hardlink-based diff --git a/fs/pipe.c b/fs/pipe.c index 42cf8ddf0e55..ab8dad3ccb6a 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -38,6 +38,12 @@ unsigned int pipe_max_size = 1048576; */ unsigned int pipe_min_size = PAGE_SIZE; +/* Maximum allocatable pages per user. Hard limit is unset by default, soft + * matches default values. + */ +unsigned long pipe_user_pages_hard; +unsigned long pipe_user_pages_soft = PIPE_DEF_BUFFERS * INR_OPEN_CUR; + /* * We use a start+len construction, which provides full use of the * allocated memory. @@ -583,20 +589,49 @@ pipe_fasync(int fd, struct file *filp, int on) return retval; } +static void account_pipe_buffers(struct pipe_inode_info *pipe, + unsigned long old, unsigned long new) +{ + atomic_long_add(new - old, &pipe->user->pipe_bufs); +} + +static bool too_many_pipe_buffers_soft(struct user_struct *user) +{ + return pipe_user_pages_soft && + atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_soft; +} + +static bool too_many_pipe_buffers_hard(struct user_struct *user) +{ + return pipe_user_pages_hard && + atomic_long_read(&user->pipe_bufs) >= pipe_user_pages_hard; +} + struct pipe_inode_info *alloc_pipe_info(void) { struct pipe_inode_info *pipe; pipe = kzalloc(sizeof(struct pipe_inode_info), GFP_KERNEL); if (pipe) { - pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * PIPE_DEF_BUFFERS, GFP_KERNEL); + unsigned long pipe_bufs = PIPE_DEF_BUFFERS; + struct user_struct *user = get_current_user(); + + if (!too_many_pipe_buffers_hard(user)) { + if (too_many_pipe_buffers_soft(user)) + pipe_bufs = 1; + pipe->bufs = kzalloc(sizeof(struct pipe_buffer) * pipe_bufs, GFP_KERNEL); + } + if (pipe->bufs) { init_waitqueue_head(&pipe->wait); pipe->r_counter = pipe->w_counter = 1; - pipe->buffers = PIPE_DEF_BUFFERS; + pipe->buffers = pipe_bufs; + pipe->user = user; + account_pipe_buffers(pipe, 0, pipe_bufs); mutex_init(&pipe->mutex); return pipe; } + free_uid(user); kfree(pipe); } @@ -607,6 +642,8 @@ void free_pipe_info(struct pipe_inode_info *pipe) { int i; + account_pipe_buffers(pipe, pipe->buffers, 0); + free_uid(pipe->user); for (i = 0; i < pipe->buffers; i++) { struct pipe_buffer *buf = pipe->bufs + i; if (buf->ops) @@ -998,6 +1035,7 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long nr_pages) memcpy(bufs + head, pipe->bufs, tail * sizeof(struct pipe_buffer)); } + account_pipe_buffers(pipe, pipe->buffers, nr_pages); pipe->curbuf = 0; kfree(pipe->bufs); pipe->bufs = bufs; @@ -1069,6 +1107,11 @@ long pipe_fcntl(struct file *file, unsigned int cmd, unsigned long arg) if (!capable(CAP_SYS_RESOURCE) && size > pipe_max_size) { ret = -EPERM; goto out; + } else if ((too_many_pipe_buffers_hard(pipe->user) || + too_many_pipe_buffers_soft(pipe->user)) && + !capable(CAP_SYS_RESOURCE) && !capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + goto out; } ret = pipe_set_size(pipe, nr_pages); break; diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index eb8b8ac6df3c..24f5470d3944 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h @@ -42,6 +42,7 @@ struct pipe_buffer { * @fasync_readers: reader side fasync * @fasync_writers: writer side fasync * @bufs: the circular array of pipe buffers + * @user: the user who created this pipe **/ struct pipe_inode_info { struct mutex mutex; @@ -57,6 +58,7 @@ struct pipe_inode_info { struct fasync_struct *fasync_readers; struct fasync_struct *fasync_writers; struct pipe_buffer *bufs; + struct user_struct *user; }; /* @@ -123,6 +125,8 @@ void pipe_unlock(struct pipe_inode_info *); void pipe_double_lock(struct pipe_inode_info *, struct pipe_inode_info *); extern unsigned int pipe_max_size, pipe_min_size; +extern unsigned long pipe_user_pages_hard; +extern unsigned long pipe_user_pages_soft; int pipe_proc_fn(struct ctl_table *, int, void __user *, size_t *, loff_t *); diff --git a/include/linux/sched.h b/include/linux/sched.h index 21a6e9649012..1c0193baea2a 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -831,6 +831,7 @@ struct user_struct { #endif unsigned long locked_shm; /* How many pages of mlocked shm ? */ unsigned long unix_inflight; /* How many files in flight in unix sockets */ + atomic_long_t pipe_bufs; /* how many pages are allocated in pipe buffers */ #ifdef CONFIG_KEYS struct key *uid_keyring; /* UID specific keyring */ diff --git a/kernel/sysctl.c b/kernel/sysctl.c index dc6858d6639e..2781141a89f9 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1735,6 +1735,20 @@ static struct ctl_table fs_table[] = { .proc_handler = &pipe_proc_fn, .extra1 = &pipe_min_size, }, + { + .procname = "pipe-user-pages-hard", + .data = &pipe_user_pages_hard, + .maxlen = sizeof(pipe_user_pages_hard), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + }, + { + .procname = "pipe-user-pages-soft", + .data = &pipe_user_pages_soft, + .maxlen = sizeof(pipe_user_pages_soft), + .mode = 0644, + .proc_handler = proc_doulongvec_minmax, + }, { } }; From 5d3bb5e616bc03dfb963ddff665023564fbcc8e8 Mon Sep 17 00:00:00 2001 From: Hari Bathini Date: Fri, 15 Apr 2016 22:48:02 +1000 Subject: [PATCH 298/656] powerpc/book3s64: Fix branching to OOL handlers in relocatable kernel commit 8ed8ab40047a570fdd8043a40c104a57248dd3fd upstream. Some of the interrupt vectors on 64-bit POWER server processors are only 32 bytes long (8 instructions), which is not enough for the full first-level interrupt handler. For these we need to branch to an out-of-line (OOL) handler. But when we are running a relocatable kernel, interrupt vectors till __end_interrupts marker are copied down to real address 0x100. So, branching to labels (ie. OOL handlers) outside this section must be handled differently (see LOAD_HANDLER()), considering relocatable kernel, which would need at least 4 instructions. However, branching from interrupt vector means that we corrupt the CFAR (come-from address register) on POWER7 and later processors as mentioned in commit 1707dd16. So, EXCEPTION_PROLOG_0 (6 instructions) that contains the part up to the point where the CFAR is saved in the PACA should be part of the short interrupt vectors before we branch out to OOL handlers. But as mentioned already, there are interrupt vectors on 64-bit POWER server processors that are only 32 bytes long (like vectors 0x4f00, 0x4f20, etc.), which cannot accomodate the above two cases at the same time owing to space constraint. Currently, in these interrupt vectors, we simply branch out to OOL handlers, without using LOAD_HANDLER(), which leaves us vulnerable when running a relocatable kernel (eg. kdump case). While this has been the case for sometime now and kdump is used widely, we were fortunate not to see any problems so far, for three reasons: 1. In almost all cases, production kernel (relocatable) is used for kdump as well, which would mean that crashed kernel's OOL handler would be at the same place where we end up branching to, from short interrupt vector of kdump kernel. 2. Also, OOL handler was unlikely the reason for crash in almost all the kdump scenarios, which meant we had a sane OOL handler from crashed kernel that we branched to. 3. On most 64-bit POWER server processors, page size is large enough that marking interrupt vector code as executable (see commit 429d2e83) leads to marking OOL handler code from crashed kernel, that sits right below interrupt vector code from kdump kernel, as executable as well. Let us fix this by moving the __end_interrupts marker down past OOL handlers to make sure that we also copy OOL handlers to real address 0x100 when running a relocatable kernel. This fix has been tested successfully in kdump scenario, on an LPAR with 4K page size by using different default/production kernel and kdump kernel. Also tested by manually corrupting the OOL handlers in the first kernel and then kdump'ing, and then causing the OOL handlers to fire - mpe. Fixes: c1fb6816fb1b ("powerpc: Add relocation on exception vector handlers") Signed-off-by: Hari Bathini Signed-off-by: Mahesh Salgaonkar Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/exceptions-64s.S | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index 0a0399c2af11..b81ccc5fb32d 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -962,11 +962,6 @@ hv_facility_unavailable_relon_trampoline: #endif STD_RELON_EXCEPTION_PSERIES(0x5700, 0x1700, altivec_assist) - /* Other future vectors */ - .align 7 - .globl __end_interrupts -__end_interrupts: - .align 7 system_call_entry: b system_call_common @@ -1253,6 +1248,17 @@ __end_handlers: STD_RELON_EXCEPTION_PSERIES_OOL(0xf60, facility_unavailable) STD_RELON_EXCEPTION_HV_OOL(0xf80, hv_facility_unavailable) + /* + * The __end_interrupts marker must be past the out-of-line (OOL) + * handlers, so that they are copied to real address 0x100 when running + * a relocatable kernel. This ensures they can be reached from the short + * trampoline handlers (like 0x4f00, 0x4f20, etc.) which branch + * directly, without using LOAD_HANDLER(). + */ + .align 7 + .globl __end_interrupts +__end_interrupts: + #if defined(CONFIG_PPC_PSERIES) || defined(CONFIG_PPC_POWERNV) /* * Data area reserved for FWNMI option. From d140d14201f8a0e5a0d3b690ebb346f380e60386 Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Wed, 27 Apr 2016 11:14:50 +1000 Subject: [PATCH 299/656] powerpc/eeh: Don't report error in eeh_pe_reset_and_recover() commit affeb0f2d3a9af419ad7ef4ac782e1540b2f7b28 upstream. The function eeh_pe_reset_and_recover() is used to recover EEH error when the passthrough device are transferred to guest and backwards, meaning the device's driver is vfio-pci or none. When the driver is vfio-pci that provides error_detected() error handler only, the handler simply stops the guest and it's not expected behaviour. On the other hand, no error handlers will be called if we don't have a bound driver. This ignores the error handler in eeh_pe_reset_and_recover() that reports the error to device driver to avoid the exceptional behaviour. Fixes: 5cfb20b9 ("powerpc/eeh: Emulate EEH recovery for VFIO devices") Signed-off-by: Gavin Shan Reviewed-by: Russell Currey Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/eeh_driver.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 52c1e273f8cd..3c42cbf2634d 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -504,9 +504,6 @@ int eeh_pe_reset_and_recover(struct eeh_pe *pe) /* Save states */ eeh_pe_dev_traverse(pe, eeh_dev_save_state, NULL); - /* Report error */ - eeh_pe_dev_traverse(pe, eeh_report_error, &result); - /* Issue reset */ ret = eeh_reset_pe(pe); if (ret) { From af64f74e5f72903de2ce891bde1386a6d3be712c Mon Sep 17 00:00:00 2001 From: "Guilherme G. Piccoli" Date: Mon, 11 Apr 2016 16:17:22 -0300 Subject: [PATCH 300/656] Revert "powerpc/eeh: Fix crash in eeh_add_device_early() on Cell" commit c2078d9ef600bdbe568c89e5ddc2c6f15b7982c8 upstream. This reverts commit 89a51df5ab1d38b257300b8ac940bbac3bb0eb9b. The function eeh_add_device_early() is used to perform EEH initialization in devices added later on the system, like in hotplug/DLPAR scenarios. Since the commit 89a51df5ab1d ("powerpc/eeh: Fix crash in eeh_add_device_early() on Cell") a new check was introduced in this function - Cell has no EEH capabilities which led to kernel oops if hotplug was performed, so checking for eeh_enabled() was introduced to avoid the issue. However, in architectures that EEH is present like pSeries or PowerNV, we might reach a case in which no PCI devices are present on boot time and so EEH is not initialized. Then, if a device is added via DLPAR for example, eeh_add_device_early() fails because eeh_enabled() is false, and EEH end up not being enabled at all. This reverts the aforementioned patch since a new verification was introduced by the commit d91dafc02f42 ("powerpc/eeh: Delay probing EEH device during hotplug") and so the original Cell issue does not happen anymore. Reviewed-by: Gavin Shan Signed-off-by: Guilherme G. Piccoli Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/eeh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/powerpc/kernel/eeh.c b/arch/powerpc/kernel/eeh.c index 40e4d4a27663..b34e8a54f7db 100644 --- a/arch/powerpc/kernel/eeh.c +++ b/arch/powerpc/kernel/eeh.c @@ -1072,7 +1072,7 @@ void eeh_add_device_early(struct pci_dn *pdn) struct pci_controller *phb; struct eeh_dev *edev = pdn_to_eeh_dev(pdn); - if (!edev || !eeh_enabled()) + if (!edev) return; if (!eeh_has_flag(EEH_PROBE_MODE_DEVTREE)) From 0118086d556d96f85ecc5455016ed1f447267bda Mon Sep 17 00:00:00 2001 From: Gavin Shan Date: Wed, 27 Apr 2016 11:14:51 +1000 Subject: [PATCH 301/656] powerpc/eeh: Restore initial state in eeh_pe_reset_and_recover() commit 5a0cdbfd17b90a89c64a71d8aec9773ecdb20d0d upstream. The function eeh_pe_reset_and_recover() is used to recover EEH error when the passthrou device are transferred to guest and backwards. The content in the device's config space will be lost on PE reset issued in the middle of the recovery. The function saves/restores it before/after the reset. However, config access to some adapters like Broadcom BCM5719 at this point will causes fenced PHB. The config space is always blocked and we save 0xFF's that are restored at late point. The memory BARs are totally corrupted, causing another EEH error upon access to one of the memory BARs. This restores the config space on those adapters like BCM5719 from the content saved to the EEH device when it's populated, to resolve above issue. Fixes: 5cfb20b9 ("powerpc/eeh: Emulate EEH recovery for VFIO devices") Signed-off-by: Gavin Shan Reviewed-by: Russell Currey Signed-off-by: Michael Ellerman Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/kernel/eeh_driver.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/powerpc/kernel/eeh_driver.c b/arch/powerpc/kernel/eeh_driver.c index 3c42cbf2634d..247a0dc012f1 100644 --- a/arch/powerpc/kernel/eeh_driver.c +++ b/arch/powerpc/kernel/eeh_driver.c @@ -166,6 +166,16 @@ static void *eeh_dev_save_state(void *data, void *userdata) if (!edev) return NULL; + /* + * We cannot access the config space on some adapters. + * Otherwise, it will cause fenced PHB. We don't save + * the content in their config space and will restore + * from the initial config space saved when the EEH + * device is created. + */ + if (edev->pe && (edev->pe->state & EEH_PE_CFG_RESTRICTED)) + return NULL; + pdev = eeh_dev_to_pci_dev(edev); if (!pdev) return NULL; @@ -305,6 +315,19 @@ static void *eeh_dev_restore_state(void *data, void *userdata) if (!edev) return NULL; + /* + * The content in the config space isn't saved because + * the blocked config space on some adapters. We have + * to restore the initial saved config space when the + * EEH device is created. + */ + if (edev->pe && (edev->pe->state & EEH_PE_CFG_RESTRICTED)) { + if (list_is_last(&edev->list, &edev->pe->edevs)) + eeh_pe_restore_bars(edev->pe); + + return NULL; + } + pdev = eeh_dev_to_pci_dev(edev); if (!pdev) return NULL; From 6232876e14a5a7fc0b0449a740bda8ffe2ed59ed Mon Sep 17 00:00:00 2001 From: Ross Lagerwall Date: Tue, 10 May 2016 16:11:00 +0100 Subject: [PATCH 302/656] xen/events: Don't move disabled irqs commit f0f393877c71ad227d36705d61d1e4062bc29cf5 upstream. Commit ff1e22e7a638 ("xen/events: Mask a moving irq") open-coded irq_move_irq() but left out checking if the IRQ is disabled. This broke resuming from suspend since it tries to move a (disabled) irq without holding the IRQ's desc->lock. Fix it by adding in a check for disabled IRQs. The resulting stacktrace was: kernel BUG at /build/linux-UbQGH5/linux-4.4.0/kernel/irq/migration.c:31! invalid opcode: 0000 [#1] SMP Modules linked in: xenfs xen_privcmd ... CPU: 0 PID: 9 Comm: migration/0 Not tainted 4.4.0-22-generic #39-Ubuntu Hardware name: Xen HVM domU, BIOS 4.6.1-xs125180 05/04/2016 task: ffff88003d75ee00 ti: ffff88003d7bc000 task.ti: ffff88003d7bc000 RIP: 0010:[] [] irq_move_masked_irq+0xd2/0xe0 RSP: 0018:ffff88003d7bfc50 EFLAGS: 00010046 RAX: 0000000000000000 RBX: ffff88003d40ba00 RCX: 0000000000000001 RDX: 0000000000000001 RSI: 0000000000000100 RDI: ffff88003d40bad8 RBP: ffff88003d7bfc68 R08: 0000000000000000 R09: ffff88003d000000 R10: 0000000000000000 R11: 000000000000023c R12: ffff88003d40bad0 R13: ffffffff81f3a4a0 R14: 0000000000000010 R15: 00000000ffffffff FS: 0000000000000000(0000) GS:ffff88003da00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00007fd4264de624 CR3: 0000000037922000 CR4: 00000000003406f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Stack: ffff88003d40ba38 0000000000000024 0000000000000000 ffff88003d7bfca0 ffffffff814c8d92 00000010813ef89d 00000000805ea732 0000000000000009 0000000000000024 ffff88003cc39b80 ffff88003d7bfce0 ffffffff814c8f66 Call Trace: [] eoi_pirq+0xb2/0xf0 [] __startup_pirq+0xe6/0x150 [] xen_irq_resume+0x319/0x360 [] xen_suspend+0xb5/0x180 [] multi_cpu_stop+0xb5/0xe0 [] ? cpu_stop_queue_work+0x80/0x80 [] cpu_stopper_thread+0xb0/0x140 [] ? finish_task_switch+0x76/0x220 [] ? __raw_callee_save___pv_queued_spin_unlock+0x11/0x20 [] smpboot_thread_fn+0x105/0x160 [] ? sort_range+0x30/0x30 [] kthread+0xd8/0xf0 [] ? kthread_create_on_node+0x1e0/0x1e0 [] ret_from_fork+0x3f/0x70 [] ? kthread_create_on_node+0x1e0/0x1e0 Signed-off-by: Ross Lagerwall Reviewed-by: Boris Ostrovsky Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- drivers/xen/events/events_base.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index 44367783f07a..83ec7b89d308 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -487,7 +487,8 @@ static void eoi_pirq(struct irq_data *data) if (!VALID_EVTCHN(evtchn)) return; - if (unlikely(irqd_is_setaffinity_pending(data))) { + if (unlikely(irqd_is_setaffinity_pending(data)) && + likely(!irqd_irq_disabled(data))) { int masked = test_and_set_mask(evtchn); clear_evtchn(evtchn); @@ -1370,7 +1371,8 @@ static void ack_dynirq(struct irq_data *data) if (!VALID_EVTCHN(evtchn)) return; - if (unlikely(irqd_is_setaffinity_pending(data))) { + if (unlikely(irqd_is_setaffinity_pending(data)) && + likely(!irqd_irq_disabled(data))) { int masked = test_and_set_mask(evtchn); clear_evtchn(evtchn); From aa1cc4d4753f56f85c3df65a574d8fdb5823c243 Mon Sep 17 00:00:00 2001 From: Juergen Gross Date: Wed, 18 May 2016 16:44:54 +0200 Subject: [PATCH 303/656] xen: use same main loop for counting and remapping pages commit dd14be92fbf5bc1ef7343f34968440e44e21b46a upstream. Instead of having two functions for cycling through the E820 map in order to count to be remapped pages and remap them later, just use one function with a caller supplied sub-function called for each region to be processed. This eliminates the possibility of a mismatch between both loops which showed up in certain configurations. Suggested-by: Ed Swierk Signed-off-by: Juergen Gross Signed-off-by: David Vrabel Signed-off-by: Greg Kroah-Hartman --- arch/x86/xen/setup.c | 65 ++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 39 deletions(-) diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c index 7ab29518a3b9..e345891450c3 100644 --- a/arch/x86/xen/setup.c +++ b/arch/x86/xen/setup.c @@ -393,6 +393,9 @@ static unsigned long __init xen_set_identity_and_remap_chunk( unsigned long i = 0; unsigned long n = end_pfn - start_pfn; + if (remap_pfn == 0) + remap_pfn = nr_pages; + while (i < n) { unsigned long cur_pfn = start_pfn + i; unsigned long left = n - i; @@ -438,17 +441,29 @@ static unsigned long __init xen_set_identity_and_remap_chunk( return remap_pfn; } -static void __init xen_set_identity_and_remap(unsigned long nr_pages) +static unsigned long __init xen_count_remap_pages( + unsigned long start_pfn, unsigned long end_pfn, unsigned long nr_pages, + unsigned long remap_pages) +{ + if (start_pfn >= nr_pages) + return remap_pages; + + return remap_pages + min(end_pfn, nr_pages) - start_pfn; +} + +static unsigned long __init xen_foreach_remap_area(unsigned long nr_pages, + unsigned long (*func)(unsigned long start_pfn, unsigned long end_pfn, + unsigned long nr_pages, unsigned long last_val)) { phys_addr_t start = 0; - unsigned long last_pfn = nr_pages; + unsigned long ret_val = 0; const struct e820entry *entry = xen_e820_map; int i; /* * Combine non-RAM regions and gaps until a RAM region (or the - * end of the map) is reached, then set the 1:1 map and - * remap the memory in those non-RAM regions. + * end of the map) is reached, then call the provided function + * to perform its duty on the non-RAM region. * * The combined non-RAM regions are rounded to a whole number * of pages so any partial pages are accessible via the 1:1 @@ -466,14 +481,13 @@ static void __init xen_set_identity_and_remap(unsigned long nr_pages) end_pfn = PFN_UP(entry->addr); if (start_pfn < end_pfn) - last_pfn = xen_set_identity_and_remap_chunk( - start_pfn, end_pfn, nr_pages, - last_pfn); + ret_val = func(start_pfn, end_pfn, nr_pages, + ret_val); start = end; } } - pr_info("Released %ld page(s)\n", xen_released_pages); + return ret_val; } /* @@ -596,35 +610,6 @@ static void __init xen_ignore_unusable(void) } } -static unsigned long __init xen_count_remap_pages(unsigned long max_pfn) -{ - unsigned long extra = 0; - unsigned long start_pfn, end_pfn; - const struct e820entry *entry = xen_e820_map; - int i; - - end_pfn = 0; - for (i = 0; i < xen_e820_map_entries; i++, entry++) { - start_pfn = PFN_DOWN(entry->addr); - /* Adjacent regions on non-page boundaries handling! */ - end_pfn = min(end_pfn, start_pfn); - - if (start_pfn >= max_pfn) - return extra + max_pfn - end_pfn; - - /* Add any holes in map to result. */ - extra += start_pfn - end_pfn; - - end_pfn = PFN_UP(entry->addr + entry->size); - end_pfn = min(end_pfn, max_pfn); - - if (entry->type != E820_RAM) - extra += end_pfn - start_pfn; - } - - return extra; -} - bool __init xen_is_e820_reserved(phys_addr_t start, phys_addr_t size) { struct e820entry *entry; @@ -804,7 +789,7 @@ char * __init xen_memory_setup(void) max_pages = xen_get_max_pages(); /* How many extra pages do we need due to remapping? */ - max_pages += xen_count_remap_pages(max_pfn); + max_pages += xen_foreach_remap_area(max_pfn, xen_count_remap_pages); if (max_pages > max_pfn) extra_pages += max_pages - max_pfn; @@ -922,7 +907,9 @@ char * __init xen_memory_setup(void) * Set identity map on non-RAM pages and prepare remapping the * underlying RAM. */ - xen_set_identity_and_remap(max_pfn); + xen_foreach_remap_area(max_pfn, xen_set_identity_and_remap_chunk); + + pr_info("Released %ld page(s)\n", xen_released_pages); return "Xen"; } From 6c1e441c3b16d417dc29397c5b88d978d4e7c414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Trnka?= Date: Fri, 20 May 2016 16:41:10 +0200 Subject: [PATCH 304/656] sunrpc: fix stripping of padded MIC tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit c0cb8bf3a8e4bd82e640862cdd8891400405cb89 upstream. The length of the GSS MIC token need not be a multiple of four bytes. It is then padded by XDR to a multiple of 4 B, but unwrap_integ_data() would previously only trim mic.len + 4 B. The remaining up to three bytes would then trigger a check in nfs4svc_decode_compoundargs(), leading to a "garbage args" error and mount failure: nfs4svc_decode_compoundargs: compound not properly padded! nfsd: failed to decode arguments! This would prevent older clients using the pre-RFC 4121 MIC format (37-byte MIC including a 9-byte OID) from mounting exports from v3.9+ servers using krb5i. The trimming was introduced by commit 4c190e2f913f ("sunrpc: trim off trailing checksum before returning decrypted or integrity authenticated buffer"). Fixes: 4c190e2f913f "unrpc: trim off trailing checksum..." Signed-off-by: Tomáš Trnka Acked-by: Jeff Layton Signed-off-by: J. Bruce Fields Signed-off-by: Greg Kroah-Hartman --- net/sunrpc/auth_gss/svcauth_gss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index 1095be9c80ab..4605dc73def6 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -857,8 +857,8 @@ unwrap_integ_data(struct svc_rqst *rqstp, struct xdr_buf *buf, u32 seq, struct g goto out; if (svc_getnl(&buf->head[0]) != seq) goto out; - /* trim off the mic at the end before returning */ - xdr_buf_trim(buf, mic.len + 4); + /* trim off the mic and padding at the end before returning */ + xdr_buf_trim(buf, round_up_to_quad(mic.len) + 4); stat = 0; out: kfree(mic.data); From 50dd02e72b9792a22d39bf73b6d2490451f14f9b Mon Sep 17 00:00:00 2001 From: Itai Handler Date: Tue, 3 Nov 2015 00:20:56 +0200 Subject: [PATCH 305/656] drm/gma500: Fix possible out of bounds read commit 7ccca1d5bf69fdd1d3c5fcf84faf1659a6e0ad11 upstream. Fix possible out of bounds read, by adding missing comma. The code may read pass the end of the dsi_errors array when the most significant bit (bit #31) in the intr_stat register is set. This bug has been detected using CppCheck (static analysis tool). Signed-off-by: Itai Handler Signed-off-by: Patrik Jakobsson Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c index 6b43ae3ffd73..1616af209bfc 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c @@ -72,7 +72,7 @@ static const char *const dsi_errors[] = { "RX Prot Violation", "HS Generic Write FIFO Full", "LP Generic Write FIFO Full", - "Generic Read Data Avail" + "Generic Read Data Avail", "Special Packet Sent", "Tearing Effect", }; From 267706b9c5bacb58aacf631b8cc5b7ec0e8cab22 Mon Sep 17 00:00:00 2001 From: Charmaine Lee Date: Tue, 12 Apr 2016 08:14:23 -0700 Subject: [PATCH 306/656] drm/vmwgfx: Enable SVGA_3D_CMD_DX_SET_PREDICATION commit 1883598d4201361a6d2ce785095695f58071ee11 upstream. Fixes piglit tests nv_conditional_render-* crashes. Signed-off-by: Charmaine Lee Reviewed-by: Brian Paul Reviewed-by: Sinclair Yeh Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 5da5de0cb522..6ae0045449b8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -3285,7 +3285,7 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = { true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_READBACK_QUERY, &vmw_cmd_invalid, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PREDICATION, &vmw_cmd_invalid, + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PREDICATION, &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_VIEWPORTS, &vmw_cmd_dx_cid_check, true, false, true), From c170833402b26fcbaa783bb044b11398724bd388 Mon Sep 17 00:00:00 2001 From: Charmaine Lee Date: Tue, 12 Apr 2016 08:19:08 -0700 Subject: [PATCH 307/656] drm/vmwgfx: use vmw_cmd_dx_cid_check for query commands. commit e02e58843153ce80a9fe7588def89b2638d40e64 upstream. Instead of calling vmw_cmd_ok, call vmw_cmd_dx_cid_check to validate the context id for query commands. Signed-off-by: Charmaine Lee Reviewed-by: Sinclair Yeh Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 6ae0045449b8..4948c1529836 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -3273,15 +3273,15 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = { &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_QUERY, &vmw_cmd_dx_define_query, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_QUERY, &vmw_cmd_ok, + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_QUERY, &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_BIND_QUERY, &vmw_cmd_dx_bind_query, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_QUERY_OFFSET, - &vmw_cmd_ok, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_BEGIN_QUERY, &vmw_cmd_ok, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_BEGIN_QUERY, &vmw_cmd_dx_cid_check, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_END_QUERY, &vmw_cmd_ok, + VMW_CMD_DEF(SVGA_3D_CMD_DX_END_QUERY, &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_READBACK_QUERY, &vmw_cmd_invalid, true, false, true), From 55d851a914b51e79b33e52dabef18fe22ea80a46 Mon Sep 17 00:00:00 2001 From: Sinclair Yeh Date: Thu, 21 Apr 2016 11:29:31 -0700 Subject: [PATCH 308/656] drm/vmwgfx: Fix order of operation commit 7851496a32319237456919575e5f4ba62f74cc7d upstream. mode->hdisplay * (var->bits_per_pixel + 7) gets evaluated before the division, potentially making the pitch larger than it should be. Since the original intention is to do a div-round-up, just use the macro instead. Signed-off-by: Sinclair Yeh Reviewed-by: Thomas Hellstrom Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 3b1faf7862a5..679a4cb98ee3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -573,9 +573,9 @@ static int vmw_fb_set_par(struct fb_info *info) mode = old_mode; old_mode = NULL; } else if (!vmw_kms_validate_mode_vram(vmw_priv, - mode->hdisplay * - (var->bits_per_pixel + 7) / 8, - mode->vdisplay)) { + mode->hdisplay * + DIV_ROUND_UP(var->bits_per_pixel, 8), + mode->vdisplay)) { drm_mode_destroy(vmw_priv->dev, mode); return -EINVAL; } From bf9be90437e6f2a6079b9a00619ba58906997f56 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 2 May 2016 10:24:41 -0400 Subject: [PATCH 309/656] drm/amdgpu: use drm_mode_vrefresh() rather than mode->vrefresh commit 6b8812eb004ee2b24aac8b1a711a0e8e797df3ce upstream. This is a port of radeon commit: 3d2d98ee1af0cf6eebfbd6bff4c17d3601ac1284 drm/radeon: use drm_mode_vrefresh() rather than mode->vrefresh to amdgpu. Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c index 7b7f4aba60c0..fe36caf1b7d7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dpm.c @@ -150,7 +150,7 @@ u32 amdgpu_dpm_get_vrefresh(struct amdgpu_device *adev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { amdgpu_crtc = to_amdgpu_crtc(crtc); if (crtc->enabled && amdgpu_crtc->enabled && amdgpu_crtc->hw_mode.clock) { - vrefresh = amdgpu_crtc->hw_mode.vrefresh; + vrefresh = drm_mode_vrefresh(&amdgpu_crtc->hw_mode); break; } } From 4630a1d74d0a3cc76573fc43fb7c7009265c035a Mon Sep 17 00:00:00 2001 From: Mario Kleiner Date: Tue, 24 May 2016 18:12:43 +0200 Subject: [PATCH 310/656] drm/amdgpu: Fix hdmi deep color support. commit 9d746ab68163d642dae13756b2b3145b2e38cb65 upstream. When porting the hdmi deep color detection code from radeon-kms to amdgpu-kms apparently some kind of copy and paste error happened, attaching an else branch to the wrong if statement. The result is that hdmi deep color mode is always disabled, regardless of gpu and display capabilities and user wishes, as the code mistakenly thinks that the display doesn't provide the required max_tmds_clock limit and falls back to 8 bpc. This patch fixes deep color support, as tested on a R9 380 Tonga Pro + suitable display, and should be backported to all kernels with amdgpu-kms support. Signed-off-by: Mario Kleiner Cc: Alex Deucher Signed-off-by: Alex Deucher Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c index 119cdc2c43e7..7ef2c13921b4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c @@ -194,12 +194,12 @@ int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector) bpc = 8; DRM_DEBUG("%s: HDMI deep color 10 bpc exceeds max tmds clock. Using %d bpc.\n", connector->name, bpc); - } else if (bpc > 8) { - /* max_tmds_clock missing, but hdmi spec mandates it for deep color. */ - DRM_DEBUG("%s: Required max tmds clock for HDMI deep color missing. Using 8 bpc.\n", - connector->name); - bpc = 8; } + } else if (bpc > 8) { + /* max_tmds_clock missing, but hdmi spec mandates it for deep color. */ + DRM_DEBUG("%s: Required max tmds clock for HDMI deep color missing. Using 8 bpc.\n", + connector->name); + bpc = 8; } } From c0217001bc050076131309291ec81310a0146e30 Mon Sep 17 00:00:00 2001 From: Lyude Date: Thu, 12 May 2016 10:56:58 -0400 Subject: [PATCH 311/656] drm/i915/fbdev: Fix num_connector references in intel_fb_initial_config() commit 14a3842a1d5945067d1dd0788f314e14d5b18e5b upstream. During boot time, MST devices usually send a ton of hotplug events irregardless of whether or not any physical hotplugs actually occurred. Hotplugs mean connectors being created/destroyed, and the number of DRM connectors changing under us. This isn't a problem if we use fb_helper->connector_count since we only set it once in the code, however if we use num_connector from struct drm_mode_config we risk it's value changing under us. On top of that, there's even a chance that dev->mode_config.num_connector != fb_helper->connector_count. If the number of connectors happens to increase under us, we'll end up using the wrong array size for memcpy and start writing beyond the actual length of the array, occasionally resulting in kernel panics. Note: This is just polish for 4.7, Dave Airlie's drm_connector refcounting fixed these bugs for real. But it's good enough duct-tape for stable kernel backporting, since backporting the refcounting changes is way too invasive. Signed-off-by: Lyude [danvet: Clarify why we need this.] Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1463065021-18280-2-git-send-email-cpaul@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_fbdev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c index 4fd5fdfef6bd..c0c094d5b822 100644 --- a/drivers/gpu/drm/i915/intel_fbdev.c +++ b/drivers/gpu/drm/i915/intel_fbdev.c @@ -362,12 +362,12 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, uint64_t conn_configured = 0, mask; int pass = 0; - save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), + save_enabled = kcalloc(fb_helper->connector_count, sizeof(bool), GFP_KERNEL); if (!save_enabled) return false; - memcpy(save_enabled, enabled, dev->mode_config.num_connector); + memcpy(save_enabled, enabled, fb_helper->connector_count); mask = (1 << fb_helper->connector_count) - 1; retry: for (i = 0; i < fb_helper->connector_count; i++) { @@ -501,7 +501,7 @@ retry: if (fallback) { bail: DRM_DEBUG_KMS("Not using firmware configuration\n"); - memcpy(enabled, save_enabled, dev->mode_config.num_connector); + memcpy(enabled, save_enabled, fb_helper->connector_count); kfree(save_enabled); return false; } From c5b424e7fd4204a16a0884c2e544ff14083cd5bd Mon Sep 17 00:00:00 2001 From: Lyude Date: Thu, 12 May 2016 10:56:59 -0400 Subject: [PATCH 312/656] drm/fb_helper: Fix references to dev->mode_config.num_connector commit 255f0e7c418ad95a4baeda017ae6182ba9b3c423 upstream. During boot, MST hotplugs are generally expected (even if no physical hotplugging occurs) and result in DRM's connector topology changing. This means that using num_connector from the current mode configuration can lead to the number of connectors changing under us. This can lead to some nasty scenarios in fbcon: - We allocate an array to the size of dev->mode_config.num_connectors. - MST hotplug occurs, dev->mode_config.num_connectors gets incremented. - We try to loop through each element in the array using the new value of dev->mode_config.num_connectors, and end up going out of bounds since dev->mode_config.num_connectors is now larger then the array we allocated. fb_helper->connector_count however, will always remain consistent while we do a modeset in fb_helper. Note: This is just polish for 4.7, Dave Airlie's drm_connector refcounting fixed these bugs for real. But it's good enough duct-tape for stable kernel backporting, since backporting the refcounting changes is way too invasive. Signed-off-by: Lyude [danvet: Clarify why we need this. Also remove the now unused "dev" local variable to appease gcc.] Signed-off-by: Daniel Vetter Link: http://patchwork.freedesktop.org/patch/msgid/1463065021-18280-3-git-send-email-cpaul@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/drm_fb_helper.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 69cbab5e5c81..5ad036741b99 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1899,7 +1899,6 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, int n, int width, int height) { int c, o; - struct drm_device *dev = fb_helper->dev; struct drm_connector *connector; const struct drm_connector_helper_funcs *connector_funcs; struct drm_encoder *encoder; @@ -1918,7 +1917,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, if (modes[n] == NULL) return best_score; - crtcs = kzalloc(dev->mode_config.num_connector * + crtcs = kzalloc(fb_helper->connector_count * sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); if (!crtcs) return best_score; @@ -1964,7 +1963,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, if (score > best_score) { best_score = score; memcpy(best_crtcs, crtcs, - dev->mode_config.num_connector * + fb_helper->connector_count * sizeof(struct drm_fb_helper_crtc *)); } } From 8453324b7f4968ad2ebd90c9831c05f9801d92c3 Mon Sep 17 00:00:00 2001 From: Lyude Date: Tue, 31 May 2016 12:49:07 -0400 Subject: [PATCH 313/656] drm/atomic: Verify connector->funcs != NULL when clearing states Unfortunately since we don't have Dave's connector refcounting patch here yet, it's very possible that drm_atomic_state_default_clear() could get called by intel_display_resume() when intel_dp_mst_destroy_connector() isn't completely finished destroying an mst connector, but has already finished setting connector->funcs to NULL. As such, we need to treat the connector like it's already been destroyed and just skip it, otherwise we'll end up dereferencing a NULL pointer. This fix is only required for 4.6 and below. David Airlie's patchseries for 4.7 to add connector reference counting provides a more proper fix for this. Changes since v1: - Fix leftover whitespace Upstream fix: 0552f7651bc2 ("drm/i915/mst: use reference counted connectors. (v3)") Reviewed-by: Daniel Vetter Signed-off-by: Lyude --- drivers/gpu/drm/drm_atomic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index aeee083c7f95..aed2e3f8a1a2 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -150,7 +150,7 @@ void drm_atomic_state_default_clear(struct drm_atomic_state *state) for (i = 0; i < state->num_connector; i++) { struct drm_connector *connector = state->connectors[i]; - if (!connector) + if (!connector || !connector->funcs) continue; /* From d7d5e9bed91c75e765f9219a58199ce9bf5e3db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 13 May 2016 17:55:17 +0300 Subject: [PATCH 314/656] drm/i915: Don't leave old junk in ilk active watermarks on readout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 7045c3689f148a0c95f42bae8ef3eb2829ac7de9 upstream. When we read out the watermark state from the hardware we're supposed to transfer that into the active watermarks, but currently we fail to any part of the active watermarks that isn't explicitly written. Let's clear it all upfront. Looks like this has been like this since the beginning, when I added the readout. No idea why I didn't clear it up. Cc: Matt Roper Fixes: 243e6a44b9ca ("drm/i915: Init HSW watermark tracking in intel_modeset_setup_hw_state()") Signed-off-by: Ville Syrjälä Reviewed-by: Matt Roper Signed-off-by: Matt Roper Link: http://patchwork.freedesktop.org/patch/msgid/1463151318-14719-2-git-send-email-ville.syrjala@linux.intel.com (cherry picked from commit 15606534bf0a65d8a74a90fd57b8712d147dbca6) Signed-off-by: Jani Nikula Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/i915/intel_pm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 0a68d2ec89dc..62284e45d531 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3880,6 +3880,8 @@ static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc) if (IS_HASWELL(dev) || IS_BROADWELL(dev)) hw->wm_linetime[pipe] = I915_READ(PIPE_WM_LINETIME(pipe)); + memset(active, 0, sizeof(*active)); + active->pipe_enabled = intel_crtc->active; if (active->pipe_enabled) { From 137bd12493e76f610aa56cee1e63e70b3707f6d8 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 12 May 2016 15:00:44 +0200 Subject: [PATCH 315/656] drm/imx: Match imx-ipuv3-crtc components using device node in platform data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 310944d148e3600dcff8b346bee7fa01d34903b1 upstream. The component master driver imx-drm-core matches component devices using their of_node. Since commit 950b410dd1ab ("gpu: ipu-v3: Fix imx-ipuv3-crtc module autoloading"), the imx-ipuv3-crtc dev->of_node is not set during probing. Before that, of_node was set and caused an of: modalias to be used instead of the platform: modalias, which broke module autoloading. On the other hand, if dev->of_node is not set yet when the imx-ipuv3-crtc probe function calls component_add, component matching in imx-drm-core fails. While dev->of_node will be set once the next component tries to bring up the component master, imx-drm-core component binding will never succeed if one of the crtc devices is probed last. Add of_node to the component platform data and match against the pdata->of_node instead of dev->of_node in imx-drm-core to work around this problem. Fixes: 950b410dd1ab ("gpu: ipu-v3: Fix imx-ipuv3-crtc module autoloading") Signed-off-by: Philipp Zabel Tested-by: Fabio Estevam Tested-by: Lothar Waßmann Tested-by: Heiko Schocher Tested-by: Chris Ruehl Signed-off-by: Greg Kroah-Hartman --- drivers/gpu/drm/imx/imx-drm-core.c | 8 ++++++++ drivers/gpu/drm/imx/ipuv3-crtc.c | 2 +- drivers/gpu/ipu-v3/ipu-common.c | 5 +++-- include/video/imx-ipu-v3.h | 2 ++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 7b990b4e96d2..5378bdc3bbf9 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -26,6 +26,7 @@ #include #include #include +#include