From 3482f2c52b77bf6596e24aae82e204a0603eba66 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 27 Mar 2014 17:18:55 -0700 Subject: of: Create of_console_check() for selecting a console specified in /chosen The devicetree has a binding for specifying the console device in the /chosen node, but the kernel doesn't use it consistently. This change adds an API for testing if a device node is a console, and adds a preferred console entry if it is. At the same time this patch removes the of_device_is_stdout_path() API since it is unused. Signed-off-by: Grant Likely Tested-by: Sascha Hauer diff --git a/drivers/of/base.c b/drivers/of/base.c index b986480..df9b2bb 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -17,6 +17,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ +#include #include #include #include @@ -2180,20 +2181,22 @@ const char *of_prop_next_string(struct property *prop, const char *cur) EXPORT_SYMBOL_GPL(of_prop_next_string); /** - * of_device_is_stdout_path - check if a device node matches the - * linux,stdout-path property - * - * Check if this device node matches the linux,stdout-path property - * in the chosen node. return true if yes, false otherwise. + * of_console_check() - Test and setup console for DT setup + * @dn - Pointer to device node + * @name - Name to use for preferred console without index. ex. "ttyS" + * @index - Index to use for preferred console. + * + * Check if the given device node matches the stdout-path property in the + * /chosen node. If it does then register it as the preferred console and return + * TRUE. Otherwise return FALSE. */ -int of_device_is_stdout_path(struct device_node *dn) +bool of_console_check(struct device_node *dn, char *name, int index) { - if (!of_stdout) + if (!dn || dn != of_stdout || console_set_on_cmdline) return false; - - return of_stdout == dn; + return add_preferred_console(name, index, NULL); } -EXPORT_SYMBOL_GPL(of_device_is_stdout_path); +EXPORT_SYMBOL_GPL(of_console_check); /** * of_find_next_cache_node - Find a node's subsidiary cache diff --git a/include/linux/of.h b/include/linux/of.h index 196b34c..9d97340 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -352,7 +352,7 @@ const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur, */ const char *of_prop_next_string(struct property *prop, const char *cur); -int of_device_is_stdout_path(struct device_node *dn); +bool of_console_check(struct device_node *dn, char *name, int index); #else /* CONFIG_OF */ @@ -564,9 +564,9 @@ static inline int of_machine_is_compatible(const char *compat) return 0; } -static inline int of_device_is_stdout_path(struct device_node *dn) +static inline bool of_console_check(const struct device_node *dn, const char *name, int index) { - return 0; + return false; } static inline const __be32 *of_prop_next_u32(struct property *prop, -- cgit v0.10.2 From a208ffd251d08ed7ba6bdf3ae1e423373fb12d3d Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 27 Mar 2014 18:29:46 -0700 Subject: of: Enable console on serial ports specified by /chosen/stdout-path If the devicetree specifies a serial port as a stdout device, then the kernel can use it as the default console if nothing else was selected on the command line. For any serial port that uses the uart_add_one_port() feature, the uart_add_one_port() has all the information needed to automatically enable the console device, which is what this patch does. With this change applied, a device tree platform can be booted without any console= parameters on the command line and the kernel will still be able to determine its console. Tested on QEMU Versatile model and i.MX Signed-off-by: Grant Likely Tested-by: Sascha Hauer diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index fbf6c5a..3ce68f9 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -2615,6 +2616,8 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) spin_lock_init(&uport->lock); lockdep_set_class(&uport->lock, &port_lock_key); } + if (uport->cons && uport->dev) + of_console_check(uport->dev->of_node, uport->cons->name, uport->line); uart_configure_port(drv, state, uport); -- cgit v0.10.2 From 003882180340a2808151d320dedd853f4cff5e4b Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 27 Mar 2014 18:35:32 -0700 Subject: arm/versatile: Add the uart as the stdout device. Add a stdout-path property to the Versatile devicetree so that automatic console selection works without needing a console= line on the kernel command line. Signed-off-by: Grant Likely diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts index e01e5a0..88e94c55 100644 --- a/arch/arm/boot/dts/versatile-ab.dts +++ b/arch/arm/boot/dts/versatile-ab.dts @@ -15,6 +15,10 @@ i2c0 = &i2c0; }; + chosen { + stdout-path = &uart0; + }; + memory { reg = <0x0 0x08000000>; }; -- cgit v0.10.2 From a752ee56ad84bf9a35b8323af1ad22b03c1df2c4 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Fri, 28 Mar 2014 08:12:18 -0700 Subject: tty: Update hypervisor tty drivers to use core stdout parsing code. The evh_bytechan, hvc_opal and hvc_vio drivers all open code the parsing of the stdout node in the device tree. This patch simplifies the driver by removing the duplicated functionality. Signed-off-by: Grant Likely diff --git a/drivers/of/base.c b/drivers/of/base.c index df9b2bb..e4f95ba 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -36,7 +36,7 @@ struct device_node *of_allnodes; EXPORT_SYMBOL(of_allnodes); struct device_node *of_chosen; struct device_node *of_aliases; -static struct device_node *of_stdout; +struct device_node *of_stdout; static struct kset *of_kset; @@ -2063,9 +2063,12 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) of_chosen = of_find_node_by_path("/chosen@0"); if (of_chosen) { + /* linux,stdout-path and /aliases/stdout are for legacy compatibility */ const char *name = of_get_property(of_chosen, "stdout-path", NULL); if (!name) name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (IS_ENABLED(CONFIG_PPC) && !name) + name = of_get_property(of_aliases, "stdout", NULL); if (name) of_stdout = of_find_node_by_path(name); } diff --git a/drivers/tty/ehv_bytechan.c b/drivers/tty/ehv_bytechan.c index 0419b69..4f485e8 100644 --- a/drivers/tty/ehv_bytechan.c +++ b/drivers/tty/ehv_bytechan.c @@ -108,55 +108,23 @@ static void disable_tx_interrupt(struct ehv_bc_data *bc) * * The byte channel to be used for the console is specified via a "stdout" * property in the /chosen node. - * - * For compatible with legacy device trees, we also look for a "stdout" alias. */ static int find_console_handle(void) { - struct device_node *np, *np2; + struct device_node *np = of_stdout; const char *sprop = NULL; const uint32_t *iprop; - np = of_find_node_by_path("/chosen"); - if (np) - sprop = of_get_property(np, "stdout-path", NULL); - - if (!np || !sprop) { - of_node_put(np); - np = of_find_node_by_name(NULL, "aliases"); - if (np) - sprop = of_get_property(np, "stdout", NULL); - } - - if (!sprop) { - of_node_put(np); - return 0; - } - /* We don't care what the aliased node is actually called. We only * care if it's compatible with "epapr,hv-byte-channel", because that - * indicates that it's a byte channel node. We use a temporary - * variable, 'np2', because we can't release 'np' until we're done with - * 'sprop'. + * indicates that it's a byte channel node. */ - np2 = of_find_node_by_path(sprop); - of_node_put(np); - np = np2; - if (!np) { - pr_warning("ehv-bc: stdout node '%s' does not exist\n", sprop); - return 0; - } - - /* Is it a byte channel? */ - if (!of_device_is_compatible(np, "epapr,hv-byte-channel")) { - of_node_put(np); + if (!np || !of_device_is_compatible(np, "epapr,hv-byte-channel")) return 0; - } stdout_irq = irq_of_parse_and_map(np, 0); if (stdout_irq == NO_IRQ) { - pr_err("ehv-bc: no 'interrupts' property in %s node\n", sprop); - of_node_put(np); + pr_err("ehv-bc: no 'interrupts' property in %s node\n", np->full_name); return 0; } @@ -167,12 +135,9 @@ static int find_console_handle(void) if (!iprop) { pr_err("ehv-bc: no 'hv-handle' property in %s node\n", np->name); - of_node_put(np); return 0; } stdout_bc = be32_to_cpu(*iprop); - - of_node_put(np); return 1; } diff --git a/drivers/tty/hvc/hvc_opal.c b/drivers/tty/hvc/hvc_opal.c index a585079..a2cc5f8 100644 --- a/drivers/tty/hvc/hvc_opal.c +++ b/drivers/tty/hvc/hvc_opal.c @@ -342,22 +342,13 @@ static void udbg_init_opal_common(void) void __init hvc_opal_init_early(void) { - struct device_node *stdout_node = NULL; + struct device_node *stdout_node = of_node_get(of_stdout); const __be32 *termno; - const char *name = NULL; const struct hv_ops *ops; u32 index; - /* find the boot console from /chosen/stdout */ - if (of_chosen) - name = of_get_property(of_chosen, "linux,stdout-path", NULL); - if (name) { - stdout_node = of_find_node_by_path(name); - if (!stdout_node) { - pr_err("hvc_opal: Failed to locate default console!\n"); - return; - } - } else { + /* If the console wasn't in /chosen, try /ibm,opal */ + if (!stdout_node) { struct device_node *opal, *np; /* Current OPAL takeover doesn't provide the stdout diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c index b594abf..5618b5f 100644 --- a/drivers/tty/hvc/hvc_vio.c +++ b/drivers/tty/hvc/hvc_vio.c @@ -404,42 +404,35 @@ module_exit(hvc_vio_exit); void __init hvc_vio_init_early(void) { - struct device_node *stdout_node; const __be32 *termno; const char *name; const struct hv_ops *ops; /* find the boot console from /chosen/stdout */ - if (!of_chosen) + if (!of_stdout) return; - name = of_get_property(of_chosen, "linux,stdout-path", NULL); - if (name == NULL) - return; - stdout_node = of_find_node_by_path(name); - if (!stdout_node) - return; - name = of_get_property(stdout_node, "name", NULL); + name = of_get_property(of_stdout, "name", NULL); if (!name) { printk(KERN_WARNING "stdout node missing 'name' property!\n"); - goto out; + return; } /* Check if it's a virtual terminal */ if (strncmp(name, "vty", 3) != 0) - goto out; - termno = of_get_property(stdout_node, "reg", NULL); + return; + termno = of_get_property(of_stdout, "reg", NULL); if (termno == NULL) - goto out; + return; hvterm_priv0.termno = of_read_number(termno, 1); spin_lock_init(&hvterm_priv0.buf_lock); hvterm_privs[0] = &hvterm_priv0; /* Check the protocol */ - if (of_device_is_compatible(stdout_node, "hvterm1")) { + if (of_device_is_compatible(of_stdout, "hvterm1")) { hvterm_priv0.proto = HV_PROTOCOL_RAW; ops = &hvterm_raw_ops; } - else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) { + else if (of_device_is_compatible(of_stdout, "hvterm-protocol")) { hvterm_priv0.proto = HV_PROTOCOL_HVSI; ops = &hvterm_hvsi_ops; hvsilib_init(&hvterm_priv0.hvsi, hvc_get_chars, hvc_put_chars, @@ -447,7 +440,7 @@ void __init hvc_vio_init_early(void) /* HVSI, perform the handshake now */ hvsilib_establish(&hvterm_priv0.hvsi); } else - goto out; + return; udbg_putc = udbg_hvc_putc; udbg_getc = udbg_hvc_getc; udbg_getc_poll = udbg_hvc_getc_poll; @@ -456,14 +449,12 @@ void __init hvc_vio_init_early(void) * backend for HVSI, only do udbg */ if (hvterm_priv0.proto == HV_PROTOCOL_HVSI) - goto out; + return; #endif /* Check whether the user has requested a different console. */ if (!strstr(cmd_line, "console=")) add_preferred_console("hvc", 0, NULL); hvc_instantiate(0, 0, ops); -out: - of_node_put(stdout_node); } /* call this from early_init() for a working debug console on diff --git a/include/linux/of.h b/include/linux/of.h index 9d97340..f0d2562 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -113,6 +113,7 @@ static inline void of_node_put(struct device_node *node) { } extern struct device_node *of_allnodes; extern struct device_node *of_chosen; extern struct device_node *of_aliases; +extern struct device_node *of_stdout; extern raw_spinlock_t devtree_lock; static inline bool of_have_populated_dt(void) -- cgit v0.10.2 From ccdb8ed3b3c739fe99a6f2f474f7ffad3203485d Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 4 Jun 2014 16:42:26 +0100 Subject: of: Migrate of_find_node_by_name() users to for_each_node_by_name() There are a bunch of users open coding the for_each_node_by_name() by calling of_find_node_by_name() directly instead of using the macro. This is getting in the way of some cleanups, and the possibility of removing of_find_node_by_name() entirely. Clean it up so that all the users are consistent. Signed-off-by: Grant Likely Cc: Rob Herring Cc: Benjamin Herrenschmidt Cc: "Rafael J. Wysocki" Cc: Greg Kroah-Hartman Cc: Takashi Iwai diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index 63d82bb..39e1d16 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c @@ -2805,25 +2805,20 @@ set_initial_features(void) /* Enable GMAC for now for PCI probing. It will be disabled * later on after PCI probe */ - np = of_find_node_by_name(NULL, "ethernet"); - while(np) { + for_each_node_by_name(np, "ethernet") if (of_device_is_compatible(np, "K2-GMAC")) g5_gmac_enable(np, 0, 1); - np = of_find_node_by_name(np, "ethernet"); - } /* Enable FW before PCI probe. Will be disabled later on * Note: We should have a batter way to check that we are * dealing with uninorth internal cell and not a PCI cell * on the external PCI. The code below works though. */ - np = of_find_node_by_name(NULL, "firewire"); - while(np) { + for_each_node_by_name(np, "firewire") { if (of_device_is_compatible(np, "pci106b,5811")) { macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; g5_fw_enable(np, 0, 1); } - np = of_find_node_by_name(np, "firewire"); } } #else /* CONFIG_POWER4 */ @@ -2834,13 +2829,11 @@ set_initial_features(void) /* Enable GMAC for now for PCI probing. It will be disabled * later on after PCI probe */ - np = of_find_node_by_name(NULL, "ethernet"); - while(np) { + for_each_node_by_name(np, "ethernet") { if (np->parent && of_device_is_compatible(np->parent, "uni-north") && of_device_is_compatible(np, "gmac")) core99_gmac_enable(np, 0, 1); - np = of_find_node_by_name(np, "ethernet"); } /* Enable FW before PCI probe. Will be disabled later on @@ -2848,8 +2841,7 @@ set_initial_features(void) * dealing with uninorth internal cell and not a PCI cell * on the external PCI. The code below works though. */ - np = of_find_node_by_name(NULL, "firewire"); - while(np) { + for_each_node_by_name(np, "firewire") { if (np->parent && of_device_is_compatible(np->parent, "uni-north") && (of_device_is_compatible(np, "pci106b,18") || @@ -2858,18 +2850,16 @@ set_initial_features(void) macio_chips[0].flags |= MACIO_FLAG_FW_SUPPORTED; core99_firewire_enable(np, 0, 1); } - np = of_find_node_by_name(np, "firewire"); } /* Enable ATA-100 before PCI probe. */ np = of_find_node_by_name(NULL, "ata-6"); - while(np) { + for_each_node_by_name(np, "ata-6") { if (np->parent && of_device_is_compatible(np->parent, "uni-north") && of_device_is_compatible(np, "kauai-ata")) { core99_ata100_enable(np, 1); } - np = of_find_node_by_name(np, "ata-6"); } /* Switch airport off */ diff --git a/arch/powerpc/platforms/powermac/pci.c b/arch/powerpc/platforms/powermac/pci.c index cf7009b..7e868cc 100644 --- a/arch/powerpc/platforms/powermac/pci.c +++ b/arch/powerpc/platforms/powermac/pci.c @@ -698,7 +698,7 @@ static void __init fixup_nec_usb2(void) { struct device_node *nec; - for (nec = NULL; (nec = of_find_node_by_name(nec, "usb")) != NULL;) { + for_each_node_by_name(nec, "usb") { struct pci_controller *hose; u32 data; const u32 *prop; diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index 5cbd4d6..af094ae 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c @@ -577,7 +577,7 @@ static void __init smp_core99_setup_i2c_hwsync(int ncpus) int ok; /* Look for the clock chip */ - while ((cc = of_find_node_by_name(cc, "i2c-hwclock")) != NULL) { + for_each_node_by_name(cc, "i2c-hwclock") { p = of_get_parent(cc); ok = p && of_device_is_compatible(p, "uni-n-i2c"); of_node_put(p); diff --git a/arch/powerpc/platforms/powermac/udbg_adb.c b/arch/powerpc/platforms/powermac/udbg_adb.c index 44e0b55..366bd22 100644 --- a/arch/powerpc/platforms/powermac/udbg_adb.c +++ b/arch/powerpc/platforms/powermac/udbg_adb.c @@ -191,7 +191,7 @@ int __init udbg_adb_init(int force_btext) * of type "adb". If not, we return a failure, but we keep the * bext output set for now */ - for (np = NULL; (np = of_find_node_by_name(np, "keyboard")) != NULL;) { + for_each_node_by_name(np, "keyboard") { struct device_node *parent = of_get_parent(np); int found = (parent && strcmp(parent->type, "adb") == 0); of_node_put(parent); diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c index f2f40e6..cd6b4cd 100644 --- a/arch/powerpc/platforms/pseries/setup.c +++ b/arch/powerpc/platforms/pseries/setup.c @@ -232,8 +232,7 @@ static void __init pseries_discover_pic(void) struct device_node *np; const char *typep; - for (np = NULL; (np = of_find_node_by_name(np, - "interrupt-controller"));) { + for_each_node_by_name(np, "interrupt-controller") { typep = of_get_property(np, "compatible", NULL); if (strstr(typep, "open-pic")) { pSeries_mpic_node = of_node_get(np); diff --git a/drivers/cpufreq/pmac64-cpufreq.c b/drivers/cpufreq/pmac64-cpufreq.c index 8bc4229..4ff8687 100644 --- a/drivers/cpufreq/pmac64-cpufreq.c +++ b/drivers/cpufreq/pmac64-cpufreq.c @@ -499,8 +499,7 @@ static int __init g5_pm72_cpufreq_init(struct device_node *cpunode) } /* Lookup the i2c hwclock */ - for (hwclock = NULL; - (hwclock = of_find_node_by_name(hwclock, "i2c-hwclock")) != NULL;){ + for_each_node_by_name(hwclock, "i2c-hwclock") { const char *loc = of_get_property(hwclock, "hwctrl-location", NULL); if (loc == NULL) diff --git a/drivers/edac/cell_edac.c b/drivers/edac/cell_edac.c index 374b57f..a12c855 100644 --- a/drivers/edac/cell_edac.c +++ b/drivers/edac/cell_edac.c @@ -134,8 +134,7 @@ static void cell_edac_init_csrows(struct mem_ctl_info *mci) int j; u32 nr_pages; - for (np = NULL; - (np = of_find_node_by_name(np, "memory")) != NULL;) { + for_each_node_by_name(np, "memory") { struct resource r; /* We "know" that the Cell firmware only creates one entry diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index 93aa29f..f2945fa 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -375,11 +375,11 @@ static void __exit cleanup_slots(void) static int __init rpaphp_init(void) { - struct device_node *dn = NULL; + struct device_node *dn; info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); - while ((dn = of_find_node_by_name(dn, "pci"))) + for_each_node_by_name(dn, "pci") rpaphp_add_slot(dn); return 0; diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 8193635..01e180b 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -1650,8 +1650,7 @@ static int __init pmz_probe(void) /* * Find all escc chips in the system */ - node_p = of_find_node_by_name(NULL, "escc"); - while (node_p) { + for_each_node_by_name(node_p, "escc") { /* * First get channel A/B node pointers * @@ -1669,7 +1668,7 @@ static int __init pmz_probe(void) of_node_put(node_b); printk(KERN_ERR "pmac_zilog: missing node %c for escc %s\n", (!node_a) ? 'a' : 'b', node_p->full_name); - goto next; + continue; } /* @@ -1696,11 +1695,9 @@ static int __init pmz_probe(void) of_node_put(node_b); memset(&pmz_ports[count], 0, sizeof(struct uart_pmac_port)); memset(&pmz_ports[count+1], 0, sizeof(struct uart_pmac_port)); - goto next; + continue; } count += 2; -next: - node_p = of_find_node_by_name(node_p, "escc"); } pmz_ports_count = count; diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c index 7a43c0c..8a431bc 100644 --- a/sound/ppc/pmac.c +++ b/sound/ppc/pmac.c @@ -992,9 +992,9 @@ static int snd_pmac_detect(struct snd_pmac *chip) return -ENODEV; if (!sound) { - sound = of_find_node_by_name(NULL, "sound"); - while (sound && sound->parent != chip->node) - sound = of_find_node_by_name(sound, "sound"); + for_each_node_by_name(sound, "sound") + if (sound->parent == chip->node) + break; } if (! sound) { of_node_put(chip->node); -- cgit v0.10.2 From 75f353b61342b5847c7f6d8499fd6301dce09845 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 24 Jun 2014 16:13:47 +0100 Subject: of/platform: Fix of_platform_device_destroy iteration of devices of_platform_destroy does not work properly, since the tree population test was iterating on all devices having as its parent the given platform device. The check was intended to check whether any other platform or amba devices created by of_platform_populate were still populated, but instead checked for every kind of device. This is wrong, since platform devices typically create a subsystem regular device and set themselves as parents. Instead, go ahead and call the unregister functions for any devices created with of_platform_populate. The driver core will take care of unbinding drivers, and drivers are responsible for getting rid of any child devices that weren't created by of_platform_populate. Signed-off-by: Grant Likely Signed-off-by: Pantelis Antoniou diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 500436f..0197725 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -422,6 +422,7 @@ static int of_platform_bus_create(struct device_node *bus, break; } } + of_node_set_flag(bus, OF_POPULATED_BUS); return rc; } @@ -508,19 +509,13 @@ EXPORT_SYMBOL_GPL(of_platform_populate); static int of_platform_device_destroy(struct device *dev, void *data) { - bool *children_left = data; - /* Do not touch devices not populated from the device tree */ - if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) { - *children_left = true; + if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) return 0; - } - /* Recurse, but don't touch this device if it has any children left */ - if (of_platform_depopulate(dev) != 0) { - *children_left = true; - return 0; - } + /* Recurse for any nodes that were treated as busses */ + if (of_node_check_flag(dev->of_node, OF_POPULATED_BUS)) + device_for_each_child(dev, NULL, of_platform_device_destroy); if (dev->bus == &platform_bus_type) platform_device_unregister(to_platform_device(dev)); @@ -528,19 +523,15 @@ static int of_platform_device_destroy(struct device *dev, void *data) else if (dev->bus == &amba_bustype) amba_device_unregister(to_amba_device(dev)); #endif - else { - *children_left = true; - return 0; - } of_node_clear_flag(dev->of_node, OF_POPULATED); - + of_node_clear_flag(dev->of_node, OF_POPULATED_BUS); return 0; } /** * of_platform_depopulate() - Remove devices populated from device tree - * @parent: device which childred will be removed + * @parent: device which children will be removed * * Complementary to of_platform_populate(), this function removes children * of the given device (and, recurrently, their children) that have been @@ -550,14 +541,9 @@ static int of_platform_device_destroy(struct device *dev, void *data) * Returns 0 when all children devices have been removed or * -EBUSY when some children remained. */ -int of_platform_depopulate(struct device *parent) +void of_platform_depopulate(struct device *parent) { - bool children_left = false; - - device_for_each_child(parent, &children_left, - of_platform_device_destroy); - - return children_left ? -EBUSY : 0; + device_for_each_child(parent, NULL, of_platform_device_destroy); } EXPORT_SYMBOL_GPL(of_platform_depopulate); diff --git a/include/linux/of.h b/include/linux/of.h index 196b34c..abf829a 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -204,6 +204,7 @@ static inline unsigned long of_read_ulong(const __be32 *cell, int size) #define OF_DYNAMIC 1 /* node and properties were allocated via kmalloc */ #define OF_DETACHED 2 /* node has been detached from the device tree */ #define OF_POPULATED 3 /* device already created for the node */ +#define OF_POPULATED_BUS 4 /* of_platform_populate recursed to children of this node */ #define OF_IS_DYNAMIC(x) test_bit(OF_DYNAMIC, &x->_flags) #define OF_MARK_DYNAMIC(x) set_bit(OF_DYNAMIC, &x->_flags) diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index d96e1ba..c2b0627 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -72,7 +72,7 @@ extern int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent); -extern int of_platform_depopulate(struct device *parent); +extern void of_platform_depopulate(struct device *parent); #else static inline int of_platform_populate(struct device_node *root, const struct of_device_id *matches, @@ -81,10 +81,7 @@ static inline int of_platform_populate(struct device_node *root, { return -ENODEV; } -static inline int of_platform_depopulate(struct device *parent) -{ - return -ENODEV; -} +static inline void of_platform_depopulate(struct device *parent) { } #endif #endif /* _LINUX_OF_PLATFORM_H */ -- cgit v0.10.2 From c05aba2bd5f9dd3f363611ff844513de1341ac60 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 4 Jul 2014 19:58:03 +0300 Subject: of: rename of_aliases_mutex to just of_mutex We're overloading usage of of_aliases_mutex for sysfs changes, so rename to something that is more generic. Signed-off-by: Pantelis Antoniou Signed-off-by: Grant Likely diff --git a/drivers/of/base.c b/drivers/of/base.c index b986480..e48a1b9 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -40,10 +40,9 @@ static struct device_node *of_stdout; static struct kset *of_kset; /* - * Used to protect the of_aliases; but also overloaded to hold off addition of - * nodes to sysfs + * Used to protect the of_aliases, to hold off addition of nodes to sysfs */ -DEFINE_MUTEX(of_aliases_mutex); +DEFINE_MUTEX(of_mutex); /* use when traversing tree through the allnext, child, sibling, * or parent members of struct device_node. @@ -255,13 +254,13 @@ int of_node_add(struct device_node *np) * Grab the mutex here so that in a race condition between of_init() and * of_node_add(), node addition will still be consistent. */ - mutex_lock(&of_aliases_mutex); + mutex_lock(&of_mutex); if (of_kset) rc = __of_node_add(np); else /* This scenario may be perfectly valid, but report it anyway */ pr_info("of_node_add(%s) before of_init()\n", np->full_name); - mutex_unlock(&of_aliases_mutex); + mutex_unlock(&of_mutex); return rc; } @@ -289,15 +288,15 @@ static int __init of_init(void) struct device_node *np; /* Create the kset, and register existing nodes */ - mutex_lock(&of_aliases_mutex); + mutex_lock(&of_mutex); of_kset = kset_create_and_add("devicetree", NULL, firmware_kobj); if (!of_kset) { - mutex_unlock(&of_aliases_mutex); + mutex_unlock(&of_mutex); return -ENOMEM; } for_each_of_allnodes(np) __of_node_add(np); - mutex_unlock(&of_aliases_mutex); + mutex_unlock(&of_mutex); /* Symlink in /proc as required by userspace ABI */ if (of_allnodes) @@ -2122,7 +2121,7 @@ int of_alias_get_id(struct device_node *np, const char *stem) struct alias_prop *app; int id = -ENODEV; - mutex_lock(&of_aliases_mutex); + mutex_lock(&of_mutex); list_for_each_entry(app, &aliases_lookup, link) { if (strcmp(app->stem, stem) != 0) continue; @@ -2132,7 +2131,7 @@ int of_alias_get_id(struct device_node *np, const char *stem) break; } } - mutex_unlock(&of_aliases_mutex); + mutex_unlock(&of_mutex); return id; } diff --git a/drivers/of/device.c b/drivers/of/device.c index dafb973..46d6c75c 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -160,7 +160,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen); seen = 0; - mutex_lock(&of_aliases_mutex); + mutex_lock(&of_mutex); list_for_each_entry(app, &aliases_lookup, link) { if (dev->of_node == app->np) { add_uevent_var(env, "OF_ALIAS_%d=%s", seen, @@ -168,7 +168,7 @@ void of_device_uevent(struct device *dev, struct kobj_uevent_env *env) seen++; } } - mutex_unlock(&of_aliases_mutex); + mutex_unlock(&of_mutex); } int of_device_uevent_modalias(struct device *dev, struct kobj_uevent_env *env) diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index ff350c8..fcc70e7 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -31,6 +31,6 @@ struct alias_prop { char stem[0]; }; -extern struct mutex of_aliases_mutex; +extern struct mutex of_mutex; extern struct list_head aliases_lookup; #endif /* _LINUX_OF_PRIVATE_H */ -- cgit v0.10.2 From 6afc0dc381573559251de9a8259404f49e6aed14 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 26 Jun 2014 15:40:48 +0100 Subject: of: Move CONFIG_OF_DYNAMIC code into a separate file Split the dynamic device tree code into a separate file to make it really clear what features CONFIF_OF_DYNAMIC add to the kernel. Without CONFIG_OF_DYNAMIC only properties can be changed, and notifiers do not get sent. Enabling it turns on reference counting, notifiers and the ability to add and remove nodes. v2: Moved of_node_release() into dynamic.c Signed-off-by: Grant Likely Signed-off-by: Pantelis Antoniou Cc: Rob Herring diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 099b1fb..08e6c0f 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -1,4 +1,5 @@ obj-y = base.o device.o platform.o +obj-$(CONFIG_OF_DYNAMIC) += dynamic.o obj-$(CONFIG_OF_FLATTREE) += fdt.o obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o obj-$(CONFIG_OF_PROMTREE) += pdt.o diff --git a/drivers/of/base.c b/drivers/of/base.c index e48a1b9..0d89556 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -88,79 +88,7 @@ int __weak of_node_to_nid(struct device_node *np) } #endif -#if defined(CONFIG_OF_DYNAMIC) -/** - * of_node_get - Increment refcount of a node - * @node: Node to inc refcount, NULL is supported to - * simplify writing of callers - * - * Returns node. - */ -struct device_node *of_node_get(struct device_node *node) -{ - if (node) - kobject_get(&node->kobj); - return node; -} -EXPORT_SYMBOL(of_node_get); - -static inline struct device_node *kobj_to_device_node(struct kobject *kobj) -{ - return container_of(kobj, struct device_node, kobj); -} - -/** - * of_node_release - release a dynamically allocated node - * @kref: kref element of the node to be released - * - * In of_node_put() this function is passed to kref_put() - * as the destructor. - */ -static void of_node_release(struct kobject *kobj) -{ - struct device_node *node = kobj_to_device_node(kobj); - struct property *prop = node->properties; - - /* We should never be releasing nodes that haven't been detached. */ - if (!of_node_check_flag(node, OF_DETACHED)) { - pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name); - dump_stack(); - return; - } - - if (!of_node_check_flag(node, OF_DYNAMIC)) - return; - - while (prop) { - struct property *next = prop->next; - kfree(prop->name); - kfree(prop->value); - kfree(prop); - prop = next; - - if (!prop) { - prop = node->deadprops; - node->deadprops = NULL; - } - } - kfree(node->full_name); - kfree(node->data); - kfree(node); -} - -/** - * of_node_put - Decrement refcount of a node - * @node: Node to dec refcount, NULL is supported to - * simplify writing of callers - * - */ -void of_node_put(struct device_node *node) -{ - if (node) - kobject_put(&node->kobj); -} -EXPORT_SYMBOL(of_node_put); -#else +#ifndef CONFIG_OF_DYNAMIC static void of_node_release(struct kobject *kobj) { /* Without CONFIG_OF_DYNAMIC, no nodes gets freed */ @@ -264,25 +192,6 @@ int of_node_add(struct device_node *np) return rc; } -#if defined(CONFIG_OF_DYNAMIC) -static void of_node_remove(struct device_node *np) -{ - struct property *pp; - - BUG_ON(!of_node_is_initialized(np)); - - /* only remove properties if on sysfs */ - if (of_node_is_attached(np)) { - for_each_property_of_node(np, pp) - sysfs_remove_bin_file(&np->kobj, &pp->attr); - kobject_del(&np->kobj); - } - - /* finally remove the kobj_init ref */ - of_node_put(np); -} -#endif - static int __init of_init(void) { struct device_node *np; @@ -1747,28 +1656,6 @@ int of_count_phandle_with_args(const struct device_node *np, const char *list_na } EXPORT_SYMBOL(of_count_phandle_with_args); -#if defined(CONFIG_OF_DYNAMIC) -static int of_property_notify(int action, struct device_node *np, - struct property *prop) -{ - struct of_prop_reconfig pr; - - /* only call notifiers if the node is attached */ - if (!of_node_is_attached(np)) - return 0; - - pr.dn = np; - pr.prop = prop; - return of_reconfig_notify(action, &pr); -} -#else -static int of_property_notify(int action, struct device_node *np, - struct property *prop) -{ - return 0; -} -#endif - /** * __of_add_property - Add a property to a node without lock operations */ @@ -1915,121 +1802,6 @@ int of_update_property(struct device_node *np, struct property *newprop) return 0; } -#if defined(CONFIG_OF_DYNAMIC) -/* - * Support for dynamic device trees. - * - * On some platforms, the device tree can be manipulated at runtime. - * The routines in this section support adding, removing and changing - * device tree nodes. - */ - -static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain); - -int of_reconfig_notifier_register(struct notifier_block *nb) -{ - return blocking_notifier_chain_register(&of_reconfig_chain, nb); -} -EXPORT_SYMBOL_GPL(of_reconfig_notifier_register); - -int of_reconfig_notifier_unregister(struct notifier_block *nb) -{ - return blocking_notifier_chain_unregister(&of_reconfig_chain, nb); -} -EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister); - -int of_reconfig_notify(unsigned long action, void *p) -{ - int rc; - - rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p); - return notifier_to_errno(rc); -} - -/** - * of_attach_node - Plug a device node into the tree and global list. - */ -int of_attach_node(struct device_node *np) -{ - unsigned long flags; - int rc; - - rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); - if (rc) - return rc; - - raw_spin_lock_irqsave(&devtree_lock, flags); - np->sibling = np->parent->child; - np->allnext = np->parent->allnext; - np->parent->allnext = np; - np->parent->child = np; - of_node_clear_flag(np, OF_DETACHED); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - - of_node_add(np); - return 0; -} - -/** - * of_detach_node - "Unplug" a node from the device tree. - * - * The caller must hold a reference to the node. The memory associated with - * the node is not freed until its refcount goes to zero. - */ -int of_detach_node(struct device_node *np) -{ - struct device_node *parent; - unsigned long flags; - int rc = 0; - - rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); - if (rc) - return rc; - - raw_spin_lock_irqsave(&devtree_lock, flags); - - if (of_node_check_flag(np, OF_DETACHED)) { - /* someone already detached it */ - raw_spin_unlock_irqrestore(&devtree_lock, flags); - return rc; - } - - parent = np->parent; - if (!parent) { - raw_spin_unlock_irqrestore(&devtree_lock, flags); - return rc; - } - - if (of_allnodes == np) - of_allnodes = np->allnext; - else { - struct device_node *prev; - for (prev = of_allnodes; - prev->allnext != np; - prev = prev->allnext) - ; - prev->allnext = np->allnext; - } - - if (parent->child == np) - parent->child = np->sibling; - else { - struct device_node *prevsib; - for (prevsib = np->parent->child; - prevsib->sibling != np; - prevsib = prevsib->sibling) - ; - prevsib->sibling = np->sibling; - } - - of_node_set_flag(np, OF_DETACHED); - raw_spin_unlock_irqrestore(&devtree_lock, flags); - - of_node_remove(np); - return rc; -} -#endif /* defined(CONFIG_OF_DYNAMIC) */ - static void of_alias_add(struct alias_prop *ap, struct device_node *np, int id, const char *stem, int stem_len) { diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c new file mode 100644 index 0000000..1259943 --- /dev/null +++ b/drivers/of/dynamic.c @@ -0,0 +1,216 @@ +/* + * Support for dynamic device trees. + * + * On some platforms, the device tree can be manipulated at runtime. + * The routines in this section support adding, removing and changing + * device tree nodes. + */ + +#include +#include +#include +#include +#include + +#include "of_private.h" + +/** + * of_node_get() - Increment refcount of a node + * @node: Node to inc refcount, NULL is supported to simplify writing of + * callers + * + * Returns node. + */ +struct device_node *of_node_get(struct device_node *node) +{ + if (node) + kobject_get(&node->kobj); + return node; +} +EXPORT_SYMBOL(of_node_get); + +/** + * of_node_put() - Decrement refcount of a node + * @node: Node to dec refcount, NULL is supported to simplify writing of + * callers + */ +void of_node_put(struct device_node *node) +{ + if (node) + kobject_put(&node->kobj); +} +EXPORT_SYMBOL(of_node_put); + +static void of_node_remove(struct device_node *np) +{ + struct property *pp; + + BUG_ON(!of_node_is_initialized(np)); + + /* only remove properties if on sysfs */ + if (of_node_is_attached(np)) { + for_each_property_of_node(np, pp) + sysfs_remove_bin_file(&np->kobj, &pp->attr); + kobject_del(&np->kobj); + } + + /* finally remove the kobj_init ref */ + of_node_put(np); +} + +static BLOCKING_NOTIFIER_HEAD(of_reconfig_chain); + +int of_reconfig_notifier_register(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&of_reconfig_chain, nb); +} +EXPORT_SYMBOL_GPL(of_reconfig_notifier_register); + +int of_reconfig_notifier_unregister(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&of_reconfig_chain, nb); +} +EXPORT_SYMBOL_GPL(of_reconfig_notifier_unregister); + +int of_reconfig_notify(unsigned long action, void *p) +{ + int rc; + + rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p); + return notifier_to_errno(rc); +} + +int of_property_notify(int action, struct device_node *np, + struct property *prop) +{ + struct of_prop_reconfig pr; + + /* only call notifiers if the node is attached */ + if (!of_node_is_attached(np)) + return 0; + + pr.dn = np; + pr.prop = prop; + return of_reconfig_notify(action, &pr); +} + +/** + * of_attach_node() - Plug a device node into the tree and global list. + */ +int of_attach_node(struct device_node *np) +{ + unsigned long flags; + int rc; + + rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); + if (rc) + return rc; + + raw_spin_lock_irqsave(&devtree_lock, flags); + np->sibling = np->parent->child; + np->allnext = np->parent->allnext; + np->parent->allnext = np; + np->parent->child = np; + of_node_clear_flag(np, OF_DETACHED); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + of_node_add(np); + return 0; +} + +/** + * of_detach_node() - "Unplug" a node from the device tree. + * + * The caller must hold a reference to the node. The memory associated with + * the node is not freed until its refcount goes to zero. + */ +int of_detach_node(struct device_node *np) +{ + struct device_node *parent; + unsigned long flags; + int rc = 0; + + rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); + if (rc) + return rc; + + raw_spin_lock_irqsave(&devtree_lock, flags); + + if (of_node_check_flag(np, OF_DETACHED)) { + /* someone already detached it */ + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return rc; + } + + parent = np->parent; + if (!parent) { + raw_spin_unlock_irqrestore(&devtree_lock, flags); + return rc; + } + + if (of_allnodes == np) + of_allnodes = np->allnext; + else { + struct device_node *prev; + for (prev = of_allnodes; + prev->allnext != np; + prev = prev->allnext) + ; + prev->allnext = np->allnext; + } + + if (parent->child == np) + parent->child = np->sibling; + else { + struct device_node *prevsib; + for (prevsib = np->parent->child; + prevsib->sibling != np; + prevsib = prevsib->sibling) + ; + prevsib->sibling = np->sibling; + } + + of_node_set_flag(np, OF_DETACHED); + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + of_node_remove(np); + return rc; +} + +/** + * of_node_release() - release a dynamically allocated node + * @kref: kref element of the node to be released + * + * In of_node_put() this function is passed to kref_put() as the destructor. + */ +void of_node_release(struct kobject *kobj) +{ + struct device_node *node = kobj_to_device_node(kobj); + struct property *prop = node->properties; + + /* We should never be releasing nodes that haven't been detached. */ + if (!of_node_check_flag(node, OF_DETACHED)) { + pr_err("ERROR: Bad of_node_put() on %s\n", node->full_name); + dump_stack(); + return; + } + + if (!of_node_check_flag(node, OF_DYNAMIC)) + return; + + while (prop) { + struct property *next = prop->next; + kfree(prop->name); + kfree(prop->value); + kfree(prop); + prop = next; + + if (!prop) { + prop = node->deadprops; + node->deadprops = NULL; + } + } + kfree(node->full_name); + kfree(node->data); + kfree(node); +} diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index fcc70e7..c270f20 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -33,4 +33,22 @@ struct alias_prop { extern struct mutex of_mutex; extern struct list_head aliases_lookup; + +static inline struct device_node *kobj_to_device_node(struct kobject *kobj) +{ + return container_of(kobj, struct device_node, kobj); +} + +#if defined(CONFIG_OF_DYNAMIC) +extern int of_property_notify(int action, struct device_node *np, + struct property *prop); +extern void of_node_release(struct kobject *kobj); +#else /* CONFIG_OF_DYNAMIC */ +static inline int of_property_notify(int action, struct device_node *np, + struct property *prop) +{ + return 0; +} +#endif /* CONFIG_OF_DYNAMIC */ + #endif /* _LINUX_OF_PRIVATE_H */ -- cgit v0.10.2 From 698433963b98d6de7b102c242805c99fda4fa1fb Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 4 Jul 2014 19:58:47 +0300 Subject: OF: Utility helper functions for dynamic nodes Introduce helper functions for working with the live DT tree, all of them related to dynamically adding/removing nodes and properties. __of_prop_dup() copies a property dynamically __of_node_alloc() creates an empty node Bug fix about prop->len == 0 by Ionut Nicu Signed-off-by: Pantelis Antoniou [glikely: Added unittest for of_copy_property and dropped fine-grained allocations] [glikely: removed name, type and phandle arguments from __of_node_alloc] Signed-off-by: Grant Likely diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 1259943..e0c4c6e 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -214,3 +214,80 @@ void of_node_release(struct kobject *kobj) kfree(node->data); kfree(node); } + +/** + * __of_prop_dup - Copy a property dynamically. + * @prop: Property to copy + * @allocflags: Allocation flags (typically pass GFP_KERNEL) + * + * Copy a property by dynamically allocating the memory of both the + * property stucture and the property name & contents. The property's + * flags have the OF_DYNAMIC bit set so that we can differentiate between + * dynamically allocated properties and not. + * Returns the newly allocated property or NULL on out of memory error. + */ +struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags) +{ + struct property *new; + + new = kzalloc(sizeof(*new), allocflags); + if (!new) + return NULL; + + /* + * NOTE: There is no check for zero length value. + * In case of a boolean property This will allocate a value + * of zero bytes. We do this to work around the use + * of of_get_property() calls on boolean values. + */ + new->name = kstrdup(prop->name, allocflags); + new->value = kmemdup(prop->value, prop->length, allocflags); + new->length = prop->length; + if (!new->name || !new->value) + goto err_free; + + /* mark the property as dynamic */ + of_property_set_flag(new, OF_DYNAMIC); + + return new; + + err_free: + kfree(new->name); + kfree(new->value); + kfree(new); + return NULL; +} + +/** + * __of_node_alloc() - Create an empty device node dynamically. + * @full_name: Full name of the new device node + * @allocflags: Allocation flags (typically pass GFP_KERNEL) + * + * Create an empty device tree node, suitable for further modification. + * The node data are dynamically allocated and all the node flags + * have the OF_DYNAMIC & OF_DETACHED bits set. + * Returns the newly allocated node or NULL on out of memory error. + */ +struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags) +{ + struct device_node *node; + + node = kzalloc(sizeof(*node), allocflags); + if (!node) + return NULL; + + node->full_name = kstrdup(full_name, allocflags); + of_node_set_flag(node, OF_DYNAMIC); + of_node_set_flag(node, OF_DETACHED); + if (!node->full_name) + goto err_free; + + of_node_init(node); + + return node; + + err_free: + kfree(node->full_name); + kfree(node); + return NULL; +} diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index c270f20..1799ed2 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -51,4 +51,14 @@ static inline int of_property_notify(int action, struct device_node *np, } #endif /* CONFIG_OF_DYNAMIC */ +/** + * General utilities for working with live trees. + * + * All functions with two leading underscores operate + * without taking node references, so you either have to + * own the devtree lock or work on detached trees only. + */ +struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags); +struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags); + #endif /* _LINUX_OF_PRIVATE_H */ diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 077314e..ee2166f 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -16,6 +16,8 @@ #include #include +#include "of_private.h" + static struct selftest_results { int passed; int failed; @@ -266,6 +268,31 @@ static void __init of_selftest_property_match_string(void) selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc); } +#define propcmp(p1, p2) (((p1)->length == (p2)->length) && \ + (p1)->value && (p2)->value && \ + !memcmp((p1)->value, (p2)->value, (p1)->length) && \ + !strcmp((p1)->name, (p2)->name)) +static void __init of_selftest_property_copy(void) +{ +#ifdef CONFIG_OF_DYNAMIC + struct property p1 = { .name = "p1", .length = 0, .value = "" }; + struct property p2 = { .name = "p2", .length = 5, .value = "abcd" }; + struct property *new; + + new = __of_prop_dup(&p1, GFP_KERNEL); + selftest(new && propcmp(&p1, new), "empty property didn't copy correctly\n"); + kfree(new->value); + kfree(new->name); + kfree(new); + + new = __of_prop_dup(&p2, GFP_KERNEL); + selftest(new && propcmp(&p2, new), "non-empty property didn't copy correctly\n"); + kfree(new->value); + kfree(new->name); + kfree(new); +#endif +} + static void __init of_selftest_parse_interrupts(void) { struct device_node *np; @@ -533,6 +560,7 @@ static int __init of_selftest(void) of_selftest_dynamic(); of_selftest_parse_phandle_with_args(); of_selftest_property_match_string(); + of_selftest_property_copy(); of_selftest_parse_interrupts(); of_selftest_parse_interrupts_extended(); of_selftest_match_node(); -- cgit v0.10.2 From d8c50088417ebf61ad8b132caad20d10f7736034 Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 4 Jul 2014 19:58:46 +0300 Subject: of: Create unlocked versions of node and property add/remove functions The DT overlay code will need to manipulate nodes and properties while already holding the devicetree lock, or on nodes that are not yet attached to the tree, but the current helper functions don't allow that. Extract the core behaviour from the accessors and create the following unlocked variants. The unlocked variants require either the lock to already be held or for the nodes to be detached from the tree. Changes to live nodes will not get updated in sysfs, so the caller must arrange for housekeeping to take place after dropping the lock. The new functions are: __of_add_property(), __of_remove_property(), __of_update_property(), __of_attach_node() and __of_detach_node(). Signed-off-by: Pantelis Antoniou [Remove unnecessary diff hunks and rewrite commit text] Signed-off-by: Grant Likely diff --git a/drivers/of/base.c b/drivers/of/base.c index 0d89556..b403f9d 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1659,7 +1659,7 @@ EXPORT_SYMBOL(of_count_phandle_with_args); /** * __of_add_property - Add a property to a node without lock operations */ -static int __of_add_property(struct device_node *np, struct property *prop) +int __of_add_property(struct device_node *np, struct property *prop) { struct property **next; @@ -1701,6 +1701,25 @@ int of_add_property(struct device_node *np, struct property *prop) return rc; } +int __of_remove_property(struct device_node *np, struct property *prop) +{ + struct property **next; + + for (next = &np->properties; *next; next = &(*next)->next) { + if (*next == prop) + break; + } + if (*next == NULL) + return -ENODEV; + + /* found the node */ + *next = prop->next; + prop->next = np->deadprops; + np->deadprops = prop; + + return 0; +} + /** * of_remove_property - Remove a property from a node. * @@ -1711,9 +1730,7 @@ int of_add_property(struct device_node *np, struct property *prop) */ int of_remove_property(struct device_node *np, struct property *prop) { - struct property **next; unsigned long flags; - int found = 0; int rc; rc = of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop); @@ -1721,22 +1738,11 @@ int of_remove_property(struct device_node *np, struct property *prop) return rc; raw_spin_lock_irqsave(&devtree_lock, flags); - next = &np->properties; - while (*next) { - if (*next == prop) { - /* found the node */ - *next = prop->next; - prop->next = np->deadprops; - np->deadprops = prop; - found = 1; - break; - } - next = &(*next)->next; - } + rc = __of_remove_property(np, prop); raw_spin_unlock_irqrestore(&devtree_lock, flags); - if (!found) - return -ENODEV; + if (rc) + return rc; /* at early boot, bail hear and defer setup to of_init() */ if (!of_kset) @@ -1747,6 +1753,32 @@ int of_remove_property(struct device_node *np, struct property *prop) return 0; } +int __of_update_property(struct device_node *np, struct property *newprop, + struct property **oldpropp) +{ + struct property **next, *oldprop; + + for (next = &np->properties; *next; next = &(*next)->next) { + if (of_prop_cmp((*next)->name, newprop->name) == 0) + break; + } + *oldpropp = oldprop = *next; + + if (oldprop) { + /* replace the node */ + newprop->next = oldprop->next; + *next = newprop; + oldprop->next = np->deadprops; + np->deadprops = oldprop; + } else { + /* new node */ + newprop->next = NULL; + *next = newprop; + } + + return 0; +} + /* * of_update_property - Update a property in a node, if the property does * not exist, add it. @@ -1758,34 +1790,19 @@ int of_remove_property(struct device_node *np, struct property *prop) */ int of_update_property(struct device_node *np, struct property *newprop) { - struct property **next, *oldprop; + struct property *oldprop; unsigned long flags; int rc; + if (!newprop->name) + return -EINVAL; + rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop); if (rc) return rc; - if (!newprop->name) - return -EINVAL; - raw_spin_lock_irqsave(&devtree_lock, flags); - next = &np->properties; - oldprop = __of_find_property(np, newprop->name, NULL); - if (!oldprop) { - /* add the new node */ - rc = __of_add_property(np, newprop); - } else while (*next) { - /* replace the node */ - if (*next == oldprop) { - newprop->next = oldprop->next; - *next = newprop; - oldprop->next = np->deadprops; - np->deadprops = oldprop; - break; - } - next = &(*next)->next; - } + rc = __of_update_property(np, newprop, &oldprop); raw_spin_unlock_irqrestore(&devtree_lock, flags); if (rc) return rc; diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index e0c4c6e..75fcc66 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -94,6 +94,15 @@ int of_property_notify(int action, struct device_node *np, return of_reconfig_notify(action, &pr); } +void __of_attach_node(struct device_node *np) +{ + np->sibling = np->parent->child; + np->allnext = np->parent->allnext; + np->parent->allnext = np; + np->parent->child = np; + of_node_clear_flag(np, OF_DETACHED); +} + /** * of_attach_node() - Plug a device node into the tree and global list. */ @@ -107,46 +116,23 @@ int of_attach_node(struct device_node *np) return rc; raw_spin_lock_irqsave(&devtree_lock, flags); - np->sibling = np->parent->child; - np->allnext = np->parent->allnext; - np->parent->allnext = np; - np->parent->child = np; - of_node_clear_flag(np, OF_DETACHED); + __of_attach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); of_node_add(np); return 0; } -/** - * of_detach_node() - "Unplug" a node from the device tree. - * - * The caller must hold a reference to the node. The memory associated with - * the node is not freed until its refcount goes to zero. - */ -int of_detach_node(struct device_node *np) +void __of_detach_node(struct device_node *np) { struct device_node *parent; - unsigned long flags; - int rc = 0; - - rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); - if (rc) - return rc; - raw_spin_lock_irqsave(&devtree_lock, flags); - - if (of_node_check_flag(np, OF_DETACHED)) { - /* someone already detached it */ - raw_spin_unlock_irqrestore(&devtree_lock, flags); - return rc; - } + if (WARN_ON(of_node_check_flag(np, OF_DETACHED))) + return; parent = np->parent; - if (!parent) { - raw_spin_unlock_irqrestore(&devtree_lock, flags); - return rc; - } + if (WARN_ON(!parent)) + return; if (of_allnodes == np) of_allnodes = np->allnext; @@ -171,6 +157,25 @@ int of_detach_node(struct device_node *np) } of_node_set_flag(np, OF_DETACHED); +} + +/** + * of_detach_node() - "Unplug" a node from the device tree. + * + * The caller must hold a reference to the node. The memory associated with + * the node is not freed until its refcount goes to zero. + */ +int of_detach_node(struct device_node *np) +{ + unsigned long flags; + int rc = 0; + + rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); + if (rc) + return rc; + + raw_spin_lock_irqsave(&devtree_lock, flags); + __of_detach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); of_node_remove(np); diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 1799ed2..0f60897 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -61,4 +61,12 @@ static inline int of_property_notify(int action, struct device_node *np, struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags); struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags); +extern int __of_add_property(struct device_node *np, struct property *prop); +extern int __of_remove_property(struct device_node *np, struct property *prop); +extern int __of_update_property(struct device_node *np, + struct property *newprop, struct property **oldprop); + +extern void __of_attach_node(struct device_node *np); +extern void __of_detach_node(struct device_node *np); + #endif /* _LINUX_OF_PRIVATE_H */ -- cgit v0.10.2 From 8a2b22a2595bf89d4396530edf8388159fad9d83 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 23 Jul 2014 17:05:06 -0600 Subject: of: Make devicetree sysfs update functions consistent. All of the DT modification functions are split into two parts, the first part manipulates the DT data structure, and the second part updates sysfs, but the code isn't very consistent about how the second half is called. They don't all enforce the same rules about when it is valid to update sysfs, and there isn't any clarity on locking. The transactional DT modification feature that is coming also needs access to these functions so that it can perform all the structure changes together, and then all the sysfs updates as a second stage instead of doing each one at a time. Fix up the second have by creating a separate __of_*_sysfs() function for each of the helpers. The new functions have consistent naming (ie. of_node_add() becomes __of_attach_node_sysfs()) and all of them now defer if of_init hasn't been called yet. Callers of the new functions must hold the of_mutex to ensure there are no race conditions with of_init(). The mutex ensures that there will only ever be one writer to the tree at any given time. There can still be any number of readers and the raw_spin_lock is still used to make sure access to the data structure is still consistent. Finally, put the function prototypes into of_private.h so they are accessible to the transaction code. Signed-off-by: Pantelis Antoniou [grant.likely: Changed suffix from _post to _sysfs to match existing code] [grant.likely: Reorganized to eliminate trivial wrappers] Signed-off-by: Grant Likely diff --git a/drivers/of/base.c b/drivers/of/base.c index b403f9d..ad4929c 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -37,10 +37,13 @@ struct device_node *of_chosen; struct device_node *of_aliases; static struct device_node *of_stdout; -static struct kset *of_kset; +struct kset *of_kset; /* - * Used to protect the of_aliases, to hold off addition of nodes to sysfs + * Used to protect the of_aliases, to hold off addition of nodes to sysfs. + * This mutex must be held whenever modifications are being made to the + * device tree. The of_{attach,detach}_node() and + * of_{add,remove,update}_property() helpers make sure this happens. */ DEFINE_MUTEX(of_mutex); @@ -127,13 +130,16 @@ static const char *safe_name(struct kobject *kobj, const char *orig_name) return name; } -static int __of_add_property_sysfs(struct device_node *np, struct property *pp) +int __of_add_property_sysfs(struct device_node *np, struct property *pp) { int rc; /* Important: Don't leak passwords */ bool secure = strncmp(pp->name, "security-", 9) == 0; + if (!of_kset || !of_node_is_attached(np)) + return 0; + sysfs_bin_attr_init(&pp->attr); pp->attr.attr.name = safe_name(&np->kobj, pp->name); pp->attr.attr.mode = secure ? S_IRUSR : S_IRUGO; @@ -145,12 +151,15 @@ static int __of_add_property_sysfs(struct device_node *np, struct property *pp) return rc; } -static int __of_node_add(struct device_node *np) +int __of_attach_node_sysfs(struct device_node *np) { const char *name; struct property *pp; int rc; + if (!of_kset) + return 0; + np->kobj.kset = of_kset; if (!np->parent) { /* Nodes without parents are new top level trees */ @@ -172,26 +181,6 @@ static int __of_node_add(struct device_node *np) return 0; } -int of_node_add(struct device_node *np) -{ - int rc = 0; - - BUG_ON(!of_node_is_initialized(np)); - - /* - * Grab the mutex here so that in a race condition between of_init() and - * of_node_add(), node addition will still be consistent. - */ - mutex_lock(&of_mutex); - if (of_kset) - rc = __of_node_add(np); - else - /* This scenario may be perfectly valid, but report it anyway */ - pr_info("of_node_add(%s) before of_init()\n", np->full_name); - mutex_unlock(&of_mutex); - return rc; -} - static int __init of_init(void) { struct device_node *np; @@ -204,7 +193,7 @@ static int __init of_init(void) return -ENOMEM; } for_each_of_allnodes(np) - __of_node_add(np); + __of_attach_node_sysfs(np); mutex_unlock(&of_mutex); /* Symlink in /proc as required by userspace ABI */ @@ -1689,15 +1678,17 @@ int of_add_property(struct device_node *np, struct property *prop) if (rc) return rc; + mutex_lock(&of_mutex); + raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_add_property(np, prop); raw_spin_unlock_irqrestore(&devtree_lock, flags); - if (rc) - return rc; - if (of_node_is_attached(np)) + if (!rc) __of_add_property_sysfs(np, prop); + mutex_unlock(&of_mutex); + return rc; } @@ -1720,6 +1711,13 @@ int __of_remove_property(struct device_node *np, struct property *prop) return 0; } +void __of_remove_property_sysfs(struct device_node *np, struct property *prop) +{ + /* at early boot, bail here and defer setup to of_init() */ + if (of_kset && of_node_is_attached(np)) + sysfs_remove_bin_file(&np->kobj, &prop->attr); +} + /** * of_remove_property - Remove a property from a node. * @@ -1737,20 +1735,18 @@ int of_remove_property(struct device_node *np, struct property *prop) if (rc) return rc; + mutex_lock(&of_mutex); + raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_remove_property(np, prop); raw_spin_unlock_irqrestore(&devtree_lock, flags); - if (rc) - return rc; - - /* at early boot, bail hear and defer setup to of_init() */ - if (!of_kset) - return 0; + if (!rc) + __of_remove_property_sysfs(np, prop); - sysfs_remove_bin_file(&np->kobj, &prop->attr); + mutex_unlock(&of_mutex); - return 0; + return rc; } int __of_update_property(struct device_node *np, struct property *newprop, @@ -1779,6 +1775,18 @@ int __of_update_property(struct device_node *np, struct property *newprop, return 0; } +void __of_update_property_sysfs(struct device_node *np, struct property *newprop, + struct property *oldprop) +{ + /* At early boot, bail out and defer setup to of_init() */ + if (!of_kset) + return; + + if (oldprop) + sysfs_remove_bin_file(&np->kobj, &oldprop->attr); + __of_add_property_sysfs(np, newprop); +} + /* * of_update_property - Update a property in a node, if the property does * not exist, add it. @@ -1801,22 +1809,18 @@ int of_update_property(struct device_node *np, struct property *newprop) if (rc) return rc; + mutex_lock(&of_mutex); + raw_spin_lock_irqsave(&devtree_lock, flags); rc = __of_update_property(np, newprop, &oldprop); raw_spin_unlock_irqrestore(&devtree_lock, flags); - if (rc) - return rc; - /* At early boot, bail out and defer setup to of_init() */ - if (!of_kset) - return 0; + if (!rc) + __of_update_property_sysfs(np, newprop, oldprop); - /* Update the sysfs attribute */ - if (oldprop) - sysfs_remove_bin_file(&np->kobj, &oldprop->attr); - __of_add_property_sysfs(np, newprop); + mutex_unlock(&of_mutex); - return 0; + return rc; } static void of_alias_add(struct alias_prop *ap, struct device_node *np, diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 75fcc66..c875787 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -41,11 +41,13 @@ void of_node_put(struct device_node *node) } EXPORT_SYMBOL(of_node_put); -static void of_node_remove(struct device_node *np) +void __of_detach_node_sysfs(struct device_node *np) { struct property *pp; BUG_ON(!of_node_is_initialized(np)); + if (!of_kset) + return; /* only remove properties if on sysfs */ if (of_node_is_attached(np)) { @@ -115,11 +117,13 @@ int of_attach_node(struct device_node *np) if (rc) return rc; + mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); __of_attach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); - of_node_add(np); + __of_attach_node_sysfs(np); + mutex_unlock(&of_mutex); return 0; } @@ -174,11 +178,13 @@ int of_detach_node(struct device_node *np) if (rc) return rc; + mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); __of_detach_node(np); raw_spin_unlock_irqrestore(&devtree_lock, flags); - of_node_remove(np); + __of_detach_node_sysfs(np); + mutex_unlock(&of_mutex); return rc; } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 0f60897..0d99ba8 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -33,6 +33,8 @@ struct alias_prop { extern struct mutex of_mutex; extern struct list_head aliases_lookup; +extern struct kset *of_kset; + static inline struct device_node *kobj_to_device_node(struct kobject *kobj) { @@ -62,11 +64,19 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags); struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags); extern int __of_add_property(struct device_node *np, struct property *prop); +extern int __of_add_property_sysfs(struct device_node *np, + struct property *prop); extern int __of_remove_property(struct device_node *np, struct property *prop); +extern void __of_remove_property_sysfs(struct device_node *np, + struct property *prop); extern int __of_update_property(struct device_node *np, struct property *newprop, struct property **oldprop); +extern void __of_update_property_sysfs(struct device_node *np, + struct property *newprop, struct property *oldprop); extern void __of_attach_node(struct device_node *np); +extern int __of_attach_node_sysfs(struct device_node *np); extern void __of_detach_node(struct device_node *np); +extern void __of_detach_node_sysfs(struct device_node *np); #endif /* _LINUX_OF_PRIVATE_H */ diff --git a/include/linux/of.h b/include/linux/of.h index abf829a..705fa12 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -74,8 +74,6 @@ struct of_phandle_args { uint32_t args[MAX_PHANDLE_ARGS]; }; -extern int of_node_add(struct device_node *node); - /* initialize a node */ extern struct kobj_type of_node_ktype; static inline void of_node_init(struct device_node *node) -- cgit v0.10.2 From 6162dbe49a451f96431a23b4821f05e3bd925bc1 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 16 Jul 2014 08:48:46 -0600 Subject: of: Make sure attached nodes don't carry along extra children The child pointer does not get cleared when attaching new nodes which could cause the tree to be inconsistent. Clear the child pointer in __of_attach_node() to be absolutely sure that the structure remains in a consistent layout. Signed-off-by: Grant Likely diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index c875787..b96d831 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -98,6 +98,7 @@ int of_property_notify(int action, struct device_node *np, void __of_attach_node(struct device_node *np) { + np->child = NULL; np->sibling = np->parent->child; np->allnext = np->parent->allnext; np->parent->allnext = np; -- cgit v0.10.2 From a25095d451ece23b1fef34474f3230100db7aa05 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Tue, 15 Jul 2014 23:25:43 -0600 Subject: of: Move dynamic node fixups out of powerpc and into common code PowerPC does an odd thing with dynamic nodes. It uses a notifier to catch new node additions and set some of the values like name and type. This makes no sense since that same code can be put directly into of_attach_node(). Besides, all dynamic node users need this, not just powerpc. Fix this problem by moving the logic out of arch/powerpc and into drivers/of/dynamic.c. It is also important to remove this notifier because we want to move the firing of notifiers from before the tree is modified to after so that the receiver gets a consistent view of the tree, but that is incompatible with notifiers that modify the node. Signed-off-by: Grant Likely Cc: Nathan Fontenot Cc: Benjamin Herrenschmidt diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index b694b07..9e8d8a8 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -821,76 +821,6 @@ int cpu_to_chip_id(int cpu) } EXPORT_SYMBOL(cpu_to_chip_id); -#ifdef CONFIG_PPC_PSERIES -/* - * Fix up the uninitialized fields in a new device node: - * name, type and pci-specific fields - */ - -static int of_finish_dynamic_node(struct device_node *node) -{ - struct device_node *parent = of_get_parent(node); - int err = 0; - const phandle *ibm_phandle; - - node->name = of_get_property(node, "name", NULL); - node->type = of_get_property(node, "device_type", NULL); - - if (!node->name) - node->name = ""; - if (!node->type) - node->type = ""; - - if (!parent) { - err = -ENODEV; - goto out; - } - - /* We don't support that function on PowerMac, at least - * not yet - */ - if (machine_is(powermac)) - return -ENODEV; - - /* fix up new node's phandle field */ - if ((ibm_phandle = of_get_property(node, "ibm,phandle", NULL))) - node->phandle = *ibm_phandle; - -out: - of_node_put(parent); - return err; -} - -static int prom_reconfig_notifier(struct notifier_block *nb, - unsigned long action, void *node) -{ - int err; - - switch (action) { - case OF_RECONFIG_ATTACH_NODE: - err = of_finish_dynamic_node(node); - if (err < 0) - printk(KERN_ERR "finish_node returned %d\n", err); - break; - default: - err = 0; - break; - } - return notifier_from_errno(err); -} - -static struct notifier_block prom_reconfig_nb = { - .notifier_call = prom_reconfig_notifier, - .priority = 10, /* This one needs to run first */ -}; - -static int __init prom_reconfig_setup(void) -{ - return of_reconfig_notifier_register(&prom_reconfig_nb); -} -__initcall(prom_reconfig_setup); -#endif - bool arch_match_cpu_phys_id(int cpu, u64 phys_id) { return (int)phys_id == get_hard_smp_processor_id(cpu); diff --git a/drivers/of/base.c b/drivers/of/base.c index ad4929c..ededf8e 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -266,8 +266,8 @@ EXPORT_SYMBOL(of_find_all_nodes); * Find a property with a given name for a given node * and return the value. */ -static const void *__of_get_property(const struct device_node *np, - const char *name, int *lenp) +const void *__of_get_property(const struct device_node *np, + const char *name, int *lenp) { struct property *pp = __of_find_property(np, name, lenp); diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index b96d831..7c020b9 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -98,6 +98,19 @@ int of_property_notify(int action, struct device_node *np, void __of_attach_node(struct device_node *np) { + const __be32 *phandle; + int sz; + + np->name = __of_get_property(np, "name", NULL) ? : ""; + np->type = __of_get_property(np, "device_type", NULL) ? : ""; + + phandle = __of_get_property(np, "phandle", &sz); + if (!phandle) + phandle = __of_get_property(np, "linux,phandle", &sz); + if (IS_ENABLED(PPC_PSERIES) && !phandle) + phandle = __of_get_property(np, "ibm,phandle", &sz); + np->phandle = (phandle && (sz >= 4)) ? be32_to_cpup(phandle) : 0; + np->child = NULL; np->sibling = np->parent->child; np->allnext = np->parent->allnext; diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 0d99ba8..8129c0e 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -63,6 +63,8 @@ static inline int of_property_notify(int action, struct device_node *np, struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags); struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags); +extern const void *__of_get_property(const struct device_node *np, + const char *name, int *lenp); extern int __of_add_property(struct device_node *np, struct property *prop); extern int __of_add_property_sysfs(struct device_node *np, struct property *prop); -- cgit v0.10.2 From 259092a35c7e11f1d4616b0f5b3ba7b851fe4fa6 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Wed, 16 Jul 2014 12:48:23 -0600 Subject: of: Reorder device tree changes and notifiers Currently, devicetree reconfig notifiers get emitted before the change is applied to the tree, but that behaviour is problematic if the receiver wants the determine the new state of the tree. The current users don't care, but the changeset code to follow will be making multiple changes at once. Reorder notifiers to get emitted after the change has been applied to the tree so that callbacks see the new tree state. At the same time, fixup the existing callbacks to expect the new order. There are a few callbacks that compare the old and new values of a changed property. Put both property pointers into the of_prop_reconfig structure. The current notifiers also allow the notifier callback to fail and cancel the change to the tree, but that feature isn't actually used. It really isn't valid to ignore a tree modification provided by firmware anyway, so remove the ability to cancel a change to the tree. Signed-off-by: Grant Likely Cc: Nathan Fontenot diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c index 7995135..ac01e18 100644 --- a/arch/powerpc/platforms/pseries/hotplug-memory.c +++ b/arch/powerpc/platforms/pseries/hotplug-memory.c @@ -194,7 +194,7 @@ static int pseries_update_drconf_memory(struct of_prop_reconfig *pr) if (!memblock_size) return -EINVAL; - p = (u32 *)of_get_property(pr->dn, "ibm,dynamic-memory", NULL); + p = (u32 *) pr->old_prop->value; if (!p) return -EINVAL; diff --git a/drivers/crypto/nx/nx-842.c b/drivers/crypto/nx/nx-842.c index 502edf0..c897c3a 100644 --- a/drivers/crypto/nx/nx-842.c +++ b/drivers/crypto/nx/nx-842.c @@ -936,28 +936,14 @@ static int nx842_OF_upd(struct property *new_prop) goto error_out; } - /* Set ptr to new property if provided */ - if (new_prop) { - /* Single property */ - if (!strncmp(new_prop->name, "status", new_prop->length)) { - status = new_prop; - - } else if (!strncmp(new_prop->name, "ibm,max-sg-len", - new_prop->length)) { - maxsglen = new_prop; - - } else if (!strncmp(new_prop->name, "ibm,max-sync-cop", - new_prop->length)) { - maxsyncop = new_prop; - - } else { - /* - * Skip the update, the property being updated - * has no impact. - */ - goto out; - } - } + /* + * If this is a property update, there are only certain properties that + * we care about. Bail if it isn't in the below list + */ + if (new_prop && (strncmp(new_prop->name, "status", new_prop->length) || + strncmp(new_prop->name, "ibm,max-sg-len", new_prop->length) || + strncmp(new_prop->name, "ibm,max-sync-cop", new_prop->length))) + goto out; /* Perform property updates */ ret = nx842_OF_upd_status(new_devdata, status); diff --git a/drivers/of/base.c b/drivers/of/base.c index ededf8e..a7ad101 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1674,10 +1674,6 @@ int of_add_property(struct device_node *np, struct property *prop) unsigned long flags; int rc; - rc = of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop); - if (rc) - return rc; - mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); @@ -1689,6 +1685,9 @@ int of_add_property(struct device_node *np, struct property *prop) mutex_unlock(&of_mutex); + if (!rc) + of_property_notify(OF_RECONFIG_ADD_PROPERTY, np, prop, NULL); + return rc; } @@ -1731,10 +1730,6 @@ int of_remove_property(struct device_node *np, struct property *prop) unsigned long flags; int rc; - rc = of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop); - if (rc) - return rc; - mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); @@ -1746,6 +1741,9 @@ int of_remove_property(struct device_node *np, struct property *prop) mutex_unlock(&of_mutex); + if (!rc) + of_property_notify(OF_RECONFIG_REMOVE_PROPERTY, np, prop, NULL); + return rc; } @@ -1805,10 +1803,6 @@ int of_update_property(struct device_node *np, struct property *newprop) if (!newprop->name) return -EINVAL; - rc = of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop); - if (rc) - return rc; - mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); @@ -1820,6 +1814,9 @@ int of_update_property(struct device_node *np, struct property *newprop) mutex_unlock(&of_mutex); + if (!rc) + of_property_notify(OF_RECONFIG_UPDATE_PROPERTY, np, newprop, oldprop); + return rc; } diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 7c020b9..7bd5501 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -83,7 +83,7 @@ int of_reconfig_notify(unsigned long action, void *p) } int of_property_notify(int action, struct device_node *np, - struct property *prop) + struct property *prop, struct property *oldprop) { struct of_prop_reconfig pr; @@ -93,6 +93,7 @@ int of_property_notify(int action, struct device_node *np, pr.dn = np; pr.prop = prop; + pr.old_prop = oldprop; return of_reconfig_notify(action, &pr); } @@ -125,11 +126,6 @@ void __of_attach_node(struct device_node *np) int of_attach_node(struct device_node *np) { unsigned long flags; - int rc; - - rc = of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); - if (rc) - return rc; mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); @@ -138,6 +134,9 @@ int of_attach_node(struct device_node *np) __of_attach_node_sysfs(np); mutex_unlock(&of_mutex); + + of_reconfig_notify(OF_RECONFIG_ATTACH_NODE, np); + return 0; } @@ -188,10 +187,6 @@ int of_detach_node(struct device_node *np) unsigned long flags; int rc = 0; - rc = of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); - if (rc) - return rc; - mutex_lock(&of_mutex); raw_spin_lock_irqsave(&devtree_lock, flags); __of_detach_node(np); @@ -199,6 +194,9 @@ int of_detach_node(struct device_node *np) __of_detach_node_sysfs(np); mutex_unlock(&of_mutex); + + of_reconfig_notify(OF_RECONFIG_DETACH_NODE, np); + return rc; } diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index 8129c0e..f69ccb1 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -43,11 +43,11 @@ static inline struct device_node *kobj_to_device_node(struct kobject *kobj) #if defined(CONFIG_OF_DYNAMIC) extern int of_property_notify(int action, struct device_node *np, - struct property *prop); + struct property *prop, struct property *old_prop); extern void of_node_release(struct kobject *kobj); #else /* CONFIG_OF_DYNAMIC */ static inline int of_property_notify(int action, struct device_node *np, - struct property *prop) + struct property *prop, struct property *old_prop) { return 0; } diff --git a/include/linux/of.h b/include/linux/of.h index 705fa12..400f18c 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -321,6 +321,7 @@ extern int of_update_property(struct device_node *np, struct property *newprop); struct of_prop_reconfig { struct device_node *dn; struct property *prop; + struct property *old_prop; }; extern int of_reconfig_notifier_register(struct notifier_block *); -- cgit v0.10.2 From 201c910bd6898d81d4ac6685d0f421b7e10f3c5d Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 4 Jul 2014 19:58:49 +0300 Subject: of: Transactional DT support. Introducing DT transactional support. A DT transaction is a method which allows one to apply changes in the live tree, in such a way that either the full set of changes take effect, or the state of the tree can be rolled-back to the state it was before it was attempted. An applied transaction can be rolled-back at any time. Documentation is in Documentation/devicetree/changesets.txt Signed-off-by: Pantelis Antoniou [glikely: Removed device notifiers and reworked to be more consistent] Signed-off-by: Grant Likely diff --git a/Documentation/devicetree/changesets.txt b/Documentation/devicetree/changesets.txt new file mode 100644 index 0000000..935ba5a --- /dev/null +++ b/Documentation/devicetree/changesets.txt @@ -0,0 +1,40 @@ +A DT changeset is a method which allows one to apply changes +in the live tree in such a way that either the full set of changes +will be applied, or none of them will be. If an error occurs partway +through applying the changeset, then the tree will be rolled back to the +previous state. A changeset can also be removed after it has been +applied. + +When a changeset is applied, all of the changes get applied to the tree +at once before emitting OF_RECONFIG notifiers. This is so that the +receiver sees a complete and consistent state of the tree when it +receives the notifier. + +The sequence of a changeset is as follows. + +1. of_changeset_init() - initializes a changeset + +2. A number of DT tree change calls, of_changeset_attach_node(), +of_changeset_detach_node(), of_changeset_add_property(), +of_changeset_remove_property, of_changeset_update_property() to prepare +a set of changes. No changes to the active tree are made at this point. +All the change operations are recorded in the of_changeset 'entries' +list. + +3. mutex_lock(of_mutex) - starts a changeset; The global of_mutex +ensures there can only be one editor at a time. + +4. of_changeset_apply() - Apply the changes to the tree. Either the +entire changeset will get applied, or if there is an error the tree will +be restored to the previous state + +5. mutex_unlock(of_mutex) - All operations complete, release the mutex + +If a successfully applied changeset needs to be removed, it can be done +with the following sequence. + +1. mutex_lock(of_mutex) + +2. of_changeset_revert() + +3. mutex_unlock(of_mutex) diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index 7bd5501..c1002b7 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -314,3 +314,347 @@ struct device_node *__of_node_alloc(const char *full_name, gfp_t allocflags) kfree(node); return NULL; } + +static void __of_changeset_entry_destroy(struct of_changeset_entry *ce) +{ + of_node_put(ce->np); + list_del(&ce->node); + kfree(ce); +} + +#ifdef DEBUG +static void __of_changeset_entry_dump(struct of_changeset_entry *ce) +{ + switch (ce->action) { + case OF_RECONFIG_ADD_PROPERTY: + pr_debug("%p: %s %s/%s\n", + ce, "ADD_PROPERTY ", ce->np->full_name, + ce->prop->name); + break; + case OF_RECONFIG_REMOVE_PROPERTY: + pr_debug("%p: %s %s/%s\n", + ce, "REMOVE_PROPERTY", ce->np->full_name, + ce->prop->name); + break; + case OF_RECONFIG_UPDATE_PROPERTY: + pr_debug("%p: %s %s/%s\n", + ce, "UPDATE_PROPERTY", ce->np->full_name, + ce->prop->name); + break; + case OF_RECONFIG_ATTACH_NODE: + pr_debug("%p: %s %s\n", + ce, "ATTACH_NODE ", ce->np->full_name); + break; + case OF_RECONFIG_DETACH_NODE: + pr_debug("%p: %s %s\n", + ce, "DETACH_NODE ", ce->np->full_name); + break; + } +} +#else +static inline void __of_changeset_entry_dump(struct of_changeset_entry *ce) +{ + /* empty */ +} +#endif + +static void __of_changeset_entry_invert(struct of_changeset_entry *ce, + struct of_changeset_entry *rce) +{ + memcpy(rce, ce, sizeof(*rce)); + + switch (ce->action) { + case OF_RECONFIG_ATTACH_NODE: + rce->action = OF_RECONFIG_DETACH_NODE; + break; + case OF_RECONFIG_DETACH_NODE: + rce->action = OF_RECONFIG_ATTACH_NODE; + break; + case OF_RECONFIG_ADD_PROPERTY: + rce->action = OF_RECONFIG_REMOVE_PROPERTY; + break; + case OF_RECONFIG_REMOVE_PROPERTY: + rce->action = OF_RECONFIG_ADD_PROPERTY; + break; + case OF_RECONFIG_UPDATE_PROPERTY: + rce->old_prop = ce->prop; + rce->prop = ce->old_prop; + break; + } +} + +static void __of_changeset_entry_notify(struct of_changeset_entry *ce, bool revert) +{ + struct of_changeset_entry ce_inverted; + int ret; + + if (revert) { + __of_changeset_entry_invert(ce, &ce_inverted); + ce = &ce_inverted; + } + + switch (ce->action) { + case OF_RECONFIG_ATTACH_NODE: + case OF_RECONFIG_DETACH_NODE: + ret = of_reconfig_notify(ce->action, ce->np); + break; + case OF_RECONFIG_ADD_PROPERTY: + case OF_RECONFIG_REMOVE_PROPERTY: + case OF_RECONFIG_UPDATE_PROPERTY: + ret = of_property_notify(ce->action, ce->np, ce->prop, ce->old_prop); + break; + default: + pr_err("%s: invalid devicetree changeset action: %i\n", __func__, + (int)ce->action); + return; + } + + if (ret) + pr_err("%s: notifier error @%s\n", __func__, ce->np->full_name); +} + +static int __of_changeset_entry_apply(struct of_changeset_entry *ce) +{ + struct property *old_prop, **propp; + unsigned long flags; + int ret = 0; + + __of_changeset_entry_dump(ce); + + raw_spin_lock_irqsave(&devtree_lock, flags); + switch (ce->action) { + case OF_RECONFIG_ATTACH_NODE: + __of_attach_node(ce->np); + break; + case OF_RECONFIG_DETACH_NODE: + __of_detach_node(ce->np); + break; + case OF_RECONFIG_ADD_PROPERTY: + /* If the property is in deadprops then it must be removed */ + for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { + if (*propp == ce->prop) { + *propp = ce->prop->next; + ce->prop->next = NULL; + break; + } + } + + ret = __of_add_property(ce->np, ce->prop); + if (ret) { + pr_err("%s: add_property failed @%s/%s\n", + __func__, ce->np->full_name, + ce->prop->name); + break; + } + break; + case OF_RECONFIG_REMOVE_PROPERTY: + ret = __of_remove_property(ce->np, ce->prop); + if (ret) { + pr_err("%s: remove_property failed @%s/%s\n", + __func__, ce->np->full_name, + ce->prop->name); + break; + } + break; + + case OF_RECONFIG_UPDATE_PROPERTY: + /* If the property is in deadprops then it must be removed */ + for (propp = &ce->np->deadprops; *propp; propp = &(*propp)->next) { + if (*propp == ce->prop) { + *propp = ce->prop->next; + ce->prop->next = NULL; + break; + } + } + + ret = __of_update_property(ce->np, ce->prop, &old_prop); + if (ret) { + pr_err("%s: update_property failed @%s/%s\n", + __func__, ce->np->full_name, + ce->prop->name); + break; + } + break; + default: + ret = -EINVAL; + } + raw_spin_unlock_irqrestore(&devtree_lock, flags); + + if (ret) + return ret; + + switch (ce->action) { + case OF_RECONFIG_ATTACH_NODE: + __of_attach_node_sysfs(ce->np); + break; + case OF_RECONFIG_DETACH_NODE: + __of_detach_node_sysfs(ce->np); + break; + case OF_RECONFIG_ADD_PROPERTY: + /* ignore duplicate names */ + __of_add_property_sysfs(ce->np, ce->prop); + break; + case OF_RECONFIG_REMOVE_PROPERTY: + __of_remove_property_sysfs(ce->np, ce->prop); + break; + case OF_RECONFIG_UPDATE_PROPERTY: + __of_update_property_sysfs(ce->np, ce->prop, ce->old_prop); + break; + } + + return 0; +} + +static inline int __of_changeset_entry_revert(struct of_changeset_entry *ce) +{ + struct of_changeset_entry ce_inverted; + + __of_changeset_entry_invert(ce, &ce_inverted); + return __of_changeset_entry_apply(&ce_inverted); +} + +/** + * of_changeset_init - Initialize a changeset for use + * + * @ocs: changeset pointer + * + * Initialize a changeset structure + */ +void of_changeset_init(struct of_changeset *ocs) +{ + memset(ocs, 0, sizeof(*ocs)); + INIT_LIST_HEAD(&ocs->entries); +} + +/** + * of_changeset_destroy - Destroy a changeset + * + * @ocs: changeset pointer + * + * Destroys a changeset. Note that if a changeset is applied, + * its changes to the tree cannot be reverted. + */ +void of_changeset_destroy(struct of_changeset *ocs) +{ + struct of_changeset_entry *ce, *cen; + + list_for_each_entry_safe_reverse(ce, cen, &ocs->entries, node) + __of_changeset_entry_destroy(ce); +} + +/** + * of_changeset_apply - Applies a changeset + * + * @ocs: changeset pointer + * + * Applies a changeset to the live tree. + * Any side-effects of live tree state changes are applied here on + * sucess, like creation/destruction of devices and side-effects + * like creation of sysfs properties and directories. + * Returns 0 on success, a negative error value in case of an error. + * On error the partially applied effects are reverted. + */ +int of_changeset_apply(struct of_changeset *ocs) +{ + struct of_changeset_entry *ce; + int ret; + + /* perform the rest of the work */ + pr_debug("of_changeset: applying...\n"); + list_for_each_entry(ce, &ocs->entries, node) { + ret = __of_changeset_entry_apply(ce); + if (ret) { + pr_err("%s: Error applying changeset (%d)\n", __func__, ret); + list_for_each_entry_continue_reverse(ce, &ocs->entries, node) + __of_changeset_entry_revert(ce); + return ret; + } + } + pr_debug("of_changeset: applied, emitting notifiers.\n"); + + /* drop the global lock while emitting notifiers */ + mutex_unlock(&of_mutex); + list_for_each_entry(ce, &ocs->entries, node) + __of_changeset_entry_notify(ce, 0); + mutex_lock(&of_mutex); + pr_debug("of_changeset: notifiers sent.\n"); + + return 0; +} + +/** + * of_changeset_revert - Reverts an applied changeset + * + * @ocs: changeset pointer + * + * Reverts a changeset returning the state of the tree to what it + * was before the application. + * Any side-effects like creation/destruction of devices and + * removal of sysfs properties and directories are applied. + * Returns 0 on success, a negative error value in case of an error. + */ +int of_changeset_revert(struct of_changeset *ocs) +{ + struct of_changeset_entry *ce; + int ret; + + pr_debug("of_changeset: reverting...\n"); + list_for_each_entry_reverse(ce, &ocs->entries, node) { + ret = __of_changeset_entry_revert(ce); + if (ret) { + pr_err("%s: Error reverting changeset (%d)\n", __func__, ret); + list_for_each_entry_continue(ce, &ocs->entries, node) + __of_changeset_entry_apply(ce); + return ret; + } + } + pr_debug("of_changeset: reverted, emitting notifiers.\n"); + + /* drop the global lock while emitting notifiers */ + mutex_unlock(&of_mutex); + list_for_each_entry_reverse(ce, &ocs->entries, node) + __of_changeset_entry_notify(ce, 1); + mutex_lock(&of_mutex); + pr_debug("of_changeset: notifiers sent.\n"); + + return 0; +} + +/** + * of_changeset_action - Perform a changeset action + * + * @ocs: changeset pointer + * @action: action to perform + * @np: Pointer to device node + * @prop: Pointer to property + * + * On action being one of: + * + OF_RECONFIG_ATTACH_NODE + * + OF_RECONFIG_DETACH_NODE, + * + OF_RECONFIG_ADD_PROPERTY + * + OF_RECONFIG_REMOVE_PROPERTY, + * + OF_RECONFIG_UPDATE_PROPERTY + * Returns 0 on success, a negative error value in case of an error. + */ +int of_changeset_action(struct of_changeset *ocs, unsigned long action, + struct device_node *np, struct property *prop) +{ + struct of_changeset_entry *ce; + + ce = kzalloc(sizeof(*ce), GFP_KERNEL); + if (!ce) { + pr_err("%s: Failed to allocate\n", __func__); + return -ENOMEM; + } + /* get a reference to the node */ + ce->action = action; + ce->np = of_node_get(np); + ce->prop = prop; + + if (action == OF_RECONFIG_UPDATE_PROPERTY && prop) + ce->old_prop = of_find_property(np, prop->name, NULL); + + /* add it to the list */ + list_add_tail(&ce->node, &ocs->entries); + return 0; +} diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h index f69ccb1..858e0a5 100644 --- a/drivers/of/of_private.h +++ b/drivers/of/of_private.h @@ -81,4 +81,13 @@ extern int __of_attach_node_sysfs(struct device_node *np); extern void __of_detach_node(struct device_node *np); extern void __of_detach_node_sysfs(struct device_node *np); +/* iterators for transactions, used for overlays */ +/* forward iterator */ +#define for_each_transaction_entry(_oft, _te) \ + list_for_each_entry(_te, &(_oft)->te_list, node) + +/* reverse iterator */ +#define for_each_transaction_entry_reverse(_oft, _te) \ + list_for_each_entry_reverse(_te, &(_oft)->te_list, node) + #endif /* _LINUX_OF_PRIVATE_H */ diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index ee2166f..04e39a1 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -293,6 +293,56 @@ static void __init of_selftest_property_copy(void) #endif } +static void __init of_selftest_changeset(void) +{ +#ifdef CONFIG_OF_DYNAMIC + struct property *ppadd, padd = { .name = "prop-add", .length = 0, .value = "" }; + struct property *ppupdate, pupdate = { .name = "prop-update", .length = 5, .value = "abcd" }; + struct property *ppremove; + struct device_node *n1, *n2, *n21, *nremove, *parent; + struct of_changeset chgset; + + of_changeset_init(&chgset); + n1 = __of_node_alloc("/testcase-data/changeset/n1", GFP_KERNEL); + selftest(n1, "testcase setup failure\n"); + n2 = __of_node_alloc("/testcase-data/changeset/n2", GFP_KERNEL); + selftest(n2, "testcase setup failure\n"); + n21 = __of_node_alloc("/testcase-data/changeset/n2/n21", GFP_KERNEL); + selftest(n21, "testcase setup failure %p\n", n21); + nremove = of_find_node_by_path("/testcase-data/changeset/node-remove"); + selftest(nremove, "testcase setup failure\n"); + ppadd = __of_prop_dup(&padd, GFP_KERNEL); + selftest(ppadd, "testcase setup failure\n"); + ppupdate = __of_prop_dup(&pupdate, GFP_KERNEL); + selftest(ppupdate, "testcase setup failure\n"); + parent = nremove->parent; + n1->parent = parent; + n2->parent = parent; + n21->parent = n2; + n2->child = n21; + ppremove = of_find_property(parent, "prop-remove", NULL); + selftest(ppremove, "failed to find removal prop"); + + of_changeset_init(&chgset); + selftest(!of_changeset_attach_node(&chgset, n1), "fail attach n1\n"); + selftest(!of_changeset_attach_node(&chgset, n2), "fail attach n2\n"); + selftest(!of_changeset_detach_node(&chgset, nremove), "fail remove node\n"); + selftest(!of_changeset_attach_node(&chgset, n21), "fail attach n21\n"); + selftest(!of_changeset_add_property(&chgset, parent, ppadd), "fail add prop\n"); + selftest(!of_changeset_update_property(&chgset, parent, ppupdate), "fail update prop\n"); + selftest(!of_changeset_remove_property(&chgset, parent, ppremove), "fail remove prop\n"); + mutex_lock(&of_mutex); + selftest(!of_changeset_apply(&chgset), "apply failed\n"); + mutex_unlock(&of_mutex); + + mutex_lock(&of_mutex); + selftest(!of_changeset_revert(&chgset), "revert failed\n"); + mutex_unlock(&of_mutex); + + of_changeset_destroy(&chgset); +#endif +} + static void __init of_selftest_parse_interrupts(void) { struct device_node *np; @@ -561,6 +611,7 @@ static int __init of_selftest(void) of_selftest_parse_phandle_with_args(); of_selftest_property_match_string(); of_selftest_property_copy(); + of_selftest_changeset(); of_selftest_parse_interrupts(); of_selftest_parse_interrupts_extended(); of_selftest_match_node(); diff --git a/drivers/of/testcase-data/testcases.dtsi b/drivers/of/testcase-data/testcases.dtsi index 6d8d980a..669bb07 100644 --- a/drivers/of/testcase-data/testcases.dtsi +++ b/drivers/of/testcase-data/testcases.dtsi @@ -1,3 +1,13 @@ +/ { + testcase-data { + changeset { + prop-update = "hello"; + prop-remove = "world"; + node-remove { + }; + }; + }; +}; #include "tests-phandle.dtsi" #include "tests-interrupts.dtsi" #include "tests-match.dtsi" diff --git a/include/linux/of.h b/include/linux/of.h index 400f18c..bc91fbb 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -786,4 +786,80 @@ typedef void (*of_init_fn_1)(struct device_node *); #define OF_DECLARE_2(table, name, compat, fn) \ _OF_DECLARE(table, name, compat, fn, of_init_fn_2) +/** + * struct of_changeset_entry - Holds a changeset entry + * + * @node: list_head for the log list + * @action: notifier action + * @np: pointer to the device node affected + * @prop: pointer to the property affected + * @old_prop: hold a pointer to the original property + * + * Every modification of the device tree during a changeset + * is held in a list of of_changeset_entry structures. + * That way we can recover from a partial application, or we can + * revert the changeset + */ +struct of_changeset_entry { + struct list_head node; + unsigned long action; + struct device_node *np; + struct property *prop; + struct property *old_prop; +}; + +/** + * struct of_changeset - changeset tracker structure + * + * @entries: list_head for the changeset entries + * + * changesets are a convenient way to apply bulk changes to the + * live tree. In case of an error, changes are rolled-back. + * changesets live on after initial application, and if not + * destroyed after use, they can be reverted in one single call. + */ +struct of_changeset { + struct list_head entries; +}; + +#ifdef CONFIG_OF_DYNAMIC +extern void of_changeset_init(struct of_changeset *ocs); +extern void of_changeset_destroy(struct of_changeset *ocs); +extern int of_changeset_apply(struct of_changeset *ocs); +extern int of_changeset_revert(struct of_changeset *ocs); +extern int of_changeset_action(struct of_changeset *ocs, + unsigned long action, struct device_node *np, + struct property *prop); + +static inline int of_changeset_attach_node(struct of_changeset *ocs, + struct device_node *np) +{ + return of_changeset_action(ocs, OF_RECONFIG_ATTACH_NODE, np, NULL); +} + +static inline int of_changeset_detach_node(struct of_changeset *ocs, + struct device_node *np) +{ + return of_changeset_action(ocs, OF_RECONFIG_DETACH_NODE, np, NULL); +} + +static inline int of_changeset_add_property(struct of_changeset *ocs, + struct device_node *np, struct property *prop) +{ + return of_changeset_action(ocs, OF_RECONFIG_ADD_PROPERTY, np, prop); +} + +static inline int of_changeset_remove_property(struct of_changeset *ocs, + struct device_node *np, struct property *prop) +{ + return of_changeset_action(ocs, OF_RECONFIG_REMOVE_PROPERTY, np, prop); +} + +static inline int of_changeset_update_property(struct of_changeset *ocs, + struct device_node *np, struct property *prop) +{ + return of_changeset_action(ocs, OF_RECONFIG_UPDATE_PROPERTY, np, prop); +} +#endif + #endif /* _LINUX_OF_H */ -- cgit v0.10.2 From 848007ba9eeae9436d988e3266f4e229ce71de87 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Thu, 26 Jun 2014 16:52:54 +0100 Subject: of: Add todo tasklist for Devicetree There are a number of outstanding workitems that need to be done on the device tree code. Start tracking them so that interested developers can pick up a task and start working on it. Signed-off-by: Grant Likely diff --git a/Documentation/devicetree/todo.txt b/Documentation/devicetree/todo.txt new file mode 100644 index 0000000..c3cf065 --- /dev/null +++ b/Documentation/devicetree/todo.txt @@ -0,0 +1,11 @@ +Todo list for devicetree: + +=== General structure === +- Switch from custom lists to (h)list_head for nodes and properties structure +- Remove of_allnodes list and iterate using list of child nodes alone + +=== CONFIG_OF_DYNAMIC === +- Switch to RCU for tree updates and get rid of global spinlock +- Document node lifecycle for CONFIG_OF_DYNAMIC +- Always set ->full_name at of_attach_node() time +- pseries: Get rid of open-coded tree modification from arch/powerpc/platforms/pseries/dlpar.c -- cgit v0.10.2 From ae9304c9d3111759fed5946272be3b13ba41f7e3 Mon Sep 17 00:00:00 2001 From: Gaurav Minocha Date: Wed, 16 Jul 2014 23:09:39 -0700 Subject: Adding selftest testdata dynamically into live tree This patch attaches selftest's device tree data (required by /drivers/of/selftest.c) dynamically into live device tree. First, it links selftest device tree data into the kernel image and then iterates over all the nodes and attaches them into the live tree. Once the testcases are complete, it removes the data attached. This patch will remove the manual process of addition and removal of selftest device tree data into the machine's dts file. Tested successfully with current selftest's testcases. Signed-off-by: Gaurav Minocha [glikely: Removed ability to build as a module and fixed no-devicetree bug] Signed-off-by: Grant Likely diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts index 65f6577..8d39677 100644 --- a/arch/arm/boot/dts/versatile-pb.dts +++ b/arch/arm/boot/dts/versatile-pb.dts @@ -46,5 +46,3 @@ }; }; }; - -#include diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 2dcb054..868f371 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -10,6 +10,7 @@ menu "Device Tree and Open Firmware support" config OF_SELFTEST bool "Device Tree Runtime self tests" depends on OF_IRQ + select OF_DYNAMIC help This option builds in test cases for the device tree infrastructure that are executed once at boot time, and the results dumped to the diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 099b1fb..b9e753b 100644 --- a/drivers/of/Makefile +++ b/drivers/of/Makefile @@ -5,7 +5,8 @@ obj-$(CONFIG_OF_PROMTREE) += pdt.o obj-$(CONFIG_OF_ADDRESS) += address.o obj-$(CONFIG_OF_IRQ) += irq.o obj-$(CONFIG_OF_NET) += of_net.o -obj-$(CONFIG_OF_SELFTEST) += selftest.o +obj-$(CONFIG_OF_SELFTEST) += of_selftest.o +of_selftest-objs := selftest.o testcase-data/testcases.dtb.o obj-$(CONFIG_OF_MDIO) += of_mdio.o obj-$(CONFIG_OF_PCI) += of_pci.o obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 077314e..df599db 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,10 @@ static struct selftest_results { int failed; } selftest_results; +#define NO_OF_NODES 2 +static struct device_node *nodes[NO_OF_NODES]; +static int last_node_index; + #define selftest(result, fmt, ...) { \ if (!(result)) { \ selftest_results.failed++; \ @@ -517,9 +522,156 @@ static void __init of_selftest_platform_populate(void) } } +/** + * update_node_properties - adds the properties + * of np into dup node (present in live tree) and + * updates parent of children of np to dup. + * + * @np: node already present in live tree + * @dup: node present in live tree to be updated + */ +static void update_node_properties(struct device_node *np, + struct device_node *dup) +{ + struct property *prop; + struct device_node *child; + + for_each_property_of_node(np, prop) + of_add_property(dup, prop); + + for_each_child_of_node(np, child) + child->parent = dup; +} + +/** + * attach_node_and_children - attaches nodes + * and its children to live tree + * + * @np: Node to attach to live tree + */ +static int attach_node_and_children(struct device_node *np) +{ + struct device_node *next, *root = np, *dup; + + if (!np) { + pr_warn("%s: No tree to attach; not running tests\n", + __func__); + return -ENODATA; + } + + + /* skip root node */ + np = np->child; + /* storing a copy in temporary node */ + dup = np; + + while (dup) { + nodes[last_node_index++] = dup; + dup = dup->sibling; + } + dup = NULL; + + while (np) { + next = np->allnext; + dup = of_find_node_by_path(np->full_name); + if (dup) + update_node_properties(np, dup); + else { + np->child = NULL; + if (np->parent == root) + np->parent = of_allnodes; + of_attach_node(np); + } + np = next; + } + + return 0; +} + +/** + * selftest_data_add - Reads, copies data from + * linked tree and attaches it to the live tree + */ +static int __init selftest_data_add(void) +{ + void *selftest_data; + struct device_node *selftest_data_node; + extern uint8_t __dtb_testcases_begin[]; + extern uint8_t __dtb_testcases_end[]; + const int size = __dtb_testcases_end - __dtb_testcases_begin; + + if (!size || !of_allnodes) { + pr_warn("%s: No testcase data to attach; not running tests\n", + __func__); + return -ENODATA; + } + + /* creating copy */ + selftest_data = kmemdup(__dtb_testcases_begin, size, GFP_KERNEL); + + if (!selftest_data) { + pr_warn("%s: Failed to allocate memory for selftest_data; " + "not running tests\n", __func__); + return -ENOMEM; + } + of_fdt_unflatten_tree(selftest_data, &selftest_data_node); + + /* attach the sub-tree to live tree */ + return attach_node_and_children(selftest_data_node); +} + +/** + * detach_node_and_children - detaches node + * and its children from live tree + * + * @np: Node to detach from live tree + */ +static void detach_node_and_children(struct device_node *np) +{ + while (np->child) + detach_node_and_children(np->child); + + while (np->sibling) + detach_node_and_children(np->sibling); + + of_detach_node(np); +} + +/** + * selftest_data_remove - removes the selftest data + * nodes from the live tree + */ +static void selftest_data_remove(void) +{ + struct device_node *np; + struct property *prop; + + while (last_node_index >= 0) { + if (nodes[last_node_index]) { + np = of_find_node_by_path(nodes[last_node_index]->full_name); + if (strcmp(np->full_name, "/aliases") != 0) { + detach_node_and_children(np->child); + of_detach_node(np); + } else { + for_each_property_of_node(np, prop) { + if (strcmp(prop->name, "testcase-alias") == 0) + of_remove_property(np, prop); + } + } + } + last_node_index--; + } +} + static int __init of_selftest(void) { struct device_node *np; + int res; + + /* adding data for selftest */ + res = selftest_data_add(); + if (res) + return res; np = of_find_node_by_path("/testcase-data/phandle-tests/consumer-a"); if (!np) { @@ -539,6 +691,10 @@ static int __init of_selftest(void) of_selftest_platform_populate(); pr_info("end of selftest - %i passed, %i failed\n", selftest_results.passed, selftest_results.failed); + + /* removing selftest data from live tree */ + selftest_data_remove(); + return 0; } late_initcall(of_selftest); diff --git a/drivers/of/testcase-data/testcases.dts b/drivers/of/testcase-data/testcases.dts new file mode 100644 index 0000000..8e7568e --- /dev/null +++ b/drivers/of/testcase-data/testcases.dts @@ -0,0 +1,5 @@ +/dts-v1/; +#include "tests-phandle.dtsi" +#include "tests-interrupts.dtsi" +#include "tests-match.dtsi" +#include "tests-platform.dtsi" diff --git a/drivers/of/testcase-data/testcases.dtsi b/drivers/of/testcase-data/testcases.dtsi deleted file mode 100644 index 6d8d980a..0000000 --- a/drivers/of/testcase-data/testcases.dtsi +++ /dev/null @@ -1,4 +0,0 @@ -#include "tests-phandle.dtsi" -#include "tests-interrupts.dtsi" -#include "tests-match.dtsi" -#include "tests-platform.dtsi" -- cgit v0.10.2 From b6ae5dc54b0a5c542d06d46b9083ceb70bf7e083 Mon Sep 17 00:00:00 2001 From: Grant Likely Date: Sat, 26 Jul 2014 10:58:43 -0600 Subject: of: typo fix in __of_prop_dup() Fix a trivial typo in a comment block. Signed-off-by: Grant Likely diff --git a/drivers/of/dynamic.c b/drivers/of/dynamic.c index c1002b7..54fecc4 100644 --- a/drivers/of/dynamic.c +++ b/drivers/of/dynamic.c @@ -259,7 +259,7 @@ struct property *__of_prop_dup(const struct property *prop, gfp_t allocflags) /* * NOTE: There is no check for zero length value. - * In case of a boolean property This will allocate a value + * In case of a boolean property, this will allocate a value * of zero bytes. We do this to work around the use * of of_get_property() calls on boolean values. */ -- cgit v0.10.2 From 3069f0c07f8d64ebf6ff5d2d1553e0a6dad4316e Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Mon, 7 Jul 2014 17:45:43 -0700 Subject: of: Use proper types for checking memory overflow Commit a67a6ed15513541579d38bcbd127e7be170710e5 (of: Check for phys_addr_t overflows in early_init_dt_add_memory_arch) corrected early_init_dt_add_memory_arch to account for overflows but did so in an unclean way using ULONG_MAX. There is no guarantee that sizeof(unsigned long) == sizeof(phys_addr_t). Check against phys_addr_t instead. Signed-off-by: Laura Abbott Tested-by: Geert Uytterhoeven Acked-by: Nicolas Pitre Signed-off-by: Grant Likely diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index b777d8f..b88a68e 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -875,24 +875,24 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, } #ifdef CONFIG_HAVE_MEMBLOCK +#define MAX_PHYS_ADDR ((phys_addr_t)~0) + void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) { const u64 phys_offset = __pa(PAGE_OFFSET); base &= PAGE_MASK; size &= PAGE_MASK; - if (sizeof(phys_addr_t) < sizeof(u64)) { - if (base > ULONG_MAX) { - pr_warning("Ignoring memory block 0x%llx - 0x%llx\n", - base, base + size); - return; - } + if (base > MAX_PHYS_ADDR) { + pr_warning("Ignoring memory block 0x%llx - 0x%llx\n", + base, base + size); + return; + } - if (base + size > ULONG_MAX) { - pr_warning("Ignoring memory range 0x%lx - 0x%llx\n", - ULONG_MAX, base + size); - size = ULONG_MAX - base; - } + if (base + size > MAX_PHYS_ADDR) { + pr_warning("Ignoring memory range 0x%lx - 0x%llx\n", + ULONG_MAX, base + size); + size = MAX_PHYS_ADDR - base; } if (base + size < phys_offset) { -- cgit v0.10.2 From 9dcfee01930e6cc1e84d28c232664f0c19a1f86c Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Mon, 14 Jul 2014 10:28:04 +0200 Subject: drivers: of: add automated assignment of reserved regions to client devices This patch adds code for automated assignment of reserved memory regions to struct device. reserved_mem->ops->device_init()/device_cleanup() callbacks are called to perform reserved memory driver specific initialization and cleanup Based on previous code provided by Josh Cartwright Signed-off-by: Marek Szyprowski Signed-off-by: Grant Likely diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 632aae8..59fb12e 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -206,8 +206,16 @@ void __init fdt_init_reserved_mem(void) for (i = 0; i < reserved_mem_count; i++) { struct reserved_mem *rmem = &reserved_mem[i]; unsigned long node = rmem->fdt_node; + int len; + const __be32 *prop; int err = 0; + prop = of_get_flat_dt_prop(node, "phandle", &len); + if (!prop) + prop = of_get_flat_dt_prop(node, "linux,phandle", &len); + if (prop) + rmem->phandle = of_read_number(prop, len/4); + if (rmem->size == 0) err = __reserved_mem_alloc_size(node, rmem->name, &rmem->base, &rmem->size); @@ -215,3 +223,65 @@ void __init fdt_init_reserved_mem(void) __reserved_mem_init_node(rmem); } } + +static inline struct reserved_mem *__find_rmem(struct device_node *node) +{ + unsigned int i; + + if (!node->phandle) + return NULL; + + for (i = 0; i < reserved_mem_count; i++) + if (reserved_mem[i].phandle == node->phandle) + return &reserved_mem[i]; + return NULL; +} + +/** + * of_reserved_mem_device_init() - assign reserved memory region to given device + * + * This function assign memory region pointed by "memory-region" device tree + * property to the given device. + */ +void of_reserved_mem_device_init(struct device *dev) +{ + struct reserved_mem *rmem; + struct device_node *np; + + np = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!np) + return; + + rmem = __find_rmem(np); + of_node_put(np); + + if (!rmem || !rmem->ops || !rmem->ops->device_init) + return; + + rmem->ops->device_init(rmem, dev); + dev_info(dev, "assigned reserved memory node %s\n", rmem->name); +} + +/** + * of_reserved_mem_device_release() - release reserved memory device structures + * + * This function releases structures allocated for memory region handling for + * the given device. + */ +void of_reserved_mem_device_release(struct device *dev) +{ + struct reserved_mem *rmem; + struct device_node *np; + + np = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!np) + return; + + rmem = __find_rmem(np); + of_node_put(np); + + if (!rmem || !rmem->ops || !rmem->ops->device_release) + return; + + rmem->ops->device_release(rmem, dev); +} diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h index 4669ddf..5b5efae 100644 --- a/include/linux/of_reserved_mem.h +++ b/include/linux/of_reserved_mem.h @@ -8,6 +8,7 @@ struct reserved_mem_ops; struct reserved_mem { const char *name; unsigned long fdt_node; + unsigned long phandle; const struct reserved_mem_ops *ops; phys_addr_t base; phys_addr_t size; @@ -27,10 +28,16 @@ typedef int (*reservedmem_of_init_fn)(struct reserved_mem *rmem); _OF_DECLARE(reservedmem, name, compat, init, reservedmem_of_init_fn) #ifdef CONFIG_OF_RESERVED_MEM +void of_reserved_mem_device_init(struct device *dev); +void of_reserved_mem_device_release(struct device *dev); + void fdt_init_reserved_mem(void); void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size); #else +static inline void of_reserved_mem_device_init(struct device *dev) { } +static inline void of_reserved_mem_device_release(struct device *pdev) { } + static inline void fdt_init_reserved_mem(void) { } static inline void fdt_reserved_mem_save_node(unsigned long node, const char *uname, phys_addr_t base, phys_addr_t size) { } -- cgit v0.10.2 From 32147e9a8ea54f2ec9914edeede8a9d9dc02e886 Mon Sep 17 00:00:00 2001 From: Gaurav Minocha Date: Fri, 25 Jul 2014 19:57:38 -0700 Subject: of: Fixing OF Selftest build error This patch is to fix following error while compiling OF selftests. "drivers/of/selftest.c:617:2: error: implicit declaration of function 'of_fdt_unflatten_tree'" Now, CONFIG_OF_SELFTEST depends on CONFIG_OF_EARLY_FLATTREE so that the broken configuration cannot be selected. Ultimately it would be a good idea to allow CONFIG_OF_SELFTEST to select CONFIG_OF_EARLY_FLATTREE, but there is a dependency problem on i386 and x86_64 that causes dtc to not get built and causes the build to fail. That problem needs to be fixed first. Signed-off-by: Gaurav Minocha Signed-off-by: Grant Likely diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig index 868f371..5160c4e 100644 --- a/drivers/of/Kconfig +++ b/drivers/of/Kconfig @@ -9,7 +9,7 @@ menu "Device Tree and Open Firmware support" config OF_SELFTEST bool "Device Tree Runtime self tests" - depends on OF_IRQ + depends on OF_IRQ && OF_EARLY_FLATTREE select OF_DYNAMIC help This option builds in test cases for the device tree infrastructure -- cgit v0.10.2