/* * Copyright 2013, Michael Ellerman, IBM Corp. * Licensed under GPLv2. */ #define _GNU_SOURCE #include #include #include #include #include #include "event.h" int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags); } void event_init_opts(struct event *e, u64 config, int type, char *name) { memset(e, 0, sizeof(*e)); e->name = name; e->attr.type = type; e->attr.config = config; e->attr.size = sizeof(e->attr); /* This has to match the structure layout in the header */ e->attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | \ PERF_FORMAT_TOTAL_TIME_RUNNING; } void event_init_named(struct event *e, u64 config, char *name) { event_init_opts(e, config, PERF_TYPE_RAW, name); } void event_init(struct event *e, u64 config) { event_init_opts(e, config, PERF_TYPE_RAW, "event"); } #define PERF_CURRENT_PID 0 #define PERF_NO_PID -1 #define PERF_NO_CPU -1 #define PERF_NO_GROUP -1 int event_open_with_options(struct event *e, pid_t pid, int cpu, int group_fd) { e->fd = perf_event_open(&e->attr, pid, cpu, group_fd, 0); if (e->fd == -1) { perror("perf_event_open"); return -1; } return 0; } int event_open_with_group(struct event *e, int group_fd) { return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, group_fd); } int event_open_with_pid(struct event *e, pid_t pid) { return event_open_with_options(e, pid, PERF_NO_CPU, PERF_NO_GROUP); } int event_open_with_cpu(struct event *e, int cpu) { return event_open_with_options(e, PERF_NO_PID, cpu, PERF_NO_GROUP); } int event_open(struct event *e) { return event_open_with_options(e, PERF_CURRENT_PID, PERF_NO_CPU, PERF_NO_GROUP); } void event_close(struct event *e) { close(e->fd); } int event_enable(struct event *e) { return ioctl(e->fd, PERF_EVENT_IOC_ENABLE); } int event_disable(struct event *e) { return ioctl(e->fd, PERF_EVENT_IOC_DISABLE); } int event_reset(struct event *e) { return ioctl(e->fd, PERF_EVENT_IOC_RESET); } int event_read(struct event *e) { int rc; rc = read(e->fd, &e->result, sizeof(e->result)); if (rc != sizeof(e->result)) { fprintf(stderr, "read error on event %p!\n", e); return -1; } return 0; } void event_report_justified(struct event *e, int name_width, int result_width) { printf("%*s: result %*llu ", name_width, e->name, result_width, e->result.value); if (e->result.running == e->result.enabled) printf("running/enabled %llu\n", e->result.running); else printf("running %llu enabled %llu\n", e->result.running, e->result.enabled); } void event_report(struct event *e) { event_report_justified(e, 0, 0); } linux/net-next.git/commit/fs?id=966d2b04e070bc040319aaebfec09e0144dc3341'>fs/xfs/xfs_qm_bhv.c
diff options
context:
space:
mode:
authorDouglas Miller <dougmill@linux.vnet.ibm.com>2017-01-28 06:42:20 -0600
committerTejun Heo <tj@kernel.org>2017-01-28 07:49:42 -0500
commit966d2b04e070bc040319aaebfec09e0144dc3341 (patch)
tree4b96156e3d1dd4dfd6039b7c219c9dc4616da52d /fs/xfs/xfs_qm_bhv.c
parent1b1bc42c1692e9b62756323c675a44cb1a1f9dbd (diff)
percpu-refcount: fix reference leak during percpu-atomic transition
percpu_ref_tryget() and percpu_ref_tryget_live() should return "true" IFF they acquire a reference. But the return value from atomic_long_inc_not_zero() is a long and may have high bits set, e.g. PERCPU_COUNT_BIAS, and the return value of the tryget routines is bool so the reference may actually be acquired but the routines return "false" which results in a reference leak since the caller assumes it does not need to do a corresponding percpu_ref_put(). This was seen when performing CPU hotplug during I/O, as hangs in blk_mq_freeze_queue_wait where percpu_ref_kill (blk_mq_freeze_queue_start) raced with percpu_ref_tryget (blk_mq_timeout_work). Sample stack trace: __switch_to+0x2c0/0x450 __schedule+0x2f8/0x970 schedule+0x48/0xc0 blk_mq_freeze_queue_wait+0x94/0x120 blk_mq_queue_reinit_work+0xb8/0x180 blk_mq_queue_reinit_prepare+0x84/0xa0 cpuhp_invoke_callback+0x17c/0x600 cpuhp_up_callbacks+0x58/0x150 _cpu_up+0xf0/0x1c0 do_cpu_up+0x120/0x150 cpu_subsys_online+0x64/0xe0 device_online+0xb4/0x120 online_store+0xb4/0xc0 dev_attr_store+0x68/0xa0 sysfs_kf_write+0x80/0xb0 kernfs_fop_write+0x17c/0x250 __vfs_write+0x6c/0x1e0 vfs_write+0xd0/0x270 SyS_write+0x6c/0x110 system_call+0x38/0xe0 Examination of the queue showed a single reference (no PERCPU_COUNT_BIAS, and __PERCPU_REF_DEAD, __PERCPU_REF_ATOMIC set) and no requests. However, conditions at the time of the race are count of PERCPU_COUNT_BIAS + 0 and __PERCPU_REF_DEAD and __PERCPU_REF_ATOMIC set. The fix is to make the tryget routines use an actual boolean internally instead of the atomic long result truncated to a int. Fixes: e625305b3907 percpu-refcount: make percpu_ref based on longs instead of ints Link: https://bugzilla.kernel.org/show_bug.cgi?id=190751 Signed-off-by: Douglas Miller <dougmill@linux.vnet.ibm.com> Reviewed-by: Jens Axboe <axboe@fb.com> Signed-off-by: Tejun Heo <tj@kernel.org> Fixes: e625305b3907 ("percpu-refcount: make percpu_ref based on longs instead of ints") Cc: stable@vger.kernel.org # v3.18+
Diffstat (limited to 'fs/xfs/xfs_qm_bhv.c')