Merge branch 'for-linus-clk' of git://ftp.arm.linux.org.uk/~rmk/linux-arm

Pull clkdev updates from Russell King:
 "This series addresses some breakage in clkdev caused by a previous
  patch set from the clk tree which introduced per-user clk structures.
  This basically renamed the existing 'struct clk' to 'struct clk_hw',
  and introduced a new 'struct clk'.

  This change will break anyone using clk_add_alias() with the common
  clk code enabled.  Thankfully, the intersection of users of
  clk_add_alias() and those using the common clk code is practically
  zero, but this is something which should be fixed to keep the code
  sane.

  The problem is that clk_add_alias() does this:

        r = clk_get(...);
        l = clkdev_alloc(r, ...);
        clk_put(...);

  which causes the alias to store a pointer to 'r', which has been
  freed.

  The original patch set tried to work around this problem incorrectly -
  at clk_get() time, it tried to convert the struct clk to a struct
  clk_hw, and then creating a new struct clk from that.  Clearly, if the
  original struct clk has been freed, then we have a use-after-free bug.

  We have other places in the tree which do something similar, so this
  series also addresses those locations too.

  This series addresses this problem by converting clkdev to store and
  use the clk_hw pointer.  This allows clk_get() to only have to create
  it's per-user struct clk from the clk_hw.  We can also get to the
  desired clk_hw at clk_add_alias() or clk lookup creation time, when
  the struct clk is "alive".

  We also perform some cleanups of the code:

   - replacing looped calls to clkdev_add() with clkdev_add_table()

   - replacing open-coded lookup allocation (which should have been
     using clkdev_alloc()) and subsequent clkdev_add() with
     clkdev_create()

   - replacing open-coded clk_add_alias() with clk_add_alias()"

* 'for-linus-clk' of git://ftp.arm.linux.org.uk/~rmk/linux-arm:
  clk: s2mps11: use clkdev_create()
  ASoC: migor: use clkdev_create()
  ARM: omap2: use clkdev_add_alias()
  ARM: omap2: use clkdev_create()
  ARM: orion: use clkdev_create()
  ARM: lpc32xx: convert to use clkdev_add_table()
  SH: use clkdev_add_table()
  clkdev: add clkdev_create() helper
  clkdev: const-ify connection id to clk_add_alias()
  clkdev: get rid of redundant clk_add_alias() prototype in linux/clk.h
  clkdev: drop __init from clkdev_add_table()
  clk: update clk API documentation to clarify clk_round_rate()
  clkdev: use clk_hw internally
This commit is contained in:
Linus Torvalds 2015-06-23 15:50:46 -07:00
commit 2ad7b44f5d
19 changed files with 108 additions and 87 deletions

View File

@ -11,6 +11,7 @@
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/clkdev.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/clk.h>

View File

@ -1238,10 +1238,7 @@ static struct clk_lookup lookups[] = {
static int __init clk_init(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(lookups); i++)
clkdev_add(&lookups[i]);
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
/*
* Setup muxed SYSCLK for HCLK PLL base -this selects the

View File

@ -7,6 +7,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clkdev.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/kernel.h>
@ -14,7 +15,6 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <linux/omapfb.h>
#include <linux/spi/spi.h>

View File

@ -232,14 +232,12 @@ void omap2xxx_clkt_vps_init(void)
struct clk_hw_omap *hw = NULL;
struct clk *clk;
const char *parent_name = "mpu_ck";
struct clk_lookup *lookup = NULL;
omap2xxx_clkt_vps_late_init();
omap2xxx_clkt_vps_check_bootloader_rates();
hw = kzalloc(sizeof(*hw), GFP_KERNEL);
lookup = kzalloc(sizeof(*lookup), GFP_KERNEL);
if (!hw || !lookup)
if (!hw)
goto cleanup;
init.name = "virt_prcm_set";
init.ops = &virt_prcm_set_ops;
@ -249,15 +247,9 @@ void omap2xxx_clkt_vps_init(void)
hw->hw.init = &init;
clk = clk_register(NULL, &hw->hw);
lookup->dev_id = NULL;
lookup->con_id = "cpufreq_ck";
lookup->clk = clk;
clkdev_add(lookup);
clkdev_create(clk, "cpufreq_ck", NULL);
return;
cleanup:
kfree(hw);
kfree(lookup);
}
#endif

View File

@ -47,7 +47,7 @@ static void _add_clkdev(struct omap_device *od, const char *clk_alias,
const char *clk_name)
{
struct clk *r;
struct clk_lookup *l;
int rc;
if (!clk_alias || !clk_name)
return;
@ -62,21 +62,15 @@ static void _add_clkdev(struct omap_device *od, const char *clk_alias,
return;
}
r = clk_get(NULL, clk_name);
if (IS_ERR(r)) {
dev_err(&od->pdev->dev,
"clk_get for %s failed\n", clk_name);
return;
rc = clk_add_alias(clk_alias, dev_name(&od->pdev->dev), clk_name, NULL);
if (rc) {
if (rc == -ENODEV || rc == -ENOMEM)
dev_err(&od->pdev->dev,
"clkdev_alloc for %s failed\n", clk_alias);
else
dev_err(&od->pdev->dev,
"clk_get for %s failed\n", clk_name);
}
l = clkdev_alloc(r, clk_alias, dev_name(&od->pdev->dev));
if (!l) {
dev_err(&od->pdev->dev,
"clkdev_alloc for %s failed\n", clk_alias);
return;
}
clkdev_add(l);
}
/**

View File

@ -10,6 +10,7 @@
*
*/
#include <linux/clkdev.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/gpio.h>

View File

@ -11,6 +11,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clkdev.h>
#include <linux/gpio.h>
#include <linux/gpio/machine.h>
#include <linux/module.h>

View File

@ -12,6 +12,7 @@
*
*/
#include <linux/clkdev.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>

View File

@ -28,11 +28,7 @@
void __init orion_clkdev_add(const char *con_id, const char *dev_id,
struct clk *clk)
{
struct clk_lookup *cl;
cl = clkdev_alloc(clk, con_id, dev_id);
if (cl)
clkdev_add(cl);
clkdev_create(clk, con_id, "%s", dev_id);
}
/* Create clkdev entries for all orion platforms except kirkwood.

View File

@ -246,8 +246,7 @@ int __init arch_clk_init(void)
for (i = 0; i < ARRAY_SIZE(main_clks); i++)
ret |= clk_register(main_clks[i]);
for (i = 0; i < ARRAY_SIZE(lookups); i++)
clkdev_add(&lookups[i]);
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
if (!ret)
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),

View File

@ -141,8 +141,8 @@ int __init arch_clk_init(void)
for (i = 0; i < ARRAY_SIZE(clks); i++)
ret |= clk_register(clks[i]);
for (i = 0; i < ARRAY_SIZE(lookups); i++)
clkdev_add(&lookups[i]);
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
if (!ret)
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),

View File

@ -164,8 +164,8 @@ int __init arch_clk_init(void)
for (i = 0; i < ARRAY_SIZE(clks); i++)
ret |= clk_register(clks[i]);
for (i = 0; i < ARRAY_SIZE(lookups); i++)
clkdev_add(&lookups[i]);
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
if (!ret)
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),

View File

@ -179,8 +179,8 @@ int __init arch_clk_init(void)
for (i = 0; i < ARRAY_SIZE(clks); i++)
ret |= clk_register(clks[i]);
for (i = 0; i < ARRAY_SIZE(lookups); i++)
clkdev_add(&lookups[i]);
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
if (!ret)
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),

View File

@ -138,8 +138,8 @@ int __init arch_clk_init(void)
for (i = 0; i < ARRAY_SIZE(clks); i++)
ret |= clk_register(clks[i]);
for (i = 0; i < ARRAY_SIZE(lookups); i++)
clkdev_add(&lookups[i]);
clkdev_add_table(lookups, ARRAY_SIZE(lookups));
if (!ret)
ret = sh_clk_div4_register(div4_clks, ARRAY_SIZE(div4_clks),

View File

@ -242,14 +242,12 @@ static int s2mps11_clk_probe(struct platform_device *pdev)
goto err_reg;
}
s2mps11_clk->lookup = clkdev_alloc(s2mps11_clk->clk,
s2mps11_clk->lookup = clkdev_create(s2mps11_clk->clk,
s2mps11_name(s2mps11_clk), NULL);
if (!s2mps11_clk->lookup) {
ret = -ENOMEM;
goto err_lup;
}
clkdev_add(s2mps11_clk->lookup);
}
for (i = 0; i < S2MPS11_CLKS_NUM; i++) {

View File

@ -177,7 +177,7 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id)
if (!cl)
goto out;
clk = __clk_create_clk(__clk_get_hw(cl->clk), dev_id, con_id);
clk = __clk_create_clk(cl->clk_hw, dev_id, con_id);
if (IS_ERR(clk))
goto out;
@ -215,18 +215,26 @@ void clk_put(struct clk *clk)
}
EXPORT_SYMBOL(clk_put);
void clkdev_add(struct clk_lookup *cl)
static void __clkdev_add(struct clk_lookup *cl)
{
mutex_lock(&clocks_mutex);
list_add_tail(&cl->node, &clocks);
mutex_unlock(&clocks_mutex);
}
void clkdev_add(struct clk_lookup *cl)
{
if (!cl->clk_hw)
cl->clk_hw = __clk_get_hw(cl->clk);
__clkdev_add(cl);
}
EXPORT_SYMBOL(clkdev_add);
void __init clkdev_add_table(struct clk_lookup *cl, size_t num)
void clkdev_add_table(struct clk_lookup *cl, size_t num)
{
mutex_lock(&clocks_mutex);
while (num--) {
cl->clk_hw = __clk_get_hw(cl->clk);
list_add_tail(&cl->node, &clocks);
cl++;
}
@ -243,7 +251,7 @@ struct clk_lookup_alloc {
};
static struct clk_lookup * __init_refok
vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
vclkdev_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
va_list ap)
{
struct clk_lookup_alloc *cla;
@ -252,7 +260,7 @@ vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
if (!cla)
return NULL;
cla->cl.clk = clk;
cla->cl.clk_hw = hw;
if (con_id) {
strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
cla->cl.con_id = cla->con_id;
@ -266,6 +274,19 @@ vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
return &cla->cl;
}
static struct clk_lookup *
vclkdev_create(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
va_list ap)
{
struct clk_lookup *cl;
cl = vclkdev_alloc(hw, con_id, dev_fmt, ap);
if (cl)
__clkdev_add(cl);
return cl;
}
struct clk_lookup * __init_refok
clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
{
@ -273,28 +294,49 @@ clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...)
va_list ap;
va_start(ap, dev_fmt);
cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
cl = vclkdev_alloc(__clk_get_hw(clk), con_id, dev_fmt, ap);
va_end(ap);
return cl;
}
EXPORT_SYMBOL(clkdev_alloc);
int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
struct device *dev)
/**
* clkdev_create - allocate and add a clkdev lookup structure
* @clk: struct clk to associate with all clk_lookups
* @con_id: connection ID string on device
* @dev_fmt: format string describing device name
*
* Returns a clk_lookup structure, which can be later unregistered and
* freed.
*/
struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id,
const char *dev_fmt, ...)
{
struct clk *r = clk_get(dev, id);
struct clk_lookup *cl;
va_list ap;
va_start(ap, dev_fmt);
cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap);
va_end(ap);
return cl;
}
EXPORT_SYMBOL_GPL(clkdev_create);
int clk_add_alias(const char *alias, const char *alias_dev_name,
const char *con_id, struct device *dev)
{
struct clk *r = clk_get(dev, con_id);
struct clk_lookup *l;
if (IS_ERR(r))
return PTR_ERR(r);
l = clkdev_alloc(r, alias, alias_dev_name);
l = clkdev_create(r, alias, "%s", alias_dev_name);
clk_put(r);
if (!l)
return -ENODEV;
clkdev_add(l);
return 0;
return l ? 0 : -ENODEV;
}
EXPORT_SYMBOL(clk_add_alias);
@ -334,15 +376,10 @@ int clk_register_clkdev(struct clk *clk, const char *con_id,
return PTR_ERR(clk);
va_start(ap, dev_fmt);
cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);
cl = vclkdev_create(__clk_get_hw(clk), con_id, dev_fmt, ap);
va_end(ap);
if (!cl)
return -ENOMEM;
clkdev_add(cl);
return 0;
return cl ? 0 : -ENOMEM;
}
EXPORT_SYMBOL(clk_register_clkdev);
@ -365,8 +402,8 @@ int clk_register_clkdevs(struct clk *clk, struct clk_lookup *cl, size_t num)
return PTR_ERR(clk);
for (i = 0; i < num; i++, cl++) {
cl->clk = clk;
clkdev_add(cl);
cl->clk_hw = __clk_get_hw(clk);
__clkdev_add(cl);
}
return 0;

View File

@ -306,6 +306,20 @@ void devm_clk_put(struct device *dev, struct clk *clk);
* @clk: clock source
* @rate: desired clock rate in Hz
*
* This answers the question "if I were to pass @rate to clk_set_rate(),
* what clock rate would I end up with?" without changing the hardware
* in any way. In other words:
*
* rate = clk_round_rate(clk, r);
*
* and:
*
* clk_set_rate(clk, r);
* rate = clk_get_rate(clk);
*
* are equivalent except the former does not modify the clock hardware
* in any way.
*
* Returns rounded clock rate in Hz, or negative errno.
*/
long clk_round_rate(struct clk *clk, unsigned long rate);
@ -471,19 +485,6 @@ static inline void clk_disable_unprepare(struct clk *clk)
clk_unprepare(clk);
}
/**
* clk_add_alias - add a new clock alias
* @alias: name for clock alias
* @alias_dev_name: device name
* @id: platform specific clock name
* @dev: device
*
* Allows using generic clock names for drivers by adding a new alias.
* Assumes clkdev, see clkdev.h for more info.
*/
int clk_add_alias(const char *alias, const char *alias_dev_name, char *id,
struct device *dev);
struct device_node;
struct of_phandle_args;

View File

@ -22,6 +22,7 @@ struct clk_lookup {
const char *dev_id;
const char *con_id;
struct clk *clk;
struct clk_hw *clk_hw;
};
#define CLKDEV_INIT(d, n, c) \
@ -37,8 +38,11 @@ struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id,
void clkdev_add(struct clk_lookup *cl);
void clkdev_drop(struct clk_lookup *cl);
struct clk_lookup *clkdev_create(struct clk *clk, const char *con_id,
const char *dev_fmt, ...);
void clkdev_add_table(struct clk_lookup *, size_t);
int clk_add_alias(const char *, const char *, char *, struct device *);
int clk_add_alias(const char *, const char *, const char *, struct device *);
int clk_register_clkdev(struct clk *, const char *, const char *, ...);
int clk_register_clkdevs(struct clk *, struct clk_lookup *, size_t);

View File

@ -162,12 +162,11 @@ static int __init migor_init(void)
if (ret < 0)
return ret;
siumckb_lookup = clkdev_alloc(&siumckb_clk, "siumckb_clk", NULL);
siumckb_lookup = clkdev_create(&siumckb_clk, "siumckb_clk", NULL);
if (!siumckb_lookup) {
ret = -ENOMEM;
goto eclkdevalloc;
}
clkdev_add(siumckb_lookup);
/* Port number used on this machine: port B */
migor_snd_device = platform_device_alloc("soc-audio", 1);