diff options
author | Nikolaus Schulz <schulz@macnetix.de> | 2012-12-23 18:49:07 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-12-27 15:44:21 -0200 |
commit | 30ad64b8ac539459f8975aa186421ef3db0bb5cb (patch) | |
tree | 1f1024574f1e7bb1ddf25ea79f3c361acfa0f3fe /drivers/media/dvb-core/dvb_net.c | |
parent | 6ae23224557d797439d02f6ce5d10a82ab544b21 (diff) |
[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/dvb_net.c')
-rw-r--r-- | drivers/media/dvb-core/dvb_net.c | 71 |
1 files changed, 48 insertions, 23 deletions
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++) |