/*
* Copyright (C) 2014-2015 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef __CYGNUS_SSP_H__
#define __CYGNUS_SSP_H__
#define CYGNUS_TDM_DAI_MAX_SLOTS 16
#define CYGNUS_MAX_PLAYBACK_PORTS 4
#define CYGNUS_MAX_CAPTURE_PORTS 3
#define CYGNUS_MAX_I2S_PORTS 3
#define CYGNUS_MAX_PORTS CYGNUS_MAX_PLAYBACK_PORTS
#define CYGNUS_AUIDO_MAX_NUM_CLKS 3
#define CYGNUS_SSP_FRAMEBITS_DIV 1
#define CYGNUS_SSPMODE_I2S 0
#define CYGNUS_SSPMODE_TDM 1
#define CYGNUS_SSPMODE_UNKNOWN -1
#define CYGNUS_SSP_CLKSRC_PLL 0
/* Max string length of our dt property names */
#define PROP_LEN_MAX 40
struct ringbuf_regs {
unsigned rdaddr;
unsigned wraddr;
unsigned baseaddr;
unsigned endaddr;
unsigned fmark; /* freemark for play, fullmark for caputure */
unsigned period_bytes;
unsigned buf_size;
};
#define RINGBUF_REG_PLAYBACK(num) ((struct ringbuf_regs) { \
.rdaddr = SRC_RBUF_ ##num## _RDADDR_OFFSET, \
.wraddr = SRC_RBUF_ ##num## _WRADDR_OFFSET, \
.baseaddr = SRC_RBUF_ ##num## _BASEADDR_OFFSET, \
.endaddr = SRC_RBUF_ ##num## _ENDADDR_OFFSET, \
.fmark = SRC_RBUF_ ##num## _FREE_MARK_OFFSET, \
.period_bytes = 0, \
.buf_size = 0, \
})
#define RINGBUF_REG_CAPTURE(num) ((struct ringbuf_regs) { \
.rdaddr = DST_RBUF_ ##num## _RDADDR_OFFSET, \
.wraddr = DST_RBUF_ ##num## _WRADDR_OFFSET, \
.baseaddr = DST_RBUF_ ##num## _BASEADDR_OFFSET, \
.endaddr = DST_RBUF_ ##num## _ENDADDR_OFFSET, \
.fmark = DST_RBUF_ ##num## _FULL_MARK_OFFSET, \
.period_bytes = 0, \
.buf_size = 0, \
})
enum cygnus_audio_port_type {
PORT_TDM,
PORT_SPDIF,
};
struct cygnus_ssp_regs {
u32 i2s_stream_cfg;
u32 i2s_cfg;
u32 i2s_cap_stream_cfg;
u32 i2s_cap_cfg;
u32 i2s_mclk_cfg;
u32 bf_destch_ctrl;
u32 bf_destch_cfg;
u32 bf_sourcech_ctrl;
u32 bf_sourcech_cfg;
u32 bf_sourcech_grp;
};
struct cygnus_track_clk {
bool cap_en;
bool play_en;
bool cap_clk_en;
bool play_clk_en;
};
struct cygnus_aio_port {
int portnum;
int mode;
bool is_slave;
int streams_on; /* will be 0 if both capture and play are off */
int fsync_width;
int port_type;
u32 mclk;
u32 lrclk;
u32 bit_per_frame;
u32 pll_clk_num;
struct cygnus_audio *cygaud;
struct cygnus_ssp_regs regs;
struct ringbuf_regs play_rb_regs;
struct ringbuf_regs capture_rb_regs;
struct snd_pcm_substream *play_stream;
struct snd_pcm_substream *capture_stream;
struct cygnus_track_clk clk_trace;
};
struct cygnus_audio {
struct cygnus_aio_port portinfo[CYGNUS_MAX_PORTS];
int irq_num;
void __iomem *audio;
struct device *dev;
void __iomem *i2s_in;
struct clk *audio_clk[CYGNUS_AUIDO_MAX_NUM_CLKS];
int active_ports;
unsigned long vco_rate;
};
extern int cygnus_ssp_get_mode(struct snd_soc_dai *cpu_dai);
extern int cygnus_ssp_add_pll_tweak_controls(struct snd_soc_pcm_runtime *rtd);
extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
int len);
extern int cygnus_soc_platform_register(struct device *dev,
struct cygnus_audio *cygaud);
extern int cygnus_soc_platform_unregister(struct device *dev);
extern int cygnus_ssp_set_custom_fsync_width(struct snd_soc_dai *cpu_dai,
int len);
#endif
ext.git/commit/tools/perf/arch/arm/util/unwind-libunwind.c?id=966d2b04e070bc040319aaebfec09e0144dc3341'>unwind-libunwind.c
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+