summary refs log tree commit diff
path: root/drivers/media/dvb-core
diff options
context:
space:
mode:
authorNikolaus Schulz <schulz@macnetix.de>2012-12-23 18:49:07 -0300
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-12-27 15:44:21 -0200
commit30ad64b8ac539459f8975aa186421ef3db0bb5cb (patch)
tree1f1024574f1e7bb1ddf25ea79f3c361acfa0f3fe /drivers/media/dvb-core
parent6ae23224557d797439d02f6ce5d10a82ab544b21 (diff)
downloadlinux-30ad64b8ac539459f8975aa186421ef3db0bb5cb.tar.gz
[media] dvb: push down ioctl lock in dvb_usercopy
Since most dvb ioctls wrap their real work with dvb_usercopy, the static mutex
used in dvb_usercopy effectively is a global lock for dvb ioctls.
Unfortunately, frontend ioctls can be blocked by the frontend thread for
several seconds; this leads to unacceptable lock contention.  Mitigate that by
pushing the mutex from dvb_usercopy down to the individual, device specific
ioctls.
There are 10 such ioctl functions using dvb_usercopy, either calling it
directly, or via the trivial wrapper dvb_generic_ioctl. The following already
employ their own locking and look safe:
    • dvb_demux_ioctl           (as per dvb_demux_do_ioctl)
    • dvb_dvr_ioctl             (as per dvb_dvr_do_ioctl)
    • dvb_osd_ioctl             (as per single non-trivial callee)
    • fdtv_ca_ioctl             (as per callees)
    • dvb_frontend_ioctl
The following functions do not, and are thus changed to use a device specific
mutex:
    • dvb_net_ioctl             (as per dvb_net_do_ioctl)
    • dvb_ca_en50221_io_ioctl   (as per dvb_ca_en50221_io_do_ioctl)
    • dvb_video_ioctl
    • dvb_audio_ioctl
    • dvb_ca_ioctl

Signed-off-by: Nikolaus Schulz <schulz@macnetix.de>
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/dvb-core')
-rw-r--r--drivers/media/dvb-core/dvb_ca_en50221.c9
-rw-r--r--drivers/media/dvb-core/dvb_net.c71
-rw-r--r--drivers/media/dvb-core/dvb_net.h1
-rw-r--r--drivers/media/dvb-core/dvbdev.c2
4 files changed, 58 insertions, 25 deletions
diff --git a/drivers/media/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb-core/dvb_ca_en50221.c
index 9be65a3b931f..190e5e0f48c7 100644
--- a/drivers/media/dvb-core/dvb_ca_en50221.c
+++ b/drivers/media/dvb-core/dvb_ca_en50221.c
@@ -156,6 +156,9 @@ struct dvb_ca_private {
 
 	/* Slot to start looking for data to read from in the next user-space read operation */
 	int next_read_slot;
+
+	/* mutex serializing ioctls */
+	struct mutex ioctl_mutex;
 };
 
 static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca);
@@ -1191,6 +1194,9 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file,
 
 	dprintk("%s\n", __func__);
 
+	if (mutex_lock_interruptible(&ca->ioctl_mutex))
+		return -ERESTARTSYS;
+
 	switch (cmd) {
 	case CA_RESET:
 		for (slot = 0; slot < ca->slot_count; slot++) {
@@ -1241,6 +1247,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file,
 		break;
 	}
 
+	mutex_unlock(&ca->ioctl_mutex);
 	return err;
 }
 
@@ -1695,6 +1702,8 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter,
 		mutex_init(&ca->slot_info[i].slot_lock);
 	}
 
+	mutex_init(&ca->ioctl_mutex);
+
 	if (signal_pending(current)) {
 		ret = -EINTR;
 		goto error;
diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c
index c2117688aa23..44225b186f6d 100644
--- a/drivers/media/dvb-core/dvb_net.c
+++ b/drivers/media/dvb-core/dvb_net.c
@@ -1345,26 +1345,35 @@ static int dvb_net_do_ioctl(struct file *file,
 {
 	struct dvb_device *dvbdev = file->private_data;
 	struct dvb_net *dvbnet = dvbdev->priv;
+	int ret = 0;
 
 	if (((file->f_flags&O_ACCMODE)==O_RDONLY))
 		return -EPERM;
 
+	if (mutex_lock_interruptible(&dvbnet->ioctl_mutex))
+		return -ERESTARTSYS;
+
 	switch (cmd) {
 	case NET_ADD_IF:
 	{
 		struct dvb_net_if *dvbnetif = parg;
 		int result;
 
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
+		if (!capable(CAP_SYS_ADMIN)) {
+			ret = -EPERM;
+			goto ioctl_error;
+		}
 
-		if (!try_module_get(dvbdev->adapter->module))
-			return -EPERM;
+		if (!try_module_get(dvbdev->adapter->module)) {
+			ret = -EPERM;
+			goto ioctl_error;
+		}
 
 		result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype);
 		if (result<0) {
 			module_put(dvbdev->adapter->module);
-			return result;
+			ret = result;
+			goto ioctl_error;
 		}
 		dvbnetif->if_num=result;
 		break;
@@ -1376,8 +1385,10 @@ static int dvb_net_do_ioctl(struct file *file,
 		struct dvb_net_if *dvbnetif = parg;
 
 		if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
-		    !dvbnet->state[dvbnetif->if_num])
-			return -EINVAL;
+		    !dvbnet->state[dvbnetif->if_num]) {
+			ret = -EINVAL;
+			goto ioctl_error;
+		}
 
 		netdev = dvbnet->device[dvbnetif->if_num];
 
@@ -1388,16 +1399,18 @@ static int dvb_net_do_ioctl(struct file *file,
 	}
 	case NET_REMOVE_IF:
 	{
-		int ret;
-
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
-		if ((unsigned long) parg >= DVB_NET_DEVICES_MAX)
-			return -EINVAL;
+		if (!capable(CAP_SYS_ADMIN)) {
+			ret = -EPERM;
+			goto ioctl_error;
+		}
+		if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) {
+			ret = -EINVAL;
+			goto ioctl_error;
+		}
 		ret = dvb_net_remove_if(dvbnet, (unsigned long) parg);
 		if (!ret)
 			module_put(dvbdev->adapter->module);
-		return ret;
+		break;
 	}
 
 	/* binary compatibility cruft */
@@ -1406,16 +1419,21 @@ static int dvb_net_do_ioctl(struct file *file,
 		struct __dvb_net_if_old *dvbnetif = parg;
 		int result;
 
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
+		if (!capable(CAP_SYS_ADMIN)) {
+			ret = -EPERM;
+			goto ioctl_error;
+		}
 
-		if (!try_module_get(dvbdev->adapter->module))
-			return -EPERM;
+		if (!try_module_get(dvbdev->adapter->module)) {
+			ret = -EPERM;
+			goto ioctl_error;
+		}
 
 		result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE);
 		if (result<0) {
 			module_put(dvbdev->adapter->module);
-			return result;
+			ret = result;
+			goto ioctl_error;
 		}
 		dvbnetif->if_num=result;
 		break;
@@ -1427,8 +1445,10 @@ static int dvb_net_do_ioctl(struct file *file,
 		struct __dvb_net_if_old *dvbnetif = parg;
 
 		if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX ||
-		    !dvbnet->state[dvbnetif->if_num])
-			return -EINVAL;
+		    !dvbnet->state[dvbnetif->if_num]) {
+			ret = -EINVAL;
+			goto ioctl_error;
+		}
 
 		netdev = dvbnet->device[dvbnetif->if_num];
 
@@ -1437,9 +1457,13 @@ static int dvb_net_do_ioctl(struct file *file,
 		break;
 	}
 	default:
-		return -ENOTTY;
+		ret = -ENOTTY;
+		break;
 	}
-	return 0;
+
+ioctl_error:
+	mutex_unlock(&dvbnet->ioctl_mutex);
+	return ret;
 }
 
 static long dvb_net_ioctl(struct file *file,
@@ -1505,6 +1529,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet,
 {
 	int i;
 
+	mutex_init(&dvbnet->ioctl_mutex);
 	dvbnet->demux = dmx;
 
 	for (i=0; i<DVB_NET_DEVICES_MAX; i++)
diff --git a/drivers/media/dvb-core/dvb_net.h b/drivers/media/dvb-core/dvb_net.h
index 1e53acd50cf4..ede78e8c8aa8 100644
--- a/drivers/media/dvb-core/dvb_net.h
+++ b/drivers/media/dvb-core/dvb_net.h
@@ -40,6 +40,7 @@ struct dvb_net {
 	int state[DVB_NET_DEVICES_MAX];
 	unsigned int exit:1;
 	struct dmx_demux *demux;
+	struct mutex ioctl_mutex;
 };
 
 void dvb_net_release(struct dvb_net *);
diff --git a/drivers/media/dvb-core/dvbdev.c b/drivers/media/dvb-core/dvbdev.c
index d33101aaf0b5..401ef64f92c6 100644
--- a/drivers/media/dvb-core/dvbdev.c
+++ b/drivers/media/dvb-core/dvbdev.c
@@ -418,10 +418,8 @@ int dvb_usercopy(struct file *file,
 	}
 
 	/* call driver */
-	mutex_lock(&dvbdev_mutex);
 	if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD)
 		err = -ENOTTY;
-	mutex_unlock(&dvbdev_mutex);
 
 	if (err < 0)
 		goto out;