1
0
Files
kernel-49/kernel/time/posix-clock.c
Greg Kroah-Hartman b32a5a8287 Merge 4.9.224 into android-4.9-q
Changes in 4.9.224
	USB: serial: qcserial: Add DW5816e support
	dp83640: reverse arguments to list_add_tail
	fq_codel: fix TCA_FQ_CODEL_DROP_BATCH_SIZE sanity checks
	net: macsec: preserve ingress frame ordering
	net/mlx4_core: Fix use of ENOSPC around mlx4_counter_alloc()
	net: usb: qmi_wwan: add support for DW5816e
	sch_choke: avoid potential panic in choke_reset()
	sch_sfq: validate silly quantum values
	bnxt_en: Fix VLAN acceleration handling in bnxt_fix_features().
	net/mlx5: Fix forced completion access non initialized command entry
	net/mlx5: Fix command entry leak in Internal Error State
	bnxt_en: Improve AER slot reset.
	Revert "ACPI / video: Add force_native quirk for HP Pavilion dv6"
	binfmt_elf: move brk out of mmap when doing direct loader exec
	USB: uas: add quirk for LaCie 2Big Quadra
	USB: serial: garmin_gps: add sanity checking for data length
	tracing: Add a vmalloc_sync_mappings() for safe measure
	mm/page_alloc: fix watchdog soft lockups during set_zone_contiguous()
	batman-adv: fix batadv_nc_random_weight_tq
	batman-adv: Fix refcnt leak in batadv_show_throughput_override
	batman-adv: Fix refcnt leak in batadv_store_throughput_override
	batman-adv: Fix refcnt leak in batadv_v_ogm_process
	objtool: Fix stack offset tracking for indirect CFAs
	scripts/decodecode: fix trapping instruction formatting
	binfmt_elf: Do not move brk for INTERP-less ET_EXEC
	ext4: add cond_resched() to ext4_protect_reserved_inode
	net: ipv6: add net argument to ip6_dst_lookup_flow
	net: ipv6_stub: use ip6_dst_lookup_flow instead of ip6_dst_lookup
	blktrace: Fix potential deadlock between delete & sysfs ops
	blktrace: fix unlocked access to init/start-stop/teardown
	blktrace: fix trace mutex deadlock
	blktrace: Protect q->blk_trace with RCU
	blktrace: fix dereference after null check
	ptp: do not explicitly set drvdata in ptp_clock_register()
	ptp: use is_visible method to hide unused attributes
	ptp: create "pins" together with the rest of attributes
	chardev: add helper function to register char devs with a struct device
	ptp: Fix pass zero to ERR_PTR() in ptp_clock_register
	ptp: fix the race between the release of ptp_clock and cdev
	ptp: free ptp device pin descriptors properly
	shmem: fix possible deadlocks on shmlock_user_lock
	net/sonic: Fix a resource leak in an error handling path in 'jazz_sonic_probe()'
	net: moxa: Fix a potential double 'free_irq()'
	drop_monitor: work around gcc-10 stringop-overflow warning
	scsi: sg: add sg_remove_request in sg_write
	spi: spi-dw: Add lock protect dw_spi rx/tx to prevent concurrent calls
	cifs: Check for timeout on Negotiate stage
	cifs: Fix a race condition with cifs_echo_request
	dmaengine: pch_dma.c: Avoid data race between probe and irq handler
	dmaengine: mmp_tdma: Reset channel error on release
	ALSA: hda/hdmi: fix race in monitor detection during probe
	drm/qxl: lost qxl_bo_kunmap_atomic_page in qxl_image_init_helper()
	ipc/util.c: sysvipc_find_ipc() incorrectly updates position index
	pinctrl: cherryview: Add missing spinlock usage in chv_gpio_irq_handler
	i40iw: Fix error handling in i40iw_manage_arp_cache()
	netfilter: conntrack: avoid gcc-10 zero-length-bounds warning
	IB/mlx4: Test return value of calls to ib_get_cached_pkey
	pnp: Use list_for_each_entry() instead of open coding
	gcc-10 warnings: fix low-hanging fruit
	kbuild: compute false-positive -Wmaybe-uninitialized cases in Kconfig
	Stop the ad-hoc games with -Wno-maybe-initialized
	net: phy: micrel: Use strlcpy() for ethtool::get_strings
	gcc-10: avoid shadowing standard library 'free()' in crypto
	gcc-10: disable 'zero-length-bounds' warning for now
	gcc-10: disable 'array-bounds' warning for now
	gcc-10: disable 'stringop-overflow' warning for now
	gcc-10: disable 'restrict' warning for now
	net: fix a potential recursive NETDEV_FEAT_CHANGE
	netlabel: cope with NULL catmap
	Revert "ipv6: add mtu lock check in __ip6_rt_update_pmtu"
	net: ipv4: really enforce backoff for redirects
	netprio_cgroup: Fix unlimited memory leak of v2 cgroups
	ALSA: hda/realtek - Limit int mic boost for Thinkpad T530
	ALSA: rawmidi: Initialize allocated buffers
	ALSA: rawmidi: Fix racy buffer resize under concurrent accesses
	ALSA: usb-audio: Add control message quirk delay for Kingston HyperX headset
	USB: gadget: fix illegal array access in binding with UDC
	usb: xhci: Fix NULL pointer dereference when enqueuing trbs from urb sg list
	ARM: dts: imx27-phytec-phycard-s-rdk: Fix the I2C1 pinctrl entries
	x86: Fix early boot crash on gcc-10, third try
	exec: Move would_dump into flush_old_exec
	usb: gadget: net2272: Fix a memory leak in an error handling path in 'net2272_plat_probe()'
	usb: gadget: audio: Fix a missing error return value in audio_bind()
	usb: gadget: legacy: fix error return code in gncm_bind()
	usb: gadget: legacy: fix error return code in cdc_bind()
	Revert "ALSA: hda/realtek: Fix pop noise on ALC225"
	ARM: dts: r8a73a4: Add missing CMT1 interrupts
	ARM: dts: r8a7740: Add missing extal2 to CPG node
	KVM: x86: Fix off-by-one error in kvm_vcpu_ioctl_x86_setup_mce
	Makefile: disallow data races on gcc-10 as well
	Linux 4.9.224

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: Ib9fdff8dd9e9c92a2e750251b98eb0e4c9496fba
2020-05-25 14:01:29 +03:00

456 lines
8.7 KiB
C

/*
* posix-clock.c - support for dynamic clock devices
*
* Copyright (C) 2010 OMICRON electronics GmbH
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/device.h>
#include <linux/export.h>
#include <linux/file.h>
#include <linux/posix-clock.h>
#include <linux/slab.h>
#include <linux/syscalls.h>
#include <linux/uaccess.h>
/*
* Returns NULL if the posix_clock instance attached to 'fp' is old and stale.
*/
static struct posix_clock *get_posix_clock(struct file *fp)
{
struct posix_clock *clk = fp->private_data;
down_read(&clk->rwsem);
if (!clk->zombie)
return clk;
up_read(&clk->rwsem);
return NULL;
}
static void put_posix_clock(struct posix_clock *clk)
{
up_read(&clk->rwsem);
}
static ssize_t posix_clock_read(struct file *fp, char __user *buf,
size_t count, loff_t *ppos)
{
struct posix_clock *clk = get_posix_clock(fp);
int err = -EINVAL;
if (!clk)
return -ENODEV;
if (clk->ops.read)
err = clk->ops.read(clk, fp->f_flags, buf, count);
put_posix_clock(clk);
return err;
}
static unsigned int posix_clock_poll(struct file *fp, poll_table *wait)
{
struct posix_clock *clk = get_posix_clock(fp);
unsigned int result = 0;
if (!clk)
return POLLERR;
if (clk->ops.poll)
result = clk->ops.poll(clk, fp, wait);
put_posix_clock(clk);
return result;
}
static int posix_clock_fasync(int fd, struct file *fp, int on)
{
struct posix_clock *clk = get_posix_clock(fp);
int err = 0;
if (!clk)
return -ENODEV;
if (clk->ops.fasync)
err = clk->ops.fasync(clk, fd, fp, on);
put_posix_clock(clk);
return err;
}
static int posix_clock_mmap(struct file *fp, struct vm_area_struct *vma)
{
struct posix_clock *clk = get_posix_clock(fp);
int err = -ENODEV;
if (!clk)
return -ENODEV;
if (clk->ops.mmap)
err = clk->ops.mmap(clk, vma);
put_posix_clock(clk);
return err;
}
static long posix_clock_ioctl(struct file *fp,
unsigned int cmd, unsigned long arg)
{
struct posix_clock *clk = get_posix_clock(fp);
int err = -ENOTTY;
if (!clk)
return -ENODEV;
if (clk->ops.ioctl)
err = clk->ops.ioctl(clk, cmd, arg);
put_posix_clock(clk);
return err;
}
#ifdef CONFIG_COMPAT
static long posix_clock_compat_ioctl(struct file *fp,
unsigned int cmd, unsigned long arg)
{
struct posix_clock *clk = get_posix_clock(fp);
int err = -ENOTTY;
if (!clk)
return -ENODEV;
if (clk->ops.ioctl)
err = clk->ops.ioctl(clk, cmd, arg);
put_posix_clock(clk);
return err;
}
#endif
static int posix_clock_open(struct inode *inode, struct file *fp)
{
int err;
struct posix_clock *clk =
container_of(inode->i_cdev, struct posix_clock, cdev);
down_read(&clk->rwsem);
if (clk->zombie) {
err = -ENODEV;
goto out;
}
if (clk->ops.open)
err = clk->ops.open(clk, fp->f_mode);
else
err = 0;
if (!err) {
get_device(clk->dev);
fp->private_data = clk;
}
out:
up_read(&clk->rwsem);
return err;
}
static int posix_clock_release(struct inode *inode, struct file *fp)
{
struct posix_clock *clk = fp->private_data;
int err = 0;
if (clk->ops.release)
err = clk->ops.release(clk);
put_device(clk->dev);
fp->private_data = NULL;
return err;
}
static const struct file_operations posix_clock_file_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = posix_clock_read,
.poll = posix_clock_poll,
.unlocked_ioctl = posix_clock_ioctl,
.open = posix_clock_open,
.release = posix_clock_release,
.fasync = posix_clock_fasync,
.mmap = posix_clock_mmap,
#ifdef CONFIG_COMPAT
.compat_ioctl = posix_clock_compat_ioctl,
#endif
};
int posix_clock_register(struct posix_clock *clk, struct device *dev)
{
int err;
init_rwsem(&clk->rwsem);
cdev_init(&clk->cdev, &posix_clock_file_operations);
err = cdev_device_add(&clk->cdev, dev);
if (err) {
pr_err("%s unable to add device %d:%d\n",
dev_name(dev), MAJOR(dev->devt), MINOR(dev->devt));
return err;
}
clk->cdev.owner = clk->ops.owner;
clk->dev = dev;
return 0;
}
EXPORT_SYMBOL_GPL(posix_clock_register);
void posix_clock_unregister(struct posix_clock *clk)
{
cdev_device_del(&clk->cdev, clk->dev);
down_write(&clk->rwsem);
clk->zombie = true;
up_write(&clk->rwsem);
put_device(clk->dev);
}
EXPORT_SYMBOL_GPL(posix_clock_unregister);
struct posix_clock_desc {
struct file *fp;
struct posix_clock *clk;
};
static int get_clock_desc(const clockid_t id, struct posix_clock_desc *cd)
{
struct file *fp = fget(CLOCKID_TO_FD(id));
int err = -EINVAL;
if (!fp)
return err;
if (fp->f_op->open != posix_clock_open || !fp->private_data)
goto out;
cd->fp = fp;
cd->clk = get_posix_clock(fp);
err = cd->clk ? 0 : -ENODEV;
out:
if (err)
fput(fp);
return err;
}
static void put_clock_desc(struct posix_clock_desc *cd)
{
put_posix_clock(cd->clk);
fput(cd->fp);
}
static int pc_clock_adjtime(clockid_t id, struct timex *tx)
{
struct posix_clock_desc cd;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
err = -EACCES;
goto out;
}
if (cd.clk->ops.clock_adjtime)
err = cd.clk->ops.clock_adjtime(cd.clk, tx);
else
err = -EOPNOTSUPP;
out:
put_clock_desc(&cd);
return err;
}
static int pc_clock_gettime(clockid_t id, struct timespec *ts)
{
struct posix_clock_desc cd;
struct timespec64 ts64;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if (cd.clk->ops.clock_gettime) {
err = cd.clk->ops.clock_gettime(cd.clk, &ts64);
*ts = timespec64_to_timespec(ts64);
}
else
err = -EOPNOTSUPP;
put_clock_desc(&cd);
return err;
}
static int pc_clock_getres(clockid_t id, struct timespec *ts)
{
struct posix_clock_desc cd;
struct timespec64 ts64;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if (cd.clk->ops.clock_getres) {
err = cd.clk->ops.clock_getres(cd.clk, &ts64);
*ts = timespec64_to_timespec(ts64);
}
else
err = -EOPNOTSUPP;
put_clock_desc(&cd);
return err;
}
static int pc_clock_settime(clockid_t id, const struct timespec *ts)
{
struct timespec64 ts64 = timespec_to_timespec64(*ts);
struct posix_clock_desc cd;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if ((cd.fp->f_mode & FMODE_WRITE) == 0) {
err = -EACCES;
goto out;
}
if (cd.clk->ops.clock_settime)
err = cd.clk->ops.clock_settime(cd.clk, &ts64);
else
err = -EOPNOTSUPP;
out:
put_clock_desc(&cd);
return err;
}
static int pc_timer_create(struct k_itimer *kit)
{
clockid_t id = kit->it_clock;
struct posix_clock_desc cd;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if (cd.clk->ops.timer_create)
err = cd.clk->ops.timer_create(cd.clk, kit);
else
err = -EOPNOTSUPP;
put_clock_desc(&cd);
return err;
}
static int pc_timer_delete(struct k_itimer *kit)
{
clockid_t id = kit->it_clock;
struct posix_clock_desc cd;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if (cd.clk->ops.timer_delete)
err = cd.clk->ops.timer_delete(cd.clk, kit);
else
err = -EOPNOTSUPP;
put_clock_desc(&cd);
return err;
}
static void pc_timer_gettime(struct k_itimer *kit, struct itimerspec *ts)
{
clockid_t id = kit->it_clock;
struct posix_clock_desc cd;
struct itimerspec64 ts64;
if (get_clock_desc(id, &cd))
return;
if (cd.clk->ops.timer_gettime) {
cd.clk->ops.timer_gettime(cd.clk, kit, &ts64);
*ts = itimerspec64_to_itimerspec(&ts64);
}
put_clock_desc(&cd);
}
static int pc_timer_settime(struct k_itimer *kit, int flags,
struct itimerspec *ts, struct itimerspec *old)
{
struct itimerspec64 ts64 = itimerspec_to_itimerspec64(ts);
clockid_t id = kit->it_clock;
struct posix_clock_desc cd;
struct itimerspec64 old64;
int err;
err = get_clock_desc(id, &cd);
if (err)
return err;
if (cd.clk->ops.timer_settime) {
err = cd.clk->ops.timer_settime(cd.clk, kit, flags, &ts64, &old64);
if (old)
*old = itimerspec64_to_itimerspec(&old64);
}
else
err = -EOPNOTSUPP;
put_clock_desc(&cd);
return err;
}
struct k_clock clock_posix_dynamic = {
.clock_getres = pc_clock_getres,
.clock_set = pc_clock_settime,
.clock_get = pc_clock_gettime,
.clock_adj = pc_clock_adjtime,
.timer_create = pc_timer_create,
.timer_set = pc_timer_settime,
.timer_del = pc_timer_delete,
.timer_get = pc_timer_gettime,
};