public inbox for linux-acpi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH -v7 0/2] ACPI: Install the ACPI IPMI opregion
@ 2010-07-14 12:54 yakui.zhao
  2010-07-14 12:54 ` [PATCH 1/2_v7] ACPI: Export symbol of pnp_bus_type yakui.zhao
  0 siblings, 1 reply; 12+ messages in thread
From: yakui.zhao @ 2010-07-14 12:54 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, minyard, bjorn.helgaas, Zhao Yakui

From: Zhao Yakui <yakui.zhao@intel.com>

Hi, 
	
    ACPI 4.0 spec adds the ACPI IPMI opregion, which means that the ACPI AML
code can also communicate with the BMC controller. This patch set is to
install the ACPI IPMI opregion space handler and enable the ACPI to access
the BMC controller through the IPMI message.

[Patch 01/02]: Export symbol of pnp_bus_type

[Patch 02/02]: Install the IPMI space handler to enable ACPI to access the BMC
controller

V6->V7: Based on Corey Minyard's comments, the IPMI opregion handler is put
into the separate file again as it only uses the API interface provided
by IPMI system. Now it is put into the ACPI subsystem.

V5->V6: Adjust the patch order and refresh the patch set.

V4->V5: According to Bjorn's comment, remove the unnecessary comment.
The debug info will be printed by using dev_err/dev_warn instead of by
using printk directly.

V3->V4: According to Bjorn's comment, delete the meaningless variable
initialization and check. We also do some cleanup. 

V2->V3: According to Bjorn's comment, this IPMI opregion code is put into
the IPMI subsystem(ipmi_si_intf.c). In such case the part of IPMI opregion
code  will be installed automatically when IPMI detection module is loaded.
When the IPMI system interface is detected by loading PNP device driver, we
will record every IPMI system interface defined in ACPI namespace and then
install the corresponding IPMI opregion space handler, which is used to enable
ACPI AML code to access the BMC controller.

V1->V2: According to Bjorn's comment, we won't install the IPMI space handler
by loading an ACPI device driver. Instead we will enumerate the ACPI IPMI
device directly in ACPI device tree and then install the IPMI space handler.
Then ACPI AML code and access the BMC controller through the IPMI space
handler.

Best regards.
   Yakui


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/2_v7] ACPI: Export symbol of pnp_bus_type
  2010-07-14 12:54 [PATCH -v7 0/2] ACPI: Install the ACPI IPMI opregion yakui.zhao
@ 2010-07-14 12:54 ` yakui.zhao
  2010-07-14 12:54   ` [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller yakui.zhao
  2010-07-14 16:57   ` [PATCH 1/2_v7] ACPI: Export symbol of pnp_bus_type Bjorn Helgaas
  0 siblings, 2 replies; 12+ messages in thread
From: yakui.zhao @ 2010-07-14 12:54 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, minyard, bjorn.helgaas, Zhao Yakui

From: Zhao Yakui <yakui.zhao@intel.com>

Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
cc: Bjorn Helgaas <bjorn.helgaas@hp.com>
---
 drivers/pnp/driver.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)

diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c
index cd11b11..9aec98a 100644
--- a/drivers/pnp/driver.c
+++ b/drivers/pnp/driver.c
@@ -218,6 +218,8 @@ struct bus_type pnp_bus_type = {
 	.dev_attrs = pnp_interface_attrs,
 };
 
+EXPORT_SYMBOL(pnp_bus_type);
+
 int pnp_register_driver(struct pnp_driver *drv)
 {
 	drv->driver.name = drv->name;
-- 
1.5.4.5


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller
  2010-07-14 12:54 ` [PATCH 1/2_v7] ACPI: Export symbol of pnp_bus_type yakui.zhao
@ 2010-07-14 12:54   ` yakui.zhao
  2010-07-14 17:24     ` Bjorn Helgaas
  2010-07-14 16:57   ` [PATCH 1/2_v7] ACPI: Export symbol of pnp_bus_type Bjorn Helgaas
  1 sibling, 1 reply; 12+ messages in thread
From: yakui.zhao @ 2010-07-14 12:54 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, minyard, bjorn.helgaas, Zhao Yakui

From: Zhao Yakui <yakui.zhao@intel.com>

ACPI 4.0 spec adds the ACPI IPMI opregion, which means that the ACPI AML
code can also communicate with the BMC controller. This is to install
the ACPI IPMI opregion and enable the ACPI to access the BMC controller
through the IPMI message.

     It will create IPMI user interface for every IPMI device detected
in ACPI namespace and install the corresponding IPMI opregion space handler.
Then it can enable ACPI to access the BMC controller through the IPMI
message.

The following describes how to process the IPMI request in IPMI space handler:
    1. format the IPMI message based on the request in AML code.
    IPMI system address. Now the address type is SYSTEM_INTERFACE_ADDR_TYPE
    IPMI net function & command
    IPMI message payload
    2. send the IPMI message by using the function of ipmi_request_settime
    3. wait for the completion of IPMI message.
    4. format the IPMI response and return it to ACPI AML code.

At the same time it also addes the module dependency. The ACPI IPMI opregion
will depend on the IPMI subsystem.

Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
cc: Bjorn Helgaas <bjorn.helgaas@hp.com>
---
 drivers/acpi/Kconfig     |   10 +
 drivers/acpi/Makefile    |    1 +
 drivers/acpi/acpi_ipmi.c |  551 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 562 insertions(+), 0 deletions(-)
 create mode 100644 drivers/acpi/acpi_ipmi.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 7464115..0fa6a01 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -204,6 +204,16 @@ config ACPI_PROCESSOR
 
 	  To compile this driver as a module, choose M here:
 	  the module will be called processor.
+config ACPI_IPMI
+	tristate "IPMI"
+	depends on EXPERIMENTAL && IPMI_SI && IPMI_HANDLER
+	default n
+	help
+	  This driver enables the ACPI to access the BMC controller. And it
+	  uses the IPMI request/response message to communicate with BMC
+	  controller, which can be found on on the server.
+
+	  To compile this driver as a module, choose M here:
 
 config ACPI_HOTPLUG_CPU
 	bool
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 6ee3316..5ca31f1 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -67,5 +67,6 @@ processor-y			+= processor_idle.o processor_thermal.o
 processor-$(CONFIG_CPU_FREQ)	+= processor_perflib.o
 
 obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
+obj-$(CONFIG_ACPI_IPMI)		+= acpi_ipmi.o
 
 obj-$(CONFIG_ACPI_APEI)		+= apei/
diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c
new file mode 100644
index 0000000..3e674df
--- /dev/null
+++ b/drivers/acpi/acpi_ipmi.c
@@ -0,0 +1,551 @@
+/*
+ *  ipmi.c - ACPI IPMI opregion
+ *
+ *  Copyright (C) 2010 Intel Corporation
+ *  Copyright (C) 2010 Zhao Yakui <yakui.zhao@intel.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 program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <linux/ipmi.h>
+#include <linux/device.h>
+#include <linux/pnp.h>
+
+MODULE_AUTHOR("Zhao Yakui");
+MODULE_DESCRIPTION("ACPI IPMI Opregion driver");
+MODULE_LICENSE("GPL");
+
+#define IPMI_FLAGS_HANDLER_INSTALL	0
+
+#define ACPI_IPMI_OK			0
+#define ACPI_IPMI_TIMEOUT		0x10
+#define ACPI_IPMI_UNKNOWN		0x07
+/* the IPMI timeout is 5s */
+#define IPMI_TIMEOUT			(5 * HZ)
+
+struct acpi_ipmi_device {
+	/* the device list attached to driver_data.ipmi_devices */
+	struct list_head head;
+	/* the IPMI request message list */
+	struct list_head tx_msg_list;
+	acpi_handle handle;
+	struct pnp_dev	*pnp_dev;
+	ipmi_user_t	user_interface;
+	struct mutex	mutex_lock;
+	int ipmi_ifnum; /* IPMI interface number */
+	long curr_msgid;
+	unsigned long flags;
+};
+
+struct ipmi_driver_data {
+	int device_count;
+	struct list_head	ipmi_devices;
+	struct ipmi_smi_watcher	bmc_events;
+	struct ipmi_user_hndl	ipmi_hndlrs;
+};
+
+struct acpi_ipmi_msg {
+	struct list_head head;
+	/*
+	 * General speaking the addr type should be SI_ADDR_TYPE. And
+	 * the addr channel should be BMC.
+	 * In fact it can also be IPMB type. But we will have to
+	 * parse it from the Netfn command buffer. It is so complex
+	 * that it is skipped.
+	 */
+	struct ipmi_addr addr;
+	long tx_msgid;
+	/* it is used to track whether the IPMI message is finished */
+	struct completion tx_complete;
+	struct kernel_ipmi_msg tx_message;
+	int	msg_done;
+	/* tx data . And copy it from ACPI object buffer */
+	u8	tx_data[64];
+	int	tx_len;
+	u8	rx_data[64];
+	int	rx_len;
+	struct acpi_ipmi_device *device;
+};
+
+/* IPMI request/response buffer per ACPI 4.0, sec 5.5.2.4.3.2 */
+struct acpi_ipmi_buffer {
+	u8 status;
+	u8 length;
+	u8 data[64];
+};
+
+static void ipmi_register_bmc(int iface, struct device *dev);
+static void ipmi_bmc_gone(int iface);
+static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data);
+
+static struct ipmi_driver_data driver_data = {
+	.ipmi_devices = LIST_HEAD_INIT(driver_data.ipmi_devices),
+	.bmc_events = {
+		.owner = THIS_MODULE,
+		.new_smi = ipmi_register_bmc,
+		.smi_gone = ipmi_bmc_gone,
+	},
+	.ipmi_hndlrs = {
+		.ipmi_recv_hndl = ipmi_msg_handler,
+	},
+};
+
+
+struct acpi_ipmi_msg *acpi_alloc_ipmi_msg(struct acpi_ipmi_device *ipmi)
+{
+	struct acpi_ipmi_msg *ipmi_msg;
+	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+
+	ipmi_msg = kzalloc(sizeof(struct acpi_ipmi_msg), GFP_KERNEL);
+	if (!ipmi_msg)	{
+		dev_warn(&pnp_dev->dev, "Can't allocate memory for ipmi_msg\n");
+		return NULL;
+	}
+	init_completion(&ipmi_msg->tx_complete);
+	INIT_LIST_HEAD(&ipmi_msg->head);
+	ipmi_msg->device = ipmi;
+	return ipmi_msg;
+}
+
+#define		IPMI_OP_RGN_NETFN(offset)	((offset >> 8) & 0xff)
+#define		IPMI_OP_RGN_CMD(offset)		(offset & 0xff)
+static void acpi_format_ipmi_msg(struct acpi_ipmi_msg *tx_msg,
+				acpi_physical_address address,
+				acpi_integer *value)
+{
+	struct kernel_ipmi_msg *msg;
+	struct acpi_ipmi_buffer *buffer;
+	struct acpi_ipmi_device *device;
+
+	msg = &tx_msg->tx_message;
+	/*
+	 * IPMI network function and command are encoded in the address
+	 * within the IPMI OpRegion; see ACPI 4.0, sec 5.5.2.4.3.
+	 */
+	msg->netfn = IPMI_OP_RGN_NETFN(address);
+	msg->cmd = IPMI_OP_RGN_CMD(address);
+	msg->data = tx_msg->tx_data;
+	/*
+	 * value is the parameter passed by the IPMI opregion space handler.
+	 * It points to the IPMI request message buffer
+	 */
+	buffer = (struct acpi_ipmi_buffer *)value;
+	/* copy the tx message data */
+	msg->data_len = buffer->length;
+	memcpy(tx_msg->tx_data, buffer->data, msg->data_len);
+	/*
+	 * now the default type is SYSTEM_INTERFACE and channel type is BMC.
+	 * If the netfn is APP_REQUEST and the cmd is SEND_MESSAGE,
+	 * the addr type should be changed to IPMB. Then we will have to parse
+	 * the IPMI request message buffer to get the IPMB address.
+	 * If so, please fix me.
+	 */
+	tx_msg->addr.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
+	tx_msg->addr.channel = IPMI_BMC_CHANNEL;
+	tx_msg->addr.data[0] = 0;
+
+	/* Get the msgid */
+	device = tx_msg->device;
+	mutex_lock(&device->mutex_lock);
+	device->curr_msgid++;
+	tx_msg->tx_msgid = device->curr_msgid;
+	mutex_unlock(&device->mutex_lock);
+}
+
+static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
+		acpi_integer *value, int timeout)
+{
+	struct acpi_ipmi_buffer *buffer;
+
+	/*
+	 * value is also used as output parameter. It represents the response
+	 * IPMI message returned by IPMI command.
+	 */
+	buffer = (struct acpi_ipmi_buffer *)value;
+	if (!timeout && !msg->msg_done) {
+		buffer->status = ACPI_IPMI_TIMEOUT;
+		return;
+	}
+	/*
+	 * If the flag of msg_done is not set or the recv length is zero, it
+	 * means that the IPMI command is not executed correctly.
+	 * The status code will be ACPI_IPMI_UNKNOWN.
+	 */
+	if (!msg->msg_done || !msg->rx_len) {
+		buffer->status = ACPI_IPMI_UNKNOWN;
+		return;
+	}
+	/*
+	 * If the IPMI response message is obtained correctly, the status code
+	 * will be ACPI_IPMI_OK
+	 */
+	buffer->status = ACPI_IPMI_OK;
+	buffer->length = msg->rx_len;
+	memcpy(buffer->data, msg->rx_data, msg->rx_len);
+	return;
+}
+static void ipmi_destroy_tx_msg(struct acpi_ipmi_device *ipmi)
+{
+	struct acpi_ipmi_msg *tx_msg = NULL, *temp;
+	int count = 20;
+	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+
+	list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list, head) {
+		/* wake up the sleep thread on the Tx msg */
+		complete(&tx_msg->tx_complete);
+	}
+
+	/* wait for about 20 ticks to flush the tx message list */
+	while (count--) {
+		if (list_empty(&ipmi->tx_msg_list))
+			break;
+		schedule_timeout(1);
+	}
+	if (!list_empty(&ipmi->tx_msg_list))
+		dev_warn(&pnp_dev->dev, "tx msg list is not NULL\n");
+
+}
+
+static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
+{
+	struct acpi_ipmi_device *ipmi_device =
+			(struct acpi_ipmi_device *)user_msg_data;
+	int msg_found = 0;
+	struct acpi_ipmi_msg *tx_msg = NULL;
+	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
+
+	if (msg->user != ipmi_device->user_interface) {
+		dev_warn(&pnp_dev->dev, "Incorrect IPMI user\n");
+		ipmi_free_recv_msg(msg);
+		return;
+	}
+	mutex_lock(&ipmi_device->mutex_lock);
+	list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
+		if (msg->msgid == tx_msg->tx_msgid) {
+			/* find the message id */
+			msg_found = 1;
+			break;
+		}
+	}
+
+	mutex_unlock(&ipmi_device->mutex_lock);
+	if (!msg_found) {
+		/* no matched msg is found . But we should free it */
+		ipmi_free_recv_msg(msg);
+		dev_warn(&pnp_dev->dev, "Incorrect MSG is found\n");
+		return;
+	}
+
+	if (msg->msg.data_len) {
+		/* copy the response data to Rx_data buffer */
+		memcpy(tx_msg->rx_data, msg->msg_data, msg->msg.data_len);
+		tx_msg->rx_len = msg->msg.data_len;
+		tx_msg->msg_done = 1;
+	}
+	complete(&tx_msg->tx_complete);
+	ipmi_free_recv_msg(msg);
+};
+
+static void ipmi_register_bmc(int iface, struct device *dev)
+{
+	struct acpi_ipmi_device *ipmi_device;
+	struct pnp_dev *device;
+	ipmi_user_t		user;
+	int err;
+
+	if (list_empty(&driver_data.ipmi_devices))
+		return;
+
+	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
+		if (ipmi_device->user_interface) {
+			/*
+			 * Only one user interface is allowed to be registered
+			 * for one IPMI device.
+			 * If we already create the user interface for
+			 * one IPMI device, skip it
+			 */
+			continue;
+		}
+		device = ipmi_device->pnp_dev;
+		if (dev == &device->dev) {
+			/*
+			 * If the dev is identical to the ACPI device,
+			 * create the user interface.
+			 */
+			err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
+						ipmi_device, &user);
+			if (err == 0) {
+				ipmi_device->user_interface = user;
+				ipmi_device->ipmi_ifnum = iface;
+			}
+
+			break;
+		}
+	}
+}
+
+static void ipmi_bmc_gone(int iface)
+{
+	struct acpi_ipmi_device *ipmi_device, *temp;
+
+	if (list_empty(&driver_data.ipmi_devices))
+		return;
+
+	list_for_each_entry_safe(ipmi_device, temp,
+				&driver_data.ipmi_devices, head) {
+		if (ipmi_device->user_interface &&
+			(ipmi_device->ipmi_ifnum == iface)) {
+			ipmi_destroy_user(ipmi_device->user_interface);
+			ipmi_device->user_interface = NULL;
+			ipmi_destroy_tx_msg(ipmi_device);
+		}
+	}
+}
+/* --------------------------------------------------------------------------
+ *			Address Space Management
+   -------------------------------------------------------------------------- */
+/*
+ * This is the IPMI opregion space handler.
+ * @function: indicates the read/write. In fact as the IPMI message is driven
+ * by command, only write is meaningful.
+ * @address: This contains the netfn/command of IPMI request message.
+ * @bits   : not used.
+ * @value  : it is an in/out parameter. It points to the IPMI message buffer.
+ *	     Before the IPMI message is sent, it represents the actual request
+ *	     IPMI message. After the IPMI message is finished, it represents
+ *	     the response IPMI message returned by IPMI command.
+ * @handler_context: IPMI device context.
+ */
+
+static acpi_status
+acpi_ipmi_space_handler(u32 function, acpi_physical_address address,
+		      u32 bits, acpi_integer *value,
+		      void *handler_context, void *region_context)
+{
+	struct acpi_ipmi_msg *tx_msg;
+	struct acpi_ipmi_device *ipmi_device =
+			(struct acpi_ipmi_device *) handler_context;
+	int err;
+	acpi_status status;
+	/*
+	 * IPMI opregion message.
+	 * IPMI message is firstly written to the BMC and system software
+	 * can get the respsonse. So it is unmeaningful for the IPMI read
+	 * access.
+	 */
+	if ((function & ACPI_IO_MASK) == ACPI_READ)
+		return AE_TYPE;
+
+	if (!ipmi_device->user_interface)
+		return AE_NOT_EXIST;
+
+	tx_msg = acpi_alloc_ipmi_msg(ipmi_device);
+	if (!tx_msg)
+		return AE_NO_MEMORY;
+
+	acpi_format_ipmi_msg(tx_msg, address, value);
+	mutex_lock(&ipmi_device->mutex_lock);
+	list_add_tail(&tx_msg->head, &ipmi_device->tx_msg_list);
+	mutex_unlock(&ipmi_device->mutex_lock);
+	err = ipmi_request_settime(ipmi_device->user_interface,
+					&tx_msg->addr,
+					tx_msg->tx_msgid,
+					&tx_msg->tx_message,
+					NULL, 0, 0, 0);
+	if (err) {
+		status = AE_ERROR;
+		goto end_label;
+	}
+	err = wait_for_completion_timeout(&tx_msg->tx_complete, IPMI_TIMEOUT);
+	acpi_format_ipmi_response(tx_msg, value, err);
+	status = AE_OK;
+
+end_label:
+	mutex_lock(&ipmi_device->mutex_lock);
+	list_del(&tx_msg->head);
+	mutex_unlock(&ipmi_device->mutex_lock);
+	kfree(tx_msg);
+	return status;
+}
+
+static void ipmi_remove_handler(struct acpi_ipmi_device *ipmi)
+{
+	if (!test_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags))
+		return;
+	acpi_remove_address_space_handler(ipmi->handle,
+				ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler);
+
+	clear_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags);
+}
+
+static int ipmi_install_handler(struct acpi_ipmi_device *ipmi)
+{
+	acpi_status status;
+
+	if (test_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags))
+		return 0;
+
+	status = acpi_install_address_space_handler(ipmi->handle,
+						    ACPI_ADR_SPACE_IPMI,
+						    &acpi_ipmi_space_handler,
+						    NULL, ipmi);
+	if (ACPI_FAILURE(status)) {
+		struct pnp_dev *pnp_dev = ipmi->pnp_dev;
+		dev_warn(&pnp_dev->dev, "Can't register IPMI opregion for %s\n",
+					pnp_dev_name(pnp_dev));
+		return -EINVAL;
+	}
+	set_bit(IPMI_FLAGS_HANDLER_INSTALL, &ipmi->flags);
+	return 0;
+}
+
+
+static const struct pnp_device_id ipmi_device_ids[] = {
+	{"IPI0001", 0},
+	{"", 0},
+};
+
+static int match_device(struct pnp_dev *dev, const struct pnp_device_id *ids)
+{
+	const struct pnp_device_id *id;
+	struct pnp_id *pnp_id;
+
+	for (id = ids;  id->id[0]; id++) {
+		pnp_id = dev->id;
+		if (!pnp_id)
+			return 0;
+
+		while (pnp_id) {
+			if (!strncasecmp(pnp_id->id, (char *)id->id,
+					strlen((char *)id->id)))
+				return 1;
+			pnp_id = pnp_id->next;
+		};
+	}
+	return 0;
+}
+
+static int pnp_ipmi_match(struct device *dev, void *data)
+{
+	struct pnp_dev *pnp_dev;
+	struct acpi_ipmi_device *ipmi_device;
+	struct acpi_device *acpi_dev;
+
+	pnp_dev = to_pnp_dev(dev);
+
+	if (!match_device(pnp_dev, ipmi_device_ids))
+		return 0;
+
+	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
+	if (!ipmi_device) {
+		dev_warn(dev, "Can't allocate memory for acpi_ipmi_device\n");
+		return -ENOMEM;
+	}
+	acpi_dev = pnp_acpi_device(pnp_dev);
+	INIT_LIST_HEAD(&ipmi_device->head);
+	ipmi_device->pnp_dev = pnp_dev;
+	ipmi_device->handle = acpi_dev->handle;
+
+	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
+	return 0;
+}
+
+static void acpi_add_ipmi_devices(void)
+{
+	struct acpi_ipmi_device *ipmi_device;
+
+	bus_for_each_dev(&pnp_bus_type, NULL, NULL, pnp_ipmi_match);
+	if (list_empty(&driver_data.ipmi_devices))
+		return;
+
+	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
+		mutex_init(&ipmi_device->mutex_lock);
+		INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
+		ipmi_install_handler(ipmi_device);
+	}
+}
+
+static void acpi_remove_ipmi_handlers(void)
+{
+	struct acpi_ipmi_device *ipmi_device, *temp;
+
+	list_for_each_entry_safe(ipmi_device, temp, &driver_data.ipmi_devices,
+					head) {
+		if (ipmi_device->user_interface) {
+			/*
+			 * If the IPMI user interface is created, it
+			 * should be destroyed.
+			 */
+			ipmi_destroy_user(ipmi_device->user_interface);
+			ipmi_device->user_interface = NULL;
+		}
+		list_del(&ipmi_device->head);
+		if (!list_empty(&ipmi_device->tx_msg_list)) {
+			/* destroy the Tx_msg list */
+			ipmi_destroy_tx_msg(ipmi_device);
+		}
+		ipmi_remove_handler(ipmi_device);
+		kfree(ipmi_device);
+	}
+	return;
+}
+
+static int __init acpi_ipmi_init(void)
+{
+	int result = 0;
+
+	if (acpi_disabled)
+		return result;
+
+	acpi_add_ipmi_devices();
+
+	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
+
+	if (result)
+		acpi_remove_ipmi_handlers();
+
+	return result;
+}
+
+static void __exit acpi_ipmi_exit(void)
+{
+	if (acpi_disabled)
+		return;
+
+	acpi_remove_ipmi_handlers();
+	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
+
+	return;
+}
+
+module_init(acpi_ipmi_init);
+module_exit(acpi_ipmi_exit);
-- 
1.5.4.5


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 1/2_v7] ACPI: Export symbol of pnp_bus_type
  2010-07-14 12:54 ` [PATCH 1/2_v7] ACPI: Export symbol of pnp_bus_type yakui.zhao
  2010-07-14 12:54   ` [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller yakui.zhao
@ 2010-07-14 16:57   ` Bjorn Helgaas
  1 sibling, 0 replies; 12+ messages in thread
From: Bjorn Helgaas @ 2010-07-14 16:57 UTC (permalink / raw)
  To: yakui.zhao; +Cc: lenb, linux-acpi, minyard

On Wednesday, July 14, 2010 06:54:53 am yakui.zhao@intel.com wrote:
> From: Zhao Yakui <yakui.zhao@intel.com>

This should have a changelog saying *why* we need to export
pnp_bus_type.

I looked it up myself, and I see that the next patch adds
acpi_ipmi, which can be a module, and acpi_ipmi references
pnp_bus_type.

I don't like this exposure of pnp_bus_type.  I don't think
drivers should be using it.  I'll comment more in a response
to the next patch.

Bjorn

> Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
> cc: Bjorn Helgaas <bjorn.helgaas@hp.com>
> ---
>  drivers/pnp/driver.c |    2 ++
>  1 files changed, 2 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/pnp/driver.c b/drivers/pnp/driver.c
> index cd11b11..9aec98a 100644
> --- a/drivers/pnp/driver.c
> +++ b/drivers/pnp/driver.c
> @@ -218,6 +218,8 @@ struct bus_type pnp_bus_type = {
>  	.dev_attrs = pnp_interface_attrs,
>  };
>  
> +EXPORT_SYMBOL(pnp_bus_type);
> +
>  int pnp_register_driver(struct pnp_driver *drv)
>  {
>  	drv->driver.name = drv->name;
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller
  2010-07-14 12:54   ` [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller yakui.zhao
@ 2010-07-14 17:24     ` Bjorn Helgaas
  2010-07-15  1:31       ` ykzhao
  0 siblings, 1 reply; 12+ messages in thread
From: Bjorn Helgaas @ 2010-07-14 17:24 UTC (permalink / raw)
  To: yakui.zhao; +Cc: lenb, linux-acpi, minyard

On Wednesday, July 14, 2010 06:54:54 am yakui.zhao@intel.com wrote:
> +config ACPI_IPMI
> +	tristate "IPMI"
> +	depends on EXPERIMENTAL && IPMI_SI && IPMI_HANDLER
> +	default n
> +	help
> +	  This driver enables the ACPI to access the BMC controller. And it
> +	  uses the IPMI request/response message to communicate with BMC
> +	  controller, which can be found on on the server.
> +
> +	  To compile this driver as a module, choose M here:

This is missing the usual "the module will be called acpi_ipmi"
text.

But I don't think this is the right design.  I think it's fine to
put all this code in a separate file and have a separate config
option for it.

However, I don't think acpi_ipmi and ipmi_si should be different
modules, because they both want to be connected to the same ACPI
device.  Making acpi_ipmi a separate module means you have to
mess around with PNP core things that drivers shouldn't be using.
For example, you have match_device(), pnp_ipmi_match(), and
acpi_add_ipmi_devices() below.  These all reimplement things
the PNP core already does.

The problem with replicating this in acpi_ipmi is that it breaks
the modularity of PNP.  It means that hotplug of ACPI devices
won't work right, because acpi_ipmi won't find out about additions
or removals of IPI0001 devices.

This ACPI opregion stuff is basically an optional feature of
whatever driver claims IPI0001 devices, and I think we need to
set up and tear down the opregion handler at the same point we
claim and release the IPI0001 devices, i.e., in ipmi_si_intf.c

> +static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
> +		acpi_integer *value, int timeout)
> +{
> +	struct acpi_ipmi_buffer *buffer;
> +
> +	/*
> +	 * value is also used as output parameter. It represents the response
> +	 * IPMI message returned by IPMI command.
> +	 */
> +	buffer = (struct acpi_ipmi_buffer *)value;
> +	if (!timeout && !msg->msg_done) {
> +		buffer->status = ACPI_IPMI_TIMEOUT;
> +		return;
> +	}
> +	/*
> +	 * If the flag of msg_done is not set or the recv length is zero, it
> +	 * means that the IPMI command is not executed correctly.
> +	 * The status code will be ACPI_IPMI_UNKNOWN.
> +	 */
> +	if (!msg->msg_done || !msg->rx_len) {
> +		buffer->status = ACPI_IPMI_UNKNOWN;
> +		return;
> +	}
> +	/*
> +	 * If the IPMI response message is obtained correctly, the status code
> +	 * will be ACPI_IPMI_OK
> +	 */
> +	buffer->status = ACPI_IPMI_OK;
> +	buffer->length = msg->rx_len;
> +	memcpy(buffer->data, msg->rx_data, msg->rx_len);
> +	return;

You never need a "return" at the end of a void function.

> +}
> +static void ipmi_destroy_tx_msg(struct acpi_ipmi_device *ipmi)
> +{
> +	struct acpi_ipmi_msg *tx_msg = NULL, *temp;

No need to initialize tx_msg here; list_for_each_entry_safe()
always sets it before you can use it.

> +	int count = 20;
> +	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
> +
> +	list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list, head) {
> +		/* wake up the sleep thread on the Tx msg */
> +		complete(&tx_msg->tx_complete);
> +	}
> +
> +	/* wait for about 20 ticks to flush the tx message list */
> +	while (count--) {
> +		if (list_empty(&ipmi->tx_msg_list))
> +			break;
> +		schedule_timeout(1);

The length of time this waits depends on the arch's jiffy length.
The jiffy length is completely independent of how long it might
take to flush the message list.  Instead of waiting for "1" jiffy,
you should use some expression involving HZ, or maybe use
msecs_to_jiffies().

> +	}
> +	if (!list_empty(&ipmi->tx_msg_list))
> +		dev_warn(&pnp_dev->dev, "tx msg list is not NULL\n");
> +
> +}
> +
> +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
> +{
> +	struct acpi_ipmi_device *ipmi_device =
> +			(struct acpi_ipmi_device *)user_msg_data;

You don't need a cast here, since user_msg_data is a "void *".

> +	int msg_found = 0;
> +	struct acpi_ipmi_msg *tx_msg = NULL;

No need to initialize tx_msg here.

> +	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
> +
> +	if (msg->user != ipmi_device->user_interface) {
> +		dev_warn(&pnp_dev->dev, "Incorrect IPMI user\n");

Include the user info here, e.g.,

    dev_warn(..., "unexpected user %#x (expected %#x)\n", msg->user,
             ipmi_device->user_interface);

> +		ipmi_free_recv_msg(msg);
> +		return;
> +	}
> +	mutex_lock(&ipmi_device->mutex_lock);
> +	list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
> +		if (msg->msgid == tx_msg->tx_msgid) {
> +			/* find the message id */

Superfluous comment; remove it.

> +			msg_found = 1;
> +			break;
> +		}
> +	}
> +
> +	mutex_unlock(&ipmi_device->mutex_lock);
> +	if (!msg_found) {
> +		/* no matched msg is found . But we should free it */

Superfluous comment; remove it.

> +		ipmi_free_recv_msg(msg);
> +		dev_warn(&pnp_dev->dev, "Incorrect MSG is found\n");

Move the dev_warn() up a line and make it say something like this:

    dev_warn(..., "unexpected response (msg ID %#x)\n", msg->msgid);

> +		return;
> +	}
> +
> +	if (msg->msg.data_len) {
> +		/* copy the response data to Rx_data buffer */
> +		memcpy(tx_msg->rx_data, msg->msg_data, msg->msg.data_len);
> +		tx_msg->rx_len = msg->msg.data_len;
> +		tx_msg->msg_done = 1;
> +	}
> +	complete(&tx_msg->tx_complete);
> +	ipmi_free_recv_msg(msg);
> +};
> +
> +static void ipmi_register_bmc(int iface, struct device *dev)
> +{
> +	struct acpi_ipmi_device *ipmi_device;
> +	struct pnp_dev *device;
> +	ipmi_user_t		user;
> +	int err;
> +
> +	if (list_empty(&driver_data.ipmi_devices))
> +		return;

You don't need to test for list_empty before using
list_for_each_entry().

> +
> +	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
> +		if (ipmi_device->user_interface) {
> +			/*
> +			 * Only one user interface is allowed to be registered
> +			 * for one IPMI device.
> +			 * If we already create the user interface for
> +			 * one IPMI device, skip it
> +			 */
> +			continue;
> +		}
> +		device = ipmi_device->pnp_dev;
> +		if (dev == &device->dev) {
> +			/*
> +			 * If the dev is identical to the ACPI device,
> +			 * create the user interface.
> +			 */
> +			err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
> +						ipmi_device, &user);
> +			if (err == 0) {
> +				ipmi_device->user_interface = user;
> +				ipmi_device->ipmi_ifnum = iface;
> +			}
> +
> +			break;
> +		}
> +	}
> +}
> +
> +static void ipmi_bmc_gone(int iface)
> +{
> +	struct acpi_ipmi_device *ipmi_device, *temp;
> +
> +	if (list_empty(&driver_data.ipmi_devices))
> +		return;

You don't need to test for list_empty before using
list_for_each_entry_safe().

> +
> +	list_for_each_entry_safe(ipmi_device, temp,
> +				&driver_data.ipmi_devices, head) {
> +		if (ipmi_device->user_interface &&
> +			(ipmi_device->ipmi_ifnum == iface)) {
> +			ipmi_destroy_user(ipmi_device->user_interface);
> +			ipmi_device->user_interface = NULL;
> +			ipmi_destroy_tx_msg(ipmi_device);
> +		}
> +	}
> +}

> +	status = acpi_install_address_space_handler(ipmi->handle,
> +						    ACPI_ADR_SPACE_IPMI,
> +						    &acpi_ipmi_space_handler,
> +						    NULL, ipmi);
> +	if (ACPI_FAILURE(status)) {
> +		struct pnp_dev *pnp_dev = ipmi->pnp_dev;
> +		dev_warn(&pnp_dev->dev, "Can't register IPMI opregion for %s\n",
> +					pnp_dev_name(pnp_dev));

I'm actually not sure what pnp_dev_name() is for.  I'd rather not
add more uses of it.  I think dev_warn() prints all the information
we need, even without using pnp_dev_name().

> +static const struct pnp_device_id ipmi_device_ids[] = {
> +	{"IPI0001", 0},
> +	{"", 0},
> +};
> +
> +static int match_device(struct pnp_dev *dev, const struct pnp_device_id *ids)
> +{
> +	const struct pnp_device_id *id;
> +	struct pnp_id *pnp_id;
> +
> +	for (id = ids;  id->id[0]; id++) {
> +		pnp_id = dev->id;
> +		if (!pnp_id)
> +			return 0;
> +
> +		while (pnp_id) {
> +			if (!strncasecmp(pnp_id->id, (char *)id->id,
> +					strlen((char *)id->id)))
> +				return 1;
> +			pnp_id = pnp_id->next;
> +		};
> +	}
> +	return 0;
> +}
> +
> +static int pnp_ipmi_match(struct device *dev, void *data)
> +{
> +	struct pnp_dev *pnp_dev;
> +	struct acpi_ipmi_device *ipmi_device;
> +	struct acpi_device *acpi_dev;
> +
> +	pnp_dev = to_pnp_dev(dev);
> +
> +	if (!match_device(pnp_dev, ipmi_device_ids))
> +		return 0;
> +
> +	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
> +	if (!ipmi_device) {
> +		dev_warn(dev, "Can't allocate memory for acpi_ipmi_device\n");
> +		return -ENOMEM;
> +	}
> +	acpi_dev = pnp_acpi_device(pnp_dev);
> +	INIT_LIST_HEAD(&ipmi_device->head);
> +	ipmi_device->pnp_dev = pnp_dev;
> +	ipmi_device->handle = acpi_dev->handle;
> +
> +	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
> +	return 0;
> +}
> +
> +static void acpi_add_ipmi_devices(void)
> +{
> +	struct acpi_ipmi_device *ipmi_device;
> +
> +	bus_for_each_dev(&pnp_bus_type, NULL, NULL, pnp_ipmi_match);
> +	if (list_empty(&driver_data.ipmi_devices))
> +		return;

You don't need to test for an empty list here.

> +
> +	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
> +		mutex_init(&ipmi_device->mutex_lock);
> +		INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
> +		ipmi_install_handler(ipmi_device);
> +	}
> +}
> +
> +static void acpi_remove_ipmi_handlers(void)
> +{
> +	struct acpi_ipmi_device *ipmi_device, *temp;
> +
> +	list_for_each_entry_safe(ipmi_device, temp, &driver_data.ipmi_devices,
> +					head) {
> +		if (ipmi_device->user_interface) {
> +			/*
> +			 * If the IPMI user interface is created, it
> +			 * should be destroyed.
> +			 */
> +			ipmi_destroy_user(ipmi_device->user_interface);
> +			ipmi_device->user_interface = NULL;
> +		}
> +		list_del(&ipmi_device->head);
> +		if (!list_empty(&ipmi_device->tx_msg_list)) {
> +			/* destroy the Tx_msg list */
> +			ipmi_destroy_tx_msg(ipmi_device);
> +		}
> +		ipmi_remove_handler(ipmi_device);
> +		kfree(ipmi_device);
> +	}
> +	return;

No "return" needed here.

> +}
> +
> +static int __init acpi_ipmi_init(void)
> +{
> +	int result = 0;
> +
> +	if (acpi_disabled)
> +		return result;
> +
> +	acpi_add_ipmi_devices();
> +
> +	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
> +
> +	if (result)
> +		acpi_remove_ipmi_handlers();
> +
> +	return result;
> +}
> +
> +static void __exit acpi_ipmi_exit(void)
> +{
> +	if (acpi_disabled)
> +		return;
> +
> +	acpi_remove_ipmi_handlers();
> +	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
> +
> +	return;

No "return" needed here.

> +}
> +
> +module_init(acpi_ipmi_init);
> +module_exit(acpi_ipmi_exit);
> 

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller
  2010-07-14 17:24     ` Bjorn Helgaas
@ 2010-07-15  1:31       ` ykzhao
  2010-07-15 16:27         ` Bjorn Helgaas
  0 siblings, 1 reply; 12+ messages in thread
From: ykzhao @ 2010-07-15  1:31 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: lenb@kernel.org, linux-acpi@vger.kernel.org, minyard@acm.org

On Thu, 2010-07-15 at 01:24 +0800, Bjorn Helgaas wrote:
> On Wednesday, July 14, 2010 06:54:54 am yakui.zhao@intel.com wrote:
> > +config ACPI_IPMI
> > +	tristate "IPMI"
> > +	depends on EXPERIMENTAL && IPMI_SI && IPMI_HANDLER
> > +	default n
> > +	help
> > +	  This driver enables the ACPI to access the BMC controller. And it
> > +	  uses the IPMI request/response message to communicate with BMC
> > +	  controller, which can be found on on the server.
> > +
> > +	  To compile this driver as a module, choose M here:
> 
> This is missing the usual "the module will be called acpi_ipmi"
> text.

Will you please look at this thread
http://marc.info/?l=linux-acpi&m=127608894610765&w=4?

Before this patch, I followed your comment and put the ACPI IPMI
opregion code into the IPMI_SI module. 

But in fact the IPMI opregion code has nothing to do with the ipmi
system interface. It is enough to use the IPMI API interface. In such
case maybe it is a better choice to put the IPMI opregion code into the
separate file. (In fact Corey suggests that the IPMI opregion code be
put into the separate file). 

> 
> But I don't think this is the right design.  I think it's fine to
> put all this code in a separate file and have a separate config
> option for it.
> 
> However, I don't think acpi_ipmi and ipmi_si should be different
> modules, because they both want to be connected to the same ACPI
> device.  Making acpi_ipmi a separate module means you have to
> mess around with PNP core things that drivers shouldn't be using.
> For example, you have match_device(), pnp_ipmi_match(), and
> acpi_add_ipmi_devices() below.  These all reimplement things
> the PNP core already does.

If we decide to put the IPMI opregion code into the separate file, the
next is how to *correctly* create the IPMI user interface between ACPI
subsystem and the corresponding BMC controller.The user interface is the
communication channel used to send/receive IPMI message to/from the BMC
controller. 
In such case we will use the IPMI smi watcher to create/destroy the IPMI
user interface between ACPI and the corresponding BMC controller. But it
is noted that we should assure whether the corresponding IPMI BMC
controller is registered by using ACPI detection mechanism when creating
the relationship between ACPI and IPMI BMC controller. This is realized
by comparing the "device handle". And before comparing the "device
handle", maybe we will have to get the corresponding device lists with
the device id "IPI0001".

Do you have other idea about how to *correctly* create the user
interface between ACPI and IPMI BMC controller?

> 
> The problem with replicating this in acpi_ipmi is that it breaks
> the modularity of PNP.  It means that hotplug of ACPI devices
> won't work right, because acpi_ipmi won't find out about additions
> or removals of IPI0001 devices.
> 
> This ACPI opregion stuff is basically an optional feature of
> whatever driver claims IPI0001 devices, and I think we need to
> set up and tear down the opregion handler at the same point we
> claim and release the IPI0001 devices, i.e., in ipmi_si_intf.c

Thanks for your comments about the following code style. I will do some
cleanup next time. But the prerequisite is whether it is necessary to
put the IPMI opregion code  into the separate file. 

> 
> > +static void acpi_format_ipmi_response(struct acpi_ipmi_msg *msg,
> > +		acpi_integer *value, int timeout)
> > +{
> > +	struct acpi_ipmi_buffer *buffer;
> > +
> > +	/*
> > +	 * value is also used as output parameter. It represents the response
> > +	 * IPMI message returned by IPMI command.
> > +	 */
> > +	buffer = (struct acpi_ipmi_buffer *)value;
> > +	if (!timeout && !msg->msg_done) {
> > +		buffer->status = ACPI_IPMI_TIMEOUT;
> > +		return;
> > +	}
> > +	/*
> > +	 * If the flag of msg_done is not set or the recv length is zero, it
> > +	 * means that the IPMI command is not executed correctly.
> > +	 * The status code will be ACPI_IPMI_UNKNOWN.
> > +	 */
> > +	if (!msg->msg_done || !msg->rx_len) {
> > +		buffer->status = ACPI_IPMI_UNKNOWN;
> > +		return;
> > +	}
> > +	/*
> > +	 * If the IPMI response message is obtained correctly, the status code
> > +	 * will be ACPI_IPMI_OK
> > +	 */
> > +	buffer->status = ACPI_IPMI_OK;
> > +	buffer->length = msg->rx_len;
> > +	memcpy(buffer->data, msg->rx_data, msg->rx_len);
> > +	return;
> 
> You never need a "return" at the end of a void function.
> 
> > +}
> > +static void ipmi_destroy_tx_msg(struct acpi_ipmi_device *ipmi)
> > +{
> > +	struct acpi_ipmi_msg *tx_msg = NULL, *temp;
> 
> No need to initialize tx_msg here; list_for_each_entry_safe()
> always sets it before you can use it.
> 
> > +	int count = 20;
> > +	struct pnp_dev *pnp_dev = ipmi->pnp_dev;
> > +
> > +	list_for_each_entry_safe(tx_msg, temp, &ipmi->tx_msg_list, head) {
> > +		/* wake up the sleep thread on the Tx msg */
> > +		complete(&tx_msg->tx_complete);
> > +	}
> > +
> > +	/* wait for about 20 ticks to flush the tx message list */
> > +	while (count--) {
> > +		if (list_empty(&ipmi->tx_msg_list))
> > +			break;
> > +		schedule_timeout(1);
> 
> The length of time this waits depends on the arch's jiffy length.
> The jiffy length is completely independent of how long it might
> take to flush the message list.  Instead of waiting for "1" jiffy,
> you should use some expression involving HZ, or maybe use
> msecs_to_jiffies().
> 
> > +	}
> > +	if (!list_empty(&ipmi->tx_msg_list))
> > +		dev_warn(&pnp_dev->dev, "tx msg list is not NULL\n");
> > +
> > +}
> > +
> > +static void ipmi_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data)
> > +{
> > +	struct acpi_ipmi_device *ipmi_device =
> > +			(struct acpi_ipmi_device *)user_msg_data;
> 
> You don't need a cast here, since user_msg_data is a "void *".
> 
> > +	int msg_found = 0;
> > +	struct acpi_ipmi_msg *tx_msg = NULL;
> 
> No need to initialize tx_msg here.
> 
> > +	struct pnp_dev *pnp_dev = ipmi_device->pnp_dev;
> > +
> > +	if (msg->user != ipmi_device->user_interface) {
> > +		dev_warn(&pnp_dev->dev, "Incorrect IPMI user\n");
> 
> Include the user info here, e.g.,
> 
>     dev_warn(..., "unexpected user %#x (expected %#x)\n", msg->user,
>              ipmi_device->user_interface);
> 
> > +		ipmi_free_recv_msg(msg);
> > +		return;
> > +	}
> > +	mutex_lock(&ipmi_device->mutex_lock);
> > +	list_for_each_entry(tx_msg, &ipmi_device->tx_msg_list, head) {
> > +		if (msg->msgid == tx_msg->tx_msgid) {
> > +			/* find the message id */
> 
> Superfluous comment; remove it.
> 
> > +			msg_found = 1;
> > +			break;
> > +		}
> > +	}
> > +
> > +	mutex_unlock(&ipmi_device->mutex_lock);
> > +	if (!msg_found) {
> > +		/* no matched msg is found . But we should free it */
> 
> Superfluous comment; remove it.
> 
> > +		ipmi_free_recv_msg(msg);
> > +		dev_warn(&pnp_dev->dev, "Incorrect MSG is found\n");
> 
> Move the dev_warn() up a line and make it say something like this:
> 
>     dev_warn(..., "unexpected response (msg ID %#x)\n", msg->msgid);
> 
> > +		return;
> > +	}
> > +
> > +	if (msg->msg.data_len) {
> > +		/* copy the response data to Rx_data buffer */
> > +		memcpy(tx_msg->rx_data, msg->msg_data, msg->msg.data_len);
> > +		tx_msg->rx_len = msg->msg.data_len;
> > +		tx_msg->msg_done = 1;
> > +	}
> > +	complete(&tx_msg->tx_complete);
> > +	ipmi_free_recv_msg(msg);
> > +};
> > +
> > +static void ipmi_register_bmc(int iface, struct device *dev)
> > +{
> > +	struct acpi_ipmi_device *ipmi_device;
> > +	struct pnp_dev *device;
> > +	ipmi_user_t		user;
> > +	int err;
> > +
> > +	if (list_empty(&driver_data.ipmi_devices))
> > +		return;
> 
> You don't need to test for list_empty before using
> list_for_each_entry().
> 
> > +
> > +	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
> > +		if (ipmi_device->user_interface) {
> > +			/*
> > +			 * Only one user interface is allowed to be registered
> > +			 * for one IPMI device.
> > +			 * If we already create the user interface for
> > +			 * one IPMI device, skip it
> > +			 */
> > +			continue;
> > +		}
> > +		device = ipmi_device->pnp_dev;
> > +		if (dev == &device->dev) {
> > +			/*
> > +			 * If the dev is identical to the ACPI device,
> > +			 * create the user interface.
> > +			 */
> > +			err = ipmi_create_user(iface, &driver_data.ipmi_hndlrs,
> > +						ipmi_device, &user);
> > +			if (err == 0) {
> > +				ipmi_device->user_interface = user;
> > +				ipmi_device->ipmi_ifnum = iface;
> > +			}
> > +
> > +			break;
> > +		}
> > +	}
> > +}
> > +
> > +static void ipmi_bmc_gone(int iface)
> > +{
> > +	struct acpi_ipmi_device *ipmi_device, *temp;
> > +
> > +	if (list_empty(&driver_data.ipmi_devices))
> > +		return;
> 
> You don't need to test for list_empty before using
> list_for_each_entry_safe()

IMO it is needed as it does the operation of list deletion.

> 
> > +
> > +	list_for_each_entry_safe(ipmi_device, temp,
> > +				&driver_data.ipmi_devices, head) {
> > +		if (ipmi_device->user_interface &&
> > +			(ipmi_device->ipmi_ifnum == iface)) {
> > +			ipmi_destroy_user(ipmi_device->user_interface);
> > +			ipmi_device->user_interface = NULL;
> > +			ipmi_destroy_tx_msg(ipmi_device);
> > +		}
> > +	}
> > +}
> 
> > +	status = acpi_install_address_space_handler(ipmi->handle,
> > +						    ACPI_ADR_SPACE_IPMI,
> > +						    &acpi_ipmi_space_handler,
> > +						    NULL, ipmi);
> > +	if (ACPI_FAILURE(status)) {
> > +		struct pnp_dev *pnp_dev = ipmi->pnp_dev;
> > +		dev_warn(&pnp_dev->dev, "Can't register IPMI opregion for %s\n",
> > +					pnp_dev_name(pnp_dev));
> 
> I'm actually not sure what pnp_dev_name() is for.  I'd rather not
> add more uses of it.  I think dev_warn() prints all the information
> we need, even without using pnp_dev_name().
> 
> > +static const struct pnp_device_id ipmi_device_ids[] = {
> > +	{"IPI0001", 0},
> > +	{"", 0},
> > +};
> > +
> > +static int match_device(struct pnp_dev *dev, const struct pnp_device_id *ids)
> > +{
> > +	const struct pnp_device_id *id;
> > +	struct pnp_id *pnp_id;
> > +
> > +	for (id = ids;  id->id[0]; id++) {
> > +		pnp_id = dev->id;
> > +		if (!pnp_id)
> > +			return 0;
> > +
> > +		while (pnp_id) {
> > +			if (!strncasecmp(pnp_id->id, (char *)id->id,
> > +					strlen((char *)id->id)))
> > +				return 1;
> > +			pnp_id = pnp_id->next;
> > +		};
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int pnp_ipmi_match(struct device *dev, void *data)
> > +{
> > +	struct pnp_dev *pnp_dev;
> > +	struct acpi_ipmi_device *ipmi_device;
> > +	struct acpi_device *acpi_dev;
> > +
> > +	pnp_dev = to_pnp_dev(dev);
> > +
> > +	if (!match_device(pnp_dev, ipmi_device_ids))
> > +		return 0;
> > +
> > +	ipmi_device = kzalloc(sizeof(*ipmi_device), GFP_KERNEL);
> > +	if (!ipmi_device) {
> > +		dev_warn(dev, "Can't allocate memory for acpi_ipmi_device\n");
> > +		return -ENOMEM;
> > +	}
> > +	acpi_dev = pnp_acpi_device(pnp_dev);
> > +	INIT_LIST_HEAD(&ipmi_device->head);
> > +	ipmi_device->pnp_dev = pnp_dev;
> > +	ipmi_device->handle = acpi_dev->handle;
> > +
> > +	list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices);
> > +	return 0;
> > +}
> > +
> > +static void acpi_add_ipmi_devices(void)
> > +{
> > +	struct acpi_ipmi_device *ipmi_device;
> > +
> > +	bus_for_each_dev(&pnp_bus_type, NULL, NULL, pnp_ipmi_match);
> > +	if (list_empty(&driver_data.ipmi_devices))
> > +		return;
> 
> You don't need to test for an empty list here.
> 
> > +
> > +	list_for_each_entry(ipmi_device, &driver_data.ipmi_devices, head) {
> > +		mutex_init(&ipmi_device->mutex_lock);
> > +		INIT_LIST_HEAD(&ipmi_device->tx_msg_list);
> > +		ipmi_install_handler(ipmi_device);
> > +	}
> > +}
> > +
> > +static void acpi_remove_ipmi_handlers(void)
> > +{
> > +	struct acpi_ipmi_device *ipmi_device, *temp;
> > +
> > +	list_for_each_entry_safe(ipmi_device, temp, &driver_data.ipmi_devices,
> > +					head) {
> > +		if (ipmi_device->user_interface) {
> > +			/*
> > +			 * If the IPMI user interface is created, it
> > +			 * should be destroyed.
> > +			 */
> > +			ipmi_destroy_user(ipmi_device->user_interface);
> > +			ipmi_device->user_interface = NULL;
> > +		}
> > +		list_del(&ipmi_device->head);
> > +		if (!list_empty(&ipmi_device->tx_msg_list)) {
> > +			/* destroy the Tx_msg list */
> > +			ipmi_destroy_tx_msg(ipmi_device);
> > +		}
> > +		ipmi_remove_handler(ipmi_device);
> > +		kfree(ipmi_device);
> > +	}
> > +	return;
> 
> No "return" needed here.
> 
> > +}
> > +
> > +static int __init acpi_ipmi_init(void)
> > +{
> > +	int result = 0;
> > +
> > +	if (acpi_disabled)
> > +		return result;
> > +
> > +	acpi_add_ipmi_devices();
> > +
> > +	result = ipmi_smi_watcher_register(&driver_data.bmc_events);
> > +
> > +	if (result)
> > +		acpi_remove_ipmi_handlers();
> > +
> > +	return result;
> > +}
> > +
> > +static void __exit acpi_ipmi_exit(void)
> > +{
> > +	if (acpi_disabled)
> > +		return;
> > +
> > +	acpi_remove_ipmi_handlers();
> > +	ipmi_smi_watcher_unregister(&driver_data.bmc_events);
> > +
> > +	return;
> 
> No "return" needed here.
> 
> > +}
> > +
> > +module_init(acpi_ipmi_init);
> > +module_exit(acpi_ipmi_exit);
> > 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller
  2010-07-15  1:31       ` ykzhao
@ 2010-07-15 16:27         ` Bjorn Helgaas
  2010-07-16  2:34           ` ykzhao
  0 siblings, 1 reply; 12+ messages in thread
From: Bjorn Helgaas @ 2010-07-15 16:27 UTC (permalink / raw)
  To: ykzhao; +Cc: lenb@kernel.org, linux-acpi@vger.kernel.org, minyard@acm.org

On Wednesday, July 14, 2010 07:31:06 pm ykzhao wrote:
> On Thu, 2010-07-15 at 01:24 +0800, Bjorn Helgaas wrote:
> Will you please look at this thread
> http://marc.info/?l=linux-acpi&m=127608894610765&w=4?
> 
> Before this patch, I followed your comment and put the ACPI IPMI
> opregion code into the IPMI_SI module. 
> 
> But in fact the IPMI opregion code has nothing to do with the ipmi
> system interface. It is enough to use the IPMI API interface. In such
> case maybe it is a better choice to put the IPMI opregion code into the
> separate file. (In fact Corey suggests that the IPMI opregion code be
> put into the separate file). 

The ACPI IPMI opregion code deals with an ACPI device, and the
obvious place where you have that device is the driver "add"
function, i.e., ipmi_pnp_probe().  That's the point where the
PNP core will help you out by matching device IDs and handling
hotplug for you.

You can kludge around and do it elsewhere, but it's going to be
more work.

> > But I don't think this is the right design.  I think it's fine to
> > put all this code in a separate file and have a separate config
> > option for it.
> > 
> > However, I don't think acpi_ipmi and ipmi_si should be different
> > modules, because they both want to be connected to the same ACPI
> > device.  Making acpi_ipmi a separate module means you have to
> > mess around with PNP core things that drivers shouldn't be using.
> > For example, you have match_device(), pnp_ipmi_match(), and
> > acpi_add_ipmi_devices() below.  These all reimplement things
> > the PNP core already does.
> 
> If we decide to put the IPMI opregion code into the separate file, the
> next is how to *correctly* create the IPMI user interface between ACPI
> subsystem and the corresponding BMC controller.The user interface is the
> communication channel used to send/receive IPMI message to/from the BMC
> controller. 
> In such case we will use the IPMI smi watcher to create/destroy the IPMI
> user interface between ACPI and the corresponding BMC controller. But it
> is noted that we should assure whether the corresponding IPMI BMC
> controller is registered by using ACPI detection mechanism when creating
> the relationship between ACPI and IPMI BMC controller. This is realized
> by comparing the "device handle". And before comparing the "device
> handle", maybe we will have to get the corresponding device lists with
> the device id "IPI0001".
> 
> Do you have other idea about how to *correctly* create the user
> interface between ACPI and IPMI BMC controller?

I don't have time right now to design something new for this.

You should handle hotplug of IPI0001 devices, but you call
acpi_add_ipmi_devices() only once, at module-load time.  That
makes me think the sequence "load ipmi_si, load acpi_ipmi,
hot-add IPI0001" will not work.  I think ipmi_si will see the
new device, but I don't think acpi_ipmi will.

We should not export pnp_bus_type.  Maybe you can avoid this by
maintaining some sort of list in ipmi_si, although then you're back
to having to deal with hotplug yourself.  You'd have to have
coordinate ipmi_pnp_probe() and ipmi_pnp_remove() events between
ipmi_si and acpi_ipmi.

> > > +static void ipmi_bmc_gone(int iface)
> > > +{
> > > +	struct acpi_ipmi_device *ipmi_device, *temp;
> > > +
> > > +	if (list_empty(&driver_data.ipmi_devices))
> > > +		return;
> > 
> > You don't need to test for list_empty before using
> > list_for_each_entry_safe()
> 
> IMO it is needed as it does the operation of list deletion.

I don't think so:

 * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry

Bjorn

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller
  2010-07-15 16:27         ` Bjorn Helgaas
@ 2010-07-16  2:34           ` ykzhao
  2010-07-16 16:01             ` Bjorn Helgaas
  0 siblings, 1 reply; 12+ messages in thread
From: ykzhao @ 2010-07-16  2:34 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: lenb@kernel.org, linux-acpi@vger.kernel.org, minyard@acm.org

On Fri, 2010-07-16 at 00:27 +0800, Bjorn Helgaas wrote:
> On Wednesday, July 14, 2010 07:31:06 pm ykzhao wrote:
> > On Thu, 2010-07-15 at 01:24 +0800, Bjorn Helgaas wrote:
> > Will you please look at this thread
> > http://marc.info/?l=linux-acpi&m=127608894610765&w=4?
> > 
> > Before this patch, I followed your comment and put the ACPI IPMI
> > opregion code into the IPMI_SI module. 
> > 
> > But in fact the IPMI opregion code has nothing to do with the ipmi
> > system interface. It is enough to use the IPMI API interface. In such
> > case maybe it is a better choice to put the IPMI opregion code into the
> > separate file. (In fact Corey suggests that the IPMI opregion code be
> > put into the separate file). 
> 
> The ACPI IPMI opregion code deals with an ACPI device, and the
> obvious place where you have that device is the driver "add"
> function, i.e., ipmi_pnp_probe().  That's the point where the
> PNP core will help you out by matching device IDs and handling
> hotplug for you.

The "IPI0001" device defined in ACPI table serves the following two
purposes:
 	1. IPMI detection and registration: This is defined in IPMI spec. Now
it is discovered in the drivers/char/ipmi/ipmi_si_intf
        2. IPMI opregion support: This is defined in ACPI4.0 spec.This
is to enable that the ACPI AML code can communicate with the BMC
controller. 

Yes. If we put the opregion code with the detection code together, it
will be simple. And it is unnecessary to get the corresponding device
list again. But as the IPMI opregion code only uses the API interface of
IPMI subsystem, it will be better to put it into the separate file. In
fact this is also Corey's idea. 

> 
> You can kludge around and do it elsewhere, but it's going to be
> more work.
> 
> > > But I don't think this is the right design.  I think it's fine to
> > > put all this code in a separate file and have a separate config
> > > option for it.
> > > 
> > > However, I don't think acpi_ipmi and ipmi_si should be different
> > > modules, because they both want to be connected to the same ACPI
> > > device.  Making acpi_ipmi a separate module means you have to
> > > mess around with PNP core things that drivers shouldn't be using.
> > > For example, you have match_device(), pnp_ipmi_match(), and
> > > acpi_add_ipmi_devices() below.  These all reimplement things
> > > the PNP core already does.
> > 
> > If we decide to put the IPMI opregion code into the separate file, the
> > next is how to *correctly* create the IPMI user interface between ACPI
> > subsystem and the corresponding BMC controller.The user interface is the
> > communication channel used to send/receive IPMI message to/from the BMC
> > controller. 
> > In such case we will use the IPMI smi watcher to create/destroy the IPMI
> > user interface between ACPI and the corresponding BMC controller. But it
> > is noted that we should assure whether the corresponding IPMI BMC
> > controller is registered by using ACPI detection mechanism when creating
> > the relationship between ACPI and IPMI BMC controller. This is realized
> > by comparing the "device handle". And before comparing the "device
> > handle", maybe we will have to get the corresponding device lists with
> > the device id "IPI0001".
> > 
> > Do you have other idea about how to *correctly* create the user
> > interface between ACPI and IPMI BMC controller?
> 
> I don't have time right now to design something new for this.

Yes. The hotplug scenario should be considered. This will be handled
under two places:
   a. the IPMI pnp detection: 
   b. install opregion handle for the hotplug IPI0001 device

But in fact the acpi_pnp device is enumerated only once at the boot
time. In such case it is impossible to get the corresponding pnp device
when one "IPI0001" device is hotplug. Of course the "ipmi_si" can't see
the hotplugged "IPI0001" device.  If we really want to handle the
hotplug scenario, IMO we should change the detection mechanism from pnp
to ACPI again. Otherwise it is difficult to handle the hotplug scenario.

It is an idea to maintain the device list of "impi_si". But if we look
at the source code of "ipmi_si_intf", the corresponding device list is
defined as *static*. 

Another matter is that the "info->dev" in ipmi_pnp_probe is changed from
acpi_dev to pnp_dev. Now in order to *correctly* create the user
interface between ACPI and BMC controller, it is realized by comparing
the "device handle". And the prerequisite is that we should get the
corresponding pnp device list of "IPI0001". 
If we don't export the symbol of "pnp_bus_type", do you have any idea to
get the corresponding device list of "IPI0001"? 

Of course I can firstly get the ACPI device list of "IPI0001" and then
get the corresponding "pnp_dev" for every ACPI device of "IPI0001". But
it is more complex. 

> 
> You should handle hotplug of IPI0001 devices, but you call
> acpi_add_ipmi_devices() only once, at module-load time.  That
> makes me think the sequence "load ipmi_si, load acpi_ipmi,
> hot-add IPI0001" will not work.  I think ipmi_si will see the
> new device, but I don't think acpi_ipmi will.
> 
> We should not export pnp_bus_type.  Maybe you can avoid this by
> maintaining some sort of list in ipmi_si, although then you're back
> to having to deal with hotplug yourself.  You'd have to have
> coordinate ipmi_pnp_probe() and ipmi_pnp_remove() events between
> ipmi_si and acpi_ipmi.
> 
> > > > +static void ipmi_bmc_gone(int iface)
> > > > +{
> > > > +	struct acpi_ipmi_device *ipmi_device, *temp;
> > > > +
> > > > +	if (list_empty(&driver_data.ipmi_devices))
> > > > +		return;
> > > 
> > > You don't need to test for list_empty before using
> > > list_for_each_entry_safe()
> > 
> > IMO it is needed as it does the operation of list deletion.
> 
> I don't think so:
> 
>  * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry

There exist a lot of examples about the removal of list_entry by using
list_for_each_entry_safe. For example:
   >drivers/char/ipmi/ipmi_msghandler.c

> 
> Bjorn


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller
  2010-07-16  2:34           ` ykzhao
@ 2010-07-16 16:01             ` Bjorn Helgaas
  2010-07-19  1:52               ` ykzhao
  0 siblings, 1 reply; 12+ messages in thread
From: Bjorn Helgaas @ 2010-07-16 16:01 UTC (permalink / raw)
  To: ykzhao; +Cc: lenb@kernel.org, linux-acpi@vger.kernel.org, minyard@acm.org

On Thursday, July 15, 2010 08:34:35 pm ykzhao wrote:
> On Fri, 2010-07-16 at 00:27 +0800, Bjorn Helgaas wrote:
> > On Wednesday, July 14, 2010 07:31:06 pm ykzhao wrote:
> > > On Thu, 2010-07-15 at 01:24 +0800, Bjorn Helgaas wrote:
> > > Will you please look at this thread
> > > http://marc.info/?l=linux-acpi&m=127608894610765&w=4?
> > > 
> > > Before this patch, I followed your comment and put the ACPI IPMI
> > > opregion code into the IPMI_SI module. 
> > > 
> > > But in fact the IPMI opregion code has nothing to do with the ipmi
> > > system interface. It is enough to use the IPMI API interface. In such
> > > case maybe it is a better choice to put the IPMI opregion code into the
> > > separate file. (In fact Corey suggests that the IPMI opregion code be
> > > put into the separate file). 
> > 
> > The ACPI IPMI opregion code deals with an ACPI device, and the
> > obvious place where you have that device is the driver "add"
> > function, i.e., ipmi_pnp_probe().  That's the point where the
> > PNP core will help you out by matching device IDs and handling
> > hotplug for you.
> 
> The "IPI0001" device defined in ACPI table serves the following two
> purposes:
>  	1. IPMI detection and registration: This is defined in IPMI spec. Now
> it is discovered in the drivers/char/ipmi/ipmi_si_intf
>         2. IPMI opregion support: This is defined in ACPI4.0 spec.This
> is to enable that the ACPI AML code can communicate with the BMC
> controller. 
> 
> Yes. If we put the opregion code with the detection code together, it
> will be simple. And it is unnecessary to get the corresponding device
> list again. But as the IPMI opregion code only uses the API interface of
> IPMI subsystem, it will be better to put it into the separate file. In
> fact this is also Corey's idea.

I don't care if you put it in a separate file.  My only point is that
you shouldn't try to duplicate the device discovery that's already
done in the PNP core.

> Yes. The hotplug scenario should be considered. This will be handled
> under two places:
>    a. the IPMI pnp detection: 
>    b. install opregion handle for the hotplug IPI0001 device
> 
> But in fact the acpi_pnp device is enumerated only once at the boot
> time.

Drivers cannot rely on anything like "the acpi_pnp device is
enumerated only once at boot-time."  Device enumeration happens
in the ACPI core and PNP core, not in drivers, and drivers can't
assume anything about when it happens.

> In such case it is impossible to get the corresponding pnp device
> when one "IPI0001" device is hotplug. Of course the "ipmi_si" can't see
> the hotplugged "IPI0001" device.  If we really want to handle the
> hotplug scenario, IMO we should change the detection mechanism from pnp
> to ACPI again. Otherwise it is difficult to handle the hotplug scenario.

No, you're missing the point.  *All* drivers should be written in
the standard style that supports hotplug, because then all devices,
both those present at boot and any added later, are handled the same
way.  

ipmi_si can indeed see any hotplugged IPI0001 device, simply because
it uses pnp_register_driver().  The PNP core calls ipmi_pnp_probe()
when any IPI0001 device is discovered.  This device could be present
at boot, or it could be added later.

> It is an idea to maintain the device list of "impi_si". But if we look
> at the source code of "ipmi_si_intf", the corresponding device list is
> defined as *static*. 

This is a problem you're creating for yourself by trying to completely
separate acpi_ipmi from ipmi_pnp_probe().  I'm suggesting that you can
make your life easier by adding some kind of hook in the ipmi_pnp_probe()
path to set up the opregion handler.

> Another matter is that the "info->dev" in ipmi_pnp_probe is changed from
> acpi_dev to pnp_dev. Now in order to *correctly* create the user
> interface between ACPI and BMC controller, it is realized by comparing
> the "device handle". And the prerequisite is that we should get the
> corresponding pnp device list of "IPI0001". 
> If we don't export the symbol of "pnp_bus_type", do you have any idea to
> get the corresponding device list of "IPI0001"? 

If you have the pnp_dev for an IPI0001 device, you can always get the
ACPI handle with pnp_acpi_device().

> > > > > +static void ipmi_bmc_gone(int iface)
> > > > > +{
> > > > > +	struct acpi_ipmi_device *ipmi_device, *temp;
> > > > > +
> > > > > +	if (list_empty(&driver_data.ipmi_devices))
> > > > > +		return;
> > > > 
> > > > You don't need to test for list_empty before using
> > > > list_for_each_entry_safe()
> > > 
> > > IMO it is needed as it does the operation of list deletion.
> > 
> > I don't think so:
> > 
> >  * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
> 
> There exist a lot of examples about the removal of list_entry by using
> list_for_each_entry_safe. For example:
>    >drivers/char/ipmi/ipmi_msghandler.c

That only means other drivers also contain this unnecessary code.

There are many examples of list deletion using list_for_each_entry_safe()
where we don't check list_empty() first.  For example:

  pnp_free_resources()
  pnp_clean_resource_table()
  pnp_free_options()
  omap_mux_late_init()
  vpe_module_exit()/release_vpe()
  fsl_dma_slave_free()
  cbe_ptcal_disable()
  pmi_of_remove()

Maybe you could remove the check and do some testing to convince
yourself that it's safe.

Bjorn

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller
  2010-07-16 16:01             ` Bjorn Helgaas
@ 2010-07-19  1:52               ` ykzhao
  2010-07-19 14:54                 ` Bjorn Helgaas
  0 siblings, 1 reply; 12+ messages in thread
From: ykzhao @ 2010-07-19  1:52 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: lenb@kernel.org, linux-acpi@vger.kernel.org, minyard@acm.org

On Sat, 2010-07-17 at 00:01 +0800, Bjorn Helgaas wrote:
> On Thursday, July 15, 2010 08:34:35 pm ykzhao wrote:
> > On Fri, 2010-07-16 at 00:27 +0800, Bjorn Helgaas wrote:
> > > On Wednesday, July 14, 2010 07:31:06 pm ykzhao wrote:
> > > > On Thu, 2010-07-15 at 01:24 +0800, Bjorn Helgaas wrote:
> > > > Will you please look at this thread
> > > > http://marc.info/?l=linux-acpi&m=127608894610765&w=4?
> > > > 
> > > > Before this patch, I followed your comment and put the ACPI IPMI
> > > > opregion code into the IPMI_SI module. 
> > > > 
> > > > But in fact the IPMI opregion code has nothing to do with the ipmi
> > > > system interface. It is enough to use the IPMI API interface. In such
> > > > case maybe it is a better choice to put the IPMI opregion code into the
> > > > separate file. (In fact Corey suggests that the IPMI opregion code be
> > > > put into the separate file). 
> > > 
> > > The ACPI IPMI opregion code deals with an ACPI device, and the
> > > obvious place where you have that device is the driver "add"
> > > function, i.e., ipmi_pnp_probe().  That's the point where the
> > > PNP core will help you out by matching device IDs and handling
> > > hotplug for you.
> > 
> > The "IPI0001" device defined in ACPI table serves the following two
> > purposes:
> >  	1. IPMI detection and registration: This is defined in IPMI spec. Now
> > it is discovered in the drivers/char/ipmi/ipmi_si_intf
> >         2. IPMI opregion support: This is defined in ACPI4.0 spec.This
> > is to enable that the ACPI AML code can communicate with the BMC
> > controller. 
> > 
> > Yes. If we put the opregion code with the detection code together, it
> > will be simple. And it is unnecessary to get the corresponding device
> > list again. But as the IPMI opregion code only uses the API interface of
> > IPMI subsystem, it will be better to put it into the separate file. In
> > fact this is also Corey's idea.
> 
> I don't care if you put it in a separate file.  My only point is that
> you shouldn't try to duplicate the device discovery that's already
> done in the PNP core.
> 
> > Yes. The hotplug scenario should be considered. This will be handled
> > under two places:
> >    a. the IPMI pnp detection: 
> >    b. install opregion handle for the hotplug IPI0001 device
> > 
> > But in fact the acpi_pnp device is enumerated only once at the boot
> > time.
> 
> Drivers cannot rely on anything like "the acpi_pnp device is
> enumerated only once at boot-time."  Device enumeration happens
> in the ACPI core and PNP core, not in drivers, and drivers can't
> assume anything about when it happens.

Yes. But in fact the ACPI pnp device is enumerated only once at the
boot time. In such case it is impossible to get the corresponding pnp
device when one "IPI0001" device is hot-plug. Is it still meaningful to
consider the "hot-plug" scenario? 

> 
> > In such case it is impossible to get the corresponding pnp device
> > when one "IPI0001" device is hotplug. Of course the "ipmi_si" can't see
> > the hotplugged "IPI0001" device.  If we really want to handle the
> > hotplug scenario, IMO we should change the detection mechanism from pnp
> > to ACPI again. Otherwise it is difficult to handle the hotplug scenario.
> 
> No, you're missing the point.  *All* drivers should be written in
> the standard style that supports hotplug, because then all devices,
> both those present at boot and any added later, are handled the same
> way.  
> 
> ipmi_si can indeed see any hotplugged IPI0001 device, simply because
> it uses pnp_register_driver().  The PNP core calls ipmi_pnp_probe()
> when any IPI0001 device is discovered.  This device could be present
> at boot, or it could be added later.

> 
> > It is an idea to maintain the device list of "impi_si". But if we look
> > at the source code of "ipmi_si_intf", the corresponding device list is
> > defined as *static*. 
> 
> This is a problem you're creating for yourself by trying to completely
> separate acpi_ipmi from ipmi_pnp_probe().  I'm suggesting that you can
> make your life easier by adding some kind of hook in the ipmi_pnp_probe()
> path to set up the opregion handler.

It is an idea to add some kind of hook. If so, 
If so, we will have to export more symbols in ipmi subsystem and the
file of drivers/acpi/acpi_ipmi.c.  But the main function in the
drivers/char/ipmi/ipmi_si_intf.c is to discovery the BMC controller and
register the corresponding IPMI system interface. Is it appropriate to
export some symbols which is not related with IPMI discovery?

> > Another matter is that the "info->dev" in ipmi_pnp_probe is changed from
> > acpi_dev to pnp_dev. Now in order to *correctly* create the user
> > interface between ACPI and BMC controller, it is realized by comparing
> > the "device handle". And the prerequisite is that we should get the
> > corresponding pnp device list of "IPI0001". 
> > If we don't export the symbol of "pnp_bus_type", do you have any idea to
> > get the corresponding device list of "IPI0001"? 
> 
> If you have the pnp_dev for an IPI0001 device, you can always get the
> ACPI handle with pnp_acpi_device().
> 
> > > > > > +static void ipmi_bmc_gone(int iface)
> > > > > > +{
> > > > > > +	struct acpi_ipmi_device *ipmi_device, *temp;
> > > > > > +
> > > > > > +	if (list_empty(&driver_data.ipmi_devices))
> > > > > > +		return;
> > > > > 
> > > > > You don't need to test for list_empty before using
> > > > > list_for_each_entry_safe()
> > > > 
> > > > IMO it is needed as it does the operation of list deletion.
> > > 
> > > I don't think so:
> > > 
> > >  * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
> > 
> > There exist a lot of examples about the removal of list_entry by using
> > list_for_each_entry_safe. For example:
> >    >drivers/char/ipmi/ipmi_msghandler.c
> 
> That only means other drivers also contain this unnecessary code.
> 
> There are many examples of list deletion using list_for_each_entry_safe()
> where we don't check list_empty() first.  For example:
> 
>   pnp_free_resources()
>   pnp_clean_resource_table()
>   pnp_free_options()
>   omap_mux_late_init()
>   vpe_module_exit()/release_vpe()
>   fsl_dma_slave_free()
>   cbe_ptcal_disable()
>   pmi_of_remove()
> 
> Maybe you could remove the check and do some testing to convince
> yourself that it's safe.
> 
> Bjorn

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller
  2010-07-19  1:52               ` ykzhao
@ 2010-07-19 14:54                 ` Bjorn Helgaas
  2010-07-20  0:09                   ` ykzhao
  0 siblings, 1 reply; 12+ messages in thread
From: Bjorn Helgaas @ 2010-07-19 14:54 UTC (permalink / raw)
  To: ykzhao; +Cc: lenb@kernel.org, linux-acpi@vger.kernel.org, minyard@acm.org

On Sunday, July 18, 2010 07:52:28 pm ykzhao wrote:
> On Sat, 2010-07-17 at 00:01 +0800, Bjorn Helgaas wrote:
> > On Thursday, July 15, 2010 08:34:35 pm ykzhao wrote:
> > > On Fri, 2010-07-16 at 00:27 +0800, Bjorn Helgaas wrote:
> > > > The ACPI IPMI opregion code deals with an ACPI device, and the
> > > > obvious place where you have that device is the driver "add"
> > > > function, i.e., ipmi_pnp_probe().  That's the point where the
> > > > PNP core will help you out by matching device IDs and handling
> > > > hotplug for you.
...
> > > Yes. The hotplug scenario should be considered. This will be handled
> > > under two places:
> > >    a. the IPMI pnp detection: 
> > >    b. install opregion handle for the hotplug IPI0001 device
> > > 
> > > But in fact the acpi_pnp device is enumerated only once at the boot
> > > time.
> > 
> > Drivers cannot rely on anything like "the acpi_pnp device is
> > enumerated only once at boot-time."  Device enumeration happens
> > in the ACPI core and PNP core, not in drivers, and drivers can't
> > assume anything about when it happens.
> 
> Yes. But in fact the ACPI pnp device is enumerated only once at the
> boot time.

NO!  You can NOT assume the ACPI PNP device is enumerated once at
boot time.

That may be the way the current implementation behaves, but the
driver interface does not specify that, and it is likely to change.

Bjorn
--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller
  2010-07-19 14:54                 ` Bjorn Helgaas
@ 2010-07-20  0:09                   ` ykzhao
  0 siblings, 0 replies; 12+ messages in thread
From: ykzhao @ 2010-07-20  0:09 UTC (permalink / raw)
  To: Bjorn Helgaas
  Cc: lenb@kernel.org, linux-acpi@vger.kernel.org, minyard@acm.org

On Mon, 2010-07-19 at 22:54 +0800, Bjorn Helgaas wrote:
> On Sunday, July 18, 2010 07:52:28 pm ykzhao wrote:
> > On Sat, 2010-07-17 at 00:01 +0800, Bjorn Helgaas wrote:
> > > On Thursday, July 15, 2010 08:34:35 pm ykzhao wrote:
> > > > On Fri, 2010-07-16 at 00:27 +0800, Bjorn Helgaas wrote:
> > > > > The ACPI IPMI opregion code deals with an ACPI device, and the
> > > > > obvious place where you have that device is the driver "add"
> > > > > function, i.e., ipmi_pnp_probe().  That's the point where the
> > > > > PNP core will help you out by matching device IDs and handling
> > > > > hotplug for you.
> ...
> > > > Yes. The hotplug scenario should be considered. This will be handled
> > > > under two places:
> > > >    a. the IPMI pnp detection: 
> > > >    b. install opregion handle for the hotplug IPI0001 device
> > > > 
> > > > But in fact the acpi_pnp device is enumerated only once at the boot
> > > > time.
> > > 
> > > Drivers cannot rely on anything like "the acpi_pnp device is
> > > enumerated only once at boot-time."  Device enumeration happens
> > > in the ACPI core and PNP core, not in drivers, and drivers can't
> > > assume anything about when it happens.
> > 
> > Yes. But in fact the ACPI pnp device is enumerated only once at the
> > boot time.
> 
> NO!  You can NOT assume the ACPI PNP device is enumerated once at
> boot time.
> 
> That may be the way the current implementation behaves, but the
> driver interface does not specify that, and it is likely to change.

Ok. I will try to use the hook to implement it.

thanks.
	Yakui
> 
> Bjorn

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2010-07-20  0:13 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-07-14 12:54 [PATCH -v7 0/2] ACPI: Install the ACPI IPMI opregion yakui.zhao
2010-07-14 12:54 ` [PATCH 1/2_v7] ACPI: Export symbol of pnp_bus_type yakui.zhao
2010-07-14 12:54   ` [PATCH_v7 2/2] ACPI/IPMI: Add the IPMI opregion driver to enable ACPI to access BMC controller yakui.zhao
2010-07-14 17:24     ` Bjorn Helgaas
2010-07-15  1:31       ` ykzhao
2010-07-15 16:27         ` Bjorn Helgaas
2010-07-16  2:34           ` ykzhao
2010-07-16 16:01             ` Bjorn Helgaas
2010-07-19  1:52               ` ykzhao
2010-07-19 14:54                 ` Bjorn Helgaas
2010-07-20  0:09                   ` ykzhao
2010-07-14 16:57   ` [PATCH 1/2_v7] ACPI: Export symbol of pnp_bus_type Bjorn Helgaas

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