Merge 2e63f6ce7e ("Merge branch 'uaccess.comedi' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs") into android-mainline

Baby steps on the way to 5.8-rc1.

A clean merge state for once...

Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I35a7b84b14b046c3cc850f3cd0b4e5aad83706f1
This commit is contained in:
Greg Kroah-Hartman
2020-06-22 14:40:56 +02:00
14 changed files with 544 additions and 770 deletions

View File

@@ -871,7 +871,7 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
return 0;
/* Try lock this page */
if (pipe_buf_steal(pipe, buf) == 0) {
if (pipe_buf_try_steal(pipe, buf)) {
/* Get reference and unlock page for moving */
get_page(buf->page);
unlock_page(buf->page);

View File

@@ -4,7 +4,6 @@ ccflags-$(CONFIG_COMEDI_DEBUG) := -DDEBUG
comedi-y := comedi_fops.o range.o drivers.o \
comedi_buf.o
comedi-$(CONFIG_PROC_FS) += proc.o
comedi-$(CONFIG_COMPAT) += comedi_compat32.o
obj-$(CONFIG_COMEDI_PCI_DRIVERS) += comedi_pci.o
obj-$(CONFIG_COMEDI_PCMCIA_DRIVERS) += comedi_pcmcia.o

View File

@@ -1,455 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* comedi/comedi_compat32.c
* 32-bit ioctl compatibility for 64-bit comedi kernel module.
*
* Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
* Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
*
* COMEDI - Linux Control and Measurement Device Interface
* Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
*/
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/fs.h>
#include "comedi.h"
#include "comedi_compat32.h"
#define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct)
#define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct)
/*
* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
* It's too late to change it now, but it only affects the command number.
*/
#define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct)
/*
* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
* It's too late to change it now, but it only affects the command number.
*/
#define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct)
#define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct)
#define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct)
struct comedi32_chaninfo_struct {
unsigned int subdev;
compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */
compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */
unsigned int unused[4];
};
struct comedi32_rangeinfo_struct {
unsigned int range_type;
compat_uptr_t range_ptr; /* 32-bit 'void *' */
};
struct comedi32_cmd_struct {
unsigned int subdev;
unsigned int flags;
unsigned int start_src;
unsigned int start_arg;
unsigned int scan_begin_src;
unsigned int scan_begin_arg;
unsigned int convert_src;
unsigned int convert_arg;
unsigned int scan_end_src;
unsigned int scan_end_arg;
unsigned int stop_src;
unsigned int stop_arg;
compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
unsigned int chanlist_len;
compat_uptr_t data; /* 32-bit 'short *' */
unsigned int data_len;
};
struct comedi32_insn_struct {
unsigned int insn;
unsigned int n;
compat_uptr_t data; /* 32-bit 'unsigned int *' */
unsigned int subdev;
unsigned int chanspec;
unsigned int unused[3];
};
struct comedi32_insnlist_struct {
unsigned int n_insns;
compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */
};
/* Handle translated ioctl. */
static int translated_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
if (file->f_op->unlocked_ioctl)
return file->f_op->unlocked_ioctl(file, cmd, arg);
return -ENOTTY;
}
/* Handle 32-bit COMEDI_CHANINFO ioctl. */
static int compat_chaninfo(struct file *file, unsigned long arg)
{
struct comedi_chaninfo __user *chaninfo;
struct comedi32_chaninfo_struct __user *chaninfo32;
int err;
union {
unsigned int uint;
compat_uptr_t uptr;
} temp;
chaninfo32 = compat_ptr(arg);
chaninfo = compat_alloc_user_space(sizeof(*chaninfo));
/* Copy chaninfo structure. Ignore unused members. */
if (!access_ok(chaninfo32, sizeof(*chaninfo32)) ||
!access_ok(chaninfo, sizeof(*chaninfo)))
return -EFAULT;
err = 0;
err |= __get_user(temp.uint, &chaninfo32->subdev);
err |= __put_user(temp.uint, &chaninfo->subdev);
err |= __get_user(temp.uptr, &chaninfo32->maxdata_list);
err |= __put_user(compat_ptr(temp.uptr), &chaninfo->maxdata_list);
err |= __get_user(temp.uptr, &chaninfo32->flaglist);
err |= __put_user(compat_ptr(temp.uptr), &chaninfo->flaglist);
err |= __get_user(temp.uptr, &chaninfo32->rangelist);
err |= __put_user(compat_ptr(temp.uptr), &chaninfo->rangelist);
if (err)
return -EFAULT;
return translated_ioctl(file, COMEDI_CHANINFO, (unsigned long)chaninfo);
}
/* Handle 32-bit COMEDI_RANGEINFO ioctl. */
static int compat_rangeinfo(struct file *file, unsigned long arg)
{
struct comedi_rangeinfo __user *rangeinfo;
struct comedi32_rangeinfo_struct __user *rangeinfo32;
int err;
union {
unsigned int uint;
compat_uptr_t uptr;
} temp;
rangeinfo32 = compat_ptr(arg);
rangeinfo = compat_alloc_user_space(sizeof(*rangeinfo));
/* Copy rangeinfo structure. */
if (!access_ok(rangeinfo32, sizeof(*rangeinfo32)) ||
!access_ok(rangeinfo, sizeof(*rangeinfo)))
return -EFAULT;
err = 0;
err |= __get_user(temp.uint, &rangeinfo32->range_type);
err |= __put_user(temp.uint, &rangeinfo->range_type);
err |= __get_user(temp.uptr, &rangeinfo32->range_ptr);
err |= __put_user(compat_ptr(temp.uptr), &rangeinfo->range_ptr);
if (err)
return -EFAULT;
return translated_ioctl(file, COMEDI_RANGEINFO,
(unsigned long)rangeinfo);
}
/* Copy 32-bit cmd structure to native cmd structure. */
static int get_compat_cmd(struct comedi_cmd __user *cmd,
struct comedi32_cmd_struct __user *cmd32)
{
int err;
union {
unsigned int uint;
compat_uptr_t uptr;
} temp;
/* Copy cmd structure. */
if (!access_ok(cmd32, sizeof(*cmd32)) ||
!access_ok(cmd, sizeof(*cmd)))
return -EFAULT;
err = 0;
err |= __get_user(temp.uint, &cmd32->subdev);
err |= __put_user(temp.uint, &cmd->subdev);
err |= __get_user(temp.uint, &cmd32->flags);
err |= __put_user(temp.uint, &cmd->flags);
err |= __get_user(temp.uint, &cmd32->start_src);
err |= __put_user(temp.uint, &cmd->start_src);
err |= __get_user(temp.uint, &cmd32->start_arg);
err |= __put_user(temp.uint, &cmd->start_arg);
err |= __get_user(temp.uint, &cmd32->scan_begin_src);
err |= __put_user(temp.uint, &cmd->scan_begin_src);
err |= __get_user(temp.uint, &cmd32->scan_begin_arg);
err |= __put_user(temp.uint, &cmd->scan_begin_arg);
err |= __get_user(temp.uint, &cmd32->convert_src);
err |= __put_user(temp.uint, &cmd->convert_src);
err |= __get_user(temp.uint, &cmd32->convert_arg);
err |= __put_user(temp.uint, &cmd->convert_arg);
err |= __get_user(temp.uint, &cmd32->scan_end_src);
err |= __put_user(temp.uint, &cmd->scan_end_src);
err |= __get_user(temp.uint, &cmd32->scan_end_arg);
err |= __put_user(temp.uint, &cmd->scan_end_arg);
err |= __get_user(temp.uint, &cmd32->stop_src);
err |= __put_user(temp.uint, &cmd->stop_src);
err |= __get_user(temp.uint, &cmd32->stop_arg);
err |= __put_user(temp.uint, &cmd->stop_arg);
err |= __get_user(temp.uptr, &cmd32->chanlist);
err |= __put_user((unsigned int __force *)compat_ptr(temp.uptr),
&cmd->chanlist);
err |= __get_user(temp.uint, &cmd32->chanlist_len);
err |= __put_user(temp.uint, &cmd->chanlist_len);
err |= __get_user(temp.uptr, &cmd32->data);
err |= __put_user(compat_ptr(temp.uptr), &cmd->data);
err |= __get_user(temp.uint, &cmd32->data_len);
err |= __put_user(temp.uint, &cmd->data_len);
return err ? -EFAULT : 0;
}
/* Copy native cmd structure to 32-bit cmd structure. */
static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
struct comedi_cmd __user *cmd)
{
int err;
unsigned int temp;
/*
* Copy back most of cmd structure.
*
* Assume the pointer values are already valid.
* (Could use ptr_to_compat() to set them.)
*/
if (!access_ok(cmd, sizeof(*cmd)) ||
!access_ok(cmd32, sizeof(*cmd32)))
return -EFAULT;
err = 0;
err |= __get_user(temp, &cmd->subdev);
err |= __put_user(temp, &cmd32->subdev);
err |= __get_user(temp, &cmd->flags);
err |= __put_user(temp, &cmd32->flags);
err |= __get_user(temp, &cmd->start_src);
err |= __put_user(temp, &cmd32->start_src);
err |= __get_user(temp, &cmd->start_arg);
err |= __put_user(temp, &cmd32->start_arg);
err |= __get_user(temp, &cmd->scan_begin_src);
err |= __put_user(temp, &cmd32->scan_begin_src);
err |= __get_user(temp, &cmd->scan_begin_arg);
err |= __put_user(temp, &cmd32->scan_begin_arg);
err |= __get_user(temp, &cmd->convert_src);
err |= __put_user(temp, &cmd32->convert_src);
err |= __get_user(temp, &cmd->convert_arg);
err |= __put_user(temp, &cmd32->convert_arg);
err |= __get_user(temp, &cmd->scan_end_src);
err |= __put_user(temp, &cmd32->scan_end_src);
err |= __get_user(temp, &cmd->scan_end_arg);
err |= __put_user(temp, &cmd32->scan_end_arg);
err |= __get_user(temp, &cmd->stop_src);
err |= __put_user(temp, &cmd32->stop_src);
err |= __get_user(temp, &cmd->stop_arg);
err |= __put_user(temp, &cmd32->stop_arg);
/* Assume chanlist pointer is unchanged. */
err |= __get_user(temp, &cmd->chanlist_len);
err |= __put_user(temp, &cmd32->chanlist_len);
/* Assume data pointer is unchanged. */
err |= __get_user(temp, &cmd->data_len);
err |= __put_user(temp, &cmd32->data_len);
return err ? -EFAULT : 0;
}
/* Handle 32-bit COMEDI_CMD ioctl. */
static int compat_cmd(struct file *file, unsigned long arg)
{
struct comedi_cmd __user *cmd;
struct comedi32_cmd_struct __user *cmd32;
int rc, err;
cmd32 = compat_ptr(arg);
cmd = compat_alloc_user_space(sizeof(*cmd));
rc = get_compat_cmd(cmd, cmd32);
if (rc)
return rc;
rc = translated_ioctl(file, COMEDI_CMD, (unsigned long)cmd);
if (rc == -EAGAIN) {
/* Special case: copy cmd back to user. */
err = put_compat_cmd(cmd32, cmd);
if (err)
rc = err;
}
return rc;
}
/* Handle 32-bit COMEDI_CMDTEST ioctl. */
static int compat_cmdtest(struct file *file, unsigned long arg)
{
struct comedi_cmd __user *cmd;
struct comedi32_cmd_struct __user *cmd32;
int rc, err;
cmd32 = compat_ptr(arg);
cmd = compat_alloc_user_space(sizeof(*cmd));
rc = get_compat_cmd(cmd, cmd32);
if (rc)
return rc;
rc = translated_ioctl(file, COMEDI_CMDTEST, (unsigned long)cmd);
if (rc < 0)
return rc;
err = put_compat_cmd(cmd32, cmd);
if (err)
rc = err;
return rc;
}
/* Copy 32-bit insn structure to native insn structure. */
static int get_compat_insn(struct comedi_insn __user *insn,
struct comedi32_insn_struct __user *insn32)
{
int err;
union {
unsigned int uint;
compat_uptr_t uptr;
} temp;
/* Copy insn structure. Ignore the unused members. */
err = 0;
if (!access_ok(insn32, sizeof(*insn32)) ||
!access_ok(insn, sizeof(*insn)))
return -EFAULT;
err |= __get_user(temp.uint, &insn32->insn);
err |= __put_user(temp.uint, &insn->insn);
err |= __get_user(temp.uint, &insn32->n);
err |= __put_user(temp.uint, &insn->n);
err |= __get_user(temp.uptr, &insn32->data);
err |= __put_user(compat_ptr(temp.uptr), &insn->data);
err |= __get_user(temp.uint, &insn32->subdev);
err |= __put_user(temp.uint, &insn->subdev);
err |= __get_user(temp.uint, &insn32->chanspec);
err |= __put_user(temp.uint, &insn->chanspec);
return err ? -EFAULT : 0;
}
/* Handle 32-bit COMEDI_INSNLIST ioctl. */
static int compat_insnlist(struct file *file, unsigned long arg)
{
struct combined_insnlist {
struct comedi_insnlist insnlist;
struct comedi_insn insn[1];
} __user *s;
struct comedi32_insnlist_struct __user *insnlist32;
struct comedi32_insn_struct __user *insn32;
compat_uptr_t uptr;
unsigned int n_insns, n;
int err, rc;
insnlist32 = compat_ptr(arg);
/* Get 32-bit insnlist structure. */
if (!access_ok(insnlist32, sizeof(*insnlist32)))
return -EFAULT;
err = 0;
err |= __get_user(n_insns, &insnlist32->n_insns);
err |= __get_user(uptr, &insnlist32->insns);
insn32 = compat_ptr(uptr);
if (err)
return -EFAULT;
/* Allocate user memory to copy insnlist and insns into. */
s = compat_alloc_user_space(offsetof(struct combined_insnlist,
insn[n_insns]));
/* Set native insnlist structure. */
if (!access_ok(&s->insnlist, sizeof(s->insnlist)))
return -EFAULT;
err |= __put_user(n_insns, &s->insnlist.n_insns);
err |= __put_user(&s->insn[0], &s->insnlist.insns);
if (err)
return -EFAULT;
/* Copy insn structures. */
for (n = 0; n < n_insns; n++) {
rc = get_compat_insn(&s->insn[n], &insn32[n]);
if (rc)
return rc;
}
return translated_ioctl(file, COMEDI_INSNLIST,
(unsigned long)&s->insnlist);
}
/* Handle 32-bit COMEDI_INSN ioctl. */
static int compat_insn(struct file *file, unsigned long arg)
{
struct comedi_insn __user *insn;
struct comedi32_insn_struct __user *insn32;
int rc;
insn32 = compat_ptr(arg);
insn = compat_alloc_user_space(sizeof(*insn));
rc = get_compat_insn(insn, insn32);
if (rc)
return rc;
return translated_ioctl(file, COMEDI_INSN, (unsigned long)insn);
}
/*
* compat_ioctl file operation.
*
* Returns -ENOIOCTLCMD for unrecognised ioctl codes.
*/
long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc;
switch (cmd) {
case COMEDI_DEVCONFIG:
case COMEDI_DEVINFO:
case COMEDI_SUBDINFO:
case COMEDI_BUFCONFIG:
case COMEDI_BUFINFO:
/* Just need to translate the pointer argument. */
arg = (unsigned long)compat_ptr(arg);
rc = translated_ioctl(file, cmd, arg);
break;
case COMEDI_LOCK:
case COMEDI_UNLOCK:
case COMEDI_CANCEL:
case COMEDI_POLL:
case COMEDI_SETRSUBD:
case COMEDI_SETWSUBD:
/* No translation needed. */
rc = translated_ioctl(file, cmd, arg);
break;
case COMEDI32_CHANINFO:
rc = compat_chaninfo(file, arg);
break;
case COMEDI32_RANGEINFO:
rc = compat_rangeinfo(file, arg);
break;
case COMEDI32_CMD:
rc = compat_cmd(file, arg);
break;
case COMEDI32_CMDTEST:
rc = compat_cmdtest(file, arg);
break;
case COMEDI32_INSNLIST:
rc = compat_insnlist(file, arg);
break;
case COMEDI32_INSN:
rc = compat_insn(file, arg);
break;
default:
rc = -ENOIOCTLCMD;
break;
}
return rc;
}

View File

@@ -1,28 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* comedi/comedi_compat32.h
* 32-bit ioctl compatibility for 64-bit comedi kernel module.
*
* Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
* Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
*
* COMEDI - Linux Control and Measurement Device Interface
* Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
*/
#ifndef _COMEDI_COMPAT32_H
#define _COMEDI_COMPAT32_H
#ifdef CONFIG_COMPAT
struct file;
long comedi_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg);
#else /* CONFIG_COMPAT */
#define comedi_compat_ioctl NULL
#endif /* CONFIG_COMPAT */
#endif /* _COMEDI_COMPAT32_H */

View File

@@ -4,13 +4,14 @@
* comedi kernel module
*
* COMEDI - Linux Control and Measurement Device Interface
* Copyright (C) 1997-2000 David A. Schleef <ds@schleef.org>
* Copyright (C) 1997-2007 David A. Schleef <ds@schleef.org>
* compat ioctls:
* Author: Ian Abbott, MEV Ltd. <abbotti@mev.co.uk>
* Copyright (C) 2007 MEV Ltd. <http://www.mev.co.uk/>
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "comedi_compat32.h"
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -27,6 +28,7 @@
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include "comedi_internal.h"
@@ -1047,31 +1049,28 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
* array of range table lengths to chaninfo->range_table_list if requested
*/
static int do_chaninfo_ioctl(struct comedi_device *dev,
struct comedi_chaninfo __user *arg)
struct comedi_chaninfo *it)
{
struct comedi_subdevice *s;
struct comedi_chaninfo it;
lockdep_assert_held(&dev->mutex);
if (copy_from_user(&it, arg, sizeof(it)))
return -EFAULT;
if (it.subdev >= dev->n_subdevices)
if (it->subdev >= dev->n_subdevices)
return -EINVAL;
s = &dev->subdevices[it.subdev];
s = &dev->subdevices[it->subdev];
if (it.maxdata_list) {
if (it->maxdata_list) {
if (s->maxdata || !s->maxdata_list)
return -EINVAL;
if (copy_to_user(it.maxdata_list, s->maxdata_list,
if (copy_to_user(it->maxdata_list, s->maxdata_list,
s->n_chan * sizeof(unsigned int)))
return -EFAULT;
}
if (it.flaglist)
if (it->flaglist)
return -EINVAL; /* flaglist not supported */
if (it.rangelist) {
if (it->rangelist) {
int i;
if (!s->range_table_list)
@@ -1079,9 +1078,9 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
for (i = 0; i < s->n_chan; i++) {
int x;
x = (dev->minor << 28) | (it.subdev << 24) | (i << 16) |
x = (dev->minor << 28) | (it->subdev << 24) | (i << 16) |
(s->range_table_list[i]->length);
if (put_user(x, it.rangelist + i))
if (put_user(x, it->rangelist + i))
return -EFAULT;
}
}
@@ -1521,34 +1520,19 @@ out:
#define MIN_SAMPLES 16
#define MAX_SAMPLES 65536
static int do_insnlist_ioctl(struct comedi_device *dev,
struct comedi_insnlist __user *arg, void *file)
struct comedi_insn *insns,
unsigned int n_insns,
void *file)
{
struct comedi_insnlist insnlist;
struct comedi_insn *insns = NULL;
unsigned int *data = NULL;
unsigned int max_n_data_required = MIN_SAMPLES;
int i = 0;
int ret = 0;
lockdep_assert_held(&dev->mutex);
if (copy_from_user(&insnlist, arg, sizeof(insnlist)))
return -EFAULT;
insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL);
if (!insns) {
ret = -ENOMEM;
goto error;
}
if (copy_from_user(insns, insnlist.insns,
sizeof(*insns) * insnlist.n_insns)) {
dev_dbg(dev->class_dev, "copy_from_user failed\n");
ret = -EFAULT;
goto error;
}
/* Determine maximum memory needed for all instructions. */
for (i = 0; i < insnlist.n_insns; ++i) {
for (i = 0; i < n_insns; ++i) {
if (insns[i].n > MAX_SAMPLES) {
dev_dbg(dev->class_dev,
"number of samples too large\n");
@@ -1566,7 +1550,7 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
goto error;
}
for (i = 0; i < insnlist.n_insns; ++i) {
for (i = 0; i < n_insns; ++i) {
if (insns[i].insn & INSN_MASK_WRITE) {
if (copy_from_user(data, insns[i].data,
insns[i].n * sizeof(unsigned int))) {
@@ -1593,7 +1577,6 @@ static int do_insnlist_ioctl(struct comedi_device *dev,
}
error:
kfree(insns);
kfree(data);
if (ret < 0)
@@ -1616,22 +1599,19 @@ error:
* data (for reads) to insn->data pointer
*/
static int do_insn_ioctl(struct comedi_device *dev,
struct comedi_insn __user *arg, void *file)
struct comedi_insn *insn, void *file)
{
struct comedi_insn insn;
unsigned int *data = NULL;
unsigned int n_data = MIN_SAMPLES;
int ret = 0;
lockdep_assert_held(&dev->mutex);
if (copy_from_user(&insn, arg, sizeof(insn)))
return -EFAULT;
n_data = max(n_data, insn.n);
n_data = max(n_data, insn->n);
/* This is where the behavior of insn and insnlist deviate. */
if (insn.n > MAX_SAMPLES) {
insn.n = MAX_SAMPLES;
if (insn->n > MAX_SAMPLES) {
insn->n = MAX_SAMPLES;
n_data = MAX_SAMPLES;
}
@@ -1641,26 +1621,26 @@ static int do_insn_ioctl(struct comedi_device *dev,
goto error;
}
if (insn.insn & INSN_MASK_WRITE) {
if (insn->insn & INSN_MASK_WRITE) {
if (copy_from_user(data,
insn.data,
insn.n * sizeof(unsigned int))) {
insn->data,
insn->n * sizeof(unsigned int))) {
ret = -EFAULT;
goto error;
}
}
ret = parse_insn(dev, &insn, data, file);
ret = parse_insn(dev, insn, data, file);
if (ret < 0)
goto error;
if (insn.insn & INSN_MASK_READ) {
if (copy_to_user(insn.data,
if (insn->insn & INSN_MASK_READ) {
if (copy_to_user(insn->data,
data,
insn.n * sizeof(unsigned int))) {
insn->n * sizeof(unsigned int))) {
ret = -EFAULT;
goto error;
}
}
ret = insn.n;
ret = insn->n;
error:
kfree(data);
@@ -1669,17 +1649,11 @@ error:
}
static int __comedi_get_user_cmd(struct comedi_device *dev,
struct comedi_cmd __user *arg,
struct comedi_cmd *cmd)
{
struct comedi_subdevice *s;
lockdep_assert_held(&dev->mutex);
if (copy_from_user(cmd, arg, sizeof(*cmd))) {
dev_dbg(dev->class_dev, "bad cmd address\n");
return -EFAULT;
}
if (cmd->subdev >= dev->n_subdevices) {
dev_dbg(dev->class_dev, "%d no such subdevice\n", cmd->subdev);
return -ENODEV;
@@ -1767,9 +1741,8 @@ static int __comedi_get_user_chanlist(struct comedi_device *dev,
* possibly modified comedi_cmd structure (when -EAGAIN returned)
*/
static int do_cmd_ioctl(struct comedi_device *dev,
struct comedi_cmd __user *arg, void *file)
struct comedi_cmd *cmd, bool *copy, void *file)
{
struct comedi_cmd cmd;
struct comedi_subdevice *s;
struct comedi_async *async;
unsigned int __user *user_chanlist;
@@ -1777,15 +1750,15 @@ static int do_cmd_ioctl(struct comedi_device *dev,
lockdep_assert_held(&dev->mutex);
/* get the user's cmd and do some simple validation */
ret = __comedi_get_user_cmd(dev, arg, &cmd);
/* do some simple cmd validation */
ret = __comedi_get_user_cmd(dev, cmd);
if (ret)
return ret;
/* save user's chanlist pointer so it can be restored later */
user_chanlist = (unsigned int __user *)cmd.chanlist;
user_chanlist = (unsigned int __user *)cmd->chanlist;
s = &dev->subdevices[cmd.subdev];
s = &dev->subdevices[cmd->subdev];
async = s->async;
/* are we locked? (ioctl lock) */
@@ -1801,13 +1774,13 @@ static int do_cmd_ioctl(struct comedi_device *dev,
}
/* make sure channel/gain list isn't too short */
if (cmd.chanlist_len < 1) {
if (cmd->chanlist_len < 1) {
dev_dbg(dev->class_dev, "channel/gain list too short %u < 1\n",
cmd.chanlist_len);
cmd->chanlist_len);
return -EINVAL;
}
async->cmd = cmd;
async->cmd = *cmd;
async->cmd.data = NULL;
/* load channel/gain list */
@@ -1819,15 +1792,11 @@ static int do_cmd_ioctl(struct comedi_device *dev,
if (async->cmd.flags & CMDF_BOGUS || ret) {
dev_dbg(dev->class_dev, "test returned %d\n", ret);
cmd = async->cmd;
*cmd = async->cmd;
/* restore chanlist pointer before copying back */
cmd.chanlist = (unsigned int __force *)user_chanlist;
cmd.data = NULL;
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
dev_dbg(dev->class_dev, "fault writing cmd\n");
ret = -EFAULT;
goto cleanup;
}
cmd->chanlist = (unsigned int __force *)user_chanlist;
cmd->data = NULL;
*copy = true;
ret = -EAGAIN;
goto cleanup;
}
@@ -1877,44 +1846,39 @@ cleanup:
* possibly modified comedi_cmd structure
*/
static int do_cmdtest_ioctl(struct comedi_device *dev,
struct comedi_cmd __user *arg, void *file)
struct comedi_cmd *cmd, bool *copy, void *file)
{
struct comedi_cmd cmd;
struct comedi_subdevice *s;
unsigned int __user *user_chanlist;
int ret;
lockdep_assert_held(&dev->mutex);
/* get the user's cmd and do some simple validation */
ret = __comedi_get_user_cmd(dev, arg, &cmd);
/* do some simple cmd validation */
ret = __comedi_get_user_cmd(dev, cmd);
if (ret)
return ret;
/* save user's chanlist pointer so it can be restored later */
user_chanlist = (unsigned int __user *)cmd.chanlist;
user_chanlist = (unsigned int __user *)cmd->chanlist;
s = &dev->subdevices[cmd.subdev];
s = &dev->subdevices[cmd->subdev];
/* user_chanlist can be NULL for COMEDI_CMDTEST ioctl */
if (user_chanlist) {
/* load channel/gain list */
ret = __comedi_get_user_chanlist(dev, s, user_chanlist, &cmd);
ret = __comedi_get_user_chanlist(dev, s, user_chanlist, cmd);
if (ret)
return ret;
}
ret = s->do_cmdtest(dev, s, &cmd);
ret = s->do_cmdtest(dev, s, cmd);
kfree(cmd.chanlist); /* free kernel copy of user chanlist */
kfree(cmd->chanlist); /* free kernel copy of user chanlist */
/* restore chanlist pointer before copying back */
cmd.chanlist = (unsigned int __force *)user_chanlist;
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
dev_dbg(dev->class_dev, "bad cmd address\n");
ret = -EFAULT;
}
cmd->chanlist = (unsigned int __force *)user_chanlist;
*copy = true;
return ret;
}
@@ -2203,12 +2167,22 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
(struct comedi_subdinfo __user *)arg,
file);
break;
case COMEDI_CHANINFO:
rc = do_chaninfo_ioctl(dev, (void __user *)arg);
case COMEDI_CHANINFO: {
struct comedi_chaninfo it;
if (copy_from_user(&it, (void __user *)arg, sizeof(it)))
rc = -EFAULT;
else
rc = do_chaninfo_ioctl(dev, &it);
break;
case COMEDI_RANGEINFO:
rc = do_rangeinfo_ioctl(dev, (void __user *)arg);
}
case COMEDI_RANGEINFO: {
struct comedi_rangeinfo it;
if (copy_from_user(&it, (void __user *)arg, sizeof(it)))
rc = -EFAULT;
else
rc = do_rangeinfo_ioctl(dev, &it);
break;
}
case COMEDI_BUFINFO:
rc = do_bufinfo_ioctl(dev,
(struct comedi_bufinfo __user *)arg,
@@ -2223,22 +2197,64 @@ static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
case COMEDI_CANCEL:
rc = do_cancel_ioctl(dev, arg, file);
break;
case COMEDI_CMD:
rc = do_cmd_ioctl(dev, (struct comedi_cmd __user *)arg, file);
case COMEDI_CMD: {
struct comedi_cmd cmd;
bool copy = false;
if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) {
rc = -EFAULT;
break;
}
rc = do_cmd_ioctl(dev, &cmd, &copy, file);
if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd)))
rc = -EFAULT;
break;
case COMEDI_CMDTEST:
rc = do_cmdtest_ioctl(dev, (struct comedi_cmd __user *)arg,
file);
}
case COMEDI_CMDTEST: {
struct comedi_cmd cmd;
bool copy = false;
if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) {
rc = -EFAULT;
break;
}
rc = do_cmdtest_ioctl(dev, &cmd, &copy, file);
if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd)))
rc = -EFAULT;
break;
case COMEDI_INSNLIST:
rc = do_insnlist_ioctl(dev,
(struct comedi_insnlist __user *)arg,
file);
}
case COMEDI_INSNLIST: {
struct comedi_insnlist insnlist;
struct comedi_insn *insns = NULL;
if (copy_from_user(&insnlist, (void __user *)arg,
sizeof(insnlist))) {
rc = -EFAULT;
break;
}
insns = kcalloc(insnlist.n_insns, sizeof(*insns), GFP_KERNEL);
if (!insns) {
rc = -ENOMEM;
break;
}
if (copy_from_user(insns, insnlist.insns,
sizeof(*insns) * insnlist.n_insns)) {
rc = -EFAULT;
kfree(insns);
break;
}
rc = do_insnlist_ioctl(dev, insns, insnlist.n_insns, file);
kfree(insns);
break;
case COMEDI_INSN:
rc = do_insn_ioctl(dev, (struct comedi_insn __user *)arg,
file);
}
case COMEDI_INSN: {
struct comedi_insn insn;
if (copy_from_user(&insn, (void __user *)arg, sizeof(insn)))
rc = -EFAULT;
else
rc = do_insn_ioctl(dev, &insn, file);
break;
}
case COMEDI_POLL:
rc = do_poll_ioctl(dev, arg, file);
break;
@@ -2808,6 +2824,344 @@ static int comedi_close(struct inode *inode, struct file *file)
return 0;
}
#ifdef CONFIG_COMPAT
#define COMEDI32_CHANINFO _IOR(CIO, 3, struct comedi32_chaninfo_struct)
#define COMEDI32_RANGEINFO _IOR(CIO, 8, struct comedi32_rangeinfo_struct)
/*
* N.B. COMEDI32_CMD and COMEDI_CMD ought to use _IOWR, not _IOR.
* It's too late to change it now, but it only affects the command number.
*/
#define COMEDI32_CMD _IOR(CIO, 9, struct comedi32_cmd_struct)
/*
* N.B. COMEDI32_CMDTEST and COMEDI_CMDTEST ought to use _IOWR, not _IOR.
* It's too late to change it now, but it only affects the command number.
*/
#define COMEDI32_CMDTEST _IOR(CIO, 10, struct comedi32_cmd_struct)
#define COMEDI32_INSNLIST _IOR(CIO, 11, struct comedi32_insnlist_struct)
#define COMEDI32_INSN _IOR(CIO, 12, struct comedi32_insn_struct)
struct comedi32_chaninfo_struct {
unsigned int subdev;
compat_uptr_t maxdata_list; /* 32-bit 'unsigned int *' */
compat_uptr_t flaglist; /* 32-bit 'unsigned int *' */
compat_uptr_t rangelist; /* 32-bit 'unsigned int *' */
unsigned int unused[4];
};
struct comedi32_rangeinfo_struct {
unsigned int range_type;
compat_uptr_t range_ptr; /* 32-bit 'void *' */
};
struct comedi32_cmd_struct {
unsigned int subdev;
unsigned int flags;
unsigned int start_src;
unsigned int start_arg;
unsigned int scan_begin_src;
unsigned int scan_begin_arg;
unsigned int convert_src;
unsigned int convert_arg;
unsigned int scan_end_src;
unsigned int scan_end_arg;
unsigned int stop_src;
unsigned int stop_arg;
compat_uptr_t chanlist; /* 32-bit 'unsigned int *' */
unsigned int chanlist_len;
compat_uptr_t data; /* 32-bit 'short *' */
unsigned int data_len;
};
struct comedi32_insn_struct {
unsigned int insn;
unsigned int n;
compat_uptr_t data; /* 32-bit 'unsigned int *' */
unsigned int subdev;
unsigned int chanspec;
unsigned int unused[3];
};
struct comedi32_insnlist_struct {
unsigned int n_insns;
compat_uptr_t insns; /* 32-bit 'struct comedi_insn *' */
};
/* Handle 32-bit COMEDI_CHANINFO ioctl. */
static int compat_chaninfo(struct file *file, unsigned long arg)
{
struct comedi_file *cfp = file->private_data;
struct comedi_device *dev = cfp->dev;
struct comedi32_chaninfo_struct chaninfo32;
struct comedi_chaninfo chaninfo;
int err;
if (copy_from_user(&chaninfo32, compat_ptr(arg), sizeof(chaninfo32)))
return -EFAULT;
memset(&chaninfo, 0, sizeof(chaninfo));
chaninfo.subdev = chaninfo32.subdev;
chaninfo.maxdata_list = compat_ptr(chaninfo32.maxdata_list);
chaninfo.flaglist = compat_ptr(chaninfo32.flaglist);
chaninfo.rangelist = compat_ptr(chaninfo32.rangelist);
mutex_lock(&dev->mutex);
err = do_chaninfo_ioctl(dev, &chaninfo);
mutex_unlock(&dev->mutex);
return err;
}
/* Handle 32-bit COMEDI_RANGEINFO ioctl. */
static int compat_rangeinfo(struct file *file, unsigned long arg)
{
struct comedi_file *cfp = file->private_data;
struct comedi_device *dev = cfp->dev;
struct comedi32_rangeinfo_struct rangeinfo32;
struct comedi_rangeinfo rangeinfo;
int err;
if (copy_from_user(&rangeinfo32, compat_ptr(arg), sizeof(rangeinfo32)))
return -EFAULT;
memset(&rangeinfo, 0, sizeof(rangeinfo));
rangeinfo.range_type = rangeinfo32.range_type;
rangeinfo.range_ptr = compat_ptr(rangeinfo32.range_ptr);
mutex_lock(&dev->mutex);
err = do_rangeinfo_ioctl(dev, &rangeinfo);
mutex_unlock(&dev->mutex);
return err;
}
/* Copy 32-bit cmd structure to native cmd structure. */
static int get_compat_cmd(struct comedi_cmd *cmd,
struct comedi32_cmd_struct __user *cmd32)
{
struct comedi32_cmd_struct v32;
if (copy_from_user(&v32, cmd32, sizeof(v32)))
return -EFAULT;
cmd->subdev = v32.subdev;
cmd->flags = v32.flags;
cmd->start_src = v32.start_src;
cmd->start_arg = v32.start_arg;
cmd->scan_begin_src = v32.scan_begin_src;
cmd->scan_begin_arg = v32.scan_begin_arg;
cmd->convert_src = v32.convert_src;
cmd->convert_arg = v32.convert_arg;
cmd->scan_end_src = v32.scan_end_src;
cmd->scan_end_arg = v32.scan_end_arg;
cmd->stop_src = v32.stop_src;
cmd->stop_arg = v32.stop_arg;
cmd->chanlist = compat_ptr(v32.chanlist);
cmd->chanlist_len = v32.chanlist_len;
cmd->data = compat_ptr(v32.data);
cmd->data_len = v32.data_len;
return 0;
}
/* Copy native cmd structure to 32-bit cmd structure. */
static int put_compat_cmd(struct comedi32_cmd_struct __user *cmd32,
struct comedi_cmd *cmd)
{
struct comedi32_cmd_struct v32;
memset(&v32, 0, sizeof(v32));
v32.subdev = cmd->subdev;
v32.flags = cmd->flags;
v32.start_src = cmd->start_src;
v32.start_arg = cmd->start_arg;
v32.scan_begin_src = cmd->scan_begin_src;
v32.scan_begin_arg = cmd->scan_begin_arg;
v32.convert_src = cmd->convert_src;
v32.convert_arg = cmd->convert_arg;
v32.scan_end_src = cmd->scan_end_src;
v32.scan_end_arg = cmd->scan_end_arg;
v32.stop_src = cmd->stop_src;
v32.stop_arg = cmd->stop_arg;
/* Assume chanlist pointer is unchanged. */
v32.chanlist = ptr_to_compat(cmd->chanlist);
v32.chanlist_len = cmd->chanlist_len;
v32.data = ptr_to_compat(cmd->data);
v32.data_len = cmd->data_len;
return copy_to_user(cmd32, &v32, sizeof(v32));
}
/* Handle 32-bit COMEDI_CMD ioctl. */
static int compat_cmd(struct file *file, unsigned long arg)
{
struct comedi_file *cfp = file->private_data;
struct comedi_device *dev = cfp->dev;
struct comedi_cmd cmd;
bool copy = false;
int rc, err;
rc = get_compat_cmd(&cmd, compat_ptr(arg));
if (rc)
return rc;
mutex_lock(&dev->mutex);
rc = do_cmd_ioctl(dev, &cmd, &copy, file);
mutex_unlock(&dev->mutex);
if (copy) {
/* Special case: copy cmd back to user. */
err = put_compat_cmd(compat_ptr(arg), &cmd);
if (err)
rc = err;
}
return rc;
}
/* Handle 32-bit COMEDI_CMDTEST ioctl. */
static int compat_cmdtest(struct file *file, unsigned long arg)
{
struct comedi_file *cfp = file->private_data;
struct comedi_device *dev = cfp->dev;
struct comedi_cmd cmd;
bool copy = false;
int rc, err;
rc = get_compat_cmd(&cmd, compat_ptr(arg));
if (rc)
return rc;
mutex_lock(&dev->mutex);
rc = do_cmdtest_ioctl(dev, &cmd, &copy, file);
mutex_unlock(&dev->mutex);
if (copy) {
err = put_compat_cmd(compat_ptr(arg), &cmd);
if (err)
rc = err;
}
return rc;
}
/* Copy 32-bit insn structure to native insn structure. */
static int get_compat_insn(struct comedi_insn *insn,
struct comedi32_insn_struct __user *insn32)
{
struct comedi32_insn_struct v32;
/* Copy insn structure. Ignore the unused members. */
if (copy_from_user(&v32, insn32, sizeof(v32)))
return -EFAULT;
memset(insn, 0, sizeof(*insn));
insn->insn = v32.insn;
insn->n = v32.n;
insn->data = compat_ptr(v32.data);
insn->subdev = v32.subdev;
insn->chanspec = v32.chanspec;
return 0;
}
/* Handle 32-bit COMEDI_INSNLIST ioctl. */
static int compat_insnlist(struct file *file, unsigned long arg)
{
struct comedi_file *cfp = file->private_data;
struct comedi_device *dev = cfp->dev;
struct comedi32_insnlist_struct insnlist32;
struct comedi32_insn_struct __user *insn32;
struct comedi_insn *insns;
unsigned int n;
int rc;
if (copy_from_user(&insnlist32, compat_ptr(arg), sizeof(insnlist32)))
return -EFAULT;
insns = kcalloc(insnlist32.n_insns, sizeof(*insns), GFP_KERNEL);
if (!insns)
return -ENOMEM;
/* Copy insn structures. */
insn32 = compat_ptr(insnlist32.insns);
for (n = 0; n < insnlist32.n_insns; n++) {
rc = get_compat_insn(insns + n, insn32 + n);
if (rc) {
kfree(insns);
return rc;
}
}
mutex_lock(&dev->mutex);
rc = do_insnlist_ioctl(dev, insns, insnlist32.n_insns, file);
mutex_unlock(&dev->mutex);
return rc;
}
/* Handle 32-bit COMEDI_INSN ioctl. */
static int compat_insn(struct file *file, unsigned long arg)
{
struct comedi_file *cfp = file->private_data;
struct comedi_device *dev = cfp->dev;
struct comedi_insn insn;
int rc;
rc = get_compat_insn(&insn, (void __user *)arg);
if (rc)
return rc;
mutex_lock(&dev->mutex);
rc = do_insn_ioctl(dev, &insn, file);
mutex_unlock(&dev->mutex);
return rc;
}
/*
* compat_ioctl file operation.
*
* Returns -ENOIOCTLCMD for unrecognised ioctl codes.
*/
static long comedi_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int rc;
switch (cmd) {
case COMEDI_DEVCONFIG:
case COMEDI_DEVINFO:
case COMEDI_SUBDINFO:
case COMEDI_BUFCONFIG:
case COMEDI_BUFINFO:
/* Just need to translate the pointer argument. */
arg = (unsigned long)compat_ptr(arg);
rc = comedi_unlocked_ioctl(file, cmd, arg);
break;
case COMEDI_LOCK:
case COMEDI_UNLOCK:
case COMEDI_CANCEL:
case COMEDI_POLL:
case COMEDI_SETRSUBD:
case COMEDI_SETWSUBD:
/* No translation needed. */
rc = comedi_unlocked_ioctl(file, cmd, arg);
break;
case COMEDI32_CHANINFO:
rc = compat_chaninfo(file, arg);
break;
case COMEDI32_RANGEINFO:
rc = compat_rangeinfo(file, arg);
break;
case COMEDI32_CMD:
rc = compat_cmd(file, arg);
break;
case COMEDI32_CMDTEST:
rc = compat_cmdtest(file, arg);
break;
case COMEDI32_INSNLIST:
rc = compat_insnlist(file, arg);
break;
case COMEDI32_INSN:
rc = compat_insn(file, arg);
break;
default:
rc = -ENOIOCTLCMD;
break;
}
return rc;
}
#else
#define comedi_compat_ioctl NULL
#endif
static const struct file_operations comedi_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = comedi_unlocked_ioctl,

View File

@@ -18,7 +18,7 @@ struct comedi_subdevice;
struct device;
int do_rangeinfo_ioctl(struct comedi_device *dev,
struct comedi_rangeinfo __user *arg);
struct comedi_rangeinfo *it);
struct comedi_device *comedi_alloc_board_minor(struct device *hardware_device);
void comedi_release_hardware_device(struct device *hardware_device);
int comedi_alloc_subdevice_minor(struct comedi_subdevice *s);

View File

@@ -46,17 +46,14 @@ EXPORT_SYMBOL_GPL(range_unknown);
* array of comedi_krange structures to rangeinfo->range_ptr pointer
*/
int do_rangeinfo_ioctl(struct comedi_device *dev,
struct comedi_rangeinfo __user *arg)
struct comedi_rangeinfo *it)
{
struct comedi_rangeinfo it;
int subd, chan;
const struct comedi_lrange *lr;
struct comedi_subdevice *s;
if (copy_from_user(&it, arg, sizeof(struct comedi_rangeinfo)))
return -EFAULT;
subd = (it.range_type >> 24) & 0xf;
chan = (it.range_type >> 16) & 0xff;
subd = (it->range_type >> 24) & 0xf;
chan = (it->range_type >> 16) & 0xff;
if (!dev->attached)
return -EINVAL;
@@ -73,15 +70,15 @@ int do_rangeinfo_ioctl(struct comedi_device *dev,
return -EINVAL;
}
if (RANGE_LENGTH(it.range_type) != lr->length) {
if (RANGE_LENGTH(it->range_type) != lr->length) {
dev_dbg(dev->class_dev,
"wrong length %d should be %d (0x%08x)\n",
RANGE_LENGTH(it.range_type),
lr->length, it.range_type);
RANGE_LENGTH(it->range_type),
lr->length, it->range_type);
return -EINVAL;
}
if (copy_to_user(it.range_ptr, lr->range,
if (copy_to_user(it->range_ptr, lr->range,
sizeof(struct comedi_krange) * lr->length))
return -EFAULT;

View File

@@ -805,7 +805,7 @@ static int fuse_try_move_page(struct fuse_copy_state *cs, struct page **pagep)
if (cs->len != PAGE_SIZE)
goto out_fallback;
if (pipe_buf_steal(cs->pipe, buf) != 0)
if (!pipe_buf_try_steal(cs->pipe, buf))
goto out_fallback;
newpage = buf->page;

View File

@@ -140,21 +140,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe,
put_page(page);
}
static int anon_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
static bool anon_pipe_buf_try_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
struct page *page = buf->page;
if (page_count(page) == 1) {
memcg_kmem_uncharge_page(page, 0);
__SetPageLocked(page);
return 0;
}
return 1;
if (page_count(page) != 1)
return false;
memcg_kmem_uncharge_page(page, 0);
__SetPageLocked(page);
return true;
}
/**
* generic_pipe_buf_steal - attempt to take ownership of a &pipe_buffer
* generic_pipe_buf_try_steal - attempt to take ownership of a &pipe_buffer
* @pipe: the pipe that the buffer belongs to
* @buf: the buffer to attempt to steal
*
@@ -165,8 +164,8 @@ static int anon_pipe_buf_steal(struct pipe_inode_info *pipe,
* he wishes; the typical use is insertion into a different file
* page cache.
*/
int generic_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
bool generic_pipe_buf_try_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
struct page *page = buf->page;
@@ -177,12 +176,11 @@ int generic_pipe_buf_steal(struct pipe_inode_info *pipe,
*/
if (page_count(page) == 1) {
lock_page(page);
return 0;
return true;
}
return 1;
return false;
}
EXPORT_SYMBOL(generic_pipe_buf_steal);
EXPORT_SYMBOL(generic_pipe_buf_try_steal);
/**
* generic_pipe_buf_get - get a reference to a &struct pipe_buffer
@@ -200,22 +198,6 @@ bool generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf)
}
EXPORT_SYMBOL(generic_pipe_buf_get);
/**
* generic_pipe_buf_confirm - verify contents of the pipe buffer
* @info: the pipe that the buffer belongs to
* @buf: the buffer to confirm
*
* Description:
* This function does nothing, because the generic pipe code uses
* pages that are always good when inserted into the pipe.
*/
int generic_pipe_buf_confirm(struct pipe_inode_info *info,
struct pipe_buffer *buf)
{
return 0;
}
EXPORT_SYMBOL(generic_pipe_buf_confirm);
/**
* generic_pipe_buf_release - put a reference to a &struct pipe_buffer
* @pipe: the pipe that the buffer belongs to
@@ -231,48 +213,12 @@ void generic_pipe_buf_release(struct pipe_inode_info *pipe,
}
EXPORT_SYMBOL(generic_pipe_buf_release);
/* New data written to a pipe may be appended to a buffer with this type. */
static const struct pipe_buf_operations anon_pipe_buf_ops = {
.confirm = generic_pipe_buf_confirm,
.release = anon_pipe_buf_release,
.steal = anon_pipe_buf_steal,
.get = generic_pipe_buf_get,
.release = anon_pipe_buf_release,
.try_steal = anon_pipe_buf_try_steal,
.get = generic_pipe_buf_get,
};
static const struct pipe_buf_operations anon_pipe_buf_nomerge_ops = {
.confirm = generic_pipe_buf_confirm,
.release = anon_pipe_buf_release,
.steal = anon_pipe_buf_steal,
.get = generic_pipe_buf_get,
};
static const struct pipe_buf_operations packet_pipe_buf_ops = {
.confirm = generic_pipe_buf_confirm,
.release = anon_pipe_buf_release,
.steal = anon_pipe_buf_steal,
.get = generic_pipe_buf_get,
};
/**
* pipe_buf_mark_unmergeable - mark a &struct pipe_buffer as unmergeable
* @buf: the buffer to mark
*
* Description:
* This function ensures that no future writes will be merged into the
* given &struct pipe_buffer. This is necessary when multiple pipe buffers
* share the same backing page.
*/
void pipe_buf_mark_unmergeable(struct pipe_buffer *buf)
{
if (buf->ops == &anon_pipe_buf_ops)
buf->ops = &anon_pipe_buf_nomerge_ops;
}
static bool pipe_buf_can_merge(struct pipe_buffer *buf)
{
return buf->ops == &anon_pipe_buf_ops;
}
/* Done while waiting without holding the pipe lock - thus the READ_ONCE() */
static inline bool pipe_readable(const struct pipe_inode_info *pipe)
{
@@ -478,7 +424,8 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
struct pipe_buffer *buf = &pipe->bufs[(head - 1) & mask];
int offset = buf->offset + buf->len;
if (pipe_buf_can_merge(buf) && offset + chars <= PAGE_SIZE) {
if ((buf->flags & PIPE_BUF_FLAG_CAN_MERGE) &&
offset + chars <= PAGE_SIZE) {
ret = pipe_buf_confirm(pipe, buf);
if (ret)
goto out;
@@ -541,11 +488,10 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from)
buf->ops = &anon_pipe_buf_ops;
buf->offset = 0;
buf->len = 0;
buf->flags = 0;
if (is_packetized(filp)) {
buf->ops = &packet_pipe_buf_ops;
if (is_packetized(filp))
buf->flags = PIPE_BUF_FLAG_PACKET;
}
else
buf->flags = PIPE_BUF_FLAG_CAN_MERGE;
pipe->tmp_page = NULL;
copied = copy_page_from_iter(page, 0, PAGE_SIZE, from);

View File

@@ -44,8 +44,8 @@
* addition of remove_mapping(). If success is returned, the caller may
* attempt to reuse this page for another destination.
*/
static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
static bool page_cache_pipe_buf_try_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
struct page *page = buf->page;
struct address_space *mapping;
@@ -76,7 +76,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe,
*/
if (remove_mapping(mapping, page)) {
buf->flags |= PIPE_BUF_FLAG_LRU;
return 0;
return true;
}
}
@@ -86,7 +86,7 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe,
*/
out_unlock:
unlock_page(page);
return 1;
return false;
}
static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,
@@ -139,27 +139,26 @@ error:
}
const struct pipe_buf_operations page_cache_pipe_buf_ops = {
.confirm = page_cache_pipe_buf_confirm,
.release = page_cache_pipe_buf_release,
.steal = page_cache_pipe_buf_steal,
.get = generic_pipe_buf_get,
.confirm = page_cache_pipe_buf_confirm,
.release = page_cache_pipe_buf_release,
.try_steal = page_cache_pipe_buf_try_steal,
.get = generic_pipe_buf_get,
};
static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
static bool user_page_pipe_buf_try_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
if (!(buf->flags & PIPE_BUF_FLAG_GIFT))
return 1;
return false;
buf->flags |= PIPE_BUF_FLAG_LRU;
return generic_pipe_buf_steal(pipe, buf);
return generic_pipe_buf_try_steal(pipe, buf);
}
static const struct pipe_buf_operations user_page_pipe_buf_ops = {
.confirm = generic_pipe_buf_confirm,
.release = page_cache_pipe_buf_release,
.steal = user_page_pipe_buf_steal,
.get = generic_pipe_buf_get,
.release = page_cache_pipe_buf_release,
.try_steal = user_page_pipe_buf_try_steal,
.get = generic_pipe_buf_get,
};
static void wakeup_pipe_readers(struct pipe_inode_info *pipe)
@@ -331,24 +330,15 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,
EXPORT_SYMBOL(generic_file_splice_read);
const struct pipe_buf_operations default_pipe_buf_ops = {
.confirm = generic_pipe_buf_confirm,
.release = generic_pipe_buf_release,
.steal = generic_pipe_buf_steal,
.get = generic_pipe_buf_get,
.release = generic_pipe_buf_release,
.try_steal = generic_pipe_buf_try_steal,
.get = generic_pipe_buf_get,
};
int generic_pipe_buf_nosteal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
return 1;
}
/* Pipe buffer operations for a socket and similar. */
const struct pipe_buf_operations nosteal_pipe_buf_ops = {
.confirm = generic_pipe_buf_confirm,
.release = generic_pipe_buf_release,
.steal = generic_pipe_buf_nosteal,
.get = generic_pipe_buf_get,
.release = generic_pipe_buf_release,
.get = generic_pipe_buf_get,
};
EXPORT_SYMBOL(nosteal_pipe_buf_ops);
@@ -852,15 +842,9 @@ EXPORT_SYMBOL(generic_splice_sendpage);
static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
loff_t *ppos, size_t len, unsigned int flags)
{
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,
loff_t *, size_t, unsigned int);
if (out->f_op->splice_write)
splice_write = out->f_op->splice_write;
else
splice_write = default_file_splice_write;
return splice_write(pipe, out, ppos, len, flags);
return out->f_op->splice_write(pipe, out, ppos, len, flags);
return default_file_splice_write(pipe, out, ppos, len, flags);
}
/*
@@ -870,8 +854,6 @@ static long do_splice_to(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{
ssize_t (*splice_read)(struct file *, loff_t *,
struct pipe_inode_info *, size_t, unsigned int);
int ret;
if (unlikely(!(in->f_mode & FMODE_READ)))
@@ -885,11 +867,8 @@ static long do_splice_to(struct file *in, loff_t *ppos,
len = MAX_RW_COUNT;
if (in->f_op->splice_read)
splice_read = in->f_op->splice_read;
else
splice_read = default_file_splice_read;
return splice_read(in, ppos, pipe, len, flags);
return in->f_op->splice_read(in, ppos, pipe, len, flags);
return default_file_splice_read(in, ppos, pipe, len, flags);
}
/**
@@ -1626,12 +1605,11 @@ retry:
*obuf = *ibuf;
/*
* Don't inherit the gift flag, we need to
* Don't inherit the gift and merge flags, we need to
* prevent multiple steals of this page.
*/
obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
pipe_buf_mark_unmergeable(obuf);
obuf->flags &= ~PIPE_BUF_FLAG_CAN_MERGE;
obuf->len = len;
ibuf->offset += len;
@@ -1719,12 +1697,11 @@ static int link_pipe(struct pipe_inode_info *ipipe,
*obuf = *ibuf;
/*
* Don't inherit the gift flag, we need to
* prevent multiple steals of this page.
* Don't inherit the gift and merge flag, we need to prevent
* multiple steals of this page.
*/
obuf->flags &= ~PIPE_BUF_FLAG_GIFT;
pipe_buf_mark_unmergeable(obuf);
obuf->flags &= ~PIPE_BUF_FLAG_CAN_MERGE;
if (obuf->len > len)
obuf->len = len;

View File

@@ -8,6 +8,7 @@
#define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */
#define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */
#define PIPE_BUF_FLAG_PACKET 0x08 /* read() as a packet */
#define PIPE_BUF_FLAG_CAN_MERGE 0x10 /* can merge buffers */
/**
* struct pipe_buffer - a linux kernel pipe buffer
@@ -69,11 +70,11 @@ struct pipe_inode_info {
* Note on the nesting of these functions:
*
* ->confirm()
* ->steal()
* ->try_steal()
*
* That is, ->steal() must be called on a confirmed buffer.
* See below for the meaning of each operation. Also see kerneldoc
* in fs/pipe.c for the pipe and generic variants of these hooks.
* That is, ->try_steal() must be called on a confirmed buffer. See below for
* the meaning of each operation. Also see the kerneldoc in fs/pipe.c for the
* pipe and generic variants of these hooks.
*/
struct pipe_buf_operations {
/*
@@ -81,7 +82,7 @@ struct pipe_buf_operations {
* and that the contents are good. If the pages in the pipe belong
* to a file system, we may need to wait for IO completion in this
* hook. Returns 0 for good, or a negative error value in case of
* error.
* error. If not present all pages are considered good.
*/
int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *);
@@ -93,13 +94,13 @@ struct pipe_buf_operations {
/*
* Attempt to take ownership of the pipe buffer and its contents.
* ->steal() returns 0 for success, in which case the contents
* of the pipe (the buf->page) is locked and now completely owned
* by the caller. The page may then be transferred to a different
* mapping, the most often used case is insertion into different
* file address space cache.
* ->try_steal() returns %true for success, in which case the contents
* of the pipe (the buf->page) is locked and now completely owned by the
* caller. The page may then be transferred to a different mapping, the
* most often used case is insertion into different file address space
* cache.
*/
int (*steal)(struct pipe_inode_info *, struct pipe_buffer *);
bool (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *);
/*
* Get a reference to the pipe buffer.
@@ -194,18 +195,22 @@ static inline void pipe_buf_release(struct pipe_inode_info *pipe,
static inline int pipe_buf_confirm(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
if (!buf->ops->confirm)
return 0;
return buf->ops->confirm(pipe, buf);
}
/**
* pipe_buf_steal - attempt to take ownership of a pipe_buffer
* pipe_buf_try_steal - attempt to take ownership of a pipe_buffer
* @pipe: the pipe that the buffer belongs to
* @buf: the buffer to attempt to steal
*/
static inline int pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
static inline bool pipe_buf_try_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
return buf->ops->steal(pipe, buf);
if (!buf->ops->try_steal)
return false;
return buf->ops->try_steal(pipe, buf);
}
/* Differs from PIPE_BUF in that PIPE_SIZE is the length of the actual
@@ -229,11 +234,8 @@ void free_pipe_info(struct pipe_inode_info *);
/* Generic pipe buffer ops functions */
bool generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *);
int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *);
int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *);
int generic_pipe_buf_nosteal(struct pipe_inode_info *, struct pipe_buffer *);
bool generic_pipe_buf_try_steal(struct pipe_inode_info *, struct pipe_buffer *);
void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *);
void pipe_buf_mark_unmergeable(struct pipe_buffer *buf);
extern const struct pipe_buf_operations nosteal_pipe_buf_ops;

View File

@@ -1177,10 +1177,9 @@ static void relay_pipe_buf_release(struct pipe_inode_info *pipe,
}
static const struct pipe_buf_operations relay_pipe_buf_ops = {
.confirm = generic_pipe_buf_confirm,
.release = relay_pipe_buf_release,
.steal = generic_pipe_buf_steal,
.get = generic_pipe_buf_get,
.release = relay_pipe_buf_release,
.try_steal = generic_pipe_buf_try_steal,
.get = generic_pipe_buf_get,
};
static void relay_page_release(struct splice_pipe_desc *spd, unsigned int i)

View File

@@ -6305,13 +6305,6 @@ static void tracing_spd_release_pipe(struct splice_pipe_desc *spd,
__free_page(spd->pages[idx]);
}
static const struct pipe_buf_operations tracing_pipe_buf_ops = {
.confirm = generic_pipe_buf_confirm,
.release = generic_pipe_buf_release,
.steal = generic_pipe_buf_steal,
.get = generic_pipe_buf_get,
};
static size_t
tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter)
{
@@ -6373,7 +6366,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp,
.partial = partial_def,
.nr_pages = 0, /* This gets updated below. */
.nr_pages_max = PIPE_DEF_BUFFERS,
.ops = &tracing_pipe_buf_ops,
.ops = &default_pipe_buf_ops,
.spd_release = tracing_spd_release_pipe,
};
ssize_t ret;
@@ -7582,9 +7575,7 @@ static bool buffer_pipe_buf_get(struct pipe_inode_info *pipe,
/* Pipe buffer operations for a buffer. */
static const struct pipe_buf_operations buffer_pipe_buf_ops = {
.confirm = generic_pipe_buf_confirm,
.release = buffer_pipe_buf_release,
.steal = generic_pipe_buf_nosteal,
.get = buffer_pipe_buf_get,
};

View File

@@ -129,16 +129,8 @@ out:
sock_put(sk);
}
static int smc_rx_pipe_buf_nosteal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
return 1;
}
static const struct pipe_buf_operations smc_pipe_ops = {
.confirm = generic_pipe_buf_confirm,
.release = smc_rx_pipe_buf_release,
.steal = smc_rx_pipe_buf_nosteal,
.get = generic_pipe_buf_get
};