/* * sound/oss/sb_midi.c * * The low level driver for the Sound Blaster DS chips. * * * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. */ #include #include #include "sound_config.h" #include "sb.h" #undef SB_TEST_IRQ /* * The DSP channel can be used either for input or output. Variable * 'sb_irq_mode' will be set when the program calls read or write first time * after open. Current version doesn't support mode changes without closing * and reopening the device. Support for this feature may be implemented in a * future version of this driver. */ static int sb_midi_open(int dev, int mode, void (*input) (int dev, unsigned char data), void (*output) (int dev) ) { sb_devc *devc = midi_devs[dev]->devc; unsigned long flags; if (devc == NULL) return -ENXIO; spin_lock_irqsave(&devc->lock, flags); if (devc->opened) { spin_unlock_irqrestore(&devc->lock, flags); return -EBUSY; } devc->opened = 1; spin_unlock_irqrestore(&devc->lock, flags); devc->irq_mode = IMODE_MIDI; devc->midi_broken = 0; sb_dsp_reset(devc); if (!sb_dsp_command(devc, 0x35)) /* Start MIDI UART mode */ { devc->opened = 0; return -EIO; } devc->intr_active = 1; if (mode & OPEN_READ) { devc->input_opened = 1; devc->midi_input_intr = input; } return 0; } static void sb_midi_close(int dev) { sb_devc *devc = midi_devs[dev]->devc; unsigned long flags; if (devc == NULL) return; spin_lock_irqsave(&devc->lock, flags); sb_dsp_reset(devc); devc->intr_active = 0; devc->input_opened = 0; devc->opened = 0; spin_unlock_irqrestore(&devc->lock, flags); } static int sb_midi_out(int dev, unsigned char midi_byte) { sb_devc *devc = midi_devs[dev]->devc; if (devc == NULL) return 1; if (devc->midi_broken) return 1; if (!sb_dsp_command(devc, midi_byte)) { devc->midi_broken = 1; return 1; } return 1; } static int sb_midi_start_read(int dev) { return 0; } static int sb_midi_end_read(int dev) { sb_devc *devc = midi_devs[dev]->devc; if (devc == NULL) return -ENXIO; sb_dsp_reset(devc); devc->intr_active = 0; return 0; } static int sb_midi_ioctl(int dev, unsigned cmd, void __user *arg) { return -EINVAL; } void sb_midi_interrupt(sb_devc * devc) { unsigned long flags; unsigned char data; if (devc == NULL) return; spin_lock_irqsave(&devc->lock, flags); data = inb(DSP_READ); if (devc->input_opened) devc->midi_input_intr(devc->my_mididev, data); spin_unlock_irqrestore(&devc->lock, flags); } #define MIDI_SYNTH_NAME "Sound Blaster Midi" #define MIDI_SYNTH_CAPS 0 #include "midi_synth.h" static struct midi_operations sb_midi_operations = { .owner = THIS_MODULE, .info = {"Sound Blaster", 0, 0, SNDCARD_SB}, .converter = &std_midi_synth, .in_info = {0}, .open = sb_midi_open, .close = sb_midi_close, .ioctl = sb_midi_ioctl, .outputc = sb_midi_out, .start_read = sb_midi_start_read, .end_read = sb_midi_end_read, }; void sb_dsp_midi_init(sb_devc * devc, struct module *owner) { int dev; if (devc->model < 2) /* No MIDI support for SB 1.x */ return; dev = sound_alloc_mididev(); if (dev == -1) { printk(KERN_ERR "sb_midi: too many MIDI devices detected\n"); return; } std_midi_synth.midi_dev = devc->my_mididev = dev; midi_devs[dev] = kmalloc(sizeof(struct midi_operations), GFP_KERNEL); if (midi_devs[dev] == NULL) { printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); sound_unload_mididev(dev); return; } memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations, sizeof(struct midi_operations)); if (owner) midi_devs[dev]->owner = owner; midi_devs[dev]->devc = devc; midi_devs[dev]->converter = kmalloc(sizeof(struct synth_operations), GFP_KERNEL); if (midi_devs[dev]->converter == NULL) { printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); kfree(midi_devs[dev]); sound_unload_mididev(dev); return; } memcpy((char *) midi_devs[dev]->converter, (char *) &std_midi_synth, sizeof(struct synth_operations)); midi_devs[dev]->converter->id = "SBMIDI"; sequencer_init(); } l'>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 /sound/soc/soc-ac97.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 'sound/soc/soc-ac97.c')