diff options
35 files changed, 1345 insertions, 620 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index d696438..e599d41 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9432,6 +9432,7 @@ F: include/*/ftrace.h F: include/linux/trace*.h F: include/trace/ F: kernel/trace/ +F: tools/testing/selftests/ftrace/ TRIVIAL PATCHES M: Jiri Kosina <trivial@kernel.org> diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 390e8e3..25721bf 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -163,18 +163,24 @@ static void dell_wmi_notify(u32 value, void *context) const struct key_entry *key; int reported_key; u16 *buffer_entry = (u16 *)obj->buffer.pointer; + int buffer_size = obj->buffer.length/2; - if (dell_new_hk_type && (buffer_entry[1] != 0x10)) { + if (buffer_size >= 2 && dell_new_hk_type && buffer_entry[1] != 0x10) { pr_info("Received unknown WMI event (0x%x)\n", buffer_entry[1]); kfree(obj); return; } - if (dell_new_hk_type || buffer_entry[1] == 0x0) + if (buffer_size >= 3 && (dell_new_hk_type || buffer_entry[1] == 0x0)) reported_key = (int)buffer_entry[2]; - else + else if (buffer_size >= 2) reported_key = (int)buffer_entry[1] & 0xffff; + else { + pr_info("Received unknown WMI event\n"); + kfree(obj); + return; + } key = sparse_keymap_entry_from_scancode(dell_wmi_input_dev, reported_key); diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index bd533c2..db79902 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -263,13 +263,11 @@ static int acpi_setter_handle(struct eeepc_laptop *eeepc, int cm, /* * Sys helpers */ -static int parse_arg(const char *buf, unsigned long count, int *val) +static int parse_arg(const char *buf, int *val) { - if (!count) - return 0; if (sscanf(buf, "%i", val) != 1) return -EINVAL; - return count; + return 0; } static ssize_t store_sys_acpi(struct device *dev, int cm, @@ -278,12 +276,13 @@ static ssize_t store_sys_acpi(struct device *dev, int cm, struct eeepc_laptop *eeepc = dev_get_drvdata(dev); int rv, value; - rv = parse_arg(buf, count, &value); - if (rv > 0) - value = set_acpi(eeepc, cm, value); - if (value < 0) + rv = parse_arg(buf, &value); + if (rv < 0) + return rv; + rv = set_acpi(eeepc, cm, value); + if (rv < 0) return -EIO; - return rv; + return count; } static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf) @@ -296,30 +295,34 @@ static ssize_t show_sys_acpi(struct device *dev, int cm, char *buf) return sprintf(buf, "%d\n", value); } -#define EEEPC_CREATE_DEVICE_ATTR(_name, _mode, _cm) \ - static ssize_t show_##_name(struct device *dev, \ +#define EEEPC_ACPI_SHOW_FUNC(_name, _cm) \ + static ssize_t _name##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ return show_sys_acpi(dev, _cm, buf); \ - } \ - static ssize_t store_##_name(struct device *dev, \ + } + +#define EEEPC_ACPI_STORE_FUNC(_name, _cm) \ + static ssize_t _name##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ return store_sys_acpi(dev, _cm, buf, count); \ - } \ - static struct device_attribute dev_attr_##_name = { \ - .attr = { \ - .name = __stringify(_name), \ - .mode = _mode }, \ - .show = show_##_name, \ - .store = store_##_name, \ } -EEEPC_CREATE_DEVICE_ATTR(camera, 0644, CM_ASL_CAMERA); -EEEPC_CREATE_DEVICE_ATTR(cardr, 0644, CM_ASL_CARDREADER); -EEEPC_CREATE_DEVICE_ATTR(disp, 0200, CM_ASL_DISPLAYSWITCH); +#define EEEPC_CREATE_DEVICE_ATTR_RW(_name, _cm) \ + EEEPC_ACPI_SHOW_FUNC(_name, _cm) \ + EEEPC_ACPI_STORE_FUNC(_name, _cm) \ + static DEVICE_ATTR_RW(_name) + +#define EEEPC_CREATE_DEVICE_ATTR_WO(_name, _cm) \ + EEEPC_ACPI_STORE_FUNC(_name, _cm) \ + static DEVICE_ATTR_WO(_name) + +EEEPC_CREATE_DEVICE_ATTR_RW(camera, CM_ASL_CAMERA); +EEEPC_CREATE_DEVICE_ATTR_RW(cardr, CM_ASL_CARDREADER); +EEEPC_CREATE_DEVICE_ATTR_WO(disp, CM_ASL_DISPLAYSWITCH); struct eeepc_cpufv { int num; @@ -329,14 +332,17 @@ struct eeepc_cpufv { static int get_cpufv(struct eeepc_laptop *eeepc, struct eeepc_cpufv *c) { c->cur = get_acpi(eeepc, CM_ASL_CPUFV); + if (c->cur < 0) + return -ENODEV; + c->num = (c->cur >> 8) & 0xff; c->cur &= 0xff; - if (c->cur < 0 || c->num <= 0 || c->num > 12) + if (c->num == 0 || c->num > 12) return -ENODEV; return 0; } -static ssize_t show_available_cpufv(struct device *dev, +static ssize_t available_cpufv_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -353,7 +359,7 @@ static ssize_t show_available_cpufv(struct device *dev, return len; } -static ssize_t show_cpufv(struct device *dev, +static ssize_t cpufv_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -365,7 +371,7 @@ static ssize_t show_cpufv(struct device *dev, return sprintf(buf, "%#x\n", (c.num << 8) | c.cur); } -static ssize_t store_cpufv(struct device *dev, +static ssize_t cpufv_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -377,16 +383,18 @@ static ssize_t store_cpufv(struct device *dev, return -EPERM; if (get_cpufv(eeepc, &c)) return -ENODEV; - rv = parse_arg(buf, count, &value); + rv = parse_arg(buf, &value); if (rv < 0) return rv; - if (!rv || value < 0 || value >= c.num) + if (value < 0 || value >= c.num) return -EINVAL; - set_acpi(eeepc, CM_ASL_CPUFV, value); - return rv; + rv = set_acpi(eeepc, CM_ASL_CPUFV, value); + if (rv) + return rv; + return count; } -static ssize_t show_cpufv_disabled(struct device *dev, +static ssize_t cpufv_disabled_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -395,14 +403,14 @@ static ssize_t show_cpufv_disabled(struct device *dev, return sprintf(buf, "%d\n", eeepc->cpufv_disabled); } -static ssize_t store_cpufv_disabled(struct device *dev, +static ssize_t cpufv_disabled_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct eeepc_laptop *eeepc = dev_get_drvdata(dev); int rv, value; - rv = parse_arg(buf, count, &value); + rv = parse_arg(buf, &value); if (rv < 0) return rv; @@ -412,7 +420,7 @@ static ssize_t store_cpufv_disabled(struct device *dev, pr_warn("cpufv enabled (not officially supported " "on this model)\n"); eeepc->cpufv_disabled = false; - return rv; + return count; case 1: return -EPERM; default: @@ -421,29 +429,9 @@ static ssize_t store_cpufv_disabled(struct device *dev, } -static struct device_attribute dev_attr_cpufv = { - .attr = { - .name = "cpufv", - .mode = 0644 }, - .show = show_cpufv, - .store = store_cpufv -}; - -static struct device_attribute dev_attr_available_cpufv = { - .attr = { - .name = "available_cpufv", - .mode = 0444 }, - .show = show_available_cpufv -}; - -static struct device_attribute dev_attr_cpufv_disabled = { - .attr = { - .name = "cpufv_disabled", - .mode = 0644 }, - .show = show_cpufv_disabled, - .store = store_cpufv_disabled -}; - +static DEVICE_ATTR_RW(cpufv); +static DEVICE_ATTR_RO(available_cpufv); +static DEVICE_ATTR_RW(cpufv_disabled); static struct attribute *platform_attributes[] = { &dev_attr_camera.attr, @@ -545,7 +533,7 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc) eeepc->tpd_led.name = "eeepc::touchpad"; eeepc->tpd_led.brightness_set = tpd_led_set; if (get_acpi(eeepc, CM_ASL_TPD) >= 0) /* if method is available */ - eeepc->tpd_led.brightness_get = tpd_led_get; + eeepc->tpd_led.brightness_get = tpd_led_get; eeepc->tpd_led.max_brightness = 1; rv = led_classdev_register(&eeepc->platform_device->dev, @@ -680,22 +668,21 @@ static int eeepc_register_rfkill_notifier(struct eeepc_laptop *eeepc, status = acpi_get_handle(NULL, node, &handle); - if (ACPI_SUCCESS(status)) { - status = acpi_install_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - eeepc_rfkill_notify, - eeepc); - if (ACPI_FAILURE(status)) - pr_warn("Failed to register notify on %s\n", node); - - /* - * Refresh pci hotplug in case the rfkill state was - * changed during setup. - */ - eeepc_rfkill_hotplug(eeepc, handle); - } else + if (ACPI_FAILURE(status)) return -ENODEV; + status = acpi_install_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + eeepc_rfkill_notify, + eeepc); + if (ACPI_FAILURE(status)) + pr_warn("Failed to register notify on %s\n", node); + + /* + * Refresh pci hotplug in case the rfkill state was + * changed during setup. + */ + eeepc_rfkill_hotplug(eeepc, handle); return 0; } @@ -707,20 +694,21 @@ static void eeepc_unregister_rfkill_notifier(struct eeepc_laptop *eeepc, status = acpi_get_handle(NULL, node, &handle); - if (ACPI_SUCCESS(status)) { - status = acpi_remove_notify_handler(handle, - ACPI_SYSTEM_NOTIFY, - eeepc_rfkill_notify); - if (ACPI_FAILURE(status)) - pr_err("Error removing rfkill notify handler %s\n", - node); - /* - * Refresh pci hotplug in case the rfkill - * state was changed after - * eeepc_unregister_rfkill_notifier() - */ - eeepc_rfkill_hotplug(eeepc, handle); - } + if (ACPI_FAILURE(status)) + return; + + status = acpi_remove_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + eeepc_rfkill_notify); + if (ACPI_FAILURE(status)) + pr_err("Error removing rfkill notify handler %s\n", + node); + /* + * Refresh pci hotplug in case the rfkill + * state was changed after + * eeepc_unregister_rfkill_notifier() + */ + eeepc_rfkill_hotplug(eeepc, handle); } static int eeepc_get_adapter_status(struct hotplug_slot *hotplug_slot, @@ -1042,10 +1030,11 @@ static ssize_t store_sys_hwmon(void (*set)(int), const char *buf, size_t count) { int rv, value; - rv = parse_arg(buf, count, &value); - if (rv > 0) - set(value); - return rv; + rv = parse_arg(buf, &value); + if (rv < 0) + return rv; + set(value); + return count; } static ssize_t show_sys_hwmon(int (*get)(void), char *buf) @@ -1053,26 +1042,36 @@ static ssize_t show_sys_hwmon(int (*get)(void), char *buf) return sprintf(buf, "%d\n", get()); } -#define EEEPC_CREATE_SENSOR_ATTR(_name, _mode, _get, _set) \ - static ssize_t show_##_name(struct device *dev, \ +#define EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ + static ssize_t _name##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ return show_sys_hwmon(_get, buf); \ - } \ - static ssize_t store_##_name(struct device *dev, \ + } + +#define EEEPC_SENSOR_STORE_FUNC(_name, _set) \ + static ssize_t _name##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ { \ return store_sys_hwmon(_set, buf, count); \ - } \ - static DEVICE_ATTR(_name, _mode, show_##_name, store_##_name) + } + +#define EEEPC_CREATE_SENSOR_ATTR_RW(_name, _get, _set) \ + EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ + EEEPC_SENSOR_STORE_FUNC(_name, _set) \ + static DEVICE_ATTR_RW(_name) -EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL); -EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR, - eeepc_get_fan_pwm, eeepc_set_fan_pwm); -EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, - eeepc_get_fan_ctrl, eeepc_set_fan_ctrl); +#define EEEPC_CREATE_SENSOR_ATTR_RO(_name, _get) \ + EEEPC_SENSOR_SHOW_FUNC(_name, _get) \ + static DEVICE_ATTR_RO(_name) + +EEEPC_CREATE_SENSOR_ATTR_RO(fan1_input, eeepc_get_fan_rpm); +EEEPC_CREATE_SENSOR_ATTR_RW(pwm1, eeepc_get_fan_pwm, + eeepc_set_fan_pwm); +EEEPC_CREATE_SENSOR_ATTR_RW(pwm1_enable, eeepc_get_fan_ctrl, + eeepc_set_fan_ctrl); static struct attribute *hwmon_attrs[] = { &dev_attr_pwm1.attr, @@ -1424,8 +1423,9 @@ static int eeepc_acpi_add(struct acpi_device *device) result = eeepc_backlight_init(eeepc); if (result) goto fail_backlight; - } else + } else { pr_info("Backlight controlled by ACPI video driver\n"); + } result = eeepc_input_init(eeepc); if (result) diff --git a/drivers/platform/x86/intel-rst.c b/drivers/platform/x86/intel-rst.c index d45bca3..7344d84 100644 --- a/drivers/platform/x86/intel-rst.c +++ b/drivers/platform/x86/intel-rst.c @@ -35,7 +35,7 @@ static ssize_t irst_show_wakeup_events(struct device *dev, acpi = to_acpi_device(dev); status = acpi_evaluate_integer(acpi->handle, "GFFS", NULL, &value); - if (!ACPI_SUCCESS(status)) + if (ACPI_FAILURE(status)) return -EINVAL; return sprintf(buf, "%lld\n", value); @@ -59,7 +59,7 @@ static ssize_t irst_store_wakeup_events(struct device *dev, status = acpi_execute_simple_method(acpi->handle, "SFFS", value); - if (!ACPI_SUCCESS(status)) + if (ACPI_FAILURE(status)) return -EINVAL; return count; @@ -81,7 +81,7 @@ static ssize_t irst_show_wakeup_time(struct device *dev, acpi = to_acpi_device(dev); status = acpi_evaluate_integer(acpi->handle, "GFTV", NULL, &value); - if (!ACPI_SUCCESS(status)) + if (ACPI_FAILURE(status)) return -EINVAL; return sprintf(buf, "%lld\n", value); @@ -105,7 +105,7 @@ static ssize_t irst_store_wakeup_time(struct device *dev, status = acpi_execute_simple_method(acpi->handle, "SFTV", value); - if (!ACPI_SUCCESS(status)) + if (ACPI_FAILURE(status)) return -EINVAL; return count; @@ -119,21 +119,16 @@ static struct device_attribute irst_timeout_attr = { static int irst_add(struct acpi_device *acpi) { - int error = 0; + int error; error = device_create_file(&acpi->dev, &irst_timeout_attr); - if (error) - goto out; + if (unlikely(error)) + return error; error = device_create_file(&acpi->dev, &irst_wakeup_attr); - if (error) - goto out_timeout; + if (unlikely(error)) + device_remove_file(&acpi->dev, &irst_timeout_attr); - return 0; - -out_timeout: - device_remove_file(&acpi->dev, &irst_timeout_attr); -out: return error; } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 3bbc6eb..f959978 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3440,7 +3440,7 @@ err_exit: delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); hotkey_dev_attributes = NULL; - return (res < 0)? res : 1; + return (res < 0) ? res : 1; } /* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser @@ -4576,7 +4576,7 @@ static int __init video_init(struct ibm_init_struct *iibm) str_supported(video_supported != TPACPI_VIDEO_NONE), video_supported); - return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1; + return (video_supported != TPACPI_VIDEO_NONE) ? 0 : 1; } static void video_exit(void) @@ -4669,7 +4669,7 @@ static int video_outputsw_set(int status) return -ENOSYS; } - return (res)? 0 : -EIO; + return (res) ? 0 : -EIO; } static int video_autosw_get(void) @@ -4695,7 +4695,7 @@ static int video_autosw_get(void) static int video_autosw_set(int enable) { - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0)) + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable) ? 1 : 0)) return -EIO; return 0; } @@ -4730,20 +4730,20 @@ static int video_outputsw_cycle(void) return -EIO; } - return (res)? 0 : -EIO; + return (res) ? 0 : -EIO; } static int video_expand_toggle(void) { switch (video_supported) { case TPACPI_VIDEO_570: - return acpi_evalf(ec_handle, NULL, "_Q17", "v")? + return acpi_evalf(ec_handle, NULL, "_Q17", "v") ? 0 : -EIO; case TPACPI_VIDEO_770: - return acpi_evalf(vid_handle, NULL, "VEXP", "v")? + return acpi_evalf(vid_handle, NULL, "VEXP", "v") ? 0 : -EIO; case TPACPI_VIDEO_NEW: - return acpi_evalf(NULL, NULL, "\\VEXP", "v")? + return acpi_evalf(NULL, NULL, "\\VEXP", "v") ? 0 : -EIO; default: return -ENOSYS; @@ -4887,14 +4887,14 @@ static int light_set_status(int status) if (tp_features.light) { if (cmos_handle) { rc = acpi_evalf(cmos_handle, NULL, NULL, "vd", - (status)? + (status) ? TP_CMOS_THINKLIGHT_ON : TP_CMOS_THINKLIGHT_OFF); } else { rc = acpi_evalf(lght_handle, NULL, NULL, "vd", - (status)? 1 : 0); + (status) ? 1 : 0); } - return (rc)? 0 : -EIO; + return (rc) ? 0 : -EIO; } return -ENXIO; @@ -4923,7 +4923,7 @@ static void light_sysfs_set(struct led_classdev *led_cdev, static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev) { - return (light_get_status() == 1)? LED_FULL : LED_OFF; + return (light_get_status() == 1) ? LED_FULL : LED_OFF; } static struct tpacpi_led_classdev tpacpi_led_thinklight = { @@ -5045,7 +5045,7 @@ static ssize_t cmos_command_store(struct device *dev, return -EINVAL; res = issue_thinkpad_cmos_command(cmos_cmd); - return (res)? res : count; + return (res) ? res : count; } static struct device_attribute dev_attr_cmos_command = @@ -5069,7 +5069,7 @@ static int __init cmos_init(struct ibm_init_struct *iibm) if (res) return res; - return (cmos_handle)? 0 : 1; + return (cmos_handle) ? 0 : 1; } static void cmos_exit(void) @@ -5179,9 +5179,9 @@ static int led_get_status(const unsigned int led) if (!acpi_evalf(ec_handle, &status, "GLED", "dd", 1 << led)) return -EIO; - led_s = (status == 0)? + led_s = (status == 0) ? TPACPI_LED_OFF : - ((status == 1)? + ((status == 1) ? TPACPI_LED_ON : TPACPI_LED_BLINK); tpacpi_led_state_cache[led] = led_s; @@ -5578,7 +5578,7 @@ static int __init beep_init(struct ibm_init_struct *iibm) tp_features.beep_needs_two_args = !!(quirks & TPACPI_BEEP_Q1); - return (beep_handle)? 0 : 1; + return (beep_handle) ? 0 : 1; } static int beep_read(struct seq_file *m) @@ -6527,7 +6527,7 @@ static int brightness_write(char *buf) if (!rc && ibm_backlight_device) backlight_force_update(ibm_backlight_device, BACKLIGHT_UPDATE_SYSFS); - return (rc == -EINTR)? -ERESTARTSYS : rc; + return (rc == -EINTR) ? -ERESTARTSYS : rc; } static struct ibm_struct brightness_driver_data = { @@ -7984,7 +7984,7 @@ static ssize_t fan_pwm1_store(struct device *dev, } mutex_unlock(&fan_mutex); - return (rc)? rc : count; + return (rc) ? rc : count; } static struct device_attribute dev_attr_fan_pwm1 = @@ -8662,7 +8662,7 @@ static const char * __init str_supported(int is_supported) { static char text_unsupported[] __initdata = "not supported"; - return (is_supported)? &text_unsupported[4] : &text_unsupported[0]; + return (is_supported) ? &text_unsupported[4] : &text_unsupported[0]; } #endif /* CONFIG_THINKPAD_ACPI_DEBUG */ @@ -8783,7 +8783,7 @@ err_out: ibm->name, ret); ibm_exit(ibm); - return (ret < 0)? ret : 0; + return (ret < 0) ? ret : 0; } /* Probing */ @@ -8794,7 +8794,7 @@ static bool __pure __init tpacpi_is_fw_digit(const char c) } /* Most models: xxyTkkWW (#.##c); Ancient 570/600 and -SL lacks (#.##c) */ -static bool __pure __init tpacpi_is_valid_fw_id(const char* const s, +static bool __pure __init tpacpi_is_valid_fw_id(const char * const s, const char t) { return s && strlen(s) >= 8 && diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index d0dce73..ef3a190 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -71,7 +71,8 @@ MODULE_LICENSE("GPL"); /* Toshiba ACPI method paths */ #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" -/* Toshiba HCI interface definitions +/* The Toshiba configuration interface is composed of the HCI and the SCI, + * which are defined as follows: * * HCI is Toshiba's "Hardware Control Interface" which is supposed to * be uniform across all their models. Ideally we would just call @@ -84,7 +85,7 @@ MODULE_LICENSE("GPL"); * conceal differences in hardware between different models. */ -#define HCI_WORDS 6 +#define TCI_WORDS 6 /* operations */ #define HCI_SET 0xff00 @@ -95,17 +96,18 @@ MODULE_LICENSE("GPL"); #define SCI_SET 0xf400 /* return codes */ -#define HCI_SUCCESS 0x0000 -#define HCI_FAILURE 0x1000 -#define HCI_NOT_SUPPORTED 0x8000 -#define HCI_EMPTY 0x8c00 -#define HCI_DATA_NOT_AVAILABLE 0x8d20 -#define HCI_NOT_INITIALIZED 0x8d50 -#define SCI_OPEN_CLOSE_OK 0x0044 -#define SCI_ALREADY_OPEN 0x8100 -#define SCI_NOT_OPENED 0x8200 -#define SCI_INPUT_DATA_ERROR 0x8300 -#define SCI_NOT_PRESENT 0x8600 +#define TOS_SUCCESS 0x0000 +#define TOS_OPEN_CLOSE_OK 0x0044 +#define TOS_FAILURE 0x1000 +#define TOS_NOT_SUPPORTED 0x8000 +#define TOS_ALREADY_OPEN 0x8100 +#define TOS_NOT_OPENED 0x8200 +#define TOS_INPUT_DATA_ERROR 0x8300 +#define TOS_WRITE_PROTECTED 0x8400 +#define TOS_NOT_PRESENT 0x8600 +#define TOS_FIFO_EMPTY 0x8c00 +#define TOS_DATA_NOT_AVAILABLE 0x8d20 +#define TOS_NOT_INITIALIZED 0x8d50 /* registers */ #define HCI_FAN 0x0004 @@ -138,8 +140,12 @@ MODULE_LICENSE("GPL"); #define HCI_WIRELESS_BT_PRESENT 0x0f #define HCI_WIRELESS_BT_ATTACH 0x40 #define HCI_WIRELESS_BT_POWER 0x80 +#define SCI_KBD_MODE_MASK 0x1f #define SCI_KBD_MODE_FNZ 0x1 #define SCI_KBD_MODE_AUTO 0x2 +#define SCI_KBD_MODE_ON 0x8 +#define SCI_KBD_MODE_OFF 0x10 +#define SCI_KBD_TIME_MAX 0x3c001a struct toshiba_acpi_dev { struct acpi_device *acpi_dev; @@ -155,6 +161,7 @@ struct toshiba_acpi_dev { int force_fan; int last_key_event; int key_event_valid; + int kbd_type; int kbd_mode; int kbd_time; @@ -190,6 +197,7 @@ static const struct key_entry toshiba_acpi_keymap[] = { { KE_KEY, 0x101, { KEY_MUTE } }, { KE_KEY, 0x102, { KEY_ZOOMOUT } }, { KE_KEY, 0x103, { KEY_ZOOMIN } }, + { KE_KEY, 0x10f, { KEY_TAB } }, { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } }, { KE_KEY, 0x139, { KEY_ZOOMRESET } }, { KE_KEY, 0x13b, { KEY_COFFEE } }, @@ -210,7 +218,11 @@ static const struct key_entry toshiba_acpi_keymap[] = { { KE_KEY, 0xb32, { KEY_NEXTSONG } }, { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, { KE_KEY, 0xb5a, { KEY_MEDIA } }, - { KE_IGNORE, 0x1430, { KEY_RESERVED } }, + { KE_IGNORE, 0x1430, { KEY_RESERVED } }, /* Wake from sleep */ + { KE_IGNORE, 0x1501, { KEY_RESERVED } }, /* Output changed */ + { KE_IGNORE, 0x1502, { KEY_RESERVED } }, /* HDMI plugged/unplugged */ + { KE_IGNORE, 0x1ABE, { KEY_RESERVED } }, /* Protection level set */ + { KE_IGNORE, 0x1ABF, { KEY_RESERVED } }, /* Protection level off */ { KE_END, 0 }, }; @@ -264,22 +276,22 @@ static int write_acpi_int(const char *methodName, int val) return (status == AE_OK) ? 0 : -EIO; } -/* Perform a raw HCI call. Here we don't care about input or output buffer - * format. +/* Perform a raw configuration call. Here we don't care about input or output + * buffer format. */ -static acpi_status hci_raw(struct toshiba_acpi_dev *dev, - const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) +static acpi_status tci_raw(struct toshiba_acpi_dev *dev, + const u32 in[TCI_WORDS], u32 out[TCI_WORDS]) { struct acpi_object_list params; - union acpi_object in_objs[HCI_WORDS]; + union acpi_object in_objs[TCI_WORDS]; struct acpi_buffer results; - union acpi_object out_objs[HCI_WORDS + 1]; + union acpi_object out_objs[TCI_WORDS + 1]; acpi_status status; int i; - params.count = HCI_WORDS; + params.count = TCI_WORDS; params.pointer = in_objs; - for (i = 0; i < HCI_WORDS; ++i) { + for (i = 0; i < TCI_WORDS; ++i) { in_objs[i].type = ACPI_TYPE_INTEGER; in_objs[i].integer.value = in[i]; } @@ -290,7 +302,7 @@ static acpi_status hci_raw(struct toshiba_acpi_dev *dev, status = acpi_evaluate_object(dev->acpi_dev->handle, (char *)dev->method_hci, ¶ms, &results); - if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { + if ((status == AE_OK) && (out_objs->package.count <= TCI_WORDS)) { for (i = 0; i < out_objs->package.count; ++i) { out[i] = out_objs->package.elements[i].integer.value; } @@ -305,47 +317,49 @@ static acpi_status hci_raw(struct toshiba_acpi_dev *dev, * may be useful (such as "not supported"). */ -static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg, - u32 in1, u32 *result) +static u32 hci_write1(struct toshiba_acpi_dev *dev, u32 reg, u32 in1) { - u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; - u32 out[HCI_WORDS]; - acpi_status status = hci_raw(dev, in, out); - *result = (status == AE_OK) ? out[0] : HCI_FAILURE; - return status; + u32 in[TCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; + u32 out[TCI_WORDS]; + acpi_status status = tci_raw(dev, in, out); + + return ACPI_SUCCESS(status) ? out[0] : TOS_FAILURE; } -static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg, - u32 *out1, u32 *result) +static u32 hci_read1(struct toshiba_acpi_dev *dev, u32 reg, u32 *out1) { - u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; - acpi_status status = hci_raw(dev, in, out); + u32 in[TCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; + u32 out[TCI_WORDS]; + acpi_status status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status)) + return TOS_FAILURE; + *out1 = out[2]; - *result = (status == AE_OK) ? out[0] : HCI_FAILURE; - return status; + + return out[0]; } -static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg, - u32 in1, u32 in2, u32 *result) +static u32 hci_write2(struct toshiba_acpi_dev *dev, u32 reg, u32 in1, u32 in2) { - u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 }; - u32 out[HCI_WORDS]; - acpi_status status = hci_raw(dev, in, out); - *result = (status == AE_OK) ? out[0] : HCI_FAILURE; - return status; + u32 in[TCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 }; + u32 out[TCI_WORDS]; + acpi_status status = tci_raw(dev, in, out); + + return ACPI_SUCCESS(status) ? out[0] : TOS_FAILURE; } -static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, - u32 *out1, u32 *out2, u32 *result) +static u32 hci_read2(struct toshiba_acpi_dev *dev, u32 reg, u32 *out1, u32 *out2) { - u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 }; - u32 out[HCI_WORDS]; - acpi_status status = hci_raw(dev, in, out); + u32 in[TCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 }; + u32 out[TCI_WORDS]; + acpi_status status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status)) + return TOS_FAILURE; + *out1 = out[2]; *out2 = out[3]; - *result = (status == AE_OK) ? out[0] : HCI_FAILURE; - return status; + + return out[0]; } /* common sci tasks @@ -353,22 +367,22 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, static int sci_open(struct toshiba_acpi_dev *dev) { - u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; + u32 in[TCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; + u32 out[TCI_WORDS]; acpi_status status; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) { pr_err("ACPI call to open SCI failed\n"); return 0; } - if (out[0] == SCI_OPEN_CLOSE_OK) { + if (out[0] == TOS_OPEN_CLOSE_OK) { return 1; - } else if (out[0] == SCI_ALREADY_OPEN) { + } else if (out[0] == TOS_ALREADY_OPEN) { pr_info("Toshiba SCI already opened\n"); return 1; - } else if (out[0] == SCI_NOT_PRESENT) { + } else if (out[0] == TOS_NOT_PRESENT) { pr_info("Toshiba SCI is not present\n"); } @@ -377,61 +391,62 @@ static int sci_open(struct toshiba_acpi_dev *dev) static void sci_close(struct toshiba_acpi_dev *dev) { - u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; + u32 in[TCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; + u32 out[TCI_WORDS]; acpi_status status; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) { pr_err("ACPI call to close SCI failed\n"); return; } - if (out[0] == SCI_OPEN_CLOSE_OK) + if (out[0] == TOS_OPEN_CLOSE_OK) return; - else if (out[0] == SCI_NOT_OPENED) + else if (out[0] == TOS_NOT_OPENED) pr_info("Toshiba SCI not opened\n"); - else if (out[0] == SCI_NOT_PRESENT) + else if (out[0] == TOS_NOT_PRESENT) pr_info("Toshiba SCI is not present\n"); } -static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg, - u32 *out1, u32 *result) +static u32 sci_read(struct toshiba_acpi_dev *dev, u32 reg, u32 *out1) { - u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; - acpi_status status = hci_raw(dev, in, out); + u32 in[TCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 }; + u32 out[TCI_WORDS]; + acpi_status status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status)) + return TOS_FAILURE; + *out1 = out[2]; - *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; - return status; + + return out[0]; } -static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg, - u32 in1, u32 *result) +static u32 sci_write(struct toshiba_acpi_dev *dev, u32 reg, u32 in1) { - u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 }; - u32 out[HCI_WORDS]; - acpi_status status = hci_raw(dev, in, out); - *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; - return status; + u32 in[TCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 }; + u32 out[TCI_WORDS]; + acpi_status status = tci_raw(dev, in, out); + + return ACPI_SUCCESS(status) ? out[0] : TOS_FAILURE; } /* Illumination support */ static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) { - u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; + u32 in[TCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 }; + u32 out[TCI_WORDS]; acpi_status status; if (!sci_open(dev)) return 0; - status = hci_raw(dev, in, out); + status = tci_raw(dev, in, out); sci_close(dev); - if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) { pr_err("ACPI call to query Illumination support failed\n"); return 0; - } else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) { + } else if (out[0] == TOS_NOT_SUPPORTED) { pr_info("Illumination device not available\n"); return 0; } @@ -445,7 +460,6 @@ static void toshiba_illumination_set(struct led_classdev *cdev, struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, led_dev); u32 state, result; - acpi_status status; /* First request : initialize communication. */ if (!sci_open(dev)) @@ -453,12 +467,12 @@ static void toshiba_illumination_set(struct led_classdev *cdev, /* Switch the illumination on/off */ state = brightness ? 1 : 0; - status = sci_write(dev, SCI_ILLUMINATION, state, &result); + result = sci_write(dev, SCI_ILLUMINATION, state); sci_close(dev); - if (ACPI_FAILURE(status)) { + if (result == TOS_FAILURE) { pr_err("ACPI call for illumination failed\n"); return; - } else if (result == HCI_NOT_SUPPORTED) { + } else if (result == TOS_NOT_SUPPORTED) { pr_info("Illumination not supported\n"); return; } @@ -469,19 +483,18 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, led_dev); u32 state, result; - acpi_status status; /*Â First request : initialize communication. */ if (!sci_open(dev)) return LED_OFF; /* Check the illumination */ - status = sci_read(dev, SCI_ILLUMINATION, &state, &result); + result = sci_read(dev, SCI_ILLUMINATION, &state); sci_close(dev); - if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) { pr_err("ACPI call for illumination failed\n"); return LED_OFF; - } else if (result == HCI_NOT_SUPPORTED) { + } else if (result == TOS_NOT_SUPPORTED) { pr_info("Illumination not supported\n"); return LED_OFF; } @@ -490,20 +503,55 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) } /* KBD Illumination */ +static int toshiba_kbd_illum_available(struct toshiba_acpi_dev *dev) +{ + u32 in[TCI_WORDS] = { SCI_GET, SCI_KBD_ILLUM_STATUS, 0, 0, 0, 0 }; + u32 out[TCI_WORDS]; + acpi_status status; + + if (!sci_open(dev)) + return 0; + + status = tci_raw(dev, in, out); + sci_close(dev); + if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) { + pr_err("ACPI call to query kbd illumination support failed\n"); + return 0; + } else if (out[0] == TOS_NOT_SUPPORTED) { + pr_info("Keyboard illumination not available\n"); + return 0; + } + + /* Check for keyboard backlight timeout max value, + * previous kbd backlight implementation set this to + * 0x3c0003, and now the new implementation set this + * to 0x3c001a, use this to distinguish between them + */ + if (out[3] == SCI_KBD_TIME_MAX) + dev->kbd_type = 2; + else + dev->kbd_type = 1; + /* Get the current keyboard backlight mode */ + dev->kbd_mode = out[2] & SCI_KBD_MODE_MASK; + /* Get the current time (1-60 seconds) */ + dev->kbd_time = out[2] >> HCI_MISC_SHIFT; + + return 1; +} + static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) { u32 result; - acpi_status status; if (!sci_open(dev)) return -EIO; - status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result); + result = sci_write(dev, SCI_KBD_ILLUM_STATUS, time); sci_close(dev); - if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) { pr_err("ACPI call to set KBD backlight status failed\n"); return -EIO; - } else if (result == HCI_NOT_SUPPORTED) { + } else if (result == TOS_NOT_SUPPORTED) { pr_info("Keyboard backlight status not supported\n"); return -ENODEV; } @@ -514,17 +562,16 @@ static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time) { u32 result; - acpi_status status; if (!sci_open(dev)) return -EIO; - status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result); + result = sci_read(dev, SCI_KBD_ILLUM_STATUS, time); sci_close(dev); - if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) { pr_err("ACPI call to get KBD backlight status failed\n"); return -EIO; - } else if (result == HCI_NOT_SUPPORTED) { + } else if (result == TOS_NOT_SUPPORTED) { pr_info("Keyboard backlight status not supported\n"); return -ENODEV; } @@ -537,14 +584,13 @@ static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev) struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, kbd_led); u32 state, result; - acpi_status status; /* Check the keyboard backlight state */ - status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result); - if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + result = hci_read1(dev, HCI_KBD_ILLUMINATION, &state); + if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) { pr_err("ACPI call to get the keyboard backlight failed\n"); return LED_OFF; - } else if (result == HCI_NOT_SUPPORTED) { + } else if (result == TOS_NOT_SUPPORTED) { pr_info("Keyboard backlight not supported\n"); return LED_OFF; } @@ -558,15 +604,14 @@ static void toshiba_kbd_backlight_set(struct led_classdev *cdev, struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, kbd_led); u32 state, result; - acpi_status status; /* Set the keyboard backlight state */ state = brightness ? 1 : 0; - status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result); - if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + result = hci_write1(dev, HCI_KBD_ILLUMINATION, state); + if (result == TOS_FAILURE || result == TOS_INPUT_DATA_ERROR) { pr_err("ACPI call to set KBD Illumination mode failed\n"); return; - } else if (result == HCI_NOT_SUPPORTED) { + } else if (result == TOS_NOT_SUPPORTED) { pr_info("Keyboard backlight not supported\n"); return; } @@ -576,17 +621,16 @@ static void toshiba_kbd_backlight_set(struct led_classdev *cdev, static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state) { u32 result; - acpi_status status; if (!sci_open(dev)) return -EIO; - status = sci_write(dev, SCI_TOUCHPAD, state, &result); + result = sci_write(dev, SCI_TOUCHPAD, state); sci_close(dev); - if (ACPI_FAILURE(status)) { + if (result == TOS_FAILURE) { pr_err("ACPI call to set the touchpad failed\n"); return -EIO; - } else if (result == HCI_NOT_SUPPORTED) { + } else if (result == TOS_NOT_SUPPORTED) { return -ENODEV; } @@ -596,17 +640,16 @@ static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state) static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) { u32 result; - acpi_status status; if (!sci_open(dev)) return -EIO; - status = sci_read(dev, SCI_TOUCHPAD, state, &result); + result = sci_read(dev, SCI_TOUCHPAD, state); sci_close(dev); - if (ACPI_FAILURE(status)) { + if (result == TOS_FAILURE) { pr_err("ACPI call to query the touchpad failed\n"); return -EIO; - } else if (result == HCI_NOT_SUPPORTED) { + } else if (result == TOS_NOT_SUPPORTED) { return -ENODEV; } @@ -617,11 +660,11 @@ static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) { acpi_status status; - u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; - u32 out[HCI_WORDS]; + u32 in[TCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; + u32 out[TCI_WORDS]; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) { pr_info("ACPI call to get ECO led failed\n"); return 0; } @@ -633,12 +676,12 @@ static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev { struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, eco_led); - u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; - u32 out[HCI_WORDS]; + u32 in[TCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; + u32 out[TCI_WORDS]; acpi_status status; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) { pr_err("ACPI call to get ECO led failed\n"); return LED_OFF; } @@ -651,14 +694,14 @@ static void toshiba_eco_mode_set_status(struct led_classdev *cdev, { struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, eco_led); - u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 }; - u32 out[HCI_WORDS]; + u32 in[TCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 }; + u32 out[TCI_WORDS]; acpi_status status; /* Switch the Eco Mode led on/off */ in[2] = (brightness) ? 1 : 0; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) { pr_err("ACPI call to set ECO led failed\n"); return; } @@ -667,22 +710,22 @@ static void toshiba_eco_mode_set_status(struct led_classdev *cdev, /* Accelerometer support */ static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev) { - u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; + u32 in[TCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 }; + u32 out[TCI_WORDS]; acpi_status status; /* Check if the accelerometer call exists, * this call also serves as initialization */ - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) { pr_err("ACPI call to query the accelerometer failed\n"); return -EIO; - } else if (out[0] == HCI_DATA_NOT_AVAILABLE || - out[0] == HCI_NOT_INITIALIZED) { + } else if (out[0] == TOS_DATA_NOT_AVAILABLE || + out[0] == TOS_NOT_INITIALIZED) { pr_err("Accelerometer not initialized\n"); return -EIO; - } else if (out[0] == HCI_NOT_SUPPORTED) { + } else if (out[0] == TOS_NOT_SUPPORTED) { pr_info("Accelerometer not supported\n"); return -ENODEV; } @@ -693,13 +736,13 @@ static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev) static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev, u32 *xy, u32 *z) { - u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 }; - u32 out[HCI_WORDS]; + u32 in[TCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 }; + u32 out[TCI_WORDS]; acpi_status status; /* Check the Accelerometer status */ - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == TOS_INPUT_DATA_ERROR) { pr_err("ACPI call to query the accelerometer failed\n"); return -EIO; } @@ -719,8 +762,8 @@ static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present) value = 0; value2 = 0; - hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); - if (hci_result == HCI_SUCCESS) + hci_result = hci_read2(dev, HCI_WIRELESS, &value, &value2); + if (hci_result == TOS_SUCCESS) *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false; return hci_result; @@ -733,7 +776,7 @@ static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state) value = 0; value2 = 0x0001; - hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); + hci_result = hci_read2(dev, HCI_WIRELESS, &value, &value2); *radio_state = value & HCI_WIRELESS_KILL_SWITCH; return hci_result; @@ -750,7 +793,7 @@ static int bt_rfkill_set_block(void *data, bool blocked) value = (blocked == false); mutex_lock(&dev->mutex); - if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) { + if (hci_get_radio_state(dev, &radio_state) != TOS_SUCCESS) { err = -EIO; goto out; } @@ -760,10 +803,10 @@ static int bt_rfkill_set_block(void *data, bool blocked) goto out; } - hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); - hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); + result1 = hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER); + result2 = hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH); - if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) + if (result1 != TOS_SUCCESS || result2 != TOS_SUCCESS) err = -EIO; else err = 0; @@ -782,7 +825,7 @@ static void bt_rfkill_poll(struct rfkill *rfkill, void *data) mutex_lock(&dev->mutex); hci_result = hci_get_radio_state(dev, &value); - if (hci_result != HCI_SUCCESS) { + if (hci_result != TOS_SUCCESS) { /* Can't do anything useful */ mutex_unlock(&dev->mutex); return; @@ -806,9 +849,9 @@ static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, bool *enabled) u32 hci_result; u32 status; - hci_read1(dev, HCI_TR_BACKLIGHT, &status, &hci_result); + hci_result = hci_read1(dev, HCI_TR_BACKLIGHT, &status); *enabled = !status; - return hci_result == HCI_SUCCESS ? 0 : -EIO; + return hci_result == TOS_SUCCESS ? 0 : -EIO; } static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable) @@ -816,8 +859,8 @@ static int set_tr_backlight_status(struct toshiba_acpi_dev *dev, bool enable) u32 hci_result; u32 value = !enable; - hci_write1(dev, HCI_TR_BACKLIGHT, value, &hci_result); - return hci_result == HCI_SUCCESS ? 0 : -EIO; + hci_result = hci_write1(dev, HCI_TR_BACKLIGHT, value); + return hci_result == TOS_SUCCESS ? 0 : -EIO; } static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; @@ -838,8 +881,8 @@ static int __get_lcd_brightness(struct toshiba_acpi_dev *dev) brightness++; } - hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); - if (hci_result == HCI_SUCCESS) + hci_result = hci_read1(dev, HCI_LCD_BRIGHTNESS, &value); + if (hci_result == TOS_SUCCESS) return brightness + (value >> HCI_LCD_BRIGHTNESS_SHIFT); return -EIO; @@ -879,8 +922,8 @@ static int lcd_proc_open(struct inode *inode, struct file *file) static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) { - u32 in[HCI_WORDS] = { HCI_SET, HCI_LCD_BRIGHTNESS, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; + u32 in[TCI_WORDS] = { HCI_SET, HCI_LCD_BRIGHTNESS, 0, 0, 0, 0 }; + u32 out[TCI_WORDS]; acpi_status status; if (dev->tr_backlight_supported) { @@ -893,19 +936,19 @@ static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value) } in[2] = value << HCI_LCD_BRIGHTNESS_SHIFT; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + status = tci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == TOS_FAILURE) { pr_err("ACPI call to set brightness failed"); return -EIO; } /* Extra check for "incomplete" backlight method, where the AML code - * doesn't check for HCI_SET or HCI_GET and returns HCI_SUCCESS, + * doesn't check for HCI_SET or HCI_GET and returns TOS_SUCCESS, * the actual brightness, and in some cases the max brightness. */ if (out[2] > 0 || out[3] == 0xE000) return -ENODEV; - return out[0] == HCI_SUCCESS ? 0 : -EIO; + return out[0] == TOS_SUCCESS ? 0 : -EIO; } static int set_lcd_status(struct backlight_device *bd) @@ -953,8 +996,8 @@ static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status) { u32 hci_result; - hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result); - return hci_result == HCI_SUCCESS ? 0 : -EIO; + hci_result = hci_read1(dev, HCI_VIDEO_OUT, status); + return hci_result == TOS_SUCCESS ? 0 : -EIO; } static int video_proc_show(struct seq_file *m, void *v) @@ -1057,8 +1100,8 @@ static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status) { u32 hci_result; - hci_read1(dev, HCI_FAN, status, &hci_result); - return hci_result == HCI_SUCCESS ? 0 : -EIO; + hci_result = hci_read1(dev, HCI_FAN, status); + return hci_result == TOS_SUCCESS ? 0 : -EIO; } static int fan_proc_show(struct seq_file *m, void *v) @@ -1097,8 +1140,8 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf, if (sscanf(cmd, " force_on : %i", &value) == 1 && value >= 0 && value <= 1) { - hci_write1(dev, HCI_FAN, value, &hci_result); - if (hci_result != HCI_SUCCESS) + hci_result = hci_write1(dev, HCI_FAN, value); + if (hci_result != TOS_SUCCESS) return -EIO; else dev->force_fan = value; @@ -1125,17 +1168,17 @@ static int keys_proc_show(struct seq_file *m, void *v) u32 value; if (!dev->key_event_valid && dev->system_event_supported) { - hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); - if (hci_result == HCI_SUCCESS) { + hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value); + if (hci_result == TOS_SUCCESS) { dev->key_event_valid = 1; dev->last_key_event = value; - } else if (hci_result == HCI_EMPTY) { + } else if (hci_result == TOS_FIFO_EMPTY) { /* better luck next time */ - } else if (hci_result == HCI_NOT_SUPPORTED) { + } else if (hci_result == TOS_NOT_SUPPORTED) { /* This is a workaround for an unresolved issue on * some machines where system events sporadically * become disabled. */ - hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); + hci_result = hci_write1(dev, HCI_SYSTEM_EVENT, 1); pr_notice("Re-enabled hotkeys\n"); } else { pr_err("Error reading hotkey status\n"); @@ -1249,6 +1292,62 @@ static const struct backlight_ops toshiba_backlight_data = { /* * Sysfs files */ +static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t toshiba_kbd_bl_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t toshiba_kbd_type_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t toshiba_available_kbd_modes_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t toshiba_touchpad_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t toshiba_touchpad_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t toshiba_position_show(struct device *dev, + struct device_attribute *attr, + char *buf); + +static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, + toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); +static DEVICE_ATTR(kbd_type, S_IRUGO, toshiba_kbd_type_show, NULL); +static DEVICE_ATTR(available_kbd_modes, S_IRUGO, + toshiba_available_kbd_modes_show, NULL); +static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, + toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); +static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR, + toshiba_touchpad_show, toshiba_touchpad_store); +static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL); + +static struct attribute *toshiba_attributes[] = { + &dev_attr_kbd_backlight_mode.attr, + &dev_attr_kbd_type.attr, + &dev_attr_available_kbd_modes.attr, + &dev_attr_kbd_backlight_timeout.attr, + &dev_attr_touchpad.attr, + &dev_attr_position.attr, + NULL, +}; + +static umode_t toshiba_sysfs_is_visible(struct kobject *, + struct attribute *, int); + +static struct attribute_group toshiba_attr_group = { + .is_visible = toshiba_sysfs_is_visible, + .attrs = toshiba_attributes, +}; static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, struct device_attribute *attr, @@ -1263,20 +1362,50 @@ static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, ret = kstrtoint(buf, 0, &mode); if (ret) return ret; - if (mode != SCI_KBD_MODE_FNZ && mode != SCI_KBD_MODE_AUTO) - return -EINVAL; + + /* Check for supported modes depending on keyboard backlight type */ + if (toshiba->kbd_type == 1) { + /* Type 1 supports SCI_KBD_MODE_FNZ and SCI_KBD_MODE_AUTO */ + if (mode != SCI_KBD_MODE_FNZ && mode != SCI_KBD_MODE_AUTO) + return -EINVAL; + } else if (toshiba->kbd_type == 2) { + /* Type 2 doesn't support SCI_KBD_MODE_FNZ */ + if (mode != SCI_KBD_MODE_AUTO && mode != SCI_KBD_MODE_ON && + mode != SCI_KBD_MODE_OFF) + return -EINVAL; + } /* Set the Keyboard Backlight Mode where: - * Mode - Auto (2) | FN-Z (1) * Auto - KBD backlight turns off automatically in given time * FN-Z - KBD backlight "toggles" when hotkey pressed + * ON - KBD backlight is always on + * OFF - KBD backlight is always off */ + + /* Only make a change if the actual mode has changed */ if (toshiba->kbd_mode != mode) { + /* Shift the time to "base time" (0x3c0000 == 60 seconds) */ time = toshiba->kbd_time << HCI_MISC_SHIFT; - time = time + toshiba->kbd_mode; + + /* OR the "base time" to the actual method format */ + if (toshiba->kbd_type == 1) { + /* Type 1 requires the current mode */ + time |= toshiba->kbd_mode; + } else if (toshiba->kbd_type == 2) { + /* Type 2 requires the desired mode */ + time |= mode; + } + ret = toshiba_kbd_illum_status_set(toshiba, time); if (ret) return ret; + + /* Update sysfs entries on successful mode change*/ + ret = sysfs_update_group(&toshiba->acpi_dev->dev.kobj, + &toshiba_attr_group); + if (ret) + return ret; + toshiba->kbd_mode = mode; } @@ -1293,7 +1422,30 @@ static ssize_t toshiba_kbd_bl_mode_show(struct device *dev, if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) return -EIO; - return sprintf(buf, "%i\n", time & 0x07); + return sprintf(buf, "%i\n", time & SCI_KBD_MODE_MASK); +} + +static ssize_t toshiba_kbd_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", toshiba->kbd_type); +} + +static ssize_t toshiba_available_kbd_modes_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + + if (toshiba->kbd_type == 1) + return sprintf(buf, "%x %x\n", + SCI_KBD_MODE_FNZ, SCI_KBD_MODE_AUTO); + + return sprintf(buf, "%x %x %x\n", + SCI_KBD_MODE_AUTO, SCI_KBD_MODE_ON, SCI_KBD_MODE_OFF); } static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev, @@ -1301,18 +1453,38 @@ static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev, const char *buf, size_t count) { struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); - int time = -1; + int time; + int ret; - if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60)) - return -EINVAL; + ret = kstrtoint(buf, 0, &time); + if (ret) + return ret; - /* Set the Keyboard Backlight Timeout: 0-60 seconds */ - if (time != -1 && toshiba->kbd_time != time) { + /* Check for supported values depending on kbd_type */ + if (toshiba->kbd_type == 1) { + if (time < 0 || time > 60) + return -EINVAL; + } else if (toshiba->kbd_type == 2) { + if (time < 1 || time > 60) + return -EINVAL; + } + + /* Set the Keyboard Backlight Timeout */ + + /* Only make a change if the actual timeout has changed */ + if (toshiba->kbd_time != time) { + /* Shift the time to "base time" (0x3c0000 == 60 seconds) */ time = time << HCI_MISC_SHIFT; - time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ? - time + 1 : time + 2; - if (toshiba_kbd_illum_status_set(toshiba, time) < 0) - return -EIO; + /* OR the "base time" to the actual method format */ + if (toshiba->kbd_type == 1) + time |= SCI_KBD_MODE_FNZ; + else if (toshiba->kbd_type == 2) + time |= SCI_KBD_MODE_AUTO; + + ret = toshiba_kbd_illum_status_set(toshiba, time); + if (ret) + return ret; + toshiba->kbd_time = time >> HCI_MISC_SHIFT; } @@ -1338,12 +1510,18 @@ static ssize_t toshiba_touchpad_store(struct device *dev, { struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); int state; + int ret; /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */ - if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) { - if (toshiba_touchpad_set(toshiba, state) < 0) - return -EIO; - } + ret = kstrtoint(buf, 0, &state); + if (ret) + return ret; + if (state != 0 && state != 1) + return -EINVAL; + + ret = toshiba_touchpad_set(toshiba, state); + if (ret) + return ret; return count; } @@ -1383,22 +1561,6 @@ static ssize_t toshiba_position_show(struct device *dev, return sprintf(buf, "%d %d %d\n", x, y, z); } -static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, - toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); -static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, - toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); -static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR, - toshiba_touchpad_show, toshiba_touchpad_store); -static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL); - -static struct attribute *toshiba_attributes[] = { - &dev_attr_kbd_backlight_mode.attr, - &dev_attr_kbd_backlight_timeout.attr, - &dev_attr_touchpad.attr, - &dev_attr_position.attr, - NULL, -}; - static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { @@ -1418,11 +1580,6 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, return exists ? attr->mode : 0; } -static struct attribute_group toshiba_attr_group = { - .is_visible = toshiba_sysfs_is_visible, - .attrs = toshiba_attributes, -}; - static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, struct serio *port) { @@ -1535,8 +1692,8 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) if (acpi_has_method(dev->acpi_dev->handle, "INFO")) dev->info_supported = 1; else { - hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); - if (hci_result == HCI_SUCCESS) + hci_result = hci_write1(dev, HCI_SYSTEM_EVENT, 1); + if (hci_result == TOS_SUCCESS) dev->system_event_supported = 1; } @@ -1558,7 +1715,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) goto err_remove_filter; } - hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result); + hci_result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE); return 0; err_remove_filter: @@ -1716,7 +1873,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) goto error; /* Register rfkill switch for Bluetooth */ - if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { + if (hci_get_bt_present(dev, &bt_present) == TOS_SUCCESS && bt_present) { dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth", &acpi_dev->dev, RFKILL_TYPE_BLUETOOTH, @@ -1754,12 +1911,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->eco_supported = 1; } - ret = toshiba_kbd_illum_status_get(dev, &dummy); - if (!ret) { - dev->kbd_time = dummy >> HCI_MISC_SHIFT; - dev->kbd_mode = dummy & 0x07; - } - dev->kbd_illum_supported = !ret; + dev->kbd_illum_supported = toshiba_kbd_illum_available(dev); /* * Only register the LED if KBD illumination is supported * and the keyboard backlight operation mode is set to FN-Z @@ -1824,26 +1976,26 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) toshiba_acpi_report_hotkey(dev, scancode); } else if (dev->system_event_supported) { do { - hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); + hci_result = hci_read1(dev, HCI_SYSTEM_EVENT, &value); switch (hci_result) { - case HCI_SUCCESS: + case TOS_SUCCESS: toshiba_acpi_report_hotkey(dev, (int)value); break; - case HCI_NOT_SUPPORTED: + case TOS_NOT_SUPPORTED: /* * This is a workaround for an unresolved * issue on some machines where system events * sporadically become disabled. */ - hci_write1(dev, HCI_SYSTEM_EVENT, 1, - &hci_result); + hci_result = + hci_write1(dev, HCI_SYSTEM_EVENT, 1); pr_notice("Re-enabled hotkeys\n"); /* fall through */ default: retries--; break; } - } while (retries && hci_result != HCI_EMPTY); + } while (retries && hci_result != TOS_FIFO_EMPTY); } } @@ -1854,7 +2006,7 @@ static int toshiba_acpi_suspend(struct device *device) u32 result; if (dev->hotkey_dev) - hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result); + result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE); return 0; } @@ -1871,7 +2023,7 @@ static int toshiba_acpi_resume(struct device *device) if (ACPI_FAILURE(status)) pr_info("Unable to re-enable hotkeys\n"); - hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result); + result = hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE); } return 0; diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index f0b0edb..662697b 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -56,6 +56,8 @@ struct ftrace_ops; typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs); +ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops); + /* * FTRACE_OPS_FL_* bits denote the state of ftrace_ops struct and are * set in the flags member. @@ -89,6 +91,9 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip, * INITIALIZED - The ftrace_ops has already been initialized (first use time * register_ftrace_function() is called, it will initialized the ops) * DELETED - The ops are being deleted, do not let them be registered again. + * ADDING - The ops is in the process of being added. + * REMOVING - The ops is in the process of being removed. + * MODIFYING - The ops is in the process of changing its filter functions. */ enum { FTRACE_OPS_FL_ENABLED = 1 << 0, @@ -100,6 +105,9 @@ enum { FTRACE_OPS_FL_STUB = 1 << 6, FTRACE_OPS_FL_INITIALIZED = 1 << 7, FTRACE_OPS_FL_DELETED = 1 << 8, + FTRACE_OPS_FL_ADDING = 1 << 9, + FTRACE_OPS_FL_REMOVING = 1 << 10, + FTRACE_OPS_FL_MODIFYING = 1 << 11, }; #ifdef CONFIG_DYNAMIC_FTRACE @@ -132,7 +140,7 @@ struct ftrace_ops { int nr_trampolines; struct ftrace_ops_hash local_hash; struct ftrace_ops_hash *func_hash; - struct ftrace_hash *tramp_hash; + struct ftrace_ops_hash old_hash; unsigned long trampoline; #endif }; diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index b1293f1..e08e21e 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -157,6 +157,12 @@ extern void syscall_unregfunc(void); * Make sure the alignment of the structure in the __tracepoints section will * not add unwanted padding between the beginning of the section and the * structure. Force alignment to the same alignment as the section start. + * + * When lockdep is enabled, we make sure to always do the RCU portions of + * the tracepoint code, regardless of whether tracing is on or we match the + * condition. This lets us find RCU issues triggered with tracepoints even + * when this tracepoint is off. This code has no purpose other than poking + * RCU a bit. */ #define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \ extern struct tracepoint __tracepoint_##name; \ @@ -167,6 +173,11 @@ extern void syscall_unregfunc(void); TP_PROTO(data_proto), \ TP_ARGS(data_args), \ TP_CONDITION(cond),,); \ + if (IS_ENABLED(CONFIG_LOCKDEP)) { \ + rcu_read_lock_sched_notrace(); \ + rcu_dereference_sched(__tracepoint_##name.funcs);\ + rcu_read_unlock_sched_notrace(); \ + } \ } \ __DECLARE_TRACE_RCU(name, PARAMS(proto), PARAMS(args), \ PARAMS(cond), PARAMS(data_proto), PARAMS(data_args)) \ diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 5916a8e..fb186b9 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -113,6 +113,9 @@ ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub; static struct ftrace_ops global_ops; static struct ftrace_ops control_ops; +static void ftrace_ops_recurs_func(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *op, struct pt_regs *regs); + #if ARCH_SUPPORTS_FTRACE_OPS static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip, struct ftrace_ops *op, struct pt_regs *regs); @@ -251,18 +254,24 @@ static void update_ftrace_function(void) ftrace_func_t func; /* + * Prepare the ftrace_ops that the arch callback will use. + * If there's only one ftrace_ops registered, the ftrace_ops_list + * will point to the ops we want. + */ + set_function_trace_op = ftrace_ops_list; + + /* If there's no ftrace_ops registered, just call the stub function */ + if (ftrace_ops_list == &ftrace_list_end) { + func = ftrace_stub; + + /* * If we are at the end of the list and this ops is * recursion safe and not dynamic and the arch supports passing ops, * then have the mcount trampoline call the function directly. */ - if (ftrace_ops_list == &ftrace_list_end || - (ftrace_ops_list->next == &ftrace_list_end && - !(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC) && - (ftrace_ops_list->flags & FTRACE_OPS_FL_RECURSION_SAFE) && - !FTRACE_FORCE_LIST_FUNC)) { - /* Set the ftrace_ops that the arch callback uses */ - set_function_trace_op = ftrace_ops_list; - func = ftrace_ops_list->func; + } else if (ftrace_ops_list->next == &ftrace_list_end) { + func = ftrace_ops_get_func(ftrace_ops_list); + } else { /* Just use the default ftrace_ops */ set_function_trace_op = &ftrace_list_end; @@ -1048,6 +1057,12 @@ static struct pid * const ftrace_swapper_pid = &init_struct_pid; static struct ftrace_ops *removed_ops; +/* + * Set when doing a global update, like enabling all recs or disabling them. + * It is not set when just updating a single ftrace_ops. + */ +static bool update_all_ops; + #ifndef CONFIG_FTRACE_MCOUNT_RECORD # error Dynamic ftrace depends on MCOUNT_RECORD #endif @@ -1307,7 +1322,6 @@ ftrace_hash_move(struct ftrace_ops *ops, int enable, struct ftrace_func_entry *entry; struct hlist_node *tn; struct hlist_head *hhd; - struct ftrace_hash *old_hash; struct ftrace_hash *new_hash; int size = src->count; int bits = 0; @@ -1352,15 +1366,28 @@ update: */ ftrace_hash_rec_disable_modify(ops, enable); - old_hash = *dst; rcu_assign_pointer(*dst, new_hash); - free_ftrace_hash_rcu(old_hash); ftrace_hash_rec_enable_modify(ops, enable); return 0; } +static bool hash_contains_ip(unsigned long ip, + struct ftrace_ops_hash *hash) +{ + /* + * The function record is a match if it exists in the filter + * hash and not in the notrace hash. Note, an emty hash is + * considered a match for the filter hash, but an empty + * notrace hash is considered not in the notrace hash. + */ + return (ftrace_hash_empty(hash->filter_hash) || + ftrace_lookup_ip(hash->filter_hash, ip)) && + (ftrace_hash_empty(hash->notrace_hash) || + !ftrace_lookup_ip(hash->notrace_hash, ip)); +} + /* * Test the hashes for this ops to see if we want to call * the ops->func or not. @@ -1376,8 +1403,7 @@ update: static int ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) { - struct ftrace_hash *filter_hash; - struct ftrace_hash *notrace_hash; + struct ftrace_ops_hash hash; int ret; #ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS @@ -1390,13 +1416,10 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip, void *regs) return 0; #endif - filter_hash = rcu_dereference_raw_notrace(ops->func_hash->filter_hash); - notrace_hash = rcu_dereference_raw_notrace(ops->func_hash->notrace_hash); + hash.filter_hash = rcu_dereference_raw_notrace(ops->func_hash->filter_hash); + hash.notrace_hash = rcu_dereference_raw_notrace(ops->func_hash->notrace_hash); - if ((ftrace_hash_empty(filter_hash) || - ftrace_lookup_ip(filter_hash, ip)) && - (ftrace_hash_empty(notrace_hash) || - !ftrace_lookup_ip(notrace_hash, ip))) + if (hash_contains_ip(ip, &hash)) ret = 1; else ret = 0; @@ -1508,46 +1531,6 @@ static bool test_rec_ops_needs_regs(struct dyn_ftrace *rec) return keep_regs; } -static void ftrace_remove_tramp(struct ftrace_ops *ops, - struct dyn_ftrace *rec) -{ - /* If TRAMP is not set, no ops should have a trampoline for this */ - if (!(rec->flags & FTRACE_FL_TRAMP)) - return; - - rec->flags &= ~FTRACE_FL_TRAMP; - - if ((!ftrace_hash_empty(ops->func_hash->filter_hash) && - !ftrace_lookup_ip(ops->func_hash->filter_hash, rec->ip)) || - ftrace_lookup_ip(ops->func_hash->notrace_hash, rec->ip)) - return; - /* - * The tramp_hash entry will be removed at time - * of update. - */ - ops->nr_trampolines--; -} - -static void ftrace_clear_tramps(struct dyn_ftrace *rec, struct ftrace_ops *ops) -{ - struct ftrace_ops *op; - - /* If TRAMP is not set, no ops should have a trampoline for this */ - if (!(rec->flags & FTRACE_FL_TRAMP)) - return; - - do_for_each_ftrace_op(op, ftrace_ops_list) { - /* - * This function is called to clear other tramps - * not the one that is being updated. - */ - if (op == ops) - continue; - if (op->nr_trampolines) - ftrace_remove_tramp(op, rec); - } while_for_each_ftrace_op(op); -} - static void __ftrace_hash_rec_update(struct ftrace_ops *ops, int filter_hash, bool inc) @@ -1636,18 +1619,16 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, * function, and the ops has a trampoline registered * for it, then we can call it directly. */ - if (ftrace_rec_count(rec) == 1 && ops->trampoline) { + if (ftrace_rec_count(rec) == 1 && ops->trampoline) rec->flags |= FTRACE_FL_TRAMP; - ops->nr_trampolines++; - } else { + else /* * If we are adding another function callback * to this function, and the previous had a * custom trampoline in use, then we need to go * back to the default trampoline. */ - ftrace_clear_tramps(rec, ops); - } + rec->flags &= ~FTRACE_FL_TRAMP; /* * If any ops wants regs saved for this function @@ -1660,9 +1641,6 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, return; rec->flags--; - if (ops->trampoline && !ftrace_rec_count(rec)) - ftrace_remove_tramp(ops, rec); - /* * If the rec had REGS enabled and the ops that is * being removed had REGS set, then see if there is @@ -1677,6 +1655,17 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops, } /* + * If the rec had TRAMP enabled, then it needs to + * be cleared. As TRAMP can only be enabled iff + * there is only a single ops attached to it. + * In otherwords, always disable it on decrementing. + * In the future, we may set it if rec count is + * decremented to one, and the ops that is left + * has a trampoline. + */ + rec->flags &= ~FTRACE_FL_TRAMP; + + /* * flags will be cleared in ftrace_check_record() * if rec count is zero. */ @@ -1895,21 +1884,72 @@ int ftrace_test_record(struct dyn_ftrace *rec, int enable) } static struct ftrace_ops * +ftrace_find_tramp_ops_any(struct dyn_ftrace *rec) +{ + struct ftrace_ops *op; + unsigned long ip = rec->ip; + + do_for_each_ftrace_op(op, ftrace_ops_list) { + + if (!op->trampoline) + continue; + + if (hash_contains_ip(ip, op->func_hash)) + return op; + } while_for_each_ftrace_op(op); + + return NULL; +} + +static struct ftrace_ops * ftrace_find_tramp_ops_curr(struct dyn_ftrace *rec) { struct ftrace_ops *op; + unsigned long ip = rec->ip; - /* Removed ops need to be tested first */ - if (removed_ops && removed_ops->tramp_hash) { - if (ftrace_lookup_ip(removed_ops->tramp_hash, rec->ip)) + /* + * Need to check removed ops first. + * If they are being removed, and this rec has a tramp, + * and this rec is in the ops list, then it would be the + * one with the tramp. + */ + if (removed_ops) { + if (hash_contains_ip(ip, &removed_ops->old_hash)) return removed_ops; } + /* + * Need to find the current trampoline for a rec. + * Now, a trampoline is only attached to a rec if there + * was a single 'ops' attached to it. But this can be called + * when we are adding another op to the rec or removing the + * current one. Thus, if the op is being added, we can + * ignore it because it hasn't attached itself to the rec + * yet. That means we just need to find the op that has a + * trampoline and is not beeing added. + */ do_for_each_ftrace_op(op, ftrace_ops_list) { - if (!op->tramp_hash) + + if (!op->trampoline) continue; - if (ftrace_lookup_ip(op->tramp_hash, rec->ip)) + /* + * If the ops is being added, it hasn't gotten to + * the point to be removed from this tree yet. + */ + if (op->flags & FTRACE_OPS_FL_ADDING) + continue; + + /* + * If the ops is not being added and has a trampoline, + * then it must be the one that we want! + */ + if (hash_contains_ip(ip, op->func_hash)) + return op; + + /* If the ops is being modified, it may be in the old hash. */ + if ((op->flags & FTRACE_OPS_FL_MODIFYING) && + hash_contains_ip(ip, &op->old_hash)) return op; } while_for_each_ftrace_op(op); @@ -1921,10 +1961,11 @@ static struct ftrace_ops * ftrace_find_tramp_ops_new(struct dyn_ftrace *rec) { struct ftrace_ops *op; + unsigned long ip = rec->ip; do_for_each_ftrace_op(op, ftrace_ops_list) { /* pass rec in as regs to have non-NULL val */ - if (ftrace_ops_test(op, rec->ip, rec)) + if (hash_contains_ip(ip, op->func_hash)) return op; } while_for_each_ftrace_op(op); @@ -2231,92 +2272,6 @@ void __weak arch_ftrace_update_code(int command) ftrace_run_stop_machine(command); } -static int ftrace_save_ops_tramp_hash(struct ftrace_ops *ops) -{ - struct ftrace_page *pg; - struct dyn_ftrace *rec; - int size, bits; - int ret; - - size = ops->nr_trampolines; - bits = 0; - /* - * Make the hash size about 1/2 the # found - */ - for (size /= 2; size; size >>= 1) - bits++; - - ops->tramp_hash = alloc_ftrace_hash(bits); - /* - * TODO: a failed allocation is going to screw up - * the accounting of what needs to be modified - * and not. For now, we kill ftrace if we fail - * to allocate here. But there are ways around this, - * but that will take a little more work. - */ - if (!ops->tramp_hash) - return -ENOMEM; - - do_for_each_ftrace_rec(pg, rec) { - if (ftrace_rec_count(rec) == 1 && - ftrace_ops_test(ops, rec->ip, rec)) { - - /* - * If another ops adds to a rec, the rec will - * lose its trampoline and never get it back - * until all ops are off of it. - */ - if (!(rec->flags & FTRACE_FL_TRAMP)) - continue; - - /* This record had better have a trampoline */ - if (FTRACE_WARN_ON(!(rec->flags & FTRACE_FL_TRAMP_EN))) - return -1; - - ret = add_hash_entry(ops->tramp_hash, rec->ip); - if (ret < 0) - return ret; - } - } while_for_each_ftrace_rec(); - - /* The number of recs in the hash must match nr_trampolines */ - if (FTRACE_WARN_ON(ops->tramp_hash->count != ops->nr_trampolines)) - pr_warn("count=%ld trampolines=%d\n", - ops->tramp_hash->count, - ops->nr_trampolines); - - return 0; -} - -static int ftrace_save_tramp_hashes(void) -{ - struct ftrace_ops *op; - int ret; - - /* - * Now that any trampoline is being used, we need to save the - * hashes for the ops that have them. This allows the mapping - * back from the record to the ops that has the trampoline to - * know what code is being replaced. Modifying code must always - * verify what it is changing. - */ - do_for_each_ftrace_op(op, ftrace_ops_list) { - - /* The tramp_hash is recreated each time. */ - free_ftrace_hash(op->tramp_hash); - op->tramp_hash = NULL; - - if (op->nr_trampolines) { - ret = ftrace_save_ops_tramp_hash(op); - if (ret) - return ret; - } - - } while_for_each_ftrace_op(op); - - return 0; -} - static void ftrace_run_update_code(int command) { int ret; @@ -2336,9 +2291,13 @@ static void ftrace_run_update_code(int command) ret = ftrace_arch_code_modify_post_process(); FTRACE_WARN_ON(ret); +} - ret = ftrace_save_tramp_hashes(); - FTRACE_WARN_ON(ret); +static void ftrace_run_modify_code(struct ftrace_ops *ops, int command) +{ + ops->flags |= FTRACE_OPS_FL_MODIFYING; + ftrace_run_update_code(command); + ops->flags &= ~FTRACE_OPS_FL_MODIFYING; } static ftrace_func_t saved_ftrace_func; @@ -2362,6 +2321,13 @@ static void ftrace_startup_enable(int command) ftrace_run_update_code(command); } +static void ftrace_startup_all(int command) +{ + update_all_ops = true; + ftrace_startup_enable(command); + update_all_ops = false; +} + static int ftrace_startup(struct ftrace_ops *ops, int command) { int ret; @@ -2376,12 +2342,22 @@ static int ftrace_startup(struct ftrace_ops *ops, int command) ftrace_start_up++; command |= FTRACE_UPDATE_CALLS; - ops->flags |= FTRACE_OPS_FL_ENABLED; + /* + * Note that ftrace probes uses this to start up + * and modify functions it will probe. But we still + * set the ADDING flag for modification, as probes + * do not have trampolines. If they add them in the + * future, then the probes will need to distinguish + * between adding and updating probes. + */ + ops->flags |= FTRACE_OPS_FL_ENABLED | FTRACE_OPS_FL_ADDING; ftrace_hash_rec_enable(ops, 1); ftrace_startup_enable(command); + ops->flags &= ~FTRACE_OPS_FL_ADDING; + return 0; } @@ -2431,11 +2407,35 @@ static int ftrace_shutdown(struct ftrace_ops *ops, int command) * If the ops uses a trampoline, then it needs to be * tested first on update. */ + ops->flags |= FTRACE_OPS_FL_REMOVING; removed_ops = ops; + /* The trampoline logic checks the old hashes */ + ops->old_hash.filter_hash = ops->func_hash->filter_hash; + ops->old_hash.notrace_hash = ops->func_hash->notrace_hash; + ftrace_run_update_code(command); + /* + * If there's no more ops registered with ftrace, run a + * sanity check to make sure all rec flags are cleared. + */ + if (ftrace_ops_list == &ftrace_list_end) { + struct ftrace_page *pg; + struct dyn_ftrace *rec; + + do_for_each_ftrace_rec(pg, rec) { + if (FTRACE_WARN_ON_ONCE(rec->flags)) + pr_warn(" %pS flags:%lx\n", + (void *)rec->ip, rec->flags); + } while_for_each_ftrace_rec(); + } + + ops->old_hash.filter_hash = NULL; + ops->old_hash.notrace_hash = NULL; + removed_ops = NULL; + ops->flags &= ~FTRACE_OPS_FL_REMOVING; /* * Dynamic ops may be freed, we must make sure that all @@ -2960,8 +2960,8 @@ static int t_show(struct seq_file *m, void *v) if (rec->flags & FTRACE_FL_TRAMP_EN) { struct ftrace_ops *ops; - ops = ftrace_find_tramp_ops_curr(rec); - if (ops && ops->trampoline) + ops = ftrace_find_tramp_ops_any(rec); + if (ops) seq_printf(m, "\ttramp: %pS", (void *)ops->trampoline); else @@ -3348,7 +3348,7 @@ static void __enable_ftrace_function_probe(void) if (ftrace_probe_registered) { /* still need to update the function call sites */ if (ftrace_enabled) - ftrace_run_update_code(FTRACE_UPDATE_CALLS); + ftrace_run_modify_code(&trace_probe_ops, FTRACE_UPDATE_CALLS); return; } @@ -3399,6 +3399,7 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, { struct ftrace_func_probe *entry; struct ftrace_hash **orig_hash = &trace_probe_ops.func_hash->filter_hash; + struct ftrace_hash *old_hash = *orig_hash; struct ftrace_hash *hash; struct ftrace_page *pg; struct dyn_ftrace *rec; @@ -3417,7 +3418,7 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, mutex_lock(&trace_probe_ops.func_hash->regex_lock); - hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, *orig_hash); + hash = alloc_and_copy_ftrace_hash(FTRACE_HASH_DEFAULT_BITS, old_hash); if (!hash) { count = -ENOMEM; goto out; @@ -3476,7 +3477,9 @@ register_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, } while_for_each_ftrace_rec(); ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash); - if (ret < 0) + if (!ret) + free_ftrace_hash_rcu(old_hash); + else count = ret; __enable_ftrace_function_probe(); @@ -3503,6 +3506,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, struct ftrace_func_probe *entry; struct ftrace_func_probe *p; struct ftrace_hash **orig_hash = &trace_probe_ops.func_hash->filter_hash; + struct ftrace_hash *old_hash = *orig_hash; struct list_head free_list; struct ftrace_hash *hash; struct hlist_node *tmp; @@ -3510,6 +3514,7 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, int type = MATCH_FULL; int i, len = 0; char *search; + int ret; if (glob && (strcmp(glob, "*") == 0 || !strlen(glob))) glob = NULL; @@ -3568,8 +3573,11 @@ __unregister_ftrace_function_probe(char *glob, struct ftrace_probe_ops *ops, * Remove after the disable is called. Otherwise, if the last * probe is removed, a null hash means *all enabled*. */ - ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash); + ret = ftrace_hash_move(&trace_probe_ops, 1, orig_hash, hash); synchronize_sched(); + if (!ret) + free_ftrace_hash_rcu(old_hash); + list_for_each_entry_safe(entry, p, &free_list, free_list) { list_del(&entry->free_list); ftrace_free_entry(entry); @@ -3759,7 +3767,7 @@ ftrace_match_addr(struct ftrace_hash *hash, unsigned long ip, int remove) static void ftrace_ops_update_code(struct ftrace_ops *ops) { if (ops->flags & FTRACE_OPS_FL_ENABLED && ftrace_enabled) - ftrace_run_update_code(FTRACE_UPDATE_CALLS); + ftrace_run_modify_code(ops, FTRACE_UPDATE_CALLS); } static int @@ -3767,6 +3775,7 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, unsigned long ip, int remove, int reset, int enable) { struct ftrace_hash **orig_hash; + struct ftrace_hash *old_hash; struct ftrace_hash *hash; int ret; @@ -3801,10 +3810,12 @@ ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len, } mutex_lock(&ftrace_lock); + old_hash = *orig_hash; ret = ftrace_hash_move(ops, enable, orig_hash, hash); - if (!ret) + if (!ret) { ftrace_ops_update_code(ops); - + free_ftrace_hash_rcu(old_hash); + } mutex_unlock(&ftrace_lock); out_regex_unlock: @@ -4013,6 +4024,7 @@ int ftrace_regex_release(struct inode *inode, struct file *file) struct seq_file *m = (struct seq_file *)file->private_data; struct ftrace_iterator *iter; struct ftrace_hash **orig_hash; + struct ftrace_hash *old_hash; struct trace_parser *parser; int filter_hash; int ret; @@ -4042,11 +4054,13 @@ int ftrace_regex_release(struct inode *inode, struct file *file) orig_hash = &iter->ops->func_hash->notrace_hash; mutex_lock(&ftrace_lock); + old_hash = *orig_hash; ret = ftrace_hash_move(iter->ops, filter_hash, orig_hash, iter->hash); - if (!ret) + if (!ret) { ftrace_ops_update_code(iter->ops); - + free_ftrace_hash_rcu(old_hash); + } mutex_unlock(&ftrace_lock); } @@ -4678,6 +4692,7 @@ core_initcall(ftrace_nodyn_init); static inline int ftrace_init_dyn_debugfs(struct dentry *d_tracer) { return 0; } static inline void ftrace_startup_enable(int command) { } +static inline void ftrace_startup_all(int command) { } /* Keep as macros so we do not need to define the commands */ # define ftrace_startup(ops, command) \ ({ \ @@ -4827,6 +4842,56 @@ static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip) } #endif +/* + * If there's only one function registered but it does not support + * recursion, this function will be called by the mcount trampoline. + * This function will handle recursion protection. + */ +static void ftrace_ops_recurs_func(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *op, struct pt_regs *regs) +{ + int bit; + + bit = trace_test_and_set_recursion(TRACE_LIST_START, TRACE_LIST_MAX); + if (bit < 0) + return; + + op->func(ip, parent_ip, op, regs); + + trace_clear_recursion(bit); +} + +/** + * ftrace_ops_get_func - get the function a trampoline should call + * @ops: the ops to get the function for + * + * Normally the mcount trampoline will call the ops->func, but there + * are times that it should not. For example, if the ops does not + * have its own recursion protection, then it should call the + * ftrace_ops_recurs_func() instead. + * + * Returns the function that the trampoline should call for @ops. + */ +ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops) +{ + /* + * If this is a dynamic ops or we force list func, + * then it needs to call the list anyway. + */ + if (ops->flags & FTRACE_OPS_FL_DYNAMIC || FTRACE_FORCE_LIST_FUNC) + return ftrace_ops_list_func; + + /* + * If the func handles its own recursion, call it directly. + * Otherwise call the recursion protected function that + * will call the ftrace ops function. + */ + if (!(ops->flags & FTRACE_OPS_FL_RECURSION_SAFE)) + return ftrace_ops_recurs_func; + + return ops->func; +} + static void clear_ftrace_swapper(void) { struct task_struct *p; @@ -4927,7 +4992,8 @@ static int ftrace_pid_add(int p) set_ftrace_pid_task(pid); ftrace_update_pid_func(); - ftrace_startup_enable(0); + + ftrace_startup_all(0); mutex_unlock(&ftrace_lock); return 0; @@ -4956,7 +5022,7 @@ static void ftrace_pid_reset(void) } ftrace_update_pid_func(); - ftrace_startup_enable(0); + ftrace_startup_all(0); mutex_unlock(&ftrace_lock); } diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index ef06ce7..0cc51ed 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -2513,8 +2513,11 @@ static __init int event_test_thread(void *unused) kfree(test_malloc); set_current_state(TASK_INTERRUPTIBLE); - while (!kthread_should_stop()) + while (!kthread_should_stop()) { schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + __set_current_state(TASK_RUNNING); return 0; } diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c index 5ef6049..b0f86ea 100644 --- a/kernel/trace/trace_selftest.c +++ b/kernel/trace/trace_selftest.c @@ -382,6 +382,8 @@ static int trace_selftest_startup_dynamic_tracing(struct tracer *trace, /* check the trace buffer */ ret = trace_test_buffer(&tr->trace_buffer, &count); + + ftrace_enabled = 1; tracing_start(); /* we should only have one item */ @@ -679,6 +681,8 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr) /* check the trace buffer */ ret = trace_test_buffer(&tr->trace_buffer, &count); + + ftrace_enabled = 1; trace->reset(tr); tracing_start(); @@ -1025,6 +1029,12 @@ trace_selftest_startup_nop(struct tracer *trace, struct trace_array *tr) #endif #ifdef CONFIG_SCHED_TRACER + +struct wakeup_test_data { + struct completion is_ready; + int go; +}; + static int trace_wakeup_test_thread(void *data) { /* Make this a -deadline thread */ @@ -1034,51 +1044,56 @@ static int trace_wakeup_test_thread(void *data) .sched_deadline = 10000000ULL, .sched_period = 10000000ULL }; - struct completion *x = data; + struct wakeup_test_data *x = data; sched_setattr(current, &attr); /* Make it know we have a new prio */ - complete(x); + complete(&x->is_ready); /* now go to sleep and let the test wake us up */ set_current_state(TASK_INTERRUPTIBLE); - schedule(); + while (!x->go) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } - complete(x); + complete(&x->is_ready); + + set_current_state(TASK_INTERRUPTIBLE); /* we are awake, now wait to disappear */ while (!kthread_should_stop()) { - /* - * This will likely be the system top priority - * task, do short sleeps to let others run. - */ - msleep(100); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); } + __set_current_state(TASK_RUNNING); + return 0; } - int trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr) { unsigned long save_max = tr->max_latency; struct task_struct *p; - struct completion is_ready; + struct wakeup_test_data data; unsigned long count; int ret; - init_completion(&is_ready); + memset(&data, 0, sizeof(data)); + + init_completion(&data.is_ready); /* create a -deadline thread */ - p = kthread_run(trace_wakeup_test_thread, &is_ready, "ftrace-test"); + p = kthread_run(trace_wakeup_test_thread, &data, "ftrace-test"); if (IS_ERR(p)) { printk(KERN_CONT "Failed to create ftrace wakeup test thread "); return -1; } /* make sure the thread is running at -deadline policy */ - wait_for_completion(&is_ready); + wait_for_completion(&data.is_ready); /* start the tracing */ ret = tracer_init(trace, tr); @@ -1099,18 +1114,20 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr) msleep(100); } - init_completion(&is_ready); + init_completion(&data.is_ready); + + data.go = 1; + /* memory barrier is in the wake_up_process() */ wake_up_process(p); /* Wait for the task to wake up */ - wait_for_completion(&is_ready); + wait_for_completion(&data.is_ready); /* stop the tracing. */ tracing_stop(); /* check both trace buffers */ ret = trace_test_buffer(&tr->trace_buffer, NULL); - printk("ret = %d\n", ret); if (!ret) ret = trace_test_buffer(&tr->max_buffer, &count); diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c index 759d5e0..4dc8b79 100644 --- a/kernel/trace/trace_syscalls.c +++ b/kernel/trace/trace_syscalls.c @@ -425,7 +425,7 @@ static void unreg_event_syscall_enter(struct ftrace_event_file *file, return; mutex_lock(&syscall_trace_lock); tr->sys_refcount_enter--; - rcu_assign_pointer(tr->enter_syscall_files[num], NULL); + RCU_INIT_POINTER(tr->enter_syscall_files[num], NULL); if (!tr->sys_refcount_enter) unregister_trace_sys_enter(ftrace_syscall_enter, tr); mutex_unlock(&syscall_trace_lock); @@ -463,7 +463,7 @@ static void unreg_event_syscall_exit(struct ftrace_event_file *file, return; mutex_lock(&syscall_trace_lock); tr->sys_refcount_exit--; - rcu_assign_pointer(tr->exit_syscall_files[num], NULL); + RCU_INIT_POINTER(tr->exit_syscall_files[num], NULL); if (!tr->sys_refcount_exit) unregister_trace_sys_exit(ftrace_syscall_exit, tr); mutex_unlock(&syscall_trace_lock); diff --git a/mm/Makefile b/mm/Makefile index 1f534a7..8405eb0 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -28,8 +28,9 @@ else obj-y += bootmem.o endif +obj-$(CONFIG_ADVISE_SYSCALLS) += fadvise.o ifdef CONFIG_MMU - obj-$(CONFIG_ADVISE_SYSCALLS) += fadvise.o madvise.o + obj-$(CONFIG_ADVISE_SYSCALLS) += madvise.o endif obj-$(CONFIG_HAVE_MEMBLOCK) += memblock.o diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index 55ab700..bf13981 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -194,6 +194,7 @@ my $config_bisect_check; my $patchcheck_type; my $patchcheck_start; +my $patchcheck_cherry; my $patchcheck_end; # set when a test is something other that just building or install @@ -320,6 +321,7 @@ my %option_map = ( "PATCHCHECK_TYPE" => \$patchcheck_type, "PATCHCHECK_START" => \$patchcheck_start, + "PATCHCHECK_CHERRY" => \$patchcheck_cherry, "PATCHCHECK_END" => \$patchcheck_end, ); @@ -1448,6 +1450,12 @@ sub wait_for_monitor { } } print "** Monitor flushed **\n"; + + # if stop is defined but wasn't hit, return error + # used by reboot (which wants to see a reboot) + if (defined($stop) && !$booted) { + $bug = 1; + } return $bug; } @@ -2336,15 +2344,17 @@ sub success { sub answer_bisect { for (;;) { - doprint "Pass or fail? [p/f]"; + doprint "Pass, fail, or skip? [p/f/s]"; my $ans = <STDIN>; chomp $ans; if ($ans eq "p" || $ans eq "P") { return 1; } elsif ($ans eq "f" || $ans eq "F") { return 0; + } elsif ($ans eq "s" || $ans eq "S") { + return -1; } else { - print "Please answer 'P' or 'F'\n"; + print "Please answer 'p', 'f', or 's'\n"; } } } @@ -2726,15 +2736,17 @@ sub bisect { run_command "git bisect start$start_files" or dodie "could not start bisect"; - run_command "git bisect good $good" or - dodie "could not set bisect good to $good"; - - run_git_bisect "git bisect bad $bad" or - dodie "could not set bisect bad to $bad"; - if (defined($replay)) { run_command "git bisect replay $replay" or dodie "failed to run replay"; + } else { + + run_command "git bisect good $good" or + dodie "could not set bisect good to $good"; + + run_git_bisect "git bisect bad $bad" or + dodie "could not set bisect bad to $bad"; + } if (defined($start)) { @@ -3181,9 +3193,16 @@ sub patchcheck { my $start = $patchcheck_start; + my $cherry = $patchcheck_cherry; + if (!defined($cherry)) { + $cherry = 0; + } + my $end = "HEAD"; if (defined($patchcheck_end)) { $end = $patchcheck_end; + } elsif ($cherry) { + die "PATCHCHECK_END must be defined with PATCHCHECK_CHERRY\n"; } # Get the true sha1's since we can use things like HEAD~3 @@ -3197,24 +3216,38 @@ sub patchcheck { $type = "boot"; } - open (IN, "git log --pretty=oneline $end|") or - dodie "could not get git list"; + if ($cherry) { + open (IN, "git cherry -v $start $end|") or + dodie "could not get git list"; + } else { + open (IN, "git log --pretty=oneline $end|") or + dodie "could not get git list"; + } my @list; while (<IN>) { chomp; + # git cherry adds a '+' we want to remove + s/^\+ //; $list[$#list+1] = $_; last if (/^$start/); } close(IN); - if ($list[$#list] !~ /^$start/) { - fail "SHA1 $start not found"; + if (!$cherry) { + if ($list[$#list] !~ /^$start/) { + fail "SHA1 $start not found"; + } + + # go backwards in the list + @list = reverse @list; } - # go backwards in the list - @list = reverse @list; + doprint("Going to test the following commits:\n"); + foreach my $l (@list) { + doprint "$l\n"; + } my $save_clean = $noclean; my %ignored_warnings; diff --git a/tools/testing/ktest/sample.conf b/tools/testing/ktest/sample.conf index 911e45a..6c58cd8 100644 --- a/tools/testing/ktest/sample.conf +++ b/tools/testing/ktest/sample.conf @@ -906,6 +906,16 @@ # # PATCHCHECK_END is the last patch to check (default HEAD) # +# PATCHCHECK_CHERRY if set to non zero, then git cherry will be +# performed against PATCHCHECK_START and PATCHCHECK_END. That is +# +# git cherry ${PATCHCHECK_START} ${PATCHCHECK_END} +# +# Then the changes found will be tested. +# +# Note, PATCHCHECK_CHERRY requires PATCHCHECK_END to be defined. +# (default 0) +# # PATCHCHECK_TYPE is required and is the type of test to run: # build, boot, test. # diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 36ff2e4..45f145c 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -14,6 +14,7 @@ TARGETS += powerpc TARGETS += user TARGETS += sysctl TARGETS += firmware +TARGETS += ftrace TARGETS_HOTPLUG = cpu-hotplug TARGETS_HOTPLUG += memory-hotplug diff --git a/tools/testing/selftests/ftrace/Makefile b/tools/testing/selftests/ftrace/Makefile new file mode 100644 index 0000000..76cc9f1 --- /dev/null +++ b/tools/testing/selftests/ftrace/Makefile @@ -0,0 +1,7 @@ +all: + +run_tests: + @/bin/sh ./ftracetest || echo "ftrace selftests: [FAIL]" + +clean: + rm -rf logs/* diff --git a/tools/testing/selftests/ftrace/README b/tools/testing/selftests/ftrace/README new file mode 100644 index 0000000..182e76f --- /dev/null +++ b/tools/testing/selftests/ftrace/README @@ -0,0 +1,82 @@ +Linux Ftrace Testcases + +This is a collection of testcases for ftrace tracing feature in the Linux +kernel. Since ftrace exports interfaces via the debugfs, we just need +shell scripts for testing. Feel free to add new test cases. + +Running the ftrace testcases +============================ + +At first, you need to be the root user to run this script. +To run all testcases: + + $ sudo ./ftracetest + +To run specific testcases: + + # ./ftracetest test.d/basic3.tc + +Or you can also run testcases under given directory: + + # ./ftracetest test.d/kprobe/ + +Contributing new testcases +========================== + +Copy test.d/template to your testcase (whose filename must have *.tc +extension) and rewrite the test description line. + + * The working directory of the script is <debugfs>/tracing/. + + * Take care with side effects as the tests are run with root privilege. + + * The tests should not run for a long period of time (more than 1 min.) + These are to be unit tests. + + * You can add a directory for your testcases under test.d/ if needed. + + * The test cases should run on dash (busybox shell) for testing on + minimal cross-build environments. + + * Note that the tests are run with "set -e" (errexit) option. If any + command fails, the test will be terminated immediately. + + * The tests can return some result codes instead of pass or fail by + using exit_unresolved, exit_untested, exit_unsupported and exit_xfail. + +Result code +=========== + +Ftracetest supports following result codes. + + * PASS: The test succeeded as expected. The test which exits with 0 is + counted as passed test. + + * FAIL: The test failed, but was expected to succeed. The test which exits + with !0 is counted as failed test. + + * UNRESOLVED: The test produced unclear or intermidiate results. + for example, the test was interrupted + or the test depends on a previous test, which failed. + or the test was set up incorrectly + The test which is in above situation, must call exit_unresolved. + + * UNTESTED: The test was not run, currently just a placeholder. + In this case, the test must call exit_untested. + + * UNSUPPORTED: The test failed because of lack of feature. + In this case, the test must call exit_unsupported. + + * XFAIL: The test failed, and was expected to fail. + To return XFAIL, call exit_xfail from the test. + +There are some sample test scripts for result code under samples/. +You can also run samples as below: + + # ./ftracetest samples/ + +TODO +==== + + * Fancy colored output :) + diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest new file mode 100755 index 0000000..a8f81c7 --- /dev/null +++ b/tools/testing/selftests/ftrace/ftracetest @@ -0,0 +1,253 @@ +#!/bin/sh + +# ftracetest - Ftrace test shell scripts +# +# Copyright (C) Hitachi Ltd., 2014 +# Written by Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> +# +# Released under the terms of the GPL v2. + +usage() { # errno [message] +[ "$2" ] && echo $2 +echo "Usage: ftracetest [options] [testcase(s)] [testcase-directory(s)]" +echo " Options:" +echo " -h|--help Show help message" +echo " -k|--keep Keep passed test logs" +echo " -d|--debug Debug mode (trace all shell commands)" +exit $1 +} + +errexit() { # message + echo "Error: $1" 1>&2 + exit 1 +} + +# Ensuring user privilege +if [ `id -u` -ne 0 ]; then + errexit "this must be run by root user" +fi + +# Utilities +absdir() { # file_path + (cd `dirname $1`; pwd) +} + +abspath() { + echo `absdir $1`/`basename $1` +} + +find_testcases() { #directory + echo `find $1 -name \*.tc` +} + +parse_opts() { # opts + local OPT_TEST_CASES= + local OPT_TEST_DIR= + + while [ "$1" ]; do + case "$1" in + --help|-h) + usage 0 + ;; + --keep|-k) + KEEP_LOG=1 + shift 1 + ;; + --debug|-d) + DEBUG=1 + shift 1 + ;; + *.tc) + if [ -f "$1" ]; then + OPT_TEST_CASES="$OPT_TEST_CASES `abspath $1`" + shift 1 + else + usage 1 "$1 is not a testcase" + fi + ;; + *) + if [ -d "$1" ]; then + OPT_TEST_DIR=`abspath $1` + OPT_TEST_CASES="$OPT_TEST_CASES `find_testcases $OPT_TEST_DIR`" + shift 1 + else + usage 1 "Invalid option ($1)" + fi + ;; + esac + done + if [ "$OPT_TEST_CASES" ]; then + TEST_CASES=$OPT_TEST_CASES + fi +} + +# Parameters +DEBUGFS_DIR=`grep debugfs /proc/mounts | cut -f2 -d' '` +TRACING_DIR=$DEBUGFS_DIR/tracing +TOP_DIR=`absdir $0` +TEST_DIR=$TOP_DIR/test.d +TEST_CASES=`find_testcases $TEST_DIR` +LOG_DIR=$TOP_DIR/logs/`date +%Y%m%d-%H%M%S`/ +KEEP_LOG=0 +DEBUG=0 +# Parse command-line options +parse_opts $* + +[ $DEBUG -ne 0 ] && set -x + +# Verify parameters +if [ -z "$DEBUGFS_DIR" -o ! -d "$TRACING_DIR" ]; then + errexit "No ftrace directory found" +fi + +# Preparing logs +LOG_FILE=$LOG_DIR/ftracetest.log +mkdir -p $LOG_DIR || errexit "Failed to make a log directory: $LOG_DIR" +date > $LOG_FILE +prlog() { # messages + echo "$@" | tee -a $LOG_FILE +} +catlog() { #file + cat $1 | tee -a $LOG_FILE +} +prlog "=== Ftrace unit tests ===" + + +# Testcase management +# Test result codes - Dejagnu extended code +PASS=0 # The test succeeded. +FAIL=1 # The test failed, but was expected to succeed. +UNRESOLVED=2 # The test produced indeterminate results. (e.g. interrupted) +UNTESTED=3 # The test was not run, currently just a placeholder. +UNSUPPORTED=4 # The test failed because of lack of feature. +XFAIL=5 # The test failed, and was expected to fail. + +# Accumulations +PASSED_CASES= +FAILED_CASES= +UNRESOLVED_CASES= +UNTESTED_CASES= +UNSUPPORTED_CASES= +XFAILED_CASES= +UNDEFINED_CASES= +TOTAL_RESULT=0 + +CASENO=0 +testcase() { # testfile + CASENO=$((CASENO+1)) + prlog -n "[$CASENO]"`grep "^#[ \t]*description:" $1 | cut -f2 -d:` +} + +eval_result() { # retval sigval + local retval=$2 + if [ $2 -eq 0 ]; then + test $1 -ne 0 && retval=$FAIL + fi + case $retval in + $PASS) + prlog " [PASS]" + PASSED_CASES="$PASSED_CASES $CASENO" + return 0 + ;; + $FAIL) + prlog " [FAIL]" + FAILED_CASES="$FAILED_CASES $CASENO" + return 1 # this is a bug. + ;; + $UNRESOLVED) + prlog " [UNRESOLVED]" + UNRESOLVED_CASES="$UNRESOLVED_CASES $CASENO" + return 1 # this is a kind of bug.. something happened. + ;; + $UNTESTED) + prlog " [UNTESTED]" + UNTESTED_CASES="$UNTESTED_CASES $CASENO" + return 0 + ;; + $UNSUPPORTED) + prlog " [UNSUPPORTED]" + UNSUPPORTED_CASES="$UNSUPPORTED_CASES $CASENO" + return 1 # this is not a bug, but the result should be reported. + ;; + $XFAIL) + prlog " [XFAIL]" + XFAILED_CASES="$XFAILED_CASES $CASENO" + return 0 + ;; + *) + prlog " [UNDEFINED]" + UNDEFINED_CASES="$UNDEFINED_CASES $CASENO" + return 1 # this must be a test bug + ;; + esac +} + +# Signal handling for result codes +SIG_RESULT= +SIG_BASE=36 # Use realtime signals +SIG_PID=$$ + +SIG_UNRESOLVED=$((SIG_BASE + UNRESOLVED)) +exit_unresolved () { + kill -s $SIG_UNRESOLVED $SIG_PID + exit 0 +} +trap 'SIG_RESULT=$UNRESOLVED' $SIG_UNRESOLVED + +SIG_UNTESTED=$((SIG_BASE + UNTESTED)) +exit_untested () { + kill -s $SIG_UNTESTED $SIG_PID + exit 0 +} +trap 'SIG_RESULT=$UNTESTED' $SIG_UNTESTED + +SIG_UNSUPPORTED=$((SIG_BASE + UNSUPPORTED)) +exit_unsupported () { + kill -s $SIG_UNSUPPORTED $SIG_PID + exit 0 +} +trap 'SIG_RESULT=$UNSUPPORTED' $SIG_UNSUPPORTED + +SIG_XFAIL=$((SIG_BASE + XFAIL)) +exit_xfail () { + kill -s $SIG_XFAIL $SIG_PID + exit 0 +} +trap 'SIG_RESULT=$XFAIL' $SIG_XFAIL + +# Run one test case +run_test() { # testfile + local testname=`basename $1` + local testlog=`mktemp --tmpdir=$LOG_DIR ${testname}-XXXXXX.log` + testcase $1 + echo "execute: "$1 > $testlog + SIG_RESULT=0 + # setup PID and PPID, $$ is not updated. + (cd $TRACING_DIR; read PID _ < /proc/self/stat ; + set -e; set -x; . $1) >> $testlog 2>&1 + eval_result $? $SIG_RESULT + if [ $? -eq 0 ]; then + # Remove test log if the test was done as it was expected. + [ $KEEP_LOG -eq 0 ] && rm $testlog + else + catlog $testlog + TOTAL_RESULT=1 + fi +} + +# Main loop +for t in $TEST_CASES; do + run_test $t +done + +prlog "" +prlog "# of passed: " `echo $PASSED_CASES | wc -w` +prlog "# of failed: " `echo $FAILED_CASES | wc -w` +prlog "# of unresolved: " `echo $UNRESOLVED_CASES | wc -w` +prlog "# of untested: " `echo $UNTESTED_CASES | wc -w` +prlog "# of unsupported: " `echo $UNSUPPORTED_CASES | wc -w` +prlog "# of xfailed: " `echo $XFAILED_CASES | wc -w` +prlog "# of undefined(test bug): " `echo $UNDEFINED_CASES | wc -w` + +# if no error, return 0 +exit $TOTAL_RESULT diff --git a/tools/testing/selftests/ftrace/samples/fail.tc b/tools/testing/selftests/ftrace/samples/fail.tc new file mode 100644 index 0000000..15e35b9 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/fail.tc @@ -0,0 +1,4 @@ +#!/bin/sh +# description: failure-case example +cat non-exist-file +echo "this is not executed" diff --git a/tools/testing/selftests/ftrace/samples/pass.tc b/tools/testing/selftests/ftrace/samples/pass.tc new file mode 100644 index 0000000..d015493 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/pass.tc @@ -0,0 +1,3 @@ +#!/bin/sh +# description: pass-case example +return 0 diff --git a/tools/testing/selftests/ftrace/samples/unresolved.tc b/tools/testing/selftests/ftrace/samples/unresolved.tc new file mode 100644 index 0000000..41e99d3 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/unresolved.tc @@ -0,0 +1,4 @@ +#!/bin/sh +# description: unresolved-case example +trap exit_unresolved INT +kill -INT $PID diff --git a/tools/testing/selftests/ftrace/samples/unsupported.tc b/tools/testing/selftests/ftrace/samples/unsupported.tc new file mode 100644 index 0000000..45910ff --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/unsupported.tc @@ -0,0 +1,3 @@ +#!/bin/sh +# description: unsupported-case example +exit_unsupported diff --git a/tools/testing/selftests/ftrace/samples/untested.tc b/tools/testing/selftests/ftrace/samples/untested.tc new file mode 100644 index 0000000..35a4594 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/untested.tc @@ -0,0 +1,3 @@ +#!/bin/sh +# description: untested-case example +exit_untested diff --git a/tools/testing/selftests/ftrace/samples/xfail.tc b/tools/testing/selftests/ftrace/samples/xfail.tc new file mode 100644 index 0000000..9dd3953 --- /dev/null +++ b/tools/testing/selftests/ftrace/samples/xfail.tc @@ -0,0 +1,3 @@ +#!/bin/sh +# description: xfail-case example +cat non-exist-file || exit_xfail diff --git a/tools/testing/selftests/ftrace/test.d/00basic/basic1.tc b/tools/testing/selftests/ftrace/test.d/00basic/basic1.tc new file mode 100644 index 0000000..9980ff1 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/00basic/basic1.tc @@ -0,0 +1,3 @@ +#!/bin/sh +# description: Basic trace file check +test -f README -a -f trace -a -f tracing_on -a -f trace_pipe diff --git a/tools/testing/selftests/ftrace/test.d/00basic/basic2.tc b/tools/testing/selftests/ftrace/test.d/00basic/basic2.tc new file mode 100644 index 0000000..bf9a7b0 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/00basic/basic2.tc @@ -0,0 +1,7 @@ +#!/bin/sh +# description: Basic test for tracers +test -f available_tracers +for t in `cat available_tracers`; do + echo $t > current_tracer +done +echo nop > current_tracer diff --git a/tools/testing/selftests/ftrace/test.d/00basic/basic3.tc b/tools/testing/selftests/ftrace/test.d/00basic/basic3.tc new file mode 100644 index 0000000..bde6625 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/00basic/basic3.tc @@ -0,0 +1,8 @@ +#!/bin/sh +# description: Basic trace clock test +test -f trace_clock +for c in `cat trace_clock | tr -d \[\]`; do + echo $c > trace_clock + grep '\['$c'\]' trace_clock +done +echo local > trace_clock diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc b/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc new file mode 100644 index 0000000..1b8b665 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/add_and_remove.tc @@ -0,0 +1,11 @@ +#!/bin/sh +# description: Kprobe dynamic event - adding and removing + +[ -f kprobe_events ] || exit_unsupported # this is configurable + +echo 0 > events/enable +echo > kprobe_events +echo p:myevent do_fork > kprobe_events +grep myevent kprobe_events +test -d events/kprobes/myevent +echo > kprobe_events diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc new file mode 100644 index 0000000..b55c840 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc @@ -0,0 +1,13 @@ +#!/bin/sh +# description: Kprobe dynamic event - busy event check + +[ -f kprobe_events ] || exit_unsupported + +echo 0 > events/enable +echo > kprobe_events +echo p:myevent do_fork > kprobe_events +test -d events/kprobes/myevent +echo 1 > events/kprobes/myevent/enable +echo > kprobe_events && exit 1 # this must fail +echo 0 > events/kprobes/myevent/enable +echo > kprobe_events # this must succeed diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc new file mode 100644 index 0000000..a603d3f --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc @@ -0,0 +1,16 @@ +#!/bin/sh +# description: Kprobe dynamic event with arguments + +[ -f kprobe_events ] || exit_unsupported # this is configurable + +echo 0 > events/enable +echo > kprobe_events +echo 'p:testprobe do_fork $stack $stack0 +0($stack)' > kprobe_events +grep testprobe kprobe_events +test -d events/kprobes/testprobe +echo 1 > events/kprobes/testprobe/enable +( echo "forked") +echo 0 > events/kprobes/testprobe/enable +echo "-:testprobe" >> kprobe_events +test -d events/kprobes/testprobe && exit 1 || exit 0 + diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc new file mode 100644 index 0000000..283c29e --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc @@ -0,0 +1,15 @@ +#!/bin/sh +# description: Kretprobe dynamic event with arguments + +[ -f kprobe_events ] || exit_unsupported # this is configurable + +echo 0 > events/enable +echo > kprobe_events +echo 'r:testprobe2 do_fork $retval' > kprobe_events +grep testprobe2 kprobe_events +test -d events/kprobes/testprobe2 +echo 1 > events/kprobes/testprobe2/enable +( echo "forked") +echo 0 > events/kprobes/testprobe2/enable +echo '-:testprobe2' >> kprobe_events +test -d events/kprobes/testprobe2 && exit 1 || exit 0 diff --git a/tools/testing/selftests/ftrace/test.d/template b/tools/testing/selftests/ftrace/test.d/template new file mode 100644 index 0000000..5448f7a --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/template @@ -0,0 +1,9 @@ +#!/bin/sh +# description: %HERE DESCRIBE WHAT THIS DOES% +# you have to add ".tc" extention for your testcase file +# Note that all tests are run with "errexit" option. + +exit 0 # Return 0 if the test is passed, otherwise return !0 +# If the test could not run because of lack of feature, call exit_unsupported +# If the test returned unclear results, call exit_unresolved +# If the test is a dummy, or a placeholder, call exit_untested diff --git a/tools/testing/selftests/memfd/Makefile b/tools/testing/selftests/memfd/Makefile index ad4ab01..b80cd10 100644 --- a/tools/testing/selftests/memfd/Makefile +++ b/tools/testing/selftests/memfd/Makefile @@ -1,38 +1,17 @@ -uname_M := $(shell uname -m 2>/dev/null || echo not) -ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/) -ifeq ($(ARCH),i386) - ARCH := x86 -endif -ifeq ($(ARCH),x86_64) - ARCH := x86 -endif - CFLAGS += -D_FILE_OFFSET_BITS=64 -CFLAGS += -I../../../../arch/x86/include/generated/uapi/ -CFLAGS += -I../../../../arch/x86/include/uapi/ CFLAGS += -I../../../../include/uapi/ CFLAGS += -I../../../../include/ all: -ifeq ($(ARCH),x86) gcc $(CFLAGS) memfd_test.c -o memfd_test -else - echo "Not an x86 target, can't build memfd selftest" -endif run_tests: all -ifeq ($(ARCH),x86) gcc $(CFLAGS) memfd_test.c -o memfd_test -endif @./memfd_test || echo "memfd_test: [FAIL]" build_fuse: -ifeq ($(ARCH),x86) gcc $(CFLAGS) fuse_mnt.c `pkg-config fuse --cflags --libs` -o fuse_mnt gcc $(CFLAGS) fuse_test.c -o fuse_test -else - echo "Not an x86 target, can't build memfd selftest" -endif run_fuse: build_fuse @./run_fuse_test.sh || echo "fuse_test: [FAIL]" diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c index 3634c90..0b9eafb 100644 --- a/tools/testing/selftests/memfd/memfd_test.c +++ b/tools/testing/selftests/memfd/memfd_test.c @@ -59,9 +59,9 @@ static void mfd_fail_new(const char *name, unsigned int flags) } } -static __u64 mfd_assert_get_seals(int fd) +static unsigned int mfd_assert_get_seals(int fd) { - long r; + int r; r = fcntl(fd, F_GET_SEALS); if (r < 0) { @@ -69,50 +69,48 @@ static __u64 mfd_assert_get_seals(int fd) abort(); } - return r; + return (unsigned int)r; } -static void mfd_assert_has_seals(int fd, __u64 seals) +static void mfd_assert_has_seals(int fd, unsigned int seals) { - __u64 s; + unsigned int s; s = mfd_assert_get_seals(fd); if (s != seals) { - printf("%llu != %llu = GET_SEALS(%d)\n", - (unsigned long long)seals, (unsigned long long)s, fd); + printf("%u != %u = GET_SEALS(%d)\n", seals, s, fd); abort(); } } -static void mfd_assert_add_seals(int fd, __u64 seals) +static void mfd_assert_add_seals(int fd, unsigned int seals) { - long r; - __u64 s; + int r; + unsigned int s; s = mfd_assert_get_seals(fd); r = fcntl(fd, F_ADD_SEALS, seals); if (r < 0) { - printf("ADD_SEALS(%d, %llu -> %llu) failed: %m\n", - fd, (unsigned long long)s, (unsigned long long)seals); + printf("ADD_SEALS(%d, %u -> %u) failed: %m\n", fd, s, seals); abort(); } } -static void mfd_fail_add_seals(int fd, __u64 seals) +static void mfd_fail_add_seals(int fd, unsigned int seals) { - long r; - __u64 s; + int r; + unsigned int s; r = fcntl(fd, F_GET_SEALS); if (r < 0) s = 0; else - s = r; + s = (unsigned int)r; r = fcntl(fd, F_ADD_SEALS, seals); if (r >= 0) { - printf("ADD_SEALS(%d, %llu -> %llu) didn't fail as expected\n", - fd, (unsigned long long)s, (unsigned long long)seals); + printf("ADD_SEALS(%d, %u -> %u) didn't fail as expected\n", + fd, s, seals); abort(); } } @@ -205,7 +203,7 @@ static void mfd_fail_open(int fd, int flags, mode_t mode) sprintf(buf, "/proc/self/fd/%d", fd); r = open(buf, flags, mode); if (r >= 0) { - printf("open(%s) didn't fail as expected\n"); + printf("open(%s) didn't fail as expected\n", buf); abort(); } } |