Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 30ad64b8 authored by Nikolaus Schulz's avatar Nikolaus Schulz Committed by Mauro Carvalho Chehab
Browse files

[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: default avatarNikolaus Schulz <schulz@macnetix.de>
Signed-off-by: default avatarMichael Krufky <mkrufky@linuxtv.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 6ae23224
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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;
+48 −23
Original line number Diff line number Diff line
@@ -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++)
+1 −0
Original line number Diff line number Diff line
@@ -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 *);
+0 −2
Original line number Diff line number Diff line
@@ -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;
+2 −0
Original line number Diff line number Diff line
@@ -2723,6 +2723,8 @@ static int __devinit av7110_attach(struct saa7146_dev* dev,
	if (ret < 0)
		goto err_av7110_exit_v4l_12;

	mutex_init(&av7110->ioctl_mutex);

#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE)
	av7110_ir_init(av7110);
#endif
Loading