All of lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Richter <stefanr@s5r6.in-berlin.de>
To: linux-media@vger.kernel.org
Cc: linux-kernel@vger.kernel.org,
	Mauro Carvalho Chehab <mchehab@infradead.org>,
	Arnd Bergmann <arnd@arndb.de>, Henrik Kurelid <henrik@kurelid.se>
Subject: [PATCH RFC v2] DVB: add dvb_generic_nonseekable_open, dvb_generic_unlocked_ioctl, use in firedtv
Date: Sun, 28 Mar 2010 16:47:06 +0200 (CEST)	[thread overview]
Message-ID: <tkrat.a7b7e7df43463db1@s5r6.in-berlin.de> (raw)
In-Reply-To: <tkrat.7662a00017241225@s5r6.in-berlin.de>

In order to remove Big Kernel Lock usages from the DVB subsystem,
we need to
  - provide .llseek file operations that do not grab the BKL (unlike
    fs/read_write.c::default_llseek) or mark files as not seekable,
  - provide .unlocked_ioctl file operations.

Add two dvb_generic_ file operations for file interfaces which are not
seekable and, respectively, do not require the BKL in their ioctl
handlers.

Use them in one driver of which I am sure of that these are applicable.
(Affected code paths in firedtv-ci were not runtime-tested since I don't
have a CAM, but the frontend ioctls were of course runtime-tested.)

Notes:

  - The dvb-core internal dvb_usercopy() API is changed to match
    .unlocked_ioctl() prototypes.

  - I suspect that all dvb_generic_open() users really want
    nonseekable_open --- then we should simply change dvb_generic_open()
    instead of adding dvb_generic_nonseekable_open() --- but I haven't
    checked other users of dvb_generic_open whether they require
    .llssek mehods other than fs/read_write.c::no_llseek.
    Applies to:
    drivers/media/dvb/ttpci/av7110.c
    drivers/media/dvb/ttpci/av7110_av.c
    drivers/media/dvb/ttpci/av7110_ca.c
    drivers/media/dvb/dvb-core/dvb_net.c
    drivers/media/dvb/dvb-core/dvb_frontend.c
    drivers/media/dvb/dvb-core/dvb_ca_en50221.c

  - To be done by those who know the code:  Check all users of
    struct dvb_device.kernel_ioctl whether they really need the BKL.
    Convert to .unlocked_ioctl and remove .kernel_ioctl and the
    temporarily introduced dvbdev.c::legacy_usercopy().
    Applies to:
    drivers/media/dvb/ttpci/av7110.c
    drivers/media/dvb/ttpci/av7110_av.c
    drivers/media/dvb/ttpci/av7110_ca.c
    drivers/media/dvb/dvb-core/dvb_frontend.c

Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
---

Update:  Split dvb_usercopy into one that follows the .unlocked_ioctl
prototype form and another one that preserves the old form.

 drivers/media/dvb/dvb-core/dmxdev.c         |   10 -
 drivers/media/dvb/dvb-core/dvb_ca_en50221.c |    6 
 drivers/media/dvb/dvb-core/dvb_net.c        |    5 
 drivers/media/dvb/dvb-core/dvbdev.c         |  190 +++++++++++++-------
 drivers/media/dvb/dvb-core/dvbdev.h         |   23 +-
 drivers/media/dvb/firewire/firedtv-ci.c     |    9 
 6 files changed, 148 insertions(+), 95 deletions(-)

Index: b/drivers/media/dvb/dvb-core/dmxdev.c
===================================================================
--- a/drivers/media/dvb/dvb-core/dmxdev.c
+++ b/drivers/media/dvb/dvb-core/dmxdev.c
@@ -963,8 +963,7 @@ dvb_demux_read(struct file *file, char _
 	return ret;
 }
 
-static int dvb_demux_do_ioctl(struct inode *inode, struct file *file,
-			      unsigned int cmd, void *parg)
+static long dvb_demux_do_ioctl(struct file *file, unsigned int cmd, void *parg)
 {
 	struct dmxdev_filter *dmxdevfilter = file->private_data;
 	struct dmxdev *dmxdev = dmxdevfilter->dev;
@@ -1087,7 +1086,7 @@ static int dvb_demux_do_ioctl(struct ino
 static int dvb_demux_ioctl(struct inode *inode, struct file *file,
 			   unsigned int cmd, unsigned long arg)
 {
-	return dvb_usercopy(inode, file, cmd, arg, dvb_demux_do_ioctl);
+	return dvb_usercopy(file, cmd, arg, dvb_demux_do_ioctl);
 }
 
 static unsigned int dvb_demux_poll(struct file *file, poll_table *wait)
@@ -1152,8 +1151,7 @@ static struct dvb_device dvbdev_demux = 
 	.fops = &dvb_demux_fops
 };
 
-static int dvb_dvr_do_ioctl(struct inode *inode, struct file *file,
-			    unsigned int cmd, void *parg)
+static long dvb_dvr_do_ioctl(struct file *file, unsigned int cmd, void *parg)
 {
 	struct dvb_device *dvbdev = file->private_data;
 	struct dmxdev *dmxdev = dvbdev->priv;
@@ -1179,7 +1177,7 @@ static int dvb_dvr_do_ioctl(struct inode
 static int dvb_dvr_ioctl(struct inode *inode, struct file *file,
 			 unsigned int cmd, unsigned long arg)
 {
-	return dvb_usercopy(inode, file, cmd, arg, dvb_dvr_do_ioctl);
+	return dvb_usercopy(file, cmd, arg, dvb_dvr_do_ioctl);
 }
 
 static unsigned int dvb_dvr_poll(struct file *file, poll_table *wait)
Index: b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
===================================================================
--- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
@@ -1181,8 +1181,8 @@ static int dvb_ca_en50221_thread(void *d
  *
  * @return 0 on success, <0 on error.
  */
-static int dvb_ca_en50221_io_do_ioctl(struct inode *inode, struct file *file,
-				      unsigned int cmd, void *parg)
+static long dvb_ca_en50221_io_do_ioctl(struct file *file,
+				       unsigned int cmd, void *parg)
 {
 	struct dvb_device *dvbdev = file->private_data;
 	struct dvb_ca_private *ca = dvbdev->priv;
@@ -1258,7 +1258,7 @@ static int dvb_ca_en50221_io_do_ioctl(st
 static int dvb_ca_en50221_io_ioctl(struct inode *inode, struct file *file,
 				   unsigned int cmd, unsigned long arg)
 {
-	return dvb_usercopy(inode, file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
+	return dvb_usercopy(file, cmd, arg, dvb_ca_en50221_io_do_ioctl);
 }
 
 
Index: b/drivers/media/dvb/dvb-core/dvb_net.c
===================================================================
--- a/drivers/media/dvb/dvb-core/dvb_net.c
+++ b/drivers/media/dvb/dvb-core/dvb_net.c
@@ -1333,8 +1333,7 @@ static int dvb_net_remove_if(struct dvb_
 	return 0;
 }
 
-static int dvb_net_do_ioctl(struct inode *inode, struct file *file,
-		  unsigned int cmd, void *parg)
+static long dvb_net_do_ioctl(struct file *file, unsigned int cmd, void *parg)
 {
 	struct dvb_device *dvbdev = file->private_data;
 	struct dvb_net *dvbnet = dvbdev->priv;
@@ -1438,7 +1437,7 @@ static int dvb_net_do_ioctl(struct inode
 static int dvb_net_ioctl(struct inode *inode, struct file *file,
 	      unsigned int cmd, unsigned long arg)
 {
-	return dvb_usercopy(inode, file, cmd, arg, dvb_net_do_ioctl);
+	return dvb_usercopy(file, cmd, arg, dvb_net_do_ioctl);
 }
 
 static int dvb_net_close(struct inode *inode, struct file *file)
Index: b/drivers/media/dvb/dvb-core/dvbdev.c
===================================================================
--- a/drivers/media/dvb/dvb-core/dvbdev.c
+++ b/drivers/media/dvb/dvb-core/dvbdev.c
@@ -135,6 +135,18 @@ int dvb_generic_open(struct inode *inode
 EXPORT_SYMBOL(dvb_generic_open);
 
 
+int dvb_generic_nonseekable_open(struct inode *inode, struct file *file)
+{
+	int retval = dvb_generic_open(inode, file);
+
+	if (retval == 0)
+		retval = nonseekable_open(inode, file);
+
+	return retval;
+}
+EXPORT_SYMBOL(dvb_generic_nonseekable_open);
+
+
 int dvb_generic_release(struct inode *inode, struct file *file)
 {
 	struct dvb_device *dvbdev = file->private_data;
@@ -154,6 +166,102 @@ int dvb_generic_release(struct inode *in
 EXPORT_SYMBOL(dvb_generic_release);
 
 
+#define STATIC_BUFFER_SIZE 128
+
+/* If necessary, swap out static *buf by a slab-allocated buffer */
+static int get_arg(unsigned int cmd, unsigned long arg, void **parg, void **buf)
+{
+	switch (_IOC_DIR(cmd)) {
+	case _IOC_NONE:
+		/*
+		 * For this command, the pointer is actually an integer
+		 * argument.
+		 */
+		*parg = (void *)arg;
+		break;
+	case _IOC_READ: /* some v4l ioctls are marked wrong ... */
+	case _IOC_WRITE:
+	case (_IOC_WRITE | _IOC_READ):
+		if (_IOC_SIZE(cmd) > STATIC_BUFFER_SIZE) {
+			*buf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
+			if (!*buf)
+				return -ENOMEM;
+		}
+		*parg = *buf;
+
+		if (copy_from_user(*parg, (void __user *)arg, _IOC_SIZE(cmd)))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+static int put_arg(unsigned int cmd, unsigned long arg, void *parg)
+{
+	switch (_IOC_DIR(cmd)) {
+	case _IOC_READ:
+	case (_IOC_WRITE | _IOC_READ):
+		if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
+			return -EFAULT;
+	}
+	return 0;
+}
+
+/*
+ * Wrapper around ioctl handlers; copies arguments and results from/ to user.
+ * Could be changed into a "generic_usercopy()" for all kernel subsystems.
+ */
+long dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
+		  long (*func)(struct file *file, unsigned int cmd, void *arg))
+{
+	char sbuf[STATIC_BUFFER_SIZE];
+	void *parg, *buf = sbuf;
+	long retval;
+
+	retval = get_arg(cmd, arg, &parg, &buf);
+	if (retval < 0)
+		goto out;
+
+	/* call driver */
+	retval = func(file, cmd, parg);
+	if (retval == -ENOIOCTLCMD)
+		retval = -EINVAL;
+	if (retval < 0)
+		goto out;
+
+	retval = put_arg(cmd, arg, parg);
+out:
+	if (buf != sbuf)
+		kfree(buf);
+	return retval;
+}
+
+static int legacy_usercopy(struct inode *inode, struct file *file,
+			   unsigned int cmd, unsigned long arg,
+			   int (*func)(struct inode *inode, struct file *file,
+			   unsigned int cmd, void *arg))
+{
+	char sbuf[STATIC_BUFFER_SIZE];
+	void *parg, *buf = sbuf;
+	int retval;
+
+	retval = get_arg(cmd, arg, &parg, &buf);
+	if (retval < 0)
+		goto out;
+
+	/* call driver */
+	retval = func(inode, file, cmd, parg);
+	if (retval == -ENOIOCTLCMD)
+		retval = -EINVAL;
+	if (retval < 0)
+		goto out;
+
+	retval = put_arg(cmd, arg, parg);
+out:
+	if (buf != sbuf)
+		kfree(buf);
+	return retval;
+}
+
 int dvb_generic_ioctl(struct inode *inode, struct file *file,
 		      unsigned int cmd, unsigned long arg)
 {
@@ -165,11 +273,27 @@ int dvb_generic_ioctl(struct inode *inod
 	if (!dvbdev->kernel_ioctl)
 		return -EINVAL;
 
-	return dvb_usercopy (inode, file, cmd, arg, dvbdev->kernel_ioctl);
+	return legacy_usercopy(inode, file, cmd, arg, dvbdev->kernel_ioctl);
 }
 EXPORT_SYMBOL(dvb_generic_ioctl);
 
 
+long dvb_generic_unlocked_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct dvb_device *dvbdev = file->private_data;
+
+	if (!dvbdev)
+		return -ENODEV;
+
+	if (!dvbdev->unlocked_ioctl)
+		return -EINVAL;
+
+	return dvb_usercopy(file, cmd, arg, dvbdev->unlocked_ioctl);
+}
+EXPORT_SYMBOL(dvb_generic_unlocked_ioctl);
+
+
 static int dvbdev_get_free_id (struct dvb_adapter *adap, int type)
 {
 	u32 id = 0;
@@ -372,70 +496,6 @@ int dvb_unregister_adapter(struct dvb_ad
 }
 EXPORT_SYMBOL(dvb_unregister_adapter);
 
-/* if the miracle happens and "generic_usercopy()" is included into
-   the kernel, then this can vanish. please don't make the mistake and
-   define this as video_usercopy(). this will introduce a dependecy
-   to the v4l "videodev.o" module, which is unnecessary for some
-   cards (ie. the budget dvb-cards don't need the v4l module...) */
-int dvb_usercopy(struct inode *inode, struct file *file,
-		     unsigned int cmd, unsigned long arg,
-		     int (*func)(struct inode *inode, struct file *file,
-		     unsigned int cmd, void *arg))
-{
-	char    sbuf[128];
-	void    *mbuf = NULL;
-	void    *parg = NULL;
-	int     err  = -EINVAL;
-
-	/*  Copy arguments into temp kernel buffer  */
-	switch (_IOC_DIR(cmd)) {
-	case _IOC_NONE:
-		/*
-		 * For this command, the pointer is actually an integer
-		 * argument.
-		 */
-		parg = (void *) arg;
-		break;
-	case _IOC_READ: /* some v4l ioctls are marked wrong ... */
-	case _IOC_WRITE:
-	case (_IOC_WRITE | _IOC_READ):
-		if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
-			parg = sbuf;
-		} else {
-			/* too big to allocate from stack */
-			mbuf = kmalloc(_IOC_SIZE(cmd),GFP_KERNEL);
-			if (NULL == mbuf)
-				return -ENOMEM;
-			parg = mbuf;
-		}
-
-		err = -EFAULT;
-		if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
-			goto out;
-		break;
-	}
-
-	/* call driver */
-	if ((err = func(inode, file, cmd, parg)) == -ENOIOCTLCMD)
-		err = -EINVAL;
-
-	if (err < 0)
-		goto out;
-
-	/*  Copy results into user buffer  */
-	switch (_IOC_DIR(cmd))
-	{
-	case _IOC_READ:
-	case (_IOC_WRITE | _IOC_READ):
-		if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
-			err = -EFAULT;
-		break;
-	}
-
-out:
-	kfree(mbuf);
-	return err;
-}
 
 static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
Index: b/drivers/media/dvb/dvb-core/dvbdev.h
===================================================================
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -118,6 +118,7 @@ struct dvb_device {
 	/* don't really need those !? -- FIXME: use video_usercopy  */
 	int (*kernel_ioctl)(struct inode *inode, struct file *file,
 			    unsigned int cmd, void *arg);
+	long (*unlocked_ioctl)(struct file *file, unsigned int cmd, void *arg);
 
 	void *priv;
 };
@@ -136,19 +137,15 @@ extern int dvb_register_device (struct d
 
 extern void dvb_unregister_device (struct dvb_device *dvbdev);
 
-extern int dvb_generic_open (struct inode *inode, struct file *file);
-extern int dvb_generic_release (struct inode *inode, struct file *file);
-extern int dvb_generic_ioctl (struct inode *inode, struct file *file,
-			      unsigned int cmd, unsigned long arg);
-
-/* we don't mess with video_usercopy() any more,
-we simply define out own dvb_usercopy(), which will hopefully become
-generic_usercopy()  someday... */
-
-extern int dvb_usercopy(struct inode *inode, struct file *file,
-			    unsigned int cmd, unsigned long arg,
-			    int (*func)(struct inode *inode, struct file *file,
-			    unsigned int cmd, void *arg));
+extern int dvb_generic_open(struct inode *inode, struct file *file);
+extern int dvb_generic_nonseekable_open(struct inode *inode, struct file *file);
+extern int dvb_generic_release(struct inode *inode, struct file *file);
+extern int dvb_generic_ioctl(struct inode *inode, struct file *file,
+			     unsigned int cmd, unsigned long arg);
+extern long dvb_generic_unlocked_ioctl(struct file *file,
+					unsigned int cmd, unsigned long arg);
+extern long dvb_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
+		long (*func)(struct file *file, unsigned int cmd, void *arg));
 
 /** generic DVB attach function. */
 #ifdef CONFIG_MEDIA_ATTACH
Index: b/drivers/media/dvb/firewire/firedtv-ci.c
===================================================================
--- a/drivers/media/dvb/firewire/firedtv-ci.c
+++ b/drivers/media/dvb/firewire/firedtv-ci.c
@@ -175,8 +175,7 @@ static int fdtv_ca_send_msg(struct fired
 	return err;
 }
 
-static int fdtv_ca_ioctl(struct inode *inode, struct file *file,
-			    unsigned int cmd, void *arg)
+static long fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg)
 {
 	struct dvb_device *dvbdev = file->private_data;
 	struct firedtv *fdtv = dvbdev->priv;
@@ -217,8 +216,8 @@ static unsigned int fdtv_ca_io_poll(stru
 
 static const struct file_operations fdtv_ca_fops = {
 	.owner		= THIS_MODULE,
-	.ioctl		= dvb_generic_ioctl,
-	.open		= dvb_generic_open,
+	.unlocked_ioctl	= dvb_generic_unlocked_ioctl,
+	.open		= dvb_generic_nonseekable_open,
 	.release	= dvb_generic_release,
 	.poll		= fdtv_ca_io_poll,
 };
@@ -228,7 +227,7 @@ static struct dvb_device fdtv_ca = {
 	.readers	= 1,
 	.writers	= 1,
 	.fops		= &fdtv_ca_fops,
-	.kernel_ioctl	= fdtv_ca_ioctl,
+	.unlocked_ioctl	= fdtv_ca_ioctl,
 };
 
 int fdtv_ca_register(struct firedtv *fdtv)

-- 
Stefan Richter
-=====-==-=- --== ===--
http://arcgraph.de/sr/


  reply	other threads:[~2010-03-28 14:47 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-03-24 21:40 [GIT, RFC] Killing the Big Kernel Lock Arnd Bergmann
2010-03-24 21:07 ` Andrew Morton
2010-03-25 10:26   ` Arnd Bergmann
2010-03-28 20:33     ` Frederic Weisbecker
2010-03-24 21:53 ` Roland Dreier
2010-03-24 21:59   ` Arnd Bergmann
2010-03-31  5:22     ` Roland Dreier
2010-03-24 22:10 ` Alan Cox
2010-03-24 22:25   ` Arnd Bergmann
2010-03-24 22:23 ` Ingo Molnar
2010-03-25 12:55 ` Jiri Kosina
2010-03-25 13:06   ` Arnd Bergmann
2010-03-25 13:38     ` Arnd Bergmann
2010-03-26 23:47       ` Stefan Richter
2010-03-27  9:16         ` [PATCH] firewire: char device files are not seekable (BKL removal) Stefan Richter
2010-03-27  9:20         ` [PATCH] ieee1394: " Stefan Richter
2010-03-27 10:40         ` [PATCH RFC] DVB: add dvb_generic_nonseekable_open, dvb_generic_unlocked_ioctl, use in firedtv Stefan Richter
2010-03-28 14:47           ` Stefan Richter [this message]
2010-03-27 14:37         ` [GIT, RFC] Killing the Big Kernel Lock Arnd Bergmann
2010-03-28 12:27           ` Stefan Richter
2010-03-28 20:05             ` Arnd Bergmann
2010-03-28 20:15               ` Frederic Weisbecker
2010-03-28 21:34                 ` Arnd Bergmann
2010-03-28 23:24                   ` Frederic Weisbecker
2010-04-08 20:45               ` Jan Blunck
2010-04-08 21:27                 ` Arnd Bergmann
2010-04-08 21:30                   ` Frederic Weisbecker
2010-04-09 11:02                   ` Jan Blunck
2010-04-10 15:13           ` Stefan Richter
2010-03-28 21:58   ` Andi Kleen
2010-03-29  1:07     ` [GIT, RFC] Killing the Big Kernel Lock II Andi Kleen
2010-03-29 11:48       ` Arnd Bergmann
2010-03-29 12:30         ` Andi Kleen
2010-03-29 14:43           ` Arnd Bergmann
2010-03-29 20:11             ` Andi Kleen
2010-03-31 15:30               ` Arnd Bergmann
2010-03-25 13:40 ` [GIT, RFC] Killing the Big Kernel Lock Dan Carpenter
2010-03-25 14:14   ` Arnd Bergmann
2010-03-28 20:04 ` Frederic Weisbecker
2010-03-28 20:11 ` Frederic Weisbecker
2010-03-28 23:18 ` Frederic Weisbecker
2010-03-28 23:38   ` Frederic Weisbecker
2010-03-29 11:04     ` Arnd Bergmann
2010-03-29 17:59       ` Frederic Weisbecker
2010-03-29 21:18         ` Arnd Bergmann
2010-03-29 12:45 ` John Kacur
2010-03-31 22:11 ` Roland Dreier
2010-03-31 22:20   ` Frederic Weisbecker
2010-04-01  8:50   ` Arnd Bergmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=tkrat.a7b7e7df43463db1@s5r6.in-berlin.de \
    --to=stefanr@s5r6.in-berlin.de \
    --cc=arnd@arndb.de \
    --cc=henrik@kurelid.se \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=mchehab@infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.