This is the 4.4.269 stable release
# gpg: Signature made Sat May 22 11:39:12 2021 EEST
# gpg: using RSA key 647F28654894E3BD457199BE38DBBDC86092693E
# gpg: Good signature from "Greg Kroah-Hartman <gregkh@kernel.org>" [full]
# gpg: gregkh@kernel.org: Verified 20 signatures in the past 2 hours. Encrypted
# 0 messages.
# By Colin Ian King (11) and others
# Via Greg Kroah-Hartman
* tag 'v4.4.269':
Linux 4.4.269
iio: tsl2583: Fix division by a zero lux_val
xhci: Do not use GFP_KERNEL in (potentially) atomic context
ipv6: remove extra dev_hold() for fallback tunnels
ip6_tunnel: sit: proper dev_{hold|put} in ndo_[un]init methods
sit: proper dev_{hold|put} in ndo_[un]init methods
ALSA: hda: generic: change the DAC ctl name for LO+SPK or LO+HP
um: Mark all kernel symbols as local
ACPI / hotplug / PCI: Fix reference count leak in enable_slot()
isdn: capi: fix mismatched prototypes
cxgb4: Fix the -Wmisleading-indentation warning
usb: sl811-hcd: improve misleading indentation
kgdb: fix gcc-11 warning on indentation
x86/msr: Fix wr/rdmsr_safe_regs_on_cpu() prototypes
kobject_uevent: remove warning in init_uevent_argv()
dm ioctl: fix out of bounds array access when no devices
thermal/core/fair share: Lock the thermal zone while looping over instances
FDDI: defxx: Make MMIO the configuration default except for EISA
KVM: x86: Cancel pvclock_gtod_work on module removal
usb: core: hub: fix race condition about TRSMRCY of resume
usb: dwc2: Fix gadget DMA unmap direction
usb: xhci: Increase timeout for HC halt
usb: fotg210-hcd: Fix an error message
squashfs: fix divide error in calculate_skip()
powerpc/64s: Fix crashes when toggling entry flush barrier
ARC: entry: fix off-by-one error in syscall number validation
kernel: kexec_file: fix error return code of kexec_calculate_store_digests()
ksm: fix potential missing rmap_item for stable_node
drm/radeon: Fix off-by-one power_state index heap overwrite
sctp: fix a SCTP_MIB_CURRESTAB leak in sctp_sf_do_dupcook_b
NFSv4.2 fix handling of sr_eof in SEEK's reply
pNFS/flexfiles: fix incorrect size check in decode_nfs_fh()
NFS: Deal correctly with attribute generation counter overflow
PCI: Release OF node in pci_scan_device()'s error path
ASoC: rt286: Make RT286_SET_GPIO_* readable and writable
powerpc/iommu: Annotate nested lock for lockdep
wl3501_cs: Fix out-of-bounds warnings in wl3501_mgmt_join
wl3501_cs: Fix out-of-bounds warnings in wl3501_send_pkt
ASoC: rt286: Generalize support for ALC3263 codec
sctp: Fix out-of-bounds warning in sctp_process_asconf_param()
kconfig: nconf: stop endless search loops
selftests: Set CC to clang in lib.mk if LLVM is set
cuse: prevent clone
mac80211: clear the beacon's CRC after channel switch
ip6_vti: proper dev_{hold|put} in ndo_[un]init methods
Bluetooth: initialize skb_queue_head at l2cap_chan_create()
Bluetooth: Set CONF_NOT_COMPLETE as l2cap_chan default
ALSA: rme9652: don't disable if not enabled
ALSA: hdspm: don't disable if not enabled
ALSA: hdsp: don't disable if not enabled
net: stmmac: Set FIFO sizes for ipq806x
tipc: convert dest node's address to network order
fs: dlm: fix debugfs dump
sctp: delay auto_asconf init until binding the first addr
Revert "net/sctp: fix race condition in sctp_destroy_sock"
kfifo: fix ternary sign extension bugs
net:nfc:digital: Fix a double free in digital_tg_recv_dep_req
powerpc/52xx: Fix an invalid ASM expression ('addi' used instead of 'add')
ath9k: Fix error check in ath9k_hw_read_revisions() for PCI devices
net: davinci_emac: Fix incorrect masking of tx and rx error channel
vsock/vmci: log once the failed queue pair allocation
mwl8k: Fix a double Free in mwl8k_probe_hw
i2c: sh7760: fix IRQ error path
powerpc/pseries: extract host bridge from pci_bus prior to bus removal
i2c: sh7760: add IRQ check
i2c: jz4780: add IRQ check
i2c: cadence: add IRQ check
net: thunderx: Fix unintentional sign extension issue
mt7601u: fix always true expression
mac80211: bail out if cipher schemes are invalid
powerpc: iommu: fix build when neither PCI or IBMVIO is set
ALSA: usb-audio: Add error checks for usb_driver_claim_interface() calls
nfc: pn533: prevent potential memory corruption
ALSA: core: remove redundant spin_lock pair in snd_card_disconnect
powerpc/prom: Mark identical_pvr_fixup as __init
net: lapbether: Prevent racing when checking whether the netif is running
HID: plantronics: Workaround for double volume key presses
x86/events/amd/iommu: Fix sysfs type mismatch
HSI: core: fix resource leaks in hsi_add_client_from_dt()
scsi: sni_53c710: Add IRQ check
scsi: sun3x_esp: Add IRQ check
scsi: jazz_esp: Add IRQ check
media: dvb-usb-remote: fix dvb_usb_nec_rc_key_to_event type mismatch
scsi: fcoe: Fix mismatched fcoe_wwn_from_mac declaration
ata: libahci_platform: fix IRQ check
sata_mv: add IRQ checks
pata_ipx4xx_cf: fix IRQ check
pata_arasan_cf: fix IRQ check
media: m88rs6000t: avoid potential out-of-bounds reads on arrays
media: omap4iss: return error code when omap4iss_get() failed
media: vivid: fix assignment of dev->fbuf_out_flags
ttyprintk: Add TTY hangup callback.
tty: fix return value for unsupported ioctls
USB: cdc-acm: fix unprivileged TIOCCSERIAL
usb: gadget: r8a66597: Add missing null check on return from platform_get_resource
crypto: qat - Fix a double free in adf_create_ring
staging: rtl8192u: Fix potential infinite loop
crypto: qat - fix error path in adf_isr_resource_alloc()
mtd: require write permissions for locking and badblock ioctls
fotg210-udc: Complete OUT requests on short packets
fotg210-udc: Don't DMA more than the buffer can take
fotg210-udc: Mask GRP2 interrupts we don't handle
fotg210-udc: Remove a dubious condition leading to fotg210_done
fotg210-udc: Fix EP0 IN requests bigger than two packets
fotg210-udc: Fix DMA on EP0 for length > max packet size
crypto: qat - don't release uninitialized resources
usb: gadget: pch_udc: Check for DMA mapping error
usb: gadget: pch_udc: Check if driver is present before calling ->setup()
usb: gadget: pch_udc: Replace cpu_to_le32() by lower_32_bits()
ARM: dts: exynos: correct PMIC interrupt trigger level on Snow
ARM: dts: exynos: correct PMIC interrupt trigger level on SMDK5250
memory: gpmc: fix out of bounds read and dereference on gpmc_cs[]
usb: gadget: pch_udc: Revert d3cb25a121 completely
KVM: s390: split kvm_s390_real_to_abs
ALSA: hda/realtek: Remove redundant entry for ALC861 Haier/Uniwill devices
ALSA: hda/realtek: Re-order ALC269 Sony quirk table entries
ALSA: hda/realtek: Re-order ALC882 Sony quirk table entries
ALSA: hda/realtek: Re-order ALC882 Acer quirk table entries
drm/radeon: fix copy of uninitialized variable back to userspace
cfg80211: scan: drop entry from hidden_list on overflow
ipw2x00: potential buffer overflow in libipw_wx_set_encodeext()
md: md_open returns -EBUSY when entering racing area
md: factor out a mddev_find_locked helper from mddev_find
tracing: Restructure trace_clock_global() to never block
tracing: Map all PIDs to command lines
tracing: Treat recording comm for idle task as a success
tracing: Use strlcpy() instead of strcpy() in __trace_find_cmdline()
misc: vmw_vmci: explicitly initialize vmci_datagram payload
misc: vmw_vmci: explicitly initialize vmci_notify_bm_set_msg struct
misc: lis3lv02d: Fix false-positive WARN on various HP models
FDDI: defxx: Bail out gracefully with unassigned PCI resource for CSR
net/nfc: fix use-after-free llcp_sock_bind/connect
hsr: use netdev_err() instead of WARN_ONCE()
Bluetooth: verify AMP hci_chan before amp_destroy
dm space map common: fix division bug in sm_ll_find_free_block()
dm persistent data: packed struct should have an aligned() attribute too
usb: gadget/function/f_fs string table fix for multiple languages
usb: gadget: dummy_hcd: fix gpf in gadget_setup
ext4: fix error code in ext4_commit_super
ext4: fix check to prevent false positive report of incorrect used inodes
ftrace: Handle commands when closing set_ftrace_filter file
jffs2: check the validity of dstlen in jffs2_zlib_compress()
powerpc: fix EDEADLOCK redefinition error in uapi/asm/errno.h
powerpc/eeh: Fix EEH handling for hugepages in ioremap space.
jffs2: Fix kasan slab-out-of-bounds problem
openvswitch: fix stack OOB read while fragmenting IPv4 packets
arm64/vdso: Discard .note.gnu.property sections in vDSO
ALSA: sb: Fix two use after free in snd_sb_qsound_build
ALSA: emu8000: Fix a use after free in snd_emu8000_create_mixer
scsi: libfc: Fix a format specifier
scsi: lpfc: Fix crash when a REG_RPI mailbox fails triggering a LOGO response
drm/amdgpu: fix NULL pointer dereference
drm/msm/mdp5: Configure PP_SYNC_HEIGHT to double the vtotal
media: gscpa/stv06xx: fix memory leak
media: dvb-usb: fix memory leak in dvb_usb_adapter_init
media: i2c: adv7511-v4l2: fix possible use-after-free in adv7511_remove()
power: supply: s3c_adc_battery: fix possible use-after-free in s3c_adc_bat_remove()
power: supply: generic-adc-battery: fix possible use-after-free in gab_remove()
clk: socfpga: arria10: Fix memory leak of socfpga_clk on error return
media: em28xx: fix memory leak
media: gspca/sq905.c: fix uninitialized variable
media: media/saa7164: fix saa7164_encoder_register() memory leak bugs
media: ite-cir: check for receive overflow
scsi: target: pscsi: Fix warning in pscsi_complete_cmd()
btrfs: convert logic BUG_ON()'s in replace_path to ASSERT()'s
phy: phy-twl4030-usb: Fix possible use-after-free in twl4030_usb_remove()
intel_th: Consistency and off-by-one fix
spi: omap-100k: Fix reference leak to master
spi: dln2: Fix reference leak to master
x86/build: Propagate $(CLANG_FLAGS) to $(REALMODE_FLAGS)
PCI: PM: Do not read power state in pci_enable_device_flags()
usb: gadget: uvc: add bInterval checking for HS mode
staging: wimax/i2400m: fix byte-order issue
fbdev: zero-fill colormap in fbcmap.c
mmc: core: Set read only for SD cards with permanent write protect bit
mmc: core: Do a power cycle when the CMD11 fails
ecryptfs: fix kernel panic with null dev_name
ACPI: custom_method: fix a possible memory leak
ACPI: custom_method: fix potential use-after-free issue
s390/disassembler: increase ebpf disasm buffer size
platform/x86: thinkpad_acpi: Correct thermal sensor allocation
USB: Add reset-resume quirk for WD19's Realtek Hub
USB: Add LPM quirk for Lenovo ThinkPad USB-C Dock Gen2 Ethernet
ALSA: usb-audio: Add MIDI quirk for Vox ToneLab EX
iwlwifi: Fix softirq/hardirq disabling in iwl_pcie_enqueue_hcmd()
net: usb: ax88179_178a: initialize local variables before use
timerfd: Reject ALARM timerfds without CAP_WAKE_ALARM
Conflicts:
drivers/mmc/core/core.c
drivers/usb/core/hub.c
kernel/trace/trace.c
Change-Id: If654a926df1053a3dd1334679898ecc983161565
606 lines
14 KiB
C
606 lines
14 KiB
C
/*
|
|
* fs/timerfd.c
|
|
*
|
|
* Copyright (C) 2007 Davide Libenzi <davidel@xmailserver.org>
|
|
*
|
|
*
|
|
* Thanks to Thomas Gleixner for code reviews and useful comments.
|
|
*
|
|
*/
|
|
|
|
#include <linux/alarmtimer.h>
|
|
#include <linux/file.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/init.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/list.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/time.h>
|
|
#include <linux/hrtimer.h>
|
|
#include <linux/anon_inodes.h>
|
|
#include <linux/timerfd.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/compat.h>
|
|
#include <linux/rcupdate.h>
|
|
|
|
struct timerfd_ctx {
|
|
union {
|
|
struct hrtimer tmr;
|
|
struct alarm alarm;
|
|
} t;
|
|
ktime_t tintv;
|
|
ktime_t moffs;
|
|
wait_queue_head_t wqh;
|
|
u64 ticks;
|
|
int clockid;
|
|
short unsigned expired;
|
|
short unsigned settime_flags; /* to show in fdinfo */
|
|
struct rcu_head rcu;
|
|
struct list_head clist;
|
|
spinlock_t cancel_lock;
|
|
bool might_cancel;
|
|
};
|
|
|
|
static atomic_t instance_count = ATOMIC_INIT(0);
|
|
|
|
static LIST_HEAD(cancel_list);
|
|
static DEFINE_SPINLOCK(cancel_lock);
|
|
|
|
static inline bool isalarm(struct timerfd_ctx *ctx)
|
|
{
|
|
return ctx->clockid == CLOCK_REALTIME_ALARM ||
|
|
ctx->clockid == CLOCK_BOOTTIME_ALARM ||
|
|
ctx->clockid == CLOCK_POWEROFF_ALARM;
|
|
}
|
|
|
|
/*
|
|
* This gets called when the timer event triggers. We set the "expired"
|
|
* flag, but we do not re-arm the timer (in case it's necessary,
|
|
* tintv.tv64 != 0) until the timer is accessed.
|
|
*/
|
|
static void timerfd_triggered(struct timerfd_ctx *ctx)
|
|
{
|
|
unsigned long flags;
|
|
|
|
spin_lock_irqsave(&ctx->wqh.lock, flags);
|
|
ctx->expired = 1;
|
|
ctx->ticks++;
|
|
wake_up_locked(&ctx->wqh);
|
|
spin_unlock_irqrestore(&ctx->wqh.lock, flags);
|
|
}
|
|
|
|
static enum hrtimer_restart timerfd_tmrproc(struct hrtimer *htmr)
|
|
{
|
|
struct timerfd_ctx *ctx = container_of(htmr, struct timerfd_ctx,
|
|
t.tmr);
|
|
timerfd_triggered(ctx);
|
|
return HRTIMER_NORESTART;
|
|
}
|
|
|
|
static enum alarmtimer_restart timerfd_alarmproc(struct alarm *alarm,
|
|
ktime_t now)
|
|
{
|
|
struct timerfd_ctx *ctx = container_of(alarm, struct timerfd_ctx,
|
|
t.alarm);
|
|
timerfd_triggered(ctx);
|
|
return ALARMTIMER_NORESTART;
|
|
}
|
|
|
|
/*
|
|
* Called when the clock was set to cancel the timers in the cancel
|
|
* list. This will wake up processes waiting on these timers. The
|
|
* wake-up requires ctx->ticks to be non zero, therefore we increment
|
|
* it before calling wake_up_locked().
|
|
*/
|
|
void timerfd_clock_was_set(void)
|
|
{
|
|
ktime_t moffs = ktime_mono_to_real((ktime_t){ .tv64 = 0 });
|
|
struct timerfd_ctx *ctx;
|
|
unsigned long flags;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(ctx, &cancel_list, clist) {
|
|
if (!ctx->might_cancel)
|
|
continue;
|
|
spin_lock_irqsave(&ctx->wqh.lock, flags);
|
|
if (ctx->moffs.tv64 != moffs.tv64) {
|
|
ctx->moffs.tv64 = KTIME_MAX;
|
|
ctx->ticks++;
|
|
wake_up_locked(&ctx->wqh);
|
|
}
|
|
spin_unlock_irqrestore(&ctx->wqh.lock, flags);
|
|
}
|
|
rcu_read_unlock();
|
|
}
|
|
|
|
static void __timerfd_remove_cancel(struct timerfd_ctx *ctx)
|
|
{
|
|
if (ctx->might_cancel) {
|
|
ctx->might_cancel = false;
|
|
spin_lock(&cancel_lock);
|
|
list_del_rcu(&ctx->clist);
|
|
spin_unlock(&cancel_lock);
|
|
}
|
|
}
|
|
|
|
static void timerfd_remove_cancel(struct timerfd_ctx *ctx)
|
|
{
|
|
spin_lock(&ctx->cancel_lock);
|
|
__timerfd_remove_cancel(ctx);
|
|
spin_unlock(&ctx->cancel_lock);
|
|
}
|
|
|
|
static bool timerfd_canceled(struct timerfd_ctx *ctx)
|
|
{
|
|
if (!ctx->might_cancel || ctx->moffs.tv64 != KTIME_MAX)
|
|
return false;
|
|
ctx->moffs = ktime_mono_to_real((ktime_t){ .tv64 = 0 });
|
|
return true;
|
|
}
|
|
|
|
static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
|
|
{
|
|
spin_lock(&ctx->cancel_lock);
|
|
if ((ctx->clockid == CLOCK_REALTIME ||
|
|
ctx->clockid == CLOCK_REALTIME_ALARM ||
|
|
ctx->clockid == CLOCK_POWEROFF_ALARM) &&
|
|
(flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
|
|
if (!ctx->might_cancel) {
|
|
ctx->might_cancel = true;
|
|
spin_lock(&cancel_lock);
|
|
list_add_rcu(&ctx->clist, &cancel_list);
|
|
spin_unlock(&cancel_lock);
|
|
}
|
|
} else {
|
|
__timerfd_remove_cancel(ctx);
|
|
}
|
|
spin_unlock(&ctx->cancel_lock);
|
|
}
|
|
|
|
static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx)
|
|
{
|
|
ktime_t remaining;
|
|
|
|
if (isalarm(ctx))
|
|
remaining = alarm_expires_remaining(&ctx->t.alarm);
|
|
else
|
|
remaining = hrtimer_expires_remaining_adjusted(&ctx->t.tmr);
|
|
|
|
return remaining.tv64 < 0 ? ktime_set(0, 0): remaining;
|
|
}
|
|
|
|
static int timerfd_setup(struct timerfd_ctx *ctx, int flags,
|
|
const struct itimerspec *ktmr)
|
|
{
|
|
enum hrtimer_mode htmode;
|
|
ktime_t texp;
|
|
int clockid = ctx->clockid;
|
|
enum alarmtimer_type type;
|
|
|
|
htmode = (flags & TFD_TIMER_ABSTIME) ?
|
|
HRTIMER_MODE_ABS: HRTIMER_MODE_REL;
|
|
|
|
texp = timespec_to_ktime(ktmr->it_value);
|
|
ctx->expired = 0;
|
|
ctx->ticks = 0;
|
|
ctx->tintv = timespec_to_ktime(ktmr->it_interval);
|
|
|
|
if (isalarm(ctx)) {
|
|
type = clock2alarm(ctx->clockid);
|
|
alarm_init(&ctx->t.alarm, type, timerfd_alarmproc);
|
|
} else {
|
|
hrtimer_init(&ctx->t.tmr, clockid, htmode);
|
|
hrtimer_set_expires(&ctx->t.tmr, texp);
|
|
ctx->t.tmr.function = timerfd_tmrproc;
|
|
}
|
|
|
|
if (texp.tv64 != 0) {
|
|
if (isalarm(ctx)) {
|
|
if (flags & TFD_TIMER_ABSTIME)
|
|
alarm_start(&ctx->t.alarm, texp);
|
|
else
|
|
alarm_start_relative(&ctx->t.alarm, texp);
|
|
} else {
|
|
hrtimer_start(&ctx->t.tmr, texp, htmode);
|
|
}
|
|
|
|
if (timerfd_canceled(ctx))
|
|
return -ECANCELED;
|
|
}
|
|
|
|
ctx->settime_flags = flags & TFD_SETTIME_FLAGS;
|
|
return 0;
|
|
}
|
|
|
|
static int timerfd_release(struct inode *inode, struct file *file)
|
|
{
|
|
struct timerfd_ctx *ctx = file->private_data;
|
|
|
|
timerfd_remove_cancel(ctx);
|
|
|
|
if (isalarm(ctx))
|
|
alarm_cancel(&ctx->t.alarm);
|
|
else
|
|
hrtimer_cancel(&ctx->t.tmr);
|
|
kfree_rcu(ctx, rcu);
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int timerfd_poll(struct file *file, poll_table *wait)
|
|
{
|
|
struct timerfd_ctx *ctx = file->private_data;
|
|
unsigned int events = 0;
|
|
unsigned long flags;
|
|
|
|
poll_wait(file, &ctx->wqh, wait);
|
|
|
|
spin_lock_irqsave(&ctx->wqh.lock, flags);
|
|
if (ctx->ticks)
|
|
events |= POLLIN;
|
|
spin_unlock_irqrestore(&ctx->wqh.lock, flags);
|
|
|
|
return events;
|
|
}
|
|
|
|
static ssize_t timerfd_read(struct file *file, char __user *buf, size_t count,
|
|
loff_t *ppos)
|
|
{
|
|
struct timerfd_ctx *ctx = file->private_data;
|
|
ssize_t res;
|
|
u64 ticks = 0;
|
|
|
|
if (count < sizeof(ticks))
|
|
return -EINVAL;
|
|
spin_lock_irq(&ctx->wqh.lock);
|
|
if (file->f_flags & O_NONBLOCK)
|
|
res = -EAGAIN;
|
|
else
|
|
res = wait_event_interruptible_locked_irq(ctx->wqh, ctx->ticks);
|
|
|
|
/*
|
|
* If clock has changed, we do not care about the
|
|
* ticks and we do not rearm the timer. Userspace must
|
|
* reevaluate anyway.
|
|
*/
|
|
if (timerfd_canceled(ctx)) {
|
|
ctx->ticks = 0;
|
|
ctx->expired = 0;
|
|
res = -ECANCELED;
|
|
}
|
|
|
|
if (ctx->ticks) {
|
|
ticks = ctx->ticks;
|
|
|
|
if (ctx->expired && ctx->tintv.tv64) {
|
|
/*
|
|
* If tintv.tv64 != 0, this is a periodic timer that
|
|
* needs to be re-armed. We avoid doing it in the timer
|
|
* callback to avoid DoS attacks specifying a very
|
|
* short timer period.
|
|
*/
|
|
if (isalarm(ctx)) {
|
|
ticks += alarm_forward_now(
|
|
&ctx->t.alarm, ctx->tintv) - 1;
|
|
alarm_restart(&ctx->t.alarm);
|
|
} else {
|
|
ticks += hrtimer_forward_now(&ctx->t.tmr,
|
|
ctx->tintv) - 1;
|
|
hrtimer_restart(&ctx->t.tmr);
|
|
}
|
|
}
|
|
ctx->expired = 0;
|
|
ctx->ticks = 0;
|
|
}
|
|
spin_unlock_irq(&ctx->wqh.lock);
|
|
if (ticks)
|
|
res = put_user(ticks, (u64 __user *) buf) ? -EFAULT: sizeof(ticks);
|
|
return res;
|
|
}
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
static void timerfd_show(struct seq_file *m, struct file *file)
|
|
{
|
|
struct timerfd_ctx *ctx = file->private_data;
|
|
struct itimerspec t;
|
|
|
|
spin_lock_irq(&ctx->wqh.lock);
|
|
t.it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
|
|
t.it_interval = ktime_to_timespec(ctx->tintv);
|
|
spin_unlock_irq(&ctx->wqh.lock);
|
|
|
|
seq_printf(m,
|
|
"clockid: %d\n"
|
|
"ticks: %llu\n"
|
|
"settime flags: 0%o\n"
|
|
"it_value: (%llu, %llu)\n"
|
|
"it_interval: (%llu, %llu)\n",
|
|
ctx->clockid,
|
|
(unsigned long long)ctx->ticks,
|
|
ctx->settime_flags,
|
|
(unsigned long long)t.it_value.tv_sec,
|
|
(unsigned long long)t.it_value.tv_nsec,
|
|
(unsigned long long)t.it_interval.tv_sec,
|
|
(unsigned long long)t.it_interval.tv_nsec);
|
|
}
|
|
#else
|
|
#define timerfd_show NULL
|
|
#endif
|
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
|
static long timerfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct timerfd_ctx *ctx = file->private_data;
|
|
int ret = 0;
|
|
|
|
switch (cmd) {
|
|
case TFD_IOC_SET_TICKS: {
|
|
u64 ticks;
|
|
|
|
if (copy_from_user(&ticks, (u64 __user *)arg, sizeof(ticks)))
|
|
return -EFAULT;
|
|
if (!ticks)
|
|
return -EINVAL;
|
|
|
|
spin_lock_irq(&ctx->wqh.lock);
|
|
if (!timerfd_canceled(ctx)) {
|
|
ctx->ticks = ticks;
|
|
wake_up_locked(&ctx->wqh);
|
|
} else
|
|
ret = -ECANCELED;
|
|
spin_unlock_irq(&ctx->wqh.lock);
|
|
break;
|
|
}
|
|
default:
|
|
ret = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#else
|
|
#define timerfd_ioctl NULL
|
|
#endif
|
|
|
|
static const struct file_operations timerfd_fops = {
|
|
.release = timerfd_release,
|
|
.poll = timerfd_poll,
|
|
.read = timerfd_read,
|
|
.llseek = noop_llseek,
|
|
.show_fdinfo = timerfd_show,
|
|
.unlocked_ioctl = timerfd_ioctl,
|
|
};
|
|
|
|
static int timerfd_fget(int fd, struct fd *p)
|
|
{
|
|
struct fd f = fdget(fd);
|
|
if (!f.file)
|
|
return -EBADF;
|
|
if (f.file->f_op != &timerfd_fops) {
|
|
fdput(f);
|
|
return -EINVAL;
|
|
}
|
|
*p = f;
|
|
return 0;
|
|
}
|
|
|
|
SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
|
|
{
|
|
int ufd;
|
|
struct timerfd_ctx *ctx;
|
|
enum alarmtimer_type type;
|
|
char task_comm_buf[TASK_COMM_LEN];
|
|
char file_name_buf[32];
|
|
int instance;
|
|
|
|
/* Check the TFD_* constants for consistency. */
|
|
BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC);
|
|
BUILD_BUG_ON(TFD_NONBLOCK != O_NONBLOCK);
|
|
|
|
if ((flags & ~TFD_CREATE_FLAGS) ||
|
|
(clockid != CLOCK_MONOTONIC &&
|
|
clockid != CLOCK_REALTIME &&
|
|
clockid != CLOCK_REALTIME_ALARM &&
|
|
clockid != CLOCK_BOOTTIME &&
|
|
clockid != CLOCK_BOOTTIME_ALARM &&
|
|
clockid != CLOCK_POWEROFF_ALARM))
|
|
return -EINVAL;
|
|
|
|
if (!capable(CAP_WAKE_ALARM) &&
|
|
(clockid == CLOCK_REALTIME_ALARM ||
|
|
clockid == CLOCK_BOOTTIME_ALARM))
|
|
return -EPERM;
|
|
|
|
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
|
if (!ctx)
|
|
return -ENOMEM;
|
|
|
|
init_waitqueue_head(&ctx->wqh);
|
|
spin_lock_init(&ctx->cancel_lock);
|
|
ctx->clockid = clockid;
|
|
|
|
if (isalarm(ctx)) {
|
|
type = clock2alarm(ctx->clockid);
|
|
alarm_init(&ctx->t.alarm, type, timerfd_alarmproc);
|
|
} else {
|
|
hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS);
|
|
}
|
|
|
|
ctx->moffs = ktime_mono_to_real((ktime_t){ .tv64 = 0 });
|
|
|
|
instance = atomic_inc_return(&instance_count);
|
|
get_task_comm(task_comm_buf, current);
|
|
snprintf(file_name_buf, sizeof(file_name_buf), "[timerfd%d_%.*s]",
|
|
instance, (int)sizeof(task_comm_buf), task_comm_buf);
|
|
|
|
ufd = anon_inode_getfd(file_name_buf, &timerfd_fops, ctx,
|
|
O_RDWR | (flags & TFD_SHARED_FCNTL_FLAGS));
|
|
if (ufd < 0)
|
|
kfree(ctx);
|
|
|
|
return ufd;
|
|
}
|
|
|
|
static int do_timerfd_settime(int ufd, int flags,
|
|
const struct itimerspec *new,
|
|
struct itimerspec *old)
|
|
{
|
|
struct fd f;
|
|
struct timerfd_ctx *ctx;
|
|
int ret;
|
|
|
|
if ((flags & ~TFD_SETTIME_FLAGS) ||
|
|
!timespec_valid(&new->it_value) ||
|
|
!timespec_valid(&new->it_interval))
|
|
return -EINVAL;
|
|
|
|
ret = timerfd_fget(ufd, &f);
|
|
if (ret)
|
|
return ret;
|
|
ctx = f.file->private_data;
|
|
|
|
if (!capable(CAP_WAKE_ALARM) && isalarm(ctx)) {
|
|
fdput(f);
|
|
return -EPERM;
|
|
}
|
|
|
|
timerfd_setup_cancel(ctx, flags);
|
|
|
|
/*
|
|
* We need to stop the existing timer before reprogramming
|
|
* it to the new values.
|
|
*/
|
|
for (;;) {
|
|
spin_lock_irq(&ctx->wqh.lock);
|
|
|
|
if (isalarm(ctx)) {
|
|
if (alarm_try_to_cancel(&ctx->t.alarm) >= 0)
|
|
break;
|
|
} else {
|
|
if (hrtimer_try_to_cancel(&ctx->t.tmr) >= 0)
|
|
break;
|
|
}
|
|
spin_unlock_irq(&ctx->wqh.lock);
|
|
cpu_relax();
|
|
}
|
|
|
|
/*
|
|
* If the timer is expired and it's periodic, we need to advance it
|
|
* because the caller may want to know the previous expiration time.
|
|
* We do not update "ticks" and "expired" since the timer will be
|
|
* re-programmed again in the following timerfd_setup() call.
|
|
*/
|
|
if (ctx->expired && ctx->tintv.tv64) {
|
|
if (isalarm(ctx))
|
|
alarm_forward_now(&ctx->t.alarm, ctx->tintv);
|
|
else
|
|
hrtimer_forward_now(&ctx->t.tmr, ctx->tintv);
|
|
}
|
|
|
|
old->it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
|
|
old->it_interval = ktime_to_timespec(ctx->tintv);
|
|
|
|
/*
|
|
* Re-program the timer to the new value ...
|
|
*/
|
|
ret = timerfd_setup(ctx, flags, new);
|
|
|
|
spin_unlock_irq(&ctx->wqh.lock);
|
|
|
|
if (ctx->clockid == CLOCK_POWEROFF_ALARM)
|
|
set_power_on_alarm();
|
|
|
|
fdput(f);
|
|
return ret;
|
|
}
|
|
|
|
static int do_timerfd_gettime(int ufd, struct itimerspec *t)
|
|
{
|
|
struct fd f;
|
|
struct timerfd_ctx *ctx;
|
|
int ret = timerfd_fget(ufd, &f);
|
|
if (ret)
|
|
return ret;
|
|
ctx = f.file->private_data;
|
|
|
|
spin_lock_irq(&ctx->wqh.lock);
|
|
if (ctx->expired && ctx->tintv.tv64) {
|
|
ctx->expired = 0;
|
|
|
|
if (isalarm(ctx)) {
|
|
ctx->ticks +=
|
|
alarm_forward_now(
|
|
&ctx->t.alarm, ctx->tintv) - 1;
|
|
alarm_restart(&ctx->t.alarm);
|
|
} else {
|
|
ctx->ticks +=
|
|
hrtimer_forward_now(&ctx->t.tmr, ctx->tintv)
|
|
- 1;
|
|
hrtimer_restart(&ctx->t.tmr);
|
|
}
|
|
}
|
|
t->it_value = ktime_to_timespec(timerfd_get_remaining(ctx));
|
|
t->it_interval = ktime_to_timespec(ctx->tintv);
|
|
spin_unlock_irq(&ctx->wqh.lock);
|
|
fdput(f);
|
|
return 0;
|
|
}
|
|
|
|
SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
|
|
const struct itimerspec __user *, utmr,
|
|
struct itimerspec __user *, otmr)
|
|
{
|
|
struct itimerspec new, old;
|
|
int ret;
|
|
|
|
if (copy_from_user(&new, utmr, sizeof(new)))
|
|
return -EFAULT;
|
|
ret = do_timerfd_settime(ufd, flags, &new, &old);
|
|
if (ret)
|
|
return ret;
|
|
if (otmr && copy_to_user(otmr, &old, sizeof(old)))
|
|
return -EFAULT;
|
|
|
|
return ret;
|
|
}
|
|
|
|
SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr)
|
|
{
|
|
struct itimerspec kotmr;
|
|
int ret = do_timerfd_gettime(ufd, &kotmr);
|
|
if (ret)
|
|
return ret;
|
|
return copy_to_user(otmr, &kotmr, sizeof(kotmr)) ? -EFAULT: 0;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
COMPAT_SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
|
|
const struct compat_itimerspec __user *, utmr,
|
|
struct compat_itimerspec __user *, otmr)
|
|
{
|
|
struct itimerspec new, old;
|
|
int ret;
|
|
|
|
if (get_compat_itimerspec(&new, utmr))
|
|
return -EFAULT;
|
|
ret = do_timerfd_settime(ufd, flags, &new, &old);
|
|
if (ret)
|
|
return ret;
|
|
if (otmr && put_compat_itimerspec(otmr, &old))
|
|
return -EFAULT;
|
|
return ret;
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE2(timerfd_gettime, int, ufd,
|
|
struct compat_itimerspec __user *, otmr)
|
|
{
|
|
struct itimerspec kotmr;
|
|
int ret = do_timerfd_gettime(ufd, &kotmr);
|
|
if (ret)
|
|
return ret;
|
|
return put_compat_itimerspec(otmr, &kotmr) ? -EFAULT: 0;
|
|
}
|
|
#endif
|