aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIlija Hadzic <ihadzic@research.bell-labs.com>2013-10-29 11:09:46 -0400
committerDave Airlie <airlied@redhat.com>2013-11-05 23:27:51 -0500
commit02ee4e9455456a9bc9ee94d30eef78fc610922c1 (patch)
tree5a80003498bc7753902e985efc2ac46fc2a7dc10
parent48b1f5ddaafa341bebbaac2a5fbaaa89b5b16281 (diff)
drm: eliminate bit-copy restoration of crtc
Bit-copying restoration of CRTC structure in failure-recovery path of drm_crtc_helper_set_config function evokes a subtle and rare, but very dangerous, corruption of CRTC mutex structure. Namely, if drm_crtc_helper_set_config takes the path under 'fail:' label *and* some other process has attempted to grab the crtc mutex (and got blocked), restoring the CRTC structure by bit-copying it will overwrite the CRTC mutex state and the waiters list pointer within the mutex structure. Consequently the blocked process will never be scheduled. This patch fixes the issue by eliminating the bit-copy restoration. The elimination is possible because previous patches have cleaned up the resoration path so that only the fields touched by the drm_crtc_helper_set_config function are saved and restored if necessary. Signed-off-by: Ilija Hadzic <ihadzic@research.bell-labs.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c33
1 files changed, 8 insertions, 25 deletions
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 930870728a9c..01361aba033b 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -604,7 +604,7 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
604int drm_crtc_helper_set_config(struct drm_mode_set *set) 604int drm_crtc_helper_set_config(struct drm_mode_set *set)
605{ 605{
606 struct drm_device *dev; 606 struct drm_device *dev;
607 struct drm_crtc *save_crtcs, *new_crtc, *crtc; 607 struct drm_crtc *new_crtc;
608 struct drm_encoder *save_encoders, *new_encoder, *encoder; 608 struct drm_encoder *save_encoders, *new_encoder, *encoder;
609 bool mode_changed = false; /* if true do a full mode set */ 609 bool mode_changed = false; /* if true do a full mode set */
610 bool fb_changed = false; /* if true and !mode_changed just do a flip */ 610 bool fb_changed = false; /* if true and !mode_changed just do a flip */
@@ -641,38 +641,28 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
641 641
642 dev = set->crtc->dev; 642 dev = set->crtc->dev;
643 643
644 /* Allocate space for the backup of all (non-pointer) crtc, encoder and 644 /*
645 * connector data. */ 645 * Allocate space for the backup of all (non-pointer) encoder and
646 save_crtcs = kzalloc(dev->mode_config.num_crtc * 646 * connector data.
647 sizeof(struct drm_crtc), GFP_KERNEL); 647 */
648 if (!save_crtcs)
649 return -ENOMEM;
650
651 save_encoders = kzalloc(dev->mode_config.num_encoder * 648 save_encoders = kzalloc(dev->mode_config.num_encoder *
652 sizeof(struct drm_encoder), GFP_KERNEL); 649 sizeof(struct drm_encoder), GFP_KERNEL);
653 if (!save_encoders) { 650 if (!save_encoders)
654 kfree(save_crtcs);
655 return -ENOMEM; 651 return -ENOMEM;
656 }
657 652
658 save_connectors = kzalloc(dev->mode_config.num_connector * 653 save_connectors = kzalloc(dev->mode_config.num_connector *
659 sizeof(struct drm_connector), GFP_KERNEL); 654 sizeof(struct drm_connector), GFP_KERNEL);
660 if (!save_connectors) { 655 if (!save_connectors) {
661 kfree(save_crtcs);
662 kfree(save_encoders); 656 kfree(save_encoders);
663 return -ENOMEM; 657 return -ENOMEM;
664 } 658 }
665 659
666 /* Copy data. Note that driver private data is not affected. 660 /*
661 * Copy data. Note that driver private data is not affected.
667 * Should anything bad happen only the expected state is 662 * Should anything bad happen only the expected state is
668 * restored, not the drivers personal bookkeeping. 663 * restored, not the drivers personal bookkeeping.
669 */ 664 */
670 count = 0; 665 count = 0;
671 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
672 save_crtcs[count++] = *crtc;
673 }
674
675 count = 0;
676 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 666 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
677 save_encoders[count++] = *encoder; 667 save_encoders[count++] = *encoder;
678 } 668 }
@@ -833,17 +823,11 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
833 823
834 kfree(save_connectors); 824 kfree(save_connectors);
835 kfree(save_encoders); 825 kfree(save_encoders);
836 kfree(save_crtcs);
837 return 0; 826 return 0;
838 827
839fail: 828fail:
840 /* Restore all previous data. */ 829 /* Restore all previous data. */
841 count = 0; 830 count = 0;
842 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
843 *crtc = save_crtcs[count++];
844 }
845
846 count = 0;
847 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { 831 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
848 *encoder = save_encoders[count++]; 832 *encoder = save_encoders[count++];
849 } 833 }
@@ -861,7 +845,6 @@ fail:
861 845
862 kfree(save_connectors); 846 kfree(save_connectors);
863 kfree(save_encoders); 847 kfree(save_encoders);
864 kfree(save_crtcs);
865 return ret; 848 return ret;
866} 849}
867EXPORT_SYMBOL(drm_crtc_helper_set_config); 850EXPORT_SYMBOL(drm_crtc_helper_set_config);