* Re: [PATCHv4] UBI: new module ubiblk: block layer on top of UBI
From: Artem Bityutskiy @ 2011-09-06 4:55 UTC (permalink / raw)
To: david.wagner; +Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse
In-Reply-To: <1314202540-5562-1-git-send-email-david.wagner@free-electrons.com>
On Wed, 2011-08-24 at 18:15 +0200, david.wagner@free-electrons.com
wrote:
> g checkpatch'd (it still complains about an assignment in an if ()), is it
> really bad ?
Well, UBI does not do this, so for the sake of consistent style you
could fix those warnings.
> about e), I have a question: it was possible to unmount a mounted filesystem
> because ot this. The fix we found was to return -EBUSY if the ubiblk device is
> opened. But is it correct to assume that a mounted filesystem will always hold
> the device opened ? If not, what is the correct way to impeach the removal of a
> ubiblk device when it is "mounted" ?
Once the block device is referenced (refcound is not zero), you should
open the underlying UBI volume (in write mode). And you close it only
when the block device's reference count becomes zero, just like gluebi
does this. So you need to enhance your ubiblk_open() and release
functions.
This should make sure that no one can remove the underlying UBI volume
when the corresponding ubiblk device is being used (e.g., mounted).
> (see ubiblk_remove(), if (dev->desc))
>
> other questions:
> is it ok not to return the error value from the notify function ? I found now
> way to return an error value w/o stopping the notifications from being emitted.
>
> there are error messages printed when a ubiblk_remove() is attempted on a
> non-existing device - that happens a lot when a UBI device is detached ; should
> the messages be debug-only (or even removed) ?
Not sure I got the question, is this an error situation? I see the
following possibilities:
1. UBI volume ubiX_Y is not used by ubiblk at all -> no issues
2. UBI volume ubiX_Y is has corresponding device ubiblkA, which is
unused, so UBI volume can be removed, which causes a notifier,
which removes ubiblkA.
3. ubiX_Y has corresponding ubiblkA, which is used, which means ubiX_Y
is still open, which means it cannot be removed, no issues.
> +/**
> + * ubi_ubiblk_init - initialize the module
> + *
> + * Get a major number and register to UBI notifications ; register the ioctl
> + * handler device
> + */
> +static int __init ubi_ubiblk_init(void)
> +{
> + int ret = 0;
> +
> + ret = register_blkdev(0, "ubiblk");
> + if (ret <= 0) {
> + pr_err("can't register the major\n");
Not very user-friendly message - I do not think it is very readable and
easy to understand. Saying "cannot register block device" would be
readable. But I'd better just not print anything. And the function
internally has some error-path prints.
> + return -ENODEV;
Why do you override the error code returned by 'register_blkdev()'?
> + }
> + ubiblk_major = ret;
> +
> + mutex_init(&devlist_lock);
Isn't it cleaner to initialize the global mutex when declaring it -
there is a macros to do this?
> +
> + ret = misc_register(&ctrl_dev);
> + if (ret) {
> + pr_err("can't register control device\n");
> + goto out_unreg_blk;
> + }
> +
> + ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
> + if (ret < 0)
> + goto out_unreg_ctrl;
> +
> + pr_info("major=%d\n", ubiblk_major);
Please, print something nicer, e.g.,
"control device major number is %d"
> +
> + return ret;
> +
> +out_unreg_ctrl:
> + misc_deregister(&ctrl_dev);
> +out_unreg_blk:
> + unregister_blkdev(ubiblk_major, "ubiblk");
> +
> + return ret;
> +}
--
Best Regards,
Artem Bityutskiy
^ permalink raw reply
* Re: [PATCH] document ubiblk's usage of the same ioctl magic as a part of UBI
From: Artem Bityutskiy @ 2011-09-06 4:58 UTC (permalink / raw)
To: David Wagner; +Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse
In-Reply-To: <1314202916-6982-1-git-send-email-david.wagner@free-electrons.com>
On Wed, 2011-08-24 at 18:21 +0200, David Wagner wrote:
> ---
> include/mtd/ubi-user.h | 1 +
> 1 files changed, 1 insertions(+), 0 deletions(-)
>
> diff --git a/include/mtd/ubi-user.h b/include/mtd/ubi-user.h
> index 3c41097..7c8bfe3 100644
> --- a/include/mtd/ubi-user.h
> +++ b/include/mtd/ubi-user.h
> @@ -188,6 +188,7 @@
> /* Set an UBI volume property */
> #define UBI_IOCSETVOLPROP _IOW(UBI_VOL_IOC_MAGIC, 6, \
> struct ubi_set_vol_prop_req)
> +/* If you add ioctls here, please note that ubiblk uses 'O'/0x10-0x11 */
>
> /* Maximum MTD device name length supported by UBI */
> #define MAX_UBI_MTD_NAME_LEN 127
Let's postpone this patch and the user-space tool until ubiblk is ready
for inclusion. I sent you some comments - the main one is the block
device reference counting and making sure that the underlying UBI volume
does not disappear when you use it.
--
Best Regards,
Artem Bityutskiy
^ permalink raw reply
* [PATCH] net: phy: Add config option to specify external switch port to be used if switch is used as PHY
From: Lambrecht Jürgen @ 2011-09-08 6:54 UTC (permalink / raw)
To: netdev@vger.kernel.org; +Cc: linux-embedded@vger.kernel.org
Hello,
In our embedded designs, this is a useful patch. Maybe it can be useful
for somebody else too.
Or maybe there are already better solutions?
I know I could also write a driver for our switch, but that is too much
effort just to select the active port.
Kind regards,
Jürgen
In embedded design, instead of a PHY, sometimes a switch is used that
behaves as a PHY through its MII port. For example to use a daisy
chain network configuration instead of an expensive star config.
In that case, many phy ports are available, but only 1 should
be used
to check link status, and not the first one available as is
the case
without this configuration (that is, set to its default value 0).
So this options specifies the switch port number to be used
to check
link status, because if the link is down, no data is sent by the
TCP/IP stack.
Signed-off-by: Jürgen Lambrecht <J.Lambrecht@televic.com>
---
drivers/net/phy/Kconfig | 14 ++++++++++++++
drivers/net/phy/mdio_bus.c | 9 +++++++++
2 files changed, 23 insertions(+), 0 deletions(-)
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index a702443..554561f 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -13,6 +13,20 @@ menuconfig PHYLIB
if PHYLIB
+config SWITCH_PHY
+ int "External switch port to be used if switch is used as PHY"
+ default "0"
+ help
+ In embedded design, instead of a PHY, sometimes a switch is
used that
+ behaves as a PHY through its MII port. For example to use a daisy
+ chain network configuration instead of an expensive star config.
+ In that case, many phy ports are available, but only 1 should
be used
+ to check link status, and not the first one available as is
the case
+ without this configuration (that is, set to its default value 0).
+ So this options specifies the switch port number to be used to
check
+ link status, because if the link is down, no data is sent by the
+ TCP/IP stack.
+
comment "MII PHY device drivers"
config MARVELL_PHY
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 6c58da2..016437a 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -112,7 +112,14 @@ int mdiobus_register(struct mii_bus *bus)
if (bus->reset)
bus->reset(bus);
+ /* The config below is always availble with CONFIG_PHYLIB. If 0, the
+ behavior is as before without this patch (or P0 of the switch is
+ taken because it is the first one found). */
+#if CONFIG_SWITCH_PHY
+ i = CONFIG_SWITCH_PHY;
+#else
for (i = 0; i < PHY_MAX_ADDR; i++) {
+#endif
if ((bus->phy_mask & (1 << i)) == 0) {
struct phy_device *phydev;
@@ -122,7 +129,9 @@ int mdiobus_register(struct mii_bus *bus)
goto error;
}
}
+#if !CONFIG_SWITCH_PHY
}
+#endif
bus->state = MDIOBUS_REGISTERED;
pr_info("%s: probed\n", bus->name);
--
1.7.1
^ permalink raw reply related
* Re: [PATCH] net: phy: Add config option to specify external switch port to be used if switch is used as PHY
From: Florian Fainelli @ 2011-09-08 8:39 UTC (permalink / raw)
To: Lambrecht Jürgen
Cc: netdev@vger.kernel.org, linux-embedded@vger.kernel.org
In-Reply-To: <4E68668F.9060008@televic.com>
Hello Jurgen,
On Thursday 08 September 2011 08:54:07 Lambrecht Jürgen wrote:
> Hello,
>
> In our embedded designs, this is a useful patch. Maybe it can be useful
> for somebody else too.
> Or maybe there are already better solutions?
> I know I could also write a driver for our switch, but that is too much
> effort just to select the active port.
This is not going to work well with all switches out there. You could use the
fixed-PHY driver to make your ethernet driver see the link as always up between
the MAC and switch CPU port.
A better solution would be to have proper switch drivers and user-space, which
reminds me that we (OpenWrt) should at some point propose our switch drivers
[1] for review.
[1]:
https://dev.openwrt.org/browser/trunk/target/linux/generic/files/drivers/net/phy/
>
> Kind regards,
> Jürgen
>
> In embedded design, instead of a PHY, sometimes a switch is used that
> behaves as a PHY through its MII port. For example to use a
> daisy chain network configuration instead of an expensive star config. In
> that case, many phy ports are available, but only 1 should be used
> to check link status, and not the first one available as is
> the case
> without this configuration (that is, set to its default value
> 0). So this options specifies the switch port number to be used to check
> link status, because if the link is down, no data is sent by the
> TCP/IP stack.
>
> Signed-off-by: Jürgen Lambrecht <J.Lambrecht@televic.com>
> ---
> drivers/net/phy/Kconfig | 14 ++++++++++++++
> drivers/net/phy/mdio_bus.c | 9 +++++++++
> 2 files changed, 23 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
> index a702443..554561f 100644
> --- a/drivers/net/phy/Kconfig
> +++ b/drivers/net/phy/Kconfig
> @@ -13,6 +13,20 @@ menuconfig PHYLIB
>
> if PHYLIB
>
> +config SWITCH_PHY
> + int "External switch port to be used if switch is used as PHY"
> + default "0"
> + help
> + In embedded design, instead of a PHY, sometimes a switch is
> used that
> + behaves as a PHY through its MII port. For example to use a daisy
> + chain network configuration instead of an expensive star config.
> + In that case, many phy ports are available, but only 1 should
> be used
> + to check link status, and not the first one available as is
> the case
> + without this configuration (that is, set to its default value 0).
> + So this options specifies the switch port number to be used to
> check
> + link status, because if the link is down, no data is sent by the
> + TCP/IP stack.
> +
> comment "MII PHY device drivers"
>
> config MARVELL_PHY
> diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
> index 6c58da2..016437a 100644
> --- a/drivers/net/phy/mdio_bus.c
> +++ b/drivers/net/phy/mdio_bus.c
> @@ -112,7 +112,14 @@ int mdiobus_register(struct mii_bus *bus)
> if (bus->reset)
> bus->reset(bus);
>
> + /* The config below is always availble with CONFIG_PHYLIB. If 0,
> the + behavior is as before without this patch (or P0 of the
> switch is + taken because it is the first one found). */
> +#if CONFIG_SWITCH_PHY
> + i = CONFIG_SWITCH_PHY;
> +#else
> for (i = 0; i < PHY_MAX_ADDR; i++) {
> +#endif
> if ((bus->phy_mask & (1 << i)) == 0) {
> struct phy_device *phydev;
>
> @@ -122,7 +129,9 @@ int mdiobus_register(struct mii_bus *bus)
> goto error;
> }
> }
> +#if !CONFIG_SWITCH_PHY
> }
> +#endif
>
> bus->state = MDIOBUS_REGISTERED;
> pr_info("%s: probed\n", bus->name);
> --
> 1.7.1
> N�����r��y����b�X��ǧv�^�)޺{.n�+���z�^�)����w*\x1fjg���\x1e�����ݢj/���z�ޖ��2�ޙ����
> &�)ߡ�a��\x7f��\x1e�G���h�\x0f�j:+v���w��٥
--
Florian
^ permalink raw reply
* Re: [PATCH] net: phy: Add config option to specify external switch port to be used if switch is used as PHY
From: Lambrecht Jürgen @ 2011-09-08 10:00 UTC (permalink / raw)
To: Florian Fainelli; +Cc: netdev@vger.kernel.org, linux-embedded@vger.kernel.org
In-Reply-To: <201109081039.35272.florian@openwrt.org>
On 09/08/2011 10:39 AM, Florian Fainelli wrote:
>
> Hello Jurgen,
>
> On Thursday 08 September 2011 08:54:07 Lambrecht J
^ permalink raw reply
* Re: [PATCH] net: phy: Add config option to specify external switch port to be used if switch is used as PHY
From: Francois Romieu @ 2011-09-08 10:13 UTC (permalink / raw)
To: Lambrecht Jürgen
Cc: netdev@vger.kernel.org, linux-embedded@vger.kernel.org
In-Reply-To: <4E68668F.9060008@televic.com>
Lambrecht Jürgen <J.Lambrecht@TELEVIC.com> :
[...]
> diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
> index 6c58da2..016437a 100644
> --- a/drivers/net/phy/mdio_bus.c
> +++ b/drivers/net/phy/mdio_bus.c
> @@ -112,7 +112,14 @@ int mdiobus_register(struct mii_bus *bus)
> if (bus->reset)
> bus->reset(bus);
>
> + /* The config below is always availble with CONFIG_PHYLIB. If 0, the
> + behavior is as before without this patch (or P0 of the switch is
> + taken because it is the first one found). */
> +#if CONFIG_SWITCH_PHY
> + i = CONFIG_SWITCH_PHY;
> +#else
> for (i = 0; i < PHY_MAX_ADDR; i++) {
> +#endif
> if ((bus->phy_mask & (1 << i)) == 0) {
> struct phy_device *phydev;
This config option may help your platform but it is nowhere reusable on a
slightly different one (each mii_bus behavior is changed).
Which driver(s) do you use that you can not set phy_mask directly ?
--
Ueimor
^ permalink raw reply
* Re: [PATCH] net: phy: Add config option to specify external switch port to be used if switch is used as PHY
From: Lambrecht Jürgen @ 2011-09-08 11:59 UTC (permalink / raw)
To: Francois Romieu; +Cc: netdev@vger.kernel.org, linux-embedded@vger.kernel.org
In-Reply-To: <20110908101315.GA27909@electric-eye.fr.zoreil.com>
On 09/08/2011 12:13 PM, Francois Romieu wrote:
>
> Lambrecht Jürgen <J.Lambrecht@TELEVIC.com> :
> [...]
> > diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
> > index 6c58da2..016437a 100644
> > --- a/drivers/net/phy/mdio_bus.c
> > +++ b/drivers/net/phy/mdio_bus.c
> > @@ -112,7 +112,14 @@ int mdiobus_register(struct mii_bus *bus)
> > if (bus->reset)
> > bus->reset(bus);
> >
> > + /* The config below is always availble with CONFIG_PHYLIB.
> If 0, the
> > + behavior is as before without this patch (or P0 of the
> switch is
> > + taken because it is the first one found). */
> > +#if CONFIG_SWITCH_PHY
> > + i = CONFIG_SWITCH_PHY;
> > +#else
> > for (i = 0; i < PHY_MAX_ADDR; i++) {
> > +#endif
> > if ((bus->phy_mask & (1 << i)) == 0) {
> > struct phy_device *phydev;
>
> This config option may help your platform but it is nowhere reusable on a
> slightly different one (each mii_bus behavior is changed).
>
> Which driver(s) do you use that you can not set phy_mask directly ?
>
The HW driver is 'FEC' for iMX Ethernet. For the PHY, just MII and PHYLIB.
I am rather new to linux, didn't knew phy_mask. Checked it now, and is
not set in fec.c.
You mean then to patch drivers/net/fec.c in the same way (as my current
patch) to set the phy_mask instead (via menuconfig, or in the platform
init)?
Thanks for your reply,
Jürgen
>
>
> --
> Ueimor
>
--
Jürgen Lambrecht
R&D Associate
Tel: +32 (0)51 303045 Fax: +32 (0)51 310670
http://www.televic-rail.com
Televic Rail NV - Leo Bekaertlaan 1 - 8870 Izegem - Belgium
Company number 0825.539.581 - RPR Kortrijk
^ permalink raw reply
* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
From: Arnd Bergmann @ 2011-09-08 15:26 UTC (permalink / raw)
To: dedekind1
Cc: linux-embedded, david.wagner, lkml, linux-mtd, Tim Bird,
David Woodhouse
In-Reply-To: <1315282208.19067.24.camel@sauron>
On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> Not sure about the bus approach - David, could you take a look at it
> please? If we can handle errors there - then we could indeed re-use the
> UBI control device. We could even re-use the ioctl data structures for
> UBI volumes creation/removal - we have plenty of space there reserved
> for future extensions.
I would generally recommend using new ioctl commands. ioctl numbers
are cheap, but complexity in data structures is not, because every
user who wants to deal with the data structures has to understand
them. Also, changing the ABI is always tricky since you have to
provide backward and forwards compatibility with existing kernels
and with existing user space.
Arnd
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply
* Re: [PATCH] net: phy: Add config option to specify external switch port to be used if switch is used as PHY
From: Francois Romieu @ 2011-09-08 21:24 UTC (permalink / raw)
To: Lambrecht Jürgen
Cc: netdev@vger.kernel.org, linux-embedded@vger.kernel.org
In-Reply-To: <4E68AE26.5020707@televic.com>
Lambrecht Jürgen <J.Lambrecht@TELEVIC.com> :
> On 09/08/2011 12:13 PM, Francois Romieu wrote:
[...]
> > Which driver(s) do you use that you can not set phy_mask directly ?
> >
> The HW driver is 'FEC' for iMX Ethernet. For the PHY, just MII and PHYLIB.
> I am rather new to linux, didn't knew phy_mask. Checked it now, and is
> not set in fec.c.
> You mean then to patch drivers/net/fec.c in the same way (as my current
> patch) to set the phy_mask instead (via menuconfig, or in the platform
> init)?
It is not my area but I would have drivers/net/fec.c::fec_devtype.driver_data
point to a real struct where the relevant phy_mask and the current quirks
are stored, then add a new entry in fec_devtype and a reference to it
(Kconfig + platform init) somewhere below arch/arm/plat-mxc.
Freescale's application note suggests that the MX25 fec allows some freedom
for the implementation of the media interface. So it may not be overkill.
--
Ueimor
^ permalink raw reply
* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
From: Artem Bityutskiy @ 2011-09-09 11:53 UTC (permalink / raw)
To: Arnd Bergmann
Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird,
David Woodhouse
In-Reply-To: <201109081726.00769.arnd@arndb.de>
On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> > Not sure about the bus approach - David, could you take a look at it
> > please? If we can handle errors there - then we could indeed re-use the
> > UBI control device. We could even re-use the ioctl data structures for
> > UBI volumes creation/removal - we have plenty of space there reserved
> > for future extensions.
>
> I would generally recommend using new ioctl commands. ioctl numbers
> are cheap, but complexity in data structures is not, because every
> user who wants to deal with the data structures has to understand
> them. Also, changing the ABI is always tricky since you have to
> provide backward and forwards compatibility with existing kernels
> and with existing user space.
Hmm, what do we do if ubiblk module is not loaded, and UBI would have
to return an error (because the block device cannot be created), how
will UBI know that ubiblk is not there? Any direct call to ubiblk from
UBI would be a direct dependency and would require ubiblk to be always
loaded, which is bad.
IOW, we need a blocking mechanism to call the upper layer's function
(ubiblk) from the lower layer (UBI) which can return an error, and which
allows to check if a ubiblk exists at all. Do we have such mechanism?
Actually the fact of invoking upper layers from lower makes me worry.
And yes, the "bus"<->"devices" model does not look suitable in our case.
We have more like hierarchy: ubiblk uses ubi which usese mtd device.
David, I am really busy and now, I suggest you to think about this. I'd
so far stick to the own ubiblk cdev approach, and would
analyse/prototype the approach of using UBI cdev for this. I provided
some concerns above. Also, think about race conditions like:
1. Someone
--
Best Regards,
Artem Bityutskiy
^ permalink raw reply
* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
From: Artem Bityutskiy @ 2011-09-09 12:02 UTC (permalink / raw)
To: david.wagner
Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
Arnd Bergmann
In-Reply-To: <1315569206.7905.41.camel@sauron>
On Fri, 2011-09-09 at 14:53 +0300, Artem Bityutskiy wrote:
> David, I am really busy and now, I suggest you to think about this. I'd
> so far stick to the own ubiblk cdev approach, and would
> analyse/prototype the approach of using UBI cdev for this. I provided
> some concerns above. Also, think about race conditions like:
>
> 1. Someone
Sorry, I wonted to talk about situations when someone opens an ubiblk
device while the underlying UBI volume is being removed, but then though
this is trivial and forgot to erase the last sentence.
Anyway, I suggest the following algorithm:
1. Stick with the own cdev approach - the driver becomes very simple
in this case - we review it.
2. Once it is ready, or in parallel, you can play with the second
approach and if you find it solid/nice, you can then provide a
new version and/or the delta.
The idea is that on step 1 we review/deal with other things, without
being blocked by the ioctl stuff.
--
Best Regards,
Artem Bityutskiy
^ permalink raw reply
* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
From: Arnd Bergmann @ 2011-09-09 14:25 UTC (permalink / raw)
To: dedekind1
Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird,
David Woodhouse
In-Reply-To: <1315569206.7905.41.camel@sauron>
On Friday 09 September 2011, Artem Bityutskiy wrote:
> On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> > On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> > > Not sure about the bus approach - David, could you take a look at it
> > > please? If we can handle errors there - then we could indeed re-use the
> > > UBI control device. We could even re-use the ioctl data structures for
> > > UBI volumes creation/removal - we have plenty of space there reserved
> > > for future extensions.
> >
> > I would generally recommend using new ioctl commands. ioctl numbers
> > are cheap, but complexity in data structures is not, because every
> > user who wants to deal with the data structures has to understand
> > them. Also, changing the ABI is always tricky since you have to
> > provide backward and forwards compatibility with existing kernels
> > and with existing user space.
>
> Hmm, what do we do if ubiblk module is not loaded, and UBI would have
> to return an error (because the block device cannot be created), how
> will UBI know that ubiblk is not there? Any direct call to ubiblk from
> UBI would be a direct dependency and would require ubiblk to be always
> loaded, which is bad.
No, the idea of this approach is that the main ubi driver creates
a device, which can always succeed. It's just that there won't
be a block device node created, because that is part of what
the ubiblk driver does.
Compare this to how scsi works:
A scsi host driver scans the host controller and adds scsi devices
internal to the kernel, each of them have a specific type (disk,
tape, ...). If the scsi disk driver is loaded, it will create
a blockdev for each disk device. It doesn't matter in which order
the drivers are loaded though.
In case of ubiblk, it's similar, except that there is no way for
the ubi layer to know if some partition should be a block device or
not, so it relies on user space to tell it.
Well, actually, you /could/ encode this somewhere so that the main
ubi layer creates different kinds of devices based on what it finds:
a ubiblk_device when it finds a partition that was created as a
block device or gluebi_device for gluebi or a ubifs volume.
> IOW, we need a blocking mechanism to call the upper layer's function
> (ubiblk) from the lower layer (UBI) which can return an error, and which
> allows to check if a ubiblk exists at all. Do we have such mechanism?
>
> Actually the fact of invoking upper layers from lower makes me worry.
Yes, you should not do that.
Arnd
^ permalink raw reply
* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
From: David Wagner @ 2011-09-09 14:41 UTC (permalink / raw)
To: dedekind1
Cc: Arnd Bergmann, linux-embedded, lkml, linux-mtd, Tim Bird,
David Woodhouse
In-Reply-To: <1315569206.7905.41.camel@sauron>
On 09/09/2011 01:53 PM, Artem Bityutskiy wrote:
> On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
>> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
>>> Not sure about the bus approach - David, could you take a look at it
>>> please? If we can handle errors there - then we could indeed re-use the
>>> UBI control device. We could even re-use the ioctl data structures for
>>> UBI volumes creation/removal - we have plenty of space there reserved
>>> for future extensions.
>>
@Arnd:
> * Use the existing UBI control device for the block devices as
> well and just add two more ioctls to create the devices.
> You can add a logical bus_type for this so that the ubi block
> driver gets automatically loaded matched with the device when
> one is created using the control device.
I certainly miss some background, I'm not sure I understand how this
works: bus_type seems suitable for pluggable devices that possess a
device ID which matches against a driver that will then get loaded. But
ubiblk devices are created by ubiblk.
So, are you suggesting to move ubiblk_create() to UBI and add a
MODULE_ALIAS to ubiblk (actually, I don't know what it would contain) ?
(I just saw that you sent an email while I was writing this one ;
however, I still understand. I'll try and read how scsi does that).
@Artem:
> Sorry, I wonted to talk about situations when someone opens an ubiblk
> device while the underlying UBI volume is being removed, but then though
> this is trivial and forgot to erase the last sentence.
Ah, yes, I guess we need to hold a vol_lock in ubiblk_remove() ?
> Anyway, I suggest the following algorithm:
>
> 1. Stick with the own cdev approach - the driver becomes very simple
> in this case - we review it.
This is the way it's implemented in v4, right ?
BTW, those are the changes made so far since v4:
* Add missing headers (they are included by other headers but it seems
to be good practice not to rely on that).
* Remove an macro rendered useless with the linked lists
* correct some formatting in kerneldoc comments
* introduce refcounting to avoid multiple opens or closing a UBI
volume while still in use
* make checkpatch happy about assignation inside a condition
* use DEFINE_MUTEX for devlist_lock
Best Regards,
David
--
David Wagner, Free Electrons
Kernel, drivers, real-time and embedded Linux
development, consulting, training and support.
http://free-electrons.com
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply
* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
From: Arnd Bergmann @ 2011-09-09 14:51 UTC (permalink / raw)
To: David Wagner
Cc: dedekind1, linux-mtd, linux-embedded, lkml, Tim Bird,
David Woodhouse
In-Reply-To: <4E6A25B1.8070400@free-electrons.com>
On Friday 09 September 2011, David Wagner wrote:
> On 09/09/2011 01:53 PM, Artem Bityutskiy wrote:
> > On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> >> On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> >>> Not sure about the bus approach - David, could you take a look at it
> >>> please? If we can handle errors there - then we could indeed re-use the
> >>> UBI control device. We could even re-use the ioctl data structures for
> >>> UBI volumes creation/removal - we have plenty of space there reserved
> >>> for future extensions.
> >>
>
> @Arnd:
> > * Use the existing UBI control device for the block devices as
> > well and just add two more ioctls to create the devices.
> > You can add a logical bus_type for this so that the ubi block
> > driver gets automatically loaded matched with the device when
> > one is created using the control device.
>
> I certainly miss some background, I'm not sure I understand how this
> works: bus_type seems suitable for pluggable devices that possess a
> device ID which matches against a driver that will then get loaded. But
> ubiblk devices are created by ubiblk.
> So, are you suggesting to move ubiblk_create() to UBI and add a
> MODULE_ALIAS to ubiblk (actually, I don't know what it would contain) ?
Sort of: You definitely move some part of ubiblk into the control
driver, but that part can be relatively small. All devices on
the ubiblk bus would have the same type, so the same probe function
would always get called.
> @Artem:
> > Sorry, I wonted to talk about situations when someone opens an ubiblk
> > device while the underlying UBI volume is being removed, but then though
> > this is trivial and forgot to erase the last sentence.
>
> Ah, yes, I guess we need to hold a vol_lock in ubiblk_remove() ?
Most importantly, you need reference counting.
Arnd
^ permalink raw reply
* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
From: Artem Bityutskiy @ 2011-09-09 15:27 UTC (permalink / raw)
To: Arnd Bergmann
Cc: david.wagner, linux-mtd, linux-embedded, lkml, Tim Bird,
David Woodhouse
In-Reply-To: <201109091625.13253.arnd@arndb.de>
On Fri, 2011-09-09 at 16:25 +0200, Arnd Bergmann wrote:
> On Friday 09 September 2011, Artem Bityutskiy wrote:
> > On Thu, 2011-09-08 at 17:26 +0200, Arnd Bergmann wrote:
> > > On Tuesday 06 September 2011, Artem Bityutskiy wrote:
> > > > Not sure about the bus approach - David, could you take a look at it
> > > > please? If we can handle errors there - then we could indeed re-use the
> > > > UBI control device. We could even re-use the ioctl data structures for
> > > > UBI volumes creation/removal - we have plenty of space there reserved
> > > > for future extensions.
> > >
> > > I would generally recommend using new ioctl commands. ioctl numbers
> > > are cheap, but complexity in data structures is not, because every
> > > user who wants to deal with the data structures has to understand
> > > them. Also, changing the ABI is always tricky since you have to
> > > provide backward and forwards compatibility with existing kernels
> > > and with existing user space.
> >
> > Hmm, what do we do if ubiblk module is not loaded, and UBI would have
> > to return an error (because the block device cannot be created), how
> > will UBI know that ubiblk is not there? Any direct call to ubiblk from
> > UBI would be a direct dependency and would require ubiblk to be always
> > loaded, which is bad.
>
> No, the idea of this approach is that the main ubi driver creates
> a device, which can always succeed. It's just that there won't
> be a block device node created, because that is part of what
> the ubiblk driver does.
>
> Compare this to how scsi works:
>
> A scsi host driver scans the host controller and adds scsi devices
> internal to the kernel, each of them have a specific type (disk,
> tape, ...). If the scsi disk driver is loaded, it will create
> a blockdev for each disk device. It doesn't matter in which order
> the drivers are loaded though.
>
> In case of ubiblk, it's similar, except that there is no way for
> the ubi layer to know if some partition should be a block device or
> not, so it relies on user space to tell it.
There is another difference, though. Disk types are persistent. UBI
volumes types are not necessarily persistent:
I may create a "just UBI volume", flash something there, may-be re-size,
do other things.
Then I want to "turn" it into a "ubiblk" volume without destroying it.
So I call the corresponding ioctl. And my requirements are:
1) if ubiblk is not loaded, I want to get -ENODEV or something
2) if ubiblk fails, I want to get -ESOMETHING as well.
3) I want to be blocked while ubiblk is creating the block device.
Similarly, I may want to ask ubiblk to "release" the volume and turn it
to "just a volume" again.
So I am not sure the analogy is very close, but thanks a lot for
providing it - it makes things clearer.
--
Best Regards,
Artem Bityutskiy
^ permalink raw reply
* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
From: Artem Bityutskiy @ 2011-09-11 10:18 UTC (permalink / raw)
To: David Wagner
Cc: Arnd Bergmann, linux-embedded, lkml, linux-mtd, Tim Bird,
David Woodhouse
In-Reply-To: <4E6A25B1.8070400@free-electrons.com>
On Fri, 2011-09-09 at 16:41 +0200, David Wagner wrote:
> > 1. Stick with the own cdev approach - the driver becomes very simple
> > in this case - we review it.
>
> This is the way it's implemented in v4, right ?
Yes, but I though I sent you some feed back with minor things as well as
with major issue related to race condigions: you should keep the UBI
volume open as long as your block device is referenced, to prevent the
UBI volume from being removed.
--
Best Regards,
Artem Bityutskiy
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply
* Re: [PATCHv3] UBI: new module ubiblk: block layer on top of UBI
From: David Wagner @ 2011-09-11 10:35 UTC (permalink / raw)
To: dedekind1
Cc: Arnd Bergmann, linux-mtd, linux-embedded, lkml, Tim Bird,
David Woodhouse
In-Reply-To: <1315736297.18731.1.camel@sauron>
On 11/09/2011 12:18, Artem Bityutskiy wrote:
> On Fri, 2011-09-09 at 16:41 +0200, David Wagner wrote:
>>> 1. Stick with the own cdev approach - the driver becomes very simple
>>> in this case - we review it.
>>
>> This is the way it's implemented in v4, right ?
>
> Yes, but I though I sent you some feed back with minor things as well as
> with major issue related to race condigions: you should keep the UBI
> volume open as long as your block device is referenced, to prevent the
> UBI volume from being removed.
>
Ah, yes, of course. I made these changes and will send them monday.
Regards,
David.
^ permalink raw reply
* [PATCHv5] UBI: new module ubiblk: block layer on top of UBI
From: David Wagner @ 2011-09-12 9:51 UTC (permalink / raw)
To: linux-mtd
Cc: linux-embedded, Arnd Bergmann, Artem Bityutskiy, David Wagner,
lkml, Tim Bird, David Woodhouse
In-Reply-To: <1308922482-14967-1-git-send-email-david.wagner@free-electrons.com>
[-- Attachment #1: Type: text/plain, Size: 25221 bytes --]
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblkX_Y, where X is the UBI device number
and Y the Volume ID).
It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.
It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.
Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.
Advantages of ubiblk over gluebi+mtdblock_ro:
* Simpler architecture
* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional MTD
device for each UBI volume, so the number of MTD devices grows quite a lot
and is a bit difficult to understand. For example, mtdblock[0-4] might be
your real MTD partitions, while mtdblock[5-9] might be your UBI volumes.
It also means that if a new real MTD partition is added, the index of all the
MTD devices exposing UBI volumes will be shifted by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are emulated on
top of volumes that come after this new one will have their ID incremented.
* ubiblk devices are created on a 'on-demand' basis, which avoids wasting
resources.
* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)
Signed-off-by: David Wagner <david.wagner@free-electrons.com>
---
changes since v4:
~~~~~~~~~~~~~~~~~
* Add missing headers (they are included by other headers but it seems to be
good practice not to rely on that).
* Remove an macro rendered useless with the linked lists
* correct some formatting in kerneldoc comments
* introduce refcounting to avoid multiple opens or closing a UBI volume while
still in use
* make checkpatch happy about assignation inside a condition
* use DEFINE_MUTEX for devlist_lock
plan for v6:
~~~~~~~~~~~~
* Use list_for_each_entry
* Dig into Arnd's suggestions, understand it and see if/how it could be done
* If it isn't rendered obsolete by the previous point, add a kernel parameter
for creating ubiblk devices at boot time (in order to have a rootfs on a
ubiblk device)
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/mtd/ubi/Kconfig | 16 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 724 ++++++++++++++++++++++++++++++++++
include/mtd/Kbuild | 1 +
include/mtd/ubiblk-user.h | 47 +++
6 files changed, 790 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
create mode 100644 include/mtd/ubiblk-user.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 845a191..b24df7f 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -150,6 +150,7 @@ Code Seq#(hex) Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'O' 00-06 mtd/ubi-user.h UBI
+'O' 10-11 mtd/ubiblk-user.h ubiblk
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict!
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..977934a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,20 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.
+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use R/O block filesystems on
+ top of UBI volumes (and hence, on top of MTDs while avoiding bad
+ blocks).
+
+ ubiblk devices are created by invoking appropriate ioctl to the
+ ubiblk_ctrl device node.
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o
ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..6abf76b
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,724 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright ������ 2003-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from gluebi.c
+ * (Artem Bityutskiy (������������������������������������������������ ������������������������������), Joern Engel)
+ * Some code taken from cdev.c (Artem Bityutskiy (������������������������������������������������ ������������������������������))
+ * Some code taken from mtd_blkdevs.c (David Woodhouse)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <mtd/ubiblk-user.h>
+#include "ubi.h"
+
+#define BLK_SIZE 512
+
+/**
+ * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume
+ * @desc: open UBI volume descriptor
+ * @vi: UBI volume information
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @refcnt: reference counter (increases with open(), decreases with release())
+ * @gd: the disk (block device) created by ubiblk
+ * @rq: the request queue to @gd
+ * @req_task: the thread processing @rq requests
+ * @vol_lock: protects write access to the elements of this structure
+ * @queue_lock: avoids concurrent accesses to the request queue
+ * @list: linked list structure
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info *vi;
+ int ubi_num;
+ int vol_id;
+ int refcnt;
+
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *req_task;
+
+ struct mutex vol_lock;
+
+ spinlock_t queue_lock;
+
+ struct list_head list;
+};
+
+/* Linked list of all ubiblk_dev instances */
+static LIST_HEAD(ubiblk_devs);
+
+/* Avoid concurrent access to the above list */
+static DEFINE_MUTEX(devlist_lock);
+
+static int ubiblk_major;
+static const struct block_device_operations ubiblk_ops;
+
+/* The device receiving the ioctls */
+static struct miscdevice ctrl_dev;
+
+static struct ubiblk_dev *ubiblk_find_dev(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct list_head *list_ptr;
+
+ /* TODO: use list_for_each_entry ? */
+ list_for_each(list_ptr, &ubiblk_devs) {
+ dev = list_entry(list_ptr, struct ubiblk_dev, list);
+ if (dev && dev->ubi_num == vi->ubi_num &&
+ dev->vol_id == vi->vol_id)
+ break;
+ dev = NULL;
+ }
+ return dev;
+}
+
+/**
+ * do_ubiblk_request - Read a LEB and fill the request buffer with the
+ * requested sector
+ *
+ * @req: the request data structure
+ * @dev: the ubiblk device on which the request is issued
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vi->usable_leb_size;
+ offset = start % dev->vi->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vi->usable_leb_size)
+ len = dev->vi->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ dev_err(disk_to_dev(dev->gd),
+ "attempting to read too far\n");
+ return -EIO;
+ }
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ dev_err(disk_to_dev(dev->gd), "ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_request - wakes the processing thread
+ *
+ * @rq: the request queue which device is to be awaken
+ */
+static void ubi_ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req = NULL;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->req_task);
+}
+
+/**
+ * ubiblk_open - open a UBI volume (get the volume descriptor)
+ *
+ * @bdev: the corresponding block device
+ * @mode: opening mode (don't care as long as ubiblk is read-only)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ int err = 0;
+
+ mutex_lock(&dev->vol_lock);
+ if (dev->refcnt > 0) {
+ /*
+ * The volume is already opened ; just increase the reference
+ * counter.
+ */
+ dev->refcnt++;
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+ }
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ dev_err(disk_to_dev(dev->gd), "failed to open");
+
+ err = PTR_ERR(dev->desc);
+ dev->desc = NULL;
+ goto out_lock;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ dev->refcnt++;
+ dev_dbg(disk_to_dev(dev->gd), "opened mode=%d\n", mode);
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+
+out_close:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_lock:
+ mutex_unlock(&dev->vol_lock);
+ return err;
+}
+
+/**
+ * ubiblk_release - close a UBI volume (close the volume descriptor)
+ *
+ * @gd: the disk that was previously opened
+ * @mode: don't care
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+
+ mutex_lock(&dev->vol_lock);
+
+ dev->refcnt--;
+ if (dev->refcnt == 0) {
+ kfree(dev->vi);
+ dev->vi = NULL;
+
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+ }
+
+ dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
+
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_thread - loop on the block request queue and wait for new
+ * requests ; run them with do_ubiblk_request(). Mostly copied from
+ * mtd_blkdevs.c
+ *
+ * @arg: the ubiblk device which request queue to process
+ */
+static int ubi_ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req)
+ req = blk_fetch_request(rq);
+ if (!req) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->vol_lock);
+ res = do_ubiblk_request(req, dev);
+ mutex_unlock(&dev->vol_lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/**
+ * ubiblk_create - create a ubiblk device proxying a UBI volume
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * req_task.
+ */
+static int ubiblk_create(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int ret = 0;
+
+ mutex_lock(&devlist_lock);
+ /* Check that the volume isn't already proxyfied */
+ if (ubiblk_find_dev(vi)) {
+ ret = -EEXIST;
+ goto out_devlist;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_devlist;
+ }
+
+ list_add(&dev->list, &ubiblk_devs);
+
+ mutex_init(&dev->vol_lock);
+ mutex_lock(&dev->vol_lock);
+
+ dev->ubi_num = vi->ubi_num;
+ dev->vol_id = vi->vol_id;
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ pr_err("ubi_open_volume failed\n");
+ ret = PTR_ERR(dev->desc);
+ goto out_vol;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ ret = -ENOMEM;
+ goto out_info;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_disk;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = ubiblk_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ set_capacity(gd,
+ (dev->vi->size *
+ dev->vi->usable_leb_size) >> 9);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("blk_init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Stolen from mtd_blkdevs.c */
+ /* Create processing req_task
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubi_ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->req_task = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->req_task)) {
+ ret = PTR_ERR(dev->req_task);
+ goto out_thread;
+ }
+
+ add_disk(dev->gd);
+
+ dev_info(disk_to_dev(dev->gd),
+ "created from ubi%d:%d(%s)\n", dev->ubi_num, dev->vol_id,
+ dev->vi->name);
+
+ kfree(dev->vi);
+ dev->vi = NULL;
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+
+ return 0;
+
+out_thread:
+ blk_cleanup_queue(dev->rq);
+out_queue:
+ put_disk(dev->gd);
+out_disk:
+ kfree(dev->vi);
+ dev->vi = NULL;
+out_info:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_vol:
+ mutex_unlock(&dev->vol_lock);
+out_devlist:
+ mutex_unlock(&devlist_lock);
+
+ return ret;
+}
+
+/**
+ * ubiblk_remove - destroy a ubiblk device
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been removed or we are requested to unproxify a volume ;
+ * destroy the corresponding ubiblk device
+ */
+static int ubiblk_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev = NULL;
+
+ mutex_lock(&devlist_lock);
+
+ dev = ubiblk_find_dev(vi);
+
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to remove %s, but it isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ if (dev->desc) {
+ mutex_unlock(&devlist_lock);
+ return -EBUSY;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev->vi);
+
+ list_del(&dev->list);
+ kfree(dev);
+
+ mutex_unlock(&devlist_lock);
+ pr_info("unproxyfied %s\n", vi->name);
+ return 0;
+}
+
+/**
+ * ubiblk_resize - resize a ubiblk device
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been resized, change the ubiblk device geometry accordingly
+ */
+static int ubiblk_resize(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ /* We don't touch the list, but we better lock it: it could be that the
+ * device gets removed between the time the device has been found and
+ * the time we access dev->gd
+ */
+ mutex_lock(&devlist_lock);
+ dev = ubiblk_find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to resize %s, which isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ set_capacity(dev->gd,
+ (vi->size * vi->usable_leb_size) >> 9);
+ dev_dbg(disk_to_dev(dev->gd), "resized to %d LEBs\n", vi->size);
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_notify - dispatches the UBI notifications
+ * copied from gluebi.c
+ *
+ * @nb: unused
+ * @notification_type: the notification type sent by UBI
+ * @ns_ptr: contains the notifications' additional informations
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resize(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+
+/**
+ * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume
+ *
+ * @file: the file on which the ioctl was invoked (usunsed)
+ * @cmd: the ioctl type
+ * @arg: additional command informations
+ */
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ dev_err(ctrl_dev.this_device, "opening ubi%d:%d failed\n",
+ req.ubi_num, req.vol_id);
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+
+ switch (cmd) {
+ case UBIBLK_IOCADD:
+ dev_info(ctrl_dev.this_device, "proxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_create(&vi);
+ break;
+ case UBIBLK_IOCDEL:
+ dev_info(ctrl_dev.this_device, "unproxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_remove(&vi);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+#ifdef CONFIG_COMPAT
+static long ubiblk_ctrl_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ unsigned long translated_arg = (unsigned long)compat_ptr(arg);
+
+ return ubiblk_ctrl_ioctl(file, cmd, translated_arg);
+}
+#endif
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = ubiblk_ctrl_compat_ioctl,
+#endif
+ .llseek = no_llseek,
+};
+static struct miscdevice ctrl_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubiblk_ctrl",
+ .fops = &ubiblk_ctrl_ops,
+};
+
+/**
+ * ubi_ubiblk_init - initialize the module
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret <= 0)
+ return ret;
+ ubiblk_major = ret;
+
+ mutex_lock(&devlist_lock);
+
+ ret = misc_register(&ctrl_dev);
+ if (ret) {
+ pr_err("can't register control device\n");
+ goto out_unreg_blk;
+ }
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_ctrl;
+
+ mutex_unlock(&devlist_lock);
+ pr_info("major device number is %d\n", ubiblk_major);
+
+ return ret;
+
+out_unreg_ctrl:
+ misc_deregister(&ctrl_dev);
+out_unreg_blk:
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ mutex_unlock(&devlist_lock);
+ return ret;
+}
+
+/**
+ * ubi_ubiblk_exit - end of life
+ *
+ * unregister the block device major, unregister from UBI notifications,
+ * unregister the ioctl handler device stop the threads and free the memory.
+ */
+static void __exit ubi_ubiblk_exit(void)
+{
+ struct list_head *list_ptr, *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_safe(list_ptr, next, &ubiblk_devs) {
+ dev = list_entry(list_ptr, struct ubiblk_dev, list);
+
+ /* TODO: it shouldn't happen, right ? */
+ if (dev->desc)
+ ubi_close_volume(dev->desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev->vi);
+ list_del(&dev->list); /* really needed ? */
+ kfree(dev);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+ pr_info("end of life\n");
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild
index 192f8fb..d0d59d8 100644
--- a/include/mtd/Kbuild
+++ b/include/mtd/Kbuild
@@ -3,3 +3,4 @@ header-y += mtd-abi.h
header-y += mtd-user.h
header-y += nftl-user.h
header-y += ubi-user.h
+header-y += ubiblk-user.h
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..61df415
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright ������ Free Electrons, 2011
+ * Copyright ������ International Business Machines Corp., 2006
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from ubi-user.h
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+} __packed;
+
+/* ioctl commands of the UBI control character device */
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+/* If you add ioctls here, please note that UBI uses 'O'/0x00-0x06 */
+
+#endif
--
1.7.0.4
[-- Attachment #2: Type: text/plain, Size: 144 bytes --]
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related
* Timer interrupt on Linux 3.0.3
From: Vineeth @ 2011-09-16 11:43 UTC (permalink / raw)
To: linux-embedded, linuxppc-embedded, linuxppc-dev
[-- Attachment #1.1: Type: text/plain, Size: 626 bytes --]
Hi,
We are porting Linux on MPC7410 based board. As there was some issue with
the processor DECrementer and timebase registers, we were using an external
count down timer as our DECementer. We used timer_interrupt() function in
arch/powerpc/kernel/time.c as our interrupt handler. Things were working
fine.
lately we moved to Linux 3.0.3. What we found was even without a proper
timer, the kernel with initramfs worked (except the calls like sleep,delay
ofcourse); Is there any major difference in the scheduler ? or the way
context switch happens ? is it not depended on the timer interrupt or the
timer ??
Thanks
Vineeth
[-- Attachment #1.2: Type: text/html, Size: 660 bytes --]
[-- Attachment #2: Type: text/plain, Size: 150 bytes --]
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: Timer interrupt on Linux 3.0.3
From: Scott Wood @ 2011-09-16 17:03 UTC (permalink / raw)
To: Vineeth; +Cc: linux-embedded, linuxppc-dev
In-Reply-To: <CAFbQSaD-c=QqSsq__VtBEcMCJabnBi4AP4KunDNV7u7xXAuaTw@mail.gmail.com>
On 09/16/2011 06:43 AM, Vineeth wrote:
> Hi,
>
> We are porting Linux on MPC7410 based board. As there was some issue
> with the processor DECrementer and timebase registers,
What was the issue? You really should try to make this work rather than
hack around it.
> lately we moved to Linux 3.0.3. What we found was even without a proper
> timer, the kernel with initramfs worked (except the calls like
> sleep,delay ofcourse); Is there any major difference in the scheduler ?
> or the way context switch happens ? is it not depended on the timer
> interrupt or the timer ??
If your timer is broken then you won't get timeslice expiration, but
basic scheduling should still work. Most scheduling (depending on type
of load, of course) will be triggered by processes blocking or being
woken up by I/O, not timeslice expiration.
-Scott
^ permalink raw reply
* Re: [PATCHv5] UBI: new module ubiblk: block layer on top of UBI
From: Artem Bityutskiy @ 2011-09-19 4:50 UTC (permalink / raw)
To: David Wagner
Cc: linux-mtd, linux-embedded, lkml, Tim Bird, David Woodhouse,
Arnd Bergmann
In-Reply-To: <1315821061-14819-1-git-send-email-david.wagner@free-electrons.com>
Hi David,
some review below.
> +/**
> + * ubiblk_release - close a UBI volume (close the volume descriptor)
> + *
> + * @gd: the disk that was previously opened
> + * @mode: don't care
> + */
> +static int ubiblk_release(struct gendisk *gd, fmode_t mode)
> +{
> + struct ubiblk_dev *dev = gd->private_data;
> +
> + mutex_lock(&dev->vol_lock);
> +
> + dev->refcnt--;
> + if (dev->refcnt == 0) {
> + kfree(dev->vi);
> + dev->vi = NULL;
> +
> + ubi_close_volume(dev->desc);
> + dev->desc = NULL;
> + }
> +
> + dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
> +
> + mutex_unlock(&dev->vol_lock);
> + return 0;
> +}
Looks buggy. I'd expect this function to remove the device from the
list as well as free it, as well as hold the devlist_mutex...
...
> +/**
> + * ubiblk_create - create a ubiblk device proxying a UBI volume
> + *
> + * @vi: the UBI volume information data structure
> + *
> + * An UBI volume has been created ; create a corresponding ubiblk device:
> + * Initialize the locks, the structure, the block layer infos and start a
> + * req_task.
> + */
> +static int ubiblk_create(struct ubi_volume_info *vi)
> +{
> + struct ubiblk_dev *dev;
> + struct gendisk *gd;
> + int ret = 0;
> +
> + mutex_lock(&devlist_lock);
> + /* Check that the volume isn't already proxyfied */
> + if (ubiblk_find_dev(vi)) {
> + ret = -EEXIST;
> + goto out_devlist;
> + }
> +
> + dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
> + if (!dev) {
> + ret = -ENOMEM;
> + goto out_devlist;
> + }
> +
> + list_add(&dev->list, &ubiblk_devs);
> +
> + mutex_init(&dev->vol_lock);
> + mutex_lock(&dev->vol_lock);
I think upi do not need to take vol_lock. You are creating the volume,
and no one can open it anyway because you have 'devlist_lock' now.
...
> + set_capacity(gd,
> + (dev->vi->size *
> + dev->vi->usable_leb_size) >> 9);
A temporary variable would be neater.
> + set_disk_ro(gd, 1);
> + dev->gd = gd;
> +
> + spin_lock_init(&dev->queue_lock);
> + dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
> + if (!dev->rq) {
> + pr_err("blk_init_queue failed\n");
> + ret = -ENODEV;
> + goto out_queue;
> + }
> + dev->rq->queuedata = dev;
> + blk_queue_logical_block_size(dev->rq, BLK_SIZE);
> + dev->gd->queue = dev->rq;
> +
> + /* Stolen from mtd_blkdevs.c */
s/Stolen/borrowed/ ?
...
> +/**
> + * ubiblk_remove - destroy a ubiblk device
> + *
> + * @vi: the UBI volume information data structure
> + *
> + * A UBI volume has been removed or we are requested to unproxify a volume ;
> + * destroy the corresponding ubiblk device
> + */
> +static int ubiblk_remove(struct ubi_volume_info *vi)
> +{
> + struct ubiblk_dev *dev = NULL;
> +
> + mutex_lock(&devlist_lock);
> +
> + dev = ubiblk_find_dev(vi);
> +
> + if (!dev) {
> + mutex_unlock(&devlist_lock);
> + pr_warn("trying to remove %s, but it isn't handled\n",
> + vi->name);
> + return -ENODEV;
> + }
> +
> + if (dev->desc) {
> + mutex_unlock(&devlist_lock);
> + return -EBUSY;
> + }
Racy. Your dev->desc is protected by the "vol_lock", here you did not
take it, but you should. And then you can release it after
"list_del(&dev->list)".
> +
> + del_gendisk(dev->gd);
> + blk_cleanup_queue(dev->rq);
> + kthread_stop(dev->req_task);
> + put_disk(dev->gd);
> +
> + kfree(dev->vi);
> +
> + list_del(&dev->list);
> + kfree(dev);
> +
> + mutex_unlock(&devlist_lock);
> + pr_info("unproxyfied %s\n", vi->name);
> + return 0;
> +}
...
> +#ifdef CONFIG_COMPAT
> +static long ubiblk_ctrl_compat_ioctl(struct file *file, unsigned int cmd,
> + unsigned long arg)
> +{
> + unsigned long translated_arg = (unsigned long)compat_ptr(arg);
> +
> + return ubiblk_ctrl_ioctl(file, cmd, translated_arg);
> +}
> +#endif
> +
> +/* ubiblk control device (receives ioctls) */
> +static const struct file_operations ubiblk_ctrl_ops = {
> + .owner = THIS_MODULE,
> + .unlocked_ioctl = ubiblk_ctrl_ioctl,
> +#ifdef CONFIG_COMPAT
> + .compat_ioctl = ubiblk_ctrl_compat_ioctl,
> +#endif
> + .llseek = no_llseek,
> +};
You do not need compat ioctl. This is needed only for old code which
does nasty things like using pointers in ioctl data structures. The idea
is that new code uses proper ioctl data structures instead. Please, kill
this.
...
> +static int __init ubi_ubiblk_init(void)
> +{
> + int ret = 0;
> +
> + ret = register_blkdev(0, "ubiblk");
> + if (ret <= 0)
> + return ret;
Error code "0" means success, so if you return 0 here, you'll confuse
the upper layers.
...
> +static void __exit ubi_ubiblk_exit(void)
> +{
> + struct list_head *list_ptr, *next;
> + struct ubiblk_dev *dev;
> +
> + ubi_unregister_volume_notifier(&ubiblk_notifier);
> + misc_deregister(&ctrl_dev);
> +
> + list_for_each_safe(list_ptr, next, &ubiblk_devs) {
> + dev = list_entry(list_ptr, struct ubiblk_dev, list);
> +
> + /* TODO: it shouldn't happen, right ? */
> + if (dev->desc)
> + ubi_close_volume(dev->desc);
Since you are using mutexes now, can you remove the TODO line? If you
think "if (dev->desc)" is still needed, then please, add WARN_ON() in
the "else" part, or just kill the "if" part.
> +
> + del_gendisk(dev->gd);
> + blk_cleanup_queue(dev->rq);
> + kthread_stop(dev->req_task);
> + put_disk(dev->gd);
> +
> + kfree(dev->vi);
> + list_del(&dev->list); /* really needed ? */
Good question, I think we should not have comments like this in the
final driver.
> + kfree(dev);
> + }
> +
> + unregister_blkdev(ubiblk_major, "ubiblk");
> + pr_info("end of life\n");
This funny but let's remove it please.
> +}
> +
> +module_init(ubi_ubiblk_init);
> +module_exit(ubi_ubiblk_exit);
> +MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
> +MODULE_AUTHOR("David Wagner");
> +MODULE_LICENSE("GPL");
....
> +/**
> + * ubiblk_ctrl_req - additional ioctl data structure
> + * @ubi_num: UBI device number
> + * @vol_id: UBI volume identifier
* @padding: reserved for future, must contain zeroes
> + */
> +struct ubiblk_ctrl_req {
> + __s32 ubi_num;
> + __s32 vol_id;
__u8[8] padding;
> +} __packed;
Please, add paddings as suggested above.
--
Best Regards,
Artem Bityutskiy
^ permalink raw reply
* Re: Timer interrupt on Linux 3.0.3
From: Vineeth @ 2011-09-21 6:56 UTC (permalink / raw)
To: Scott Wood; +Cc: linuxppc-dev, linux-embedded
In-Reply-To: <4E73816C.6000304@freescale.com>
[-- Attachment #1.1: Type: text/plain, Size: 1453 bytes --]
>> What was the issue? You really should try to make this work rather than
>> hack around it.
what we found was the decrementer is not generating an exception when it
becomes 0. and the timebase registers are not getting incremented too. The
exceptions are enabled in MSR registers.
we are using embedded60x port of linux3.0.3; we've written the dts file with
proper clock-frequency/timebase-frequency values.
Do you think we might've missed some configuration in the timer perspective
?
On Fri, Sep 16, 2011 at 10:33 PM, Scott Wood <scottwood@freescale.com>wrote:
> On 09/16/2011 06:43 AM, Vineeth wrote:
> > Hi,
> >
> > We are porting Linux on MPC7410 based board. As there was some issue
> > with the processor DECrementer and timebase registers,
>
> What was the issue? You really should try to make this work rather than
> hack around it.
>
> > lately we moved to Linux 3.0.3. What we found was even without a proper
> > timer, the kernel with initramfs worked (except the calls like
> > sleep,delay ofcourse); Is there any major difference in the scheduler ?
> > or the way context switch happens ? is it not depended on the timer
> > interrupt or the timer ??
>
> If your timer is broken then you won't get timeslice expiration, but
> basic scheduling should still work. Most scheduling (depending on type
> of load, of course) will be triggered by processes blocking or being
> woken up by I/O, not timeslice expiration.
>
> -Scott
>
>
[-- Attachment #1.2: Type: text/html, Size: 1918 bytes --]
[-- Attachment #2: Type: text/plain, Size: 150 bytes --]
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev
^ permalink raw reply
* Re: Timer interrupt on Linux 3.0.3
From: Scott Wood @ 2011-09-21 19:41 UTC (permalink / raw)
To: Vineeth; +Cc: linux-embedded, linuxppc-dev
In-Reply-To: <CAFbQSaBcdLCoVY471mVLAyM=eYDSN77=DODPasMaAEm6pRm7jQ@mail.gmail.com>
On 09/21/2011 01:56 AM, Vineeth wrote:
>>> What was the issue? You really should try to make this work rather than
>>> hack around it.
>
> what we found was the decrementer is not generating an exception when it
> becomes 0. and the timebase registers are not getting incremented too.
Does the decrementer actually tick until it reaches zero, or do it and
the timebase never tick?
Is the TBEN input to the CPU asserted?
-Scott
^ permalink raw reply
* Re: Timer interrupt on Linux 3.0.3
From: MohanReddy koppula @ 2011-09-22 6:22 UTC (permalink / raw)
To: Scott Wood; +Cc: Vineeth, linuxppc-dev, linux-embedded
In-Reply-To: <4E7A3DED.9030402@freescale.com>
I had the same issue with an MPC885 board. My kernel was 2.6.33. On
that board decrementer exception was not working. I replaced the
board, took new board (MPC885 only, just another board) and the same
kernel worked fine. I don't know how the problem was solved.
-Mohan
On 9/22/11, Scott Wood <scottwood@freescale.com> wrote:
> On 09/21/2011 01:56 AM, Vineeth wrote:
>>>> What was the issue? You really should try to make this work rather than
>>>> hack around it.
>>
>> what we found was the decrementer is not generating an exception when it
>> becomes 0. and the timebase registers are not getting incremented too.
>
> Does the decrementer actually tick until it reaches zero, or do it and
> the timebase never tick?
>
> Is the TBEN input to the CPU asserted?
>
> -Scott
>
> _______________________________________________
> Linuxppc-dev mailing list
> Linuxppc-dev@lists.ozlabs.org
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
^ permalink raw reply
* [PATCHv6] UBI: new module ubiblk: block layer on top of UBI
From: David Wagner @ 2011-09-22 7:58 UTC (permalink / raw)
To: linux-mtd
Cc: linux-embedded, lkml, Tim Bird, David Woodhouse, David Wagner,
Artem Bityutskiy, Arnd Bergmann
In-Reply-To: <1308922482-14967-1-git-send-email-david.wagner@free-electrons.com>
ubiblk is a read-only block layer on top of UBI. It presents UBI volumes as
read-only block devices (named ubiblkX_Y, where X is the UBI device number
and Y the Volume ID).
It is used by putting a block filesystem image on a UBI volume, creating the
corresponding ubiblk device and then mounting it.
It uses the UBI API to register to UBI notifications and to read from the
volumes. It also creates a ubiblk_ctrl device node that simply receives ioctl
from a userspace tool for creating/removing ubiblk devices.
Some code is taken from mtd_blkdevs and gluebi. Some code for the ioctl part is
also inspired from ubi's core.
Advantages of ubiblk over gluebi+mtdblock_ro:
* Simpler architecture
* The numbering of devices is much easier with ubiblk than with
gluebi+mtdblock_ro. With gluebi+mtdblock_ro, you get one additional MTD
device for each UBI volume, so the number of MTD devices grows quite a lot
and is a bit difficult to understand. For example, mtdblock[0-4] might be
your real MTD partitions, while mtdblock[5-9] might be your UBI volumes.
It also means that if a new real MTD partition is added, the index of all the
MTD devices exposing UBI volumes will be shifted by one, which is a bit
confusing/annoying.
As well, if you add an UBI volume, the mtdblock devices that are emulated on
top of volumes that come after this new one will have their ID incremented.
* ubiblk devices are created on a 'on-demand' basis, which avoids wasting
resources.
* It is also possible to specify a "volume" parameter in order to create a
ubiblk device at init time. This makes possible to put a rootfs on a ubiblk
device.
* The performance appears to be slightly better with ubiblk than
gluebi+mtdblock_ro, according to our benchmarks (see
http://elinux.org/Flash_Filesystem_Benchmarks_2.6.39)
Signed-off-by: David Wagner <david.wagner@free-electrons.com>
Cc: Artem Bityutskiy <dedekind1@gmail.com>
Cc: Arnd Bergmann <arnd@arndb.de>
---
changelog since v5:
~~~~~~~~~~~~~~~~~~~
* Use list_for_each_entry instead of list_for_each
* Add a "volume" parameter for creating a device at init time. It allows to
put a rootfs on the volume. Format: volume=<ubi_num>:<vol_id>
* Locking: remove useless locks and add necessary new locks
* Remove the CONFIG_COMPAT code
* Don't walk the error path if register_blkdev returns 0
* WARN when a ubiblk device still has an open descriptor at exit time (happens
with rmmod -f).
* Use temporary variables for readability
* Add dynamic debug regarding refcounting
* Remove a useless check in ubiblk_create (the test consisted in opening the
proxified volume)
* In ubiblk_create(), directly use vi (passed as argument) instead of dev->vi
* No need to remove the devices from the linked list at exit time: it doesn't
free any memory
* Add padding in the ioctl data structure
* remove a useless kfree(dev->vi) in ubiblk_remove: the refcounting assures
that it has been freed before (in ubiblk_release). Same in exit.
@Artem regarding ubiblk_release:
it is only called when the device is closed (eg, unmounted), not when it is
removed (eg the volume disappears or the user send an ioctl to remove it). The
function responsible for removing a device (and thus removing it from the linked
list) is ubiblk_remove.
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/mtd/ubi/Kconfig | 16 +
drivers/mtd/ubi/Makefile | 1 +
drivers/mtd/ubi/ubiblk.c | 751 ++++++++++++++++++++++++++++++++++
include/mtd/Kbuild | 1 +
include/mtd/ubiblk-user.h | 49 +++
6 files changed, 819 insertions(+), 0 deletions(-)
create mode 100644 drivers/mtd/ubi/ubiblk.c
create mode 100644 include/mtd/ubiblk-user.h
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 845a191..b24df7f 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -150,6 +150,7 @@ Code Seq#(hex) Include File Comments
'M' 00-0F drivers/video/fsl-diu-fb.h conflict!
'N' 00-1F drivers/usb/scanner.h
'O' 00-06 mtd/ubi-user.h UBI
+'O' 10-11 mtd/ubiblk-user.h ubiblk
'P' all linux/soundcard.h conflict!
'P' 60-6F sound/sscape_ioctl.h conflict!
'P' 00-0F drivers/usb/class/usblp.c conflict!
diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig
index 4dcc752..977934a 100644
--- a/drivers/mtd/ubi/Kconfig
+++ b/drivers/mtd/ubi/Kconfig
@@ -60,4 +60,20 @@ config MTD_UBI_DEBUG
help
This option enables UBI debugging.
+config MTD_UBI_UBIBLK
+ tristate "Read-only block transition layer on top of UBI"
+ help
+ Read-only block interface on top of UBI.
+
+ This option adds ubiblk, which creates a read-ony block device from
+ UBI volumes. It makes it possible to use R/O block filesystems on
+ top of UBI volumes (and hence, on top of MTDs while avoiding bad
+ blocks).
+
+ ubiblk devices are created by invoking appropriate ioctl to the
+ ubiblk_ctrl device node.
+
+ The devices are named ubiblkX_Y where X is the UBI number and Y is
+ the Volume ID.
+
endif # MTD_UBI
diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile
index c9302a5..354b2df 100644
--- a/drivers/mtd/ubi/Makefile
+++ b/drivers/mtd/ubi/Makefile
@@ -5,3 +5,4 @@ ubi-y += misc.o
ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o
obj-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o
+obj-$(CONFIG_MTD_UBI_UBIBLK) += ubiblk.o
diff --git a/drivers/mtd/ubi/ubiblk.c b/drivers/mtd/ubi/ubiblk.c
new file mode 100644
index 0000000..d22ae4f
--- /dev/null
+++ b/drivers/mtd/ubi/ubiblk.c
@@ -0,0 +1,751 @@
+/*
+ * Copyright (c) Free Electrons, 2011
+ * Copyright (c) International Business Machines Corp., 2006
+ * Copyright © 2003-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from gluebi.c
+ * (Artem Bityutskiy (Битюцкий Артём), Joern Engel)
+ * Some code taken from cdev.c (Artem Bityutskiy (Битюцкий Артём))
+ * Some code taken from mtd_blkdevs.c (David Woodhouse)
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/ubi.h>
+#include <linux/blkdev.h>
+#include <linux/miscdevice.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <mtd/ubiblk-user.h>
+#include "ubi.h"
+
+#define BLK_SIZE 512
+
+/**
+ * struct ubiblk_dev - represents a ubiblk device, proxying a UBI volume
+ * @desc: open UBI volume descriptor
+ * @vi: UBI volume information
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @refcnt: reference counter (increases with open(), decreases with release())
+ * @gd: the disk (block device) created by ubiblk
+ * @rq: the request queue to @gd
+ * @req_task: the thread processing @rq requests
+ * @vol_lock: protects write access to the elements of this structure
+ * @queue_lock: avoids concurrent accesses to the request queue
+ * @list: linked list structure
+ */
+struct ubiblk_dev {
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info *vi;
+ int ubi_num;
+ int vol_id;
+ int refcnt;
+
+ struct gendisk *gd;
+ struct request_queue *rq;
+ struct task_struct *req_task;
+
+ struct mutex vol_lock;
+
+ spinlock_t queue_lock;
+
+ struct list_head list;
+};
+
+/* Linked list of all ubiblk_dev instances */
+static LIST_HEAD(ubiblk_devs);
+
+/* Avoid concurrent access to the above list */
+static DEFINE_MUTEX(devlist_lock);
+
+static int ubiblk_major;
+static const struct block_device_operations ubiblk_ops;
+
+/* The device receiving the ioctls */
+static struct miscdevice ctrl_dev;
+
+#define VOL_PARAM_MAXLEN 8
+static char *volume;
+module_param(volume, charp, 0000);
+MODULE_PARM_DESC(volume,
+ "Format: volume=<ubi_num>:<vol_id>\n"
+ "Create a ubiblk device at init time. Useful for mounting it as root "
+ "device.");
+
+static struct ubiblk_dev *ubiblk_find_dev(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+
+ list_for_each_entry(dev, &ubiblk_devs, list) {
+ if (dev && dev->ubi_num == vi->ubi_num &&
+ dev->vol_id == vi->vol_id)
+ return dev;
+ }
+ return NULL;
+}
+
+/**
+ * do_ubiblk_request - Read a LEB and fill the request buffer with the
+ * requested sector
+ *
+ * @req: the request data structure
+ * @dev: the ubiblk device on which the request is issued
+ */
+static int do_ubiblk_request(struct request *req, struct ubiblk_dev *dev)
+{
+ unsigned long start, len, read_bytes;
+ int offset;
+ int leb;
+ int ret;
+
+ start = blk_rq_pos(req) << 9;
+ len = blk_rq_cur_bytes(req);
+ read_bytes = 0;
+
+ /* We are always reading. No need to handle writing for now */
+
+ leb = start / dev->vi->usable_leb_size;
+ offset = start % dev->vi->usable_leb_size;
+
+ do {
+ if (offset + len > dev->vi->usable_leb_size)
+ len = dev->vi->usable_leb_size - offset;
+
+ if (unlikely(blk_rq_pos(req) + blk_rq_cur_sectors(req) >
+ get_capacity(req->rq_disk))) {
+ dev_err(disk_to_dev(dev->gd),
+ "attempting to read too far\n");
+ return -EIO;
+ }
+
+ /* Read (len) bytes of LEB (leb) from (offset) and put the
+ * result in the buffer given by the request.
+ * If the request is overlapping on several lebs, (read_bytes)
+ * will be > 0 and the data will be put in the buffer at
+ * offset (read_bytes)
+ */
+ ret = ubi_read(dev->desc, leb, req->buffer + read_bytes,
+ offset, len);
+
+ if (ret) {
+ dev_err(disk_to_dev(dev->gd), "ubi_read error\n");
+ return ret;
+ }
+
+ read_bytes += len;
+
+ len = blk_rq_cur_bytes(req) - read_bytes;
+ leb++;
+ offset = 0;
+ } while (read_bytes < blk_rq_cur_bytes(req));
+
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_request - wakes the processing thread
+ *
+ * @rq: the request queue which device is to be awaken
+ */
+static void ubi_ubiblk_request(struct request_queue *rq)
+{
+ struct ubiblk_dev *dev;
+ struct request *req = NULL;
+
+ dev = rq->queuedata;
+
+ if (!dev)
+ while ((req = blk_fetch_request(rq)) != NULL)
+ __blk_end_request_all(req, -ENODEV);
+ else
+ wake_up_process(dev->req_task);
+}
+
+/**
+ * ubiblk_open - open a UBI volume (get the volume descriptor)
+ *
+ * @bdev: the corresponding block device
+ * @mode: opening mode (don't care as long as ubiblk is read-only)
+ */
+static int ubiblk_open(struct block_device *bdev, fmode_t mode)
+{
+ struct ubiblk_dev *dev = bdev->bd_disk->private_data;
+ int err = 0;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "open(); refcnt = %d\n", dev->refcnt);
+ if (dev->refcnt > 0) {
+ /*
+ * The volume is already opened ; just increase the reference
+ * counter.
+ */
+ dev->refcnt++;
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+ }
+
+ dev->desc = ubi_open_volume(dev->ubi_num, dev->vol_id,
+ UBI_READONLY);
+ if (IS_ERR(dev->desc)) {
+ dev_err(disk_to_dev(dev->gd), "failed to open");
+
+ err = PTR_ERR(dev->desc);
+ dev->desc = NULL;
+ goto out_lock;
+ }
+
+ dev->vi = kzalloc(sizeof(struct ubi_volume_info), GFP_KERNEL);
+ if (!dev->vi) {
+ err = -ENOMEM;
+ goto out_close;
+ }
+ ubi_get_volume_info(dev->desc, dev->vi);
+
+ dev->refcnt++;
+ dev_dbg(disk_to_dev(dev->gd), "opened mode=%d\n", mode);
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+
+out_close:
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+out_lock:
+ mutex_unlock(&dev->vol_lock);
+ return err;
+}
+
+/**
+ * ubiblk_release - close a UBI volume (close the volume descriptor)
+ *
+ * @gd: the disk that was previously opened
+ * @mode: don't care
+ */
+static int ubiblk_release(struct gendisk *gd, fmode_t mode)
+{
+ struct ubiblk_dev *dev = gd->private_data;
+
+ mutex_lock(&dev->vol_lock);
+ dev_dbg(disk_to_dev(dev->gd), "release(); refcnt = %d\n", dev->refcnt);
+
+ dev->refcnt--;
+ if (dev->refcnt == 0) {
+ kfree(dev->vi);
+ dev->vi = NULL;
+
+ ubi_close_volume(dev->desc);
+ dev->desc = NULL;
+
+ dev_dbg(disk_to_dev(dev->gd), "released, mode=%d\n", mode);
+ }
+
+ mutex_unlock(&dev->vol_lock);
+ return 0;
+}
+
+/**
+ * ubi_ubiblk_thread - loop on the block request queue and wait for new
+ * requests ; run them with do_ubiblk_request(). Mostly copied from
+ * mtd_blkdevs.c
+ *
+ * @arg: the ubiblk device which request queue to process
+ */
+static int ubi_ubiblk_thread(void *arg)
+{
+ struct ubiblk_dev *dev = arg;
+ struct request_queue *rq = dev->rq;
+ struct request *req = NULL;
+
+ spin_lock_irq(rq->queue_lock);
+
+ while (!kthread_should_stop()) {
+ int res;
+
+ if (!req)
+ req = blk_fetch_request(rq);
+ if (!req) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ if (kthread_should_stop())
+ set_current_state(TASK_RUNNING);
+
+ spin_unlock_irq(rq->queue_lock);
+ schedule();
+ spin_lock_irq(rq->queue_lock);
+ continue;
+ }
+
+ spin_unlock_irq(rq->queue_lock);
+
+ mutex_lock(&dev->vol_lock);
+ res = do_ubiblk_request(req, dev);
+ mutex_unlock(&dev->vol_lock);
+
+ spin_lock_irq(rq->queue_lock);
+
+ if (!__blk_end_request_cur(req, res))
+ req = NULL;
+ }
+
+ if (req)
+ __blk_end_request_all(req, -EIO);
+
+ spin_unlock_irq(rq->queue_lock);
+
+ return 0;
+}
+
+/**
+ * ubiblk_create - create a ubiblk device proxying a UBI volume
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * An UBI volume has been created ; create a corresponding ubiblk device:
+ * Initialize the locks, the structure, the block layer infos and start a
+ * req_task.
+ */
+static int ubiblk_create(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ struct gendisk *gd;
+ int disk_capacity;
+ int ret = 0;
+
+ mutex_lock(&devlist_lock);
+ /* Check that the volume isn't already proxyfied */
+ if (ubiblk_find_dev(vi)) {
+ ret = -EEXIST;
+ goto out_lock;
+ }
+
+ dev = kzalloc(sizeof(struct ubiblk_dev), GFP_KERNEL);
+ if (!dev) {
+ ret = -ENOMEM;
+ goto out_lock;
+ }
+
+ mutex_init(&dev->vol_lock);
+
+ dev->ubi_num = vi->ubi_num;
+ dev->vol_id = vi->vol_id;
+
+ /* Initialize the gendisk of this ubiblk device */
+ gd = alloc_disk(1);
+ if (!gd) {
+ pr_err("alloc_disk failed\n");
+ ret = -ENODEV;
+ goto out_lock;
+ }
+
+ gd->fops = &ubiblk_ops;
+ gd->major = ubiblk_major;
+ gd->first_minor = dev->ubi_num * UBI_MAX_VOLUMES + dev->vol_id;
+ gd->private_data = dev;
+ sprintf(gd->disk_name, "ubiblk%d_%d", dev->ubi_num, dev->vol_id);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(gd, disk_capacity);
+ set_disk_ro(gd, 1);
+ dev->gd = gd;
+
+ spin_lock_init(&dev->queue_lock);
+ dev->rq = blk_init_queue(ubi_ubiblk_request, &dev->queue_lock);
+ if (!dev->rq) {
+ pr_err("blk_init_queue failed\n");
+ ret = -ENODEV;
+ goto out_queue;
+ }
+ dev->rq->queuedata = dev;
+ blk_queue_logical_block_size(dev->rq, BLK_SIZE);
+ dev->gd->queue = dev->rq;
+
+ /* Borrowed from mtd_blkdevs.c */
+ /* Create processing req_task
+ *
+ * The processing of the request has to be done in process context (it
+ * might sleep) but blk_run_queue can't block ; so we need to separate
+ * the event of a request being added to the queue (which triggers the
+ * callback ubi_ubiblk_request - that is set with blk_init_queue())
+ * and the processing of that request.
+ *
+ * Thus, the sole purpose of ubi_ubiblk_reuqest is to wake the kthread
+ * up so that it will process the request queue
+ */
+ dev->req_task = kthread_run(ubi_ubiblk_thread, dev, "%s%d_%d",
+ "kubiblk", dev->ubi_num, dev->vol_id);
+ if (IS_ERR(dev->req_task)) {
+ ret = PTR_ERR(dev->req_task);
+ goto out_thread;
+ }
+
+ list_add(&dev->list, &ubiblk_devs);
+ add_disk(dev->gd);
+
+ dev_info(disk_to_dev(dev->gd),
+ "created from ubi%d:%d(%s)\n", dev->ubi_num, dev->vol_id,
+ vi->name);
+
+ mutex_unlock(&devlist_lock);
+
+ return 0;
+
+out_thread:
+ blk_cleanup_queue(dev->rq);
+out_queue:
+ put_disk(dev->gd);
+out_lock:
+ mutex_unlock(&devlist_lock);
+
+ return ret;
+}
+
+/**
+ * ubiblk_remove - destroy a ubiblk device
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been removed or we are requested to unproxify a volume ;
+ * destroy the corresponding ubiblk device
+ */
+static int ubiblk_remove(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev = NULL;
+
+ mutex_lock(&devlist_lock);
+
+ dev = ubiblk_find_dev(vi);
+
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to remove %s, but it isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ if (dev->desc) {
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+ return -EBUSY;
+ }
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ list_del(&dev->list);
+ mutex_unlock(&dev->vol_lock);
+ mutex_unlock(&devlist_lock);
+
+ kfree(dev);
+ pr_info("unproxyfied %s\n", vi->name);
+ return 0;
+}
+
+/**
+ * ubiblk_resize - resize a ubiblk device
+ *
+ * @vi: the UBI volume information data structure
+ *
+ * A UBI volume has been resized, change the ubiblk device geometry accordingly
+ */
+static int ubiblk_resize(struct ubi_volume_info *vi)
+{
+ struct ubiblk_dev *dev;
+ int disk_capacity;
+
+ /* We don't touch the list, but we better lock it: it could be that the
+ * device gets removed between the time the device has been found and
+ * the time we access dev->gd
+ */
+ mutex_lock(&devlist_lock);
+ dev = ubiblk_find_dev(vi);
+ if (!dev) {
+ mutex_unlock(&devlist_lock);
+ pr_warn("trying to resize %s, which isn't handled\n",
+ vi->name);
+ return -ENODEV;
+ }
+
+ mutex_lock(&dev->vol_lock);
+ disk_capacity = (vi->size * vi->usable_leb_size) >> 9;
+ set_capacity(dev->gd, disk_capacity);
+ dev_dbg(disk_to_dev(dev->gd), "resized to %d LEBs\n", vi->size);
+ mutex_unlock(&dev->vol_lock);
+
+ mutex_unlock(&devlist_lock);
+ return 0;
+}
+
+/**
+ * ubiblk_notify - dispatches the UBI notifications
+ * copied from gluebi.c
+ *
+ * @nb: unused
+ * @notification_type: the notification type sent by UBI
+ * @ns_ptr: contains the notifications' additional informations
+ */
+static int ubiblk_notify(struct notifier_block *nb,
+ unsigned long notification_type, void *ns_ptr)
+{
+ struct ubi_notification *nt = ns_ptr;
+
+ switch (notification_type) {
+ case UBI_VOLUME_REMOVED:
+ ubiblk_remove(&nt->vi);
+ break;
+ case UBI_VOLUME_RESIZED:
+ ubiblk_resize(&nt->vi);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+static const struct block_device_operations ubiblk_ops = {
+ .owner = THIS_MODULE,
+ .open = ubiblk_open,
+ .release = ubiblk_release,
+};
+
+static struct notifier_block ubiblk_notifier = {
+ .notifier_call = ubiblk_notify,
+};
+
+
+/**
+ * ubiblk_ctrl_ioctl - ioctl handling for proxying/unproxying a UBI volume
+ *
+ * @file: the file on which the ioctl was invoked (usunsed)
+ * @cmd: the ioctl type
+ * @arg: additional command informations
+ */
+static long ubiblk_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = 0;
+ void __user *argp = (void __user *)arg;
+
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+ struct ubiblk_ctrl_req req;
+
+ if (!capable(CAP_SYS_RESOURCE))
+ return -EPERM;
+
+ err = copy_from_user(&req, argp, sizeof(struct ubiblk_ctrl_req));
+ if (err)
+ return -EFAULT;
+
+ if (req.ubi_num < 0 || req.vol_id < 0)
+ return -EINVAL;
+
+ desc = ubi_open_volume(req.ubi_num, req.vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ dev_err(ctrl_dev.this_device, "opening ubi%d:%d failed\n",
+ req.ubi_num, req.vol_id);
+ return PTR_ERR(desc);
+ }
+
+ ubi_get_volume_info(desc, &vi);
+
+ switch (cmd) {
+ case UBIBLK_IOCADD:
+ dev_info(ctrl_dev.this_device, "proxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_create(&vi);
+ break;
+ case UBIBLK_IOCDEL:
+ dev_info(ctrl_dev.this_device, "unproxying ubi%d:%d\n",
+ req.ubi_num, req.vol_id);
+ err = ubiblk_remove(&vi);
+ break;
+
+ default:
+ err = -ENOTTY;
+ break;
+ }
+
+ ubi_close_volume(desc);
+
+ return err;
+}
+
+/* ubiblk control device (receives ioctls) */
+static const struct file_operations ubiblk_ctrl_ops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ubiblk_ctrl_ioctl,
+ .llseek = no_llseek,
+};
+static struct miscdevice ctrl_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "ubiblk_ctrl",
+ .fops = &ubiblk_ctrl_ops,
+};
+
+/**
+ * ubiblk_parse_volume_param - parse the "volume" module parameter
+ *
+ * @ubi_num: where to store the UBI device number
+ * @vol_id: where to store the volume ID
+ */
+static int __init ubiblk_parse_volume_param(int *ubi_num, int *vol_id)
+{
+ char *tokens[2] = {NULL, NULL};
+ char buf[VOL_PARAM_MAXLEN];
+ char *pbuf = buf;
+ int len = strlen(buf);
+ int err;
+
+ if (len > VOL_PARAM_MAXLEN || len == 0)
+ return -1;
+
+ strcpy(buf, volume);
+ tokens[0] = strsep(&pbuf, ":");
+ tokens[1] = strsep(&pbuf, ":");
+
+ if (pbuf)
+ return -1; /* There are surnumerous parameters */
+
+ err = kstrtoint(tokens[0], 10, ubi_num);
+ if (err < 0 || *ubi_num < 0)
+ return -1;
+
+ err = kstrtoint(tokens[1], 10, vol_id);
+ if (err < 0 || *vol_id < 0)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * ubiblk_inittime_volume - create a volume at init time
+ *
+ * If the user passed a "ubiblk.volume" argument, check it and create the said
+ * volume.
+ */
+static void __init ubiblk_inittime_volume(void)
+{
+ int ubi_num, vol_id;
+ int err;
+ struct ubi_volume_desc *desc;
+ struct ubi_volume_info vi;
+
+ if (ubiblk_parse_volume_param(&ubi_num, &vol_id) < 0) {
+ pr_err("cannot parse the volume parameter");
+ return;
+ }
+
+ desc = ubi_open_volume(ubi_num, vol_id, UBI_READONLY);
+ if (IS_ERR(desc)) {
+ pr_err("opening ubi%d:%d failed: %ld\n", ubi_num, vol_id,
+ PTR_ERR(desc));
+ return;
+ }
+ ubi_get_volume_info(desc, &vi);
+
+ err = ubiblk_create(&vi);
+ if (err < 0) {
+ pr_err("can't create the initial device "
+ "ubiblk%d_%d: %d\n", ubi_num, vol_id, err);
+ }
+ ubi_close_volume(desc);
+}
+
+/**
+ * ubi_ubiblk_init - initialize the module
+ *
+ * Get a major number and register to UBI notifications ; register the ioctl
+ * handler device
+ */
+static int __init ubi_ubiblk_init(void)
+{
+ int ret = 0;
+
+ ret = register_blkdev(0, "ubiblk");
+ if (ret < 0)
+ return ret;
+ ubiblk_major = ret;
+
+ /* Check if the user wants a volume to be proxified at init time */
+ if (volume)
+ ubiblk_inittime_volume();
+
+ ret = ubi_register_volume_notifier(&ubiblk_notifier, 1);
+ if (ret < 0)
+ goto out_unreg_blk;
+
+ ret = misc_register(&ctrl_dev);
+ if (ret) {
+ pr_err("can't register control device\n");
+ goto out_unreg_notifier;
+ }
+
+ pr_info("major device number is %d\n", ubiblk_major);
+
+ return ret;
+
+out_unreg_notifier:
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+out_unreg_blk:
+ unregister_blkdev(ubiblk_major, "ubiblk");
+
+ return ret;
+}
+
+/**
+ * ubi_ubiblk_exit - end of life
+ *
+ * unregister the block device major, unregister from UBI notifications,
+ * unregister the ioctl handler device stop the threads and free the memory.
+ */
+static void __exit ubi_ubiblk_exit(void)
+{
+ struct ubiblk_dev *next;
+ struct ubiblk_dev *dev;
+
+ ubi_unregister_volume_notifier(&ubiblk_notifier);
+ misc_deregister(&ctrl_dev);
+
+ list_for_each_entry_safe(dev, next, &ubiblk_devs, list) {
+ /* The module is being forcefully removed */
+ WARN_ON(dev->desc);
+
+ del_gendisk(dev->gd);
+ blk_cleanup_queue(dev->rq);
+ kthread_stop(dev->req_task);
+ put_disk(dev->gd);
+
+ kfree(dev);
+ }
+
+ unregister_blkdev(ubiblk_major, "ubiblk");
+}
+
+module_init(ubi_ubiblk_init);
+module_exit(ubi_ubiblk_exit);
+MODULE_DESCRIPTION("Read-only block transition layer on top of UBI");
+MODULE_AUTHOR("David Wagner");
+MODULE_LICENSE("GPL");
diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild
index 192f8fb..d0d59d8 100644
--- a/include/mtd/Kbuild
+++ b/include/mtd/Kbuild
@@ -3,3 +3,4 @@ header-y += mtd-abi.h
header-y += mtd-user.h
header-y += nftl-user.h
header-y += ubi-user.h
+header-y += ubiblk-user.h
diff --git a/include/mtd/ubiblk-user.h b/include/mtd/ubiblk-user.h
new file mode 100644
index 0000000..96858c7
--- /dev/null
+++ b/include/mtd/ubiblk-user.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright © Free Electrons, 2011
+ * Copyright © International Business Machines Corp., 2006
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: David Wagner
+ * Some code taken from ubi-user.h
+ */
+
+#ifndef __UBIBLK_USER_H__
+#define __UBIBLK_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * ubiblk_ctrl_req - additional ioctl data structure
+ * @ubi_num: UBI device number
+ * @vol_id: UBI volume identifier
+ * @padding: reserved for future, must contain zeroes
+ */
+struct ubiblk_ctrl_req {
+ __s32 ubi_num;
+ __s32 vol_id;
+ __u8 padding[8];
+} __packed;
+
+/* ioctl commands of the UBI control character device */
+#define UBIBLK_CTRL_IOC_MAGIC 'O'
+
+/* Create a ubiblk device from a UBI volume */
+#define UBIBLK_IOCADD _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x10, struct ubiblk_ctrl_req)
+/* Delete a ubiblk device */
+#define UBIBLK_IOCDEL _IOW(UBIBLK_CTRL_IOC_MAGIC, 0x11, struct ubiblk_ctrl_req)
+/* If you add ioctls here, please note that UBI uses 'O'/0x00-0x06 */
+
+#endif
--
1.7.0.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox