diff options
author | Nikolaus Schulz <schulz@macnetix.de> | 2012-12-23 16:49:07 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-12-27 12:44:21 -0500 |
commit | 30ad64b8ac539459f8975aa186421ef3db0bb5cb (patch) | |
tree | 1f1024574f1e7bb1ddf25ea79f3c361acfa0f3fe | |
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>
-rw-r--r-- | drivers/media/dvb-core/dvb_ca_en50221.c | 9 | ||||
-rw-r--r-- | drivers/media/dvb-core/dvb_net.c | 71 | ||||
-rw-r--r-- | drivers/media/dvb-core/dvb_net.h | 1 | ||||
-rw-r--r-- | drivers/media/dvb-core/dvbdev.c | 2 | ||||
-rw-r--r-- | drivers/media/pci/ttpci/av7110.c | 2 | ||||
-rw-r--r-- | drivers/media/pci/ttpci/av7110.h | 2 | ||||
-rw-r--r-- | drivers/media/pci/ttpci/av7110_av.c | 8 | ||||
-rw-r--r-- | drivers/media/pci/ttpci/av7110_ca.c | 24 |
8 files changed, 87 insertions, 32 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 { | |||
156 | 156 | ||
157 | /* Slot to start looking for data to read from in the next user-space read operation */ | 157 | /* Slot to start looking for data to read from in the next user-space read operation */ |
158 | int next_read_slot; | 158 | int next_read_slot; |
159 | |||
160 | /* mutex serializing ioctls */ | ||
161 | struct mutex ioctl_mutex; | ||
159 | }; | 162 | }; |
160 | 163 | ||
161 | static void dvb_ca_en50221_thread_wakeup(struct dvb_ca_private *ca); | 164 | 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, | |||
1191 | 1194 | ||
1192 | dprintk("%s\n", __func__); | 1195 | dprintk("%s\n", __func__); |
1193 | 1196 | ||
1197 | if (mutex_lock_interruptible(&ca->ioctl_mutex)) | ||
1198 | return -ERESTARTSYS; | ||
1199 | |||
1194 | switch (cmd) { | 1200 | switch (cmd) { |
1195 | case CA_RESET: | 1201 | case CA_RESET: |
1196 | for (slot = 0; slot < ca->slot_count; slot++) { | 1202 | for (slot = 0; slot < ca->slot_count; slot++) { |
@@ -1241,6 +1247,7 @@ static int dvb_ca_en50221_io_do_ioctl(struct file *file, | |||
1241 | break; | 1247 | break; |
1242 | } | 1248 | } |
1243 | 1249 | ||
1250 | mutex_unlock(&ca->ioctl_mutex); | ||
1244 | return err; | 1251 | return err; |
1245 | } | 1252 | } |
1246 | 1253 | ||
@@ -1695,6 +1702,8 @@ int dvb_ca_en50221_init(struct dvb_adapter *dvb_adapter, | |||
1695 | mutex_init(&ca->slot_info[i].slot_lock); | 1702 | mutex_init(&ca->slot_info[i].slot_lock); |
1696 | } | 1703 | } |
1697 | 1704 | ||
1705 | mutex_init(&ca->ioctl_mutex); | ||
1706 | |||
1698 | if (signal_pending(current)) { | 1707 | if (signal_pending(current)) { |
1699 | ret = -EINTR; | 1708 | ret = -EINTR; |
1700 | goto error; | 1709 | 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, | |||
1345 | { | 1345 | { |
1346 | struct dvb_device *dvbdev = file->private_data; | 1346 | struct dvb_device *dvbdev = file->private_data; |
1347 | struct dvb_net *dvbnet = dvbdev->priv; | 1347 | struct dvb_net *dvbnet = dvbdev->priv; |
1348 | int ret = 0; | ||
1348 | 1349 | ||
1349 | if (((file->f_flags&O_ACCMODE)==O_RDONLY)) | 1350 | if (((file->f_flags&O_ACCMODE)==O_RDONLY)) |
1350 | return -EPERM; | 1351 | return -EPERM; |
1351 | 1352 | ||
1353 | if (mutex_lock_interruptible(&dvbnet->ioctl_mutex)) | ||
1354 | return -ERESTARTSYS; | ||
1355 | |||
1352 | switch (cmd) { | 1356 | switch (cmd) { |
1353 | case NET_ADD_IF: | 1357 | case NET_ADD_IF: |
1354 | { | 1358 | { |
1355 | struct dvb_net_if *dvbnetif = parg; | 1359 | struct dvb_net_if *dvbnetif = parg; |
1356 | int result; | 1360 | int result; |
1357 | 1361 | ||
1358 | if (!capable(CAP_SYS_ADMIN)) | 1362 | if (!capable(CAP_SYS_ADMIN)) { |
1359 | return -EPERM; | 1363 | ret = -EPERM; |
1364 | goto ioctl_error; | ||
1365 | } | ||
1360 | 1366 | ||
1361 | if (!try_module_get(dvbdev->adapter->module)) | 1367 | if (!try_module_get(dvbdev->adapter->module)) { |
1362 | return -EPERM; | 1368 | ret = -EPERM; |
1369 | goto ioctl_error; | ||
1370 | } | ||
1363 | 1371 | ||
1364 | result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype); | 1372 | result=dvb_net_add_if(dvbnet, dvbnetif->pid, dvbnetif->feedtype); |
1365 | if (result<0) { | 1373 | if (result<0) { |
1366 | module_put(dvbdev->adapter->module); | 1374 | module_put(dvbdev->adapter->module); |
1367 | return result; | 1375 | ret = result; |
1376 | goto ioctl_error; | ||
1368 | } | 1377 | } |
1369 | dvbnetif->if_num=result; | 1378 | dvbnetif->if_num=result; |
1370 | break; | 1379 | break; |
@@ -1376,8 +1385,10 @@ static int dvb_net_do_ioctl(struct file *file, | |||
1376 | struct dvb_net_if *dvbnetif = parg; | 1385 | struct dvb_net_if *dvbnetif = parg; |
1377 | 1386 | ||
1378 | if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || | 1387 | if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || |
1379 | !dvbnet->state[dvbnetif->if_num]) | 1388 | !dvbnet->state[dvbnetif->if_num]) { |
1380 | return -EINVAL; | 1389 | ret = -EINVAL; |
1390 | goto ioctl_error; | ||
1391 | } | ||
1381 | 1392 | ||
1382 | netdev = dvbnet->device[dvbnetif->if_num]; | 1393 | netdev = dvbnet->device[dvbnetif->if_num]; |
1383 | 1394 | ||
@@ -1388,16 +1399,18 @@ static int dvb_net_do_ioctl(struct file *file, | |||
1388 | } | 1399 | } |
1389 | case NET_REMOVE_IF: | 1400 | case NET_REMOVE_IF: |
1390 | { | 1401 | { |
1391 | int ret; | 1402 | if (!capable(CAP_SYS_ADMIN)) { |
1392 | 1403 | ret = -EPERM; | |
1393 | if (!capable(CAP_SYS_ADMIN)) | 1404 | goto ioctl_error; |
1394 | return -EPERM; | 1405 | } |
1395 | if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) | 1406 | if ((unsigned long) parg >= DVB_NET_DEVICES_MAX) { |
1396 | return -EINVAL; | 1407 | ret = -EINVAL; |
1408 | goto ioctl_error; | ||
1409 | } | ||
1397 | ret = dvb_net_remove_if(dvbnet, (unsigned long) parg); | 1410 | ret = dvb_net_remove_if(dvbnet, (unsigned long) parg); |
1398 | if (!ret) | 1411 | if (!ret) |
1399 | module_put(dvbdev->adapter->module); | 1412 | module_put(dvbdev->adapter->module); |
1400 | return ret; | 1413 | break; |
1401 | } | 1414 | } |
1402 | 1415 | ||
1403 | /* binary compatibility cruft */ | 1416 | /* binary compatibility cruft */ |
@@ -1406,16 +1419,21 @@ static int dvb_net_do_ioctl(struct file *file, | |||
1406 | struct __dvb_net_if_old *dvbnetif = parg; | 1419 | struct __dvb_net_if_old *dvbnetif = parg; |
1407 | int result; | 1420 | int result; |
1408 | 1421 | ||
1409 | if (!capable(CAP_SYS_ADMIN)) | 1422 | if (!capable(CAP_SYS_ADMIN)) { |
1410 | return -EPERM; | 1423 | ret = -EPERM; |
1424 | goto ioctl_error; | ||
1425 | } | ||
1411 | 1426 | ||
1412 | if (!try_module_get(dvbdev->adapter->module)) | 1427 | if (!try_module_get(dvbdev->adapter->module)) { |
1413 | return -EPERM; | 1428 | ret = -EPERM; |
1429 | goto ioctl_error; | ||
1430 | } | ||
1414 | 1431 | ||
1415 | result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE); | 1432 | result=dvb_net_add_if(dvbnet, dvbnetif->pid, DVB_NET_FEEDTYPE_MPE); |
1416 | if (result<0) { | 1433 | if (result<0) { |
1417 | module_put(dvbdev->adapter->module); | 1434 | module_put(dvbdev->adapter->module); |
1418 | return result; | 1435 | ret = result; |
1436 | goto ioctl_error; | ||
1419 | } | 1437 | } |
1420 | dvbnetif->if_num=result; | 1438 | dvbnetif->if_num=result; |
1421 | break; | 1439 | break; |
@@ -1427,8 +1445,10 @@ static int dvb_net_do_ioctl(struct file *file, | |||
1427 | struct __dvb_net_if_old *dvbnetif = parg; | 1445 | struct __dvb_net_if_old *dvbnetif = parg; |
1428 | 1446 | ||
1429 | if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || | 1447 | if (dvbnetif->if_num >= DVB_NET_DEVICES_MAX || |
1430 | !dvbnet->state[dvbnetif->if_num]) | 1448 | !dvbnet->state[dvbnetif->if_num]) { |
1431 | return -EINVAL; | 1449 | ret = -EINVAL; |
1450 | goto ioctl_error; | ||
1451 | } | ||
1432 | 1452 | ||
1433 | netdev = dvbnet->device[dvbnetif->if_num]; | 1453 | netdev = dvbnet->device[dvbnetif->if_num]; |
1434 | 1454 | ||
@@ -1437,9 +1457,13 @@ static int dvb_net_do_ioctl(struct file *file, | |||
1437 | break; | 1457 | break; |
1438 | } | 1458 | } |
1439 | default: | 1459 | default: |
1440 | return -ENOTTY; | 1460 | ret = -ENOTTY; |
1461 | break; | ||
1441 | } | 1462 | } |
1442 | return 0; | 1463 | |
1464 | ioctl_error: | ||
1465 | mutex_unlock(&dvbnet->ioctl_mutex); | ||
1466 | return ret; | ||
1443 | } | 1467 | } |
1444 | 1468 | ||
1445 | static long dvb_net_ioctl(struct file *file, | 1469 | static long dvb_net_ioctl(struct file *file, |
@@ -1505,6 +1529,7 @@ int dvb_net_init (struct dvb_adapter *adap, struct dvb_net *dvbnet, | |||
1505 | { | 1529 | { |
1506 | int i; | 1530 | int i; |
1507 | 1531 | ||
1532 | mutex_init(&dvbnet->ioctl_mutex); | ||
1508 | dvbnet->demux = dmx; | 1533 | dvbnet->demux = dmx; |
1509 | 1534 | ||
1510 | for (i=0; i<DVB_NET_DEVICES_MAX; i++) | 1535 | 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 { | |||
40 | int state[DVB_NET_DEVICES_MAX]; | 40 | int state[DVB_NET_DEVICES_MAX]; |
41 | unsigned int exit:1; | 41 | unsigned int exit:1; |
42 | struct dmx_demux *demux; | 42 | struct dmx_demux *demux; |
43 | struct mutex ioctl_mutex; | ||
43 | }; | 44 | }; |
44 | 45 | ||
45 | void dvb_net_release(struct dvb_net *); | 46 | 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, | |||
418 | } | 418 | } |
419 | 419 | ||
420 | /* call driver */ | 420 | /* call driver */ |
421 | mutex_lock(&dvbdev_mutex); | ||
422 | if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD) | 421 | if ((err = func(file, cmd, parg)) == -ENOIOCTLCMD) |
423 | err = -ENOTTY; | 422 | err = -ENOTTY; |
424 | mutex_unlock(&dvbdev_mutex); | ||
425 | 423 | ||
426 | if (err < 0) | 424 | if (err < 0) |
427 | goto out; | 425 | goto out; |
diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index 4bd8bd56befc..2f54e2b741f6 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c | |||
@@ -2723,6 +2723,8 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, | |||
2723 | if (ret < 0) | 2723 | if (ret < 0) |
2724 | goto err_av7110_exit_v4l_12; | 2724 | goto err_av7110_exit_v4l_12; |
2725 | 2725 | ||
2726 | mutex_init(&av7110->ioctl_mutex); | ||
2727 | |||
2726 | #if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) | 2728 | #if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) |
2727 | av7110_ir_init(av7110); | 2729 | av7110_ir_init(av7110); |
2728 | #endif | 2730 | #endif |
diff --git a/drivers/media/pci/ttpci/av7110.h b/drivers/media/pci/ttpci/av7110.h index a378662b1dcf..ef3d9606b269 100644 --- a/drivers/media/pci/ttpci/av7110.h +++ b/drivers/media/pci/ttpci/av7110.h | |||
@@ -271,6 +271,8 @@ struct av7110 { | |||
271 | struct dvb_frontend* fe; | 271 | struct dvb_frontend* fe; |
272 | fe_status_t fe_status; | 272 | fe_status_t fe_status; |
273 | 273 | ||
274 | struct mutex ioctl_mutex; | ||
275 | |||
274 | /* crash recovery */ | 276 | /* crash recovery */ |
275 | void (*recover)(struct av7110* av7110); | 277 | void (*recover)(struct av7110* av7110); |
276 | fe_sec_voltage_t saved_voltage; | 278 | fe_sec_voltage_t saved_voltage; |
diff --git a/drivers/media/pci/ttpci/av7110_av.c b/drivers/media/pci/ttpci/av7110_av.c index 952b33dbac4f..301029ca4535 100644 --- a/drivers/media/pci/ttpci/av7110_av.c +++ b/drivers/media/pci/ttpci/av7110_av.c | |||
@@ -1109,6 +1109,9 @@ static int dvb_video_ioctl(struct file *file, | |||
1109 | } | 1109 | } |
1110 | } | 1110 | } |
1111 | 1111 | ||
1112 | if (mutex_lock_interruptible(&av7110->ioctl_mutex)) | ||
1113 | return -ERESTARTSYS; | ||
1114 | |||
1112 | switch (cmd) { | 1115 | switch (cmd) { |
1113 | case VIDEO_STOP: | 1116 | case VIDEO_STOP: |
1114 | av7110->videostate.play_state = VIDEO_STOPPED; | 1117 | av7110->videostate.play_state = VIDEO_STOPPED; |
@@ -1297,6 +1300,7 @@ static int dvb_video_ioctl(struct file *file, | |||
1297 | break; | 1300 | break; |
1298 | } | 1301 | } |
1299 | 1302 | ||
1303 | mutex_unlock(&av7110->ioctl_mutex); | ||
1300 | return ret; | 1304 | return ret; |
1301 | } | 1305 | } |
1302 | 1306 | ||
@@ -1314,6 +1318,9 @@ static int dvb_audio_ioctl(struct file *file, | |||
1314 | (cmd != AUDIO_GET_STATUS)) | 1318 | (cmd != AUDIO_GET_STATUS)) |
1315 | return -EPERM; | 1319 | return -EPERM; |
1316 | 1320 | ||
1321 | if (mutex_lock_interruptible(&av7110->ioctl_mutex)) | ||
1322 | return -ERESTARTSYS; | ||
1323 | |||
1317 | switch (cmd) { | 1324 | switch (cmd) { |
1318 | case AUDIO_STOP: | 1325 | case AUDIO_STOP: |
1319 | if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) | 1326 | if (av7110->audiostate.stream_source == AUDIO_SOURCE_MEMORY) |
@@ -1442,6 +1449,7 @@ static int dvb_audio_ioctl(struct file *file, | |||
1442 | ret = -ENOIOCTLCMD; | 1449 | ret = -ENOIOCTLCMD; |
1443 | } | 1450 | } |
1444 | 1451 | ||
1452 | mutex_unlock(&av7110->ioctl_mutex); | ||
1445 | return ret; | 1453 | return ret; |
1446 | } | 1454 | } |
1447 | 1455 | ||
diff --git a/drivers/media/pci/ttpci/av7110_ca.c b/drivers/media/pci/ttpci/av7110_ca.c index 9fc1dd0ba4c3..a6079b90252a 100644 --- a/drivers/media/pci/ttpci/av7110_ca.c +++ b/drivers/media/pci/ttpci/av7110_ca.c | |||
@@ -253,12 +253,17 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) | |||
253 | struct dvb_device *dvbdev = file->private_data; | 253 | struct dvb_device *dvbdev = file->private_data; |
254 | struct av7110 *av7110 = dvbdev->priv; | 254 | struct av7110 *av7110 = dvbdev->priv; |
255 | unsigned long arg = (unsigned long) parg; | 255 | unsigned long arg = (unsigned long) parg; |
256 | int ret = 0; | ||
256 | 257 | ||
257 | dprintk(8, "av7110:%p\n",av7110); | 258 | dprintk(8, "av7110:%p\n",av7110); |
258 | 259 | ||
260 | if (mutex_lock_interruptible(&av7110->ioctl_mutex)) | ||
261 | return -ERESTARTSYS; | ||
262 | |||
259 | switch (cmd) { | 263 | switch (cmd) { |
260 | case CA_RESET: | 264 | case CA_RESET: |
261 | return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]); | 265 | ret = ci_ll_reset(&av7110->ci_wbuffer, file, arg, |
266 | &av7110->ci_slot[0]); | ||
262 | break; | 267 | break; |
263 | case CA_GET_CAP: | 268 | case CA_GET_CAP: |
264 | { | 269 | { |
@@ -277,8 +282,10 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) | |||
277 | { | 282 | { |
278 | ca_slot_info_t *info=(ca_slot_info_t *)parg; | 283 | ca_slot_info_t *info=(ca_slot_info_t *)parg; |
279 | 284 | ||
280 | if (info->num < 0 || info->num > 1) | 285 | if (info->num < 0 || info->num > 1) { |
286 | mutex_unlock(&av7110->ioctl_mutex); | ||
281 | return -EINVAL; | 287 | return -EINVAL; |
288 | } | ||
282 | av7110->ci_slot[info->num].num = info->num; | 289 | av7110->ci_slot[info->num].num = info->num; |
283 | av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? | 290 | av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? |
284 | CA_CI_LINK : CA_CI; | 291 | CA_CI_LINK : CA_CI; |
@@ -306,10 +313,10 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) | |||
306 | { | 313 | { |
307 | ca_descr_t *descr = (ca_descr_t*) parg; | 314 | ca_descr_t *descr = (ca_descr_t*) parg; |
308 | 315 | ||
309 | if (descr->index >= 16) | 316 | if (descr->index >= 16 || descr->parity > 1) { |
310 | return -EINVAL; | 317 | mutex_unlock(&av7110->ioctl_mutex); |
311 | if (descr->parity > 1) | ||
312 | return -EINVAL; | 318 | return -EINVAL; |
319 | } | ||
313 | av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, | 320 | av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5, |
314 | (descr->index<<8)|descr->parity, | 321 | (descr->index<<8)|descr->parity, |
315 | (descr->cw[0]<<8)|descr->cw[1], | 322 | (descr->cw[0]<<8)|descr->cw[1], |
@@ -320,9 +327,12 @@ static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) | |||
320 | } | 327 | } |
321 | 328 | ||
322 | default: | 329 | default: |
323 | return -EINVAL; | 330 | ret = -EINVAL; |
331 | break; | ||
324 | } | 332 | } |
325 | return 0; | 333 | |
334 | mutex_unlock(&av7110->ioctl_mutex); | ||
335 | return ret; | ||
326 | } | 336 | } |
327 | 337 | ||
328 | static ssize_t dvb_ca_write(struct file *file, const char __user *buf, | 338 | static ssize_t dvb_ca_write(struct file *file, const char __user *buf, |