diff options
Diffstat (limited to 'drivers/char/ipmi/ipmi_si_intf.c')
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 192 |
1 files changed, 77 insertions, 115 deletions
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 6768cb2dd740..ff870aa91cfe 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ipmi_si.c * @@ -10,27 +11,6 @@ * * Copyright 2002 MontaVista Software Inc. * Copyright 2006 IBM Corp., Christian Krafft <krafft@de.ibm.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ /* @@ -252,6 +232,12 @@ struct smi_info { /* Default driver model device. */ struct platform_device *pdev; + /* Have we added the device group to the device? */ + bool dev_group_added; + + /* Have we added the platform device? */ + bool pdev_registered; + /* Counters and things for the proc filesystem. */ atomic_t stats[SI_NUM_STATS]; @@ -275,7 +261,8 @@ static int num_max_busy_us; static bool unload_when_empty = true; static int try_smi_init(struct smi_info *smi); -static void cleanup_one_si(struct smi_info *to_clean); +static void shutdown_one_si(struct smi_info *smi_info); +static void cleanup_one_si(struct smi_info *smi_info); static void cleanup_ipmi_si(void); #ifdef DEBUG_TIMING @@ -2017,18 +2004,13 @@ int ipmi_si_add_smi(struct si_sm_io *io) ipmi_addr_src_to_str(new_smi->io.addr_source), si_to_str[new_smi->io.si_type]); - /* So we know not to free it unless we have allocated one. */ - new_smi->intf = NULL; - new_smi->si_sm = NULL; - new_smi->handlers = NULL; - list_add_tail(&new_smi->link, &smi_infos); if (initialized) { rv = try_smi_init(new_smi); if (rv) { - mutex_unlock(&smi_infos_lock); cleanup_one_si(new_smi); + mutex_unlock(&smi_infos_lock); return rv; } } @@ -2047,7 +2029,6 @@ static int try_smi_init(struct smi_info *new_smi) int rv = 0; int i; char *init_name = NULL; - bool platform_device_registered = false; pr_info(PFX "Trying %s-specified %s state machine at %s address 0x%lx, slave address 0x%x, irq %d\n", ipmi_addr_src_to_str(new_smi->io.addr_source), @@ -2090,6 +2071,7 @@ static int try_smi_init(struct smi_info *new_smi) new_smi->intf_num); if (!new_smi->pdev) { pr_err(PFX "Unable to allocate platform device\n"); + rv = -ENOMEM; goto out_err; } new_smi->io.dev = &new_smi->pdev->dev; @@ -2168,7 +2150,7 @@ static int try_smi_init(struct smi_info *new_smi) atomic_set(&new_smi->req_events, 1); } - if (new_smi->pdev) { + if (new_smi->pdev && !new_smi->pdev_registered) { rv = platform_device_add(new_smi->pdev); if (rv) { dev_err(new_smi->io.dev, @@ -2176,7 +2158,7 @@ static int try_smi_init(struct smi_info *new_smi) rv); goto out_err; } - platform_device_registered = true; + new_smi->pdev_registered = true; } dev_set_drvdata(new_smi->io.dev, new_smi); @@ -2185,8 +2167,9 @@ static int try_smi_init(struct smi_info *new_smi) dev_err(new_smi->io.dev, "Unable to add device attributes: error %d\n", rv); - goto out_err_stop_timer; + goto out_err; } + new_smi->dev_group_added = true; rv = ipmi_register_smi(&handlers, new_smi, @@ -2196,7 +2179,7 @@ static int try_smi_init(struct smi_info *new_smi) dev_err(new_smi->io.dev, "Unable to register device: error %d\n", rv); - goto out_err_remove_attrs; + goto out_err; } #ifdef CONFIG_IPMI_PROC_INTERFACE @@ -2206,7 +2189,7 @@ static int try_smi_init(struct smi_info *new_smi) if (rv) { dev_err(new_smi->io.dev, "Unable to create proc entry: %d\n", rv); - goto out_err_stop_timer; + goto out_err; } rv = ipmi_smi_add_proc_entry(new_smi->intf, "si_stats", @@ -2215,7 +2198,7 @@ static int try_smi_init(struct smi_info *new_smi) if (rv) { dev_err(new_smi->io.dev, "Unable to create proc entry: %d\n", rv); - goto out_err_stop_timer; + goto out_err; } rv = ipmi_smi_add_proc_entry(new_smi->intf, "params", @@ -2224,7 +2207,7 @@ static int try_smi_init(struct smi_info *new_smi) if (rv) { dev_err(new_smi->io.dev, "Unable to create proc entry: %d\n", rv); - goto out_err_stop_timer; + goto out_err; } #endif @@ -2239,56 +2222,8 @@ static int try_smi_init(struct smi_info *new_smi) return 0; -out_err_remove_attrs: - device_remove_group(new_smi->io.dev, &ipmi_si_dev_attr_group); - dev_set_drvdata(new_smi->io.dev, NULL); - -out_err_stop_timer: - stop_timer_and_thread(new_smi); - out_err: - new_smi->interrupt_disabled = true; - - if (new_smi->intf) { - ipmi_smi_t intf = new_smi->intf; - new_smi->intf = NULL; - ipmi_unregister_smi(intf); - } - - if (new_smi->io.irq_cleanup) { - new_smi->io.irq_cleanup(&new_smi->io); - new_smi->io.irq_cleanup = NULL; - } - - /* - * Wait until we know that we are out of any interrupt - * handlers might have been running before we freed the - * interrupt. - */ - synchronize_sched(); - - if (new_smi->si_sm) { - if (new_smi->handlers) - new_smi->handlers->cleanup(new_smi->si_sm); - kfree(new_smi->si_sm); - new_smi->si_sm = NULL; - } - if (new_smi->io.addr_source_cleanup) { - new_smi->io.addr_source_cleanup(&new_smi->io); - new_smi->io.addr_source_cleanup = NULL; - } - if (new_smi->io.io_cleanup) { - new_smi->io.io_cleanup(&new_smi->io); - new_smi->io.io_cleanup = NULL; - } - - if (new_smi->pdev) { - if (platform_device_registered) - platform_device_unregister(new_smi->pdev); - else - platform_device_put(new_smi->pdev); - new_smi->pdev = NULL; - } + shutdown_one_si(new_smi); kfree(init_name); @@ -2366,17 +2301,14 @@ skip_fallback_noirq: } module_init(init_ipmi_si); -static void cleanup_one_si(struct smi_info *to_clean) +static void shutdown_one_si(struct smi_info *smi_info) { int rv = 0; - if (!to_clean) - return; - - if (to_clean->intf) { - ipmi_smi_t intf = to_clean->intf; + if (smi_info->intf) { + ipmi_smi_t intf = smi_info->intf; - to_clean->intf = NULL; + smi_info->intf = NULL; rv = ipmi_unregister_smi(intf); if (rv) { pr_err(PFX "Unable to unregister device: errno=%d\n", @@ -2384,49 +2316,79 @@ static void cleanup_one_si(struct smi_info *to_clean) } } - device_remove_group(to_clean->io.dev, &ipmi_si_dev_attr_group); - dev_set_drvdata(to_clean->io.dev, NULL); - - list_del(&to_clean->link); + if (smi_info->dev_group_added) { + device_remove_group(smi_info->io.dev, &ipmi_si_dev_attr_group); + smi_info->dev_group_added = false; + } + if (smi_info->io.dev) + dev_set_drvdata(smi_info->io.dev, NULL); /* * Make sure that interrupts, the timer and the thread are * stopped and will not run again. */ - if (to_clean->io.irq_cleanup) - to_clean->io.irq_cleanup(&to_clean->io); - stop_timer_and_thread(to_clean); + smi_info->interrupt_disabled = true; + if (smi_info->io.irq_cleanup) { + smi_info->io.irq_cleanup(&smi_info->io); + smi_info->io.irq_cleanup = NULL; + } + stop_timer_and_thread(smi_info); + + /* + * Wait until we know that we are out of any interrupt + * handlers might have been running before we freed the + * interrupt. + */ + synchronize_sched(); /* * Timeouts are stopped, now make sure the interrupts are off * in the BMC. Note that timers and CPU interrupts are off, * so no need for locks. */ - while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { - poll(to_clean); + while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) { + poll(smi_info); schedule_timeout_uninterruptible(1); } - if (to_clean->handlers) - disable_si_irq(to_clean); - while (to_clean->curr_msg || (to_clean->si_state != SI_NORMAL)) { - poll(to_clean); + if (smi_info->handlers) + disable_si_irq(smi_info); + while (smi_info->curr_msg || (smi_info->si_state != SI_NORMAL)) { + poll(smi_info); schedule_timeout_uninterruptible(1); } + if (smi_info->handlers) + smi_info->handlers->cleanup(smi_info->si_sm); + + if (smi_info->io.addr_source_cleanup) { + smi_info->io.addr_source_cleanup(&smi_info->io); + smi_info->io.addr_source_cleanup = NULL; + } + if (smi_info->io.io_cleanup) { + smi_info->io.io_cleanup(&smi_info->io); + smi_info->io.io_cleanup = NULL; + } - if (to_clean->handlers) - to_clean->handlers->cleanup(to_clean->si_sm); + kfree(smi_info->si_sm); + smi_info->si_sm = NULL; +} - kfree(to_clean->si_sm); +static void cleanup_one_si(struct smi_info *smi_info) +{ + if (!smi_info) + return; - if (to_clean->io.addr_source_cleanup) - to_clean->io.addr_source_cleanup(&to_clean->io); - if (to_clean->io.io_cleanup) - to_clean->io.io_cleanup(&to_clean->io); + list_del(&smi_info->link); - if (to_clean->pdev) - platform_device_unregister(to_clean->pdev); + shutdown_one_si(smi_info); + + if (smi_info->pdev) { + if (smi_info->pdev_registered) + platform_device_unregister(smi_info->pdev); + else + platform_device_put(smi_info->pdev); + } - kfree(to_clean); + kfree(smi_info); } int ipmi_si_remove_by_dev(struct device *dev) |