Embedded Linux development
 help / color / mirror / Atom feed
* [PATCH 0/4] Add eCAP driver support for PWM based backlight control.
From: sugumar @ 2010-10-08 13:16 UTC (permalink / raw)
  To: linux-embedded; +Cc: davinci-linux-open-source, sugumar

The following set of patches adds support for eCAP driver for PWM
generation. It also adds platform support for back light device.
The patch as a whole adds support for back light control through eCAP module.

The patch has been tested on Bill Gatliff's tree.
	
sugumar (4):
  davinci: da8xx: eCAP driver for PWM signal generation
  davinci: da850: Add platform specific support for eCAP driver
  Modify the back light driver to support the new PWM framework
  davinci: da850/omap-l138 evm: Platform support for backlight driver

 arch/arm/mach-davinci/board-da850-evm.c    |   37 +++
 arch/arm/mach-davinci/da850.c              |   69 ++++++
 arch/arm/mach-davinci/include/mach/da8xx.h |    1 +
 arch/arm/mach-davinci/include/mach/mux.h   |    3 +
 drivers/pwm/Kconfig                        |   11 +
 drivers/pwm/Makefile                       |    1 +
 drivers/pwm/ecap.c                         |  330 ++++++++++++++++++++++++++++
 drivers/video/backlight/pwm_bl.c           |   28 ++-
 include/linux/pwm_backlight.h              |    3 +-
 9 files changed, 469 insertions(+), 14 deletions(-)
 create mode 100644 drivers/pwm/ecap.c

^ permalink raw reply

* [PATCH 1/4] davinci: da8xx: eCAP driver for PWM signal generation
From: sugumar @ 2010-10-08 13:17 UTC (permalink / raw)
  To: linux-embedded; +Cc: davinci-linux-open-source, sugumar

OMAPL138/DA850 contains three instances of eCAP module. Each eCAP
module has one dedicated pin that can be used either in capture
mode(input) or in PWM mode. For more information on eCAP module
operation, please refer to the following url.

	http://focus.ti.com/lit/ug/sprufl2a/sprufl2a.pdf

This patch adds eCAP driver support for PWM signal generation.

Signed-off-by: sugumar <sugumar@ti.com>
---
 drivers/pwm/Kconfig  |   10 ++
 drivers/pwm/Makefile |    1 +
 drivers/pwm/ecap.c   |  330 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 341 insertions(+), 0 deletions(-)
 create mode 100644 drivers/pwm/ecap.c

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index def003b..6f32dd6 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -33,4 +33,14 @@ config GPIO_PWM
          This option enables a single-channel PWM device using
 	 a kernel interval timer and a GPIO pin.  If unsure, say N.
 
+config ECAP_PWM
+	tristate "eCAP PWM support"
+	depends on ARCH_DAVINCI_DA8XX
+	help
+	  This option enables device driver support for eCAP module found
+	  on DA8xx Processors. eCAP module is used to geenrate wide range
+	  of PWM waveforms. Maximum frequency generated is equal to half
+	  the system clock frequency.
+	  Say Y to enable the eCAP support. If you want to build it as a
+	  module, Say M.
 endif
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 03ae2cd..6f02c9b 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_GENERIC_PWM)	+= pwm.o
 obj-$(CONFIG_ATMEL_PWM)		+= atmel-pwm.o
 obj-$(CONFIG_PXA_PWM)		+= pxa-pwm.o
 obj-$(CONFIG_GPIO_PWM)		+= gpio.o
+obj-$(CONFIG_ECAP_PWM)		+= ecap.o
diff --git a/drivers/pwm/ecap.c b/drivers/pwm/ecap.c
new file mode 100644
index 0000000..763586e
--- /dev/null
+++ b/drivers/pwm/ecap.c
@@ -0,0 +1,330 @@
+/*
+ * eCAP driver for PWM output generation
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed .as is. WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/pwm/pwm.h>
+#include <linux/slab.h>
+
+#define TIMER_CTR_REG			0x0
+#define CAPTURE_2_REG			0x0c
+#define CAPTURE_3_REG			0x10
+#define CAPTURE_4_REG			0x14
+#define CAPTURE_CTRL2_REG		0x2A
+
+#define ECTRL2_SYNCOSEL_MASK		(0x03 << 6)
+
+#define ECTRL2_MDSL_ECAP		BIT(9)
+#define ECTRL2_CTRSTP_FREERUN		BIT(4)
+#define ECTRL2_PLSL_LOW			BIT(10)
+#define ECTRL2_SYNC_EN			BIT(5)
+
+struct ecap_pwm {
+	struct pwm_device	pwm;
+	spinlock_t	lock;
+	struct clk	*clk;
+	int	clk_enabled;
+	void __iomem	*mmio_base;
+};
+
+static inline struct ecap_pwm *to_ecap_pwm(const struct pwm_channel *p)
+{
+	return container_of(p->pwm, struct ecap_pwm, pwm);
+}
+
+static int ecap_pwm_stop(struct pwm_channel *p)
+{
+	unsigned long flags;
+	struct ecap_pwm *ep = to_ecap_pwm(p);
+
+	clk_enable(ep->clk);
+
+	spin_lock_irqsave(&p->lock, flags);
+	__raw_writew(__raw_readw(ep->mmio_base + CAPTURE_CTRL2_REG) &
+		~BIT(4), ep->mmio_base + CAPTURE_CTRL2_REG);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	clk_disable(ep->clk);
+	if (ep->clk_enabled) {
+		clk_disable(ep->clk);
+		ep->clk_enabled = 0;
+	}
+
+	return 0;
+}
+
+static int ecap_pwm_start(struct pwm_channel *p)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct ecap_pwm *ep = to_ecap_pwm(p);
+
+	clk_enable(ep->clk);
+
+	spin_lock_irqsave(&p->lock, flags);
+	__raw_writew(__raw_readw(ep->mmio_base + CAPTURE_CTRL2_REG) |
+		BIT(4), ep->mmio_base + CAPTURE_CTRL2_REG);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	clk_disable(ep->clk);
+	if (!ep->clk_enabled) {
+		ret = clk_enable(ep->clk);
+		if (ret)
+			return ret;
+		ep->clk_enabled = 1;
+	}
+
+	return ret;
+}
+
+static int ecap_pwm_set_polarity(struct pwm_channel *p, char pol)
+{
+	unsigned long flags;
+	struct ecap_pwm *ep = to_ecap_pwm(p);
+
+	clk_enable(ep->clk);
+
+	spin_lock_irqsave(&p->lock, flags);
+	 __raw_writew(__raw_readw(ep->mmio_base + CAPTURE_CTRL2_REG) &
+		 (~BIT(10) | pol), ep->mmio_base + CAPTURE_CTRL2_REG);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	clk_disable(ep->clk);
+	return 0;
+}
+
+static int ecap_pwm_unsynchronize(struct pwm_channel *p)
+{
+	unsigned long flags;
+	struct ecap_pwm *ep = to_ecap_pwm(p);
+
+	clk_enable(ep->clk);
+
+	spin_lock_irqsave(&p->lock, flags);
+	 __raw_writew(__raw_readw(ep->mmio_base + CAPTURE_CTRL2_REG) &
+		 (~ECTRL2_SYNCOSEL_MASK | 0x2 << 6) & ~ECTRL2_SYNC_EN,
+			ep->mmio_base + CAPTURE_CTRL2_REG);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	clk_disable(ep->clk);
+	return 0;
+}
+
+static int ecap_pwm_synchronize(struct pwm_channel *p)
+{
+	unsigned long flags;
+	struct ecap_pwm *ep = to_ecap_pwm(p);
+
+	clk_enable(ep->clk);
+
+	spin_lock_irqsave(&p->lock, flags);
+	 __raw_writew(__raw_readw(ep->mmio_base + CAPTURE_CTRL2_REG) &
+		 ((~ECTRL2_SYNCOSEL_MASK) | ECTRL2_SYNC_EN),
+			ep->mmio_base + CAPTURE_CTRL2_REG);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	clk_disable(ep->clk);
+	return 0;
+}
+
+static int ecap_pwm_config_period(struct pwm_channel *p)
+{
+	unsigned long flags;
+	struct ecap_pwm *ep = to_ecap_pwm(p);
+
+	 clk_enable(ep->clk);
+
+	spin_lock_irqsave(&p->lock, flags);
+	__raw_writel((p->period_ticks) - 1, ep->mmio_base + CAPTURE_3_REG);
+	__raw_writew(ECTRL2_MDSL_ECAP | ECTRL2_SYNCOSEL_MASK |
+		 ECTRL2_CTRSTP_FREERUN, ep->mmio_base + CAPTURE_CTRL2_REG);
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	clk_disable(ep->clk);
+	return 0;
+}
+
+
+static int ecap_pwm_config_duty(struct pwm_channel *p)
+{
+	unsigned long flags;
+	struct ecap_pwm *ep = to_ecap_pwm(p);
+
+	clk_enable(ep->clk);
+
+	spin_lock_irqsave(&p->lock, flags);
+	__raw_writew(ECTRL2_MDSL_ECAP | ECTRL2_SYNCOSEL_MASK |
+	 ECTRL2_CTRSTP_FREERUN, ep->mmio_base + CAPTURE_CTRL2_REG);
+	if (p->duty_ticks > 0) {
+		__raw_writel(p->duty_ticks, ep->mmio_base + CAPTURE_4_REG);
+	} else {
+	__raw_writel(p->duty_ticks, ep->mmio_base + CAPTURE_2_REG);
+	__raw_writel(0, ep->mmio_base + TIMER_CTR_REG);
+	}
+	spin_unlock_irqrestore(&p->lock, flags);
+
+	clk_disable(ep->clk);
+	return 0;
+}
+
+static int ecap_pwm_config(struct pwm_channel *p,
+				struct pwm_channel_config *c)
+{
+	int ret = 0;
+	switch (c->config_mask) {
+
+	case PWM_CONFIG_DUTY_TICKS:
+		p->duty_ticks = c->duty_ticks;
+		ret = ecap_pwm_config_duty(p);
+		break;
+
+	case PWM_CONFIG_PERIOD_TICKS:
+		p->period_ticks = c->period_ticks;
+		ret = ecap_pwm_config_period(p);
+		break;
+
+	case PWM_CONFIG_POLARITY:
+		ret = ecap_pwm_set_polarity(p, c->polarity);
+		break;
+
+	case PWM_CONFIG_START:
+		ret = ecap_pwm_start(p);
+		break;
+
+	case PWM_CONFIG_STOP:
+		ret = ecap_pwm_stop(p);
+		break;
+	}
+
+	return ret;
+}
+
+static int ecap_pwm_request(struct pwm_channel *p)
+{
+	struct ecap_pwm *ep = to_ecap_pwm(p);
+
+	p->tick_hz = clk_get_rate(ep->clk);
+	return 0;
+}
+
+static int __init ecap_probe(struct platform_device *pdev)
+{
+	struct ecap_pwm *ep = NULL;
+	struct resource *r;
+	int ret = 0;
+
+	ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+	if (!ep) {
+		dev_err(&pdev->dev, "failed to allocate memory\n");
+		ret = -ENOMEM;
+		goto err_ecap_pwm_alloc;
+	}
+
+	ep->clk = clk_get(&pdev->dev, "ecap");
+	if (IS_ERR(ep->clk)) {
+		ret = PTR_ERR(ep->clk);
+		goto err_free;
+	}
+
+	spin_lock_init(&ep->lock);
+	ep->pwm.dev = &pdev->dev;
+	ep->pwm.bus_id = dev_name(&pdev->dev);
+	ep->pwm.owner = THIS_MODULE;
+	ep->pwm.config = ecap_pwm_config;
+	ep->pwm.request = ecap_pwm_request;
+	ep->pwm.nchan = 1;
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		dev_err(&pdev->dev, "no memory resource defined\n");
+		ret = -ENODEV;
+		goto err_free_clk;
+	}
+
+	r = request_mem_region(r->start, resource_size(r), pdev->name);
+	if (!r) {
+		dev_err(&pdev->dev, "failed to request memory resource\n");
+		ret = -EBUSY;
+		goto err_free_clk;
+	}
+
+	ep->mmio_base = ioremap(r->start, resource_size(r));
+	if (!ep->mmio_base) {
+		dev_err(&pdev->dev, "failed to ioremap() registers\n");
+		ret = -ENODEV;
+		goto err_free_mem;
+	}
+
+	ret =  pwm_register(&ep->pwm);
+	platform_set_drvdata(pdev, ep);
+	return 0;
+
+err_free_mem:
+	release_mem_region(r->start, resource_size(r));
+err_free_clk:
+	clk_put(ep->clk);
+err_free:
+	kfree(ep);
+err_ecap_pwm_alloc:
+	return ret;
+}
+
+static int __devexit ecap_remove(struct platform_device *pdev)
+{
+	struct ecap_pwm *ep = platform_get_drvdata(pdev);
+	struct resource *r;
+
+	ep = platform_get_drvdata(pdev);
+	if (!ep)
+		return -ENODEV;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, resource_size(r));
+	platform_set_drvdata(pdev, NULL);
+	pwm_unregister(&ep->pwm);
+	iounmap(ep->mmio_base);
+	clk_put(ep->clk);
+	kfree(ep);
+
+	return 0;
+}
+
+static struct platform_driver ecap_driver = {
+	.driver	= {
+		.name	= "ecap",
+		.owner	= THIS_MODULE,
+	},
+	.probe	= ecap_probe,
+	.remove	= __devexit_p(ecap_remove),
+};
+
+static int __init ecap_init(void)
+{
+	return platform_driver_register(&ecap_driver);
+}
+
+static void __exit ecap_exit(void)
+{
+	platform_driver_unregister(&ecap_driver);
+}
+
+module_init(ecap_init);
+module_exit(ecap_exit);
+
+MODULE_AUTHOR("sugumar <sugumar@ti.com>");
+MODULE_ALIAS("platform:ecap");
+MODULE_LICENSE("GPL v2");
-- 
1.5.6

^ permalink raw reply related

* [PATCH 2/4] davinci: da850: Add platform specific support for eCAP driver
From: sugumar @ 2010-10-08 13:17 UTC (permalink / raw)
  To: linux-embedded; +Cc: davinci-linux-open-source, sugumar

This patch registers the eCAP driver as a platform device

Signed-off-by: sugumar <sugumar@ti.com>
---
 arch/arm/mach-davinci/da850.c              |   69 ++++++++++++++++++++++++++++
 arch/arm/mach-davinci/include/mach/da8xx.h |    1 +
 arch/arm/mach-davinci/include/mach/mux.h   |    3 +
 3 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
index 6b8331b..2adfb08 100644
--- a/arch/arm/mach-davinci/da850.c
+++ b/arch/arm/mach-davinci/da850.c
@@ -336,6 +336,13 @@ static struct clk aemif_clk = {
 	.flags		= ALWAYS_ENABLED,
 };
 
+static struct clk ecap_clk = {
+	.name	= "ecap",
+	.parent	= &pll0_sysclk2,
+	.lpsc	= DA8XX_LPSC1_ECAP,
+	.gpsc	= 1,
+};
+
 static struct clk_lookup da850_clks[] = {
 	CLK(NULL,		"ref",		&ref_clk),
 	CLK(NULL,		"pll0",		&pll0_clk),
@@ -377,6 +384,7 @@ static struct clk_lookup da850_clks[] = {
 	CLK("da8xx_lcdc.0",	NULL,		&lcdc_clk),
 	CLK("davinci_mmc.0",	NULL,		&mmcsd_clk),
 	CLK(NULL,		"aemif",	&aemif_clk),
+	 CLK(NULL,               "ecap",         &ecap_clk),
 	CLK(NULL,		NULL,		NULL),
 };
 
@@ -539,6 +547,9 @@ static const struct mux_config da850_pins[] = {
 	MUX_CFG(DA850, GPIO4_0,		10,	28,	15,	8,	false)
 	MUX_CFG(DA850, GPIO4_1,		10,	24,	15,	8,	false)
 	MUX_CFG(DA850, RTC_ALARM,	0,	28,	15,	2,	false)
+	MUX_CFG(DA850, ECAP0_APWM0,     2,      28,     15,     2,	false)
+	MUX_CFG(DA850, ECAP1_APWM1,     1,      28,     15,     4,	false)
+	MUX_CFG(DA850, ECAP2_APWM2,     1,      0,      15,     4,	false)
 #endif
 };
 
@@ -1029,6 +1040,64 @@ static int da850_round_armrate(struct clk *clk, unsigned long rate)
 }
 #endif
 
+
+#define DA8XX_ECAP0_BASE        0x01F06000
+
+static struct resource da850_ecap0_resource[] = {
+	{
+	.start	= DA8XX_ECAP0_BASE,
+	.end	= DA8XX_ECAP0_BASE + 0xfff,
+	.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device da850_ecap0_dev = {
+	.name	= "ecap",
+	.id	= 0,
+	.resource	= da850_ecap0_resource,
+	.num_resources	= ARRAY_SIZE(da850_ecap0_resource),
+};
+
+#define DA8XX_ECAP1_BASE        0x01F07000
+
+static struct resource da850_ecap1_resource[] = {
+	{
+	.start	= DA8XX_ECAP1_BASE,
+	.end	= DA8XX_ECAP1_BASE + 0xfff,
+	.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device da850_ecap1_dev = {
+	.name	= "ecap",
+	.id	= 1,
+	.resource	= da850_ecap1_resource,
+	.num_resources	= ARRAY_SIZE(da850_ecap1_resource),
+};
+
+#define DA8XX_ECAP2_BASE        0x01F08000
+
+static struct resource da850_ecap2_resource[] = {
+	{
+	.start	= DA8XX_ECAP2_BASE,
+	.end	= DA8XX_ECAP2_BASE + 0xfff,
+	.flags	= IORESOURCE_MEM,
+	},
+};
+
+static struct platform_device da850_ecap2_dev = {
+	.name	= "ecap",
+	.id	= 2,
+	.resource	= da850_ecap2_resource,
+	.num_resources	= ARRAY_SIZE(da850_ecap2_resource),
+};
+
+int __init da850_register_ecap(void)
+{
+	return platform_device_register(&da850_ecap2_dev);
+}
+
+
 int da850_register_pm(struct platform_device *pdev)
 {
 	int ret;
diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h
index 1b31a9a..63e36d5 100644
--- a/arch/arm/mach-davinci/include/mach/da8xx.h
+++ b/arch/arm/mach-davinci/include/mach/da8xx.h
@@ -81,6 +81,7 @@ int da850_register_cpufreq(void);
 int da8xx_register_cpuidle(void);
 void __iomem * __init da8xx_get_mem_ctlr(void);
 int da850_register_pm(struct platform_device *pdev);
+int da850_register_ecap(void);
 
 extern struct platform_device da8xx_serial_device;
 extern struct emac_platform_data da8xx_emac_pdata;
diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h
index de11aac..c82cb49 100644
--- a/arch/arm/mach-davinci/include/mach/mux.h
+++ b/arch/arm/mach-davinci/include/mach/mux.h
@@ -914,6 +914,9 @@ enum davinci_da850_index {
 	DA850_GPIO4_0,
 	DA850_GPIO4_1,
 	DA850_RTC_ALARM,
+	DA850_ECAP0_APWM0,
+	DA850_ECAP1_APWM1,
+	DA850_ECAP2_APWM2,
 };
 
 enum davinci_tnetv107x_index {
-- 
1.5.6

^ permalink raw reply related

* [PATCH 3/4] Modify the back light driver to support the new PWM framework
From: sugumar @ 2010-10-08 13:17 UTC (permalink / raw)
  To: linux-embedded; +Cc: davinci-linux-open-source, sugumar

This patch makes necessary changes to the existing backlight
driver to work with the new PWM framework

Signed-off-by: sugumar <sugumar@ti.com>
---
 drivers/pwm/Kconfig              |    1 +
 drivers/video/backlight/pwm_bl.c |   28 +++++++++++++++-------------
 include/linux/pwm_backlight.h    |    3 ++-
 3 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 6f32dd6..0c90d98 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -5,6 +5,7 @@
 menuconfig GENERIC_PWM
 	tristate "PWM Support"
 	depends on SYSFS
+	select HAVE_PWM
 	help
 	  This enables PWM support through the generic PWM API.
 	  If unsure, say N.
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 5504435..591a2fd 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -17,12 +17,12 @@
 #include <linux/fb.h>
 #include <linux/backlight.h>
 #include <linux/err.h>
-#include <linux/pwm.h>
+#include <linux/pwm/pwm.h>
 #include <linux/pwm_backlight.h>
 #include <linux/slab.h>
 
 struct pwm_bl_data {
-	struct pwm_device	*pwm;
+	struct pwm_channel	*pwm;
 	struct device		*dev;
 	unsigned int		period;
 	int			(*notify)(struct device *,
@@ -45,11 +45,13 @@ static int pwm_backlight_update_status(struct backlight_device *bl)
 		brightness = pb->notify(pb->dev, brightness);
 
 	if (brightness == 0) {
-		pwm_config(pb->pwm, 0, pb->period);
-		pwm_disable(pb->pwm);
+		pwm_set_period_ns(pb->pwm, pb->period);
+		pwm_set_duty_ns(pb->pwm, 0);
+		pwm_stop(pb->pwm);
 	} else {
-		pwm_config(pb->pwm, brightness * pb->period / max, pb->period);
-		pwm_enable(pb->pwm);
+		pwm_set_period_ns(pb->pwm, pb->period);
+		pwm_set_duty_ns(pb->pwm, brightness * pb->period / max);
+		pwm_start(pb->pwm);
 	}
 	return 0;
 }
@@ -94,7 +96,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 	pb->notify = data->notify;
 	pb->dev = &pdev->dev;
 
-	pb->pwm = pwm_request(data->pwm_id, "backlight");
+	pb->pwm = pwm_request(data->pwm_id, data->ch, "backlight");
 	if (IS_ERR(pb->pwm)) {
 		dev_err(&pdev->dev, "unable to request PWM for backlight\n");
 		ret = PTR_ERR(pb->pwm);
@@ -119,7 +121,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
 	return 0;
 
 err_bl:
-	pwm_free(pb->pwm);
+	pwm_release(pb->pwm);
 err_pwm:
 	kfree(pb);
 err_alloc:
@@ -135,9 +137,9 @@ static int pwm_backlight_remove(struct platform_device *pdev)
 	struct pwm_bl_data *pb = dev_get_drvdata(&bl->dev);
 
 	backlight_device_unregister(bl);
-	pwm_config(pb->pwm, 0, pb->period);
-	pwm_disable(pb->pwm);
-	pwm_free(pb->pwm);
+	pwm_set_duty_ns(pb->pwm, 0);
+	pwm_stop(pb->pwm);
+	pwm_release(pb->pwm);
 	kfree(pb);
 	if (data->exit)
 		data->exit(&pdev->dev);
@@ -153,8 +155,8 @@ static int pwm_backlight_suspend(struct platform_device *pdev,
 
 	if (pb->notify)
 		pb->notify(pb->dev, 0);
-	pwm_config(pb->pwm, 0, pb->period);
-	pwm_disable(pb->pwm);
+	pwm_set_duty_ns(pb->pwm, 0);
+	pwm_stop(pb->pwm);
 	return 0;
 }
 
diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h
index 01b3d75..eb325bb 100644
--- a/include/linux/pwm_backlight.h
+++ b/include/linux/pwm_backlight.h
@@ -5,7 +5,8 @@
 #define __LINUX_PWM_BACKLIGHT_H
 
 struct platform_pwm_backlight_data {
-	int pwm_id;
+	const char *pwm_id;
+	int ch;
 	unsigned int max_brightness;
 	unsigned int dft_brightness;
 	unsigned int pwm_period_ns;
-- 
1.5.6

^ permalink raw reply related

* [PATCH 4/4] davinci: da850/omap-l138 evm: Platform support for backlight driver
From: sugumar @ 2010-10-08 13:18 UTC (permalink / raw)
  To: linux-embedded; +Cc: davinci-linux-open-source, sugumar

on DA850/OMAP-L138 EVM, eCAP module is used to control the LCD
backlight. This patch registers the backlight device as platform device,
confgiures the pins for PWM output and also sets the backlight specific
data such as period, maximum intensity and default brightness.

Signed-off-by: sugumar <sugumar@ti.com>
---
 arch/arm/mach-davinci/board-da850-evm.c |   37 +++++++++++++++++++++++++++++++
 1 files changed, 37 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index b280efb..fe88e37 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -37,6 +37,7 @@
 #include <mach/nand.h>
 #include <mach/mux.h>
 
+#include <linux/pwm_backlight.h>
 #define DA850_EVM_PHY_MASK		0x1
 #define DA850_EVM_MDIO_FREQUENCY	2200000 /* PHY bus frequency */
 
@@ -48,6 +49,28 @@
 
 #define DA850_MII_MDIO_CLKEN_PIN	GPIO_TO_PIN(2, 6)
 
+
+#define DAVINCI_BACKLIGHT_MAX_BRIGHTNESS	250
+#define DAVINVI_BACKLIGHT_DEFAULT_BRIGHTNESS	250
+#define DAVINCI_PWM_PERIOD_NANO_SECONDS		(1000000 * 10)
+
+static struct platform_pwm_backlight_data da850evm_backlight_data = {
+	.pwm_id		= "ecap.2",
+	.ch		= 0,
+	.max_brightness	= DAVINCI_BACKLIGHT_MAX_BRIGHTNESS,
+	.dft_brightness	= DAVINVI_BACKLIGHT_DEFAULT_BRIGHTNESS,
+	.pwm_period_ns	= DAVINCI_PWM_PERIOD_NANO_SECONDS,
+};
+
+static struct platform_device da850evm_backlight = {
+	.name	= "pwm-backlight",
+	.id	= -1,
+	.dev	= {
+	.platform_data	= &da850evm_backlight_data,
+	},
+};
+
+
 static struct mtd_partition da850_evm_norflash_partition[] = {
 	{
 		.name           = "bootloaders + env",
@@ -633,6 +656,20 @@ static int __init da850_evm_config_emac(void)
 		pr_warning("da850_evm_init: emac registration failed: %d\n",
 				ret);
 
+	ret = davinci_cfg_reg(DA850_ECAP2_APWM2);
+	if (ret)
+		pr_warning("da850_evm_init:ecap mux failed: %d\n", ret);
+
+	ret = da850_register_ecap();
+	if (ret)
+		pr_warning("da850_evm_init: eCAP registration failed: %d\n",
+			ret);
+
+	ret = platform_device_register(&da850evm_backlight);
+	if (ret)
+		pr_warning("da850_evm_init: backlight device registration "
+			"failed: %d\n", ret);
+
 	return 0;
 }
 device_initcall(da850_evm_config_emac);
-- 
1.5.6

^ permalink raw reply related

* logfs segfaults at umount time
From: Michael Opdenacker @ 2010-10-10 16:11 UTC (permalink / raw)
  To: joern; +Cc: logfs, linux-embedded mailing list, Tim Bird

Hi Jörn,

I'm running some flash filesystem benchmarks for CELF, and I'm facing
kernel segfaults when I try to umount my logfs filesystem.

This happens with both 2.6.35 and 2.6.36-rc7. See the below trace.

Has anyone else faced the same kind of issue? Don't hesitate to get back
to me if you need more details or if there are commands you would like
me to run.

Thank you in advance for your help,

Cheers,

Michael.


root@calao:~# cat /proc/cmdline
console=ttyS0,115200n8 root=/dev/nfs ip=192.168.2.100
nfsroot=192.168.2.1:/home/mike/work/celf/RFS mtdparts=atmel_nand:128m(Main)
root@calao:~# flash_eraseall /dev/mtd0
Erasing 128 Kibyte @ 8000000 -- 100 % complete.
root@calao:~# mkfs.logfs /dev/mtdblock0
Will create filesystem with the following details:
              hex:   decimal:
fssize=    8000000  134217728
segsize=     40000     262144
blocksize=    1000       4096
writesize=       1          1

Do you wish to continue (yes/no)
yes

Finished generating LogFS
root@calao:~# mount -t logfs /dev/mtdblock0 /mnt/flash/
root@calao:~# cp -rf /fs/8m/* /mnt/flash/
eth0: TX underrun, resetting buffers
eth0: TX underrun, resetting buffers
root@calao:~# umount /mnt/flash/
kernel BUG at fs/logfs/segment.c:858!
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c14a4000
[00000000] *pgd=23a3c031, *pte=00000000, *ppte=00000000
Internal error: Oops: 817 [#1]
last sysfs file: /sys/devices/virtual/vc/vcs6/uevent
Modules linked in: logfs zlib_deflate zlib_inflate
CPU: 0    Not tainted  (2.6.36-rc7 #1)
PC is at __bug+0x18/0x24
LR is at __bug+0x14/0x24
pc : [<c0025764>]    lr : [<c0025760>]    psr: 20000013
sp : c2d91e18  ip : 00000000  fp : c2d91e84
r10: c03c3b80  r9 : 00000009  r8 : 00000000
r7 : c2d91e60  r6 : c3601d58  r5 : c3601d58  r4 : c03c3b80
r3 : 00000000  r2 : c033e368  r1 : 60000013  r0 : 0000002c
Flags: nzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 0005317f  Table: 214a4000  DAC: 00000015
Process umount (pid: 1292, stack limit = 0xc2d90270)
Stack: (0xc2d91e18 to 0xc2d92000)
1e00:                                                       bf022620
bf022630
1e20: bf022620 c0078360 ffffffff c0078a04 ffffffff c3601d58 c2d91e60
00000000
1e40: 0000000e 00000081 ffffffff c3601d58 c2d91e60 c0078b2c 00000000
00000000
1e60: 0000000e 00000000 c03e01a0 c03e01c0 c03e01e0 c03df100 c03df120
c03df140
1e80: c03df160 c03df180 c03df1a0 c03c3b80 c03986c0 c0398e40 c03c3ba0
c03c3bc0
1ea0: ffffffff ffffffff ffffffff 00000000 c2c1f800 c2d90000 c380dc60
c380dc78
1ec0: 00000000 c0078d70 ffffffff ffffffff c3601760 c3601cb8 c2c1f800
bf022104
1ee0: c3691830 c3601760 c035e708 c3601cb8 c035e708 c2c1f800 c035e848
c2d90000
1f00: c380dc60 c380dc78 00000000 c00a90f4 c3601cb8 c00a99b8 c2c1f800
c2d90000
1f20: bf0272e4 c035e848 c2d90000 c0098d04 c2c1f800 c3a7b000 c380dc60
bf0248e4
1f40: c2c1f800 bf028228 c380dc60 c0097f70 00000000 c2d91f68 c380dc60
c00ad3ac
1f60: 4003a000 c380dc78 c2d91f68 c2d91f68 c2d91f70 c2d91f70 c380dc60
c35fbab8
1f80: c2d90000 0001c530 0001aa1c 0001c518 00000034 c0021fe8 c2d90000
00000000
1fa0: 0001bab4 c0021e40 0001c530 0001aa1c 0001c530 00000000 00000048
00000000
1fc0: 0001c530 0001aa1c 0001c518 00000034 00000000 00000000 00000000
0001bab4
1fe0: 0001c560 be983c08 402c1c5c 402c1c7c 60000010 0001c530 e7d82003
e7c12007
[<c0025764>] (__bug+0x18/0x24) from [<bf022630>]
(map_invalidatepage+0x10/0x18 [logfs])
[<bf022630>] (map_invalidatepage+0x10/0x18 [logfs]) from [<c0078360>]
(do_invalidatepage+0x20/0x28)
[<c0078360>] (do_invalidatepage+0x20/0x28) from [<c0078a04>]
(truncate_inode_page+0xd4/0xe4)
[<c0078a04>] (truncate_inode_page+0xd4/0xe4) from [<c0078b2c>]
(truncate_inode_pages_range+0x118/0x344)
[<c0078b2c>] (truncate_inode_pages_range+0x118/0x344) from [<c0078d70>]
(truncate_inode_pages+0x18/0x20)
[<c0078d70>] (truncate_inode_pages+0x18/0x20) from [<bf022104>]
(logfs_evict_inode+0x40/0x188 [logfs])
[<bf022104>] (logfs_evict_inode+0x40/0x188 [logfs]) from [<c00a90f4>]
(evict+0x20/0xb0)
[<c00a90f4>] (evict+0x20/0xb0) from [<c00a99b8>] (iput+0x178/0x254)
[<c00a99b8>] (iput+0x178/0x254) from [<c0098d04>]
(generic_shutdown_super+0x78/0xe4)
[<c0098d04>] (generic_shutdown_super+0x78/0xe4) from [<bf0248e4>]
(logfs_kill_sb+0x40/0xf8 [logfs])
[<bf0248e4>] (logfs_kill_sb+0x40/0xf8 [logfs]) from [<c0097f70>]
(deactivate_locked_super+0x44/0x58)
[<c0097f70>] (deactivate_locked_super+0x44/0x58) from [<c00ad3ac>]
(sys_umount+0x6c/0x33c)
[<c00ad3ac>] (sys_umount+0x6c/0x33c) from [<c0021e40>]
(ret_fast_syscall+0x0/0x2c)
Code: e1a01000 e59f000c eb096367 e3a03000 (e5833000)
---[ end trace f09ed3af1a44d711 ]---
------------[ cut here ]------------
WARNING: at kernel/exit.c:899 do_exit+0x5d4/0x624()
Modules linked in: logfs zlib_deflate zlib_inflate
[<c0027ae8>] (unwind_backtrace+0x0/0xf4) from [<c003a310>]
(warn_slowpath_common+0x4c/0x64)
[<c003a310>] (warn_slowpath_common+0x4c/0x64) from [<c003a344>]
(warn_slowpath_null+0x1c/0x24)
[<c003a344>] (warn_slowpath_null+0x1c/0x24) from [<c003e0a4>]
(do_exit+0x5d4/0x624)
[<c003e0a4>] (do_exit+0x5d4/0x624) from [<c0025c90>] (die+0x188/0x1c0)
[<c0025c90>] (die+0x188/0x1c0) from [<c0029044>]
(__do_kernel_fault+0x64/0x84)
[<c0029044>] (__do_kernel_fault+0x64/0x84) from [<c0029198>]
(do_page_fault+0x134/0x1d8)
[<c0029198>] (do_page_fault+0x134/0x1d8) from [<c00212e4>]
(do_DataAbort+0x34/0x98)
[<c00212e4>] (do_DataAbort+0x34/0x98) from [<c0021a6c>]
(__dabt_svc+0x4c/0x60)
Exception stack(0xc2d91dd0 to 0xc2d91e18)
1dc0:                                     0000002c 60000013 c033e368
00000000
1de0: c03c3b80 c3601d58 c3601d58 c2d91e60 00000000 00000009 c03c3b80
c2d91e84
1e00: 00000000 c2d91e18 c0025760 c0025764 20000013 ffffffff
[<c0021a6c>] (__dabt_svc+0x4c/0x60) from [<c0025764>] (__bug+0x18/0x24)
[<c0025764>] (__bug+0x18/0x24) from [<bf022630>]
(map_invalidatepage+0x10/0x18 [logfs])
[<bf022630>] (map_invalidatepage+0x10/0x18 [logfs]) from [<c0078360>]
(do_invalidatepage+0x20/0x28)
[<c0078360>] (do_invalidatepage+0x20/0x28) from [<c0078a04>]
(truncate_inode_page+0xd4/0xe4)
[<c0078a04>] (truncate_inode_page+0xd4/0xe4) from [<c0078b2c>]
(truncate_inode_pages_range+0x118/0x344)
[<c0078b2c>] (truncate_inode_pages_range+0x118/0x344) from [<c0078d70>]
(truncate_inode_pages+0x18/0x20)
[<c0078d70>] (truncate_inode_pages+0x18/0x20) from [<bf022104>]
(logfs_evict_inode+0x40/0x188 [logfs])
[<bf022104>] (logfs_evict_inode+0x40/0x188 [logfs]) from [<c00a90f4>]
(evict+0x20/0xb0)
[<c00a90f4>] (evict+0x20/0xb0) from [<c00a99b8>] (iput+0x178/0x254)
[<c00a99b8>] (iput+0x178/0x254) from [<c0098d04>]
(generic_shutdown_super+0x78/0xe4)
[<c0098d04>] (generic_shutdown_super+0x78/0xe4) from [<bf0248e4>]
(logfs_kill_sb+0x40/0xf8 [logfs])
[<bf0248e4>] (logfs_kill_sb+0x40/0xf8 [logfs]) from [<c0097f70>]
(deactivate_locked_super+0x44/0x58)
[<c0097f70>] (deactivate_locked_super+0x44/0x58) from [<c00ad3ac>]
(sys_umount+0x6c/0x33c)
[<c00ad3ac>] (sys_umount+0x6c/0x33c) from [<c0021e40>]
(ret_fast_syscall+0x0/0x2c)
---[ end trace f09ed3af1a44d712 ]---
Segmentation fault

-- 
Michael Opdenacker, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
+ 33 621 604 642

^ permalink raw reply

* [PATCH 00/16] pramfs: persistent and protected RAM Filesystem
From: Marco Stornelli @ 2010-10-10 16:26 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Tim Bird, Linux FS Devel

Hi all,

after a lot of improvement, test, bug fix and new features, it's the
moment for third round with the kernel community to submit PRAMFS for
mainline.

Since the last review (June 2009) a lot of things are changed:

- removed any reference of BKL
- fixed the endianess for the fs layout
- added support for extended attributes, ACLs and security labels
- moved out any pte manipulations from fs and inserted them in mm
- implemented the new truncate convention
- fixed problems with 64bit archs

...and much more. Complete "story" in the ChangeLog inserted in the
documentation file.

In addition, in the web site tech page (http:\\pramfs.sourceforge.net),
you can find a lot of information about implementation, technical
details, benchmarking and so on.

All the work to mainline this feature is sponsored by the CE Linux Forum.

Regards,
Marco

^ permalink raw reply

* [PATCH 01/16] pramfs: documentation
From: Marco Stornelli @ 2010-10-10 16:27 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Documentation for PRAMFS.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/Documentation/filesystems/pramfs.txt linux-2.6.36/Documentation/filesystems/pramfs.txt
--- linux-2.6.36-orig/Documentation/filesystems/pramfs.txt	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/Documentation/filesystems/pramfs.txt	2010-09-25 15:10:41.000000000 +0200
@@ -0,0 +1,295 @@
+
+PRAMFS Overview
+===============
+
+Many embedded systems have a block of non-volatile RAM seperate from
+normal system memory, i.e. of which the kernel maintains no memory page
+descriptors. For such systems it would be beneficial to mount a
+fast read/write filesystem over this "I/O memory", for storing frequently
+accessed data that must survive system reboots and power cycles. An
+example usage might be system logs under /var/log, or a user address
+book in a cell phone or PDA.
+
+Linux traditionally had no support for a persistent, non-volatile RAM-based
+filesystem, persistent meaning the filesystem survives a system reboot
+or power cycle intact. The RAM-based filesystems such as tmpfs and ramfs
+have no actual backing store but exist entirely in the page and buffer
+caches, hence the filesystem disappears after a system reboot or
+power cycle.
+
+A relatively straight-forward solution is to write a simple block driver
+for the non-volatile RAM, and mount over it any disk-based filesystem such
+as ext2, ext3, ext4, etc.
+
+But the disk-based fs over non-volatile RAM block driver approach has
+some drawbacks:
+
+1. Complexity of disk-based fs: disk-based filesystems such as ext2/ext3/ext4
+   were designed for optimum performance on spinning disk media, so they
+   implement features such as block groups, which attempts to group inode data
+   into a contiguous set of data blocks to minimize disk seeking when accessing
+   files. For RAM there is no such concern; a file's data blocks can be
+   scattered throughout the media with no access speed penalty at all. So block
+   groups in a filesystem mounted over RAM just adds unnecessary
+   complexity. A better approach is to use a filesystem specifically
+   tailored to RAM media which does away with these disk-based features.
+   This increases the efficient use of space on the media, i.e. more
+   space is dedicated to actual file data storage and less to meta-data
+   needed to maintain that file data.
+
+2. Different problems between disks and RAM: Because PRAMFS attempts to avoid
+   filesystem corruption caused by kernel bugs, dirty pages in the page cache
+   are not allowed to be written back to the backing-store RAM. This way, an
+   errant write into the page cache will not get written back to the filesystem.
+   However, if the backing-store RAM is comparable in access speed to system
+   memory, the penalty of not using caching is minimal. With this consideration
+   better to move file data directly between the user buffers and the backing
+   store RAM, i.e. use direct I/O. This prevents the unnecessary populating of
+   the page cache with dirty pages. However direct I/O has to be enabled at
+   every file open. To enable direct I/O at all times for all regular files
+   requires either that applications be modified to include the O_DIRECT flag on
+   all file opens, or that the filesystem used performs direct I/O by default.
+
+The Persistent/Protected RAM Special Filesystem (PRAMFS) is a read/write
+filesystem that has been designed to address these issues. PRAMFS is targeted
+to fast I/O memory, and if the memory is non-volatile, the filesystem will be
+persistent.
+
+In PRAMFS, direct I/O is enabled across all files in the filesystem, in other
+words the O_DIRECT flag is forced on every open of a PRAMFS file. Also, file
+I/O in the PRAMFS is always synchronous. There is no need to block the current
+process while the transfer to/from the PRAMFS is in progress, since one of
+the requirements of the PRAMFS is that the filesystem exist in fast RAM. So
+file I/O in PRAMFS is always direct, synchronous, and never blocks.
+
+The data organization in PRAMFS can be thought of as an extremely simplified
+version of ext2, such that the ratio of data to meta-data is very high.
+
+PRAMFS supports the execute-in-place. With Xip, instead of keeping data in the
+page cache, the need to have a page cache copy is eliminated completely.
+Read&write type operations are performed directly from/to the memory. For file
+mappings, the RAM itself is mapped directly into userspace. Xip, in addition,
+speed-up the applications start-up time because it removes the needs of any
+copies.
+
+PRAMFS is write protected. The page table entries that map the backing-store
+RAM are normally marked read-only. Write operations into the filesystem
+temporarily mark the affected pages as writeable, the write operation is
+carried out with locks held, and then the pte is marked read-only again.
+This feature provides protection against filesystem corruption caused by errant
+writes into the RAM due to kernel bugs for instance. In case there are systems
+where the write protection is not possible (for instance the RAM cannot be
+mapped with page tables), this feature can be disabled via
+CONFIG_PRAMFS_WRITE_PROTECT config option.
+
+PRAMFS supports extended attributes, ACLs and security labels.
+
+In summary, PRAMFS is a light-weight, space-efficient special filesystem that
+is ideal for systems with a block of fast non-volatile RAM that need to access
+data on it using a standard filesytem interface.
+
+Supported mount options
+=======================
+
+The PRAMFS currently requires one mount option, and there are several
+optional mount options:
+
+physaddr=	Required. It tells PRAMFS the physical address of the
+		start of the RAM that makes up the filesystem. The
+		physical address must be located on a page boundary.
+
+init=		Optional. It is used to initialize the memory to an
+		empty filesystem. Any data in an existing filesystem
+		will be lost if this option is given. The parameter to
+		"init=" is the RAM in kilo/mega/giga bytes.
+
+bs=		Optional. It is used to specify a block size. It is
+		ignored if the "init=" option is not specified, since
+		otherwise the block size is read from the PRAMFS
+		super-block. The default blocksize is 2048 bytes,
+		and the allowed block sizes are 512, 1024, 2048, and
+		4096.
+
+bpi=		Optional. It is used to specify the bytes per inode
+		ratio, i.e. For every N bytes in the filesystem, an
+		inode will be created. This behaves the same as the "-i"
+		option to mke2fs. It is ignored if the "init=" option is
+		not specified.
+
+N=		Optional. It is used to specify the number of inodes to
+		allocate in the inode table. If the option is not
+		specified, the bytes-per-inode ratio is used the
+		calculate the number of inodes. If neither the "N=" or
+		"bpi=" options are specified, the default behavior is to
+		reserve 5% of the total space in the filesystem for the
+		inode table. This option behaves the same as the "-N"
+		option to mke2fs. It is ignored if the "init=" option is
+		not specified.
+
+Examples:
+
+mount -t pramfs -o physaddr=0x20000000,init=1M,bs=1k none /mnt/pram
+
+This example locates the filesystem at physical address 0x20000000, and
+also requests an empty filesystem be initialized, of total size of one
+megabytes and blocksize of one kilobytes. The mount point is /mnt/pram.
+
+mount -t pramfs -o physaddr=0x20000000 none /mnt/pram
+
+This example locates the filesystem at physical address 0x20000000 as in
+the first example, but uses the intact filesystem that already exists.
+
+Current Limitations
+===================
+
+- The RAM used for PRAMFS must be directly addressable.
+
+- PRAMFS does not support hard links.
+
+- PRAMFS supports only private memory mappings. This allows most
+  executables to run, but programs that attempt shared memory
+  mappings, such as X apps that use X shared memory, will fail.
+
+- PRAMFS does not support quota settings.
+
+Further Documentation
+=====================
+
+If you are interested in the internal design of PRAMFS, there is
+documentation available at the Sourceforge PRAMFS home page at
+http://pramfs.sourceforge.net/.
+
+Please send bug reports/comments/feedback to the pramfs development
+list at sourceforge: pramfs-devel@lists.sourceforge.net.
+
+
+ChangeLog
+=========
+
+1.2.1
+	- kernel 2.6.36
+	- replaced d_add with d_splice_alias in pram_lookup to support NFS
+	- reworked pram_get_parent
+	- added disable/enable irq in the pram_{memlock|memunlock}_range
+	  in order to avoid "open windows" with writeable memory
+	- when xattr support was enabled, the semaphore xattr_sem wasn't
+	  initialized
+	- implemented new truncate convention
+	- removed pram_truncate_page
+	- fixed a possible oops in pram_put_super, a super block field was
+	  used after iounmap
+	- fixed file holes management
+	- super block s_size field now is 64bit
+
+1.2.0
+	- kernel 2.6.35
+	- added support for setattr callback, now truncate command works
+	- added support for extended attributes, ACLs, security labels
+	- fixed scalability problem with truncate mutex
+	- fixed bitmap endianess and problems with 64bit archs (bug 3013785)
+	- replaced functions pram_fill_new_inode and pram_fill_inode with
+	  pram_read_inode and pram_iget
+	- added support for export operations, now it's possible to use
+	  NFS to export pram mount points
+	- added generation field in the inode struct for file versioning
+	  (for NFS)
+	- fixed the settings of fields s_maxbytes and s_time_gran in the super
+	  block struct
+	- removed any reference to quota operations, they are not supported
+
+1.1.6
+	- kernel 2.6.33
+	- added preempt_enable/disable in pram_{memlock|memunlock}_range in
+	  order to avoid race conditions
+	- replaced pram_unlock_* and pram_lock_* with pram_memlock_* and
+	  pram_memunlock_*
+
+1.1.5
+	- kernel 2.6.31
+	- reworked pram_writeable in order to use the funcions
+	  write_on_kernel_pte_range and write_off_kernel_pte_range
+	- added writeable_kernel_pte_range in order to compile on ARM arch
+	- added truncate_inode_pages() in pram_delete_inode()
+	- fixed an endianess conversion error in super.c, df command didn't work
+	- fixed an error in pram_find_data_block(), some code was relative to
+	  32-bit design yet
+	- fixed an error in pram_free_block(), endianess conversion not needed
+	- fixed lock usage, possible deadlock recognized by lockdep
+
+1.1.4
+	- fs layout now endianess indipendent
+	- debug functions replaced with pr_*
+	- changed CONFIG_PRAMFS_NOWP to CONFIG_PRAMFS_WRITE_PROTECT and
+	  revert the meaning
+	- removed the rootfs mounting
+	- redundant superblock
+	- crc performed with crc16
+	- moved out pte manipulations
+	- used only flush_kernel_tlb_range instead of non standard
+	  flush_kernel_tlb_page
+	- replaced BKL with a mutex
+
+1.1.3:
+	- kernel 2.6.30
+	- replaced DQUOT macros with vfs_dq_* functions in inode.c
+
+1.1.2:
+	- kernel 2.6.29
+	- replaced current->fsuid and current->fsgid in inode.c with
+	  current_fsuid() and current_fsgid() macro
+	- replaced pram_fsync with simple_file_sync (deleted fsync.c)
+	- removed ioctl.c
+	- fix the minus inside pram_sync_super and pram_sync_inode
+	  inline functions
+	- now flags must be explicit in pram_lock/unlock_inode,
+	  pram_lock/unlock_super and in pram_lock/unlock_block
+	- fix compiler warning about unused flags variable
+	- now multiple instances of pramfs are allowed
+	- added management of mode, uid and gid mount options
+	- renamed find_pramfs_super() in pram_get_super() and changed its
+	  behaviour to remove the dependecy from a kernel symbol not exported
+	- added superblock operation pram_show_options
+	- added pram_parse_options function
+	- added checks for some mount parameters to avoid kernel crashes
+	- now a physical address of zero is allowed
+	- added the use of request_mem_region_exclusive
+	- now phys_addr in the struct pram_sb_info is phys_addr_t type
+
+1.1.1:
+	- kernel 2.6.28.1
+	- now the code is compliant to kernel coding style
+	- replaced typedef pram_off_t with the standard off_t
+
+1.1.0
+	- kernel 2.6.27
+	- added xip feature.
+
+1.0.4:
+	- kernel 2.6.10
+	- include <asm/tlbflush.h> in wprotect.c for
+	  flush_tlb_kernel_range().
+	- fixed a bug in pram_mknod(). The pramfs inode needs
+	  updating after calling init_special_inode() to update
+	  the rdev.
+1.0.3:
+	- kernel 2.6.9.
+	- __ioremap() definition not consistent across archs, use
+	  ioremap() instead.
+	- flush_tlb_kernel_page() is only available on some archs.
+	- fixed bug in pram_fill_super(): root inode pointer needs
+	  to be recalculated after remapping whole fs.
+1.0.2:
+	- kernel 2.6.4.
+	- use pram_truncate() in pram_delete_inode().
+	- dangling pram_lock_inode() removed in pram_truncate_blocks().
+	- edits to this README
+
+1.0.1:
+	- port to kernel 2.6.3.
+	- implement direct_IO() method instead of custom file read/write
+	  methods.
+	- do away with __ioremap_readonly() requirement.
+	- implement inode truncate() method.
+
+1.0.0:
+	- Started ChangeLog (kernel 2.4.22).
diff -Nurp linux-2.6.36-orig/Documentation/filesystems/xip.txt linux-2.6.36/Documentation/filesystems/xip.txt
--- linux-2.6.36-orig/Documentation/filesystems/xip.txt	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/Documentation/filesystems/xip.txt	2010-09-14 18:49:52.000000000 +0200
@@ -49,6 +49,8 @@ This address space operation is mutually
 do page cache read/write operations.
 The following filesystems support it as of today:
 - ext2: the second extended filesystem, see Documentation/filesystems/ext2.txt
+- pramfs: persistent and protected RAM filesystem, see
+  Documentation/filesystems/pramfs.txt
  A set of file operations that do utilize get_xip_page can be found in
 mm/filemap_xip.c . The following file operation implementations are provided:
 

^ permalink raw reply

* [PATCH 02/16] pramfs: super block operations
From: Marco Stornelli @ 2010-10-10 16:28 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Super block operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/super.c linux-2.6.36/fs/pramfs/super.c
--- linux-2.6.36-orig/fs/pramfs/super.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/super.c	2010-09-25 14:09:47.000000000 +0200
@@ -0,0 +1,740 @@
+/*
+ * FILE NAME fs/pramfs/super.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Super block operations.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/parser.h>
+#include <linux/vfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
+#include <linux/mm.h>
+#include <linux/ctype.h>
+#include <linux/bitops.h>
+#include <linux/magic.h>
+#include <linux/exportfs.h>
+#include <linux/random.h>
+#include "xattr.h"
+#include "pram.h"
+
+static struct super_operations pram_sops;
+static const struct export_operations pram_export_ops;
+static struct kmem_cache *pram_inode_cachep;
+
+#ifdef CONFIG_PRAMFS_TEST
+static void *first_pram_super;
+
+struct pram_super_block *get_pram_super(void)
+{
+	return (struct pram_super_block *)first_pram_super;
+}
+EXPORT_SYMBOL(get_pram_super);
+#endif
+
+static void pram_set_blocksize(struct super_block *sb, unsigned long size)
+{
+	int bits;
+
+	/*
+	* We've already validated the user input and the value here must be
+	* between PRAM_MAX_BLOCK_SIZE and PRAM_MIN_BLOCK_SIZE
+	* and it must be a power of 2.
+	*/
+	bits = fls(size) - 1;
+	sb->s_blocksize_bits = bits;
+	sb->s_blocksize = (1<<bits);
+}
+
+static inline void *pram_ioremap(phys_addr_t phys_addr, ssize_t size)
+{
+	void *retval;
+
+	/*
+	 * NOTE: Userland may not map this resource, we will mark the region so
+	 * /dev/mem and the sysfs MMIO access will not be allowed. This
+	 * restriction depends on STRICT_DEVMEM option. If this option is
+	 * disabled or not available we mark the region only as busy.
+	 */
+	retval = request_mem_region_exclusive(phys_addr, size, "pramfs");
+	if (!retval)
+		goto fail;
+
+	retval = ioremap_nocache(phys_addr, size);
+
+	if (retval)
+		wrprotect(retval, size);
+fail:
+	return retval;
+}
+
+static loff_t pram_max_size(int bits)
+{
+	loff_t res;
+	res = (1ULL << (3*bits - 6)) - 1;
+
+	if (res > MAX_LFS_FILESIZE)
+		res = MAX_LFS_FILESIZE;
+
+	pram_info("Max file size %llu bytes", res);
+	return res;
+}
+
+enum {
+	Opt_addr, Opt_bpi, Opt_size,
+	Opt_num_inodes, Opt_mode, Opt_uid,
+	Opt_gid, Opt_blocksize, Opt_err
+};
+
+static const match_table_t tokens = {
+	{Opt_bpi,	"physaddr=%x"},
+	{Opt_bpi,	"bpi=%u"},
+	{Opt_size,	"init=%s"},
+	{Opt_num_inodes, "N=%u"},
+	{Opt_mode,	"mode=%o"},
+	{Opt_uid,	"uid=%u"},
+	{Opt_gid,	"gid=%u"},
+	{Opt_blocksize,	"bs=%s"},
+	{Opt_err,	NULL},
+};
+
+static phys_addr_t get_phys_addr(void **data)
+{
+	phys_addr_t phys_addr;
+	char *options = (char *) *data;
+
+	if (!options || strncmp(options, "physaddr=", 9) != 0)
+		return (phys_addr_t)ULLONG_MAX;
+	options += 9;
+	phys_addr = (phys_addr_t)simple_strtoull(options, &options, 0);
+	if (*options && *options != ',') {
+		pram_err("Invalid phys addr specification: %s\n",
+		       (char *) *data);
+		return (phys_addr_t)ULLONG_MAX;
+	}
+	if (phys_addr & (PAGE_SIZE - 1)) {
+		pram_err("physical address 0x%16llx for pramfs isn't "
+			  "aligned to a page boundary\n",
+			  (u64)phys_addr);
+		return (phys_addr_t)ULLONG_MAX;
+	}
+	if (*options == ',')
+		options++;
+	*data = (void *) options;
+	return phys_addr;
+}
+
+static int pram_parse_options(char *options, struct pram_sb_info *sbi)
+{
+	char *p, *rest;
+	substring_t args[MAX_OPT_ARGS];
+	int option;
+
+	if (!options)
+		return 0;
+
+	while ((p = strsep(&options, ",")) != NULL) {
+		int token;
+		if (!*p)
+			continue;
+
+		token = match_token(p, tokens, args);
+		switch (token) {
+			case Opt_addr: {
+				/* physaddr managed in get_phys_addr() */
+				break;
+			}
+			case Opt_bpi: {
+				if (match_int(&args[0], &option))
+					goto bad_val;
+				sbi->bpi = option;
+				break;
+			}
+			case Opt_uid: {
+				if (match_int(&args[0], &option))
+					goto bad_val;
+				sbi->uid = option;
+				break;
+			}
+			case Opt_gid: {
+				if (match_int(&args[0], &option))
+					goto bad_val;
+				sbi->gid = option;
+				break;
+			}
+			case Opt_mode: {
+				if (match_octal(&args[0], &option))
+					goto bad_val;
+				sbi->mode = option & 01777U;
+				break;
+			}
+			case Opt_size: {
+				/* memparse() will accept a K/M/G without a digit */
+				if (!isdigit(*args[0].from))
+					goto bad_val;
+				sbi->initsize = memparse(args[0].from, &rest);
+				break;
+			}
+			case Opt_num_inodes: {
+				if (match_int(&args[0], &option))
+					goto bad_val;
+				sbi->num_inodes = option;
+				break;
+			}
+			case Opt_blocksize: {
+				/* memparse() will accept a K/M/G without a digit */
+				if (!isdigit(*args[0].from))
+					goto bad_val;
+				sbi->blocksize = memparse(args[0].from, &rest);
+				if (sbi->blocksize < PRAM_MIN_BLOCK_SIZE ||
+					sbi->blocksize > PRAM_MAX_BLOCK_SIZE ||
+					!is_power_of_2(sbi->blocksize))
+					goto bad_val;
+				break;
+			}
+			default: {
+				pram_err("Bad mount option: \"%s\"\n", p);
+				return -EINVAL;
+				break;
+			}
+		}
+	}
+
+	return 0;
+
+bad_val:
+	pram_err("Bad value '%s' for mount option '%s'\n", args[0].from, p);
+	return -EINVAL;
+}
+
+static struct pram_inode *pram_init(struct super_block *sb, unsigned long size)
+{
+	unsigned long bpi, num_inodes, bitmap_size, blocksize, num_blocks;
+	u64 bitmap_start;
+	struct pram_inode *root_i;
+	struct pram_super_block *super;
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+
+	pram_info("creating an empty pramfs of size %lu\n", size);
+#ifdef CONFIG_PRAMFS_XIP
+	pram_info("pramfs with xip enabled\n");
+#endif
+	sbi->virt_addr = pram_ioremap(sbi->phys_addr, size);
+	if (!sbi->virt_addr) {
+		pram_err("ioremap of the pramfs image failed\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+#ifdef CONFIG_PRAMFS_TEST
+	if (!first_pram_super)
+		first_pram_super = sbi->virt_addr;
+#endif
+
+	if (!sbi->blocksize)
+		blocksize = PRAM_DEF_BLOCK_SIZE;
+	else
+		blocksize = sbi->blocksize;
+
+	pram_set_blocksize(sb, blocksize);
+	blocksize = sb->s_blocksize;
+
+	if (sbi->blocksize && sbi->blocksize != blocksize)
+		sbi->blocksize = blocksize;
+
+	if (size < blocksize) {
+		pram_err("size smaller then block size\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	if (!sbi->bpi)
+		/* default is that 5% of the filesystem is
+		   devoted to the inode table */
+		bpi = 20 * PRAM_INODE_SIZE;
+	else
+		bpi = sbi->bpi;
+
+	if (!sbi->num_inodes)
+		num_inodes = size / bpi;
+	else
+		num_inodes = sbi->num_inodes;
+
+	/* up num_inodes such that the end of the inode table
+	   (and start of bitmap) is on a block boundary */
+	bitmap_start = (PRAM_SB_SIZE*2) + (num_inodes<<PRAM_INODE_BITS);
+	if (bitmap_start & (blocksize - 1))
+		bitmap_start = (bitmap_start + blocksize) &
+			~(blocksize-1);
+	num_inodes = (bitmap_start - (PRAM_SB_SIZE*2)) >> PRAM_INODE_BITS;
+
+	if (sbi->num_inodes && num_inodes != sbi->num_inodes)
+		sbi->num_inodes = num_inodes;
+
+	num_blocks = (size - bitmap_start) >> sb->s_blocksize_bits;
+
+	if (!num_blocks) {
+		pram_err("num blocks equals to zero\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* calc the data blocks in-use bitmap size in bytes */
+	if (num_blocks & 7)
+		bitmap_size = ((num_blocks + 8) & ~7) >> 3;
+	else
+		bitmap_size = num_blocks >> 3;
+	/* round it up to the nearest blocksize boundary */
+	if (bitmap_size & (blocksize - 1))
+		bitmap_size = (bitmap_size + blocksize) & ~(blocksize-1);
+
+	pram_info("blocksize %lu, num inodes %lu, num blocks %lu\n",
+		  blocksize, num_inodes, num_blocks);
+	pram_dbg("bitmap start 0x%08x, bitmap size %lu\n",
+		 (unsigned int)bitmap_start, bitmap_size);
+	pram_dbg("max name length %d\n", PRAM_NAME_LEN);
+
+	super = pram_get_super(sb);
+	pram_memunlock_range(super, bitmap_start + bitmap_size);
+
+	/* clear out super-block and inode table */
+	memset(super, 0, bitmap_start);
+	super->s_size = cpu_to_be64(size);
+	super->s_blocksize = cpu_to_be32(blocksize);
+	super->s_inodes_count = cpu_to_be32(num_inodes);
+	super->s_blocks_count = cpu_to_be32(num_blocks);
+	super->s_free_inodes_count = cpu_to_be32(num_inodes - 1);
+	super->s_bitmap_blocks = cpu_to_be32(bitmap_size >> sb->s_blocksize_bits);
+	super->s_free_blocks_count = cpu_to_be32(num_blocks - be32_to_cpu(super->s_bitmap_blocks));
+	super->s_free_inode_hint = cpu_to_be32(1);
+	super->s_bitmap_start = cpu_to_be64(bitmap_start);
+	super->s_magic = cpu_to_be16(PRAM_SUPER_MAGIC);
+	pram_sync_super(super);
+
+	root_i = pram_get_inode(sb, PRAM_ROOT_INO);
+
+	root_i->i_mode = cpu_to_be32(sbi->mode);
+	root_i->i_mode = cpu_to_be16(root_i->i_mode | S_IFDIR);
+	root_i->i_uid = cpu_to_be32(sbi->uid);
+	root_i->i_gid = cpu_to_be32(sbi->gid);
+	root_i->i_links_count = cpu_to_be16(2);
+	root_i->i_d.d_parent = cpu_to_be64(PRAM_ROOT_INO);
+	pram_sync_inode(root_i);
+
+	pram_init_bitmap(sb);
+
+	pram_memlock_range(super, bitmap_start + bitmap_size);
+
+	return root_i;
+}
+
+static int pram_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct pram_super_block *super, *super_redund;
+	struct pram_inode *root_i;
+	struct pram_sb_info *sbi = NULL;
+	u64 root_offset;
+	unsigned long blocksize, initsize = 0;
+	u32 random = 0;
+	int retval = -EINVAL;
+
+	sbi = kzalloc(sizeof(struct pram_sb_info), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+	sb->s_fs_info = sbi;
+
+#ifdef CONFIG_PRAMFS_XATTR
+	spin_lock_init(&sbi->desc_tree_lock);
+	sbi->desc_tree.rb_node = NULL;
+#endif
+
+	sbi->phys_addr = get_phys_addr(&data);
+	if (sbi->phys_addr == (phys_addr_t)ULLONG_MAX)
+		goto out;
+
+	get_random_bytes(&random, sizeof(u32));
+	atomic_set(&sbi->next_generation, random);
+
+	/* Init with default values */
+	sbi->mode = (S_IRWXUGO | S_ISVTX);
+	sbi->uid = current_fsuid();
+	sbi->gid = current_fsgid();
+
+	if (pram_parse_options(data, sbi))
+		goto out;
+
+	initsize = sbi->initsize;
+
+	/* Init a new pramfs instance */
+	if (initsize) {
+		root_i = pram_init(sb, initsize);
+
+		if (IS_ERR(root_i))
+			goto out;
+
+		super = pram_get_super(sb);
+
+		goto setup_sb;
+	}
+
+	pram_dbg("checking physical address 0x%016llx for pramfs image\n",
+		   (u64)sbi->phys_addr);
+
+	/* Map only one page for now. Will remap it when fs size is known. */
+	initsize = PAGE_SIZE;
+	sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize);
+	if (!sbi->virt_addr) {
+		pram_err("ioremap of the pramfs image failed\n");
+		goto out;
+	}
+
+	super = pram_get_super(sb);
+	super_redund = pram_get_redund_super(sb);
+
+	/* Do sanity checks on the superblock */
+	if (be16_to_cpu(super->s_magic) != PRAM_SUPER_MAGIC) {
+		if (be16_to_cpu(super_redund->s_magic) != PRAM_SUPER_MAGIC) {
+			if (!silent)
+				pram_err("Can't find a valid pramfs "
+								"partition\n");
+			goto out;
+		} else {
+			pram_warn("Error in super block: try to repair it with "
+							  "the redundant copy");
+			/* Try to auto-recover the super block */
+			memcpy(super, super_redund, PRAM_SB_SIZE);
+		}
+	}
+
+	/* Read the superblock */
+	if (pram_calc_checksum((u8 *)super, PRAM_SB_SIZE)) {
+		if (pram_calc_checksum((u8 *)super_redund, PRAM_SB_SIZE)) {
+			pram_err("checksum error in super block\n");
+			goto out;
+		} else {
+			pram_warn("Error in super block: try to repair it with "
+							  "the redundant copy");
+			/* Try to auto-recover the super block */
+			memcpy(super, super_redund, PRAM_SB_SIZE);
+		}
+	}
+
+	blocksize = be32_to_cpu(super->s_blocksize);
+	pram_set_blocksize(sb, blocksize);
+
+	initsize = be64_to_cpu(super->s_size);
+	pram_info("pramfs image appears to be %lu KB in size\n", initsize>>10);
+	pram_info("blocksize %lu\n", blocksize);
+
+	/* Read the root inode */
+	root_i = pram_get_inode(sb, PRAM_ROOT_INO);
+
+	/* Check that the root inode is in a sane state */
+	if (pram_calc_checksum((u8 *)root_i, PRAM_INODE_SIZE)) {
+		pram_err("checksum error in root inode!\n");
+		goto out;
+	}
+
+	if (be64_to_cpu(root_i->i_d.d_next)) {
+		pram_err("root->next not NULL??!!\n");
+		goto out;
+	}
+
+	if (!S_ISDIR(be16_to_cpu(root_i->i_mode))) {
+		pram_err("root is not a directory!\n");
+		goto out;
+	}
+
+	root_offset = be64_to_cpu(root_i->i_type.dir.head);
+	if (root_offset == 0)
+		pram_dbg("empty filesystem\n");
+
+	/* Remap the whole filesystem now */
+	iounmap(sbi->virt_addr);
+	release_mem_region(sbi->phys_addr, PAGE_SIZE);
+	sbi->virt_addr = pram_ioremap(sbi->phys_addr, initsize);
+	if (!sbi->virt_addr) {
+		pram_err("ioremap of the pramfs image failed\n");
+		goto out;
+	}
+	super = pram_get_super(sb);
+	root_i = pram_get_inode(sb, PRAM_ROOT_INO);
+
+#ifdef CONFIG_PRAMFS_TEST
+	if (!first_pram_super)
+		first_pram_super = sbi->virt_addr;
+#endif
+
+	/* Set it all up.. */
+ setup_sb:
+	sb->s_magic = be16_to_cpu(super->s_magic);
+	sb->s_op = &pram_sops;
+	sb->s_maxbytes = pram_max_size(sb->s_blocksize_bits);
+	sb->s_time_gran = 1;
+	sb->s_export_op = &pram_export_ops;
+	sb->s_xattr = pram_xattr_handlers;
+#ifdef	CONFIG_PRAMFS_POSIX_ACL
+	sb->s_flags |= MS_POSIXACL;
+#endif
+	sb->s_root = d_alloc_root(pram_iget(sb, PRAM_ROOT_INO));
+
+	retval = 0;
+ out:
+	if (retval && sbi->virt_addr) {
+		iounmap(sbi->virt_addr);
+		release_mem_region(sbi->phys_addr, initsize);
+	}
+
+	return retval;
+}
+
+int pram_statfs(struct dentry *d, struct kstatfs *buf)
+{
+	struct super_block *sb = d->d_sb;
+	struct pram_super_block *ps = pram_get_super(sb);
+
+	buf->f_type = PRAM_SUPER_MAGIC;
+	buf->f_bsize = sb->s_blocksize;
+	buf->f_blocks = be32_to_cpu(ps->s_blocks_count);
+	buf->f_bfree = buf->f_bavail = pram_count_free_blocks(sb);
+	buf->f_files = be32_to_cpu(ps->s_inodes_count);
+	buf->f_ffree = be32_to_cpu(ps->s_free_inodes_count);
+	buf->f_namelen = PRAM_NAME_LEN;
+	return 0;
+}
+
+static int pram_show_options(struct seq_file *seq, struct vfsmount *vfs)
+{
+	struct pram_sb_info *sbi = (struct pram_sb_info *)vfs->mnt_sb->s_fs_info;
+
+	seq_printf(seq, ",physaddr=0x%016llx", (u64)sbi->phys_addr);
+	if (sbi->initsize)
+		seq_printf(seq, ",init=%luk", sbi->initsize >> 10);
+	if (sbi->blocksize)
+		seq_printf(seq, ",bs=%lu", sbi->blocksize);
+	if (sbi->bpi)
+		seq_printf(seq, ",bpi=%lu", sbi->bpi);
+	if (sbi->num_inodes)
+		seq_printf(seq, ",N=%lu", sbi->num_inodes);
+	if (sbi->mode != (S_IRWXUGO | S_ISVTX))
+		seq_printf(seq, ",mode=%03o", sbi->mode);
+	if (sbi->uid != 0)
+		seq_printf(seq, ",uid=%u", sbi->uid);
+	if (sbi->gid != 0)
+		seq_printf(seq, ",gid=%u", sbi->gid);
+
+	return 0;
+}
+
+int pram_remount(struct super_block *sb, int *mntflags, char *data)
+{
+	struct pram_super_block *ps;
+
+	if ((*mntflags & MS_RDONLY) != (sb->s_flags & MS_RDONLY)) {
+		ps = pram_get_super(sb);
+		pram_memunlock_super(ps);
+		ps->s_mtime = cpu_to_be32(get_seconds()); /* update mount time */
+		pram_memlock_super(ps);
+	}
+
+	return 0;
+}
+
+void pram_put_super(struct super_block *sb)
+{
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	struct pram_super_block *ps = pram_get_super(sb);
+	u64 size = be64_to_cpu(ps->s_size);
+
+#ifdef CONFIG_PRAMFS_TEST
+	if (first_pram_super == sbi->virt_addr)
+		first_pram_super = NULL;
+#endif
+
+	pram_xattr_put_super(sb);
+	/* It's unmount time, so unmap the pramfs memory */
+	if (sbi->virt_addr) {
+		iounmap(sbi->virt_addr);
+		sbi->virt_addr = NULL;
+		release_mem_region(sbi->phys_addr, size);
+	}
+
+	sb->s_fs_info = NULL;
+	kfree(sbi);
+}
+
+static struct inode *pram_alloc_inode(struct super_block *sb)
+{
+	struct pram_inode_vfs *vi = (struct pram_inode_vfs *)
+				kmem_cache_alloc(pram_inode_cachep, GFP_KERNEL);
+	if (!vi)
+		return NULL;
+	vi->vfs_inode.i_version = 1;
+	return &vi->vfs_inode;
+}
+
+static void pram_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(pram_inode_cachep, PRAM_I(inode));
+}
+
+static void init_once(void *foo)
+{
+	struct pram_inode_vfs *vi = (struct pram_inode_vfs *) foo;
+
+#ifdef CONFIG_PRAMFS_XATTR
+	init_rwsem(&vi->xattr_sem);
+#endif
+	mutex_init(&vi->truncate_mutex);
+	inode_init_once(&vi->vfs_inode);
+}
+
+static int __init init_inodecache(void)
+{
+	pram_inode_cachep = kmem_cache_create("pram_inode_cache",
+					     sizeof(struct pram_inode_vfs),
+					     0, (SLAB_RECLAIM_ACCOUNT|
+						SLAB_MEM_SPREAD),
+					     init_once);
+	if (pram_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+static void destroy_inodecache(void)
+{
+	kmem_cache_destroy(pram_inode_cachep);
+}
+
+/*
+ * the super block writes are all done "on the fly", so the
+ * super block is never in a "dirty" state, so there's no need
+ * for write_super.
+ */
+static struct super_operations pram_sops = {
+	.alloc_inode	= pram_alloc_inode,
+	.destroy_inode	= pram_destroy_inode,
+	.write_inode	= pram_write_inode,
+	.dirty_inode	= pram_dirty_inode,
+	.evict_inode	= pram_evict_inode,
+	.put_super	= pram_put_super,
+	.statfs		= pram_statfs,
+	.remount_fs	= pram_remount,
+	.show_options	= pram_show_options,
+};
+
+static int pram_get_sb(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+{
+	return get_sb_nodev(fs_type, flags, data, pram_fill_super, mnt);
+}
+
+static struct file_system_type pram_fs_type = {
+	.owner          = THIS_MODULE,
+	.name           = "pramfs",
+	.get_sb         = pram_get_sb,
+	.kill_sb        = kill_anon_super,
+};
+
+static struct inode *pram_nfs_get_inode(struct super_block *sb,
+		u64 ino, u32 generation)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	struct inode *inode;
+
+	if (ino < PRAM_ROOT_INO)
+		return ERR_PTR(-ESTALE);
+	if (((ino - PRAM_ROOT_INO) >> PRAM_INODE_BITS) > be32_to_cpu(ps->s_inodes_count))
+		return ERR_PTR(-ESTALE);
+
+	inode = pram_iget(sb, ino);
+	if (!inode)
+		return ERR_PTR(-ESTALE);
+	if (generation && inode->i_generation != generation) {
+		/* we didn't find the right inode.. */
+		iput(inode);
+		return ERR_PTR(-ESTALE);
+	}
+	return inode;
+}
+
+static struct dentry *
+pram_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len,
+		   int fh_type)
+{
+	return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
+				    pram_nfs_get_inode);
+}
+
+static struct dentry *
+pram_fh_to_parent(struct super_block *sb, struct fid *fid, int fh_len,
+		   int fh_type)
+{
+	return generic_fh_to_parent(sb, fid, fh_len, fh_type,
+				    pram_nfs_get_inode);
+}
+
+static const struct export_operations pram_export_ops = {
+	.fh_to_dentry = pram_fh_to_dentry,
+	.fh_to_parent = pram_fh_to_parent,
+	.get_parent = pram_get_parent,
+};
+
+static int __init init_pram_fs(void)
+{
+	int rc = 0;
+
+	rc = init_pram_xattr();
+	if (rc)
+		return rc;
+
+	rc = init_inodecache();
+	if (rc)
+		goto out1;
+
+	rc = bdi_init(&pram_backing_dev_info);
+	if (rc)
+		goto out2;
+
+	rc = register_filesystem(&pram_fs_type);
+	if (rc)
+		goto out3;
+
+	return 0;
+
+out3:
+	bdi_destroy(&pram_backing_dev_info);
+out2:
+	destroy_inodecache();
+out1:
+	exit_pram_xattr();
+	return rc;
+}
+
+static void __exit exit_pram_fs(void)
+{
+	unregister_filesystem(&pram_fs_type);
+	bdi_destroy(&pram_backing_dev_info);
+	destroy_inodecache();
+	exit_pram_xattr();
+}
+
+MODULE_AUTHOR("Marco Stornelli <marco.stornelli@gmail.com>");
+MODULE_DESCRIPTION("Protected/Persistent RAM Filesystem");
+MODULE_LICENSE("GPL");
+
+module_init(init_pram_fs)
+module_exit(exit_pram_fs)
 

^ permalink raw reply

* [PATCH 03/17] pramfs: inode operations
From: Marco Stornelli @ 2010-10-10 16:29 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Inode methods (allocate/free/read/write).

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/inode.c linux-2.6.36/fs/pramfs/inode.c
--- linux-2.6.36-orig/fs/pramfs/inode.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/inode.c	2010-09-26 18:04:38.000000000 +0200
@@ -0,0 +1,710 @@
+/*
+ * FILE NAME fs/pramfs/inode.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Inode methods (allocate/free/read/write).
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/highuid.h>
+#include <linux/quotaops.h>
+#include <linux/module.h>
+#include <linux/mpage.h>
+#include <linux/backing-dev.h>
+#include "pram.h"
+#include "xattr.h"
+#include "xip.h"
+#include "acl.h"
+
+struct backing_dev_info pram_backing_dev_info __read_mostly = {
+	.ra_pages       = 0,    /* No readahead */
+	.capabilities	= BDI_CAP_NO_ACCT_AND_WRITEBACK,
+};
+
+/*
+ * allocate a data block for inode and return it's absolute blocknr.
+ * Zeroes out the block if zero set. Increments inode->i_blocks.
+ */
+static int pram_new_data_block(struct inode *inode, unsigned long *blocknr, int zero)
+{
+	int errval = pram_new_block(inode->i_sb, blocknr, zero);
+
+	if (!errval) {
+		struct pram_inode *pi = pram_get_inode(inode->i_sb,
+							inode->i_ino);
+		inode->i_blocks++;
+		pram_memunlock_inode(pi);
+		pi->i_blocks = cpu_to_be32(inode->i_blocks);
+		pram_memlock_inode(pi);
+	}
+
+	return errval;
+}
+
+/*
+ * find the offset to the block represented by the given inode's file
+ * relative block number.
+ */
+u64 pram_find_data_block(struct inode *inode, int file_blocknr)
+{
+	struct super_block *sb = inode->i_sb;
+	struct pram_inode *pi;
+	u64 *row; /* ptr to row block */
+	u64 *col; /* ptr to column blocks */
+	u64 bp = 0;
+	int i_row, i_col;
+	int N = sb->s_blocksize >> 3; /* num block ptrs per block */
+	int Nbits = sb->s_blocksize_bits - 3;
+
+	pi = pram_get_inode(sb, inode->i_ino);
+
+	i_row = file_blocknr >> Nbits;
+	i_col  = file_blocknr & (N-1);
+
+	row = pram_get_block(sb, be64_to_cpu(pi->i_type.reg.row_block));
+	if (row) {
+		col = pram_get_block(sb, be64_to_cpu(row[i_row]));
+		if (col)
+			bp = be64_to_cpu(col[i_col]);
+	}
+
+	return bp;
+}
+
+/*
+ * Free data blocks from inode in the range start <=> end
+ */
+static void __pram_truncate_blocks(struct inode *inode, loff_t start, loff_t end)
+{
+	struct super_block *sb = inode->i_sb;
+	struct pram_inode *pi = pram_get_inode(sb, inode->i_ino);
+	int N = sb->s_blocksize >> 3; /* num block ptrs per block */
+	int Nbits = sb->s_blocksize_bits - 3;
+	int first_row_index, last_row_index, i, j;
+	unsigned long blocknr, first_blocknr, last_blocknr;
+	unsigned int freed = 0;
+	u64 *row; /* ptr to row block */
+	u64 *col; /* ptr to column blocks */
+
+	if (start > end || !inode->i_blocks || !pi->i_type.reg.row_block)
+		return;
+
+	mutex_lock(&PRAM_I(inode)->truncate_mutex);
+
+	first_blocknr = (start + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+	last_blocknr = (end + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+	first_row_index = first_blocknr >> Nbits;
+	last_row_index  = last_blocknr >> Nbits;
+
+	row = pram_get_block(sb, be64_to_cpu(pi->i_type.reg.row_block));
+
+	for (i = first_row_index; i <= last_row_index; i++) {
+		int first_col_index = (i == first_row_index) ?
+			first_blocknr & (N-1) : 0;
+		int last_col_index = (i == last_row_index) ?
+			last_blocknr & (N-1) : N-1;
+
+		if (unlikely(!row[i]))
+			continue;
+
+		col = pram_get_block(sb, be64_to_cpu(row[i]));
+
+		for (j = first_col_index; j <= last_col_index; j++) {
+
+			if (unlikely(!col[j]))
+				continue;
+
+			blocknr = pram_get_blocknr(sb, be64_to_cpu(col[j]));
+			pram_free_block(sb, blocknr);
+			freed++;
+			pram_memunlock_block(sb, col);
+			col[j] = 0;
+			pram_memlock_block(sb, col);
+		}
+
+		if (first_col_index == 0) {
+			blocknr = pram_get_blocknr(sb, be64_to_cpu(row[i]));
+			pram_free_block(sb, blocknr);
+			pram_memunlock_block(sb, row);
+			row[i] = 0;
+			pram_memlock_block(sb, row);
+		}
+	}
+
+	inode->i_blocks -= freed;
+
+	if (start == 0) {
+		blocknr = pram_get_blocknr(sb, be64_to_cpu(pi->i_type.reg.row_block));
+		pram_free_block(sb, blocknr);
+		pram_memunlock_inode(pi);
+		pi->i_type.reg.row_block = 0;
+		pram_memlock_inode(pi);
+	}
+	pram_memunlock_inode(pi);
+	pi->i_blocks = cpu_to_be32(inode->i_blocks);
+	pram_memlock_inode(pi);
+
+	mutex_unlock(&PRAM_I(inode)->truncate_mutex);
+}
+
+static void pram_truncate_blocks(struct inode *inode, loff_t start, loff_t end)
+{
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	      S_ISLNK(inode->i_mode)))
+		return;
+	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+		return;
+
+	__pram_truncate_blocks(inode, start, end);
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+	pram_update_inode(inode);
+}
+
+/*
+ * Allocate num data blocks for inode, starting at given file-relative
+ * block number. All blocks except the last are zeroed out.
+ */
+int pram_alloc_blocks(struct inode *inode, int file_blocknr, int num)
+{
+	struct super_block *sb = inode->i_sb;
+	struct pram_inode *pi = pram_get_inode(sb, inode->i_ino);
+	int N = sb->s_blocksize >> 3; /* num block ptrs per block */
+	int Nbits = sb->s_blocksize_bits - 3;
+	int first_file_blocknr;
+	int last_file_blocknr;
+	int first_row_index, last_row_index;
+	int i, j, errval;
+	unsigned long blocknr;
+	u64 *row;
+	u64 *col;
+
+	if (!pi->i_type.reg.row_block) {
+		/* alloc the 2nd order array block */
+		errval = pram_new_block(sb, &blocknr, 1);
+		if (errval) {
+			pram_err("failed to alloc 2nd order array block\n");
+			goto fail;
+		}
+		pram_memunlock_inode(pi);
+		pi->i_type.reg.row_block = cpu_to_be64(pram_get_block_off(sb, blocknr));
+		pram_memlock_inode(pi);
+	}
+
+	row = pram_get_block(sb, be64_to_cpu(pi->i_type.reg.row_block));
+
+	first_file_blocknr = file_blocknr;
+	last_file_blocknr = file_blocknr + num - 1;
+
+	first_row_index = first_file_blocknr >> Nbits;
+	last_row_index  = last_file_blocknr >> Nbits;
+
+	for (i = first_row_index; i <= last_row_index; i++) {
+		int first_col_index, last_col_index;
+
+		/*
+		 * we are starting a new row, so make sure
+		 * there is a block allocated for the row.
+		 */
+		if (!row[i]) {
+			/* allocate the row block */
+			errval = pram_new_block(sb, &blocknr, 1);
+			if (errval) {
+				pram_err("failed to alloc row block\n");
+				goto fail;
+			}
+			pram_memunlock_block(sb, row);
+			row[i] = cpu_to_be64(pram_get_block_off(sb, blocknr));
+			pram_memlock_block(sb, row);
+		}
+		col = pram_get_block(sb, be64_to_cpu(row[i]));
+
+		first_col_index = (i == first_row_index) ?
+			first_file_blocknr & (N-1) : 0;
+
+		last_col_index = (i == last_row_index) ?
+			last_file_blocknr & (N-1) : N-1;
+
+		for (j = first_col_index; j <= last_col_index; j++) {
+			int last_block =
+				(i == last_row_index) && (j == last_col_index);
+			if (!col[j]) {
+				errval = pram_new_data_block(inode,
+							      &blocknr,
+							      !last_block);
+				if (errval) {
+					pram_err("failed to alloc "
+						  "data block\n");
+					goto fail;
+				}
+				pram_memunlock_block(sb, col);
+				col[j] = cpu_to_be64(pram_get_block_off(sb, blocknr));
+				pram_memlock_block(sb, col);
+			}
+		}
+	}
+
+	errval = 0;
+ fail:
+	return errval;
+}
+
+static int pram_read_inode(struct inode *inode, struct pram_inode *pi)
+{
+	int ret = -EIO;
+
+	if (pram_calc_checksum((u8 *)pi, PRAM_INODE_SIZE)) {
+		pram_err("checksum error in inode %08x\n",
+			  (u32)inode->i_ino);
+		goto bad_inode;
+	}
+
+	inode->i_mode = be16_to_cpu(pi->i_mode);
+	inode->i_uid = be32_to_cpu(pi->i_uid);
+	inode->i_gid = be32_to_cpu(pi->i_gid);
+	inode->i_nlink = be16_to_cpu(pi->i_links_count);
+	inode->i_size = be32_to_cpu(pi->i_size);
+	inode->i_atime.tv_sec = be32_to_cpu(pi->i_atime);
+	inode->i_ctime.tv_sec = be32_to_cpu(pi->i_ctime);
+	inode->i_mtime.tv_sec = be32_to_cpu(pi->i_mtime);
+	inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec =
+		inode->i_ctime.tv_nsec = 0;
+	inode->i_generation = be32_to_cpu(pi->i_generation);
+
+	/* check if the inode is active. */
+	if (inode->i_nlink == 0 && (inode->i_mode == 0 || be32_to_cpu(pi->i_dtime))) {
+		/* this inode is deleted */
+		pram_dbg("read inode: inode %lu not active", inode->i_ino);
+		ret = -EINVAL;
+		goto bad_inode;
+	}
+
+	inode->i_blocks = be32_to_cpu(pi->i_blocks);
+	inode->i_ino = pram_get_inodenr(inode->i_sb, pi);
+	inode->i_mapping->a_ops = &pram_aops;
+	inode->i_mapping->backing_dev_info = &pram_backing_dev_info;
+
+	insert_inode_hash(inode);
+	switch (inode->i_mode & S_IFMT) {
+	case S_IFREG:
+		inode->i_op = &pram_file_inode_operations;
+		inode->i_fop = &pram_file_operations;
+		break;
+	case S_IFDIR:
+		inode->i_op = &pram_dir_inode_operations;
+		inode->i_fop = &pram_dir_operations;
+		break;
+	case S_IFLNK:
+		inode->i_op = &pram_symlink_inode_operations;
+		break;
+	default:
+		inode->i_size = 0;
+		init_special_inode(inode, inode->i_mode,
+				   be32_to_cpu(pi->i_type.dev.rdev));
+		break;
+	}
+
+	return 0;
+
+ bad_inode:
+	make_bad_inode(inode);
+	return ret;
+}
+
+int pram_update_inode(struct inode *inode)
+{
+	struct pram_inode *pi;
+	int retval = 0;
+
+	pi = pram_get_inode(inode->i_sb, inode->i_ino);
+
+	pram_memunlock_inode(pi);
+	pi->i_mode = cpu_to_be16(inode->i_mode);
+	pi->i_uid = cpu_to_be32(inode->i_uid);
+	pi->i_gid = cpu_to_be32(inode->i_gid);
+	pi->i_links_count = cpu_to_be16(inode->i_nlink);
+	pi->i_size = cpu_to_be32(inode->i_size);
+	pi->i_blocks = cpu_to_be32(inode->i_blocks);
+	pi->i_atime = cpu_to_be32(inode->i_atime.tv_sec);
+	pi->i_ctime = cpu_to_be32(inode->i_ctime.tv_sec);
+	pi->i_mtime = cpu_to_be32(inode->i_mtime.tv_sec);
+	pi->i_generation = cpu_to_be32(inode->i_generation);
+
+	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+		pi->i_type.dev.rdev = cpu_to_be32(inode->i_rdev);
+
+	pram_memlock_inode(pi);
+	return retval;
+}
+
+/*
+ * NOTE! When we get the inode, we're the only people
+ * that have access to it, and as such there are no
+ * race conditions we have to worry about. The inode
+ * is not on the hash-lists, and it cannot be reached
+ * through the filesystem because the directory entry
+ * has been deleted earlier.
+ */
+static void pram_free_inode(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct pram_super_block *ps;
+	struct pram_inode *pi;
+	unsigned long inode_nr;
+
+	pram_xattr_delete_inode(inode);
+
+	lock_super(sb);
+
+	inode_nr = (inode->i_ino - PRAM_ROOT_INO) >> PRAM_INODE_BITS;
+
+	pi = pram_get_inode(sb, inode->i_ino);
+	pram_memunlock_inode(pi);
+	pi->i_dtime = cpu_to_be32(get_seconds());
+	pi->i_type.reg.row_block = 0;
+	pi->i_xattr = 0;
+	pram_memlock_inode(pi);
+
+	/* increment s_free_inodes_count */
+	ps = pram_get_super(sb);
+	pram_memunlock_super(ps);
+	if (inode_nr < be32_to_cpu(ps->s_free_inode_hint))
+		ps->s_free_inode_hint = cpu_to_be32(inode_nr);
+	be32_add_cpu(&ps->s_free_inodes_count, 1);
+	if (be32_to_cpu(ps->s_free_inodes_count) == be32_to_cpu(ps->s_inodes_count) - 1) {
+		/* filesystem is empty */
+		pram_dbg("fs is empty!\n");
+		ps->s_free_inode_hint = cpu_to_be32(1);
+	}
+	pram_memlock_super(ps);
+
+	unlock_super(sb);
+}
+
+struct inode *pram_iget(struct super_block *sb, unsigned long ino)
+{
+	struct inode *inode;
+	struct pram_inode *pi;
+	int err;
+
+	inode = iget_locked(sb, ino);
+	if (unlikely(!inode))
+		return ERR_PTR(-ENOMEM);
+	if (!(inode->i_state & I_NEW))
+		return inode;
+
+	pi = pram_get_inode(sb, ino);
+	if (!pi) {
+		err = -EACCES;
+		goto fail;
+	}
+	err = pram_read_inode(inode, pi);
+	if (unlikely(err))
+		goto fail;
+
+	unlock_new_inode(inode);
+	return inode;
+fail:
+	iget_failed(inode);
+	return ERR_PTR(err);
+}
+
+void pram_evict_inode(struct inode *inode)
+{
+	int want_delete = 0;
+
+	if (!inode->i_nlink && !is_bad_inode(inode))
+		want_delete = 1;
+
+	truncate_inode_pages(&inode->i_data, 0);
+
+	if (want_delete) {
+		/* unlink from chain in the inode's directory */
+		pram_remove_link(inode);
+		if (inode->i_blocks)
+			pram_truncate_blocks(inode, 0, inode->i_size);
+		inode->i_size = 0;
+	}
+
+	invalidate_inode_buffers(inode);
+	end_writeback(inode);
+
+	if (want_delete)
+		pram_free_inode(inode);
+}
+
+
+struct inode *pram_new_inode(struct inode *dir, int mode)
+{
+	struct super_block *sb;
+	struct pram_sb_info *sbi;
+	struct pram_super_block *ps;
+	struct inode *inode;
+	struct pram_inode *pi = NULL;
+	int i, errval;
+	ino_t ino = 0;
+
+	sb = dir->i_sb;
+	sbi = (struct pram_sb_info *)sb->s_fs_info;
+	inode = new_inode(sb);
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	lock_super(sb);
+	ps = pram_get_super(sb);
+
+	if (ps->s_free_inodes_count) {
+		/* find the oldest unused pram inode */
+		for (i = be32_to_cpu(ps->s_free_inode_hint); i < be32_to_cpu(ps->s_inodes_count); i++) {
+			ino = PRAM_ROOT_INO + (i << PRAM_INODE_BITS);
+			pi = pram_get_inode(sb, ino);
+			/* check if the inode is active. */
+			if (be16_to_cpu(pi->i_links_count) == 0 &&
+			   (be16_to_cpu(pi->i_mode) == 0 ||
+			   be32_to_cpu(pi->i_dtime))) {
+				/* this inode is deleted */
+				break;
+			}
+		}
+
+		if (i >= be32_to_cpu(ps->s_inodes_count)) {
+			pram_err("s_free_inodes_count!=0 but none free!?\n");
+			errval = -ENOSPC;
+			goto fail1;
+		}
+
+		pram_dbg("allocating inode %lu\n", ino);
+		pram_memunlock_super(ps);
+		be32_add_cpu(&ps->s_free_inodes_count, -1);
+		if (i < be32_to_cpu(ps->s_inodes_count)-1)
+			ps->s_free_inode_hint = cpu_to_be32(i+1);
+		else
+			ps->s_free_inode_hint = 0;
+		pram_memlock_super(ps);
+	} else {
+		pram_err("no space left to create new inode!\n");
+		errval = -ENOSPC;
+		goto fail1;
+	}
+
+	/* chosen inode is in ino */
+
+	inode->i_ino = ino;
+	inode_init_owner(inode, dir, mode);
+	inode->i_blocks = inode->i_size = 0;
+	inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+
+	inode->i_generation = atomic_add_return(1, &sbi->next_generation);
+
+	pram_memunlock_inode(pi);
+	pi->i_d.d_next = 0;
+	pi->i_d.d_prev = 0;
+	pram_memlock_inode(pi);
+
+	if (insert_inode_locked(inode) < 0) {
+		errval = -EINVAL;
+		goto fail2;
+	}
+	pram_write_inode(inode, 0);
+
+	errval = pram_init_acl(inode, dir);
+	if (errval)
+		goto fail2;
+
+	errval = pram_init_security(inode, dir);
+	if (errval)
+		goto fail2;
+
+	unlock_super(sb);
+
+	return inode;
+fail2:
+	unlock_super(sb);
+	unlock_new_inode(inode);
+	iput(inode);
+	return ERR_PTR(errval);
+fail1:
+	unlock_super(sb);
+	make_bad_inode(inode);
+	iput(inode);
+	return ERR_PTR(errval);
+}
+
+int pram_write_inode(struct inode *inode, struct writeback_control *wbc)
+{
+	return pram_update_inode(inode);
+}
+
+/*
+ * dirty_inode() is called from __mark_inode_dirty()
+ */
+void pram_dirty_inode(struct inode *inode)
+{
+	pram_write_inode(inode, NULL);
+}
+
+/* pram_get_and_update_block()
+ *
+ * Look for a block. If not found it can create a new one if create is
+ * different from zero.
+ *
+ * It returns zero if plain lookup failed or blocks mapped or allocated
+ * (plain lookup failed is not an error, e.g. for holes). Minor than zero
+ * otherwise.
+ */
+int pram_get_and_update_block(struct inode *inode, sector_t iblock,
+				     struct buffer_head *bh, int create)
+{
+	struct super_block *sb = inode->i_sb;
+	unsigned int blocksize = 1 << inode->i_blkbits;
+	int err = 0;
+	u64 block;
+	void *bp;
+
+	mutex_lock(&PRAM_I(inode)->truncate_mutex);
+
+	block = pram_find_data_block(inode, iblock);
+
+	if (!block) {
+		if (!create)
+			goto out;
+
+		err = pram_alloc_blocks(inode, iblock, 1);
+		if (err)
+			goto out;
+		block = pram_find_data_block(inode, iblock);
+		if (!block) {
+			err = -EIO;
+			goto out;
+		}
+		set_buffer_new(bh);
+	}
+
+	bh->b_blocknr = block;
+	set_buffer_mapped(bh);
+
+	/* now update the buffer synchronously */
+	bp = pram_get_block(sb, block);
+	if (buffer_new(bh)) {
+		pram_memunlock_block(sb, bp);
+		memset(bp, 0, blocksize);
+		pram_memlock_block(sb, bp);
+		memset(bh->b_data, 0, blocksize);
+	} else {
+		memcpy(bh->b_data, bp, blocksize);
+	}
+
+	set_buffer_uptodate(bh);
+
+ out:
+	mutex_unlock(&PRAM_I(inode)->truncate_mutex);
+	return err;
+}
+
+/*
+ * Called to zeros out a single block. It's used in the "resize"
+ * to avoid to keep data in case the file grow up again.
+ */
+static int pram_clear_block(struct inode *inode, loff_t newsize)
+{
+	pgoff_t index = newsize >> PAGE_CACHE_SHIFT;
+	unsigned long offset = newsize & (PAGE_CACHE_SIZE - 1);
+	unsigned long blocksize, length;
+	sector_t iblock;
+	u64 blockoff;
+	char *bp;
+	int ret = 0;
+
+	blocksize = 1 << inode->i_blkbits;
+	length = offset & (blocksize - 1);
+
+	/* Block boundary ? */
+	if (!length)
+		goto out;
+
+	length = blocksize - length;
+	iblock = (sector_t)index << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+
+	mutex_lock(&PRAM_I(inode)->truncate_mutex);
+	blockoff = pram_find_data_block(inode, iblock);
+
+	/* Hole ? */
+	if (!blockoff)
+		goto out_unlock;
+
+	bp = pram_get_block(inode->i_sb, blockoff);
+	if (!bp) {
+		ret = -EACCES;
+		goto out_unlock;
+	}
+	pram_memunlock_block(inode->i_sb, bp);
+	memset(bp + offset, 0, length);
+	pram_memlock_block(inode->i_sb, bp);
+
+out_unlock:
+	mutex_unlock(&PRAM_I(inode)->truncate_mutex);
+out:
+	return ret;
+}
+
+static int pram_setsize(struct inode *inode, loff_t newsize)
+{
+	int ret = 0;
+	loff_t oldsize;
+
+	if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
+	    S_ISLNK(inode->i_mode)))
+		return -EINVAL;
+	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
+		return -EPERM;
+
+	ret = pram_clear_block(inode, newsize);
+	if (ret)
+		return ret;
+
+	oldsize = inode->i_size;
+	i_size_write(inode, newsize);
+	__pram_truncate_blocks(inode, newsize, oldsize);
+	inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
+	pram_update_inode(inode);
+
+	return ret;
+}
+
+int pram_notify_change(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+
+	error = inode_change_ok(inode, attr);
+	if (error)
+		return error;
+
+	if (attr->ia_valid & ATTR_SIZE && attr->ia_size != inode->i_size) {
+		error = pram_setsize(inode, attr->ia_size);
+		if (error)
+			return error;
+	}
+	setattr_copy(inode, attr);
+	if (attr->ia_valid & ATTR_MODE)
+		error = pram_acl_chmod(inode);
+	error = pram_update_inode(inode);
+
+	return error;
+}
+
+struct address_space_operations pram_aops = {
+	.readpage	= pram_readpage,
+	.direct_IO	= pram_direct_IO,
+	.get_xip_mem	= pram_get_xip_mem,
+};
 

^ permalink raw reply

* [PATCH 04/16] pramfs: file operations
From: Marco Stornelli @ 2010-10-10 16:29 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

File operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/file.c linux-2.6.36/fs/pramfs/file.c
--- linux-2.6.36-orig/fs/pramfs/file.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/file.c	2010-09-24 18:34:03.000000000 +0200
@@ -0,0 +1,166 @@
+/*
+ * FILE NAME fs/pramfs/file.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * File operations for files.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uio.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include "pram.h"
+#include "acl.h"
+#include "xip.h"
+#include "xattr.h"
+
+static int pram_open_file(struct inode *inode, struct file *filp)
+{
+#ifndef CONFIG_PRAMFS_XIP
+	/* Without XIP we force to use Direct IO */
+	filp->f_flags |= O_DIRECT;
+#endif
+	return generic_file_open(inode, filp);
+}
+
+ssize_t __pram_direct_IO(int rw, struct kiocb *iocb,
+		   const struct iovec *iov,
+		   loff_t offset, unsigned long nr_segs)
+{
+	struct file *file = iocb->ki_filp;
+	struct inode *inode = file->f_mapping->host;
+	struct super_block *sb = inode->i_sb;
+	int progress = 0, hole = 0;
+	ssize_t retval = 0;
+	void *tmp = NULL;
+	unsigned long blocknr, blockoff;
+	int num_blocks, blocksize_mask, blocksize, blocksize_bits;
+	char __user *buf = iov->iov_base;
+	size_t length = iov_length(iov, nr_segs);
+
+	if (length < 0)
+		return -EINVAL;
+	if ((rw == READ) && (offset + length > inode->i_size))
+		length = inode->i_size - offset;
+	if (!length)
+		goto out;
+
+	blocksize_bits = inode->i_sb->s_blocksize_bits;
+	blocksize = 1 << blocksize_bits;
+	blocksize_mask = blocksize - 1;
+
+	/* find starting block number to access */
+	blocknr = offset >> blocksize_bits;
+	/* find starting offset within starting block */
+	blockoff = offset & blocksize_mask;
+	/* find number of blocks to access */
+	num_blocks = (blockoff + length + blocksize_mask) >> blocksize_bits;
+
+	if (rw == WRITE) {
+		/* prepare a temporary buffer to hold a user data block
+		   for writing. */
+		tmp = kmalloc(blocksize, GFP_KERNEL);
+		if (!tmp)
+			return -ENOMEM;
+		/* now allocate the data blocks we'll need */
+		retval = pram_alloc_blocks(inode, blocknr, num_blocks);
+		if (retval)
+			goto fail1;
+	}
+
+	while (length) {
+		int count;
+		u8 *bp = NULL;
+		u64 block = pram_find_data_block(inode, blocknr++);
+		if (unlikely(!block && rw == READ)) {
+			/* We are falling in a hole */
+			hole = 1;
+		} else {
+			bp = (u8 *)pram_get_block(sb, block);
+			if (!bp)
+				goto fail2;
+		}
+
+		count = blockoff + length > blocksize ?
+			blocksize - blockoff : length;
+
+		if (rw == READ) {
+			if (unlikely(hole)) {
+				retval = clear_user(buf, count);
+				if (retval) {
+					retval = -EFAULT;
+					goto fail1;
+				}
+			} else {
+				retval = copy_to_user(buf, &bp[blockoff], count);
+				if (retval) {
+					retval = -EFAULT;
+					goto fail1;
+				}
+			}
+		} else {
+			retval = copy_from_user(tmp, buf, count);
+			if (retval) {
+				retval = -EFAULT;
+				goto fail1;
+			}
+
+			pram_memunlock_block(inode->i_sb, bp);
+			memcpy(&bp[blockoff], tmp, count);
+			pram_memlock_block(inode->i_sb, bp);
+		}
+
+		progress += count;
+		buf += count;
+		length -= count;
+		blockoff = 0;
+		hole = 0;
+	}
+
+fail2:
+	retval = progress;
+fail1:
+	kfree(tmp);
+out:
+	return retval;
+}
+
+static int __pram_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	/* Only private mappings */
+	if (vma->vm_flags & VM_SHARED)
+		return -EINVAL;
+	return generic_file_mmap(file, vma);
+}
+
+struct file_operations pram_file_operations = {
+	.llseek		= generic_file_llseek,
+	.read		= pram_read,
+	.write		= pram_write,
+	.aio_read	= pram_aio_read,
+	.aio_write	= pram_aio_write,
+	.mmap		= pram_mmap,
+	.open		= pram_open_file,
+	.fsync		= noop_fsync,
+};
+
+struct inode_operations pram_file_inode_operations = {
+#ifdef CONFIG_PRAMFS_XATTR
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= pram_listxattr,
+	.removexattr	= generic_removexattr,
+#endif
+	.setattr	= pram_notify_change,
+	.check_acl	= pram_check_acl,
+};
 

^ permalink raw reply

* [PATCH 05/16] pramfs: block allocation
From: Marco Stornelli @ 2010-10-10 16:30 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Block allocation operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/balloc.c linux-2.6.36/fs/pramfs/balloc.c
--- linux-2.6.36-orig/fs/pramfs/balloc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/balloc.c	2010-09-26 18:05:06.000000000 +0200
@@ -0,0 +1,155 @@
+/*
+ * FILE NAME fs/pramfs/balloc.c
+ *
+ * BRIEF MODULE DESCRIPTION
+ *
+ * The blocks allocation and deallocation routines.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/fs.h>
+#include <linux/bitops.h>
+#include "pram.h"
+
+/*
+ * This just marks in-use the blocks that make up the bitmap.
+ * The bitmap must be writeable before calling.
+ */
+void pram_init_bitmap(struct super_block *sb)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	u64 *bitmap = pram_get_bitmap(sb);
+	int blocks = be32_to_cpu(ps->s_bitmap_blocks);
+
+	memset(bitmap, 0, blocks << sb->s_blocksize_bits);
+
+	while (blocks >= 64) {
+		*bitmap++ = (u64)ULLONG_MAX;
+		blocks -= 64;
+	}
+
+	if (blocks)
+		*bitmap = cpu_to_le64((1ULL << blocks) - 1);
+}
+
+
+/* Free absolute blocknr */
+void pram_free_block(struct super_block *sb, unsigned long blocknr)
+{
+	struct pram_super_block *ps;
+	u64 bitmap_block;
+	unsigned long bitmap_bnr;
+	void *bitmap;
+	void *bp;
+
+	lock_super(sb);
+
+	bitmap = pram_get_bitmap(sb);
+	/*
+	 * find the block within the bitmap that contains the inuse bit
+	 * for the block we need to free. We need to unlock this bitmap
+	 * block to clear the inuse bit.
+	 */
+	bitmap_bnr = blocknr >> (3 + sb->s_blocksize_bits);
+	bitmap_block = pram_get_block_off(sb, bitmap_bnr);
+	bp = pram_get_block(sb, bitmap_block);
+
+	pram_memunlock_block(sb, bp);
+	pram_clear_bit(blocknr, bitmap); /* mark the block free */
+	pram_memlock_block(sb, bp);
+
+	ps = pram_get_super(sb);
+	pram_memunlock_super(ps);
+	if (blocknr < be32_to_cpu(ps->s_free_blocknr_hint))
+		ps->s_free_blocknr_hint = cpu_to_be32(blocknr);
+	be32_add_cpu(&ps->s_free_blocks_count, 1);
+	pram_memlock_super(ps);
+
+	unlock_super(sb);
+}
+
+
+/*
+ * allocate a block and return it's absolute blocknr. Zeroes out the
+ * block if zero set.
+ */
+int pram_new_block(struct super_block *sb, unsigned long *blocknr, int zero)
+{
+	struct pram_super_block *ps;
+	off_t bitmap_block;
+	unsigned long bnr, bitmap_bnr;
+	int errval;
+	void *bitmap;
+	void *bp;
+
+	lock_super(sb);
+	ps = pram_get_super(sb);
+	bitmap = pram_get_bitmap(sb);
+
+	if (ps->s_free_blocks_count) {
+		/* find the oldest unused block */
+		bnr = pram_find_next_zero_bit(bitmap,
+					 be32_to_cpu(ps->s_blocks_count),
+					 be32_to_cpu(ps->s_free_blocknr_hint));
+
+		if (bnr < be32_to_cpu(ps->s_bitmap_blocks) ||
+				bnr >= be32_to_cpu(ps->s_blocks_count)) {
+			pram_err("no free blocks found!\n");
+			errval = -ENOSPC;
+			goto fail;
+		}
+
+		pram_dbg("allocating blocknr %lu\n", bnr);
+		pram_memunlock_super(ps);
+		be32_add_cpu(&ps->s_free_blocks_count, -1);
+		if (bnr < (be32_to_cpu(ps->s_blocks_count)-1))
+			ps->s_free_blocknr_hint = cpu_to_be32(bnr+1);
+		else
+			ps->s_free_blocknr_hint = 0;
+		pram_memlock_super(ps);
+	} else {
+		pram_err("all blocks allocated\n");
+		errval = -ENOSPC;
+		goto fail;
+	}
+
+	/*
+	 * find the block within the bitmap that contains the inuse bit
+	 * for the unused block we just found. We need to unlock it to
+	 * set the inuse bit.
+	 */
+	bitmap_bnr = bnr >> (3 + sb->s_blocksize_bits);
+	bitmap_block = pram_get_block_off(sb, bitmap_bnr);
+	bp = pram_get_block(sb, bitmap_block);
+
+	pram_memunlock_block(sb, bp);
+	pram_set_bit(bnr, bitmap); /* mark the new block in use */
+	pram_memlock_block(sb, bp);
+
+	if (zero) {
+		bp = pram_get_block(sb, pram_get_block_off(sb, bnr));
+		pram_memunlock_block(sb, bp);
+		memset(bp, 0, sb->s_blocksize);
+		pram_memlock_block(sb, bp);
+	}
+
+	*blocknr = bnr;
+	pram_dbg("allocated blocknr %lu", bnr);
+	errval = 0;
+ fail:
+	unlock_super(sb);
+	return errval;
+}
+
+unsigned long pram_count_free_blocks(struct super_block *sb)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	return be32_to_cpu(ps->s_free_blocks_count);
+}
 

^ permalink raw reply

* [PATCH 06/16] pramfs: inode operations for dirs
From: Marco Stornelli @ 2010-10-10 16:30 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Inode operations for directories.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/namei.c linux-2.6.36/fs/pramfs/namei.c
--- linux-2.6.36-orig/fs/pramfs/namei.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/namei.c	2010-09-18 12:00:35.000000000 +0200
@@ -0,0 +1,363 @@
+/*
+ * FILE NAME fs/pramfs/namei.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Inode operations for directories.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include "pram.h"
+#include "acl.h"
+#include "xattr.h"
+
+/*
+ * Couple of helper functions - make the code slightly cleaner.
+ */
+
+static inline void pram_inc_count(struct inode *inode)
+{
+	inode->i_nlink++;
+	pram_write_inode(inode, 0);
+}
+
+static inline void pram_dec_count(struct inode *inode)
+{
+	if (inode->i_nlink) {
+		inode->i_nlink--;
+		pram_write_inode(inode, 0);
+	}
+}
+
+static inline int pram_add_nondir(struct inode *dir,
+				   struct dentry *dentry,
+				   struct inode *inode)
+{
+	int err = pram_add_link(dentry, inode);
+	if (!err) {
+		d_instantiate(dentry, inode);
+		unlock_new_inode(inode);
+		return 0;
+	}
+	pram_dec_count(inode);
+	unlock_new_inode(inode);
+	iput(inode);
+	return err;
+}
+
+/*
+ * Methods themselves.
+ */
+
+static ino_t
+pram_inode_by_name(struct inode *dir,
+		   struct dentry *dentry)
+{
+	struct pram_inode *pi;
+	ino_t ino;
+	int namelen;
+
+	pi = pram_get_inode(dir->i_sb, dir->i_ino);
+	ino = be64_to_cpu(pi->i_type.dir.head);
+
+	while (ino) {
+		pi = pram_get_inode(dir->i_sb, ino);
+
+		if (pi->i_links_count) {
+			namelen = strlen(pi->i_d.d_name);
+
+			if (namelen == dentry->d_name.len &&
+			    !memcmp(dentry->d_name.name,
+				    pi->i_d.d_name, namelen))
+				break;
+		}
+
+		ino = be64_to_cpu(pi->i_d.d_next);
+	}
+
+	return ino;
+}
+
+static struct dentry *
+pram_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+{
+	struct inode *inode = NULL;
+	ino_t ino;
+
+	if (dentry->d_name.len > PRAM_NAME_LEN)
+		return ERR_PTR(-ENAMETOOLONG);
+
+	ino = pram_inode_by_name(dir, dentry);
+	if (ino) {
+		inode = pram_iget(dir->i_sb, ino);
+		if (!inode)
+			return ERR_PTR(-EACCES);
+	}
+
+	d_splice_alias(inode, dentry);
+	return NULL;
+}
+
+
+/*
+ * By the time this is called, we already have created
+ * the directory cache entry for the new file, but it
+ * is so far negative - it has no inode.
+ *
+ * If the create succeeds, we fill in the inode information
+ * with d_instantiate().
+ */
+static int pram_create(struct inode *dir, struct dentry *dentry,
+			int mode, struct nameidata *nd)
+{
+	struct inode *inode = pram_new_inode(dir, mode);
+	int err = PTR_ERR(inode);
+	if (!IS_ERR(inode)) {
+
+		inode->i_op = &pram_file_inode_operations;
+		inode->i_fop = &pram_file_operations;
+		inode->i_mapping->a_ops = &pram_aops;
+		err = pram_add_nondir(dir, dentry, inode);
+	}
+	return err;
+}
+
+static int pram_mknod(struct inode *dir, struct dentry *dentry, int mode,
+		       dev_t rdev)
+{
+	struct inode *inode = pram_new_inode(dir, mode);
+	int err = PTR_ERR(inode);
+	if (!IS_ERR(inode)) {
+		init_special_inode(inode, mode, rdev);
+		pram_write_inode(inode, 0); /* update rdev */
+		err = pram_add_nondir(dir, dentry, inode);
+	}
+	return err;
+}
+
+static int pram_symlink(struct inode *dir,
+			  struct dentry *dentry,
+			  const char *symname)
+{
+	struct super_block *sb = dir->i_sb;
+	int err = -ENAMETOOLONG;
+	unsigned len = strlen(symname);
+	struct inode *inode;
+
+	if (len+1 > sb->s_blocksize)
+		goto out;
+
+	inode = pram_new_inode(dir, S_IFLNK | S_IRWXUGO);
+	err = PTR_ERR(inode);
+	if (IS_ERR(inode))
+		goto out;
+
+	inode->i_op = &pram_symlink_inode_operations;
+	inode->i_mapping->a_ops = &pram_aops;
+
+	err = pram_block_symlink(inode, symname, len);
+	if (err)
+		goto out_fail;
+
+	inode->i_size = len;
+	pram_write_inode(inode, 0);
+
+	err = pram_add_nondir(dir, dentry, inode);
+out:
+	return err;
+
+out_fail:
+	pram_dec_count(inode);
+	unlock_new_inode(inode);
+	iput(inode);
+	goto out;
+}
+
+static int pram_link(struct dentry *dest_dentry,
+		       struct inode *dir,
+		       struct dentry *dentry)
+{
+	pram_dbg("hard links not supported\n");
+	return -EOPNOTSUPP;
+}
+
+static int pram_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	inode->i_ctime = dir->i_ctime;
+	pram_dec_count(inode);
+	return 0;
+}
+
+static int pram_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+	struct inode *inode;
+	struct pram_inode *pi;
+	int err = -EMLINK;
+
+	if (dir->i_nlink >= PRAM_LINK_MAX)
+		goto out;
+
+	pram_inc_count(dir);
+
+	inode = pram_new_inode(dir, S_IFDIR | mode);
+	err = PTR_ERR(inode);
+	if (IS_ERR(inode))
+		goto out_dir;
+
+	inode->i_op = &pram_dir_inode_operations;
+	inode->i_fop = &pram_dir_operations;
+	inode->i_mapping->a_ops = &pram_aops;
+
+	pram_inc_count(inode);
+
+	/* make the new directory empty */
+	pi = pram_get_inode(dir->i_sb, inode->i_ino);
+	pram_memunlock_inode(pi);
+	pi->i_type.dir.head = pi->i_type.dir.tail = 0;
+	pram_memlock_inode(pi);
+
+	err = pram_add_link(dentry, inode);
+	if (err)
+		goto out_fail;
+
+	d_instantiate(dentry, inode);
+	unlock_new_inode(inode);
+out:
+	return err;
+
+out_fail:
+	pram_dec_count(inode);
+	pram_dec_count(inode);
+	unlock_new_inode(inode);
+	iput(inode);
+out_dir:
+	pram_dec_count(dir);
+	goto out;
+}
+
+static int pram_rmdir(struct inode *dir, struct dentry *dentry)
+{
+	struct inode *inode = dentry->d_inode;
+	struct pram_inode *pi;
+	int err = -ENOTEMPTY;
+
+	if (!inode)
+		return -ENOENT;
+
+	pi = pram_get_inode(dir->i_sb, inode->i_ino);
+
+	/* directory to delete is empty? */
+	if (pi->i_type.dir.tail == 0) {
+		inode->i_ctime = dir->i_ctime;
+		inode->i_size = 0;
+		inode->i_nlink = 0;
+		pram_write_inode(inode, 0);
+		pram_dec_count(dir);
+		err = 0;
+	} else {
+		pram_dbg("dir not empty\n");
+	}
+
+	return err;
+}
+
+static int pram_rename(struct inode  *old_dir,
+			struct dentry *old_dentry,
+			struct inode  *new_dir,
+			struct dentry *new_dentry)
+{
+	struct inode *old_inode = old_dentry->d_inode;
+	struct inode *new_inode = new_dentry->d_inode;
+	struct pram_inode *pi_new;
+	int err = -ENOENT;
+
+	if (new_inode) {
+		err = -ENOTEMPTY;
+		pi_new = pram_get_inode(new_dir->i_sb, new_inode->i_ino);
+		if (S_ISDIR(old_inode->i_mode)) {
+			if (pi_new->i_type.dir.tail != 0)
+				goto out;
+			if (new_inode->i_nlink)
+				new_inode->i_nlink--;
+		}
+
+		new_inode->i_ctime = CURRENT_TIME;
+		pram_dec_count(new_inode);
+	} else {
+		if (S_ISDIR(old_inode->i_mode)) {
+			err = -EMLINK;
+			if (new_dir->i_nlink >= PRAM_LINK_MAX)
+				goto out;
+			pram_dec_count(old_dir);
+			pram_inc_count(new_dir);
+		}
+	}
+
+	/* unlink the inode from the old directory ... */
+	err = pram_remove_link(old_inode);
+	if (err)
+		goto out;
+
+	/* and link it into the new directory. */
+	err = pram_add_link(new_dentry, old_inode);
+	if (err)
+		goto out;
+
+	err = 0;
+ out:
+	return err;
+}
+
+struct dentry *pram_get_parent(struct dentry *child)
+{
+	struct inode *inode;
+	struct pram_inode *pi, *piparent;
+	ino_t ino;
+
+	pi = pram_get_inode(child->d_inode->i_sb, child->d_inode->i_ino);
+	if (!pi)
+		return ERR_PTR(-EACCES);
+
+	piparent = pram_get_inode(child->d_inode->i_sb, be64_to_cpu(pi->i_d.d_parent));
+	if (!pi)
+		return ERR_PTR(-ENOENT);
+
+	ino = pram_get_inodenr(child->d_inode->i_sb, piparent);
+	if (ino) {
+		inode = pram_iget(child->d_inode->i_sb, ino);
+		if (!inode)
+			return ERR_PTR(-EACCES);
+	} else
+		return ERR_PTR(-ENOENT);
+
+	return d_obtain_alias(inode);
+}
+
+struct inode_operations pram_dir_inode_operations = {
+	.create		= pram_create,
+	.lookup		= pram_lookup,
+	.link		= pram_link,
+	.unlink		= pram_unlink,
+	.symlink	= pram_symlink,
+	.mkdir		= pram_mkdir,
+	.rmdir		= pram_rmdir,
+	.mknod		= pram_mknod,
+	.rename		= pram_rename,
+#ifdef CONFIG_PRAMFS_XATTR
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= pram_listxattr,
+	.removexattr	= generic_removexattr,
+#endif
+	.setattr	= pram_notify_change,
+	.check_acl	= pram_check_acl,
+};
 

^ permalink raw reply

* [PATCH 07/16] pramfs: symbolic links
From: Marco Stornelli @ 2010-10-10 16:31 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Symlink operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/symlink.c linux-2.6.36/fs/pramfs/symlink.c
--- linux-2.6.36-orig/fs/pramfs/symlink.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/symlink.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,78 @@
+/*
+ * FILE NAME fs/pramfs/symlink.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Symlink operations
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/fs.h>
+#include "pram.h"
+#include "xattr.h"
+
+int pram_block_symlink(struct inode *inode, const char *symname, int len)
+{
+	struct super_block *sb = inode->i_sb;
+	u64 block;
+	char *blockp;
+	int err;
+
+	err = pram_alloc_blocks(inode, 0, 1);
+	if (err)
+		return err;
+
+	block = pram_find_data_block(inode, 0);
+	blockp = pram_get_block(sb, block);
+
+	pram_memunlock_block(sb, blockp);
+	memcpy(blockp, symname, len);
+	blockp[len] = '\0';
+	pram_memlock_block(sb, blockp);
+	return 0;
+}
+
+static int pram_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+	struct inode *inode = dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	u64 block;
+	char *blockp;
+
+	block = pram_find_data_block(inode, 0);
+	blockp = pram_get_block(sb, block);
+	return vfs_readlink(dentry, buffer, buflen, blockp);
+}
+
+static void *pram_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	struct inode *inode = dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	off_t block;
+	int status;
+	char *blockp;
+
+	block = pram_find_data_block(inode, 0);
+	blockp = pram_get_block(sb, block);
+	status = vfs_follow_link(nd, blockp);
+	return ERR_PTR(status);
+}
+
+struct inode_operations pram_symlink_inode_operations = {
+	.readlink	= pram_readlink,
+	.follow_link	= pram_follow_link,
+	.setattr	= pram_notify_change,
+#ifdef CONFIG_PRAMFS_XATTR
+	.setxattr	= generic_setxattr,
+	.getxattr	= generic_getxattr,
+	.listxattr	= pram_listxattr,
+	.removexattr	= generic_removexattr,
+#endif
+};
 

^ permalink raw reply

* [PATCH 08/16] pramfs: headers
From: Marco Stornelli @ 2010-10-10 16:31 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Definitions for the PRAMFS filesystem.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/pram.h linux-2.6.36/fs/pramfs/pram.h
--- linux-2.6.36-orig/fs/pramfs/pram.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/pram.h	2010-09-23 21:04:20.000000000 +0200
@@ -0,0 +1,316 @@
+/*
+ * FILE NAME pram.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Definitions for the PRAMFS filesystem.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef __PRAM_H
+#define __PRAM_H
+
+#include <linux/buffer_head.h>
+#include <linux/pram_fs.h>
+#include <linux/pram_fs_sb.h>
+#include <linux/crc16.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+/*
+ * Debug code
+ */
+#define pram_dbg(s, args...)	pr_debug("PRAMFS: "s, ## args)
+#define pram_err(s, args...)	pr_err("PRAMFS: "s, ## args)
+#define pram_warn(s, args...)	pr_warning("PRAMFS: "s, ## args)
+#define pram_info(s, args...)	pr_info("PRAMFS: "s, ## args)
+
+/* Function Prototypes */
+
+#ifdef CONFIG_PRAMFS_XIP
+
+#define pram_read	xip_file_read
+#define pram_write	xip_file_write
+#define pram_mmap	xip_file_mmap
+#define pram_aio_read	NULL
+#define pram_aio_write	NULL
+#define pram_readpage	NULL
+#define pram_direct_IO	NULL
+
+#else
+
+#define pram_read	do_sync_read
+#define pram_write	do_sync_write
+#define pram_mmap	__pram_mmap
+#define pram_aio_read	generic_file_aio_read
+#define pram_aio_write	generic_file_aio_write
+#define pram_direct_IO	__pram_direct_IO
+#define pram_readpage	__pram_readpage
+
+extern int pram_get_and_update_block(struct inode *inode, sector_t iblock,
+				     struct buffer_head *bh, int create);
+
+static inline int __pram_readpage(struct file *file, struct page *page)
+{
+	return block_read_full_page(page, pram_get_and_update_block);
+}
+
+/* file.c */
+extern ssize_t __pram_direct_IO(int rw, struct kiocb *iocb,
+			  const struct iovec *iov,
+			  loff_t offset, unsigned long nr_segs);
+
+
+#endif /* CONFIG_PRAMFS_XIP */
+
+#define pram_set_bit			ext2_set_bit
+#define pram_clear_bit			ext2_clear_bit
+#define pram_find_next_zero_bit		ext2_find_next_zero_bit
+
+/* balloc.c */
+extern void pram_init_bitmap(struct super_block *sb);
+extern void pram_free_block(struct super_block *sb, unsigned long blocknr);
+extern int pram_new_block(struct super_block *sb, unsigned long *blocknr, int zero);
+extern unsigned long pram_count_free_blocks(struct super_block *sb);
+
+/* dir.c */
+extern int pram_add_link(struct dentry *dentry, struct inode *inode);
+extern int pram_remove_link(struct inode *inode);
+
+/* namei.c */
+extern struct dentry *pram_get_parent(struct dentry *child);
+
+/* inode.c */
+extern int pram_alloc_blocks(struct inode *inode, int file_blocknr, int num);
+extern u64 pram_find_data_block(struct inode *inode,
+					 int file_blocknr);
+
+extern struct inode *pram_iget(struct super_block *sb, unsigned long ino);
+extern void pram_put_inode(struct inode *inode);
+extern void pram_evict_inode(struct inode *inode);
+extern struct inode *pram_new_inode(struct inode *dir, int mode);
+extern int pram_update_inode(struct inode *inode);
+extern int pram_write_inode(struct inode *inode, struct writeback_control *wbc);
+extern void pram_dirty_inode(struct inode *inode);
+extern int pram_notify_change(struct dentry *dentry, struct iattr *attr);
+
+
+/* super.c */
+#ifdef CONFIG_PRAMFS_TEST
+extern struct pram_super_block *get_pram_super(void);
+#endif
+extern struct super_block *pram_read_super(struct super_block *sb,
+					      void *data,
+					      int silent);
+extern int pram_statfs(struct dentry *d, struct kstatfs *buf);
+extern int pram_remount(struct super_block *sb, int *flags, char *data);
+
+/* symlink.c */
+extern int pram_block_symlink(struct inode *inode,
+			       const char *symname, int len);
+
+
+#ifdef CONFIG_PRAMFS_WRITE_PROTECT
+extern void pram_writeable(void *vaddr, unsigned long size, int rw);
+
+#define wrprotect(addr, size) pram_writeable(addr, size, 0)
+
+#else
+
+#define wrprotect(addr, size) do {} while (0)
+
+#endif /* CONFIG PRAMFS_WRITE_PROTECT */
+
+/* Inline functions start here */
+
+static inline int pram_calc_checksum(u8 *data, int n)
+{
+	u16 crc = 0;
+	crc = crc16(~0, (__u8 *)data + sizeof(__be16), n - sizeof(__be16));
+	if (*((__be16 *)data) == cpu_to_be16(crc))
+		return 0;
+	else
+		return 1;
+}
+
+/* If this is part of a read-modify-write of the super block,
+   pram_memunlock_super() before calling! */
+static inline struct pram_super_block *
+pram_get_super(struct super_block *sb)
+{
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	return (struct pram_super_block *)sbi->virt_addr;
+}
+
+static inline struct pram_super_block *
+pram_get_redund_super(struct super_block *sb)
+{
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	return (struct pram_super_block *)(sbi->virt_addr + PRAM_SB_SIZE);
+}
+
+/* pram_memunlock_super() before calling! */
+static inline void pram_sync_super(struct pram_super_block *ps)
+{
+	u16 crc = 0;
+	ps->s_wtime = cpu_to_be32(get_seconds());
+	ps->s_sum = 0;
+	crc = crc16(~0, (__u8 *)ps + sizeof(__be16), PRAM_SB_SIZE - sizeof(__be16));
+	ps->s_sum = cpu_to_be16(crc);
+	/* Keep sync redundant super block */
+	memcpy((void *)ps + PRAM_SB_SIZE, (void *)ps, PRAM_SB_SIZE);
+}
+
+/* pram_memunlock_inode() before calling! */
+static inline void pram_sync_inode(struct pram_inode *pi)
+{
+	u16 crc = 0;
+	pi->i_sum = 0;
+	crc = crc16(~0, (__u8 *)pi + sizeof(__be16), PRAM_INODE_SIZE - sizeof(__be16));
+	pi->i_sum = cpu_to_be16(crc);
+}
+
+extern unsigned long start;
+extern unsigned long end;
+
+#ifdef CONFIG_PRAMFS_WRITE_PROTECT
+static inline void pram_memunlock_range(void *p, unsigned long len)
+{
+	local_irq_disable();
+	preempt_disable();
+	pram_writeable(p, len, 1);
+}
+
+static inline void pram_memlock_range(void *p, unsigned long len)
+{
+	pram_writeable(p, len, 0);
+	local_irq_enable();
+	preempt_enable();
+}
+#else
+static inline void pram_memunlock_range(void *p, unsigned long len) {}
+static inline void pram_memlock_range(void *p, unsigned long len) {}
+#endif
+
+/* write protection for super block */
+#define pram_memunlock_super(ps) \
+	pram_memunlock_range((ps), PRAM_SB_SIZE)
+#define pram_memlock_super(ps) {\
+	pram_sync_super(ps);\
+	pram_memlock_range((ps), PRAM_SB_SIZE);\
+}
+
+/* write protection for inode metadata */
+#define pram_memunlock_inode(pi) \
+	pram_memunlock_range((pi), PRAM_INODE_SIZE)
+#define pram_memlock_inode(pi) {\
+	pram_sync_inode(pi);\
+	pram_memlock_range((pi), PRAM_INODE_SIZE);\
+}
+
+/* write protection for a data block */
+#define pram_memunlock_block(sb, bp) \
+	pram_memunlock_range((bp), (sb)->s_blocksize)
+#define pram_memlock_block(sb, bp) \
+	pram_memlock_range((bp), (sb)->s_blocksize)
+
+static inline void *
+pram_get_bitmap(struct super_block *sb)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	return (void *)ps + be64_to_cpu(ps->s_bitmap_start);
+}
+
+/* If this is part of a read-modify-write of the inode metadata,
+   pram_memunlock_inode() before calling! */
+static inline struct pram_inode *
+pram_get_inode(struct super_block *sb, u64 ino)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	return ino ? (struct pram_inode *)((void *)ps + ino) : NULL;
+}
+
+static inline ino_t
+pram_get_inodenr(struct super_block *sb, struct pram_inode *pi)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	return (ino_t)((unsigned long)pi - (unsigned long)ps);
+}
+
+static inline u64
+pram_get_block_off(struct super_block *sb, unsigned long blocknr)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	return (u64)(be64_to_cpu(ps->s_bitmap_start) +
+			     (blocknr << sb->s_blocksize_bits));
+}
+
+static inline unsigned long
+pram_get_blocknr(struct super_block *sb, u64 block)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	return (block - be64_to_cpu(ps->s_bitmap_start)) >> sb->s_blocksize_bits;
+}
+
+/* If this is part of a read-modify-write of the block,
+   pram_memunlock_block() before calling! */
+static inline void *
+pram_get_block(struct super_block *sb, u64 block)
+{
+	struct pram_super_block *ps = pram_get_super(sb);
+	return block ? ((void *)ps + block) : NULL;
+}
+
+struct pram_inode_vfs {
+#ifdef CONFIG_PRAMFS_XATTR
+	/*
+	 * Extended attributes can be read independently of the main file
+	 * data. Taking i_mutex even when reading would cause contention
+	 * between readers of EAs and writers of regular file data, so
+	 * instead we synchronize on xattr_sem when reading or changing
+	 * EAs.
+	 */
+	struct rw_semaphore xattr_sem;
+#endif
+	/*
+	 * truncate_mutex is for serialising the truncate path against
+	 * get/update block.
+	 */
+	struct mutex truncate_mutex;
+	struct inode vfs_inode;
+};
+
+static inline struct pram_inode_vfs *PRAM_I(struct inode *inode)
+{
+	return container_of(inode, struct pram_inode_vfs, vfs_inode);
+}
+
+/*
+ * Inodes and files operations
+ */
+
+/* dir.c */
+extern struct file_operations pram_dir_operations;
+
+/* file.c */
+extern struct inode_operations pram_file_inode_operations;
+extern struct file_operations pram_file_operations;
+
+/* inode.c */
+extern struct address_space_operations pram_aops;
+
+/* namei.c */
+extern struct inode_operations pram_dir_inode_operations;
+
+/* symlink.c */
+extern struct inode_operations pram_symlink_inode_operations;
+
+extern struct backing_dev_info pram_backing_dev_info;
+
+#endif	/* __PRAM_H */
diff -Nurp linux-2.6.36-orig/include/linux/magic.h linux-2.6.36/include/linux/magic.h
--- linux-2.6.36-orig/include/linux/magic.h	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/include/linux/magic.h	2010-09-14 18:49:52.000000000 +0200
@@ -39,6 +39,7 @@
 #define OPENPROM_SUPER_MAGIC	0x9fa1
 #define PROC_SUPER_MAGIC	0x9fa0
 #define QNX4_SUPER_MAGIC	0x002f		/* qnx4 fs detection */
+#define PRAM_SUPER_MAGIC	0xEFFA
  #define REISERFS_SUPER_MAGIC	0x52654973	/* used by gcc */
 					/* used by file system utilities that
diff -Nurp linux-2.6.36-orig/include/linux/pram_fs.h linux-2.6.36/include/linux/pram_fs.h
--- linux-2.6.36-orig/include/linux/pram_fs.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/include/linux/pram_fs.h	2010-09-25 14:10:14.000000000 +0200
@@ -0,0 +1,118 @@
+/*
+ * FILE NAME include/linux/pram_fs.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Definitions for the PRAMFS filesystem.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#ifndef _LINUX_PRAM_FS_H
+#define _LINUX_PRAM_FS_H
+
+#include <linux/types.h>
+#include <linux/magic.h>
+
+/*
+ * The PRAM filesystem constants/structures
+ */
+
+/*
+ * Maximal count of links to a file
+ */
+#define PRAM_LINK_MAX		32000
+
+#define PRAM_MIN_BLOCK_SIZE 512
+#define PRAM_MAX_BLOCK_SIZE 4096
+#define PRAM_DEF_BLOCK_SIZE 2048
+
+#define PRAM_INODE_SIZE 128 /* must be power of two */
+#define PRAM_INODE_BITS   7
+
+/*
+ * Structure of a directory entry in PRAMFS.
+ * Offsets are to the inode that holds the referenced dentry.
+ */
+struct pram_dentry {
+	__be64	d_next;     /* next dentry in this directory */
+	__be64	d_prev;     /* previous dentry in this directory */
+	__be64	d_parent;   /* parent directory */
+	char	d_name[0];
+};
+
+
+/*
+ * Structure of an inode in PRAMFS
+ */
+struct pram_inode {
+	__be16	i_sum;          /* checksum of this inode */
+	__be32	i_uid;		/* Owner Uid */
+	__be32	i_gid;		/* Group Id */
+	__be16	i_mode;		/* File mode */
+	__be16	i_links_count;	/* Links count */
+	__be32	i_blocks;	/* Blocks count */
+	__be32	i_size;		/* Size of data in bytes */
+	__be32	i_atime;	/* Access time */
+	__be32	i_ctime;	/* Creation time */
+	__be32	i_mtime;	/* Modification time */
+	__be32	i_dtime;	/* Deletion Time */
+	__be64	i_xattr;	/* Extended attribute block */
+	__be32	i_generation;	/* File version (for NFS) */
+
+	union {
+		struct {
+			/*
+			 * ptr to row block of 2D block pointer array,
+			 * file block #'s 0 to (blocksize/8)^2 - 1.
+			 */
+			__be64 row_block;
+		} reg;   /* regular file or symlink inode */
+		struct {
+			__be64 head; /* first entry in this directory */
+			__be64 tail; /* last entry in this directory */
+		} dir;
+		struct {
+			__be32 rdev; /* major/minor # */
+		} dev;   /* device inode */
+	} i_type;
+
+	struct pram_dentry i_d;
+};
+
+#define PRAM_NAME_LEN \
+    (PRAM_INODE_SIZE - offsetof(struct pram_inode, i_d.d_name) - 1)
+
+
+#define PRAM_SB_SIZE 128 /* must be power of two */
+
+/*
+ * Structure of the super block in PRAMFS
+ */
+struct pram_super_block {
+	__be16	s_sum;          /* checksum of this sb, including padding */
+	__be64	s_size;         /* total size of fs in bytes */
+	__be32	s_blocksize;    /* blocksize in bytes */
+	__be32	s_inodes_count;	/* total inodes count (used or free) */
+	__be32	s_free_inodes_count;/* free inodes count */
+	__be32	s_free_inode_hint;  /* start hint for locating free inodes */
+	__be32	s_blocks_count;	/* total data blocks count (used or free) */
+	__be32	s_free_blocks_count;/* free data blocks count */
+	__be32	s_free_blocknr_hint;/* free data blocks count */
+	__be64	s_bitmap_start; /* data block in-use bitmap location */
+	__be32	s_bitmap_blocks;/* size of bitmap in number of blocks */
+	__be32	s_mtime;	/* Mount time */
+	__be32	s_wtime;	/* Write time */
+	__be16	s_magic;	/* Magic signature */
+	char	s_volume_name[16]; /* volume name */
+};
+
+/* The root inode follows immediately after the redundant super block */
+#define PRAM_ROOT_INO (PRAM_SB_SIZE*2)
+
+#endif	/* _LINUX_PRAM_FS_H */
diff -Nurp linux-2.6.36-orig/include/linux/pram_fs_sb.h linux-2.6.36/include/linux/pram_fs_sb.h
--- linux-2.6.36-orig/include/linux/pram_fs_sb.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/include/linux/pram_fs_sb.h	2010-09-18 08:39:39.000000000 +0200
@@ -0,0 +1,44 @@
+/*
+ * FILE NAME include/linux/pram_fs_sb.h
+ *
+ * Definitions for the PRAM filesystem.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _LINUX_PRAM_FS_SB
+#define _LINUX_PRAM_FS_SB
+
+/*
+ * PRAM filesystem super-block data in memory
+ */
+struct pram_sb_info {
+	/*
+	 * base physical and virtual address of PRAMFS (which is also
+	 * the pointer to the super block)
+	 */
+	phys_addr_t phys_addr;
+	void *virt_addr;
+
+	/* Mount options */
+	unsigned long bpi;
+	unsigned long num_inodes;
+	unsigned long blocksize;
+	unsigned long initsize;
+	uid_t uid;		    /* Mount uid for root directory */
+	gid_t gid;		    /* Mount gid for root directory */
+	mode_t mode;		    /* Mount mode for root directory */
+	atomic_t next_generation;
+#ifdef CONFIG_PRAMFS_XATTR
+	struct rb_root desc_tree;
+	spinlock_t desc_tree_lock;
+#endif
+};
+
+#endif	/* _LINUX_PRAM_FS_SB */
 

^ permalink raw reply

* [PATCH 09/16] pramfs: dir operations
From: Marco Stornelli @ 2010-10-10 16:32 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

File operations for directories.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/dir.c linux-2.6.36/fs/pramfs/dir.c
--- linux-2.6.36-orig/fs/pramfs/dir.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/dir.c	2010-09-17 19:08:54.000000000 +0200
@@ -0,0 +1,215 @@
+/*
+ * FILE NAME fs/pramfs/dir.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * File operations for directories.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include "pram.h"
+
+/*
+ *	Parent is locked.
+ */
+int pram_add_link(struct dentry *dentry, struct inode *inode)
+{
+	struct inode *dir = dentry->d_parent->d_inode;
+	struct pram_inode *pidir, *pi, *pitail = NULL;
+	u64 tail_ino, prev_ino;
+
+	const char *name = dentry->d_name.name;
+
+	int namelen = dentry->d_name.len > PRAM_NAME_LEN ?
+		PRAM_NAME_LEN : dentry->d_name.len;
+
+	pidir = pram_get_inode(dir->i_sb, dir->i_ino);
+	pi = pram_get_inode(dir->i_sb, inode->i_ino);
+
+	dir->i_mtime = dir->i_ctime = CURRENT_TIME;
+
+	tail_ino = be64_to_cpu(pidir->i_type.dir.tail);
+	if (tail_ino != 0) {
+		pitail = pram_get_inode(dir->i_sb, tail_ino);
+		pram_memunlock_inode(pitail);
+		pitail->i_d.d_next = cpu_to_be64(inode->i_ino);
+		pram_memlock_inode(pitail);
+
+		prev_ino = tail_ino;
+
+		pram_memunlock_inode(pidir);
+		pidir->i_type.dir.tail = cpu_to_be64(inode->i_ino);
+		pidir->i_mtime = cpu_to_be32(dir->i_mtime.tv_sec);
+		pidir->i_ctime = cpu_to_be32(dir->i_ctime.tv_sec);
+		pram_memlock_inode(pidir);
+	} else {
+		/* the directory is empty */
+		prev_ino = 0;
+
+		pram_memunlock_inode(pidir);
+		pidir->i_type.dir.tail = cpu_to_be64(inode->i_ino);
+		pidir->i_type.dir.head = cpu_to_be64(inode->i_ino);
+		pidir->i_mtime = cpu_to_be32(dir->i_mtime.tv_sec);
+		pidir->i_ctime = cpu_to_be32(dir->i_ctime.tv_sec);
+		pram_memlock_inode(pidir);
+	}
+
+
+	pram_memunlock_inode(pi);
+	pi->i_d.d_prev = cpu_to_be64(prev_ino);
+	pi->i_d.d_parent = cpu_to_be64(dir->i_ino);
+	memcpy(pi->i_d.d_name, name, namelen);
+	pi->i_d.d_name[namelen] = '\0';
+	pram_memlock_inode(pi);
+	return 0;
+}
+
+int pram_remove_link(struct inode *inode)
+{
+	struct super_block *sb = inode->i_sb;
+	struct pram_inode *prev = NULL;
+	struct pram_inode *next = NULL;
+	struct pram_inode *pidir, *pi;
+
+	pi = pram_get_inode(sb, inode->i_ino);
+	pidir = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_parent));
+	if (!pidir)
+		return -EACCES;
+
+	if (inode->i_ino == be64_to_cpu(pidir->i_type.dir.head)) {
+		/* first inode in directory */
+		next = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_next));
+
+		if (next) {
+			pram_memunlock_inode(next);
+			next->i_d.d_prev = 0;
+			pram_memlock_inode(next);
+
+			pram_memunlock_inode(pidir);
+			pidir->i_type.dir.head = pi->i_d.d_next;
+		} else {
+			pram_memunlock_inode(pidir);
+			pidir->i_type.dir.head = 0;
+			pidir->i_type.dir.tail = 0;
+		}
+		pram_memlock_inode(pidir);
+	} else if (inode->i_ino == be64_to_cpu(pidir->i_type.dir.tail)) {
+		/* last inode in directory */
+		prev = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_prev));
+
+		pram_memunlock_inode(prev);
+		prev->i_d.d_next = 0;
+		pram_memlock_inode(prev);
+
+		pram_memunlock_inode(pidir);
+		pidir->i_type.dir.tail = pi->i_d.d_prev;
+		pram_memlock_inode(pidir);
+	} else {
+		/* somewhere in the middle */
+		prev = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_prev));
+		next = pram_get_inode(sb, be64_to_cpu(pi->i_d.d_next));
+
+		if (prev && next) {
+			pram_memunlock_inode(prev);
+			prev->i_d.d_next = pi->i_d.d_next;
+			pram_memlock_inode(prev);
+
+			pram_memunlock_inode(next);
+			next->i_d.d_prev = pi->i_d.d_prev;
+			pram_memlock_inode(next);
+		}
+	}
+
+	pram_memunlock_inode(pi);
+	pi->i_d.d_next = 0;
+	pi->i_d.d_prev = 0;
+	pi->i_d.d_parent = 0;
+	pram_memlock_inode(pi);
+
+	return 0;
+}
+
+#define S_SHIFT 12
+static unsigned int dtype_by_mode[S_IFMT >> S_SHIFT] = {
+	[S_IFREG >> S_SHIFT]    DT_REG,
+	[S_IFDIR >> S_SHIFT]    DT_DIR,
+	[S_IFCHR >> S_SHIFT]    DT_CHR,
+	[S_IFBLK >> S_SHIFT]    DT_BLK,
+	[S_IFIFO >> S_SHIFT]    DT_FIFO,
+	[S_IFSOCK >> S_SHIFT]   DT_SOCK,
+	[S_IFLNK >> S_SHIFT]    DT_LNK,
+};
+
+static int pram_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+	struct inode *inode = filp->f_dentry->d_inode;
+	struct super_block *sb = inode->i_sb;
+	struct pram_inode *pi;
+	int namelen, ret = 0;
+	char *name;
+	ino_t ino;
+
+	pi = pram_get_inode(sb, inode->i_ino);
+
+	switch ((unsigned long)filp->f_pos) {
+	case 0:
+		ret = filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR);
+		filp->f_pos++;
+		return ret;
+	case 1:
+		ret = filldir(dirent, "..", 2, 1, be64_to_cpu(pi->i_d.d_parent), DT_DIR);
+		ino = be64_to_cpu(pi->i_type.dir.head);
+		filp->f_pos = ino ? ino : 2;
+		return ret;
+	case 2:
+		ino = be64_to_cpu(pi->i_type.dir.head);
+		if (ino) {
+			filp->f_pos = ino;
+			pi = pram_get_inode(sb, ino);
+			break;
+		} else {
+			/* the directory is empty */
+			filp->f_pos = 2;
+			return 0;
+		}
+	case 3:
+		return 0;
+	default:
+		ino = filp->f_pos;
+		pi = pram_get_inode(sb, ino);
+		break;
+	}
+
+	while (pi && !be16_to_cpu(pi->i_links_count)) {
+		ino = filp->f_pos = be64_to_cpu(pi->i_d.d_next);
+		pi = pram_get_inode(sb, ino);
+	}
+
+	if (pi) {
+		name = pi->i_d.d_name;
+		namelen = strlen(name);
+
+		ret = filldir(dirent, name, namelen,
+			      filp->f_pos, ino,
+			      dtype_by_mode[(be16_to_cpu(pi->i_mode) & S_IFMT)>>S_SHIFT]);
+		filp->f_pos = pi->i_d.d_next ? be64_to_cpu(pi->i_d.d_next) : 3;
+	} else
+		filp->f_pos = 3;
+
+	return ret;
+}
+
+struct file_operations pram_dir_operations = {
+	.read		= generic_read_dir,
+	.readdir	= pram_readdir,
+	.fsync		= noop_fsync,
+};
 

^ permalink raw reply

* [PATCH 10/16] pramfs: xip operations
From: Marco Stornelli @ 2010-10-10 16:32 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

XIP operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/xip.c linux-2.6.36/fs/pramfs/xip.c
--- linux-2.6.36-orig/fs/pramfs/xip.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xip.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,90 @@
+/*
+ * FILE NAME fs/pramfs/xip.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * XIP operations.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/buffer_head.h>
+#include "pram.h"
+#include "xip.h"
+
+static int pram_find_and_alloc_blocks(struct inode *inode, sector_t iblock,
+				     sector_t *data_block, int create)
+{
+	int err = -EIO;
+	u64 block;
+
+	mutex_lock(&PRAM_I(inode)->truncate_lock);
+
+	block = pram_find_data_block(inode, iblock);
+
+	if (!block) {
+		if (!create) {
+			err = -ENODATA;
+			goto err;
+		}
+
+		err = pram_alloc_blocks(inode, iblock, 1);
+		if (err)
+			goto err;
+
+		block = pram_find_data_block(inode, iblock);
+		if (!block) {
+			err = -ENODATA;
+			goto err;
+		}
+	}
+
+	*data_block = block;
+	err = 0;
+
+ err:
+	mutex_unlock(&PRAM_I(inode)->truncate_lock);
+	return err;
+}
+
+
+static int __pram_get_block(struct inode *inode, pgoff_t pgoff, int create,
+		   sector_t *result)
+{
+	int rc = 0;
+	sector_t iblock;
+
+	/* find starting block number to access */
+	iblock = (sector_t)pgoff << (PAGE_CACHE_SHIFT - inode->i_blkbits);
+
+	rc = pram_find_and_alloc_blocks(inode, iblock, result, create);
+
+	if (rc == -ENODATA)
+		BUG_ON(create);
+
+	return rc;
+}
+
+int pram_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, int create,
+				void **kmem, unsigned long *pfn)
+{
+	int rc;
+	sector_t block;
+
+	/* first, retrieve the block */
+	rc = __pram_get_block(mapping->host, pgoff, create, &block);
+	if (rc)
+		goto exit;
+
+	*kmem = pram_get_block(mapping->host->i_sb, block);
+	*pfn = page_to_pfn(virt_to_page((unsigned long)*kmem));
+
+exit:
+	return rc;
+}
diff -Nurp linux-2.6.36-orig/fs/pramfs/xip.h linux-2.6.36/fs/pramfs/xip.h
--- linux-2.6.36-orig/fs/pramfs/xip.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xip.h	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,24 @@
+/*
+ * FILE NAME fs/pramfs/xip.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * XIP operations.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifdef CONFIG_PRAMFS_XIP
+
+int pram_get_xip_mem(struct address_space *, pgoff_t, int, void **,
+							      unsigned long *);
+
+#else
+
+#define pram_get_xip_mem NULL
+
+#endif
+
 

^ permalink raw reply

* [PATCH 11/16] pramfs: ACL management
From: Marco Stornelli @ 2010-10-10 16:33 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

ACL operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/acl.c linux-2.6.36/fs/pramfs/acl.c
--- linux-2.6.36-orig/fs/pramfs/acl.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/acl.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,418 @@
+/*
+ * FILE NAME fs/pramfs/acl.h
+ *
+ * BRIEF MODULE DESCRIPTION
+ *
+ * POSIX ACL operations
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ *
+ * based on fs/ext2/acl.c with the following copyright:
+ *
+ * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/capability.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include "pram.h"
+#include "xattr.h"
+#include "acl.h"
+
+/*
+ * Load ACL information from filesystem.
+ */
+static struct posix_acl *pram_acl_load(const void *value, size_t size)
+{
+	const char *end = (char *)value + size;
+	int n, count;
+	struct posix_acl *acl;
+
+	if (!value)
+		return NULL;
+	if (size < sizeof(struct pram_acl_header))
+		 return ERR_PTR(-EINVAL);
+	if (((struct pram_acl_header *)value)->a_version !=
+	    cpu_to_be32(PRAM_ACL_VERSION))
+		return ERR_PTR(-EINVAL);
+	value = (char *)value + sizeof(struct pram_acl_header);
+	count = pram_acl_count(size);
+	if (count < 0)
+		return ERR_PTR(-EINVAL);
+	if (count == 0)
+		return NULL;
+	acl = posix_acl_alloc(count, GFP_KERNEL);
+	if (!acl)
+		return ERR_PTR(-ENOMEM);
+	for (n = 0; n < count; n++) {
+		struct pram_acl_entry *entry = (struct pram_acl_entry *)value;
+		if ((char *)value + sizeof(struct pram_acl_entry_short) > end)
+			goto fail;
+		acl->a_entries[n].e_tag  = be16_to_cpu(entry->e_tag);
+		acl->a_entries[n].e_perm = be16_to_cpu(entry->e_perm);
+		switch (acl->a_entries[n].e_tag) {
+		case ACL_USER_OBJ:
+		case ACL_GROUP_OBJ:
+		case ACL_MASK:
+		case ACL_OTHER:
+			value = (char *)value +
+				sizeof(struct pram_acl_entry_short);
+			acl->a_entries[n].e_id = ACL_UNDEFINED_ID;
+			break;
+		case ACL_USER:
+		case ACL_GROUP:
+			value = (char *)value + sizeof(struct pram_acl_entry);
+			if ((char *)value > end)
+				goto fail;
+			acl->a_entries[n].e_id =
+				be32_to_cpu(entry->e_id);
+			break;
+		default:
+			goto fail;
+		}
+	}
+	if (value != end)
+		goto fail;
+	return acl;
+
+fail:
+	posix_acl_release(acl);
+	return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Save ACL information into the filesystem.
+ */
+static void *pram_acl_save(const struct posix_acl *acl, size_t *size)
+{
+	struct pram_acl_header *ext_acl;
+	char *e;
+	size_t n;
+
+	*size = pram_acl_size(acl->a_count);
+	ext_acl = kmalloc(sizeof(struct pram_acl_header) + acl->a_count *
+			sizeof(struct pram_acl_entry), GFP_KERNEL);
+	if (!ext_acl)
+		return ERR_PTR(-ENOMEM);
+	ext_acl->a_version = cpu_to_be32(PRAM_ACL_VERSION);
+	e = (char *)ext_acl + sizeof(struct pram_acl_header);
+	for (n = 0; n < acl->a_count; n++) {
+		struct pram_acl_entry *entry = (struct pram_acl_entry *)e;
+		entry->e_tag  = cpu_to_be16(acl->a_entries[n].e_tag);
+		entry->e_perm = cpu_to_be16(acl->a_entries[n].e_perm);
+		switch (acl->a_entries[n].e_tag) {
+		case ACL_USER:
+		case ACL_GROUP:
+			entry->e_id =
+				cpu_to_be32(acl->a_entries[n].e_id);
+			e += sizeof(struct pram_acl_entry);
+			break;
+		case ACL_USER_OBJ:
+		case ACL_GROUP_OBJ:
+		case ACL_MASK:
+		case ACL_OTHER:
+			e += sizeof(struct pram_acl_entry_short);
+			break;
+		default:
+			goto fail;
+		}
+	}
+	return (char *)ext_acl;
+
+fail:
+	kfree(ext_acl);
+	return ERR_PTR(-EINVAL);
+}
+
+/*
+ * inode->i_mutex: don't care
+ */
+static struct posix_acl *pram_get_acl(struct inode *inode, int type)
+{
+	int name_index;
+	char *value = NULL;
+	struct posix_acl *acl;
+	int retval;
+
+	acl = get_cached_acl(inode, type);
+	if (acl != ACL_NOT_CACHED)
+		return acl;
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+		name_index = PRAM_XATTR_INDEX_POSIX_ACL_ACCESS;
+		break;
+	case ACL_TYPE_DEFAULT:
+		name_index = PRAM_XATTR_INDEX_POSIX_ACL_DEFAULT;
+		break;
+	default:
+		BUG();
+	}
+	retval = pram_xattr_get(inode, name_index, "", NULL, 0);
+	if (retval > 0) {
+		value = kmalloc(retval, GFP_KERNEL);
+		if (!value)
+			return ERR_PTR(-ENOMEM);
+		retval = pram_xattr_get(inode, name_index, "", value, retval);
+	}
+	if (retval > 0)
+		acl = pram_acl_load(value, retval);
+	else if (retval == -ENODATA || retval == -ENOSYS)
+		acl = NULL;
+	else
+		acl = ERR_PTR(retval);
+	kfree(value);
+
+	if (!IS_ERR(acl))
+		set_cached_acl(inode, type, acl);
+
+	return acl;
+}
+
+/*
+ * inode->i_mutex: down
+ */
+static int pram_set_acl(struct inode *inode, int type, struct posix_acl *acl)
+{
+	int name_index;
+	void *value = NULL;
+	size_t size = 0;
+	int error;
+
+	if (S_ISLNK(inode->i_mode))
+		return -EOPNOTSUPP;
+
+	switch (type) {
+	case ACL_TYPE_ACCESS:
+		name_index = PRAM_XATTR_INDEX_POSIX_ACL_ACCESS;
+		if (acl) {
+			mode_t mode = inode->i_mode;
+			error = posix_acl_equiv_mode(acl, &mode);
+			if (error < 0)
+				return error;
+			else {
+				inode->i_mode = mode;
+				inode->i_ctime = CURRENT_TIME_SEC;
+				mark_inode_dirty(inode);
+				if (error == 0)
+					acl = NULL;
+			}
+		}
+		break;
+	case ACL_TYPE_DEFAULT:
+		name_index = PRAM_XATTR_INDEX_POSIX_ACL_DEFAULT;
+		if (!S_ISDIR(inode->i_mode))
+			return acl ? -EACCES : 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (acl) {
+		value = pram_acl_save(acl, &size);
+		if (IS_ERR(value))
+			return (int)PTR_ERR(value);
+	}
+
+	error = pram_xattr_set(inode, name_index, "", value, size, 0);
+
+	kfree(value);
+	if (!error)
+		set_cached_acl(inode, type, acl);
+	return error;
+}
+
+int pram_check_acl(struct inode *inode, int mask)
+{
+	struct posix_acl *acl = pram_get_acl(inode, ACL_TYPE_ACCESS);
+
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (acl) {
+		int error = posix_acl_permission(inode, acl, mask);
+		posix_acl_release(acl);
+		return error;
+	}
+
+	return -EAGAIN;
+}
+
+/*
+ * Initialize the ACLs of a new inode. Called from pram_new_inode.
+ *
+ * dir->i_mutex: down
+ * inode->i_mutex: up (access to inode is still exclusive)
+ */
+int pram_init_acl(struct inode *inode, struct inode *dir)
+{
+	struct posix_acl *acl = NULL;
+	int error = 0;
+
+	if (!S_ISLNK(inode->i_mode)) {
+		acl = pram_get_acl(dir, ACL_TYPE_DEFAULT);
+		if (IS_ERR(acl))
+			return PTR_ERR(acl);
+		if (!acl)
+			inode->i_mode &= ~current_umask();
+	}
+
+	if (acl) {
+		struct posix_acl *clone;
+		mode_t mode;
+
+		if (S_ISDIR(inode->i_mode)) {
+			error = pram_set_acl(inode, ACL_TYPE_DEFAULT, acl);
+			if (error)
+				goto cleanup;
+		}
+		clone = posix_acl_clone(acl, GFP_KERNEL);
+		error = -ENOMEM;
+		if (!clone)
+			goto cleanup;
+		mode = inode->i_mode;
+		error = posix_acl_create_masq(clone, &mode);
+		if (error >= 0) {
+			inode->i_mode = mode;
+			if (error > 0) {
+				/* This is an extended ACL */
+				error = pram_set_acl(inode,
+						     ACL_TYPE_ACCESS, clone);
+			}
+		}
+		posix_acl_release(clone);
+	}
+cleanup:
+       posix_acl_release(acl);
+       return error;
+}
+
+/*
+ * Does chmod for an inode that may have an Access Control List. The
+ * inode->i_mode field must be updated to the desired value by the caller
+ * before calling this function.
+ * Returns 0 on success, or a negative error number.
+ *
+ * We change the ACL rather than storing some ACL entries in the file
+ * mode permission bits (which would be more efficient), because that
+ * would break once additional permissions (like  ACL_APPEND, ACL_DELETE
+ * for directories) are added. There are no more bits available in the
+ * file mode.
+ *
+ * inode->i_mutex: down
+ */
+int pram_acl_chmod(struct inode *inode)
+{
+	struct posix_acl *acl, *clone;
+	int error;
+
+	if (S_ISLNK(inode->i_mode))
+		return -EOPNOTSUPP;
+	acl = pram_get_acl(inode, ACL_TYPE_ACCESS);
+	if (IS_ERR(acl) || !acl)
+		return PTR_ERR(acl);
+	clone = posix_acl_clone(acl, GFP_KERNEL);
+	posix_acl_release(acl);
+	if (!clone)
+		return -ENOMEM;
+	error = posix_acl_chmod_masq(clone, inode->i_mode);
+	if (!error)
+		error = pram_set_acl(inode, ACL_TYPE_ACCESS, clone);
+	posix_acl_release(clone);
+	return error;
+}
+
+/*
+ * Extended attribut handlers
+ */
+static size_t pram_xattr_list_acl_access(struct dentry *dentry, char *list,
+					size_t list_size, const char *name,
+					size_t name_len, int type)
+{
+	const size_t size = sizeof(POSIX_ACL_XATTR_ACCESS);
+
+	if (list && size <= list_size)
+		memcpy(list, POSIX_ACL_XATTR_ACCESS, size);
+	return size;
+}
+
+static size_t pram_xattr_list_acl_default(struct dentry *dentry, char *list,
+					  size_t list_size, const char *name,
+					  size_t name_len, int type)
+{
+	const size_t size = sizeof(POSIX_ACL_XATTR_DEFAULT);
+
+	if (list && size <= list_size)
+		memcpy(list, POSIX_ACL_XATTR_DEFAULT, size);
+	return size;
+}
+
+static int pram_xattr_get_acl(struct dentry *dentry, const char *name,
+			      void *buffer, size_t size, int type)
+{
+	struct posix_acl *acl;
+	int error;
+
+	if (strcmp(name, "") != 0)
+		return -EINVAL;
+
+	acl = pram_get_acl(dentry->d_inode, type);
+	if (IS_ERR(acl))
+		return PTR_ERR(acl);
+	if (acl == NULL)
+		return -ENODATA;
+	error = posix_acl_to_xattr(acl, buffer, size);
+	posix_acl_release(acl);
+
+	return error;
+}
+
+static int pram_xattr_set_acl(struct dentry *dentry, const char *name,
+			      const void *value, size_t size, int flags, int type)
+{
+	struct posix_acl *acl;
+	int error;
+
+	if (strcmp(name, "") != 0)
+		return -EINVAL;
+	if (!is_owner_or_cap(dentry->d_inode))
+		return -EPERM;
+
+	if (value) {
+		acl = posix_acl_from_xattr(value, size);
+		if (IS_ERR(acl))
+			return PTR_ERR(acl);
+		else if (acl) {
+			error = posix_acl_valid(acl);
+			if (error)
+				goto release_and_out;
+		}
+	} else
+		acl = NULL;
+
+	error = pram_set_acl(dentry->d_inode, type, acl);
+
+release_and_out:
+	posix_acl_release(acl);
+	return error;
+}
+
+const struct xattr_handler pram_xattr_acl_access_handler = {
+	.prefix	= POSIX_ACL_XATTR_ACCESS,
+	.flags	= ACL_TYPE_ACCESS,
+	.list	= pram_xattr_list_acl_access,
+	.get	= pram_xattr_get_acl,
+	.set	= pram_xattr_set_acl,
+};
+
+const struct xattr_handler pram_xattr_acl_default_handler = {
+	.prefix	= POSIX_ACL_XATTR_DEFAULT,
+	.flags	= ACL_TYPE_DEFAULT,
+	.list	= pram_xattr_list_acl_default,
+	.get	= pram_xattr_get_acl,
+	.set	= pram_xattr_set_acl,
+};
diff -Nurp linux-2.6.36-orig/fs/pramfs/acl.h linux-2.6.36/fs/pramfs/acl.h
--- linux-2.6.36-orig/fs/pramfs/acl.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/acl.h	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,88 @@
+/*
+ * FILE NAME fs/pramfs/acl.h
+ *
+ * BRIEF MODULE DESCRIPTION
+ *
+ * POSIX ACL operations
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * Based on fs/ext2/acl.h with the following copyright:
+ *
+ * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
+ *
+ */
+
+#include <linux/posix_acl_xattr.h>
+
+#define PRAM_ACL_VERSION	0x0001
+
+struct pram_acl_entry {
+	__be16		e_tag;
+	__be16		e_perm;
+	__be32		e_id;
+};
+
+struct pram_acl_entry_short {
+	__be16		e_tag;
+	__be16		e_perm;
+};
+
+struct pram_acl_header {
+	__be32		a_version;
+};
+
+static inline size_t pram_acl_size(int count)
+{
+	if (count <= 4) {
+		return sizeof(struct pram_acl_header) +
+		       count * sizeof(struct pram_acl_entry_short);
+	} else {
+		return sizeof(struct pram_acl_header) +
+		       4 * sizeof(struct pram_acl_entry_short) +
+		       (count - 4) * sizeof(struct pram_acl_entry);
+	}
+}
+
+static inline int pram_acl_count(size_t size)
+{
+	ssize_t s;
+	size -= sizeof(struct pram_acl_header);
+	s = size - 4 * sizeof(struct pram_acl_entry_short);
+	if (s < 0) {
+		if (size % sizeof(struct pram_acl_entry_short))
+			return -1;
+		return size / sizeof(struct pram_acl_entry_short);
+	} else {
+		if (s % sizeof(struct pram_acl_entry))
+			return -1;
+		return s / sizeof(struct pram_acl_entry) + 4;
+	}
+}
+
+#ifdef CONFIG_PRAMFS_POSIX_ACL
+
+/* acl.c */
+extern int pram_check_acl(struct inode *, int);
+extern int pram_acl_chmod(struct inode *);
+extern int pram_init_acl(struct inode *, struct inode *);
+
+#else
+#include <linux/sched.h>
+#define pram_check_acl	NULL
+#define pram_get_acl	NULL
+#define pram_set_acl	NULL
+
+static inline int pram_acl_chmod(struct inode *inode)
+{
+	return 0;
+}
+
+static inline int pram_init_acl(struct inode *inode, struct inode *dir)
+{
+	return 0;
+}
+#endif
 

^ permalink raw reply

* [PATCH 12/16] pramfs: extended attributes
From: Marco Stornelli @ 2010-10-10 16:34 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Extended attributes operations.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr.c linux-2.6.36/fs/pramfs/xattr.c
--- linux-2.6.36-orig/fs/pramfs/xattr.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr.c	2010-09-14 19:45:40.000000000 +0200
@@ -0,0 +1,1108 @@
+/*
+ * FILE NAME fs/pramfs/xattr.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Extended attributes operations.
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ *
+ * based on fs/ext2/xattr.c with the following copyright:
+ *
+ * Fix by Harrison Xing <harrison@mountainviewdata.com>.
+ * Extended attributes for symlinks and special files added per
+ *  suggestion of Luka Renko <luka.renko@hermes.si>.
+ * xattr consolidation Copyright (c) 2004 James Morris <jmorris@redhat.com>,
+ *  Red Hat Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+/*
+ * Extended attributes are stored in blocks allocated outside of
+ * any inode. The i_xattr field is then made to point to this allocated
+ * block. If all extended attributes of an inode are identical, these
+ * inodes may share the same extended attribute block. Such situations
+ * are automatically detected by keeping a cache of recent attribute block
+ * numbers and hashes over the block's contents in memory.
+ *
+ *
+ * Extended attribute block layout:
+ *
+ *   +------------------+
+ *   | header           |
+ *   | entry 1          | |
+ *   | entry 2          | | growing downwards
+ *   | entry 3          | v
+ *   | four null bytes  |
+ *   | . . .            |
+ *   | value 1          | ^
+ *   | value 3          | | growing upwards
+ *   | value 2          | |
+ *   +------------------+
+ *
+ * The block header is followed by multiple entry descriptors. These entry
+ * descriptors are variable in size, and alligned to PRAM_XATTR_PAD
+ * byte boundaries. The entry descriptors are sorted by attribute name,
+ * so that two extended attribute blocks can be compared efficiently.
+ *
+ * Attribute values are aligned to the end of the block, stored in
+ * no specific order. They are also padded to PRAM_XATTR_PAD byte
+ * boundaries. No additional gaps are left between them.
+ *
+ * Locking strategy
+ * ----------------
+ * pi->i_xattr is protected by PRAM_I(inode)->xattr_sem.
+ * EA blocks are only changed if they are exclusive to an inode, so
+ * holding xattr_sem also means that nothing but the EA block's reference
+ * count will change. Multiple writers to an EA block are synchronized
+ * by the mutex in each block descriptor. Block descriptors are kept in a
+ * red black tree and the key is the absolute block number.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mbcache.h>
+#include <linux/rwsem.h>
+#include <linux/security.h>
+#include "pram.h"
+#include "xattr.h"
+#include "acl.h"
+#include "desctree.h"
+
+#define HDR(bp) ((struct pram_xattr_header *)(bp))
+#define ENTRY(ptr) ((struct pram_xattr_entry *)(ptr))
+#define FIRST_ENTRY(bh) ENTRY(HDR(bh)+1)
+#define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0)
+#define GET_DESC(sbi, blocknr) lookup_xblock_desc(sbi, blocknr, pram_xblock_desc_cache, 1)
+#define LOOKUP_DESC(sbi, blocknr) lookup_xblock_desc(sbi, blocknr, NULL, 0)
+
+#ifdef PRAM_XATTR_DEBUG
+# define ea_idebug(inode, f...) do { \
+		printk(KERN_DEBUG "inode %ld: ", inode->i_ino); \
+		printk(f); \
+		printk("\n"); \
+	} while (0)
+# define ea_bdebug(blocknr, f...) do { \
+		printk(KERN_DEBUG "block %lu: ", blocknr); \
+		printk(f); \
+		printk("\n"); \
+	} while (0)
+#else
+# define ea_idebug(f...)
+# define ea_bdebug(f...)
+#endif
+
+static int pram_xattr_set2(struct inode *, char *, struct pram_xblock_desc *, struct pram_xattr_header *);
+
+static int pram_xattr_cache_insert(struct super_block *sb, unsigned long blocknr, u32 xhash);
+static struct pram_xblock_desc *pram_xattr_cache_find(struct inode *,
+						 struct pram_xattr_header *);
+static void pram_xattr_rehash(struct pram_xattr_header *,
+			      struct pram_xattr_entry *);
+
+static struct mb_cache *pram_xattr_cache;
+static struct kmem_cache *pram_xblock_desc_cache;
+
+static const struct xattr_handler *pram_xattr_handler_map[] = {
+	[PRAM_XATTR_INDEX_USER]		     = &pram_xattr_user_handler,
+#ifdef CONFIG_PRAMFS_POSIX_ACL
+	[PRAM_XATTR_INDEX_POSIX_ACL_ACCESS]  = &pram_xattr_acl_access_handler,
+	[PRAM_XATTR_INDEX_POSIX_ACL_DEFAULT] = &pram_xattr_acl_default_handler,
+#endif
+	[PRAM_XATTR_INDEX_TRUSTED]	     = &pram_xattr_trusted_handler,
+#ifdef CONFIG_PRAMFS_SECURITY
+	[PRAM_XATTR_INDEX_SECURITY]	     = &pram_xattr_security_handler,
+#endif
+};
+
+const struct xattr_handler *pram_xattr_handlers[] = {
+	&pram_xattr_user_handler,
+	&pram_xattr_trusted_handler,
+#ifdef CONFIG_PRAMFS_POSIX_ACL
+	&pram_xattr_acl_access_handler,
+	&pram_xattr_acl_default_handler,
+#endif
+#ifdef CONFIG_PRAMFS_SECURITY
+	&pram_xattr_security_handler,
+#endif
+	NULL
+};
+
+static void desc_put(struct super_block *sb, struct pram_xblock_desc *desc)
+{
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	if (!put_xblock_desc(sbi, desc)) {
+		/* Ok we can free the block and its descriptor */
+		pram_dbg("freeing block %lu and its descriptor", desc->blocknr);
+		pram_free_block(sb, desc->blocknr);
+		kmem_cache_free(pram_xblock_desc_cache, desc);
+	}
+}
+
+static inline const struct xattr_handler *pram_xattr_handler(int name_index)
+{
+	const struct xattr_handler *handler = NULL;
+
+	if (name_index > 0 && name_index < ARRAY_SIZE(pram_xattr_handler_map))
+		handler = pram_xattr_handler_map[name_index];
+	return handler;
+}
+
+/*
+ * pram_xattr_get()
+ *
+ * Copy an extended attribute into the buffer
+ * provided, or compute the buffer size required.
+ * Buffer is NULL to compute the size of the buffer required.
+ *
+ * Returns a negative error number on failure, or the number of bytes
+ * used / required on success.
+ */
+int pram_xattr_get(struct inode *inode, int name_index, const char *name,
+	       void *buffer, size_t buffer_size)
+{
+	char *bp = NULL;
+	struct pram_xattr_entry *entry;
+	struct pram_xblock_desc *desc;
+	struct pram_inode *pi;
+	size_t name_len, size;
+	char *end;
+	int error = 0;
+	unsigned long blocknr;
+	struct super_block *sb = inode->i_sb;
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+
+	ea_idebug(inode, "name=%d<%s>, buffer=%p, buffer_size=%ld",
+		  name_index, name, buffer, (long)buffer_size);
+
+	pi = pram_get_inode(sb, inode->i_ino);
+	if (!pi)
+		return -EINVAL;
+	if (name == NULL)
+		return -EINVAL;
+	down_read(&PRAM_I(inode)->xattr_sem);
+	error = -ENODATA;
+	if (!pi->i_xattr)
+		goto cleanup;
+	ea_idebug(inode, "reading block %llu", be64_to_cpu(pi->i_xattr));
+	bp = pram_get_block(sb, be64_to_cpu(pi->i_xattr));
+	error = -EIO;
+	if (!bp)
+		goto cleanup;
+	end = bp + sb->s_blocksize;
+	blocknr = pram_get_blocknr(sb, be64_to_cpu(pi->i_xattr));
+	ea_bdebug(blocknr, "refcount=%d", be32_to_cpu(HDR(bp)->h_refcount));
+	if (HDR(bp)->h_magic != cpu_to_be32(PRAM_XATTR_MAGIC)) {
+bad_block:	pram_err("inode %ld: bad block %llu", inode->i_ino,
+		be64_to_cpu(pi->i_xattr));
+		error = -EIO;
+		goto cleanup;
+	}
+	/* find named attribute */
+	name_len = strlen(name);
+
+	error = -ERANGE;
+	if (name_len > 255)
+		goto cleanup;
+	entry = FIRST_ENTRY(bp);
+	while (!IS_LAST_ENTRY(entry)) {
+		struct pram_xattr_entry *next =
+			PRAM_XATTR_NEXT(entry);
+		if ((char *)next >= end)
+			goto bad_block;
+		if (name_index == entry->e_name_index &&
+		    name_len == entry->e_name_len &&
+		    memcmp(name, entry->e_name, name_len) == 0)
+			goto found;
+		entry = next;
+	}
+	/* Check the remaining name entries */
+	while (!IS_LAST_ENTRY(entry)) {
+		struct pram_xattr_entry *next =
+			PRAM_XATTR_NEXT(entry);
+		if ((char *)next >= end)
+			goto bad_block;
+		entry = next;
+	}
+
+	desc = GET_DESC(sbi, blocknr);
+	if (IS_ERR(desc)) {
+		error = -ENOMEM;
+		goto cleanup;
+	}
+	desc_put(sb, desc);
+	if (pram_xattr_cache_insert(sb, blocknr,
+					be32_to_cpu(HDR(bp)->h_hash)))
+		ea_idebug(inode, "cache insert failed");
+	error = -ENODATA;
+	goto cleanup;
+found:
+	/* check the buffer size */
+	if (entry->e_value_block != 0)
+		goto bad_block;
+	size = be32_to_cpu(entry->e_value_size);
+	if (size > inode->i_sb->s_blocksize ||
+	    be16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)
+		goto bad_block;
+
+	desc = GET_DESC(sbi, blocknr);
+	if (IS_ERR(desc)) {
+		error = -ENOMEM;
+		goto cleanup;
+	}
+	desc_put(sb, desc);
+	if (pram_xattr_cache_insert(sb, blocknr,
+					be32_to_cpu(HDR(bp)->h_hash)))
+		ea_idebug(inode, "cache insert failed");
+	if (buffer) {
+		error = -ERANGE;
+		if (size > buffer_size)
+			goto cleanup;
+		/* return value of attribute */
+		memcpy(buffer, bp + be16_to_cpu(entry->e_value_offs),
+			size);
+	}
+	error = size;
+
+cleanup:
+	up_read(&PRAM_I(inode)->xattr_sem);
+
+	return error;
+}
+
+/*
+ * pram_xattr_list()
+ *
+ * Copy a list of attribute names into the buffer
+ * provided, or compute the buffer size required.
+ * Buffer is NULL to compute the size of the buffer required.
+ *
+ * Returns a negative error number on failure, or the number of bytes
+ * used / required on success.
+ */
+static int pram_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size)
+{
+	struct inode *inode = dentry->d_inode;
+	char *bp = NULL;
+	struct pram_xattr_entry *entry;
+	struct pram_xblock_desc *desc;
+	struct pram_inode *pi;
+	char *end;
+	size_t rest = buffer_size;
+	int error = 0;
+	unsigned long blocknr;
+	struct super_block *sb = inode->i_sb;
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+
+	ea_idebug(inode, "buffer=%p, buffer_size=%ld",
+		  buffer, (long)buffer_size);
+
+	pi = pram_get_inode(sb, inode->i_ino);
+	if (!pi)
+		return error;
+	down_read(&PRAM_I(inode)->xattr_sem);
+	error = 0;
+	if (!pi->i_xattr)
+		goto cleanup;
+	ea_idebug(inode, "reading block %llu", be64_to_cpu(pi->i_xattr));
+	bp = pram_get_block(sb, be64_to_cpu(pi->i_xattr));
+	blocknr = pram_get_blocknr(sb, be64_to_cpu(pi->i_xattr));
+	error = -EIO;
+	if (!bp)
+		goto cleanup;
+	ea_bdebug(blocknr, "refcount=%d", be32_to_cpu(HDR(bp)->h_refcount));
+	end = bp + sb->s_blocksize;
+	if (HDR(bp)->h_magic != cpu_to_be32(PRAM_XATTR_MAGIC)) {
+bad_block:	pram_err("inode %ld: bad block %llu", inode->i_ino,
+			be64_to_cpu(pi->i_xattr));
+		error = -EIO;
+		goto cleanup;
+	}
+
+	/* check the on-disk data structure */
+	entry = FIRST_ENTRY(bp);
+	while (!IS_LAST_ENTRY(entry)) {
+		struct pram_xattr_entry *next = PRAM_XATTR_NEXT(entry);
+
+		if ((char *)next >= end)
+			goto bad_block;
+		entry = next;
+	}
+
+	desc = GET_DESC(sbi, blocknr);
+	if (IS_ERR(desc)) {
+		error = -ENOMEM;
+		goto cleanup;
+	}
+	desc_put(sb, desc);
+	if (pram_xattr_cache_insert(sb, blocknr,
+					be32_to_cpu(HDR(bp)->h_hash)))
+			ea_idebug(inode, "cache insert failed");
+
+	/* list the attribute names */
+	for (entry = FIRST_ENTRY(bp); !IS_LAST_ENTRY(entry);
+	     entry = PRAM_XATTR_NEXT(entry)) {
+		const struct xattr_handler *handler =
+			pram_xattr_handler(entry->e_name_index);
+
+		if (handler) {
+			size_t size = handler->list(dentry, buffer, rest,
+						    entry->e_name,
+						    entry->e_name_len,
+						    handler->flags);
+			if (buffer) {
+				if (size > rest) {
+					error = -ERANGE;
+					goto cleanup;
+				}
+				buffer += size;
+			}
+			rest -= size;
+		}
+	}
+	error = buffer_size - rest;  /* total size */
+
+cleanup:
+	up_read(&PRAM_I(inode)->xattr_sem);
+
+	return error;
+}
+
+/*
+ * Inode operation listxattr()
+ *
+ * dentry->d_inode->i_mutex: don't care
+ */
+ssize_t pram_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+	return pram_xattr_list(dentry, buffer, size);
+}
+
+/*
+ * pram_xattr_set()
+ *
+ * Create, replace or remove an extended attribute for this inode. Buffer
+ * is NULL to remove an existing extended attribute, and non-NULL to
+ * either replace an existing extended attribute, or create a new extended
+ * attribute. The flags XATTR_REPLACE and XATTR_CREATE
+ * specify that an extended attribute must exist and must not exist
+ * previous to the call, respectively.
+ *
+ * Returns 0, or a negative error number on failure.
+ */
+int pram_xattr_set(struct inode *inode, int name_index, const char *name,
+	       const void *value, size_t value_len, int flags)
+{
+	struct super_block *sb = inode->i_sb;
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	struct pram_xattr_header *header = NULL;
+	struct pram_xattr_entry *here, *last;
+	struct pram_inode *pi;
+	struct pram_xblock_desc *desc = NULL;
+	size_t name_len, free, min_offs = sb->s_blocksize;
+	int not_found = 1, error;
+	char *end;
+	char *bp = NULL;
+	unsigned long blocknr = 0;
+
+	/*
+	 * header -- Points either into bp, or to a temporarily
+	 *           allocated buffer.
+	 * here -- The named entry found, or the place for inserting, within
+	 *         the block pointed to by header.
+	 * last -- Points right after the last named entry within the block
+	 *         pointed to by header.
+	 * min_offs -- The offset of the first value (values are aligned
+	 *             towards the end of the block).
+	 * end -- Points right after the block pointed to by header.
+	 */
+
+	ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld",
+		  name_index, name, value, (long)value_len);
+
+	if (value == NULL)
+		value_len = 0;
+	if (name == NULL)
+		return -EINVAL;
+	name_len = strlen(name);
+	if (name_len > 255 || value_len > sb->s_blocksize)
+		return -ERANGE;
+	pi = pram_get_inode(sb, inode->i_ino);
+	if (!pi)
+		return -EINVAL;
+	down_write(&PRAM_I(inode)->xattr_sem);
+	if (pi->i_xattr) {
+		/* The inode already has an extended attribute block. */
+		bp = pram_get_block(sb, be64_to_cpu(pi->i_xattr));
+		error = -EIO;
+		if (!bp)
+			goto cleanup;
+		blocknr = pram_get_blocknr(sb, be64_to_cpu(pi->i_xattr));
+		ea_bdebug(blocknr, "refcount=%d", be32_to_cpu(HDR(bp)->h_refcount));
+		header = HDR(bp);
+		end = bp + sb->s_blocksize;
+		if (header->h_magic != cpu_to_be32(PRAM_XATTR_MAGIC)) {
+bad_block:		pram_err("inode %ld: bad block %llu", inode->i_ino,
+				   be64_to_cpu(pi->i_xattr));
+			error = -EIO;
+			goto cleanup;
+		}
+		/* Find the named attribute. */
+		here = FIRST_ENTRY(bp);
+		while (!IS_LAST_ENTRY(here)) {
+			struct pram_xattr_entry *next = PRAM_XATTR_NEXT(here);
+			if ((char *)next >= end)
+				goto bad_block;
+			if (!here->e_value_block && here->e_value_size) {
+				size_t offs = be16_to_cpu(here->e_value_offs);
+				if (offs < min_offs)
+					min_offs = offs;
+			}
+			not_found = name_index - here->e_name_index;
+			if (!not_found)
+				not_found = name_len - here->e_name_len;
+			if (!not_found)
+				not_found = memcmp(name, here->e_name, name_len);
+			if (not_found <= 0)
+				break;
+			here = next;
+		}
+		last = here;
+		/* We still need to compute min_offs and last. */
+		while (!IS_LAST_ENTRY(last)) {
+			struct pram_xattr_entry *next = PRAM_XATTR_NEXT(last);
+			if ((char *)next >= end)
+				goto bad_block;
+			if (!last->e_value_block && last->e_value_size) {
+				size_t offs = be16_to_cpu(last->e_value_offs);
+				if (offs < min_offs)
+					min_offs = offs;
+			}
+			last = next;
+		}
+
+		/* Check whether we have enough space left. */
+		free = min_offs - ((char *)last - (char *)header) - sizeof(__u32);
+	} else {
+		/* We will use a new extended attribute block. */
+		free = sb->s_blocksize -
+			sizeof(struct pram_xattr_header) - sizeof(__u32);
+		here = last = NULL;  /* avoid gcc uninitialized warning. */
+	}
+
+	if (not_found) {
+		/* Request to remove a nonexistent attribute? */
+		error = -ENODATA;
+		if (flags & XATTR_REPLACE)
+			goto cleanup;
+		error = 0;
+		if (value == NULL)
+			goto cleanup;
+	} else {
+		/* Request to create an existing attribute? */
+		error = -EEXIST;
+		if (flags & XATTR_CREATE)
+			goto cleanup;
+		if (!here->e_value_block && here->e_value_size) {
+			size_t size = be32_to_cpu(here->e_value_size);
+
+			if (be16_to_cpu(here->e_value_offs) + size >
+			    sb->s_blocksize || size > sb->s_blocksize)
+				goto bad_block;
+			free += PRAM_XATTR_SIZE(size);
+		}
+		free += PRAM_XATTR_LEN(name_len);
+	}
+	error = -ENOSPC;
+	if (free < PRAM_XATTR_LEN(name_len) + PRAM_XATTR_SIZE(value_len))
+		goto cleanup;
+
+	/* Here we know that we can set the new attribute. */
+
+	if (header) {
+		struct mb_cache_entry *ce;
+
+		desc = GET_DESC(sbi, blocknr);
+		if (IS_ERR(desc)) {
+			error = -ENOMEM;
+			goto cleanup;
+		}
+
+		/* assert(header == HDR(bp)); */
+		ce = mb_cache_entry_get(pram_xattr_cache, (struct block_device *)sbi,
+							blocknr);
+		mutex_lock(&desc->lock);
+		pram_memunlock_block(sb, bp);
+		if (header->h_refcount == cpu_to_be32(1)) {
+			ea_bdebug(blocknr, "modifying in-place");
+			if (ce)
+				mb_cache_entry_free(ce);
+			/* keep it locked while modifying it. */
+		} else {
+			int offset;
+
+			if (ce)
+				mb_cache_entry_release(ce);
+			pram_memlock_block(sb, bp);
+			mutex_unlock(&desc->lock);
+			ea_bdebug(desc->blocknr, "cloning");
+			header = kmalloc(inode->i_sb->s_blocksize, GFP_KERNEL);
+			error = -ENOMEM;
+			if (header == NULL)
+				goto cleanup;
+			memcpy(header, HDR(bp), inode->i_sb->s_blocksize);
+			header->h_refcount = cpu_to_be32(1);
+
+			offset = (char *)here - bp;
+			here = ENTRY((char *)header + offset);
+			offset = (char *)last - bp;
+			last = ENTRY((char *)header + offset);
+		}
+	} else {
+		/* Allocate a buffer where we construct the new block. */
+		header = kzalloc(sb->s_blocksize, GFP_KERNEL);
+		error = -ENOMEM;
+		if (header == NULL)
+			goto cleanup;
+		end = (char *)header + sb->s_blocksize;
+		header->h_magic = cpu_to_be32(PRAM_XATTR_MAGIC);
+		header->h_refcount = cpu_to_be32(1);
+		last = here = ENTRY(header+1);
+	}
+
+	/* Iff we are modifying the block in-place, the block is locked here. */
+
+	if (not_found) {
+		/* Insert the new name. */
+		size_t size = PRAM_XATTR_LEN(name_len);
+		size_t rest = (char *)last - (char *)here;
+		memmove((char *)here + size, here, rest);
+		memset(here, 0, size);
+		here->e_name_index = name_index;
+		here->e_name_len = name_len;
+		memcpy(here->e_name, name, name_len);
+	} else {
+		if (!here->e_value_block && here->e_value_size) {
+			char *first_val = (char *)header + min_offs;
+			size_t offs = be16_to_cpu(here->e_value_offs);
+			char *val = (char *)header + offs;
+			size_t size = PRAM_XATTR_SIZE(
+				be32_to_cpu(here->e_value_size));
+
+			if (size == PRAM_XATTR_SIZE(value_len)) {
+				/* The old and the new value have the same
+				   size. Just replace. */
+				here->e_value_size = cpu_to_be32(value_len);
+				memset(val + size - PRAM_XATTR_PAD, 0,
+				       PRAM_XATTR_PAD); /* Clear pad bytes. */
+				memcpy(val, value, value_len);
+				goto skip_replace;
+			}
+
+			/* Remove the old value. */
+			memmove(first_val + size, first_val, val - first_val);
+			memset(first_val, 0, size);
+			here->e_value_offs = 0;
+			min_offs += size;
+
+			/* Adjust all value offsets. */
+			last = ENTRY(header+1);
+			while (!IS_LAST_ENTRY(last)) {
+				size_t o = be16_to_cpu(last->e_value_offs);
+				if (!last->e_value_block && o < offs)
+					last->e_value_offs =
+						cpu_to_be16(o + size);
+				last = PRAM_XATTR_NEXT(last);
+			}
+		}
+		if (value == NULL) {
+			/* Remove the old name. */
+			size_t size = PRAM_XATTR_LEN(name_len);
+			last = ENTRY((char *)last - size);
+			memmove(here, (char *)here + size,
+				(char *)last - (char *)here);
+			memset(last, 0, size);
+		}
+	}
+
+	if (value != NULL) {
+		/* Insert the new value. */
+		here->e_value_size = cpu_to_be32(value_len);
+		if (value_len) {
+			size_t size = PRAM_XATTR_SIZE(value_len);
+			char *val = (char *)header + min_offs - size;
+			here->e_value_offs =
+				cpu_to_be16((char *)val - (char *)header);
+			memset(val + size - PRAM_XATTR_PAD, 0,
+			       PRAM_XATTR_PAD); /* Clear the pad bytes. */
+			memcpy(val, value, value_len);
+		}
+	}
+
+skip_replace:
+	if (IS_LAST_ENTRY(ENTRY(header+1))) {
+		/* This block is now empty. */
+		if (bp && header == HDR(bp)) {
+			/* we were modifying in-place. */
+			pram_memlock_block(sb, bp);
+			mutex_unlock(&desc->lock);
+		}
+		error = pram_xattr_set2(inode, bp, desc, NULL);
+	} else {
+		pram_xattr_rehash(header, here);
+		if (bp && header == HDR(bp)) {
+			/* we were modifying in-place. */
+			pram_memlock_block(sb, bp);
+			mutex_unlock(&desc->lock);
+		}
+		error = pram_xattr_set2(inode, bp, desc, header);
+	}
+
+cleanup:
+	desc_put(sb, desc);
+	if (!(bp && header == HDR(bp)))
+		kfree(header);
+	up_write(&PRAM_I(inode)->xattr_sem);
+
+	return error;
+}
+
+/*
+ * Second half of pram_xattr_set(): Update the file system.
+ */
+static int pram_xattr_set2(struct inode *inode, char *old_bp,
+			   struct pram_xblock_desc *old_desc,
+			   struct pram_xattr_header *header)
+{
+	struct super_block *sb = inode->i_sb;
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	struct pram_xblock_desc *new_desc = NULL;
+	unsigned long blocknr;
+	struct pram_inode *pi;
+	int error;
+	char *new_bp = NULL;
+
+	if (header) {
+		new_desc = pram_xattr_cache_find(inode, header);
+		if (new_desc) {
+			new_bp = pram_get_block(sb, pram_get_block_off(sb, new_desc->blocknr));
+			/* We found an identical block in the cache. */
+			if (new_bp == old_bp) {
+				ea_bdebug(new_desc->blocknr, "keeping this block");
+			} else {
+				/* The old block is released after updating
+				   the inode.  */
+				ea_bdebug(new_desc->blocknr, "reusing block");
+				pram_memunlock_block(sb, new_bp);
+				be32_add_cpu(&HDR(new_bp)->h_refcount, 1);
+				pram_memlock_block(sb, new_bp);
+				ea_bdebug(new_desc->blocknr, "refcount now=%d",
+					be32_to_cpu(HDR(new_bp)->h_refcount));
+			}
+			blocknr = new_desc->blocknr;
+			mutex_unlock(&new_desc->lock);
+			desc_put(sb, new_desc);
+		} else if (old_bp && header == HDR(old_bp)) {
+			/* Keep this block. No need to lock the block as we
+			   don't need to change the reference count. */
+			new_bp = old_bp;
+			pram_xattr_cache_insert(sb, old_desc->blocknr, HDR(new_bp)->h_hash);
+			blocknr = old_desc->blocknr;
+		} else {
+			/* We need to allocate a new block */
+			struct pram_xblock_desc *new_desc;
+
+			error = pram_new_block(sb, &blocknr, 1);
+			if (error)
+				goto out;
+			ea_idebug(inode, "creating block %lu", blocknr);
+			new_desc = kmem_cache_alloc(pram_xblock_desc_cache, GFP_KERNEL);
+			if (!new_desc) {
+				pram_free_block(sb, blocknr);
+				error = -EIO;
+				goto out;
+			}
+			xblock_desc_init_always(new_desc);
+			new_desc->blocknr = blocknr;
+			new_bp = pram_get_block(sb, pram_get_block_off(sb, blocknr));
+			if (!new_bp) {
+				pram_free_block(sb, blocknr);
+				kmem_cache_free(pram_xblock_desc_cache, new_desc);
+				error = -EIO;
+				goto out;
+			}
+			pram_memunlock_block(sb, new_bp);
+			memcpy(new_bp, header, sb->s_blocksize);
+			pram_memlock_block(sb, new_bp);
+			insert_xblock_desc(sbi, new_desc);
+			pram_xattr_cache_insert(sb, new_desc->blocknr, HDR(new_bp)->h_hash);
+		}
+	}
+
+	/* Update the inode. */
+	pi = pram_get_inode(sb, inode->i_ino);
+	pram_memunlock_inode(pi);
+	pi->i_xattr = new_bp ? be64_to_cpu(pram_get_block_off(sb, blocknr)) : 0;
+	inode->i_ctime = CURRENT_TIME_SEC;
+	pi->i_ctime = cpu_to_be32(inode->i_ctime.tv_sec);
+	pram_memlock_inode(pi);
+
+	error = 0;
+	if (old_bp && old_bp != new_bp) {
+		struct mb_cache_entry *ce;
+
+		/* Here old_desc MUST be valid or we have a bug */
+		BUG_ON(!old_desc);
+
+		/*
+		 * If there was an old block and we are no longer using it,
+		 * release the old block.
+		 */
+		ce = mb_cache_entry_get(pram_xattr_cache, (struct block_device *)sbi,
+					old_desc->blocknr);
+		mutex_lock(&old_desc->lock);
+		if (HDR(old_bp)->h_refcount == cpu_to_be32(1)) {
+			/* Free the old block. */
+			if (ce)
+				mb_cache_entry_free(ce);
+			ea_bdebug(old_desc->blocknr, "freeing");
+			mutex_unlock(&old_desc->lock);
+			/* Caller will call desc_put later */
+			mark_free_desc(old_desc);
+		} else {
+			/* Decrement the refcount only. */
+			pram_memunlock_block(sb, old_bp);
+			be32_add_cpu(&HDR(old_bp)->h_refcount, -1);
+			pram_memlock_block(sb, old_bp);
+			if (ce)
+				mb_cache_entry_release(ce);
+			ea_bdebug(old_desc->blocknr, "refcount now=%d",
+			be32_to_cpu(HDR(old_bp)->h_refcount));
+			mutex_unlock(&old_desc->lock);
+		}
+	}
+
+out:
+	return error;
+}
+
+/*
+ * pram_xattr_delete_inode()
+ *
+ * Free extended attribute resources associated with this inode. This
+ * is called immediately before an inode is freed.
+ */
+void pram_xattr_delete_inode(struct inode *inode)
+{
+	char *bp = NULL;
+	struct mb_cache_entry *ce;
+	struct pram_inode *pi;
+	struct pram_xblock_desc *desc;
+	struct super_block *sb = inode->i_sb;
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	unsigned long blocknr;
+
+	pi = pram_get_inode(sb, inode->i_ino);
+	if (!pi)
+		goto cleanup;
+	down_write(&PRAM_I(inode)->xattr_sem);
+	if (!pi->i_xattr)
+		goto cleanup;
+	bp = pram_get_block(sb, be64_to_cpu(pi->i_xattr));
+	if (!bp) {
+		pram_err("inode %ld: block %llu read error", inode->i_ino,
+			be64_to_cpu(pi->i_xattr));
+		goto cleanup;
+	}
+	blocknr = pram_get_blocknr(sb, be64_to_cpu(pi->i_xattr));
+	if (HDR(bp)->h_magic != cpu_to_be32(PRAM_XATTR_MAGIC)) {
+		pram_err("inode %ld: bad block %llu", inode->i_ino,
+			be64_to_cpu(pi->i_xattr));
+		goto cleanup;
+	}
+	ce = mb_cache_entry_get(pram_xattr_cache, (struct block_device *)sbi, blocknr);
+	desc = LOOKUP_DESC(sbi, blocknr);
+	if (!desc) {
+		pram_err("descriptor not found for blocknr %lu", blocknr);
+		goto cleanup;
+	}
+	mutex_lock(&desc->lock);
+	if (HDR(bp)->h_refcount == cpu_to_be32(1)) {
+		if (ce)
+			mb_cache_entry_free(ce);
+		mark_free_desc(desc);
+	} else {
+		be32_add_cpu(&HDR(bp)->h_refcount, -1);
+		if (ce)
+			mb_cache_entry_release(ce);
+		ea_bdebug(blocknr, "refcount now=%d",
+			be32_to_cpu(HDR(bp)->h_refcount));
+		mutex_unlock(&desc->lock);
+	}
+	desc_put(sb, desc);
+
+cleanup:
+	up_write(&PRAM_I(inode)->xattr_sem);
+}
+
+/*
+ * pram_xattr_put_super()
+ *
+ * This is called when a file system is unmounted.
+ */
+void pram_xattr_put_super(struct super_block *sb)
+{
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	/*
+	* NOTE: we haven't got any block device to use with mb. Mb code
+	* doesn't use the pointer but it uses only the address as unique
+	* key so it's safe to use a "general purpose" address. We use
+	* super block info data as unique key. Maybe it'd be better to
+	* change mb code in order to use a generic void pointer to a
+	* generic id.
+	*/
+	mb_cache_shrink((struct block_device *)sbi);
+	erase_tree(sbi, pram_xblock_desc_cache);
+	kmem_cache_shrink(pram_xblock_desc_cache);
+}
+
+
+/*
+ * pram_xattr_cache_insert()
+ *
+ * Create a new entry in the extended attribute cache, and insert
+ * it unless such an entry is already in the cache.
+ *
+ * Returns 0, or a negative error number on failure.
+ */
+static int pram_xattr_cache_insert(struct super_block *sb, unsigned long blocknr, u32 xhash)
+{
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+	__u32 hash = be32_to_cpu(xhash);
+	struct mb_cache_entry *ce;
+	int error;
+
+	ce = mb_cache_entry_alloc(pram_xattr_cache, GFP_NOFS);
+	if (!ce)
+		return -ENOMEM;
+	error = mb_cache_entry_insert(ce, (struct block_device *)sbi, blocknr, hash);
+	if (error) {
+		mb_cache_entry_free(ce);
+		if (error == -EBUSY) {
+			ea_bdebug(blocknr, "already in cache");
+			error = 0;
+		}
+	} else {
+		ea_bdebug(blocknr, "inserting [%x]", (int)hash);
+		mb_cache_entry_release(ce);
+	}
+	return error;
+}
+
+/*
+ * pram_xattr_cmp()
+ *
+ * Compare two extended attribute blocks for equality.
+ *
+ * Returns 0 if the blocks are equal, 1 if they differ, and
+ * a negative error number on errors.
+ */
+static int pram_xattr_cmp(struct pram_xattr_header *header1,
+			  struct pram_xattr_header *header2)
+{
+	struct pram_xattr_entry *entry1, *entry2;
+
+	entry1 = ENTRY(header1+1);
+	entry2 = ENTRY(header2+1);
+	while (!IS_LAST_ENTRY(entry1)) {
+		if (IS_LAST_ENTRY(entry2))
+			return 1;
+		if (entry1->e_hash != entry2->e_hash ||
+		    entry1->e_name_index != entry2->e_name_index ||
+		    entry1->e_name_len != entry2->e_name_len ||
+		    entry1->e_value_size != entry2->e_value_size ||
+		    memcmp(entry1->e_name, entry2->e_name, entry1->e_name_len))
+			return 1;
+		if (entry1->e_value_block != 0 || entry2->e_value_block != 0)
+			return -EIO;
+		if (memcmp((char *)header1 + be16_to_cpu(entry1->e_value_offs),
+			   (char *)header2 + be16_to_cpu(entry2->e_value_offs),
+			   be32_to_cpu(entry1->e_value_size)))
+			return 1;
+
+		entry1 = PRAM_XATTR_NEXT(entry1);
+		entry2 = PRAM_XATTR_NEXT(entry2);
+	}
+	if (!IS_LAST_ENTRY(entry2))
+		return 1;
+	return 0;
+}
+
+/*
+ * pram_xattr_cache_find()
+ *
+ * Find an identical extended attribute block.
+ *
+ * Returns a locked extended block descriptor for the block found, or
+ * NULL if such a block was not found or an error occurred.
+ * The block, however, is not memory unlocked.
+ */
+static struct pram_xblock_desc *pram_xattr_cache_find(struct inode *inode, struct pram_xattr_header *header)
+{
+	__u32 hash = be32_to_cpu(header->h_hash);
+	struct mb_cache_entry *ce;
+	struct pram_xblock_desc *desc;
+	struct super_block *sb = inode->i_sb;
+	struct pram_sb_info *sbi = (struct pram_sb_info *)sb->s_fs_info;
+
+	if (!header->h_hash)
+		return NULL;  /* never share */
+	ea_idebug(inode, "looking for cached blocks [%x]", (int)hash);
+again:
+	ce = mb_cache_entry_find_first(pram_xattr_cache, (struct block_device *)sbi, hash);
+	while (ce) {
+		char *bp;
+
+		if (IS_ERR(ce)) {
+			if (PTR_ERR(ce) == -EAGAIN)
+				goto again;
+			break;
+		}
+
+		bp = pram_get_block(sb, pram_get_block_off(sb, (unsigned long)ce->e_block));
+		if (!bp) {
+			pram_err("inode %ld: block %ld read error",
+				inode->i_ino, (unsigned long) ce->e_block);
+		} else {
+			desc = LOOKUP_DESC(sbi, ce->e_block);
+			if (!desc) {
+				mb_cache_entry_release(ce);
+				return NULL;
+			}
+			mutex_lock(&desc->lock);
+			if (be32_to_cpu(HDR(bp)->h_refcount) >
+				   PRAM_XATTR_REFCOUNT_MAX) {
+				ea_idebug(inode, "block %ld refcount %d>%d",
+					  (unsigned long) ce->e_block,
+					  be32_to_cpu(HDR(bp)->h_refcount),
+					  PRAM_XATTR_REFCOUNT_MAX);
+			} else if (!pram_xattr_cmp(header, HDR(bp))) {
+				mb_cache_entry_release(ce);
+				return desc;
+			}
+			mutex_unlock(&desc->lock);
+		}
+		ce = mb_cache_entry_find_next(ce, (struct block_device *)sbi, hash);
+	}
+	return NULL;
+}
+
+#define NAME_HASH_SHIFT 5
+#define VALUE_HASH_SHIFT 16
+
+/*
+ * pram_xattr_hash_entry()
+ *
+ * Compute the hash of an extended attribute.
+ */
+static inline void pram_xattr_hash_entry(struct pram_xattr_header *header,
+					 struct pram_xattr_entry *entry)
+{
+	__u32 hash = 0;
+	char *name = entry->e_name;
+	int n;
+
+	for (n = 0; n < entry->e_name_len; n++) {
+		hash = (hash << NAME_HASH_SHIFT) ^
+		       (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^
+		       *name++;
+	}
+
+	if (entry->e_value_block == 0 && entry->e_value_size != 0) {
+		__be32 *value = (__be32 *)((char *)header +
+			be16_to_cpu(entry->e_value_offs));
+		for (n = (be32_to_cpu(entry->e_value_size) +
+		     PRAM_XATTR_ROUND) >> PRAM_XATTR_PAD_BITS; n; n--) {
+			hash = (hash << VALUE_HASH_SHIFT) ^
+			       (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^
+			       be32_to_cpu(*value++);
+		}
+	}
+	entry->e_hash = cpu_to_be32(hash);
+}
+
+#undef NAME_HASH_SHIFT
+#undef VALUE_HASH_SHIFT
+
+#define BLOCK_HASH_SHIFT 16
+
+/*
+ * pram_xattr_rehash()
+ *
+ * Re-compute the extended attribute hash value after an entry has changed.
+ */
+static void pram_xattr_rehash(struct pram_xattr_header *header,
+			      struct pram_xattr_entry *entry)
+{
+	struct pram_xattr_entry *here;
+	__u32 hash = 0;
+
+	pram_xattr_hash_entry(header, entry);
+	here = ENTRY(header+1);
+	while (!IS_LAST_ENTRY(here)) {
+		if (!here->e_hash) {
+			/* Block is not shared if an entry's hash value == 0 */
+			hash = 0;
+			break;
+		}
+		hash = (hash << BLOCK_HASH_SHIFT) ^
+		       (hash >> (8*sizeof(hash) - BLOCK_HASH_SHIFT)) ^
+		       be32_to_cpu(here->e_hash);
+		here = PRAM_XATTR_NEXT(here);
+	}
+	header->h_hash = cpu_to_be32(hash);
+}
+
+#undef BLOCK_HASH_SHIFT
+
+static void init_xblock_desc_once(void *foo)
+{
+	struct pram_xblock_desc *desc = (struct pram_xblock_desc *) foo;
+
+	xblock_desc_init_once(desc);
+}
+
+int __init init_pram_xattr(void)
+{
+	int ret = 0;
+	pram_xattr_cache = mb_cache_create("pram_xattr", 6);
+	if (!pram_xattr_cache) {
+		ret = -ENOMEM;
+		goto fail1;
+	}
+
+	pram_xblock_desc_cache = kmem_cache_create("pram_xblock_desc",
+					     sizeof(struct pram_xblock_desc),
+					     0, (SLAB_RECLAIM_ACCOUNT|
+						SLAB_MEM_SPREAD),
+					     init_xblock_desc_once);
+	if (!pram_xblock_desc_cache) {
+		ret = -ENOMEM;
+		goto fail2;
+	}
+
+	return 0;
+fail2:
+	mb_cache_destroy(pram_xattr_cache);
+fail1:
+	return ret;
+}
+
+void exit_pram_xattr(void)
+{
+	mb_cache_destroy(pram_xattr_cache);
+	kmem_cache_destroy(pram_xblock_desc_cache);
+}
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr.h linux-2.6.36/fs/pramfs/xattr.h
--- linux-2.6.36-orig/fs/pramfs/xattr.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr.h	2010-09-18 08:40:35.000000000 +0200
@@ -0,0 +1,134 @@
+/*
+ * FILE NAME fs/pramfs/xattr.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Extended attributes for the pram filesystem.
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ *
+ * based on fs/ext2/xattr.h with the following copyright:
+ *
+ *(C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/xattr.h>
+
+/* Magic value in attribute blocks */
+#define PRAM_XATTR_MAGIC		0x6d617270
+
+/* Maximum number of references to one attribute block */
+#define PRAM_XATTR_REFCOUNT_MAX		1024
+
+/* Name indexes */
+#define PRAM_XATTR_INDEX_USER			1
+#define PRAM_XATTR_INDEX_POSIX_ACL_ACCESS	2
+#define PRAM_XATTR_INDEX_POSIX_ACL_DEFAULT	3
+#define PRAM_XATTR_INDEX_TRUSTED		4
+#define	PRAM_XATTR_INDEX_LUSTRE			5
+#define PRAM_XATTR_INDEX_SECURITY	        6
+
+struct pram_xattr_header {
+	__be32	h_magic;	/* magic number for identification */
+	__be32	h_refcount;	/* reference count */
+	__be32	h_hash;		/* hash value of all attributes */
+	__u32	h_reserved[4];	/* zero right now */
+};
+
+struct pram_xattr_entry {
+	__u8	e_name_len;	/* length of name */
+	__u8	e_name_index;	/* attribute name index */
+	__be16	e_value_offs;	/* offset in disk block of value */
+	__be32	e_value_block;	/* disk block attribute is stored on (n/i) */
+	__be32	e_value_size;	/* size of attribute value */
+	__be32	e_hash;		/* hash value of name and value */
+	char	e_name[0];	/* attribute name */
+};
+
+#define PRAM_XATTR_PAD_BITS		2
+#define PRAM_XATTR_PAD		(1<<PRAM_XATTR_PAD_BITS)
+#define PRAM_XATTR_ROUND		(PRAM_XATTR_PAD-1)
+#define PRAM_XATTR_LEN(name_len) \
+	(((name_len) + PRAM_XATTR_ROUND + \
+	sizeof(struct pram_xattr_entry)) & ~PRAM_XATTR_ROUND)
+#define PRAM_XATTR_NEXT(entry) \
+	((struct pram_xattr_entry *)( \
+	  (char *)(entry) + PRAM_XATTR_LEN((entry)->e_name_len)))
+#define PRAM_XATTR_SIZE(size) \
+	(((size) + PRAM_XATTR_ROUND) & ~PRAM_XATTR_ROUND)
+
+#ifdef CONFIG_PRAMFS_XATTR
+
+extern const struct xattr_handler pram_xattr_user_handler;
+extern const struct xattr_handler pram_xattr_trusted_handler;
+extern const struct xattr_handler pram_xattr_acl_access_handler;
+extern const struct xattr_handler pram_xattr_acl_default_handler;
+extern const struct xattr_handler pram_xattr_security_handler;
+
+extern ssize_t pram_listxattr(struct dentry *, char *, size_t);
+
+extern int pram_xattr_get(struct inode *, int, const char *, void *, size_t);
+extern int pram_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
+
+extern void pram_xattr_delete_inode(struct inode *);
+extern void pram_xattr_put_super(struct super_block *);
+
+extern int init_pram_xattr(void) __init;
+extern void exit_pram_xattr(void);
+
+extern const struct xattr_handler *pram_xattr_handlers[];
+
+# else  /* CONFIG_PRAMFS_XATTR */
+
+static inline int
+pram_xattr_get(struct inode *inode, int name_index,
+	       const char *name, void *buffer, size_t size)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int
+pram_xattr_set(struct inode *inode, int name_index, const char *name,
+	       const void *value, size_t size, int flags)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void
+pram_xattr_delete_inode(struct inode *inode)
+{
+}
+
+static inline void
+pram_xattr_put_super(struct super_block *sb)
+{
+}
+
+static inline int
+init_pram_xattr(void)
+{
+	return 0;
+}
+
+static inline void
+exit_pram_xattr(void)
+{
+}
+
+#define pram_xattr_handlers NULL
+
+# endif  /* CONFIG_PRAMFS_XATTR */
+
+#ifdef CONFIG_PRAMFS_SECURITY
+extern int pram_init_security(struct inode *inode, struct inode *dir);
+#else
+static inline int pram_init_security(struct inode *inode, struct inode *dir)
+{
+	return 0;
+}
+#endif
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr_security.c linux-2.6.36/fs/pramfs/xattr_security.c
--- linux-2.6.36-orig/fs/pramfs/xattr_security.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr_security.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,80 @@
+/*
+ * FILE NAME fs/pramfs/xattr_security.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Handler for storing security labels as extended attributes.
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/pram_fs.h>
+#include <linux/security.h>
+#include "xattr.h"
+
+static size_t pram_xattr_security_list(struct dentry *dentry, char *list,
+				       size_t list_size, const char *name,
+				       size_t name_len, int type)
+{
+	const int prefix_len = XATTR_SECURITY_PREFIX_LEN;
+	const size_t total_len = prefix_len + name_len + 1;
+
+	if (list && total_len <= list_size) {
+		memcpy(list, XATTR_SECURITY_PREFIX, prefix_len);
+		memcpy(list+prefix_len, name, name_len);
+		list[prefix_len + name_len] = '\0';
+	}
+	return total_len;
+}
+
+static int pram_xattr_security_get(struct dentry *dentry, const char *name,
+		       void *buffer, size_t size, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_get(dentry->d_inode, PRAM_XATTR_INDEX_SECURITY, name,
+			      buffer, size);
+}
+
+static int pram_xattr_security_set(struct dentry *dentry, const char *name,
+		const void *value, size_t size, int flags, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_set(dentry->d_inode, PRAM_XATTR_INDEX_SECURITY, name,
+			      value, size, flags);
+}
+
+int pram_init_security(struct inode *inode, struct inode *dir)
+{
+	int err;
+	size_t len;
+	void *value;
+	char *name;
+
+	err = security_inode_init_security(inode, dir, &name, &value, &len);
+	if (err) {
+		if (err == -EOPNOTSUPP)
+			return 0;
+		return err;
+	}
+	err = pram_xattr_set(inode, PRAM_XATTR_INDEX_SECURITY,
+			     name, value, len, 0);
+	kfree(name);
+	kfree(value);
+	return err;
+}
+
+const struct xattr_handler pram_xattr_security_handler = {
+	.prefix	= XATTR_SECURITY_PREFIX,
+	.list	= pram_xattr_security_list,
+	.get	= pram_xattr_security_get,
+	.set	= pram_xattr_security_set,
+};
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr_trusted.c linux-2.6.36/fs/pramfs/xattr_trusted.c
--- linux-2.6.36-orig/fs/pramfs/xattr_trusted.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr_trusted.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,67 @@
+/*
+ * FILE NAME fs/pramfs/xattr_trusted.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Handler for trusted extended attributes.
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ *
+ * based on fs/ext2/xattr_trusted.c with the following copyright:
+ *
+ * Copyright (C) 2003 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/capability.h>
+#include <linux/fs.h>
+#include <linux/pram_fs.h>
+#include "xattr.h"
+
+static size_t pram_xattr_trusted_list(struct dentry *dentry, char *list,
+				size_t list_size, const char *name,
+				size_t name_len, int type)
+{
+	const int prefix_len = XATTR_TRUSTED_PREFIX_LEN;
+	const size_t total_len = prefix_len + name_len + 1;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return 0;
+
+	if (list && total_len <= list_size) {
+		memcpy(list, XATTR_TRUSTED_PREFIX, prefix_len);
+		memcpy(list+prefix_len, name, name_len);
+		list[prefix_len + name_len] = '\0';
+	}
+	return total_len;
+}
+
+static int pram_xattr_trusted_get(struct dentry *dentry, const char *name,
+				void *buffer, size_t size, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_get(dentry->d_inode, PRAM_XATTR_INDEX_TRUSTED, name,
+			      buffer, size);
+}
+
+static int pram_xattr_trusted_set(struct dentry *dentry, const char *name,
+		const void *value, size_t size, int flags, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_set(dentry->d_inode, PRAM_XATTR_INDEX_TRUSTED, name,
+			      value, size, flags);
+}
+
+const struct xattr_handler pram_xattr_trusted_handler = {
+	.prefix	= XATTR_TRUSTED_PREFIX,
+	.list	= pram_xattr_trusted_list,
+	.get	= pram_xattr_trusted_get,
+	.set	= pram_xattr_trusted_set,
+};
diff -Nurp linux-2.6.36-orig/fs/pramfs/xattr_user.c linux-2.6.36/fs/pramfs/xattr_user.c
--- linux-2.6.36-orig/fs/pramfs/xattr_user.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/xattr_user.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,63 @@
+/*
+ * FILE NAME fs/pramfs/xattr_user.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Handler for extended user attributes.
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ *
+ * based on fs/ext2/xattr_user.c with the following copyright:
+ *
+ * Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include "pram.h"
+#include "xattr.h"
+
+static size_t pram_xattr_user_list(struct dentry *dentry, char *list, size_t list_size,
+		const char *name, size_t name_len, int type)
+{
+	const size_t prefix_len = XATTR_USER_PREFIX_LEN;
+	const size_t total_len = prefix_len + name_len + 1;
+
+	if (list && total_len <= list_size) {
+		memcpy(list, XATTR_USER_PREFIX, prefix_len);
+		memcpy(list+prefix_len, name, name_len);
+		list[prefix_len + name_len] = '\0';
+	}
+	return total_len;
+}
+
+static int pram_xattr_user_get(struct dentry *dentry, const char *name,
+		void *buffer, size_t size, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+	return pram_xattr_get(dentry->d_inode, PRAM_XATTR_INDEX_USER,
+			      name, buffer, size);
+}
+
+static int pram_xattr_user_set(struct dentry *dentry, const char *name,
+		const void *value, size_t size, int flags, int type)
+{
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+
+	return pram_xattr_set(dentry->d_inode, PRAM_XATTR_INDEX_USER,
+			      name, value, size, flags);
+}
+
+const struct xattr_handler pram_xattr_user_handler = {
+	.prefix	= XATTR_USER_PREFIX,
+	.list	= pram_xattr_user_list,
+	.get	= pram_xattr_user_get,
+	.set	= pram_xattr_user_set,
+};
 

^ permalink raw reply

* [PATCH 13/16] pramfs: xattr block descriptors tree
From: Marco Stornelli @ 2010-10-10 16:35 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Extended attributes block descriptors tree.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/desctree.c linux-2.6.36/fs/pramfs/desctree.c
--- linux-2.6.36-orig/fs/pramfs/desctree.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/desctree.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,184 @@
+/*
+ * FILE NAME fs/pramfs/desctree.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Extended attributes block descriptors tree.
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/spinlock.h>
+#include "desctree.h"
+#include "pram.h"
+
+/* xblock_desc_init_always()
+ *
+ * These are initializations that need to be done on every
+ * descriptor allocation as the fields are not initialised
+ * by slab allocation.
+ */
+void xblock_desc_init_always(struct pram_xblock_desc *desc)
+{
+	atomic_set(&desc->refcount, 0);
+	desc->blocknr = 0;
+	desc->flags = 0;
+}
+
+/* xblock_desc_init_once()
+ *
+ * These are initializations that only need to be done
+ * once, because the fields are idempotent across use
+ * of the descriptor, so let the slab aware of that.
+ */
+void xblock_desc_init_once(struct pram_xblock_desc *desc)
+{
+	mutex_init(&desc->lock);
+}
+
+/* __insert_xblock_desc()
+ *
+ * Insert a new descriptor in the tree.
+ *
+ */
+static void __insert_xblock_desc(struct pram_sb_info *sbi,
+				 unsigned long blocknr, struct rb_node *node)
+{
+	struct rb_node **p = &(sbi->desc_tree.rb_node);
+	struct rb_node *parent = NULL;
+	struct pram_xblock_desc *desc;
+
+	while (*p) {
+		parent = *p;
+		desc = rb_entry(parent, struct pram_xblock_desc, node);
+
+		if (blocknr < desc->blocknr)
+			p = &(*p)->rb_left;
+		else if (blocknr > desc->blocknr)
+			p = &(*p)->rb_right;
+		else
+			/* Oops...an other descriptor for the same block ? */
+			BUG();
+	}
+
+	rb_link_node(node, parent, p);
+	rb_insert_color(node, &sbi->desc_tree);
+}
+
+void insert_xblock_desc(struct pram_sb_info *sbi, struct pram_xblock_desc *desc)
+{
+	spin_lock(&sbi->desc_tree_lock);
+	__insert_xblock_desc(sbi, desc->blocknr, &desc->node);
+	spin_unlock(&sbi->desc_tree_lock);
+};
+
+/* __lookup_xblock_desc()
+ *
+ * Search an extended attribute descriptor in the tree via the
+ * block number. It returns the descriptor if it's found or
+ * NULL. If not found it creates a new descriptor if create is not 0.
+ */
+static struct pram_xblock_desc *__lookup_xblock_desc(struct pram_sb_info *sbi,
+					    unsigned long blocknr,
+					    struct kmem_cache *cache,
+					    int create)
+{
+	struct rb_node *n = sbi->desc_tree.rb_node;
+	struct pram_xblock_desc *desc = NULL;
+
+	while (n) {
+		desc = rb_entry(n, struct pram_xblock_desc, node);
+
+		if (blocknr < desc->blocknr)
+			n = n->rb_left;
+		else if (blocknr > desc->blocknr)
+			n = n->rb_right;
+		else {
+			atomic_inc(&desc->refcount);
+			goto out;
+		}
+	}
+
+	/* not found */
+	if (create) {
+		desc = kmem_cache_alloc(cache, GFP_NOFS);
+		if (!desc)
+			return ERR_PTR(-ENOMEM);
+		xblock_desc_init_always(desc);
+		atomic_set(&desc->refcount, 1);
+		desc->blocknr = blocknr;
+		__insert_xblock_desc(sbi, desc->blocknr, &desc->node);
+	}
+out:
+	return desc;
+}
+
+struct pram_xblock_desc *lookup_xblock_desc(struct pram_sb_info *sbi,
+					    unsigned long blocknr,
+					    struct kmem_cache *cache,
+					    int create)
+{
+	struct pram_xblock_desc *desc = NULL;
+
+	spin_lock(&sbi->desc_tree_lock);
+	desc = __lookup_xblock_desc(sbi, blocknr, cache, create);
+	spin_unlock(&sbi->desc_tree_lock);
+	return desc;
+}
+
+/* put_xblock_desc()
+ *
+ * Decrement the reference count and if it reaches zero and the
+ * desciptor has been marked to be free, then we free it.
+ * It returns 0 if the descriptor has been deleted and 1 otherwise.
+ */
+int put_xblock_desc(struct pram_sb_info *sbi, struct pram_xblock_desc *desc)
+{
+	int ret = 1;
+	if (!desc)
+		return ret;
+
+	if (atomic_dec_and_lock(&desc->refcount, &sbi->desc_tree_lock)) {
+		if (test_bit(FREEING, &desc->flags)) {
+			rb_erase(&desc->node, &sbi->desc_tree);
+			pram_dbg("erasing desc for block %lu\n", desc->blocknr);
+			ret = 0;
+		}
+		spin_unlock(&sbi->desc_tree_lock);
+	}
+	return ret;
+};
+
+/* mark_free_desc()
+ *
+ * Mark free a descriptor. The descriptor will be deleted later in the
+ * put_xblock_desc().
+ */
+void mark_free_desc(struct pram_xblock_desc *desc)
+{
+	set_bit(FREEING, &desc->flags);
+}
+
+/* erase_tree()
+ *
+ * Free all objects in the tree.
+ */
+void erase_tree(struct pram_sb_info *sbi, struct kmem_cache *cachep)
+{
+	struct rb_node *n;
+	struct pram_xblock_desc *desc;
+
+	spin_lock(&sbi->desc_tree_lock);
+	n = rb_first(&sbi->desc_tree);
+	while (n) {
+		desc = rb_entry(n, struct pram_xblock_desc, node);
+		rb_erase(n, &sbi->desc_tree);
+		kmem_cache_free(cachep, desc);
+		n = rb_next(n);
+	}
+	spin_unlock(&sbi->desc_tree_lock);
+}
diff -Nurp linux-2.6.36-orig/fs/pramfs/desctree.h linux-2.6.36/fs/pramfs/desctree.h
--- linux-2.6.36-orig/fs/pramfs/desctree.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/desctree.h	2010-09-18 08:41:02.000000000 +0200
@@ -0,0 +1,46 @@
+/*
+ * FILE NAME fs/pramfs/desctree.h
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Extended attributes block descriptors tree.
+ *
+ * Copyright 2010 Marco Stornelli <marco.stornelli@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <asm/atomic.h>
+#include <linux/slab.h>
+#include "pram.h"
+
+struct pram_xblock_desc {
+#define FREEING (1UL << 1)
+	unsigned long flags;	/* descriptor flags */
+	atomic_t refcount;	/* users count of this descriptor */
+	unsigned long blocknr;	/* absolute block number */
+	struct mutex lock;	/* block lock */
+	struct rb_node node;	/* node in the rb tree */
+};
+
+extern struct pram_xblock_desc *lookup_xblock_desc(struct pram_sb_info *sbi,
+						   unsigned long blocknr,
+						   struct kmem_cache *, int);
+extern void insert_xblock_desc(struct pram_sb_info *sbi,
+			       struct pram_xblock_desc *desc);
+extern void mark_free_desc(struct pram_xblock_desc *desc);
+extern int put_xblock_desc(struct pram_sb_info *sbi,
+			   struct pram_xblock_desc *desc);
+extern void xblock_desc_init_always(struct pram_xblock_desc *desc);
+extern void xblock_desc_init_once(struct pram_xblock_desc *desc);
+extern void erase_tree(struct pram_sb_info *sbi,
+		       struct kmem_cache *);
+
+static inline void xblock_desc_init(struct pram_xblock_desc *desc)
+{
+	xblock_desc_init_always(desc);
+	xblock_desc_init_once(desc);
+};
+
 

^ permalink raw reply

* [PATCH 14(16] pramfs: memory protection
From: Marco Stornelli @ 2010-10-10 16:36 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird, linux-mm

From: Marco Stornelli <marco.stornelli@gmail.com>

Memory write protection.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/wprotect.c linux-2.6.36/fs/pramfs/wprotect.c
--- linux-2.6.36-orig/fs/pramfs/wprotect.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/wprotect.c	2010-09-26 18:04:07.000000000 +0200
@@ -0,0 +1,31 @@
+/*
+ * FILE NAME fs/pramfs/wprotect.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Write protection for the filesystem pages.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include "pram.h"
+
+void pram_writeable(void *vaddr, unsigned long size, int rw)
+{
+	int ret = 0;
+
+	ret = rw ? write_on_kernel_pte_range((unsigned long)vaddr, size) :
+		    write_off_kernel_pte_range((unsigned long)vaddr, size);
+
+	BUG_ON(ret);
+}
diff -Nurp linux-2.6.36-orig/include/linux/mm.h linux-2.6.36/include/linux/mm.h
--- linux-2.6.36-orig/include/linux/mm.h	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/include/linux/mm.h	2010-09-14 18:49:52.000000000 +0200
@@ -811,6 +811,11 @@ int follow_phys(struct vm_area_struct *v
 		unsigned int flags, unsigned long *prot, resource_size_t *phys);
 int generic_access_phys(struct vm_area_struct *vma, unsigned long addr,
 			void *buf, int len, int write);
+int writeable_kernel_pte_range(unsigned long address, unsigned long size,
+				unsigned int rw);
+
+#define write_on_kernel_pte_range(addr, size) writeable_kernel_pte_range(addr, size, 1)
+#define write_off_kernel_pte_range(addr, size) writeable_kernel_pte_range(addr, size, 0)
  static inline void unmap_shared_mapping_range(struct address_space *mapping,
 		loff_t const holebegin, loff_t const holelen)
diff -Nurp linux-2.6.36-orig/mm/memory.c linux-2.6.36/mm/memory.c
--- linux-2.6.36-orig/mm/memory.c	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/mm/memory.c	2010-09-14 18:49:52.000000000 +0200
@@ -3587,3 +3587,49 @@ void might_fault(void)
 }
 EXPORT_SYMBOL(might_fault);
 #endif
+
+int writeable_kernel_pte_range(unsigned long address, unsigned long size,
+							      unsigned int rw)
+{
+
+	unsigned long addr = address & PAGE_MASK;
+	unsigned long end = address + size;
+	unsigned long start = addr;
+	int ret = -EINVAL;
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	pte_t *ptep, pte;
+
+	spin_lock_irq(&init_mm.page_table_lock);
+
+	do {
+		pgd = pgd_offset(&init_mm, address);
+		if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
+			goto out;
+
+		pud = pud_offset(pgd, address);
+		if (pud_none(*pud) || unlikely(pud_bad(*pud)))
+			goto out;
+
+		pmd = pmd_offset(pud, address);
+		if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
+			goto out;
+
+		ptep = pte_offset_kernel(pmd, addr);
+		pte = *ptep;
+		if (pte_present(pte)) {
+			  pte = rw ? pte_mkwrite(pte) : pte_wrprotect(pte);
+			  *ptep = pte;
+		}
+		addr += PAGE_SIZE;
+	} while (addr && (addr < end));
+
+	ret = 0;
+
+out:
+	flush_tlb_kernel_range(start, end);
+	spin_unlock_irq(&init_mm.page_table_lock);
+	return ret;
+}
+EXPORT_SYMBOL(writeable_kernel_pte_range);
 

^ permalink raw reply

* [PATCH 15/16] pramfs: test module
From: Marco Stornelli @ 2010-10-10 16:37 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Test module.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/pramfs/pramfs_test.c linux-2.6.36/fs/pramfs/pramfs_test.c
--- linux-2.6.36-orig/fs/pramfs/pramfs_test.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/pramfs_test.c	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,49 @@
+/*
+ * FILE NAME fs/pramfs/namei.c
+ *
+ * BRIEF DESCRIPTION
+ *
+ * Pramfs test module.
+ *
+ * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
+ * Copyright 2003 Sony Corporation
+ * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
+ * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include "pram.h"
+
+int __init test_pramfs_write(void)
+{
+	struct pram_super_block *psb;
+
+	psb = get_pram_super();
+	if (!psb) {
+		printk(KERN_ERR
+		"%s: PRAMFS super block not found (not mounted?)\n",
+		__func__);
+		return 1;
+	}
+
+	/*
+	 * Attempt an unprotected clear of checksum information in the
+	 * superblock, this should cause a kernel page protection fault.
+	 */
+	printk("%s: writing to kernel VA %p\n", __func__, psb);
+	psb->s_sum = 0;
+
+	return 0;
+}
+
+void test_pramfs_write_cleanup(void) {}
+
+/* Module information */
+MODULE_LICENSE("GPL");
+module_init(test_pramfs_write);
+module_exit(test_pramfs_write_cleanup);
 

^ permalink raw reply

* [PATCH 16/16] pramfs Makefile and Kconfig
From: Marco Stornelli @ 2010-10-10 16:39 UTC (permalink / raw)
  To: Linux Kernel; +Cc: Linux Embedded, Linux FS Devel, Tim Bird

From: Marco Stornelli <marco.stornelli@gmail.com>

Makefile and Kconfig.

Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
---
diff -Nurp linux-2.6.36-orig/fs/Makefile linux-2.6.36/fs/Makefile
--- linux-2.6.36-orig/fs/Makefile	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/fs/Makefile	2010-09-14 18:49:52.000000000 +0200
@@ -126,3 +126,4 @@ obj-$(CONFIG_BTRFS_FS)		+= btrfs/
 obj-$(CONFIG_GFS2_FS)           += gfs2/
 obj-$(CONFIG_EXOFS_FS)          += exofs/
 obj-$(CONFIG_CEPH_FS)		+= ceph/
+obj-$(CONFIG_PRAMFS)		+= pramfs/
diff -Nurp linux-2.6.36-orig/fs/Kconfig linux-2.6.36/fs/Kconfig
--- linux-2.6.36-orig/fs/Kconfig	2010-09-13 01:07:37.000000000 +0200
+++ linux-2.6.36/fs/Kconfig	2010-09-14 18:49:52.000000000 +0200
@@ -13,7 +13,7 @@ source "fs/ext4/Kconfig"
 config FS_XIP
 # execute in place
 	bool
-	depends on EXT2_FS_XIP
+	depends on EXT2_FS_XIP || PRAMFS_XIP
 	default y
  source "fs/jbd/Kconfig"
@@ -25,13 +25,14 @@ config FS_MBCACHE
 	default y if EXT2_FS=y && EXT2_FS_XATTR
 	default y if EXT3_FS=y && EXT3_FS_XATTR
 	default y if EXT4_FS=y && EXT4_FS_XATTR
-	default m if EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS_XATTR
+	default y if PRAMFS=y && PRAMFS_XATTR
+	default m if EXT2_FS_XATTR || EXT3_FS_XATTR || EXT4_FS_XATTR || PRAMFS_XATTR
  source "fs/reiserfs/Kconfig"
 source "fs/jfs/Kconfig"
  config FS_POSIX_ACL
-# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs/nfs4)
+# Posix ACL utility routines (for now, only ext2/ext3/jfs/reiserfs/nfs4/pramfs)
 #
 # NOTE: you can implement Posix ACLs without these helpers (XFS does).
 # 	Never use this symbol for ifdefs.
@@ -189,6 +190,7 @@ source "fs/romfs/Kconfig"
 source "fs/sysv/Kconfig"
 source "fs/ufs/Kconfig"
 source "fs/exofs/Kconfig"
+source "fs/pramfs/Kconfig"
  endif # MISC_FILESYSTEMS
 diff -Nurp linux-2.6.36-orig/fs/pramfs/Kconfig linux-2.6.36/fs/pramfs/Kconfig
--- linux-2.6.36-orig/fs/pramfs/Kconfig	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/Kconfig	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,72 @@
+config PRAMFS
+	tristate "Persistent and Protected RAM file system support"
+	depends on HAS_IOMEM && EXPERIMENTAL
+	select CRC16
+	help
+	   If your system has a block of fast (comparable in access speed to
+	   system memory) and non-volatile RAM and you wish to mount a
+	   light-weight, full-featured, and space-efficient filesystem over it,
+	   say Y here, and read <file:Documentation/filesystems/pramfs.txt>.
+
+	   To compile this as a module,  choose M here: the module will be
+	   called pramfs.ko.
+
+config PRAMFS_XIP
+	bool "Enable Execute-in-place in PRAMFS"
+	depends on PRAMFS && !PRAMFS_WRITE_PROTECT
+	help
+	   Say Y here to enable xip feature of PRAMFS.
+
+config PRAMFS_WRITE_PROTECT
+	bool "Enable PRAMFS write protection"
+	depends on PRAMFS && MMU
+	default y
+	help
+	   Say Y here to enable the write protect feature of PRAMFS.
+
+config PRAMFS_XATTR
+	bool "PRAMFS extended attributes"
+	depends on PRAMFS
+	help
+	  Extended attributes are name:value pairs associated with inodes by
+	  the kernel or by users (see the attr(5) manual page, or visit
+	  <http://acl.bestbits.at/> for details).
+
+	  If unsure, say N.
+
+config PRAMFS_POSIX_ACL
+	bool "PRAMFS POSIX Access Control Lists"
+	depends on PRAMFS_XATTR
+	select FS_POSIX_ACL
+	help
+	  Posix Access Control Lists (ACLs) support permissions for users and
+	  groups beyond the owner/group/world scheme.
+
+	  To learn more about Access Control Lists, visit the Posix ACLs for
+	  Linux website <http://acl.bestbits.at/>.
+
+	  If you don't know what Access Control Lists are, say N
+
+config PRAMFS_SECURITY
+	bool "PRAMFS Security Labels"
+	depends on PRAMFS_XATTR
+	help
+	  Security labels support alternative access control models
+	  implemented by security modules like SELinux.  This option
+	  enables an extended attribute handler for file security
+	  labels in the pram filesystem.
+
+	  If you are not using a security module that requires using
+	  extended attributes for file security labels, say N.
+
+config PRAMFS_TEST
+	boolean
+	depends on PRAMFS
+
+config TEST_MODULE
+	tristate "PRAMFS Test"
+	depends on PRAMFS && m
+	select PRAMFS_TEST
+	help
+	  Say Y here to build a simple module to test the protection of
+	  PRAMFS. The module will be called pramfs_test.ko.
diff -Nurp linux-2.6.36-orig/fs/pramfs/Makefile linux-2.6.36/fs/pramfs/Makefile
--- linux-2.6.36-orig/fs/pramfs/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.36/fs/pramfs/Makefile	2010-09-14 18:49:52.000000000 +0200
@@ -0,0 +1,14 @@
+#
+# Makefile for the linux pram-filesystem routines.
+#
+
+obj-$(CONFIG_PRAMFS) += pramfs.o
+obj-$(CONFIG_TEST_MODULE) += pramfs_test.o
+
+pramfs-y := balloc.o dir.o file.o inode.o namei.o super.o symlink.o
+
+pramfs-$(CONFIG_PRAMFS_WRITE_PROTECT) += wprotect.o
+pramfs-$(CONFIG_PRAMFS_XIP) += xip.o
+pramfs-$(CONFIG_PRAMFS_XATTR) += xattr.o xattr_user.o xattr_trusted.o desctree.o
+pramfs-$(CONFIG_PRAMFS_POSIX_ACL) += acl.o
+pramfs-$(CONFIG_PRAMFS_SECURITY) += xattr_security.o
 

^ permalink raw reply

* Re: [PATCH 14(16] pramfs: memory protection
From: Andi Kleen @ 2010-10-10 16:46 UTC (permalink / raw)
  To: Marco Stornelli
  Cc: Linux Kernel, Linux Embedded, Linux FS Devel, Tim Bird, linux-mm
In-Reply-To: <4CB1EBA2.8090409@gmail.com>

Marco Stornelli <marco.stornelli@gmail.com> writes:
> +
> +	do {
> +		pgd = pgd_offset(&init_mm, address);
> +		if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
> +			goto out;
> +
> +		pud = pud_offset(pgd, address);
> +		if (pud_none(*pud) || unlikely(pud_bad(*pud)))
> +			goto out;
> +
> +		pmd = pmd_offset(pud, address);
> +		if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
> +			goto out;
> +
> +		ptep = pte_offset_kernel(pmd, addr);
> +		pte = *ptep;
> +		if (pte_present(pte)) {

This won't work at all on x86 because you don't handle large 
pages.

And it doesn't work on x86-64 because the first 2GB are double
mapped (direct and kernel text mapping)

Thirdly I expect it won't either on architectures that map
the direct mapping with special registers (like IA64 or MIPS)

I'm not sure this is very useful anyways. It doesn't protect
against stray DMA and it doesn't protect against writes through
broken user PTEs.

-Andi

-- 
ak@linux.intel.com -- Speaking for myself only.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>


^ permalink raw reply

* Re: [PATCH 15/16] pramfs: test module
From: Randy Dunlap @ 2010-10-10 16:57 UTC (permalink / raw)
  To: Marco Stornelli; +Cc: Linux Kernel, Linux Embedded, Linux FS Devel, Tim Bird
In-Reply-To: <4CB1EBDD.8050509@gmail.com>

On Sun, 10 Oct 2010 18:37:49 +0200 Marco Stornelli wrote:

> From: Marco Stornelli <marco.stornelli@gmail.com>
> 
> Test module.
> 
> Signed-off-by: Marco Stornelli <marco.stornelli@gmail.com>
> ---
> diff -Nurp linux-2.6.36-orig/fs/pramfs/pramfs_test.c linux-2.6.36/fs/pramfs/pramfs_test.c
> --- linux-2.6.36-orig/fs/pramfs/pramfs_test.c	1970-01-01 01:00:00.000000000 +0100
> +++ linux-2.6.36/fs/pramfs/pramfs_test.c	2010-09-14 18:49:52.000000000 +0200
> @@ -0,0 +1,49 @@
> +/*
> + * FILE NAME fs/pramfs/namei.c
> + *
> + * BRIEF DESCRIPTION
> + *
> + * Pramfs test module.
> + *
> + * Copyright 2009-2010 Marco Stornelli <marco.stornelli@gmail.com>
> + * Copyright 2003 Sony Corporation
> + * Copyright 2003 Matsushita Electric Industrial Co., Ltd.
> + * 2003-2004 (c) MontaVista Software, Inc. , Steve Longerbeam
> + * This file is licensed under the terms of the GNU General Public
> + * License version 2. This program is licensed "as is" without any
> + * warranty of any kind, whether express or implied.
> + */
> +#include <linux/module.h>
> +#include <linux/version.h>
> +#include <linux/init.h>
> +#include <linux/fs.h>
> +#include "pram.h"
> +
> +int __init test_pramfs_write(void)
> +{
> +	struct pram_super_block *psb;
> +
> +	psb = get_pram_super();
> +	if (!psb) {
> +		printk(KERN_ERR
> +		"%s: PRAMFS super block not found (not mounted?)\n",
> +		__func__);

Above 2 lines need to indented more.

> +		return 1;
> +	}
> +
> +	/*
> +	 * Attempt an unprotected clear of checksum information in the
> +	 * superblock, this should cause a kernel page protection fault.
> +	 */
> +	printk("%s: writing to kernel VA %p\n", __func__, psb);
> +	psb->s_sum = 0;
> +
> +	return 0;
> +}
> +
> +void test_pramfs_write_cleanup(void) {}
> +
> +/* Module information */
> +MODULE_LICENSE("GPL");
> +module_init(test_pramfs_write);
> +module_exit(test_pramfs_write_cleanup);


---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox