diff options
author | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-03-05 09:21:07 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <m.chehab@samsung.com> | 2014-03-11 05:56:46 -0400 |
commit | 47677e51e2a4040c204d7971a5103592600185b1 (patch) | |
tree | 58196e42f5ad37ee12333a69593d444f0d1e10de | |
parent | 88e4fcda55e07278fcf5f6eea684685ffc0633e2 (diff) |
[media] em28xx: Only deallocate struct em28xx after finishing all extensions
We can't free struct em28xx while one of the extensions is still
using it.
So, add a kref() to control it, freeing it only after the
extensions fini calls.
Reviewed-by: Frank Schäfer <fschaefer.oss@googlemail.com>
Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
-rw-r--r-- | drivers/media/usb/em28xx/em28xx-audio.c | 7 | ||||
-rw-r--r-- | drivers/media/usb/em28xx/em28xx-cards.c | 32 | ||||
-rw-r--r-- | drivers/media/usb/em28xx/em28xx-dvb.c | 5 | ||||
-rw-r--r-- | drivers/media/usb/em28xx/em28xx-input.c | 8 | ||||
-rw-r--r-- | drivers/media/usb/em28xx/em28xx-video.c | 15 | ||||
-rw-r--r-- | drivers/media/usb/em28xx/em28xx.h | 8 |
6 files changed, 56 insertions, 19 deletions
diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 0f5b6f3e7a3f..f75c0a5494d6 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c | |||
@@ -301,6 +301,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) | |||
301 | goto err; | 301 | goto err; |
302 | } | 302 | } |
303 | 303 | ||
304 | kref_get(&dev->ref); | ||
304 | dev->adev.users++; | 305 | dev->adev.users++; |
305 | mutex_unlock(&dev->lock); | 306 | mutex_unlock(&dev->lock); |
306 | 307 | ||
@@ -341,6 +342,7 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) | |||
341 | substream->runtime->dma_area = NULL; | 342 | substream->runtime->dma_area = NULL; |
342 | } | 343 | } |
343 | mutex_unlock(&dev->lock); | 344 | mutex_unlock(&dev->lock); |
345 | kref_put(&dev->ref, em28xx_free_device); | ||
344 | 346 | ||
345 | return 0; | 347 | return 0; |
346 | } | 348 | } |
@@ -895,6 +897,8 @@ static int em28xx_audio_init(struct em28xx *dev) | |||
895 | 897 | ||
896 | em28xx_info("Binding audio extension\n"); | 898 | em28xx_info("Binding audio extension\n"); |
897 | 899 | ||
900 | kref_get(&dev->ref); | ||
901 | |||
898 | printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " | 902 | printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " |
899 | "Rechberger\n"); | 903 | "Rechberger\n"); |
900 | printk(KERN_INFO | 904 | printk(KERN_INFO |
@@ -967,7 +971,7 @@ static int em28xx_audio_fini(struct em28xx *dev) | |||
967 | if (dev == NULL) | 971 | if (dev == NULL) |
968 | return 0; | 972 | return 0; |
969 | 973 | ||
970 | if (dev->has_alsa_audio != 1) { | 974 | if (!dev->has_alsa_audio) { |
971 | /* This device does not support the extension (in this case | 975 | /* This device does not support the extension (in this case |
972 | the device is expecting the snd-usb-audio module or | 976 | the device is expecting the snd-usb-audio module or |
973 | doesn't have analog audio support at all) */ | 977 | doesn't have analog audio support at all) */ |
@@ -986,6 +990,7 @@ static int em28xx_audio_fini(struct em28xx *dev) | |||
986 | dev->adev.sndcard = NULL; | 990 | dev->adev.sndcard = NULL; |
987 | } | 991 | } |
988 | 992 | ||
993 | kref_put(&dev->ref, em28xx_free_device); | ||
989 | return 0; | 994 | return 0; |
990 | } | 995 | } |
991 | 996 | ||
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 2fb300e882f0..e7ec3b7866f1 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c | |||
@@ -2939,7 +2939,7 @@ static void flush_request_modules(struct em28xx *dev) | |||
2939 | * unregisters the v4l2,i2c and usb devices | 2939 | * unregisters the v4l2,i2c and usb devices |
2940 | * called when the device gets disconnected or at module unload | 2940 | * called when the device gets disconnected or at module unload |
2941 | */ | 2941 | */ |
2942 | void em28xx_release_resources(struct em28xx *dev) | 2942 | static void em28xx_release_resources(struct em28xx *dev) |
2943 | { | 2943 | { |
2944 | /*FIXME: I2C IR should be disconnected */ | 2944 | /*FIXME: I2C IR should be disconnected */ |
2945 | 2945 | ||
@@ -2956,7 +2956,27 @@ void em28xx_release_resources(struct em28xx *dev) | |||
2956 | 2956 | ||
2957 | mutex_unlock(&dev->lock); | 2957 | mutex_unlock(&dev->lock); |
2958 | }; | 2958 | }; |
2959 | EXPORT_SYMBOL_GPL(em28xx_release_resources); | 2959 | |
2960 | /** | ||
2961 | * em28xx_free_device() - Free em28xx device | ||
2962 | * | ||
2963 | * @ref: struct kref for em28xx device | ||
2964 | * | ||
2965 | * This is called when all extensions and em28xx core unregisters a device | ||
2966 | */ | ||
2967 | void em28xx_free_device(struct kref *ref) | ||
2968 | { | ||
2969 | struct em28xx *dev = kref_to_dev(ref); | ||
2970 | |||
2971 | em28xx_info("Freeing device\n"); | ||
2972 | |||
2973 | if (!dev->disconnected) | ||
2974 | em28xx_release_resources(dev); | ||
2975 | |||
2976 | kfree(dev->alt_max_pkt_size_isoc); | ||
2977 | kfree(dev); | ||
2978 | } | ||
2979 | EXPORT_SYMBOL_GPL(em28xx_free_device); | ||
2960 | 2980 | ||
2961 | /* | 2981 | /* |
2962 | * em28xx_init_dev() | 2982 | * em28xx_init_dev() |
@@ -3409,6 +3429,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, | |||
3409 | dev->dvb_xfer_bulk ? "bulk" : "isoc"); | 3429 | dev->dvb_xfer_bulk ? "bulk" : "isoc"); |
3410 | } | 3430 | } |
3411 | 3431 | ||
3432 | kref_init(&dev->ref); | ||
3433 | |||
3412 | request_modules(dev); | 3434 | request_modules(dev); |
3413 | 3435 | ||
3414 | /* Should be the last thing to do, to avoid newer udev's to | 3436 | /* Should be the last thing to do, to avoid newer udev's to |
@@ -3453,11 +3475,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) | |||
3453 | em28xx_close_extension(dev); | 3475 | em28xx_close_extension(dev); |
3454 | 3476 | ||
3455 | em28xx_release_resources(dev); | 3477 | em28xx_release_resources(dev); |
3456 | 3478 | kref_put(&dev->ref, em28xx_free_device); | |
3457 | if (!dev->users) { | ||
3458 | kfree(dev->alt_max_pkt_size_isoc); | ||
3459 | kfree(dev); | ||
3460 | } | ||
3461 | } | 3479 | } |
3462 | 3480 | ||
3463 | static int em28xx_usb_suspend(struct usb_interface *interface, | 3481 | static int em28xx_usb_suspend(struct usb_interface *interface, |
diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index d4986bdfbdc3..cacdca3a3412 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c | |||
@@ -1043,7 +1043,6 @@ static int em28xx_dvb_init(struct em28xx *dev) | |||
1043 | em28xx_info("Binding DVB extension\n"); | 1043 | em28xx_info("Binding DVB extension\n"); |
1044 | 1044 | ||
1045 | dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); | 1045 | dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); |
1046 | |||
1047 | if (dvb == NULL) { | 1046 | if (dvb == NULL) { |
1048 | em28xx_info("em28xx_dvb: memory allocation failed\n"); | 1047 | em28xx_info("em28xx_dvb: memory allocation failed\n"); |
1049 | return -ENOMEM; | 1048 | return -ENOMEM; |
@@ -1521,6 +1520,9 @@ static int em28xx_dvb_init(struct em28xx *dev) | |||
1521 | dvb->adapter.mfe_shared = mfe_shared; | 1520 | dvb->adapter.mfe_shared = mfe_shared; |
1522 | 1521 | ||
1523 | em28xx_info("DVB extension successfully initialized\n"); | 1522 | em28xx_info("DVB extension successfully initialized\n"); |
1523 | |||
1524 | kref_get(&dev->ref); | ||
1525 | |||
1524 | ret: | 1526 | ret: |
1525 | em28xx_set_mode(dev, EM28XX_SUSPEND); | 1527 | em28xx_set_mode(dev, EM28XX_SUSPEND); |
1526 | mutex_unlock(&dev->lock); | 1528 | mutex_unlock(&dev->lock); |
@@ -1577,6 +1579,7 @@ static int em28xx_dvb_fini(struct em28xx *dev) | |||
1577 | em28xx_unregister_dvb(dvb); | 1579 | em28xx_unregister_dvb(dvb); |
1578 | kfree(dvb); | 1580 | kfree(dvb); |
1579 | dev->dvb = NULL; | 1581 | dev->dvb = NULL; |
1582 | kref_put(&dev->ref, em28xx_free_device); | ||
1580 | } | 1583 | } |
1581 | 1584 | ||
1582 | return 0; | 1585 | return 0; |
diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index 47a2c1dcccbf..2a9bf667f208 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c | |||
@@ -676,6 +676,8 @@ static int em28xx_ir_init(struct em28xx *dev) | |||
676 | return 0; | 676 | return 0; |
677 | } | 677 | } |
678 | 678 | ||
679 | kref_get(&dev->ref); | ||
680 | |||
679 | if (dev->board.buttons) | 681 | if (dev->board.buttons) |
680 | em28xx_init_buttons(dev); | 682 | em28xx_init_buttons(dev); |
681 | 683 | ||
@@ -816,7 +818,7 @@ static int em28xx_ir_fini(struct em28xx *dev) | |||
816 | 818 | ||
817 | /* skip detach on non attached boards */ | 819 | /* skip detach on non attached boards */ |
818 | if (!ir) | 820 | if (!ir) |
819 | return 0; | 821 | goto ref_put; |
820 | 822 | ||
821 | if (ir->rc) | 823 | if (ir->rc) |
822 | rc_unregister_device(ir->rc); | 824 | rc_unregister_device(ir->rc); |
@@ -824,6 +826,10 @@ static int em28xx_ir_fini(struct em28xx *dev) | |||
824 | /* done */ | 826 | /* done */ |
825 | kfree(ir); | 827 | kfree(ir); |
826 | dev->ir = NULL; | 828 | dev->ir = NULL; |
829 | |||
830 | ref_put: | ||
831 | kref_put(&dev->ref, em28xx_free_device); | ||
832 | |||
827 | return 0; | 833 | return 0; |
828 | } | 834 | } |
829 | 835 | ||
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 13466c47023c..0856e5d367b6 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c | |||
@@ -1837,7 +1837,6 @@ static int em28xx_v4l2_open(struct file *filp) | |||
1837 | video_device_node_name(vdev), v4l2_type_names[fh_type], | 1837 | video_device_node_name(vdev), v4l2_type_names[fh_type], |
1838 | dev->users); | 1838 | dev->users); |
1839 | 1839 | ||
1840 | |||
1841 | if (mutex_lock_interruptible(&dev->lock)) | 1840 | if (mutex_lock_interruptible(&dev->lock)) |
1842 | return -ERESTARTSYS; | 1841 | return -ERESTARTSYS; |
1843 | fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); | 1842 | fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL); |
@@ -1869,6 +1868,7 @@ static int em28xx_v4l2_open(struct file *filp) | |||
1869 | v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); | 1868 | v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); |
1870 | } | 1869 | } |
1871 | 1870 | ||
1871 | kref_get(&dev->ref); | ||
1872 | dev->users++; | 1872 | dev->users++; |
1873 | 1873 | ||
1874 | mutex_unlock(&dev->lock); | 1874 | mutex_unlock(&dev->lock); |
@@ -1926,9 +1926,8 @@ static int em28xx_v4l2_fini(struct em28xx *dev) | |||
1926 | dev->clk = NULL; | 1926 | dev->clk = NULL; |
1927 | } | 1927 | } |
1928 | 1928 | ||
1929 | if (dev->users) | ||
1930 | em28xx_warn("Device is open ! Memory deallocation is deferred on last close.\n"); | ||
1931 | mutex_unlock(&dev->lock); | 1929 | mutex_unlock(&dev->lock); |
1930 | kref_put(&dev->ref, em28xx_free_device); | ||
1932 | 1931 | ||
1933 | return 0; | 1932 | return 0; |
1934 | } | 1933 | } |
@@ -1976,11 +1975,9 @@ static int em28xx_v4l2_close(struct file *filp) | |||
1976 | mutex_lock(&dev->lock); | 1975 | mutex_lock(&dev->lock); |
1977 | 1976 | ||
1978 | if (dev->users == 1) { | 1977 | if (dev->users == 1) { |
1979 | /* free the remaining resources if device is disconnected */ | 1978 | /* No sense to try to write to the device */ |
1980 | if (dev->disconnected) { | 1979 | if (dev->disconnected) |
1981 | kfree(dev->alt_max_pkt_size_isoc); | ||
1982 | goto exit; | 1980 | goto exit; |
1983 | } | ||
1984 | 1981 | ||
1985 | /* Save some power by putting tuner to sleep */ | 1982 | /* Save some power by putting tuner to sleep */ |
1986 | v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); | 1983 | v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); |
@@ -2001,6 +1998,8 @@ static int em28xx_v4l2_close(struct file *filp) | |||
2001 | exit: | 1998 | exit: |
2002 | dev->users--; | 1999 | dev->users--; |
2003 | mutex_unlock(&dev->lock); | 2000 | mutex_unlock(&dev->lock); |
2001 | kref_put(&dev->ref, em28xx_free_device); | ||
2002 | |||
2004 | return 0; | 2003 | return 0; |
2005 | } | 2004 | } |
2006 | 2005 | ||
@@ -2515,6 +2514,8 @@ static int em28xx_v4l2_init(struct em28xx *dev) | |||
2515 | 2514 | ||
2516 | em28xx_info("V4L2 extension successfully initialized\n"); | 2515 | em28xx_info("V4L2 extension successfully initialized\n"); |
2517 | 2516 | ||
2517 | kref_get(&dev->ref); | ||
2518 | |||
2518 | mutex_unlock(&dev->lock); | 2519 | mutex_unlock(&dev->lock); |
2519 | return 0; | 2520 | return 0; |
2520 | 2521 | ||
diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 9e44f5bfc48b..2051fc9fb932 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h | |||
@@ -32,6 +32,7 @@ | |||
32 | #include <linux/workqueue.h> | 32 | #include <linux/workqueue.h> |
33 | #include <linux/i2c.h> | 33 | #include <linux/i2c.h> |
34 | #include <linux/mutex.h> | 34 | #include <linux/mutex.h> |
35 | #include <linux/kref.h> | ||
35 | #include <linux/videodev2.h> | 36 | #include <linux/videodev2.h> |
36 | 37 | ||
37 | #include <media/videobuf2-vmalloc.h> | 38 | #include <media/videobuf2-vmalloc.h> |
@@ -536,9 +537,10 @@ struct em28xx_i2c_bus { | |||
536 | enum em28xx_i2c_algo_type algo_type; | 537 | enum em28xx_i2c_algo_type algo_type; |
537 | }; | 538 | }; |
538 | 539 | ||
539 | |||
540 | /* main device struct */ | 540 | /* main device struct */ |
541 | struct em28xx { | 541 | struct em28xx { |
542 | struct kref ref; | ||
543 | |||
542 | /* generic device properties */ | 544 | /* generic device properties */ |
543 | char name[30]; /* name (including minor) of the device */ | 545 | char name[30]; /* name (including minor) of the device */ |
544 | int model; /* index in the device_data struct */ | 546 | int model; /* index in the device_data struct */ |
@@ -710,6 +712,8 @@ struct em28xx { | |||
710 | struct em28xx_dvb *dvb; | 712 | struct em28xx_dvb *dvb; |
711 | }; | 713 | }; |
712 | 714 | ||
715 | #define kref_to_dev(d) container_of(d, struct em28xx, ref) | ||
716 | |||
713 | struct em28xx_ops { | 717 | struct em28xx_ops { |
714 | struct list_head next; | 718 | struct list_head next; |
715 | char *name; | 719 | char *name; |
@@ -771,7 +775,7 @@ extern struct em28xx_board em28xx_boards[]; | |||
771 | extern struct usb_device_id em28xx_id_table[]; | 775 | extern struct usb_device_id em28xx_id_table[]; |
772 | int em28xx_tuner_callback(void *ptr, int component, int command, int arg); | 776 | int em28xx_tuner_callback(void *ptr, int component, int command, int arg); |
773 | void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl); | 777 | void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl); |
774 | void em28xx_release_resources(struct em28xx *dev); | 778 | void em28xx_free_device(struct kref *ref); |
775 | 779 | ||
776 | /* Provided by em28xx-camera.c */ | 780 | /* Provided by em28xx-camera.c */ |
777 | int em28xx_detect_sensor(struct em28xx *dev); | 781 | int em28xx_detect_sensor(struct em28xx *dev); |