|
@@ -1,3 +1,4 @@
|
|
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
/*
|
|
* ipmi_si.c
|
|
* ipmi_si.c
|
|
*
|
|
*
|
|
@@ -10,27 +11,6 @@
|
|
*
|
|
*
|
|
* Copyright 2002 MontaVista Software Inc.
|
|
* Copyright 2002 MontaVista Software Inc.
|
|
* Copyright 2006 IBM Corp., Christian Krafft <krafft@de.ibm.com>
|
|
* 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. */
|
|
/* Default driver model device. */
|
|
struct platform_device *pdev;
|
|
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. */
|
|
/* Counters and things for the proc filesystem. */
|
|
atomic_t stats[SI_NUM_STATS];
|
|
atomic_t stats[SI_NUM_STATS];
|
|
|
|
|
|
@@ -275,7 +261,8 @@ static int num_max_busy_us;
|
|
static bool unload_when_empty = true;
|
|
static bool unload_when_empty = true;
|
|
|
|
|
|
static int try_smi_init(struct smi_info *smi);
|
|
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);
|
|
static void cleanup_ipmi_si(void);
|
|
|
|
|
|
#ifdef DEBUG_TIMING
|
|
#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),
|
|
ipmi_addr_src_to_str(new_smi->io.addr_source),
|
|
si_to_str[new_smi->io.si_type]);
|
|
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);
|
|
list_add_tail(&new_smi->link, &smi_infos);
|
|
|
|
|
|
if (initialized) {
|
|
if (initialized) {
|
|
rv = try_smi_init(new_smi);
|
|
rv = try_smi_init(new_smi);
|
|
if (rv) {
|
|
if (rv) {
|
|
- mutex_unlock(&smi_infos_lock);
|
|
|
|
cleanup_one_si(new_smi);
|
|
cleanup_one_si(new_smi);
|
|
|
|
+ mutex_unlock(&smi_infos_lock);
|
|
return rv;
|
|
return rv;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -2047,7 +2029,6 @@ static int try_smi_init(struct smi_info *new_smi)
|
|
int rv = 0;
|
|
int rv = 0;
|
|
int i;
|
|
int i;
|
|
char *init_name = NULL;
|
|
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",
|
|
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),
|
|
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);
|
|
new_smi->intf_num);
|
|
if (!new_smi->pdev) {
|
|
if (!new_smi->pdev) {
|
|
pr_err(PFX "Unable to allocate platform device\n");
|
|
pr_err(PFX "Unable to allocate platform device\n");
|
|
|
|
+ rv = -ENOMEM;
|
|
goto out_err;
|
|
goto out_err;
|
|
}
|
|
}
|
|
new_smi->io.dev = &new_smi->pdev->dev;
|
|
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);
|
|
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);
|
|
rv = platform_device_add(new_smi->pdev);
|
|
if (rv) {
|
|
if (rv) {
|
|
dev_err(new_smi->io.dev,
|
|
dev_err(new_smi->io.dev,
|
|
@@ -2176,7 +2158,7 @@ static int try_smi_init(struct smi_info *new_smi)
|
|
rv);
|
|
rv);
|
|
goto out_err;
|
|
goto out_err;
|
|
}
|
|
}
|
|
- platform_device_registered = true;
|
|
|
|
|
|
+ new_smi->pdev_registered = true;
|
|
}
|
|
}
|
|
|
|
|
|
dev_set_drvdata(new_smi->io.dev, new_smi);
|
|
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,
|
|
dev_err(new_smi->io.dev,
|
|
"Unable to add device attributes: error %d\n",
|
|
"Unable to add device attributes: error %d\n",
|
|
rv);
|
|
rv);
|
|
- goto out_err_stop_timer;
|
|
|
|
|
|
+ goto out_err;
|
|
}
|
|
}
|
|
|
|
+ new_smi->dev_group_added = true;
|
|
|
|
|
|
rv = ipmi_register_smi(&handlers,
|
|
rv = ipmi_register_smi(&handlers,
|
|
new_smi,
|
|
new_smi,
|
|
@@ -2196,7 +2179,7 @@ static int try_smi_init(struct smi_info *new_smi)
|
|
dev_err(new_smi->io.dev,
|
|
dev_err(new_smi->io.dev,
|
|
"Unable to register device: error %d\n",
|
|
"Unable to register device: error %d\n",
|
|
rv);
|
|
rv);
|
|
- goto out_err_remove_attrs;
|
|
|
|
|
|
+ goto out_err;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_IPMI_PROC_INTERFACE
|
|
#ifdef CONFIG_IPMI_PROC_INTERFACE
|
|
@@ -2206,7 +2189,7 @@ static int try_smi_init(struct smi_info *new_smi)
|
|
if (rv) {
|
|
if (rv) {
|
|
dev_err(new_smi->io.dev,
|
|
dev_err(new_smi->io.dev,
|
|
"Unable to create proc entry: %d\n", rv);
|
|
"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",
|
|
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) {
|
|
if (rv) {
|
|
dev_err(new_smi->io.dev,
|
|
dev_err(new_smi->io.dev,
|
|
"Unable to create proc entry: %d\n", rv);
|
|
"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",
|
|
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) {
|
|
if (rv) {
|
|
dev_err(new_smi->io.dev,
|
|
dev_err(new_smi->io.dev,
|
|
"Unable to create proc entry: %d\n", rv);
|
|
"Unable to create proc entry: %d\n", rv);
|
|
- goto out_err_stop_timer;
|
|
|
|
|
|
+ goto out_err;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
@@ -2239,56 +2222,8 @@ static int try_smi_init(struct smi_info *new_smi)
|
|
|
|
|
|
return 0;
|
|
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:
|
|
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);
|
|
kfree(init_name);
|
|
|
|
|
|
@@ -2366,17 +2301,14 @@ skip_fallback_noirq:
|
|
}
|
|
}
|
|
module_init(init_ipmi_si);
|
|
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;
|
|
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);
|
|
rv = ipmi_unregister_smi(intf);
|
|
if (rv) {
|
|
if (rv) {
|
|
pr_err(PFX "Unable to unregister device: errno=%d\n",
|
|
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
|
|
* Make sure that interrupts, the timer and the thread are
|
|
* stopped and will not run again.
|
|
* 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
|
|
* Timeouts are stopped, now make sure the interrupts are off
|
|
* in the BMC. Note that timers and CPU interrupts are off,
|
|
* in the BMC. Note that timers and CPU interrupts are off,
|
|
* so no need for locks.
|
|
* 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);
|
|
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);
|
|
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)
|
|
int ipmi_si_remove_by_dev(struct device *dev)
|