Patchwork rtc: fix chardev initialization races

login
register
mail settings
Submitter Ales Novak
Date Feb. 26, 2014, 10:33 a.m.
Message ID <1393410793-7315-1-git-send-email-alnovak@suse.cz>
Download mbox | patch
Permalink /patch/324263/
State New
Headers show

Comments

Ales Novak - Feb. 26, 2014, 10:33 a.m.
In many rtc modules, the chardevice file in rtc module probe is
being created prematurely. If the probe fails after the chardevice
file has been created (e.g. after rtc_device_register), it's possible
for a program to open() it, which subsequently can cause memory
corruption.

The race looks like that (thanks Jiri):

CPU0:                                CPU1:
sys_load_module()
 do_init_module()
  do_one_initcall()
   cmos_do_probe()
    rtc_device_register()
     __register_chrdev()
     cdev->owner = struct module*
                                     open("/dev/rtc0")
    rtc_device_unregister()
  module_put()
  free_module()
   module_free(mod->module_core)
   /* struct module *module is now
      freed */
                                      chrdev_open()
                                       spin_lock(cdev_lock)
                                       cdev_get()
                                        try_module_get()
                                         module_is_live()
                                         /* dereferences already
                                            freed struct module* */

This patch is proposing a solution, splitting the function
{devm_,}rtc_device_register into {devm_,}rtc_device_register{_fs,}.
The {devm_}rtc_device_register_fs which is creating the files, should
be called after it is clear that the probe will pass. It will set the
RTC_DEV_FILES_EXIST into rtc_device->flags.

In case of probe not passing, the rtc_device_unregister will try to
delete the files only if RTC_DEV_FILES_EXIST is set in rtc_device->flags.

Acked-by: Jiri Kosina <jkosina@suse.cz>
Signed-off-by: Ales Novak <alnovak@suse.cz>
---
 drivers/rtc/class.c        | 47 ++++++++++++++++++++++++++++++++--------------
 drivers/rtc/rtc-ab8500.c   |  1 +
 drivers/rtc/rtc-at91sam9.c |  2 ++
 drivers/rtc/rtc-bfin.c     |  2 ++
 drivers/rtc/rtc-cmos.c     |  2 ++
 drivers/rtc/rtc-davinci.c  |  2 ++
 drivers/rtc/rtc-ds1305.c   |  2 ++
 drivers/rtc/rtc-ds1307.c   |  2 ++
 drivers/rtc/rtc-ds1511.c   |  2 ++
 drivers/rtc/rtc-ds1553.c   |  2 ++
 drivers/rtc/rtc-ds1672.c   |  2 ++
 drivers/rtc/rtc-ds1742.c   |  2 ++
 drivers/rtc/rtc-ds3232.c   |  2 ++
 drivers/rtc/rtc-ep93xx.c   |  2 ++
 drivers/rtc/rtc-isl1208.c  |  2 ++
 drivers/rtc/rtc-jz4740.c   |  2 ++
 drivers/rtc/rtc-m41t80.c   |  2 ++
 drivers/rtc/rtc-m48t59.c   |  2 ++
 drivers/rtc/rtc-max8998.c  |  1 +
 drivers/rtc/rtc-mrst.c     |  2 ++
 drivers/rtc/rtc-nuc900.c   |  2 ++
 drivers/rtc/rtc-omap.c     |  2 ++
 drivers/rtc/rtc-pcap.c     |  2 ++
 drivers/rtc/rtc-pcf2123.c  |  1 +
 drivers/rtc/rtc-pl031.c    |  2 ++
 drivers/rtc/rtc-r9701.c    |  2 ++
 drivers/rtc/rtc-rp5c01.c   |  2 ++
 drivers/rtc/rtc-rs5c372.c  |  2 ++
 drivers/rtc/rtc-rv3029c2.c |  1 +
 drivers/rtc/rtc-rx8025.c   |  2 ++
 drivers/rtc/rtc-spear.c    |  2 ++
 drivers/rtc/rtc-stk17ta8.c |  2 ++
 drivers/rtc/rtc-stmp3xxx.c |  2 ++
 drivers/rtc/rtc-tegra.c    |  2 ++
 drivers/rtc/rtc-test.c     |  2 ++
 drivers/rtc/rtc-twl.c      |  3 +++
 drivers/rtc/rtc-tx4939.c   |  3 +++
 drivers/rtc/rtc-vr41xx.c   |  2 ++
 drivers/rtc/rtc-vt8500.c   |  2 ++
 drivers/rtc/rtc-x1205.c    |  2 ++
 include/linux/rtc.h        |  7 ++++++-
 41 files changed, 115 insertions(+), 15 deletions(-)
Andrew Morton - Feb. 27, 2014, 12:33 a.m.
On Wed, 26 Feb 2014 11:33:13 +0100 Ales Novak <alnovak@suse.cz> wrote:

> In many rtc modules, the chardevice file in rtc module probe is
> being created prematurely. If the probe fails after the chardevice
> file has been created (e.g. after rtc_device_register), it's possible
> for a program to open() it, which subsequently can cause memory
> corruption.
> 
> The race looks like that (thanks Jiri):
> 
> CPU0:                                CPU1:
> sys_load_module()
>  do_init_module()
>   do_one_initcall()
>    cmos_do_probe()
>     rtc_device_register()
>      __register_chrdev()
>      cdev->owner = struct module*
>                                      open("/dev/rtc0")
>     rtc_device_unregister()
>   module_put()
>   free_module()
>    module_free(mod->module_core)
>    /* struct module *module is now
>       freed */
>                                       chrdev_open()
>                                        spin_lock(cdev_lock)
>                                        cdev_get()
>                                         try_module_get()
>                                          module_is_live()
>                                          /* dereferences already
>                                             freed struct module* */
> 
> This patch is proposing a solution, splitting the function
> {devm_,}rtc_device_register into {devm_,}rtc_device_register{_fs,}.
> The {devm_}rtc_device_register_fs which is creating the files, should
> be called after it is clear that the probe will pass. It will set the
> RTC_DEV_FILES_EXIST into rtc_device->flags.
> 
> In case of probe not passing, the rtc_device_unregister will try to
> delete the files only if RTC_DEV_FILES_EXIST is set in rtc_device->flags.
> 
> ..
>
> --- a/drivers/rtc/class.c
> +++ b/drivers/rtc/class.c
> @@ -252,6 +245,21 @@ exit:
>  }
>  EXPORT_SYMBOL_GPL(rtc_device_register);
>  
> +/**
> + * rtc_device_register_fs - creates dev+sysfs+proc files
> + * for rtc device
> + */
> +void rtc_device_register_fs(struct device *dev, struct rtc_device *rtc)
> +{
> +	rtc_dev_add_device(rtc);
> +	rtc_sysfs_add_device(rtc);
> +	rtc_proc_add_device(rtc);

It's a bit sloppy that rtc core simply ignores the error codes from the
driver core functions.  All these things could have failed and the
driver just keeps on trying to work.

I guess that can be fixed separately, but perhaps you'd like to take a
look, see how complex it will be to correctly unwind after such errors?

> +	set_bit(RTC_DEV_FILES_EXIST, &rtc->flags);
> +	dev_info(dev, "rtc core: registered %s as %s\n",
> +			rtc->name, dev_name(&rtc->dev));
> +}
> +EXPORT_SYMBOL_GPL(rtc_device_register_fs);
>  
>  /**
>   * rtc_device_unregister - removes the previously registered RTC class device
> @@ -262,13 +270,15 @@ void rtc_device_unregister(struct rtc_device *rtc)
>  {
>  	if (get_device(&rtc->dev) != NULL) {
>  		mutex_lock(&rtc->ops_lock);
> -		/* remove innards of this RTC, then disable it, before
> -		 * letting any rtc_class_open() users access it again
> -		 */
> -		rtc_sysfs_del_device(rtc);
> -		rtc_dev_del_device(rtc);
> -		rtc_proc_del_device(rtc);
> -		device_unregister(&rtc->dev);
> +		if (test_and_clear_bit(RTC_DEV_FILES_EXIST, &rtc->flags)) {
> +			/* remove innards of this RTC, then disable it, before
> +			 * letting any rtc_class_open() users access it again
> +			 */
> +			rtc_sysfs_del_device(rtc);
> +			rtc_dev_del_device(rtc);
> +			rtc_proc_del_device(rtc);
> +			device_unregister(&rtc->dev);

Presumably it doesn't matter, but it would be nice to do the del
functions in the reverse order of the add functions. 
rtc_proc_del_device() then rtc_sysfs_del_device() then
rtc_dev_del_device().

Patch

diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 589351e..6af8355 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -230,13 +230,6 @@  struct rtc_device *rtc_device_register(const char *name, struct device *dev,
 		goto exit_kfree;
 	}
 
-	rtc_dev_add_device(rtc);
-	rtc_sysfs_add_device(rtc);
-	rtc_proc_add_device(rtc);
-
-	dev_info(dev, "rtc core: registered %s as %s\n",
-			rtc->name, dev_name(&rtc->dev));
-
 	return rtc;
 
 exit_kfree:
@@ -252,6 +245,21 @@  exit:
 }
 EXPORT_SYMBOL_GPL(rtc_device_register);
 
+/**
+ * rtc_device_register_fs - creates dev+sysfs+proc files
+ * for rtc device
+ */
+void rtc_device_register_fs(struct device *dev, struct rtc_device *rtc)
+{
+	rtc_dev_add_device(rtc);
+	rtc_sysfs_add_device(rtc);
+	rtc_proc_add_device(rtc);
+
+	set_bit(RTC_DEV_FILES_EXIST, &rtc->flags);
+	dev_info(dev, "rtc core: registered %s as %s\n",
+			rtc->name, dev_name(&rtc->dev));
+}
+EXPORT_SYMBOL_GPL(rtc_device_register_fs);
 
 /**
  * rtc_device_unregister - removes the previously registered RTC class device
@@ -262,13 +270,15 @@  void rtc_device_unregister(struct rtc_device *rtc)
 {
 	if (get_device(&rtc->dev) != NULL) {
 		mutex_lock(&rtc->ops_lock);
-		/* remove innards of this RTC, then disable it, before
-		 * letting any rtc_class_open() users access it again
-		 */
-		rtc_sysfs_del_device(rtc);
-		rtc_dev_del_device(rtc);
-		rtc_proc_del_device(rtc);
-		device_unregister(&rtc->dev);
+		if (test_and_clear_bit(RTC_DEV_FILES_EXIST, &rtc->flags)) {
+			/* remove innards of this RTC, then disable it, before
+			 * letting any rtc_class_open() users access it again
+			 */
+			rtc_sysfs_del_device(rtc);
+			rtc_dev_del_device(rtc);
+			rtc_proc_del_device(rtc);
+			device_unregister(&rtc->dev);
+		}
 		rtc->ops = NULL;
 		mutex_unlock(&rtc->ops_lock);
 		put_device(&rtc->dev);
@@ -328,6 +338,15 @@  struct rtc_device *devm_rtc_device_register(struct device *dev,
 EXPORT_SYMBOL_GPL(devm_rtc_device_register);
 
 /**
+ * devm_rtc_device_register_fs - create proc&dev&sys files for the rtc device
+ */
+void devm_rtc_device_register_fs(struct device *dev, struct rtc_device *rtc)
+{
+	rtc_device_register_fs(dev, rtc);
+}
+EXPORT_SYMBOL_GPL(devm_rtc_device_register_fs);
+
+/**
  * devm_rtc_device_unregister - resource managed devm_rtc_device_unregister()
  * @dev: the device to unregister
  * @rtc: the RTC class device to unregister
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index 727e2f5..8e25da6 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -503,6 +503,7 @@  static int ab8500_rtc_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "sysfs RTC failed to register\n");
 		return err;
 	}
+	devm_rtc_device_register_fs(&pdev->dev, rtc);
 
 	return 0;
 }
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index 309b8b3..0108c6a 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -369,6 +369,8 @@  static int at91_rtc_probe(struct platform_device *pdev)
 		dev_warn(&pdev->dev, "%s: SET TIME!\n",
 				dev_name(&rtc->rtcdev->dev));
 
+	devm_rtc_device_register_fs(&pdev->dev, rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c
index 0c53f45..a7190b8 100644
--- a/drivers/rtc/rtc-bfin.c
+++ b/drivers/rtc/rtc-bfin.c
@@ -380,6 +380,8 @@  static int bfin_rtc_probe(struct platform_device *pdev)
 	bfin_rtc_reset(dev, RTC_ISTAT_WRITE_COMPLETE);
 	bfin_write_RTC_SWCNT(0);
 
+	devm_rtc_device_register_fs(&pdev->dev, rtc->rtc_dev);
+
 	return 0;
 
 err:
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index cae212f..fff8b9d 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -784,6 +784,8 @@  cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
 		goto cleanup2;
 	}
 
+	rtc_device_register_fs(dev, cmos_rtc.rtc);
+
 	dev_info(dev, "%s%s, %zd bytes nvram%s\n",
 		!is_valid_irq(rtc_irq) ? "no alarms" :
 			cmos_rtc.mon_alrm ? "alarms up to one year" :
diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c
index 24677ef8..57467ac 100644
--- a/drivers/rtc/rtc-davinci.c
+++ b/drivers/rtc/rtc-davinci.c
@@ -554,6 +554,8 @@  static int __init davinci_rtc_probe(struct platform_device *pdev)
 
 	device_init_wakeup(&pdev->dev, 0);
 
+	devm_rtc_device_register_fs(&pdev->dev, davinci_rtc->rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
index 2dd586a..fb85d20 100644
--- a/drivers/rtc/rtc-ds1305.c
+++ b/drivers/rtc/rtc-ds1305.c
@@ -771,6 +771,8 @@  static int ds1305_probe(struct spi_device *spi)
 		return status;
 	}
 
+	devm_rtc_device_register_fs(&spi->dev, ds1305->rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 4e75345..31cffee 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -972,6 +972,8 @@  read_rtc:
 		dev_info(&client->dev, "%zu bytes nvram\n", ds1307->nvram->size);
 	}
 
+	devm_rtc_device_register_fs(&client->dev, ds1307->rtc);
+
 	return 0;
 
 err_irq:
diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c
index bc7b4fc..14f076d 100644
--- a/drivers/rtc/rtc-ds1511.c
+++ b/drivers/rtc/rtc-ds1511.c
@@ -534,6 +534,8 @@  static int ds1511_rtc_probe(struct platform_device *pdev)
 
 	ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
 
+	devm_rtc_device_register_fs(&pdev->dev, rtc);
+
 	return ret;
 }
 
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index fd31571..735ea10 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -329,6 +329,8 @@  static int ds1553_rtc_probe(struct platform_device *pdev)
 
 	ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
 
+	devm_rtc_device_register_fs(&pdev->dev, rtc);
+
 	return ret;
 }
 
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
index 18e2d84..2ad2578 100644
--- a/drivers/rtc/rtc-ds1672.c
+++ b/drivers/rtc/rtc-ds1672.c
@@ -189,6 +189,8 @@  static int ds1672_probe(struct i2c_client *client,
 	if (err)
 		goto exit_devreg;
 
+	devm_rtc_device_register_fs(&client->dev, rtc);
+
 	return 0;
 
  exit_devreg:
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index 5a1f3b2..d4444b0 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -205,6 +205,8 @@  static int ds1742_rtc_probe(struct platform_device *pdev)
 
 	ret = sysfs_create_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
 
+	devm_rtc_device_register_fs(&pdev->dev, rtc);
+
 	return ret;
 }
 
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
index b83bb5a5..778a9d5 100644
--- a/drivers/rtc/rtc-ds3232.c
+++ b/drivers/rtc/rtc-ds3232.c
@@ -427,6 +427,8 @@  static int ds3232_probe(struct i2c_client *client,
 		}
 	}
 
+	devm_rtc_device_register_fs(&client->dev, ds3232->rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
index 5e4f5dc..fbe9a01 100644
--- a/drivers/rtc/rtc-ep93xx.c
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -156,6 +156,8 @@  static int ep93xx_rtc_probe(struct platform_device *pdev)
 	if (err)
 		goto exit;
 
+	devm_rtc_device_register_fs(&pdev->dev, ep93xx_rtc->rtc);
+
 	return 0;
 
 exit:
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index c3c549d..bc25c5b 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -681,6 +681,8 @@  isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
 	if (rc)
 		return rc;
 
+	devm_rtc_device_register_fs(&client->dev, rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index 1b126d2..925600b 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -278,6 +278,8 @@  static int jz4740_rtc_probe(struct platform_device *pdev)
 		}
 	}
 
+	devm_rtc_device_register_fs(&pdev->dev, rtc->rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index a5248aa..cd51429 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -707,6 +707,8 @@  static int m41t80_probe(struct i2c_client *client,
 		}
 	}
 #endif
+	devm_rtc_device_register_fs(&client->dev, rtc);
+
 	return 0;
 
 st_err:
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
index 11880c1..027bcbd 100644
--- a/drivers/rtc/rtc-m48t59.c
+++ b/drivers/rtc/rtc-m48t59.c
@@ -489,6 +489,8 @@  static int m48t59_rtc_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	devm_rtc_device_register_fs(&pdev->dev, m48t59->rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c
index f098ad8..c74020d 100644
--- a/drivers/rtc/rtc-max8998.c
+++ b/drivers/rtc/rtc-max8998.c
@@ -300,6 +300,7 @@  no_irq:
 		dev_warn(&pdev->dev, "LP3974 with RTC REGERR option."
 				" RTC updates will be extremely slow.\n");
 	}
+	devm_rtc_device_register_fs(&pdev->dev, rtc);
 
 	return 0;
 }
diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c
index e2436d1..f61a2ef 100644
--- a/drivers/rtc/rtc-mrst.c
+++ b/drivers/rtc/rtc-mrst.c
@@ -374,6 +374,7 @@  static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem,
 			goto cleanup1;
 		}
 	}
+	rtc_device_register_fs(dev, mrst_rtc.rtc);
 	dev_dbg(dev, "initialised\n");
 	return 0;
 
@@ -403,6 +404,7 @@  static void rtc_mrst_do_remove(struct device *dev)
 	if (mrst->irq)
 		free_irq(mrst->irq, mrst->rtc);
 
+	rtc_device_unregister_fs(mrst->rtc);
 	rtc_device_unregister(mrst->rtc);
 	mrst->rtc = NULL;
 
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
index 248653c..64dd6e2 100644
--- a/drivers/rtc/rtc-nuc900.c
+++ b/drivers/rtc/rtc-nuc900.c
@@ -257,6 +257,8 @@  static int __init nuc900_rtc_probe(struct platform_device *pdev)
 		return -EBUSY;
 	}
 
+	devm_rtc_device_register_fs(&pdev->dev, nuc900_rtc->rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 26de5f8..5fb141f 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -449,6 +449,8 @@  static int __init omap_rtc_probe(struct platform_device *pdev)
 	if (reg != new_ctrl)
 		rtc_write(new_ctrl, OMAP_RTC_CTRL_REG);
 
+	devm_rtc_device_register_fs(&pdev->dev, rtc);
+
 	return 0;
 
 fail0:
diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c
index 40b5c63..9aaac77 100644
--- a/drivers/rtc/rtc-pcap.c
+++ b/drivers/rtc/rtc-pcap.c
@@ -172,6 +172,8 @@  static int __init pcap_rtc_probe(struct platform_device *pdev)
 	if (err)
 		return err;
 
+	devm_rtc_device_register_fs(&pdev->dev, pcap_rtc->rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index d1953bb..874d85d 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -313,6 +313,7 @@  static int pcf2123_probe(struct spi_device *spi)
 			goto sysfs_exit;
 		}
 	}
+	devm_rtc_device_register_fs(&spi->dev, rtc);
 
 	return 0;
 
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index 99181fff..5f04b29 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -306,6 +306,7 @@  static int pl031_remove(struct amba_device *adev)
 	struct pl031_local *ldata = dev_get_drvdata(&adev->dev);
 
 	free_irq(adev->irq[0], ldata);
+	rtc_device_unregister_fs(ldata->rtc);
 	rtc_device_unregister(ldata->rtc);
 	iounmap(ldata->base);
 	kfree(ldata);
@@ -384,6 +385,7 @@  static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
 		goto out_no_irq;
 	}
 
+	rtc_device_register_fs(&adev->dev, ldata->rtc);
 	return 0;
 
 out_no_irq:
diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c
index feeedbd..5f39f64 100644
--- a/drivers/rtc/rtc-r9701.c
+++ b/drivers/rtc/rtc-r9701.c
@@ -161,6 +161,8 @@  static int r9701_probe(struct spi_device *spi)
 
 	spi_set_drvdata(spi, rtc);
 
+	devm_rtc_device_register_fs(&spi->dev, rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
index 89d0736..83d8655 100644
--- a/drivers/rtc/rtc-rp5c01.c
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -259,6 +259,8 @@  static int __init rp5c01_rtc_probe(struct platform_device *dev)
 	if (error)
 		return error;
 
+	devm_rtc_device_register_fs(&dev->dev, rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index ccf54f0..a426eca 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -668,6 +668,8 @@  static int rs5c372_probe(struct i2c_client *client,
 	if (err)
 		goto exit;
 
+	devm_rtc_device_register_fs(&client->dev, rs5c372->rtc);
+
 	return 0;
 
 exit:
diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c
index 1a779a6..57e7d2e 100644
--- a/drivers/rtc/rtc-rv3029c2.c
+++ b/drivers/rtc/rtc-rv3029c2.c
@@ -408,6 +408,7 @@  static int rv3029c2_probe(struct i2c_client *client,
 		dev_err(&client->dev, "reading status failed\n");
 		return rc;
 	}
+	devm_rtc_device_register_fs(&client->dev, rtc);
 
 	return 0;
 }
diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c
index 8fa23ea..b07fac1 100644
--- a/drivers/rtc/rtc-rx8025.c
+++ b/drivers/rtc/rtc-rx8025.c
@@ -597,6 +597,8 @@  static int rx8025_probe(struct i2c_client *client,
 	if (err)
 		goto errout_irq;
 
+	devm_rtc_device_register_fs(&client->dev, rx8025->rtc);
+
 	return 0;
 
 errout_irq:
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
index c492cf0..a644636 100644
--- a/drivers/rtc/rtc-spear.c
+++ b/drivers/rtc/rtc-spear.c
@@ -414,6 +414,8 @@  static int spear_rtc_probe(struct platform_device *pdev)
 	if (!device_can_wakeup(&pdev->dev))
 		device_init_wakeup(&pdev->dev, 1);
 
+	devm_rtc_device_register_fs(&pdev->dev, config->rtc);
+
 	return 0;
 
 err_disable_clock:
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
index a176ba6..ec53d2a 100644
--- a/drivers/rtc/rtc-stk17ta8.c
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -338,6 +338,8 @@  static int stk17ta8_rtc_probe(struct platform_device *pdev)
 
 	ret = sysfs_create_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
 
+	devm_rtc_device_register_fs(&pdev->dev, pdata->rtc);
+
 	return ret;
 }
 
diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c
index ea96492..0d8c575 100644
--- a/drivers/rtc/rtc-stmp3xxx.c
+++ b/drivers/rtc/rtc-stmp3xxx.c
@@ -304,6 +304,8 @@  static int stmp3xxx_rtc_probe(struct platform_device *pdev)
 		return err;
 	}
 
+	devm_rtc_device_register_fs(&pdev->dev, rtc_data->rtc);
+
 	stmp3xxx_wdt_register(pdev);
 	return 0;
 }
diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c
index 76af92a..084d1ab 100644
--- a/drivers/rtc/rtc-tegra.c
+++ b/drivers/rtc/rtc-tegra.c
@@ -365,6 +365,8 @@  static int __init tegra_rtc_probe(struct platform_device *pdev)
 
 	dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
 
+	devm_rtc_device_register_fs(&pdev->dev, info->rtc_dev);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c
index 7746e65..0737eca 100644
--- a/drivers/rtc/rtc-test.c
+++ b/drivers/rtc/rtc-test.c
@@ -114,6 +114,8 @@  static int test_probe(struct platform_device *plat_dev)
 
 	platform_set_drvdata(plat_dev, rtc);
 
+	devm_rtc_device_register_fs(&plat_dev->dev, rtc);
+
 	return 0;
 
 err:
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index 1915464..e9614e8 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -544,6 +544,9 @@  static int twl_rtc_probe(struct platform_device *pdev)
 	}
 
 	platform_set_drvdata(pdev, rtc);
+
+	devm_rtc_device_register_fs(&pdev->dev, rtc);
+
 	return 0;
 }
 
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index 4f87234..40b68f93 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -269,6 +269,9 @@  static int __init tx4939_rtc_probe(struct platform_device *pdev)
 	pdata->rtc = rtc;
 	ret = sysfs_create_bin_file(&pdev->dev.kobj, &tx4939_rtc_nvram_attr);
 
+	if (!ret)
+		devm_rtc_device_register_fs(&pdev->dev, rtc);
+
 	return ret;
 }
 
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index 88c9c92..d84865b 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -355,6 +355,8 @@  static int rtc_probe(struct platform_device *pdev)
 	disable_irq(aie_irq);
 	disable_irq(pie_irq);
 
+	devm_rtc_device_register_fs(&pdev->dev, rtc);
+
 	dev_info(&pdev->dev, "Real Time Clock of NEC VR4100 series\n");
 
 	return 0;
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
index df2ef3e..121f544 100644
--- a/drivers/rtc/rtc-vt8500.c
+++ b/drivers/rtc/rtc-vt8500.c
@@ -269,6 +269,8 @@  static int vt8500_rtc_probe(struct platform_device *pdev)
 		goto err_return;
 	}
 
+	devm_rtc_device_register_fs(&pdev->dev, vt8500_rtc->rtc);
+
 	return 0;
 
 err_return:
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
index 365dc65..679df80 100644
--- a/drivers/rtc/rtc-x1205.c
+++ b/drivers/rtc/rtc-x1205.c
@@ -662,6 +662,8 @@  static int x1205_probe(struct i2c_client *client,
 	if (err)
 		return err;
 
+	devm_rtc_device_register_fs(&client->dev, rtc);
+
 	return 0;
 }
 
diff --git a/include/linux/rtc.h b/include/linux/rtc.h
index c2c2897..245432a 100644
--- a/include/linux/rtc.h
+++ b/include/linux/rtc.h
@@ -81,7 +81,9 @@  struct rtc_timer {
 
 
 /* flags */
-#define RTC_DEV_BUSY 0
+#define RTC_DEV_BUSY        0
+/* flag indicating the files for the device have been created */
+#define RTC_DEV_FILES_EXIST 1
 
 struct rtc_device
 {
@@ -133,10 +135,13 @@  extern struct rtc_device *rtc_device_register(const char *name,
 					struct device *dev,
 					const struct rtc_class_ops *ops,
 					struct module *owner);
+
+extern void rtc_device_register_fs(struct device *dev, struct rtc_device *rtc);
 extern struct rtc_device *devm_rtc_device_register(struct device *dev,
 					const char *name,
 					const struct rtc_class_ops *ops,
 					struct module *owner);
+extern void devm_rtc_device_register_fs(struct device *dev, struct rtc_device *rtc);
 extern void rtc_device_unregister(struct rtc_device *rtc);
 extern void devm_rtc_device_unregister(struct device *dev,
 					struct rtc_device *rtc);