Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / devfreq: exynos4_bus: honor RCU lock usage

OPP pointers cannot be expected to be valid beyond the boundary
of rcu_read_lock and rcu_read_unlock. Unfortunately, the current
exynos4 busfreq driver does not honor the usage constraint and stores
the OPP pointer in struct busfreq_data. This could potentially
become invalid later such as: across devfreq opp change decisions,
resulting in unpredictable behavior.

To fix this, we introduce a busfreq specific busfreq_opp_info
structure which is used to handle OPP information. OPP information
is de-referenced to voltage and frequency pairs as needed into
busfreq_opp_info structure and used as needed.

Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Nishanth Menon and committed by
Rafael J. Wysocki
8fa938ac bcb27549

+67 -27
+67 -27
drivers/devfreq/exynos4_bus.c
··· 73 73 #define EX4210_LV_NUM (LV_2 + 1) 74 74 #define EX4x12_LV_NUM (LV_4 + 1) 75 75 76 + /** 77 + * struct busfreq_opp_info - opp information for bus 78 + * @rate: Frequency in hertz 79 + * @volt: Voltage in microvolts corresponding to this OPP 80 + */ 81 + struct busfreq_opp_info { 82 + unsigned long rate; 83 + unsigned long volt; 84 + }; 85 + 76 86 struct busfreq_data { 77 87 enum exynos4_busf_type type; 78 88 struct device *dev; ··· 90 80 bool disabled; 91 81 struct regulator *vdd_int; 92 82 struct regulator *vdd_mif; /* Exynos4412/4212 only */ 93 - struct opp *curr_opp; 83 + struct busfreq_opp_info curr_oppinfo; 94 84 struct exynos4_ppmu dmc[2]; 95 85 96 86 struct notifier_block pm_notifier; ··· 306 296 }; 307 297 308 298 309 - static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp) 299 + static int exynos4210_set_busclk(struct busfreq_data *data, 300 + struct busfreq_opp_info *oppi) 310 301 { 311 302 unsigned int index; 312 303 unsigned int tmp; 313 304 314 305 for (index = LV_0; index < EX4210_LV_NUM; index++) 315 - if (opp_get_freq(opp) == exynos4210_busclk_table[index].clk) 306 + if (oppi->rate == exynos4210_busclk_table[index].clk) 316 307 break; 317 308 318 309 if (index == EX4210_LV_NUM) ··· 372 361 return 0; 373 362 } 374 363 375 - static int exynos4x12_set_busclk(struct busfreq_data *data, struct opp *opp) 364 + static int exynos4x12_set_busclk(struct busfreq_data *data, 365 + struct busfreq_opp_info *oppi) 376 366 { 377 367 unsigned int index; 378 368 unsigned int tmp; 379 369 380 370 for (index = LV_0; index < EX4x12_LV_NUM; index++) 381 - if (opp_get_freq(opp) == exynos4x12_mifclk_table[index].clk) 371 + if (oppi->rate == exynos4x12_mifclk_table[index].clk) 382 372 break; 383 373 384 374 if (index == EX4x12_LV_NUM) ··· 588 576 return -EINVAL; 589 577 } 590 578 591 - static int exynos4_bus_setvolt(struct busfreq_data *data, struct opp *opp, 592 - struct opp *oldopp) 579 + static int exynos4_bus_setvolt(struct busfreq_data *data, 580 + struct busfreq_opp_info *oppi, 581 + struct busfreq_opp_info *oldoppi) 593 582 { 594 583 int err = 0, tmp; 595 - unsigned long volt = opp_get_voltage(opp); 584 + unsigned long volt = oppi->volt; 596 585 597 586 switch (data->type) { 598 587 case TYPE_BUSF_EXYNOS4210: ··· 608 595 if (err) 609 596 break; 610 597 611 - tmp = exynos4x12_get_intspec(opp_get_freq(opp)); 598 + tmp = exynos4x12_get_intspec(oppi->rate); 612 599 if (tmp < 0) { 613 600 err = tmp; 614 601 regulator_set_voltage(data->vdd_mif, 615 - opp_get_voltage(oldopp), 602 + oldoppi->volt, 616 603 MAX_SAFEVOLT); 617 604 break; 618 605 } ··· 622 609 /* Try to recover */ 623 610 if (err) 624 611 regulator_set_voltage(data->vdd_mif, 625 - opp_get_voltage(oldopp), 612 + oldoppi->volt, 626 613 MAX_SAFEVOLT); 627 614 break; 628 615 default: ··· 639 626 struct platform_device *pdev = container_of(dev, struct platform_device, 640 627 dev); 641 628 struct busfreq_data *data = platform_get_drvdata(pdev); 642 - struct opp *opp = devfreq_recommended_opp(dev, _freq, flags); 643 - unsigned long freq = opp_get_freq(opp); 644 - unsigned long old_freq = opp_get_freq(data->curr_opp); 629 + struct opp *opp; 630 + unsigned long freq; 631 + unsigned long old_freq = data->curr_oppinfo.rate; 632 + struct busfreq_opp_info new_oppinfo; 645 633 646 - if (IS_ERR(opp)) 634 + rcu_read_lock(); 635 + opp = devfreq_recommended_opp(dev, _freq, flags); 636 + if (IS_ERR(opp)) { 637 + rcu_read_unlock(); 647 638 return PTR_ERR(opp); 639 + } 640 + new_oppinfo.rate = opp_get_freq(opp); 641 + new_oppinfo.volt = opp_get_voltage(opp); 642 + rcu_read_unlock(); 643 + freq = new_oppinfo.rate; 648 644 649 645 if (old_freq == freq) 650 646 return 0; 651 647 652 - dev_dbg(dev, "targetting %lukHz %luuV\n", freq, opp_get_voltage(opp)); 648 + dev_dbg(dev, "targetting %lukHz %luuV\n", freq, new_oppinfo.volt); 653 649 654 650 mutex_lock(&data->lock); 655 651 ··· 666 644 goto out; 667 645 668 646 if (old_freq < freq) 669 - err = exynos4_bus_setvolt(data, opp, data->curr_opp); 647 + err = exynos4_bus_setvolt(data, &new_oppinfo, 648 + &data->curr_oppinfo); 670 649 if (err) 671 650 goto out; 672 651 673 652 if (old_freq != freq) { 674 653 switch (data->type) { 675 654 case TYPE_BUSF_EXYNOS4210: 676 - err = exynos4210_set_busclk(data, opp); 655 + err = exynos4210_set_busclk(data, &new_oppinfo); 677 656 break; 678 657 case TYPE_BUSF_EXYNOS4x12: 679 - err = exynos4x12_set_busclk(data, opp); 658 + err = exynos4x12_set_busclk(data, &new_oppinfo); 680 659 break; 681 660 default: 682 661 err = -EINVAL; ··· 687 664 goto out; 688 665 689 666 if (old_freq > freq) 690 - err = exynos4_bus_setvolt(data, opp, data->curr_opp); 667 + err = exynos4_bus_setvolt(data, &new_oppinfo, 668 + &data->curr_oppinfo); 691 669 if (err) 692 670 goto out; 693 671 694 - data->curr_opp = opp; 672 + data->curr_oppinfo = new_oppinfo; 695 673 out: 696 674 mutex_unlock(&data->lock); 697 675 return err; ··· 726 702 727 703 exynos4_read_ppmu(data); 728 704 busier_dmc = exynos4_get_busier_dmc(data); 729 - stat->current_frequency = opp_get_freq(data->curr_opp); 705 + stat->current_frequency = data->curr_oppinfo.rate; 730 706 731 707 if (busier_dmc) 732 708 addr = S5P_VA_DMC1; ··· 957 933 struct busfreq_data *data = container_of(this, struct busfreq_data, 958 934 pm_notifier); 959 935 struct opp *opp; 936 + struct busfreq_opp_info new_oppinfo; 960 937 unsigned long maxfreq = ULONG_MAX; 961 938 int err = 0; 962 939 ··· 968 943 969 944 data->disabled = true; 970 945 946 + rcu_read_lock(); 971 947 opp = opp_find_freq_floor(data->dev, &maxfreq); 948 + if (IS_ERR(opp)) { 949 + rcu_read_unlock(); 950 + dev_err(data->dev, "%s: unable to find a min freq\n", 951 + __func__); 952 + return PTR_ERR(opp); 953 + } 954 + new_oppinfo.rate = opp_get_freq(opp); 955 + new_oppinfo.volt = opp_get_voltage(opp); 956 + rcu_read_unlock(); 972 957 973 - err = exynos4_bus_setvolt(data, opp, data->curr_opp); 958 + err = exynos4_bus_setvolt(data, &new_oppinfo, 959 + &data->curr_oppinfo); 974 960 if (err) 975 961 goto unlock; 976 962 977 963 switch (data->type) { 978 964 case TYPE_BUSF_EXYNOS4210: 979 - err = exynos4210_set_busclk(data, opp); 965 + err = exynos4210_set_busclk(data, &new_oppinfo); 980 966 break; 981 967 case TYPE_BUSF_EXYNOS4x12: 982 - err = exynos4x12_set_busclk(data, opp); 968 + err = exynos4x12_set_busclk(data, &new_oppinfo); 983 969 break; 984 970 default: 985 971 err = -EINVAL; ··· 998 962 if (err) 999 963 goto unlock; 1000 964 1001 - data->curr_opp = opp; 965 + data->curr_oppinfo = new_oppinfo; 1002 966 unlock: 1003 967 mutex_unlock(&data->lock); 1004 968 if (err) ··· 1063 1027 } 1064 1028 } 1065 1029 1030 + rcu_read_lock(); 1066 1031 opp = opp_find_freq_floor(dev, &exynos4_devfreq_profile.initial_freq); 1067 1032 if (IS_ERR(opp)) { 1033 + rcu_read_unlock(); 1068 1034 dev_err(dev, "Invalid initial frequency %lu kHz.\n", 1069 1035 exynos4_devfreq_profile.initial_freq); 1070 1036 return PTR_ERR(opp); 1071 1037 } 1072 - data->curr_opp = opp; 1038 + data->curr_oppinfo.rate = opp_get_freq(opp); 1039 + data->curr_oppinfo.volt = opp_get_voltage(opp); 1040 + rcu_read_unlock(); 1073 1041 1074 1042 platform_set_drvdata(pdev, data); 1075 1043